Skip to content

feat(stack-supabase): EQL v3 encrypted-JSON querying (containment + selector predicates)#653

Open
coderdan wants to merge 2 commits into
mainfrom
feat/eql-v3-supabase-json
Open

feat(stack-supabase): EQL v3 encrypted-JSON querying (containment + selector predicates)#653
coderdan wants to merge 2 commits into
mainfrom
feat/eql-v3-supabase-json

Conversation

@coderdan

@coderdan coderdan commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Closes #650. Adds encrypted-JSON querying for types.Json columns to the v3 Supabase adapter — the PostgREST counterpart of the Drizzle #621 containment and #623 selector work.

The wire-form investigation (the hard part per #650)

A live probe against PostgREST v12.2.12 + eql 3.0.0 (with log_statement=all to read the emitted SQL) settled what is reachable:

Capability Wire form Verdict
Containment ?doc=cs.<storage envelope> ✅ PostgREST casts the operand to the column's own domain → eql_v3."@>"(eql_v3_json, eql_v3_json)ste_vec_contains
Selector eq same, with the path-shaped needle ✅ equality-at-a-path IS containment of {user:{role:'admin'}}
Selector ne not.cs. ✅ and absent-path rows are included — matching Drizzle's documented ne semantics for free
Selector ordering ❌ PostgREST wraps arrow paths in to_jsonb(col) (observed: WHERE to_jsonb("t"."doc")->$1 > $2), stripping the domain; bare-column comparison is blocked by design (P0001)

Ordering needs an EQL-bundle overload — filed as cipherstash/encrypt-query-language#407 (needle-comparison semantics for the currently-blocked (eql_v3_json, eql_v3_json) comparisons). Until then the skill steers ordering-at-a-path to Drizzle's ops.selector().

Surface

es.from("events").select("id").contains("payload", { user: { role: "admin" } })
es.from("events").select("id").selectorEq("payload", "$.user.role", "admin")
es.from("events").select("id").selectorNe("payload", "$.user.role", "admin") // includes absent-path rows
  • Typed key sets: V3SearchableJsonKeys; a types.Json column no longer erases to never (core QueryTypesForColumn gains the searchableJson arm).
  • Guards: matches() steers to contains()/selectorEq() on JSON columns; scalar ops rejected by capability; paths/leaves validated with the same rules as Drizzle (shared helpers moved to @cipherstash/stack/adapter-kit; drizzle re-exports unchanged).
  • The operand is a full storage envelope — same INTERIM shape and GET-query-string caveat as every v3 filter operand (documented in the skill).

Testing

  • 17 unit tests (operand routing, guards, path validation) + 6 type-level tests (key-set narrowing, operand shapes).
  • 11 live integration tests with real crypto against real PostgREST as anon — containment (multi-leaf, absent-value), selector eq (nested paths, string leaves, absent path), selector ne absent-path inclusion, raw cs, capability rejections.
  • Full suites green: stack 816 unit + type tests, drizzle 368 unit + 16 live JSON (proving the helper move), supabase 468 unit + 31 type + 677 integration.
  • Skills: stash-supabase gains an "Encrypted JSON querying" section (incl. the ordering limitation and ciphertext-in-WHERE disclosure); the stale contains()-always-throws bullet corrected.
  • Changeset: @cipherstash/stack-supabase minor + @cipherstash/stack minor.

https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w

Summary by CodeRabbit

  • New Features

    • Added encrypted JSON querying for Supabase v3 types.Json columns, including exact encrypted sub-document containment via contains().
    • Added JSONPath-based equality/inequality filters via selectorEq() and selectorNe(), with nested dot-notation paths.
    • Added support for structured and raw encrypted containment filtering, plus not(... ) for JSON negation.
  • Bug Fixes

    • Improved validation for invalid selector paths, unsupported operands, and incompatible operators.
    • Fixed encrypted-JSON query typing so types.Json columns are correctly queryable.
  • Tests

    • Added unit, type-level, and live integration coverage for encrypted JSON querying semantics.
  • Documentation

    • Updated Supabase and encryption documentation for supported patterns, limitations, and missing-path behavior.

…electors)

Closes #650. The v3 Supabase adapter gains encrypted-JSON querying on
types.Json columns:

- contains(col, subDocument) — exact encrypted ste_vec containment. A live
  PostgREST probe established the wire form: PostgREST casts the `cs.` filter
  operand to the column's own domain, so a storage-encrypted needle reaches
  eql_v3."@>"(eql_v3_json, eql_v3_json) → ste_vec_contains. The operand is a
  full storage envelope, the same INTERIM shape (and GET-query-string caveat)
  as every other v3 filter operand.
- selectorEq/selectorNe(col, path, value) — JSONPath equality at a
  dot-notation path, compiled to containment of the reconstructed needle
  ({user:{role:'admin'}}); ne compiles to not.cs and INCLUDES rows lacking
  the path, mirroring the Drizzle selector's documented semantics (verified
  live). Paths/leaves share the Drizzle adapter's validation.
- Selector ORDERING is not expressible over PostgREST: arrow paths get
  to_jsonb-wrapped (domain stripped) and bare-column comparison is blocked by
  design. Filed cipherstash/encrypt-query-language#407 proposing a
  needle-comparison overload; until then the methods don't exist and the
  skill points at Drizzle's ops.selector() for ordering.

Core: QueryTypesForColumn gains the searchableJson arm (types.Json no longer
resolves to never, so the typed key sets stop erasing JSON columns), and the
JSONPath selector-path helpers move from stack-drizzle to
@cipherstash/stack/adapter-kit (drizzle re-exports them unchanged) so both
adapters share one validation surface.

Adapter plumbing: V3SearchableJsonKeys key set + typed contains overload +
selector methods on both builder interfaces; the capability resolver re-types
the cs-collected term to searchableJson on JSON columns (free-text and JSON
capabilities are mutually exclusive by construction); matches() steers to
contains()/selectorEq() on JSON columns; not(col,'contains',…) is allowed on
JSON (honest negated exact containment) and stays rejected on text.

Tests: 17 unit (operand routing, guards, path validation), 6 type-level
(key-set narrowing, operand shapes), 11 live integration with real crypto
against real PostgREST as anon (containment, selector eq/ne incl. absent-path
inclusion, raw cs, capability rejections). Full suites: stack 816, drizzle
368 unit + 16 live JSON, supabase 468 unit + 677 integration — all green.

Claude-Session: https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w
@coderdan coderdan requested a review from a team as a code owner July 15, 2026 09:55
@changeset-bot

changeset-bot Bot commented Jul 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b257953

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 10 packages
Name Type
@cipherstash/stack-supabase Minor
@cipherstash/stack Minor
stash Patch
@cipherstash/basic-example Patch
@cipherstash/bench Patch
@cipherstash/prisma-next Patch
@cipherstash/stack-drizzle Patch
@cipherstash/test-kit Patch
@cipherstash/prisma-next-example Patch
@cipherstash/e2e Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Supabase v3 now supports encrypted JSON containment and JSONPath selector equality/inequality. Shared selector validation is exposed through adapter-kit, QueryTypesForColumn recognizes searchableJson, and tests and documentation cover runtime, type-level, and live integration behavior.

Changes

Encrypted JSON querying

Layer / File(s) Summary
Shared selector contracts
packages/stack/src/eql/v3/selector-path.ts, packages/stack/src/eql/v3/columns.ts, packages/stack/src/adapter-kit.ts
Adds shared JSONPath parsing, validation, selector-document reconstruction, and searchableJson query typing.
Supabase encrypted JSON operators
packages/stack-supabase/src/types.ts, packages/stack-supabase/src/query-builder-v3.ts, packages/stack-supabase/src/helpers.ts
Adds typed and runtime support for contains, selectorEq, selectorNe, and negated JSON containment with capability checks.
Query behavior validation
packages/stack-supabase/__tests__/*, packages/stack-supabase/integration/json.integration.test.ts
Covers operand encryption, selector validation, compile-time gates, raw filter routing, and live containment and selector semantics.
Adapter reuse and release documentation
packages/stack-drizzle/src/v3/operators.ts, .changeset/*, skills/*/SKILL.md
Centralizes Drizzle selector helpers and documents the Supabase encrypted JSON query surface.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

Possibly related PRs

  • cipherstash/stack#651 — Modifies related Drizzle selector machinery that now consumes the shared path helpers.

Suggested reviewers: calvinbrewer

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: encrypted JSON querying with containment and selector predicates.
Linked Issues check ✅ Passed The PR implements containment, selectorEq/selectorNe, searchableJson typing, live tests, and shared JSONPath validation required by #650.
Out of Scope Changes check ✅ Passed The extra docs, changeset, adapter exports, and test updates are directly supporting the encrypted-JSON querying work.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/eql-v3-supabase-json

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…uerying

Ten findings from the high-effort review of #653, all fixed:

1. Guard bypass on non-method spellings: the freeTextSearch→searchableJson
   re-typing now enforces the operand boundary AT THE RESOLVER — raw
   .filter(col,'cs',string), .or() string/structured conditions, and
   not(col,'contains',scalar) get the actionable sub-document steer instead of
   silently storage-encrypting a JSON scalar; not(col,'matches',…) on a JSON
   column gets the same steer matches() gives.
2. Type-surface regression: scalar predicates (eq/gt/in/…) are compile-excluded
   again for types.Json columns (V3FilterableKeys now excludes columns with no
   scalar capability); dedicated filter('cs')/not('contains') overloads keep the
   raw spellings typed. Pinned with @ts-expect-error tests.
3. Empty/non-plain needles: contains('payload', {}) and [] are rejected
   ("matches every row" — Drizzle parity), and Date/Map/RegExp instances get
   the sub-document steer instead of serializing to scalars or {}.
4. Array-leaf semantics: the live probe showed finder-claimed ANY-element
   matching is WRONG with real protect-ffi — a scalar needle does NOT match an
   array at the path (elements are encoded under their own selectors). Docs
   corrected to the observed truth and pinned by live fixtures (scalar needle
   misses the array row; the full array through contains() hits it).
5. SQL-NULL documents: selectorNe now compiles to a structured OR
   (payload.is.null, payload.not.cs.<needle>) so NULL-document rows are
   INCLUDED, matching Drizzle's ne for both absence cases — verified live.
6. Integration test: removed the phantom two-arg from(TABLE, schema) (v3 from()
   takes one argument) and the identity casts; tsc over integration/ is clean.
7. Exposure honesty: the skill now states the JSON-needle ciphertext scales
   per-node (Drizzle containment ships none) and cites the new tracking issue
   #654; empty-needle rejection documented.
8. Changeset gains the required 'stash': patch for the skills change.
9. stash-encryption skill now names the Supabase route beside Drizzle's for
   both containment and selectors (with the ordering caveat).
10. Live decrypt round-trip added (select payload → toEqual original doc), plus
    live not-contains, and the unit not-contains asserts the encrypted envelope.

Also from below-the-fold: the INTENTIONAL FORK JSDoc re-attached to
EncryptionOperatorError (the re-export had split them); parseSelectorSegments
rejects whitespace at segment boundaries (was silently addressing 'user ');
unsupportedLeafReason handles null explicitly (was steering null to
contains()); the as-unknown-as double-cast replaced with a plain widening;
dead V3ContainsValue removed; core's matrix test-d pins
QueryTypesForColumn<Json> = 'searchableJson'; stale "three scalar kinds"
comment fixed.

Suites: supabase 476 unit + 31 type + 681 live integration (15 in the JSON
suite incl. NULL-doc and array-leaf fixtures), stack 62 type tests, drizzle
368 — all green.

Claude-Session: https://claude.ai/code/session_01MxTTPaPP16m6br7Hoab94w

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@skills/stash-encryption/SKILL.md`:
- Around line 688-697: Remove the references to the sibling “stash-supabase” and
“stash-drizzle” skills in the JSON containment and JSONPath selector
documentation. Make this section self-contained by retaining or adding the
necessary integration details directly in SKILL.md, without directing users to
other skill files.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 66894212-682c-43f9-b3be-d5566c41cbad

📥 Commits

Reviewing files that changed from the base of the PR and between 5fe9a2f and b257953.

📒 Files selected for processing (12)
  • .changeset/supabase-v3-json-querying.md
  • packages/stack-drizzle/src/v3/operators.ts
  • packages/stack-supabase/__tests__/supabase-v3-builder.test.ts
  • packages/stack-supabase/__tests__/supabase-v3-json.test.ts
  • packages/stack-supabase/__tests__/supabase-v3.test-d.ts
  • packages/stack-supabase/integration/json.integration.test.ts
  • packages/stack-supabase/src/query-builder-v3.ts
  • packages/stack-supabase/src/types.ts
  • packages/stack/__tests__/v3-matrix/matrix.test-d.ts
  • packages/stack/src/eql/v3/selector-path.ts
  • skills/stash-encryption/SKILL.md
  • skills/stash-supabase/SKILL.md
🚧 Files skipped from review as they are similar to previous changes (9)
  • packages/stack-supabase/tests/supabase-v3.test-d.ts
  • packages/stack-supabase/tests/supabase-v3-builder.test.ts
  • .changeset/supabase-v3-json-querying.md
  • packages/stack/src/eql/v3/selector-path.ts
  • packages/stack-supabase/integration/json.integration.test.ts
  • skills/stash-supabase/SKILL.md
  • packages/stack-supabase/tests/supabase-v3-json.test.ts
  • packages/stack-supabase/src/query-builder-v3.ts
  • packages/stack-drizzle/src/v3/operators.ts

Comment on lines +688 to +697
Through the Drizzle v3 integration this is `ops.contains(col, subObject)` (on Supabase: `contains(col, subObject)` — see the `stash-supabase` skill) — see
the `stash-drizzle` skill.

**JSONPath selector-with-constraint** is the second query pattern: it compares
the encrypted value at a JSONPath, e.g. `metadata->'age' > 21`. Through the
Drizzle v3 integration this is `ops.selector(col, '$.path').{eq,ne,gt,gte,lt,lte}(value)`.
Its unique power over containment is **ordering at a path**
Drizzle v3 integration this is `ops.selector(col, '$.path').{eq,ne,gt,gte,lt,lte}(value)`;
on Supabase it is `selectorEq(col, path, value)` / `selectorNe(col, path, value)`
— equality/inequality only, since selector ORDERING over PostgREST needs an
EQL-bundle change (cipherstash/encrypt-query-language#407); see the
`stash-supabase` skill. Drizzle's unique power over containment is **ordering at a path**

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove references to sibling skill files.

The documentation instructs users to refer to the stash-supabase and stash-drizzle skills. As per path instructions, each skill file must be self-sufficient and should not rely on sibling files for content that must ship to users, because only SKILL.md is inlined.

📝 Proposed fix
-Through the Drizzle v3 integration this is `ops.contains(col, subObject)` (on Supabase: `contains(col, subObject)` — see the `stash-supabase` skill) — see
-the `stash-drizzle` skill.
+Through the Drizzle v3 integration this is `ops.contains(col, subObject)`. On Supabase, this is `contains(col, subObject)`.
 
 **JSONPath selector-with-constraint** is the second query pattern: it compares
 the encrypted value at a JSONPath, e.g. `metadata->'age' > 21`. Through the
 Drizzle v3 integration this is `ops.selector(col, '$.path').{eq,ne,gt,gte,lt,lte}(value)`;
 on Supabase it is `selectorEq(col, path, value)` / `selectorNe(col, path, value)`
 — equality/inequality only, since selector ORDERING over PostgREST needs an
-EQL-bundle change (cipherstash/encrypt-query-language#407); see the
-`stash-supabase` skill. Drizzle's unique power over containment is **ordering at a path**
+EQL-bundle change (cipherstash/encrypt-query-language#407). Drizzle's unique power over containment is **ordering at a path**
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Through the Drizzle v3 integration this is `ops.contains(col, subObject)` (on Supabase: `contains(col, subObject)` — see the `stash-supabase` skill) — see
the `stash-drizzle` skill.
**JSONPath selector-with-constraint** is the second query pattern: it compares
the encrypted value at a JSONPath, e.g. `metadata->'age' > 21`. Through the
Drizzle v3 integration this is `ops.selector(col, '$.path').{eq,ne,gt,gte,lt,lte}(value)`.
Its unique power over containment is **ordering at a path**
Drizzle v3 integration this is `ops.selector(col, '$.path').{eq,ne,gt,gte,lt,lte}(value)`;
on Supabase it is `selectorEq(col, path, value)` / `selectorNe(col, path, value)`
— equality/inequality only, since selector ORDERING over PostgREST needs an
EQL-bundle change (cipherstash/encrypt-query-language#407); see the
`stash-supabase` skill. Drizzle's unique power over containment is **ordering at a path**
Through the Drizzle v3 integration this is `ops.contains(col, subObject)`. On Supabase, this is `contains(col, subObject)`.
**JSONPath selector-with-constraint** is the second query pattern: it compares
the encrypted value at a JSONPath, e.g. `metadata->'age' > 21`. Through the
Drizzle v3 integration this is `ops.selector(col, '$.path').{eq,ne,gt,gte,lt,lte}(value)`;
on Supabase it is `selectorEq(col, path, value)` / `selectorNe(col, path, value)`
— equality/inequality only, since selector ORDERING over PostgREST needs an
EQL-bundle change (cipherstash/encrypt-query-language#407). Drizzle's unique power over containment is **ordering at a path**
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/stash-encryption/SKILL.md` around lines 688 - 697, Remove the
references to the sibling “stash-supabase” and “stash-drizzle” skills in the
JSON containment and JSONPath selector documentation. Make this section
self-contained by retaining or adding the necessary integration details directly
in SKILL.md, without directing users to other skill files.

Source: Coding guidelines

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds EQL v3 encrypted-JSON querying support to the Supabase v3 adapter (@cipherstash/stack-supabase) for types.Json (public.eql_v3_json) columns, including exact encrypted containment and JSONPath selector equality/inequality, and shares selector-path parsing/validation logic across first-party adapters via @cipherstash/stack/adapter-kit.

Changes:

  • Adds a shared selector-path helper module (parse/validate path, reconstruct selector needle document, scalar-leaf gating) and re-exports it from @cipherstash/stack/adapter-kit.
  • Extends core v3 column typing to include searchableJson (QueryTypesForColumn) and pins the behavior in core type-level tests.
  • Implements Supabase v3 encrypted JSON querying (contains, selectorEq, selectorNe, raw cs/not.cs routing) with guards + adds unit/type/live integration coverage; updates shipped skills + adds changeset.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
skills/stash-supabase/SKILL.md Documents encrypted JSON querying on Supabase v3 (containment + selector eq/ne) and updates prior guidance about contains().
skills/stash-encryption/SKILL.md Cross-skill documentation update to mention the Supabase selector eq/ne surface and ordering limitation.
packages/stack/src/eql/v3/selector-path.ts New shared JSON selector path parsing/validation + selector needle reconstruction + leaf gating.
packages/stack/src/eql/v3/columns.ts Extends QueryTypesForColumn to include the searchableJson capability arm.
packages/stack/src/adapter-kit.ts Re-exports selector-path helpers from adapter-kit for adapter reuse.
packages/stack/tests/v3-matrix/matrix.test-d.ts Pins types.JsonQueryTypesForColumn = 'searchableJson' in core type suite.
packages/stack-supabase/src/types.ts Adds JSON-query key sets and method overload types (V3SearchableJsonKeys, selector leaf types, JSON contains values).
packages/stack-supabase/src/query-builder-v3.ts Implements encrypted JSON containment/selector filters over PostgREST cs/not.cs, plus runtime operand/path/leaf guards and capability routing.
packages/stack-supabase/src/helpers.ts Updates operator→queryType mapping commentary for contains/matches behavior with JSON columns.
packages/stack-supabase/integration/json.integration.test.ts New live PostgREST integration tests for encrypted JSON containment + selector eq/ne semantics (incl. absent-path + SQL-NULL behavior).
packages/stack-supabase/tests/supabase-v3.test-d.ts Adds type-level coverage for JSON query key sets and selector/contains overload correctness.
packages/stack-supabase/tests/supabase-v3-json.test.ts Adds unit tests around operand routing, guards, and mock-encryption behavior for JSON querying.
packages/stack-supabase/tests/supabase-v3-builder.test.ts Updates a guard test to reflect the newly supported searchableJson query type.
packages/stack-supabase/tests/helpers/supabase-mock.ts Adjusts the mock envelope tagging to support null-prototype selector needle objects.
packages/stack-drizzle/src/v3/operators.ts Removes duplicated selector-path helpers and re-uses the shared adapter-kit implementation (with compatibility re-exports).
.changeset/supabase-v3-json-querying.md Adds release notes and version bumps for @cipherstash/stack-supabase, @cipherstash/stack, and stash (skills ship).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +219 to +223
* The scalar leaf value the selector methods compare at a JSONPath. Objects and
* arrays are rejected (that shape is `contains()`); see the adapters' shared
* `unsupportedLeafReason`.
*/
export type SelectorLeafValue = string | number | bigint | boolean | Date
type Instance = Awaited<ReturnType<typeof encryptedSupabaseV3>>
let instance: Instance

type Row = { row_key: string; payload: JsonDocument; test_run_id: string }
Comment on lines +84 to +86
throw new Error(
`[supabase v3]: encrypted JSON containment on column "${column}" takes a sub-document (plain object or array) to match, got ${value === null ? 'null' : Array.isArray(value) ? 'an array' : typeof value === 'object' ? (value as object).constructor?.name || 'a non-plain object' : typeof value}.`,
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

EQL v3 JSON on Supabase: encrypted containment + JSONPath selector querying

2 participants