-
Notifications
You must be signed in to change notification settings - Fork 3
refactor(stack): EQL v3 types namespace on @cipherstash/stack/eql/v3
#541
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
tobyhede
merged 12 commits into
feat/eql-v3-text-search-schema
from
feat/eql-v3-types-module
Jul 6, 2026
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
c20cecd
refactor(stack): extract EQL v3 DSL into eql/v3 types module
tobyhede 380be64
fix(stack): guard against duplicate v3 column DB names in build()
tobyhede 684bdbc
fix(stack): export v3 Buildable* contracts from public types entrypoint
tobyhede a672451
test(stack): migrate v3-matrix suite to eql/v3 types namespace
tobyhede 790f2f6
test(stack): cover lock-context encrypt guards and wasm newClient shape
tobyhede 4f0982d
test(stack): re-enable matrix-live-pg live SQL coverage (stale skip)
tobyhede c627aba
fix(stack): emit hm index for text order domains (eql_v3 SQL domain r…
tobyhede c7690e3
test(stack): v3 lock-context coverage + text_match/no-op/empty-config…
tobyhede 0eebebb
chore(stack): EQL v3 maintainability follow-ups from the #547 review
freshtonic beddbf0
test(stack): cover empty text order pg rejection
tobyhede b0b9789
test(stack): avoid empty text search pg seed
tobyhede dcd0fe7
test(stack): normalize date storage decrypt assertion
tobyhede File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 6 additions & 0 deletions
6
docs/superpowers/plans/2026-06-30-eql-v3-text-search-schema-plan.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 6 additions & 0 deletions
6
docs/superpowers/specs/2026-06-30-eql-v3-text-search-schema-design.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
packages/stack/__tests__/encrypt-lock-context-guards.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,103 @@ | ||
| /** | ||
| * Offline guard tests for the lock-context encrypt path. | ||
| * | ||
| * `EncryptOperationWithLockContext.execute()` re-applies the NaN / Infinity | ||
| * runtime guards that the non-lock `EncryptOperation.execute()` has. The | ||
| * non-lock guards are exercised by the live `number-protect.test.ts` (its | ||
| * `beforeAll` builds a real client), but the lock-context arm — reached via | ||
| * `encrypt(value).withLockContext(...)` — had no coverage in any suite. These | ||
| * tests mock `@cipherstash/protect-ffi` so they run in CI without credentials | ||
| * and assert that: | ||
| * 1. NaN / +Infinity / -Infinity are rejected as failures with the same | ||
| * messages as the non-lock path, and | ||
| * 2. the guard short-circuits *before* the FFI encrypt call (a leaked NaN | ||
| * must never reach the ciphertext boundary). | ||
| * | ||
| * Every case runs against both a v2 fluent-builder column and a v3 domain | ||
| * column: the guards live on the shared `EncryptOperationWithLockContext`, so | ||
| * both schema styles must take the identical short-circuit path. | ||
| */ | ||
|
|
||
| import { beforeEach, describe, expect, it, vi } from 'vitest' | ||
| import { encryptedTable as encryptedTableV3, types } from '@/eql/v3' | ||
| import { LockContext } from '@/identity' | ||
| import { Encryption } from '@/index' | ||
| import { encryptedColumn, encryptedTable } from '@/schema' | ||
|
|
||
| vi.mock('@cipherstash/protect-ffi', () => ({ | ||
| // `getErrorCode` does `error instanceof ProtectError` on the failure path, | ||
| // so the mock must export the class even though the guards throw plain Errors. | ||
| ProtectError: class ProtectError extends Error {}, | ||
| newClient: vi.fn(async () => ({ __mock: 'client' })), | ||
| encrypt: vi.fn(async () => ({ v: 2, c: 'ciphertext' })), | ||
| decrypt: vi.fn(async () => 'decrypted'), | ||
| })) | ||
|
|
||
| import * as ffi from '@cipherstash/protect-ffi' | ||
|
|
||
| const users = encryptedTable('users', { | ||
| score: encryptedColumn('score').dataType('number').equality().orderAndRange(), | ||
|
tobyhede marked this conversation as resolved.
|
||
| }) | ||
|
|
||
| const usersV3 = encryptedTableV3('users_v3', { | ||
| score: types.Int4Ord('score'), | ||
| }) | ||
|
|
||
| // biome-ignore lint/suspicious/noExplicitAny: test helper reads the Result union | ||
| const failure = (result: any) => result.failure | ||
|
|
||
| let client: Awaited<ReturnType<typeof Encryption>> | ||
|
|
||
| beforeEach(async () => { | ||
| vi.clearAllMocks() | ||
| process.env.CS_WORKSPACE_CRN = 'crn:ap-southeast-2.aws:test-workspace' | ||
|
tobyhede marked this conversation as resolved.
|
||
| client = await Encryption({ schemas: [users, usersV3] }) | ||
| }) | ||
|
|
||
| describe.each([ | ||
| ['v2 fluent builder', { column: users.score, table: users }], | ||
| ['v3 domain type', { column: usersV3.score, table: usersV3 }], | ||
| ] as const)('encrypt with lock context rejects non-finite numbers (%s)', (_variant, target) => { | ||
| it('rejects NaN and never reaches the FFI', async () => { | ||
| const result = await client | ||
| .encrypt(Number.NaN, target) | ||
| .withLockContext(new LockContext()) | ||
|
|
||
| expect(failure(result)).toBeDefined() | ||
| expect(failure(result)?.message).toContain('Cannot encrypt NaN value') | ||
| expect(vi.mocked(ffi.encrypt)).not.toHaveBeenCalled() | ||
| }) | ||
|
|
||
| it('rejects +Infinity and never reaches the FFI', async () => { | ||
| const result = await client | ||
| .encrypt(Number.POSITIVE_INFINITY, target) | ||
| .withLockContext(new LockContext()) | ||
|
|
||
| expect(failure(result)).toBeDefined() | ||
| expect(failure(result)?.message).toContain('Cannot encrypt Infinity value') | ||
| expect(vi.mocked(ffi.encrypt)).not.toHaveBeenCalled() | ||
| }) | ||
|
|
||
| it('rejects -Infinity and never reaches the FFI', async () => { | ||
| const result = await client | ||
| .encrypt(Number.NEGATIVE_INFINITY, target) | ||
| .withLockContext(new LockContext()) | ||
|
|
||
| expect(failure(result)).toBeDefined() | ||
| expect(failure(result)?.message).toContain('Cannot encrypt Infinity value') | ||
| expect(vi.mocked(ffi.encrypt)).not.toHaveBeenCalled() | ||
| }) | ||
|
|
||
| it('accepts a finite number and forwards it to the FFI', async () => { | ||
| // Positive control: proves the guards above reject *because* of the value, | ||
| // not because the lock-context path is broken for all numbers. | ||
| const result = await client | ||
| .encrypt(42, target) | ||
| .withLockContext(new LockContext()) | ||
|
|
||
| expect(failure(result)).toBeUndefined() | ||
| expect(vi.mocked(ffi.encrypt)).toHaveBeenCalledTimes(1) | ||
| const opts = vi.mocked(ffi.encrypt).mock.calls[0][1] | ||
| expect(opts.plaintext).toBe(42) | ||
| }) | ||
| }) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import { describe } from 'vitest' | ||
|
|
||
| /** | ||
| * Shared env gates for the live (network-touching) suites. Previously each | ||
| * live suite re-declared these; one definition keeps the credential list — | ||
| * and therefore what "live" means — from drifting between files. | ||
| * | ||
| * Callers must `import 'dotenv/config'` BEFORE importing this module (all | ||
| * live suites already do, as their first import) so the env is populated | ||
| * when these are evaluated. | ||
| */ | ||
|
|
||
| /** True when live CipherStash (ZeroKMS/CTS) credentials are configured. */ | ||
| export const LIVE_CIPHERSTASH_ENABLED = Boolean( | ||
| process.env.CS_WORKSPACE_CRN && | ||
| process.env.CS_CLIENT_ID && | ||
| process.env.CS_CLIENT_KEY && | ||
| process.env.CS_CLIENT_ACCESS_KEY, | ||
| ) | ||
|
|
||
| /** True when live credentials AND a Postgres `DATABASE_URL` are configured. */ | ||
| export const LIVE_EQL_V3_PG_ENABLED = Boolean( | ||
| process.env.DATABASE_URL && LIVE_CIPHERSTASH_ENABLED, | ||
| ) | ||
|
|
||
| export const describeLive = LIVE_CIPHERSTASH_ENABLED ? describe : describe.skip | ||
|
|
||
| export const describeLivePg = LIVE_EQL_V3_PG_ENABLED ? describe : describe.skip |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.