@classytic/arc 2.15.3 → 2.16.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.
Files changed (159) hide show
  1. package/README.md +1 -0
  2. package/bin/arc.js +12 -0
  3. package/dist/{BaseController-dx3m2J8V.mjs → BaseController-DlCCTIxJ.mjs} +61 -19
  4. package/dist/{HookSystem-Iiebom92.mjs → HookSystem-Cmf7-Etp.mjs} +8 -4
  5. package/dist/{QueryCache-D41bfdBB.d.mts → QueryCache-SvmT_9ti.d.mts} +1 -1
  6. package/dist/{ResourceRegistry-CTERg_2x.mjs → ResourceRegistry-f48hFk3m.mjs} +52 -9
  7. package/dist/audit/index.d.mts +1 -1
  8. package/dist/audit/index.mjs +4 -2
  9. package/dist/auth/index.d.mts +4 -4
  10. package/dist/auth/index.mjs +4 -4
  11. package/dist/auth/redis-session.d.mts +1 -1
  12. package/dist/{betterAuthOpenApi--M_i87dQ.mjs → betterAuthOpenApi-ClWxaceA.mjs} +10 -6
  13. package/dist/buildHandler-BZX6zzDM.mjs +300 -0
  14. package/dist/cache/index.d.mts +3 -3
  15. package/dist/cache/index.mjs +3 -3
  16. package/dist/{caching-SM8gghN6.mjs → caching-TeHE8G-v.mjs} +1 -1
  17. package/dist/cli/commands/describe.d.mts +35 -1
  18. package/dist/cli/commands/describe.mjs +52 -12
  19. package/dist/cli/commands/docs.d.mts +1 -4
  20. package/dist/cli/commands/docs.mjs +4 -16
  21. package/dist/cli/commands/generate.d.mts +2 -20
  22. package/dist/cli/commands/generate.mjs +1 -546
  23. package/dist/cli/commands/init.d.mts +2 -40
  24. package/dist/cli/commands/init.mjs +1 -3036
  25. package/dist/cli/commands/introspect.mjs +53 -64
  26. package/dist/cli/index.d.mts +2 -2
  27. package/dist/cli/index.mjs +2 -2
  28. package/dist/{constants-Cxde4rpC.mjs → constants-TrJVIJl0.mjs} +7 -0
  29. package/dist/core/index.d.mts +3 -3
  30. package/dist/core/index.mjs +5 -5
  31. package/dist/{core-CvmOqEms.mjs → core-DBJ_j6rX.mjs} +222 -44
  32. package/dist/createActionRouter-DUpN3Dd1.mjs +288 -0
  33. package/dist/{createAggregationRouter-B0bPDf5b.mjs → createAggregationRouter-Dq-TUCuY.mjs} +3 -2
  34. package/dist/{createApp-PFegs47-.mjs → createApp-DNccuhyI.mjs} +16 -14
  35. package/dist/{defineEvent-D5h7EvAx.mjs → defineEvent-DRwY0fYm.mjs} +1 -1
  36. package/dist/docs/index.d.mts +2 -2
  37. package/dist/docs/index.mjs +1 -1
  38. package/dist/{errorHandler-Bk-AGhkU.mjs → errorHandler-DpoXQHZ9.mjs} +17 -14
  39. package/dist/errors-C1lX_jlm.d.mts +91 -0
  40. package/dist/{eventPlugin-CaKTYkYM.mjs → eventPlugin-C2cGqtRO.mjs} +1 -1
  41. package/dist/{eventPlugin-qXpqTebY.d.mts → eventPlugin-CtHC_av1.d.mts} +1 -1
  42. package/dist/events/index.d.mts +3 -3
  43. package/dist/events/index.mjs +5 -5
  44. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  45. package/dist/events/transports/redis.d.mts +1 -1
  46. package/dist/factory/index.d.mts +1 -1
  47. package/dist/factory/index.mjs +2 -2
  48. package/dist/{fields-COhcH3fk.d.mts → fields-Anj0xdih.d.mts} +1 -1
  49. package/dist/generate-BWFwgcCM.d.mts +38 -0
  50. package/dist/generate-CYac-OLv.mjs +654 -0
  51. package/dist/hooks/index.d.mts +1 -1
  52. package/dist/hooks/index.mjs +1 -1
  53. package/dist/idempotency/index.d.mts +2 -2
  54. package/dist/idempotency/index.mjs +1 -1
  55. package/dist/idempotency/redis.d.mts +1 -1
  56. package/dist/{index-BTqLEvhu.d.mts → index-3oIimXQn.d.mts} +12 -12
  57. package/dist/{index-BstGxcc3.d.mts → index-B-ulKx5P.d.mts} +55 -4
  58. package/dist/{index-BswOSJCE.d.mts → index-CkW0flkU.d.mts} +355 -16
  59. package/dist/index.d.mts +6 -6
  60. package/dist/index.mjs +7 -8
  61. package/dist/init-Dv71MsJr.d.mts +71 -0
  62. package/dist/init-HDvoO9L5.mjs +3098 -0
  63. package/dist/integrations/event-gateway.d.mts +2 -2
  64. package/dist/integrations/event-gateway.mjs +1 -1
  65. package/dist/integrations/index.d.mts +2 -2
  66. package/dist/integrations/jobs.mjs +3 -3
  67. package/dist/integrations/mcp/index.d.mts +239 -7
  68. package/dist/integrations/mcp/index.mjs +2 -528
  69. package/dist/integrations/mcp/testing.d.mts +2 -2
  70. package/dist/integrations/mcp/testing.mjs +6 -10
  71. package/dist/integrations/streamline.d.mts +71 -2
  72. package/dist/integrations/streamline.mjs +81 -8
  73. package/dist/integrations/websocket-redis.d.mts +1 -1
  74. package/dist/integrations/websocket.d.mts +1 -1
  75. package/dist/integrations/websocket.mjs +1 -0
  76. package/dist/loadResourcesFromEntry-BLMEI2Xa.mjs +51 -0
  77. package/dist/{resourceToTools-tFYUNmM0.mjs → mcpPlugin-7vGV51ED.mjs} +1021 -318
  78. package/dist/{memory-UBydS5ku.mjs → memory-QOLe11D5.mjs} +2 -0
  79. package/dist/middleware/index.d.mts +1 -1
  80. package/dist/middleware/index.mjs +1 -1
  81. package/dist/{openapi-BHXhoX8O.mjs → openapi-34T9yNwd.mjs} +47 -36
  82. package/dist/permissions/index.d.mts +2 -2
  83. package/dist/permissions/index.mjs +1 -1
  84. package/dist/{permissions-ohQyv50e.mjs → permissions-CTxMrreC.mjs} +2 -2
  85. package/dist/{pipe-Zr0KXjQe.mjs → pipe-DiCyvyPN.mjs} +1 -0
  86. package/dist/pipeline/index.d.mts +1 -1
  87. package/dist/pipeline/index.mjs +1 -1
  88. package/dist/plugins/index.d.mts +5 -5
  89. package/dist/plugins/index.mjs +10 -10
  90. package/dist/plugins/response-cache.mjs +5 -5
  91. package/dist/plugins/tracing-entry.d.mts +1 -1
  92. package/dist/plugins/tracing-entry.mjs +1 -1
  93. package/dist/{pluralize-DQgqgifU.mjs → pluralize-B9M8xvy-.mjs} +2 -1
  94. package/dist/presets/filesUpload.d.mts +4 -4
  95. package/dist/presets/filesUpload.mjs +2 -2
  96. package/dist/presets/index.d.mts +1 -1
  97. package/dist/presets/index.mjs +1 -1
  98. package/dist/presets/multiTenant.d.mts +1 -1
  99. package/dist/presets/multiTenant.mjs +4 -3
  100. package/dist/presets/search.d.mts +2 -2
  101. package/dist/presets/search.mjs +1 -1
  102. package/dist/{presets-BbkjdPeH.mjs → presets-C9BE6WaZ.mjs} +2 -2
  103. package/dist/{queryCachePlugin-m1XsgAIJ.mjs → queryCachePlugin-B4XMSSe7.mjs} +2 -2
  104. package/dist/{queryCachePlugin-CqMdLI2-.d.mts → queryCachePlugin-Biqzfbi5.d.mts} +2 -2
  105. package/dist/{redis-DiMkdHEl.d.mts → redis-Cyzrz6SX.d.mts} +1 -1
  106. package/dist/{redis-stream-D6HzR1Z_.d.mts → redis-stream-DT-YjzrB.d.mts} +1 -1
  107. package/dist/registry/index.d.mts +319 -2
  108. package/dist/registry/index.mjs +3 -3
  109. package/dist/registry-BBE23CDj.mjs +576 -0
  110. package/dist/{routerShared-DrOa-26E.mjs → routerShared-CZV5aabX.mjs} +3 -3
  111. package/dist/scope/index.d.mts +3 -3
  112. package/dist/scope/index.mjs +3 -3
  113. package/dist/{sse-Bz-5ZeTt.mjs → sse-BY6sTy4P.mjs} +1 -1
  114. package/dist/testing/index.d.mts +2 -2
  115. package/dist/testing/index.mjs +16 -7
  116. package/dist/testing/storageContract.d.mts +1 -1
  117. package/dist/types/index.d.mts +5 -5
  118. package/dist/types/storage.d.mts +1 -1
  119. package/dist/{types-C_s5moIu.mjs → types-Bi0r0vjG.mjs} +53 -1
  120. package/dist/{types-BQsjgQzS.d.mts → types-BsJMEQ4D.d.mts} +106 -12
  121. package/dist/{types-DrBaUwyV.d.mts → types-D-fYtKjb.d.mts} +33 -10
  122. package/dist/{types-CTYvcwHe.d.mts → types-DVfpSfx2.d.mts} +42 -1
  123. package/dist/utils/index.d.mts +1286 -2
  124. package/dist/utils/index.mjs +1 -1
  125. package/dist/{utils-_h9B3c57.mjs → utils-DC5ycPfr.mjs} +89 -40
  126. package/dist/{buildHandler-CcFOpJLh.mjs → validate-By96rH0r.mjs} +8 -299
  127. package/dist/{versioning-hmkPcDlX.d.mts → versioning-ZwX9tmbS.d.mts} +1 -1
  128. package/package.json +22 -29
  129. package/skills/arc/SKILL.md +299 -689
  130. package/skills/arc/references/auth.md +19 -7
  131. package/skills/arc-code-review/SKILL.md +1 -1
  132. package/skills/arc-code-review/references/arc-cheatsheet.md +100 -322
  133. package/dist/createActionRouter-S3MLVYot.mjs +0 -220
  134. package/dist/index-bRjYu21O.d.mts +0 -1320
  135. package/dist/org/index.d.mts +0 -66
  136. package/dist/org/index.mjs +0 -486
  137. package/dist/org/types.d.mts +0 -82
  138. package/dist/org/types.mjs +0 -1
  139. package/dist/registry-I-ogLgL9.mjs +0 -46
  140. /package/dist/{EventTransport-CT_52aWU.d.mts → EventTransport-C-2oAHtw.d.mts} +0 -0
  141. /package/dist/{EventTransport-DLWoUMHy.mjs → EventTransport-Hxvv5QQz.mjs} +0 -0
  142. /package/dist/{actionPermissions-CyUkQu6O.mjs → actionPermissions-Bjmvn7Eb.mjs} +0 -0
  143. /package/dist/{elevation-BXOWoGCF.d.mts → elevation-0YBpa663.d.mts} +0 -0
  144. /package/dist/{elevation-DgoeTyfX.mjs → elevation-Dci0AYLT.mjs} +0 -0
  145. /package/dist/{errorHandler-DFr45ZG4.d.mts → errorHandler-mHuyWzZE.d.mts} +0 -0
  146. /package/dist/{externalPaths-BD5nw6St.d.mts → externalPaths-DFg-2KTp.d.mts} +0 -0
  147. /package/dist/{interface-beEtJyWM.d.mts → interface-CH0OQudo.d.mts} +0 -0
  148. /package/dist/{interface-DfLGcus7.d.mts → interface-NwJ_qPlY.d.mts} +0 -0
  149. /package/dist/{keys-CGcCbNyu.mjs → keys-DopsCuyQ.mjs} +0 -0
  150. /package/dist/{loadResources-DBMQg_Aj.mjs → loadResources-ChQEj8ih.mjs} +0 -0
  151. /package/dist/{metrics-Qnvwc-LQ.mjs → metrics-TuOmguhi.mjs} +0 -0
  152. /package/dist/{replyHelpers-CK-FNO8E.mjs → replyHelpers-C-gD32oF.mjs} +0 -0
  153. /package/dist/{schemaIR-lYhC2gE5.mjs → schemaIR-Ctc89DSn.mjs} +0 -0
  154. /package/dist/{sessionManager-C4Le_UB3.d.mts → sessionManager-BqFegc0W.d.mts} +0 -0
  155. /package/dist/{storage-Dfzt4VTl.d.mts → storage-D2KZJAmn.d.mts} +0 -0
  156. /package/dist/{store-helpers-BkIN9-vu.mjs → store-helpers-B0sunfZZ.mjs} +0 -0
  157. /package/dist/{tracing-QJVprktp.d.mts → tracing-Dm8n7Cnn.d.mts} +0 -0
  158. /package/dist/{versioning-BUrT5aP4.mjs → versioning-B6mimogM.mjs} +0 -0
  159. /package/dist/{websocket-ChC2rqe1.d.mts → websocket-BkjeGZRn.d.mts} +0 -0
@@ -1,6 +1,22 @@
1
1
  import { f as createError, i as NotFoundError, r as ForbiddenError } from "../errors-j4aJm1Wg.mjs";
2
+ import fp from "fastify-plugin";
2
3
  //#region src/integrations/streamline.ts
3
4
  /**
5
+ * Streamline >= 2.3.3 dead-letter discriminator. The run.status stays
6
+ * `'failed'`; the discrimination is `error.code`:
7
+ * - `'stale_heartbeat'` — sweeper terminated; transient crash signal.
8
+ * - `'dead_lettered'` — exceeded `maxStaleRecoveries`; permanent.
9
+ * - `'VERSION_MISMATCH'` — engine deployed a step graph the run can't
10
+ * resume against; admin must rewind / migrate / cancel.
11
+ *
12
+ * Hosts switch on `error.code` for dashboards / alerting.
13
+ */
14
+ const STREAMLINE_FAILURE_CODES = {
15
+ STALE_HEARTBEAT: "stale_heartbeat",
16
+ DEAD_LETTERED: "dead_lettered",
17
+ VERSION_MISMATCH: "VERSION_MISMATCH"
18
+ };
19
+ /**
4
20
  * Full event list published on a streamline workflow's internal `eventBus`
5
21
  * (tracks streamline 2.3's `EventPayloadMap` in
6
22
  * `@classytic/streamline/src/core/events.ts`).
@@ -44,6 +60,7 @@ const STREAMLINE_TERMINAL_EVENTS = [
44
60
  ];
45
61
  const streamlinePluginImpl = async (fastify, options) => {
46
62
  const { workflows, prefix = "/workflows", auth = true, bridgeEvents = true, enableStreaming = false, enableHookEndpoint = false, tenantResolver, bypassTenantResolver, permissions: perms } = options;
63
+ const routeScope = prefix;
47
64
  const bridgeBus = options.bridgeBusEvents ?? false;
48
65
  const registry = /* @__PURE__ */ new Map();
49
66
  for (const wf of workflows) {
@@ -70,10 +87,35 @@ const streamlinePluginImpl = async (fastify, options) => {
70
87
  return check(request);
71
88
  };
72
89
  for (const [id, wf] of registry) {
73
- const routePrefix = `${prefix}/${id}`;
90
+ const routePrefix = `${routeScope}/${id}`;
74
91
  fastify.post(`${routePrefix}/start`, { preHandler: authPreHandler }, async (request, reply) => {
75
92
  if (!await checkPerm("start", request)) throw new ForbiddenError();
76
- const { input, meta, idempotencyKey, priority } = request.body ?? {};
93
+ const body = request.body;
94
+ if (body !== null && body !== void 0 && typeof body !== "object") throw createError(422, `[Arc/Streamline] '/${id}/start' body must be a JSON object.`, {
95
+ code: "arc.streamline.invalid_body",
96
+ workflowId: id
97
+ });
98
+ const envelopeKeys = new Set([
99
+ "input",
100
+ "meta",
101
+ "idempotencyKey",
102
+ "priority"
103
+ ]);
104
+ const bodyRecord = body ?? {};
105
+ const unknownKeys = Object.keys(bodyRecord).filter((k) => !envelopeKeys.has(k));
106
+ const hasInputKey = Object.hasOwn(bodyRecord, "input");
107
+ if (unknownKeys.length > 0 && !hasInputKey) throw createError(422, `[Arc/Streamline] '/${id}/start' expects '{ input: {...} }'. Got top-level keys [${unknownKeys.join(", ")}] but no 'input' key. Wrap your workflow payload: { "input": { ${unknownKeys.map((k) => `"${k}": ...`).join(", ")} } }.`, {
108
+ code: "arc.streamline.missing_input_envelope",
109
+ workflowId: id,
110
+ received: unknownKeys,
111
+ expected: "input"
112
+ });
113
+ if (unknownKeys.length > 0) throw createError(422, `[Arc/Streamline] '/${id}/start' got unknown top-level keys [${unknownKeys.join(", ")}]. Allowed envelope keys: input, meta, idempotencyKey, priority. Did you mean to nest these under 'input'?`, {
114
+ code: "arc.streamline.unknown_envelope_keys",
115
+ workflowId: id,
116
+ unknown: unknownKeys
117
+ });
118
+ const { input, meta, idempotencyKey, priority } = bodyRecord;
77
119
  const tenantOpts = resolveTenantOpts(request);
78
120
  const run = await wf.start(input, {
79
121
  meta,
@@ -161,6 +203,24 @@ const streamlinePluginImpl = async (fastify, options) => {
161
203
  const { runId } = request.params;
162
204
  return await wf.engine.execute(runId);
163
205
  });
206
+ const deleteRepo = wf.container?.repository;
207
+ const repoDeleteFn = deleteRepo?.delete;
208
+ const repoGetByIdFn = deleteRepo?.getById;
209
+ if (repoDeleteFn && repoGetByIdFn) fastify.delete(`${routePrefix}/runs/:runId`, { preHandler: authPreHandler }, async (request, reply) => {
210
+ if (!await checkPerm("cancel", request)) throw new ForbiddenError();
211
+ const { runId } = request.params;
212
+ const tenantOpts = resolveTenantOpts(request);
213
+ const repoOpts = {
214
+ ...tenantOpts.tenantId !== void 0 ? { tenantId: tenantOpts.tenantId } : {},
215
+ ...tenantOpts.bypassTenant ? { bypassTenant: true } : {}
216
+ };
217
+ if (!await repoGetByIdFn(runId, repoOpts)) throw new NotFoundError(`Workflow run ${runId} not found`);
218
+ try {
219
+ await wf.cancel(runId);
220
+ } catch {}
221
+ await repoDeleteFn(runId, repoOpts);
222
+ return reply.status(204).send();
223
+ });
164
224
  if (wf.engine.waitFor) fastify.get(`${routePrefix}/runs/:runId/wait`, { preHandler: authPreHandler }, async (request, _reply) => {
165
225
  if (!await checkPerm("get", request)) throw new ForbiddenError();
166
226
  const { runId } = request.params;
@@ -239,7 +299,7 @@ const streamlinePluginImpl = async (fastify, options) => {
239
299
  }
240
300
  if (enableHookEndpoint) {
241
301
  let resumeHookFn;
242
- fastify.post(`${prefix}/hooks/:token`, { preHandler: authPreHandler }, async (request, _reply) => {
302
+ fastify.post(`${routeScope}/hooks/:token`, { preHandler: authPreHandler }, async (request, _reply) => {
243
303
  if (!resumeHookFn) resumeHookFn = (await import("@classytic/streamline")).resumeHook;
244
304
  const { token } = request.params;
245
305
  const result = await resumeHookFn(token, request.body);
@@ -249,7 +309,7 @@ const streamlinePluginImpl = async (fastify, options) => {
249
309
  };
250
310
  });
251
311
  }
252
- fastify.get(prefix, { preHandler: authPreHandler }, async () => {
312
+ fastify.get(routeScope || "/", { preHandler: authPreHandler }, async () => {
253
313
  return Array.from(registry.entries()).map(([id, wf]) => ({
254
314
  id,
255
315
  name: wf.definition.name ?? id,
@@ -257,10 +317,23 @@ const streamlinePluginImpl = async (fastify, options) => {
257
317
  }));
258
318
  });
259
319
  fastify.addHook("onClose", async () => {
260
- for (const wf of registry.values()) wf.shutdown?.();
320
+ for (const wf of registry.values()) {
321
+ wf.shutdown?.();
322
+ wf.container?.dispose?.();
323
+ }
261
324
  });
262
325
  };
263
- /** Pluggable streamline integration for Arc */
264
- const streamlinePlugin = streamlinePluginImpl;
326
+ /**
327
+ * Pluggable streamline integration for Arc.
328
+ *
329
+ * Wrapped in `fastify-plugin` so Fastify treats `options.prefix` as a
330
+ * plain plugin option (NOT an encapsulation prefix). Without the wrapper,
331
+ * Fastify would prepend `options.prefix` to every route, then the plugin
332
+ * code would prepend it again — the duplicate-prefix bug.
333
+ */
334
+ const streamlinePlugin = fp(streamlinePluginImpl, {
335
+ name: "streamline-routes",
336
+ fastify: "5.x"
337
+ });
265
338
  //#endregion
266
- export { STREAMLINE_BUS_EVENTS, STREAMLINE_TERMINAL_EVENTS, streamlinePlugin };
339
+ export { STREAMLINE_BUS_EVENTS, STREAMLINE_FAILURE_CODES, STREAMLINE_TERMINAL_EVENTS, streamlinePlugin };
@@ -1,4 +1,4 @@
1
- import { c as WebSocketAdapter } from "../websocket-ChC2rqe1.mjs";
1
+ import { c as WebSocketAdapter } from "../websocket-BkjeGZRn.mjs";
2
2
 
3
3
  //#region src/integrations/websocket-redis.d.ts
4
4
  interface RedisLike {
@@ -1,2 +1,2 @@
1
- import { a as WebSocketMessage, c as WebSocketAdapter, i as WebSocketClient, n as websocketPlugin, o as WebSocketPluginOptions, r as AuthResult, s as LocalWebSocketAdapter, t as RoomManager } from "../websocket-ChC2rqe1.mjs";
1
+ import { a as WebSocketMessage, c as WebSocketAdapter, i as WebSocketClient, n as websocketPlugin, o as WebSocketPluginOptions, r as AuthResult, s as LocalWebSocketAdapter, t as RoomManager } from "../websocket-BkjeGZRn.mjs";
2
2
  export { AuthResult, LocalWebSocketAdapter, RoomManager, WebSocketAdapter, WebSocketClient, WebSocketMessage, WebSocketPluginOptions, websocketPlugin };
@@ -375,6 +375,7 @@ const websocketPluginImpl = async (fastify, options) => {
375
375
  if (room.startsWith("org:")) {
376
376
  const parts = room.split(":");
377
377
  const orgId = parts[1];
378
+ if (!orgId) return;
378
379
  const actualRoom = parts.slice(2).join(":");
379
380
  rooms.broadcastToOrg(orgId, actualRoom, message);
380
381
  } else rooms.broadcast(room, message);
@@ -0,0 +1,51 @@
1
+ import { t as ResourceRegistry } from "./ResourceRegistry-f48hFk3m.mjs";
2
+ import { resolve } from "node:path";
3
+ import { pathToFileURL } from "node:url";
4
+ //#region src/cli/utils/loadResourcesFromEntry.ts
5
+ /**
6
+ * Shared loader for CLI commands that need to read a host's resource graph
7
+ * from a built entry file (`introspect`, `docs`, `describe`).
8
+ *
9
+ * Each command historically duplicated the same dance: `pathToFileURL` →
10
+ * dynamic `import()` → walk the module's exports → match anything that
11
+ * looks structurally like a `ResourceDefinition` (carries `name`,
12
+ * `_registryMeta`, `toPlugin`) → register into a fresh `ResourceRegistry`.
13
+ * Extracting the duplication here means a future signature change to
14
+ * the registry surface or the resource-shape sentinel only touches one
15
+ * spot.
16
+ *
17
+ * The structural match (vs `instanceof ResourceDefinition`) is deliberate:
18
+ * the host's entry file is a compiled `dist/` artifact that may have been
19
+ * built against a different arc minor version, so a class-identity check
20
+ * would reject genuinely-compatible objects across version drift.
21
+ */
22
+ /**
23
+ * Load the host's entry file, walk its exports, and register every
24
+ * `ResourceDefinition`-shaped value into a fresh `ResourceRegistry`.
25
+ *
26
+ * Returns `{ registry, registered }`:
27
+ * - `registry` is populated and ready to query (`getAll()`, `getStats()`,
28
+ * `getIntrospection()`).
29
+ * - `registered` is the count — callers can short-circuit with a
30
+ * friendly "no resources found" message when it's zero.
31
+ */
32
+ async function loadResourcesFromEntry(entryPath) {
33
+ const entryModule = await import(pathToFileURL(resolve(process.cwd(), entryPath)).href);
34
+ const registry = new ResourceRegistry();
35
+ let registered = 0;
36
+ const tryRegister = (value) => {
37
+ if (value && typeof value === "object" && "name" in value && "_registryMeta" in value && "toPlugin" in value) {
38
+ const resourceLike = value;
39
+ registry.register(resourceLike, resourceLike._registryMeta ?? {});
40
+ registered++;
41
+ }
42
+ };
43
+ for (const exported of Object.values(entryModule)) if (Array.isArray(exported)) for (const entry of exported) tryRegister(entry);
44
+ else tryRegister(exported);
45
+ return {
46
+ registry,
47
+ registered
48
+ };
49
+ }
50
+ //#endregion
51
+ export { loadResourcesFromEntry as t };