@nwire/scan 0.9.2 → 0.10.1
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/scan.d.ts +84 -389
- package/dist/scan.js +166 -479
- package/dist/vite-plugin.d.ts +7 -4
- package/dist/vite-plugin.js +6 -14
- package/dist/zod-to-json.d.ts +0 -1
- package/dist/zod-to-json.js +0 -1
- package/package.json +5 -5
- package/dist/__tests__/scan-station-mgmt.test.d.ts +0 -7
- package/dist/__tests__/scan-station-mgmt.test.d.ts.map +0 -1
- package/dist/__tests__/scan-station-mgmt.test.js +0 -34
- package/dist/__tests__/scan-station-mgmt.test.js.map +0 -1
- package/dist/__tests__/scan.test.d.ts +0 -5
- package/dist/__tests__/scan.test.d.ts.map +0 -1
- package/dist/__tests__/scan.test.js +0 -141
- package/dist/__tests__/scan.test.js.map +0 -1
- package/dist/scan.d.ts.map +0 -1
- package/dist/scan.js.map +0 -1
- package/dist/vite-plugin.d.ts.map +0 -1
- package/dist/vite-plugin.js.map +0 -1
- package/dist/zod-to-json.d.ts.map +0 -1
- package/dist/zod-to-json.js.map +0 -1
package/dist/scan.js
CHANGED
|
@@ -1,36 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@nwire/scan` —
|
|
2
|
+
* `@nwire/scan` — produces the `.nwire/` cache by inspecting booted apps.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Each input must be a `ForgeApp` (or any object exposing `appName`,
|
|
5
|
+
* `dispatcher()`, `container`, `runtime`). The scanner walks the
|
|
6
|
+
* dispatcher metadata maps to collect actions, actors, projections,
|
|
7
|
+
* queries, workflows, and external calls, and reads `container.list()`
|
|
8
|
+
* for DI bindings + `runtime.listHooks()` for hooks.
|
|
8
9
|
*
|
|
9
|
-
*
|
|
10
|
+
* Callers are responsible for booting their apps before scanning — the
|
|
11
|
+
* forge plugin populates the dispatcher during `app.start()`.
|
|
10
12
|
*/
|
|
11
|
-
import {
|
|
12
|
-
import { listHooks } from "@nwire/hooks";
|
|
13
|
+
import { writeFile, mkdir } from "node:fs/promises";
|
|
13
14
|
import { resolve } from "node:path";
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
15
|
+
// Pull the ForgeDispatcher off an App in either 0.9 or 0.10 shape. Returns
|
|
16
|
+
// undefined when the App doesn't have forge wired (plain HTTP-only App) —
|
|
17
|
+
// the scanner skips forge-specific collection in that case.
|
|
18
|
+
function resolveDispatcher(app) {
|
|
19
|
+
if (typeof app.dispatcher === "function") {
|
|
20
|
+
try {
|
|
21
|
+
const d = app.dispatcher();
|
|
22
|
+
if (d)
|
|
23
|
+
return d;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
/* fall through */
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (typeof app.container?.resolve === "function") {
|
|
30
|
+
try {
|
|
31
|
+
const d = app.container.resolve("forge.dispatcher");
|
|
32
|
+
if (d)
|
|
33
|
+
return d;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
/* not bound — plain app, no forge */
|
|
37
|
+
}
|
|
29
38
|
}
|
|
30
|
-
return
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
function sourceOf(value) {
|
|
42
|
+
return value?.$source;
|
|
43
|
+
}
|
|
44
|
+
function safeMap(map) {
|
|
45
|
+
return map ?? new Map();
|
|
31
46
|
}
|
|
32
47
|
export function buildCache(apps, options = {}) {
|
|
33
48
|
const out = {
|
|
49
|
+
generatedAt: new Date().toISOString(),
|
|
34
50
|
apps: [],
|
|
35
51
|
modules: [],
|
|
36
52
|
actions: [],
|
|
@@ -47,490 +63,161 @@ export function buildCache(apps, options = {}) {
|
|
|
47
63
|
inboxes: [],
|
|
48
64
|
crons: [],
|
|
49
65
|
commands: [],
|
|
66
|
+
hooks: [],
|
|
50
67
|
plugins: [],
|
|
51
68
|
bindings: [],
|
|
52
69
|
resources: [],
|
|
53
70
|
errors: [],
|
|
54
71
|
middleware: [],
|
|
72
|
+
graph: { events: [] },
|
|
55
73
|
};
|
|
56
|
-
const
|
|
74
|
+
const eventNamesSeen = new Set();
|
|
57
75
|
for (const app of apps) {
|
|
58
|
-
out.apps.push({
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
65
|
-
// Capture plugin + module-as-plugin entries. Modules participate in
|
|
66
|
-
// the same lifecycle as plugins (PluginRegistered { kind: "module" }
|
|
67
|
-
// etc.); we tag them so Studio can render the right affordance.
|
|
68
|
-
for (const module of app.modules) {
|
|
69
|
-
out.plugins.push({
|
|
70
|
-
name: module.name,
|
|
71
|
-
kind: "module",
|
|
72
|
-
app: app.name,
|
|
73
|
-
source: module.$source,
|
|
74
|
-
});
|
|
76
|
+
out.apps.push({ name: app.appName, modules: [] });
|
|
77
|
+
const dispatcher = resolveDispatcher(app);
|
|
78
|
+
if (!dispatcher) {
|
|
79
|
+
// Plain HTTP App with no forge — the scanner has nothing to
|
|
80
|
+
// collect from the runtime side. Continue to the next App.
|
|
81
|
+
continue;
|
|
75
82
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
// ── Actions (via handlers) ──────────────────────────────────────
|
|
84
|
+
const handlers = safeMap(dispatcher.handlers);
|
|
85
|
+
for (const [name, handler] of handlers) {
|
|
86
|
+
const action = handler.action ?? {};
|
|
87
|
+
out.actions.push({
|
|
88
|
+
name,
|
|
89
|
+
app: app.appName,
|
|
90
|
+
public: Boolean(action.$public),
|
|
91
|
+
inputSchema: action.schema,
|
|
92
|
+
source: sourceOf(action),
|
|
82
93
|
});
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
?.container;
|
|
92
|
-
if (ctn?.list) {
|
|
93
|
-
try {
|
|
94
|
-
for (const b of ctn.list()) {
|
|
95
|
-
out.bindings.push({
|
|
96
|
-
name: b.name,
|
|
97
|
-
kind: b.kind,
|
|
98
|
-
app: app.name,
|
|
99
|
-
source: b.source,
|
|
94
|
+
for (const ev of (action.emits ?? [])) {
|
|
95
|
+
if (ev?.name && !eventNamesSeen.has(ev.name)) {
|
|
96
|
+
eventNamesSeen.add(ev.name);
|
|
97
|
+
out.events.push({
|
|
98
|
+
name: ev.name,
|
|
99
|
+
app: app.appName,
|
|
100
|
+
public: Boolean(ev.$public),
|
|
101
|
+
source: sourceOf(ev),
|
|
100
102
|
});
|
|
101
103
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// Defensive: a misbehaving custom container should never break the cache.
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
// Commands ride on the AppDefinition opt-in: modules don't have them
|
|
108
|
-
// (operator scripts aren't domain intent), but apps MAY carry a
|
|
109
|
-
// `commands: CommandDefinition[]` field. Read defensively — most apps
|
|
110
|
-
// won't populate it, and we never want missing-property to crash scan.
|
|
111
|
-
const appCommands = app.commands ?? [];
|
|
112
|
-
for (const command of appCommands) {
|
|
113
|
-
out.commands.push({
|
|
114
|
-
name: command.name,
|
|
115
|
-
describe: command.describe,
|
|
116
|
-
app: app.name,
|
|
117
|
-
hasArgsSchema: command.args !== undefined,
|
|
118
|
-
source: command.$source,
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
// Resolvers as a concept were removed — operations now ship as
|
|
122
|
-
// RouteBinding values wired on `httpInterface`. Scan reads them
|
|
123
|
-
// from the http builder's manifest at boot, not from the app.
|
|
124
|
-
//
|
|
125
|
-
// `mounts` stays as a parameter so callers can still pass route
|
|
126
|
-
// metadata explicitly; it's just no longer derived from
|
|
127
|
-
// `app.resolvers`.
|
|
128
|
-
void mounts;
|
|
129
|
-
void zodToJsonSchema;
|
|
130
|
-
for (const module of app.modules) {
|
|
131
|
-
const m = module.manifest;
|
|
132
|
-
const provides = {
|
|
133
|
-
events: (m.events ?? []).map((e) => e.name),
|
|
134
|
-
actions: (m.actions ?? []).map((a) => a.name),
|
|
135
|
-
};
|
|
136
|
-
const needs = {
|
|
137
|
-
events: (m.needs?.events ?? []).map((e) => e.name),
|
|
138
|
-
externalEvents: (m.needs?.externalEvents ?? []).map((e) => e.name),
|
|
139
|
-
actions: (m.needs?.actions ?? []).map((a) => a.name),
|
|
140
|
-
};
|
|
141
|
-
out.modules.push({
|
|
142
|
-
name: module.name,
|
|
143
|
-
app: app.name,
|
|
144
|
-
provides,
|
|
145
|
-
needs,
|
|
146
|
-
counts: {
|
|
147
|
-
actions: (m.actions ?? []).length,
|
|
148
|
-
actors: (m.actors ?? []).length,
|
|
149
|
-
projections: (m.projections ?? []).length,
|
|
150
|
-
queries: (m.queries ?? []).length,
|
|
151
|
-
workflows: (m.workflows ?? []).length,
|
|
152
|
-
events: (m.events ?? []).length,
|
|
153
|
-
// routes are app-level (httpInterface().wire()); not module-owned.
|
|
154
|
-
routes: 0,
|
|
155
|
-
},
|
|
156
|
-
description: m.description,
|
|
157
|
-
owners: m.owners,
|
|
158
|
-
journey: m.journey,
|
|
159
|
-
source: module.$source,
|
|
160
|
-
});
|
|
161
|
-
for (const action of m.actions ?? []) {
|
|
162
|
-
out.actions.push({
|
|
163
|
-
name: action.name,
|
|
164
|
-
description: action.description,
|
|
165
|
-
module: module.name,
|
|
166
|
-
app: app.name,
|
|
167
|
-
schema: zodToJsonSchema(action.schema),
|
|
168
|
-
retry: action.retry,
|
|
169
|
-
policy: action.policy,
|
|
170
|
-
hasInlineHandler: Boolean(action.handler),
|
|
171
|
-
emits: (action.emits ?? []).map((e) => e.name),
|
|
172
|
-
public: module.publicSurface.actions.has(action.name),
|
|
173
|
-
persona: action.persona,
|
|
174
|
-
journeyStep: action.journeyStep,
|
|
175
|
-
capability: action.capability,
|
|
176
|
-
slo: action.slo,
|
|
177
|
-
tags: action.tags,
|
|
178
|
-
source: action.$source,
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
for (const event of m.events ?? []) {
|
|
182
|
-
out.events.push({
|
|
183
|
-
name: event.name,
|
|
184
|
-
description: event.description,
|
|
185
|
-
module: module.name,
|
|
186
|
-
app: app.name,
|
|
187
|
-
visibility: event.visibility ?? "public",
|
|
188
|
-
public: module.publicSurface.events.has(event.name),
|
|
189
|
-
schema: zodToJsonSchema(event.schema),
|
|
190
|
-
outcome: event.outcome,
|
|
191
|
-
businessWeight: event.businessWeight,
|
|
192
|
-
audience: event.audience,
|
|
193
|
-
source: event.$source,
|
|
194
|
-
});
|
|
195
|
-
}
|
|
196
|
-
for (const actor of m.actors ?? []) {
|
|
197
|
-
out.actors.push({
|
|
198
|
-
name: actor.name,
|
|
199
|
-
module: module.name,
|
|
200
|
-
app: app.name,
|
|
201
|
-
key: actor.key,
|
|
202
|
-
initial: actor.initial,
|
|
203
|
-
states: Object.entries(actor.states).map(([stateName, cfg]) => ({
|
|
204
|
-
name: stateName,
|
|
205
|
-
final: cfg.final,
|
|
206
|
-
on: Object.entries(cfg.on ?? {}).map(([eventName, reaction]) => ({
|
|
207
|
-
eventName,
|
|
208
|
-
target: reaction.target,
|
|
209
|
-
})),
|
|
210
|
-
after: Object.entries(cfg.after ?? {}).map(([timerName, spec]) => ({
|
|
211
|
-
timerName,
|
|
212
|
-
action: typeof spec === "string" ? spec : spec.action,
|
|
213
|
-
delay: typeof spec === "string" ? timerName : spec.delay,
|
|
214
|
-
})),
|
|
215
|
-
})),
|
|
216
|
-
methods: Object.keys(actor.methods ?? {}),
|
|
217
|
-
schema: zodToJsonSchema(actor.schema),
|
|
218
|
-
stuckThresholds: actor.stuckThresholds,
|
|
219
|
-
slas: actor.slas,
|
|
220
|
-
source: actor.$source,
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
for (const projection of m.projections ?? []) {
|
|
224
|
-
out.projections.push({
|
|
225
|
-
name: projection.name,
|
|
226
|
-
module: module.name,
|
|
227
|
-
app: app.name,
|
|
228
|
-
listens: (projection.listens ?? []).map((e) => e.name),
|
|
229
|
-
description: projection.description,
|
|
230
|
-
freshness: projection.freshness,
|
|
231
|
-
source: projection.$source,
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
for (const query of m.queries ?? []) {
|
|
235
|
-
out.queries.push({
|
|
236
|
-
name: query.name,
|
|
237
|
-
description: query.description,
|
|
238
|
-
module: module.name,
|
|
239
|
-
app: app.name,
|
|
240
|
-
projection: query.projection?.name,
|
|
241
|
-
schema: zodToJsonSchema(query.schema),
|
|
242
|
-
public: module.publicSurface.queries.has(query.name),
|
|
243
|
-
slo: query.slo,
|
|
244
|
-
cacheable: query.cacheable,
|
|
245
|
-
source: query.$source,
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
// Routes are no longer module-owned. The interface layer (httpInterface
|
|
249
|
-
// builders + .wire() bindings) lives in the app composition; route
|
|
250
|
-
// emission walks the running wire's introspection mount.
|
|
251
|
-
for (const call of m.externalCalls ?? []) {
|
|
252
|
-
out.externalCalls.push({
|
|
253
|
-
name: call.name,
|
|
254
|
-
description: call.description,
|
|
255
|
-
module: module.name,
|
|
256
|
-
app: app.name,
|
|
257
|
-
target: call.target,
|
|
258
|
-
request: zodToJsonSchema(call.request),
|
|
259
|
-
response: call.response ? zodToJsonSchema(call.response) : undefined,
|
|
260
|
-
hasIdempotencyKey: call.hasIdempotencyKey,
|
|
261
|
-
slo: call.slo,
|
|
262
|
-
retry: call.retry ? { max: call.retry.max, backoff: call.retry.backoff } : undefined,
|
|
263
|
-
tags: call.tags,
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
for (const wh of m.inboundWebhooks ?? []) {
|
|
267
|
-
const routes = {};
|
|
268
|
-
for (const [discValue, action] of Object.entries(wh.routes)) {
|
|
269
|
-
routes[discValue] = action.name;
|
|
104
|
+
if (ev?.name) {
|
|
105
|
+
out.graph.events.push({ from: name, to: ev.name, via: "emits" });
|
|
270
106
|
}
|
|
271
|
-
out.inboundWebhooks.push({
|
|
272
|
-
name: wh.name,
|
|
273
|
-
description: wh.description,
|
|
274
|
-
module: module.name,
|
|
275
|
-
app: app.name,
|
|
276
|
-
source: wh.source,
|
|
277
|
-
path: wh.path,
|
|
278
|
-
hasSignatureVerifier: !!wh.verifySignature,
|
|
279
|
-
dedupe: wh.dedupe ? { window: wh.dedupe.window } : undefined,
|
|
280
|
-
discriminator: wh.discriminator,
|
|
281
|
-
routes,
|
|
282
|
-
tags: wh.tags,
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
for (const ob of m.outboxes ?? []) {
|
|
286
|
-
out.outboxes.push({
|
|
287
|
-
name: ob.name,
|
|
288
|
-
description: ob.description,
|
|
289
|
-
module: module.name,
|
|
290
|
-
app: app.name,
|
|
291
|
-
publishes: ob.publishes.map((e) => e.name),
|
|
292
|
-
flushIntervalMs: ob.flushIntervalMs,
|
|
293
|
-
maxBatch: ob.maxBatch,
|
|
294
|
-
tags: ob.tags,
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
for (const ib of m.inboxes ?? []) {
|
|
298
|
-
out.inboxes.push({
|
|
299
|
-
name: ib.name,
|
|
300
|
-
description: ib.description,
|
|
301
|
-
module: module.name,
|
|
302
|
-
app: app.name,
|
|
303
|
-
window: ib.window,
|
|
304
|
-
on: ib.on.map((a) => a.name),
|
|
305
|
-
tags: ib.tags,
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
for (const cron of m.crons ?? []) {
|
|
309
|
-
out.crons.push({
|
|
310
|
-
name: cron.name,
|
|
311
|
-
description: cron.description,
|
|
312
|
-
module: module.name,
|
|
313
|
-
app: app.name,
|
|
314
|
-
schedule: cron.schedule,
|
|
315
|
-
dispatches: cron.dispatches.name,
|
|
316
|
-
timezone: cron.timezone,
|
|
317
|
-
tags: cron.tags,
|
|
318
|
-
});
|
|
319
107
|
}
|
|
320
108
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
for (const ev of out.events) {
|
|
326
|
-
eventOwners.set(ev.name, { app: ev.app, module: ev.module });
|
|
327
|
-
}
|
|
328
|
-
const edges = new Map();
|
|
329
|
-
const seed = (eventName) => {
|
|
330
|
-
const owner = eventOwners.get(eventName);
|
|
331
|
-
if (!owner)
|
|
332
|
-
return null;
|
|
333
|
-
let e = edges.get(eventName);
|
|
334
|
-
if (!e) {
|
|
335
|
-
e = { event: eventName, producer: owner, consumers: [] };
|
|
336
|
-
edges.set(eventName, e);
|
|
109
|
+
// ── Actors ──────────────────────────────────────────────────────
|
|
110
|
+
const actors = safeMap(dispatcher.actors);
|
|
111
|
+
for (const [name, def] of actors) {
|
|
112
|
+
out.actors.push({ name, app: app.appName, source: sourceOf(def) });
|
|
337
113
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
description: workflow.description,
|
|
351
|
-
subscribesTo: [...workflow.subscribedEvents],
|
|
352
|
-
dispatches: [...workflow.dispatchedActions],
|
|
353
|
-
public: module.publicSurface.workflows.has(workflow.name),
|
|
354
|
-
source: workflow.$source,
|
|
355
|
-
});
|
|
356
|
-
for (const eventName of workflow.subscribedEvents) {
|
|
357
|
-
const edge = seed(eventName);
|
|
358
|
-
if (!edge)
|
|
359
|
-
continue;
|
|
360
|
-
edge.consumers.push({
|
|
361
|
-
app: app.name,
|
|
362
|
-
module: module.name,
|
|
363
|
-
via: "workflow",
|
|
114
|
+
// ── Projections + listened events ──────────────────────────────
|
|
115
|
+
const projections = safeMap(dispatcher.projections);
|
|
116
|
+
for (const [name, def] of projections) {
|
|
117
|
+
out.projections.push({ name, app: app.appName, source: sourceOf(def) });
|
|
118
|
+
for (const ev of (def.listens ?? [])) {
|
|
119
|
+
if (ev?.name && !eventNamesSeen.has(ev.name)) {
|
|
120
|
+
eventNamesSeen.add(ev.name);
|
|
121
|
+
out.events.push({
|
|
122
|
+
name: ev.name,
|
|
123
|
+
app: app.appName,
|
|
124
|
+
public: Boolean(ev.$public),
|
|
125
|
+
source: sourceOf(ev),
|
|
364
126
|
});
|
|
365
127
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
for (const actor of module.manifest.actors ?? []) {
|
|
369
|
-
for (const [stateName, cfg] of Object.entries(actor.states)) {
|
|
370
|
-
for (const eventName of Object.keys(cfg.on ?? {})) {
|
|
371
|
-
const edge = seed(eventName);
|
|
372
|
-
if (!edge)
|
|
373
|
-
continue;
|
|
374
|
-
edge.consumers.push({
|
|
375
|
-
app: app.name,
|
|
376
|
-
module: module.name,
|
|
377
|
-
via: "actor",
|
|
378
|
-
});
|
|
379
|
-
}
|
|
128
|
+
if (ev?.name) {
|
|
129
|
+
out.graph.events.push({ from: ev.name, to: name, via: "folds" });
|
|
380
130
|
}
|
|
381
131
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
132
|
+
}
|
|
133
|
+
// ── Queries ─────────────────────────────────────────────────────
|
|
134
|
+
const queries = safeMap(dispatcher.queries);
|
|
135
|
+
for (const [name, def] of queries) {
|
|
136
|
+
out.queries.push({
|
|
137
|
+
name,
|
|
138
|
+
app: app.appName,
|
|
139
|
+
public: Boolean(def.$public),
|
|
140
|
+
source: sourceOf(def),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// ── Workflows + subscribed/dispatched edges ────────────────────
|
|
144
|
+
const workflowDefs = dispatcher.listWorkflows?.() ?? [];
|
|
145
|
+
for (const def of workflowDefs) {
|
|
146
|
+
const name = String(def.name);
|
|
147
|
+
out.workflows.push({
|
|
148
|
+
name,
|
|
149
|
+
app: app.appName,
|
|
150
|
+
public: Boolean(def.$public),
|
|
151
|
+
source: sourceOf(def),
|
|
152
|
+
});
|
|
153
|
+
for (const evName of (def.subscribedEvents ?? new Set())) {
|
|
154
|
+
out.graph.events.push({ from: evName, to: name, via: "subscribes" });
|
|
394
155
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
const edge = seed(ev.name);
|
|
398
|
-
if (!edge)
|
|
399
|
-
continue;
|
|
400
|
-
edge.consumers.push({
|
|
401
|
-
app: app.name,
|
|
402
|
-
module: module.name,
|
|
403
|
-
via: "external",
|
|
404
|
-
});
|
|
156
|
+
for (const actName of (def.dispatchedActions ?? new Set())) {
|
|
157
|
+
out.graph.events.push({ from: name, to: actName, via: "dispatches" });
|
|
405
158
|
}
|
|
406
159
|
}
|
|
160
|
+
// ── External calls ─────────────────────────────────────────────
|
|
161
|
+
const externalCalls = safeMap(dispatcher.externalCalls);
|
|
162
|
+
for (const [name, def] of externalCalls) {
|
|
163
|
+
out.externalCalls.push({ name, app: app.appName, source: sourceOf(def) });
|
|
164
|
+
}
|
|
165
|
+
// ── DI bindings ────────────────────────────────────────────────
|
|
166
|
+
const bindings = app.container.list?.() ?? [];
|
|
167
|
+
for (const b of bindings) {
|
|
168
|
+
out.bindings.push({
|
|
169
|
+
name: b.name,
|
|
170
|
+
kind: (b.kind === "singleton" || b.kind === "transient" ? b.kind : "singleton"),
|
|
171
|
+
app: app.appName,
|
|
172
|
+
source: b.source,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
// ── Hooks ──────────────────────────────────────────────────────
|
|
176
|
+
const hooks = app.runtime?.listHooks?.() ?? [];
|
|
177
|
+
for (const h of hooks) {
|
|
178
|
+
out.hooks.push({
|
|
179
|
+
id: h.id,
|
|
180
|
+
name: h.name,
|
|
181
|
+
chain: h.chain,
|
|
182
|
+
listeners: h.listeners,
|
|
183
|
+
source: h.source,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
407
186
|
}
|
|
408
|
-
//
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
for (const input of options.resources ?? []) {
|
|
414
|
-
const tagged = unwrapInput(input);
|
|
415
|
-
const d = tagged.definition;
|
|
416
|
-
out.resources.push({
|
|
417
|
-
name: d.name,
|
|
418
|
-
summary: d.summary,
|
|
419
|
-
description: d.description,
|
|
420
|
-
app: tagged.app,
|
|
421
|
-
module: tagged.module,
|
|
422
|
-
public: d.public ?? [],
|
|
423
|
-
schema: d.schema ? zodToJsonSchema(d.schema) : {},
|
|
424
|
-
exampleVariants: Object.keys(d.examples ?? {}),
|
|
425
|
-
audience: d.audience,
|
|
426
|
-
source: d.$source,
|
|
427
|
-
});
|
|
187
|
+
// ── Routes (from passed-in interfaces) ────────────────────────────
|
|
188
|
+
for (const iface of options.interfaces ?? []) {
|
|
189
|
+
for (const r of iface.listRoutes?.() ?? []) {
|
|
190
|
+
out.routes.push({ method: r.method, path: r.path, action: r.action });
|
|
191
|
+
}
|
|
428
192
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
out.errors.push({
|
|
433
|
-
code: d.code,
|
|
434
|
-
status: d.status,
|
|
435
|
-
summary: d.summary,
|
|
436
|
-
description: d.description,
|
|
437
|
-
app: tagged.app,
|
|
438
|
-
module: tagged.module,
|
|
439
|
-
tags: d.tags,
|
|
440
|
-
source: d.$source,
|
|
441
|
-
});
|
|
193
|
+
// ── Resolver mounts (operator-supplied) ──────────────────────────
|
|
194
|
+
for (const m of options.mounts ?? []) {
|
|
195
|
+
out.resolvers.push({ name: m.resolverName, app: "" });
|
|
442
196
|
}
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
// stay consistent across the static + live views.
|
|
450
|
-
name: d.name ?? "anonymous",
|
|
451
|
-
app: tagged.app,
|
|
452
|
-
module: tagged.module,
|
|
453
|
-
where: tagged.where,
|
|
454
|
-
source: d.$source,
|
|
455
|
-
});
|
|
197
|
+
// ── Resources / errors / middleware (operator-supplied) ──────────
|
|
198
|
+
for (const r of options.resources ?? []) {
|
|
199
|
+
if ("definition" in r) {
|
|
200
|
+
const def = r.definition;
|
|
201
|
+
out.resources.push({ name: String(def?.name ?? ""), app: r.app, module: r.module, source: sourceOf(def) });
|
|
202
|
+
}
|
|
456
203
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
for (const r of iface.listRoutes()) {
|
|
462
|
-
out.routes.push({
|
|
463
|
-
method: r.verb.toUpperCase(),
|
|
464
|
-
path: r.path,
|
|
465
|
-
source: r.source,
|
|
466
|
-
});
|
|
204
|
+
for (const e of options.errors ?? []) {
|
|
205
|
+
if ("definition" in e) {
|
|
206
|
+
const def = e.definition;
|
|
207
|
+
out.errors.push({ code: String(def?.code ?? ""), app: e.app, module: e.module, source: sourceOf(def) });
|
|
467
208
|
}
|
|
468
209
|
}
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
resolvers: out.resolvers,
|
|
479
|
-
routes: out.routes,
|
|
480
|
-
workflows: out.workflows,
|
|
481
|
-
externalCalls: out.externalCalls,
|
|
482
|
-
inboundWebhooks: out.inboundWebhooks,
|
|
483
|
-
outboxes: out.outboxes,
|
|
484
|
-
inboxes: out.inboxes,
|
|
485
|
-
crons: out.crons,
|
|
486
|
-
commands: out.commands,
|
|
487
|
-
plugins: out.plugins,
|
|
488
|
-
// Snapshot every hook registered up to this point. `listHooks` walks
|
|
489
|
-
// the @nwire/hooks process-wide registry; runtime construction +
|
|
490
|
-
// plugin register + module wiring all run during the caller's
|
|
491
|
-
// `import(apps)`, so the snapshot is comprehensive at scan time.
|
|
492
|
-
hooks: listHooks().map((h) => ({
|
|
493
|
-
id: h.id,
|
|
494
|
-
name: h.name,
|
|
495
|
-
chain: h.chain,
|
|
496
|
-
listeners: h.listeners,
|
|
497
|
-
source: h.source,
|
|
498
|
-
})),
|
|
499
|
-
bindings: out.bindings,
|
|
500
|
-
resources: out.resources,
|
|
501
|
-
errors: out.errors,
|
|
502
|
-
middleware: out.middleware,
|
|
503
|
-
graph: { events: Array.from(edges.values()) },
|
|
504
|
-
};
|
|
210
|
+
for (const m of options.middleware ?? []) {
|
|
211
|
+
if ("definition" in m) {
|
|
212
|
+
out.middleware.push({ name: m.definition.name, where: m.where });
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
out.middleware.push({ name: m.name });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return out;
|
|
505
219
|
}
|
|
506
220
|
export async function writeCache(cache, dir) {
|
|
507
221
|
await mkdir(dir, { recursive: true });
|
|
508
|
-
await
|
|
509
|
-
writeFile(resolve(dir, "manifest.json"), JSON.stringify(cache, null, 2)),
|
|
510
|
-
writeFile(resolve(dir, "actions.json"), JSON.stringify(cache.actions, null, 2)),
|
|
511
|
-
writeFile(resolve(dir, "events.json"), JSON.stringify(cache.events, null, 2)),
|
|
512
|
-
writeFile(resolve(dir, "actors.json"), JSON.stringify(cache.actors, null, 2)),
|
|
513
|
-
writeFile(resolve(dir, "projections.json"), JSON.stringify(cache.projections, null, 2)),
|
|
514
|
-
writeFile(resolve(dir, "queries.json"), JSON.stringify(cache.queries, null, 2)),
|
|
515
|
-
writeFile(resolve(dir, "modules.json"), JSON.stringify(cache.modules, null, 2)),
|
|
516
|
-
writeFile(resolve(dir, "apps.json"), JSON.stringify(cache.apps, null, 2)),
|
|
517
|
-
writeFile(resolve(dir, "resolvers.json"), JSON.stringify(cache.resolvers, null, 2)),
|
|
518
|
-
writeFile(resolve(dir, "routes.json"), JSON.stringify(cache.routes, null, 2)),
|
|
519
|
-
writeFile(resolve(dir, "workflows.json"), JSON.stringify(cache.workflows, null, 2)),
|
|
520
|
-
writeFile(resolve(dir, "external-calls.json"), JSON.stringify(cache.externalCalls, null, 2)),
|
|
521
|
-
writeFile(resolve(dir, "inbound-webhooks.json"), JSON.stringify(cache.inboundWebhooks, null, 2)),
|
|
522
|
-
writeFile(resolve(dir, "outboxes.json"), JSON.stringify(cache.outboxes, null, 2)),
|
|
523
|
-
writeFile(resolve(dir, "inboxes.json"), JSON.stringify(cache.inboxes, null, 2)),
|
|
524
|
-
writeFile(resolve(dir, "crons.json"), JSON.stringify(cache.crons, null, 2)),
|
|
525
|
-
writeFile(resolve(dir, "commands.json"), JSON.stringify(cache.commands, null, 2)),
|
|
526
|
-
writeFile(resolve(dir, "hooks.json"), JSON.stringify(cache.hooks, null, 2)),
|
|
527
|
-
writeFile(resolve(dir, "plugins.json"), JSON.stringify(cache.plugins, null, 2)),
|
|
528
|
-
writeFile(resolve(dir, "di.json"), JSON.stringify(cache.bindings, null, 2)),
|
|
529
|
-
writeFile(resolve(dir, "models.json"), JSON.stringify(cache.resources, null, 2)),
|
|
530
|
-
writeFile(resolve(dir, "errors.json"), JSON.stringify(cache.errors, null, 2)),
|
|
531
|
-
writeFile(resolve(dir, "middleware.json"), JSON.stringify(cache.middleware, null, 2)),
|
|
532
|
-
writeFile(resolve(dir, "graph.json"), JSON.stringify(cache.graph, null, 2)),
|
|
533
|
-
]);
|
|
222
|
+
await writeFile(resolve(dir, "manifest.json"), JSON.stringify(cache, null, 2));
|
|
534
223
|
}
|
|
535
|
-
export { zodToJsonSchema };
|
|
536
|
-
//# sourceMappingURL=scan.js.map
|