sporades 0.8.3 → 0.8.5
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/CHANGES.md +10 -0
- package/bin/sporades-host-helper.js +1 -1
- package/bin/sporades.js +186 -27
- package/dist/cli/cli-version.d.ts +1 -1
- package/dist/cli/cli-version.js +1 -1
- package/dist/server-runtime-source.d.ts +1 -1
- package/dist/server-runtime-source.d.ts.map +1 -1
- package/dist/server-runtime-source.js +108 -6
- package/dist/server-runtime-source.js.map +1 -1
- package/dist/templates/client-runtime-template.d.ts.map +1 -1
- package/dist/templates/client-runtime-template.js +90 -20
- package/dist/templates/client-runtime-template.js.map +1 -1
- package/package.json +1 -1
- package/src/types/client.d.ts +7 -7
- package/src/types/server.d.ts +10 -3
package/CHANGES.md
CHANGED
|
@@ -6,12 +6,22 @@ Changes since v0.8.1.
|
|
|
6
6
|
|
|
7
7
|
### 🚀 Features
|
|
8
8
|
|
|
9
|
+
- Add reactive query arguments (5b882f5).
|
|
9
10
|
- Add exact pagination and join admission (63f68a0).
|
|
10
11
|
|
|
11
12
|
### 🐛 Bug Fixes
|
|
12
13
|
|
|
13
14
|
- Resolve npm audit vulnerabilities (a6a4b51).
|
|
14
15
|
|
|
16
|
+
### 📝 Documentation
|
|
17
|
+
|
|
18
|
+
- Describe parameterized queries (287ef63).
|
|
19
|
+
- Plan reactive query arguments (4876db8).
|
|
20
|
+
|
|
21
|
+
### 📦 Packaging
|
|
22
|
+
|
|
23
|
+
- Release v0.8.3 (c17bb1b).
|
|
24
|
+
|
|
15
25
|
## 0.8.1 - 2026-08-14
|
|
16
26
|
|
|
17
27
|
Corrects the incomplete `0.8.0` package release with the merged `main` runtime,
|
|
@@ -418,7 +418,7 @@ function writeEnvelope(result, failed = false) {
|
|
|
418
418
|
}
|
|
419
419
|
|
|
420
420
|
// src/cli/cli-version.ts
|
|
421
|
-
var CLI_VERSION = "0.8.
|
|
421
|
+
var CLI_VERSION = "0.8.5";
|
|
422
422
|
|
|
423
423
|
// src/cli/schedule-inspection-envelope.ts
|
|
424
424
|
function sanitizeScheduleInspectionEnvelope(envelope, invalid) {
|
package/bin/sporades.js
CHANGED
|
@@ -38,11 +38,69 @@ export function onMessage(listener) {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export const queries = {
|
|
41
|
-
subscribe(name, listener) {
|
|
42
|
-
|
|
41
|
+
subscribe(name, listener, ...args) {
|
|
42
|
+
const normalizedArgs = args.length === 1 && args[0]?.[normalizedQueryArgumentsMarker]
|
|
43
|
+
? args[0]
|
|
44
|
+
: normalizeQueryArguments(args);
|
|
45
|
+
return connect().subscribeNormalizedQuery(name, listener, normalizedArgs);
|
|
43
46
|
},
|
|
44
47
|
};
|
|
45
48
|
|
|
49
|
+
const queryArgumentLimitBytes = 65536;
|
|
50
|
+
const normalizedQueryArgumentsMarker = Symbol("sporades.normalizedQueryArguments");
|
|
51
|
+
|
|
52
|
+
function normalizeQueryArguments(args) {
|
|
53
|
+
if (!Array.isArray(args)) throw new TypeError("Query arguments must be an array.");
|
|
54
|
+
const ancestors = new Set();
|
|
55
|
+
const snapshot = normalizeQueryArgumentValue(args, ancestors);
|
|
56
|
+
const identity = JSON.stringify(snapshot);
|
|
57
|
+
const byteLength = new TextEncoder().encode(identity).byteLength;
|
|
58
|
+
if (byteLength > queryArgumentLimitBytes) throw new RangeError("Query arguments exceed the 65536-byte limit.");
|
|
59
|
+
return Object.freeze({ snapshot, identity, byteLength, [normalizedQueryArgumentsMarker]: true });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function normalizeQueryArgumentValue(value, ancestors) {
|
|
63
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
64
|
+
if (typeof value === "number") {
|
|
65
|
+
if (!Number.isFinite(value)) throw new TypeError("Query arguments must contain JSON values.");
|
|
66
|
+
return value;
|
|
67
|
+
}
|
|
68
|
+
if (Array.isArray(value)) {
|
|
69
|
+
if (Object.getOwnPropertySymbols(value).length > 0) throw new TypeError("Query arguments must not contain symbol keys.");
|
|
70
|
+
if (ancestors.has(value)) throw new TypeError("Query arguments must not contain cycles.");
|
|
71
|
+
if (Object.getOwnPropertyNames(value).some((key) => key !== "length" && (!/^(0|[1-9]\\d*)$/.test(key) || Number(key) >= value.length))) {
|
|
72
|
+
throw new TypeError("Query arguments must not contain non-index array properties.");
|
|
73
|
+
}
|
|
74
|
+
ancestors.add(value);
|
|
75
|
+
const snapshot = [];
|
|
76
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
77
|
+
if (!Object.hasOwn(value, index)) throw new TypeError("Query arguments must not contain sparse arrays.");
|
|
78
|
+
snapshot.push(normalizeQueryArgumentValue(value[index], ancestors));
|
|
79
|
+
}
|
|
80
|
+
ancestors.delete(value);
|
|
81
|
+
return Object.freeze(snapshot);
|
|
82
|
+
}
|
|
83
|
+
if (!value || typeof value !== "object") throw new TypeError("Query arguments must contain JSON values.");
|
|
84
|
+
if (Object.getOwnPropertySymbols(value).length > 0) throw new TypeError("Query arguments must not contain symbol keys.");
|
|
85
|
+
const prototype = Object.getPrototypeOf(value);
|
|
86
|
+
if (prototype !== Object.prototype && prototype !== null) throw new TypeError("Query arguments must contain plain JSON objects.");
|
|
87
|
+
if (ancestors.has(value)) throw new TypeError("Query arguments must not contain cycles.");
|
|
88
|
+
ancestors.add(value);
|
|
89
|
+
const snapshot = Object.create(null);
|
|
90
|
+
for (const key of Object.getOwnPropertyNames(value).sort()) {
|
|
91
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
92
|
+
if (!descriptor || !descriptor.enumerable || !Object.hasOwn(descriptor, "value")) throw new TypeError("Query arguments must contain plain JSON objects.");
|
|
93
|
+
Object.defineProperty(snapshot, key, {
|
|
94
|
+
value: normalizeQueryArgumentValue(descriptor.value, ancestors),
|
|
95
|
+
enumerable: true,
|
|
96
|
+
configurable: false,
|
|
97
|
+
writable: false,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
ancestors.delete(value);
|
|
101
|
+
return Object.freeze(snapshot);
|
|
102
|
+
}
|
|
103
|
+
|
|
46
104
|
export const mutations = {
|
|
47
105
|
run(name, ...args) {
|
|
48
106
|
return connect().mutate(name, args);
|
|
@@ -171,13 +229,14 @@ export const files = {
|
|
|
171
229
|
export function createHooks(primitives) {
|
|
172
230
|
const { useEffect, useState } = primitives;
|
|
173
231
|
|
|
174
|
-
function useQuery(name) {
|
|
232
|
+
function useQuery(name, ...args) {
|
|
233
|
+
const normalizedArgs = normalizeQueryArguments(args);
|
|
175
234
|
const [state, setState] = useState({ data: null, error: null, loading: true });
|
|
176
235
|
|
|
177
236
|
useEffect(() => {
|
|
178
|
-
const subscription = queries.subscribe(name, setState);
|
|
237
|
+
const subscription = queries.subscribe(name, setState, normalizedArgs);
|
|
179
238
|
return () => subscription.unsubscribe();
|
|
180
|
-
}, [name]);
|
|
239
|
+
}, [name, normalizedArgs.identity]);
|
|
181
240
|
|
|
182
241
|
return state;
|
|
183
242
|
}
|
|
@@ -237,9 +296,10 @@ export function createHooks(primitives) {
|
|
|
237
296
|
export function createVueComposables(primitives) {
|
|
238
297
|
const { reactive, onScopeDispose } = primitives;
|
|
239
298
|
|
|
240
|
-
function useQuery(name) {
|
|
299
|
+
function useQuery(name, ...args) {
|
|
300
|
+
const normalizedArgs = normalizeQueryArguments(args);
|
|
241
301
|
const state = reactive({ data: null, error: null, loading: true });
|
|
242
|
-
const subscription = queries.subscribe(name, (nextState) => Object.assign(state, nextState));
|
|
302
|
+
const subscription = queries.subscribe(name, (nextState) => Object.assign(state, nextState), normalizedArgs);
|
|
243
303
|
onScopeDispose(() => subscription.unsubscribe());
|
|
244
304
|
return state;
|
|
245
305
|
}
|
|
@@ -293,9 +353,10 @@ export function createVueComposables(primitives) {
|
|
|
293
353
|
export function createSolidPrimitives(primitives) {
|
|
294
354
|
const { createSignal, onCleanup } = primitives;
|
|
295
355
|
|
|
296
|
-
function createQuery(name) {
|
|
356
|
+
function createQuery(name, ...args) {
|
|
357
|
+
const normalizedArgs = normalizeQueryArguments(args);
|
|
297
358
|
const [state, setState] = createSignal({ data: null, error: null, loading: true });
|
|
298
|
-
const subscription = queries.subscribe(name, setState);
|
|
359
|
+
const subscription = queries.subscribe(name, setState, normalizedArgs);
|
|
299
360
|
onCleanup(() => subscription.unsubscribe());
|
|
300
361
|
return state;
|
|
301
362
|
}
|
|
@@ -389,8 +450,9 @@ export function createLitControllers() {
|
|
|
389
450
|
return controller;
|
|
390
451
|
}
|
|
391
452
|
|
|
392
|
-
function queryController(host, name) {
|
|
393
|
-
|
|
453
|
+
function queryController(host, name, ...args) {
|
|
454
|
+
const normalizedArgs = normalizeQueryArguments(args);
|
|
455
|
+
return observedController(host, { data: null, error: null, loading: true }, (publish) => queries.subscribe(name, publish, normalizedArgs));
|
|
394
456
|
}
|
|
395
457
|
|
|
396
458
|
function mutationController(host, name) {
|
|
@@ -477,7 +539,10 @@ export function createInfernoAdapters() {
|
|
|
477
539
|
};
|
|
478
540
|
return adapter;
|
|
479
541
|
}
|
|
480
|
-
const queryAdapter = (host, name
|
|
542
|
+
const queryAdapter = (host, name, ...args) => {
|
|
543
|
+
const normalizedArgs = normalizeQueryArguments(args);
|
|
544
|
+
return observedAdapter(host, { data: null, error: null, loading: true }, (publish) => queries.subscribe(name, publish, normalizedArgs));
|
|
545
|
+
};
|
|
481
546
|
function mutationAdapter(host, name) {
|
|
482
547
|
let pending = 0, latestInvocation = 0;
|
|
483
548
|
const adapter = { state: { data: null, error: null, loading: false }, async run(...args) {
|
|
@@ -513,10 +578,11 @@ export function createInfernoAdapters() {
|
|
|
513
578
|
}
|
|
514
579
|
|
|
515
580
|
export function createSvelteStores() {
|
|
516
|
-
function queryStore(name) {
|
|
581
|
+
function queryStore(name, ...args) {
|
|
582
|
+
const normalizedArgs = normalizeQueryArguments(args);
|
|
517
583
|
return createLazyStore(
|
|
518
584
|
{ data: null, error: null, loading: true },
|
|
519
|
-
(publish) => queries.subscribe(name, publish).unsubscribe,
|
|
585
|
+
(publish) => queries.subscribe(name, publish, normalizedArgs).unsubscribe,
|
|
520
586
|
true,
|
|
521
587
|
);
|
|
522
588
|
}
|
|
@@ -688,6 +754,7 @@ function createConnection() {
|
|
|
688
754
|
id: subscription.id,
|
|
689
755
|
type: "query.subscribe",
|
|
690
756
|
query: subscription.name,
|
|
757
|
+
args: subscription.args.snapshot,
|
|
691
758
|
});
|
|
692
759
|
}
|
|
693
760
|
});
|
|
@@ -1073,17 +1140,19 @@ function createConnection() {
|
|
|
1073
1140
|
confirmPasswordReset(code, newPassword) {
|
|
1074
1141
|
return request("auth.confirmPasswordReset", { code, newPassword });
|
|
1075
1142
|
},
|
|
1076
|
-
|
|
1143
|
+
subscribeNormalizedQuery(name, listener, args) {
|
|
1077
1144
|
if (typeof name !== "string" || !name) throw new TypeError("queries.subscribe requires a query name.");
|
|
1078
1145
|
if (typeof listener !== "function") throw new TypeError("queries.subscribe requires a listener function.");
|
|
1079
|
-
|
|
1146
|
+
const channelsForName = queryChannels.get(name) ?? new Map();
|
|
1147
|
+
if (!queryChannels.has(name)) queryChannels.set(name, channelsForName);
|
|
1148
|
+
let subscription = channelsForName.get(args.identity);
|
|
1080
1149
|
if (!subscription) {
|
|
1081
1150
|
const id = nextId++;
|
|
1082
|
-
subscription = { id, name, listeners: new Set(), latest: null };
|
|
1083
|
-
|
|
1151
|
+
subscription = { id, name, args, listeners: new Set(), latest: null };
|
|
1152
|
+
channelsForName.set(args.identity, subscription);
|
|
1084
1153
|
subscriptions.set(id, subscription);
|
|
1085
1154
|
const activeSocket = open();
|
|
1086
|
-
if (activeSocket.readyState === WebSocket.OPEN) send({ id, type: "query.subscribe", query: name });
|
|
1155
|
+
if (activeSocket.readyState === WebSocket.OPEN) send({ id, type: "query.subscribe", query: name, args: args.snapshot });
|
|
1087
1156
|
}
|
|
1088
1157
|
subscription.listeners.add(listener);
|
|
1089
1158
|
listener(subscription.latest ?? { data: null, error: null, loading: true });
|
|
@@ -1093,7 +1162,8 @@ function createConnection() {
|
|
|
1093
1162
|
active = false;
|
|
1094
1163
|
subscription.listeners.delete(listener);
|
|
1095
1164
|
if (subscription.listeners.size === 0) {
|
|
1096
|
-
|
|
1165
|
+
channelsForName.delete(args.identity);
|
|
1166
|
+
if (channelsForName.size === 0) queryChannels.delete(name);
|
|
1097
1167
|
subscriptions.delete(subscription.id);
|
|
1098
1168
|
sendIfOpen({ id: nextId++, type: "query.unsubscribe", subscriptionId: subscription.id });
|
|
1099
1169
|
}
|
|
@@ -16621,7 +16691,30 @@ function createWebSocketHub(getDatabase, trustedRefresh = null) {
|
|
|
16621
16691
|
});
|
|
16622
16692
|
return;
|
|
16623
16693
|
}
|
|
16624
|
-
|
|
16694
|
+
let args;
|
|
16695
|
+
try {
|
|
16696
|
+
args = normalizeQueryArguments(message.args ?? []);
|
|
16697
|
+
} catch {
|
|
16698
|
+
sendJson(client, {
|
|
16699
|
+
id: message.id,
|
|
16700
|
+
type: "query.result",
|
|
16701
|
+
query: queryName,
|
|
16702
|
+
data: null,
|
|
16703
|
+
error: invalidQueryArgumentsError()
|
|
16704
|
+
});
|
|
16705
|
+
return;
|
|
16706
|
+
}
|
|
16707
|
+
if (!message.query && args.length > 0) {
|
|
16708
|
+
sendJson(client, {
|
|
16709
|
+
id: message.id,
|
|
16710
|
+
type: "query.result",
|
|
16711
|
+
query: queryName,
|
|
16712
|
+
data: null,
|
|
16713
|
+
error: invalidQueryArgumentsError()
|
|
16714
|
+
});
|
|
16715
|
+
return;
|
|
16716
|
+
}
|
|
16717
|
+
const subscription = { id: message.id, name: queryName, args, style: message.query ? "direct" : "rows", generation: 0 };
|
|
16625
16718
|
client.subscriptions.set(message.id, subscription);
|
|
16626
16719
|
void sendQueryResult(client, subscription, (error) => sendUnhandledMessageError(client, rawMessage, error));
|
|
16627
16720
|
return;
|
|
@@ -17006,7 +17099,7 @@ function createWebSocketHub(getDatabase, trustedRefresh = null) {
|
|
|
17006
17099
|
subscription.generation = generation;
|
|
17007
17100
|
try {
|
|
17008
17101
|
const database = getDatabase();
|
|
17009
|
-
const result = await runQuery(database, client.session.auth, subscription.name);
|
|
17102
|
+
const result = await runQuery(database, client.session.auth, subscription.name, subscription.args);
|
|
17010
17103
|
const data = subscription.style === "direct" ? result.data ?? result.rows : { rows: result.data ?? result.rows };
|
|
17011
17104
|
if (client.subscriptions.get(subscription.id) !== subscription || subscription.generation !== generation) return;
|
|
17012
17105
|
sendJson(client, {
|
|
@@ -17228,7 +17321,13 @@ function sendJsonWithCompletion(client, message, timeoutMs = 250) {
|
|
|
17228
17321
|
}
|
|
17229
17322
|
});
|
|
17230
17323
|
}
|
|
17231
|
-
async function runQuery(database, auth, queryName) {
|
|
17324
|
+
async function runQuery(database, auth, queryName, rawArgs = []) {
|
|
17325
|
+
let args;
|
|
17326
|
+
try {
|
|
17327
|
+
args = normalizeQueryArguments(rawArgs);
|
|
17328
|
+
} catch {
|
|
17329
|
+
return { rows: null, data: null, error: invalidQueryArgumentsError() };
|
|
17330
|
+
}
|
|
17232
17331
|
let context;
|
|
17233
17332
|
try {
|
|
17234
17333
|
context = await applyContextMiddleware(database, createMutationContext(database, auth), "query");
|
|
@@ -17242,9 +17341,10 @@ async function runQuery(database, auth, queryName) {
|
|
|
17242
17341
|
};
|
|
17243
17342
|
}
|
|
17244
17343
|
if (queryName === "ctx.env") {
|
|
17344
|
+
if (args.length > 0) return { rows: null, data: null, error: invalidQueryArgumentsError() };
|
|
17245
17345
|
return { data: context.env, error: null };
|
|
17246
17346
|
}
|
|
17247
|
-
const customResult = await runCustomQuery(database, context, queryName);
|
|
17347
|
+
const customResult = await runCustomQuery(database, context, queryName, args);
|
|
17248
17348
|
if (customResult) {
|
|
17249
17349
|
return customResult;
|
|
17250
17350
|
}
|
|
@@ -17258,6 +17358,7 @@ async function runQuery(database, auth, queryName) {
|
|
|
17258
17358
|
}
|
|
17259
17359
|
};
|
|
17260
17360
|
}
|
|
17361
|
+
if (args.length > 0) return { rows: null, data: null, error: invalidQueryArgumentsError() };
|
|
17261
17362
|
const cacheKey = `${table.name}:${context.auth.userId}`;
|
|
17262
17363
|
if (!database.rowCache.has(cacheKey)) {
|
|
17263
17364
|
const columns = ["id", "createdAt", "updatedAt", ...table.fields.map((field) => field.name)];
|
|
@@ -17272,14 +17373,14 @@ async function runQuery(database, auth, queryName) {
|
|
|
17272
17373
|
const rows = await filterRowsByReadAcl(database, table, database.rowCache.get(cacheKey), context);
|
|
17273
17374
|
return { rows, error: null };
|
|
17274
17375
|
}
|
|
17275
|
-
async function runCustomQuery(database, context, queryName) {
|
|
17376
|
+
async function runCustomQuery(database, context, queryName, args) {
|
|
17276
17377
|
const handler = database.queries.find((candidate) => candidate.name === queryName);
|
|
17277
17378
|
if (!handler) {
|
|
17278
17379
|
return null;
|
|
17279
17380
|
}
|
|
17280
17381
|
try {
|
|
17281
17382
|
const queryHandler = typeof handler.handler === "function" ? handler.handler : new Function(`return (${handler.handlerSource});`)();
|
|
17282
|
-
const data = await queryHandler(context);
|
|
17383
|
+
const data = await queryHandler(context, ...args);
|
|
17283
17384
|
assertJsonCompatible(data);
|
|
17284
17385
|
return { data, error: null };
|
|
17285
17386
|
} catch (error) {
|
|
@@ -17296,6 +17397,64 @@ async function runCustomQuery(database, context, queryName) {
|
|
|
17296
17397
|
};
|
|
17297
17398
|
}
|
|
17298
17399
|
}
|
|
17400
|
+
var QUERY_ARGUMENT_LIMIT_BYTES = 65536;
|
|
17401
|
+
function invalidQueryArgumentsError() {
|
|
17402
|
+
return {
|
|
17403
|
+
message: "Invalid query arguments.",
|
|
17404
|
+
hint: "Use a JSON-compatible argument array no larger than 65536 UTF-8 bytes."
|
|
17405
|
+
};
|
|
17406
|
+
}
|
|
17407
|
+
function normalizeQueryArguments(value) {
|
|
17408
|
+
if (!Array.isArray(value)) throw new Error("Query arguments must be an array.");
|
|
17409
|
+
const snapshot = normalizeQueryArgumentValue(value, /* @__PURE__ */ new Set());
|
|
17410
|
+
const canonicalJson = JSON.stringify(snapshot);
|
|
17411
|
+
if (Buffer.byteLength(canonicalJson, "utf8") > QUERY_ARGUMENT_LIMIT_BYTES) {
|
|
17412
|
+
throw new Error("Query arguments are too large.");
|
|
17413
|
+
}
|
|
17414
|
+
return snapshot;
|
|
17415
|
+
}
|
|
17416
|
+
function normalizeQueryArgumentValue(value, ancestors) {
|
|
17417
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return value;
|
|
17418
|
+
if (typeof value === "number") {
|
|
17419
|
+
if (!Number.isFinite(value)) throw new Error("Query arguments must contain finite numbers.");
|
|
17420
|
+
return value;
|
|
17421
|
+
}
|
|
17422
|
+
if (typeof value !== "object") throw new Error("Query arguments must be JSON-compatible.");
|
|
17423
|
+
if (Object.getOwnPropertySymbols(value).length > 0) throw new Error("Query arguments must not contain symbol keys.");
|
|
17424
|
+
if (ancestors.has(value)) throw new Error("Query arguments must not contain cycles.");
|
|
17425
|
+
ancestors.add(value);
|
|
17426
|
+
try {
|
|
17427
|
+
if (Array.isArray(value)) {
|
|
17428
|
+
if (Object.getOwnPropertyNames(value).some((key) => key !== "length" && (!/^(0|[1-9]\\d*)$/.test(key) || Number(key) >= value.length))) {
|
|
17429
|
+
throw new Error("Query arguments must not contain non-index array properties.");
|
|
17430
|
+
}
|
|
17431
|
+
const copy2 = [];
|
|
17432
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
17433
|
+
if (!Object.hasOwn(value, index)) throw new Error("Query arguments must not contain sparse arrays.");
|
|
17434
|
+
copy2.push(normalizeQueryArgumentValue(value[index], ancestors));
|
|
17435
|
+
}
|
|
17436
|
+
return Object.freeze(copy2);
|
|
17437
|
+
}
|
|
17438
|
+
const prototype = Object.getPrototypeOf(value);
|
|
17439
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
17440
|
+
throw new Error("Query arguments must contain plain objects only.");
|
|
17441
|
+
}
|
|
17442
|
+
const copy = /* @__PURE__ */ Object.create(null);
|
|
17443
|
+
for (const key of Object.getOwnPropertyNames(value).sort()) {
|
|
17444
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
17445
|
+
if (!descriptor || !descriptor.enumerable || !("value" in descriptor)) throw new Error("Query arguments must not contain accessors.");
|
|
17446
|
+
Object.defineProperty(copy, key, {
|
|
17447
|
+
value: normalizeQueryArgumentValue(descriptor.value, ancestors),
|
|
17448
|
+
enumerable: true,
|
|
17449
|
+
writable: false,
|
|
17450
|
+
configurable: false
|
|
17451
|
+
});
|
|
17452
|
+
}
|
|
17453
|
+
return Object.freeze(copy);
|
|
17454
|
+
} finally {
|
|
17455
|
+
ancestors.delete(value);
|
|
17456
|
+
}
|
|
17457
|
+
}
|
|
17299
17458
|
async function runMutation(database, auth, mutationName, args) {
|
|
17300
17459
|
let context;
|
|
17301
17460
|
let result;
|
|
@@ -23645,7 +23804,7 @@ jobs:
|
|
|
23645
23804
|
}
|
|
23646
23805
|
|
|
23647
23806
|
// src/cli/cli-version.ts
|
|
23648
|
-
var CLI_VERSION = "0.8.
|
|
23807
|
+
var CLI_VERSION = "0.8.5";
|
|
23649
23808
|
|
|
23650
23809
|
// src/cli/sporades.ts
|
|
23651
23810
|
var SUPPORTED_TEMPLATES = new Set(CLIENT_TEMPLATES);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.8.
|
|
1
|
+
export declare const CLI_VERSION = "0.8.5";
|
|
2
2
|
//# sourceMappingURL=cli-version.d.ts.map
|
package/dist/cli/cli-version.js
CHANGED
|
@@ -144,7 +144,7 @@ export declare function sendEmailPasswordResetLink(database: LooseRecord, sessio
|
|
|
144
144
|
ok: boolean;
|
|
145
145
|
error?: undefined;
|
|
146
146
|
}>;
|
|
147
|
-
export declare function runQuery(database: LooseRecord, auth: any, queryName: string): Promise<any>;
|
|
147
|
+
export declare function runQuery(database: LooseRecord, auth: any, queryName: string, rawArgs?: unknown): Promise<any>;
|
|
148
148
|
export declare function runMutation(database: LooseRecord, auth: any, mutationName: string, args: any): Promise<any>;
|
|
149
149
|
export declare function runAppMessage(database: LooseRecord, auth: any, messageName: any, data: any, options?: LooseRecord): Promise<any>;
|
|
150
150
|
export declare function runCurrentUserJobWorker(database: LooseRecord): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-runtime-source.d.ts","sourceRoot":"","sources":["../src/server-runtime-source.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"server-runtime-source.d.ts","sourceRoot":"","sources":["../src/server-runtime-source.ts"],"names":[],"mappings":"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"}
|
|
@@ -2548,7 +2548,31 @@ export function createWebSocketHub(getDatabase, trustedRefresh = null) {
|
|
|
2548
2548
|
});
|
|
2549
2549
|
return;
|
|
2550
2550
|
}
|
|
2551
|
-
|
|
2551
|
+
let args;
|
|
2552
|
+
try {
|
|
2553
|
+
args = normalizeQueryArguments(message.args ?? []);
|
|
2554
|
+
}
|
|
2555
|
+
catch {
|
|
2556
|
+
sendJson(client, {
|
|
2557
|
+
id: message.id,
|
|
2558
|
+
type: "query.result",
|
|
2559
|
+
query: queryName,
|
|
2560
|
+
data: null,
|
|
2561
|
+
error: invalidQueryArgumentsError(),
|
|
2562
|
+
});
|
|
2563
|
+
return;
|
|
2564
|
+
}
|
|
2565
|
+
if (!message.query && args.length > 0) {
|
|
2566
|
+
sendJson(client, {
|
|
2567
|
+
id: message.id,
|
|
2568
|
+
type: "query.result",
|
|
2569
|
+
query: queryName,
|
|
2570
|
+
data: null,
|
|
2571
|
+
error: invalidQueryArgumentsError(),
|
|
2572
|
+
});
|
|
2573
|
+
return;
|
|
2574
|
+
}
|
|
2575
|
+
const subscription = { id: message.id, name: queryName, args, style: message.query ? "direct" : "rows", generation: 0 };
|
|
2552
2576
|
client.subscriptions.set(message.id, subscription);
|
|
2553
2577
|
void sendQueryResult(client, subscription, (error) => sendUnhandledMessageError(client, rawMessage, error));
|
|
2554
2578
|
return;
|
|
@@ -2959,7 +2983,7 @@ export function createWebSocketHub(getDatabase, trustedRefresh = null) {
|
|
|
2959
2983
|
subscription.generation = generation;
|
|
2960
2984
|
try {
|
|
2961
2985
|
const database = getDatabase();
|
|
2962
|
-
const result = await runQuery(database, client.session.auth, subscription.name);
|
|
2986
|
+
const result = await runQuery(database, client.session.auth, subscription.name, subscription.args);
|
|
2963
2987
|
const data = subscription.style === "direct"
|
|
2964
2988
|
? (result.data ?? result.rows)
|
|
2965
2989
|
: { rows: result.data ?? result.rows };
|
|
@@ -3197,7 +3221,14 @@ function sendJsonWithCompletion(client, message, timeoutMs = 250) {
|
|
|
3197
3221
|
}
|
|
3198
3222
|
});
|
|
3199
3223
|
}
|
|
3200
|
-
export async function runQuery(database, auth, queryName) {
|
|
3224
|
+
export async function runQuery(database, auth, queryName, rawArgs = []) {
|
|
3225
|
+
let args;
|
|
3226
|
+
try {
|
|
3227
|
+
args = normalizeQueryArguments(rawArgs);
|
|
3228
|
+
}
|
|
3229
|
+
catch {
|
|
3230
|
+
return { rows: null, data: null, error: invalidQueryArgumentsError() };
|
|
3231
|
+
}
|
|
3201
3232
|
let context;
|
|
3202
3233
|
try {
|
|
3203
3234
|
context = await applyContextMiddleware(database, createMutationContext(database, auth), "query");
|
|
@@ -3212,9 +3243,11 @@ export async function runQuery(database, auth, queryName) {
|
|
|
3212
3243
|
};
|
|
3213
3244
|
}
|
|
3214
3245
|
if (queryName === "ctx.env") {
|
|
3246
|
+
if (args.length > 0)
|
|
3247
|
+
return { rows: null, data: null, error: invalidQueryArgumentsError() };
|
|
3215
3248
|
return { data: context.env, error: null };
|
|
3216
3249
|
}
|
|
3217
|
-
const customResult = await runCustomQuery(database, context, queryName);
|
|
3250
|
+
const customResult = await runCustomQuery(database, context, queryName, args);
|
|
3218
3251
|
if (customResult) {
|
|
3219
3252
|
return customResult;
|
|
3220
3253
|
}
|
|
@@ -3228,6 +3261,8 @@ export async function runQuery(database, auth, queryName) {
|
|
|
3228
3261
|
},
|
|
3229
3262
|
};
|
|
3230
3263
|
}
|
|
3264
|
+
if (args.length > 0)
|
|
3265
|
+
return { rows: null, data: null, error: invalidQueryArgumentsError() };
|
|
3231
3266
|
const cacheKey = `${table.name}:${context.auth.userId}`;
|
|
3232
3267
|
if (!database.rowCache.has(cacheKey)) {
|
|
3233
3268
|
const columns = ["id", "createdAt", "updatedAt", ...table.fields.map((field) => field.name)];
|
|
@@ -3242,7 +3277,7 @@ export async function runQuery(database, auth, queryName) {
|
|
|
3242
3277
|
const rows = await filterRowsByReadAcl(database, table, database.rowCache.get(cacheKey), context);
|
|
3243
3278
|
return { rows, error: null };
|
|
3244
3279
|
}
|
|
3245
|
-
async function runCustomQuery(database, context, queryName) {
|
|
3280
|
+
async function runCustomQuery(database, context, queryName, args) {
|
|
3246
3281
|
const handler = database.queries.find((candidate) => candidate.name === queryName);
|
|
3247
3282
|
if (!handler) {
|
|
3248
3283
|
return null;
|
|
@@ -3251,7 +3286,7 @@ async function runCustomQuery(database, context, queryName) {
|
|
|
3251
3286
|
const queryHandler = typeof handler.handler === "function"
|
|
3252
3287
|
? handler.handler
|
|
3253
3288
|
: new Function(`return (${handler.handlerSource});`)();
|
|
3254
|
-
const data = await queryHandler(context);
|
|
3289
|
+
const data = await queryHandler(context, ...args);
|
|
3255
3290
|
assertJsonCompatible(data);
|
|
3256
3291
|
return { data, error: null };
|
|
3257
3292
|
}
|
|
@@ -3269,6 +3304,73 @@ async function runCustomQuery(database, context, queryName) {
|
|
|
3269
3304
|
};
|
|
3270
3305
|
}
|
|
3271
3306
|
}
|
|
3307
|
+
const QUERY_ARGUMENT_LIMIT_BYTES = 65536;
|
|
3308
|
+
function invalidQueryArgumentsError() {
|
|
3309
|
+
return {
|
|
3310
|
+
message: "Invalid query arguments.",
|
|
3311
|
+
hint: "Use a JSON-compatible argument array no larger than 65536 UTF-8 bytes.",
|
|
3312
|
+
};
|
|
3313
|
+
}
|
|
3314
|
+
function normalizeQueryArguments(value) {
|
|
3315
|
+
if (!Array.isArray(value))
|
|
3316
|
+
throw new Error("Query arguments must be an array.");
|
|
3317
|
+
const snapshot = normalizeQueryArgumentValue(value, new Set());
|
|
3318
|
+
const canonicalJson = JSON.stringify(snapshot);
|
|
3319
|
+
if (Buffer.byteLength(canonicalJson, "utf8") > QUERY_ARGUMENT_LIMIT_BYTES) {
|
|
3320
|
+
throw new Error("Query arguments are too large.");
|
|
3321
|
+
}
|
|
3322
|
+
return snapshot;
|
|
3323
|
+
}
|
|
3324
|
+
function normalizeQueryArgumentValue(value, ancestors) {
|
|
3325
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
3326
|
+
return value;
|
|
3327
|
+
if (typeof value === "number") {
|
|
3328
|
+
if (!Number.isFinite(value))
|
|
3329
|
+
throw new Error("Query arguments must contain finite numbers.");
|
|
3330
|
+
return value;
|
|
3331
|
+
}
|
|
3332
|
+
if (typeof value !== "object")
|
|
3333
|
+
throw new Error("Query arguments must be JSON-compatible.");
|
|
3334
|
+
if (Object.getOwnPropertySymbols(value).length > 0)
|
|
3335
|
+
throw new Error("Query arguments must not contain symbol keys.");
|
|
3336
|
+
if (ancestors.has(value))
|
|
3337
|
+
throw new Error("Query arguments must not contain cycles.");
|
|
3338
|
+
ancestors.add(value);
|
|
3339
|
+
try {
|
|
3340
|
+
if (Array.isArray(value)) {
|
|
3341
|
+
if (Object.getOwnPropertyNames(value).some((key) => key !== "length" && (!/^(0|[1-9]\\d*)$/.test(key) || Number(key) >= value.length))) {
|
|
3342
|
+
throw new Error("Query arguments must not contain non-index array properties.");
|
|
3343
|
+
}
|
|
3344
|
+
const copy = [];
|
|
3345
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
3346
|
+
if (!Object.hasOwn(value, index))
|
|
3347
|
+
throw new Error("Query arguments must not contain sparse arrays.");
|
|
3348
|
+
copy.push(normalizeQueryArgumentValue(value[index], ancestors));
|
|
3349
|
+
}
|
|
3350
|
+
return Object.freeze(copy);
|
|
3351
|
+
}
|
|
3352
|
+
const prototype = Object.getPrototypeOf(value);
|
|
3353
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
3354
|
+
throw new Error("Query arguments must contain plain objects only.");
|
|
3355
|
+
}
|
|
3356
|
+
const copy = Object.create(null);
|
|
3357
|
+
for (const key of Object.getOwnPropertyNames(value).sort()) {
|
|
3358
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
3359
|
+
if (!descriptor || !descriptor.enumerable || !("value" in descriptor))
|
|
3360
|
+
throw new Error("Query arguments must not contain accessors.");
|
|
3361
|
+
Object.defineProperty(copy, key, {
|
|
3362
|
+
value: normalizeQueryArgumentValue(descriptor.value, ancestors),
|
|
3363
|
+
enumerable: true,
|
|
3364
|
+
writable: false,
|
|
3365
|
+
configurable: false,
|
|
3366
|
+
});
|
|
3367
|
+
}
|
|
3368
|
+
return Object.freeze(copy);
|
|
3369
|
+
}
|
|
3370
|
+
finally {
|
|
3371
|
+
ancestors.delete(value);
|
|
3372
|
+
}
|
|
3373
|
+
}
|
|
3272
3374
|
export async function runMutation(database, auth, mutationName, args) {
|
|
3273
3375
|
let context;
|
|
3274
3376
|
let result;
|