@cipherstash/stack 0.18.0 → 1.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +704 -0
- package/README.md +97 -79
- package/dist/adapter-kit.cjs +923 -0
- package/dist/adapter-kit.cjs.map +1 -0
- package/dist/adapter-kit.d.cts +82 -0
- package/dist/adapter-kit.d.ts +82 -0
- package/dist/adapter-kit.js +47 -0
- package/dist/adapter-kit.js.map +1 -0
- package/dist/base-operation-AOAIvsSB.d.cts +32 -0
- package/dist/base-operation-FXEzUXIq.d.ts +32 -0
- package/dist/{chunk-VPOTNP4K.js → chunk-3B5ZX3IS.js} +3 -1
- package/dist/chunk-3B5ZX3IS.js.map +1 -0
- package/dist/chunk-7333ZC6L.js +48 -0
- package/dist/chunk-7333ZC6L.js.map +1 -0
- package/dist/{chunk-XWPGAHBZ.js → chunk-BNO32ZMV.js} +1 -1
- package/dist/chunk-BNO32ZMV.js.map +1 -0
- package/dist/chunk-CLM7E4I6.js +4439 -0
- package/dist/chunk-CLM7E4I6.js.map +1 -0
- package/dist/{chunk-LLCCNKDL.js → chunk-HQANMV7R.js} +3 -3
- package/dist/{chunk-LLCCNKDL.js.map → chunk-HQANMV7R.js.map} +1 -1
- package/dist/chunk-JSG2AMDI.js +17 -0
- package/dist/chunk-JSG2AMDI.js.map +1 -0
- package/dist/chunk-OFQ555AX.js +191 -0
- package/dist/chunk-OFQ555AX.js.map +1 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-PZ5AY32C.js.map +1 -0
- package/dist/chunk-V2Q3NYIH.js +494 -0
- package/dist/chunk-V2Q3NYIH.js.map +1 -0
- package/dist/chunk-X3JRXEIB.js +98 -0
- package/dist/chunk-X3JRXEIB.js.map +1 -0
- package/dist/{chunk-ZYOZHZMJ.js → chunk-ZTP5QJ27.js} +193 -169
- package/dist/chunk-ZTP5QJ27.js.map +1 -0
- package/dist/client.cjs +4124 -49
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +6 -5
- package/dist/client.d.ts +6 -5
- package/dist/client.js +3 -1
- package/dist/columns-D2_YzrCX.d.cts +622 -0
- package/dist/columns-rZc7fQHI.d.ts +622 -0
- package/dist/dynamodb/index.cjs +19 -12
- package/dist/dynamodb/index.cjs.map +1 -1
- package/dist/dynamodb/index.d.cts +3 -2
- package/dist/dynamodb/index.d.ts +3 -2
- package/dist/dynamodb/index.js +8 -10
- package/dist/dynamodb/index.js.map +1 -1
- package/dist/encryption/index.cjs +4496 -222
- package/dist/encryption/index.cjs.map +1 -1
- package/dist/encryption/index.d.cts +807 -6
- package/dist/encryption/index.d.ts +807 -6
- package/dist/encryption/index.js +15 -7
- package/dist/encryption/v3.cjs +7896 -0
- package/dist/encryption/v3.cjs.map +1 -0
- package/dist/encryption/v3.d.cts +103 -0
- package/dist/encryption/v3.d.ts +103 -0
- package/dist/encryption/v3.js +189 -0
- package/dist/encryption/v3.js.map +1 -0
- package/dist/eql/v3/index.cjs +663 -0
- package/dist/eql/v3/index.cjs.map +1 -0
- package/dist/eql/v3/index.d.cts +173 -0
- package/dist/eql/v3/index.d.ts +173 -0
- package/dist/eql/v3/index.js +99 -0
- package/dist/eql/v3/index.js.map +1 -0
- package/dist/errors/index.js +1 -0
- package/dist/identity/index.cjs +55 -35
- package/dist/identity/index.cjs.map +1 -1
- package/dist/identity/index.d.cts +54 -30
- package/dist/identity/index.d.ts +54 -30
- package/dist/identity/index.js +9 -177
- package/dist/identity/index.js.map +1 -1
- package/dist/index-BquA71_Y.d.ts +24 -0
- package/dist/index-fhWTOV0K.d.cts +24 -0
- package/dist/index.cjs +4547 -243
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -19
- package/dist/index.d.ts +18 -19
- package/dist/index.js +20 -5
- package/dist/index.js.map +1 -1
- package/dist/schema/index.cjs +4128 -49
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.cts +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.js +3 -1
- package/dist/{types-public-BTxqjmtv.d.cts → types-public-QMjYNfQO.d.cts} +448 -251
- package/dist/{types-public-BTxqjmtv.d.ts → types-public-QMjYNfQO.d.ts} +448 -251
- package/dist/types-public.cjs.map +1 -1
- package/dist/types-public.d.cts +2 -2
- package/dist/types-public.d.ts +2 -2
- package/dist/types-public.js +2 -1
- package/dist/wasm-inline.d.ts +1643 -0
- package/dist/wasm-inline.js +4901 -0
- package/dist/wasm-inline.js.map +1 -0
- package/package.json +74 -52
- package/dist/chunk-VPOTNP4K.js.map +0 -1
- package/dist/chunk-XWPGAHBZ.js.map +0 -1
- package/dist/chunk-YWQXTZ5M.js +0 -383
- package/dist/chunk-YWQXTZ5M.js.map +0 -1
- package/dist/chunk-ZYOZHZMJ.js.map +0 -1
- package/dist/client-CIsfZ2zg.d.ts +0 -692
- package/dist/client-Dt98Rugc.d.cts +0 -692
- package/dist/drizzle/index.cjs +0 -1558
- package/dist/drizzle/index.cjs.map +0 -1
- package/dist/drizzle/index.d.cts +0 -358
- package/dist/drizzle/index.d.ts +0 -358
- package/dist/drizzle/index.js +0 -1215
- package/dist/drizzle/index.js.map +0 -1
- package/dist/supabase/index.cjs +0 -1526
- package/dist/supabase/index.cjs.map +0 -1
- package/dist/supabase/index.d.cts +0 -144
- package/dist/supabase/index.d.ts +0 -144
- package/dist/supabase/index.js +0 -899
- package/dist/supabase/index.js.map +0 -1
package/dist/supabase/index.cjs
DELETED
|
@@ -1,1526 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/supabase/index.ts
|
|
21
|
-
var supabase_exports = {};
|
|
22
|
-
__export(supabase_exports, {
|
|
23
|
-
encryptedSupabase: () => encryptedSupabase
|
|
24
|
-
});
|
|
25
|
-
module.exports = __toCommonJS(supabase_exports);
|
|
26
|
-
|
|
27
|
-
// src/encryption/helpers/index.ts
|
|
28
|
-
function encryptedToPgComposite(obj) {
|
|
29
|
-
return {
|
|
30
|
-
data: obj
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
function modelToEncryptedPgComposites(model) {
|
|
34
|
-
const result = {};
|
|
35
|
-
for (const [key, value] of Object.entries(model)) {
|
|
36
|
-
if (isEncryptedPayload(value)) {
|
|
37
|
-
result[key] = encryptedToPgComposite(value);
|
|
38
|
-
} else {
|
|
39
|
-
result[key] = value;
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return result;
|
|
43
|
-
}
|
|
44
|
-
function bulkModelsToEncryptedPgComposites(models) {
|
|
45
|
-
return models.map((model) => modelToEncryptedPgComposites(model));
|
|
46
|
-
}
|
|
47
|
-
function isEncryptedPayload(value) {
|
|
48
|
-
if (value === null) return false;
|
|
49
|
-
if (typeof value !== "object") return false;
|
|
50
|
-
const obj = value;
|
|
51
|
-
if (!("v" in obj) || typeof obj.v !== "number") return false;
|
|
52
|
-
if (!("i" in obj) || typeof obj.i !== "object") return false;
|
|
53
|
-
if (!("c" in obj) && !("sv" in obj)) return false;
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// src/schema/index.ts
|
|
58
|
-
var import_zod = require("zod");
|
|
59
|
-
var eqlCastAsEnum = import_zod.z.enum([
|
|
60
|
-
"text",
|
|
61
|
-
"int",
|
|
62
|
-
"small_int",
|
|
63
|
-
"big_int",
|
|
64
|
-
"real",
|
|
65
|
-
"double",
|
|
66
|
-
"boolean",
|
|
67
|
-
"date",
|
|
68
|
-
"jsonb"
|
|
69
|
-
]).default("text");
|
|
70
|
-
var castAsEnum = import_zod.z.enum(["bigint", "boolean", "date", "number", "string", "json", "text"]).default("text");
|
|
71
|
-
var tokenFilterSchema = import_zod.z.object({
|
|
72
|
-
kind: import_zod.z.literal("downcase")
|
|
73
|
-
});
|
|
74
|
-
var tokenizerSchema = import_zod.z.union([
|
|
75
|
-
import_zod.z.object({
|
|
76
|
-
kind: import_zod.z.literal("standard")
|
|
77
|
-
}),
|
|
78
|
-
import_zod.z.object({
|
|
79
|
-
kind: import_zod.z.literal("ngram"),
|
|
80
|
-
token_length: import_zod.z.number()
|
|
81
|
-
})
|
|
82
|
-
]).default({ kind: "ngram", token_length: 3 }).optional();
|
|
83
|
-
var oreIndexOptsSchema = import_zod.z.object({});
|
|
84
|
-
var uniqueIndexOptsSchema = import_zod.z.object({
|
|
85
|
-
token_filters: import_zod.z.array(tokenFilterSchema).default([]).optional()
|
|
86
|
-
});
|
|
87
|
-
var matchIndexOptsSchema = import_zod.z.object({
|
|
88
|
-
tokenizer: tokenizerSchema,
|
|
89
|
-
token_filters: import_zod.z.array(tokenFilterSchema).default([]).optional(),
|
|
90
|
-
k: import_zod.z.number().default(6).optional(),
|
|
91
|
-
m: import_zod.z.number().default(2048).optional(),
|
|
92
|
-
include_original: import_zod.z.boolean().default(false).optional()
|
|
93
|
-
});
|
|
94
|
-
var arrayIndexModeSchema = import_zod.z.union([
|
|
95
|
-
import_zod.z.literal("all"),
|
|
96
|
-
import_zod.z.literal("none"),
|
|
97
|
-
import_zod.z.object({
|
|
98
|
-
item: import_zod.z.boolean().optional(),
|
|
99
|
-
wildcard: import_zod.z.boolean().optional(),
|
|
100
|
-
position: import_zod.z.boolean().optional()
|
|
101
|
-
})
|
|
102
|
-
]);
|
|
103
|
-
var steVecIndexOptsSchema = import_zod.z.object({
|
|
104
|
-
prefix: import_zod.z.string(),
|
|
105
|
-
array_index_mode: arrayIndexModeSchema.optional()
|
|
106
|
-
});
|
|
107
|
-
var indexesSchema = import_zod.z.object({
|
|
108
|
-
ore: oreIndexOptsSchema.optional(),
|
|
109
|
-
unique: uniqueIndexOptsSchema.optional(),
|
|
110
|
-
match: matchIndexOptsSchema.optional(),
|
|
111
|
-
ste_vec: steVecIndexOptsSchema.optional()
|
|
112
|
-
}).default({});
|
|
113
|
-
var columnSchema = import_zod.z.object({
|
|
114
|
-
cast_as: castAsEnum,
|
|
115
|
-
indexes: indexesSchema
|
|
116
|
-
}).default({});
|
|
117
|
-
var tableSchema = import_zod.z.record(columnSchema).default({});
|
|
118
|
-
var tablesSchema = import_zod.z.record(tableSchema).default({});
|
|
119
|
-
var encryptConfigSchema = import_zod.z.object({
|
|
120
|
-
v: import_zod.z.number(),
|
|
121
|
-
tables: tablesSchema
|
|
122
|
-
});
|
|
123
|
-
var EncryptedColumn = class {
|
|
124
|
-
columnName;
|
|
125
|
-
castAsValue;
|
|
126
|
-
indexesValue = {};
|
|
127
|
-
constructor(columnName) {
|
|
128
|
-
this.columnName = columnName;
|
|
129
|
-
this.castAsValue = "string";
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Set or override the plaintext data type for this column.
|
|
133
|
-
*
|
|
134
|
-
* By default all columns are treated as `'string'`. Use this method to specify
|
|
135
|
-
* a different type so the encryption layer knows how to encode the plaintext
|
|
136
|
-
* before encrypting.
|
|
137
|
-
*
|
|
138
|
-
* @param castAs - The plaintext data type: `'string'`, `'number'`, `'boolean'`, `'date'`, `'bigint'`, or `'json'`.
|
|
139
|
-
* @returns This `EncryptedColumn` instance for method chaining.
|
|
140
|
-
*
|
|
141
|
-
* @example
|
|
142
|
-
* ```typescript
|
|
143
|
-
* import { encryptedColumn } from "@cipherstash/stack/schema"
|
|
144
|
-
*
|
|
145
|
-
* const dateOfBirth = encryptedColumn("date_of_birth").dataType("date")
|
|
146
|
-
* ```
|
|
147
|
-
*/
|
|
148
|
-
dataType(castAs) {
|
|
149
|
-
this.castAsValue = castAs;
|
|
150
|
-
return this;
|
|
151
|
-
}
|
|
152
|
-
/**
|
|
153
|
-
* Enable Order-Revealing Encryption (ORE) indexing on this column.
|
|
154
|
-
*
|
|
155
|
-
* ORE allows sorting, comparison, and range queries on encrypted data.
|
|
156
|
-
* Use with `encryptQuery` and `queryType: 'orderAndRange'`.
|
|
157
|
-
*
|
|
158
|
-
* @returns This `EncryptedColumn` instance for method chaining.
|
|
159
|
-
*
|
|
160
|
-
* @example
|
|
161
|
-
* ```typescript
|
|
162
|
-
* import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
|
|
163
|
-
*
|
|
164
|
-
* const users = encryptedTable("users", {
|
|
165
|
-
* email: encryptedColumn("email").orderAndRange(),
|
|
166
|
-
* })
|
|
167
|
-
* ```
|
|
168
|
-
*/
|
|
169
|
-
orderAndRange() {
|
|
170
|
-
this.indexesValue.ore = {};
|
|
171
|
-
return this;
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Enable an exact-match (unique) index on this column.
|
|
175
|
-
*
|
|
176
|
-
* Allows equality queries on encrypted data. Use with `encryptQuery`
|
|
177
|
-
* and `queryType: 'equality'`.
|
|
178
|
-
*
|
|
179
|
-
* @param tokenFilters - Optional array of token filters (e.g. `[{ kind: 'downcase' }]`).
|
|
180
|
-
* When omitted, no token filters are applied.
|
|
181
|
-
* @returns This `EncryptedColumn` instance for method chaining.
|
|
182
|
-
*
|
|
183
|
-
* @example
|
|
184
|
-
* ```typescript
|
|
185
|
-
* import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
|
|
186
|
-
*
|
|
187
|
-
* const users = encryptedTable("users", {
|
|
188
|
-
* email: encryptedColumn("email").equality(),
|
|
189
|
-
* })
|
|
190
|
-
* ```
|
|
191
|
-
*/
|
|
192
|
-
equality(tokenFilters) {
|
|
193
|
-
this.indexesValue.unique = {
|
|
194
|
-
token_filters: tokenFilters ?? []
|
|
195
|
-
};
|
|
196
|
-
return this;
|
|
197
|
-
}
|
|
198
|
-
/**
|
|
199
|
-
* Enable a full-text / fuzzy search (match) index on this column.
|
|
200
|
-
*
|
|
201
|
-
* Uses n-gram tokenization by default for substring and fuzzy matching.
|
|
202
|
-
* Use with `encryptQuery` and `queryType: 'freeTextSearch'`.
|
|
203
|
-
*
|
|
204
|
-
* @param opts - Optional match index configuration. Defaults to 3-character ngram
|
|
205
|
-
* tokenization with a downcase filter, `k=6`, `m=2048`, and `include_original=true`.
|
|
206
|
-
* @returns This `EncryptedColumn` instance for method chaining.
|
|
207
|
-
*
|
|
208
|
-
* @example
|
|
209
|
-
* ```typescript
|
|
210
|
-
* import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
|
|
211
|
-
*
|
|
212
|
-
* const users = encryptedTable("users", {
|
|
213
|
-
* email: encryptedColumn("email").freeTextSearch(),
|
|
214
|
-
* })
|
|
215
|
-
*
|
|
216
|
-
* // With custom options
|
|
217
|
-
* const posts = encryptedTable("posts", {
|
|
218
|
-
* body: encryptedColumn("body").freeTextSearch({
|
|
219
|
-
* tokenizer: { kind: "ngram", token_length: 4 },
|
|
220
|
-
* k: 8,
|
|
221
|
-
* m: 4096,
|
|
222
|
-
* }),
|
|
223
|
-
* })
|
|
224
|
-
* ```
|
|
225
|
-
*/
|
|
226
|
-
freeTextSearch(opts) {
|
|
227
|
-
this.indexesValue.match = {
|
|
228
|
-
tokenizer: opts?.tokenizer ?? { kind: "ngram", token_length: 3 },
|
|
229
|
-
token_filters: opts?.token_filters ?? [
|
|
230
|
-
{
|
|
231
|
-
kind: "downcase"
|
|
232
|
-
}
|
|
233
|
-
],
|
|
234
|
-
k: opts?.k ?? 6,
|
|
235
|
-
m: opts?.m ?? 2048,
|
|
236
|
-
include_original: opts?.include_original ?? true
|
|
237
|
-
};
|
|
238
|
-
return this;
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Configure this column for searchable encrypted JSON (STE-Vec).
|
|
242
|
-
*
|
|
243
|
-
* Enables encrypted JSONPath selector queries (e.g. `'$.user.email'`) and
|
|
244
|
-
* containment queries (e.g. `{ role: 'admin' }`). Automatically sets the
|
|
245
|
-
* data type to `'json'`.
|
|
246
|
-
*
|
|
247
|
-
* When used with `encryptQuery`, the query operation is auto-inferred from
|
|
248
|
-
* the plaintext type: strings become selector queries, objects/arrays become
|
|
249
|
-
* containment queries.
|
|
250
|
-
*
|
|
251
|
-
* @returns This `EncryptedColumn` instance for method chaining.
|
|
252
|
-
*
|
|
253
|
-
* @example
|
|
254
|
-
* ```typescript
|
|
255
|
-
* import { encryptedTable, encryptedColumn } from "@cipherstash/stack/schema"
|
|
256
|
-
*
|
|
257
|
-
* const documents = encryptedTable("documents", {
|
|
258
|
-
* metadata: encryptedColumn("metadata").searchableJson(),
|
|
259
|
-
* })
|
|
260
|
-
* ```
|
|
261
|
-
*/
|
|
262
|
-
searchableJson() {
|
|
263
|
-
this.castAsValue = "json";
|
|
264
|
-
this.indexesValue.ste_vec = { prefix: "enabled", array_index_mode: "all" };
|
|
265
|
-
return this;
|
|
266
|
-
}
|
|
267
|
-
build() {
|
|
268
|
-
return {
|
|
269
|
-
cast_as: this.castAsValue,
|
|
270
|
-
indexes: this.indexesValue
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
getName() {
|
|
274
|
-
return this.columnName;
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
// ../../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
|
|
279
|
-
function formatDuration(ms) {
|
|
280
|
-
if (ms < 1e3) return `${Math.round(ms)}ms`;
|
|
281
|
-
return `${(ms / 1e3).toFixed(2)}s`;
|
|
282
|
-
}
|
|
283
|
-
function isDev() {
|
|
284
|
-
if (typeof process !== "undefined") return process.env.NODE_ENV !== "production";
|
|
285
|
-
if (typeof window !== "undefined") return true;
|
|
286
|
-
return false;
|
|
287
|
-
}
|
|
288
|
-
function detectEnvironment() {
|
|
289
|
-
const env = typeof process !== "undefined" ? process.env : {};
|
|
290
|
-
const defaultEnvironment = isDev() ? "development" : "production";
|
|
291
|
-
return {
|
|
292
|
-
environment: env.NODE_ENV || defaultEnvironment,
|
|
293
|
-
service: env.SERVICE_NAME || "app",
|
|
294
|
-
version: env.APP_VERSION,
|
|
295
|
-
commitHash: env.COMMIT_SHA || env.GITHUB_SHA || env.VERCEL_GIT_COMMIT_SHA || env.CF_PAGES_COMMIT_SHA,
|
|
296
|
-
region: env.VERCEL_REGION || env.AWS_REGION || env.FLY_REGION || env.CF_REGION
|
|
297
|
-
};
|
|
298
|
-
}
|
|
299
|
-
function getConsoleMethod(level) {
|
|
300
|
-
return level;
|
|
301
|
-
}
|
|
302
|
-
var colors = {
|
|
303
|
-
reset: "\x1B[0m",
|
|
304
|
-
bold: "\x1B[1m",
|
|
305
|
-
dim: "\x1B[2m",
|
|
306
|
-
red: "\x1B[31m",
|
|
307
|
-
green: "\x1B[32m",
|
|
308
|
-
yellow: "\x1B[33m",
|
|
309
|
-
blue: "\x1B[34m",
|
|
310
|
-
magenta: "\x1B[35m",
|
|
311
|
-
cyan: "\x1B[36m",
|
|
312
|
-
white: "\x1B[37m",
|
|
313
|
-
gray: "\x1B[90m"
|
|
314
|
-
};
|
|
315
|
-
function getLevelColor(level) {
|
|
316
|
-
switch (level) {
|
|
317
|
-
case "error":
|
|
318
|
-
return colors.red;
|
|
319
|
-
case "warn":
|
|
320
|
-
return colors.yellow;
|
|
321
|
-
case "info":
|
|
322
|
-
return colors.cyan;
|
|
323
|
-
case "debug":
|
|
324
|
-
return colors.gray;
|
|
325
|
-
default:
|
|
326
|
-
return colors.white;
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
function matchesPattern(path, pattern) {
|
|
330
|
-
const regexPattern = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/{{GLOBSTAR}}/g, ".*").replace(/\?/g, "[^/]");
|
|
331
|
-
return new RegExp(`^${regexPattern}$`).test(path);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// ../../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
|
|
335
|
-
function isPlainObject(val) {
|
|
336
|
-
return val !== null && typeof val === "object" && !Array.isArray(val);
|
|
337
|
-
}
|
|
338
|
-
function deepDefaults(base, defaults) {
|
|
339
|
-
const result = { ...base };
|
|
340
|
-
for (const key in defaults) {
|
|
341
|
-
const baseVal = result[key];
|
|
342
|
-
const defaultVal = defaults[key];
|
|
343
|
-
if (baseVal === void 0 || baseVal === null) result[key] = defaultVal;
|
|
344
|
-
else if (isPlainObject(baseVal) && isPlainObject(defaultVal)) result[key] = deepDefaults(baseVal, defaultVal);
|
|
345
|
-
}
|
|
346
|
-
return result;
|
|
347
|
-
}
|
|
348
|
-
var globalEnv = {
|
|
349
|
-
service: "app",
|
|
350
|
-
environment: "development"
|
|
351
|
-
};
|
|
352
|
-
var globalPretty = isDev();
|
|
353
|
-
var globalSampling = {};
|
|
354
|
-
var globalStringify = true;
|
|
355
|
-
var globalDrain;
|
|
356
|
-
var globalEnabled = true;
|
|
357
|
-
function initLogger(config = {}) {
|
|
358
|
-
globalEnabled = config.enabled ?? true;
|
|
359
|
-
const detected = detectEnvironment();
|
|
360
|
-
globalEnv = {
|
|
361
|
-
service: config.env?.service ?? detected.service ?? "app",
|
|
362
|
-
environment: config.env?.environment ?? detected.environment ?? "development",
|
|
363
|
-
version: config.env?.version ?? detected.version,
|
|
364
|
-
commitHash: config.env?.commitHash ?? detected.commitHash,
|
|
365
|
-
region: config.env?.region ?? detected.region
|
|
366
|
-
};
|
|
367
|
-
globalPretty = config.pretty ?? isDev();
|
|
368
|
-
globalSampling = config.sampling ?? {};
|
|
369
|
-
globalStringify = config.stringify ?? true;
|
|
370
|
-
globalDrain = config.drain;
|
|
371
|
-
}
|
|
372
|
-
function shouldSample(level) {
|
|
373
|
-
const { rates } = globalSampling;
|
|
374
|
-
if (!rates) return true;
|
|
375
|
-
const percentage = level === "error" && rates.error === void 0 ? 100 : rates[level] ?? 100;
|
|
376
|
-
if (percentage <= 0) return false;
|
|
377
|
-
if (percentage >= 100) return true;
|
|
378
|
-
return Math.random() * 100 < percentage;
|
|
379
|
-
}
|
|
380
|
-
function shouldKeep(ctx) {
|
|
381
|
-
const { keep } = globalSampling;
|
|
382
|
-
if (!keep?.length) return false;
|
|
383
|
-
return keep.some((condition) => {
|
|
384
|
-
if (condition.status !== void 0 && ctx.status !== void 0 && ctx.status >= condition.status) return true;
|
|
385
|
-
if (condition.duration !== void 0 && ctx.duration !== void 0 && ctx.duration >= condition.duration) return true;
|
|
386
|
-
if (condition.path && ctx.path && matchesPattern(ctx.path, condition.path)) return true;
|
|
387
|
-
return false;
|
|
388
|
-
});
|
|
389
|
-
}
|
|
390
|
-
function emitWideEvent(level, event, skipSamplingCheck = false) {
|
|
391
|
-
if (!globalEnabled) return null;
|
|
392
|
-
if (!skipSamplingCheck && !shouldSample(level)) return null;
|
|
393
|
-
const formatted = {
|
|
394
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
395
|
-
level,
|
|
396
|
-
...globalEnv,
|
|
397
|
-
...event
|
|
398
|
-
};
|
|
399
|
-
if (globalPretty) prettyPrintWideEvent(formatted);
|
|
400
|
-
else if (globalStringify) console[getConsoleMethod(level)](JSON.stringify(formatted));
|
|
401
|
-
else console[getConsoleMethod(level)](formatted);
|
|
402
|
-
if (globalDrain) Promise.resolve(globalDrain({ event: formatted })).catch((err) => {
|
|
403
|
-
console.error("[evlog] drain failed:", err);
|
|
404
|
-
});
|
|
405
|
-
return formatted;
|
|
406
|
-
}
|
|
407
|
-
function emitTaggedLog(level, tag, message) {
|
|
408
|
-
if (!globalEnabled) return;
|
|
409
|
-
if (globalPretty) {
|
|
410
|
-
if (!shouldSample(level)) return;
|
|
411
|
-
const color = getLevelColor(level);
|
|
412
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
|
|
413
|
-
console.log(`${colors.dim}${timestamp}${colors.reset} ${color}[${tag}]${colors.reset} ${message}`);
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
emitWideEvent(level, {
|
|
417
|
-
tag,
|
|
418
|
-
message
|
|
419
|
-
});
|
|
420
|
-
}
|
|
421
|
-
function formatValue(value) {
|
|
422
|
-
if (value === null || value === void 0) return String(value);
|
|
423
|
-
if (typeof value === "object") {
|
|
424
|
-
const pairs = [];
|
|
425
|
-
for (const [k, v] of Object.entries(value)) if (v !== void 0 && v !== null) if (typeof v === "object") pairs.push(`${k}=${JSON.stringify(v)}`);
|
|
426
|
-
else pairs.push(`${k}=${v}`);
|
|
427
|
-
return pairs.join(" ");
|
|
428
|
-
}
|
|
429
|
-
return String(value);
|
|
430
|
-
}
|
|
431
|
-
function prettyPrintWideEvent(event) {
|
|
432
|
-
const { timestamp, level, service, environment, version, ...rest } = event;
|
|
433
|
-
const levelColor = getLevelColor(level);
|
|
434
|
-
const ts = timestamp.slice(11, 23);
|
|
435
|
-
let header = `${colors.dim}${ts}${colors.reset} ${levelColor}${level.toUpperCase()}${colors.reset}`;
|
|
436
|
-
header += ` ${colors.cyan}[${service}]${colors.reset}`;
|
|
437
|
-
if (rest.method && rest.path) {
|
|
438
|
-
header += ` ${rest.method} ${rest.path}`;
|
|
439
|
-
delete rest.method;
|
|
440
|
-
delete rest.path;
|
|
441
|
-
}
|
|
442
|
-
if (rest.status) {
|
|
443
|
-
const statusColor = rest.status >= 400 ? colors.red : colors.green;
|
|
444
|
-
header += ` ${statusColor}${rest.status}${colors.reset}`;
|
|
445
|
-
delete rest.status;
|
|
446
|
-
}
|
|
447
|
-
if (rest.duration) {
|
|
448
|
-
header += ` ${colors.dim}in ${rest.duration}${colors.reset}`;
|
|
449
|
-
delete rest.duration;
|
|
450
|
-
}
|
|
451
|
-
console.log(header);
|
|
452
|
-
const entries = Object.entries(rest).filter(([_, v]) => v !== void 0);
|
|
453
|
-
const lastIndex = entries.length - 1;
|
|
454
|
-
entries.forEach(([key, value], index) => {
|
|
455
|
-
const prefix = index === lastIndex ? "\u2514\u2500" : "\u251C\u2500";
|
|
456
|
-
const formatted = formatValue(value);
|
|
457
|
-
console.log(` ${colors.dim}${prefix}${colors.reset} ${colors.cyan}${key}:${colors.reset} ${formatted}`);
|
|
458
|
-
});
|
|
459
|
-
}
|
|
460
|
-
function createLogMethod(level) {
|
|
461
|
-
return function logMethod(tagOrEvent, message) {
|
|
462
|
-
if (typeof tagOrEvent === "string" && message !== void 0) emitTaggedLog(level, tagOrEvent, message);
|
|
463
|
-
else if (typeof tagOrEvent === "object") emitWideEvent(level, tagOrEvent);
|
|
464
|
-
else emitTaggedLog(level, "log", String(tagOrEvent));
|
|
465
|
-
};
|
|
466
|
-
}
|
|
467
|
-
var _log = {
|
|
468
|
-
info: createLogMethod("info"),
|
|
469
|
-
error: createLogMethod("error"),
|
|
470
|
-
warn: createLogMethod("warn"),
|
|
471
|
-
debug: createLogMethod("debug")
|
|
472
|
-
};
|
|
473
|
-
var noopLogger = {
|
|
474
|
-
set() {
|
|
475
|
-
},
|
|
476
|
-
error() {
|
|
477
|
-
},
|
|
478
|
-
info() {
|
|
479
|
-
},
|
|
480
|
-
warn() {
|
|
481
|
-
},
|
|
482
|
-
emit() {
|
|
483
|
-
return null;
|
|
484
|
-
},
|
|
485
|
-
getContext() {
|
|
486
|
-
return {};
|
|
487
|
-
}
|
|
488
|
-
};
|
|
489
|
-
function createRequestLogger(options = {}) {
|
|
490
|
-
if (!globalEnabled) return noopLogger;
|
|
491
|
-
const startTime = Date.now();
|
|
492
|
-
let context = {
|
|
493
|
-
method: options.method,
|
|
494
|
-
path: options.path,
|
|
495
|
-
requestId: options.requestId
|
|
496
|
-
};
|
|
497
|
-
let hasError = false;
|
|
498
|
-
let hasWarn = false;
|
|
499
|
-
function addRequestLog(level, message) {
|
|
500
|
-
const entry = {
|
|
501
|
-
level,
|
|
502
|
-
message,
|
|
503
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
504
|
-
};
|
|
505
|
-
const requestLogs = Array.isArray(context.requestLogs) ? [...context.requestLogs, entry] : [entry];
|
|
506
|
-
context = {
|
|
507
|
-
...context,
|
|
508
|
-
requestLogs
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
return {
|
|
512
|
-
set(data) {
|
|
513
|
-
context = deepDefaults(data, context);
|
|
514
|
-
},
|
|
515
|
-
error(error, errorContext) {
|
|
516
|
-
hasError = true;
|
|
517
|
-
const err = typeof error === "string" ? new Error(error) : error;
|
|
518
|
-
context = deepDefaults({
|
|
519
|
-
...errorContext,
|
|
520
|
-
error: {
|
|
521
|
-
name: err.name,
|
|
522
|
-
message: err.message,
|
|
523
|
-
stack: err.stack,
|
|
524
|
-
..."status" in err && { status: err.status },
|
|
525
|
-
..."statusText" in err && { statusText: err.statusText },
|
|
526
|
-
..."statusCode" in err && { statusCode: err.statusCode },
|
|
527
|
-
..."statusMessage" in err && { statusMessage: err.statusMessage },
|
|
528
|
-
..."data" in err && { data: err.data },
|
|
529
|
-
..."cause" in err && { cause: err.cause }
|
|
530
|
-
}
|
|
531
|
-
}, context);
|
|
532
|
-
},
|
|
533
|
-
info(message, infoContext) {
|
|
534
|
-
addRequestLog("info", message);
|
|
535
|
-
if (infoContext) {
|
|
536
|
-
const { requestLogs: _, ...rest } = infoContext;
|
|
537
|
-
context = deepDefaults(rest, context);
|
|
538
|
-
}
|
|
539
|
-
},
|
|
540
|
-
warn(message, warnContext) {
|
|
541
|
-
hasWarn = true;
|
|
542
|
-
addRequestLog("warn", message);
|
|
543
|
-
if (warnContext) {
|
|
544
|
-
const { requestLogs: _, ...rest } = warnContext;
|
|
545
|
-
context = deepDefaults(rest, context);
|
|
546
|
-
}
|
|
547
|
-
},
|
|
548
|
-
emit(overrides) {
|
|
549
|
-
const durationMs = Date.now() - startTime;
|
|
550
|
-
const duration = formatDuration(durationMs);
|
|
551
|
-
const level = hasError ? "error" : hasWarn ? "warn" : "info";
|
|
552
|
-
const { _forceKeep, ...restOverrides } = overrides ?? {};
|
|
553
|
-
const tailCtx = {
|
|
554
|
-
status: context.status ?? restOverrides.status,
|
|
555
|
-
duration: durationMs,
|
|
556
|
-
path: context.path,
|
|
557
|
-
method: context.method,
|
|
558
|
-
context: {
|
|
559
|
-
...context,
|
|
560
|
-
...restOverrides
|
|
561
|
-
}
|
|
562
|
-
};
|
|
563
|
-
if (!(_forceKeep || shouldKeep(tailCtx)) && !shouldSample(level)) return null;
|
|
564
|
-
return emitWideEvent(level, {
|
|
565
|
-
...context,
|
|
566
|
-
...restOverrides,
|
|
567
|
-
duration
|
|
568
|
-
}, true);
|
|
569
|
-
},
|
|
570
|
-
getContext() {
|
|
571
|
-
return { ...context };
|
|
572
|
-
}
|
|
573
|
-
};
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
// src/utils/logger/index.ts
|
|
577
|
-
var validLevels = ["debug", "info", "error"];
|
|
578
|
-
function levelFromEnv() {
|
|
579
|
-
const env = process.env.STASH_STACK_LOG;
|
|
580
|
-
if (env && validLevels.includes(env)) return env;
|
|
581
|
-
return "error";
|
|
582
|
-
}
|
|
583
|
-
function samplingRatesForLevel(level) {
|
|
584
|
-
switch (level) {
|
|
585
|
-
case "debug":
|
|
586
|
-
return { debug: 100, info: 100, warn: 100, error: 100 };
|
|
587
|
-
case "info":
|
|
588
|
-
return { debug: 0, info: 100, warn: 100, error: 100 };
|
|
589
|
-
case "error":
|
|
590
|
-
default:
|
|
591
|
-
return { debug: 0, info: 0, warn: 0, error: 100 };
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
var initialized = false;
|
|
595
|
-
function initStackLogger() {
|
|
596
|
-
if (initialized) return;
|
|
597
|
-
initialized = true;
|
|
598
|
-
const level = levelFromEnv();
|
|
599
|
-
const rates = samplingRatesForLevel(level);
|
|
600
|
-
initLogger({
|
|
601
|
-
env: { service: "@cipherstash/stack" },
|
|
602
|
-
enabled: true,
|
|
603
|
-
sampling: { rates }
|
|
604
|
-
});
|
|
605
|
-
}
|
|
606
|
-
initStackLogger();
|
|
607
|
-
function safeMessage(args) {
|
|
608
|
-
return typeof args[0] === "string" ? args[0] : "";
|
|
609
|
-
}
|
|
610
|
-
var logger = {
|
|
611
|
-
debug(...args) {
|
|
612
|
-
const log = createRequestLogger();
|
|
613
|
-
log.set({
|
|
614
|
-
level: "debug",
|
|
615
|
-
source: "@cipherstash/stack",
|
|
616
|
-
message: safeMessage(args)
|
|
617
|
-
});
|
|
618
|
-
log.emit();
|
|
619
|
-
},
|
|
620
|
-
info(...args) {
|
|
621
|
-
const log = createRequestLogger();
|
|
622
|
-
log.set({ source: "@cipherstash/stack" });
|
|
623
|
-
log.info(safeMessage(args));
|
|
624
|
-
log.emit();
|
|
625
|
-
},
|
|
626
|
-
warn(...args) {
|
|
627
|
-
const log = createRequestLogger();
|
|
628
|
-
log.warn(safeMessage(args));
|
|
629
|
-
log.emit();
|
|
630
|
-
},
|
|
631
|
-
error(...args) {
|
|
632
|
-
const log = createRequestLogger();
|
|
633
|
-
log.error(safeMessage(args));
|
|
634
|
-
log.emit();
|
|
635
|
-
}
|
|
636
|
-
};
|
|
637
|
-
|
|
638
|
-
// src/supabase/helpers.ts
|
|
639
|
-
function getEncryptedColumnNames(schema) {
|
|
640
|
-
const built = schema.build();
|
|
641
|
-
return Object.keys(built.columns);
|
|
642
|
-
}
|
|
643
|
-
function isEncryptedColumn(columnName, encryptedColumnNames) {
|
|
644
|
-
return encryptedColumnNames.includes(columnName);
|
|
645
|
-
}
|
|
646
|
-
function addJsonbCasts(columns, encryptedColumnNames) {
|
|
647
|
-
return columns.split(",").map((col) => {
|
|
648
|
-
const trimmed = col.trim();
|
|
649
|
-
if (!trimmed) return col;
|
|
650
|
-
if (trimmed.includes("::")) return col;
|
|
651
|
-
if (trimmed.includes("(") || trimmed.includes(".")) return col;
|
|
652
|
-
const parts = trimmed.split(/\s+/);
|
|
653
|
-
const colName = parts[0];
|
|
654
|
-
if (isEncryptedColumn(colName, encryptedColumnNames)) {
|
|
655
|
-
const leadingWhitespace = col.match(/^(\s*)/)?.[1] ?? "";
|
|
656
|
-
if (parts.length > 1) {
|
|
657
|
-
return `${leadingWhitespace}${colName}::jsonb ${parts.slice(1).join(" ")}`;
|
|
658
|
-
}
|
|
659
|
-
return `${leadingWhitespace}${colName}::jsonb`;
|
|
660
|
-
}
|
|
661
|
-
return col;
|
|
662
|
-
}).join(",");
|
|
663
|
-
}
|
|
664
|
-
function mapFilterOpToQueryType(op) {
|
|
665
|
-
switch (op) {
|
|
666
|
-
case "eq":
|
|
667
|
-
case "neq":
|
|
668
|
-
case "in":
|
|
669
|
-
case "is":
|
|
670
|
-
return "equality";
|
|
671
|
-
case "like":
|
|
672
|
-
case "ilike":
|
|
673
|
-
return "freeTextSearch";
|
|
674
|
-
case "gt":
|
|
675
|
-
case "gte":
|
|
676
|
-
case "lt":
|
|
677
|
-
case "lte":
|
|
678
|
-
return "orderAndRange";
|
|
679
|
-
default:
|
|
680
|
-
return "equality";
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
function parseOrString(orString) {
|
|
684
|
-
const conditions = [];
|
|
685
|
-
const parts = splitOrString(orString);
|
|
686
|
-
for (const part of parts) {
|
|
687
|
-
const trimmed = part.trim();
|
|
688
|
-
if (!trimmed) continue;
|
|
689
|
-
const firstDot = trimmed.indexOf(".");
|
|
690
|
-
if (firstDot === -1) continue;
|
|
691
|
-
const column = trimmed.slice(0, firstDot);
|
|
692
|
-
const rest = trimmed.slice(firstDot + 1);
|
|
693
|
-
const secondDot = rest.indexOf(".");
|
|
694
|
-
if (secondDot === -1) continue;
|
|
695
|
-
const op = rest.slice(0, secondDot);
|
|
696
|
-
const value = rest.slice(secondDot + 1);
|
|
697
|
-
const parsedValue = parseOrValue(value);
|
|
698
|
-
conditions.push({ column, op, value: parsedValue });
|
|
699
|
-
}
|
|
700
|
-
return conditions;
|
|
701
|
-
}
|
|
702
|
-
function rebuildOrString(conditions) {
|
|
703
|
-
return conditions.map((c) => {
|
|
704
|
-
const value = formatOrValue(c.value);
|
|
705
|
-
return `${c.column}.${c.op}.${value}`;
|
|
706
|
-
}).join(",");
|
|
707
|
-
}
|
|
708
|
-
function splitOrString(input) {
|
|
709
|
-
const parts = [];
|
|
710
|
-
let current = "";
|
|
711
|
-
let depth = 0;
|
|
712
|
-
let inQuotes = false;
|
|
713
|
-
for (const char of input) {
|
|
714
|
-
if (char === '"' && depth === 0) {
|
|
715
|
-
inQuotes = !inQuotes;
|
|
716
|
-
current += char;
|
|
717
|
-
} else if (char === "(" && !inQuotes) {
|
|
718
|
-
depth++;
|
|
719
|
-
current += char;
|
|
720
|
-
} else if (char === ")" && !inQuotes) {
|
|
721
|
-
depth--;
|
|
722
|
-
current += char;
|
|
723
|
-
} else if (char === "," && depth === 0 && !inQuotes) {
|
|
724
|
-
parts.push(current);
|
|
725
|
-
current = "";
|
|
726
|
-
} else {
|
|
727
|
-
current += char;
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
if (current) {
|
|
731
|
-
parts.push(current);
|
|
732
|
-
}
|
|
733
|
-
return parts;
|
|
734
|
-
}
|
|
735
|
-
function parseOrValue(value) {
|
|
736
|
-
if (value.startsWith('"') && value.endsWith('"')) {
|
|
737
|
-
return value.slice(1, -1);
|
|
738
|
-
}
|
|
739
|
-
if (value.startsWith("(") && value.endsWith(")")) {
|
|
740
|
-
return value.slice(1, -1).split(",").map((v) => v.trim());
|
|
741
|
-
}
|
|
742
|
-
if (value === "true") return true;
|
|
743
|
-
if (value === "false") return false;
|
|
744
|
-
if (value === "null") return null;
|
|
745
|
-
return value;
|
|
746
|
-
}
|
|
747
|
-
var POSTGREST_RESERVED = /[,().]/;
|
|
748
|
-
function formatOrValue(value) {
|
|
749
|
-
if (Array.isArray(value)) {
|
|
750
|
-
return `(${value.join(",")})`;
|
|
751
|
-
}
|
|
752
|
-
if (value === null) return "null";
|
|
753
|
-
if (value === true) return "true";
|
|
754
|
-
if (value === false) return "false";
|
|
755
|
-
const str = String(value);
|
|
756
|
-
if (POSTGREST_RESERVED.test(str)) {
|
|
757
|
-
return `"${str}"`;
|
|
758
|
-
}
|
|
759
|
-
return str;
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
// src/supabase/query-builder.ts
|
|
763
|
-
var EncryptedQueryBuilderImpl = class {
|
|
764
|
-
tableName;
|
|
765
|
-
schema;
|
|
766
|
-
encryptionClient;
|
|
767
|
-
supabaseClient;
|
|
768
|
-
encryptedColumnNames;
|
|
769
|
-
// Recorded operations
|
|
770
|
-
mutation = null;
|
|
771
|
-
selectColumns = null;
|
|
772
|
-
selectOptions = void 0;
|
|
773
|
-
filters = [];
|
|
774
|
-
orFilters = [];
|
|
775
|
-
matchFilters = [];
|
|
776
|
-
notFilters = [];
|
|
777
|
-
rawFilters = [];
|
|
778
|
-
transforms = [];
|
|
779
|
-
resultMode = "array";
|
|
780
|
-
shouldThrowOnError = false;
|
|
781
|
-
// Encryption-specific state
|
|
782
|
-
lockContext = null;
|
|
783
|
-
auditConfig = null;
|
|
784
|
-
constructor(tableName, schema, encryptionClient, supabaseClient) {
|
|
785
|
-
this.tableName = tableName;
|
|
786
|
-
this.schema = schema;
|
|
787
|
-
this.encryptionClient = encryptionClient;
|
|
788
|
-
this.supabaseClient = supabaseClient;
|
|
789
|
-
this.encryptedColumnNames = getEncryptedColumnNames(schema);
|
|
790
|
-
}
|
|
791
|
-
// ---------------------------------------------------------------------------
|
|
792
|
-
// Mutation methods
|
|
793
|
-
// ---------------------------------------------------------------------------
|
|
794
|
-
select(columns, options) {
|
|
795
|
-
if (columns === "*") {
|
|
796
|
-
throw new Error(
|
|
797
|
-
"encryptedSupabase does not support select('*'). Please list columns explicitly so that encrypted columns can be cast with ::jsonb."
|
|
798
|
-
);
|
|
799
|
-
}
|
|
800
|
-
this.selectColumns = columns;
|
|
801
|
-
this.selectOptions = options;
|
|
802
|
-
return this;
|
|
803
|
-
}
|
|
804
|
-
insert(data, options) {
|
|
805
|
-
this.mutation = {
|
|
806
|
-
kind: "insert",
|
|
807
|
-
data,
|
|
808
|
-
options
|
|
809
|
-
};
|
|
810
|
-
return this;
|
|
811
|
-
}
|
|
812
|
-
update(data, options) {
|
|
813
|
-
this.mutation = {
|
|
814
|
-
kind: "update",
|
|
815
|
-
data,
|
|
816
|
-
options
|
|
817
|
-
};
|
|
818
|
-
return this;
|
|
819
|
-
}
|
|
820
|
-
upsert(data, options) {
|
|
821
|
-
this.mutation = {
|
|
822
|
-
kind: "upsert",
|
|
823
|
-
data,
|
|
824
|
-
options
|
|
825
|
-
};
|
|
826
|
-
return this;
|
|
827
|
-
}
|
|
828
|
-
delete(options) {
|
|
829
|
-
this.mutation = { kind: "delete", options };
|
|
830
|
-
return this;
|
|
831
|
-
}
|
|
832
|
-
// ---------------------------------------------------------------------------
|
|
833
|
-
// Filter methods
|
|
834
|
-
// ---------------------------------------------------------------------------
|
|
835
|
-
eq(column, value) {
|
|
836
|
-
this.filters.push({ op: "eq", column, value });
|
|
837
|
-
return this;
|
|
838
|
-
}
|
|
839
|
-
neq(column, value) {
|
|
840
|
-
this.filters.push({ op: "neq", column, value });
|
|
841
|
-
return this;
|
|
842
|
-
}
|
|
843
|
-
gt(column, value) {
|
|
844
|
-
this.filters.push({ op: "gt", column, value });
|
|
845
|
-
return this;
|
|
846
|
-
}
|
|
847
|
-
gte(column, value) {
|
|
848
|
-
this.filters.push({ op: "gte", column, value });
|
|
849
|
-
return this;
|
|
850
|
-
}
|
|
851
|
-
lt(column, value) {
|
|
852
|
-
this.filters.push({ op: "lt", column, value });
|
|
853
|
-
return this;
|
|
854
|
-
}
|
|
855
|
-
lte(column, value) {
|
|
856
|
-
this.filters.push({ op: "lte", column, value });
|
|
857
|
-
return this;
|
|
858
|
-
}
|
|
859
|
-
like(column, pattern) {
|
|
860
|
-
this.filters.push({ op: "like", column, value: pattern });
|
|
861
|
-
return this;
|
|
862
|
-
}
|
|
863
|
-
ilike(column, pattern) {
|
|
864
|
-
this.filters.push({ op: "ilike", column, value: pattern });
|
|
865
|
-
return this;
|
|
866
|
-
}
|
|
867
|
-
is(column, value) {
|
|
868
|
-
this.filters.push({ op: "is", column, value });
|
|
869
|
-
return this;
|
|
870
|
-
}
|
|
871
|
-
in(column, values) {
|
|
872
|
-
this.filters.push({ op: "in", column, value: values });
|
|
873
|
-
return this;
|
|
874
|
-
}
|
|
875
|
-
filter(column, operator, value) {
|
|
876
|
-
this.rawFilters.push({ column, operator, value });
|
|
877
|
-
return this;
|
|
878
|
-
}
|
|
879
|
-
not(column, operator, value) {
|
|
880
|
-
this.notFilters.push({ column, op: operator, value });
|
|
881
|
-
return this;
|
|
882
|
-
}
|
|
883
|
-
or(filtersOrConditions, options) {
|
|
884
|
-
if (typeof filtersOrConditions === "string") {
|
|
885
|
-
this.orFilters.push({
|
|
886
|
-
kind: "string",
|
|
887
|
-
value: filtersOrConditions,
|
|
888
|
-
referencedTable: options?.referencedTable ?? options?.foreignTable
|
|
889
|
-
});
|
|
890
|
-
} else {
|
|
891
|
-
this.orFilters.push({
|
|
892
|
-
kind: "structured",
|
|
893
|
-
conditions: filtersOrConditions
|
|
894
|
-
});
|
|
895
|
-
}
|
|
896
|
-
return this;
|
|
897
|
-
}
|
|
898
|
-
match(query) {
|
|
899
|
-
this.matchFilters.push({ query });
|
|
900
|
-
return this;
|
|
901
|
-
}
|
|
902
|
-
// ---------------------------------------------------------------------------
|
|
903
|
-
// Transform methods (passthrough)
|
|
904
|
-
// ---------------------------------------------------------------------------
|
|
905
|
-
order(column, options) {
|
|
906
|
-
this.transforms.push({ kind: "order", column, options });
|
|
907
|
-
return this;
|
|
908
|
-
}
|
|
909
|
-
limit(count, options) {
|
|
910
|
-
this.transforms.push({ kind: "limit", count, options });
|
|
911
|
-
return this;
|
|
912
|
-
}
|
|
913
|
-
range(from, to, options) {
|
|
914
|
-
this.transforms.push({ kind: "range", from, to, options });
|
|
915
|
-
return this;
|
|
916
|
-
}
|
|
917
|
-
single() {
|
|
918
|
-
this.resultMode = "single";
|
|
919
|
-
this.transforms.push({ kind: "single" });
|
|
920
|
-
return this;
|
|
921
|
-
}
|
|
922
|
-
maybeSingle() {
|
|
923
|
-
this.resultMode = "maybeSingle";
|
|
924
|
-
this.transforms.push({ kind: "maybeSingle" });
|
|
925
|
-
return this;
|
|
926
|
-
}
|
|
927
|
-
csv() {
|
|
928
|
-
this.transforms.push({ kind: "csv" });
|
|
929
|
-
return this;
|
|
930
|
-
}
|
|
931
|
-
abortSignal(signal) {
|
|
932
|
-
this.transforms.push({ kind: "abortSignal", signal });
|
|
933
|
-
return this;
|
|
934
|
-
}
|
|
935
|
-
throwOnError() {
|
|
936
|
-
this.shouldThrowOnError = true;
|
|
937
|
-
this.transforms.push({ kind: "throwOnError" });
|
|
938
|
-
return this;
|
|
939
|
-
}
|
|
940
|
-
returns() {
|
|
941
|
-
return this;
|
|
942
|
-
}
|
|
943
|
-
// ---------------------------------------------------------------------------
|
|
944
|
-
// Encryption-specific methods
|
|
945
|
-
// ---------------------------------------------------------------------------
|
|
946
|
-
withLockContext(lockContext) {
|
|
947
|
-
this.lockContext = lockContext;
|
|
948
|
-
return this;
|
|
949
|
-
}
|
|
950
|
-
audit(config) {
|
|
951
|
-
this.auditConfig = config;
|
|
952
|
-
return this;
|
|
953
|
-
}
|
|
954
|
-
// ---------------------------------------------------------------------------
|
|
955
|
-
// PromiseLike implementation (deferred execution)
|
|
956
|
-
// ---------------------------------------------------------------------------
|
|
957
|
-
then(onfulfilled, onrejected) {
|
|
958
|
-
return this.execute().then(onfulfilled, onrejected);
|
|
959
|
-
}
|
|
960
|
-
// ---------------------------------------------------------------------------
|
|
961
|
-
// Core execution
|
|
962
|
-
// ---------------------------------------------------------------------------
|
|
963
|
-
async execute() {
|
|
964
|
-
try {
|
|
965
|
-
logger.debug(`Supabase encrypted query on table "${this.tableName}".`);
|
|
966
|
-
const encryptedMutation = await this.encryptMutationData();
|
|
967
|
-
const selectString = this.buildSelectString();
|
|
968
|
-
const encryptedFilters = await this.encryptFilterValues();
|
|
969
|
-
const result = await this.buildAndExecuteQuery(
|
|
970
|
-
encryptedMutation,
|
|
971
|
-
selectString,
|
|
972
|
-
encryptedFilters
|
|
973
|
-
);
|
|
974
|
-
return await this.decryptResults(result);
|
|
975
|
-
} catch (err) {
|
|
976
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
977
|
-
logger.error(
|
|
978
|
-
`Supabase encrypted query failed on table "${this.tableName}": ${message}`
|
|
979
|
-
);
|
|
980
|
-
const error = {
|
|
981
|
-
message,
|
|
982
|
-
encryptionError: void 0
|
|
983
|
-
};
|
|
984
|
-
if (this.shouldThrowOnError) {
|
|
985
|
-
throw err;
|
|
986
|
-
}
|
|
987
|
-
return {
|
|
988
|
-
data: null,
|
|
989
|
-
error,
|
|
990
|
-
count: null,
|
|
991
|
-
status: 500,
|
|
992
|
-
statusText: "Encryption Error"
|
|
993
|
-
};
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
// ---------------------------------------------------------------------------
|
|
997
|
-
// Step 1: Encrypt mutation data
|
|
998
|
-
// ---------------------------------------------------------------------------
|
|
999
|
-
async encryptMutationData() {
|
|
1000
|
-
if (!this.mutation) return null;
|
|
1001
|
-
if (this.mutation.kind === "delete") return null;
|
|
1002
|
-
const data = this.mutation.data;
|
|
1003
|
-
if (Array.isArray(data)) {
|
|
1004
|
-
const baseOp2 = this.encryptionClient.bulkEncryptModels(data, this.schema);
|
|
1005
|
-
const op2 = this.lockContext ? baseOp2.withLockContext(this.lockContext) : baseOp2;
|
|
1006
|
-
if (this.auditConfig) op2.audit(this.auditConfig);
|
|
1007
|
-
const result2 = await op2;
|
|
1008
|
-
if (result2.failure) {
|
|
1009
|
-
logger.error(
|
|
1010
|
-
`Supabase: failed to encrypt models for table "${this.tableName}"`
|
|
1011
|
-
);
|
|
1012
|
-
throw new EncryptionFailedError(
|
|
1013
|
-
`Failed to encrypt models: ${result2.failure.message}`,
|
|
1014
|
-
result2.failure
|
|
1015
|
-
);
|
|
1016
|
-
}
|
|
1017
|
-
return bulkModelsToEncryptedPgComposites(result2.data);
|
|
1018
|
-
}
|
|
1019
|
-
const baseOp = this.encryptionClient.encryptModel(data, this.schema);
|
|
1020
|
-
const op = this.lockContext ? baseOp.withLockContext(this.lockContext) : baseOp;
|
|
1021
|
-
if (this.auditConfig) op.audit(this.auditConfig);
|
|
1022
|
-
const result = await op;
|
|
1023
|
-
if (result.failure) {
|
|
1024
|
-
logger.error(
|
|
1025
|
-
`Supabase: failed to encrypt model for table "${this.tableName}"`
|
|
1026
|
-
);
|
|
1027
|
-
throw new EncryptionFailedError(
|
|
1028
|
-
`Failed to encrypt model: ${result.failure.message}`,
|
|
1029
|
-
result.failure
|
|
1030
|
-
);
|
|
1031
|
-
}
|
|
1032
|
-
return modelToEncryptedPgComposites(result.data);
|
|
1033
|
-
}
|
|
1034
|
-
// ---------------------------------------------------------------------------
|
|
1035
|
-
// Step 2: Build select string with casts
|
|
1036
|
-
// ---------------------------------------------------------------------------
|
|
1037
|
-
buildSelectString() {
|
|
1038
|
-
if (this.selectColumns === null) return null;
|
|
1039
|
-
return addJsonbCasts(this.selectColumns, this.encryptedColumnNames);
|
|
1040
|
-
}
|
|
1041
|
-
// ---------------------------------------------------------------------------
|
|
1042
|
-
// Step 3: Encrypt filter values
|
|
1043
|
-
// ---------------------------------------------------------------------------
|
|
1044
|
-
async encryptFilterValues() {
|
|
1045
|
-
const terms = [];
|
|
1046
|
-
const termMap = [];
|
|
1047
|
-
const tableColumns = this.getColumnMap();
|
|
1048
|
-
for (let i = 0; i < this.filters.length; i++) {
|
|
1049
|
-
const f = this.filters[i];
|
|
1050
|
-
if (!isEncryptedColumn(f.column, this.encryptedColumnNames)) continue;
|
|
1051
|
-
const column = tableColumns[f.column];
|
|
1052
|
-
if (!column) continue;
|
|
1053
|
-
if (f.op === "in" && Array.isArray(f.value)) {
|
|
1054
|
-
for (let j = 0; j < f.value.length; j++) {
|
|
1055
|
-
terms.push({
|
|
1056
|
-
value: f.value[j],
|
|
1057
|
-
column,
|
|
1058
|
-
table: this.schema,
|
|
1059
|
-
queryType: mapFilterOpToQueryType(f.op),
|
|
1060
|
-
returnType: "composite-literal"
|
|
1061
|
-
});
|
|
1062
|
-
termMap.push({ source: "filter", filterIndex: i, inIndex: j });
|
|
1063
|
-
}
|
|
1064
|
-
} else if (f.op === "is") {
|
|
1065
|
-
continue;
|
|
1066
|
-
} else {
|
|
1067
|
-
terms.push({
|
|
1068
|
-
value: f.value,
|
|
1069
|
-
column,
|
|
1070
|
-
table: this.schema,
|
|
1071
|
-
queryType: mapFilterOpToQueryType(f.op),
|
|
1072
|
-
returnType: "composite-literal"
|
|
1073
|
-
});
|
|
1074
|
-
termMap.push({ source: "filter", filterIndex: i });
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
for (let i = 0; i < this.matchFilters.length; i++) {
|
|
1078
|
-
const mf = this.matchFilters[i];
|
|
1079
|
-
for (const [colName, value] of Object.entries(mf.query)) {
|
|
1080
|
-
if (!isEncryptedColumn(colName, this.encryptedColumnNames)) continue;
|
|
1081
|
-
const column = tableColumns[colName];
|
|
1082
|
-
if (!column) continue;
|
|
1083
|
-
terms.push({
|
|
1084
|
-
value,
|
|
1085
|
-
column,
|
|
1086
|
-
table: this.schema,
|
|
1087
|
-
queryType: "equality",
|
|
1088
|
-
returnType: "composite-literal"
|
|
1089
|
-
});
|
|
1090
|
-
termMap.push({ source: "match", matchIndex: i, column: colName });
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
for (let i = 0; i < this.notFilters.length; i++) {
|
|
1094
|
-
const nf = this.notFilters[i];
|
|
1095
|
-
if (!isEncryptedColumn(nf.column, this.encryptedColumnNames)) continue;
|
|
1096
|
-
const column = tableColumns[nf.column];
|
|
1097
|
-
if (!column) continue;
|
|
1098
|
-
terms.push({
|
|
1099
|
-
value: nf.value,
|
|
1100
|
-
column,
|
|
1101
|
-
table: this.schema,
|
|
1102
|
-
queryType: mapFilterOpToQueryType(nf.op),
|
|
1103
|
-
returnType: "composite-literal"
|
|
1104
|
-
});
|
|
1105
|
-
termMap.push({ source: "not", notIndex: i });
|
|
1106
|
-
}
|
|
1107
|
-
for (let i = 0; i < this.orFilters.length; i++) {
|
|
1108
|
-
const of_ = this.orFilters[i];
|
|
1109
|
-
if (of_.kind === "string") {
|
|
1110
|
-
const parsed = parseOrString(of_.value);
|
|
1111
|
-
for (let j = 0; j < parsed.length; j++) {
|
|
1112
|
-
const cond = parsed[j];
|
|
1113
|
-
if (!isEncryptedColumn(cond.column, this.encryptedColumnNames))
|
|
1114
|
-
continue;
|
|
1115
|
-
const column = tableColumns[cond.column];
|
|
1116
|
-
if (!column) continue;
|
|
1117
|
-
terms.push({
|
|
1118
|
-
value: cond.value,
|
|
1119
|
-
column,
|
|
1120
|
-
table: this.schema,
|
|
1121
|
-
queryType: mapFilterOpToQueryType(cond.op),
|
|
1122
|
-
returnType: "composite-literal"
|
|
1123
|
-
});
|
|
1124
|
-
termMap.push({ source: "or-string", orIndex: i, conditionIndex: j });
|
|
1125
|
-
}
|
|
1126
|
-
} else {
|
|
1127
|
-
for (let j = 0; j < of_.conditions.length; j++) {
|
|
1128
|
-
const cond = of_.conditions[j];
|
|
1129
|
-
if (!isEncryptedColumn(cond.column, this.encryptedColumnNames))
|
|
1130
|
-
continue;
|
|
1131
|
-
const column = tableColumns[cond.column];
|
|
1132
|
-
if (!column) continue;
|
|
1133
|
-
terms.push({
|
|
1134
|
-
value: cond.value,
|
|
1135
|
-
column,
|
|
1136
|
-
table: this.schema,
|
|
1137
|
-
queryType: mapFilterOpToQueryType(cond.op),
|
|
1138
|
-
returnType: "composite-literal"
|
|
1139
|
-
});
|
|
1140
|
-
termMap.push({
|
|
1141
|
-
source: "or-structured",
|
|
1142
|
-
orIndex: i,
|
|
1143
|
-
conditionIndex: j
|
|
1144
|
-
});
|
|
1145
|
-
}
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
for (let i = 0; i < this.rawFilters.length; i++) {
|
|
1149
|
-
const rf = this.rawFilters[i];
|
|
1150
|
-
if (!isEncryptedColumn(rf.column, this.encryptedColumnNames)) continue;
|
|
1151
|
-
const column = tableColumns[rf.column];
|
|
1152
|
-
if (!column) continue;
|
|
1153
|
-
terms.push({
|
|
1154
|
-
value: rf.value,
|
|
1155
|
-
column,
|
|
1156
|
-
table: this.schema,
|
|
1157
|
-
queryType: "equality",
|
|
1158
|
-
returnType: "composite-literal"
|
|
1159
|
-
});
|
|
1160
|
-
termMap.push({ source: "raw", rawIndex: i });
|
|
1161
|
-
}
|
|
1162
|
-
if (terms.length === 0) {
|
|
1163
|
-
return { encryptedValues: [], termMap: [] };
|
|
1164
|
-
}
|
|
1165
|
-
const baseOp = this.encryptionClient.encryptQuery(terms);
|
|
1166
|
-
const op = this.lockContext ? baseOp.withLockContext(this.lockContext) : baseOp;
|
|
1167
|
-
if (this.auditConfig) op.audit(this.auditConfig);
|
|
1168
|
-
const result = await op;
|
|
1169
|
-
if (result.failure) {
|
|
1170
|
-
logger.error(
|
|
1171
|
-
`Supabase: failed to encrypt query terms for table "${this.tableName}"`
|
|
1172
|
-
);
|
|
1173
|
-
throw new EncryptionFailedError(
|
|
1174
|
-
`Failed to encrypt query terms: ${result.failure.message}`,
|
|
1175
|
-
result.failure
|
|
1176
|
-
);
|
|
1177
|
-
}
|
|
1178
|
-
return { encryptedValues: result.data, termMap };
|
|
1179
|
-
}
|
|
1180
|
-
// ---------------------------------------------------------------------------
|
|
1181
|
-
// Step 4: Build and execute real Supabase query
|
|
1182
|
-
// ---------------------------------------------------------------------------
|
|
1183
|
-
async buildAndExecuteQuery(encryptedMutation, selectString, encryptedFilters) {
|
|
1184
|
-
let query = this.supabaseClient.from(this.tableName);
|
|
1185
|
-
if (this.mutation) {
|
|
1186
|
-
switch (this.mutation.kind) {
|
|
1187
|
-
case "insert":
|
|
1188
|
-
query = query.insert(encryptedMutation, this.mutation.options);
|
|
1189
|
-
break;
|
|
1190
|
-
case "update":
|
|
1191
|
-
query = query.update(encryptedMutation, this.mutation.options);
|
|
1192
|
-
break;
|
|
1193
|
-
case "upsert":
|
|
1194
|
-
query = query.upsert(encryptedMutation, this.mutation.options);
|
|
1195
|
-
break;
|
|
1196
|
-
case "delete":
|
|
1197
|
-
query = query.delete(this.mutation.options);
|
|
1198
|
-
break;
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
if (selectString !== null) {
|
|
1202
|
-
query = query.select(selectString, this.selectOptions);
|
|
1203
|
-
} else if (!this.mutation) {
|
|
1204
|
-
query = query.select("*", this.selectOptions);
|
|
1205
|
-
}
|
|
1206
|
-
query = this.applyFilters(query, encryptedFilters);
|
|
1207
|
-
for (const t of this.transforms) {
|
|
1208
|
-
switch (t.kind) {
|
|
1209
|
-
case "order":
|
|
1210
|
-
query = query.order(t.column, t.options);
|
|
1211
|
-
break;
|
|
1212
|
-
case "limit":
|
|
1213
|
-
query = query.limit(t.count, t.options);
|
|
1214
|
-
break;
|
|
1215
|
-
case "range":
|
|
1216
|
-
query = query.range(t.from, t.to, t.options);
|
|
1217
|
-
break;
|
|
1218
|
-
case "single":
|
|
1219
|
-
query = query.single();
|
|
1220
|
-
break;
|
|
1221
|
-
case "maybeSingle":
|
|
1222
|
-
query = query.maybeSingle();
|
|
1223
|
-
break;
|
|
1224
|
-
case "csv":
|
|
1225
|
-
query = query.csv();
|
|
1226
|
-
break;
|
|
1227
|
-
case "abortSignal":
|
|
1228
|
-
query = query.abortSignal(t.signal);
|
|
1229
|
-
break;
|
|
1230
|
-
case "throwOnError":
|
|
1231
|
-
query = query.throwOnError();
|
|
1232
|
-
break;
|
|
1233
|
-
}
|
|
1234
|
-
}
|
|
1235
|
-
const result = await query;
|
|
1236
|
-
return result;
|
|
1237
|
-
}
|
|
1238
|
-
// ---------------------------------------------------------------------------
|
|
1239
|
-
// Apply filters with encrypted values substituted
|
|
1240
|
-
// ---------------------------------------------------------------------------
|
|
1241
|
-
applyFilters(query, encryptedFilters) {
|
|
1242
|
-
let q = query;
|
|
1243
|
-
const filterValueMap = /* @__PURE__ */ new Map();
|
|
1244
|
-
const filterInMap = /* @__PURE__ */ new Map();
|
|
1245
|
-
const matchValueMap = /* @__PURE__ */ new Map();
|
|
1246
|
-
const notValueMap = /* @__PURE__ */ new Map();
|
|
1247
|
-
const rawValueMap = /* @__PURE__ */ new Map();
|
|
1248
|
-
const orStringConditionMap = /* @__PURE__ */ new Map();
|
|
1249
|
-
const orStructuredConditionMap = /* @__PURE__ */ new Map();
|
|
1250
|
-
for (let i = 0; i < encryptedFilters.termMap.length; i++) {
|
|
1251
|
-
const mapping = encryptedFilters.termMap[i];
|
|
1252
|
-
const encValue = encryptedFilters.encryptedValues[i];
|
|
1253
|
-
switch (mapping.source) {
|
|
1254
|
-
case "filter":
|
|
1255
|
-
if (mapping.inIndex !== void 0) {
|
|
1256
|
-
filterInMap.set(
|
|
1257
|
-
`${mapping.filterIndex}:${mapping.inIndex}`,
|
|
1258
|
-
encValue
|
|
1259
|
-
);
|
|
1260
|
-
} else {
|
|
1261
|
-
filterValueMap.set(mapping.filterIndex, encValue);
|
|
1262
|
-
}
|
|
1263
|
-
break;
|
|
1264
|
-
case "match":
|
|
1265
|
-
matchValueMap.set(`${mapping.matchIndex}:${mapping.column}`, encValue);
|
|
1266
|
-
break;
|
|
1267
|
-
case "not":
|
|
1268
|
-
notValueMap.set(mapping.notIndex, encValue);
|
|
1269
|
-
break;
|
|
1270
|
-
case "raw":
|
|
1271
|
-
rawValueMap.set(mapping.rawIndex, encValue);
|
|
1272
|
-
break;
|
|
1273
|
-
case "or-string":
|
|
1274
|
-
orStringConditionMap.set(
|
|
1275
|
-
`${mapping.orIndex}:${mapping.conditionIndex}`,
|
|
1276
|
-
encValue
|
|
1277
|
-
);
|
|
1278
|
-
break;
|
|
1279
|
-
case "or-structured":
|
|
1280
|
-
orStructuredConditionMap.set(
|
|
1281
|
-
`${mapping.orIndex}:${mapping.conditionIndex}`,
|
|
1282
|
-
encValue
|
|
1283
|
-
);
|
|
1284
|
-
break;
|
|
1285
|
-
}
|
|
1286
|
-
}
|
|
1287
|
-
for (let i = 0; i < this.filters.length; i++) {
|
|
1288
|
-
const f = this.filters[i];
|
|
1289
|
-
let value = f.value;
|
|
1290
|
-
if (filterValueMap.has(i)) {
|
|
1291
|
-
value = filterValueMap.get(i);
|
|
1292
|
-
} else if (f.op === "in" && Array.isArray(f.value)) {
|
|
1293
|
-
value = f.value.map((v, j) => {
|
|
1294
|
-
const key = `${i}:${j}`;
|
|
1295
|
-
return filterInMap.has(key) ? filterInMap.get(key) : v;
|
|
1296
|
-
});
|
|
1297
|
-
}
|
|
1298
|
-
switch (f.op) {
|
|
1299
|
-
case "eq":
|
|
1300
|
-
q = q.eq(f.column, value);
|
|
1301
|
-
break;
|
|
1302
|
-
case "neq":
|
|
1303
|
-
q = q.neq(f.column, value);
|
|
1304
|
-
break;
|
|
1305
|
-
case "gt":
|
|
1306
|
-
q = q.gt(f.column, value);
|
|
1307
|
-
break;
|
|
1308
|
-
case "gte":
|
|
1309
|
-
q = q.gte(f.column, value);
|
|
1310
|
-
break;
|
|
1311
|
-
case "lt":
|
|
1312
|
-
q = q.lt(f.column, value);
|
|
1313
|
-
break;
|
|
1314
|
-
case "lte":
|
|
1315
|
-
q = q.lte(f.column, value);
|
|
1316
|
-
break;
|
|
1317
|
-
case "like":
|
|
1318
|
-
q = q.like(f.column, value);
|
|
1319
|
-
break;
|
|
1320
|
-
case "ilike":
|
|
1321
|
-
q = q.ilike(f.column, value);
|
|
1322
|
-
break;
|
|
1323
|
-
case "is":
|
|
1324
|
-
q = q.is(f.column, value);
|
|
1325
|
-
break;
|
|
1326
|
-
case "in":
|
|
1327
|
-
q = q.in(f.column, value);
|
|
1328
|
-
break;
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
for (let i = 0; i < this.matchFilters.length; i++) {
|
|
1332
|
-
const mf = this.matchFilters[i];
|
|
1333
|
-
const resolvedQuery = {};
|
|
1334
|
-
for (const [colName, originalValue] of Object.entries(mf.query)) {
|
|
1335
|
-
const key = `${i}:${colName}`;
|
|
1336
|
-
resolvedQuery[colName] = matchValueMap.has(key) ? matchValueMap.get(key) : originalValue;
|
|
1337
|
-
}
|
|
1338
|
-
q = q.match(resolvedQuery);
|
|
1339
|
-
}
|
|
1340
|
-
for (let i = 0; i < this.notFilters.length; i++) {
|
|
1341
|
-
const nf = this.notFilters[i];
|
|
1342
|
-
const value = notValueMap.has(i) ? notValueMap.get(i) : nf.value;
|
|
1343
|
-
q = q.not(nf.column, nf.op, value);
|
|
1344
|
-
}
|
|
1345
|
-
for (let i = 0; i < this.orFilters.length; i++) {
|
|
1346
|
-
const of_ = this.orFilters[i];
|
|
1347
|
-
if (of_.kind === "string") {
|
|
1348
|
-
const parsed = parseOrString(of_.value);
|
|
1349
|
-
let hasEncrypted = false;
|
|
1350
|
-
for (let j = 0; j < parsed.length; j++) {
|
|
1351
|
-
const key = `${i}:${j}`;
|
|
1352
|
-
if (orStringConditionMap.has(key)) {
|
|
1353
|
-
parsed[j] = { ...parsed[j], value: orStringConditionMap.get(key) };
|
|
1354
|
-
hasEncrypted = true;
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
if (hasEncrypted) {
|
|
1358
|
-
q = q.or(rebuildOrString(parsed), {
|
|
1359
|
-
referencedTable: of_.referencedTable
|
|
1360
|
-
});
|
|
1361
|
-
} else {
|
|
1362
|
-
q = q.or(of_.value, { referencedTable: of_.referencedTable });
|
|
1363
|
-
}
|
|
1364
|
-
} else {
|
|
1365
|
-
const conditions = of_.conditions.map((cond, j) => {
|
|
1366
|
-
const key = `${i}:${j}`;
|
|
1367
|
-
if (orStructuredConditionMap.has(key)) {
|
|
1368
|
-
return { ...cond, value: orStructuredConditionMap.get(key) };
|
|
1369
|
-
}
|
|
1370
|
-
return cond;
|
|
1371
|
-
});
|
|
1372
|
-
q = q.or(rebuildOrString(conditions));
|
|
1373
|
-
}
|
|
1374
|
-
}
|
|
1375
|
-
for (let i = 0; i < this.rawFilters.length; i++) {
|
|
1376
|
-
const rf = this.rawFilters[i];
|
|
1377
|
-
const value = rawValueMap.has(i) ? rawValueMap.get(i) : rf.value;
|
|
1378
|
-
q = q.filter(rf.column, rf.operator, value);
|
|
1379
|
-
}
|
|
1380
|
-
return q;
|
|
1381
|
-
}
|
|
1382
|
-
// ---------------------------------------------------------------------------
|
|
1383
|
-
// Step 5: Decrypt results
|
|
1384
|
-
// ---------------------------------------------------------------------------
|
|
1385
|
-
async decryptResults(result) {
|
|
1386
|
-
if (result.error) {
|
|
1387
|
-
return {
|
|
1388
|
-
data: null,
|
|
1389
|
-
error: {
|
|
1390
|
-
message: result.error.message,
|
|
1391
|
-
details: result.error.details,
|
|
1392
|
-
hint: result.error.hint,
|
|
1393
|
-
code: result.error.code
|
|
1394
|
-
},
|
|
1395
|
-
count: result.count ?? null,
|
|
1396
|
-
status: result.status,
|
|
1397
|
-
statusText: result.statusText
|
|
1398
|
-
};
|
|
1399
|
-
}
|
|
1400
|
-
if (result.data === null || result.data === void 0) {
|
|
1401
|
-
return {
|
|
1402
|
-
data: null,
|
|
1403
|
-
error: null,
|
|
1404
|
-
count: result.count ?? null,
|
|
1405
|
-
status: result.status,
|
|
1406
|
-
statusText: result.statusText
|
|
1407
|
-
};
|
|
1408
|
-
}
|
|
1409
|
-
const hasSelect = this.selectColumns !== null;
|
|
1410
|
-
const hasMutationWithReturning = this.mutation !== null && hasSelect;
|
|
1411
|
-
if (!hasSelect && !hasMutationWithReturning) {
|
|
1412
|
-
return {
|
|
1413
|
-
data: result.data,
|
|
1414
|
-
error: null,
|
|
1415
|
-
count: result.count ?? null,
|
|
1416
|
-
status: result.status,
|
|
1417
|
-
statusText: result.statusText
|
|
1418
|
-
};
|
|
1419
|
-
}
|
|
1420
|
-
if (this.resultMode === "single" || this.resultMode === "maybeSingle") {
|
|
1421
|
-
if (result.data === null) {
|
|
1422
|
-
return {
|
|
1423
|
-
data: null,
|
|
1424
|
-
error: null,
|
|
1425
|
-
count: result.count ?? null,
|
|
1426
|
-
status: result.status,
|
|
1427
|
-
statusText: result.statusText
|
|
1428
|
-
};
|
|
1429
|
-
}
|
|
1430
|
-
const baseDecryptOp = this.encryptionClient.decryptModel(
|
|
1431
|
-
result.data
|
|
1432
|
-
);
|
|
1433
|
-
const decryptOp = this.lockContext ? baseDecryptOp.withLockContext(this.lockContext) : baseDecryptOp;
|
|
1434
|
-
if (this.auditConfig) decryptOp.audit(this.auditConfig);
|
|
1435
|
-
const decrypted2 = await decryptOp;
|
|
1436
|
-
if (decrypted2.failure) {
|
|
1437
|
-
logger.error(
|
|
1438
|
-
`Supabase: failed to decrypt model for table "${this.tableName}"`
|
|
1439
|
-
);
|
|
1440
|
-
throw new EncryptionFailedError(
|
|
1441
|
-
`Failed to decrypt model: ${decrypted2.failure.message}`,
|
|
1442
|
-
decrypted2.failure
|
|
1443
|
-
);
|
|
1444
|
-
}
|
|
1445
|
-
return {
|
|
1446
|
-
data: decrypted2.data,
|
|
1447
|
-
error: null,
|
|
1448
|
-
count: result.count ?? null,
|
|
1449
|
-
status: result.status,
|
|
1450
|
-
statusText: result.statusText
|
|
1451
|
-
};
|
|
1452
|
-
}
|
|
1453
|
-
const dataArray = result.data;
|
|
1454
|
-
if (dataArray.length === 0) {
|
|
1455
|
-
return {
|
|
1456
|
-
data: [],
|
|
1457
|
-
error: null,
|
|
1458
|
-
count: result.count ?? null,
|
|
1459
|
-
status: result.status,
|
|
1460
|
-
statusText: result.statusText
|
|
1461
|
-
};
|
|
1462
|
-
}
|
|
1463
|
-
const baseBulkDecryptOp = this.encryptionClient.bulkDecryptModels(dataArray);
|
|
1464
|
-
const bulkDecryptOp = this.lockContext ? baseBulkDecryptOp.withLockContext(this.lockContext) : baseBulkDecryptOp;
|
|
1465
|
-
if (this.auditConfig) bulkDecryptOp.audit(this.auditConfig);
|
|
1466
|
-
const decrypted = await bulkDecryptOp;
|
|
1467
|
-
if (decrypted.failure) {
|
|
1468
|
-
logger.error(
|
|
1469
|
-
`Supabase: failed to decrypt models for table "${this.tableName}"`
|
|
1470
|
-
);
|
|
1471
|
-
throw new EncryptionFailedError(
|
|
1472
|
-
`Failed to decrypt models: ${decrypted.failure.message}`,
|
|
1473
|
-
decrypted.failure
|
|
1474
|
-
);
|
|
1475
|
-
}
|
|
1476
|
-
return {
|
|
1477
|
-
data: decrypted.data,
|
|
1478
|
-
error: null,
|
|
1479
|
-
count: result.count ?? null,
|
|
1480
|
-
status: result.status,
|
|
1481
|
-
statusText: result.statusText
|
|
1482
|
-
};
|
|
1483
|
-
}
|
|
1484
|
-
// ---------------------------------------------------------------------------
|
|
1485
|
-
// Helpers
|
|
1486
|
-
// ---------------------------------------------------------------------------
|
|
1487
|
-
getColumnMap() {
|
|
1488
|
-
const map = {};
|
|
1489
|
-
const schema = this.schema;
|
|
1490
|
-
for (const colName of this.encryptedColumnNames) {
|
|
1491
|
-
const col = schema[colName];
|
|
1492
|
-
if (col instanceof EncryptedColumn) {
|
|
1493
|
-
map[colName] = col;
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1496
|
-
return map;
|
|
1497
|
-
}
|
|
1498
|
-
};
|
|
1499
|
-
var EncryptionFailedError = class extends Error {
|
|
1500
|
-
encryptionError;
|
|
1501
|
-
constructor(message, encryptionError) {
|
|
1502
|
-
super(message);
|
|
1503
|
-
this.name = "EncryptionFailedError";
|
|
1504
|
-
this.encryptionError = encryptionError;
|
|
1505
|
-
}
|
|
1506
|
-
};
|
|
1507
|
-
|
|
1508
|
-
// src/supabase/index.ts
|
|
1509
|
-
function encryptedSupabase(config) {
|
|
1510
|
-
const { encryptionClient, supabaseClient } = config;
|
|
1511
|
-
return {
|
|
1512
|
-
from(tableName, schema) {
|
|
1513
|
-
return new EncryptedQueryBuilderImpl(
|
|
1514
|
-
tableName,
|
|
1515
|
-
schema,
|
|
1516
|
-
encryptionClient,
|
|
1517
|
-
supabaseClient
|
|
1518
|
-
);
|
|
1519
|
-
}
|
|
1520
|
-
};
|
|
1521
|
-
}
|
|
1522
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
1523
|
-
0 && (module.exports = {
|
|
1524
|
-
encryptedSupabase
|
|
1525
|
-
});
|
|
1526
|
-
//# sourceMappingURL=index.cjs.map
|