@cipherstash/stack 0.18.0 → 1.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CHANGELOG.md +704 -0
  2. package/README.md +97 -79
  3. package/dist/adapter-kit.cjs +923 -0
  4. package/dist/adapter-kit.cjs.map +1 -0
  5. package/dist/adapter-kit.d.cts +82 -0
  6. package/dist/adapter-kit.d.ts +82 -0
  7. package/dist/adapter-kit.js +47 -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-VPOTNP4K.js → chunk-3B5ZX3IS.js} +3 -1
  12. package/dist/chunk-3B5ZX3IS.js.map +1 -0
  13. package/dist/chunk-7333ZC6L.js +48 -0
  14. package/dist/chunk-7333ZC6L.js.map +1 -0
  15. package/dist/{chunk-XWPGAHBZ.js → chunk-BNO32ZMV.js} +1 -1
  16. package/dist/chunk-BNO32ZMV.js.map +1 -0
  17. package/dist/chunk-CLM7E4I6.js +4439 -0
  18. package/dist/chunk-CLM7E4I6.js.map +1 -0
  19. package/dist/{chunk-LLCCNKDL.js → chunk-HQANMV7R.js} +3 -3
  20. package/dist/{chunk-LLCCNKDL.js.map → chunk-HQANMV7R.js.map} +1 -1
  21. package/dist/chunk-JSG2AMDI.js +17 -0
  22. package/dist/chunk-JSG2AMDI.js.map +1 -0
  23. package/dist/chunk-OFQ555AX.js +191 -0
  24. package/dist/chunk-OFQ555AX.js.map +1 -0
  25. package/dist/chunk-PZ5AY32C.js +10 -0
  26. package/dist/chunk-PZ5AY32C.js.map +1 -0
  27. package/dist/chunk-V2Q3NYIH.js +494 -0
  28. package/dist/chunk-V2Q3NYIH.js.map +1 -0
  29. package/dist/chunk-X3JRXEIB.js +98 -0
  30. package/dist/chunk-X3JRXEIB.js.map +1 -0
  31. package/dist/{chunk-ZYOZHZMJ.js → chunk-ZTP5QJ27.js} +193 -169
  32. package/dist/chunk-ZTP5QJ27.js.map +1 -0
  33. package/dist/client.cjs +4124 -49
  34. package/dist/client.cjs.map +1 -1
  35. package/dist/client.d.cts +6 -5
  36. package/dist/client.d.ts +6 -5
  37. package/dist/client.js +3 -1
  38. package/dist/columns-D2_YzrCX.d.cts +622 -0
  39. package/dist/columns-rZc7fQHI.d.ts +622 -0
  40. package/dist/dynamodb/index.cjs +19 -12
  41. package/dist/dynamodb/index.cjs.map +1 -1
  42. package/dist/dynamodb/index.d.cts +3 -2
  43. package/dist/dynamodb/index.d.ts +3 -2
  44. package/dist/dynamodb/index.js +8 -10
  45. package/dist/dynamodb/index.js.map +1 -1
  46. package/dist/encryption/index.cjs +4496 -222
  47. package/dist/encryption/index.cjs.map +1 -1
  48. package/dist/encryption/index.d.cts +807 -6
  49. package/dist/encryption/index.d.ts +807 -6
  50. package/dist/encryption/index.js +15 -7
  51. package/dist/encryption/v3.cjs +7896 -0
  52. package/dist/encryption/v3.cjs.map +1 -0
  53. package/dist/encryption/v3.d.cts +103 -0
  54. package/dist/encryption/v3.d.ts +103 -0
  55. package/dist/encryption/v3.js +189 -0
  56. package/dist/encryption/v3.js.map +1 -0
  57. package/dist/eql/v3/index.cjs +663 -0
  58. package/dist/eql/v3/index.cjs.map +1 -0
  59. package/dist/eql/v3/index.d.cts +173 -0
  60. package/dist/eql/v3/index.d.ts +173 -0
  61. package/dist/eql/v3/index.js +99 -0
  62. package/dist/eql/v3/index.js.map +1 -0
  63. package/dist/errors/index.js +1 -0
  64. package/dist/identity/index.cjs +55 -35
  65. package/dist/identity/index.cjs.map +1 -1
  66. package/dist/identity/index.d.cts +54 -30
  67. package/dist/identity/index.d.ts +54 -30
  68. package/dist/identity/index.js +9 -177
  69. package/dist/identity/index.js.map +1 -1
  70. package/dist/index-BquA71_Y.d.ts +24 -0
  71. package/dist/index-fhWTOV0K.d.cts +24 -0
  72. package/dist/index.cjs +4547 -243
  73. package/dist/index.cjs.map +1 -1
  74. package/dist/index.d.cts +18 -19
  75. package/dist/index.d.ts +18 -19
  76. package/dist/index.js +20 -5
  77. package/dist/index.js.map +1 -1
  78. package/dist/schema/index.cjs +4128 -49
  79. package/dist/schema/index.cjs.map +1 -1
  80. package/dist/schema/index.d.cts +1 -1
  81. package/dist/schema/index.d.ts +1 -1
  82. package/dist/schema/index.js +3 -1
  83. package/dist/{types-public-BTxqjmtv.d.cts → types-public-QMjYNfQO.d.cts} +448 -251
  84. package/dist/{types-public-BTxqjmtv.d.ts → types-public-QMjYNfQO.d.ts} +448 -251
  85. package/dist/types-public.cjs.map +1 -1
  86. package/dist/types-public.d.cts +2 -2
  87. package/dist/types-public.d.ts +2 -2
  88. package/dist/types-public.js +2 -1
  89. package/dist/wasm-inline.d.ts +1643 -0
  90. package/dist/wasm-inline.js +4901 -0
  91. package/dist/wasm-inline.js.map +1 -0
  92. package/package.json +74 -52
  93. package/dist/chunk-VPOTNP4K.js.map +0 -1
  94. package/dist/chunk-XWPGAHBZ.js.map +0 -1
  95. package/dist/chunk-YWQXTZ5M.js +0 -383
  96. package/dist/chunk-YWQXTZ5M.js.map +0 -1
  97. package/dist/chunk-ZYOZHZMJ.js.map +0 -1
  98. package/dist/client-CIsfZ2zg.d.ts +0 -692
  99. package/dist/client-Dt98Rugc.d.cts +0 -692
  100. package/dist/drizzle/index.cjs +0 -1558
  101. package/dist/drizzle/index.cjs.map +0 -1
  102. package/dist/drizzle/index.d.cts +0 -358
  103. package/dist/drizzle/index.d.ts +0 -358
  104. package/dist/drizzle/index.js +0 -1215
  105. package/dist/drizzle/index.js.map +0 -1
  106. package/dist/supabase/index.cjs +0 -1526
  107. package/dist/supabase/index.cjs.map +0 -1
  108. package/dist/supabase/index.d.cts +0 -144
  109. package/dist/supabase/index.d.ts +0 -144
  110. package/dist/supabase/index.js +0 -899
  111. package/dist/supabase/index.js.map +0 -1
@@ -1,899 +0,0 @@
1
- import {
2
- bulkModelsToEncryptedPgComposites,
3
- modelToEncryptedPgComposites
4
- } from "../chunk-XWPGAHBZ.js";
5
- import {
6
- EncryptedColumn
7
- } from "../chunk-YWQXTZ5M.js";
8
- import {
9
- logger
10
- } from "../chunk-LLCCNKDL.js";
11
-
12
- // src/supabase/helpers.ts
13
- function getEncryptedColumnNames(schema) {
14
- const built = schema.build();
15
- return Object.keys(built.columns);
16
- }
17
- function isEncryptedColumn(columnName, encryptedColumnNames) {
18
- return encryptedColumnNames.includes(columnName);
19
- }
20
- function addJsonbCasts(columns, encryptedColumnNames) {
21
- return columns.split(",").map((col) => {
22
- const trimmed = col.trim();
23
- if (!trimmed) return col;
24
- if (trimmed.includes("::")) return col;
25
- if (trimmed.includes("(") || trimmed.includes(".")) return col;
26
- const parts = trimmed.split(/\s+/);
27
- const colName = parts[0];
28
- if (isEncryptedColumn(colName, encryptedColumnNames)) {
29
- const leadingWhitespace = col.match(/^(\s*)/)?.[1] ?? "";
30
- if (parts.length > 1) {
31
- return `${leadingWhitespace}${colName}::jsonb ${parts.slice(1).join(" ")}`;
32
- }
33
- return `${leadingWhitespace}${colName}::jsonb`;
34
- }
35
- return col;
36
- }).join(",");
37
- }
38
- function mapFilterOpToQueryType(op) {
39
- switch (op) {
40
- case "eq":
41
- case "neq":
42
- case "in":
43
- case "is":
44
- return "equality";
45
- case "like":
46
- case "ilike":
47
- return "freeTextSearch";
48
- case "gt":
49
- case "gte":
50
- case "lt":
51
- case "lte":
52
- return "orderAndRange";
53
- default:
54
- return "equality";
55
- }
56
- }
57
- function parseOrString(orString) {
58
- const conditions = [];
59
- const parts = splitOrString(orString);
60
- for (const part of parts) {
61
- const trimmed = part.trim();
62
- if (!trimmed) continue;
63
- const firstDot = trimmed.indexOf(".");
64
- if (firstDot === -1) continue;
65
- const column = trimmed.slice(0, firstDot);
66
- const rest = trimmed.slice(firstDot + 1);
67
- const secondDot = rest.indexOf(".");
68
- if (secondDot === -1) continue;
69
- const op = rest.slice(0, secondDot);
70
- const value = rest.slice(secondDot + 1);
71
- const parsedValue = parseOrValue(value);
72
- conditions.push({ column, op, value: parsedValue });
73
- }
74
- return conditions;
75
- }
76
- function rebuildOrString(conditions) {
77
- return conditions.map((c) => {
78
- const value = formatOrValue(c.value);
79
- return `${c.column}.${c.op}.${value}`;
80
- }).join(",");
81
- }
82
- function splitOrString(input) {
83
- const parts = [];
84
- let current = "";
85
- let depth = 0;
86
- let inQuotes = false;
87
- for (const char of input) {
88
- if (char === '"' && depth === 0) {
89
- inQuotes = !inQuotes;
90
- current += char;
91
- } else if (char === "(" && !inQuotes) {
92
- depth++;
93
- current += char;
94
- } else if (char === ")" && !inQuotes) {
95
- depth--;
96
- current += char;
97
- } else if (char === "," && depth === 0 && !inQuotes) {
98
- parts.push(current);
99
- current = "";
100
- } else {
101
- current += char;
102
- }
103
- }
104
- if (current) {
105
- parts.push(current);
106
- }
107
- return parts;
108
- }
109
- function parseOrValue(value) {
110
- if (value.startsWith('"') && value.endsWith('"')) {
111
- return value.slice(1, -1);
112
- }
113
- if (value.startsWith("(") && value.endsWith(")")) {
114
- return value.slice(1, -1).split(",").map((v) => v.trim());
115
- }
116
- if (value === "true") return true;
117
- if (value === "false") return false;
118
- if (value === "null") return null;
119
- return value;
120
- }
121
- var POSTGREST_RESERVED = /[,().]/;
122
- function formatOrValue(value) {
123
- if (Array.isArray(value)) {
124
- return `(${value.join(",")})`;
125
- }
126
- if (value === null) return "null";
127
- if (value === true) return "true";
128
- if (value === false) return "false";
129
- const str = String(value);
130
- if (POSTGREST_RESERVED.test(str)) {
131
- return `"${str}"`;
132
- }
133
- return str;
134
- }
135
-
136
- // src/supabase/query-builder.ts
137
- var EncryptedQueryBuilderImpl = class {
138
- tableName;
139
- schema;
140
- encryptionClient;
141
- supabaseClient;
142
- encryptedColumnNames;
143
- // Recorded operations
144
- mutation = null;
145
- selectColumns = null;
146
- selectOptions = void 0;
147
- filters = [];
148
- orFilters = [];
149
- matchFilters = [];
150
- notFilters = [];
151
- rawFilters = [];
152
- transforms = [];
153
- resultMode = "array";
154
- shouldThrowOnError = false;
155
- // Encryption-specific state
156
- lockContext = null;
157
- auditConfig = null;
158
- constructor(tableName, schema, encryptionClient, supabaseClient) {
159
- this.tableName = tableName;
160
- this.schema = schema;
161
- this.encryptionClient = encryptionClient;
162
- this.supabaseClient = supabaseClient;
163
- this.encryptedColumnNames = getEncryptedColumnNames(schema);
164
- }
165
- // ---------------------------------------------------------------------------
166
- // Mutation methods
167
- // ---------------------------------------------------------------------------
168
- select(columns, options) {
169
- if (columns === "*") {
170
- throw new Error(
171
- "encryptedSupabase does not support select('*'). Please list columns explicitly so that encrypted columns can be cast with ::jsonb."
172
- );
173
- }
174
- this.selectColumns = columns;
175
- this.selectOptions = options;
176
- return this;
177
- }
178
- insert(data, options) {
179
- this.mutation = {
180
- kind: "insert",
181
- data,
182
- options
183
- };
184
- return this;
185
- }
186
- update(data, options) {
187
- this.mutation = {
188
- kind: "update",
189
- data,
190
- options
191
- };
192
- return this;
193
- }
194
- upsert(data, options) {
195
- this.mutation = {
196
- kind: "upsert",
197
- data,
198
- options
199
- };
200
- return this;
201
- }
202
- delete(options) {
203
- this.mutation = { kind: "delete", options };
204
- return this;
205
- }
206
- // ---------------------------------------------------------------------------
207
- // Filter methods
208
- // ---------------------------------------------------------------------------
209
- eq(column, value) {
210
- this.filters.push({ op: "eq", column, value });
211
- return this;
212
- }
213
- neq(column, value) {
214
- this.filters.push({ op: "neq", column, value });
215
- return this;
216
- }
217
- gt(column, value) {
218
- this.filters.push({ op: "gt", column, value });
219
- return this;
220
- }
221
- gte(column, value) {
222
- this.filters.push({ op: "gte", column, value });
223
- return this;
224
- }
225
- lt(column, value) {
226
- this.filters.push({ op: "lt", column, value });
227
- return this;
228
- }
229
- lte(column, value) {
230
- this.filters.push({ op: "lte", column, value });
231
- return this;
232
- }
233
- like(column, pattern) {
234
- this.filters.push({ op: "like", column, value: pattern });
235
- return this;
236
- }
237
- ilike(column, pattern) {
238
- this.filters.push({ op: "ilike", column, value: pattern });
239
- return this;
240
- }
241
- is(column, value) {
242
- this.filters.push({ op: "is", column, value });
243
- return this;
244
- }
245
- in(column, values) {
246
- this.filters.push({ op: "in", column, value: values });
247
- return this;
248
- }
249
- filter(column, operator, value) {
250
- this.rawFilters.push({ column, operator, value });
251
- return this;
252
- }
253
- not(column, operator, value) {
254
- this.notFilters.push({ column, op: operator, value });
255
- return this;
256
- }
257
- or(filtersOrConditions, options) {
258
- if (typeof filtersOrConditions === "string") {
259
- this.orFilters.push({
260
- kind: "string",
261
- value: filtersOrConditions,
262
- referencedTable: options?.referencedTable ?? options?.foreignTable
263
- });
264
- } else {
265
- this.orFilters.push({
266
- kind: "structured",
267
- conditions: filtersOrConditions
268
- });
269
- }
270
- return this;
271
- }
272
- match(query) {
273
- this.matchFilters.push({ query });
274
- return this;
275
- }
276
- // ---------------------------------------------------------------------------
277
- // Transform methods (passthrough)
278
- // ---------------------------------------------------------------------------
279
- order(column, options) {
280
- this.transforms.push({ kind: "order", column, options });
281
- return this;
282
- }
283
- limit(count, options) {
284
- this.transforms.push({ kind: "limit", count, options });
285
- return this;
286
- }
287
- range(from, to, options) {
288
- this.transforms.push({ kind: "range", from, to, options });
289
- return this;
290
- }
291
- single() {
292
- this.resultMode = "single";
293
- this.transforms.push({ kind: "single" });
294
- return this;
295
- }
296
- maybeSingle() {
297
- this.resultMode = "maybeSingle";
298
- this.transforms.push({ kind: "maybeSingle" });
299
- return this;
300
- }
301
- csv() {
302
- this.transforms.push({ kind: "csv" });
303
- return this;
304
- }
305
- abortSignal(signal) {
306
- this.transforms.push({ kind: "abortSignal", signal });
307
- return this;
308
- }
309
- throwOnError() {
310
- this.shouldThrowOnError = true;
311
- this.transforms.push({ kind: "throwOnError" });
312
- return this;
313
- }
314
- returns() {
315
- return this;
316
- }
317
- // ---------------------------------------------------------------------------
318
- // Encryption-specific methods
319
- // ---------------------------------------------------------------------------
320
- withLockContext(lockContext) {
321
- this.lockContext = lockContext;
322
- return this;
323
- }
324
- audit(config) {
325
- this.auditConfig = config;
326
- return this;
327
- }
328
- // ---------------------------------------------------------------------------
329
- // PromiseLike implementation (deferred execution)
330
- // ---------------------------------------------------------------------------
331
- then(onfulfilled, onrejected) {
332
- return this.execute().then(onfulfilled, onrejected);
333
- }
334
- // ---------------------------------------------------------------------------
335
- // Core execution
336
- // ---------------------------------------------------------------------------
337
- async execute() {
338
- try {
339
- logger.debug(`Supabase encrypted query on table "${this.tableName}".`);
340
- const encryptedMutation = await this.encryptMutationData();
341
- const selectString = this.buildSelectString();
342
- const encryptedFilters = await this.encryptFilterValues();
343
- const result = await this.buildAndExecuteQuery(
344
- encryptedMutation,
345
- selectString,
346
- encryptedFilters
347
- );
348
- return await this.decryptResults(result);
349
- } catch (err) {
350
- const message = err instanceof Error ? err.message : String(err);
351
- logger.error(
352
- `Supabase encrypted query failed on table "${this.tableName}": ${message}`
353
- );
354
- const error = {
355
- message,
356
- encryptionError: void 0
357
- };
358
- if (this.shouldThrowOnError) {
359
- throw err;
360
- }
361
- return {
362
- data: null,
363
- error,
364
- count: null,
365
- status: 500,
366
- statusText: "Encryption Error"
367
- };
368
- }
369
- }
370
- // ---------------------------------------------------------------------------
371
- // Step 1: Encrypt mutation data
372
- // ---------------------------------------------------------------------------
373
- async encryptMutationData() {
374
- if (!this.mutation) return null;
375
- if (this.mutation.kind === "delete") return null;
376
- const data = this.mutation.data;
377
- if (Array.isArray(data)) {
378
- const baseOp2 = this.encryptionClient.bulkEncryptModels(data, this.schema);
379
- const op2 = this.lockContext ? baseOp2.withLockContext(this.lockContext) : baseOp2;
380
- if (this.auditConfig) op2.audit(this.auditConfig);
381
- const result2 = await op2;
382
- if (result2.failure) {
383
- logger.error(
384
- `Supabase: failed to encrypt models for table "${this.tableName}"`
385
- );
386
- throw new EncryptionFailedError(
387
- `Failed to encrypt models: ${result2.failure.message}`,
388
- result2.failure
389
- );
390
- }
391
- return bulkModelsToEncryptedPgComposites(result2.data);
392
- }
393
- const baseOp = this.encryptionClient.encryptModel(data, this.schema);
394
- const op = this.lockContext ? baseOp.withLockContext(this.lockContext) : baseOp;
395
- if (this.auditConfig) op.audit(this.auditConfig);
396
- const result = await op;
397
- if (result.failure) {
398
- logger.error(
399
- `Supabase: failed to encrypt model for table "${this.tableName}"`
400
- );
401
- throw new EncryptionFailedError(
402
- `Failed to encrypt model: ${result.failure.message}`,
403
- result.failure
404
- );
405
- }
406
- return modelToEncryptedPgComposites(result.data);
407
- }
408
- // ---------------------------------------------------------------------------
409
- // Step 2: Build select string with casts
410
- // ---------------------------------------------------------------------------
411
- buildSelectString() {
412
- if (this.selectColumns === null) return null;
413
- return addJsonbCasts(this.selectColumns, this.encryptedColumnNames);
414
- }
415
- // ---------------------------------------------------------------------------
416
- // Step 3: Encrypt filter values
417
- // ---------------------------------------------------------------------------
418
- async encryptFilterValues() {
419
- const terms = [];
420
- const termMap = [];
421
- const tableColumns = this.getColumnMap();
422
- for (let i = 0; i < this.filters.length; i++) {
423
- const f = this.filters[i];
424
- if (!isEncryptedColumn(f.column, this.encryptedColumnNames)) continue;
425
- const column = tableColumns[f.column];
426
- if (!column) continue;
427
- if (f.op === "in" && Array.isArray(f.value)) {
428
- for (let j = 0; j < f.value.length; j++) {
429
- terms.push({
430
- value: f.value[j],
431
- column,
432
- table: this.schema,
433
- queryType: mapFilterOpToQueryType(f.op),
434
- returnType: "composite-literal"
435
- });
436
- termMap.push({ source: "filter", filterIndex: i, inIndex: j });
437
- }
438
- } else if (f.op === "is") {
439
- continue;
440
- } else {
441
- terms.push({
442
- value: f.value,
443
- column,
444
- table: this.schema,
445
- queryType: mapFilterOpToQueryType(f.op),
446
- returnType: "composite-literal"
447
- });
448
- termMap.push({ source: "filter", filterIndex: i });
449
- }
450
- }
451
- for (let i = 0; i < this.matchFilters.length; i++) {
452
- const mf = this.matchFilters[i];
453
- for (const [colName, value] of Object.entries(mf.query)) {
454
- if (!isEncryptedColumn(colName, this.encryptedColumnNames)) continue;
455
- const column = tableColumns[colName];
456
- if (!column) continue;
457
- terms.push({
458
- value,
459
- column,
460
- table: this.schema,
461
- queryType: "equality",
462
- returnType: "composite-literal"
463
- });
464
- termMap.push({ source: "match", matchIndex: i, column: colName });
465
- }
466
- }
467
- for (let i = 0; i < this.notFilters.length; i++) {
468
- const nf = this.notFilters[i];
469
- if (!isEncryptedColumn(nf.column, this.encryptedColumnNames)) continue;
470
- const column = tableColumns[nf.column];
471
- if (!column) continue;
472
- terms.push({
473
- value: nf.value,
474
- column,
475
- table: this.schema,
476
- queryType: mapFilterOpToQueryType(nf.op),
477
- returnType: "composite-literal"
478
- });
479
- termMap.push({ source: "not", notIndex: i });
480
- }
481
- for (let i = 0; i < this.orFilters.length; i++) {
482
- const of_ = this.orFilters[i];
483
- if (of_.kind === "string") {
484
- const parsed = parseOrString(of_.value);
485
- for (let j = 0; j < parsed.length; j++) {
486
- const cond = parsed[j];
487
- if (!isEncryptedColumn(cond.column, this.encryptedColumnNames))
488
- continue;
489
- const column = tableColumns[cond.column];
490
- if (!column) continue;
491
- terms.push({
492
- value: cond.value,
493
- column,
494
- table: this.schema,
495
- queryType: mapFilterOpToQueryType(cond.op),
496
- returnType: "composite-literal"
497
- });
498
- termMap.push({ source: "or-string", orIndex: i, conditionIndex: j });
499
- }
500
- } else {
501
- for (let j = 0; j < of_.conditions.length; j++) {
502
- const cond = of_.conditions[j];
503
- if (!isEncryptedColumn(cond.column, this.encryptedColumnNames))
504
- continue;
505
- const column = tableColumns[cond.column];
506
- if (!column) continue;
507
- terms.push({
508
- value: cond.value,
509
- column,
510
- table: this.schema,
511
- queryType: mapFilterOpToQueryType(cond.op),
512
- returnType: "composite-literal"
513
- });
514
- termMap.push({
515
- source: "or-structured",
516
- orIndex: i,
517
- conditionIndex: j
518
- });
519
- }
520
- }
521
- }
522
- for (let i = 0; i < this.rawFilters.length; i++) {
523
- const rf = this.rawFilters[i];
524
- if (!isEncryptedColumn(rf.column, this.encryptedColumnNames)) continue;
525
- const column = tableColumns[rf.column];
526
- if (!column) continue;
527
- terms.push({
528
- value: rf.value,
529
- column,
530
- table: this.schema,
531
- queryType: "equality",
532
- returnType: "composite-literal"
533
- });
534
- termMap.push({ source: "raw", rawIndex: i });
535
- }
536
- if (terms.length === 0) {
537
- return { encryptedValues: [], termMap: [] };
538
- }
539
- const baseOp = this.encryptionClient.encryptQuery(terms);
540
- const op = this.lockContext ? baseOp.withLockContext(this.lockContext) : baseOp;
541
- if (this.auditConfig) op.audit(this.auditConfig);
542
- const result = await op;
543
- if (result.failure) {
544
- logger.error(
545
- `Supabase: failed to encrypt query terms for table "${this.tableName}"`
546
- );
547
- throw new EncryptionFailedError(
548
- `Failed to encrypt query terms: ${result.failure.message}`,
549
- result.failure
550
- );
551
- }
552
- return { encryptedValues: result.data, termMap };
553
- }
554
- // ---------------------------------------------------------------------------
555
- // Step 4: Build and execute real Supabase query
556
- // ---------------------------------------------------------------------------
557
- async buildAndExecuteQuery(encryptedMutation, selectString, encryptedFilters) {
558
- let query = this.supabaseClient.from(this.tableName);
559
- if (this.mutation) {
560
- switch (this.mutation.kind) {
561
- case "insert":
562
- query = query.insert(encryptedMutation, this.mutation.options);
563
- break;
564
- case "update":
565
- query = query.update(encryptedMutation, this.mutation.options);
566
- break;
567
- case "upsert":
568
- query = query.upsert(encryptedMutation, this.mutation.options);
569
- break;
570
- case "delete":
571
- query = query.delete(this.mutation.options);
572
- break;
573
- }
574
- }
575
- if (selectString !== null) {
576
- query = query.select(selectString, this.selectOptions);
577
- } else if (!this.mutation) {
578
- query = query.select("*", this.selectOptions);
579
- }
580
- query = this.applyFilters(query, encryptedFilters);
581
- for (const t of this.transforms) {
582
- switch (t.kind) {
583
- case "order":
584
- query = query.order(t.column, t.options);
585
- break;
586
- case "limit":
587
- query = query.limit(t.count, t.options);
588
- break;
589
- case "range":
590
- query = query.range(t.from, t.to, t.options);
591
- break;
592
- case "single":
593
- query = query.single();
594
- break;
595
- case "maybeSingle":
596
- query = query.maybeSingle();
597
- break;
598
- case "csv":
599
- query = query.csv();
600
- break;
601
- case "abortSignal":
602
- query = query.abortSignal(t.signal);
603
- break;
604
- case "throwOnError":
605
- query = query.throwOnError();
606
- break;
607
- }
608
- }
609
- const result = await query;
610
- return result;
611
- }
612
- // ---------------------------------------------------------------------------
613
- // Apply filters with encrypted values substituted
614
- // ---------------------------------------------------------------------------
615
- applyFilters(query, encryptedFilters) {
616
- let q = query;
617
- const filterValueMap = /* @__PURE__ */ new Map();
618
- const filterInMap = /* @__PURE__ */ new Map();
619
- const matchValueMap = /* @__PURE__ */ new Map();
620
- const notValueMap = /* @__PURE__ */ new Map();
621
- const rawValueMap = /* @__PURE__ */ new Map();
622
- const orStringConditionMap = /* @__PURE__ */ new Map();
623
- const orStructuredConditionMap = /* @__PURE__ */ new Map();
624
- for (let i = 0; i < encryptedFilters.termMap.length; i++) {
625
- const mapping = encryptedFilters.termMap[i];
626
- const encValue = encryptedFilters.encryptedValues[i];
627
- switch (mapping.source) {
628
- case "filter":
629
- if (mapping.inIndex !== void 0) {
630
- filterInMap.set(
631
- `${mapping.filterIndex}:${mapping.inIndex}`,
632
- encValue
633
- );
634
- } else {
635
- filterValueMap.set(mapping.filterIndex, encValue);
636
- }
637
- break;
638
- case "match":
639
- matchValueMap.set(`${mapping.matchIndex}:${mapping.column}`, encValue);
640
- break;
641
- case "not":
642
- notValueMap.set(mapping.notIndex, encValue);
643
- break;
644
- case "raw":
645
- rawValueMap.set(mapping.rawIndex, encValue);
646
- break;
647
- case "or-string":
648
- orStringConditionMap.set(
649
- `${mapping.orIndex}:${mapping.conditionIndex}`,
650
- encValue
651
- );
652
- break;
653
- case "or-structured":
654
- orStructuredConditionMap.set(
655
- `${mapping.orIndex}:${mapping.conditionIndex}`,
656
- encValue
657
- );
658
- break;
659
- }
660
- }
661
- for (let i = 0; i < this.filters.length; i++) {
662
- const f = this.filters[i];
663
- let value = f.value;
664
- if (filterValueMap.has(i)) {
665
- value = filterValueMap.get(i);
666
- } else if (f.op === "in" && Array.isArray(f.value)) {
667
- value = f.value.map((v, j) => {
668
- const key = `${i}:${j}`;
669
- return filterInMap.has(key) ? filterInMap.get(key) : v;
670
- });
671
- }
672
- switch (f.op) {
673
- case "eq":
674
- q = q.eq(f.column, value);
675
- break;
676
- case "neq":
677
- q = q.neq(f.column, value);
678
- break;
679
- case "gt":
680
- q = q.gt(f.column, value);
681
- break;
682
- case "gte":
683
- q = q.gte(f.column, value);
684
- break;
685
- case "lt":
686
- q = q.lt(f.column, value);
687
- break;
688
- case "lte":
689
- q = q.lte(f.column, value);
690
- break;
691
- case "like":
692
- q = q.like(f.column, value);
693
- break;
694
- case "ilike":
695
- q = q.ilike(f.column, value);
696
- break;
697
- case "is":
698
- q = q.is(f.column, value);
699
- break;
700
- case "in":
701
- q = q.in(f.column, value);
702
- break;
703
- }
704
- }
705
- for (let i = 0; i < this.matchFilters.length; i++) {
706
- const mf = this.matchFilters[i];
707
- const resolvedQuery = {};
708
- for (const [colName, originalValue] of Object.entries(mf.query)) {
709
- const key = `${i}:${colName}`;
710
- resolvedQuery[colName] = matchValueMap.has(key) ? matchValueMap.get(key) : originalValue;
711
- }
712
- q = q.match(resolvedQuery);
713
- }
714
- for (let i = 0; i < this.notFilters.length; i++) {
715
- const nf = this.notFilters[i];
716
- const value = notValueMap.has(i) ? notValueMap.get(i) : nf.value;
717
- q = q.not(nf.column, nf.op, value);
718
- }
719
- for (let i = 0; i < this.orFilters.length; i++) {
720
- const of_ = this.orFilters[i];
721
- if (of_.kind === "string") {
722
- const parsed = parseOrString(of_.value);
723
- let hasEncrypted = false;
724
- for (let j = 0; j < parsed.length; j++) {
725
- const key = `${i}:${j}`;
726
- if (orStringConditionMap.has(key)) {
727
- parsed[j] = { ...parsed[j], value: orStringConditionMap.get(key) };
728
- hasEncrypted = true;
729
- }
730
- }
731
- if (hasEncrypted) {
732
- q = q.or(rebuildOrString(parsed), {
733
- referencedTable: of_.referencedTable
734
- });
735
- } else {
736
- q = q.or(of_.value, { referencedTable: of_.referencedTable });
737
- }
738
- } else {
739
- const conditions = of_.conditions.map((cond, j) => {
740
- const key = `${i}:${j}`;
741
- if (orStructuredConditionMap.has(key)) {
742
- return { ...cond, value: orStructuredConditionMap.get(key) };
743
- }
744
- return cond;
745
- });
746
- q = q.or(rebuildOrString(conditions));
747
- }
748
- }
749
- for (let i = 0; i < this.rawFilters.length; i++) {
750
- const rf = this.rawFilters[i];
751
- const value = rawValueMap.has(i) ? rawValueMap.get(i) : rf.value;
752
- q = q.filter(rf.column, rf.operator, value);
753
- }
754
- return q;
755
- }
756
- // ---------------------------------------------------------------------------
757
- // Step 5: Decrypt results
758
- // ---------------------------------------------------------------------------
759
- async decryptResults(result) {
760
- if (result.error) {
761
- return {
762
- data: null,
763
- error: {
764
- message: result.error.message,
765
- details: result.error.details,
766
- hint: result.error.hint,
767
- code: result.error.code
768
- },
769
- count: result.count ?? null,
770
- status: result.status,
771
- statusText: result.statusText
772
- };
773
- }
774
- if (result.data === null || result.data === void 0) {
775
- return {
776
- data: null,
777
- error: null,
778
- count: result.count ?? null,
779
- status: result.status,
780
- statusText: result.statusText
781
- };
782
- }
783
- const hasSelect = this.selectColumns !== null;
784
- const hasMutationWithReturning = this.mutation !== null && hasSelect;
785
- if (!hasSelect && !hasMutationWithReturning) {
786
- return {
787
- data: result.data,
788
- error: null,
789
- count: result.count ?? null,
790
- status: result.status,
791
- statusText: result.statusText
792
- };
793
- }
794
- if (this.resultMode === "single" || this.resultMode === "maybeSingle") {
795
- if (result.data === null) {
796
- return {
797
- data: null,
798
- error: null,
799
- count: result.count ?? null,
800
- status: result.status,
801
- statusText: result.statusText
802
- };
803
- }
804
- const baseDecryptOp = this.encryptionClient.decryptModel(
805
- result.data
806
- );
807
- const decryptOp = this.lockContext ? baseDecryptOp.withLockContext(this.lockContext) : baseDecryptOp;
808
- if (this.auditConfig) decryptOp.audit(this.auditConfig);
809
- const decrypted2 = await decryptOp;
810
- if (decrypted2.failure) {
811
- logger.error(
812
- `Supabase: failed to decrypt model for table "${this.tableName}"`
813
- );
814
- throw new EncryptionFailedError(
815
- `Failed to decrypt model: ${decrypted2.failure.message}`,
816
- decrypted2.failure
817
- );
818
- }
819
- return {
820
- data: decrypted2.data,
821
- error: null,
822
- count: result.count ?? null,
823
- status: result.status,
824
- statusText: result.statusText
825
- };
826
- }
827
- const dataArray = result.data;
828
- if (dataArray.length === 0) {
829
- return {
830
- data: [],
831
- error: null,
832
- count: result.count ?? null,
833
- status: result.status,
834
- statusText: result.statusText
835
- };
836
- }
837
- const baseBulkDecryptOp = this.encryptionClient.bulkDecryptModels(dataArray);
838
- const bulkDecryptOp = this.lockContext ? baseBulkDecryptOp.withLockContext(this.lockContext) : baseBulkDecryptOp;
839
- if (this.auditConfig) bulkDecryptOp.audit(this.auditConfig);
840
- const decrypted = await bulkDecryptOp;
841
- if (decrypted.failure) {
842
- logger.error(
843
- `Supabase: failed to decrypt models for table "${this.tableName}"`
844
- );
845
- throw new EncryptionFailedError(
846
- `Failed to decrypt models: ${decrypted.failure.message}`,
847
- decrypted.failure
848
- );
849
- }
850
- return {
851
- data: decrypted.data,
852
- error: null,
853
- count: result.count ?? null,
854
- status: result.status,
855
- statusText: result.statusText
856
- };
857
- }
858
- // ---------------------------------------------------------------------------
859
- // Helpers
860
- // ---------------------------------------------------------------------------
861
- getColumnMap() {
862
- const map = {};
863
- const schema = this.schema;
864
- for (const colName of this.encryptedColumnNames) {
865
- const col = schema[colName];
866
- if (col instanceof EncryptedColumn) {
867
- map[colName] = col;
868
- }
869
- }
870
- return map;
871
- }
872
- };
873
- var EncryptionFailedError = class extends Error {
874
- encryptionError;
875
- constructor(message, encryptionError) {
876
- super(message);
877
- this.name = "EncryptionFailedError";
878
- this.encryptionError = encryptionError;
879
- }
880
- };
881
-
882
- // src/supabase/index.ts
883
- function encryptedSupabase(config) {
884
- const { encryptionClient, supabaseClient } = config;
885
- return {
886
- from(tableName, schema) {
887
- return new EncryptedQueryBuilderImpl(
888
- tableName,
889
- schema,
890
- encryptionClient,
891
- supabaseClient
892
- );
893
- }
894
- };
895
- }
896
- export {
897
- encryptedSupabase
898
- };
899
- //# sourceMappingURL=index.js.map