@classytic/arc 2.19.0 → 2.21.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 +1 -1
- package/bin/arc.js +37 -0
- package/dist/{BaseController-Dy6EQHjM.mjs → BaseController-BnLYgE9A.mjs} +4 -4
- package/dist/{EventTransport-IEn7Tn-Q.d.mts → EventTransport-QmY7A258.d.mts} +0 -1
- package/dist/{QueryCache-TRfXau3W.d.mts → QueryCache-Z1B38koh.d.mts} +0 -1
- package/dist/{ResourceRegistry-5fKZwhK_.mjs → ResourceRegistry-7RnrEWNP.mjs} +1 -1
- package/dist/{actionPermissions-D7NGwcAR.mjs → actionPermissions-ze70xXJi.mjs} +1 -1
- package/dist/audit/index.d.mts +9 -5
- package/dist/audit/index.mjs +1 -1
- package/dist/auth/audit.d.mts +7 -4
- package/dist/auth/audit.mjs +5 -0
- package/dist/auth/index.d.mts +7 -14
- package/dist/auth/index.mjs +7 -5
- package/dist/auth/redis-session.d.mts +1 -2
- package/dist/{buildHandler-B0qvAguf.mjs → buildHandler-D_2ULWxZ.mjs} +3 -3
- package/dist/cache/index.d.mts +2 -3
- package/dist/cache/index.mjs +2 -2
- package/dist/cli/commands/describe.d.mts +1 -2
- package/dist/cli/commands/describe.mjs +2 -2
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/generate.mjs +1 -1
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +4 -13
- package/dist/cli/index.mjs +2 -2
- package/dist/{constants-gaEeMlz-.mjs → constants-DNre0wRg.mjs} +1 -1
- package/dist/context/index.d.mts +2 -2
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +5 -5
- package/dist/{core-Ba-6Sue6.mjs → core-DZ6Xe1jR.mjs} +182 -55
- package/dist/{createActionRouter-B_WzBFyf.mjs → createActionRouter-BWMDW-dL.mjs} +4 -4
- package/dist/{createAggregationRouter-B_G_nIjm.mjs → createAggregationRouter-l5mNdYrZ.mjs} +5 -4
- package/dist/{createApp--703hQE1.mjs → createApp-Bi61XxqU.mjs} +229 -24
- package/dist/discovery/index.d.mts +0 -1
- package/dist/docs/index.d.mts +1 -2
- package/dist/docs/index.mjs +1 -1
- package/dist/{elevation-xGUBq6p_.d.mts → elevation-C9s5hFP_.d.mts} +0 -1
- package/dist/encryption/index.d.mts +2 -144
- package/dist/{errorHandler-BtUUDPEa.d.mts → errorHandler-B0e-bq-O.d.mts} +0 -1
- package/dist/{errors-DOHkjQlD.d.mts → errors-DHcHVAgk.d.mts} +0 -1
- package/dist/{eventPlugin-DbBFd3x_.d.mts → eventPlugin-A6EbthXR.d.mts} +10 -6
- package/dist/events/index.d.mts +9 -6
- package/dist/events/index.mjs +9 -6
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis.d.mts +1 -2
- package/dist/factory/index.d.mts +5 -4
- package/dist/factory/index.mjs +3 -3
- package/dist/{fastify-augmentation-D-5_yQJi.d.mts → fastify-augmentation-DafigGQp.d.mts} +12 -2
- package/dist/{fields-C7ahdGYf.d.mts → fields-BGqrAa_u.d.mts} +1 -2
- package/dist/{generate-Brans3fW.mjs → generate-usRSdQmP.mjs} +4 -3
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.d.mts +4 -3
- package/dist/idempotency/index.mjs +1 -1
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-CP6A1Ffj.d.mts → index-B5N-NaHs.d.mts} +88 -7
- package/dist/{index-CRKiiP5o.d.mts → index-B5PoNbfu.d.mts} +41 -7
- package/dist/{index-CUTG2Cuo.d.mts → index-Dtrj6XZk.d.mts} +90 -29
- package/dist/index.d.mts +7 -7
- package/dist/index.mjs +7 -7
- package/dist/{init-Bqpt1Hxf.mjs → init-0YhHkXGk.mjs} +239 -140
- package/dist/integrations/event-gateway.d.mts +2 -3
- package/dist/integrations/index.d.mts +2 -2
- package/dist/integrations/jobs.d.mts +0 -1
- package/dist/integrations/mcp/index.d.mts +6 -7
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.d.mts +1 -2
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/integrations/streamline.d.mts +19 -7
- package/dist/integrations/streamline.mjs +9 -5
- package/dist/integrations/webhooks.d.mts +0 -1
- package/dist/integrations/websocket-pushref-redis.d.mts +1 -2
- package/dist/integrations/websocket-redis.d.mts +1 -2
- package/dist/integrations/websocket.d.mts +2 -2
- package/dist/{loadResources-B8UmpITt.mjs → loadResources-DpU8pfjV.mjs} +4 -3
- package/dist/{loadResourcesFromEntry-BTlFEx05.mjs → loadResourcesFromEntry-OMCvIEE7.mjs} +1 -1
- package/dist/{mcpPlugin-C088Nd-G.mjs → mcpPlugin-B9XjjNVG.mjs} +148 -49
- package/dist/middleware/index.d.mts +1 -2
- package/dist/middleware/index.mjs +1 -1
- package/dist/module-WeuU5jfd.d.mts +429 -0
- package/dist/{openapi-DZ-5a5Op.mjs → openapi-Kyod_di6.mjs} +3 -3
- package/dist/permissions/index.d.mts +3 -3
- package/dist/permissions/index.mjs +2 -2
- package/dist/{permissions-BPB1xqGf.mjs → permissions-BJMuKMl7.mjs} +43 -3
- package/dist/pipeline/index.d.mts +1 -2
- package/dist/plugins/index.d.mts +87 -10
- package/dist/plugins/index.mjs +91 -4
- package/dist/plugins/response-cache.d.mts +5 -3
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/{pluralize-DccFV9R7.mjs → pluralize-BhrgwZb9.mjs} +3 -3
- package/dist/presets/filesUpload.d.mts +4 -5
- package/dist/presets/filesUpload.mjs +1 -1
- package/dist/presets/index.d.mts +16 -3
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -2
- package/dist/presets/multiTenant.mjs +1 -1
- package/dist/presets/search.d.mts +2 -3
- package/dist/presets/search.mjs +3 -2
- package/dist/{presets-BtyFwTO4.mjs → presets-gw0ciIUD.mjs} +1 -1
- package/dist/{pushref-registry-DnCqL3aa.d.mts → pushref-registry-BbcnpNTm.d.mts} +4 -2
- package/dist/{queryCachePlugin-xzFyKAjT.d.mts → queryCachePlugin-CfYWq-Ak.d.mts} +1 -2
- package/dist/{queryCachePlugin-emHb0CuX.mjs → queryCachePlugin-DzqLN904.mjs} +2 -2
- package/dist/{redis-C3W0qbz8.d.mts → redis-UDtLaNj6.d.mts} +0 -1
- package/dist/{redis-stream-DsP1vqZB.d.mts → redis-stream-iVR2l7aS.d.mts} +1 -2
- package/dist/{registerRoute-BdV8DDwU.mjs → registerRoute-oGAl0oA8.mjs} +1 -1
- package/dist/registry/index.d.mts +2 -2
- package/dist/registry/index.mjs +2 -2
- package/dist/{registry-Bm9LjWKs.mjs → registry-vcOCtPij.mjs} +1 -1
- package/dist/{schemaIR-D1MJwqmp.mjs → schemaIR-ajetw3Fv.mjs} +1 -42
- package/dist/schemas/index.d.mts +0 -1
- package/dist/scim/index.d.mts +0 -1
- package/dist/scim/index.mjs +2 -2
- package/dist/scope/index.d.mts +2 -3
- package/dist/{sessionManager-BXG-HGwG.d.mts → sessionManager-BHOM3N_0.d.mts} +10 -6
- package/dist/{storage-Co_PQpeQ.d.mts → storage-DLUZ04kK.d.mts} +8 -4
- package/dist/sync/changelog.d.mts +2 -0
- package/dist/sync/changelog.mjs +2 -0
- package/dist/testing/index.d.mts +2 -3
- package/dist/testing/index.mjs +6 -3
- package/dist/testing/storageContract.d.mts +1 -2
- package/dist/{tracing-D8jlGXP7.d.mts → tracing-CsGKQJGg.d.mts} +0 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/storage.d.mts +1 -1
- package/dist/types-Cc91nXuA.d.mts +132 -0
- package/dist/{types-LNebCoJx.d.mts → types-DH0GupI7.d.mts} +8 -4
- package/dist/{types-DpvSF5Hh.d.mts → types-DRj-Ut4n.d.mts} +1 -2
- package/dist/{types-9_wtNPsI.d.mts → types-DpxoNX5v.d.mts} +112 -214
- package/dist/utils/index.d.mts +182 -128
- package/dist/utils/index.mjs +2 -2
- package/dist/{utils-C6QQ9-hd.mjs → utils-CO8a3B_1.mjs} +1435 -1391
- package/dist/{validate-CRcaWzp2.mjs → validate-C2mGgSXg.mjs} +3 -3
- package/dist/{versioning-D_eBCfNr.d.mts → versioning-DQ4mCvg0.d.mts} +1 -2
- package/dist/{websocket-MlYlrYq0.d.mts → websocket-uuWqM6UY.d.mts} +5 -4
- package/package.json +18 -13
- package/skills/arc/SKILL.md +10 -0
- package/skills/arc/references/mcp.md +2 -0
- package/skills/arc-module-publishing/SKILL.md +242 -0
- package/skills/arc-module-publishing/references/authoring-modules.md +272 -0
- package/skills/arc-module-publishing/references/package-manifest.md +155 -0
- package/skills/arc-module-publishing/references/publishing-workflow.md +182 -0
|
@@ -3,6 +3,134 @@ import { arcLog } from "./logger/index.mjs";
|
|
|
3
3
|
import { n as PUBLIC_SCOPE } from "./types-bHblZ9YN.mjs";
|
|
4
4
|
import Fastify from "fastify";
|
|
5
5
|
import qs from "qs";
|
|
6
|
+
//#region src/factory/module.ts
|
|
7
|
+
/**
|
|
8
|
+
* Resolve any module-input form to a concrete `ArcModule`. A thunk is invoked
|
|
9
|
+
* (this is where a dynamic `import()` fires); a promise (or plain module) is
|
|
10
|
+
* awaited. Called once per module at the start of the bootstrap phase.
|
|
11
|
+
*/
|
|
12
|
+
async function resolveModule(input) {
|
|
13
|
+
const resolved = typeof input === "function" ? await input() : await input;
|
|
14
|
+
if (!resolved || typeof resolved !== "object" || typeof resolved.name !== "string") throw new Error("[arc] a `modules` entry resolved to something that is not an ArcModule (expected an object with a string `name`). Check dynamic-import thunks return `createXModule(deps)` (or the module object), not the namespace.");
|
|
15
|
+
return resolved;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Order modules for composition by their `dependsOn` edges — a STABLE
|
|
19
|
+
* topological sort. Called once at the start of the bootstrap phase; every
|
|
20
|
+
* subsequent phase (bootstrap, resources, afterResources, and reverse-order
|
|
21
|
+
* onClose) iterates the returned list, so a module's declared dependencies are
|
|
22
|
+
* always composed before it.
|
|
23
|
+
*
|
|
24
|
+
* "Stable" = modules with no edge between them keep their original list order,
|
|
25
|
+
* so declaring `dependsOn` on one module never silently reorders an unrelated
|
|
26
|
+
* one. Backward compatible: a `modules` array with NO `dependsOn` anywhere is
|
|
27
|
+
* returned unchanged.
|
|
28
|
+
*
|
|
29
|
+
* Fail-fast — throws (never reorders past a broken contract) on:
|
|
30
|
+
* - duplicate module names (the name is the graph key)
|
|
31
|
+
* - a `dependsOn` name not present in the composed set
|
|
32
|
+
* - a self-reference (`dependsOn` includes the module's own name)
|
|
33
|
+
* - a dependency cycle (reports the concrete `a → b → … → a` path)
|
|
34
|
+
*/
|
|
35
|
+
function orderModules(modules) {
|
|
36
|
+
const byName = /* @__PURE__ */ new Map();
|
|
37
|
+
for (const m of modules) {
|
|
38
|
+
if (byName.has(m.name)) throw new Error(`[arc] Duplicate module name "${m.name}" — composed twice; check your modules array.`);
|
|
39
|
+
byName.set(m.name, m);
|
|
40
|
+
}
|
|
41
|
+
for (const m of modules) for (const dep of m.dependsOn ?? []) {
|
|
42
|
+
if (dep === m.name) throw new Error(`[arc] module "${m.name}" dependsOn itself — remove the self-reference.`);
|
|
43
|
+
if (!byName.has(dep)) throw new Error(`[arc] module "${m.name}" dependsOn "${dep}", which is not composed. Add the "${dep}" module to createApp({ modules }) (before this one is fine — arc orders them), or drop the dependency. Composed modules: ${[...byName.keys()].join(", ") || "(none)"}.`);
|
|
44
|
+
}
|
|
45
|
+
if (modules.every((m) => !m.dependsOn || m.dependsOn.length === 0)) return [...modules];
|
|
46
|
+
const originalIndex = /* @__PURE__ */ new Map();
|
|
47
|
+
let nextIndex = 0;
|
|
48
|
+
for (const m of modules) originalIndex.set(m.name, nextIndex++);
|
|
49
|
+
const pendingDeps = new Map(modules.map((m) => [m.name, (m.dependsOn ?? []).length]));
|
|
50
|
+
const dependents = /* @__PURE__ */ new Map();
|
|
51
|
+
for (const m of modules) for (const dep of m.dependsOn ?? []) {
|
|
52
|
+
const list = dependents.get(dep);
|
|
53
|
+
if (list) list.push(m.name);
|
|
54
|
+
else dependents.set(dep, [m.name]);
|
|
55
|
+
}
|
|
56
|
+
const ordered = [];
|
|
57
|
+
const remaining = new Set(modules.map((m) => m.name));
|
|
58
|
+
while (remaining.size > 0) {
|
|
59
|
+
let pick;
|
|
60
|
+
let pickIndex = Number.POSITIVE_INFINITY;
|
|
61
|
+
for (const name of remaining) {
|
|
62
|
+
if (pendingDeps.get(name) !== 0) continue;
|
|
63
|
+
const index = originalIndex.get(name) ?? Number.POSITIVE_INFINITY;
|
|
64
|
+
if (index < pickIndex) {
|
|
65
|
+
pick = name;
|
|
66
|
+
pickIndex = index;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
if (pick === void 0) throw new Error(describeModuleCycle(remaining, byName));
|
|
70
|
+
const picked = byName.get(pick);
|
|
71
|
+
if (!picked) throw new Error(`[arc] internal: ordered module "${pick}" is missing from the index`);
|
|
72
|
+
ordered.push(picked);
|
|
73
|
+
remaining.delete(pick);
|
|
74
|
+
for (const dependent of dependents.get(pick) ?? []) pendingDeps.set(dependent, (pendingDeps.get(dependent) ?? 1) - 1);
|
|
75
|
+
}
|
|
76
|
+
return ordered;
|
|
77
|
+
}
|
|
78
|
+
/** Walk the still-unordered subgraph for one concrete cycle path. */
|
|
79
|
+
function describeModuleCycle(remaining, byName) {
|
|
80
|
+
const stack = [];
|
|
81
|
+
const onStack = /* @__PURE__ */ new Set();
|
|
82
|
+
const done = /* @__PURE__ */ new Set();
|
|
83
|
+
let cycle = null;
|
|
84
|
+
const walk = (name) => {
|
|
85
|
+
if (cycle) return;
|
|
86
|
+
stack.push(name);
|
|
87
|
+
onStack.add(name);
|
|
88
|
+
for (const dep of byName.get(name)?.dependsOn ?? []) {
|
|
89
|
+
if (!remaining.has(dep)) continue;
|
|
90
|
+
if (onStack.has(dep)) {
|
|
91
|
+
cycle = [...stack.slice(stack.indexOf(dep)), dep];
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (!done.has(dep)) walk(dep);
|
|
95
|
+
if (cycle) return;
|
|
96
|
+
}
|
|
97
|
+
onStack.delete(name);
|
|
98
|
+
stack.pop();
|
|
99
|
+
done.add(name);
|
|
100
|
+
};
|
|
101
|
+
for (const name of remaining) {
|
|
102
|
+
if (!done.has(name)) walk(name);
|
|
103
|
+
if (cycle) break;
|
|
104
|
+
}
|
|
105
|
+
return `[arc] module dependency cycle: ${cycle ? cycle.join(" → ") : [...remaining].join(", ")}. Modules cannot dependsOn each other circularly — break it with a shared module both point at, or wire the softer direction through an event/port instead of a hard dependsOn.`;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Identity helper for authoring a typed module — mirrors `defineResource`.
|
|
109
|
+
* Gives inference + a single obvious construction site; does no work.
|
|
110
|
+
*
|
|
111
|
+
* `TExports` is inferred from the `bootstrap` return value, so a module
|
|
112
|
+
* author gets a typed public export for free:
|
|
113
|
+
*
|
|
114
|
+
* ```ts
|
|
115
|
+
* export const accountingModule = (deps: Deps) =>
|
|
116
|
+
* defineModule({
|
|
117
|
+
* name: "accounting",
|
|
118
|
+
* bootstrap: async () => createAccountingEngine(deps), // TExports inferred
|
|
119
|
+
* });
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
function defineModule(module) {
|
|
123
|
+
return module;
|
|
124
|
+
}
|
|
125
|
+
function getModuleExports(fastify, name) {
|
|
126
|
+
const modules = fastify.arc?.modules;
|
|
127
|
+
if (!modules || !Object.hasOwn(modules, name)) {
|
|
128
|
+
const available = modules ? Object.keys(modules) : [];
|
|
129
|
+
throw new Error(`[arc] no public export recorded for module "${name}".\nA module's export is its bootstrap() return value; it is readable only AFTER that module's bootstrap ran (dependsOn / list order = init order).\nModules with recorded exports: ${available.length ? available.join(", ") : "(none)"}.`);
|
|
130
|
+
}
|
|
131
|
+
return modules[name];
|
|
132
|
+
}
|
|
133
|
+
//#endregion
|
|
6
134
|
//#region src/factory/presets.ts
|
|
7
135
|
/**
|
|
8
136
|
* Production preset - strict security, performance optimized
|
|
@@ -245,7 +373,7 @@ async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
|
245
373
|
trackPlugin("arc-caching", opts);
|
|
246
374
|
}
|
|
247
375
|
if (config.arcPlugins?.queryCache) {
|
|
248
|
-
const { queryCachePlugin } = await import("./queryCachePlugin-
|
|
376
|
+
const { queryCachePlugin } = await import("./queryCachePlugin-DzqLN904.mjs").then((n) => n.n);
|
|
249
377
|
const opts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
|
|
250
378
|
const store = config.stores?.queryCache ?? new (await (import("./memory-CLXA2VeJ.mjs").then((n) => n.n))).MemoryCacheStore();
|
|
251
379
|
await fastify.register(queryCachePlugin, {
|
|
@@ -344,11 +472,20 @@ async function registerElevation(fastify, config, trackPlugin) {
|
|
|
344
472
|
}
|
|
345
473
|
/**
|
|
346
474
|
* Register error handler plugin (opt-out).
|
|
475
|
+
*
|
|
476
|
+
* `moduleErrorMappers` — mappers shipped by composed arc modules
|
|
477
|
+
* (`defineModule({ errorMappers })`), appended AFTER host-declared mappers
|
|
478
|
+
* so host config keeps priority. Collected by `createApp` from the
|
|
479
|
+
* pre-resolved module graph.
|
|
347
480
|
*/
|
|
348
|
-
async function registerErrorHandler(fastify, config, trackPlugin) {
|
|
481
|
+
async function registerErrorHandler(fastify, config, trackPlugin, moduleErrorMappers) {
|
|
349
482
|
if (config.errorHandler === false) return;
|
|
350
483
|
const { errorHandlerPlugin } = await import("./errorHandler-Dxf6P_kV.mjs").then((n) => n.r);
|
|
351
|
-
|
|
484
|
+
let errorOpts = typeof config.errorHandler === "object" ? config.errorHandler : { includeStack: config.preset !== "production" };
|
|
485
|
+
if (moduleErrorMappers && moduleErrorMappers.length > 0) errorOpts = {
|
|
486
|
+
...errorOpts,
|
|
487
|
+
errorMappers: [...errorOpts.errorMappers ?? [], ...moduleErrorMappers]
|
|
488
|
+
};
|
|
352
489
|
await fastify.register(errorHandlerPlugin, errorOpts);
|
|
353
490
|
trackPlugin("arc-error-handler", errorOpts);
|
|
354
491
|
fastify.log.debug("Arc error handler enabled");
|
|
@@ -401,17 +538,31 @@ async function registerOne(parent, resource) {
|
|
|
401
538
|
}
|
|
402
539
|
}
|
|
403
540
|
/**
|
|
404
|
-
* Execute the full resource lifecycle
|
|
405
|
-
*
|
|
406
|
-
*
|
|
407
|
-
*
|
|
541
|
+
* Execute the full resource lifecycle. The module graph is resolved + ordered
|
|
542
|
+
* by `dependsOn` (stable topological sort) once, up FRONT — before any
|
|
543
|
+
* side-effecting plugin — so a bad graph fails fast with no half-initialised
|
|
544
|
+
* infra. Every phase below iterates that order; each module phase runs BEFORE
|
|
545
|
+
* its app-level counterpart:
|
|
546
|
+
*
|
|
547
|
+
* 1. resolve + orderModules — validate the graph (dup names, missing/self
|
|
548
|
+
* deps, cycles fail fast) BEFORE side effects
|
|
549
|
+
* 2. plugins() — app infra (DB, data, webhooks)
|
|
550
|
+
* 3. module.plugins — module infra, dependsOn order, before bootstraps
|
|
551
|
+
* 4a. module.bootstrap — engine init (return value → arc.modules[name])
|
|
552
|
+
* 4b. bootstrap[] — app-level domain init
|
|
553
|
+
* 5. resources factory (if any) — resolved AFTER bootstrap, so engine-backed
|
|
408
554
|
* adapters can `await ensureEngine()` and pass
|
|
409
555
|
* live models/repos into `defineResource(...)`
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
*
|
|
556
|
+
* 5b. module resources — RESOLVED after the app factory, but PREPENDED
|
|
557
|
+
* so they REGISTER before app resources (two
|
|
558
|
+
* orderings, both intentional — see §5b)
|
|
559
|
+
* 6. module.afterResources → afterResources() — post-registration wiring
|
|
560
|
+
* 7. onReady — lifecycle hook
|
|
561
|
+
* 7b. onClose — module.onClose (reverse composition order)
|
|
562
|
+
* THEN app onClose, in ONE hook so module
|
|
563
|
+
* teardown runs before shared infra closes.
|
|
413
564
|
*/
|
|
414
|
-
async function registerResources(fastify, config) {
|
|
565
|
+
async function registerResources(fastify, config, preResolvedModules) {
|
|
415
566
|
if (config.preset === "production") {
|
|
416
567
|
if (config.strictResources === void 0) config = {
|
|
417
568
|
...config,
|
|
@@ -422,10 +573,37 @@ async function registerResources(fastify, config) {
|
|
|
422
573
|
strictResourceDir: true
|
|
423
574
|
};
|
|
424
575
|
}
|
|
576
|
+
const modules = preResolvedModules ?? orderModules(await Promise.all((config.modules ?? []).map((m) => resolveModule(m))));
|
|
425
577
|
if (config.plugins) {
|
|
426
578
|
await config.plugins(fastify);
|
|
427
579
|
fastify.log.debug("Custom plugins registered");
|
|
428
580
|
}
|
|
581
|
+
for (const m of modules) {
|
|
582
|
+
if (!m.plugins) continue;
|
|
583
|
+
try {
|
|
584
|
+
await m.plugins(fastify);
|
|
585
|
+
} catch (err) {
|
|
586
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
587
|
+
throw new Error(`[arc] module "${m.name}" plugins() threw: ${msg}`, { cause: err });
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
for (const m of modules) {
|
|
591
|
+
if (!m.bootstrap) continue;
|
|
592
|
+
let exported;
|
|
593
|
+
try {
|
|
594
|
+
exported = await m.bootstrap(fastify);
|
|
595
|
+
} catch (err) {
|
|
596
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
597
|
+
throw new Error(`[arc] module "${m.name}" bootstrap threw: ${msg}`, { cause: err });
|
|
598
|
+
}
|
|
599
|
+
if (exported !== void 0) {
|
|
600
|
+
const arc = fastify.arc;
|
|
601
|
+
if (!arc) throw new Error(`[arc] module "${m.name}" returned a public export but fastify.arc is not decorated — arcCorePlugin must register first.`);
|
|
602
|
+
if (!arc.modules) arc.modules = Object.create(null);
|
|
603
|
+
arc.modules[m.name] = exported;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
if (modules.length) fastify.log.debug(`${modules.length} module(s) composed (in dependency order): ${modules.map((m) => m.name).join(" → ")}`);
|
|
429
607
|
if (config.bootstrap?.length) {
|
|
430
608
|
for (const init of config.bootstrap) await init(fastify);
|
|
431
609
|
fastify.log.debug(`${config.bootstrap.length} bootstrap function(s) executed`);
|
|
@@ -448,7 +626,7 @@ async function registerResources(fastify, config) {
|
|
|
448
626
|
let discoveryPath;
|
|
449
627
|
let discoveryYieldedZero = false;
|
|
450
628
|
if (resolvedResources === void 0 && config.resourceDir) {
|
|
451
|
-
const { loadResources } = await import("./loadResources-
|
|
629
|
+
const { loadResources } = await import("./loadResources-DpU8pfjV.mjs").then((n) => n.n);
|
|
452
630
|
const { resolve, dirname } = await import("node:path");
|
|
453
631
|
const { fileURLToPath } = await import("node:url");
|
|
454
632
|
const rawDir = config.resourceDir;
|
|
@@ -462,6 +640,20 @@ async function registerResources(fastify, config) {
|
|
|
462
640
|
}
|
|
463
641
|
resolvedResources = discovered;
|
|
464
642
|
}
|
|
643
|
+
if (modules.length) {
|
|
644
|
+
const moduleResources = [];
|
|
645
|
+
for (const m of modules) {
|
|
646
|
+
if (!m.resources) continue;
|
|
647
|
+
try {
|
|
648
|
+
const rs = typeof m.resources === "function" ? await m.resources(fastify) : m.resources;
|
|
649
|
+
moduleResources.push(...rs);
|
|
650
|
+
} catch (err) {
|
|
651
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
652
|
+
throw new Error(`[arc] module "${m.name}" resources factory threw: ${msg}. Check the module's bootstrap ran and its adapters received booted engines.`, { cause: err });
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (moduleResources.length) resolvedResources = [...moduleResources, ...resolvedResources ?? []];
|
|
656
|
+
}
|
|
465
657
|
if (resolvedResources && resolvedResources.length > 0) {
|
|
466
658
|
const seen = /* @__PURE__ */ new Set();
|
|
467
659
|
for (const resource of resolvedResources) if (resource.name) {
|
|
@@ -490,6 +682,15 @@ async function registerResources(fastify, config) {
|
|
|
490
682
|
const hints = discoveryYieldedZero ? ` but yielded 0 resources. Check the path, file naming (*.resource.{ts,js,mts,mjs}), and runtime layout (src/ vs dist/). Use \`strictResourceDir: true\` to fail boot.` : "";
|
|
491
683
|
fastify.log.warn(`0 resources registered${prefix}${scanned}${hints}`);
|
|
492
684
|
}
|
|
685
|
+
for (const m of modules) {
|
|
686
|
+
if (!m.afterResources) continue;
|
|
687
|
+
try {
|
|
688
|
+
await m.afterResources(fastify);
|
|
689
|
+
} catch (err) {
|
|
690
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
691
|
+
throw new Error(`[arc] module "${m.name}" afterResources() threw: ${msg}`, { cause: err });
|
|
692
|
+
}
|
|
693
|
+
}
|
|
493
694
|
if (config.afterResources) {
|
|
494
695
|
await config.afterResources(fastify);
|
|
495
696
|
fastify.log.debug("afterResources hook executed");
|
|
@@ -500,12 +701,12 @@ async function registerResources(fastify, config) {
|
|
|
500
701
|
await onReady(fastify);
|
|
501
702
|
});
|
|
502
703
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
}
|
|
704
|
+
const closers = modules.filter((m) => m.onClose).reverse();
|
|
705
|
+
const appOnClose = config.onClose;
|
|
706
|
+
if (closers.length || appOnClose) fastify.addHook("onClose", async () => {
|
|
707
|
+
for (const m of closers) await m.onClose?.(fastify);
|
|
708
|
+
if (appOnClose) await appOnClose(fastify);
|
|
709
|
+
});
|
|
509
710
|
}
|
|
510
711
|
//#endregion
|
|
511
712
|
//#region src/factory/registerSecurity.ts
|
|
@@ -522,7 +723,8 @@ function buildRateLimitOpts(input) {
|
|
|
522
723
|
};
|
|
523
724
|
const matchesPath = compilePathMatcher(skipPaths);
|
|
524
725
|
const combined = async (req, key) => {
|
|
525
|
-
|
|
726
|
+
const path = (req.url ?? "").split("?", 1)[0] ?? "";
|
|
727
|
+
if (matchesPath(path)) return true;
|
|
526
728
|
if (typeof allowList === "function") return await allowList(req, key);
|
|
527
729
|
if (Array.isArray(allowList)) return allowList.includes(key);
|
|
528
730
|
return false;
|
|
@@ -711,7 +913,7 @@ var createApp_exports = /* @__PURE__ */ __exportAll({
|
|
|
711
913
|
createApp: () => createApp,
|
|
712
914
|
resolveLoggerConfig: () => resolveLoggerConfig
|
|
713
915
|
});
|
|
714
|
-
const MEMORY_STORE_NAMES = new Set(["memory", "memory-cache"]);
|
|
916
|
+
const MEMORY_STORE_NAMES = /* @__PURE__ */ new Set(["memory", "memory-cache"]);
|
|
715
917
|
/**
|
|
716
918
|
* Default redact paths layered into Fastify's pino logger when the host
|
|
717
919
|
* doesn't supply a `logger.redact` of their own. Covers the common token /
|
|
@@ -813,7 +1015,7 @@ Missing:\n${lines}\n\nFix: pass durable adapters under \`stores\`, or use runtim
|
|
|
813
1015
|
*
|
|
814
1016
|
* Boot order:
|
|
815
1017
|
* ```
|
|
816
|
-
* 0. Logger, validation, preset merge
|
|
1018
|
+
* 0. Logger, validation → beforeBoot() (DB connect etc.), preset merge
|
|
817
1019
|
* 1. Create Fastify instance
|
|
818
1020
|
* 2. Security plugins (Helmet, CORS, Rate Limit) — opt-out
|
|
819
1021
|
* 3. Utility plugins (Under Pressure, Sensible, Multipart, Raw Body)
|
|
@@ -834,10 +1036,13 @@ async function createApp(options) {
|
|
|
834
1036
|
}
|
|
835
1037
|
validateAuthOptions(options);
|
|
836
1038
|
const deferredWarnings = validateDistributedRuntime(options);
|
|
1039
|
+
if (options.beforeBoot) await options.beforeBoot();
|
|
837
1040
|
const config = {
|
|
838
1041
|
...options.preset ? getPreset(options.preset) : {},
|
|
839
1042
|
...options
|
|
840
1043
|
};
|
|
1044
|
+
const resolvedModules = orderModules(await Promise.all((config.modules ?? []).map((m) => resolveModule(m))));
|
|
1045
|
+
const moduleErrorMappers = resolvedModules.flatMap((m) => m.errorMappers ?? []);
|
|
841
1046
|
const fastify = Fastify({
|
|
842
1047
|
logger: resolveLoggerConfig(config.logger),
|
|
843
1048
|
trustProxy: config.trustProxy ?? false,
|
|
@@ -893,8 +1098,8 @@ async function createApp(options) {
|
|
|
893
1098
|
decorateRequestScope(fastify);
|
|
894
1099
|
await registerAuth(fastify, config, trackPlugin);
|
|
895
1100
|
await registerElevation(fastify, config, trackPlugin);
|
|
896
|
-
await registerErrorHandler(fastify, config, trackPlugin);
|
|
897
|
-
await registerResources(fastify, config);
|
|
1101
|
+
await registerErrorHandler(fastify, config, trackPlugin, moduleErrorMappers);
|
|
1102
|
+
await registerResources(fastify, config, resolvedModules);
|
|
898
1103
|
if (config.replyHelpers) {
|
|
899
1104
|
const { replyHelpersPlugin } = await import("./replyHelpers-67tRawQZ.mjs").then((n) => n.n);
|
|
900
1105
|
await fastify.register(replyHelpersPlugin);
|
|
@@ -947,4 +1152,4 @@ const ArcFactory = {
|
|
|
947
1152
|
}
|
|
948
1153
|
};
|
|
949
1154
|
//#endregion
|
|
950
|
-
export { edgePreset as a, testingPreset as c, developmentPreset as i, createApp as n, getPreset as o, createApp_exports as r, productionPreset as s, ArcFactory as t };
|
|
1155
|
+
export { edgePreset as a, testingPreset as c, orderModules as d, resolveModule as f, developmentPreset as i, defineModule as l, createApp as n, getPreset as o, createApp_exports as r, productionPreset as s, ArcFactory as t, getModuleExports as u };
|
package/dist/docs/index.d.mts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { p as RegistryEntry } from "../index-
|
|
1
|
+
import { p as RegistryEntry } from "../index-Dtrj6XZk.mjs";
|
|
2
2
|
import { t as ExternalOpenApiPaths } from "../externalPaths-BE6wFpYv.mjs";
|
|
3
3
|
import { FastifyPluginAsync } from "fastify";
|
|
4
|
-
|
|
5
4
|
//#region src/docs/openapi/types.d.ts
|
|
6
5
|
/**
|
|
7
6
|
* OpenAPI 3.0 type primitives used by arc's spec emitter.
|
package/dist/docs/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as getUserRoles } from "../types-XTApa8b6.mjs";
|
|
2
|
-
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-
|
|
2
|
+
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-Kyod_di6.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/docs/scalar.ts
|
|
5
5
|
const scalarPlugin = async (fastify, opts = {}) => {
|
|
@@ -1,135 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
//#region src/encryption/types.d.ts
|
|
4
|
-
/** JWE key-management algorithm — the protected-header `alg`. */
|
|
5
|
-
type JweAlg = "RSA-OAEP-256" | "RSA-OAEP-384" | "RSA-OAEP-512" | "ECDH-ES" | "ECDH-ES+A256KW" | "A256GCMKW" | "dir";
|
|
6
|
-
/** JWE content-encryption algorithm — the protected-header `enc`. AEAD only. */
|
|
7
|
-
type JweEnc = "A128GCM" | "A192GCM" | "A256GCM";
|
|
8
|
-
/** Encryption strategy for a route's response. */
|
|
9
|
-
type EncryptionMode = "jwe" | "fields";
|
|
10
|
-
/**
|
|
11
|
-
* Opaque key material. For `jwe` mode this is a `jose` `KeyLike` /
|
|
12
|
-
* `CryptoKey` / `Uint8Array`; for `fields` mode a 32-byte secret
|
|
13
|
-
* (`Buffer` / `Uint8Array`). Kept `unknown` at the contract layer so the
|
|
14
|
-
* `KeyProvider` interface carries no hard `jose` type dependency — hosts
|
|
15
|
-
* without `jose` installed can still implement field-mode providers.
|
|
16
|
-
*/
|
|
17
|
-
type KeyMaterial = unknown;
|
|
18
|
-
/** A key paired with its identifier — what encrypt operations consume. */
|
|
19
|
-
interface ActiveKey {
|
|
20
|
-
/** Key id, stamped into the JWE `kid` header / field envelope for rotation. */
|
|
21
|
-
readonly kid: string;
|
|
22
|
-
/** The key material (public key for JWE, 32-byte secret for fields). */
|
|
23
|
-
readonly key: KeyMaterial;
|
|
24
|
-
/** Optional per-key `alg` override (else the plugin default applies). */
|
|
25
|
-
readonly alg?: JweAlg;
|
|
26
|
-
}
|
|
27
|
-
/** Context handed to the key provider so resolution can be request-scoped. */
|
|
28
|
-
interface KeyContext {
|
|
29
|
-
readonly request: FastifyRequest;
|
|
30
|
-
}
|
|
31
|
-
type MaybePromise<T> = T | Promise<T>;
|
|
32
|
-
/**
|
|
33
|
-
* Pluggable key resolution. Arc owns the request-time plumbing — `kid` →
|
|
34
|
-
* key, algorithm allowlist enforcement; the host owns the trust store —
|
|
35
|
-
* which keys are valid and where they live (file / env / AWS KMS / GCP KMS
|
|
36
|
-
* / Vault). Encryption and decryption are deliberately separate operations
|
|
37
|
-
* because they often use DIFFERENT keys (JWE: a public key encrypts the
|
|
38
|
-
* outbound response, a private key by `kid` decrypts the inbound request —
|
|
39
|
-
* exactly Visa's two-key-pair model).
|
|
40
|
-
*
|
|
41
|
-
* Rotation mirrors `@fastify/secure-session`: the active key encrypts; any
|
|
42
|
-
* key resolvable by `kid` can decrypt, so you can roll keys without dropping
|
|
43
|
-
* in-flight traffic.
|
|
44
|
-
*/
|
|
45
|
-
interface KeyProvider {
|
|
46
|
-
/** Active key for encrypting OUTBOUND responses. */
|
|
47
|
-
encryptionKey(ctx: KeyContext): MaybePromise<ActiveKey>;
|
|
48
|
-
/** Resolve a DECRYPTION key by the inbound message's `kid`. */
|
|
49
|
-
decryptionKey(kid: string, ctx: KeyContext): MaybePromise<KeyMaterial | undefined>;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Per-resource encryption directive — the typed slice plugins register on
|
|
53
|
-
* `ResourceExtensions`. Declared on a resource via
|
|
54
|
-
* `defineResource({ extensions: { encryption: { … } } })`; arc threads it
|
|
55
|
-
* to request time on `request.routeOptions.config.arcExtensions.encryption`.
|
|
56
|
-
*/
|
|
57
|
-
interface EncryptionDirective {
|
|
58
|
-
/** Strategy. Defaults to the plugin-level `mode` (default `'jwe'`). */
|
|
59
|
-
readonly mode?: EncryptionMode;
|
|
60
|
-
/**
|
|
61
|
-
* For `mode: 'fields'` — dot-paths of fields to encrypt, resolved against
|
|
62
|
-
* the serialized response body (e.g. `'cardNumber'`, `'account.iban'`).
|
|
63
|
-
* Ignored for `mode: 'jwe'` (the whole body is encrypted).
|
|
64
|
-
*/
|
|
65
|
-
readonly fields?: readonly string[];
|
|
66
|
-
/**
|
|
67
|
-
* Explicit opt-out — set `false` to disable encryption for this resource
|
|
68
|
-
* even when a plugin-level default (`routes`) would otherwise apply.
|
|
69
|
-
*/
|
|
70
|
-
readonly enabled?: boolean;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Plugin-level options. `keyProvider` is the only required field; everything
|
|
74
|
-
* else has a secure default.
|
|
75
|
-
*/
|
|
76
|
-
interface EncryptionOptions {
|
|
77
|
-
/**
|
|
78
|
-
* Master on/off. Default `true`. Set `false` to register the plugin as a
|
|
79
|
-
* typed no-op (config-gated environments) — same pattern as
|
|
80
|
-
* `idempotencyPlugin`.
|
|
81
|
-
*/
|
|
82
|
-
readonly enabled?: boolean;
|
|
83
|
-
/** Key provider — resolves `kid` → key for encrypt/decrypt. Required. */
|
|
84
|
-
readonly keyProvider: KeyProvider;
|
|
85
|
-
/** Default mode when a route opts in without naming one. Default `'jwe'`. */
|
|
86
|
-
readonly mode?: EncryptionMode;
|
|
87
|
-
/** JWE key-management algorithm. Default `'RSA-OAEP-256'`. */
|
|
88
|
-
readonly alg?: JweAlg;
|
|
89
|
-
/** JWE content-encryption algorithm. Default `'A256GCM'`. */
|
|
90
|
-
readonly enc?: JweEnc;
|
|
91
|
-
/**
|
|
92
|
-
* Inbound-decryption algorithm allowlists — block algorithm-substitution
|
|
93
|
-
* downgrade attacks. Default `[alg]` / `[enc]`. Set explicitly only when a
|
|
94
|
-
* client legitimately uses more than one algorithm.
|
|
95
|
-
*/
|
|
96
|
-
readonly allowedAlgs?: readonly JweAlg[];
|
|
97
|
-
readonly allowedEncs?: readonly JweEnc[];
|
|
98
|
-
/**
|
|
99
|
-
* Fallback opt-in for routes that don't declare
|
|
100
|
-
* `extensions.encryption`. Return a directive (or `true` for the default
|
|
101
|
-
* mode) to encrypt, `false`/`undefined` to skip. The per-resource
|
|
102
|
-
* `extensions.encryption` ALWAYS wins when present. Use this to scope ALE
|
|
103
|
-
* to sensitive prefixes without touching every resource.
|
|
104
|
-
*
|
|
105
|
-
* @example confine ALE to `/payments` and `/accounts`
|
|
106
|
-
* ```ts
|
|
107
|
-
* routes: (req) => /^\/(payments|accounts)\b/.test(req.url)
|
|
108
|
-
* ```
|
|
109
|
-
*/
|
|
110
|
-
readonly routes?: (request: FastifyRequest) => EncryptionDirective | boolean | undefined;
|
|
111
|
-
/** Decrypt inbound `application/jose` request bodies. Default `true`. */
|
|
112
|
-
readonly decryptRequests?: boolean;
|
|
113
|
-
/** Media type that signals an encrypted REQUEST body. Default `'application/jose'`. */
|
|
114
|
-
readonly contentType?: string;
|
|
115
|
-
/** Content-Type set on full-body JWE RESPONSES. Default `'application/jose'`. */
|
|
116
|
-
readonly responseContentType?: string;
|
|
117
|
-
/**
|
|
118
|
-
* Response header advertising that the body is encrypted, so generic
|
|
119
|
-
* client SDKs know to decrypt. Default `'x-encrypted'`; set `false` to omit.
|
|
120
|
-
*/
|
|
121
|
-
readonly flagHeader?: string | false;
|
|
122
|
-
}
|
|
123
|
-
/** Request decorations added by the plugin. */
|
|
124
|
-
declare module "fastify" {
|
|
125
|
-
interface FastifyRequest {
|
|
126
|
-
/** `true` when the inbound body was decrypted from a JWE by this plugin. */
|
|
127
|
-
requestDecrypted?: boolean;
|
|
128
|
-
/** `true` when the outbound body was encrypted by this plugin. */
|
|
129
|
-
responseEncrypted?: boolean;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
//#endregion
|
|
1
|
+
import { a as JweAlg, c as KeyMaterial, i as EncryptionOptions, l as KeyProvider, n as EncryptionDirective, o as JweEnc, r as EncryptionMode, s as KeyContext, t as ActiveKey } from "../types-Cc91nXuA.mjs";
|
|
2
|
+
import { FastifyPluginAsync } from "fastify";
|
|
133
3
|
//#region src/encryption/encryptionPlugin.d.ts
|
|
134
4
|
declare const encryptionPlugin: FastifyPluginAsync<EncryptionOptions>;
|
|
135
5
|
declare const _default: FastifyPluginAsync<EncryptionOptions>;
|
|
@@ -207,16 +77,4 @@ interface StaticKeyProviderOptions {
|
|
|
207
77
|
*/
|
|
208
78
|
declare function createStaticKeyProvider(options: StaticKeyProviderOptions): KeyProvider;
|
|
209
79
|
//#endregion
|
|
210
|
-
//#region src/encryption/index.d.ts
|
|
211
|
-
declare module "../types/resource.js" {
|
|
212
|
-
interface ResourceExtensions {
|
|
213
|
-
/**
|
|
214
|
-
* Encrypt this resource's responses — full-body JWE or specific fields.
|
|
215
|
-
* Threaded to request time on
|
|
216
|
-
* `request.routeOptions.config.arcExtensions.encryption`.
|
|
217
|
-
*/
|
|
218
|
-
encryption?: EncryptionDirective;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
//#endregion
|
|
222
80
|
export { type ActiveKey, type EncryptionDirective, type EncryptionMode, type EncryptionOptions, type JweAlg, type JweEnc, type KeyContext, type KeyMaterial, type KeyProvider, type ParsedFieldEnvelope, type StaticKeyProviderOptions, createStaticKeyProvider, decryptField, _default as default, encryptField, encryptionPlugin, parseFieldEnvelope };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "./EventTransport-
|
|
1
|
+
import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "./EventTransport-QmY7A258.mjs";
|
|
2
2
|
import { FastifyPluginAsync } from "fastify";
|
|
3
|
-
|
|
4
3
|
//#region src/events/defineEvent.d.ts
|
|
5
4
|
interface EventSchema {
|
|
6
5
|
type: "object";
|
|
@@ -249,7 +248,8 @@ interface EventPluginOptions {
|
|
|
249
248
|
* failed events are published to the `$deadLetter` event type by default.
|
|
250
249
|
*/
|
|
251
250
|
deadLetterQueue?: {
|
|
252
|
-
/** Custom store function. If omitted, publishes to '$deadLetter' event type. */
|
|
251
|
+
/** Custom store function. If omitted, publishes to '$deadLetter' event type. */
|
|
252
|
+
store?: (event: DomainEvent, errors: Error[]) => void | Promise<void>;
|
|
253
253
|
};
|
|
254
254
|
/** Callback after successful publish (for metrics/tracking) */
|
|
255
255
|
onPublish?: (event: DomainEvent) => void;
|
|
@@ -302,9 +302,13 @@ interface EventPluginOptions {
|
|
|
302
302
|
declare module "fastify" {
|
|
303
303
|
interface FastifyInstance {
|
|
304
304
|
events: {
|
|
305
|
-
/** Publish an event */
|
|
306
|
-
|
|
307
|
-
|
|
305
|
+
/** Publish an event */
|
|
306
|
+
publish: <T>(type: string, payload: T, meta?: Partial<DomainEvent["meta"]>) => Promise<void>;
|
|
307
|
+
/** Subscribe to events */
|
|
308
|
+
subscribe: (pattern: string, handler: EventHandler) => Promise<() => void>;
|
|
309
|
+
/** Get transport name */
|
|
310
|
+
transportName: string;
|
|
311
|
+
/** Event registry for introspection (undefined when no registry configured) */
|
|
308
312
|
registry?: EventRegistry;
|
|
309
313
|
};
|
|
310
314
|
}
|
package/dist/events/index.d.mts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { a as EventTransport, i as EventLogger, n as DomainEvent, o as MemoryEventTransport, r as EventHandler, s as MemoryEventTransportOptions, t as DeadLetteredEvent } from "../EventTransport-
|
|
2
|
-
import { a as withRetry, c as EventDefinitionOutput, d as EventSchema, f as ValidationResult, i as createDeadLetterPublisher, l as EventRegistry, m as defineEvent, n as eventPlugin, o as CustomValidator, p as createEventRegistry, r as RetryOptions, s as EventDefinitionInput, t as EventPluginOptions, u as EventRegistryOptions } from "../eventPlugin-
|
|
1
|
+
import { a as EventTransport, i as EventLogger, n as DomainEvent, o as MemoryEventTransport, r as EventHandler, s as MemoryEventTransportOptions, t as DeadLetteredEvent } from "../EventTransport-QmY7A258.mjs";
|
|
2
|
+
import { a as withRetry, c as EventDefinitionOutput, d as EventSchema, f as ValidationResult, i as createDeadLetterPublisher, l as EventRegistry, m as defineEvent, n as eventPlugin, o as CustomValidator, p as createEventRegistry, r as RetryOptions, s as EventDefinitionInput, t as EventPluginOptions, u as EventRegistryOptions } from "../eventPlugin-A6EbthXR.mjs";
|
|
3
3
|
import { RedisEventTransportOptions, RedisLike } from "./transports/redis.mjs";
|
|
4
|
-
import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-
|
|
4
|
+
import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-iVR2l7aS.mjs";
|
|
5
5
|
import { RepositoryLike } from "@classytic/repo-core/adapter";
|
|
6
|
-
|
|
7
6
|
//#region src/events/eventTypes.d.ts
|
|
8
7
|
/**
|
|
9
8
|
* Event Type Constants and Helpers
|
|
@@ -38,14 +37,18 @@ type CrudEventSuffix = (typeof CRUD_EVENT_SUFFIXES)[number];
|
|
|
38
37
|
declare function crudEventType(resource: string, suffix: CrudEventSuffix): string;
|
|
39
38
|
/** Arc framework lifecycle events — emitted automatically by the framework */
|
|
40
39
|
declare const ARC_LIFECYCLE_EVENTS: Readonly<{
|
|
41
|
-
/** Emitted when a resource plugin is registered */
|
|
40
|
+
/** Emitted when a resource plugin is registered */
|
|
41
|
+
readonly RESOURCE_REGISTERED: "arc.resource.registered";
|
|
42
|
+
/** Emitted when Arc is fully ready (all resources registered, onReady fired) */
|
|
42
43
|
readonly READY: "arc.ready";
|
|
43
44
|
}>;
|
|
44
45
|
/** Type for Arc lifecycle event names */
|
|
45
46
|
type ArcLifecycleEvent = (typeof ARC_LIFECYCLE_EVENTS)[keyof typeof ARC_LIFECYCLE_EVENTS];
|
|
46
47
|
/** Cache-specific event types for observability and external triggers */
|
|
47
48
|
declare const CACHE_EVENTS: Readonly<{
|
|
48
|
-
/** Emitted when a resource's cache version is bumped */
|
|
49
|
+
/** Emitted when a resource's cache version is bumped */
|
|
50
|
+
readonly VERSION_BUMPED: "arc.cache.version.bumped";
|
|
51
|
+
/** Emitted when a tag version is bumped */
|
|
49
52
|
readonly TAG_VERSION_BUMPED: "arc.cache.tag.bumped";
|
|
50
53
|
}>;
|
|
51
54
|
/** Type for cache event names */
|