@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
@@ -30,6 +30,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/encryption/v3.ts
31
31
  var v3_exports = {};
32
32
  __export(v3_exports, {
33
+ EncryptedBigintColumn: () => EncryptedBigintColumn,
34
+ EncryptedBigintEqColumn: () => EncryptedBigintEqColumn,
35
+ EncryptedBigintOrdColumn: () => EncryptedBigintOrdColumn,
36
+ EncryptedBigintOrdOreColumn: () => EncryptedBigintOrdOreColumn,
33
37
  EncryptedBooleanColumn: () => EncryptedBooleanColumn,
34
38
  EncryptedDateColumn: () => EncryptedDateColumn,
35
39
  EncryptedDateEqColumn: () => EncryptedDateEqColumn,
@@ -43,6 +47,7 @@ __export(v3_exports, {
43
47
  EncryptedIntegerEqColumn: () => EncryptedIntegerEqColumn,
44
48
  EncryptedIntegerOrdColumn: () => EncryptedIntegerOrdColumn,
45
49
  EncryptedIntegerOrdOreColumn: () => EncryptedIntegerOrdOreColumn,
50
+ EncryptedJsonColumn: () => EncryptedJsonColumn,
46
51
  EncryptedNumericColumn: () => EncryptedNumericColumn,
47
52
  EncryptedNumericEqColumn: () => EncryptedNumericEqColumn,
48
53
  EncryptedNumericOrdColumn: () => EncryptedNumericOrdColumn,
@@ -75,6 +80,408 @@ __export(v3_exports, {
75
80
  });
76
81
  module.exports = __toCommonJS(v3_exports);
77
82
 
83
+ // src/schema/match-defaults.ts
84
+ function defaultMatchOpts() {
85
+ return {
86
+ tokenizer: { kind: "ngram", token_length: 3 },
87
+ token_filters: [{ kind: "downcase" }],
88
+ k: 6,
89
+ m: 2048,
90
+ include_original: true
91
+ };
92
+ }
93
+
94
+ // src/eql/v3/columns.ts
95
+ var DATE_LIKE_CASTS = ["date", "timestamp"];
96
+ var STORAGE_ONLY = {
97
+ equality: false,
98
+ orderAndRange: false,
99
+ freeTextSearch: false
100
+ };
101
+ var EQUALITY_ONLY = {
102
+ equality: true,
103
+ orderAndRange: false,
104
+ freeTextSearch: false
105
+ };
106
+ var ORDER_AND_RANGE = {
107
+ equality: true,
108
+ orderAndRange: true,
109
+ freeTextSearch: false
110
+ };
111
+ var MATCH_ONLY = {
112
+ equality: false,
113
+ orderAndRange: false,
114
+ freeTextSearch: true
115
+ };
116
+ var TEXT_SEARCH = {
117
+ equality: true,
118
+ orderAndRange: true,
119
+ freeTextSearch: true
120
+ };
121
+ var TEXT_SEARCH_EQL_TYPE = "public.eql_v3_text_search";
122
+ var INTEGER = {
123
+ eqlType: "public.eql_v3_integer",
124
+ castAs: "number",
125
+ capabilities: STORAGE_ONLY
126
+ };
127
+ var INTEGER_EQ = {
128
+ eqlType: "public.eql_v3_integer_eq",
129
+ castAs: "number",
130
+ capabilities: EQUALITY_ONLY
131
+ };
132
+ var INTEGER_ORD_ORE = {
133
+ eqlType: "public.eql_v3_integer_ord_ore",
134
+ castAs: "number",
135
+ capabilities: ORDER_AND_RANGE
136
+ };
137
+ var INTEGER_ORD = {
138
+ eqlType: "public.eql_v3_integer_ord",
139
+ castAs: "number",
140
+ capabilities: ORDER_AND_RANGE
141
+ };
142
+ var SMALLINT = {
143
+ eqlType: "public.eql_v3_smallint",
144
+ castAs: "number",
145
+ capabilities: STORAGE_ONLY
146
+ };
147
+ var SMALLINT_EQ = {
148
+ eqlType: "public.eql_v3_smallint_eq",
149
+ castAs: "number",
150
+ capabilities: EQUALITY_ONLY
151
+ };
152
+ var SMALLINT_ORD_ORE = {
153
+ eqlType: "public.eql_v3_smallint_ord_ore",
154
+ castAs: "number",
155
+ capabilities: ORDER_AND_RANGE
156
+ };
157
+ var SMALLINT_ORD = {
158
+ eqlType: "public.eql_v3_smallint_ord",
159
+ castAs: "number",
160
+ capabilities: ORDER_AND_RANGE
161
+ };
162
+ var BIGINT = {
163
+ eqlType: "public.eql_v3_bigint",
164
+ castAs: "bigint",
165
+ capabilities: STORAGE_ONLY
166
+ };
167
+ var BIGINT_EQ = {
168
+ eqlType: "public.eql_v3_bigint_eq",
169
+ castAs: "bigint",
170
+ capabilities: EQUALITY_ONLY
171
+ };
172
+ var BIGINT_ORD_ORE = {
173
+ eqlType: "public.eql_v3_bigint_ord_ore",
174
+ castAs: "bigint",
175
+ capabilities: ORDER_AND_RANGE
176
+ };
177
+ var BIGINT_ORD = {
178
+ eqlType: "public.eql_v3_bigint_ord",
179
+ castAs: "bigint",
180
+ capabilities: ORDER_AND_RANGE
181
+ };
182
+ var DATE = {
183
+ eqlType: "public.eql_v3_date",
184
+ castAs: "date",
185
+ capabilities: STORAGE_ONLY
186
+ };
187
+ var DATE_EQ = {
188
+ eqlType: "public.eql_v3_date_eq",
189
+ castAs: "date",
190
+ capabilities: EQUALITY_ONLY
191
+ };
192
+ var DATE_ORD_ORE = {
193
+ eqlType: "public.eql_v3_date_ord_ore",
194
+ castAs: "date",
195
+ capabilities: ORDER_AND_RANGE
196
+ };
197
+ var DATE_ORD = {
198
+ eqlType: "public.eql_v3_date_ord",
199
+ castAs: "date",
200
+ capabilities: ORDER_AND_RANGE
201
+ };
202
+ var TIMESTAMP = {
203
+ eqlType: "public.eql_v3_timestamp",
204
+ castAs: "timestamp",
205
+ capabilities: STORAGE_ONLY
206
+ };
207
+ var TIMESTAMP_EQ = {
208
+ eqlType: "public.eql_v3_timestamp_eq",
209
+ castAs: "timestamp",
210
+ capabilities: EQUALITY_ONLY
211
+ };
212
+ var TIMESTAMP_ORD_ORE = {
213
+ eqlType: "public.eql_v3_timestamp_ord_ore",
214
+ castAs: "timestamp",
215
+ capabilities: ORDER_AND_RANGE
216
+ };
217
+ var TIMESTAMP_ORD = {
218
+ eqlType: "public.eql_v3_timestamp_ord",
219
+ castAs: "timestamp",
220
+ capabilities: ORDER_AND_RANGE
221
+ };
222
+ var NUMERIC = {
223
+ eqlType: "public.eql_v3_numeric",
224
+ castAs: "number",
225
+ capabilities: STORAGE_ONLY
226
+ };
227
+ var NUMERIC_EQ = {
228
+ eqlType: "public.eql_v3_numeric_eq",
229
+ castAs: "number",
230
+ capabilities: EQUALITY_ONLY
231
+ };
232
+ var NUMERIC_ORD_ORE = {
233
+ eqlType: "public.eql_v3_numeric_ord_ore",
234
+ castAs: "number",
235
+ capabilities: ORDER_AND_RANGE
236
+ };
237
+ var NUMERIC_ORD = {
238
+ eqlType: "public.eql_v3_numeric_ord",
239
+ castAs: "number",
240
+ capabilities: ORDER_AND_RANGE
241
+ };
242
+ var TEXT = {
243
+ eqlType: "public.eql_v3_text",
244
+ castAs: "string",
245
+ capabilities: STORAGE_ONLY
246
+ };
247
+ var TEXT_EQ = {
248
+ eqlType: "public.eql_v3_text_eq",
249
+ castAs: "string",
250
+ capabilities: EQUALITY_ONLY
251
+ };
252
+ var TEXT_MATCH = {
253
+ eqlType: "public.eql_v3_text_match",
254
+ castAs: "string",
255
+ capabilities: MATCH_ONLY
256
+ };
257
+ var TEXT_ORD_ORE = {
258
+ eqlType: "public.eql_v3_text_ord_ore",
259
+ castAs: "string",
260
+ capabilities: ORDER_AND_RANGE
261
+ };
262
+ var TEXT_ORD = {
263
+ eqlType: "public.eql_v3_text_ord",
264
+ castAs: "string",
265
+ capabilities: ORDER_AND_RANGE
266
+ };
267
+ var BOOLEAN = {
268
+ eqlType: "public.eql_v3_boolean",
269
+ castAs: "boolean",
270
+ capabilities: STORAGE_ONLY
271
+ };
272
+ var REAL = {
273
+ eqlType: "public.eql_v3_real",
274
+ castAs: "number",
275
+ capabilities: STORAGE_ONLY
276
+ };
277
+ var REAL_EQ = {
278
+ eqlType: "public.eql_v3_real_eq",
279
+ castAs: "number",
280
+ capabilities: EQUALITY_ONLY
281
+ };
282
+ var REAL_ORD_ORE = {
283
+ eqlType: "public.eql_v3_real_ord_ore",
284
+ castAs: "number",
285
+ capabilities: ORDER_AND_RANGE
286
+ };
287
+ var REAL_ORD = {
288
+ eqlType: "public.eql_v3_real_ord",
289
+ castAs: "number",
290
+ capabilities: ORDER_AND_RANGE
291
+ };
292
+ var DOUBLE = {
293
+ eqlType: "public.eql_v3_double",
294
+ castAs: "number",
295
+ capabilities: STORAGE_ONLY
296
+ };
297
+ var DOUBLE_EQ = {
298
+ eqlType: "public.eql_v3_double_eq",
299
+ castAs: "number",
300
+ capabilities: EQUALITY_ONLY
301
+ };
302
+ var DOUBLE_ORD_ORE = {
303
+ eqlType: "public.eql_v3_double_ord_ore",
304
+ castAs: "number",
305
+ capabilities: ORDER_AND_RANGE
306
+ };
307
+ var DOUBLE_ORD = {
308
+ eqlType: "public.eql_v3_double_ord",
309
+ castAs: "number",
310
+ capabilities: ORDER_AND_RANGE
311
+ };
312
+ function indexesForCapabilities(capabilities, castAs, ordering) {
313
+ const indexes = {};
314
+ if (capabilities.equality && (!capabilities.orderAndRange || castAs === "string")) {
315
+ indexes.unique = { token_filters: [] };
316
+ }
317
+ if (capabilities.orderAndRange) {
318
+ indexes[ordering] = {};
319
+ }
320
+ if (capabilities.freeTextSearch) {
321
+ indexes.match = { ...defaultMatchOpts(), include_original: false };
322
+ }
323
+ if (capabilities.searchableJson) {
324
+ indexes.ste_vec = {
325
+ prefix: "enabled",
326
+ array_index_mode: { item: true, wildcard: true, position: false },
327
+ mode: "compat"
328
+ };
329
+ }
330
+ return indexes;
331
+ }
332
+ function orderingForEqlType(eqlType) {
333
+ return eqlType.endsWith("_ord_ore") ? "ore" : "ope";
334
+ }
335
+ function isQueryableCapabilities(capabilities) {
336
+ return capabilities.equality || capabilities.orderAndRange || capabilities.freeTextSearch || (capabilities.searchableJson ?? false);
337
+ }
338
+ var EncryptedV3Column = class {
339
+ constructor(columnName, definition) {
340
+ this.columnName = columnName;
341
+ this.definition = definition;
342
+ }
343
+ columnName;
344
+ definition;
345
+ getName() {
346
+ return this.columnName;
347
+ }
348
+ /** The concrete EQL v3 domain name. Metadata only; not emitted by `build()`. */
349
+ getEqlType() {
350
+ return this.definition.eqlType;
351
+ }
352
+ /** The semantic query capabilities this domain exposes. Metadata only. */
353
+ getQueryCapabilities() {
354
+ return this.definition.capabilities;
355
+ }
356
+ /** `true` when this domain can produce at least one kind of query term. */
357
+ isQueryable() {
358
+ return isQueryableCapabilities(
359
+ this.definition.capabilities
360
+ );
361
+ }
362
+ /** Emit the encrypt-config column: `cast_as` plus capability-derived indexes. */
363
+ build() {
364
+ return {
365
+ cast_as: this.definition.castAs,
366
+ indexes: indexesForCapabilities(
367
+ this.definition.capabilities,
368
+ this.definition.castAs,
369
+ orderingForEqlType(this.definition.eqlType)
370
+ )
371
+ };
372
+ }
373
+ };
374
+ var TEXT_SEARCH_DOMAIN = {
375
+ eqlType: TEXT_SEARCH_EQL_TYPE,
376
+ castAs: "string",
377
+ capabilities: TEXT_SEARCH
378
+ };
379
+ var EncryptedTextSearchColumn = class extends EncryptedV3Column {
380
+ constructor(columnName) {
381
+ super(columnName, TEXT_SEARCH_DOMAIN);
382
+ }
383
+ };
384
+ var EncryptedIntegerColumn = class extends EncryptedV3Column {
385
+ };
386
+ var EncryptedIntegerEqColumn = class extends EncryptedV3Column {
387
+ };
388
+ var EncryptedIntegerOrdOreColumn = class extends EncryptedV3Column {
389
+ };
390
+ var EncryptedIntegerOrdColumn = class extends EncryptedV3Column {
391
+ };
392
+ var EncryptedSmallintColumn = class extends EncryptedV3Column {
393
+ };
394
+ var EncryptedSmallintEqColumn = class extends EncryptedV3Column {
395
+ };
396
+ var EncryptedSmallintOrdOreColumn = class extends EncryptedV3Column {
397
+ };
398
+ var EncryptedSmallintOrdColumn = class extends EncryptedV3Column {
399
+ };
400
+ var EncryptedBigintColumn = class extends EncryptedV3Column {
401
+ };
402
+ var EncryptedBigintEqColumn = class extends EncryptedV3Column {
403
+ };
404
+ var EncryptedBigintOrdOreColumn = class extends EncryptedV3Column {
405
+ };
406
+ var EncryptedBigintOrdColumn = class extends EncryptedV3Column {
407
+ };
408
+ var EncryptedDateColumn = class extends EncryptedV3Column {
409
+ };
410
+ var EncryptedDateEqColumn = class extends EncryptedV3Column {
411
+ };
412
+ var EncryptedDateOrdOreColumn = class extends EncryptedV3Column {
413
+ };
414
+ var EncryptedDateOrdColumn = class extends EncryptedV3Column {
415
+ };
416
+ var EncryptedTimestampColumn = class extends EncryptedV3Column {
417
+ };
418
+ var EncryptedTimestampEqColumn = class extends EncryptedV3Column {
419
+ };
420
+ var EncryptedTimestampOrdOreColumn = class extends EncryptedV3Column {
421
+ };
422
+ var EncryptedTimestampOrdColumn = class extends EncryptedV3Column {
423
+ };
424
+ var EncryptedNumericColumn = class extends EncryptedV3Column {
425
+ };
426
+ var EncryptedNumericEqColumn = class extends EncryptedV3Column {
427
+ };
428
+ var EncryptedNumericOrdOreColumn = class extends EncryptedV3Column {
429
+ };
430
+ var EncryptedNumericOrdColumn = class extends EncryptedV3Column {
431
+ };
432
+ var EncryptedTextColumn = class extends EncryptedV3Column {
433
+ };
434
+ var EncryptedTextEqColumn = class extends EncryptedV3Column {
435
+ };
436
+ var EncryptedTextMatchColumn = class extends EncryptedV3Column {
437
+ };
438
+ var EncryptedTextOrdOreColumn = class extends EncryptedV3Column {
439
+ };
440
+ var EncryptedTextOrdColumn = class extends EncryptedV3Column {
441
+ };
442
+ var EncryptedBooleanColumn = class extends EncryptedV3Column {
443
+ };
444
+ var EncryptedRealColumn = class extends EncryptedV3Column {
445
+ };
446
+ var EncryptedRealEqColumn = class extends EncryptedV3Column {
447
+ };
448
+ var EncryptedRealOrdOreColumn = class extends EncryptedV3Column {
449
+ };
450
+ var EncryptedRealOrdColumn = class extends EncryptedV3Column {
451
+ };
452
+ var EncryptedDoubleColumn = class extends EncryptedV3Column {
453
+ };
454
+ var EncryptedDoubleEqColumn = class extends EncryptedV3Column {
455
+ };
456
+ var EncryptedDoubleOrdOreColumn = class extends EncryptedV3Column {
457
+ };
458
+ var EncryptedDoubleOrdColumn = class extends EncryptedV3Column {
459
+ };
460
+ var JSON_DOMAIN = {
461
+ eqlType: "public.eql_v3_json",
462
+ castAs: "json",
463
+ capabilities: {
464
+ equality: false,
465
+ orderAndRange: false,
466
+ freeTextSearch: false,
467
+ searchableJson: true
468
+ }
469
+ };
470
+ var EncryptedJsonColumn = class extends EncryptedV3Column {
471
+ constructor(columnName) {
472
+ super(columnName, JSON_DOMAIN);
473
+ }
474
+ };
475
+
476
+ // src/errors/index.ts
477
+ var EncryptionErrorTypes = {
478
+ ClientInitError: "ClientInitError",
479
+ EncryptionError: "EncryptionError",
480
+ DecryptionError: "DecryptionError",
481
+ LockContextError: "LockContextError",
482
+ CtsTokenError: "CtsTokenError"
483
+ };
484
+
78
485
  // ../../node_modules/.pnpm/@byteslice+result@0.2.0/node_modules/@byteslice/result/dist/result.mjs
79
486
  function ensureError(ex) {
80
487
  return ex instanceof Error ? ex : new Error("Something went wrong");
@@ -100,15 +507,6 @@ function validate(uuid) {
100
507
  }
101
508
  var validate_default = validate;
102
509
 
103
- // src/errors/index.ts
104
- var EncryptionErrorTypes = {
105
- ClientInitError: "ClientInitError",
106
- EncryptionError: "EncryptionError",
107
- DecryptionError: "DecryptionError",
108
- LockContextError: "LockContextError",
109
- CtsTokenError: "CtsTokenError"
110
- };
111
-
112
510
  // ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
113
511
  var external_exports = {};
114
512
  __export(external_exports, {
@@ -4150,24 +4548,6 @@ var coerce = {
4150
4548
  };
4151
4549
  var NEVER = INVALID;
4152
4550
 
4153
- // src/schema/match-defaults.ts
4154
- function defaultMatchOpts() {
4155
- return {
4156
- tokenizer: { kind: "ngram", token_length: 3 },
4157
- token_filters: [{ kind: "downcase" }],
4158
- k: 6,
4159
- m: 2048,
4160
- include_original: true
4161
- };
4162
- }
4163
- function cloneMatchOpts(opts) {
4164
- return {
4165
- ...opts,
4166
- tokenizer: { ...opts.tokenizer },
4167
- token_filters: opts.token_filters.map((f) => ({ ...f }))
4168
- };
4169
- }
4170
-
4171
4551
  // src/schema/index.ts
4172
4552
  var eqlCastAsEnum = external_exports.enum([
4173
4553
  "text",
@@ -4204,6 +4584,7 @@ var tokenizerSchema = external_exports.union([
4204
4584
  })
4205
4585
  ]).default({ kind: "ngram", token_length: 3 }).optional();
4206
4586
  var oreIndexOptsSchema = external_exports.object({});
4587
+ var opeIndexOptsSchema = external_exports.object({});
4207
4588
  var uniqueIndexOptsSchema = external_exports.object({
4208
4589
  token_filters: external_exports.array(tokenFilterSchema).default([]).optional()
4209
4590
  });
@@ -4225,10 +4606,12 @@ var arrayIndexModeSchema = external_exports.union([
4225
4606
  ]);
4226
4607
  var steVecIndexOptsSchema = external_exports.object({
4227
4608
  prefix: external_exports.string(),
4228
- array_index_mode: arrayIndexModeSchema.optional()
4609
+ array_index_mode: arrayIndexModeSchema.optional(),
4610
+ mode: external_exports.enum(["compat", "standard"]).optional()
4229
4611
  });
4230
4612
  var indexesSchema = external_exports.object({
4231
4613
  ore: oreIndexOptsSchema.optional(),
4614
+ ope: opeIndexOptsSchema.optional(),
4232
4615
  unique: uniqueIndexOptsSchema.optional(),
4233
4616
  match: matchIndexOptsSchema.optional(),
4234
4617
  ste_vec: steVecIndexOptsSchema.optional()
@@ -4840,6 +5223,8 @@ var LockContext = class {
4840
5223
 
4841
5224
  // src/types.ts
4842
5225
  var queryTypeToFfi = {
5226
+ // v3 `_ord` domains carry `ope` instead — `resolveIndexType` swaps this
5227
+ // static default for the ordering index the column actually configures.
4843
5228
  orderAndRange: "ore",
4844
5229
  freeTextSearch: "match",
4845
5230
  equality: "unique",
@@ -4862,6 +5247,7 @@ function inferIndexType(column) {
4862
5247
  if (indexes.unique) return "unique";
4863
5248
  if (indexes.match) return "match";
4864
5249
  if (indexes.ore) return "ore";
5250
+ if (indexes.ope) return "ope";
4865
5251
  if (indexes.ste_vec) return "ste_vec";
4866
5252
  throw new Error(
4867
5253
  `Column "${column.getName()}" has no suitable index for queries`
@@ -4883,6 +5269,7 @@ function validateIndexType(column, indexType) {
4883
5269
  unique: !!indexes.unique,
4884
5270
  match: !!indexes.match,
4885
5271
  ore: !!indexes.ore,
5272
+ ope: !!indexes.ope,
4886
5273
  ste_vec: !!indexes.ste_vec
4887
5274
  };
4888
5275
  if (!indexMap[indexType]) {
@@ -4891,17 +5278,25 @@ function validateIndexType(column, indexType) {
4891
5278
  );
4892
5279
  }
4893
5280
  }
4894
- function resolvesEqualityViaOre(column) {
4895
- if (!("getQueryCapabilities" in column)) return false;
4896
- if (!column.getQueryCapabilities().equality) return false;
5281
+ function equalityOrderingIndex(column) {
5282
+ if (!("getQueryCapabilities" in column)) return null;
5283
+ if (!column.getQueryCapabilities().equality) return null;
4897
5284
  const indexes = column.build().indexes ?? {};
4898
- return !indexes.unique && !!indexes.ore;
5285
+ if (indexes.unique) return null;
5286
+ if (indexes.ore) return "ore";
5287
+ if (indexes.ope) return "ope";
5288
+ return null;
4899
5289
  }
4900
5290
  function resolveIndexType(column, queryType, plaintext) {
4901
- const indexType = queryType ? queryTypeToFfi[queryType] : inferIndexType(column);
5291
+ let indexType = queryType ? queryTypeToFfi[queryType] : inferIndexType(column);
4902
5292
  if (queryType) {
4903
- if (queryType === "equality" && resolvesEqualityViaOre(column)) {
4904
- return { indexType: "ore" };
5293
+ if (queryType === "equality") {
5294
+ const ordering = equalityOrderingIndex(column);
5295
+ if (ordering) return { indexType: ordering };
5296
+ }
5297
+ if (queryType === "orderAndRange" && indexType === "ore") {
5298
+ const indexes = column.build().indexes ?? {};
5299
+ if (!indexes.ore && indexes.ope) indexType = "ope";
4905
5300
  }
4906
5301
  validateIndexType(column, indexType);
4907
5302
  if (queryType === "searchableJson") {
@@ -4922,6 +5317,11 @@ function resolveIndexType(column, queryType, plaintext) {
4922
5317
  }
4923
5318
 
4924
5319
  // src/encryption/helpers/validation.ts
5320
+ var INT64_MIN = -9223372036854775808n;
5321
+ var INT64_MAX = 9223372036854775807n;
5322
+ function isBigintOutOfInt64Range(value) {
5323
+ return typeof value === "bigint" && (value < INT64_MIN || value > INT64_MAX);
5324
+ }
4925
5325
  function validateNumericValue(value) {
4926
5326
  if (typeof value === "number" && Number.isNaN(value)) {
4927
5327
  return {
@@ -4939,6 +5339,14 @@ function validateNumericValue(value) {
4939
5339
  }
4940
5340
  };
4941
5341
  }
5342
+ if (isBigintOutOfInt64Range(value)) {
5343
+ return {
5344
+ failure: {
5345
+ type: EncryptionErrorTypes.EncryptionError,
5346
+ message: "[encryption]: Cannot encrypt bigint value out of int64 range"
5347
+ }
5348
+ };
5349
+ }
4942
5350
  return void 0;
4943
5351
  }
4944
5352
  function assertValidNumericValue(value) {
@@ -4948,9 +5356,14 @@ function assertValidNumericValue(value) {
4948
5356
  if (typeof value === "number" && !Number.isFinite(value)) {
4949
5357
  throw new Error("[encryption]: Cannot encrypt Infinity value");
4950
5358
  }
5359
+ if (isBigintOutOfInt64Range(value)) {
5360
+ throw new Error(
5361
+ "[encryption]: Cannot encrypt bigint value out of int64 range"
5362
+ );
5363
+ }
4951
5364
  }
4952
5365
  function assertValueIndexCompatibility(value, indexType, columnName) {
4953
- if (typeof value === "number" && indexType === "match") {
5366
+ if ((typeof value === "number" || typeof value === "bigint") && indexType === "match") {
4954
5367
  throw new Error(
4955
5368
  `[encryption]: Cannot use 'match' index with numeric value on column "${columnName}". The 'freeTextSearch' index only supports string values. Configure the column with 'orderAndRange()' or 'equality()' for numeric queries.`
4956
5369
  );
@@ -5383,6 +5796,7 @@ function prepareFieldsForEncryption(model, table) {
5383
5796
  );
5384
5797
  }
5385
5798
  } else if (columnPaths2.includes(fullKey)) {
5799
+ assertValidNumericValue(value);
5386
5800
  const id = index.toString();
5387
5801
  keyMap[id] = fullKey;
5388
5802
  operationFields[fullKey] = value;
@@ -5561,6 +5975,7 @@ function prepareBulkModelsForOperation(models, table) {
5561
5975
  );
5562
5976
  }
5563
5977
  } else if (columnPaths.includes(fullKey)) {
5978
+ assertValidNumericValue(value);
5564
5979
  const id = index.toString();
5565
5980
  keyMap[id] = { modelIndex, fieldKey: fullKey };
5566
5981
  modelOperationFields[fullKey] = value;
@@ -5872,13 +6287,16 @@ var BulkDecryptModelsOperationWithLockContext = class extends EncryptionOperatio
5872
6287
  // src/encryption/operations/bulk-encrypt.ts
5873
6288
  var import_protect_ffi5 = require("@cipherstash/protect-ffi");
5874
6289
  var createEncryptPayloads = (plaintexts, column, table, lockContext) => {
5875
- return plaintexts.filter(({ plaintext }) => plaintext !== null).map(({ id, plaintext }) => ({
5876
- id,
5877
- plaintext,
5878
- column: column.getName(),
5879
- table: table.tableName,
5880
- ...lockContext && { lockContext }
5881
- }));
6290
+ return plaintexts.filter(({ plaintext }) => plaintext !== null).map(({ id, plaintext }) => {
6291
+ assertValidNumericValue(plaintext);
6292
+ return {
6293
+ id,
6294
+ plaintext,
6295
+ column: column.getName(),
6296
+ table: table.tableName,
6297
+ ...lockContext && { lockContext }
6298
+ };
6299
+ });
5882
6300
  };
5883
6301
  var createNullResult2 = (plaintexts) => plaintexts.map(({ id }) => ({ id, data: null }));
5884
6302
  var mapEncryptedDataToResult = (plaintexts, encryptedData) => {
@@ -6602,956 +7020,600 @@ var EncryptQueryOperation = class extends EncryptionOperation {
6602
7020
  this.opts.queryType,
6603
7021
  plaintext
6604
7022
  );
6605
- assertValueIndexCompatibility(
6606
- plaintext,
6607
- indexType,
6608
- this.opts.column.getName()
6609
- );
6610
- const encrypted = await (0, import_protect_ffi8.encryptQuery)(this.client, {
6611
- // `Plaintext` widens the FFI `JsPlaintext` with `Date` (serialized via
6612
- // `toJSON` at the boundary); cast until the upstream input union is
6613
- // corrected to include it.
6614
- plaintext,
6615
- column: this.opts.column.getName(),
6616
- table: this.opts.table.tableName,
6617
- indexType,
6618
- queryOp,
6619
- unverifiedContext: metadata
6620
- });
6621
- return formatEncryptedResult(encrypted, this.opts.returnType);
6622
- },
6623
- (error) => {
6624
- log.set({ errorCode: getErrorCode(error) ?? "unknown" });
6625
- return {
6626
- type: EncryptionErrorTypes.EncryptionError,
6627
- message: error.message,
6628
- code: getErrorCode(error)
6629
- };
6630
- }
6631
- );
6632
- log.emit();
6633
- return result;
6634
- }
6635
- getOperation() {
6636
- return { client: this.client, plaintext: this.plaintext, ...this.opts };
6637
- }
6638
- };
6639
- var EncryptQueryOperationWithLockContext = class extends EncryptionOperation {
6640
- constructor(client, plaintext, opts, lockContext, auditMetadata) {
6641
- super();
6642
- this.client = client;
6643
- this.plaintext = plaintext;
6644
- this.opts = opts;
6645
- this.lockContext = lockContext;
6646
- this.auditMetadata = auditMetadata;
6647
- }
6648
- client;
6649
- plaintext;
6650
- opts;
6651
- lockContext;
6652
- async execute() {
6653
- const log = createRequestLogger();
6654
- log.set({
6655
- op: "encryptQuery",
6656
- table: this.opts.table.tableName,
6657
- column: this.opts.column.getName(),
6658
- queryType: this.opts.queryType,
6659
- lockContext: true
6660
- });
6661
- if (this.plaintext === null || this.plaintext === void 0) {
6662
- log.emit();
6663
- return { data: null };
6664
- }
6665
- const plaintext = this.plaintext;
6666
- const validationError = validateNumericValue(plaintext);
6667
- if (validationError?.failure) {
6668
- log.emit();
6669
- return { failure: validationError.failure };
6670
- }
6671
- const result = await withResult(
6672
- async () => {
6673
- if (!this.client) throw noClientError();
6674
- const context = resolveLockContext(this.lockContext);
6675
- const { metadata } = this.getAuditData();
6676
- const { indexType, queryOp } = resolveIndexType(
6677
- this.opts.column,
6678
- this.opts.queryType,
6679
- plaintext
6680
- );
6681
- assertValueIndexCompatibility(
6682
- plaintext,
6683
- indexType,
6684
- this.opts.column.getName()
6685
- );
6686
- const encrypted = await (0, import_protect_ffi8.encryptQuery)(this.client, {
6687
- // `Plaintext` widens the FFI `JsPlaintext` with `Date` (serialized via
6688
- // `toJSON` at the boundary); cast until the upstream input union is
6689
- // corrected to include it.
6690
- plaintext,
6691
- column: this.opts.column.getName(),
6692
- table: this.opts.table.tableName,
6693
- indexType,
6694
- queryOp,
6695
- lockContext: context,
6696
- unverifiedContext: metadata
6697
- });
6698
- return formatEncryptedResult(encrypted, this.opts.returnType);
6699
- },
6700
- (error) => {
6701
- log.set({ errorCode: getErrorCode(error) ?? "unknown" });
6702
- return {
6703
- type: EncryptionErrorTypes.EncryptionError,
6704
- message: error.message,
6705
- code: getErrorCode(error)
6706
- };
6707
- }
6708
- );
6709
- log.emit();
6710
- return result;
6711
- }
6712
- };
6713
-
6714
- // src/encryption/index.ts
6715
- var noClientError = () => new Error(
6716
- "The Encryption client has not been initialized. Please call init() before using the client."
6717
- );
6718
- function resolveEqlVersion(schemas, explicit) {
6719
- const v3Count = schemas.filter(
6720
- (schema) => typeof schema.buildColumnKeyMap === "function"
6721
- ).length;
6722
- if (v3Count > 0 && v3Count < schemas.length) {
6723
- throw new Error(
6724
- "[encryption]: cannot mix EQL v2 and EQL v3 tables in one client \u2014 one client emits exactly one wire format. Create separate clients for the v2 and v3 schemas."
6725
- );
6726
- }
6727
- if (explicit !== void 0) {
6728
- return explicit;
6729
- }
6730
- return v3Count === 0 ? void 0 : 3;
6731
- }
6732
- var EncryptionClient = class {
6733
- client;
6734
- encryptConfig;
6735
- /**
6736
- * Initializes the EncryptionClient with the provided configuration.
6737
- * @internal
6738
- * @param config - The configuration object for initializing the client.
6739
- * @returns A promise that resolves to a {@link Result} containing the initialized EncryptionClient or an {@link EncryptionError}.
6740
- **/
6741
- async init(config) {
6742
- return await withResult(
6743
- async () => {
6744
- const validated = encryptConfigSchema.parse(
6745
- config.encryptConfig
6746
- );
6747
- logger.debug(
6748
- "Initializing the Encryption client with the following config:",
6749
- {
6750
- encryptConfig: validated
6751
- }
7023
+ assertValueIndexCompatibility(
7024
+ plaintext,
7025
+ indexType,
7026
+ this.opts.column.getName()
6752
7027
  );
6753
- this.client = await (0, import_protect_ffi9.newClient)({
6754
- encryptConfig: validated,
6755
- clientOpts: {
6756
- workspaceCrn: config.workspaceCrn,
6757
- accessKey: config.accessKey,
6758
- clientId: config.clientId,
6759
- clientKey: config.clientKey,
6760
- keyset: toFfiKeysetIdentifier(config.keyset)
6761
- },
6762
- strategy: config.authStrategy,
6763
- eqlVersion: config.eqlVersion
7028
+ const encrypted = await (0, import_protect_ffi8.encryptQuery)(this.client, {
7029
+ // `Plaintext` widens the FFI `JsPlaintext` with `Date` (serialized via
7030
+ // `toJSON` at the boundary); cast until the upstream input union is
7031
+ // corrected to include it.
7032
+ plaintext,
7033
+ column: this.opts.column.getName(),
7034
+ table: this.opts.table.tableName,
7035
+ indexType,
7036
+ queryOp,
7037
+ unverifiedContext: metadata
6764
7038
  });
6765
- this.encryptConfig = validated;
6766
- logger.debug("Successfully initialized the Encryption client.");
6767
- return this;
7039
+ return formatEncryptedResult(encrypted, this.opts.returnType);
6768
7040
  },
6769
- (error) => ({
6770
- type: EncryptionErrorTypes.ClientInitError,
6771
- message: error.message
6772
- })
7041
+ (error) => {
7042
+ log.set({ errorCode: getErrorCode(error) ?? "unknown" });
7043
+ return {
7044
+ type: EncryptionErrorTypes.EncryptionError,
7045
+ message: error.message,
7046
+ code: getErrorCode(error)
7047
+ };
7048
+ }
6773
7049
  );
7050
+ log.emit();
7051
+ return result;
6774
7052
  }
6775
- /**
6776
- * Encrypt a value - returns a promise which resolves to an encrypted value.
6777
- *
6778
- * @param plaintext - The plaintext value to be encrypted.
6779
- * @param opts - Options specifying the column (or nested field) and table for encryption. See {@link EncryptOptions}.
6780
- * @returns An EncryptOperation that can be awaited or chained with additional methods.
6781
- *
6782
- * @example
6783
- * The following example demonstrates how to encrypt a value using the Encryption client.
6784
- * It includes defining an encryption schema with {@link encryptedTable} and {@link encryptedColumn},
6785
- * initializing the client with {@link Encryption}, and performing the encryption.
6786
- *
6787
- * `encrypt` returns an {@link EncryptOperation} which can be awaited to get a {@link Result}
6788
- * which can either be the encrypted value or an {@link EncryptionError}.
6789
- *
6790
- * ```typescript
6791
- * // Define encryption schema
6792
- * import { Encryption } from "@cipherstash/stack"
6793
- * import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
6794
- * const userSchema = encryptedTable("users", {
6795
- * email: encryptedColumn("email"),
6796
- * });
6797
- *
6798
- * // Initialize Encryption client
6799
- * const client = await Encryption({ schemas: [userSchema] })
6800
- *
6801
- * // Encrypt a value
6802
- * const encryptedResult = await client.encrypt(
6803
- * "person@example.com",
6804
- * { column: userSchema.email, table: userSchema }
6805
- * )
6806
- *
6807
- * // Handle encryption result
6808
- * if (encryptedResult.failure) {
6809
- * throw new Error(`Encryption failed: ${encryptedResult.failure.message}`);
6810
- * }
6811
- *
6812
- * console.log("Encrypted data:", encryptedResult.data);
6813
- * ```
6814
- *
6815
- * @example
6816
- * When encrypting data, a {@link LockContext} can be provided to tie the encryption to a specific user or session.
6817
- * This ensures that the same lock context is required for decryption.
6818
- *
6819
- * The following example demonstrates how to create a lock context using a user's JWT token
6820
- * and use it during encryption.
6821
- *
6822
- * ```typescript
6823
- * // Define encryption schema and initialize client as above
6824
- *
6825
- * // Create a lock for the user's `sub` claim from their JWT
6826
- * const lc = new LockContext();
6827
- * const lockContext = await lc.identify(userJwt);
6828
- *
6829
- * if (lockContext.failure) {
6830
- * // Handle the failure
6831
- * }
6832
- *
6833
- * // Encrypt a value with the lock context
6834
- * // Decryption will then require the same lock context
6835
- * const encryptedResult = await client.encrypt(
6836
- * "person@example.com",
6837
- * { column: userSchema.email, table: userSchema }
6838
- * )
6839
- * .withLockContext(lockContext)
6840
- * ```
6841
- *
6842
- * @see {@link EncryptOptions}
6843
- * @see {@link Result}
6844
- * @see {@link encryptedTable}
6845
- * @see {@link encryptedColumn}
6846
- * @see {@link encryptedField}
6847
- * @see {@link LockContext}
6848
- * @see {@link EncryptOperation}
6849
- */
6850
- encrypt(plaintext, opts) {
6851
- return new EncryptOperation(this.client, plaintext, opts);
6852
- }
6853
- encryptQuery(plaintextOrTerms, opts) {
6854
- if (!opts && isScalarQueryTermArray(plaintextOrTerms)) {
6855
- return new BatchEncryptQueryOperation(this.client, plaintextOrTerms);
6856
- }
6857
- if (Array.isArray(plaintextOrTerms) && plaintextOrTerms.length === 0 && !opts) {
6858
- return new BatchEncryptQueryOperation(
6859
- this.client,
6860
- []
6861
- );
6862
- }
6863
- if (!opts) {
6864
- throw new Error("EncryptQueryOptions are required");
6865
- }
6866
- return new EncryptQueryOperation(
6867
- this.client,
6868
- plaintextOrTerms,
6869
- opts
6870
- );
7053
+ getOperation() {
7054
+ return { client: this.client, plaintext: this.plaintext, ...this.opts };
6871
7055
  }
6872
- /**
6873
- * Decryption - returns a promise which resolves to a decrypted value.
6874
- *
6875
- * @param encryptedData - The encrypted data to be decrypted.
6876
- * @returns A DecryptOperation that can be awaited or chained with additional methods.
6877
- *
6878
- * @example
6879
- * The following example demonstrates how to decrypt a value that was previously encrypted using the {@link encrypt} method.
6880
- * It includes encrypting a value first, then decrypting it, and handling the result.
6881
- *
6882
- * ```typescript
6883
- * const encryptedData = await client.encrypt(
6884
- * "person@example.com",
6885
- * { column: "email", table: "users" }
6886
- * )
6887
- * const decryptResult = await client.decrypt(encryptedData)
6888
- * if (decryptResult.failure) {
6889
- * throw new Error(`Decryption failed: ${decryptResult.failure.message}`);
6890
- * }
6891
- * console.log("Decrypted data:", decryptResult.data);
6892
- * ```
6893
- *
6894
- * @example
6895
- * Provide a lock context when decrypting:
6896
- * ```typescript
6897
- * await client.decrypt(encryptedData)
6898
- * .withLockContext(lockContext)
6899
- * ```
6900
- *
6901
- * @remarks
6902
- * The public input type rejects null, but at runtime `decrypt` will
6903
- * short-circuit and return null when given a null ciphertext
6904
- * (defense in depth for legacy / manually-NULLed DB rows reached via
6905
- * casts or dynamic field walking). The narrow return type holds for
6906
- * any caller that respects the input contract.
6907
- *
6908
- * @see {@link LockContext}
6909
- * @see {@link DecryptOperation}
6910
- */
6911
- decrypt(encryptedData) {
6912
- return new DecryptOperation(this.client, encryptedData);
7056
+ };
7057
+ var EncryptQueryOperationWithLockContext = class extends EncryptionOperation {
7058
+ constructor(client, plaintext, opts, lockContext, auditMetadata) {
7059
+ super();
7060
+ this.client = client;
7061
+ this.plaintext = plaintext;
7062
+ this.opts = opts;
7063
+ this.lockContext = lockContext;
7064
+ this.auditMetadata = auditMetadata;
6913
7065
  }
6914
- /**
6915
- * Encrypt a model (object) based on the table schema.
6916
- *
6917
- * Only fields whose keys match columns defined in the table schema are encrypted.
6918
- * All other fields are passed through unchanged. Returns a thenable operation
6919
- * that supports `.withLockContext()` for identity-aware encryption.
6920
- *
6921
- * The return type is **schema-aware**: fields matching the table schema are
6922
- * typed as `Encrypted`, while other fields retain their original types. For
6923
- * best results, let TypeScript infer the type parameters from the arguments
6924
- * rather than providing an explicit type argument.
6925
- *
6926
- * @param input - The model object with plaintext values to encrypt.
6927
- * @param table - The table schema defining which fields to encrypt.
6928
- * @returns An `EncryptModelOperation` that can be awaited to get a `Result`
6929
- * containing the model with schema-defined fields typed as `Encrypted`,
6930
- * or an `EncryptionError`.
6931
- *
6932
- * @example
6933
- * ```typescript
6934
- * import { Encryption } from "@cipherstash/stack"
6935
- * import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
6936
- *
6937
- * type User = { id: string; email: string; createdAt: Date }
6938
- *
6939
- * const usersSchema = encryptedTable("users", {
6940
- * email: encryptedColumn("email").equality(),
6941
- * })
6942
- *
6943
- * const client = await Encryption({ schemas: [usersSchema] })
6944
- *
6945
- * // Let TypeScript infer the return type from the schema.
6946
- * // result.data.email is typed as `Encrypted`, result.data.id stays `string`.
6947
- * const result = await client.encryptModel(
6948
- * { id: "user_123", email: "alice@example.com", createdAt: new Date() },
6949
- * usersSchema,
6950
- * )
6951
- *
6952
- * if (result.failure) {
6953
- * console.error(result.failure.message)
6954
- * } else {
6955
- * console.log(result.data.id) // string
6956
- * console.log(result.data.email) // Encrypted
6957
- * }
6958
- * ```
6959
- */
6960
- encryptModel(input, table) {
6961
- return new EncryptModelOperation(
6962
- this.client,
6963
- input,
6964
- table
7066
+ client;
7067
+ plaintext;
7068
+ opts;
7069
+ lockContext;
7070
+ async execute() {
7071
+ const log = createRequestLogger();
7072
+ log.set({
7073
+ op: "encryptQuery",
7074
+ table: this.opts.table.tableName,
7075
+ column: this.opts.column.getName(),
7076
+ queryType: this.opts.queryType,
7077
+ lockContext: true
7078
+ });
7079
+ if (this.plaintext === null || this.plaintext === void 0) {
7080
+ log.emit();
7081
+ return { data: null };
7082
+ }
7083
+ const plaintext = this.plaintext;
7084
+ const validationError = validateNumericValue(plaintext);
7085
+ if (validationError?.failure) {
7086
+ log.emit();
7087
+ return { failure: validationError.failure };
7088
+ }
7089
+ const result = await withResult(
7090
+ async () => {
7091
+ if (!this.client) throw noClientError();
7092
+ const context = resolveLockContext(this.lockContext);
7093
+ const { metadata } = this.getAuditData();
7094
+ const { indexType, queryOp } = resolveIndexType(
7095
+ this.opts.column,
7096
+ this.opts.queryType,
7097
+ plaintext
7098
+ );
7099
+ assertValueIndexCompatibility(
7100
+ plaintext,
7101
+ indexType,
7102
+ this.opts.column.getName()
7103
+ );
7104
+ const encrypted = await (0, import_protect_ffi8.encryptQuery)(this.client, {
7105
+ // `Plaintext` widens the FFI `JsPlaintext` with `Date` (serialized via
7106
+ // `toJSON` at the boundary); cast until the upstream input union is
7107
+ // corrected to include it.
7108
+ plaintext,
7109
+ column: this.opts.column.getName(),
7110
+ table: this.opts.table.tableName,
7111
+ indexType,
7112
+ queryOp,
7113
+ lockContext: context,
7114
+ unverifiedContext: metadata
7115
+ });
7116
+ return formatEncryptedResult(encrypted, this.opts.returnType);
7117
+ },
7118
+ (error) => {
7119
+ log.set({ errorCode: getErrorCode(error) ?? "unknown" });
7120
+ return {
7121
+ type: EncryptionErrorTypes.EncryptionError,
7122
+ message: error.message,
7123
+ code: getErrorCode(error)
7124
+ };
7125
+ }
6965
7126
  );
7127
+ log.emit();
7128
+ return result;
6966
7129
  }
6967
- /**
6968
- * Decrypt a model (object) whose fields contain encrypted values.
6969
- *
6970
- * Identifies encrypted fields automatically and decrypts them, returning the
6971
- * model with plaintext values. Returns a thenable operation that supports
6972
- * `.withLockContext()` for identity-aware decryption.
6973
- *
6974
- * @param input - The model object with encrypted field values.
6975
- * @returns A `DecryptModelOperation<T>` that can be awaited to get a `Result`
6976
- * containing the model with decrypted plaintext fields, or an `EncryptionError`.
6977
- *
6978
- * @example
6979
- * ```typescript
6980
- * // Decrypt a previously encrypted model
6981
- * const decrypted = await client.decryptModel<User>(encryptedUser)
6982
- *
6983
- * if (decrypted.failure) {
6984
- * console.error(decrypted.failure.message)
6985
- * } else {
6986
- * console.log(decrypted.data.email) // "alice@example.com"
6987
- * }
6988
- *
6989
- * // With a lock context
6990
- * const decrypted = await client
6991
- * .decryptModel<User>(encryptedUser)
6992
- * .withLockContext(lockContext)
6993
- * ```
6994
- */
6995
- decryptModel(input) {
6996
- return new DecryptModelOperation(this.client, input);
6997
- }
6998
- /**
6999
- * Encrypt multiple models (objects) in a single bulk operation.
7000
- *
7001
- * Performs a single call to ZeroKMS regardless of the number of models,
7002
- * while still using a unique key for each encrypted value. Only fields
7003
- * matching the table schema are encrypted; other fields pass through unchanged.
7004
- *
7005
- * The return type is **schema-aware**: fields matching the table schema are
7006
- * typed as `Encrypted`, while other fields retain their original types. For
7007
- * best results, let TypeScript infer the type parameters from the arguments.
7008
- *
7009
- * @param input - An array of model objects with plaintext values to encrypt.
7010
- * @param table - The table schema defining which fields to encrypt.
7011
- * @returns A `BulkEncryptModelsOperation` that can be awaited to get a `Result`
7012
- * containing an array of models with schema-defined fields typed as `Encrypted`,
7013
- * or an `EncryptionError`.
7014
- *
7015
- * @example
7016
- * ```typescript
7017
- * import { Encryption } from "@cipherstash/stack"
7018
- * import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
7019
- *
7020
- * type User = { id: string; email: string }
7021
- *
7022
- * const usersSchema = encryptedTable("users", {
7023
- * email: encryptedColumn("email"),
7024
- * })
7025
- *
7026
- * const client = await Encryption({ schemas: [usersSchema] })
7027
- *
7028
- * // Let TypeScript infer the return type from the schema.
7029
- * // Each item's email is typed as `Encrypted`, id stays `string`.
7030
- * const result = await client.bulkEncryptModels(
7031
- * [
7032
- * { id: "1", email: "alice@example.com" },
7033
- * { id: "2", email: "bob@example.com" },
7034
- * ],
7035
- * usersSchema,
7036
- * )
7037
- *
7038
- * if (!result.failure) {
7039
- * console.log(result.data) // array of models with encrypted email fields
7040
- * }
7041
- * ```
7042
- */
7043
- bulkEncryptModels(input, table) {
7044
- return new BulkEncryptModelsOperation(
7045
- this.client,
7046
- input,
7047
- table
7130
+ };
7131
+
7132
+ // src/encryption/index.ts
7133
+ var noClientError = () => new Error(
7134
+ "The Encryption client has not been initialized. Please call init() before using the client."
7135
+ );
7136
+ function resolveEqlVersion(schemas, explicit) {
7137
+ const v3Count = schemas.filter(
7138
+ (schema) => typeof schema.buildColumnKeyMap === "function"
7139
+ ).length;
7140
+ if (v3Count > 0 && v3Count < schemas.length) {
7141
+ throw new Error(
7142
+ "[encryption]: cannot mix EQL v2 and EQL v3 tables in one client \u2014 one client emits exactly one wire format. Create separate clients for the v2 and v3 schemas."
7048
7143
  );
7049
7144
  }
7050
- /**
7051
- * Decrypt multiple models (objects) in a single bulk operation.
7052
- *
7053
- * Performs a single call to ZeroKMS regardless of the number of models,
7054
- * restoring all encrypted fields to their original plaintext values.
7055
- *
7056
- * @param input - An array of model objects with encrypted field values.
7057
- * @returns A `BulkDecryptModelsOperation<T>` that can be awaited to get a `Result`
7058
- * containing an array of models with decrypted plaintext fields, or an `EncryptionError`.
7059
- *
7060
- * @example
7061
- * ```typescript
7062
- * const encryptedUsers = encryptedResult.data // from bulkEncryptModels
7063
- *
7064
- * const result = await client.bulkDecryptModels<User>(encryptedUsers)
7065
- *
7066
- * if (!result.failure) {
7067
- * for (const user of result.data) {
7068
- * console.log(user.email) // plaintext email
7069
- * }
7070
- * }
7071
- *
7072
- * // With a lock context
7073
- * const result = await client
7074
- * .bulkDecryptModels<User>(encryptedUsers)
7075
- * .withLockContext(lockContext)
7076
- * ```
7077
- */
7078
- bulkDecryptModels(input) {
7079
- return new BulkDecryptModelsOperation(this.client, input);
7145
+ if (explicit !== void 0) {
7146
+ return explicit;
7080
7147
  }
7148
+ return v3Count === 0 ? void 0 : 3;
7149
+ }
7150
+ var EncryptionClient = class {
7151
+ client;
7152
+ encryptConfig;
7081
7153
  /**
7082
- * Encrypt multiple plaintext values in a single bulk operation.
7083
- *
7084
- * Each value is encrypted with its own unique key via a single call to ZeroKMS.
7085
- * Values can include optional `id` fields for correlating results back to
7086
- * your application data.
7087
- *
7088
- * @param plaintexts - An array of objects with `plaintext` (and optional `id`) fields.
7089
- * @param opts - Options specifying the target column (or nested {@link encryptedField}) and table. See {@link EncryptOptions}.
7090
- * @returns A `BulkEncryptOperation` that can be awaited to get a `Result`
7091
- * containing an array of `{ id?, data: Encrypted }` objects, or an `EncryptionError`.
7092
- *
7093
- * @example
7094
- * ```typescript
7095
- * import { Encryption } from "@cipherstash/stack"
7096
- * import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
7097
- *
7098
- * const users = encryptedTable("users", {
7099
- * email: encryptedColumn("email"),
7100
- * })
7101
- * const client = await Encryption({ schemas: [users] })
7102
- *
7103
- * const result = await client.bulkEncrypt(
7104
- * [
7105
- * { id: "u1", plaintext: "alice@example.com" },
7106
- * { id: "u2", plaintext: "bob@example.com" },
7107
- * ],
7108
- * { column: users.email, table: users },
7109
- * )
7110
- *
7111
- * if (!result.failure) {
7112
- * // result.data = [{ id: "u1", data: Encrypted }, { id: "u2", data: Encrypted }, ...]
7113
- * console.log(result.data)
7114
- * }
7115
- * ```
7116
- */
7117
- bulkEncrypt(plaintexts, opts) {
7118
- return new BulkEncryptOperation(this.client, plaintexts, opts);
7154
+ * Initializes the EncryptionClient with the provided configuration.
7155
+ * @internal
7156
+ * @param config - The configuration object for initializing the client.
7157
+ * @returns A promise that resolves to a {@link Result} containing the initialized EncryptionClient or an {@link EncryptionError}.
7158
+ **/
7159
+ async init(config) {
7160
+ return await withResult(
7161
+ async () => {
7162
+ const validated = encryptConfigSchema.parse(
7163
+ config.encryptConfig
7164
+ );
7165
+ logger.debug(
7166
+ "Initializing the Encryption client with the following config:",
7167
+ {
7168
+ encryptConfig: validated
7169
+ }
7170
+ );
7171
+ this.client = await (0, import_protect_ffi9.newClient)({
7172
+ encryptConfig: validated,
7173
+ clientOpts: {
7174
+ workspaceCrn: config.workspaceCrn,
7175
+ accessKey: config.accessKey,
7176
+ clientId: config.clientId,
7177
+ clientKey: config.clientKey,
7178
+ keyset: toFfiKeysetIdentifier(config.keyset)
7179
+ },
7180
+ strategy: config.authStrategy,
7181
+ eqlVersion: config.eqlVersion
7182
+ });
7183
+ this.encryptConfig = validated;
7184
+ logger.debug("Successfully initialized the Encryption client.");
7185
+ return this;
7186
+ },
7187
+ (error) => ({
7188
+ type: EncryptionErrorTypes.ClientInitError,
7189
+ message: error.message
7190
+ })
7191
+ );
7119
7192
  }
7120
7193
  /**
7121
- * Decrypt multiple encrypted values in a single bulk operation.
7122
- *
7123
- * Performs a single call to ZeroKMS to decrypt all values. The result uses
7124
- * a multi-status pattern: each item in the returned array has either a `data`
7125
- * field (success) or an `error` field (failure), allowing graceful handling
7126
- * of partial failures.
7194
+ * Encrypt a value - returns a promise which resolves to an encrypted value.
7127
7195
  *
7128
- * @param encryptedPayloads - An array of objects with `data` (encrypted payload) and optional `id` fields.
7129
- * @returns A `BulkDecryptOperation` that can be awaited to get a `Result`
7130
- * containing an array of `{ id?, data: plaintext }` or `{ id?, error: string }` objects,
7131
- * or an `EncryptionError` if the entire operation fails.
7196
+ * @param plaintext - The plaintext value to be encrypted.
7197
+ * @param opts - Options specifying the column (or nested field) and table for encryption. See {@link EncryptOptions}.
7198
+ * @returns An EncryptOperation that can be awaited or chained with additional methods.
7132
7199
  *
7133
7200
  * @example
7134
- * ```typescript
7135
- * const encrypted = await client.bulkEncrypt(plaintexts, { column: users.email, table: users })
7136
- *
7137
- * const result = await client.bulkDecrypt(encrypted.data)
7201
+ * The following example demonstrates how to encrypt a value using the Encryption client.
7202
+ * It includes defining an encryption schema with {@link encryptedTable} and {@link encryptedColumn},
7203
+ * initializing the client with {@link Encryption}, and performing the encryption.
7138
7204
  *
7139
- * if (!result.failure) {
7140
- * for (const item of result.data) {
7141
- * if ("data" in item) {
7142
- * console.log(`${item.id}: ${item.data}`)
7143
- * } else {
7144
- * console.error(`${item.id} failed: ${item.error}`)
7145
- * }
7146
- * }
7147
- * }
7148
- * ```
7149
- */
7150
- bulkDecrypt(encryptedPayloads) {
7151
- return new BulkDecryptOperation(this.client, encryptedPayloads);
7152
- }
7153
- /**
7154
- * Get the encrypt config object.
7205
+ * `encrypt` returns an {@link EncryptOperation} which can be awaited to get a {@link Result}
7206
+ * which can either be the encrypted value or an {@link EncryptionError}.
7155
7207
  *
7156
- * @returns The encrypt config object.
7157
- */
7158
- getEncryptConfig() {
7159
- return this.encryptConfig;
7160
- }
7161
- };
7162
- var warnedStrategyDeprecated = false;
7163
- function warnStrategyDeprecated() {
7164
- if (warnedStrategyDeprecated) return;
7165
- warnedStrategyDeprecated = true;
7166
- console.warn(
7167
- "[encryption]: `config.strategy` is deprecated and will be removed in a future release \u2014 use `config.authStrategy` instead."
7168
- );
7169
- }
7170
- var Encryption = async (config) => {
7171
- const { schemas, config: clientConfig } = config;
7172
- if (!schemas.length) {
7173
- throw new Error(
7174
- "[encryption]: At least one encryptedTable must be provided to initialize the encryption client"
7175
- );
7176
- }
7177
- if (clientConfig?.keyset && "id" in clientConfig.keyset && !validate_default(clientConfig.keyset.id)) {
7178
- throw new Error(
7179
- "[encryption]: Invalid UUID provided for keyset id. Must be a valid UUID."
7180
- );
7181
- }
7182
- if (clientConfig?.strategy) {
7183
- warnStrategyDeprecated();
7184
- }
7185
- const authStrategy = clientConfig?.authStrategy ?? clientConfig?.strategy;
7186
- const client = new EncryptionClient();
7187
- const encryptConfig = buildEncryptConfig(...schemas);
7188
- const eqlVersion = resolveEqlVersion(schemas, clientConfig?.eqlVersion);
7189
- const result = await client.init({
7190
- encryptConfig,
7191
- ...clientConfig,
7192
- authStrategy,
7193
- eqlVersion
7194
- });
7195
- if (result.failure) {
7196
- throw new Error(`[encryption]: ${result.failure.message}`);
7197
- }
7198
- return result.data;
7199
- };
7200
-
7201
- // src/eql/v3/columns.ts
7202
- var STORAGE_ONLY = {
7203
- equality: false,
7204
- orderAndRange: false,
7205
- freeTextSearch: false
7206
- };
7207
- var EQUALITY_ONLY = {
7208
- equality: true,
7209
- orderAndRange: false,
7210
- freeTextSearch: false
7211
- };
7212
- var ORDER_AND_RANGE = {
7213
- equality: true,
7214
- orderAndRange: true,
7215
- freeTextSearch: false
7216
- };
7217
- var MATCH_ONLY = {
7218
- equality: false,
7219
- orderAndRange: false,
7220
- freeTextSearch: true
7221
- };
7222
- var TEXT_SEARCH = {
7223
- equality: true,
7224
- orderAndRange: true,
7225
- freeTextSearch: true
7226
- };
7227
- var TEXT_SEARCH_EQL_TYPE = "eql_v3.text_search";
7228
- var INTEGER = {
7229
- eqlType: "eql_v3.integer",
7230
- castAs: "number",
7231
- capabilities: STORAGE_ONLY
7232
- };
7233
- var INTEGER_EQ = {
7234
- eqlType: "eql_v3.integer_eq",
7235
- castAs: "number",
7236
- capabilities: EQUALITY_ONLY
7237
- };
7238
- var INTEGER_ORD_ORE = {
7239
- eqlType: "eql_v3.integer_ord_ore",
7240
- castAs: "number",
7241
- capabilities: ORDER_AND_RANGE
7242
- };
7243
- var INTEGER_ORD = {
7244
- eqlType: "eql_v3.integer_ord",
7245
- castAs: "number",
7246
- capabilities: ORDER_AND_RANGE
7247
- };
7248
- var SMALLINT = {
7249
- eqlType: "eql_v3.smallint",
7250
- castAs: "number",
7251
- capabilities: STORAGE_ONLY
7252
- };
7253
- var SMALLINT_EQ = {
7254
- eqlType: "eql_v3.smallint_eq",
7255
- castAs: "number",
7256
- capabilities: EQUALITY_ONLY
7257
- };
7258
- var SMALLINT_ORD_ORE = {
7259
- eqlType: "eql_v3.smallint_ord_ore",
7260
- castAs: "number",
7261
- capabilities: ORDER_AND_RANGE
7262
- };
7263
- var SMALLINT_ORD = {
7264
- eqlType: "eql_v3.smallint_ord",
7265
- castAs: "number",
7266
- capabilities: ORDER_AND_RANGE
7267
- };
7268
- var DATE = {
7269
- eqlType: "eql_v3.date",
7270
- castAs: "date",
7271
- capabilities: STORAGE_ONLY
7272
- };
7273
- var DATE_EQ = {
7274
- eqlType: "eql_v3.date_eq",
7275
- castAs: "date",
7276
- capabilities: EQUALITY_ONLY
7277
- };
7278
- var DATE_ORD_ORE = {
7279
- eqlType: "eql_v3.date_ord_ore",
7280
- castAs: "date",
7281
- capabilities: ORDER_AND_RANGE
7282
- };
7283
- var DATE_ORD = {
7284
- eqlType: "eql_v3.date_ord",
7285
- castAs: "date",
7286
- capabilities: ORDER_AND_RANGE
7287
- };
7288
- var TIMESTAMP = {
7289
- eqlType: "eql_v3.timestamp",
7290
- castAs: "timestamp",
7291
- capabilities: STORAGE_ONLY
7292
- };
7293
- var TIMESTAMP_EQ = {
7294
- eqlType: "eql_v3.timestamp_eq",
7295
- castAs: "timestamp",
7296
- capabilities: EQUALITY_ONLY
7297
- };
7298
- var TIMESTAMP_ORD_ORE = {
7299
- eqlType: "eql_v3.timestamp_ord_ore",
7300
- castAs: "timestamp",
7301
- capabilities: ORDER_AND_RANGE
7302
- };
7303
- var TIMESTAMP_ORD = {
7304
- eqlType: "eql_v3.timestamp_ord",
7305
- castAs: "timestamp",
7306
- capabilities: ORDER_AND_RANGE
7307
- };
7308
- var NUMERIC = {
7309
- eqlType: "eql_v3.numeric",
7310
- castAs: "number",
7311
- capabilities: STORAGE_ONLY
7312
- };
7313
- var NUMERIC_EQ = {
7314
- eqlType: "eql_v3.numeric_eq",
7315
- castAs: "number",
7316
- capabilities: EQUALITY_ONLY
7317
- };
7318
- var NUMERIC_ORD_ORE = {
7319
- eqlType: "eql_v3.numeric_ord_ore",
7320
- castAs: "number",
7321
- capabilities: ORDER_AND_RANGE
7322
- };
7323
- var NUMERIC_ORD = {
7324
- eqlType: "eql_v3.numeric_ord",
7325
- castAs: "number",
7326
- capabilities: ORDER_AND_RANGE
7327
- };
7328
- var TEXT = {
7329
- eqlType: "eql_v3.text",
7330
- castAs: "string",
7331
- capabilities: STORAGE_ONLY
7332
- };
7333
- var TEXT_EQ = {
7334
- eqlType: "eql_v3.text_eq",
7335
- castAs: "string",
7336
- capabilities: EQUALITY_ONLY
7337
- };
7338
- var TEXT_MATCH = {
7339
- eqlType: "eql_v3.text_match",
7340
- castAs: "string",
7341
- capabilities: MATCH_ONLY
7342
- };
7343
- var TEXT_ORD_ORE = {
7344
- eqlType: "eql_v3.text_ord_ore",
7345
- castAs: "string",
7346
- capabilities: ORDER_AND_RANGE
7347
- };
7348
- var TEXT_ORD = {
7349
- eqlType: "eql_v3.text_ord",
7350
- castAs: "string",
7351
- capabilities: ORDER_AND_RANGE
7352
- };
7353
- var BOOLEAN = {
7354
- eqlType: "eql_v3.boolean",
7355
- castAs: "boolean",
7356
- capabilities: STORAGE_ONLY
7357
- };
7358
- var REAL = {
7359
- eqlType: "eql_v3.real",
7360
- castAs: "number",
7361
- capabilities: STORAGE_ONLY
7362
- };
7363
- var REAL_EQ = {
7364
- eqlType: "eql_v3.real_eq",
7365
- castAs: "number",
7366
- capabilities: EQUALITY_ONLY
7367
- };
7368
- var REAL_ORD_ORE = {
7369
- eqlType: "eql_v3.real_ord_ore",
7370
- castAs: "number",
7371
- capabilities: ORDER_AND_RANGE
7372
- };
7373
- var REAL_ORD = {
7374
- eqlType: "eql_v3.real_ord",
7375
- castAs: "number",
7376
- capabilities: ORDER_AND_RANGE
7377
- };
7378
- var DOUBLE = {
7379
- eqlType: "eql_v3.double",
7380
- castAs: "number",
7381
- capabilities: STORAGE_ONLY
7382
- };
7383
- var DOUBLE_EQ = {
7384
- eqlType: "eql_v3.double_eq",
7385
- castAs: "number",
7386
- capabilities: EQUALITY_ONLY
7387
- };
7388
- var DOUBLE_ORD_ORE = {
7389
- eqlType: "eql_v3.double_ord_ore",
7390
- castAs: "number",
7391
- capabilities: ORDER_AND_RANGE
7392
- };
7393
- var DOUBLE_ORD = {
7394
- eqlType: "eql_v3.double_ord",
7395
- castAs: "number",
7396
- capabilities: ORDER_AND_RANGE
7397
- };
7398
- function indexesForCapabilities(capabilities, castAs) {
7399
- const indexes = {};
7400
- if (capabilities.equality && (!capabilities.orderAndRange || castAs === "string")) {
7401
- indexes.unique = { token_filters: [] };
7402
- }
7403
- if (capabilities.orderAndRange) {
7404
- indexes.ore = {};
7405
- }
7406
- if (capabilities.freeTextSearch) {
7407
- indexes.match = defaultMatchOpts();
7208
+ * ```typescript
7209
+ * // Define encryption schema
7210
+ * import { Encryption } from "@cipherstash/stack"
7211
+ * import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
7212
+ * const userSchema = encryptedTable("users", {
7213
+ * email: encryptedColumn("email"),
7214
+ * });
7215
+ *
7216
+ * // Initialize Encryption client
7217
+ * const client = await Encryption({ schemas: [userSchema] })
7218
+ *
7219
+ * // Encrypt a value
7220
+ * const encryptedResult = await client.encrypt(
7221
+ * "person@example.com",
7222
+ * { column: userSchema.email, table: userSchema }
7223
+ * )
7224
+ *
7225
+ * // Handle encryption result
7226
+ * if (encryptedResult.failure) {
7227
+ * throw new Error(`Encryption failed: ${encryptedResult.failure.message}`);
7228
+ * }
7229
+ *
7230
+ * console.log("Encrypted data:", encryptedResult.data);
7231
+ * ```
7232
+ *
7233
+ * @example
7234
+ * When encrypting data, a {@link LockContext} can be provided to tie the encryption to a specific user or session.
7235
+ * This ensures that the same lock context is required for decryption.
7236
+ *
7237
+ * The following example demonstrates how to create a lock context using a user's JWT token
7238
+ * and use it during encryption.
7239
+ *
7240
+ * ```typescript
7241
+ * // Define encryption schema and initialize client as above
7242
+ *
7243
+ * // Create a lock for the user's `sub` claim from their JWT
7244
+ * const lc = new LockContext();
7245
+ * const lockContext = await lc.identify(userJwt);
7246
+ *
7247
+ * if (lockContext.failure) {
7248
+ * // Handle the failure
7249
+ * }
7250
+ *
7251
+ * // Encrypt a value with the lock context
7252
+ * // Decryption will then require the same lock context
7253
+ * const encryptedResult = await client.encrypt(
7254
+ * "person@example.com",
7255
+ * { column: userSchema.email, table: userSchema }
7256
+ * )
7257
+ * .withLockContext(lockContext)
7258
+ * ```
7259
+ *
7260
+ * @see {@link EncryptOptions}
7261
+ * @see {@link Result}
7262
+ * @see {@link encryptedTable}
7263
+ * @see {@link encryptedColumn}
7264
+ * @see {@link encryptedField}
7265
+ * @see {@link LockContext}
7266
+ * @see {@link EncryptOperation}
7267
+ */
7268
+ encrypt(plaintext, opts) {
7269
+ return new EncryptOperation(this.client, plaintext, opts);
7408
7270
  }
7409
- return indexes;
7410
- }
7411
- function isQueryableCapabilities(capabilities) {
7412
- return capabilities.equality || capabilities.orderAndRange || capabilities.freeTextSearch;
7413
- }
7414
- var EncryptedV3Column = class {
7415
- constructor(columnName, definition) {
7416
- this.columnName = columnName;
7417
- this.definition = definition;
7271
+ encryptQuery(plaintextOrTerms, opts) {
7272
+ if (!opts && isScalarQueryTermArray(plaintextOrTerms)) {
7273
+ return new BatchEncryptQueryOperation(this.client, plaintextOrTerms);
7274
+ }
7275
+ if (Array.isArray(plaintextOrTerms) && plaintextOrTerms.length === 0 && !opts) {
7276
+ return new BatchEncryptQueryOperation(
7277
+ this.client,
7278
+ []
7279
+ );
7280
+ }
7281
+ if (!opts) {
7282
+ throw new Error("EncryptQueryOptions are required");
7283
+ }
7284
+ return new EncryptQueryOperation(
7285
+ this.client,
7286
+ plaintextOrTerms,
7287
+ opts
7288
+ );
7418
7289
  }
7419
- columnName;
7420
- definition;
7421
- getName() {
7422
- return this.columnName;
7290
+ /**
7291
+ * Decryption - returns a promise which resolves to a decrypted value.
7292
+ *
7293
+ * @param encryptedData - The encrypted data to be decrypted.
7294
+ * @returns A DecryptOperation that can be awaited or chained with additional methods.
7295
+ *
7296
+ * @example
7297
+ * The following example demonstrates how to decrypt a value that was previously encrypted using the {@link encrypt} method.
7298
+ * It includes encrypting a value first, then decrypting it, and handling the result.
7299
+ *
7300
+ * ```typescript
7301
+ * const encryptedData = await client.encrypt(
7302
+ * "person@example.com",
7303
+ * { column: "email", table: "users" }
7304
+ * )
7305
+ * const decryptResult = await client.decrypt(encryptedData)
7306
+ * if (decryptResult.failure) {
7307
+ * throw new Error(`Decryption failed: ${decryptResult.failure.message}`);
7308
+ * }
7309
+ * console.log("Decrypted data:", decryptResult.data);
7310
+ * ```
7311
+ *
7312
+ * @example
7313
+ * Provide a lock context when decrypting:
7314
+ * ```typescript
7315
+ * await client.decrypt(encryptedData)
7316
+ * .withLockContext(lockContext)
7317
+ * ```
7318
+ *
7319
+ * @remarks
7320
+ * The public input type rejects null, but at runtime `decrypt` will
7321
+ * short-circuit and return null when given a null ciphertext
7322
+ * (defense in depth for legacy / manually-NULLed DB rows reached via
7323
+ * casts or dynamic field walking). The narrow return type holds for
7324
+ * any caller that respects the input contract.
7325
+ *
7326
+ * @see {@link LockContext}
7327
+ * @see {@link DecryptOperation}
7328
+ */
7329
+ decrypt(encryptedData) {
7330
+ return new DecryptOperation(this.client, encryptedData);
7423
7331
  }
7424
- /** The concrete EQL v3 domain name. Metadata only; not emitted by `build()`. */
7425
- getEqlType() {
7426
- return this.definition.eqlType;
7332
+ /**
7333
+ * Encrypt a model (object) based on the table schema.
7334
+ *
7335
+ * Only fields whose keys match columns defined in the table schema are encrypted.
7336
+ * All other fields are passed through unchanged. Returns a thenable operation
7337
+ * that supports `.withLockContext()` for identity-aware encryption.
7338
+ *
7339
+ * The return type is **schema-aware**: fields matching the table schema are
7340
+ * typed as `Encrypted`, while other fields retain their original types. For
7341
+ * best results, let TypeScript infer the type parameters from the arguments
7342
+ * rather than providing an explicit type argument.
7343
+ *
7344
+ * @param input - The model object with plaintext values to encrypt.
7345
+ * @param table - The table schema defining which fields to encrypt.
7346
+ * @returns An `EncryptModelOperation` that can be awaited to get a `Result`
7347
+ * containing the model with schema-defined fields typed as `Encrypted`,
7348
+ * or an `EncryptionError`.
7349
+ *
7350
+ * @example
7351
+ * ```typescript
7352
+ * import { Encryption } from "@cipherstash/stack"
7353
+ * import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
7354
+ *
7355
+ * type User = { id: string; email: string; createdAt: Date }
7356
+ *
7357
+ * const usersSchema = encryptedTable("users", {
7358
+ * email: encryptedColumn("email").equality(),
7359
+ * })
7360
+ *
7361
+ * const client = await Encryption({ schemas: [usersSchema] })
7362
+ *
7363
+ * // Let TypeScript infer the return type from the schema.
7364
+ * // result.data.email is typed as `Encrypted`, result.data.id stays `string`.
7365
+ * const result = await client.encryptModel(
7366
+ * { id: "user_123", email: "alice@example.com", createdAt: new Date() },
7367
+ * usersSchema,
7368
+ * )
7369
+ *
7370
+ * if (result.failure) {
7371
+ * console.error(result.failure.message)
7372
+ * } else {
7373
+ * console.log(result.data.id) // string
7374
+ * console.log(result.data.email) // Encrypted
7375
+ * }
7376
+ * ```
7377
+ */
7378
+ encryptModel(input, table) {
7379
+ return new EncryptModelOperation(
7380
+ this.client,
7381
+ input,
7382
+ table
7383
+ );
7427
7384
  }
7428
- /** The semantic query capabilities this domain exposes. Metadata only. */
7429
- getQueryCapabilities() {
7430
- return this.definition.capabilities;
7385
+ /**
7386
+ * Decrypt a model (object) whose fields contain encrypted values.
7387
+ *
7388
+ * Identifies encrypted fields automatically and decrypts them, returning the
7389
+ * model with plaintext values. Returns a thenable operation that supports
7390
+ * `.withLockContext()` for identity-aware decryption.
7391
+ *
7392
+ * @param input - The model object with encrypted field values.
7393
+ * @returns A `DecryptModelOperation<T>` that can be awaited to get a `Result`
7394
+ * containing the model with decrypted plaintext fields, or an `EncryptionError`.
7395
+ *
7396
+ * @example
7397
+ * ```typescript
7398
+ * // Decrypt a previously encrypted model
7399
+ * const decrypted = await client.decryptModel<User>(encryptedUser)
7400
+ *
7401
+ * if (decrypted.failure) {
7402
+ * console.error(decrypted.failure.message)
7403
+ * } else {
7404
+ * console.log(decrypted.data.email) // "alice@example.com"
7405
+ * }
7406
+ *
7407
+ * // With a lock context
7408
+ * const decrypted = await client
7409
+ * .decryptModel<User>(encryptedUser)
7410
+ * .withLockContext(lockContext)
7411
+ * ```
7412
+ */
7413
+ decryptModel(input) {
7414
+ return new DecryptModelOperation(this.client, input);
7431
7415
  }
7432
- /** `true` when this domain can produce at least one kind of query term. */
7433
- isQueryable() {
7434
- return isQueryableCapabilities(
7435
- this.definition.capabilities
7416
+ /**
7417
+ * Encrypt multiple models (objects) in a single bulk operation.
7418
+ *
7419
+ * Performs a single call to ZeroKMS regardless of the number of models,
7420
+ * while still using a unique key for each encrypted value. Only fields
7421
+ * matching the table schema are encrypted; other fields pass through unchanged.
7422
+ *
7423
+ * The return type is **schema-aware**: fields matching the table schema are
7424
+ * typed as `Encrypted`, while other fields retain their original types. For
7425
+ * best results, let TypeScript infer the type parameters from the arguments.
7426
+ *
7427
+ * @param input - An array of model objects with plaintext values to encrypt.
7428
+ * @param table - The table schema defining which fields to encrypt.
7429
+ * @returns A `BulkEncryptModelsOperation` that can be awaited to get a `Result`
7430
+ * containing an array of models with schema-defined fields typed as `Encrypted`,
7431
+ * or an `EncryptionError`.
7432
+ *
7433
+ * @example
7434
+ * ```typescript
7435
+ * import { Encryption } from "@cipherstash/stack"
7436
+ * import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
7437
+ *
7438
+ * type User = { id: string; email: string }
7439
+ *
7440
+ * const usersSchema = encryptedTable("users", {
7441
+ * email: encryptedColumn("email"),
7442
+ * })
7443
+ *
7444
+ * const client = await Encryption({ schemas: [usersSchema] })
7445
+ *
7446
+ * // Let TypeScript infer the return type from the schema.
7447
+ * // Each item's email is typed as `Encrypted`, id stays `string`.
7448
+ * const result = await client.bulkEncryptModels(
7449
+ * [
7450
+ * { id: "1", email: "alice@example.com" },
7451
+ * { id: "2", email: "bob@example.com" },
7452
+ * ],
7453
+ * usersSchema,
7454
+ * )
7455
+ *
7456
+ * if (!result.failure) {
7457
+ * console.log(result.data) // array of models with encrypted email fields
7458
+ * }
7459
+ * ```
7460
+ */
7461
+ bulkEncryptModels(input, table) {
7462
+ return new BulkEncryptModelsOperation(
7463
+ this.client,
7464
+ input,
7465
+ table
7436
7466
  );
7437
7467
  }
7438
- /** Emit the encrypt-config column: `cast_as` plus capability-derived indexes. */
7439
- build() {
7440
- return {
7441
- cast_as: this.definition.castAs,
7442
- indexes: indexesForCapabilities(
7443
- this.definition.capabilities,
7444
- this.definition.castAs
7445
- )
7446
- };
7468
+ /**
7469
+ * Decrypt multiple models (objects) in a single bulk operation.
7470
+ *
7471
+ * Performs a single call to ZeroKMS regardless of the number of models,
7472
+ * restoring all encrypted fields to their original plaintext values.
7473
+ *
7474
+ * @param input - An array of model objects with encrypted field values.
7475
+ * @returns A `BulkDecryptModelsOperation<T>` that can be awaited to get a `Result`
7476
+ * containing an array of models with decrypted plaintext fields, or an `EncryptionError`.
7477
+ *
7478
+ * @example
7479
+ * ```typescript
7480
+ * const encryptedUsers = encryptedResult.data // from bulkEncryptModels
7481
+ *
7482
+ * const result = await client.bulkDecryptModels<User>(encryptedUsers)
7483
+ *
7484
+ * if (!result.failure) {
7485
+ * for (const user of result.data) {
7486
+ * console.log(user.email) // plaintext email
7487
+ * }
7488
+ * }
7489
+ *
7490
+ * // With a lock context
7491
+ * const result = await client
7492
+ * .bulkDecryptModels<User>(encryptedUsers)
7493
+ * .withLockContext(lockContext)
7494
+ * ```
7495
+ */
7496
+ bulkDecryptModels(input) {
7497
+ return new BulkDecryptModelsOperation(this.client, input);
7447
7498
  }
7448
- };
7449
- var TEXT_SEARCH_DOMAIN = {
7450
- eqlType: TEXT_SEARCH_EQL_TYPE,
7451
- castAs: "string",
7452
- capabilities: TEXT_SEARCH
7453
- };
7454
- var EncryptedTextSearchColumn = class extends EncryptedV3Column {
7455
- matchOpts;
7456
- constructor(columnName) {
7457
- super(columnName, TEXT_SEARCH_DOMAIN);
7458
- this.matchOpts = defaultMatchOpts();
7499
+ /**
7500
+ * Encrypt multiple plaintext values in a single bulk operation.
7501
+ *
7502
+ * Each value is encrypted with its own unique key via a single call to ZeroKMS.
7503
+ * Values can include optional `id` fields for correlating results back to
7504
+ * your application data.
7505
+ *
7506
+ * @param plaintexts - An array of objects with `plaintext` (and optional `id`) fields.
7507
+ * @param opts - Options specifying the target column (or nested {@link encryptedField}) and table. See {@link EncryptOptions}.
7508
+ * @returns A `BulkEncryptOperation` that can be awaited to get a `Result`
7509
+ * containing an array of `{ id?, data: Encrypted }` objects, or an `EncryptionError`.
7510
+ *
7511
+ * @example
7512
+ * ```typescript
7513
+ * import { Encryption } from "@cipherstash/stack"
7514
+ * import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
7515
+ *
7516
+ * const users = encryptedTable("users", {
7517
+ * email: encryptedColumn("email"),
7518
+ * })
7519
+ * const client = await Encryption({ schemas: [users] })
7520
+ *
7521
+ * const result = await client.bulkEncrypt(
7522
+ * [
7523
+ * { id: "u1", plaintext: "alice@example.com" },
7524
+ * { id: "u2", plaintext: "bob@example.com" },
7525
+ * ],
7526
+ * { column: users.email, table: users },
7527
+ * )
7528
+ *
7529
+ * if (!result.failure) {
7530
+ * // result.data = [{ id: "u1", data: Encrypted }, { id: "u2", data: Encrypted }, ...]
7531
+ * console.log(result.data)
7532
+ * }
7533
+ * ```
7534
+ */
7535
+ bulkEncrypt(plaintexts, opts) {
7536
+ return new BulkEncryptOperation(this.client, plaintexts, opts);
7459
7537
  }
7460
7538
  /**
7461
- * Tune the match index. Each provided key replaces its default; omitted
7462
- * keys keep the default. This NEVER enables a capability — match is always
7463
- * on for this type. Merge semantics mirror v2's `opts?.x ?? default`.
7539
+ * Decrypt multiple encrypted values in a single bulk operation.
7540
+ *
7541
+ * Performs a single call to ZeroKMS to decrypt all values. The result uses
7542
+ * a multi-status pattern: each item in the returned array has either a `data`
7543
+ * field (success) or an `error` field (failure), allowing graceful handling
7544
+ * of partial failures.
7545
+ *
7546
+ * @param encryptedPayloads - An array of objects with `data` (encrypted payload) and optional `id` fields.
7547
+ * @returns A `BulkDecryptOperation` that can be awaited to get a `Result`
7548
+ * containing an array of `{ id?, data: plaintext }` or `{ id?, error: string }` objects,
7549
+ * or an `EncryptionError` if the entire operation fails.
7550
+ *
7551
+ * @example
7552
+ * ```typescript
7553
+ * const encrypted = await client.bulkEncrypt(plaintexts, { column: users.email, table: users })
7554
+ *
7555
+ * const result = await client.bulkDecrypt(encrypted.data)
7556
+ *
7557
+ * if (!result.failure) {
7558
+ * for (const item of result.data) {
7559
+ * if ("data" in item) {
7560
+ * console.log(`${item.id}: ${item.data}`)
7561
+ * } else {
7562
+ * console.error(`${item.id} failed: ${item.error}`)
7563
+ * }
7564
+ * }
7565
+ * }
7566
+ * ```
7464
7567
  */
7465
- freeTextSearch(opts) {
7466
- const defaults = defaultMatchOpts();
7467
- this.matchOpts = cloneMatchOpts({
7468
- tokenizer: opts?.tokenizer ?? defaults.tokenizer,
7469
- token_filters: opts?.token_filters ?? defaults.token_filters,
7470
- k: opts?.k ?? defaults.k,
7471
- m: opts?.m ?? defaults.m,
7472
- include_original: opts?.include_original ?? defaults.include_original
7473
- });
7474
- return this;
7568
+ bulkDecrypt(encryptedPayloads) {
7569
+ return new BulkDecryptOperation(this.client, encryptedPayloads);
7475
7570
  }
7476
- /** Emit the encrypt-config column. Byte-identical to a v2 equality+order+match column. */
7477
- build() {
7478
- return {
7479
- cast_as: "string",
7480
- indexes: {
7481
- unique: { token_filters: [] },
7482
- ore: {},
7483
- match: cloneMatchOpts(this.matchOpts)
7484
- }
7485
- };
7571
+ /**
7572
+ * Get the encrypt config object.
7573
+ *
7574
+ * @returns The encrypt config object.
7575
+ */
7576
+ getEncryptConfig() {
7577
+ return this.encryptConfig;
7486
7578
  }
7487
7579
  };
7488
- var EncryptedIntegerColumn = class extends EncryptedV3Column {
7489
- };
7490
- var EncryptedIntegerEqColumn = class extends EncryptedV3Column {
7491
- };
7492
- var EncryptedIntegerOrdOreColumn = class extends EncryptedV3Column {
7493
- };
7494
- var EncryptedIntegerOrdColumn = class extends EncryptedV3Column {
7495
- };
7496
- var EncryptedSmallintColumn = class extends EncryptedV3Column {
7497
- };
7498
- var EncryptedSmallintEqColumn = class extends EncryptedV3Column {
7499
- };
7500
- var EncryptedSmallintOrdOreColumn = class extends EncryptedV3Column {
7501
- };
7502
- var EncryptedSmallintOrdColumn = class extends EncryptedV3Column {
7503
- };
7504
- var EncryptedDateColumn = class extends EncryptedV3Column {
7505
- };
7506
- var EncryptedDateEqColumn = class extends EncryptedV3Column {
7507
- };
7508
- var EncryptedDateOrdOreColumn = class extends EncryptedV3Column {
7509
- };
7510
- var EncryptedDateOrdColumn = class extends EncryptedV3Column {
7511
- };
7512
- var EncryptedTimestampColumn = class extends EncryptedV3Column {
7513
- };
7514
- var EncryptedTimestampEqColumn = class extends EncryptedV3Column {
7515
- };
7516
- var EncryptedTimestampOrdOreColumn = class extends EncryptedV3Column {
7517
- };
7518
- var EncryptedTimestampOrdColumn = class extends EncryptedV3Column {
7519
- };
7520
- var EncryptedNumericColumn = class extends EncryptedV3Column {
7521
- };
7522
- var EncryptedNumericEqColumn = class extends EncryptedV3Column {
7523
- };
7524
- var EncryptedNumericOrdOreColumn = class extends EncryptedV3Column {
7525
- };
7526
- var EncryptedNumericOrdColumn = class extends EncryptedV3Column {
7527
- };
7528
- var EncryptedTextColumn = class extends EncryptedV3Column {
7529
- };
7530
- var EncryptedTextEqColumn = class extends EncryptedV3Column {
7531
- };
7532
- var EncryptedTextMatchColumn = class extends EncryptedV3Column {
7533
- };
7534
- var EncryptedTextOrdOreColumn = class extends EncryptedV3Column {
7535
- };
7536
- var EncryptedTextOrdColumn = class extends EncryptedV3Column {
7537
- };
7538
- var EncryptedBooleanColumn = class extends EncryptedV3Column {
7539
- };
7540
- var EncryptedRealColumn = class extends EncryptedV3Column {
7541
- };
7542
- var EncryptedRealEqColumn = class extends EncryptedV3Column {
7543
- };
7544
- var EncryptedRealOrdOreColumn = class extends EncryptedV3Column {
7545
- };
7546
- var EncryptedRealOrdColumn = class extends EncryptedV3Column {
7547
- };
7548
- var EncryptedDoubleColumn = class extends EncryptedV3Column {
7549
- };
7550
- var EncryptedDoubleEqColumn = class extends EncryptedV3Column {
7551
- };
7552
- var EncryptedDoubleOrdOreColumn = class extends EncryptedV3Column {
7553
- };
7554
- var EncryptedDoubleOrdColumn = class extends EncryptedV3Column {
7580
+ var warnedStrategyDeprecated = false;
7581
+ function warnStrategyDeprecated() {
7582
+ if (warnedStrategyDeprecated) return;
7583
+ warnedStrategyDeprecated = true;
7584
+ console.warn(
7585
+ "[encryption]: `config.strategy` is deprecated and will be removed in a future release \u2014 use `config.authStrategy` instead."
7586
+ );
7587
+ }
7588
+ var Encryption = async (config) => {
7589
+ const { schemas, config: clientConfig } = config;
7590
+ if (!schemas.length) {
7591
+ throw new Error(
7592
+ "[encryption]: At least one encryptedTable must be provided to initialize the encryption client"
7593
+ );
7594
+ }
7595
+ if (clientConfig?.keyset && "id" in clientConfig.keyset && !validate_default(clientConfig.keyset.id)) {
7596
+ throw new Error(
7597
+ "[encryption]: Invalid UUID provided for keyset id. Must be a valid UUID."
7598
+ );
7599
+ }
7600
+ if (clientConfig?.strategy) {
7601
+ warnStrategyDeprecated();
7602
+ }
7603
+ const authStrategy = clientConfig?.authStrategy ?? clientConfig?.strategy;
7604
+ const client = new EncryptionClient();
7605
+ const encryptConfig = buildEncryptConfig(...schemas);
7606
+ const eqlVersion = resolveEqlVersion(schemas, clientConfig?.eqlVersion);
7607
+ const result = await client.init({
7608
+ encryptConfig,
7609
+ ...clientConfig,
7610
+ authStrategy,
7611
+ eqlVersion
7612
+ });
7613
+ if (result.failure) {
7614
+ throw new Error(`[encryption]: ${result.failure.message}`);
7615
+ }
7616
+ return result.data;
7555
7617
  };
7556
7618
 
7557
7619
  // src/eql/v3/table.ts
@@ -7563,7 +7625,7 @@ var EncryptedTable = class {
7563
7625
  tableName;
7564
7626
  columnBuilders;
7565
7627
  build() {
7566
- const builtColumns = {};
7628
+ const builtColumns = /* @__PURE__ */ Object.create(null);
7567
7629
  for (const builder of Object.values(this.columnBuilders)) {
7568
7630
  const name = builder.getName();
7569
7631
  if (Object.hasOwn(builtColumns, name)) {
@@ -7584,9 +7646,19 @@ var EncryptedTable = class {
7584
7646
  * encrypt config and FFI by DB name — `build()` keys columns by DB name, so
7585
7647
  * the two only agree when property == name. This recovers the mapping that
7586
7648
  * `build()` discards.
7649
+ *
7650
+ * NULL PROTOTYPE — load-bearing. Callers index this map by a column name that
7651
+ * ultimately comes from the database (`addJsonbCastsV3`, `filterColumnName`,
7652
+ * the mutation transform). On a plain object literal, a column named
7653
+ * `constructor` / `toString` / `valueOf` / `__proto__` resolves to an
7654
+ * inherited `Object.prototype` member, which is truthy — so a *plaintext*
7655
+ * column with such a name would be mistaken for a mapped encrypted column and
7656
+ * its `Object.prototype` value interpolated into the emitted select string.
7657
+ * `encryptedTable()` rejects such names as JS *properties*, but nothing
7658
+ * constrains the DB column names a table may contain.
7587
7659
  */
7588
7660
  buildColumnKeyMap() {
7589
- const map = {};
7661
+ const map = /* @__PURE__ */ Object.create(null);
7590
7662
  for (const [property, builder] of Object.entries(this.columnBuilders)) {
7591
7663
  map[property] = builder.getName();
7592
7664
  }
@@ -7648,6 +7720,12 @@ var types = {
7648
7720
  SmallintEq: (name) => new EncryptedSmallintEqColumn(name, SMALLINT_EQ),
7649
7721
  SmallintOrdOre: (name) => new EncryptedSmallintOrdOreColumn(name, SMALLINT_ORD_ORE),
7650
7722
  SmallintOrd: (name) => new EncryptedSmallintOrdColumn(name, SMALLINT_ORD),
7723
+ // bigint (int8) — plaintext is a JS `bigint`, round-tripped losslessly by
7724
+ // the native protect-ffi boundary (see ./columns)
7725
+ Bigint: (name) => new EncryptedBigintColumn(name, BIGINT),
7726
+ BigintEq: (name) => new EncryptedBigintEqColumn(name, BIGINT_EQ),
7727
+ BigintOrdOre: (name) => new EncryptedBigintOrdOreColumn(name, BIGINT_ORD_ORE),
7728
+ BigintOrd: (name) => new EncryptedBigintOrdColumn(name, BIGINT_ORD),
7651
7729
  // date
7652
7730
  Date: (name) => new EncryptedDateColumn(name, DATE),
7653
7731
  DateEq: (name) => new EncryptedDateEqColumn(name, DATE_EQ),
@@ -7681,7 +7759,14 @@ var types = {
7681
7759
  Double: (name) => new EncryptedDoubleColumn(name, DOUBLE),
7682
7760
  DoubleEq: (name) => new EncryptedDoubleEqColumn(name, DOUBLE_EQ),
7683
7761
  DoubleOrdOre: (name) => new EncryptedDoubleOrdOreColumn(name, DOUBLE_ORD_ORE),
7684
- DoubleOrd: (name) => new EncryptedDoubleOrdColumn(name, DOUBLE_ORD)
7762
+ DoubleOrd: (name) => new EncryptedDoubleOrdColumn(name, DOUBLE_ORD),
7763
+ // json (encrypted JSONB document, ste_vec containment)
7764
+ Json: (name) => new EncryptedJsonColumn(name)
7765
+ // `satisfies` is load-bearing, not decoration: `DOMAIN_REGISTRY` derives itself
7766
+ // by calling every value here at module load. A non-factory export would throw
7767
+ // during module evaluation and take the supabase introspect/schema-build/verify
7768
+ // path down with it. This turns that into a compile error at the offending line.
7769
+ // `as const` applies first, so literal key inference is preserved.
7685
7770
  };
7686
7771
 
7687
7772
  // src/encryption/v3.ts
@@ -7690,7 +7775,7 @@ function rowReconstructor(table) {
7690
7775
  const propToDb = table.buildColumnKeyMap();
7691
7776
  const dateProperties = Object.entries(propToDb).filter(([, dbName]) => {
7692
7777
  const castAs = columns[dbName]?.cast_as;
7693
- return castAs === "date" || castAs === "timestamp";
7778
+ return DATE_LIKE_CASTS.includes(castAs);
7694
7779
  }).map(([property]) => property);
7695
7780
  return (row) => {
7696
7781
  const out = { ...row };
@@ -7702,24 +7787,40 @@ function rowReconstructor(table) {
7702
7787
  return out;
7703
7788
  };
7704
7789
  }
7705
- function typedClient(client, ..._schemas) {
7790
+ function typedClient(client, ...schemas) {
7791
+ const reconstructors = /* @__PURE__ */ new Map();
7792
+ for (const table of schemas) {
7793
+ reconstructors.set(table.tableName, rowReconstructor(table));
7794
+ }
7795
+ const unknownTableFailure = {
7796
+ failure: {
7797
+ type: EncryptionErrorTypes.DecryptionError,
7798
+ message: "[eql/v3]: decryptModel received a table this client was not initialized with \u2014 pass a table given to EncryptionV3/typedClient"
7799
+ }
7800
+ };
7801
+ function encryptQuery(plaintextOrTerms, opts) {
7802
+ return client.encryptQuery(plaintextOrTerms, opts);
7803
+ }
7706
7804
  return {
7707
7805
  encrypt: (plaintext, opts) => client.encrypt(plaintext, opts),
7708
- encryptQuery: (plaintext, opts) => client.encryptQuery(plaintext, opts),
7806
+ encryptQuery,
7709
7807
  encryptModel: (input, table) => client.encryptModel(input, table),
7710
7808
  bulkEncryptModels: (input, table) => client.bulkEncryptModels(input, table),
7711
7809
  decrypt: (encrypted) => client.decrypt(encrypted),
7712
7810
  decryptModel: async (input, table, lockContext) => {
7811
+ const reconstruct = reconstructors.get(table.tableName);
7812
+ if (!reconstruct) return unknownTableFailure;
7713
7813
  const op = client.decryptModel(input);
7714
7814
  const result = await (lockContext ? op.withLockContext(lockContext) : op);
7715
7815
  if (result.failure) return result;
7716
- return { data: rowReconstructor(table)(result.data) };
7816
+ return { data: reconstruct(result.data) };
7717
7817
  },
7718
7818
  bulkDecryptModels: async (input, table, lockContext) => {
7819
+ const reconstruct = reconstructors.get(table.tableName);
7820
+ if (!reconstruct) return unknownTableFailure;
7719
7821
  const op = client.bulkDecryptModels(input);
7720
7822
  const result = await (lockContext ? op.withLockContext(lockContext) : op);
7721
7823
  if (result.failure) return result;
7722
- const reconstruct = rowReconstructor(table);
7723
7824
  return {
7724
7825
  data: result.data.map(
7725
7826
  (row) => reconstruct(row)
@@ -7734,17 +7835,20 @@ function typedClient(client, ..._schemas) {
7734
7835
  async function EncryptionV3(config) {
7735
7836
  const client = await Encryption({
7736
7837
  schemas: config.schemas,
7737
- // v3 schemas emit the EQL v3 wire format. Auto-detection in
7738
- // `Encryption` would resolve the same way (every v3 table carries the
7739
- // `buildColumnKeyMap` marker), but the version is set explicitly here
7740
- // so the contract doesn't hinge on duck-typing while still honouring
7741
- // a caller's explicit override.
7742
- config: { ...config.config, eqlVersion: config.config?.eqlVersion ?? 3 }
7838
+ // Force the v3 EQL wire format. protect-ffi's newClient defaults to
7839
+ // eqlVersion 2; a v2-mode client cannot resolve v3 concrete-type columns
7840
+ // and fails every encrypt with "Cannot convert undefined or null to
7841
+ // object". This is a v3-only invariant, so it overrides any user value.
7842
+ config: { ...config.config, eqlVersion: 3 }
7743
7843
  });
7744
7844
  return typedClient(client, ...config.schemas);
7745
7845
  }
7746
7846
  // Annotate the CommonJS export names for ESM import in node:
7747
7847
  0 && (module.exports = {
7848
+ EncryptedBigintColumn,
7849
+ EncryptedBigintEqColumn,
7850
+ EncryptedBigintOrdColumn,
7851
+ EncryptedBigintOrdOreColumn,
7748
7852
  EncryptedBooleanColumn,
7749
7853
  EncryptedDateColumn,
7750
7854
  EncryptedDateEqColumn,
@@ -7758,6 +7862,7 @@ async function EncryptionV3(config) {
7758
7862
  EncryptedIntegerEqColumn,
7759
7863
  EncryptedIntegerOrdColumn,
7760
7864
  EncryptedIntegerOrdOreColumn,
7865
+ EncryptedJsonColumn,
7761
7866
  EncryptedNumericColumn,
7762
7867
  EncryptedNumericEqColumn,
7763
7868
  EncryptedNumericOrdColumn,