@cipherstash/stack 0.18.0 → 1.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +704 -0
- package/README.md +97 -79
- package/dist/adapter-kit.cjs +923 -0
- package/dist/adapter-kit.cjs.map +1 -0
- package/dist/adapter-kit.d.cts +82 -0
- package/dist/adapter-kit.d.ts +82 -0
- package/dist/adapter-kit.js +47 -0
- package/dist/adapter-kit.js.map +1 -0
- package/dist/base-operation-AOAIvsSB.d.cts +32 -0
- package/dist/base-operation-FXEzUXIq.d.ts +32 -0
- package/dist/{chunk-VPOTNP4K.js → chunk-3B5ZX3IS.js} +3 -1
- package/dist/chunk-3B5ZX3IS.js.map +1 -0
- package/dist/chunk-7333ZC6L.js +48 -0
- package/dist/chunk-7333ZC6L.js.map +1 -0
- package/dist/{chunk-XWPGAHBZ.js → chunk-BNO32ZMV.js} +1 -1
- package/dist/chunk-BNO32ZMV.js.map +1 -0
- package/dist/chunk-CLM7E4I6.js +4439 -0
- package/dist/chunk-CLM7E4I6.js.map +1 -0
- package/dist/{chunk-LLCCNKDL.js → chunk-HQANMV7R.js} +3 -3
- package/dist/{chunk-LLCCNKDL.js.map → chunk-HQANMV7R.js.map} +1 -1
- package/dist/chunk-JSG2AMDI.js +17 -0
- package/dist/chunk-JSG2AMDI.js.map +1 -0
- package/dist/chunk-OFQ555AX.js +191 -0
- package/dist/chunk-OFQ555AX.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-V2Q3NYIH.js +494 -0
- package/dist/chunk-V2Q3NYIH.js.map +1 -0
- package/dist/chunk-X3JRXEIB.js +98 -0
- package/dist/chunk-X3JRXEIB.js.map +1 -0
- package/dist/{chunk-ZYOZHZMJ.js → chunk-ZTP5QJ27.js} +193 -169
- package/dist/chunk-ZTP5QJ27.js.map +1 -0
- package/dist/client.cjs +4124 -49
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +6 -5
- package/dist/client.d.ts +6 -5
- package/dist/client.js +3 -1
- package/dist/columns-D2_YzrCX.d.cts +622 -0
- package/dist/columns-rZc7fQHI.d.ts +622 -0
- package/dist/dynamodb/index.cjs +19 -12
- package/dist/dynamodb/index.cjs.map +1 -1
- package/dist/dynamodb/index.d.cts +3 -2
- package/dist/dynamodb/index.d.ts +3 -2
- package/dist/dynamodb/index.js +8 -10
- package/dist/dynamodb/index.js.map +1 -1
- package/dist/encryption/index.cjs +4496 -222
- package/dist/encryption/index.cjs.map +1 -1
- package/dist/encryption/index.d.cts +807 -6
- package/dist/encryption/index.d.ts +807 -6
- package/dist/encryption/index.js +15 -7
- package/dist/encryption/v3.cjs +7896 -0
- package/dist/encryption/v3.cjs.map +1 -0
- package/dist/encryption/v3.d.cts +103 -0
- package/dist/encryption/v3.d.ts +103 -0
- package/dist/encryption/v3.js +189 -0
- package/dist/encryption/v3.js.map +1 -0
- package/dist/eql/v3/index.cjs +663 -0
- package/dist/eql/v3/index.cjs.map +1 -0
- package/dist/eql/v3/index.d.cts +173 -0
- package/dist/eql/v3/index.d.ts +173 -0
- package/dist/eql/v3/index.js +99 -0
- package/dist/eql/v3/index.js.map +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/identity/index.cjs +55 -35
- package/dist/identity/index.cjs.map +1 -1
- package/dist/identity/index.d.cts +54 -30
- package/dist/identity/index.d.ts +54 -30
- package/dist/identity/index.js +9 -177
- package/dist/identity/index.js.map +1 -1
- package/dist/index-BquA71_Y.d.ts +24 -0
- package/dist/index-fhWTOV0K.d.cts +24 -0
- package/dist/index.cjs +4547 -243
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -19
- package/dist/index.d.ts +18 -19
- package/dist/index.js +20 -5
- package/dist/index.js.map +1 -1
- package/dist/schema/index.cjs +4128 -49
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.cts +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.js +3 -1
- package/dist/{types-public-BTxqjmtv.d.cts → types-public-QMjYNfQO.d.cts} +448 -251
- package/dist/{types-public-BTxqjmtv.d.ts → types-public-QMjYNfQO.d.ts} +448 -251
- package/dist/types-public.cjs.map +1 -1
- package/dist/types-public.d.cts +2 -2
- package/dist/types-public.d.ts +2 -2
- package/dist/types-public.js +2 -1
- package/dist/wasm-inline.d.ts +1643 -0
- package/dist/wasm-inline.js +4901 -0
- package/dist/wasm-inline.js.map +1 -0
- package/package.json +74 -52
- package/dist/chunk-VPOTNP4K.js.map +0 -1
- package/dist/chunk-XWPGAHBZ.js.map +0 -1
- package/dist/chunk-YWQXTZ5M.js +0 -383
- package/dist/chunk-YWQXTZ5M.js.map +0 -1
- package/dist/chunk-ZYOZHZMJ.js.map +0 -1
- package/dist/client-CIsfZ2zg.d.ts +0 -692
- package/dist/client-Dt98Rugc.d.cts +0 -692
- package/dist/drizzle/index.cjs +0 -1558
- package/dist/drizzle/index.cjs.map +0 -1
- package/dist/drizzle/index.d.cts +0 -358
- package/dist/drizzle/index.d.ts +0 -358
- package/dist/drizzle/index.js +0 -1215
- package/dist/drizzle/index.js.map +0 -1
- package/dist/supabase/index.cjs +0 -1526
- package/dist/supabase/index.cjs.map +0 -1
- package/dist/supabase/index.d.cts +0 -144
- package/dist/supabase/index.d.ts +0 -144
- package/dist/supabase/index.js +0 -899
- package/dist/supabase/index.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/adapter-kit.ts","../src/encryption/helpers/index.ts","../src/schema/match-defaults.ts","../src/eql/v3/columns.ts","../src/eql/v3/types.ts","../src/eql/v3/domain-registry.ts","../../../node_modules/.pnpm/evlog@1.11.0_next@15.5.18_react-dom@19.2.3_react@19.2.3__react@19.2.3__react@19.2.3/node_modules/evlog/src/utils.ts","../../../node_modules/.pnpm/evlog@1.11.0_next@15.5.18_react-dom@19.2.3_react@19.2.3__react@19.2.3__react@19.2.3/node_modules/evlog/src/logger.ts","../src/utils/logger/index.ts"],"sourcesContent":["/**\n * Internal support surface for FIRST-PARTY adapter packages\n * (`@cipherstash/stack-supabase`, `@cipherstash/stack-drizzle`).\n *\n * These symbols were previously reached through deep `@/` internal imports while\n * the Supabase and Drizzle adapters lived INSIDE this package. Splitting them into\n * their own packages (see issue #627) means those imports must resolve across a\n * package boundary — so exactly the symbols the adapters consume are re-exported\n * here, from one narrow, versioned entry point, rather than leaking six internal\n * module paths into the public surface.\n *\n * NOT a general-purpose public API. This is the seam between core and the\n * first-party adapters; treat it as `@internal`-adjacent. Anything an END USER\n * should reach has a dedicated subpath (`./schema`, `./eql/v3`, `./types`,\n * `./identity`, `./errors`). If a symbol here graduates to genuine public use,\n * promote it to one of those instead of widening this file's contract.\n *\n * The full member list is small on purpose; every addition is an audit decision.\n */\n\n// Model → encrypted-PG-composite helpers used by the v2 Supabase mutation path.\nexport {\n bulkModelsToEncryptedPgComposites,\n modelToEncryptedPgComposites,\n} from './encryption/helpers/index.js'\n\n// Audit config carried by chainable operations.\nexport type { AuditConfig } from './encryption/operations/base-operation.js'\n\n// v3 column model + the date-like cast set the Supabase builder uses to\n// reconstruct `Date` values from PostgREST select aliases.\nexport {\n type AnyEncryptedV3Column,\n DATE_LIKE_CASTS,\n EncryptedV3Column,\n} from './eql/v3/columns.js'\n\n// Domain registry: the Supabase adapter classifies introspected columns by their\n// Postgres domain and rebuilds each column's encryption config from it.\nexport {\n DOMAIN_REGISTRY,\n factoryForDomain,\n stripDomainSchema,\n} from './eql/v3/domain-registry.js'\n\n// Shared match-index guard (short-needle rejection), reused by both adapters.\nexport { matchNeedleError } from './schema/match-defaults.js'\n// Shared structured logger (adapters log rejections through the same instance).\nexport { logger } from './utils/logger/index.js'\n","import type {\n Encrypted as CipherStashEncrypted,\n EncryptedQuery as CipherStashEncryptedQuery,\n EncryptedV3Query as CipherStashEncryptedV3Query,\n KeysetIdentifier as KeysetIdentifierFfi,\n} from '@cipherstash/protect-ffi'\nimport type { Encrypted, EncryptedQueryResult, KeysetIdentifier } from '@/types'\n\n/**\n * The shape `encryptQuery` / `encryptQueryBulk` can return: a full storage\n * payload (returned for v2 `ste_vec_term` containment queries), a query-only\n * payload with no ciphertext (scalar `unique`/`match`/`ore` lookups and\n * `ste_vec_selector` path queries), or — under `eqlVersion: 3` — the\n * `eql_v3.jsonb_query` containment needle.\n *\n * TODO: duplicated in `@cipherstash/protect` — see\n * `packages/protect/src/helpers/index.ts`. Both copies should be removed once\n * `@cipherstash/protect-ffi` exports a named alias for the `encryptQuery`\n * return type (https://github.com/cipherstash/stack/pull/473).\n */\ntype EncryptedQueryTerm =\n | CipherStashEncrypted\n | CipherStashEncryptedQuery\n | CipherStashEncryptedV3Query\n\nexport type EncryptedPgComposite = {\n data: Encrypted\n}\n\n/**\n * Helper function to transform an encrypted payload into a PostgreSQL composite type.\n * Use this when inserting data via Supabase or similar clients.\n */\nexport function encryptedToPgComposite(obj: Encrypted): EncryptedPgComposite {\n return {\n data: obj,\n }\n}\n\n/**\n * Helper function to transform an encrypted payload into a PostgreSQL composite literal string.\n * Use this when querying with `.eq()` or similar equality operations in Supabase.\n *\n * @example\n * ```typescript\n * const literal = encryptedToCompositeLiteral(encrypted)\n * await supabase.from('table').select().eq('column', literal)\n * ```\n */\nexport function encryptedToCompositeLiteral(obj: EncryptedQueryTerm): string {\n return `(${JSON.stringify(JSON.stringify(obj))})`\n}\n\n/**\n * Helper function to transform an encrypted payload into an escaped PostgreSQL composite literal string.\n * Use this when you need the composite literal format to be escaped as a string value.\n *\n * @example\n * ```typescript\n * const escapedLiteral = encryptedToEscapedCompositeLiteral(encrypted)\n * ```\n */\nexport function encryptedToEscapedCompositeLiteral(\n obj: EncryptedQueryTerm,\n): string {\n return JSON.stringify(encryptedToCompositeLiteral(obj))\n}\n\n/**\n * Format an encrypted result based on the requested return type.\n *\n * - `'composite-literal'` → PostgreSQL composite literal string `(\"json\")`\n * - `'escaped-composite-literal'` → escaped variant `\"(\\\"json\\\")\"`\n * - default (`'eql'` or omitted) → raw encrypted object\n */\nexport function formatEncryptedResult(\n encrypted: EncryptedQueryTerm,\n returnType?: string,\n): EncryptedQueryResult {\n if (returnType === 'composite-literal') {\n return encryptedToCompositeLiteral(encrypted)\n }\n if (returnType === 'escaped-composite-literal') {\n return encryptedToEscapedCompositeLiteral(encrypted)\n }\n return encrypted\n}\n\n/**\n * Helper function to transform a model's encrypted fields into PostgreSQL composite types\n */\nexport function modelToEncryptedPgComposites<T extends Record<string, unknown>>(\n model: T,\n): T {\n const result: Record<string, unknown> = {}\n\n for (const [key, value] of Object.entries(model)) {\n if (isEncryptedPayload(value)) {\n result[key] = encryptedToPgComposite(value)\n } else {\n result[key] = value\n }\n }\n\n return result as T\n}\n\n/**\n * Helper function to transform multiple models' encrypted fields into PostgreSQL composite types\n */\nexport function bulkModelsToEncryptedPgComposites<\n T extends Record<string, unknown>,\n>(models: T[]): T[] {\n return models.map((model) => modelToEncryptedPgComposites(model))\n}\n\nexport function toFfiKeysetIdentifier(\n keyset: KeysetIdentifier | undefined,\n): KeysetIdentifierFfi | undefined {\n if (!keyset) return undefined\n\n if ('name' in keyset) {\n return { Name: keyset.name }\n }\n\n return { Uuid: keyset.id }\n}\n\n/**\n * Helper function to check if a value is an encrypted payload\n */\nexport function isEncryptedPayload(value: unknown): value is Encrypted {\n if (value === null) return false\n if (typeof value !== 'object') return false\n\n const obj = value as Record<string, unknown>\n\n // Must have version field (number)\n if (!('v' in obj) || typeof obj.v !== 'number') return false\n\n // Must have index field (object)\n if (!('i' in obj) || typeof obj.i !== 'object') return false\n\n // Must have either ciphertext (c) or searchable vector (sv)\n if (!('c' in obj) && !('sv' in obj)) return false\n\n return true\n}\n\nexport {\n buildNestedObject,\n parseJsonbPath,\n toJsonPath,\n} from './jsonb'\n","import type { MatchIndexOpts } from './index'\n\n/**\n * Fully-resolved match-index options: every field present and non-`undefined`.\n *\n * `MatchIndexOpts` (the user-facing tuning input) has all fields optional —\n * each is `.default(...).optional()` in the zod schema, so its inferred type is\n * `T | undefined`. This type pins the BUILT/resolved shape explicitly via\n * `NonNullable<...>`, which states the non-null intent directly and is robust\n * regardless of `Required<>`'s subtle, `exactOptionalPropertyTypes`-dependent\n * stripping semantics.\n */\nexport type BuiltMatchIndexOpts = {\n tokenizer: NonNullable<MatchIndexOpts['tokenizer']>\n token_filters: NonNullable<MatchIndexOpts['token_filters']>\n k: NonNullable<MatchIndexOpts['k']>\n m: NonNullable<MatchIndexOpts['m']>\n include_original: NonNullable<MatchIndexOpts['include_original']>\n}\n\n/**\n * Default match-index parameters — the single source of truth shared by the\n * v2 `freeTextSearch()` builder and the v3 domain builders.\n *\n * `include_original: true` is the v2 builder's historical default rather than\n * the zod-schema default of `false`. It is INERT: protect-ffi accepts the flag\n * and ignores it (measured across 0.24 and 0.29, EQL v2 and v3 — the emitted\n * bloom is trigram-only either way). It is kept here only so the v2 emitted\n * config does not move. The v3 domain builders override it to `false`, the\n * value a substring-search domain wants if ffi ever starts honouring it.\n *\n * In particular it does NOT let a value shorter than the tokenizer's\n * `token_length` be matched: such a value blooms to nothing under either\n * setting, which is exactly the fail-open {@link matchNeedleError} guards.\n *\n * This is a FACTORY (not a shared `const`) so every caller gets fresh, unaliased\n * nested objects (`tokenizer`, `token_filters` and the `{ kind: 'downcase' }`\n * inside it). A shared const would be shallow-copied by `{ ...DEFAULT }`, leaving\n * those nested objects aliased across every column — a caller mutating one built\n * config could then corrupt the defaults used by later columns.\n */\nexport function defaultMatchOpts(): BuiltMatchIndexOpts {\n return {\n tokenizer: { kind: 'ngram', token_length: 3 },\n token_filters: [{ kind: 'downcase' }],\n k: 6,\n m: 2048,\n include_original: true,\n }\n}\n\n/**\n * Deep-clone a built match block (`tokenizer` and `token_filters` are its only\n * nested values) so no emitted config or stored builder state ever aliases\n * another's nested objects — a caller mutating one cannot corrupt the other.\n */\nexport function cloneMatchOpts(opts: BuiltMatchIndexOpts): BuiltMatchIndexOpts {\n return {\n ...opts,\n tokenizer: { ...opts.tokenizer },\n token_filters: opts.token_filters.map((f) => ({ ...f })),\n }\n}\n\n/**\n * Resolve user-supplied `freeTextSearch(opts)` input into a fully-built match\n * block: each provided key replaces its default, omitted keys keep the default\n * (`opts?.x ?? default.x`). The single source of truth for that five-field merge\n * shared by the v2 `freeTextSearch()` builder and the v3 domain builders.\n *\n * The result is deep-cloned ({@link cloneMatchOpts}) so a caller mutating their\n * own `opts` object (or its nested `tokenizer`/`token_filters`) after this call\n * can never leak into the stored builder state or the emitted config — clone-on-\n * write for both builders, not just v3.\n */\nexport function resolveMatchOpts(opts?: MatchIndexOpts): BuiltMatchIndexOpts {\n const defaults = defaultMatchOpts()\n return cloneMatchOpts({\n tokenizer: opts?.tokenizer ?? defaults.tokenizer,\n token_filters: opts?.token_filters ?? defaults.token_filters,\n k: opts?.k ?? defaults.k,\n m: opts?.m ?? defaults.m,\n include_original: opts?.include_original ?? defaults.include_original,\n })\n}\n\n/**\n * The shortest needle a match index can answer, or `undefined` when its\n * tokenizer imposes no floor (`standard` splits on word boundaries, so any\n * non-empty needle yields at least one token).\n *\n * Accepts the loose {@link MatchIndexOpts} because a `ColumnSchema`'s built\n * `indexes.match` is typed from the zod schema, where `tokenizer` is optional.\n * An absent tokenizer resolves to the same default the schema itself applies\n * (`ngram`, `token_length: 3`) rather than skipping the floor — skipping would\n * reintroduce the fail-open this guard exists to close.\n */\nexport function matchNeedleMinLength(opts: MatchIndexOpts): number | undefined {\n const tokenizer = opts.tokenizer ?? defaultMatchOpts().tokenizer\n return tokenizer.kind === 'ngram' ? tokenizer.token_length : undefined\n}\n\n/**\n * Why a needle cannot be answered by this match index, or `undefined` when it\n * can. Callers throw their own error type with this as the reason.\n *\n * A needle that tokenizes to NOTHING has an empty bloom filter, and\n * `stored_bf @> '{}'` is true for every row (\"contains nothing, contained by\n * everything\"). Such a query is unanswerable rather than merely unmatched, so\n * it must fail loudly instead of silently returning the whole table. Two ways\n * to tokenize to nothing:\n *\n * - the empty needle, under ANY tokenizer;\n * - a needle shorter than the ngram tokenizer's `token_length`.\n *\n * The ngram floor counts Unicode CODEPOINTS, because that is what the tokenizer\n * counts. `needle.length` would count UTF-16 code units and wave through an\n * astral-plane needle: `'👍👍'` is 4 code units but only 2 codepoints, yields no\n * trigram, and (measured live) matched every row. Graphemes are the wrong unit\n * in the other direction — NFD `'éé'` is 4 codepoints but 2 grapheme clusters,\n * and does yield trigrams.\n *\n * Currently wired ONLY into the v3 Drizzle adapter (`eql/v3/drizzle/operators`).\n * It lives here, beside the shared match defaults, because v2 builds the same\n * bloom filters and needs the same floor — but v2's `like`/`ilike` path remains\n * unguarded and still fails open. Do not reuse this for v2 without first\n * measuring what its tokenizer actually receives: v2 needles carry SQL wildcards\n * (`'%ada%'`), so the floor may have to apply to the unwrapped term rather than\n * to the string the caller passed.\n */\nexport function matchNeedleError(\n needle: unknown,\n opts: MatchIndexOpts,\n): string | undefined {\n if (typeof needle !== 'string') return undefined\n\n // Codepoints, not UTF-16 code units — see above.\n const length = [...needle].length\n const min = matchNeedleMinLength(opts)\n\n if (length === 0) {\n return `free-text search needs a non-empty search term; an empty term produces no tokens and would match every row.`\n }\n if (min === undefined || length >= min) return undefined\n return `free-text search needs at least ${min} characters (the index tokenizer's token_length), but the search term ${JSON.stringify(needle)} has ${length}. A shorter term produces no tokens and would match every row.`\n}\n","import type { ColumnSchema } from '@/schema'\nimport { defaultMatchOpts } from '@/schema/match-defaults'\n\n/**\n * The query capabilities a v3 concrete domain exposes. These are SDK-facing\n * semantic flags describing what kinds of query terms a column can produce —\n * NOT the raw EQL index keys. They are metadata only and never emitted by\n * `build()`.\n *\n * - `equality`: exact-match lookups (EQL `hm`, or comparison via `ob`).\n * - `orderAndRange`: comparison / range lookups (EQL `op`, or `ob` on `_ord_ore` domains).\n * - `freeTextSearch`: tokenised substring match (EQL `bf`).\n * - `searchableJson`: encrypted-JSONB containment / selector lookups (EQL\n * `ste_vec`). Optional and mutually exclusive with the scalar flags — a\n * `public.eql_v3_json` document is queried by structure, not by scalar term.\n */\nexport type QueryCapabilities = Readonly<{\n equality: boolean\n orderAndRange: boolean\n freeTextSearch: boolean\n searchableJson?: boolean\n}>\n\n/**\n * The `cast_as` kinds whose decrypted plaintext reconstructs to a JS `Date`.\n *\n * SINGLE SOURCE OF TRUTH for the date-like set. Both the type-level\n * {@link PlaintextFromKind} and the runtime `rowReconstructor` (encryption/v3.ts)\n * derive their \"reconstructs to `Date`\" decision from this array, so the next\n * `Date`-backed cast is added in exactly one place — never hand-synced across a\n * type and a runtime guard that could silently drift.\n *\n * (`timestamp` reconstructs to `Date` just like `date`, but its `cast_as` tells\n * the FFI not to truncate the time-of-day.)\n */\nexport const DATE_LIKE_CASTS = ['date', 'timestamp'] as const\n/** A `cast_as` kind that reconstructs to `Date` — see {@link DATE_LIKE_CASTS}. */\nexport type DateLikeCast = (typeof DATE_LIKE_CASTS)[number]\n\n/** The plaintext (TypeScript) kind a v3 domain decrypts to. A subset of the\n * SDK `CastAs` enum, restricted to the scalar kinds v3 domains actually use. */\ntype PlaintextKind =\n | 'string'\n | 'number'\n | 'bigint'\n | 'boolean'\n | 'json'\n | DateLikeCast\n\n/**\n * The full, literal definition of a v3 domain. This is the LOAD-BEARING type:\n * the base column class carries a private field of this type so that every\n * concrete (otherwise-empty) subclass is discriminated by its literal\n * `eqlType`/`castAs`/`capabilities` — TypeScript empty subclasses are NOT\n * nominal, so without this a storage-only `boolean` column would be assignable to\n * a storage-only `date` column and plaintext inference would collapse.\n */\ntype V3DomainDefinition = Readonly<{\n eqlType: `public.${string}`\n castAs: PlaintextKind\n capabilities: QueryCapabilities\n}>\n\n/** Type-level mirror of {@link isQueryableCapabilities}: `false` for a\n * storage-only domain (all capability flags `false`), `true` otherwise. */\ntype QueryableFlag<D extends V3DomainDefinition> = D['capabilities'] extends {\n equality: false\n orderAndRange: false\n freeTextSearch: false\n searchableJson?: false | undefined\n}\n ? false\n : true\n\nconst STORAGE_ONLY = {\n equality: false,\n orderAndRange: false,\n freeTextSearch: false,\n} as const\n\nconst EQUALITY_ONLY = {\n equality: true,\n orderAndRange: false,\n freeTextSearch: false,\n} as const\n\nconst ORDER_AND_RANGE = {\n equality: true,\n orderAndRange: true,\n freeTextSearch: false,\n} as const\n\nconst MATCH_ONLY = {\n equality: false,\n orderAndRange: false,\n freeTextSearch: true,\n} as const\n\nconst TEXT_SEARCH = {\n equality: true,\n orderAndRange: true,\n freeTextSearch: true,\n} as const\n\n/**\n * The concrete EQL v3 domain name for a full-capability text column.\n * Recorded as metadata for future DDL / query-dialect increments; it is\n * intentionally absent from the emitted encrypt config.\n */\nexport const TEXT_SEARCH_EQL_TYPE = 'public.eql_v3_text_search'\n\n// Per-domain literal definitions. Each concrete column subclass is parameterised\n// by `typeof <CONST>`; the literal `eqlType`/`castAs`/`capabilities` on each is\n// what makes the otherwise-empty subclasses nominally distinct (see\n// V3DomainDefinition). Order mirrors eql-bindings `CATALOG` order.\n//\n// Exported for the `types` namespace factory (see ./types); they are internal\n// building blocks and are intentionally NOT re-exported from the public barrel.\nexport const INTEGER = {\n eqlType: 'public.eql_v3_integer',\n castAs: 'number',\n capabilities: STORAGE_ONLY,\n} as const\nexport const INTEGER_EQ = {\n eqlType: 'public.eql_v3_integer_eq',\n castAs: 'number',\n capabilities: EQUALITY_ONLY,\n} as const\nexport const INTEGER_ORD_ORE = {\n eqlType: 'public.eql_v3_integer_ord_ore',\n castAs: 'number',\n capabilities: ORDER_AND_RANGE,\n} as const\nexport const INTEGER_ORD = {\n eqlType: 'public.eql_v3_integer_ord',\n castAs: 'number',\n capabilities: ORDER_AND_RANGE,\n} as const\n\nexport const SMALLINT = {\n eqlType: 'public.eql_v3_smallint',\n castAs: 'number',\n capabilities: STORAGE_ONLY,\n} as const\nexport const SMALLINT_EQ = {\n eqlType: 'public.eql_v3_smallint_eq',\n castAs: 'number',\n capabilities: EQUALITY_ONLY,\n} as const\nexport const SMALLINT_ORD_ORE = {\n eqlType: 'public.eql_v3_smallint_ord_ore',\n castAs: 'number',\n capabilities: ORDER_AND_RANGE,\n} as const\nexport const SMALLINT_ORD = {\n eqlType: 'public.eql_v3_smallint_ord',\n castAs: 'number',\n capabilities: ORDER_AND_RANGE,\n} as const\n\n// bigint (int8) domains. Plaintext is a JS `bigint` (always decrypts to\n// `bigint`); bounds are the full i64 range. protect-ffi round-trips an in-range\n// native bigint losslessly, and values outside the signed 64-bit range are\n// rejected client-side by `assertValidNumericValue` before they reach the FFI\n// (`*_ord_ope` variants are out of scope — CIP-3403.)\nexport const BIGINT = {\n eqlType: 'public.eql_v3_bigint',\n castAs: 'bigint',\n capabilities: STORAGE_ONLY,\n} as const\nexport const BIGINT_EQ = {\n eqlType: 'public.eql_v3_bigint_eq',\n castAs: 'bigint',\n capabilities: EQUALITY_ONLY,\n} as const\nexport const BIGINT_ORD_ORE = {\n eqlType: 'public.eql_v3_bigint_ord_ore',\n castAs: 'bigint',\n capabilities: ORDER_AND_RANGE,\n} as const\nexport const BIGINT_ORD = {\n eqlType: 'public.eql_v3_bigint_ord',\n castAs: 'bigint',\n capabilities: ORDER_AND_RANGE,\n} as const\n\nexport const DATE = {\n eqlType: 'public.eql_v3_date',\n castAs: 'date',\n capabilities: STORAGE_ONLY,\n} as const\nexport const DATE_EQ = {\n eqlType: 'public.eql_v3_date_eq',\n castAs: 'date',\n capabilities: EQUALITY_ONLY,\n} as const\nexport const DATE_ORD_ORE = {\n eqlType: 'public.eql_v3_date_ord_ore',\n castAs: 'date',\n capabilities: ORDER_AND_RANGE,\n} as const\nexport const DATE_ORD = {\n eqlType: 'public.eql_v3_date_ord',\n castAs: 'date',\n capabilities: ORDER_AND_RANGE,\n} as const\n\nexport const TIMESTAMP = {\n eqlType: 'public.eql_v3_timestamp',\n castAs: 'timestamp',\n capabilities: STORAGE_ONLY,\n} as const\nexport const TIMESTAMP_EQ = {\n eqlType: 'public.eql_v3_timestamp_eq',\n castAs: 'timestamp',\n capabilities: EQUALITY_ONLY,\n} as const\nexport const TIMESTAMP_ORD_ORE = {\n eqlType: 'public.eql_v3_timestamp_ord_ore',\n castAs: 'timestamp',\n capabilities: ORDER_AND_RANGE,\n} as const\nexport const TIMESTAMP_ORD = {\n eqlType: 'public.eql_v3_timestamp_ord',\n castAs: 'timestamp',\n capabilities: ORDER_AND_RANGE,\n} as const\n\nexport const NUMERIC = {\n eqlType: 'public.eql_v3_numeric',\n castAs: 'number',\n capabilities: STORAGE_ONLY,\n} as const\nexport const NUMERIC_EQ = {\n eqlType: 'public.eql_v3_numeric_eq',\n castAs: 'number',\n capabilities: EQUALITY_ONLY,\n} as const\nexport const NUMERIC_ORD_ORE = {\n eqlType: 'public.eql_v3_numeric_ord_ore',\n castAs: 'number',\n capabilities: ORDER_AND_RANGE,\n} as const\nexport const NUMERIC_ORD = {\n eqlType: 'public.eql_v3_numeric_ord',\n castAs: 'number',\n capabilities: ORDER_AND_RANGE,\n} as const\n\nexport const TEXT = {\n eqlType: 'public.eql_v3_text',\n castAs: 'string',\n capabilities: STORAGE_ONLY,\n} as const\nexport const TEXT_EQ = {\n eqlType: 'public.eql_v3_text_eq',\n castAs: 'string',\n capabilities: EQUALITY_ONLY,\n} as const\nexport const TEXT_MATCH = {\n eqlType: 'public.eql_v3_text_match',\n castAs: 'string',\n capabilities: MATCH_ONLY,\n} as const\nexport const TEXT_ORD_ORE = {\n eqlType: 'public.eql_v3_text_ord_ore',\n castAs: 'string',\n capabilities: ORDER_AND_RANGE,\n} as const\nexport const TEXT_ORD = {\n eqlType: 'public.eql_v3_text_ord',\n castAs: 'string',\n capabilities: ORDER_AND_RANGE,\n} as const\n\nexport const BOOLEAN = {\n eqlType: 'public.eql_v3_boolean',\n castAs: 'boolean',\n capabilities: STORAGE_ONLY,\n} as const\n\nexport const REAL = {\n eqlType: 'public.eql_v3_real',\n castAs: 'number',\n capabilities: STORAGE_ONLY,\n} as const\nexport const REAL_EQ = {\n eqlType: 'public.eql_v3_real_eq',\n castAs: 'number',\n capabilities: EQUALITY_ONLY,\n} as const\nexport const REAL_ORD_ORE = {\n eqlType: 'public.eql_v3_real_ord_ore',\n castAs: 'number',\n capabilities: ORDER_AND_RANGE,\n} as const\nexport const REAL_ORD = {\n eqlType: 'public.eql_v3_real_ord',\n castAs: 'number',\n capabilities: ORDER_AND_RANGE,\n} as const\n\nexport const DOUBLE = {\n eqlType: 'public.eql_v3_double',\n castAs: 'number',\n capabilities: STORAGE_ONLY,\n} as const\nexport const DOUBLE_EQ = {\n eqlType: 'public.eql_v3_double_eq',\n castAs: 'number',\n capabilities: EQUALITY_ONLY,\n} as const\nexport const DOUBLE_ORD_ORE = {\n eqlType: 'public.eql_v3_double_ord_ore',\n castAs: 'number',\n capabilities: ORDER_AND_RANGE,\n} as const\nexport const DOUBLE_ORD = {\n eqlType: 'public.eql_v3_double_ord',\n castAs: 'number',\n capabilities: ORDER_AND_RANGE,\n} as const\n\n/**\n * Translate a domain's semantic {@link QueryCapabilities} (plus its plaintext\n * `castAs`, which decides how equality is answered, and its ordering flavour)\n * into the concrete EQL index block emitted by `build()`.\n *\n * - `unique` (the `hm` HMAC index) whenever equality is answered via HMAC:\n * equality-only domains of ANY type, AND text order domains. Text equality is\n * HMAC-based — the `public.eql_v3_text_ord` / `public.eql_v3_text_ord_ore` SQL domains\n * REQUIRE `hm` in the stored ciphertext (their `eql_v3.eq_term` extracts it).\n * - ONE ordering index for any order/range domain, selected by the domain's\n * name (eql-3.0.0): plain `_ord` domains (and `text_search`) are OPE-backed —\n * `ope`, the `op` CLLW-OPE term — while `_ord_ore` domains keep block-ORE —\n * `ore`, the `ob` term. The two terms are not cross-comparable, and each\n * domain's SQL CHECK requires exactly its own. For numeric/date order\n * domains the ordering term also answers equality (via the SQL `=`\n * operator), so those emit no `hm`. Text order domains emit BOTH `unique`\n * and the ordering index.\n * - `match` (the `bf` bloom-filter index) for free-text search, deep-cloned from\n * the per-call defaults so no nested object is ever shared across columns, and\n * emitted with `include_original: false` (see below).\n */\nfunction indexesForCapabilities(\n capabilities: QueryCapabilities,\n castAs: PlaintextKind,\n ordering: 'ope' | 'ore',\n): ColumnSchema['indexes'] {\n const indexes: ColumnSchema['indexes'] = {}\n\n // Text equality is always HMAC-based, so a text order domain (`string` +\n // order/range) still needs `unique`; numeric/date order domains answer\n // equality via their ordering term and must NOT emit `unique`.\n if (\n capabilities.equality &&\n (!capabilities.orderAndRange || castAs === 'string')\n ) {\n indexes.unique = { token_filters: [] }\n }\n\n if (capabilities.orderAndRange) {\n indexes[ordering] = {}\n }\n\n if (capabilities.freeTextSearch) {\n // The factory returns fresh, unaliased nested objects per call, so no\n // column's emitted match block ever shares state with another's.\n //\n // `include_original` is overridden off the v2 builder's `true`. protect-ffi\n // ignores the flag entirely (measured across 0.24 and 0.29, EQL v2 and v3:\n // the emitted bloom is trigram-only either way, and a value shorter than\n // `token_length` blooms to nothing regardless), so this is inert today. It\n // is set to the value a substring-search domain actually wants — matching\n // the zod schema's own default — so that a protect-ffi release which starts\n // honouring the flag cannot silently break `contains`.\n indexes.match = { ...defaultMatchOpts(), include_original: false }\n }\n\n if (capabilities.searchableJson) {\n // Encrypted-JSONB index (the protect-ffi config kind is named `ste_vec`).\n // `prefix: 'enabled'` is a sentinel the table's `build()` rewrites to\n // `${tableName}/${columnName}` — the same scheme v2 `searchableJson` uses —\n // so each document's selectors are scoped to their column.\n //\n // `array_index_mode` indexes array elements by identity (`item`) and enables\n // `$[*]` wildcard selectors, but deliberately drops `position`: `@>`\n // containment must be a subset test (jsonb `@>` semantics), so `{roles:['x']}`\n // matches any document whose `roles` array contains `x` REGARDLESS of index.\n // With positional terms emitted (v2 `searchableJson`'s `'all'`), the needle\n // for `x`-at-index-0 would miss a document holding `x` at index 1.\n //\n // `mode: 'compat'` is REQUIRED for eql-3.0.0: `public.eql_v3_json` orders the\n // document's entries by the CLLW-OPE `op` term, so the index must emit `op`\n // (compat) terms. `'standard'` emits v2's CLLW-ORE `oc` terms, which the v3\n // domain rejects at encrypt time.\n indexes.ste_vec = {\n prefix: 'enabled',\n array_index_mode: { item: true, wildcard: true, position: false },\n mode: 'compat',\n }\n }\n\n return indexes\n}\n\n/**\n * The ordering flavour a domain's name pins (eql-3.0.0): `_ord_ore` domains\n * are block-ORE (`ore` index, `ob` term); every other ordering domain —\n * `_ord` and `text_search` — is CLLW-OPE (`ope` index, `op` term).\n */\nfunction orderingForEqlType(eqlType: string): 'ope' | 'ore' {\n return eqlType.endsWith('_ord_ore') ? 'ore' : 'ope'\n}\n\n/** Whether a domain's capabilities make it queryable at all (any flag set). */\nfunction isQueryableCapabilities(capabilities: QueryCapabilities): boolean {\n return (\n capabilities.equality ||\n capabilities.orderAndRange ||\n capabilities.freeTextSearch ||\n (capabilities.searchableJson ?? false)\n )\n}\n\n/**\n * Shared base for every v3 concrete domain column. Parameterised by the FULL\n * literal {@link V3DomainDefinition} (not by capabilities alone): the private\n * `definition` field carries the literal `eqlType`/`castAs`/`capabilities`, so\n * two otherwise-empty subclasses (e.g. `EncryptedBooleanColumn` and\n * `EncryptedDateColumn`, both storage-only) are NOT mutually assignable. This\n * nominality is what keeps plaintext inference precise.\n */\nexport class EncryptedV3Column<D extends V3DomainDefinition> {\n constructor(\n private readonly columnName: string,\n private readonly definition: D,\n ) {}\n\n getName(): string {\n return this.columnName\n }\n\n /** The concrete EQL v3 domain name. Metadata only; not emitted by `build()`. */\n getEqlType(): D['eqlType'] {\n return this.definition.eqlType\n }\n\n /** The semantic query capabilities this domain exposes. Metadata only. */\n getQueryCapabilities(): D['capabilities'] {\n return this.definition.capabilities\n }\n\n /** `true` when this domain can produce at least one kind of query term. */\n isQueryable(): QueryableFlag<D> {\n return isQueryableCapabilities(\n this.definition.capabilities,\n ) as QueryableFlag<D>\n }\n\n /** Emit the encrypt-config column: `cast_as` plus capability-derived indexes. */\n build(): ColumnSchema {\n return {\n cast_as: this.definition.castAs,\n indexes: indexesForCapabilities(\n this.definition.capabilities,\n this.definition.castAs,\n orderingForEqlType(this.definition.eqlType),\n ),\n }\n }\n}\n\nconst TEXT_SEARCH_DOMAIN = {\n eqlType: TEXT_SEARCH_EQL_TYPE,\n castAs: 'string',\n capabilities: TEXT_SEARCH,\n} as const\n\n/**\n * Builder for a `public.eql_v3_text_search` column.\n *\n * The concrete type inherently enables equality + order/range + free-text\n * match — there are no capability-enabling or tuning methods. The match index\n * is always emitted with the default configuration.\n *\n * NOTE — querying: a `text_search` column emits all three indexes (`unique`,\n * `ope`, `match`), and the shared index-inference picks them by fixed priority\n * `unique > match > ordering`. So `encryptQuery(value, { column, table })` with NO\n * explicit `queryType` builds an EQUALITY term (via `unique`), NOT a free-text\n * match — a substring like `'joh'` then matches nothing. To run a free-text\n * match query you MUST pass `queryType: 'freeTextSearch'`:\n *\n * ```typescript\n * // equality (default): exact value only\n * client.encryptQuery('john@example.com', { column: users.email, table: users })\n * // free-text match: substring/token search\n * client.encryptQuery('joh', { column: users.email, table: users, queryType: 'freeTextSearch' })\n * ```\n */\nexport class EncryptedTextSearchColumn extends EncryptedV3Column<\n typeof TEXT_SEARCH_DOMAIN\n> {\n constructor(columnName: string) {\n super(columnName, TEXT_SEARCH_DOMAIN)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Concrete domain columns\n//\n// Every concrete class is an empty subclass parameterised by its literal domain\n// definition (see EncryptedV3Column). The `types` namespace (see ./types)\n// constructs these with the SAME literal constant so the instance's private\n// `definition` field carries full literal type data — that is what keeps\n// distinct domains nominally incompatible.\n// ---------------------------------------------------------------------------\n\n// integer\nexport class EncryptedIntegerColumn extends EncryptedV3Column<typeof INTEGER> {}\nexport class EncryptedIntegerEqColumn extends EncryptedV3Column<\n typeof INTEGER_EQ\n> {}\nexport class EncryptedIntegerOrdOreColumn extends EncryptedV3Column<\n typeof INTEGER_ORD_ORE\n> {}\nexport class EncryptedIntegerOrdColumn extends EncryptedV3Column<\n typeof INTEGER_ORD\n> {}\n\n// smallint\nexport class EncryptedSmallintColumn extends EncryptedV3Column<\n typeof SMALLINT\n> {}\nexport class EncryptedSmallintEqColumn extends EncryptedV3Column<\n typeof SMALLINT_EQ\n> {}\nexport class EncryptedSmallintOrdOreColumn extends EncryptedV3Column<\n typeof SMALLINT_ORD_ORE\n> {}\nexport class EncryptedSmallintOrdColumn extends EncryptedV3Column<\n typeof SMALLINT_ORD\n> {}\n\n// bigint (int8) — plaintext is a JS `bigint`, round-tripped losslessly by the\n// native protect-ffi boundary (see the BIGINT domain definitions above).\nexport class EncryptedBigintColumn extends EncryptedV3Column<typeof BIGINT> {}\nexport class EncryptedBigintEqColumn extends EncryptedV3Column<\n typeof BIGINT_EQ\n> {}\nexport class EncryptedBigintOrdOreColumn extends EncryptedV3Column<\n typeof BIGINT_ORD_ORE\n> {}\nexport class EncryptedBigintOrdColumn extends EncryptedV3Column<\n typeof BIGINT_ORD\n> {}\n\n// date\nexport class EncryptedDateColumn extends EncryptedV3Column<typeof DATE> {}\nexport class EncryptedDateEqColumn extends EncryptedV3Column<typeof DATE_EQ> {}\nexport class EncryptedDateOrdOreColumn extends EncryptedV3Column<\n typeof DATE_ORD_ORE\n> {}\nexport class EncryptedDateOrdColumn extends EncryptedV3Column<\n typeof DATE_ORD\n> {}\n\n// timestamp\nexport class EncryptedTimestampColumn extends EncryptedV3Column<\n typeof TIMESTAMP\n> {}\nexport class EncryptedTimestampEqColumn extends EncryptedV3Column<\n typeof TIMESTAMP_EQ\n> {}\nexport class EncryptedTimestampOrdOreColumn extends EncryptedV3Column<\n typeof TIMESTAMP_ORD_ORE\n> {}\nexport class EncryptedTimestampOrdColumn extends EncryptedV3Column<\n typeof TIMESTAMP_ORD\n> {}\n\n// numeric\nexport class EncryptedNumericColumn extends EncryptedV3Column<typeof NUMERIC> {}\nexport class EncryptedNumericEqColumn extends EncryptedV3Column<\n typeof NUMERIC_EQ\n> {}\nexport class EncryptedNumericOrdOreColumn extends EncryptedV3Column<\n typeof NUMERIC_ORD_ORE\n> {}\nexport class EncryptedNumericOrdColumn extends EncryptedV3Column<\n typeof NUMERIC_ORD\n> {}\n\n// text (text_search is defined above with its match-tuning override)\nexport class EncryptedTextColumn extends EncryptedV3Column<typeof TEXT> {}\nexport class EncryptedTextEqColumn extends EncryptedV3Column<typeof TEXT_EQ> {}\nexport class EncryptedTextMatchColumn extends EncryptedV3Column<\n typeof TEXT_MATCH\n> {}\nexport class EncryptedTextOrdOreColumn extends EncryptedV3Column<\n typeof TEXT_ORD_ORE\n> {}\nexport class EncryptedTextOrdColumn extends EncryptedV3Column<\n typeof TEXT_ORD\n> {}\n\n// boolean\nexport class EncryptedBooleanColumn extends EncryptedV3Column<typeof BOOLEAN> {}\n\n// real\nexport class EncryptedRealColumn extends EncryptedV3Column<typeof REAL> {}\nexport class EncryptedRealEqColumn extends EncryptedV3Column<typeof REAL_EQ> {}\nexport class EncryptedRealOrdOreColumn extends EncryptedV3Column<\n typeof REAL_ORD_ORE\n> {}\nexport class EncryptedRealOrdColumn extends EncryptedV3Column<\n typeof REAL_ORD\n> {}\n\n// double\nexport class EncryptedDoubleColumn extends EncryptedV3Column<typeof DOUBLE> {}\nexport class EncryptedDoubleEqColumn extends EncryptedV3Column<\n typeof DOUBLE_EQ\n> {}\nexport class EncryptedDoubleOrdOreColumn extends EncryptedV3Column<\n typeof DOUBLE_ORD_ORE\n> {}\nexport class EncryptedDoubleOrdColumn extends EncryptedV3Column<\n typeof DOUBLE_ORD\n> {}\n\n// json\nconst JSON_DOMAIN = {\n eqlType: 'public.eql_v3_json',\n castAs: 'json',\n capabilities: {\n equality: false,\n orderAndRange: false,\n freeTextSearch: false,\n searchableJson: true,\n },\n} as const\n\n/**\n * Builder for a `public.eql_v3_json` column — an encrypted JSONB document\n * (a {@link JsonDocument}: object, array, or null). It round-trips through\n * `encrypt`/`decrypt`, and containment queries use the encrypted-JSONB index\n * emitted by `build()`. (The stored payload is protect-ffi's `SteVecDocument`,\n * and the config index kind is `ste_vec` — both protect-ffi names.)\n */\nexport class EncryptedJsonColumn extends EncryptedV3Column<typeof JSON_DOMAIN> {\n constructor(columnName: string) {\n super(columnName, JSON_DOMAIN)\n }\n}\n\n/**\n * Union of every v3 concrete column type. Used as the value type for v3 table\n * columns so a table may mix any generated domains.\n */\nexport type AnyEncryptedV3Column =\n | EncryptedIntegerColumn\n | EncryptedIntegerEqColumn\n | EncryptedIntegerOrdOreColumn\n | EncryptedIntegerOrdColumn\n | EncryptedSmallintColumn\n | EncryptedSmallintEqColumn\n | EncryptedSmallintOrdOreColumn\n | EncryptedSmallintOrdColumn\n | EncryptedBigintColumn\n | EncryptedBigintEqColumn\n | EncryptedBigintOrdOreColumn\n | EncryptedBigintOrdColumn\n | EncryptedDateColumn\n | EncryptedDateEqColumn\n | EncryptedDateOrdOreColumn\n | EncryptedDateOrdColumn\n | EncryptedTimestampColumn\n | EncryptedTimestampEqColumn\n | EncryptedTimestampOrdOreColumn\n | EncryptedTimestampOrdColumn\n | EncryptedNumericColumn\n | EncryptedNumericEqColumn\n | EncryptedNumericOrdOreColumn\n | EncryptedNumericOrdColumn\n | EncryptedTextColumn\n | EncryptedTextEqColumn\n | EncryptedTextMatchColumn\n | EncryptedTextOrdOreColumn\n | EncryptedTextOrdColumn\n | EncryptedTextSearchColumn\n | EncryptedBooleanColumn\n | EncryptedRealColumn\n | EncryptedRealEqColumn\n | EncryptedRealOrdOreColumn\n | EncryptedRealOrdColumn\n | EncryptedDoubleColumn\n | EncryptedDoubleEqColumn\n | EncryptedDoubleOrdOreColumn\n | EncryptedDoubleOrdColumn\n | EncryptedJsonColumn\n\n/**\n * A factory that builds a concrete v3 column for a given DB column name.\n *\n * Declared here rather than beside `DOMAIN_REGISTRY` so that `./types` can\n * constrain its own export against it without importing from `./domain-registry`,\n * which imports `./types` back as a value.\n */\nexport type V3ColumnFactory = (name: string) => AnyEncryptedV3Column\n\n/**\n * Shape of v3 table columns: every value is a v3 concrete column builder.\n * (Nested fields are deferred to later increments.)\n */\nexport type EncryptedV3TableColumn = {\n [key: string]: AnyEncryptedV3Column\n}\n\n/** A JSON value at any depth. Used for the values NESTED inside a document. */\nexport type JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue }\n\n/**\n * The plaintext a `public.eql_v3_json` column encrypts and reconstructs: a JSON\n * DOCUMENT — an object, an array, or `null`. NOT a bare top-level scalar:\n * protect-ffi's `json` cast rejects a top-level string/number/boolean\n * (\"Cannot convert … to Json\"), because a scalar belongs in a scalar domain\n * (`types.TextEq`, `types.IntegerEq`, …). Nested scalars are ordinary\n * {@link JsonValue}s and are fully supported.\n */\nexport type JsonDocument = null | JsonValue[] | { [key: string]: JsonValue }\n\n/** Map a domain's {@link PlaintextKind} to its TypeScript plaintext type. */\ntype PlaintextFromKind<K extends PlaintextKind> = K extends 'string'\n ? string\n : K extends 'number'\n ? number\n : K extends 'bigint'\n ? bigint\n : K extends 'boolean'\n ? boolean\n : K extends DateLikeCast\n ? Date\n : K extends 'json'\n ? JsonDocument\n : never\n\n/**\n * The plaintext type for a single v3 column, read from the literal domain\n * definition carried on the base class's private field. This is stable across\n * empty subclasses that share the same base generic — a subclass-name\n * conditional would collapse because those subclasses are structurally\n * assignable to one another.\n */\nexport type PlaintextForColumn<C> =\n C extends EncryptedV3Column<infer D> ? PlaintextFromKind<D['castAs']> : never\n\n/**\n * The user-facing `queryType` names a v3 column supports, derived 1:1 from its\n * capability flags. Resolves to `never` for a storage-only column (all flags\n * `false`) and for any non-v3 value. The names mirror the {@link QueryCapabilities}\n * keys and the first three {@link import('@/types').QueryTypeName} members.\n */\nexport type QueryTypesForColumn<C> =\n C extends EncryptedV3Column<infer D>\n ?\n | (D['capabilities']['equality'] extends true ? 'equality' : never)\n | (D['capabilities']['orderAndRange'] extends true\n ? 'orderAndRange'\n : never)\n | (D['capabilities']['freeTextSearch'] extends true\n ? 'freeTextSearch'\n : never)\n : never\n\n/**\n * The concrete EQL v3 type string for a single column, read from the literal\n * domain definition carried on the base class's private field (mirrors\n * {@link PlaintextForColumn}). Distributes over a union of columns, so\n * `EqlTypeForColumn<AnyEncryptedV3Column>` yields the union of every domain's\n * `eqlType` — the canonical, source-of-truth key set for a type-driven test\n * matrix keyed by domain.\n */\nexport type EqlTypeForColumn<C> =\n C extends EncryptedV3Column<infer D> ? D['eqlType'] : never\n","import {\n BIGINT,\n BIGINT_EQ,\n BIGINT_ORD,\n BIGINT_ORD_ORE,\n BOOLEAN,\n DATE,\n DATE_EQ,\n DATE_ORD,\n DATE_ORD_ORE,\n DOUBLE,\n DOUBLE_EQ,\n DOUBLE_ORD,\n DOUBLE_ORD_ORE,\n EncryptedBigintColumn,\n EncryptedBigintEqColumn,\n EncryptedBigintOrdColumn,\n EncryptedBigintOrdOreColumn,\n EncryptedBooleanColumn,\n EncryptedDateColumn,\n EncryptedDateEqColumn,\n EncryptedDateOrdColumn,\n EncryptedDateOrdOreColumn,\n EncryptedDoubleColumn,\n EncryptedDoubleEqColumn,\n EncryptedDoubleOrdColumn,\n EncryptedDoubleOrdOreColumn,\n EncryptedIntegerColumn,\n EncryptedIntegerEqColumn,\n EncryptedIntegerOrdColumn,\n EncryptedIntegerOrdOreColumn,\n EncryptedJsonColumn,\n EncryptedNumericColumn,\n EncryptedNumericEqColumn,\n EncryptedNumericOrdColumn,\n EncryptedNumericOrdOreColumn,\n EncryptedRealColumn,\n EncryptedRealEqColumn,\n EncryptedRealOrdColumn,\n EncryptedRealOrdOreColumn,\n EncryptedSmallintColumn,\n EncryptedSmallintEqColumn,\n EncryptedSmallintOrdColumn,\n EncryptedSmallintOrdOreColumn,\n EncryptedTextColumn,\n EncryptedTextEqColumn,\n EncryptedTextMatchColumn,\n EncryptedTextOrdColumn,\n EncryptedTextOrdOreColumn,\n EncryptedTextSearchColumn,\n EncryptedTimestampColumn,\n EncryptedTimestampEqColumn,\n EncryptedTimestampOrdColumn,\n EncryptedTimestampOrdOreColumn,\n INTEGER,\n INTEGER_EQ,\n INTEGER_ORD,\n INTEGER_ORD_ORE,\n NUMERIC,\n NUMERIC_EQ,\n NUMERIC_ORD,\n NUMERIC_ORD_ORE,\n REAL,\n REAL_EQ,\n REAL_ORD,\n REAL_ORD_ORE,\n SMALLINT,\n SMALLINT_EQ,\n SMALLINT_ORD,\n SMALLINT_ORD_ORE,\n TEXT,\n TEXT_EQ,\n TEXT_MATCH,\n TEXT_ORD,\n TEXT_ORD_ORE,\n TIMESTAMP,\n TIMESTAMP_EQ,\n TIMESTAMP_ORD,\n TIMESTAMP_ORD_ORE,\n type V3ColumnFactory,\n} from './columns'\n\n/**\n * The v3 column-type namespace. Each member is a public, semantic factory name\n * for a concrete EQL v3 column. Most members mirror the underlying domain name;\n * JS-friendly scalar names map to Postgres concrete domains, e.g.\n * `types.IntegerOrd` builds a `public.eql_v3_integer_ord` column and `types.Boolean`\n * builds a `public.eql_v3_boolean` column.\n *\n * If these factory names stop matching the emitted capability/domain surface,\n * the stack package is out of sync with the underlying EQL build. That is a\n * version mismatch, not a harmless naming drift.\n *\n * Each factory returns the CONCRETE column class instance (never the widened\n * `AnyEncryptedV3Column`) so per-column plaintext / query-capability inference\n * stays precise.\n *\n * ```ts\n * import { encryptedTable, types } from '@cipherstash/stack/eql/v3'\n *\n * const events = encryptedTable('events', {\n * actor: types.TextEq('actor'), // equality\n * weight: types.IntegerOrd('weight'), // order + range\n * createdAt: types.Timestamp('created_at'), // storage only\n * })\n * ```\n *\n * `types.TextSearch` is fully described by its type — like every other domain,\n * it has no capability-bearing or tuning chain. Its match index is always\n * emitted with the default configuration; run a free-text query by passing\n * `queryType: 'freeTextSearch'` to `encryptQuery`.\n */\nexport const types = {\n // integer\n Integer: (name: string) => new EncryptedIntegerColumn(name, INTEGER),\n IntegerEq: (name: string) => new EncryptedIntegerEqColumn(name, INTEGER_EQ),\n IntegerOrdOre: (name: string) =>\n new EncryptedIntegerOrdOreColumn(name, INTEGER_ORD_ORE),\n IntegerOrd: (name: string) =>\n new EncryptedIntegerOrdColumn(name, INTEGER_ORD),\n\n // smallint\n Smallint: (name: string) => new EncryptedSmallintColumn(name, SMALLINT),\n SmallintEq: (name: string) =>\n new EncryptedSmallintEqColumn(name, SMALLINT_EQ),\n SmallintOrdOre: (name: string) =>\n new EncryptedSmallintOrdOreColumn(name, SMALLINT_ORD_ORE),\n SmallintOrd: (name: string) =>\n new EncryptedSmallintOrdColumn(name, SMALLINT_ORD),\n\n // bigint (int8) — plaintext is a JS `bigint`, round-tripped losslessly by\n // the native protect-ffi boundary (see ./columns)\n Bigint: (name: string) => new EncryptedBigintColumn(name, BIGINT),\n BigintEq: (name: string) => new EncryptedBigintEqColumn(name, BIGINT_EQ),\n BigintOrdOre: (name: string) =>\n new EncryptedBigintOrdOreColumn(name, BIGINT_ORD_ORE),\n BigintOrd: (name: string) => new EncryptedBigintOrdColumn(name, BIGINT_ORD),\n\n // date\n Date: (name: string) => new EncryptedDateColumn(name, DATE),\n DateEq: (name: string) => new EncryptedDateEqColumn(name, DATE_EQ),\n DateOrdOre: (name: string) =>\n new EncryptedDateOrdOreColumn(name, DATE_ORD_ORE),\n DateOrd: (name: string) => new EncryptedDateOrdColumn(name, DATE_ORD),\n\n // timestamp\n Timestamp: (name: string) => new EncryptedTimestampColumn(name, TIMESTAMP),\n TimestampEq: (name: string) =>\n new EncryptedTimestampEqColumn(name, TIMESTAMP_EQ),\n TimestampOrdOre: (name: string) =>\n new EncryptedTimestampOrdOreColumn(name, TIMESTAMP_ORD_ORE),\n TimestampOrd: (name: string) =>\n new EncryptedTimestampOrdColumn(name, TIMESTAMP_ORD),\n\n // numeric\n Numeric: (name: string) => new EncryptedNumericColumn(name, NUMERIC),\n NumericEq: (name: string) => new EncryptedNumericEqColumn(name, NUMERIC_EQ),\n NumericOrdOre: (name: string) =>\n new EncryptedNumericOrdOreColumn(name, NUMERIC_ORD_ORE),\n NumericOrd: (name: string) =>\n new EncryptedNumericOrdColumn(name, NUMERIC_ORD),\n\n // text\n Text: (name: string) => new EncryptedTextColumn(name, TEXT),\n TextEq: (name: string) => new EncryptedTextEqColumn(name, TEXT_EQ),\n TextMatch: (name: string) => new EncryptedTextMatchColumn(name, TEXT_MATCH),\n TextOrdOre: (name: string) =>\n new EncryptedTextOrdOreColumn(name, TEXT_ORD_ORE),\n TextOrd: (name: string) => new EncryptedTextOrdColumn(name, TEXT_ORD),\n TextSearch: (name: string) => new EncryptedTextSearchColumn(name),\n\n // boolean\n Boolean: (name: string) => new EncryptedBooleanColumn(name, BOOLEAN),\n\n // real\n Real: (name: string) => new EncryptedRealColumn(name, REAL),\n RealEq: (name: string) => new EncryptedRealEqColumn(name, REAL_EQ),\n RealOrdOre: (name: string) =>\n new EncryptedRealOrdOreColumn(name, REAL_ORD_ORE),\n RealOrd: (name: string) => new EncryptedRealOrdColumn(name, REAL_ORD),\n\n // double\n Double: (name: string) => new EncryptedDoubleColumn(name, DOUBLE),\n DoubleEq: (name: string) => new EncryptedDoubleEqColumn(name, DOUBLE_EQ),\n DoubleOrdOre: (name: string) =>\n new EncryptedDoubleOrdOreColumn(name, DOUBLE_ORD_ORE),\n DoubleOrd: (name: string) => new EncryptedDoubleOrdColumn(name, DOUBLE_ORD),\n\n // json (encrypted JSONB document, ste_vec containment)\n Json: (name: string) => new EncryptedJsonColumn(name),\n // `satisfies` is load-bearing, not decoration: `DOMAIN_REGISTRY` derives itself\n // by calling every value here at module load. A non-factory export would throw\n // during module evaluation and take the supabase introspect/schema-build/verify\n // path down with it. This turns that into a compile error at the offending line.\n // `as const` applies first, so literal key inference is preserved.\n} as const satisfies Record<string, V3ColumnFactory>\n","import type { V3ColumnFactory } from './columns'\nimport { types } from './types'\n\nexport type { V3ColumnFactory } from './columns'\n\n/**\n * Unqualified Postgres `domain_name` → the `eql/v3/types.ts` factory that\n * builds that domain's column.\n *\n * DERIVED, not hand-listed. Every factory already carries its domain in the\n * constant it passes to the column constructor, so the key is recoverable from\n * the factory itself — and `types` and this registry cannot drift. Adding a\n * domain to `types` is enough; forgetting an entry here is no longer possible.\n *\n * The keys are an external contract (they are the `information_schema` query\n * parameter in `../../supabase/introspect.ts`). Because they are derived from\n * `getEqlType()`, no test that also derives them can detect a corrupted domain\n * constant — `eql-v3-domain-registry.test.ts` pins them against a hand-written\n * literal list for exactly that reason.\n *\n * `factory('_probe')` runs at module load, so a non-factory value in `types`\n * would throw here and take the importing modules (`../../supabase/introspect.ts`,\n * `schema-builder.ts`, `verify.ts`) down with it. `types` is declared\n * `satisfies Record<string, V3ColumnFactory>` so that mistake is a compile error\n * at its source instead. Do not reintroduce a cast on `Object.values` below: it\n * would silence exactly the check that keeps this loop total.\n */\n// NULL PROTOTYPE — load-bearing. A plain object literal inherits from\n// Object.prototype, so `DOMAIN_REGISTRY['constructor']` returns a *function*\n// and passes a truthiness check. A column whose domain is named `constructor`,\n// `toString`, `valueOf` or `__proto__` would then be treated as a modelled EQL\n// domain and \"synthesized\" from Object.prototype.constructor. `factoryForDomain`\n// additionally guards with `Object.hasOwn`; both are kept — belt and braces,\n// because a future refactor that drops the null prototype must not silently\n// reopen the hole. It also makes the `Object.hasOwn` collision check below\n// exact: on a plain object `'constructor' in registry` is spuriously true.\nexport const DOMAIN_REGISTRY: Record<string, V3ColumnFactory> = (() => {\n const registry = Object.create(null) as Record<string, V3ColumnFactory>\n for (const factory of Object.values(types)) {\n // Probe name only: the constructors store their arguments and nothing else,\n // so building one column per domain at module load is free of side effects.\n const key = stripDomainSchema(factory('_probe').getEqlType())\n if (Object.hasOwn(registry, key)) {\n throw new Error(`duplicate EQL v3 domain key: ${key}`)\n }\n registry[key] = factory\n }\n return registry\n})()\n\n/** Strip a leading `public.` schema qualifier from a qualified `eqlType`. */\nexport function stripDomainSchema(eqlType: string): string {\n return eqlType.startsWith('public.')\n ? eqlType.slice('public.'.length)\n : eqlType\n}\n\n/**\n * Look up the factory for an unqualified domain name, or `undefined`.\n *\n * `Object.hasOwn` is required, not decorative: without it a domain named\n * `constructor` / `toString` / `valueOf` / `__proto__` resolves to an inherited\n * `Object.prototype` member and violates the \"unknown domain = plaintext\" rule.\n */\nexport function factoryForDomain(\n domainName: string,\n): V3ColumnFactory | undefined {\n return Object.hasOwn(DOMAIN_REGISTRY, domainName)\n ? DOMAIN_REGISTRY[domainName]\n : undefined\n}\n","import type { EnvironmentContext, LogLevel } from './types'\n\nexport function formatDuration(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)}ms`\n }\n return `${(ms / 1000).toFixed(2)}s`\n}\n\nexport function isServer(): boolean {\n return typeof window === 'undefined'\n}\n\nexport function isClient(): boolean {\n return typeof window !== 'undefined'\n}\n\nexport function isDev(): boolean {\n if (typeof process !== 'undefined') {\n return process.env.NODE_ENV !== 'production'\n }\n if (typeof window !== 'undefined') {\n return true\n }\n return false\n}\n\nexport function detectEnvironment(): Partial<EnvironmentContext> {\n const env = typeof process !== 'undefined' ? process.env : {}\n const defaultEnvironment = isDev() ? 'development' : 'production'\n\n return {\n environment: env.NODE_ENV || defaultEnvironment,\n service: env.SERVICE_NAME || 'app',\n version: env.APP_VERSION,\n commitHash: env.COMMIT_SHA\n || env.GITHUB_SHA\n || env.VERCEL_GIT_COMMIT_SHA\n || env.CF_PAGES_COMMIT_SHA,\n region: env.VERCEL_REGION\n || env.AWS_REGION\n || env.FLY_REGION\n || env.CF_REGION,\n }\n}\n\nexport function getConsoleMethod(level: LogLevel): LogLevel {\n return level\n}\n\nexport const colors = {\n reset: '\\x1B[0m',\n bold: '\\x1B[1m',\n dim: '\\x1B[2m',\n red: '\\x1B[31m',\n green: '\\x1B[32m',\n yellow: '\\x1B[33m',\n blue: '\\x1B[34m',\n magenta: '\\x1B[35m',\n cyan: '\\x1B[36m',\n white: '\\x1B[37m',\n gray: '\\x1B[90m',\n} as const\n\nexport function getLevelColor(level: string): string {\n switch (level) {\n case 'error':\n return colors.red\n case 'warn':\n return colors.yellow\n case 'info':\n return colors.cyan\n case 'debug':\n return colors.gray\n default:\n return colors.white\n }\n}\n\n/** Headers that should never be passed to hooks for security */\nexport const SENSITIVE_HEADERS = [\n 'authorization',\n 'cookie',\n 'set-cookie',\n 'x-api-key',\n 'x-auth-token',\n 'proxy-authorization',\n]\n\nexport function filterSafeHeaders(headers: Record<string, string>): Record<string, string> {\n const safeHeaders: Record<string, string> = {}\n\n for (const [key, value] of Object.entries(headers)) {\n if (!SENSITIVE_HEADERS.includes(key.toLowerCase())) {\n safeHeaders[key] = value\n }\n }\n\n return safeHeaders\n}\n\n/**\n * Match a path against a glob pattern.\n * Supports * (any chars except /) and ** (any chars including /).\n */\nexport function matchesPattern(path: string, pattern: string): boolean {\n const regexPattern = pattern\n .replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&') // Escape special regex chars except * and ?\n .replace(/\\*\\*/g, '{{GLOBSTAR}}') // Temp placeholder for **\n .replace(/\\*/g, '[^/]*') // * matches anything except /\n .replace(/{{GLOBSTAR}}/g, '.*') // ** matches anything including /\n .replace(/\\?/g, '[^/]') // ? matches single char except /\n\n const regex = new RegExp(`^${regexPattern}$`)\n return regex.test(path)\n}\n","import type { DrainContext, EnvironmentContext, FieldContext, Log, LogLevel, LoggerConfig, RequestLogger, RequestLoggerOptions, SamplingConfig, TailSamplingContext, WideEvent } from './types'\nimport { colors, detectEnvironment, formatDuration, getConsoleMethod, getLevelColor, isDev, matchesPattern } from './utils'\n\nfunction isPlainObject(val: unknown): val is Record<string, unknown> {\n return val !== null && typeof val === 'object' && !Array.isArray(val)\n}\n\nfunction deepDefaults(base: Record<string, unknown>, defaults: Record<string, unknown>): Record<string, unknown> {\n const result = { ...base }\n for (const key in defaults) {\n const baseVal = result[key]\n const defaultVal = defaults[key]\n if (baseVal === undefined || baseVal === null) {\n result[key] = defaultVal\n } else if (isPlainObject(baseVal) && isPlainObject(defaultVal)) {\n result[key] = deepDefaults(baseVal, defaultVal)\n }\n }\n return result\n}\n\nlet globalEnv: EnvironmentContext = {\n service: 'app',\n environment: 'development',\n}\n\nlet globalPretty = isDev()\nlet globalSampling: SamplingConfig = {}\nlet globalStringify = true\nlet globalDrain: ((ctx: DrainContext) => void | Promise<void>) | undefined\nlet globalEnabled = true\n\n/**\n * Initialize the logger with configuration.\n * Call this once at application startup.\n */\nexport function initLogger(config: LoggerConfig = {}): void {\n globalEnabled = config.enabled ?? true\n const detected = detectEnvironment()\n\n globalEnv = {\n service: config.env?.service ?? detected.service ?? 'app',\n environment: config.env?.environment ?? detected.environment ?? 'development',\n version: config.env?.version ?? detected.version,\n commitHash: config.env?.commitHash ?? detected.commitHash,\n region: config.env?.region ?? detected.region,\n }\n\n globalPretty = config.pretty ?? isDev()\n globalSampling = config.sampling ?? {}\n globalStringify = config.stringify ?? true\n globalDrain = config.drain\n}\n\n/**\n * Check if logging is globally enabled.\n */\nexport function isEnabled(): boolean {\n return globalEnabled\n}\n\n/**\n * Determine if a log at the given level should be emitted based on sampling config.\n * Error level defaults to 100% (always logged) unless explicitly configured otherwise.\n */\nfunction shouldSample(level: LogLevel): boolean {\n const { rates } = globalSampling\n if (!rates) {\n return true // No sampling configured, log everything\n }\n\n // Error defaults to 100% unless explicitly set\n const percentage = level === 'error' && rates.error === undefined\n ? 100\n : rates[level] ?? 100\n\n // 0% = never log, 100% = always log\n if (percentage <= 0) return false\n if (percentage >= 100) return true\n\n return Math.random() * 100 < percentage\n}\n\n/**\n * Evaluate tail sampling conditions to determine if a log should be force-kept.\n * Returns true if ANY condition matches (OR logic).\n */\nexport function shouldKeep(ctx: TailSamplingContext): boolean {\n const { keep } = globalSampling\n if (!keep?.length) return false\n\n return keep.some((condition) => {\n if (condition.status !== undefined && ctx.status !== undefined && ctx.status >= condition.status) {\n return true\n }\n if (condition.duration !== undefined && ctx.duration !== undefined && ctx.duration >= condition.duration) {\n return true\n }\n if (condition.path && ctx.path && matchesPattern(ctx.path, condition.path)) {\n return true\n }\n return false\n })\n}\n\nfunction emitWideEvent(level: LogLevel, event: Record<string, unknown>, skipSamplingCheck = false): WideEvent | null {\n if (!globalEnabled) return null\n\n if (!skipSamplingCheck && !shouldSample(level)) {\n return null\n }\n\n const formatted: WideEvent = {\n timestamp: new Date().toISOString(),\n level,\n ...globalEnv,\n ...event,\n }\n\n if (globalPretty) {\n prettyPrintWideEvent(formatted)\n } else if (globalStringify) {\n console[getConsoleMethod(level)](JSON.stringify(formatted))\n } else {\n console[getConsoleMethod(level)](formatted)\n }\n\n if (globalDrain) {\n Promise.resolve(globalDrain({ event: formatted })).catch((err) => {\n console.error('[evlog] drain failed:', err)\n })\n }\n\n return formatted\n}\n\nfunction emitTaggedLog(level: LogLevel, tag: string, message: string): void {\n if (!globalEnabled) return\n\n if (globalPretty) {\n if (!shouldSample(level)) {\n return\n }\n const color = getLevelColor(level)\n const timestamp = new Date().toISOString().slice(11, 23)\n console.log(`${colors.dim}${timestamp}${colors.reset} ${color}[${tag}]${colors.reset} ${message}`)\n return\n }\n emitWideEvent(level, { tag, message })\n}\n\nfunction formatValue(value: unknown): string {\n if (value === null || value === undefined) {\n return String(value)\n }\n if (typeof value === 'object') {\n // Flatten object to key=value pairs\n const pairs: string[] = []\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n if (v !== undefined && v !== null) {\n if (typeof v === 'object') {\n // For nested objects, show as JSON\n pairs.push(`${k}=${JSON.stringify(v)}`)\n } else {\n pairs.push(`${k}=${v}`)\n }\n }\n }\n return pairs.join(' ')\n }\n return String(value)\n}\n\nfunction prettyPrintWideEvent(event: Record<string, unknown>): void {\n const { timestamp, level, service, environment, version, ...rest } = event\n const levelColor = getLevelColor(level as string)\n const ts = (timestamp as string).slice(11, 23)\n\n let header = `${colors.dim}${ts}${colors.reset} ${levelColor}${(level as string).toUpperCase()}${colors.reset}`\n header += ` ${colors.cyan}[${service}]${colors.reset}`\n\n if (rest.method && rest.path) {\n header += ` ${rest.method} ${rest.path}`\n delete rest.method\n delete rest.path\n }\n\n if (rest.status) {\n const statusColor = (rest.status as number) >= 400 ? colors.red : colors.green\n header += ` ${statusColor}${rest.status}${colors.reset}`\n delete rest.status\n }\n\n if (rest.duration) {\n header += ` ${colors.dim}in ${rest.duration}${colors.reset}`\n delete rest.duration\n }\n\n console.log(header)\n\n const entries = Object.entries(rest).filter(([_, v]) => v !== undefined)\n const lastIndex = entries.length - 1\n\n entries.forEach(([key, value], index) => {\n const isLast = index === lastIndex\n const prefix = isLast ? '└─' : '├─'\n const formatted = formatValue(value)\n console.log(` ${colors.dim}${prefix}${colors.reset} ${colors.cyan}${key}:${colors.reset} ${formatted}`)\n })\n}\n\nfunction createLogMethod(level: LogLevel) {\n return function logMethod(tagOrEvent: string | Record<string, unknown>, message?: string): void {\n if (typeof tagOrEvent === 'string' && message !== undefined) {\n emitTaggedLog(level, tagOrEvent, message)\n } else if (typeof tagOrEvent === 'object') {\n emitWideEvent(level, tagOrEvent)\n } else {\n emitTaggedLog(level, 'log', String(tagOrEvent))\n }\n }\n}\n\n/**\n * Simple logging API - as easy as console.log\n *\n * @example\n * ```ts\n * log.info('auth', 'User logged in')\n * log.error({ action: 'payment', error: 'failed' })\n * ```\n */\nconst _log: Log = {\n info: createLogMethod('info'),\n error: createLogMethod('error'),\n warn: createLogMethod('warn'),\n debug: createLogMethod('debug'),\n}\n\nexport { _log as log }\n\nconst noopLogger: RequestLogger = {\n set() {},\n error() {},\n info() {},\n warn() {},\n emit() {\n return null\n },\n getContext() {\n return {}\n },\n}\n\n/**\n * Create a request-scoped logger for building wide events.\n *\n * @example\n * ```ts\n * const log = createRequestLogger({ method: 'POST', path: '/checkout' })\n * log.set({ user: { id: '123' } })\n * log.set({ cart: { items: 3 } })\n * log.emit()\n * ```\n */\nexport function createRequestLogger<T extends object = Record<string, unknown>>(options: RequestLoggerOptions = {}): RequestLogger<T> {\n if (!globalEnabled) return noopLogger as RequestLogger<T>\n\n const startTime = Date.now()\n let context: Record<string, unknown> = {\n method: options.method,\n path: options.path,\n requestId: options.requestId,\n }\n let hasError = false\n let hasWarn = false\n\n function addRequestLog(level: 'info' | 'warn', message: string): void {\n const entry = {\n level,\n message,\n timestamp: new Date().toISOString(),\n }\n\n const requestLogs = Array.isArray(context.requestLogs)\n ? [...context.requestLogs, entry]\n : [entry]\n\n context = {\n ...context,\n requestLogs,\n }\n }\n\n return {\n set(data: FieldContext<T>): void {\n context = deepDefaults(data as Record<string, unknown>, context) as Record<string, unknown>\n },\n\n error(error: Error | string, errorContext?: FieldContext<T>): void {\n hasError = true\n const err = typeof error === 'string' ? new Error(error) : error\n\n const errorData = {\n ...(errorContext as Record<string, unknown>),\n error: {\n name: err.name,\n message: err.message,\n stack: err.stack,\n ...('status' in err && { status: (err as Record<string, unknown>).status }),\n ...('statusText' in err && { statusText: (err as Record<string, unknown>).statusText }),\n ...('statusCode' in err && { statusCode: (err as Record<string, unknown>).statusCode }),\n ...('statusMessage' in err && { statusMessage: (err as Record<string, unknown>).statusMessage }),\n ...('data' in err && { data: (err as Record<string, unknown>).data }),\n ...('cause' in err && { cause: (err as unknown as Record<string, unknown>).cause }),\n },\n }\n context = deepDefaults(errorData, context) as Record<string, unknown>\n },\n\n info(message: string, infoContext?: FieldContext<T>): void {\n addRequestLog('info', message)\n if (infoContext) {\n const { requestLogs: _, ...rest } = infoContext as Record<string, unknown>\n context = deepDefaults(rest, context) as Record<string, unknown>\n }\n },\n\n warn(message: string, warnContext?: FieldContext<T>): void {\n hasWarn = true\n addRequestLog('warn', message)\n if (warnContext) {\n const { requestLogs: _, ...rest } = warnContext as Record<string, unknown>\n context = deepDefaults(rest, context) as Record<string, unknown>\n }\n },\n\n emit(overrides?: FieldContext<T> & { _forceKeep?: boolean }): WideEvent | null {\n const durationMs = Date.now() - startTime\n const duration = formatDuration(durationMs)\n const level: LogLevel = hasError ? 'error' : hasWarn ? 'warn' : 'info'\n\n // Extract _forceKeep from overrides (set by evlog:emit:keep hook)\n const { _forceKeep, ...restOverrides } = (overrides ?? {}) as Record<string, unknown> & { _forceKeep?: boolean }\n\n // Build tail sampling context\n const tailCtx: TailSamplingContext = {\n status: (context.status ?? restOverrides.status) as number | undefined,\n duration: durationMs,\n path: context.path as string | undefined,\n method: context.method as string | undefined,\n context: { ...context, ...restOverrides },\n }\n\n // Tail sampling: force keep if hook or built-in conditions match\n const forceKeep = _forceKeep || shouldKeep(tailCtx)\n\n // Apply head sampling only if not force-kept\n if (!forceKeep && !shouldSample(level)) {\n return null\n }\n\n return emitWideEvent(level, {\n ...context,\n ...restOverrides,\n duration,\n }, true)\n },\n\n getContext(): FieldContext<T> & Record<string, unknown> {\n return { ...context }\n },\n }\n}\n\n/**\n * Get the current environment context.\n */\nexport function getEnvironment(): EnvironmentContext {\n return { ...globalEnv }\n}\n","import { createRequestLogger, initLogger } from 'evlog'\n\n/**\n * Log level for the Stack logger.\n *\n * Configured via the `STASH_STACK_LOG` environment variable.\n *\n * - `'error'` — Only errors (default when `STASH_STACK_LOG` is not set).\n * - `'info'` — Info and errors.\n * - `'debug'` — Debug, info, and errors.\n */\nexport type LogLevel = 'debug' | 'info' | 'error'\n\nconst validLevels: readonly LogLevel[] = ['debug', 'info', 'error'] as const\n\nfunction levelFromEnv(): LogLevel {\n const env = process.env.STASH_STACK_LOG\n if (env && validLevels.includes(env as LogLevel)) return env as LogLevel\n return 'error'\n}\n\nfunction samplingRatesForLevel(level: LogLevel): Record<string, number> {\n // evlog uses sampling rates: 100 = always emit, 0 = never emit\n switch (level) {\n case 'debug':\n return { debug: 100, info: 100, warn: 100, error: 100 }\n case 'info':\n return { debug: 0, info: 100, warn: 100, error: 100 }\n case 'error':\n default:\n return { debug: 0, info: 0, warn: 0, error: 100 }\n }\n}\n\nlet initialized = false\n\n/**\n * Initialize the Stack logger.\n *\n * The log level is read from the `STASH_STACK_LOG` environment variable.\n * When the variable is not set, the default is `'error'` (errors only).\n *\n * @internal\n */\nexport function initStackLogger(): void {\n if (initialized) return\n initialized = true\n\n const level = levelFromEnv()\n const rates = samplingRatesForLevel(level)\n\n initLogger({\n env: { service: '@cipherstash/stack' },\n enabled: true,\n sampling: { rates },\n })\n}\n\n// Auto-init with defaults on first import\ninitStackLogger()\n\nexport { createRequestLogger }\n\n// Stringify only the first arg (the message string); drop subsequent args\n// which may contain sensitive objects (e.g. encryptConfig, plaintext).\nfunction safeMessage(args: unknown[]): string {\n return typeof args[0] === 'string' ? args[0] : ''\n}\n\n// Logger for simple one-off logs used across Stack interfaces.\nexport const logger = {\n debug(...args: unknown[]) {\n const log = createRequestLogger()\n log.set({\n level: 'debug',\n source: '@cipherstash/stack',\n message: safeMessage(args),\n })\n log.emit()\n },\n info(...args: unknown[]) {\n const log = createRequestLogger()\n log.set({ source: '@cipherstash/stack' })\n log.info(safeMessage(args))\n log.emit()\n },\n warn(...args: unknown[]) {\n const log = createRequestLogger()\n log.warn(safeMessage(args))\n log.emit()\n },\n error(...args: unknown[]) {\n const log = createRequestLogger()\n log.error(safeMessage(args))\n log.emit()\n },\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export { b as bulkModelsToEncryptedPgComposites, m as modelToEncryptedPgComposites } from './index-fhWTOV0K.cjs';
|
|
2
|
+
export { A as AuditConfig } from './base-operation-AOAIvsSB.cjs';
|
|
3
|
+
import { Z as V3ColumnFactory } from './columns-D2_YzrCX.cjs';
|
|
4
|
+
export { A as AnyEncryptedV3Column, D as DATE_LIKE_CASTS, Q as EncryptedV3Column } from './columns-D2_YzrCX.cjs';
|
|
5
|
+
import { M as MatchIndexOpts } from './types-public-QMjYNfQO.cjs';
|
|
6
|
+
import '@byteslice/result';
|
|
7
|
+
import './errors/index.cjs';
|
|
8
|
+
import '@cipherstash/protect-ffi';
|
|
9
|
+
import 'zod';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Unqualified Postgres `domain_name` → the `eql/v3/types.ts` factory that
|
|
13
|
+
* builds that domain's column.
|
|
14
|
+
*
|
|
15
|
+
* DERIVED, not hand-listed. Every factory already carries its domain in the
|
|
16
|
+
* constant it passes to the column constructor, so the key is recoverable from
|
|
17
|
+
* the factory itself — and `types` and this registry cannot drift. Adding a
|
|
18
|
+
* domain to `types` is enough; forgetting an entry here is no longer possible.
|
|
19
|
+
*
|
|
20
|
+
* The keys are an external contract (they are the `information_schema` query
|
|
21
|
+
* parameter in `../../supabase/introspect.ts`). Because they are derived from
|
|
22
|
+
* `getEqlType()`, no test that also derives them can detect a corrupted domain
|
|
23
|
+
* constant — `eql-v3-domain-registry.test.ts` pins them against a hand-written
|
|
24
|
+
* literal list for exactly that reason.
|
|
25
|
+
*
|
|
26
|
+
* `factory('_probe')` runs at module load, so a non-factory value in `types`
|
|
27
|
+
* would throw here and take the importing modules (`../../supabase/introspect.ts`,
|
|
28
|
+
* `schema-builder.ts`, `verify.ts`) down with it. `types` is declared
|
|
29
|
+
* `satisfies Record<string, V3ColumnFactory>` so that mistake is a compile error
|
|
30
|
+
* at its source instead. Do not reintroduce a cast on `Object.values` below: it
|
|
31
|
+
* would silence exactly the check that keeps this loop total.
|
|
32
|
+
*/
|
|
33
|
+
declare const DOMAIN_REGISTRY: Record<string, V3ColumnFactory>;
|
|
34
|
+
/** Strip a leading `public.` schema qualifier from a qualified `eqlType`. */
|
|
35
|
+
declare function stripDomainSchema(eqlType: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Look up the factory for an unqualified domain name, or `undefined`.
|
|
38
|
+
*
|
|
39
|
+
* `Object.hasOwn` is required, not decorative: without it a domain named
|
|
40
|
+
* `constructor` / `toString` / `valueOf` / `__proto__` resolves to an inherited
|
|
41
|
+
* `Object.prototype` member and violates the "unknown domain = plaintext" rule.
|
|
42
|
+
*/
|
|
43
|
+
declare function factoryForDomain(domainName: string): V3ColumnFactory | undefined;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Why a needle cannot be answered by this match index, or `undefined` when it
|
|
47
|
+
* can. Callers throw their own error type with this as the reason.
|
|
48
|
+
*
|
|
49
|
+
* A needle that tokenizes to NOTHING has an empty bloom filter, and
|
|
50
|
+
* `stored_bf @> '{}'` is true for every row ("contains nothing, contained by
|
|
51
|
+
* everything"). Such a query is unanswerable rather than merely unmatched, so
|
|
52
|
+
* it must fail loudly instead of silently returning the whole table. Two ways
|
|
53
|
+
* to tokenize to nothing:
|
|
54
|
+
*
|
|
55
|
+
* - the empty needle, under ANY tokenizer;
|
|
56
|
+
* - a needle shorter than the ngram tokenizer's `token_length`.
|
|
57
|
+
*
|
|
58
|
+
* The ngram floor counts Unicode CODEPOINTS, because that is what the tokenizer
|
|
59
|
+
* counts. `needle.length` would count UTF-16 code units and wave through an
|
|
60
|
+
* astral-plane needle: `'👍👍'` is 4 code units but only 2 codepoints, yields no
|
|
61
|
+
* trigram, and (measured live) matched every row. Graphemes are the wrong unit
|
|
62
|
+
* in the other direction — NFD `'éé'` is 4 codepoints but 2 grapheme clusters,
|
|
63
|
+
* and does yield trigrams.
|
|
64
|
+
*
|
|
65
|
+
* Currently wired ONLY into the v3 Drizzle adapter (`eql/v3/drizzle/operators`).
|
|
66
|
+
* It lives here, beside the shared match defaults, because v2 builds the same
|
|
67
|
+
* bloom filters and needs the same floor — but v2's `like`/`ilike` path remains
|
|
68
|
+
* unguarded and still fails open. Do not reuse this for v2 without first
|
|
69
|
+
* measuring what its tokenizer actually receives: v2 needles carry SQL wildcards
|
|
70
|
+
* (`'%ada%'`), so the floor may have to apply to the unwrapped term rather than
|
|
71
|
+
* to the string the caller passed.
|
|
72
|
+
*/
|
|
73
|
+
declare function matchNeedleError(needle: unknown, opts: MatchIndexOpts): string | undefined;
|
|
74
|
+
|
|
75
|
+
declare const logger: {
|
|
76
|
+
debug(...args: unknown[]): void;
|
|
77
|
+
info(...args: unknown[]): void;
|
|
78
|
+
warn(...args: unknown[]): void;
|
|
79
|
+
error(...args: unknown[]): void;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export { DOMAIN_REGISTRY, factoryForDomain, logger, matchNeedleError, stripDomainSchema };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export { b as bulkModelsToEncryptedPgComposites, m as modelToEncryptedPgComposites } from './index-BquA71_Y.js';
|
|
2
|
+
export { A as AuditConfig } from './base-operation-FXEzUXIq.js';
|
|
3
|
+
import { Z as V3ColumnFactory } from './columns-rZc7fQHI.js';
|
|
4
|
+
export { A as AnyEncryptedV3Column, D as DATE_LIKE_CASTS, Q as EncryptedV3Column } from './columns-rZc7fQHI.js';
|
|
5
|
+
import { M as MatchIndexOpts } from './types-public-QMjYNfQO.js';
|
|
6
|
+
import '@byteslice/result';
|
|
7
|
+
import './errors/index.js';
|
|
8
|
+
import '@cipherstash/protect-ffi';
|
|
9
|
+
import 'zod';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Unqualified Postgres `domain_name` → the `eql/v3/types.ts` factory that
|
|
13
|
+
* builds that domain's column.
|
|
14
|
+
*
|
|
15
|
+
* DERIVED, not hand-listed. Every factory already carries its domain in the
|
|
16
|
+
* constant it passes to the column constructor, so the key is recoverable from
|
|
17
|
+
* the factory itself — and `types` and this registry cannot drift. Adding a
|
|
18
|
+
* domain to `types` is enough; forgetting an entry here is no longer possible.
|
|
19
|
+
*
|
|
20
|
+
* The keys are an external contract (they are the `information_schema` query
|
|
21
|
+
* parameter in `../../supabase/introspect.ts`). Because they are derived from
|
|
22
|
+
* `getEqlType()`, no test that also derives them can detect a corrupted domain
|
|
23
|
+
* constant — `eql-v3-domain-registry.test.ts` pins them against a hand-written
|
|
24
|
+
* literal list for exactly that reason.
|
|
25
|
+
*
|
|
26
|
+
* `factory('_probe')` runs at module load, so a non-factory value in `types`
|
|
27
|
+
* would throw here and take the importing modules (`../../supabase/introspect.ts`,
|
|
28
|
+
* `schema-builder.ts`, `verify.ts`) down with it. `types` is declared
|
|
29
|
+
* `satisfies Record<string, V3ColumnFactory>` so that mistake is a compile error
|
|
30
|
+
* at its source instead. Do not reintroduce a cast on `Object.values` below: it
|
|
31
|
+
* would silence exactly the check that keeps this loop total.
|
|
32
|
+
*/
|
|
33
|
+
declare const DOMAIN_REGISTRY: Record<string, V3ColumnFactory>;
|
|
34
|
+
/** Strip a leading `public.` schema qualifier from a qualified `eqlType`. */
|
|
35
|
+
declare function stripDomainSchema(eqlType: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Look up the factory for an unqualified domain name, or `undefined`.
|
|
38
|
+
*
|
|
39
|
+
* `Object.hasOwn` is required, not decorative: without it a domain named
|
|
40
|
+
* `constructor` / `toString` / `valueOf` / `__proto__` resolves to an inherited
|
|
41
|
+
* `Object.prototype` member and violates the "unknown domain = plaintext" rule.
|
|
42
|
+
*/
|
|
43
|
+
declare function factoryForDomain(domainName: string): V3ColumnFactory | undefined;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Why a needle cannot be answered by this match index, or `undefined` when it
|
|
47
|
+
* can. Callers throw their own error type with this as the reason.
|
|
48
|
+
*
|
|
49
|
+
* A needle that tokenizes to NOTHING has an empty bloom filter, and
|
|
50
|
+
* `stored_bf @> '{}'` is true for every row ("contains nothing, contained by
|
|
51
|
+
* everything"). Such a query is unanswerable rather than merely unmatched, so
|
|
52
|
+
* it must fail loudly instead of silently returning the whole table. Two ways
|
|
53
|
+
* to tokenize to nothing:
|
|
54
|
+
*
|
|
55
|
+
* - the empty needle, under ANY tokenizer;
|
|
56
|
+
* - a needle shorter than the ngram tokenizer's `token_length`.
|
|
57
|
+
*
|
|
58
|
+
* The ngram floor counts Unicode CODEPOINTS, because that is what the tokenizer
|
|
59
|
+
* counts. `needle.length` would count UTF-16 code units and wave through an
|
|
60
|
+
* astral-plane needle: `'👍👍'` is 4 code units but only 2 codepoints, yields no
|
|
61
|
+
* trigram, and (measured live) matched every row. Graphemes are the wrong unit
|
|
62
|
+
* in the other direction — NFD `'éé'` is 4 codepoints but 2 grapheme clusters,
|
|
63
|
+
* and does yield trigrams.
|
|
64
|
+
*
|
|
65
|
+
* Currently wired ONLY into the v3 Drizzle adapter (`eql/v3/drizzle/operators`).
|
|
66
|
+
* It lives here, beside the shared match defaults, because v2 builds the same
|
|
67
|
+
* bloom filters and needs the same floor — but v2's `like`/`ilike` path remains
|
|
68
|
+
* unguarded and still fails open. Do not reuse this for v2 without first
|
|
69
|
+
* measuring what its tokenizer actually receives: v2 needles carry SQL wildcards
|
|
70
|
+
* (`'%ada%'`), so the floor may have to apply to the unwrapped term rather than
|
|
71
|
+
* to the string the caller passed.
|
|
72
|
+
*/
|
|
73
|
+
declare function matchNeedleError(needle: unknown, opts: MatchIndexOpts): string | undefined;
|
|
74
|
+
|
|
75
|
+
declare const logger: {
|
|
76
|
+
debug(...args: unknown[]): void;
|
|
77
|
+
info(...args: unknown[]): void;
|
|
78
|
+
warn(...args: unknown[]): void;
|
|
79
|
+
error(...args: unknown[]): void;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export { DOMAIN_REGISTRY, factoryForDomain, logger, matchNeedleError, stripDomainSchema };
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DATE_LIKE_CASTS,
|
|
3
|
+
EncryptedV3Column,
|
|
4
|
+
types
|
|
5
|
+
} from "./chunk-V2Q3NYIH.js";
|
|
6
|
+
import {
|
|
7
|
+
bulkModelsToEncryptedPgComposites,
|
|
8
|
+
modelToEncryptedPgComposites
|
|
9
|
+
} from "./chunk-BNO32ZMV.js";
|
|
10
|
+
import {
|
|
11
|
+
matchNeedleError
|
|
12
|
+
} from "./chunk-7333ZC6L.js";
|
|
13
|
+
import {
|
|
14
|
+
logger
|
|
15
|
+
} from "./chunk-HQANMV7R.js";
|
|
16
|
+
import "./chunk-PZ5AY32C.js";
|
|
17
|
+
|
|
18
|
+
// src/eql/v3/domain-registry.ts
|
|
19
|
+
var DOMAIN_REGISTRY = (() => {
|
|
20
|
+
const registry = /* @__PURE__ */ Object.create(null);
|
|
21
|
+
for (const factory of Object.values(types)) {
|
|
22
|
+
const key = stripDomainSchema(factory("_probe").getEqlType());
|
|
23
|
+
if (Object.hasOwn(registry, key)) {
|
|
24
|
+
throw new Error(`duplicate EQL v3 domain key: ${key}`);
|
|
25
|
+
}
|
|
26
|
+
registry[key] = factory;
|
|
27
|
+
}
|
|
28
|
+
return registry;
|
|
29
|
+
})();
|
|
30
|
+
function stripDomainSchema(eqlType) {
|
|
31
|
+
return eqlType.startsWith("public.") ? eqlType.slice("public.".length) : eqlType;
|
|
32
|
+
}
|
|
33
|
+
function factoryForDomain(domainName) {
|
|
34
|
+
return Object.hasOwn(DOMAIN_REGISTRY, domainName) ? DOMAIN_REGISTRY[domainName] : void 0;
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
DATE_LIKE_CASTS,
|
|
38
|
+
DOMAIN_REGISTRY,
|
|
39
|
+
EncryptedV3Column,
|
|
40
|
+
bulkModelsToEncryptedPgComposites,
|
|
41
|
+
factoryForDomain,
|
|
42
|
+
logger,
|
|
43
|
+
matchNeedleError,
|
|
44
|
+
modelToEncryptedPgComposites,
|
|
45
|
+
stripDomainSchema
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=adapter-kit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/eql/v3/domain-registry.ts"],"sourcesContent":["import type { V3ColumnFactory } from './columns'\nimport { types } from './types'\n\nexport type { V3ColumnFactory } from './columns'\n\n/**\n * Unqualified Postgres `domain_name` → the `eql/v3/types.ts` factory that\n * builds that domain's column.\n *\n * DERIVED, not hand-listed. Every factory already carries its domain in the\n * constant it passes to the column constructor, so the key is recoverable from\n * the factory itself — and `types` and this registry cannot drift. Adding a\n * domain to `types` is enough; forgetting an entry here is no longer possible.\n *\n * The keys are an external contract (they are the `information_schema` query\n * parameter in `../../supabase/introspect.ts`). Because they are derived from\n * `getEqlType()`, no test that also derives them can detect a corrupted domain\n * constant — `eql-v3-domain-registry.test.ts` pins them against a hand-written\n * literal list for exactly that reason.\n *\n * `factory('_probe')` runs at module load, so a non-factory value in `types`\n * would throw here and take the importing modules (`../../supabase/introspect.ts`,\n * `schema-builder.ts`, `verify.ts`) down with it. `types` is declared\n * `satisfies Record<string, V3ColumnFactory>` so that mistake is a compile error\n * at its source instead. Do not reintroduce a cast on `Object.values` below: it\n * would silence exactly the check that keeps this loop total.\n */\n// NULL PROTOTYPE — load-bearing. A plain object literal inherits from\n// Object.prototype, so `DOMAIN_REGISTRY['constructor']` returns a *function*\n// and passes a truthiness check. A column whose domain is named `constructor`,\n// `toString`, `valueOf` or `__proto__` would then be treated as a modelled EQL\n// domain and \"synthesized\" from Object.prototype.constructor. `factoryForDomain`\n// additionally guards with `Object.hasOwn`; both are kept — belt and braces,\n// because a future refactor that drops the null prototype must not silently\n// reopen the hole. It also makes the `Object.hasOwn` collision check below\n// exact: on a plain object `'constructor' in registry` is spuriously true.\nexport const DOMAIN_REGISTRY: Record<string, V3ColumnFactory> = (() => {\n const registry = Object.create(null) as Record<string, V3ColumnFactory>\n for (const factory of Object.values(types)) {\n // Probe name only: the constructors store their arguments and nothing else,\n // so building one column per domain at module load is free of side effects.\n const key = stripDomainSchema(factory('_probe').getEqlType())\n if (Object.hasOwn(registry, key)) {\n throw new Error(`duplicate EQL v3 domain key: ${key}`)\n }\n registry[key] = factory\n }\n return registry\n})()\n\n/** Strip a leading `public.` schema qualifier from a qualified `eqlType`. */\nexport function stripDomainSchema(eqlType: string): string {\n return eqlType.startsWith('public.')\n ? eqlType.slice('public.'.length)\n : eqlType\n}\n\n/**\n * Look up the factory for an unqualified domain name, or `undefined`.\n *\n * `Object.hasOwn` is required, not decorative: without it a domain named\n * `constructor` / `toString` / `valueOf` / `__proto__` resolves to an inherited\n * `Object.prototype` member and violates the \"unknown domain = plaintext\" rule.\n */\nexport function factoryForDomain(\n domainName: string,\n): V3ColumnFactory | undefined {\n return Object.hasOwn(DOMAIN_REGISTRY, domainName)\n ? DOMAIN_REGISTRY[domainName]\n : undefined\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAoCO,IAAM,mBAAoD,MAAM;AACrE,QAAM,WAAW,uBAAO,OAAO,IAAI;AACnC,aAAW,WAAW,OAAO,OAAO,KAAK,GAAG;AAG1C,UAAM,MAAM,kBAAkB,QAAQ,QAAQ,EAAE,WAAW,CAAC;AAC5D,QAAI,OAAO,OAAO,UAAU,GAAG,GAAG;AAChC,YAAM,IAAI,MAAM,gCAAgC,GAAG,EAAE;AAAA,IACvD;AACA,aAAS,GAAG,IAAI;AAAA,EAClB;AACA,SAAO;AACT,GAAG;AAGI,SAAS,kBAAkB,SAAyB;AACzD,SAAO,QAAQ,WAAW,SAAS,IAC/B,QAAQ,MAAM,UAAU,MAAM,IAC9B;AACN;AASO,SAAS,iBACd,YAC6B;AAC7B,SAAO,OAAO,OAAO,iBAAiB,UAAU,IAC5C,gBAAgB,UAAU,IAC1B;AACN;","names":[]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Result } from '@byteslice/result';
|
|
2
|
+
import { EncryptionError } from './errors/index.cjs';
|
|
3
|
+
|
|
4
|
+
type AuditConfig = {
|
|
5
|
+
metadata?: Record<string, unknown>;
|
|
6
|
+
};
|
|
7
|
+
type AuditData = {
|
|
8
|
+
metadata?: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
declare abstract class EncryptionOperation<T> {
|
|
11
|
+
protected auditMetadata?: Record<string, unknown>;
|
|
12
|
+
/**
|
|
13
|
+
* Attach audit metadata to this operation. Can be chained.
|
|
14
|
+
* @param config Configuration for ZeroKMS audit logging
|
|
15
|
+
* @param config.metadata Arbitrary JSON object for appending metadata to the audit log
|
|
16
|
+
*/
|
|
17
|
+
audit(config: AuditConfig): this;
|
|
18
|
+
/**
|
|
19
|
+
* Get the audit data for this operation.
|
|
20
|
+
*/
|
|
21
|
+
getAuditData(): AuditData;
|
|
22
|
+
/**
|
|
23
|
+
* Execute the operation and return a Result
|
|
24
|
+
*/
|
|
25
|
+
abstract execute(): Promise<Result<T, EncryptionError>>;
|
|
26
|
+
/**
|
|
27
|
+
* Make the operation thenable
|
|
28
|
+
*/
|
|
29
|
+
then<TResult1 = Result<T, EncryptionError>, TResult2 = never>(onfulfilled?: ((value: Result<T, EncryptionError>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { type AuditConfig as A, EncryptionOperation as E };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Result } from '@byteslice/result';
|
|
2
|
+
import { EncryptionError } from './errors/index.js';
|
|
3
|
+
|
|
4
|
+
type AuditConfig = {
|
|
5
|
+
metadata?: Record<string, unknown>;
|
|
6
|
+
};
|
|
7
|
+
type AuditData = {
|
|
8
|
+
metadata?: Record<string, unknown>;
|
|
9
|
+
};
|
|
10
|
+
declare abstract class EncryptionOperation<T> {
|
|
11
|
+
protected auditMetadata?: Record<string, unknown>;
|
|
12
|
+
/**
|
|
13
|
+
* Attach audit metadata to this operation. Can be chained.
|
|
14
|
+
* @param config Configuration for ZeroKMS audit logging
|
|
15
|
+
* @param config.metadata Arbitrary JSON object for appending metadata to the audit log
|
|
16
|
+
*/
|
|
17
|
+
audit(config: AuditConfig): this;
|
|
18
|
+
/**
|
|
19
|
+
* Get the audit data for this operation.
|
|
20
|
+
*/
|
|
21
|
+
getAuditData(): AuditData;
|
|
22
|
+
/**
|
|
23
|
+
* Execute the operation and return a Result
|
|
24
|
+
*/
|
|
25
|
+
abstract execute(): Promise<Result<T, EncryptionError>>;
|
|
26
|
+
/**
|
|
27
|
+
* Make the operation thenable
|
|
28
|
+
*/
|
|
29
|
+
then<TResult1 = Result<T, EncryptionError>, TResult2 = never>(onfulfilled?: ((value: Result<T, EncryptionError>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { type AuditConfig as A, EncryptionOperation as E };
|
|
@@ -8,6 +8,8 @@ var queryTypes = {
|
|
|
8
8
|
searchableJson: "searchableJson"
|
|
9
9
|
};
|
|
10
10
|
var queryTypeToFfi = {
|
|
11
|
+
// v3 `_ord` domains carry `ope` instead — `resolveIndexType` swaps this
|
|
12
|
+
// static default for the ordering index the column actually configures.
|
|
11
13
|
orderAndRange: "ore",
|
|
12
14
|
freeTextSearch: "match",
|
|
13
15
|
equality: "unique",
|
|
@@ -25,4 +27,4 @@ export {
|
|
|
25
27
|
queryTypeToFfi,
|
|
26
28
|
queryTypeToQueryOp
|
|
27
29
|
};
|
|
28
|
-
//# sourceMappingURL=chunk-
|
|
30
|
+
//# sourceMappingURL=chunk-3B5ZX3IS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import type {\n AuthStrategy,\n Encrypted as CipherStashEncrypted,\n EncryptedPayload as CipherStashEncryptedPayload,\n EncryptedQuery as CipherStashEncryptedQuery,\n EncryptedV3Query as CipherStashEncryptedV3Query,\n JsPlaintext,\n newClient,\n QueryOpName,\n} from '@cipherstash/protect-ffi'\nimport type {\n ColumnSchema,\n EncryptedColumn,\n EncryptedField,\n EncryptedTableColumn,\n // Imported type-only for the TSDoc {@link} references in the comments below.\n encryptedColumn,\n encryptedField,\n} from '@/schema'\n\n/**\n * A pluggable authentication strategy for ZeroKMS requests. Any object\n * with a `getToken(): Promise<{ token: string }>` method satisfies it —\n * notably the strategies from `@cipherstash/auth`: `OidcFederationStrategy`\n * (per-user, identity-bound encryption) and `AccessKeyStrategy`\n * (service-to-service / CI). When supplied to {@link ClientConfig.authStrategy},\n * `getToken()` is invoked on every ZeroKMS request, taking precedence over\n * the default `auto` strategy.\n *\n * @see ClientConfig.authStrategy\n */\nexport type { AuthStrategy }\n\n// ---------------------------------------------------------------------------\n// Branded type utilities\n// ---------------------------------------------------------------------------\n\n/** Brand symbol for nominal typing */\ndeclare const __brand: unique symbol\n\n/** Creates a branded type that is structurally incompatible with the base type */\ntype Brand<T, B extends string> = T & { readonly [__brand]: B }\n\n// ---------------------------------------------------------------------------\n// Core types\n// ---------------------------------------------------------------------------\n\nexport type Client = Awaited<ReturnType<typeof newClient>> | undefined\n\n/** A branded type representing encrypted data. Cannot be accidentally used as plaintext. */\nexport type EncryptedValue = Brand<CipherStashEncrypted, 'encrypted'>\n\n/** Structural type representing encrypted data stored in the database. Always\n * carries a ciphertext. Covers BOTH wire formats: the EQL v2.3 payloads\n * (`k: \"ct\"` / `k: \"sv\"`) and the EQL v3 payloads (flat `{v: 3, i, c, …}`\n * scalars and `{v: 3, k: \"sv\", i, sv}` SteVec documents). Which format\n * `encrypt` produces is selected by the client's\n * {@link ClientConfig.eqlVersion}; `decrypt` accepts both regardless.\n * v3 scalars carry no `k` discriminator, so narrow with `'k' in payload`\n * before reading it. See also `EncryptedValue` for branded nominal typing,\n * and {@link EncryptedQuery} for the search-term shape returned by\n * `encryptQuery`. */\nexport type Encrypted = CipherStashEncryptedPayload\n\n/** Structural type representing an encrypted query term (search needle)\n * returned by `encryptQuery` / `encryptQueryBulk` for scalar\n * (`unique` / `match` / `ore`) lookups and `ste_vec_selector` JSON path\n * queries, plus — under `eqlVersion: 3` — the `eql_v3.jsonb_query`\n * containment needle. Carries no ciphertext — matched against stored\n * values, never decrypted. v2 JSON containment queries (`ste_vec_term`)\n * return a storage-shaped {@link Encrypted} payload instead. */\nexport type EncryptedQuery =\n | CipherStashEncryptedQuery\n | CipherStashEncryptedV3Query\n\n/**\n * Plaintext values the SDK accepts for encryption.\n *\n * Widens the FFI's `JsPlaintext` (`string | number | boolean |\n * Record<string, unknown> | JsPlaintext[]`) with `Date` and `bigint`. `Date`\n * is a supported cast target that is omitted from the FFI's `JsPlaintext` INPUT\n * union, but it serializes at the boundary via `toJSON` (ISO string), so it is\n * accepted on the way in.\n *\n * `bigint` is the plaintext for the v3 int8/bigint domains (see `eql/v3`),\n * which always decrypt to a JS `bigint`. protect-ffi 0.28 marshals a native\n * `bigint` across the Neon boundary losslessly. i64 bounds\n * (`-2^63 … 2^63 - 1`) are enforced at the protect-ffi boundary, not here —\n * out-of-range values surface as encryption errors from the FFI.\n *\n * When the upstream FFI `JsPlaintext` includes `Date` and `bigint`, both extra\n * arms can collapse back into `JsPlaintext`.\n */\nexport type Plaintext = JsPlaintext | Date | bigint\n\n// ---------------------------------------------------------------------------\n// Client configuration\n// ---------------------------------------------------------------------------\n\nexport type KeysetIdentifier = { name: string } | { id: string }\n\nexport type ClientConfig = {\n /**\n * The CipherStash workspace CRN (Cloud Resource Name).\n * Format: `crn:<region>.aws:<workspace-id>`.\n * Can also be set via the `CS_WORKSPACE_CRN` environment variable.\n */\n workspaceCrn?: string\n\n /**\n * The API access key used for authenticating with the CipherStash API.\n * Can also be set via the `CS_CLIENT_ACCESS_KEY` environment variable.\n * Obtain this from the CipherStash dashboard after creating a workspace.\n */\n accessKey?: string\n\n /**\n * The client identifier used to authenticate with CipherStash services.\n * Can also be set via the `CS_CLIENT_ID` environment variable.\n * Generated during workspace onboarding in the CipherStash dashboard.\n */\n clientId?: string\n\n /**\n * The client key material used in combination with ZeroKMS for encryption operations.\n * Can also be set via the `CS_CLIENT_KEY` environment variable.\n * Generated during workspace onboarding in the CipherStash dashboard.\n */\n clientKey?: string\n\n /**\n * An optional keyset identifier for multi-tenant encryption.\n * Each keyset provides cryptographic isolation, giving each tenant its own keyspace.\n * Specify by name (`{ name: \"tenant-a\" }`) or UUID (`{ id: \"...\" }`).\n * Keysets are created and managed in the\n * [dashboard](https://dashboard.cipherstash.com/workspaces/_/keysets); omit to\n * use the workspace's default keyset. A client is bound to one keyset for its\n * lifetime, so use one client per tenant.\n *\n * @see {@link Encryption} for the full keysets walkthrough.\n */\n keyset?: KeysetIdentifier\n\n /**\n * An optional authentication strategy for ZeroKMS requests, from\n * `@cipherstash/auth` (re-exported by `@cipherstash/stack`). When provided,\n * its `getToken()` is invoked on every ZeroKMS request and takes precedence\n * over the default `auto` strategy (the `clientKey` is still required for\n * encryption). Use:\n *\n * - `OidcFederationStrategy` for per-user, identity-bound encryption —\n * federates an end user's OIDC JWT into a CTS service token, so requests\n * authenticate as that user. Pair with `.withLockContext({ identityClaim })`\n * to bind the data key to a claim. This replaces the older\n * `LockContext.identify()` ceremony.\n * - `AccessKeyStrategy` for service-to-service / CI, or any custom\n * `{ getToken() }` object for bespoke token acquisition / caching.\n *\n * Leave unset to use the default `auto` strategy, which reads credentials\n * from the `CS_*` environment variables and falls back to the local dev\n * profile created by `npx stash auth login`.\n *\n * @see {@link AuthStrategy}\n * @see {@link Encryption} for a full walkthrough of the authentication options.\n */\n authStrategy?: AuthStrategy\n\n /**\n * @deprecated Renamed to {@link ClientConfig.authStrategy}. Still honoured for\n * backwards compatibility — passing it logs a deprecation warning at runtime —\n * but it will be removed in a future release. Set `authStrategy` instead.\n */\n strategy?: AuthStrategy\n\n /**\n * The EQL wire version the client emits — one FFI client always emits\n * exactly one wire format.\n *\n * - `2` (the protect-ffi default): payloads target the\n * `eql_v2_encrypted` column type.\n * - `3`: payloads target the per-capability `eql_v3` domains\n * (`eql_v3.text_eq`, `eql_v3.integer_ord_ore`, `eql_v3.json`, …),\n * derived from each column's `cast_as` and indexes.\n *\n * When omitted, {@link Encryption} auto-detects from the schema set:\n * EQL v3 tables (from `@cipherstash/stack/v3`, marked by\n * `buildColumnKeyMap()`) select `3`; v2 tables leave the FFI default\n * (`2`) untouched. Mixing v2 and v3 tables in one client is an error —\n * split them across two clients instead.\n *\n * `decrypt` accepts BOTH formats regardless of this setting, so v2 and\n * v3 data can coexist during a migration.\n *\n * Under `3`, `encryptQuery` returns EQL v3 query operands (protect-ffi\n * 0.29+): term-only scalar operands for the `eql_v3.query_<name>` twins,\n * the `eql_v3.query_jsonb` containment needle, and bare selector-hash\n * strings for JSON path queries.\n */\n eqlVersion?: 2 | 3\n}\n\ntype AtLeastOneCsTable<T> = [T, ...T[]]\n\n/** Structural contract for a column builder the client can consume for STORAGE\n * (`encrypt`). Satisfied by v2 `EncryptedColumn` / `EncryptedField` AND v3\n * `EncryptedTextSearchColumn` — fields ARE encryptable, so this stays wide. */\nexport interface BuildableColumn {\n getName(): string\n build(): ColumnSchema\n}\n\n/** Structural contract for a column the client can consume for QUERIES\n * (`encryptQuery` / search terms). Narrower than `BuildableColumn`: it must\n * EXCLUDE non-queryable `EncryptedField` (a field has no indexes). A v2\n * `EncryptedColumn` qualifies via the nominal arm; a v3 queryable concrete\n * type qualifies via the `getEqlType()` structural arm; `EncryptedField` (no\n * `getEqlType`, not an `EncryptedColumn`) is rejected. */\nexport interface BuildableV3QueryableColumn extends BuildableColumn {\n getEqlType(): string\n getQueryCapabilities(): {\n equality: boolean\n orderAndRange: boolean\n freeTextSearch: boolean\n }\n isQueryable(): true\n}\n\nexport type BuildableQueryColumn = EncryptedColumn | BuildableV3QueryableColumn\n\n/** Structural contract for a table builder the client can consume. Satisfied by\n * v2 and v3 `EncryptedTable` alike. */\nexport interface BuildableTable {\n tableName: string\n build(): { tableName: string; columns: Record<string, ColumnSchema> }\n /**\n * Optional map from a model field's JS property name to its encrypt-config\n * column name (the DB name). Present when the two can differ — v3 tables key\n * their config by DB name (`column.getName()`) while models are written with\n * JS property keys, so the model path must match by property but address the\n * FFI/config by DB name.\n *\n * Absent on v2 tables, whose `build()` already keys columns by the JS property\n * name; the model path then matches and addresses by that same key.\n */\n buildColumnKeyMap?(): Record<string, string>\n}\n\nexport type EncryptionClientConfig = {\n schemas: AtLeastOneCsTable<BuildableTable>\n config?: ClientConfig\n}\n\n/**\n * The literal column map of a buildable table, read from its type-level\n * `_columnType` brand. Both v2 and v3 `EncryptedTable` carry this brand, so this\n * recovers the literal column keys structurally.\n *\n * This deliberately uses the `_columnType` brand rather than `build().columns`:\n * `BuildableTable.build()` is typed to return `Record<string, ColumnSchema>`,\n * which erases the literal keys and would mark EVERY model field as encrypted.\n *\n * The fallbacks resolve to `Record<never, never>` (a no-key type), NOT `never`:\n * a value typed as the bare structural `BuildableTable` carries no `_columnType`\n * brand, and `keyof never` is `string | number | symbol` — which would wrongly\n * mark EVERY model field as encrypted. `keyof Record<never, never>` is `never`,\n * so `EncryptedFromBuildableTable` degrades gracefully to the model unchanged.\n */\nexport type BuildableTableColumns<T extends BuildableTable> = T extends {\n readonly _columnType: infer C\n}\n ? C extends Record<string, unknown>\n ? C\n : Record<never, never>\n : Record<never, never>\n\n/**\n * Maps a plaintext model type to its encrypted form using a buildable table.\n *\n * Fields whose keys match a column defined in `Table` (via its `_columnType`\n * brand) become `Encrypted` (`Encrypted | null` when the source field is\n * nullable); all other fields retain their original types from `T`. Works for\n * both v2 and v3 tables. See {@link EncryptedFromSchema} for the v2-specific\n * variant retained for backward compatibility.\n */\nexport type EncryptedFromBuildableTable<T, Table extends BuildableTable> = {\n [K in keyof T]: [K] extends [keyof BuildableTableColumns<Table>]\n ? null extends T[K]\n ? Encrypted | null\n : Encrypted\n : T[K]\n}\n\n// ---------------------------------------------------------------------------\n// Encrypt / decrypt operation options and results\n// ---------------------------------------------------------------------------\n\n/**\n * Options for single-value encrypt operations.\n * Use a column from your table schema (from {@link encryptedColumn}) or a nested\n * field (from {@link encryptedField}) as the target for encryption.\n */\nexport type EncryptOptions = {\n /** The column or nested field to encrypt into. From {@link EncryptedColumn} or {@link EncryptedField}. */\n column: BuildableColumn // storage: fields are encryptable, so stays wide\n table: BuildableTable\n}\n\n/** Format for encrypted query/search term return values */\nexport type EncryptedReturnType =\n | 'eql'\n | 'composite-literal'\n | 'escaped-composite-literal'\n\nexport type SearchTerm = {\n value: Plaintext\n column: BuildableQueryColumn // query: excludes non-queryable EncryptedField\n table: BuildableTable\n returnType?: EncryptedReturnType\n}\n\n/** Encrypted search term result. `eql` return type yields either a storage\n * payload (`Encrypted`, for `ste_vec_term`) or a query-only term\n * (`EncryptedQuery`, for scalar lookups and `ste_vec_selector`); the\n * `composite-literal` return types yield a string. */\nexport type EncryptedSearchTerm = Encrypted | EncryptedQuery | string\n\n/** Result of encryptQuery (single or batch). `eql` return type yields either a\n * storage payload (`Encrypted`) or a query-only term (`EncryptedQuery`); the\n * `composite-literal` return types yield a string. */\n// null elements appear in the batch path when a term has a null/undefined\n// value — the operation preserves position so callers can correlate results\n// with their input array.\nexport type EncryptedQueryResult = Encrypted | EncryptedQuery | string | null\n\n// ---------------------------------------------------------------------------\n// Model field types (encrypted vs decrypted views)\n// ---------------------------------------------------------------------------\n\nexport type EncryptedFields<T> = {\n [K in keyof T as NonNullable<T[K]> extends Encrypted ? K : never]: T[K]\n}\n\nexport type OtherFields<T> = {\n [K in keyof T as NonNullable<T[K]> extends Encrypted ? never : K]: T[K]\n}\n\nexport type DecryptedFields<T> = {\n [K in keyof T as NonNullable<T[K]> extends Encrypted\n ? K\n : never]: null extends T[K] ? string | null : string\n}\n\n/** Model with encrypted fields replaced by plaintext (decrypted) values */\nexport type Decrypted<T> = OtherFields<T> & DecryptedFields<T>\n\n/**\n * Maps a plaintext model type to its encrypted form using the table schema.\n *\n * Fields whose keys match columns defined in `S` become `Encrypted`;\n * all other fields retain their original types from `T`.\n *\n * When `S` is the widened `EncryptedTableColumn` (e.g. when a user passes an\n * explicit `<User>` type argument without specifying `S`), the type degrades\n * gracefully to `T` — preserving backward compatibility.\n *\n * @typeParam T - The plaintext model type (e.g. `{ id: string; email: string }`)\n * @typeParam S - The table schema column definition, inferred from the `table` argument\n *\n * @example\n * ```typescript\n * type User = { id: string; email: string }\n * // With a schema that defines `email`:\n * type Encrypted = EncryptedFromSchema<User, { email: EncryptedColumn }>\n * // => { id: string; email: Encrypted }\n * ```\n */\nexport type EncryptedFromSchema<T, S extends EncryptedTableColumn> = {\n [K in keyof T]: [K] extends [keyof S]\n ? [S[K & keyof S]] extends [EncryptedColumn | EncryptedField]\n ? null extends T[K]\n ? Encrypted | null\n : Encrypted\n : T[K]\n : T[K]\n}\n\n// ---------------------------------------------------------------------------\n// Bulk operations\n// ---------------------------------------------------------------------------\n\n// Bulk payloads admit null elements — bulk operations pass them through\n// untouched (encrypt(null) -> null, decrypt(null) -> null) so callers can\n// process mixed nullable arrays without filtering ahead of time. The\n// runtime guards in the operation classes preserve the null in the\n// position-stable output.\nexport type BulkEncryptPayload = Array<{\n id?: string\n plaintext: Plaintext | null\n}>\n\nexport type BulkEncryptedData = Array<{ id?: string; data: Encrypted | null }>\nexport type BulkDecryptPayload = Array<{ id?: string; data: Encrypted | null }>\nexport type BulkDecryptedData = Array<DecryptionResult<JsPlaintext | null>>\n\ntype DecryptionSuccess<T> = { error?: never; data: T; id?: string }\ntype DecryptionError<T> = { error: T; id?: string; data?: never }\n\n/**\n * Result type for individual items in bulk decrypt operations.\n * Uses `error`/`data` fields (not `failure`/`data`) since bulk operations\n * can have per-item failures.\n */\nexport type DecryptionResult<T> = DecryptionSuccess<T> | DecryptionError<T>\n\n// ---------------------------------------------------------------------------\n// Query types (for searchable encryption / encryptQuery)\n// ---------------------------------------------------------------------------\n\n/**\n * User-facing query type names for encrypting query values.\n *\n * - `'equality'`: Exact match. [Exact Queries](https://cipherstash.com/docs/stack/cipherstash/encryption/searchable-encryption)\n * - `'freeTextSearch'`: Text search. [Match Queries](https://cipherstash.com/docs/stack/cipherstash/encryption/searchable-encryption)\n * - `'orderAndRange'`: Comparison and range. [Range Queries](https://cipherstash.com/docs/stack/cipherstash/encryption/searchable-encryption)\n * - `'steVecSelector'`: JSONPath selector (e.g. `'$.user.email'`)\n * - `'steVecTerm'`: Containment (e.g. `{ role: 'admin' }`)\n * - `'searchableJson'`: Auto-infers selector or term from plaintext type (recommended)\n */\nexport type QueryTypeName =\n | 'orderAndRange'\n | 'freeTextSearch'\n | 'equality'\n | 'steVecSelector'\n | 'steVecTerm'\n | 'searchableJson'\n\n/** @internal */\nexport type FfiIndexTypeName = 'ore' | 'ope' | 'match' | 'unique' | 'ste_vec'\n\nexport const queryTypes = {\n orderAndRange: 'orderAndRange',\n freeTextSearch: 'freeTextSearch',\n equality: 'equality',\n steVecSelector: 'steVecSelector',\n steVecTerm: 'steVecTerm',\n searchableJson: 'searchableJson',\n} as const satisfies Record<string, QueryTypeName>\n\n/** @internal */\nexport const queryTypeToFfi: Record<QueryTypeName, FfiIndexTypeName> = {\n // v3 `_ord` domains carry `ope` instead — `resolveIndexType` swaps this\n // static default for the ordering index the column actually configures.\n orderAndRange: 'ore',\n freeTextSearch: 'match',\n equality: 'unique',\n steVecSelector: 'ste_vec',\n steVecTerm: 'ste_vec',\n searchableJson: 'ste_vec',\n}\n\n/** @internal */\nexport const queryTypeToQueryOp: Partial<Record<QueryTypeName, QueryOpName>> = {\n steVecSelector: 'ste_vec_selector',\n steVecTerm: 'ste_vec_term',\n}\n\n/** @internal */\nexport type QueryTermBase = {\n column: BuildableQueryColumn // query: excludes non-queryable EncryptedField\n table: BuildableTable\n queryType?: QueryTypeName\n returnType?: EncryptedReturnType\n}\n\nexport type EncryptQueryOptions = QueryTermBase\n\nexport type ScalarQueryTerm = QueryTermBase & {\n value: Plaintext\n}\n"],"mappings":";AAubO,IAAM,aAAa;AAAA,EACxB,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,gBAAgB;AAClB;AAGO,IAAM,iBAA0D;AAAA;AAAA;AAAA,EAGrE,eAAe;AAAA,EACf,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,gBAAgB;AAAA,EAChB,YAAY;AAAA,EACZ,gBAAgB;AAClB;AAGO,IAAM,qBAAkE;AAAA,EAC7E,gBAAgB;AAAA,EAChB,YAAY;AACd;","names":[]}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// src/schema/match-defaults.ts
|
|
2
|
+
function defaultMatchOpts() {
|
|
3
|
+
return {
|
|
4
|
+
tokenizer: { kind: "ngram", token_length: 3 },
|
|
5
|
+
token_filters: [{ kind: "downcase" }],
|
|
6
|
+
k: 6,
|
|
7
|
+
m: 2048,
|
|
8
|
+
include_original: true
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function cloneMatchOpts(opts) {
|
|
12
|
+
return {
|
|
13
|
+
...opts,
|
|
14
|
+
tokenizer: { ...opts.tokenizer },
|
|
15
|
+
token_filters: opts.token_filters.map((f) => ({ ...f }))
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function resolveMatchOpts(opts) {
|
|
19
|
+
const defaults = defaultMatchOpts();
|
|
20
|
+
return cloneMatchOpts({
|
|
21
|
+
tokenizer: opts?.tokenizer ?? defaults.tokenizer,
|
|
22
|
+
token_filters: opts?.token_filters ?? defaults.token_filters,
|
|
23
|
+
k: opts?.k ?? defaults.k,
|
|
24
|
+
m: opts?.m ?? defaults.m,
|
|
25
|
+
include_original: opts?.include_original ?? defaults.include_original
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function matchNeedleMinLength(opts) {
|
|
29
|
+
const tokenizer = opts.tokenizer ?? defaultMatchOpts().tokenizer;
|
|
30
|
+
return tokenizer.kind === "ngram" ? tokenizer.token_length : void 0;
|
|
31
|
+
}
|
|
32
|
+
function matchNeedleError(needle, opts) {
|
|
33
|
+
if (typeof needle !== "string") return void 0;
|
|
34
|
+
const length = [...needle].length;
|
|
35
|
+
const min = matchNeedleMinLength(opts);
|
|
36
|
+
if (length === 0) {
|
|
37
|
+
return `free-text search needs a non-empty search term; an empty term produces no tokens and would match every row.`;
|
|
38
|
+
}
|
|
39
|
+
if (min === void 0 || length >= min) return void 0;
|
|
40
|
+
return `free-text search needs at least ${min} characters (the index tokenizer's token_length), but the search term ${JSON.stringify(needle)} has ${length}. A shorter term produces no tokens and would match every row.`;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export {
|
|
44
|
+
defaultMatchOpts,
|
|
45
|
+
resolveMatchOpts,
|
|
46
|
+
matchNeedleError
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=chunk-7333ZC6L.js.map
|