@cipherstash/stack 0.16.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/{chunk-6FD7OBGM.js → chunk-LLCCNKDL.js} +3 -3
  3. package/dist/chunk-LLCCNKDL.js.map +1 -0
  4. package/dist/{chunk-MD6742R6.js → chunk-VPOTNP4K.js} +1 -1
  5. package/dist/chunk-VPOTNP4K.js.map +1 -0
  6. package/dist/{chunk-4RNBI3UH.js → chunk-XWPGAHBZ.js} +1 -1
  7. package/dist/chunk-XWPGAHBZ.js.map +1 -0
  8. package/dist/{chunk-JDZ2QUUF.js → chunk-YWQXTZ5M.js} +4 -2
  9. package/dist/{chunk-JDZ2QUUF.js.map → chunk-YWQXTZ5M.js.map} +1 -1
  10. package/dist/{chunk-CY5B4LIF.js → chunk-ZYOZHZMJ.js} +160 -52
  11. package/dist/chunk-ZYOZHZMJ.js.map +1 -0
  12. package/dist/{client-BLbA6aFD.d.ts → client-CIsfZ2zg.d.ts} +17 -22
  13. package/dist/{client-C_7TF8EL.d.cts → client-Dt98Rugc.d.cts} +17 -22
  14. package/dist/client.cjs +2 -0
  15. package/dist/client.cjs.map +1 -1
  16. package/dist/client.d.cts +2 -2
  17. package/dist/client.d.ts +2 -2
  18. package/dist/client.js +1 -1
  19. package/dist/drizzle/index.cjs +6 -3
  20. package/dist/drizzle/index.cjs.map +1 -1
  21. package/dist/drizzle/index.d.cts +2 -2
  22. package/dist/drizzle/index.d.ts +2 -2
  23. package/dist/drizzle/index.js +6 -5
  24. package/dist/drizzle/index.js.map +1 -1
  25. package/dist/dynamodb/index.cjs +14 -4
  26. package/dist/dynamodb/index.cjs.map +1 -1
  27. package/dist/dynamodb/index.d.cts +12 -12
  28. package/dist/dynamodb/index.d.ts +12 -12
  29. package/dist/dynamodb/index.js +9 -3
  30. package/dist/dynamodb/index.js.map +1 -1
  31. package/dist/encryption/index.cjs +168 -50
  32. package/dist/encryption/index.cjs.map +1 -1
  33. package/dist/encryption/index.d.cts +2 -2
  34. package/dist/encryption/index.d.ts +2 -2
  35. package/dist/encryption/index.js +25 -5
  36. package/dist/identity/index.cjs +2 -2
  37. package/dist/identity/index.cjs.map +1 -1
  38. package/dist/identity/index.js +3 -3
  39. package/dist/index.cjs +150 -50
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.cts +3 -3
  42. package/dist/index.d.ts +3 -3
  43. package/dist/index.js +5 -5
  44. package/dist/schema/index.cjs +3 -1
  45. package/dist/schema/index.cjs.map +1 -1
  46. package/dist/schema/index.d.cts +1 -1
  47. package/dist/schema/index.d.ts +1 -1
  48. package/dist/schema/index.js +1 -1
  49. package/dist/supabase/index.cjs +2 -2
  50. package/dist/supabase/index.cjs.map +1 -1
  51. package/dist/supabase/index.d.cts +2 -2
  52. package/dist/supabase/index.d.ts +2 -2
  53. package/dist/supabase/index.js +3 -3
  54. package/dist/{types-public-Dv-r6Ik0.d.ts → types-public-BTxqjmtv.d.cts} +95 -80
  55. package/dist/{types-public-Dv-r6Ik0.d.cts → types-public-BTxqjmtv.d.ts} +95 -80
  56. package/dist/types-public.cjs.map +1 -1
  57. package/dist/types-public.d.cts +1 -1
  58. package/dist/types-public.d.ts +1 -1
  59. package/dist/types-public.js +1 -1
  60. package/package.json +12 -12
  61. package/dist/chunk-4RNBI3UH.js.map +0 -1
  62. package/dist/chunk-6FD7OBGM.js.map +0 -1
  63. package/dist/chunk-CY5B4LIF.js.map +0 -1
  64. package/dist/chunk-MD6742R6.js.map +0 -1
package/dist/index.cjs CHANGED
@@ -292,6 +292,8 @@ var EncryptedTable = class {
292
292
  this.tableName = tableName;
293
293
  this.columnBuilders = columnBuilders;
294
294
  }
295
+ tableName;
296
+ columnBuilders;
295
297
  /**
296
298
  * Compile this table schema into a `TableDefinition` used internally by the encryption client.
297
299
  *
@@ -368,7 +370,7 @@ function encryptedField(valueName) {
368
370
  }
369
371
  function buildEncryptConfig(...protectTables) {
370
372
  const config = {
371
- v: 2,
373
+ v: 1,
372
374
  tables: {}
373
375
  };
374
376
  for (const tb of protectTables) {
@@ -387,7 +389,7 @@ var EncryptionErrorTypes = {
387
389
  CtsTokenError: "CtsTokenError"
388
390
  };
389
391
 
390
- // ../../node_modules/.pnpm/evlog@1.9.0/node_modules/evlog/dist/utils.mjs
392
+ // ../../node_modules/.pnpm/evlog@1.11.0_next@15.5.10_react-dom@19.2.3_react@19.2.3__react@19.2.3__react@19.2.3/node_modules/evlog/dist/utils.mjs
391
393
  function formatDuration(ms) {
392
394
  if (ms < 1e3) return `${Math.round(ms)}ms`;
393
395
  return `${(ms / 1e3).toFixed(2)}s`;
@@ -443,7 +445,7 @@ function matchesPattern(path, pattern) {
443
445
  return new RegExp(`^${regexPattern}$`).test(path);
444
446
  }
445
447
 
446
- // ../../node_modules/.pnpm/evlog@1.9.0/node_modules/evlog/dist/logger.mjs
448
+ // ../../node_modules/.pnpm/evlog@1.11.0_next@15.5.10_react-dom@19.2.3_react@19.2.3__react@19.2.3__react@19.2.3/node_modules/evlog/dist/logger.mjs
447
449
  function isPlainObject(val) {
448
450
  return val !== null && typeof val === "object" && !Array.isArray(val);
449
451
  }
@@ -952,6 +954,15 @@ var EncryptionOperation = class {
952
954
  };
953
955
 
954
956
  // src/encryption/operations/batch-encrypt-query.ts
957
+ function filterNullTerms(terms) {
958
+ const nonNullTerms = [];
959
+ terms.forEach((term, index) => {
960
+ if (term.value !== null && term.value !== void 0) {
961
+ nonNullTerms.push({ term, originalIndex: index });
962
+ }
963
+ });
964
+ return { nonNullTerms };
965
+ }
955
966
  function buildQueryPayload(term, lockContext) {
956
967
  assertValidNumericValue(term.value);
957
968
  const { indexType, queryOp } = resolveIndexType(
@@ -972,10 +983,15 @@ function buildQueryPayload(term, lockContext) {
972
983
  }
973
984
  return payload;
974
985
  }
975
- function assembleResults(terms, encryptedValues) {
976
- return terms.map(
977
- (term, i) => formatEncryptedResult(encryptedValues[i], term.returnType)
978
- );
986
+ function assembleResults(totalLength, encryptedValues, nonNullTerms) {
987
+ const results = new Array(totalLength).fill(null);
988
+ nonNullTerms.forEach(({ term, originalIndex }, i) => {
989
+ results[originalIndex] = formatEncryptedResult(
990
+ encryptedValues[i],
991
+ term.returnType
992
+ );
993
+ });
994
+ return results;
979
995
  }
980
996
  var BatchEncryptQueryOperation = class extends EncryptionOperation {
981
997
  constructor(client, terms) {
@@ -983,6 +999,8 @@ var BatchEncryptQueryOperation = class extends EncryptionOperation {
983
999
  this.client = client;
984
1000
  this.terms = terms;
985
1001
  }
1002
+ client;
1003
+ terms;
986
1004
  withLockContext(lockContext) {
987
1005
  return new BatchEncryptQueryOperationWithLockContext(
988
1006
  this.client,
@@ -1002,18 +1020,23 @@ var BatchEncryptQueryOperation = class extends EncryptionOperation {
1002
1020
  log.emit();
1003
1021
  return { data: [] };
1004
1022
  }
1023
+ const { nonNullTerms } = filterNullTerms(this.terms);
1024
+ if (nonNullTerms.length === 0) {
1025
+ log.emit();
1026
+ return { data: this.terms.map(() => null) };
1027
+ }
1005
1028
  const result = await (0, import_result.withResult)(
1006
1029
  async () => {
1007
1030
  if (!this.client) throw noClientError();
1008
1031
  const { metadata } = this.getAuditData();
1009
- const queries = this.terms.map(
1010
- (term) => buildQueryPayload(term)
1032
+ const queries = nonNullTerms.map(
1033
+ ({ term }) => buildQueryPayload(term)
1011
1034
  );
1012
1035
  const encrypted = await (0, import_protect_ffi2.encryptQueryBulk)(this.client, {
1013
1036
  queries,
1014
1037
  unverifiedContext: metadata
1015
1038
  });
1016
- return assembleResults(this.terms, encrypted);
1039
+ return assembleResults(this.terms.length, encrypted, nonNullTerms);
1017
1040
  },
1018
1041
  (error) => {
1019
1042
  log.set({ errorCode: getErrorCode(error) ?? "unknown" });
@@ -1036,6 +1059,9 @@ var BatchEncryptQueryOperationWithLockContext = class extends EncryptionOperatio
1036
1059
  this.lockContext = lockContext;
1037
1060
  this.auditMetadata = auditMetadata;
1038
1061
  }
1062
+ client;
1063
+ terms;
1064
+ lockContext;
1039
1065
  async execute() {
1040
1066
  const log = createRequestLogger();
1041
1067
  log.set({
@@ -1047,6 +1073,11 @@ var BatchEncryptQueryOperationWithLockContext = class extends EncryptionOperatio
1047
1073
  log.emit();
1048
1074
  return { data: [] };
1049
1075
  }
1076
+ const { nonNullTerms } = filterNullTerms(this.terms);
1077
+ if (nonNullTerms.length === 0) {
1078
+ log.emit();
1079
+ return { data: this.terms.map(() => null) };
1080
+ }
1050
1081
  const lockContextResult = await this.lockContext.getLockContext();
1051
1082
  if (lockContextResult.failure) {
1052
1083
  log.emit();
@@ -1057,15 +1088,15 @@ var BatchEncryptQueryOperationWithLockContext = class extends EncryptionOperatio
1057
1088
  async () => {
1058
1089
  if (!this.client) throw noClientError();
1059
1090
  const { metadata } = this.getAuditData();
1060
- const queries = this.terms.map(
1061
- (term) => buildQueryPayload(term, context)
1091
+ const queries = nonNullTerms.map(
1092
+ ({ term }) => buildQueryPayload(term, context)
1062
1093
  );
1063
1094
  const encrypted = await (0, import_protect_ffi2.encryptQueryBulk)(this.client, {
1064
1095
  queries,
1065
1096
  serviceToken: ctsToken,
1066
1097
  unverifiedContext: metadata
1067
1098
  });
1068
- return assembleResults(this.terms, encrypted);
1099
+ return assembleResults(this.terms.length, encrypted, nonNullTerms);
1069
1100
  },
1070
1101
  (error) => {
1071
1102
  log.set({ errorCode: getErrorCode(error) ?? "unknown" });
@@ -1085,25 +1116,30 @@ var BatchEncryptQueryOperationWithLockContext = class extends EncryptionOperatio
1085
1116
  var import_result2 = require("@byteslice/result");
1086
1117
  var import_protect_ffi3 = require("@cipherstash/protect-ffi");
1087
1118
  var createDecryptPayloads = (encryptedPayloads, lockContext) => {
1088
- return encryptedPayloads.map(({ id, data }) => ({
1119
+ return encryptedPayloads.filter(({ data }) => data !== null).map(({ id, data }) => ({
1089
1120
  id,
1090
1121
  ciphertext: data,
1091
1122
  ...lockContext && { lockContext }
1092
1123
  }));
1093
1124
  };
1125
+ var createNullResult = (encryptedPayloads) => encryptedPayloads.map(({ id }) => ({ id, data: null }));
1094
1126
  var mapDecryptedDataToResult = (encryptedPayloads, decryptedData) => {
1095
- return decryptedData.map((decryptResult, i) => {
1096
- if ("error" in decryptResult) {
1097
- return {
1098
- id: encryptedPayloads[i].id,
1099
- error: decryptResult.error
1100
- };
1127
+ const result = new Array(encryptedPayloads.length);
1128
+ let decryptedIndex = 0;
1129
+ for (let i = 0; i < encryptedPayloads.length; i++) {
1130
+ if (encryptedPayloads[i].data === null) {
1131
+ result[i] = { id: encryptedPayloads[i].id, data: null };
1132
+ } else {
1133
+ const decryptResult = decryptedData[decryptedIndex];
1134
+ if ("error" in decryptResult) {
1135
+ result[i] = { id: encryptedPayloads[i].id, error: decryptResult.error };
1136
+ } else {
1137
+ result[i] = { id: encryptedPayloads[i].id, data: decryptResult.data };
1138
+ }
1139
+ decryptedIndex++;
1101
1140
  }
1102
- return {
1103
- id: encryptedPayloads[i].id,
1104
- data: decryptResult.data
1105
- };
1106
- });
1141
+ }
1142
+ return result;
1107
1143
  };
1108
1144
  var BulkDecryptOperation = class extends EncryptionOperation {
1109
1145
  client;
@@ -1128,10 +1164,13 @@ var BulkDecryptOperation = class extends EncryptionOperation {
1128
1164
  if (!this.client) throw noClientError();
1129
1165
  if (!this.encryptedPayloads || this.encryptedPayloads.length === 0)
1130
1166
  return [];
1131
- const payloads = createDecryptPayloads(this.encryptedPayloads);
1167
+ const nonNullPayloads = createDecryptPayloads(this.encryptedPayloads);
1168
+ if (nonNullPayloads.length === 0) {
1169
+ return createNullResult(this.encryptedPayloads);
1170
+ }
1132
1171
  const { metadata } = this.getAuditData();
1133
1172
  const decryptedData = await (0, import_protect_ffi3.decryptBulkFallible)(this.client, {
1134
- ciphertexts: payloads,
1173
+ ciphertexts: nonNullPayloads,
1135
1174
  unverifiedContext: metadata
1136
1175
  });
1137
1176
  return mapDecryptedDataToResult(this.encryptedPayloads, decryptedData);
@@ -1183,13 +1222,16 @@ var BulkDecryptOperationWithLockContext = class extends EncryptionOperation {
1183
1222
  if (context.failure) {
1184
1223
  throw new Error(`[encryption]: ${context.failure.message}`);
1185
1224
  }
1186
- const payloads = createDecryptPayloads(
1225
+ const nonNullPayloads = createDecryptPayloads(
1187
1226
  encryptedPayloads,
1188
1227
  context.data.context
1189
1228
  );
1229
+ if (nonNullPayloads.length === 0) {
1230
+ return createNullResult(encryptedPayloads);
1231
+ }
1190
1232
  const { metadata } = this.getAuditData();
1191
1233
  const decryptedData = await (0, import_protect_ffi3.decryptBulkFallible)(client, {
1192
- ciphertexts: payloads,
1234
+ ciphertexts: nonNullPayloads,
1193
1235
  serviceToken: context.data.ctsToken,
1194
1236
  unverifiedContext: metadata
1195
1237
  });
@@ -1828,7 +1870,7 @@ var BulkDecryptModelsOperationWithLockContext = class extends EncryptionOperatio
1828
1870
  var import_result4 = require("@byteslice/result");
1829
1871
  var import_protect_ffi5 = require("@cipherstash/protect-ffi");
1830
1872
  var createEncryptPayloads = (plaintexts, column, table, lockContext) => {
1831
- return plaintexts.map(({ id, plaintext }) => ({
1873
+ return plaintexts.filter(({ plaintext }) => plaintext !== null).map(({ id, plaintext }) => ({
1832
1874
  id,
1833
1875
  plaintext,
1834
1876
  column: column.getName(),
@@ -1836,6 +1878,20 @@ var createEncryptPayloads = (plaintexts, column, table, lockContext) => {
1836
1878
  ...lockContext && { lockContext }
1837
1879
  }));
1838
1880
  };
1881
+ var createNullResult2 = (plaintexts) => plaintexts.map(({ id }) => ({ id, data: null }));
1882
+ var mapEncryptedDataToResult = (plaintexts, encryptedData) => {
1883
+ const result = new Array(plaintexts.length);
1884
+ let encryptedIndex = 0;
1885
+ for (let i = 0; i < plaintexts.length; i++) {
1886
+ if (plaintexts[i].plaintext === null) {
1887
+ result[i] = { id: plaintexts[i].id, data: null };
1888
+ } else {
1889
+ result[i] = { id: plaintexts[i].id, data: encryptedData[encryptedIndex] };
1890
+ encryptedIndex++;
1891
+ }
1892
+ }
1893
+ return result;
1894
+ };
1839
1895
  var BulkEncryptOperation = class extends EncryptionOperation {
1840
1896
  client;
1841
1897
  plaintexts;
@@ -1868,20 +1924,20 @@ var BulkEncryptOperation = class extends EncryptionOperation {
1868
1924
  if (!this.plaintexts || this.plaintexts.length === 0) {
1869
1925
  return [];
1870
1926
  }
1871
- const payloads = createEncryptPayloads(
1927
+ const nonNullPayloads = createEncryptPayloads(
1872
1928
  this.plaintexts,
1873
1929
  this.column,
1874
1930
  this.table
1875
1931
  );
1932
+ if (nonNullPayloads.length === 0) {
1933
+ return createNullResult2(this.plaintexts);
1934
+ }
1876
1935
  const { metadata } = this.getAuditData();
1877
1936
  const encryptedData = await (0, import_protect_ffi5.encryptBulk)(this.client, {
1878
- plaintexts: payloads,
1937
+ plaintexts: nonNullPayloads,
1879
1938
  unverifiedContext: metadata
1880
1939
  });
1881
- return encryptedData.map((data, i) => ({
1882
- id: this.plaintexts[i].id,
1883
- data
1884
- }));
1940
+ return mapEncryptedDataToResult(this.plaintexts, encryptedData);
1885
1941
  },
1886
1942
  (error) => {
1887
1943
  log.set({ errorCode: getErrorCode(error) ?? "unknown" });
@@ -1938,22 +1994,22 @@ var BulkEncryptOperationWithLockContext = class extends EncryptionOperation {
1938
1994
  if (context.failure) {
1939
1995
  throw new Error(`[encryption]: ${context.failure.message}`);
1940
1996
  }
1941
- const payloads = createEncryptPayloads(
1997
+ const nonNullPayloads = createEncryptPayloads(
1942
1998
  plaintexts,
1943
1999
  column,
1944
2000
  table,
1945
2001
  context.data.context
1946
2002
  );
2003
+ if (nonNullPayloads.length === 0) {
2004
+ return createNullResult2(plaintexts);
2005
+ }
1947
2006
  const { metadata } = this.getAuditData();
1948
2007
  const encryptedData = await (0, import_protect_ffi5.encryptBulk)(client, {
1949
- plaintexts: payloads,
2008
+ plaintexts: nonNullPayloads,
1950
2009
  serviceToken: context.data.ctsToken,
1951
2010
  unverifiedContext: metadata
1952
2011
  });
1953
- return encryptedData.map((data, i) => ({
1954
- id: plaintexts[i].id,
1955
- data
1956
- }));
2012
+ return mapEncryptedDataToResult(plaintexts, encryptedData);
1957
2013
  },
1958
2014
  (error) => {
1959
2015
  log.set({ errorCode: getErrorCode(error) ?? "unknown" });
@@ -2083,6 +2139,10 @@ var import_result6 = require("@byteslice/result");
2083
2139
  var import_protect_ffi6 = require("@cipherstash/protect-ffi");
2084
2140
  var DecryptOperation = class extends EncryptionOperation {
2085
2141
  client;
2142
+ // Internally widened to allow null so the runtime guard below can
2143
+ // short-circuit on legacy / manually-NULLed rows. The public
2144
+ // `Encryption.decrypt()` signature still rejects null at the type
2145
+ // layer; this is defense in depth for direct construction.
2086
2146
  encryptedData;
2087
2147
  constructor(client, encryptedData) {
2088
2148
  super();
@@ -2103,6 +2163,9 @@ var DecryptOperation = class extends EncryptionOperation {
2103
2163
  if (!this.client) {
2104
2164
  throw noClientError();
2105
2165
  }
2166
+ if (this.encryptedData === null) {
2167
+ return null;
2168
+ }
2106
2169
  const { metadata } = this.getAuditData();
2107
2170
  return await (0, import_protect_ffi6.decrypt)(this.client, {
2108
2171
  ciphertext: this.encryptedData,
@@ -2153,6 +2216,9 @@ var DecryptOperationWithLockContext = class extends EncryptionOperation {
2153
2216
  if (!client) {
2154
2217
  throw noClientError();
2155
2218
  }
2219
+ if (encryptedData === null) {
2220
+ return null;
2221
+ }
2156
2222
  const { metadata } = this.getAuditData();
2157
2223
  const context = await this.lockContext.getLockContext();
2158
2224
  if (context.failure) {
@@ -2280,6 +2346,10 @@ var import_result8 = require("@byteslice/result");
2280
2346
  var import_protect_ffi7 = require("@cipherstash/protect-ffi");
2281
2347
  var EncryptOperation = class extends EncryptionOperation {
2282
2348
  client;
2349
+ // Internally widened to allow null so the runtime guard below can
2350
+ // short-circuit. The public `Encryption.encrypt()` signature still
2351
+ // rejects null at the type layer; this is defense in depth for callers
2352
+ // that reach this class through casts or dynamic field walking.
2283
2353
  plaintext;
2284
2354
  column;
2285
2355
  table;
@@ -2306,6 +2376,9 @@ var EncryptOperation = class extends EncryptionOperation {
2306
2376
  if (!this.client) {
2307
2377
  throw noClientError();
2308
2378
  }
2379
+ if (this.plaintext === null) {
2380
+ return null;
2381
+ }
2309
2382
  if (typeof this.plaintext === "number" && Number.isNaN(this.plaintext)) {
2310
2383
  throw new Error("[encryption]: Cannot encrypt NaN value");
2311
2384
  }
@@ -2367,6 +2440,9 @@ var EncryptOperationWithLockContext = class extends EncryptionOperation {
2367
2440
  if (!client) {
2368
2441
  throw noClientError();
2369
2442
  }
2443
+ if (plaintext === null) {
2444
+ return null;
2445
+ }
2370
2446
  const { metadata } = this.getAuditData();
2371
2447
  const context = await this.lockContext.getLockContext();
2372
2448
  if (context.failure) {
@@ -2512,6 +2588,9 @@ var EncryptQueryOperation = class extends EncryptionOperation {
2512
2588
  this.plaintext = plaintext;
2513
2589
  this.opts = opts;
2514
2590
  }
2591
+ client;
2592
+ plaintext;
2593
+ opts;
2515
2594
  withLockContext(lockContext) {
2516
2595
  return new EncryptQueryOperationWithLockContext(
2517
2596
  this.client,
@@ -2530,7 +2609,12 @@ var EncryptQueryOperation = class extends EncryptionOperation {
2530
2609
  queryType: this.opts.queryType,
2531
2610
  lockContext: false
2532
2611
  });
2533
- const validationError = validateNumericValue(this.plaintext);
2612
+ if (this.plaintext === null || this.plaintext === void 0) {
2613
+ log.emit();
2614
+ return { data: null };
2615
+ }
2616
+ const plaintext = this.plaintext;
2617
+ const validationError = validateNumericValue(plaintext);
2534
2618
  if (validationError?.failure) {
2535
2619
  log.emit();
2536
2620
  return { failure: validationError.failure };
@@ -2542,15 +2626,15 @@ var EncryptQueryOperation = class extends EncryptionOperation {
2542
2626
  const { indexType, queryOp } = resolveIndexType(
2543
2627
  this.opts.column,
2544
2628
  this.opts.queryType,
2545
- this.plaintext
2629
+ plaintext
2546
2630
  );
2547
2631
  assertValueIndexCompatibility(
2548
- this.plaintext,
2632
+ plaintext,
2549
2633
  indexType,
2550
2634
  this.opts.column.getName()
2551
2635
  );
2552
2636
  const encrypted = await (0, import_protect_ffi8.encryptQuery)(this.client, {
2553
- plaintext: this.plaintext,
2637
+ plaintext,
2554
2638
  column: this.opts.column.getName(),
2555
2639
  table: this.opts.table.tableName,
2556
2640
  indexType,
@@ -2584,6 +2668,10 @@ var EncryptQueryOperationWithLockContext = class extends EncryptionOperation {
2584
2668
  this.lockContext = lockContext;
2585
2669
  this.auditMetadata = auditMetadata;
2586
2670
  }
2671
+ client;
2672
+ plaintext;
2673
+ opts;
2674
+ lockContext;
2587
2675
  async execute() {
2588
2676
  const log = createRequestLogger();
2589
2677
  log.set({
@@ -2593,7 +2681,12 @@ var EncryptQueryOperationWithLockContext = class extends EncryptionOperation {
2593
2681
  queryType: this.opts.queryType,
2594
2682
  lockContext: true
2595
2683
  });
2596
- const validationError = validateNumericValue(this.plaintext);
2684
+ if (this.plaintext === null || this.plaintext === void 0) {
2685
+ log.emit();
2686
+ return { data: null };
2687
+ }
2688
+ const plaintext = this.plaintext;
2689
+ const validationError = validateNumericValue(plaintext);
2597
2690
  if (validationError?.failure) {
2598
2691
  log.emit();
2599
2692
  return { failure: validationError.failure };
@@ -2611,15 +2704,15 @@ var EncryptQueryOperationWithLockContext = class extends EncryptionOperation {
2611
2704
  const { indexType, queryOp } = resolveIndexType(
2612
2705
  this.opts.column,
2613
2706
  this.opts.queryType,
2614
- this.plaintext
2707
+ plaintext
2615
2708
  );
2616
2709
  assertValueIndexCompatibility(
2617
- this.plaintext,
2710
+ plaintext,
2618
2711
  indexType,
2619
2712
  this.opts.column.getName()
2620
2713
  );
2621
2714
  const encrypted = await (0, import_protect_ffi8.encryptQuery)(this.client, {
2622
- plaintext: this.plaintext,
2715
+ plaintext,
2623
2716
  column: this.opts.column.getName(),
2624
2717
  table: this.opts.table.tableName,
2625
2718
  indexType,
@@ -2815,6 +2908,13 @@ var EncryptionClient = class {
2815
2908
  * .withLockContext(lockContext)
2816
2909
  * ```
2817
2910
  *
2911
+ * @remarks
2912
+ * The public input type rejects null, but at runtime `decrypt` will
2913
+ * short-circuit and return null when given a null ciphertext
2914
+ * (defense in depth for legacy / manually-NULLed DB rows reached via
2915
+ * casts or dynamic field walking). The narrow return type holds for
2916
+ * any caller that respects the input contract.
2917
+ *
2818
2918
  * @see {@link LockContext}
2819
2919
  * @see {@link DecryptOperation}
2820
2920
  */