@palbase/backend 16.0.0 → 17.1.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 +101 -960
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +82 -61
- package/dist/test/index.d.ts +82 -61
- package/dist/test/index.js +101 -462
- 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,120 @@
|
|
|
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
52
|
return {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
return {
|
|
250
|
-
get,
|
|
251
|
-
set,
|
|
252
|
-
async del(key) {
|
|
253
|
-
store.delete(key);
|
|
254
|
-
},
|
|
255
|
-
async incr(key) {
|
|
256
|
-
const raw = store.get(key);
|
|
257
|
-
const current = typeof raw === "number" ? raw : parseInt(String(raw ?? "0"), 10);
|
|
258
|
-
const next = current + 1;
|
|
259
|
-
store.set(key, next);
|
|
260
|
-
return next;
|
|
261
|
-
},
|
|
262
|
-
async getOrSet(key, ttl, fn) {
|
|
263
|
-
const hit = await get(key);
|
|
264
|
-
if (hit !== null) {
|
|
265
|
-
return hit;
|
|
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 signInAs(name) {
|
|
61
|
+
const identity = (config.identities ?? {})[name];
|
|
62
|
+
if (!identity) {
|
|
63
|
+
const declared = Object.keys(config.identities ?? {});
|
|
64
|
+
throw new Error(
|
|
65
|
+
`no test identity named ${JSON.stringify(name)} \u2014 the deploy mints one per user declared in config/test-users.ts` + (declared.length ? `; this run has: ${declared.join(", ")}` : ", and this project declares none")
|
|
66
|
+
);
|
|
266
67
|
}
|
|
267
|
-
|
|
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")
|
|
68
|
+
return this.signIn(identity);
|
|
343
69
|
},
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
}
|
|
70
|
+
async signIn(credentials) {
|
|
71
|
+
const result = await call(
|
|
72
|
+
"POST",
|
|
73
|
+
"/auth/login",
|
|
74
|
+
credentials
|
|
75
|
+
);
|
|
76
|
+
bearer = result.access_token;
|
|
77
|
+
return result.user ?? { id: "" };
|
|
371
78
|
},
|
|
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")
|
|
79
|
+
async signOut() {
|
|
80
|
+
await call("POST", "/auth/logout", void 0);
|
|
81
|
+
bearer = null;
|
|
387
82
|
},
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
eventNames: () => notImpl("analytics.management.eventNames"),
|
|
391
|
-
userDetail: () => notImpl("analytics.management.userDetail"),
|
|
392
|
-
deleteUser: () => notImpl("analytics.management.deleteUser")
|
|
83
|
+
asAnonymous() {
|
|
84
|
+
bearer = null;
|
|
393
85
|
}
|
|
394
86
|
};
|
|
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
87
|
}
|
|
417
|
-
function
|
|
418
|
-
return {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
88
|
+
function parseIdentities(raw) {
|
|
89
|
+
if (!raw) return {};
|
|
90
|
+
try {
|
|
91
|
+
return JSON.parse(raw);
|
|
92
|
+
} catch {
|
|
93
|
+
return {};
|
|
94
|
+
}
|
|
423
95
|
}
|
|
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
|
-
}
|
|
96
|
+
function safeParse(text) {
|
|
97
|
+
try {
|
|
98
|
+
return JSON.parse(text);
|
|
99
|
+
} catch {
|
|
100
|
+
return text;
|
|
437
101
|
}
|
|
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
102
|
}
|
|
103
|
+
var configured = null;
|
|
104
|
+
var api = new Proxy({}, {
|
|
105
|
+
get(_target, prop) {
|
|
106
|
+
configured ??= createTestApi({
|
|
107
|
+
baseUrl: process.env.PALBASE_TEST_BASE_URL ?? "",
|
|
108
|
+
apiKey: process.env.PALBASE_TEST_API_KEY ?? "",
|
|
109
|
+
candidateToken: process.env.PALBASE_TEST_CANDIDATE ?? "",
|
|
110
|
+
identities: parseIdentities(process.env.PALBASE_TEST_IDENTITIES)
|
|
111
|
+
});
|
|
112
|
+
return Reflect.get(configured, prop, configured);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
477
115
|
export {
|
|
478
|
-
|
|
479
|
-
|
|
116
|
+
TestApiError,
|
|
117
|
+
api,
|
|
118
|
+
createTestApi
|
|
480
119
|
};
|
|
481
120
|
//# sourceMappingURL=index.js.map
|