@fitzzero/quickdraw-core 5.1.2 → 5.1.3
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/README.md +793 -57
- package/dist/{admin-DWWrT1h6.d.ts → admin-CS7JB_UH.d.ts} +2 -2
- package/dist/{adminSchemas-D4bFKsrb.d.ts → adminSchemas-29cGgrg5.d.ts} +1 -1
- package/dist/{auth-DXiSJZaf.d.ts → auth-DtdYjrZ9.d.ts} +11 -7
- package/dist/{caller-BT-Mv4bD.d.ts → caller-BTwGl7vm.d.ts} +183 -15
- package/dist/chunk-2FDSKLBO.js +82 -0
- package/dist/chunk-2FDSKLBO.js.map +1 -0
- package/dist/{chunk-GSIPUNJL.js → chunk-64I6MYBC.js} +139 -792
- package/dist/chunk-64I6MYBC.js.map +1 -0
- package/dist/chunk-7473HK24.js +17 -0
- package/dist/chunk-7473HK24.js.map +1 -0
- package/dist/chunk-7Y5ROPXL.js +33 -0
- package/dist/chunk-7Y5ROPXL.js.map +1 -0
- package/dist/chunk-A2RBQD66.js +7 -0
- package/dist/chunk-A2RBQD66.js.map +1 -0
- package/dist/chunk-ASAMUGHG.js +651 -0
- package/dist/chunk-ASAMUGHG.js.map +1 -0
- package/dist/{chunk-B2P7AZ25.js → chunk-CYE4UBOB.js} +6 -4
- package/dist/chunk-CYE4UBOB.js.map +1 -0
- package/dist/chunk-DNP65TD5.js +832 -0
- package/dist/chunk-DNP65TD5.js.map +1 -0
- package/dist/{chunk-5CGO426C.js → chunk-GYLWEJHW.js} +5 -1
- package/dist/chunk-GYLWEJHW.js.map +1 -0
- package/dist/{chunk-AONAFOYU.js → chunk-I3JCFS7H.js} +25 -13
- package/dist/chunk-I3JCFS7H.js.map +1 -0
- package/dist/{chunk-X5CWPX7H.js → chunk-IIQ3WMJK.js} +13 -2
- package/dist/chunk-IIQ3WMJK.js.map +1 -0
- package/dist/{chunk-6K5BY47X.js → chunk-JVTONQFI.js} +1277 -277
- package/dist/chunk-JVTONQFI.js.map +1 -0
- package/dist/{chunk-RL4VKLBX.js → chunk-KKQANRLG.js} +2 -2
- package/dist/{chunk-RL4VKLBX.js.map → chunk-KKQANRLG.js.map} +1 -1
- package/dist/{chunk-GKH3LJ7N.js → chunk-MPQAT55F.js} +38 -205
- package/dist/chunk-MPQAT55F.js.map +1 -0
- package/dist/chunk-NX2V6SIL.js +202 -0
- package/dist/chunk-NX2V6SIL.js.map +1 -0
- package/dist/{chunk-7QCUX4TK.js → chunk-NZHA4TZD.js} +72 -175
- package/dist/chunk-NZHA4TZD.js.map +1 -0
- package/dist/{chunk-MBH6IDSM.js → chunk-OCZJ4EYV.js} +40 -17
- package/dist/chunk-OCZJ4EYV.js.map +1 -0
- package/dist/{chunk-RJ5I2VO3.js → chunk-PHCURHC7.js} +6 -6
- package/dist/chunk-PHCURHC7.js.map +1 -0
- package/dist/{chunk-4YO5H5ON.js → chunk-Q4OGQM5I.js} +4 -10
- package/dist/chunk-Q4OGQM5I.js.map +1 -0
- package/dist/chunk-QSU47XEX.js +13 -0
- package/dist/chunk-QSU47XEX.js.map +1 -0
- package/dist/{chunk-LCC7HNFP.js → chunk-RZXJTDH5.js} +2 -2
- package/dist/{chunk-Z2LZ7IZ7.js → chunk-SN3OZVEQ.js} +5 -31
- package/dist/chunk-SN3OZVEQ.js.map +1 -0
- package/dist/{chunk-Z5ILULF2.js → chunk-SQ5VUG77.js} +4 -3
- package/dist/{chunk-Z5ILULF2.js.map → chunk-SQ5VUG77.js.map} +1 -1
- package/dist/chunk-STH5DLQP.js +1192 -0
- package/dist/chunk-STH5DLQP.js.map +1 -0
- package/dist/{chunk-NMXYENH4.js → chunk-VZFJTKIW.js} +6 -6
- package/dist/{chunk-753VJIUP.js → chunk-X4ZU4QH5.js} +43 -9
- package/dist/chunk-X4ZU4QH5.js.map +1 -0
- package/dist/chunk-XD7SRDUY.js +188 -0
- package/dist/chunk-XD7SRDUY.js.map +1 -0
- package/dist/chunk-Z7I6SONO.js +825 -0
- package/dist/chunk-Z7I6SONO.js.map +1 -0
- package/dist/cli/quickdraw-docs.js +131 -113
- package/dist/cli/quickdraw-docs.js.map +1 -1
- package/dist/client/core.d.ts +387 -0
- package/dist/client/core.js +109 -0
- package/dist/client/core.js.map +1 -0
- package/dist/client/index.d.ts +30 -193
- package/dist/client/index.js +56 -217
- package/dist/client/index.js.map +1 -1
- package/dist/{clientTypes-hWcPUVS9.d.ts → clientTypes-CUUNJwWs.d.ts} +3 -3
- package/dist/{connection-CgijdGpT.d.ts → connection-2EeQ_Et0.d.ts} +16 -9
- package/dist/{createServer-Cp-Wk500.d.ts → createServer-Bx5HOcYK.d.ts} +37 -9
- package/dist/{createTestApp-BB_PkqQ7.d.ts → createTestApp-DAMANT79.d.ts} +36 -9
- package/dist/{envelope-DzWyIXjj.d.ts → envelope-BMBaxppV.d.ts} +200 -8
- package/dist/{http-DotQ5bj8.d.ts → http-YzLAar3w.d.ts} +15 -50
- package/dist/index.d.ts +7 -6
- package/dist/index.js +61 -28
- package/dist/index.js.map +1 -1
- package/dist/{infer-BmoZsL9a.d.ts → infer-Br62cnyW.d.ts} +39 -1
- package/dist/{metrics-DW80Exve.d.ts → metrics-CTVpw0gu.d.ts} +2 -2
- package/dist/parser.js +2 -1
- package/dist/prisma/index.d.ts +13 -6
- package/dist/prisma/index.js +155 -9
- package/dist/prisma/index.js.map +1 -1
- package/dist/remote-N7OUAGOW.js +15 -0
- package/dist/remote-N7OUAGOW.js.map +1 -0
- package/dist/server/auth/index.d.ts +3 -3
- package/dist/server/auth/index.js +5 -3
- package/dist/server/auth/index.js.map +1 -1
- package/dist/server/index.d.ts +140 -36
- package/dist/server/index.js +162 -55
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/index.d.ts +34 -18
- package/dist/server/mcp/index.js +29 -12
- package/dist/server/mcp/index.js.map +1 -1
- package/dist/server/otel.d.ts +3 -3
- package/dist/{sharing-UDLn1049.d.ts → sharing-BrsmlDv_.d.ts} +3 -3
- package/dist/testing/client.d.ts +13 -13
- package/dist/testing/client.js +17 -10
- package/dist/testing/client.js.map +1 -1
- package/dist/testing/index.d.ts +49 -12
- package/dist/testing/index.js +102 -19
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock.d.ts +5 -5
- package/dist/testing/mock.js +15 -9
- package/dist/tracked-BR5b2O-U.d.ts +43 -0
- package/dist/{types-CYliTP65.d.ts → types-CLRCQGnO.d.ts} +4 -3
- package/dist/types-tMagHbjy.d.ts +151 -0
- package/dist/utils/index.d.ts +16 -4
- package/dist/utils/index.js +6 -3
- package/package.json +5 -1
- package/dist/chunk-4YO5H5ON.js.map +0 -1
- package/dist/chunk-5CGO426C.js.map +0 -1
- package/dist/chunk-6K5BY47X.js.map +0 -1
- package/dist/chunk-753VJIUP.js.map +0 -1
- package/dist/chunk-7QCUX4TK.js.map +0 -1
- package/dist/chunk-AONAFOYU.js.map +0 -1
- package/dist/chunk-B2P7AZ25.js.map +0 -1
- package/dist/chunk-GKH3LJ7N.js.map +0 -1
- package/dist/chunk-GSIPUNJL.js.map +0 -1
- package/dist/chunk-MBH6IDSM.js.map +0 -1
- package/dist/chunk-RJ5I2VO3.js.map +0 -1
- package/dist/chunk-X5CWPX7H.js.map +0 -1
- package/dist/chunk-Z2LZ7IZ7.js.map +0 -1
- /package/dist/{chunk-LCC7HNFP.js.map → chunk-RZXJTDH5.js.map} +0 -0
- /package/dist/{chunk-NMXYENH4.js.map → chunk-VZFJTKIW.js.map} +0 -0
package/dist/server/index.js
CHANGED
|
@@ -49,9 +49,10 @@ import {
|
|
|
49
49
|
orderByOf,
|
|
50
50
|
resolver,
|
|
51
51
|
rowlessProblem,
|
|
52
|
+
seedRoomHosts,
|
|
52
53
|
selectWith,
|
|
53
54
|
watchAdapterClients
|
|
54
|
-
} from "../chunk-
|
|
55
|
+
} from "../chunk-JVTONQFI.js";
|
|
55
56
|
import {
|
|
56
57
|
DevWarningError,
|
|
57
58
|
N_PLUS_ONE_STATEMENTS,
|
|
@@ -59,14 +60,14 @@ import {
|
|
|
59
60
|
currentRev,
|
|
60
61
|
formatDevWarning,
|
|
61
62
|
runBeforeAnyDispatcher
|
|
62
|
-
} from "../chunk-
|
|
63
|
+
} from "../chunk-SQ5VUG77.js";
|
|
63
64
|
import {
|
|
64
65
|
crud,
|
|
65
66
|
crudSpecOf,
|
|
66
67
|
sharing,
|
|
67
68
|
sharingSpecOf
|
|
68
|
-
} from "../chunk-
|
|
69
|
-
import "../chunk-
|
|
69
|
+
} from "../chunk-VZFJTKIW.js";
|
|
70
|
+
import "../chunk-KKQANRLG.js";
|
|
70
71
|
import {
|
|
71
72
|
SUBSCRIBER_LEVELS,
|
|
72
73
|
abortError,
|
|
@@ -92,27 +93,42 @@ import {
|
|
|
92
93
|
schemaOutputOf,
|
|
93
94
|
serviceGrant,
|
|
94
95
|
strip
|
|
95
|
-
} from "../chunk-
|
|
96
|
-
import "../chunk-
|
|
96
|
+
} from "../chunk-OCZJ4EYV.js";
|
|
97
|
+
import "../chunk-RZXJTDH5.js";
|
|
97
98
|
import {
|
|
98
99
|
recordSocketSession,
|
|
99
100
|
toCallReply,
|
|
100
101
|
unreadable
|
|
101
|
-
} from "../chunk-
|
|
102
|
+
} from "../chunk-PHCURHC7.js";
|
|
102
103
|
import {
|
|
103
104
|
ANY_FIELD,
|
|
104
105
|
modelKey,
|
|
105
106
|
storageOf
|
|
106
107
|
} from "../chunk-DUBU3BB3.js";
|
|
108
|
+
import {
|
|
109
|
+
search,
|
|
110
|
+
searchSpecOf
|
|
111
|
+
} from "../chunk-XD7SRDUY.js";
|
|
112
|
+
import "../chunk-4D2DOGAW.js";
|
|
113
|
+
import {
|
|
114
|
+
ACCESS_LEVELS,
|
|
115
|
+
isAccessLevel
|
|
116
|
+
} from "../chunk-AK23XSWD.js";
|
|
117
|
+
import {
|
|
118
|
+
consoleLogger,
|
|
119
|
+
errorText
|
|
120
|
+
} from "../chunk-DKE6QO26.js";
|
|
121
|
+
import "../chunk-7473HK24.js";
|
|
122
|
+
import "../chunk-2FDSKLBO.js";
|
|
107
123
|
import {
|
|
108
124
|
ADMIN_FIELD_TYPES,
|
|
109
125
|
ADMIN_NEVER_WRITABLE,
|
|
110
126
|
MAX_CURSOR_LENGTH,
|
|
111
127
|
admin,
|
|
112
128
|
adminSpecOf,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
129
|
+
writtenData
|
|
130
|
+
} from "../chunk-MPQAT55F.js";
|
|
131
|
+
import "../chunk-A2RBQD66.js";
|
|
116
132
|
import {
|
|
117
133
|
MAX_ENTITY_IDS_PER_SOCKET,
|
|
118
134
|
MAX_SCOPES_PER_SOCKET,
|
|
@@ -120,25 +136,19 @@ import {
|
|
|
120
136
|
entityRoom,
|
|
121
137
|
reservedRoomPrefix,
|
|
122
138
|
socketIdShaped
|
|
123
|
-
} from "../chunk-
|
|
139
|
+
} from "../chunk-X4ZU4QH5.js";
|
|
124
140
|
import {
|
|
125
141
|
CHANNEL_DEFAULT_RATE,
|
|
142
|
+
isHostServed,
|
|
126
143
|
isScopedStream
|
|
127
|
-
} from "../chunk-
|
|
128
|
-
import "../chunk-4D2DOGAW.js";
|
|
129
|
-
import {
|
|
130
|
-
ACCESS_LEVELS,
|
|
131
|
-
isAccessLevel
|
|
132
|
-
} from "../chunk-AK23XSWD.js";
|
|
144
|
+
} from "../chunk-GYLWEJHW.js";
|
|
133
145
|
import "../chunk-47AEXVDM.js";
|
|
134
|
-
import "../chunk-
|
|
135
|
-
import
|
|
136
|
-
consoleLogger,
|
|
137
|
-
errorText
|
|
138
|
-
} from "../chunk-DKE6QO26.js";
|
|
146
|
+
import "../chunk-SN3OZVEQ.js";
|
|
147
|
+
import "../chunk-7Y5ROPXL.js";
|
|
139
148
|
import {
|
|
140
149
|
QuickdrawError
|
|
141
|
-
} from "../chunk-
|
|
150
|
+
} from "../chunk-Q4OGQM5I.js";
|
|
151
|
+
import "../chunk-QSU47XEX.js";
|
|
142
152
|
|
|
143
153
|
// src/server/realtime/define.ts
|
|
144
154
|
function isRecord(value) {
|
|
@@ -224,7 +234,7 @@ function checkChannel(service, name, def, entry, kinds, fail5) {
|
|
|
224
234
|
});
|
|
225
235
|
}
|
|
226
236
|
function compileChannels(contract, value, kinds, fail5) {
|
|
227
|
-
const declared = Object.entries(contract.channels);
|
|
237
|
+
const declared = Object.entries(contract.channels).filter(([, def]) => !isHostServed(def));
|
|
228
238
|
if (value === void 0 && declared.length === 0) {
|
|
229
239
|
return /* @__PURE__ */ new Map();
|
|
230
240
|
}
|
|
@@ -242,6 +252,11 @@ function compileChannels(contract, value, kinds, fail5) {
|
|
|
242
252
|
if (def === void 0) {
|
|
243
253
|
fail5(`"${name}" is not a channel of the contract`);
|
|
244
254
|
}
|
|
255
|
+
if (isHostServed(def)) {
|
|
256
|
+
fail5(
|
|
257
|
+
`channel "${name}" is served by the room's host (serve: "host"): its messages never reach the server, so it takes no handler`
|
|
258
|
+
);
|
|
259
|
+
}
|
|
245
260
|
channels.set(name, checkChannel(contract.name, name, def, entry, admitted, fail5));
|
|
246
261
|
}
|
|
247
262
|
return channels;
|
|
@@ -310,6 +325,11 @@ function checkStreamOptions(name, def, entry, fail5) {
|
|
|
310
325
|
return { computeSeed: seed, validate };
|
|
311
326
|
}
|
|
312
327
|
function checkStream(name, def, needs, options, fail5) {
|
|
328
|
+
if (isHostServed(def) && options !== void 0) {
|
|
329
|
+
fail5(
|
|
330
|
+
`streams.${name}: the stream is served by the room's host (serve: "host"), whose client pushes its items and keeps its seed: the server takes no options for it`
|
|
331
|
+
);
|
|
332
|
+
}
|
|
313
333
|
const access = engineForm(def.access);
|
|
314
334
|
if (typeof access === "object" && "entry" in access && !needs.hasPolicy) {
|
|
315
335
|
fail5(
|
|
@@ -329,7 +349,8 @@ function checkStream(name, def, needs, options, fail5) {
|
|
|
329
349
|
...checkStreamOptions(name, def, options, fail5),
|
|
330
350
|
volatile: def.volatile === true,
|
|
331
351
|
access,
|
|
332
|
-
room: roomAccess(def.access)
|
|
352
|
+
room: roomAccess(def.access),
|
|
353
|
+
host: isHostServed(def)
|
|
333
354
|
});
|
|
334
355
|
}
|
|
335
356
|
function compileStreams(contract, needs, value, fail5) {
|
|
@@ -506,6 +527,7 @@ var DEFINITION_KEYS = /* @__PURE__ */ new Set([
|
|
|
506
527
|
"channels",
|
|
507
528
|
"streams",
|
|
508
529
|
"onRoomLeave",
|
|
530
|
+
"onHostLeave",
|
|
509
531
|
"adminBypass",
|
|
510
532
|
"kinds"
|
|
511
533
|
]);
|
|
@@ -724,13 +746,16 @@ function buildService(runtime, contract, definition) {
|
|
|
724
746
|
fail5("the definition must be an object");
|
|
725
747
|
}
|
|
726
748
|
checkKeys(definition, DEFINITION_KEYS, "the definition", fail5);
|
|
727
|
-
const { adminBypass = true, onRoomLeave } = definition;
|
|
749
|
+
const { adminBypass = true, onRoomLeave, onHostLeave } = definition;
|
|
728
750
|
if (typeof adminBypass !== "boolean") {
|
|
729
751
|
fail5("adminBypass must be a boolean");
|
|
730
752
|
}
|
|
731
753
|
if (onRoomLeave !== void 0 && typeof onRoomLeave !== "function") {
|
|
732
754
|
fail5("onRoomLeave must be a function of the leave and a run context");
|
|
733
755
|
}
|
|
756
|
+
if (onHostLeave !== void 0 && typeof onHostLeave !== "function") {
|
|
757
|
+
fail5("onHostLeave must be a function of the leave and a run context");
|
|
758
|
+
}
|
|
734
759
|
const data = checkServiceData(definition, runtime.kinds, fail5);
|
|
735
760
|
const projections = compileProjections(checked2, definition.project, fail5);
|
|
736
761
|
const collections = compileCollections(
|
|
@@ -760,7 +785,8 @@ function buildService(runtime, contract, definition) {
|
|
|
760
785
|
definition.streams,
|
|
761
786
|
fail5
|
|
762
787
|
),
|
|
763
|
-
onRoomLeave
|
|
788
|
+
onRoomLeave,
|
|
789
|
+
onHostLeave
|
|
764
790
|
});
|
|
765
791
|
checkHandlers(service, fail5);
|
|
766
792
|
registerRuntime(service, runtime);
|
|
@@ -801,6 +827,10 @@ function streamOf(current, contract, name) {
|
|
|
801
827
|
},
|
|
802
828
|
pushMany(...args) {
|
|
803
829
|
handle().pushMany(...args);
|
|
830
|
+
},
|
|
831
|
+
// Async, so every refusal (no dispatcher, a host-served stream, a bad scope) rejects.
|
|
832
|
+
async subscribers(...args) {
|
|
833
|
+
return await handle().subscribers(...args);
|
|
804
834
|
}
|
|
805
835
|
});
|
|
806
836
|
}
|
|
@@ -2133,7 +2163,8 @@ async function authorizeScope(run, collection, scope) {
|
|
|
2133
2163
|
const rule = {
|
|
2134
2164
|
service: runtime.service,
|
|
2135
2165
|
anchor: collection.anchor?.name,
|
|
2136
|
-
access: collection.access
|
|
2166
|
+
access: collection.access,
|
|
2167
|
+
requires: collection.requires
|
|
2137
2168
|
};
|
|
2138
2169
|
const allowed = await allowedScopes(runtime.access, rule, principal, [scope]);
|
|
2139
2170
|
if (!allowed.has(scope)) {
|
|
@@ -3558,6 +3589,45 @@ async function checkWrite(call, context, data) {
|
|
|
3558
3589
|
["data"]
|
|
3559
3590
|
);
|
|
3560
3591
|
}
|
|
3592
|
+
function preparedIssues(call, columns) {
|
|
3593
|
+
const never = Object.keys(columns).filter((name) => ADMIN_NEVER_WRITABLE.includes(name)).map((name) => ({
|
|
3594
|
+
path: ["data", name],
|
|
3595
|
+
message: `"${name}" is not writable; the database sets it`
|
|
3596
|
+
}));
|
|
3597
|
+
return [...never, ...columnValueIssues(call.runtime.service, columns, ["data"])];
|
|
3598
|
+
}
|
|
3599
|
+
async function prepareWrite(call, context, write, ctx, db) {
|
|
3600
|
+
const { prepare } = context;
|
|
3601
|
+
const data = write.method === "adminDelete" ? {} : write.data;
|
|
3602
|
+
if (prepare === void 0) {
|
|
3603
|
+
return data;
|
|
3604
|
+
}
|
|
3605
|
+
const returned = await checked(() => prepare(write, ctx, db));
|
|
3606
|
+
if (write.method === "adminDelete" || returned === void 0) {
|
|
3607
|
+
return data;
|
|
3608
|
+
}
|
|
3609
|
+
const { name } = call.runtime.service;
|
|
3610
|
+
if (!isPlainObject(returned)) {
|
|
3611
|
+
throw new QuickdrawError(
|
|
3612
|
+
"INTERNAL",
|
|
3613
|
+
`The admin kit's prepare for ${name} must return column values, or nothing`
|
|
3614
|
+
);
|
|
3615
|
+
}
|
|
3616
|
+
const columns = writtenData(returned);
|
|
3617
|
+
const issues = preparedIssues(call, columns);
|
|
3618
|
+
if (issues.length > 0) {
|
|
3619
|
+
throw new QuickdrawError(
|
|
3620
|
+
"VALIDATION",
|
|
3621
|
+
`The admin kit's prepare for ${name} returned data the kit does not write`,
|
|
3622
|
+
{ issues }
|
|
3623
|
+
);
|
|
3624
|
+
}
|
|
3625
|
+
const level = rowLevel(context.form, "Moderate");
|
|
3626
|
+
await checkAnchorColumns(call.runtime, call.principal, columns, level, {
|
|
3627
|
+
creating: write.method === "adminCreate"
|
|
3628
|
+
});
|
|
3629
|
+
return { ...data, ...columns };
|
|
3630
|
+
}
|
|
3561
3631
|
function isValueRefusal(error) {
|
|
3562
3632
|
if (!(error instanceof Error)) {
|
|
3563
3633
|
return false;
|
|
@@ -3608,37 +3678,65 @@ function committed(context, args, write) {
|
|
|
3608
3678
|
failed(error);
|
|
3609
3679
|
}
|
|
3610
3680
|
}
|
|
3611
|
-
async function
|
|
3612
|
-
const
|
|
3613
|
-
|
|
3681
|
+
async function rowBefore(context, call, table, write) {
|
|
3682
|
+
const readsAsItDeletes = write.method === "adminDelete" && context.prepare === void 0;
|
|
3683
|
+
if (write.method === "adminCreate" || readsAsItDeletes) {
|
|
3684
|
+
return void 0;
|
|
3685
|
+
}
|
|
3686
|
+
const row = await table.findUnique({ where: { id: write.id }, select: call.projection.select });
|
|
3687
|
+
return requireRow(row, `No such ${call.model}`);
|
|
3688
|
+
}
|
|
3689
|
+
function prepareCall(write, before) {
|
|
3690
|
+
if (write.method === "adminCreate") {
|
|
3691
|
+
return Object.freeze({ method: write.method, data: Object.freeze({ ...write.data }) });
|
|
3692
|
+
}
|
|
3693
|
+
if (write.method === "adminUpdate") {
|
|
3694
|
+
const data = Object.freeze({ ...write.data });
|
|
3695
|
+
return Object.freeze({ method: write.method, id: write.id, data, before });
|
|
3696
|
+
}
|
|
3697
|
+
return Object.freeze({ method: write.method, id: write.id, before });
|
|
3698
|
+
}
|
|
3699
|
+
async function written(context, call, args, write, perform) {
|
|
3700
|
+
const { prepare, onWrite, onCommitted } = context;
|
|
3701
|
+
const data = write.method === "adminDelete" ? {} : write.data;
|
|
3702
|
+
const attempt = async (table, values, before) => {
|
|
3614
3703
|
try {
|
|
3615
|
-
return await perform(table, before);
|
|
3704
|
+
return await perform({ table, values, before });
|
|
3616
3705
|
} catch (error) {
|
|
3617
3706
|
throw writeRefusal(error);
|
|
3618
3707
|
}
|
|
3619
3708
|
};
|
|
3620
|
-
if (onWrite === void 0 && onCommitted === void 0) {
|
|
3621
|
-
return (await attempt(call.table, false)).reply;
|
|
3709
|
+
if (prepare === void 0 && onWrite === void 0 && onCommitted === void 0) {
|
|
3710
|
+
return (await attempt(call.table, data, false)).reply;
|
|
3622
3711
|
}
|
|
3623
|
-
|
|
3712
|
+
const ctx = args.ctx;
|
|
3713
|
+
let heard;
|
|
3624
3714
|
const reply = await inKitTransaction(
|
|
3625
3715
|
args.db,
|
|
3626
3716
|
async (tx) => {
|
|
3627
|
-
const
|
|
3717
|
+
const table = tableOf2(tx, call.model);
|
|
3628
3718
|
const whole = (row) => row ? rowWhole(call, row) : null;
|
|
3629
|
-
|
|
3630
|
-
|
|
3719
|
+
const first = await rowBefore(context, call, table, write);
|
|
3720
|
+
const before = first === void 0 ? void 0 : whole(first);
|
|
3721
|
+
const values = await prepareWrite(call, context, prepareCall(write, before), ctx, tx);
|
|
3722
|
+
const done = await attempt(
|
|
3723
|
+
table,
|
|
3724
|
+
values,
|
|
3725
|
+
write.method === "adminDelete" && first === void 0
|
|
3726
|
+
);
|
|
3727
|
+
heard = {
|
|
3728
|
+
method: write.method,
|
|
3631
3729
|
id: done.id,
|
|
3632
|
-
...method === "adminCreate" ? {} : { before: whole(done.before) },
|
|
3730
|
+
...write.method === "adminCreate" ? {} : { before: before ?? whole(done.before) },
|
|
3633
3731
|
after: whole(done.after)
|
|
3634
3732
|
};
|
|
3635
|
-
await onWrite?.(
|
|
3733
|
+
await onWrite?.(heard, ctx, tx);
|
|
3636
3734
|
return done.reply;
|
|
3637
3735
|
},
|
|
3638
|
-
{ owner: "The admin kit's writes with onWrite or onCommitted" }
|
|
3736
|
+
{ owner: "The admin kit's writes with prepare, onWrite or onCommitted" }
|
|
3639
3737
|
);
|
|
3640
|
-
if (
|
|
3641
|
-
committed(context, args,
|
|
3738
|
+
if (heard !== void 0) {
|
|
3739
|
+
committed(context, args, heard);
|
|
3642
3740
|
}
|
|
3643
3741
|
return reply;
|
|
3644
3742
|
}
|
|
@@ -3657,8 +3755,10 @@ function adminCreateHandler(context) {
|
|
|
3657
3755
|
const call = adminCall(args.ctx, args.db, context.fieldsFor);
|
|
3658
3756
|
const { data } = args.input;
|
|
3659
3757
|
await checkWrite(call, context, data);
|
|
3660
|
-
|
|
3661
|
-
|
|
3758
|
+
const { select } = call.projection;
|
|
3759
|
+
const write = { method: "adminCreate", data };
|
|
3760
|
+
return await written(context, call, args, write, async ({ table, values }) => {
|
|
3761
|
+
const after = await table.create({ data: values, select });
|
|
3662
3762
|
return { id: idOf(after), after, reply: rowOut(call, after) };
|
|
3663
3763
|
});
|
|
3664
3764
|
};
|
|
@@ -3673,10 +3773,10 @@ function adminUpdateHandler(context) {
|
|
|
3673
3773
|
return await readRow(call, id);
|
|
3674
3774
|
}
|
|
3675
3775
|
const { select } = call.projection;
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
const after = await table.update({ where: { id }, data, select });
|
|
3679
|
-
return { id,
|
|
3776
|
+
const write = { method: "adminUpdate", id, data };
|
|
3777
|
+
return await written(context, call, args, write, async ({ table, values }) => {
|
|
3778
|
+
const after = await table.update({ where: { id }, data: values, select });
|
|
3779
|
+
return { id, after, reply: rowOut(call, after) };
|
|
3680
3780
|
});
|
|
3681
3781
|
};
|
|
3682
3782
|
return handler;
|
|
@@ -3685,10 +3785,11 @@ function adminDeleteHandler(context) {
|
|
|
3685
3785
|
const handler = async (args) => {
|
|
3686
3786
|
const call = adminCall(args.ctx, args.db, context.fieldsFor);
|
|
3687
3787
|
const { id } = args.input;
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
const
|
|
3691
|
-
|
|
3788
|
+
const write = { method: "adminDelete", id };
|
|
3789
|
+
return await written(context, call, args, write, async ({ table, before }) => {
|
|
3790
|
+
const select = before ? call.projection.select : { id: true };
|
|
3791
|
+
const row = await table.delete({ where: { id }, select });
|
|
3792
|
+
return { id, ...before ? { before: row } : {}, after: null, reply: null };
|
|
3692
3793
|
});
|
|
3693
3794
|
};
|
|
3694
3795
|
return handler;
|
|
@@ -3739,6 +3840,7 @@ var OPTION_KEYS3 = [
|
|
|
3739
3840
|
"fieldOverrides",
|
|
3740
3841
|
"rowless",
|
|
3741
3842
|
"grants",
|
|
3843
|
+
"prepare",
|
|
3742
3844
|
"onWrite",
|
|
3743
3845
|
"onCommitted"
|
|
3744
3846
|
];
|
|
@@ -3776,7 +3878,7 @@ function checkOptions3(options) {
|
|
|
3776
3878
|
}
|
|
3777
3879
|
if (!isRecord11(options)) {
|
|
3778
3880
|
fail4(
|
|
3779
|
-
"options must be { access?, displayName?, hiddenFields?, editable?, fieldOverrides?, rowless?, grants?, onWrite?, onCommitted? }"
|
|
3881
|
+
"options must be { access?, displayName?, hiddenFields?, editable?, fieldOverrides?, rowless?, grants?, prepare?, onWrite?, onCommitted? }"
|
|
3780
3882
|
);
|
|
3781
3883
|
}
|
|
3782
3884
|
const unknownKey = Object.keys(options).find((key) => !OPTION_KEYS3.includes(key));
|
|
@@ -3884,7 +3986,10 @@ function handlers4(contract, options) {
|
|
|
3884
3986
|
const rowless = rowlessMethods(checked2.rowless, names, fail4);
|
|
3885
3987
|
const fieldsFor = fieldsByColumn(contract, kit, checked2);
|
|
3886
3988
|
const fields = fieldsFor(void 0);
|
|
3887
|
-
const { onWrite, onCommitted } = checked2;
|
|
3989
|
+
const { prepare, onWrite, onCommitted } = checked2;
|
|
3990
|
+
if (prepare !== void 0 && typeof prepare !== "function") {
|
|
3991
|
+
fail4("prepare must be a function of (call, ctx, db)");
|
|
3992
|
+
}
|
|
3888
3993
|
if (onWrite !== void 0 && typeof onWrite !== "function") {
|
|
3889
3994
|
fail4("onWrite must be a function of (write, ctx, db)");
|
|
3890
3995
|
}
|
|
@@ -3892,6 +3997,7 @@ function handlers4(contract, options) {
|
|
|
3892
3997
|
fail4("onCommitted must be a function of (write, ctx)");
|
|
3893
3998
|
}
|
|
3894
3999
|
const hooks = {
|
|
4000
|
+
prepare,
|
|
3895
4001
|
onWrite,
|
|
3896
4002
|
onCommitted
|
|
3897
4003
|
};
|
|
@@ -3970,6 +4076,7 @@ async function setupRedisAdapter(io, options = {}) {
|
|
|
3970
4076
|
await Promise.all([pubClient.connect(), subClient.connect()]);
|
|
3971
4077
|
io.adapter(createAdapter(pubClient, subClient, { key }));
|
|
3972
4078
|
watchAdapterClients(io, logger);
|
|
4079
|
+
seedRoomHosts(io);
|
|
3973
4080
|
logger.info(`Redis adapter connected to ${host}:${port}`);
|
|
3974
4081
|
const cleanup = async () => {
|
|
3975
4082
|
try {
|