@nwire/scan 0.10.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/scan.d.ts +65 -13
- package/dist/scan.js +183 -23
- package/package.json +5 -5
package/dist/scan.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@nwire/scan` — produces the `.nwire/` cache by inspecting booted apps.
|
|
3
3
|
*
|
|
4
|
-
* Each input
|
|
5
|
-
* `
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Each input is an `App` (or any object exposing `appName`, `container`,
|
|
5
|
+
* `runtime`). When forge is installed, the scanner walks the dispatcher
|
|
6
|
+
* metadata maps to collect actions, actors, projections, queries,
|
|
7
|
+
* workflows, and external calls. It also reads `container.list()` for
|
|
8
|
+
* DI bindings and `runtime.listHooks()` for hooks.
|
|
9
9
|
*
|
|
10
10
|
* Callers are responsible for booting their apps before scanning — the
|
|
11
11
|
* forge plugin populates the dispatcher during `app.start()`.
|
|
@@ -18,9 +18,28 @@ export interface SourceLocationEntry {
|
|
|
18
18
|
export interface ActionEntry {
|
|
19
19
|
readonly name: string;
|
|
20
20
|
readonly app: string;
|
|
21
|
-
readonly
|
|
21
|
+
readonly description?: string;
|
|
22
22
|
readonly public: boolean;
|
|
23
23
|
readonly inputSchema?: unknown;
|
|
24
|
+
/** Event names this action emits, in declaration order. */
|
|
25
|
+
readonly emits: readonly string[];
|
|
26
|
+
/** True when a handler was wired via defineAction({handler}) — false for split definitions. */
|
|
27
|
+
readonly hasInlineHandler: boolean;
|
|
28
|
+
readonly persona?: string;
|
|
29
|
+
readonly journeyStep?: string;
|
|
30
|
+
readonly capability?: string;
|
|
31
|
+
readonly slo?: {
|
|
32
|
+
p95LatencyMs?: number;
|
|
33
|
+
successRate?: number;
|
|
34
|
+
};
|
|
35
|
+
readonly retry?: {
|
|
36
|
+
max: number;
|
|
37
|
+
backoff?: string;
|
|
38
|
+
baseDelayMs?: number;
|
|
39
|
+
maxDelayMs?: number;
|
|
40
|
+
};
|
|
41
|
+
readonly policy?: string | readonly string[] | readonly [action: string, subject: string];
|
|
42
|
+
readonly tags?: readonly string[];
|
|
24
43
|
readonly source?: SourceLocationEntry;
|
|
25
44
|
}
|
|
26
45
|
export interface ExternalCallEntry {
|
|
@@ -58,42 +77,62 @@ export interface WorkflowEntry {
|
|
|
58
77
|
readonly name: string;
|
|
59
78
|
readonly app: string;
|
|
60
79
|
readonly public: boolean;
|
|
80
|
+
readonly description?: string;
|
|
81
|
+
/** Event names this workflow listens to. */
|
|
82
|
+
readonly subscribesTo: readonly string[];
|
|
83
|
+
/** Action names this workflow dispatches inside its body. */
|
|
84
|
+
readonly dispatches: readonly string[];
|
|
61
85
|
readonly source?: SourceLocationEntry;
|
|
62
86
|
}
|
|
63
87
|
export interface EventEntry {
|
|
64
88
|
readonly name: string;
|
|
65
89
|
readonly app: string;
|
|
66
90
|
readonly public: boolean;
|
|
91
|
+
readonly description?: string;
|
|
92
|
+
readonly version?: number;
|
|
93
|
+
/** Free-form labels for audience filtering ("product", "analytics", "partner-billing"). */
|
|
94
|
+
readonly audience?: readonly string[];
|
|
67
95
|
readonly source?: SourceLocationEntry;
|
|
68
96
|
}
|
|
69
97
|
export interface ActorEntry {
|
|
70
98
|
readonly name: string;
|
|
71
99
|
readonly app: string;
|
|
100
|
+
/** State names declared in the actor's `states` map. */
|
|
101
|
+
readonly states: readonly string[];
|
|
72
102
|
readonly source?: SourceLocationEntry;
|
|
73
103
|
}
|
|
74
104
|
export interface ProjectionEntry {
|
|
75
105
|
readonly name: string;
|
|
76
106
|
readonly app: string;
|
|
107
|
+
readonly description?: string;
|
|
108
|
+
/** Event names this projection folds in via its `listens` array. */
|
|
109
|
+
readonly listens: readonly string[];
|
|
77
110
|
readonly source?: SourceLocationEntry;
|
|
78
111
|
}
|
|
79
112
|
export interface QueryEntry {
|
|
80
113
|
readonly name: string;
|
|
81
114
|
readonly app: string;
|
|
82
115
|
readonly public: boolean;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
export interface ModuleEntry {
|
|
86
|
-
readonly name: string;
|
|
87
|
-
readonly app: string;
|
|
116
|
+
/** Name of the projection this query reads from (projection-form queries only). */
|
|
117
|
+
readonly projection?: string;
|
|
88
118
|
readonly source?: SourceLocationEntry;
|
|
89
119
|
}
|
|
90
120
|
export interface AppEntry {
|
|
91
121
|
readonly name: string;
|
|
92
122
|
readonly description?: string;
|
|
93
|
-
|
|
123
|
+
/** Plugin names installed on this app, in install order. */
|
|
124
|
+
readonly plugins: readonly string[];
|
|
94
125
|
readonly tenantModel?: string;
|
|
95
126
|
readonly tenantKey?: string;
|
|
96
127
|
}
|
|
128
|
+
export interface SinkEntry {
|
|
129
|
+
readonly name: string;
|
|
130
|
+
readonly app: string;
|
|
131
|
+
/** Adapter kind tag — "bullmq", "nats", "capture", etc. */
|
|
132
|
+
readonly kind?: string;
|
|
133
|
+
readonly position: "early" | "middle" | "terminal";
|
|
134
|
+
readonly direction: "outbound";
|
|
135
|
+
}
|
|
97
136
|
export interface RouteEntry {
|
|
98
137
|
readonly method: string;
|
|
99
138
|
readonly path: string;
|
|
@@ -161,7 +200,6 @@ export interface EventGraphEdge {
|
|
|
161
200
|
export interface Cache {
|
|
162
201
|
readonly generatedAt: string;
|
|
163
202
|
readonly apps: readonly AppEntry[];
|
|
164
|
-
readonly modules: readonly ModuleEntry[];
|
|
165
203
|
readonly actions: readonly ActionEntry[];
|
|
166
204
|
readonly events: readonly EventEntry[];
|
|
167
205
|
readonly actors: readonly ActorEntry[];
|
|
@@ -178,6 +216,7 @@ export interface Cache {
|
|
|
178
216
|
readonly commands: readonly CommandEntry[];
|
|
179
217
|
readonly hooks: readonly HookEntry[];
|
|
180
218
|
readonly plugins: readonly PluginEntry[];
|
|
219
|
+
readonly sinks: readonly SinkEntry[];
|
|
181
220
|
readonly bindings: readonly DIBindingEntry[];
|
|
182
221
|
readonly resources: readonly ResourceEntry[];
|
|
183
222
|
readonly errors: readonly ErrorEntry[];
|
|
@@ -221,7 +260,13 @@ export interface BuildCacheOptions {
|
|
|
221
260
|
}
|
|
222
261
|
interface ScannedApp {
|
|
223
262
|
readonly appName: string;
|
|
263
|
+
readonly description?: string;
|
|
224
264
|
dispatcher?: () => any;
|
|
265
|
+
/** Plugin definitions installed via createApp({plugins}) or .with(). */
|
|
266
|
+
readonly plugins?: readonly {
|
|
267
|
+
name: string;
|
|
268
|
+
$source?: SourceLocationEntry;
|
|
269
|
+
}[];
|
|
225
270
|
container: {
|
|
226
271
|
resolve?: <T = any>(name: string) => T;
|
|
227
272
|
list?(): readonly {
|
|
@@ -238,6 +283,13 @@ interface ScannedApp {
|
|
|
238
283
|
listeners: number;
|
|
239
284
|
source?: SourceLocationEntry;
|
|
240
285
|
}[];
|
|
286
|
+
listSinkStages?(): readonly {
|
|
287
|
+
name: string;
|
|
288
|
+
kind?: string;
|
|
289
|
+
position: "early" | "middle" | "terminal";
|
|
290
|
+
direction?: "outbound";
|
|
291
|
+
}[];
|
|
292
|
+
hooks?: Record<string, any>;
|
|
241
293
|
};
|
|
242
294
|
}
|
|
243
295
|
export declare function buildCache(apps: readonly ScannedApp[], options?: BuildCacheOptions): Cache;
|
package/dist/scan.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `@nwire/scan` — produces the `.nwire/` cache by inspecting booted apps.
|
|
3
3
|
*
|
|
4
|
-
* Each input
|
|
5
|
-
* `
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Each input is an `App` (or any object exposing `appName`, `container`,
|
|
5
|
+
* `runtime`). When forge is installed, the scanner walks the dispatcher
|
|
6
|
+
* metadata maps to collect actions, actors, projections, queries,
|
|
7
|
+
* workflows, and external calls. It also reads `container.list()` for
|
|
8
|
+
* DI bindings and `runtime.listHooks()` for hooks.
|
|
9
9
|
*
|
|
10
10
|
* Callers are responsible for booting their apps before scanning — the
|
|
11
11
|
* forge plugin populates the dispatcher during `app.start()`.
|
|
@@ -41,6 +41,70 @@ function resolveDispatcher(app) {
|
|
|
41
41
|
function sourceOf(value) {
|
|
42
42
|
return value?.$source;
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Coerce a Zod schema into a JSON-Schema-shaped `{ type, properties,
|
|
46
|
+
* required }` value Studio's form renderer + SchemaTree understand. The
|
|
47
|
+
* scanner doesn't carry a Zod dep, so we walk the runtime shape Zod 4
|
|
48
|
+
* publishes (`def.type`, `def.shape`, `def.innerType`, etc.) and project
|
|
49
|
+
* the bits Studio needs.
|
|
50
|
+
*
|
|
51
|
+
* Falls back to the raw value when the shape is unrecognisable so the
|
|
52
|
+
* SchemaTree's "raw JSON" view still shows something.
|
|
53
|
+
*/
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
55
|
+
function zodToJsonSchema(schema) {
|
|
56
|
+
if (!schema || typeof schema !== "object")
|
|
57
|
+
return schema;
|
|
58
|
+
const s = schema;
|
|
59
|
+
const def = (s.def ?? s._def ?? {});
|
|
60
|
+
const type = String(def.type ?? "");
|
|
61
|
+
switch (type) {
|
|
62
|
+
case "object": {
|
|
63
|
+
const shape = (def.shape ?? {});
|
|
64
|
+
const properties = {};
|
|
65
|
+
const required = [];
|
|
66
|
+
for (const [key, raw] of Object.entries(shape)) {
|
|
67
|
+
const field = raw;
|
|
68
|
+
const inner = zodToJsonSchema(field);
|
|
69
|
+
properties[key] = inner;
|
|
70
|
+
// A field is required when its def isn't `optional`/`nullable`.
|
|
71
|
+
const innerType = (field.def ?? {}).type;
|
|
72
|
+
if (innerType !== "optional" && innerType !== "nullable") {
|
|
73
|
+
required.push(key);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return { type: "object", properties, required };
|
|
77
|
+
}
|
|
78
|
+
case "string":
|
|
79
|
+
return { type: "string" };
|
|
80
|
+
case "number":
|
|
81
|
+
return { type: "number" };
|
|
82
|
+
case "boolean":
|
|
83
|
+
return { type: "boolean" };
|
|
84
|
+
case "literal":
|
|
85
|
+
return { type: typeof def.value, enum: [def.value] };
|
|
86
|
+
case "enum": {
|
|
87
|
+
const opts = (def.entries ?? def.values ?? []);
|
|
88
|
+
return { type: "string", enum: opts };
|
|
89
|
+
}
|
|
90
|
+
case "array":
|
|
91
|
+
return { type: "array", items: zodToJsonSchema(def.element) };
|
|
92
|
+
case "optional":
|
|
93
|
+
case "nullable":
|
|
94
|
+
case "default":
|
|
95
|
+
return zodToJsonSchema(def.innerType);
|
|
96
|
+
case "union": {
|
|
97
|
+
const opts = (def.options ?? []);
|
|
98
|
+
return { anyOf: opts.map((o) => zodToJsonSchema(o)) };
|
|
99
|
+
}
|
|
100
|
+
case "record":
|
|
101
|
+
return { type: "object", additionalProperties: zodToJsonSchema(def.valueType) };
|
|
102
|
+
default:
|
|
103
|
+
// Unrecognised — return the raw shape so the SchemaTree at least
|
|
104
|
+
// shows the JSON, even if the form renderer can't drive it.
|
|
105
|
+
return schema;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
44
108
|
function safeMap(map) {
|
|
45
109
|
return map ?? new Map();
|
|
46
110
|
}
|
|
@@ -48,7 +112,6 @@ export function buildCache(apps, options = {}) {
|
|
|
48
112
|
const out = {
|
|
49
113
|
generatedAt: new Date().toISOString(),
|
|
50
114
|
apps: [],
|
|
51
|
-
modules: [],
|
|
52
115
|
actions: [],
|
|
53
116
|
events: [],
|
|
54
117
|
actors: [],
|
|
@@ -65,6 +128,7 @@ export function buildCache(apps, options = {}) {
|
|
|
65
128
|
commands: [],
|
|
66
129
|
hooks: [],
|
|
67
130
|
plugins: [],
|
|
131
|
+
sinks: [],
|
|
68
132
|
bindings: [],
|
|
69
133
|
resources: [],
|
|
70
134
|
errors: [],
|
|
@@ -73,7 +137,32 @@ export function buildCache(apps, options = {}) {
|
|
|
73
137
|
};
|
|
74
138
|
const eventNamesSeen = new Set();
|
|
75
139
|
for (const app of apps) {
|
|
76
|
-
|
|
140
|
+
const pluginNames = (app.plugins ?? []).map((p) => p.name);
|
|
141
|
+
out.apps.push({
|
|
142
|
+
name: app.appName,
|
|
143
|
+
description: app.description,
|
|
144
|
+
plugins: pluginNames,
|
|
145
|
+
});
|
|
146
|
+
// ── Plugins ─────────────────────────────────────────────────────
|
|
147
|
+
for (const plugin of app.plugins ?? []) {
|
|
148
|
+
out.plugins.push({
|
|
149
|
+
name: plugin.name,
|
|
150
|
+
kind: "plugin",
|
|
151
|
+
app: app.appName,
|
|
152
|
+
source: plugin.$source,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
// ── Sink stages (outbound) ─────────────────────────────────────
|
|
156
|
+
const sinkStages = app.runtime?.listSinkStages?.() ?? [];
|
|
157
|
+
for (const stage of sinkStages) {
|
|
158
|
+
out.sinks.push({
|
|
159
|
+
name: stage.name,
|
|
160
|
+
app: app.appName,
|
|
161
|
+
kind: stage.kind,
|
|
162
|
+
position: stage.position,
|
|
163
|
+
direction: stage.direction ?? "outbound",
|
|
164
|
+
});
|
|
165
|
+
}
|
|
77
166
|
const dispatcher = resolveDispatcher(app);
|
|
78
167
|
if (!dispatcher) {
|
|
79
168
|
// Plain HTTP App with no forge — the scanner has nothing to
|
|
@@ -84,20 +173,34 @@ export function buildCache(apps, options = {}) {
|
|
|
84
173
|
const handlers = safeMap(dispatcher.handlers);
|
|
85
174
|
for (const [name, handler] of handlers) {
|
|
86
175
|
const action = handler.action ?? {};
|
|
176
|
+
const emits = (action.emits ?? []);
|
|
87
177
|
out.actions.push({
|
|
88
178
|
name,
|
|
89
179
|
app: app.appName,
|
|
180
|
+
description: typeof action.description === "string" ? action.description : undefined,
|
|
90
181
|
public: Boolean(action.$public),
|
|
91
|
-
inputSchema: action.schema,
|
|
182
|
+
inputSchema: zodToJsonSchema(action.schema),
|
|
183
|
+
emits: emits.map((e) => String(e?.name ?? "")).filter(Boolean),
|
|
184
|
+
hasInlineHandler: typeof action.handler === "object" && action.handler !== null,
|
|
185
|
+
persona: typeof action.persona === "string" ? action.persona : undefined,
|
|
186
|
+
journeyStep: typeof action.journeyStep === "string" ? action.journeyStep : undefined,
|
|
187
|
+
capability: typeof action.capability === "string" ? action.capability : undefined,
|
|
188
|
+
slo: action.slo,
|
|
189
|
+
retry: action.retry,
|
|
190
|
+
policy: action.policy,
|
|
191
|
+
tags: action.tags,
|
|
92
192
|
source: sourceOf(action),
|
|
93
193
|
});
|
|
94
|
-
for (const ev of
|
|
194
|
+
for (const ev of emits) {
|
|
95
195
|
if (ev?.name && !eventNamesSeen.has(ev.name)) {
|
|
96
196
|
eventNamesSeen.add(ev.name);
|
|
97
197
|
out.events.push({
|
|
98
198
|
name: ev.name,
|
|
99
199
|
app: app.appName,
|
|
100
200
|
public: Boolean(ev.$public),
|
|
201
|
+
description: typeof ev.description === "string" ? ev.description : undefined,
|
|
202
|
+
version: typeof ev.version === "number" ? ev.version : undefined,
|
|
203
|
+
audience: Array.isArray(ev.audience) ? ev.audience : undefined,
|
|
101
204
|
source: sourceOf(ev),
|
|
102
205
|
});
|
|
103
206
|
}
|
|
@@ -109,19 +212,37 @@ export function buildCache(apps, options = {}) {
|
|
|
109
212
|
// ── Actors ──────────────────────────────────────────────────────
|
|
110
213
|
const actors = safeMap(dispatcher.actors);
|
|
111
214
|
for (const [name, def] of actors) {
|
|
112
|
-
|
|
215
|
+
const stateNames = def.states && typeof def.states === "object"
|
|
216
|
+
? Object.keys(def.states)
|
|
217
|
+
: [];
|
|
218
|
+
out.actors.push({
|
|
219
|
+
name,
|
|
220
|
+
app: app.appName,
|
|
221
|
+
states: stateNames,
|
|
222
|
+
source: sourceOf(def),
|
|
223
|
+
});
|
|
113
224
|
}
|
|
114
225
|
// ── Projections + listened events ──────────────────────────────
|
|
115
226
|
const projections = safeMap(dispatcher.projections);
|
|
116
227
|
for (const [name, def] of projections) {
|
|
117
|
-
|
|
118
|
-
|
|
228
|
+
const listens = (def.listens ?? []);
|
|
229
|
+
out.projections.push({
|
|
230
|
+
name,
|
|
231
|
+
app: app.appName,
|
|
232
|
+
description: typeof def.description === "string" ? def.description : undefined,
|
|
233
|
+
listens: listens.map((e) => String(e?.name ?? "")).filter(Boolean),
|
|
234
|
+
source: sourceOf(def),
|
|
235
|
+
});
|
|
236
|
+
for (const ev of listens) {
|
|
119
237
|
if (ev?.name && !eventNamesSeen.has(ev.name)) {
|
|
120
238
|
eventNamesSeen.add(ev.name);
|
|
121
239
|
out.events.push({
|
|
122
240
|
name: ev.name,
|
|
123
241
|
app: app.appName,
|
|
124
242
|
public: Boolean(ev.$public),
|
|
243
|
+
description: typeof ev.description === "string" ? ev.description : undefined,
|
|
244
|
+
version: typeof ev.version === "number" ? ev.version : undefined,
|
|
245
|
+
audience: Array.isArray(ev.audience) ? ev.audience : undefined,
|
|
125
246
|
source: sourceOf(ev),
|
|
126
247
|
});
|
|
127
248
|
}
|
|
@@ -133,10 +254,12 @@ export function buildCache(apps, options = {}) {
|
|
|
133
254
|
// ── Queries ─────────────────────────────────────────────────────
|
|
134
255
|
const queries = safeMap(dispatcher.queries);
|
|
135
256
|
for (const [name, def] of queries) {
|
|
257
|
+
const proj = def.projection;
|
|
136
258
|
out.queries.push({
|
|
137
259
|
name,
|
|
138
260
|
app: app.appName,
|
|
139
261
|
public: Boolean(def.$public),
|
|
262
|
+
projection: typeof proj?.name === "string" ? proj.name : undefined,
|
|
140
263
|
source: sourceOf(def),
|
|
141
264
|
});
|
|
142
265
|
}
|
|
@@ -144,10 +267,15 @@ export function buildCache(apps, options = {}) {
|
|
|
144
267
|
const workflowDefs = dispatcher.listWorkflows?.() ?? [];
|
|
145
268
|
for (const def of workflowDefs) {
|
|
146
269
|
const name = String(def.name);
|
|
270
|
+
const subscribed = (def.subscribedEvents ?? new Set());
|
|
271
|
+
const dispatched = (def.dispatchedActions ?? new Set());
|
|
147
272
|
out.workflows.push({
|
|
148
273
|
name,
|
|
149
274
|
app: app.appName,
|
|
150
275
|
public: Boolean(def.$public),
|
|
276
|
+
description: typeof def.description === "string" ? def.description : undefined,
|
|
277
|
+
subscribesTo: [...subscribed],
|
|
278
|
+
dispatches: [...dispatched],
|
|
151
279
|
source: sourceOf(def),
|
|
152
280
|
});
|
|
153
281
|
for (const evName of (def.subscribedEvents ?? new Set())) {
|
|
@@ -173,15 +301,37 @@ export function buildCache(apps, options = {}) {
|
|
|
173
301
|
});
|
|
174
302
|
}
|
|
175
303
|
// ── Hooks ──────────────────────────────────────────────────────
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
304
|
+
// Prefer the structural listHooks helper if the runtime exposes one;
|
|
305
|
+
// otherwise walk `runtime.hooks` map and read step counts off each
|
|
306
|
+
// hook. Framework slots that haven't been materialised by a plugin
|
|
307
|
+
// (`runtime.defineHook(name)`) are absent — that's correct, we don't
|
|
308
|
+
// want to surface dead slots.
|
|
309
|
+
const explicit = app.runtime?.listHooks?.();
|
|
310
|
+
if (explicit) {
|
|
311
|
+
for (const h of explicit) {
|
|
312
|
+
out.hooks.push({
|
|
313
|
+
id: h.id,
|
|
314
|
+
name: h.name,
|
|
315
|
+
chain: h.chain,
|
|
316
|
+
listeners: h.listeners,
|
|
317
|
+
source: h.source,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
else if (app.runtime?.hooks) {
|
|
322
|
+
const hookMap = app.runtime.hooks;
|
|
323
|
+
for (const [slotName, hookValue] of Object.entries(hookMap)) {
|
|
324
|
+
if (!hookValue || typeof hookValue !== "object")
|
|
325
|
+
continue;
|
|
326
|
+
const h = hookValue;
|
|
327
|
+
const counts = typeof h.stepCounts === "function" ? h.stepCounts() : { chain: 0, listeners: 0 };
|
|
328
|
+
out.hooks.push({
|
|
329
|
+
id: h.id ?? slotName,
|
|
330
|
+
name: slotName,
|
|
331
|
+
chain: counts.chain,
|
|
332
|
+
listeners: counts.listeners,
|
|
333
|
+
});
|
|
334
|
+
}
|
|
185
335
|
}
|
|
186
336
|
}
|
|
187
337
|
// ── Routes (from passed-in interfaces) ────────────────────────────
|
|
@@ -198,13 +348,23 @@ export function buildCache(apps, options = {}) {
|
|
|
198
348
|
for (const r of options.resources ?? []) {
|
|
199
349
|
if ("definition" in r) {
|
|
200
350
|
const def = r.definition;
|
|
201
|
-
out.resources.push({
|
|
351
|
+
out.resources.push({
|
|
352
|
+
name: String(def?.name ?? ""),
|
|
353
|
+
app: r.app,
|
|
354
|
+
module: r.module,
|
|
355
|
+
source: sourceOf(def),
|
|
356
|
+
});
|
|
202
357
|
}
|
|
203
358
|
}
|
|
204
359
|
for (const e of options.errors ?? []) {
|
|
205
360
|
if ("definition" in e) {
|
|
206
361
|
const def = e.definition;
|
|
207
|
-
out.errors.push({
|
|
362
|
+
out.errors.push({
|
|
363
|
+
code: String(def?.code ?? ""),
|
|
364
|
+
app: e.app,
|
|
365
|
+
module: e.module,
|
|
366
|
+
source: sourceOf(def),
|
|
367
|
+
});
|
|
208
368
|
}
|
|
209
369
|
}
|
|
210
370
|
for (const m of options.middleware ?? []) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nwire/scan",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Nwire — system registry scanner. Walks AppDefinition[] manifests and writes the .nwire/ cache (actions, events, actors, projections, queries, modules, routes, event graph). Vite plugin + standalone function.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cache",
|
|
@@ -33,16 +33,16 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"zod": "^4.4.3",
|
|
36
|
-
"@nwire/
|
|
37
|
-
"@nwire/messages": "0.
|
|
38
|
-
"@nwire/
|
|
36
|
+
"@nwire/forge": "0.11.0",
|
|
37
|
+
"@nwire/messages": "0.11.0",
|
|
38
|
+
"@nwire/hooks": "0.11.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^22.19.9",
|
|
42
42
|
"typescript": "^5.9.3",
|
|
43
43
|
"vite": "npm:rolldown-vite@latest",
|
|
44
44
|
"vitest": "^4.0.18",
|
|
45
|
-
"@nwire/container": "0.
|
|
45
|
+
"@nwire/container": "0.11.0"
|
|
46
46
|
},
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "tsc && node ../../scripts/fix-dist-extensions.mjs dist",
|