@cipherstash/stack 0.19.0 → 1.0.0-rc.1

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.
Files changed (101) hide show
  1. package/CHANGELOG.md +596 -0
  2. package/README.md +376 -276
  3. package/dist/adapter-kit.cjs +1002 -0
  4. package/dist/adapter-kit.cjs.map +1 -0
  5. package/dist/adapter-kit.d.cts +120 -0
  6. package/dist/adapter-kit.d.ts +120 -0
  7. package/dist/adapter-kit.js +122 -0
  8. package/dist/adapter-kit.js.map +1 -0
  9. package/dist/base-operation-AOAIvsSB.d.cts +32 -0
  10. package/dist/base-operation-FXEzUXIq.d.ts +32 -0
  11. package/dist/{chunk-4AVL4VZD.js → chunk-3B5ZX3IS.js} +3 -1
  12. package/dist/chunk-3B5ZX3IS.js.map +1 -0
  13. package/dist/{chunk-U66S7VIF.js → chunk-6SGN52W6.js} +166 -107
  14. package/dist/chunk-6SGN52W6.js.map +1 -0
  15. package/dist/chunk-7333ZC6L.js +48 -0
  16. package/dist/chunk-7333ZC6L.js.map +1 -0
  17. package/dist/{chunk-MP3SSDNN.js → chunk-CLM7E4I6.js} +15 -15
  18. package/dist/{chunk-MP3SSDNN.js.map → chunk-CLM7E4I6.js.map} +1 -1
  19. package/dist/{chunk-OFQ555AX.js → chunk-IDKP6ABU.js} +2 -2
  20. package/dist/{chunk-36AA7IBJ.js → chunk-L7ISHSG7.js} +53 -20
  21. package/dist/chunk-L7ISHSG7.js.map +1 -0
  22. package/dist/{chunk-LBMC4D6D.js → chunk-NVKK7UDN.js} +1 -1
  23. package/dist/chunk-NVKK7UDN.js.map +1 -0
  24. package/dist/chunk-X3JRXEIB.js +98 -0
  25. package/dist/chunk-X3JRXEIB.js.map +1 -0
  26. package/dist/client.cjs +29 -12
  27. package/dist/client.cjs.map +1 -1
  28. package/dist/client.d.cts +3 -2
  29. package/dist/client.d.ts +3 -2
  30. package/dist/client.js +2 -2
  31. package/dist/{table-CIH7jZ2h.d.ts → columns-0lbT9stl.d.ts} +157 -138
  32. package/dist/{table-DihEAlxG.d.cts → columns-Bxv7Oo9o.d.cts} +157 -138
  33. package/dist/dynamodb/index.d.cts +3 -2
  34. package/dist/dynamodb/index.d.ts +3 -2
  35. package/dist/encryption/index.cjs +54 -16
  36. package/dist/encryption/index.cjs.map +1 -1
  37. package/dist/encryption/index.d.cts +807 -6
  38. package/dist/encryption/index.d.ts +807 -6
  39. package/dist/encryption/index.js +6 -6
  40. package/dist/encryption/v3.cjs +1078 -973
  41. package/dist/encryption/v3.cjs.map +1 -1
  42. package/dist/encryption/v3.d.cts +15 -12
  43. package/dist/encryption/v3.d.ts +15 -12
  44. package/dist/encryption/v3.js +50 -22
  45. package/dist/encryption/v3.js.map +1 -1
  46. package/dist/eql/v3/index.cjs +130 -79
  47. package/dist/eql/v3/index.cjs.map +1 -1
  48. package/dist/eql/v3/index.d.cts +115 -13
  49. package/dist/eql/v3/index.d.ts +115 -13
  50. package/dist/eql/v3/index.js +16 -6
  51. package/dist/errors/index.cjs.map +1 -1
  52. package/dist/errors/index.d.cts +5 -5
  53. package/dist/errors/index.d.ts +5 -5
  54. package/dist/errors/index.js +1 -1
  55. package/dist/identity/index.cjs.map +1 -1
  56. package/dist/identity/index.js +2 -2
  57. package/dist/index-BquA71_Y.d.ts +24 -0
  58. package/dist/index-fhWTOV0K.d.cts +24 -0
  59. package/dist/index.cjs +79 -27
  60. package/dist/index.cjs.map +1 -1
  61. package/dist/index.d.cts +5 -17
  62. package/dist/index.d.ts +5 -17
  63. package/dist/index.js +6 -6
  64. package/dist/schema/index.cjs +29 -12
  65. package/dist/schema/index.cjs.map +1 -1
  66. package/dist/schema/index.d.cts +1 -1
  67. package/dist/schema/index.d.ts +1 -1
  68. package/dist/schema/index.js +2 -2
  69. package/dist/{types-public-CpS5KjwX.d.ts → types-public-QMjYNfQO.d.cts} +765 -726
  70. package/dist/{types-public-CpS5KjwX.d.cts → types-public-QMjYNfQO.d.ts} +765 -726
  71. package/dist/types-public.cjs.map +1 -1
  72. package/dist/types-public.d.cts +1 -1
  73. package/dist/types-public.d.ts +1 -1
  74. package/dist/types-public.js +1 -1
  75. package/dist/wasm-inline.d.ts +779 -405
  76. package/dist/wasm-inline.js +606 -299
  77. package/dist/wasm-inline.js.map +1 -1
  78. package/package.json +25 -53
  79. package/dist/chunk-36AA7IBJ.js.map +0 -1
  80. package/dist/chunk-4AVL4VZD.js.map +0 -1
  81. package/dist/chunk-IADZCZEA.js +0 -23
  82. package/dist/chunk-IADZCZEA.js.map +0 -1
  83. package/dist/chunk-IBSK6P33.js +0 -209
  84. package/dist/chunk-IBSK6P33.js.map +0 -1
  85. package/dist/chunk-LBMC4D6D.js.map +0 -1
  86. package/dist/chunk-U66S7VIF.js.map +0 -1
  87. package/dist/client-DSGHBN-g.d.cts +0 -834
  88. package/dist/client-DfCrlHXh.d.ts +0 -834
  89. package/dist/drizzle/index.cjs +0 -5617
  90. package/dist/drizzle/index.cjs.map +0 -1
  91. package/dist/drizzle/index.d.cts +0 -358
  92. package/dist/drizzle/index.d.ts +0 -358
  93. package/dist/drizzle/index.js +0 -1220
  94. package/dist/drizzle/index.js.map +0 -1
  95. package/dist/supabase/index.cjs +0 -5951
  96. package/dist/supabase/index.cjs.map +0 -1
  97. package/dist/supabase/index.d.cts +0 -223
  98. package/dist/supabase/index.d.ts +0 -223
  99. package/dist/supabase/index.js +0 -1217
  100. package/dist/supabase/index.js.map +0 -1
  101. /package/dist/{chunk-OFQ555AX.js.map → chunk-IDKP6ABU.js.map} +0 -0
@@ -1,13 +1,15 @@
1
1
  import { Result } from '@byteslice/result';
2
- import { A as AnyV3Table, C as ColumnsOf, P as PlaintextForColumn, Q as QueryableColumnsOf, a as QueryTypesForColumn, V as V3ModelInput, b as V3EncryptedModel, c as V3DecryptedModel } from '../table-DihEAlxG.cjs';
3
- export { d as AnyEncryptedV3Column, E as EncryptedBooleanColumn, e as EncryptedDateColumn, f as EncryptedDateEqColumn, g as EncryptedDateOrdColumn, h as EncryptedDateOrdOreColumn, i as EncryptedDoubleColumn, j as EncryptedDoubleEqColumn, k as EncryptedDoubleOrdColumn, l as EncryptedDoubleOrdOreColumn, m as EncryptedIntegerColumn, n as EncryptedIntegerEqColumn, o as EncryptedIntegerOrdColumn, p as EncryptedIntegerOrdOreColumn, q as EncryptedNumericColumn, r as EncryptedNumericEqColumn, s as EncryptedNumericOrdColumn, t as EncryptedNumericOrdOreColumn, u as EncryptedRealColumn, v as EncryptedRealEqColumn, w as EncryptedRealOrdColumn, x as EncryptedRealOrdOreColumn, y as EncryptedSmallintColumn, z as EncryptedSmallintEqColumn, B as EncryptedSmallintOrdColumn, D as EncryptedSmallintOrdOreColumn, F as EncryptedTable, G as EncryptedTextColumn, H as EncryptedTextEqColumn, I as EncryptedTextMatchColumn, J as EncryptedTextOrdColumn, K as EncryptedTextOrdOreColumn, L as EncryptedTextSearchColumn, M as EncryptedTimestampColumn, N as EncryptedTimestampEqColumn, O as EncryptedTimestampOrdColumn, R as EncryptedTimestampOrdOreColumn, S as EncryptedV3TableColumn, T as EqlTypeForColumn, U as InferEncrypted, W as InferPlaintext, X as QueryCapabilities, Y as TEXT_SEARCH_EQL_TYPE, Z as buildEncryptConfig, _ as encryptedTable } from '../table-DihEAlxG.cjs';
4
- export { types } from '../eql/v3/index.cjs';
2
+ import { V as PlaintextForColumn, X as QueryTypesForColumn } from '../columns-Bxv7Oo9o.cjs';
3
+ export { A as AnyEncryptedV3Column, E as EncryptedBigintColumn, a as EncryptedBigintEqColumn, b as EncryptedBigintOrdColumn, c as EncryptedBigintOrdOreColumn, d as EncryptedBooleanColumn, e as EncryptedDateColumn, f as EncryptedDateEqColumn, g as EncryptedDateOrdColumn, h as EncryptedDateOrdOreColumn, i as EncryptedDoubleColumn, j as EncryptedDoubleEqColumn, k as EncryptedDoubleOrdColumn, l as EncryptedDoubleOrdOreColumn, m as EncryptedIntegerColumn, n as EncryptedIntegerEqColumn, o as EncryptedIntegerOrdColumn, p as EncryptedIntegerOrdOreColumn, q as EncryptedJsonColumn, r as EncryptedNumericColumn, s as EncryptedNumericEqColumn, t as EncryptedNumericOrdColumn, u as EncryptedNumericOrdOreColumn, v as EncryptedRealColumn, w as EncryptedRealEqColumn, x as EncryptedRealOrdColumn, y as EncryptedRealOrdOreColumn, z as EncryptedSmallintColumn, B as EncryptedSmallintEqColumn, C as EncryptedSmallintOrdColumn, F as EncryptedSmallintOrdOreColumn, G as EncryptedTextColumn, H as EncryptedTextEqColumn, I as EncryptedTextMatchColumn, J as EncryptedTextOrdColumn, K as EncryptedTextOrdOreColumn, L as EncryptedTextSearchColumn, M as EncryptedTimestampColumn, N as EncryptedTimestampEqColumn, O as EncryptedTimestampOrdColumn, P as EncryptedTimestampOrdOreColumn, R as EncryptedV3TableColumn, S as EqlTypeForColumn, T as JsonDocument, U as JsonValue, W as QueryCapabilities, Y as TEXT_SEARCH_EQL_TYPE } from '../columns-Bxv7Oo9o.cjs';
4
+ import { AnyV3Table, ColumnsOf, QueryableColumnsOf, V3ModelInput, V3EncryptedModel, V3DecryptedModel } from '../eql/v3/index.cjs';
5
+ export { EncryptedTable, InferEncrypted, InferPlaintext, buildEncryptConfig, encryptedTable, types } from '../eql/v3/index.cjs';
5
6
  import { EncryptionError } from '../errors/index.cjs';
6
7
  import { LockContextInput } from '../identity/index.cjs';
7
- import { C as ClientConfig, E as EncryptedReturnType, a as Encrypted, B as BulkEncryptPayload, b as EncryptOptions, c as BulkDecryptPayload } from '../types-public-CpS5KjwX.cjs';
8
- import { E as EncryptOperation, a as EncryptQueryOperation, b as EncryptModelOperation, B as BulkEncryptModelsOperation, D as DecryptOperation, c as BulkEncryptOperation, d as BulkDecryptOperation, e as EncryptionClient } from '../client-DSGHBN-g.cjs';
8
+ import { j as ClientConfig, x as EncryptedReturnType, S as ScalarQueryTerm, p as Encrypted, g as BulkEncryptPayload, n as EncryptOptions, e as BulkDecryptPayload } from '../types-public-QMjYNfQO.cjs';
9
+ import { EncryptOperation, EncryptQueryOperation, BatchEncryptQueryOperation, EncryptModelOperation, BulkEncryptModelsOperation, DecryptOperation, BulkEncryptOperation, BulkDecryptOperation, EncryptionClient } from './index.cjs';
9
10
  import '@cipherstash/protect-ffi';
10
11
  import 'zod';
12
+ import '../base-operation-AOAIvsSB.cjs';
11
13
 
12
14
  /**
13
15
  * A strongly-typed view over an {@link EncryptionClient} for EQL v3 schemas.
@@ -38,6 +40,13 @@ interface TypedEncryptionClient<S extends readonly AnyV3Table[]> {
38
40
  queryType?: QT;
39
41
  returnType?: EncryptedReturnType;
40
42
  }): EncryptQueryOperation;
43
+ /**
44
+ * Batch form: encrypt many query terms in one crossing. Mirrors the nominal
45
+ * {@link EncryptionClient} overload — the per-term columns are heterogeneous,
46
+ * so the terms are the base {@link ScalarQueryTerm} rather than a per-column
47
+ * narrowed type. Consumed by the Drizzle `inArray`/`notInArray` operators.
48
+ */
49
+ encryptQuery(terms: readonly ScalarQueryTerm[]): BatchEncryptQueryOperation;
41
50
  encryptModel<Table extends S[number], T extends Record<string, unknown>>(input: V3ModelInput<Table, T>, table: Table): EncryptModelOperation<V3EncryptedModel<Table, T>>;
42
51
  bulkEncryptModels<Table extends S[number], T extends Record<string, unknown>>(input: Array<V3ModelInput<Table, T>>, table: Table): BulkEncryptModelsOperation<V3EncryptedModel<Table, T>>;
43
52
  /**
@@ -70,18 +79,12 @@ interface TypedEncryptionClient<S extends readonly AnyV3Table[]> {
70
79
  * The `schemas` are captured with a `const` type parameter so array-literal
71
80
  * widening does not collapse per-table inference.
72
81
  */
73
- declare function typedClient<const S extends readonly AnyV3Table[]>(client: EncryptionClient, ..._schemas: S): TypedEncryptionClient<S>;
82
+ declare function typedClient<const S extends readonly AnyV3Table[]>(client: EncryptionClient, ...schemas: S): TypedEncryptionClient<S>;
74
83
  /**
75
84
  * Build a {@link TypedEncryptionClient} for EQL v3 schemas — the strongly-typed
76
85
  * counterpart to {@link Encryption}. Mirrors its config, then retypes the client
77
86
  * against the provided v3 `schemas`.
78
87
  *
79
- * The underlying client is created with `eqlVersion: 3` (protect-ffi 0.27+),
80
- * so `encrypt` / `encryptModel` emit EQL v3 wire payloads for the
81
- * per-capability `eql_v3` domains. Pass an explicit `config.eqlVersion` to
82
- * override — e.g. `2` to write v2-wire from a v3 schema set during a
83
- * migration.
84
- *
85
88
  * @example
86
89
  * ```typescript
87
90
  * import { EncryptionV3, encryptedTable, types } from "@cipherstash/stack/v3"
@@ -1,13 +1,15 @@
1
1
  import { Result } from '@byteslice/result';
2
- import { A as AnyV3Table, C as ColumnsOf, P as PlaintextForColumn, Q as QueryableColumnsOf, a as QueryTypesForColumn, V as V3ModelInput, b as V3EncryptedModel, c as V3DecryptedModel } from '../table-CIH7jZ2h.js';
3
- export { d as AnyEncryptedV3Column, E as EncryptedBooleanColumn, e as EncryptedDateColumn, f as EncryptedDateEqColumn, g as EncryptedDateOrdColumn, h as EncryptedDateOrdOreColumn, i as EncryptedDoubleColumn, j as EncryptedDoubleEqColumn, k as EncryptedDoubleOrdColumn, l as EncryptedDoubleOrdOreColumn, m as EncryptedIntegerColumn, n as EncryptedIntegerEqColumn, o as EncryptedIntegerOrdColumn, p as EncryptedIntegerOrdOreColumn, q as EncryptedNumericColumn, r as EncryptedNumericEqColumn, s as EncryptedNumericOrdColumn, t as EncryptedNumericOrdOreColumn, u as EncryptedRealColumn, v as EncryptedRealEqColumn, w as EncryptedRealOrdColumn, x as EncryptedRealOrdOreColumn, y as EncryptedSmallintColumn, z as EncryptedSmallintEqColumn, B as EncryptedSmallintOrdColumn, D as EncryptedSmallintOrdOreColumn, F as EncryptedTable, G as EncryptedTextColumn, H as EncryptedTextEqColumn, I as EncryptedTextMatchColumn, J as EncryptedTextOrdColumn, K as EncryptedTextOrdOreColumn, L as EncryptedTextSearchColumn, M as EncryptedTimestampColumn, N as EncryptedTimestampEqColumn, O as EncryptedTimestampOrdColumn, R as EncryptedTimestampOrdOreColumn, S as EncryptedV3TableColumn, T as EqlTypeForColumn, U as InferEncrypted, W as InferPlaintext, X as QueryCapabilities, Y as TEXT_SEARCH_EQL_TYPE, Z as buildEncryptConfig, _ as encryptedTable } from '../table-CIH7jZ2h.js';
4
- export { types } from '../eql/v3/index.js';
2
+ import { V as PlaintextForColumn, X as QueryTypesForColumn } from '../columns-0lbT9stl.js';
3
+ export { A as AnyEncryptedV3Column, E as EncryptedBigintColumn, a as EncryptedBigintEqColumn, b as EncryptedBigintOrdColumn, c as EncryptedBigintOrdOreColumn, d as EncryptedBooleanColumn, e as EncryptedDateColumn, f as EncryptedDateEqColumn, g as EncryptedDateOrdColumn, h as EncryptedDateOrdOreColumn, i as EncryptedDoubleColumn, j as EncryptedDoubleEqColumn, k as EncryptedDoubleOrdColumn, l as EncryptedDoubleOrdOreColumn, m as EncryptedIntegerColumn, n as EncryptedIntegerEqColumn, o as EncryptedIntegerOrdColumn, p as EncryptedIntegerOrdOreColumn, q as EncryptedJsonColumn, r as EncryptedNumericColumn, s as EncryptedNumericEqColumn, t as EncryptedNumericOrdColumn, u as EncryptedNumericOrdOreColumn, v as EncryptedRealColumn, w as EncryptedRealEqColumn, x as EncryptedRealOrdColumn, y as EncryptedRealOrdOreColumn, z as EncryptedSmallintColumn, B as EncryptedSmallintEqColumn, C as EncryptedSmallintOrdColumn, F as EncryptedSmallintOrdOreColumn, G as EncryptedTextColumn, H as EncryptedTextEqColumn, I as EncryptedTextMatchColumn, J as EncryptedTextOrdColumn, K as EncryptedTextOrdOreColumn, L as EncryptedTextSearchColumn, M as EncryptedTimestampColumn, N as EncryptedTimestampEqColumn, O as EncryptedTimestampOrdColumn, P as EncryptedTimestampOrdOreColumn, R as EncryptedV3TableColumn, S as EqlTypeForColumn, T as JsonDocument, U as JsonValue, W as QueryCapabilities, Y as TEXT_SEARCH_EQL_TYPE } from '../columns-0lbT9stl.js';
4
+ import { AnyV3Table, ColumnsOf, QueryableColumnsOf, V3ModelInput, V3EncryptedModel, V3DecryptedModel } from '../eql/v3/index.js';
5
+ export { EncryptedTable, InferEncrypted, InferPlaintext, buildEncryptConfig, encryptedTable, types } from '../eql/v3/index.js';
5
6
  import { EncryptionError } from '../errors/index.js';
6
7
  import { LockContextInput } from '../identity/index.js';
7
- import { C as ClientConfig, E as EncryptedReturnType, a as Encrypted, B as BulkEncryptPayload, b as EncryptOptions, c as BulkDecryptPayload } from '../types-public-CpS5KjwX.js';
8
- import { E as EncryptOperation, a as EncryptQueryOperation, b as EncryptModelOperation, B as BulkEncryptModelsOperation, D as DecryptOperation, c as BulkEncryptOperation, d as BulkDecryptOperation, e as EncryptionClient } from '../client-DfCrlHXh.js';
8
+ import { j as ClientConfig, x as EncryptedReturnType, S as ScalarQueryTerm, p as Encrypted, g as BulkEncryptPayload, n as EncryptOptions, e as BulkDecryptPayload } from '../types-public-QMjYNfQO.js';
9
+ import { EncryptOperation, EncryptQueryOperation, BatchEncryptQueryOperation, EncryptModelOperation, BulkEncryptModelsOperation, DecryptOperation, BulkEncryptOperation, BulkDecryptOperation, EncryptionClient } from './index.js';
9
10
  import '@cipherstash/protect-ffi';
10
11
  import 'zod';
12
+ import '../base-operation-FXEzUXIq.js';
11
13
 
12
14
  /**
13
15
  * A strongly-typed view over an {@link EncryptionClient} for EQL v3 schemas.
@@ -38,6 +40,13 @@ interface TypedEncryptionClient<S extends readonly AnyV3Table[]> {
38
40
  queryType?: QT;
39
41
  returnType?: EncryptedReturnType;
40
42
  }): EncryptQueryOperation;
43
+ /**
44
+ * Batch form: encrypt many query terms in one crossing. Mirrors the nominal
45
+ * {@link EncryptionClient} overload — the per-term columns are heterogeneous,
46
+ * so the terms are the base {@link ScalarQueryTerm} rather than a per-column
47
+ * narrowed type. Consumed by the Drizzle `inArray`/`notInArray` operators.
48
+ */
49
+ encryptQuery(terms: readonly ScalarQueryTerm[]): BatchEncryptQueryOperation;
41
50
  encryptModel<Table extends S[number], T extends Record<string, unknown>>(input: V3ModelInput<Table, T>, table: Table): EncryptModelOperation<V3EncryptedModel<Table, T>>;
42
51
  bulkEncryptModels<Table extends S[number], T extends Record<string, unknown>>(input: Array<V3ModelInput<Table, T>>, table: Table): BulkEncryptModelsOperation<V3EncryptedModel<Table, T>>;
43
52
  /**
@@ -70,18 +79,12 @@ interface TypedEncryptionClient<S extends readonly AnyV3Table[]> {
70
79
  * The `schemas` are captured with a `const` type parameter so array-literal
71
80
  * widening does not collapse per-table inference.
72
81
  */
73
- declare function typedClient<const S extends readonly AnyV3Table[]>(client: EncryptionClient, ..._schemas: S): TypedEncryptionClient<S>;
82
+ declare function typedClient<const S extends readonly AnyV3Table[]>(client: EncryptionClient, ...schemas: S): TypedEncryptionClient<S>;
74
83
  /**
75
84
  * Build a {@link TypedEncryptionClient} for EQL v3 schemas — the strongly-typed
76
85
  * counterpart to {@link Encryption}. Mirrors its config, then retypes the client
77
86
  * against the provided v3 `schemas`.
78
87
  *
79
- * The underlying client is created with `eqlVersion: 3` (protect-ffi 0.27+),
80
- * so `encrypt` / `encryptModel` emit EQL v3 wire payloads for the
81
- * per-capability `eql_v3` domains. Pass an explicit `config.eqlVersion` to
82
- * override — e.g. `2` to write v2-wire from a v3 schema set during a
83
- * migration.
84
- *
85
88
  * @example
86
89
  * ```typescript
87
90
  * import { EncryptionV3, encryptedTable, types } from "@cipherstash/stack/v3"
@@ -1,10 +1,14 @@
1
1
  import {
2
2
  EncryptedTable,
3
3
  buildEncryptConfig,
4
- encryptedTable,
5
- types
6
- } from "../chunk-IBSK6P33.js";
4
+ encryptedTable
5
+ } from "../chunk-X3JRXEIB.js";
7
6
  import {
7
+ DATE_LIKE_CASTS,
8
+ EncryptedBigintColumn,
9
+ EncryptedBigintEqColumn,
10
+ EncryptedBigintOrdColumn,
11
+ EncryptedBigintOrdOreColumn,
8
12
  EncryptedBooleanColumn,
9
13
  EncryptedDateColumn,
10
14
  EncryptedDateEqColumn,
@@ -18,6 +22,7 @@ import {
18
22
  EncryptedIntegerEqColumn,
19
23
  EncryptedIntegerOrdColumn,
20
24
  EncryptedIntegerOrdOreColumn,
25
+ EncryptedJsonColumn,
21
26
  EncryptedNumericColumn,
22
27
  EncryptedNumericEqColumn,
23
28
  EncryptedNumericOrdColumn,
@@ -40,19 +45,22 @@ import {
40
45
  EncryptedTimestampEqColumn,
41
46
  EncryptedTimestampOrdColumn,
42
47
  EncryptedTimestampOrdOreColumn,
43
- TEXT_SEARCH_EQL_TYPE
44
- } from "../chunk-U66S7VIF.js";
48
+ TEXT_SEARCH_EQL_TYPE,
49
+ types
50
+ } from "../chunk-6SGN52W6.js";
45
51
  import {
46
52
  Encryption
47
- } from "../chunk-36AA7IBJ.js";
48
- import "../chunk-OFQ555AX.js";
53
+ } from "../chunk-L7ISHSG7.js";
54
+ import "../chunk-IDKP6ABU.js";
49
55
  import "../chunk-BNO32ZMV.js";
50
- import "../chunk-MP3SSDNN.js";
51
- import "../chunk-IADZCZEA.js";
52
- import "../chunk-4AVL4VZD.js";
56
+ import "../chunk-CLM7E4I6.js";
57
+ import "../chunk-7333ZC6L.js";
58
+ import "../chunk-3B5ZX3IS.js";
53
59
  import "../chunk-JSG2AMDI.js";
54
60
  import "../chunk-HQANMV7R.js";
55
- import "../chunk-LBMC4D6D.js";
61
+ import {
62
+ EncryptionErrorTypes
63
+ } from "../chunk-NVKK7UDN.js";
56
64
  import "../chunk-PZ5AY32C.js";
57
65
 
58
66
  // src/encryption/v3.ts
@@ -61,7 +69,7 @@ function rowReconstructor(table) {
61
69
  const propToDb = table.buildColumnKeyMap();
62
70
  const dateProperties = Object.entries(propToDb).filter(([, dbName]) => {
63
71
  const castAs = columns[dbName]?.cast_as;
64
- return castAs === "date" || castAs === "timestamp";
72
+ return DATE_LIKE_CASTS.includes(castAs);
65
73
  }).map(([property]) => property);
66
74
  return (row) => {
67
75
  const out = { ...row };
@@ -73,24 +81,40 @@ function rowReconstructor(table) {
73
81
  return out;
74
82
  };
75
83
  }
76
- function typedClient(client, ..._schemas) {
84
+ function typedClient(client, ...schemas) {
85
+ const reconstructors = /* @__PURE__ */ new Map();
86
+ for (const table of schemas) {
87
+ reconstructors.set(table.tableName, rowReconstructor(table));
88
+ }
89
+ const unknownTableFailure = {
90
+ failure: {
91
+ type: EncryptionErrorTypes.DecryptionError,
92
+ message: "[eql/v3]: decryptModel received a table this client was not initialized with \u2014 pass a table given to EncryptionV3/typedClient"
93
+ }
94
+ };
95
+ function encryptQuery(plaintextOrTerms, opts) {
96
+ return client.encryptQuery(plaintextOrTerms, opts);
97
+ }
77
98
  return {
78
99
  encrypt: (plaintext, opts) => client.encrypt(plaintext, opts),
79
- encryptQuery: (plaintext, opts) => client.encryptQuery(plaintext, opts),
100
+ encryptQuery,
80
101
  encryptModel: (input, table) => client.encryptModel(input, table),
81
102
  bulkEncryptModels: (input, table) => client.bulkEncryptModels(input, table),
82
103
  decrypt: (encrypted) => client.decrypt(encrypted),
83
104
  decryptModel: async (input, table, lockContext) => {
105
+ const reconstruct = reconstructors.get(table.tableName);
106
+ if (!reconstruct) return unknownTableFailure;
84
107
  const op = client.decryptModel(input);
85
108
  const result = await (lockContext ? op.withLockContext(lockContext) : op);
86
109
  if (result.failure) return result;
87
- return { data: rowReconstructor(table)(result.data) };
110
+ return { data: reconstruct(result.data) };
88
111
  },
89
112
  bulkDecryptModels: async (input, table, lockContext) => {
113
+ const reconstruct = reconstructors.get(table.tableName);
114
+ if (!reconstruct) return unknownTableFailure;
90
115
  const op = client.bulkDecryptModels(input);
91
116
  const result = await (lockContext ? op.withLockContext(lockContext) : op);
92
117
  if (result.failure) return result;
93
- const reconstruct = rowReconstructor(table);
94
118
  return {
95
119
  data: result.data.map(
96
120
  (row) => reconstruct(row)
@@ -105,16 +129,19 @@ function typedClient(client, ..._schemas) {
105
129
  async function EncryptionV3(config) {
106
130
  const client = await Encryption({
107
131
  schemas: config.schemas,
108
- // v3 schemas emit the EQL v3 wire format. Auto-detection in
109
- // `Encryption` would resolve the same way (every v3 table carries the
110
- // `buildColumnKeyMap` marker), but the version is set explicitly here
111
- // so the contract doesn't hinge on duck-typing while still honouring
112
- // a caller's explicit override.
113
- config: { ...config.config, eqlVersion: config.config?.eqlVersion ?? 3 }
132
+ // Force the v3 EQL wire format. protect-ffi's newClient defaults to
133
+ // eqlVersion 2; a v2-mode client cannot resolve v3 concrete-type columns
134
+ // and fails every encrypt with "Cannot convert undefined or null to
135
+ // object". This is a v3-only invariant, so it overrides any user value.
136
+ config: { ...config.config, eqlVersion: 3 }
114
137
  });
115
138
  return typedClient(client, ...config.schemas);
116
139
  }
117
140
  export {
141
+ EncryptedBigintColumn,
142
+ EncryptedBigintEqColumn,
143
+ EncryptedBigintOrdColumn,
144
+ EncryptedBigintOrdOreColumn,
118
145
  EncryptedBooleanColumn,
119
146
  EncryptedDateColumn,
120
147
  EncryptedDateEqColumn,
@@ -128,6 +155,7 @@ export {
128
155
  EncryptedIntegerEqColumn,
129
156
  EncryptedIntegerOrdColumn,
130
157
  EncryptedIntegerOrdOreColumn,
158
+ EncryptedJsonColumn,
131
159
  EncryptedNumericColumn,
132
160
  EncryptedNumericEqColumn,
133
161
  EncryptedNumericOrdColumn,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/encryption/v3.ts"],"sourcesContent":["import type { Result } from '@byteslice/result'\nimport type {\n AnyV3Table,\n ColumnsOf,\n PlaintextForColumn,\n QueryableColumnsOf,\n QueryTypesForColumn,\n V3DecryptedModel,\n V3EncryptedModel,\n V3ModelInput,\n} from '@/eql/v3'\nimport type { EncryptionError } from '@/errors'\nimport type { LockContextInput } from '@/identity'\nimport type {\n BulkDecryptPayload,\n BulkEncryptPayload,\n ClientConfig,\n Encrypted,\n EncryptedReturnType,\n EncryptOptions,\n} from '@/types'\nimport {\n type BulkDecryptOperation,\n type BulkEncryptModelsOperation,\n type BulkEncryptOperation,\n type DecryptOperation,\n Encryption,\n type EncryptionClient,\n type EncryptModelOperation,\n type EncryptOperation,\n type EncryptQueryOperation,\n} from './index'\n\n/**\n * A strongly-typed view over an {@link EncryptionClient} for EQL v3 schemas.\n *\n * Every method derives its types from the concrete `table` / `column` builder\n * arguments (which carry their branded types at the call site), so:\n * - `encrypt` / `encryptQuery` pin the plaintext to the column's domain type\n * (`text → string`, `timestamp → Date`, …);\n * - `encryptQuery` additionally constrains `queryType` to the column's\n * capabilities and rejects storage-only columns outright;\n * - `encryptModel` / `bulkEncryptModels` validate schema-column fields against\n * their inferred plaintext type (passthrough fields are untouched) and return\n * a precise encrypted model;\n * - `decryptModel` / `bulkDecryptModels` return the precise plaintext model,\n * reconstructing `Date` values from the encrypt-config `cast_as`.\n *\n * @typeParam S - the tuple of registered v3 tables; `table` arguments must be a\n * member of this tuple.\n */\nexport interface TypedEncryptionClient<S extends readonly AnyV3Table[]> {\n encrypt<Table extends S[number], Col extends ColumnsOf<Table>>(\n plaintext: PlaintextForColumn<Col>,\n opts: { table: Table; column: Col },\n ): EncryptOperation\n\n encryptQuery<\n Table extends S[number],\n Col extends QueryableColumnsOf<Table>,\n QT extends QueryTypesForColumn<Col> = QueryTypesForColumn<Col>,\n >(\n plaintext: PlaintextForColumn<Col>,\n opts: {\n table: Table\n column: Col\n queryType?: QT\n returnType?: EncryptedReturnType\n },\n ): EncryptQueryOperation\n\n encryptModel<Table extends S[number], T extends Record<string, unknown>>(\n input: V3ModelInput<Table, T>,\n table: Table,\n ): EncryptModelOperation<V3EncryptedModel<Table, T>>\n\n bulkEncryptModels<Table extends S[number], T extends Record<string, unknown>>(\n input: Array<V3ModelInput<Table, T>>,\n table: Table,\n ): BulkEncryptModelsOperation<V3EncryptedModel<Table, T>>\n\n /**\n * Decrypt a single value. Cannot be strongly typed — a lone ciphertext carries\n * no column identity — so it resolves to the FFI plaintext union unchanged.\n */\n decrypt(encrypted: Encrypted): DecryptOperation\n\n /**\n * Decrypt a model, returning the precise plaintext shape for `table`. `Date`\n * columns are reconstructed from the encrypt-config `cast_as`.\n *\n * Pass `lockContext` to decrypt identity-bound data — the same context that\n * was supplied at encrypt time must be provided here.\n *\n * Unlike the encrypt operations this returns a plain `Promise<Result<…>>`\n * rather than a chainable operation, because it maps the resolved value.\n */\n decryptModel<Table extends S[number], T extends Record<string, unknown>>(\n input: T,\n table: Table,\n lockContext?: LockContextInput,\n ): Promise<Result<V3DecryptedModel<Table, T>, EncryptionError>>\n\n bulkDecryptModels<Table extends S[number], T extends Record<string, unknown>>(\n input: Array<T>,\n table: Table,\n lockContext?: LockContextInput,\n ): Promise<Result<Array<V3DecryptedModel<Table, T>>, EncryptionError>>\n\n // Parity passthroughs — not v3-strengthened, delegated as-is.\n bulkEncrypt(\n plaintexts: BulkEncryptPayload,\n opts: EncryptOptions,\n ): BulkEncryptOperation\n bulkDecrypt(payloads: BulkDecryptPayload): BulkDecryptOperation\n getEncryptConfig(): ReturnType<EncryptionClient['getEncryptConfig']>\n}\n\n/**\n * Build a per-row reconstructor of `Date` values from the table's\n * encrypt-config `cast_as`. The FFI returns `JsPlaintext`\n * (string/number/boolean/…) with no `Date`, so those columns arrive as their\n * serialized form and are rebuilt here. Safe (idempotent) if the FFI ever\n * returns `Date` directly: `new Date(date)` is a no-op.\n *\n * A factory rather than a `(row, table)` function so the table config —\n * row-invariant, but non-trivial to build — is derived once per call site,\n * not once per row on the bulk path.\n *\n * NOTE: `bigint` reconstruction is intentionally absent — bigint domains are\n * omitted from the v3 SDK until the native FFI supports lossless bigint I/O.\n */\nfunction rowReconstructor(\n table: AnyV3Table,\n): (row: Record<string, unknown>) => Record<string, unknown> {\n // The decrypted row is keyed by JS property name, but `cast_as` lives on the\n // config keyed by DB name — bridge the two via the table's property→DB map.\n const { columns } = table.build()\n const propToDb = table.buildColumnKeyMap()\n // Only date/timestamp columns need per-row work; resolve them up front.\n // Both kinds decrypt to a JS `Date` — 'timestamp' additionally preserves\n // the time-of-day component through the FFI.\n const dateProperties = Object.entries(propToDb)\n .filter(([, dbName]) => {\n const castAs = columns[dbName]?.cast_as\n return castAs === 'date' || castAs === 'timestamp'\n })\n .map(([property]) => property)\n\n return (row) => {\n const out: Record<string, unknown> = { ...row }\n for (const property of dateProperties) {\n const value = out[property]\n if (value == null) continue\n out[property] = new Date(value as string | number | Date)\n }\n return out\n }\n}\n\n/**\n * Wrap an already-built {@link EncryptionClient} in a {@link TypedEncryptionClient}\n * for the given v3 schemas. Zero runtime cost for the encrypt/query paths (the\n * underlying operations are returned unchanged); the decrypt-model paths add a\n * per-column `Date` reconstruction step.\n *\n * The `schemas` are captured with a `const` type parameter so array-literal\n * widening does not collapse per-table inference.\n */\nexport function typedClient<const S extends readonly AnyV3Table[]>(\n client: EncryptionClient,\n ..._schemas: S\n): TypedEncryptionClient<S> {\n return {\n encrypt: (plaintext, opts) =>\n client.encrypt(plaintext as never, opts as never),\n encryptQuery: (plaintext, opts) =>\n client.encryptQuery(plaintext as never, opts as never),\n encryptModel: (input, table) =>\n client.encryptModel(input as never, table as never) as never,\n bulkEncryptModels: (input, table) =>\n client.bulkEncryptModels(input as never, table as never) as never,\n decrypt: (encrypted) => client.decrypt(encrypted),\n decryptModel: async (input, table, lockContext) => {\n const op = client.decryptModel(input as never)\n const result = await (lockContext ? op.withLockContext(lockContext) : op)\n if (result.failure) return result as never\n return { data: rowReconstructor(table)(result.data) } as never\n },\n bulkDecryptModels: async (input, table, lockContext) => {\n const op = client.bulkDecryptModels(input as never)\n const result = await (lockContext ? op.withLockContext(lockContext) : op)\n if (result.failure) return result as never\n const reconstruct = rowReconstructor(table)\n return {\n data: result.data.map((row) =>\n reconstruct(row as Record<string, unknown>),\n ),\n } as never\n },\n bulkEncrypt: (plaintexts, opts) => client.bulkEncrypt(plaintexts, opts),\n bulkDecrypt: (payloads) => client.bulkDecrypt(payloads),\n getEncryptConfig: () => client.getEncryptConfig(),\n } satisfies TypedEncryptionClient<S>\n}\n\n/**\n * Build a {@link TypedEncryptionClient} for EQL v3 schemas — the strongly-typed\n * counterpart to {@link Encryption}. Mirrors its config, then retypes the client\n * against the provided v3 `schemas`.\n *\n * The underlying client is created with `eqlVersion: 3` (protect-ffi 0.27+),\n * so `encrypt` / `encryptModel` emit EQL v3 wire payloads for the\n * per-capability `eql_v3` domains. Pass an explicit `config.eqlVersion` to\n * override — e.g. `2` to write v2-wire from a v3 schema set during a\n * migration.\n *\n * @example\n * ```typescript\n * import { EncryptionV3, encryptedTable, types } from \"@cipherstash/stack/v3\"\n *\n * const users = encryptedTable(\"users\", { email: types.TextSearch(\"email\") })\n * const client = await EncryptionV3({ schemas: [users] })\n *\n * await client.encrypt(\"a@b.com\", { table: users, column: users.email })\n * ```\n */\nexport async function EncryptionV3<\n const S extends readonly AnyV3Table[],\n>(config: {\n schemas: S\n config?: ClientConfig\n}): Promise<TypedEncryptionClient<S>> {\n const client = await Encryption({\n schemas: config.schemas as unknown as Parameters<\n typeof Encryption\n >[0]['schemas'],\n // v3 schemas emit the EQL v3 wire format. Auto-detection in\n // `Encryption` would resolve the same way (every v3 table carries the\n // `buildColumnKeyMap` marker), but the version is set explicitly here\n // so the contract doesn't hinge on duck-typing — while still honouring\n // a caller's explicit override.\n config: { ...config.config, eqlVersion: config.config?.eqlVersion ?? 3 },\n })\n return typedClient(client, ...config.schemas)\n}\n\n// Single import surface: re-export the v3 `types` namespace + table API + type\n// helpers so `@cipherstash/stack/v3` provides everything needed to author and\n// use a schema.\nexport * from '@/eql/v3'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoIA,SAAS,iBACP,OAC2D;AAG3D,QAAM,EAAE,QAAQ,IAAI,MAAM,MAAM;AAChC,QAAM,WAAW,MAAM,kBAAkB;AAIzC,QAAM,iBAAiB,OAAO,QAAQ,QAAQ,EAC3C,OAAO,CAAC,CAAC,EAAE,MAAM,MAAM;AACtB,UAAM,SAAS,QAAQ,MAAM,GAAG;AAChC,WAAO,WAAW,UAAU,WAAW;AAAA,EACzC,CAAC,EACA,IAAI,CAAC,CAAC,QAAQ,MAAM,QAAQ;AAE/B,SAAO,CAAC,QAAQ;AACd,UAAM,MAA+B,EAAE,GAAG,IAAI;AAC9C,eAAW,YAAY,gBAAgB;AACrC,YAAM,QAAQ,IAAI,QAAQ;AAC1B,UAAI,SAAS,KAAM;AACnB,UAAI,QAAQ,IAAI,IAAI,KAAK,KAA+B;AAAA,IAC1D;AACA,WAAO;AAAA,EACT;AACF;AAWO,SAAS,YACd,WACG,UACuB;AAC1B,SAAO;AAAA,IACL,SAAS,CAAC,WAAW,SACnB,OAAO,QAAQ,WAAoB,IAAa;AAAA,IAClD,cAAc,CAAC,WAAW,SACxB,OAAO,aAAa,WAAoB,IAAa;AAAA,IACvD,cAAc,CAAC,OAAO,UACpB,OAAO,aAAa,OAAgB,KAAc;AAAA,IACpD,mBAAmB,CAAC,OAAO,UACzB,OAAO,kBAAkB,OAAgB,KAAc;AAAA,IACzD,SAAS,CAAC,cAAc,OAAO,QAAQ,SAAS;AAAA,IAChD,cAAc,OAAO,OAAO,OAAO,gBAAgB;AACjD,YAAM,KAAK,OAAO,aAAa,KAAc;AAC7C,YAAM,SAAS,OAAO,cAAc,GAAG,gBAAgB,WAAW,IAAI;AACtE,UAAI,OAAO,QAAS,QAAO;AAC3B,aAAO,EAAE,MAAM,iBAAiB,KAAK,EAAE,OAAO,IAAI,EAAE;AAAA,IACtD;AAAA,IACA,mBAAmB,OAAO,OAAO,OAAO,gBAAgB;AACtD,YAAM,KAAK,OAAO,kBAAkB,KAAc;AAClD,YAAM,SAAS,OAAO,cAAc,GAAG,gBAAgB,WAAW,IAAI;AACtE,UAAI,OAAO,QAAS,QAAO;AAC3B,YAAM,cAAc,iBAAiB,KAAK;AAC1C,aAAO;AAAA,QACL,MAAM,OAAO,KAAK;AAAA,UAAI,CAAC,QACrB,YAAY,GAA8B;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAAA,IACA,aAAa,CAAC,YAAY,SAAS,OAAO,YAAY,YAAY,IAAI;AAAA,IACtE,aAAa,CAAC,aAAa,OAAO,YAAY,QAAQ;AAAA,IACtD,kBAAkB,MAAM,OAAO,iBAAiB;AAAA,EAClD;AACF;AAuBA,eAAsB,aAEpB,QAGoC;AACpC,QAAM,SAAS,MAAM,WAAW;AAAA,IAC9B,SAAS,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQhB,QAAQ,EAAE,GAAG,OAAO,QAAQ,YAAY,OAAO,QAAQ,cAAc,EAAE;AAAA,EACzE,CAAC;AACD,SAAO,YAAY,QAAQ,GAAG,OAAO,OAAO;AAC9C;","names":[]}
1
+ {"version":3,"sources":["../../src/encryption/v3.ts"],"sourcesContent":["import type { Result } from '@byteslice/result'\nimport type {\n AnyV3Table,\n ColumnsOf,\n PlaintextForColumn,\n QueryableColumnsOf,\n QueryTypesForColumn,\n V3DecryptedModel,\n V3EncryptedModel,\n V3ModelInput,\n} from '@/eql/v3'\nimport { DATE_LIKE_CASTS } from '@/eql/v3/columns'\nimport { type EncryptionError, EncryptionErrorTypes } from '@/errors'\nimport type { LockContextInput } from '@/identity'\nimport type {\n BulkDecryptPayload,\n BulkEncryptPayload,\n ClientConfig,\n Encrypted,\n EncryptedReturnType,\n EncryptOptions,\n ScalarQueryTerm,\n} from '@/types'\nimport {\n type BatchEncryptQueryOperation,\n type BulkDecryptOperation,\n type BulkEncryptModelsOperation,\n type BulkEncryptOperation,\n type DecryptOperation,\n Encryption,\n type EncryptionClient,\n type EncryptModelOperation,\n type EncryptOperation,\n type EncryptQueryOperation,\n} from './index'\n\n/**\n * A strongly-typed view over an {@link EncryptionClient} for EQL v3 schemas.\n *\n * Every method derives its types from the concrete `table` / `column` builder\n * arguments (which carry their branded types at the call site), so:\n * - `encrypt` / `encryptQuery` pin the plaintext to the column's domain type\n * (`text → string`, `timestamp → Date`, …);\n * - `encryptQuery` additionally constrains `queryType` to the column's\n * capabilities and rejects storage-only columns outright;\n * - `encryptModel` / `bulkEncryptModels` validate schema-column fields against\n * their inferred plaintext type (passthrough fields are untouched) and return\n * a precise encrypted model;\n * - `decryptModel` / `bulkDecryptModels` return the precise plaintext model,\n * reconstructing `Date` values from the encrypt-config `cast_as`.\n *\n * @typeParam S - the tuple of registered v3 tables; `table` arguments must be a\n * member of this tuple.\n */\nexport interface TypedEncryptionClient<S extends readonly AnyV3Table[]> {\n encrypt<Table extends S[number], Col extends ColumnsOf<Table>>(\n plaintext: PlaintextForColumn<Col>,\n opts: { table: Table; column: Col },\n ): EncryptOperation\n\n encryptQuery<\n Table extends S[number],\n Col extends QueryableColumnsOf<Table>,\n QT extends QueryTypesForColumn<Col> = QueryTypesForColumn<Col>,\n >(\n plaintext: PlaintextForColumn<Col>,\n opts: {\n table: Table\n column: Col\n queryType?: QT\n returnType?: EncryptedReturnType\n },\n ): EncryptQueryOperation\n\n /**\n * Batch form: encrypt many query terms in one crossing. Mirrors the nominal\n * {@link EncryptionClient} overload — the per-term columns are heterogeneous,\n * so the terms are the base {@link ScalarQueryTerm} rather than a per-column\n * narrowed type. Consumed by the Drizzle `inArray`/`notInArray` operators.\n */\n encryptQuery(terms: readonly ScalarQueryTerm[]): BatchEncryptQueryOperation\n\n encryptModel<Table extends S[number], T extends Record<string, unknown>>(\n input: V3ModelInput<Table, T>,\n table: Table,\n ): EncryptModelOperation<V3EncryptedModel<Table, T>>\n\n bulkEncryptModels<Table extends S[number], T extends Record<string, unknown>>(\n input: Array<V3ModelInput<Table, T>>,\n table: Table,\n ): BulkEncryptModelsOperation<V3EncryptedModel<Table, T>>\n\n /**\n * Decrypt a single value. Cannot be strongly typed — a lone ciphertext carries\n * no column identity — so it resolves to the FFI plaintext union unchanged.\n */\n decrypt(encrypted: Encrypted): DecryptOperation\n\n /**\n * Decrypt a model, returning the precise plaintext shape for `table`. `Date`\n * columns are reconstructed from the encrypt-config `cast_as`.\n *\n * Pass `lockContext` to decrypt identity-bound data — the same context that\n * was supplied at encrypt time must be provided here.\n *\n * Unlike the encrypt operations this returns a plain `Promise<Result<…>>`\n * rather than a chainable operation, because it maps the resolved value.\n */\n decryptModel<Table extends S[number], T extends Record<string, unknown>>(\n input: T,\n table: Table,\n lockContext?: LockContextInput,\n ): Promise<Result<V3DecryptedModel<Table, T>, EncryptionError>>\n\n bulkDecryptModels<Table extends S[number], T extends Record<string, unknown>>(\n input: Array<T>,\n table: Table,\n lockContext?: LockContextInput,\n ): Promise<Result<Array<V3DecryptedModel<Table, T>>, EncryptionError>>\n\n // Parity passthroughs — not v3-strengthened, delegated as-is.\n bulkEncrypt(\n plaintexts: BulkEncryptPayload,\n opts: EncryptOptions,\n ): BulkEncryptOperation\n bulkDecrypt(payloads: BulkDecryptPayload): BulkDecryptOperation\n getEncryptConfig(): ReturnType<EncryptionClient['getEncryptConfig']>\n}\n\n/**\n * Build a per-row reconstructor of `Date` values from the table's\n * encrypt-config `cast_as`. The FFI returns `JsPlaintext`\n * (string/number/boolean/…) with no `Date`, so those columns arrive as their\n * serialized form and are rebuilt here. Safe (idempotent) if the FFI ever\n * returns `Date` directly: `new Date(date)` is a no-op.\n *\n * A factory rather than a `(row, table)` function so the table config —\n * row-invariant, but non-trivial to build — is derived once per call site,\n * not once per row on the bulk path.\n *\n * NOTE: only date-like casts need per-row reconstruction. `bigint` (int8)\n * needs none — protect-ffi 0.28 returns a native JS `bigint` on decrypt\n * (and bounds-checks/encodes it on encrypt), so those columns pass through\n * unchanged, exactly like `string`/`number`/`boolean`.\n */\nfunction rowReconstructor(\n table: AnyV3Table,\n): (row: Record<string, unknown>) => Record<string, unknown> {\n // The decrypted row is keyed by JS property name, but `cast_as` lives on the\n // config keyed by DB name — bridge the two via the table's property→DB map.\n const { columns } = table.build()\n const propToDb = table.buildColumnKeyMap()\n // Only date-like columns need per-row work; resolve them up front.\n const dateProperties = Object.entries(propToDb)\n .filter(([, dbName]) => {\n const castAs = columns[dbName]?.cast_as\n // Date-like casts share one source of truth with the type-level\n // reconstruction (`PlaintextFromKind`) — see `DATE_LIKE_CASTS`.\n return (DATE_LIKE_CASTS as readonly string[]).includes(castAs as string)\n })\n .map(([property]) => property)\n\n return (row) => {\n const out: Record<string, unknown> = { ...row }\n for (const property of dateProperties) {\n const value = out[property]\n if (value == null) continue\n out[property] = new Date(value as string | number | Date)\n }\n return out\n }\n}\n\n/**\n * Wrap an already-built {@link EncryptionClient} in a {@link TypedEncryptionClient}\n * for the given v3 schemas. Zero runtime cost for the encrypt/query paths (the\n * underlying operations are returned unchanged); the decrypt-model paths add a\n * per-column `Date` reconstruction step.\n *\n * The `schemas` are captured with a `const` type parameter so array-literal\n * widening does not collapse per-table inference.\n */\nexport function typedClient<const S extends readonly AnyV3Table[]>(\n client: EncryptionClient,\n ...schemas: S\n): TypedEncryptionClient<S> {\n // Precompute one row reconstructor per schema table at construction. This runs\n // each table's `build()` — which throws on duplicate DB column names — ONCE,\n // here, off the Result-returning decrypt path. `decryptModel`/\n // `bulkDecryptModels` therefore never call `build()` (whose throw would surface\n // as a promise rejection and break their `Promise<Result<…>>` contract) and no\n // longer rebuild the row-invariant config on every call.\n // Keyed by `tableName`, not table object identity: `AnyV3Table` is\n // structurally typed, so a table re-imported from another module (or rebuilt\n // after an HMR reload) satisfies `Table extends S[number]` yet is a different\n // object. Identity keying would fail those valid calls. `tableName` is the\n // semantic identity the FFI encrypt config and `build()` already key on.\n const reconstructors = new Map<\n string,\n (row: Record<string, unknown>) => Record<string, unknown>\n >()\n for (const table of schemas) {\n reconstructors.set(table.tableName, rowReconstructor(table))\n }\n\n // A table not among the schemas this client was initialized with has no\n // precomputed reconstructor. Return a Result failure rather than building one\n // inline, which could throw and reject the Result-shaped decrypt promise.\n const unknownTableFailure: { failure: EncryptionError } = {\n failure: {\n type: EncryptionErrorTypes.DecryptionError,\n message:\n '[eql/v3]: decryptModel received a table this client was not initialized with — pass a table given to EncryptionV3/typedClient',\n },\n }\n\n // Overloaded so the implementation is checked against BOTH forms directly —\n // no whole-value cast. The two public signatures mirror the interface member;\n // the hidden implementation signature is broad and forwards to the nominal\n // client (which routes to the batch operation when no `opts` are supplied).\n // Only the forwarded args are `as never`, exactly as the sibling wrappers\n // below: one forwarding body cannot re-derive the nominal client's per-column\n // signatures.\n function encryptQuery<\n Table extends S[number],\n Col extends QueryableColumnsOf<Table>,\n QT extends QueryTypesForColumn<Col> = QueryTypesForColumn<Col>,\n >(\n plaintext: PlaintextForColumn<Col>,\n opts: {\n table: Table\n column: Col\n queryType?: QT\n returnType?: EncryptedReturnType\n },\n ): EncryptQueryOperation\n function encryptQuery(\n terms: readonly ScalarQueryTerm[],\n ): BatchEncryptQueryOperation\n function encryptQuery(\n plaintextOrTerms: unknown,\n opts?: unknown,\n ): EncryptQueryOperation | BatchEncryptQueryOperation {\n return client.encryptQuery(plaintextOrTerms as never, opts as never)\n }\n\n return {\n encrypt: (plaintext, opts) =>\n client.encrypt(plaintext as never, opts as never),\n encryptQuery,\n encryptModel: (input, table) =>\n client.encryptModel(input as never, table as never) as never,\n bulkEncryptModels: (input, table) =>\n client.bulkEncryptModels(input as never, table as never) as never,\n decrypt: (encrypted) => client.decrypt(encrypted),\n decryptModel: async (input, table, lockContext) => {\n const reconstruct = reconstructors.get(table.tableName)\n if (!reconstruct) return unknownTableFailure as never\n const op = client.decryptModel(input as never)\n const result = await (lockContext ? op.withLockContext(lockContext) : op)\n if (result.failure) return result as never\n return { data: reconstruct(result.data) } as never\n },\n bulkDecryptModels: async (input, table, lockContext) => {\n const reconstruct = reconstructors.get(table.tableName)\n if (!reconstruct) return unknownTableFailure as never\n const op = client.bulkDecryptModels(input as never)\n const result = await (lockContext ? op.withLockContext(lockContext) : op)\n if (result.failure) return result as never\n return {\n data: result.data.map((row) =>\n reconstruct(row as Record<string, unknown>),\n ),\n } as never\n },\n bulkEncrypt: (plaintexts, opts) => client.bulkEncrypt(plaintexts, opts),\n bulkDecrypt: (payloads) => client.bulkDecrypt(payloads),\n getEncryptConfig: () => client.getEncryptConfig(),\n } satisfies TypedEncryptionClient<S>\n}\n\n/**\n * Build a {@link TypedEncryptionClient} for EQL v3 schemas — the strongly-typed\n * counterpart to {@link Encryption}. Mirrors its config, then retypes the client\n * against the provided v3 `schemas`.\n *\n * @example\n * ```typescript\n * import { EncryptionV3, encryptedTable, types } from \"@cipherstash/stack/v3\"\n *\n * const users = encryptedTable(\"users\", { email: types.TextSearch(\"email\") })\n * const client = await EncryptionV3({ schemas: [users] })\n *\n * await client.encrypt(\"a@b.com\", { table: users, column: users.email })\n * ```\n */\nexport async function EncryptionV3<\n const S extends readonly AnyV3Table[],\n>(config: {\n schemas: S\n config?: ClientConfig\n}): Promise<TypedEncryptionClient<S>> {\n const client = await Encryption({\n schemas: config.schemas as unknown as Parameters<\n typeof Encryption\n >[0]['schemas'],\n // Force the v3 EQL wire format. protect-ffi's newClient defaults to\n // eqlVersion 2; a v2-mode client cannot resolve v3 concrete-type columns\n // and fails every encrypt with \"Cannot convert undefined or null to\n // object\". This is a v3-only invariant, so it overrides any user value.\n config: { ...config.config, eqlVersion: 3 },\n })\n return typedClient(client, ...config.schemas)\n}\n\n// Single import surface: re-export the v3 `types` namespace + table API + type\n// helpers so `@cipherstash/stack/v3` provides everything needed to author and\n// use a schema.\nexport * from '@/eql/v3'\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiJA,SAAS,iBACP,OAC2D;AAG3D,QAAM,EAAE,QAAQ,IAAI,MAAM,MAAM;AAChC,QAAM,WAAW,MAAM,kBAAkB;AAEzC,QAAM,iBAAiB,OAAO,QAAQ,QAAQ,EAC3C,OAAO,CAAC,CAAC,EAAE,MAAM,MAAM;AACtB,UAAM,SAAS,QAAQ,MAAM,GAAG;AAGhC,WAAQ,gBAAsC,SAAS,MAAgB;AAAA,EACzE,CAAC,EACA,IAAI,CAAC,CAAC,QAAQ,MAAM,QAAQ;AAE/B,SAAO,CAAC,QAAQ;AACd,UAAM,MAA+B,EAAE,GAAG,IAAI;AAC9C,eAAW,YAAY,gBAAgB;AACrC,YAAM,QAAQ,IAAI,QAAQ;AAC1B,UAAI,SAAS,KAAM;AACnB,UAAI,QAAQ,IAAI,IAAI,KAAK,KAA+B;AAAA,IAC1D;AACA,WAAO;AAAA,EACT;AACF;AAWO,SAAS,YACd,WACG,SACuB;AAY1B,QAAM,iBAAiB,oBAAI,IAGzB;AACF,aAAW,SAAS,SAAS;AAC3B,mBAAe,IAAI,MAAM,WAAW,iBAAiB,KAAK,CAAC;AAAA,EAC7D;AAKA,QAAM,sBAAoD;AAAA,IACxD,SAAS;AAAA,MACP,MAAM,qBAAqB;AAAA,MAC3B,SACE;AAAA,IACJ;AAAA,EACF;AAyBA,WAAS,aACP,kBACA,MACoD;AACpD,WAAO,OAAO,aAAa,kBAA2B,IAAa;AAAA,EACrE;AAEA,SAAO;AAAA,IACL,SAAS,CAAC,WAAW,SACnB,OAAO,QAAQ,WAAoB,IAAa;AAAA,IAClD;AAAA,IACA,cAAc,CAAC,OAAO,UACpB,OAAO,aAAa,OAAgB,KAAc;AAAA,IACpD,mBAAmB,CAAC,OAAO,UACzB,OAAO,kBAAkB,OAAgB,KAAc;AAAA,IACzD,SAAS,CAAC,cAAc,OAAO,QAAQ,SAAS;AAAA,IAChD,cAAc,OAAO,OAAO,OAAO,gBAAgB;AACjD,YAAM,cAAc,eAAe,IAAI,MAAM,SAAS;AACtD,UAAI,CAAC,YAAa,QAAO;AACzB,YAAM,KAAK,OAAO,aAAa,KAAc;AAC7C,YAAM,SAAS,OAAO,cAAc,GAAG,gBAAgB,WAAW,IAAI;AACtE,UAAI,OAAO,QAAS,QAAO;AAC3B,aAAO,EAAE,MAAM,YAAY,OAAO,IAAI,EAAE;AAAA,IAC1C;AAAA,IACA,mBAAmB,OAAO,OAAO,OAAO,gBAAgB;AACtD,YAAM,cAAc,eAAe,IAAI,MAAM,SAAS;AACtD,UAAI,CAAC,YAAa,QAAO;AACzB,YAAM,KAAK,OAAO,kBAAkB,KAAc;AAClD,YAAM,SAAS,OAAO,cAAc,GAAG,gBAAgB,WAAW,IAAI;AACtE,UAAI,OAAO,QAAS,QAAO;AAC3B,aAAO;AAAA,QACL,MAAM,OAAO,KAAK;AAAA,UAAI,CAAC,QACrB,YAAY,GAA8B;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAAA,IACA,aAAa,CAAC,YAAY,SAAS,OAAO,YAAY,YAAY,IAAI;AAAA,IACtE,aAAa,CAAC,aAAa,OAAO,YAAY,QAAQ;AAAA,IACtD,kBAAkB,MAAM,OAAO,iBAAiB;AAAA,EAClD;AACF;AAiBA,eAAsB,aAEpB,QAGoC;AACpC,QAAM,SAAS,MAAM,WAAW;AAAA,IAC9B,SAAS,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,IAOhB,QAAQ,EAAE,GAAG,OAAO,QAAQ,YAAY,EAAE;AAAA,EAC5C,CAAC;AACD,SAAO,YAAY,QAAQ,GAAG,OAAO,OAAO;AAC9C;","names":[]}