@palbase/backend 16.0.0 → 17.0.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/dist/{chunk-7LAXRLPG.js → chunk-IBIME7N2.js} +321 -2
- package/dist/chunk-IBIME7N2.js.map +1 -0
- package/dist/db/index.d.cts +1 -2
- package/dist/db/index.d.ts +1 -2
- package/dist/db/index.js +6 -8
- package/dist/{endpoint-BW8bn1y-.d.cts → index-D6mRh4yq.d.cts} +654 -1
- package/dist/{endpoint-BW8bn1y-.d.ts → index-J1tnAnFj.d.ts} +654 -1
- package/dist/index.d.cts +2 -4
- package/dist/index.d.ts +2 -4
- package/dist/index.js +135 -23
- package/dist/index.js.map +1 -1
- package/dist/test/index.cjs +84 -962
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +64 -61
- package/dist/test/index.d.ts +64 -61
- package/dist/test/index.js +85 -465
- package/dist/test/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-7LAXRLPG.js.map +0 -1
- package/dist/chunk-I72YYSEI.js +0 -148
- package/dist/chunk-I72YYSEI.js.map +0 -1
- package/dist/chunk-ZZZHVPOQ.js +0 -327
- package/dist/chunk-ZZZHVPOQ.js.map +0 -1
- package/dist/index-Cww79LRh.d.cts +0 -656
- package/dist/index-IYODApZV.d.ts +0 -656
package/dist/test/index.js
CHANGED
|
@@ -1,481 +1,101 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
function rowsOf(table) {
|
|
15
|
-
let rows = store.get(table);
|
|
16
|
-
if (!rows) {
|
|
17
|
-
rows = [];
|
|
18
|
-
store.set(table, rows);
|
|
19
|
-
}
|
|
20
|
-
return rows;
|
|
21
|
-
}
|
|
22
|
-
function track(map, table, row) {
|
|
23
|
-
const list = map.get(table);
|
|
24
|
-
if (list) list.push(row);
|
|
25
|
-
else map.set(table, [row]);
|
|
1
|
+
// src/test/api.ts
|
|
2
|
+
var TestApiError = class extends Error {
|
|
3
|
+
status;
|
|
4
|
+
error;
|
|
5
|
+
data;
|
|
6
|
+
constructor(method, path, status, body) {
|
|
7
|
+
const envelope = body ?? {};
|
|
8
|
+
const code = envelope.error ?? String(status);
|
|
9
|
+
super(`${method} ${path} \u2192 ${status} ${code}${envelope.error_description ? `: ${envelope.error_description}` : ""}`);
|
|
10
|
+
this.name = "TestApiError";
|
|
11
|
+
this.status = status;
|
|
12
|
+
this.error = code;
|
|
13
|
+
this.data = envelope.data;
|
|
26
14
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
rowsOf(table).push(record);
|
|
34
|
-
track(tracked.inserted, table, record);
|
|
35
|
-
return record;
|
|
36
|
-
},
|
|
37
|
-
async update(table, id, data) {
|
|
38
|
-
const rows = store.get(table) ?? [];
|
|
39
|
-
const idx = rows.findIndex((r) => r["id"] === id);
|
|
40
|
-
const updated = idx >= 0 ? { ...rows[idx], ...data } : { id, ...data };
|
|
41
|
-
if (idx >= 0) {
|
|
42
|
-
rows[idx] = updated;
|
|
43
|
-
}
|
|
44
|
-
track(tracked.updated, table, updated);
|
|
45
|
-
return updated;
|
|
46
|
-
},
|
|
47
|
-
async delete(table, id) {
|
|
48
|
-
const rows = store.get(table) ?? [];
|
|
49
|
-
const idx = rows.findIndex((r) => r["id"] === id);
|
|
50
|
-
if (idx >= 0) rows.splice(idx, 1);
|
|
51
|
-
const list = tracked.deleted.get(table);
|
|
52
|
-
if (list) list.push(id);
|
|
53
|
-
else tracked.deleted.set(table, [id]);
|
|
54
|
-
},
|
|
55
|
-
async findById(table, id) {
|
|
56
|
-
const rows = store.get(table) ?? [];
|
|
57
|
-
return rows.find((r) => r["id"] === id) ?? null;
|
|
58
|
-
},
|
|
59
|
-
async findMany(table, query) {
|
|
60
|
-
const rows = store.get(table) ?? [];
|
|
61
|
-
if (!query) return rows;
|
|
62
|
-
return rows.filter(
|
|
63
|
-
(row) => Object.entries(query).every(([key, val]) => row[key] === val)
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
async function txPlan(plan) {
|
|
68
|
-
const snapshot = /* @__PURE__ */ new Map();
|
|
69
|
-
for (const [table, rows] of store) snapshot.set(table, [...rows]);
|
|
70
|
-
const trackedSnapshot = {
|
|
71
|
-
inserted: cloneTracked(tracked.inserted),
|
|
72
|
-
updated: cloneTracked(tracked.updated),
|
|
73
|
-
deleted: new Map([...tracked.deleted].map(([k, v]) => [k, [...v]]))
|
|
74
|
-
};
|
|
75
|
-
const results = [];
|
|
76
|
-
try {
|
|
77
|
-
for (const op of plan.ops) {
|
|
78
|
-
const result = applyOp(op, results);
|
|
79
|
-
results.push(result);
|
|
80
|
-
const failure = guardFailure(op.guard, result.rows.length);
|
|
81
|
-
if (failure) throw failure;
|
|
82
|
-
}
|
|
83
|
-
} catch (err) {
|
|
84
|
-
store.clear();
|
|
85
|
-
for (const [table, rows] of snapshot) store.set(table, rows);
|
|
86
|
-
tracked.inserted = trackedSnapshot.inserted;
|
|
87
|
-
tracked.updated = trackedSnapshot.updated;
|
|
88
|
-
tracked.deleted = trackedSnapshot.deleted;
|
|
89
|
-
throw err;
|
|
90
|
-
}
|
|
91
|
-
return { results };
|
|
92
|
-
}
|
|
93
|
-
function applyOp(op, results) {
|
|
94
|
-
switch (op.op) {
|
|
95
|
-
case "insert": {
|
|
96
|
-
const record = { id: crypto.randomUUID(), ...resolveMap(op.values ?? {}, results, null) };
|
|
97
|
-
rowsOf(op.table).push(record);
|
|
98
|
-
track(tracked.inserted, op.table, record);
|
|
99
|
-
return { rows: [record], rows_affected: 1 };
|
|
100
|
-
}
|
|
101
|
-
case "insertMany": {
|
|
102
|
-
const written = (op.rows ?? []).map((row) => {
|
|
103
|
-
const record = { id: crypto.randomUUID(), ...resolveMap(row, results, null) };
|
|
104
|
-
rowsOf(op.table).push(record);
|
|
105
|
-
track(tracked.inserted, op.table, record);
|
|
106
|
-
return record;
|
|
107
|
-
});
|
|
108
|
-
return { rows: written, rows_affected: written.length };
|
|
109
|
-
}
|
|
110
|
-
case "update": {
|
|
111
|
-
const rows = rowsOf(op.table);
|
|
112
|
-
const where = resolveMap(op.where ?? {}, results, null);
|
|
113
|
-
const written = [];
|
|
114
|
-
for (let i = 0; i < rows.length; i++) {
|
|
115
|
-
const row = rows[i];
|
|
116
|
-
if (!row || !matches(row, where)) continue;
|
|
117
|
-
const next = { ...row, ...resolveMap(op.set ?? {}, results, row) };
|
|
118
|
-
rows[i] = next;
|
|
119
|
-
track(tracked.updated, op.table, next);
|
|
120
|
-
written.push(next);
|
|
121
|
-
}
|
|
122
|
-
return { rows: written, rows_affected: written.length };
|
|
123
|
-
}
|
|
124
|
-
case "delete": {
|
|
125
|
-
const rows = rowsOf(op.table);
|
|
126
|
-
const where = resolveMap(op.where ?? {}, results, null);
|
|
127
|
-
const removed = rows.filter((row) => matches(row, where));
|
|
128
|
-
for (const row of removed) {
|
|
129
|
-
rows.splice(rows.indexOf(row), 1);
|
|
130
|
-
const id = row["id"];
|
|
131
|
-
const list = tracked.deleted.get(op.table);
|
|
132
|
-
const key = typeof id === "string" ? id : String(id);
|
|
133
|
-
if (list) list.push(key);
|
|
134
|
-
else tracked.deleted.set(op.table, [key]);
|
|
135
|
-
}
|
|
136
|
-
return { rows: removed, rows_affected: removed.length };
|
|
137
|
-
}
|
|
138
|
-
case "select": {
|
|
139
|
-
const where = resolveMap(op.where ?? {}, results, null);
|
|
140
|
-
let found = rowsOf(op.table).filter((row) => matches(row, where));
|
|
141
|
-
if (op.limit !== void 0) found = found.slice(0, op.limit);
|
|
142
|
-
return { rows: found, rows_affected: found.length };
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
const client = {
|
|
147
|
-
...ops,
|
|
148
|
-
txPlan,
|
|
149
|
-
// In tests there is no real DB role; `asService()` returns the same
|
|
150
|
-
// in-memory client so RLS-bypass code paths still hit the same store and
|
|
151
|
-
// tracking maps. The omitted `asService` matches the contract (no
|
|
152
|
-
// double-bypass), so callers can't recurse.
|
|
153
|
-
asService() {
|
|
154
|
-
return client;
|
|
155
|
-
},
|
|
156
|
-
inserted(table) {
|
|
157
|
-
return tracked.inserted.get(table) ?? [];
|
|
158
|
-
},
|
|
159
|
-
updated(table) {
|
|
160
|
-
return tracked.updated.get(table) ?? [];
|
|
161
|
-
},
|
|
162
|
-
deleted(table) {
|
|
163
|
-
return tracked.deleted.get(table) ?? [];
|
|
164
|
-
},
|
|
165
|
-
seed(table, data) {
|
|
166
|
-
store.set(table, [...data]);
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
return client;
|
|
170
|
-
}
|
|
171
|
-
function cloneTracked(map) {
|
|
172
|
-
return new Map([...map].map(([k, v]) => [k, [...v]]));
|
|
173
|
-
}
|
|
174
|
-
function resolveValue(value, results, current, column) {
|
|
175
|
-
if (typeof value !== "object" || value === null) return value;
|
|
176
|
-
const tagged = value;
|
|
177
|
-
if (tagged.$ref) {
|
|
178
|
-
const row = results[tagged.$ref.op]?.rows[0];
|
|
179
|
-
if (!row) {
|
|
180
|
-
throw txRejection(409, "tx_ref_unresolved", {
|
|
181
|
-
message: `operation ${tagged.$ref.op} produced no row to reference`
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
return row[tagged.$ref.field];
|
|
185
|
-
}
|
|
186
|
-
if (tagged.$expr) {
|
|
187
|
-
const fn = tagged.$expr["fn"];
|
|
188
|
-
if (fn === "now") return (/* @__PURE__ */ new Date()).toISOString();
|
|
189
|
-
const by = Number(tagged.$expr["by"]);
|
|
190
|
-
const base = Number(current?.[column] ?? 0);
|
|
191
|
-
return fn === "dec" ? base - by : base + by;
|
|
15
|
+
};
|
|
16
|
+
function required(value, envName) {
|
|
17
|
+
if (!value) {
|
|
18
|
+
throw new Error(
|
|
19
|
+
`${envName} is not set \u2014 the test client has nowhere to send requests. This is set by the deploy that runs your tests; if you are running them by hand, set it yourself.`
|
|
20
|
+
);
|
|
192
21
|
}
|
|
193
22
|
return value;
|
|
194
23
|
}
|
|
195
|
-
function
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
24
|
+
function createTestApi(config) {
|
|
25
|
+
const baseUrl = required(config.baseUrl, "PALBASE_TEST_BASE_URL").replace(/\/$/, "");
|
|
26
|
+
const apiKey = required(config.apiKey, "PALBASE_TEST_API_KEY");
|
|
27
|
+
const candidateToken = required(config.candidateToken, "PALBASE_TEST_CANDIDATE");
|
|
28
|
+
const requests = [];
|
|
29
|
+
let bearer = null;
|
|
30
|
+
async function call(method, path, body, opts = {}) {
|
|
31
|
+
const headers = {
|
|
32
|
+
apikey: apiKey,
|
|
33
|
+
// Selects the release under test. Omit it and the gateway serves the LIVE
|
|
34
|
+
// one, which would make the whole suite grade the wrong code.
|
|
35
|
+
"x-palbase-candidate": candidateToken,
|
|
36
|
+
...opts.headers
|
|
37
|
+
};
|
|
38
|
+
if (bearer) headers.authorization = `Bearer ${bearer}`;
|
|
39
|
+
if (body !== void 0) headers["content-type"] = "application/json";
|
|
40
|
+
const startedAt = Date.now();
|
|
41
|
+
const res = await fetch(`${baseUrl}${path}`, {
|
|
42
|
+
method,
|
|
43
|
+
headers,
|
|
44
|
+
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
45
|
+
});
|
|
46
|
+
const text = await res.text();
|
|
47
|
+
const parsed = text ? safeParse(text) : void 0;
|
|
48
|
+
requests.push({ method, path, status: res.status, ms: Date.now() - startedAt });
|
|
49
|
+
if (!res.ok) throw new TestApiError(method, path, res.status, parsed);
|
|
50
|
+
return parsed;
|
|
199
51
|
}
|
|
200
|
-
return out;
|
|
201
|
-
}
|
|
202
|
-
function matches(row, where) {
|
|
203
|
-
return Object.entries(where).every(
|
|
204
|
-
([key, value]) => value === null ? row[key] === null || row[key] === void 0 : row[key] === value
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
function guardFailure(guard, count) {
|
|
208
|
-
if (!guard) return null;
|
|
209
|
-
const ok = guard.kind === "one" ? count === 1 : guard.kind === "none" ? count === 0 : guard.kind === "atLeast" ? count >= guard.n : count <= guard.n;
|
|
210
|
-
if (ok) return null;
|
|
211
|
-
return txRejection(409, "tx_guard_failed", {
|
|
212
|
-
slot: guard.slot,
|
|
213
|
-
message: `expected ${guard.kind} ${guard.n} row(s), got ${count}`
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
function txRejection(status, code, extra) {
|
|
217
|
-
const err = new Error(extra.message);
|
|
218
|
-
err.status = status;
|
|
219
|
-
err.error_code = code;
|
|
220
|
-
if (extra.slot !== void 0) err.slot = extra.slot;
|
|
221
|
-
return err;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// src/test/context.ts
|
|
225
|
-
function createMockLogger(logs) {
|
|
226
|
-
return {
|
|
227
|
-
info(message, ...args) {
|
|
228
|
-
logs.push({ level: "info", message, args });
|
|
229
|
-
},
|
|
230
|
-
warn(message, ...args) {
|
|
231
|
-
logs.push({ level: "warn", message, args });
|
|
232
|
-
},
|
|
233
|
-
error(message, ...args) {
|
|
234
|
-
logs.push({ level: "error", message, args });
|
|
235
|
-
},
|
|
236
|
-
debug(message, ...args) {
|
|
237
|
-
logs.push({ level: "debug", message, args });
|
|
238
|
-
}
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
function createMockCache() {
|
|
242
|
-
const store = /* @__PURE__ */ new Map();
|
|
243
|
-
const get = async (key) => {
|
|
244
|
-
return store.has(key) ? store.get(key) : null;
|
|
245
|
-
};
|
|
246
|
-
const set = async (key, value, _ttl) => {
|
|
247
|
-
store.set(key, value);
|
|
248
|
-
};
|
|
249
52
|
return {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
return hit;
|
|
266
|
-
}
|
|
267
|
-
const value = await fn();
|
|
268
|
-
await set(key, value, ttl);
|
|
269
|
-
return value;
|
|
270
|
-
}
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
function createMockModuleClients() {
|
|
274
|
-
const notImpl = (label) => {
|
|
275
|
-
throw new Error(
|
|
276
|
-
`${label} not configured in test mock \u2014 override the matching client on the returned context`
|
|
277
|
-
);
|
|
278
|
-
};
|
|
279
|
-
const docs = {
|
|
280
|
-
collection: () => notImpl("docs.collection"),
|
|
281
|
-
doc: () => notImpl("docs.doc")
|
|
282
|
-
};
|
|
283
|
-
const auth = {
|
|
284
|
-
verifyUserToken: () => notImpl("auth.verifyUserToken"),
|
|
285
|
-
getSession: () => notImpl("auth.getSession"),
|
|
286
|
-
mfa: {
|
|
287
|
-
enroll: () => notImpl("auth.mfa.enroll"),
|
|
288
|
-
verifyEnrollment: () => notImpl("auth.mfa.verifyEnrollment"),
|
|
289
|
-
challenge: () => notImpl("auth.mfa.challenge"),
|
|
290
|
-
recovery: () => notImpl("auth.mfa.recovery"),
|
|
291
|
-
listFactors: () => notImpl("auth.mfa.listFactors"),
|
|
292
|
-
removeFactor: () => notImpl("auth.mfa.removeFactor"),
|
|
293
|
-
regenerateRecoveryCodes: () => notImpl("auth.mfa.regenerateRecoveryCodes"),
|
|
294
|
-
emailEnroll: () => notImpl("auth.mfa.emailEnroll"),
|
|
295
|
-
emailChallenge: () => notImpl("auth.mfa.emailChallenge"),
|
|
296
|
-
emailVerify: () => notImpl("auth.mfa.emailVerify")
|
|
297
|
-
},
|
|
298
|
-
device: {
|
|
299
|
-
generateChallenge: () => notImpl("auth.device.generateChallenge"),
|
|
300
|
-
attestAndroid: () => notImpl("auth.device.attestAndroid"),
|
|
301
|
-
attestiOS: () => notImpl("auth.device.attestiOS"),
|
|
302
|
-
bind: () => notImpl("auth.device.bind"),
|
|
303
|
-
list: () => notImpl("auth.device.list"),
|
|
304
|
-
delete: () => notImpl("auth.device.delete"),
|
|
305
|
-
verifyRequestSignature: () => notImpl("auth.device.verifyRequestSignature"),
|
|
306
|
-
getToken: () => notImpl("auth.device.getToken"),
|
|
307
|
-
get isActive() {
|
|
308
|
-
return notImpl("auth.device.isActive");
|
|
309
|
-
},
|
|
310
|
-
setCachedToken: () => notImpl("auth.device.setCachedToken"),
|
|
311
|
-
dispose: () => notImpl("auth.device.dispose")
|
|
312
|
-
}
|
|
313
|
-
};
|
|
314
|
-
const storage = {
|
|
315
|
-
bucket: () => notImpl("storage.bucket")
|
|
316
|
-
};
|
|
317
|
-
const realtime = {
|
|
318
|
-
broadcast: async () => ({ data: void 0, error: null })
|
|
319
|
-
};
|
|
320
|
-
const functions = {
|
|
321
|
-
invoke: () => notImpl("functions.invoke")
|
|
322
|
-
};
|
|
323
|
-
const flags = {
|
|
324
|
-
isEnabled: () => notImpl("flags.isEnabled"),
|
|
325
|
-
getVariant: () => notImpl("flags.getVariant"),
|
|
326
|
-
getAll: () => notImpl("flags.getAll"),
|
|
327
|
-
setOverride: () => notImpl("flags.setOverride"),
|
|
328
|
-
asService: () => ({
|
|
329
|
-
setOverrideForUser: () => notImpl("flags.asService.setOverrideForUser"),
|
|
330
|
-
setOverridesForUser: () => notImpl("flags.asService.setOverridesForUser"),
|
|
331
|
-
clearOverrideForUser: () => notImpl("flags.asService.clearOverrideForUser"),
|
|
332
|
-
clearAllOverridesForUser: () => notImpl("flags.asService.clearAllOverridesForUser"),
|
|
333
|
-
batchSetOverrides: () => notImpl("flags.asService.batchSetOverrides")
|
|
334
|
-
})
|
|
335
|
-
};
|
|
336
|
-
const notifications = {
|
|
337
|
-
push: { send: () => notImpl("notifications.push.send") },
|
|
338
|
-
email: { send: () => notImpl("notifications.email.send") },
|
|
339
|
-
sms: { send: () => notImpl("notifications.sms.send") },
|
|
340
|
-
verifications: {
|
|
341
|
-
start: () => notImpl("notifications.verifications.start"),
|
|
342
|
-
check: () => notImpl("notifications.verifications.check")
|
|
343
|
-
},
|
|
344
|
-
inbox: {
|
|
345
|
-
send: () => notImpl("notifications.inbox.send"),
|
|
346
|
-
list: () => notImpl("notifications.inbox.list"),
|
|
347
|
-
unreadCount: () => notImpl("notifications.inbox.unreadCount"),
|
|
348
|
-
markRead: () => notImpl("notifications.inbox.markRead"),
|
|
349
|
-
markAllRead: () => notImpl("notifications.inbox.markAllRead"),
|
|
350
|
-
archive: () => notImpl("notifications.inbox.archive")
|
|
351
|
-
},
|
|
352
|
-
preferences: {
|
|
353
|
-
get: () => notImpl("notifications.preferences.get"),
|
|
354
|
-
update: () => notImpl("notifications.preferences.update")
|
|
355
|
-
},
|
|
356
|
-
templates: {
|
|
357
|
-
email: {
|
|
358
|
-
list: () => notImpl("notifications.templates.email.list"),
|
|
359
|
-
get: () => notImpl("notifications.templates.email.get"),
|
|
360
|
-
create: () => notImpl("notifications.templates.email.create"),
|
|
361
|
-
update: () => notImpl("notifications.templates.email.update"),
|
|
362
|
-
delete: () => notImpl("notifications.templates.email.delete")
|
|
363
|
-
},
|
|
364
|
-
sms: {
|
|
365
|
-
list: () => notImpl("notifications.templates.sms.list"),
|
|
366
|
-
get: () => notImpl("notifications.templates.sms.get"),
|
|
367
|
-
create: () => notImpl("notifications.templates.sms.create"),
|
|
368
|
-
update: () => notImpl("notifications.templates.sms.update"),
|
|
369
|
-
delete: () => notImpl("notifications.templates.sms.delete")
|
|
370
|
-
}
|
|
53
|
+
requests,
|
|
54
|
+
get: (path, opts) => call("GET", path, void 0, opts),
|
|
55
|
+
post: (path, body, opts) => call("POST", path, body, opts),
|
|
56
|
+
patch: (path, body, opts) => call("PATCH", path, body, opts),
|
|
57
|
+
put: (path, body, opts) => call("PUT", path, body, opts),
|
|
58
|
+
delete: (path, opts) => call("DELETE", path, void 0, opts),
|
|
59
|
+
query: (path, body, opts) => call("QUERY", path, body, opts),
|
|
60
|
+
async signIn(credentials) {
|
|
61
|
+
const result = await call(
|
|
62
|
+
"POST",
|
|
63
|
+
"/auth/login",
|
|
64
|
+
credentials
|
|
65
|
+
);
|
|
66
|
+
bearer = result.access_token;
|
|
67
|
+
return result.user ?? { id: "" };
|
|
371
68
|
},
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
const analytics = {
|
|
376
|
-
capture: () => notImpl("analytics.capture"),
|
|
377
|
-
identify: () => notImpl("analytics.identify"),
|
|
378
|
-
screen: () => notImpl("analytics.screen"),
|
|
379
|
-
query: {
|
|
380
|
-
count: () => notImpl("analytics.query.count"),
|
|
381
|
-
events: () => notImpl("analytics.query.events"),
|
|
382
|
-
properties: () => notImpl("analytics.query.properties"),
|
|
383
|
-
users: () => notImpl("analytics.query.users"),
|
|
384
|
-
funnel: () => notImpl("analytics.query.funnel"),
|
|
385
|
-
retention: () => notImpl("analytics.query.retention"),
|
|
386
|
-
cohort: () => notImpl("analytics.query.cohort")
|
|
69
|
+
async signOut() {
|
|
70
|
+
await call("POST", "/auth/logout", void 0);
|
|
71
|
+
bearer = null;
|
|
387
72
|
},
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
eventNames: () => notImpl("analytics.management.eventNames"),
|
|
391
|
-
userDetail: () => notImpl("analytics.management.userDetail"),
|
|
392
|
-
deleteUser: () => notImpl("analytics.management.deleteUser")
|
|
73
|
+
asAnonymous() {
|
|
74
|
+
bearer = null;
|
|
393
75
|
}
|
|
394
76
|
};
|
|
395
|
-
const links = {
|
|
396
|
-
create: () => notImpl("links.create"),
|
|
397
|
-
list: () => notImpl("links.list"),
|
|
398
|
-
get: () => notImpl("links.get"),
|
|
399
|
-
update: () => notImpl("links.update"),
|
|
400
|
-
delete: () => notImpl("links.delete"),
|
|
401
|
-
analytics: () => notImpl("links.analytics"),
|
|
402
|
-
qrCode: () => notImpl("links.qrCode"),
|
|
403
|
-
match: () => notImpl("links.match")
|
|
404
|
-
};
|
|
405
|
-
return {
|
|
406
|
-
auth,
|
|
407
|
-
storage,
|
|
408
|
-
docs,
|
|
409
|
-
realtime,
|
|
410
|
-
functions,
|
|
411
|
-
flags,
|
|
412
|
-
notifications,
|
|
413
|
-
analytics,
|
|
414
|
-
links
|
|
415
|
-
};
|
|
416
|
-
}
|
|
417
|
-
function createMockPurchases() {
|
|
418
|
-
return {
|
|
419
|
-
resolveSubject: async ({ userRef }) => ({ subjectId: `psj_test_${userRef}` }),
|
|
420
|
-
require: async () => void 0,
|
|
421
|
-
withSpend: async (_subjectId, _key, _opts, handler) => handler()
|
|
422
|
-
};
|
|
423
77
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
};
|
|
430
|
-
function createTestContext(options = {}) {
|
|
431
|
-
const logs = [];
|
|
432
|
-
const db = createMockDB();
|
|
433
|
-
if (options.db?.seed) {
|
|
434
|
-
for (const [table, data] of Object.entries(options.db.seed)) {
|
|
435
|
-
db.seed(table, data);
|
|
436
|
-
}
|
|
78
|
+
function safeParse(text) {
|
|
79
|
+
try {
|
|
80
|
+
return JSON.parse(text);
|
|
81
|
+
} catch {
|
|
82
|
+
return text;
|
|
437
83
|
}
|
|
438
|
-
const log = createMockLogger(logs);
|
|
439
|
-
const cache = createMockCache();
|
|
440
|
-
const moduleClients = createMockModuleClients();
|
|
441
|
-
__setRuntime({
|
|
442
|
-
Database: db,
|
|
443
|
-
Documents: moduleClients.docs,
|
|
444
|
-
Storage: moduleClients.storage,
|
|
445
|
-
Cache: cache,
|
|
446
|
-
Log: log,
|
|
447
|
-
Notifications: moduleClients.notifications,
|
|
448
|
-
Flags: moduleClients.flags,
|
|
449
|
-
Realtime: moduleClients.realtime,
|
|
450
|
-
Purchases: createMockPurchases()
|
|
451
|
-
});
|
|
452
|
-
const ctx = {
|
|
453
|
-
input: options.input ?? {},
|
|
454
|
-
params: options.params ?? {},
|
|
455
|
-
query: options.query ?? {},
|
|
456
|
-
headers: options.headers ?? {},
|
|
457
|
-
user: options.user ?? null,
|
|
458
|
-
client: NULL_CLIENT_INFO,
|
|
459
|
-
method: "POST",
|
|
460
|
-
file: null,
|
|
461
|
-
db,
|
|
462
|
-
env: options.env ?? {},
|
|
463
|
-
log,
|
|
464
|
-
cache,
|
|
465
|
-
...moduleClients,
|
|
466
|
-
// Empty errors map in tests by default. Tests that exercise an endpoint's
|
|
467
|
-
// declared errors construct their own throwers; this stub satisfies the
|
|
468
|
-
// PBRequest shape without forcing every test to declare `errors:`.
|
|
469
|
-
errors: {},
|
|
470
|
-
requestId: "req_test_000000000000",
|
|
471
|
-
traceId: "0".repeat(32),
|
|
472
|
-
spanId: "0".repeat(16),
|
|
473
|
-
logs
|
|
474
|
-
};
|
|
475
|
-
return ctx;
|
|
476
84
|
}
|
|
85
|
+
var configured = null;
|
|
86
|
+
var api = new Proxy({}, {
|
|
87
|
+
get(_target, prop) {
|
|
88
|
+
configured ??= createTestApi({
|
|
89
|
+
baseUrl: process.env.PALBASE_TEST_BASE_URL ?? "",
|
|
90
|
+
apiKey: process.env.PALBASE_TEST_API_KEY ?? "",
|
|
91
|
+
candidateToken: process.env.PALBASE_TEST_CANDIDATE ?? ""
|
|
92
|
+
});
|
|
93
|
+
return Reflect.get(configured, prop, configured);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
477
96
|
export {
|
|
478
|
-
|
|
479
|
-
|
|
97
|
+
TestApiError,
|
|
98
|
+
api,
|
|
99
|
+
createTestApi
|
|
480
100
|
};
|
|
481
101
|
//# sourceMappingURL=index.js.map
|