@fitzzero/quickdraw-core 5.0.1 → 5.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/README.md +303 -32
- package/dist/{admin-CBlTY0do.d.ts → admin-DKwkIh5i.d.ts} +2 -2
- package/dist/{adminSchemas-zji11V74.d.ts → adminSchemas-2DUA_hZ8.d.ts} +1 -1
- package/dist/{auth-CMyRkfjZ.d.ts → auth-BhWqdyb-.d.ts} +2 -2
- package/dist/{caller-BqCYnpmC.d.ts → caller-CXTFswBO.d.ts} +27 -5
- package/dist/{chunk-2XJFNVHW.js → chunk-2PWMUVC7.js} +1 -1
- package/dist/{chunk-2XJFNVHW.js.map → chunk-2PWMUVC7.js.map} +1 -1
- package/dist/{chunk-3JNMVB2V.js → chunk-2UELNHDD.js} +2 -2
- package/dist/{chunk-HQ6WIRE2.js → chunk-3674EIRT.js} +4 -4
- package/dist/{chunk-NS5PUHIZ.js → chunk-A6DJBGOG.js} +130 -23
- package/dist/chunk-A6DJBGOG.js.map +1 -0
- package/dist/{chunk-ZACRRYWL.js → chunk-JZBCLNDU.js} +4 -4
- package/dist/{chunk-O2PWC2YW.js → chunk-KNDDCVQJ.js} +3 -3
- package/dist/{chunk-5YRQ26LF.js → chunk-RT6OILOI.js} +1 -1
- package/dist/chunk-RT6OILOI.js.map +1 -0
- package/dist/{chunk-WVIRXYI4.js → chunk-VJTFXHOK.js} +2 -2
- package/dist/{chunk-WVIRXYI4.js.map → chunk-VJTFXHOK.js.map} +1 -1
- package/dist/chunk-XNLWBZIG.js +9 -0
- package/dist/chunk-XNLWBZIG.js.map +1 -0
- package/dist/cli/quickdraw-docs.js +241 -27
- package/dist/cli/quickdraw-docs.js.map +1 -1
- package/dist/client/index.d.ts +7 -7
- package/dist/client/index.js +4 -4
- package/dist/{clientTypes-DuZW_8mb.d.ts → clientTypes-Bc-G8n-U.d.ts} +3 -3
- package/dist/{connection-DuN8GvcD.d.ts → connection-Cr1BU07W.d.ts} +2 -2
- package/dist/{createServer-CGsJlxM8.d.ts → createServer-CP9eDHZZ.d.ts} +30 -9
- package/dist/{createTestApp-Cg2CZa1g.d.ts → createTestApp-UKVGEmOf.d.ts} +6 -6
- package/dist/{envelope-nf5eGB-a.d.ts → envelope-Cp3YcVxC.d.ts} +1 -1
- package/dist/{http-D3fSFQ57.d.ts → http-Djj2Inuu.d.ts} +6 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +26 -6
- package/dist/index.js.map +1 -1
- package/dist/{infer-Dw21kmLw.d.ts → infer-Teo1xpsd.d.ts} +17 -0
- package/dist/{metrics-C0WvB7-I.d.ts → metrics-BdBu5o8G.d.ts} +2 -2
- package/dist/prisma/index.d.ts +4 -4
- package/dist/server/auth/index.d.ts +3 -3
- package/dist/server/index.d.ts +47 -26
- package/dist/server/index.js +62 -36
- package/dist/server/index.js.map +1 -1
- package/dist/server/mcp/index.d.ts +51 -11
- package/dist/server/mcp/index.js +129 -8
- package/dist/server/mcp/index.js.map +1 -1
- package/dist/server/otel.d.ts +3 -3
- package/dist/{sharing-CoreezAq.d.ts → sharing-DVa8W-yR.d.ts} +3 -3
- package/dist/testing/client.d.ts +13 -13
- package/dist/testing/client.js +5 -5
- package/dist/testing/index.d.ts +156 -12
- package/dist/testing/index.js +1036 -21
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/mock.d.ts +5 -5
- package/dist/testing/mock.js +4 -4
- package/dist/{types-Bwg3kIEc.d.ts → types-Cu0vmx7c.d.ts} +8 -2
- package/dist/utils/index.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-5YRQ26LF.js.map +0 -1
- package/dist/chunk-NS5PUHIZ.js.map +0 -1
- /package/dist/{chunk-3JNMVB2V.js.map → chunk-2UELNHDD.js.map} +0 -0
- /package/dist/{chunk-HQ6WIRE2.js.map → chunk-3674EIRT.js.map} +0 -0
- /package/dist/{chunk-ZACRRYWL.js.map → chunk-JZBCLNDU.js.map} +0 -0
- /package/dist/{chunk-O2PWC2YW.js.map → chunk-KNDDCVQJ.js.map} +0 -0
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
defaultToolName
|
|
4
|
+
} from "../chunk-XNLWBZIG.js";
|
|
2
5
|
import {
|
|
3
6
|
CHANNEL_DEFAULT_RATE,
|
|
4
7
|
DEFAULT_COLLECTION_LIMIT,
|
|
5
8
|
DEFAULT_COLLECTION_MAX_LIMIT,
|
|
6
9
|
isScopedStream
|
|
7
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-RT6OILOI.js";
|
|
8
11
|
import {
|
|
9
12
|
hasJsonSchema,
|
|
10
13
|
isStandardSchema
|
|
@@ -76,6 +79,12 @@ function recordsOf(value) {
|
|
|
76
79
|
}
|
|
77
80
|
return value;
|
|
78
81
|
}
|
|
82
|
+
function kindsOf(value) {
|
|
83
|
+
return Array.isArray(value) && value.every((kind) => typeof kind === "string") ? value : void 0;
|
|
84
|
+
}
|
|
85
|
+
function namesOf(value) {
|
|
86
|
+
return Array.isArray(value) ? value.filter((name) => typeof name === "string") : [];
|
|
87
|
+
}
|
|
79
88
|
function mapEach(records, read) {
|
|
80
89
|
return new Map([...records].map(([name, record]) => [name, read(record)]));
|
|
81
90
|
}
|
|
@@ -94,13 +103,17 @@ function serviceDocOf(value) {
|
|
|
94
103
|
}
|
|
95
104
|
return Object.freeze({
|
|
96
105
|
name: value.name,
|
|
106
|
+
model: typeof value.model === "string" ? value.model : void 0,
|
|
107
|
+
writes: namesOf(value.writes),
|
|
108
|
+
affects: Array.isArray(value.affects) ? value.affects.map((link) => contractName(isRecord(link) ? link.service : void 0)).filter((name) => name !== void 0) : [],
|
|
97
109
|
policy: value.access,
|
|
98
110
|
adminBypass: value.adminBypass !== false,
|
|
111
|
+
kinds: kindsOf(value.kinds),
|
|
99
112
|
watchAccess: value.watchAccess,
|
|
100
113
|
methods: new Map(
|
|
101
114
|
Object.entries(methods).map(([name, method]) => [
|
|
102
115
|
name,
|
|
103
|
-
{ access: method.access, rowless: method.rowless === true }
|
|
116
|
+
{ access: method.access, rowless: method.rowless === true, kinds: kindsOf(method.kinds) }
|
|
104
117
|
])
|
|
105
118
|
),
|
|
106
119
|
collections: mapEach(collections, (collection) => ({
|
|
@@ -140,8 +153,11 @@ function servicesOf(exports) {
|
|
|
140
153
|
[...found].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([name, { doc }]) => [name, doc])
|
|
141
154
|
);
|
|
142
155
|
}
|
|
156
|
+
function contractName(contract) {
|
|
157
|
+
return isRecord(contract) && typeof contract.name === "string" ? contract.name : void 0;
|
|
158
|
+
}
|
|
143
159
|
function serviceName(contract) {
|
|
144
|
-
return
|
|
160
|
+
return contractName(contract) ?? "another service";
|
|
145
161
|
}
|
|
146
162
|
function idOf(id) {
|
|
147
163
|
if (id === void 0) {
|
|
@@ -220,13 +236,164 @@ function collectionAccessText(collection) {
|
|
|
220
236
|
return `${String(collection.level)} or more on the ${code(serviceName(collection.anchor))} row the scope names`;
|
|
221
237
|
}
|
|
222
238
|
|
|
239
|
+
// src/cli/dependencies.ts
|
|
240
|
+
var KINDS = ["inherit", "writes", "affects", "scope", "anchor"];
|
|
241
|
+
function isRecord2(value) {
|
|
242
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
243
|
+
}
|
|
244
|
+
function modelKey(model) {
|
|
245
|
+
return `${model.charAt(0).toLowerCase()}${model.slice(1)}`;
|
|
246
|
+
}
|
|
247
|
+
var MAX_POLICY_DEPTH2 = 3;
|
|
248
|
+
function inheritsOf(policy, depth = 0) {
|
|
249
|
+
if (!isRecord2(policy)) {
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
if (policy.kind === "inherit") {
|
|
253
|
+
const from = contractName(policy.from);
|
|
254
|
+
return from === void 0 ? [] : [{ from, via: String(policy.via) }];
|
|
255
|
+
}
|
|
256
|
+
if (policy.kind === "anyOf" && Array.isArray(policy.policies) && depth < MAX_POLICY_DEPTH2) {
|
|
257
|
+
return policy.policies.flatMap((member) => inheritsOf(member, depth + 1));
|
|
258
|
+
}
|
|
259
|
+
return [];
|
|
260
|
+
}
|
|
261
|
+
function scopeOf(form) {
|
|
262
|
+
return isRecord2(form) && typeof form.scope === "string" ? contractName(form.of) : void 0;
|
|
263
|
+
}
|
|
264
|
+
function dependenciesOfService(service, contract, owners) {
|
|
265
|
+
const found = /* @__PURE__ */ new Map();
|
|
266
|
+
const add = (target, kind, text) => {
|
|
267
|
+
if (target === void 0 || target === service.name) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
found.set(target, [...found.get(target) ?? [], { kind, text }]);
|
|
271
|
+
};
|
|
272
|
+
for (const { from, via } of inheritsOf(service.policy)) {
|
|
273
|
+
add(from, "inherit", `row policy: ${code("inherit")} through the ${code(via)} column`);
|
|
274
|
+
}
|
|
275
|
+
const models = [];
|
|
276
|
+
for (const model of service.writes) {
|
|
277
|
+
const serving = owners.get(modelKey(model)) ?? [];
|
|
278
|
+
if (serving.length === 0) {
|
|
279
|
+
models.push(model);
|
|
280
|
+
}
|
|
281
|
+
for (const owner of serving) {
|
|
282
|
+
add(owner, "writes", `${code("writes")}: its ${code(model)} model`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
for (const target of new Set(service.affects)) {
|
|
286
|
+
add(target, "affects", `${code("affects")}: a write to a row here changes its rows`);
|
|
287
|
+
}
|
|
288
|
+
for (const [name, method] of service.methods) {
|
|
289
|
+
add(scopeOf(method.access), "scope", `method ${code(name)}: ${code("{ scope, of }")}`);
|
|
290
|
+
}
|
|
291
|
+
for (const [name, access] of service.channels) {
|
|
292
|
+
add(scopeOf(access), "scope", `channel ${code(name)}: ${code("{ scope, of }")}`);
|
|
293
|
+
}
|
|
294
|
+
for (const [name, stream] of Object.entries(contract?.streams ?? {})) {
|
|
295
|
+
add(scopeOf(stream.access), "scope", `stream ${code(name)}: ${code("{ scope, of }")}`);
|
|
296
|
+
}
|
|
297
|
+
for (const [name, collection] of service.collections) {
|
|
298
|
+
add(
|
|
299
|
+
contractName(collection.anchor),
|
|
300
|
+
"anchor",
|
|
301
|
+
`collection ${code(name)}: anchored on its rows`
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
return {
|
|
305
|
+
services: [...found].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([name, reasons]) => ({
|
|
306
|
+
service: name,
|
|
307
|
+
reasons: KINDS.flatMap((kind) => reasons.filter((reason) => reason.kind === kind))
|
|
308
|
+
})),
|
|
309
|
+
models
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function dependenciesOf(contracts, services) {
|
|
313
|
+
const owners = /* @__PURE__ */ new Map();
|
|
314
|
+
for (const service of services.values()) {
|
|
315
|
+
if (service.model !== void 0) {
|
|
316
|
+
const key = modelKey(service.model);
|
|
317
|
+
owners.set(key, [...owners.get(key) ?? [], service.name]);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
const byName = new Map(contracts.map((contract) => [contract.name, contract]));
|
|
321
|
+
return new Map(
|
|
322
|
+
[...services.values()].map((service) => [
|
|
323
|
+
service.name,
|
|
324
|
+
dependenciesOfService(service, byName.get(service.name), owners)
|
|
325
|
+
])
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
function kindsOf2(dependency) {
|
|
329
|
+
return KINDS.filter((kind) => dependency.reasons.some((reason) => reason.kind === kind));
|
|
330
|
+
}
|
|
331
|
+
function dependsOnSection(dependencies) {
|
|
332
|
+
const { services, models } = dependencies;
|
|
333
|
+
const unserved = models.length === 0 ? [] : [`It also ${code("writes")} models no service's rows live in: ${list(models)}.`];
|
|
334
|
+
if (services.length === 0) {
|
|
335
|
+
return section("## Depends on", ["No other service: its declarations name none.", ...unserved]);
|
|
336
|
+
}
|
|
337
|
+
return section("## Depends on", [
|
|
338
|
+
`The other services its declarations name: its row policy, ${code("writes")}, ${code("affects")}, ${code("{ scope, of }")} access forms and collection anchors. A handler's undeclared reads are not listed.`,
|
|
339
|
+
table(
|
|
340
|
+
["Service", "Declared by"],
|
|
341
|
+
services.map(({ service, reasons }) => [
|
|
342
|
+
code(service),
|
|
343
|
+
reasons.map((reason) => reason.text).join("; ")
|
|
344
|
+
])
|
|
345
|
+
),
|
|
346
|
+
...unserved
|
|
347
|
+
]);
|
|
348
|
+
}
|
|
349
|
+
function nodeIds(names) {
|
|
350
|
+
const ids = /* @__PURE__ */ new Map();
|
|
351
|
+
const taken = /* @__PURE__ */ new Set();
|
|
352
|
+
for (const name of names) {
|
|
353
|
+
const safe = name.replace(/\W/g, "_");
|
|
354
|
+
let id = /^end$/i.test(safe) ? `${safe}_` : safe;
|
|
355
|
+
for (let n = 2; taken.has(id); n += 1) {
|
|
356
|
+
id = `${safe}_${n}`;
|
|
357
|
+
}
|
|
358
|
+
taken.add(id);
|
|
359
|
+
ids.set(name, id);
|
|
360
|
+
}
|
|
361
|
+
return ids;
|
|
362
|
+
}
|
|
363
|
+
function dependencyGraph(dependencies) {
|
|
364
|
+
const edges = [...dependencies].flatMap(
|
|
365
|
+
([from, { services }]) => services.map((dependency) => ({
|
|
366
|
+
from,
|
|
367
|
+
to: dependency.service,
|
|
368
|
+
label: kindsOf2(dependency).join(", ")
|
|
369
|
+
}))
|
|
370
|
+
);
|
|
371
|
+
if (edges.length === 0) {
|
|
372
|
+
return section("## Dependencies", ["No service's declarations name another service."]);
|
|
373
|
+
}
|
|
374
|
+
const names = [...new Set(edges.flatMap(({ from, to }) => [from, to]))].sort();
|
|
375
|
+
const ids = nodeIds(names);
|
|
376
|
+
const id = (name) => ids.get(name) ?? name;
|
|
377
|
+
const nodes = names.filter((name) => id(name) !== name).map((name) => ` ${id(name)}["${name.replace(/"/g, "#quot;")}"]`);
|
|
378
|
+
return section("## Dependencies", [
|
|
379
|
+
`An arrow points from a service to another its declarations name: ${code("inherit")} (its row policy), ${code("writes")} (that service's model), ${code("affects")}, ${code("scope")} (a ${code("{ scope, of }")} access form) or ${code("anchor")} (a collection's anchor). Each service's page lists them.`,
|
|
380
|
+
[
|
|
381
|
+
"```mermaid",
|
|
382
|
+
"graph LR",
|
|
383
|
+
...nodes,
|
|
384
|
+
...edges.map(({ from, to, label }) => ` ${id(from)} -->|${label}| ${id(to)}`),
|
|
385
|
+
"```"
|
|
386
|
+
]
|
|
387
|
+
]);
|
|
388
|
+
}
|
|
389
|
+
|
|
223
390
|
// src/cli/schemaText.ts
|
|
224
391
|
var NO_JSON_SCHEMA = "unknown (no JSON Schema)";
|
|
225
392
|
var TARGET = "draft-07";
|
|
226
393
|
var MAX_DEPTH = 8;
|
|
227
394
|
var IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
|
|
228
395
|
var ROOT = "#";
|
|
229
|
-
function
|
|
396
|
+
function isRecord3(value) {
|
|
230
397
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
231
398
|
}
|
|
232
399
|
function jsonSchemaOf(schema, mode) {
|
|
@@ -235,7 +402,7 @@ function jsonSchemaOf(schema, mode) {
|
|
|
235
402
|
}
|
|
236
403
|
try {
|
|
237
404
|
const json = schema["~standard"].jsonSchema[mode]({ target: TARGET });
|
|
238
|
-
return
|
|
405
|
+
return isRecord3(json) ? json : void 0;
|
|
239
406
|
} catch {
|
|
240
407
|
return void 0;
|
|
241
408
|
}
|
|
@@ -247,8 +414,8 @@ function definitionOf(root, ref) {
|
|
|
247
414
|
}
|
|
248
415
|
const [, group = "", name = ""] = match;
|
|
249
416
|
const definitions = root[group];
|
|
250
|
-
const found =
|
|
251
|
-
return
|
|
417
|
+
const found = isRecord3(definitions) ? definitions[decodeURIComponent(name)] : void 0;
|
|
418
|
+
return isRecord3(found) ? found : void 0;
|
|
252
419
|
}
|
|
253
420
|
function literal(value) {
|
|
254
421
|
return value === void 0 ? "undefined" : JSON.stringify(value);
|
|
@@ -287,13 +454,13 @@ function requiredOf(schema) {
|
|
|
287
454
|
return new Set(Array.isArray(schema.required) ? schema.required.map(String) : []);
|
|
288
455
|
}
|
|
289
456
|
function objectText(schema, scope) {
|
|
290
|
-
const properties =
|
|
457
|
+
const properties = isRecord3(schema.properties) ? schema.properties : {};
|
|
291
458
|
const required = requiredOf(schema);
|
|
292
459
|
const members = Object.entries(properties).map(
|
|
293
460
|
([name, value]) => `${propertyName(name, !required.has(name))}: ${textOf(value, scope)}`
|
|
294
461
|
);
|
|
295
462
|
const extra = schema.additionalProperties;
|
|
296
|
-
if (
|
|
463
|
+
if (isRecord3(extra)) {
|
|
297
464
|
const value = textOf(extra, scope);
|
|
298
465
|
if (members.length === 0) {
|
|
299
466
|
return `Record<string, ${value}>`;
|
|
@@ -351,10 +518,10 @@ function refText(ref, scope) {
|
|
|
351
518
|
return textOf(target, { ...scope, open: [...scope.open, ref] });
|
|
352
519
|
}
|
|
353
520
|
function textOf(schema, scope) {
|
|
354
|
-
if (schema === true ||
|
|
521
|
+
if (schema === true || isRecord3(schema) && Object.keys(schema).length === 0) {
|
|
355
522
|
return "unknown";
|
|
356
523
|
}
|
|
357
|
-
if (!
|
|
524
|
+
if (!isRecord3(schema) || scope.depth > MAX_DEPTH) {
|
|
358
525
|
return schema === false ? "never" : "unknown";
|
|
359
526
|
}
|
|
360
527
|
const inner = { ...scope, depth: scope.depth + 1 };
|
|
@@ -381,7 +548,7 @@ function textOf(schema, scope) {
|
|
|
381
548
|
if (typeof type === "string") {
|
|
382
549
|
return typeNameText(type, schema, inner);
|
|
383
550
|
}
|
|
384
|
-
return
|
|
551
|
+
return isRecord3(schema.properties) ? objectText(schema, inner) : "unknown";
|
|
385
552
|
}
|
|
386
553
|
function schemaText(schema) {
|
|
387
554
|
return textOf(schema, { root: schema, open: [ROOT], depth: 0 });
|
|
@@ -447,7 +614,7 @@ function schemaNotes(schema) {
|
|
|
447
614
|
}
|
|
448
615
|
function schemaFields(schema) {
|
|
449
616
|
const properties = schema.properties;
|
|
450
|
-
if (schema.type !== "object" || !
|
|
617
|
+
if (schema.type !== "object" || !isRecord3(properties) || Object.keys(properties).length === 0) {
|
|
451
618
|
return void 0;
|
|
452
619
|
}
|
|
453
620
|
const required = requiredOf(schema);
|
|
@@ -456,7 +623,7 @@ function schemaFields(schema) {
|
|
|
456
623
|
name,
|
|
457
624
|
optional: !required.has(name),
|
|
458
625
|
type: textOf(value, scope),
|
|
459
|
-
notes:
|
|
626
|
+
notes: isRecord3(value) ? schemaNotes(value) : ""
|
|
460
627
|
}));
|
|
461
628
|
}
|
|
462
629
|
|
|
@@ -547,6 +714,24 @@ function kindLine(method) {
|
|
|
547
714
|
}
|
|
548
715
|
return `A query that watches the ${code(method.watch.collection)} collection: a cached result is fetched again when the scope its input names changes.`;
|
|
549
716
|
}
|
|
717
|
+
function described(text) {
|
|
718
|
+
return text === void 0 ? [] : [paragraph(text)];
|
|
719
|
+
}
|
|
720
|
+
function callableBy(name, access) {
|
|
721
|
+
const method = access?.service?.methods.get(name);
|
|
722
|
+
if (method?.kinds === void 0) {
|
|
723
|
+
return [];
|
|
724
|
+
}
|
|
725
|
+
const anonymous = method.access === "public" ? ", or anonymously" : "";
|
|
726
|
+
return [`- Callable by: ${list(method.kinds)}${anonymous}`];
|
|
727
|
+
}
|
|
728
|
+
function methodFacts(service, name, method, access) {
|
|
729
|
+
return [
|
|
730
|
+
`- MCP tool (default name): ${code(defaultToolName(service, name))}`,
|
|
731
|
+
...method.kind === "query" ? ["- MCP read-only hint: yes, as on every query's tool"] : [],
|
|
732
|
+
...callableBy(name, access)
|
|
733
|
+
];
|
|
734
|
+
}
|
|
550
735
|
function methodAccess(name, access) {
|
|
551
736
|
const method = access?.service?.methods.get(name);
|
|
552
737
|
if (method === void 0) {
|
|
@@ -557,11 +742,11 @@ function methodAccess(name, access) {
|
|
|
557
742
|
] : [];
|
|
558
743
|
return [`Access: ${accessFormText(method.access)}.`, ...rowless];
|
|
559
744
|
}
|
|
560
|
-
function methodSection(name, method, access) {
|
|
561
|
-
const described = method.describe === void 0 ? [] : [paragraph(method.describe)];
|
|
745
|
+
function methodSection(service, name, method, access) {
|
|
562
746
|
return section(`### ${code(name)}`, [
|
|
563
|
-
...described,
|
|
747
|
+
...described(method.describe),
|
|
564
748
|
kindLine(method),
|
|
749
|
+
methodFacts(service, name, method, access),
|
|
565
750
|
...methodAccess(name, access),
|
|
566
751
|
...inputBlocks(method),
|
|
567
752
|
`Output: ${outputText(method.output)}.`
|
|
@@ -575,7 +760,7 @@ function methodsSection(contract, access) {
|
|
|
575
760
|
return [
|
|
576
761
|
"## Methods",
|
|
577
762
|
"",
|
|
578
|
-
...entries.flatMap(([name, method]) => methodSection(name, method, access))
|
|
763
|
+
...entries.flatMap(([name, method]) => methodSection(contract.name, name, method, access))
|
|
579
764
|
];
|
|
580
765
|
}
|
|
581
766
|
function scopeText(scope) {
|
|
@@ -603,6 +788,7 @@ function collectionSection(name, collection, access) {
|
|
|
603
788
|
const maxLimit = collection.maxLimit ?? DEFAULT_COLLECTION_MAX_LIMIT;
|
|
604
789
|
const served = access?.service?.collections.get(name);
|
|
605
790
|
return section(`### ${code(name)}`, [
|
|
791
|
+
...described(collection.describe),
|
|
606
792
|
optionTable([
|
|
607
793
|
["Scope", scopeText(collection.scope)],
|
|
608
794
|
["Item", code(collection.item)],
|
|
@@ -673,6 +859,7 @@ function streamSection(name, stream, access) {
|
|
|
673
859
|
const scoped = isScopedStream(stream);
|
|
674
860
|
const served = access?.service?.streams.get(name);
|
|
675
861
|
return section(`### ${code(name)}`, [
|
|
862
|
+
...described(stream.describe),
|
|
676
863
|
optionTable([
|
|
677
864
|
["Item", code(typeOf(stream.item, "output"))],
|
|
678
865
|
["Scope", scoped ? `one feed per ${code(stream.scope ?? "")}` : "one global feed"],
|
|
@@ -700,6 +887,7 @@ function channelSection(name, channel, access) {
|
|
|
700
887
|
const rate = channel.ratePerSecond ?? CHANNEL_DEFAULT_RATE;
|
|
701
888
|
const served = access?.service?.channels.has(name) === true;
|
|
702
889
|
return section(`### ${code(name)}`, [
|
|
890
|
+
...described(channel.describe),
|
|
703
891
|
optionTable([
|
|
704
892
|
["Payload", code(typeOf(channel.payload, "input"))],
|
|
705
893
|
[
|
|
@@ -717,6 +905,7 @@ function channelSection(name, channel, access) {
|
|
|
717
905
|
}
|
|
718
906
|
function eventSection(name, event) {
|
|
719
907
|
return section(`### ${code(name)}`, [
|
|
908
|
+
...described(event.describe),
|
|
720
909
|
optionTable([["Payload", code(typeOf(event.payload, "output"))]])
|
|
721
910
|
]);
|
|
722
911
|
}
|
|
@@ -745,6 +934,10 @@ function accessSection(contract, access) {
|
|
|
745
934
|
"Service-wide Admin",
|
|
746
935
|
service.adminBypass ? "passes every check of the service" : `passes only the forms that name ${code("service")} (${code("adminBypass: false")})`
|
|
747
936
|
],
|
|
937
|
+
[
|
|
938
|
+
"Principal kinds",
|
|
939
|
+
service.kinds === void 0 ? void 0 : `${list(service.kinds)}: its methods, subscriptions and channels refuse every other kind`
|
|
940
|
+
],
|
|
748
941
|
["Change topic", watchAccessText(service.watchAccess)],
|
|
749
942
|
["Field levels", fieldLevelsText(contract)]
|
|
750
943
|
]),
|
|
@@ -757,7 +950,9 @@ function renderService(contract, access) {
|
|
|
757
950
|
"",
|
|
758
951
|
`# ${contract.name}`,
|
|
759
952
|
"",
|
|
953
|
+
...described(contract.describe).flatMap((text) => [text, ""]),
|
|
760
954
|
...accessSection(contract, access),
|
|
955
|
+
...access?.dependencies === void 0 ? [] : dependsOnSection(access.dependencies),
|
|
761
956
|
...entitySection(contract),
|
|
762
957
|
...projectionsSection(contract),
|
|
763
958
|
...methodsSection(contract, access),
|
|
@@ -787,10 +982,12 @@ function renderService(contract, access) {
|
|
|
787
982
|
function fileOf(contract) {
|
|
788
983
|
return `${contract.name.replace(/[^\w.-]/g, "-")}.md`;
|
|
789
984
|
}
|
|
790
|
-
function renderIndex(contracts) {
|
|
985
|
+
function renderIndex(contracts, dependencies) {
|
|
791
986
|
const count = (members) => String(Object.keys(members).length);
|
|
987
|
+
const hasDescribe = contracts.some((contract) => contract.describe !== void 0);
|
|
792
988
|
const rows = contracts.map((contract) => [
|
|
793
989
|
`[${contract.name}](${fileOf(contract)})`,
|
|
990
|
+
...hasDescribe ? [contract.describe ?? ""] : [],
|
|
794
991
|
count(contract.methods),
|
|
795
992
|
count(contract.collections),
|
|
796
993
|
count(contract.streams),
|
|
@@ -804,9 +1001,21 @@ function renderIndex(contracts) {
|
|
|
804
1001
|
"",
|
|
805
1002
|
"One page per service, generated from its contract by `quickdraw-docs`.",
|
|
806
1003
|
"",
|
|
807
|
-
...table(
|
|
1004
|
+
...table(
|
|
1005
|
+
[
|
|
1006
|
+
"Service",
|
|
1007
|
+
...hasDescribe ? ["Description"] : [],
|
|
1008
|
+
"Methods",
|
|
1009
|
+
"Collections",
|
|
1010
|
+
"Streams",
|
|
1011
|
+
"Channels",
|
|
1012
|
+
"Events"
|
|
1013
|
+
],
|
|
1014
|
+
rows
|
|
1015
|
+
),
|
|
1016
|
+
...dependencies === void 0 ? [] : ["", ...dependencyGraph(dependencies)]
|
|
808
1017
|
];
|
|
809
|
-
return `${lines.join("\n")}
|
|
1018
|
+
return `${lines.join("\n").trimEnd()}
|
|
810
1019
|
`;
|
|
811
1020
|
}
|
|
812
1021
|
|
|
@@ -815,7 +1024,8 @@ var USAGE = `Usage: quickdraw-docs <module> [--services <module>] [--out <dir>]
|
|
|
815
1024
|
|
|
816
1025
|
<module> a module exporting the app's contracts: each one, or a map of them
|
|
817
1026
|
--services <module> a module exporting the services (each, or a list of them): the pages
|
|
818
|
-
then also say who may call each method, from the services' access
|
|
1027
|
+
then also say who may call each method, from the services' access,
|
|
1028
|
+
and which services each depends on, with a graph on the index
|
|
819
1029
|
--out <dir> where the pages go: one per service, plus README.md (default: docs/api)
|
|
820
1030
|
--check write nothing; exit 1 when the pages on disk differ from the contracts
|
|
821
1031
|
`;
|
|
@@ -829,11 +1039,11 @@ var CONTRACT_KEYS = [
|
|
|
829
1039
|
"channels",
|
|
830
1040
|
"events"
|
|
831
1041
|
];
|
|
832
|
-
function
|
|
1042
|
+
function isRecord4(value) {
|
|
833
1043
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
834
1044
|
}
|
|
835
1045
|
function isContract(value) {
|
|
836
|
-
return
|
|
1046
|
+
return isRecord4(value) && Object.isFrozen(value) && typeof value.name === "string" && "entity" in value && CONTRACT_KEYS.every((key) => isRecord4(value[key]));
|
|
837
1047
|
}
|
|
838
1048
|
function contractsOf(exports) {
|
|
839
1049
|
const found = /* @__PURE__ */ new Map();
|
|
@@ -847,7 +1057,7 @@ function contractsOf(exports) {
|
|
|
847
1057
|
for (const value of Object.values(exports)) {
|
|
848
1058
|
if (isContract(value)) {
|
|
849
1059
|
add(value);
|
|
850
|
-
} else if (
|
|
1060
|
+
} else if (isRecord4(value)) {
|
|
851
1061
|
Object.values(value).filter(isContract).forEach(add);
|
|
852
1062
|
}
|
|
853
1063
|
}
|
|
@@ -864,6 +1074,7 @@ function generateDocs(contracts, options = {}) {
|
|
|
864
1074
|
);
|
|
865
1075
|
}
|
|
866
1076
|
}
|
|
1077
|
+
const dependencies = services === void 0 ? void 0 : dependenciesOf(contracts, services);
|
|
867
1078
|
const files = /* @__PURE__ */ new Map();
|
|
868
1079
|
for (const contract of contracts) {
|
|
869
1080
|
const file = fileOf(contract);
|
|
@@ -874,11 +1085,14 @@ function generateDocs(contracts, options = {}) {
|
|
|
874
1085
|
file,
|
|
875
1086
|
renderService(
|
|
876
1087
|
contract,
|
|
877
|
-
services === void 0 ? void 0 : {
|
|
1088
|
+
services === void 0 ? void 0 : {
|
|
1089
|
+
service: services.get(contract.name),
|
|
1090
|
+
dependencies: dependencies?.get(contract.name)
|
|
1091
|
+
}
|
|
878
1092
|
)
|
|
879
1093
|
);
|
|
880
1094
|
}
|
|
881
|
-
files.set(INDEX_FILE, renderIndex(contracts));
|
|
1095
|
+
files.set(INDEX_FILE, renderIndex(contracts, dependencies));
|
|
882
1096
|
return files;
|
|
883
1097
|
}
|
|
884
1098
|
function readText(path) {
|