@classytic/arc 2.41.1 → 2.43.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/{EventTransport-CbUKDzZZ.d.mts → EventTransport-CiicKz2Z.d.mts} +1 -1
- package/dist/{EventTransport-DSqD_Xsg.mjs → EventTransport-tzA7CAB4.mjs} +22 -17
- package/dist/{HookSystem-CMF94SEA.mjs → HookSystem-B6UaKRbF.mjs} +2 -2
- package/dist/{QueryCache-DFA-QrU9.d.mts → QueryCache--5uAuYPW.d.mts} +7 -3
- package/dist/{ResourceRegistry-MGpXoxHZ.mjs → ResourceRegistry-CuCARu2B.mjs} +44 -13
- package/dist/actionPermissions-BotX1YOa.mjs +776 -0
- package/dist/audit/index.d.mts +43 -5
- package/dist/audit/index.mjs +48 -26
- package/dist/auth/audit.mjs +1 -1
- package/dist/auth/index.d.mts +43 -13
- package/dist/auth/index.mjs +130 -155
- package/dist/auth/redis-session.mjs +1 -1
- package/dist/{authorizationDecision-DawKGoXb.mjs → authorizationDecision-DQYrkl5f.mjs} +7 -102
- package/dist/{base-CxrV1LmG.d.mts → base-CZyixEQR.d.mts} +27 -5
- package/dist/boundedMap-BULzfXYU.mjs +57 -0
- package/dist/{buildHandler-CvxKOc0m.mjs → buildHandler-CRQ8EcX5.mjs} +4 -4
- package/dist/cache/index.d.mts +22 -34
- package/dist/cache/index.mjs +4 -4
- package/dist/{caching-DbCBxlgw.mjs → caching-Cc2M1DwO.mjs} +3 -3
- package/dist/claimed-B10RTfkM.mjs +8 -0
- package/dist/cleanup/index.d.mts +6 -10
- package/dist/cleanup/index.mjs +27 -73
- package/dist/cli/commands/describe.d.mts +4 -2
- package/dist/cli/commands/describe.mjs +7 -4
- package/dist/cli/commands/docs.mjs +3 -3
- package/dist/cli/commands/doctor.mjs +1 -1
- package/dist/cli/commands/generate.mjs +2 -2
- package/dist/cli/commands/init.mjs +2 -2
- package/dist/cli/commands/introspect.mjs +2 -2
- package/dist/cli/index.mjs +3 -3
- package/dist/{constants-D5Ubn1fe.mjs → constants-Cu7Mx7_U.mjs} +4 -2
- package/dist/context/index.d.mts +2 -5
- package/dist/context/index.mjs +18 -38
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +8 -8
- package/dist/{core-5lazhva9.mjs → core-Cb0LjccA.mjs} +5 -4
- package/dist/{createActionRouter-DNqoaK81.mjs → createActionRouter-xjqonMRO.mjs} +87 -39
- package/dist/{createAggregationRouter-BcbQJf10.mjs → createAggregationRouter-zRYWw_5D.mjs} +11 -8
- package/dist/{preloadResources-f3boenVZ.mjs → createApp-D-953ZAq.mjs} +299 -390
- package/dist/{createMcpServer-CbDWkZ1T.d.mts → createMcpServer-C9cxS8zt.d.mts} +1 -1
- package/dist/{defineAggregation-D_eJjm5A.mjs → defineAggregation-Bm8RjMbq.mjs} +1 -1
- package/dist/{defineResource-DxkMTjRn.mjs → defineResource-DIC7JZGU.mjs} +118 -1024
- package/dist/discovery/index.mjs +1 -1
- package/dist/docs/index.d.mts +10 -4
- package/dist/docs/index.mjs +4 -4
- package/dist/{elevation-BXCfVcNG.d.mts → elevation-BW2J3tNj.d.mts} +1 -2
- package/dist/{elevation-CkvG0LG2.mjs → elevation-DG8SrLN-.mjs} +41 -12
- package/dist/encryption/index.mjs +3 -3
- package/dist/{errorHandler-D1GHKGlV.mjs → errorHandler-BnfDviL1.mjs} +26 -5
- package/dist/{errors-BH7YU_9O.mjs → errors-CtrywMHP.mjs} +58 -23
- package/dist/{errors-BGdVwP25.d.mts → errors-JYc9vwqE.d.mts} +9 -0
- package/dist/{eventPlugin-B52abJ8W.d.mts → eventPlugin-BtmA4-3P.d.mts} +65 -9
- package/dist/{eventPlugin-zZY-unhr.mjs → eventPlugin-Q6BhzHzs.mjs} +82 -21
- package/dist/{subscribe-helpers-DatDkcuG.mjs → eventTypes-Cdhm6ZlV.mjs} +151 -17
- package/dist/events/index.d.mts +33 -8
- package/dist/events/index.mjs +49 -53
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +139 -157
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/events/transports/redis.mjs +2 -2
- package/dist/factory/index.d.mts +32 -17
- package/dist/factory/index.mjs +213 -32
- package/dist/{fastify-augmentation-Cq2nHVaW.d.mts → fastify-augmentation-DhUJ-mvo.d.mts} +11 -25
- package/dist/{fields-I_EeRWND.d.mts → fields-BBSpf8pS.d.mts} +1 -1
- package/dist/{fields-BHrkoF5y.mjs → fields-ToPYpMqS.mjs} +1 -1
- package/dist/filter-merge-IpGywvym.mjs +99 -0
- package/dist/{generate-BLRf2m3y.mjs → generate-DRTkLlIl.mjs} +2 -2
- package/dist/{headers-BIXWKrzQ.mjs → headers-BbkIF6vA.mjs} +2 -2
- package/dist/health-D-Ft6JM6.mjs +22 -0
- package/dist/{health-ZRHeDHXH.mjs → health-DqbsG-_w.mjs} +115 -55
- package/dist/hooks/index.d.mts +2 -2
- package/dist/hooks/index.mjs +2 -2
- package/dist/idempotency/index.d.mts +31 -7
- package/dist/idempotency/index.mjs +155 -98
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +48 -11
- package/dist/{index-BHdZD8zK.d.mts → index-Bxriq3so.d.mts} +84 -21
- package/dist/{preloadResources-CjS2OfpU.d.mts → index-C8zVGYsk.d.mts} +37 -38
- package/dist/{index-D4u76eQW.d.mts → index-CRoPtISf.d.mts} +23 -73
- package/dist/{index-CbJiYrfv.d.mts → index-Chgz3d0i.d.mts} +1 -1
- package/dist/index-JdbZAIsm.d.mts +510 -0
- package/dist/{index-DHbyPo5j.d.mts → index-mqaqZNpe.d.mts} +253 -254
- package/dist/index.d.mts +8 -8
- package/dist/index.mjs +11 -11
- package/dist/{init-CpLkjU18.mjs → init-B_oUYDXI.mjs} +1 -1
- package/dist/integrations/event-gateway.d.mts +8 -3
- package/dist/integrations/event-gateway.mjs +4 -4
- package/dist/integrations/index.d.mts +3 -3
- package/dist/integrations/index.mjs +1 -1
- package/dist/integrations/jobs/index.d.mts +1 -1
- package/dist/integrations/jobs/index.mjs +18 -5
- package/dist/integrations/mcp/index.d.mts +72 -59
- package/dist/integrations/mcp/index.mjs +19 -4
- package/dist/integrations/mcp/testing.d.mts +2 -2
- package/dist/integrations/mcp/testing.mjs +2 -2
- package/dist/integrations/streamline.d.mts +78 -136
- package/dist/integrations/streamline.mjs +116 -54
- package/dist/integrations/webhooks.d.mts +60 -120
- package/dist/integrations/webhooks.mjs +36 -80
- package/dist/integrations/websocket-pushref-redis.d.mts +1 -1
- package/dist/integrations/websocket-pushref-redis.mjs +1 -1
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket-redis.mjs +1 -1
- package/dist/integrations/websocket.d.mts +3 -3
- package/dist/integrations/websocket.mjs +2 -2
- package/dist/{interface-DEWNOMza.d.mts → interface-C0395yUN.d.mts} +13 -1
- package/dist/{jsonBody-ELR9lfeU.mjs → jsonBody-B4YVGcql.mjs} +2 -2
- package/dist/{keys-cpZCtcIE.mjs → keys-B2KtgMLJ.mjs} +7 -18
- package/dist/{loadResources-BPfWiI5s.mjs → loadResources-Dn4lalw2.mjs} +3 -3
- package/dist/{loadResourcesFromEntry-ixtfL3J2.mjs → loadResourcesFromEntry-3sLYbuse.mjs} +2 -2
- package/dist/logger/index.d.mts +1 -8
- package/dist/logger/index.mjs +3 -153
- package/dist/logger-DXjfIqjV.mjs +160 -0
- package/dist/{mcpPlugin-BX5PujI0.mjs → mcpPlugin-C08HalN5.mjs} +433 -170
- package/dist/{memory-Iotz0cCx.mjs → memory-9Cp7611C.mjs} +36 -34
- package/dist/{metrics-NMsmpRmY.mjs → metrics-CWI11eRu.mjs} +39 -72
- package/dist/middleware/index.d.mts +2 -2
- package/dist/middleware/index.mjs +3 -3
- package/dist/migrations/index.mjs +2 -2
- package/dist/{module-DpLJG2_N.mjs → module-BSvOQC7E.mjs} +8 -8
- package/dist/multiTenant-cY0FkDiA.d.mts +104 -0
- package/dist/{multipartBody-DtRWM6_U.mjs → multipartBody-CXM2a-Dy.mjs} +46 -7
- package/dist/{openapi-CmFf3yq_.mjs → openapi-DT_G4VhY.mjs} +48 -10
- package/dist/outbox-admin/index.d.mts +3 -3
- package/dist/outbox-admin/index.mjs +6 -18
- package/dist/{outbox-module-D2z5WkDY.mjs → outbox-module-Bjgzl6tg.mjs} +203 -33
- package/dist/{outbox-module-BHdUnuuK.d.mts → outbox-module-gZCHoE4H.d.mts} +79 -6
- package/dist/permissions/index.d.mts +5 -4
- package/dist/permissions/index.mjs +8 -6
- package/dist/{permissions-D8TBH44E.mjs → permissions-Cm0uYHvd.mjs} +149 -33
- package/dist/{pipe-FMWNheSO.mjs → pipe-BqeGT6UO.mjs} +2 -2
- package/dist/pipeline/index.d.mts +2 -2
- package/dist/pipeline/index.mjs +2 -2
- package/dist/plan-FVR6LP59.mjs +565 -0
- package/dist/{BaseController-CYEwjyjN.mjs → plugin-BccET1tL.mjs} +1256 -345
- package/dist/plugins/index.d.mts +14 -11
- package/dist/plugins/index.mjs +44 -22
- package/dist/plugins/response-cache.mjs +8 -14
- package/dist/plugins/tracing-entry.mjs +2 -2
- package/dist/{pluralize-CXWZIzTv.mjs → pluralize-CVIOcgMF.mjs} +1 -1
- package/dist/presets/filesUpload.d.mts +2 -2
- package/dist/presets/filesUpload.mjs +5 -5
- package/dist/presets/index.d.mts +26 -31
- package/dist/presets/index.mjs +4 -4
- package/dist/presets/multiTenant.d.mts +2 -107
- package/dist/presets/multiTenant.mjs +22 -162
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +2 -2
- package/dist/{presets-ZJSPgmT2.mjs → presets-Cpvj6fGO.mjs} +99 -107
- package/dist/{projection-BzIgzoKZ.mjs → projection-Ce4aQhQk.mjs} +2 -2
- package/dist/{pushref-registry-Dov9l4qE.d.mts → pushref-registry-UxCkoA4C.d.mts} +1 -1
- package/dist/{queryCachePlugin-BzqxconG.d.mts → queryCachePlugin-C2lZjTxk.d.mts} +1 -1
- package/dist/{queryCachePlugin-DZHPOm9p.mjs → queryCachePlugin-CFWNH8aT.mjs} +12 -14
- package/dist/{rateLimitKey-BvOvcBDi.mjs → rateLimitKey-C4O-kPm0.mjs} +1 -1
- package/dist/{realtime-8tIQWZz4.mjs → realtime-DmPE9S54.mjs} +25 -21
- package/dist/redis-stream-BMk-bpQS.d.mts +219 -0
- package/dist/{redis-Bzi1y3F9.d.mts → redis-vLvtcGy_.d.mts} +13 -24
- package/dist/redisShapes-CQksY67q.d.mts +55 -0
- package/dist/registry/index.d.mts +9 -4
- package/dist/registry/index.mjs +4 -4
- package/dist/{registry-DNkgGQcK.mjs → registry-Bcz9G1bh.mjs} +10 -8
- package/dist/{renewingLease-yF8BdW4b.mjs → renewingLease-qODa7KJQ.mjs} +1 -1
- package/dist/{replyHelpers-CNdvMfM4.mjs → replyHelpers-CG_VtVgS.mjs} +2 -2
- package/dist/{requestContext-cDkAUr-P.mjs → requestContext-Dsu9P1bV.mjs} +1 -1
- package/dist/requirement-CbiLdQ1n.d.mts +22 -0
- package/dist/requirement-KDdoRcOP.mjs +35 -0
- package/dist/resourceRateLimit-Wf6d6FL9.d.mts +30 -0
- package/dist/resourceRateLimit-dSjVr1pk.mjs +180 -0
- package/dist/{rolldown-runtime-eysyzFAF.mjs → rolldown-runtime-D1w9Y1VJ.mjs} +1 -1
- package/dist/routeValidator-D36SW8qt.mjs +70 -0
- package/dist/{runtimeCapabilities-CkhS6jn6.mjs → runtimeCapabilities-0tccjRlj.mjs} +1 -1
- package/dist/{schedules-j6uXXAeJ.mjs → schedules-C-OpmHBq.mjs} +46 -37
- package/dist/{schemaConverter-BVA7cFRG.mjs → schemaConverter-BORek3O-.mjs} +2 -2
- package/dist/{schemaIR-CTvPDoPB.mjs → schemaIR-C7Vbp71I.mjs} +2 -2
- package/dist/schemas/index.d.mts +10 -0
- package/dist/schemas/index.mjs +8 -3
- package/dist/scim/index.mjs +2 -2
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +6 -6
- package/dist/{scope-BUi1xV1I.mjs → scope-DQthe88b.mjs} +4 -4
- package/dist/shutdown-MqA81z_P.mjs +59 -0
- package/dist/{simpleEqualityMatcher-C9bzq2gO.mjs → simpleEqualityMatcher-auW8QjXN.mjs} +1 -1
- package/dist/{sse-BeqVKQKu.mjs → sse-CYzQBVFe.mjs} +11 -15
- package/dist/{sseStream-Ppzo-WUH.mjs → sseStream-B5_Afiqn.mjs} +2 -2
- package/dist/{store-helpers-dW71XVWL.mjs → store-helpers-BOzk-CuX.mjs} +1 -1
- package/dist/{streaming-IcRFjWQU.mjs → streaming-DjTQhZrN.mjs} +1 -1
- package/dist/subscriberIdentity-CW39uA8G.mjs +28 -0
- package/dist/tenancy--ed9eH_z.mjs +43 -0
- package/dist/tenantPreparation-BKon6Yox.mjs +67 -0
- package/dist/testing/cleanupStoreContract.mjs +1 -1
- package/dist/testing/index.d.mts +193 -175
- package/dist/testing/index.mjs +286 -212
- package/dist/testing/outboxStoreContract.mjs +137 -2
- package/dist/testing/requiredPorts.d.mts +17 -0
- package/dist/testing/requiredPorts.mjs +65 -0
- package/dist/testing/storageContract.mjs +1 -1
- package/dist/timing-qczZ0YC-.mjs +46 -0
- package/dist/{transactionContext-CPbdlnVw.mjs → transactionContext-DjFkrNG6.mjs} +1 -1
- package/dist/{typeGuards-BLjPgVBm.mjs → typeGuards-CySF12G-.mjs} +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/index.mjs +1 -1
- package/dist/types/storage.mjs +1 -1
- package/dist/{types-CWQn9-IC.d.mts → types-B4Aq4Sdj.d.mts} +22 -9
- package/dist/{types-DfVDvkdP.d.mts → types-BJ-2XqVF.d.mts} +189 -204
- package/dist/{types-CgenB1gv.mjs → types-Bk9NOgOC.mjs} +4 -4
- package/dist/usage/index.d.mts +2 -3
- package/dist/usage/index.mjs +5 -5
- package/dist/{userHelpers-ChRvkuSZ.mjs → userHelpers-aklawRaK.mjs} +1 -1
- package/dist/utils/index.d.mts +84 -223
- package/dist/utils/index.mjs +13 -12
- package/dist/{utils-CxcctrAh.mjs → utils-ec1oTo1n.mjs} +315 -404
- package/dist/{validate-DrYWnOzI.mjs → validate-9yzHC1e2.mjs} +6 -14
- package/dist/{versioning-D6JIK8Tk.d.mts → versioning-Bsb51gGT.d.mts} +28 -13
- package/dist/{versioning-DKkT4hNJ.mjs → versioning-fhhWHV74.mjs} +4 -4
- package/dist/{websocket-BOXVggMf.d.mts → websocket-Cs4awVyD.d.mts} +1 -1
- package/dist/{workScope-CxKqoIFn.mjs → workScope-zpPyge4t.mjs} +2 -2
- package/package.json +16 -12
- package/skills/arc/SKILL.md +13 -4
- package/skills/arc/references/auth.md +8 -5
- package/skills/arc/references/integrations.md +1 -1
- package/skills/arc/references/mcp.md +8 -6
- package/skills/arc/references/multi-tenancy.md +19 -14
- package/skills/arc/references/production.md +27 -10
- package/dist/actionPermissions-BoPGQ5-4.mjs +0 -350
- package/dist/index-CFfLKMH4.d.mts +0 -290
- package/dist/interface-YzIbF7Ii.mjs +0 -17
- package/dist/redis-stream-BPKscsTB.d.mts +0 -260
- package/dist/registerRoute-Bsnk6NiZ.mjs +0 -364
- package/dist/sync/changelog.d.mts +0 -3
- package/dist/sync/changelog.mjs +0 -3
|
@@ -1,350 +0,0 @@
|
|
|
1
|
-
/*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
|
|
2
|
-
import { n as getUserRoles } from "./userHelpers-ChRvkuSZ.mjs";
|
|
3
|
-
import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-CgenB1gv.mjs";
|
|
4
|
-
import { a as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-BHrkoF5y.mjs";
|
|
5
|
-
import { t as buildRequestScopeProjection } from "./projection-BzIgzoKZ.mjs";
|
|
6
|
-
import { t as executePipeline } from "./pipe-FMWNheSO.mjs";
|
|
7
|
-
import { isPaginatedResult, toCanonicalList } from "@classytic/repo-core/pagination";
|
|
8
|
-
//#region src/core/fastifyAdapter.ts
|
|
9
|
-
/** Type guard for Mongoose-like documents with toObject() */
|
|
10
|
-
function isMongooseDoc(obj) {
|
|
11
|
-
return !!obj && typeof obj === "object" && "toObject" in obj && typeof obj.toObject === "function";
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Apply field mask to a single object
|
|
15
|
-
* Filters fields based on include/exclude rules
|
|
16
|
-
*/
|
|
17
|
-
function applyFieldMaskToObject(obj, fieldMask) {
|
|
18
|
-
if (!obj || typeof obj !== "object") return obj;
|
|
19
|
-
const plain = isMongooseDoc(obj) ? obj.toObject() : obj;
|
|
20
|
-
const { include, exclude } = fieldMask;
|
|
21
|
-
if (include && include.length > 0) {
|
|
22
|
-
const filtered = {};
|
|
23
|
-
for (const field of include) if (field in plain) filtered[field] = plain[field];
|
|
24
|
-
return filtered;
|
|
25
|
-
}
|
|
26
|
-
if (exclude && exclude.length > 0) {
|
|
27
|
-
const filtered = { ...plain };
|
|
28
|
-
for (const field of exclude) delete filtered[field];
|
|
29
|
-
return filtered;
|
|
30
|
-
}
|
|
31
|
-
return plain;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Apply field mask to response data (handles both objects and arrays)
|
|
35
|
-
*/
|
|
36
|
-
function applyFieldMask(data, fieldMask) {
|
|
37
|
-
if (!fieldMask) return data;
|
|
38
|
-
if (Array.isArray(data)) return data.map((item) => applyFieldMaskToObject(item, fieldMask));
|
|
39
|
-
if (data && typeof data === "object") return applyFieldMaskToObject(data, fieldMask);
|
|
40
|
-
return data;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Project `FastifyRequest` into the curated `IRequestContext` shape that
|
|
44
|
-
* arc's controllers receive. This is a developer-facing facade, not a
|
|
45
|
-
* portability boundary — arc is Fastify-only. The projection lifts the
|
|
46
|
-
* fields tenant-scoped controllers reach for (`organizationId`,
|
|
47
|
-
* `teamId`, `scope`, `context`, server accessor) onto a flat, typed
|
|
48
|
-
* surface so handler code doesn't have to dig through Fastify's
|
|
49
|
-
* decorator surface or `req.metadata._scope` internals.
|
|
50
|
-
*/
|
|
51
|
-
function createRequestContext(req) {
|
|
52
|
-
const reqWithExtras = req;
|
|
53
|
-
const requestContext = reqWithExtras.context ?? {};
|
|
54
|
-
const srv = req.server;
|
|
55
|
-
const serverAccessor = {
|
|
56
|
-
events: srv && "events" in srv ? srv.events : void 0,
|
|
57
|
-
audit: srv && "audit" in srv ? srv.audit : void 0,
|
|
58
|
-
queryCache: srv && "queryCache" in srv ? srv.queryCache : void 0,
|
|
59
|
-
log: req.log
|
|
60
|
-
};
|
|
61
|
-
const rawScope = reqWithExtras.scope;
|
|
62
|
-
const scopeProjection = buildRequestScopeProjection(rawScope);
|
|
63
|
-
return {
|
|
64
|
-
query: reqWithExtras.query ?? {},
|
|
65
|
-
body: reqWithExtras.body ?? {},
|
|
66
|
-
params: reqWithExtras.params ?? {},
|
|
67
|
-
headers: reqWithExtras.headers,
|
|
68
|
-
user: reqWithExtras.user ? (() => {
|
|
69
|
-
const user = reqWithExtras.user;
|
|
70
|
-
const rawId = user._id ?? user.id;
|
|
71
|
-
const normalizedId = rawId ? String(rawId) : void 0;
|
|
72
|
-
return {
|
|
73
|
-
...user,
|
|
74
|
-
id: normalizedId,
|
|
75
|
-
_id: normalizedId
|
|
76
|
-
};
|
|
77
|
-
})() : null,
|
|
78
|
-
context: requestContext,
|
|
79
|
-
scope: scopeProjection,
|
|
80
|
-
metadata: {
|
|
81
|
-
...reqWithExtras.context,
|
|
82
|
-
arc: reqWithExtras.arc,
|
|
83
|
-
_scope: rawScope,
|
|
84
|
-
_ownershipCheck: reqWithExtras._ownershipCheck,
|
|
85
|
-
_crossTenantRead: reqWithExtras._crossTenantRead,
|
|
86
|
-
_policyFilters: reqWithExtras._policyFilters ?? {},
|
|
87
|
-
log: reqWithExtras.log
|
|
88
|
-
},
|
|
89
|
-
server: serverAccessor
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Get typed auth context from an IRequestContext.
|
|
94
|
-
* Use this in controller overrides to access request context.
|
|
95
|
-
*
|
|
96
|
-
* For org scope, use `getControllerScope(req)` instead.
|
|
97
|
-
*/
|
|
98
|
-
function getControllerContext(req) {
|
|
99
|
-
return req.context ?? req.metadata ?? {};
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Get request scope from an IRequestContext.
|
|
103
|
-
* Returns the RequestScope set by auth adapters.
|
|
104
|
-
*/
|
|
105
|
-
function getControllerScope(req) {
|
|
106
|
-
return req.metadata?._scope ?? PUBLIC_SCOPE;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Compute per-field capability metadata for the current user.
|
|
110
|
-
* Only includes fields that have restrictions — unrestricted fields
|
|
111
|
-
* are omitted (frontend defaults to { readable: true, writable: true }).
|
|
112
|
-
*/
|
|
113
|
-
function computeFieldCapabilities(fieldPerms, effectiveRoles) {
|
|
114
|
-
const caps = {};
|
|
115
|
-
for (const [field, perm] of Object.entries(fieldPerms)) {
|
|
116
|
-
let readable = true;
|
|
117
|
-
let writable = true;
|
|
118
|
-
switch (perm._type) {
|
|
119
|
-
case "hidden":
|
|
120
|
-
readable = false;
|
|
121
|
-
writable = false;
|
|
122
|
-
break;
|
|
123
|
-
case "visibleTo":
|
|
124
|
-
readable = perm.roles?.some((r) => effectiveRoles.includes(r)) ?? false;
|
|
125
|
-
break;
|
|
126
|
-
case "writableBy": writable = perm.roles?.some((r) => effectiveRoles.includes(r)) ?? false;
|
|
127
|
-
}
|
|
128
|
-
caps[field] = {
|
|
129
|
-
readable,
|
|
130
|
-
writable
|
|
131
|
-
};
|
|
132
|
-
}
|
|
133
|
-
return caps;
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Send a controller's `IControllerResponse` through Fastify's reply.
|
|
137
|
-
*
|
|
138
|
-
* The `IControllerResponse<T>` envelope is arc's internal pipeline
|
|
139
|
-
* contract — it's how custom `status`, `meta`, `headers`, and field
|
|
140
|
-
* permissions thread from a handler through interceptors to the wire
|
|
141
|
-
* without each step having to touch `reply` directly. Not a portability
|
|
142
|
-
* abstraction (arc is Fastify-only); a pipeline-internal one.
|
|
143
|
-
*
|
|
144
|
-
* **Auto-envelope of bare returns.** Custom route handlers are
|
|
145
|
-
* expected to return the envelope shape so this function can apply
|
|
146
|
-
* field permissions, attach `fieldCaps`, and flatten paginated lists.
|
|
147
|
-
* A handler that returns raw `T` (array, plain object, primitive) used
|
|
148
|
-
* to silently produce a broken response — `response.data` was
|
|
149
|
-
* `undefined`, the body sent was `undefined`, and any declared
|
|
150
|
-
* `fields:` permission map was bypassed (the leak shape mentora
|
|
151
|
-
* flagged). 2.17 detects bare returns and wraps them into
|
|
152
|
-
* `{ data: response, status: 200 }` before the field-permission
|
|
153
|
-
* pipeline runs, so `fields:` applies to ALL custom routes whether the
|
|
154
|
-
* handler envelopes its return value or not. The envelope shape is
|
|
155
|
-
* still the canonical contract — auto-wrapping is a safety net, not
|
|
156
|
-
* an invitation to drop the envelope.
|
|
157
|
-
*/
|
|
158
|
-
function sendControllerResponse(reply, response, request) {
|
|
159
|
-
if (response === null || typeof response !== "object" || !Object.hasOwn(response, "data")) response = {
|
|
160
|
-
data: response,
|
|
161
|
-
status: 200
|
|
162
|
-
};
|
|
163
|
-
const reqWithExtras = request;
|
|
164
|
-
const fieldMaskConfig = reqWithExtras?.fieldMask;
|
|
165
|
-
const arcMeta = reqWithExtras?.arc;
|
|
166
|
-
const scope = reqWithExtras?.scope ?? PUBLIC_SCOPE;
|
|
167
|
-
const fieldPerms = isElevated(scope) ? void 0 : arcMeta?.fields;
|
|
168
|
-
const effectiveRoles = fieldPerms ? resolveEffectiveRoles(getUserRoles(reqWithExtras?.user), isMember(scope) ? scope.orgRoles : []) : [];
|
|
169
|
-
const fieldCaps = fieldPerms ? computeFieldCapabilities(fieldPerms, effectiveRoles) : void 0;
|
|
170
|
-
const hasFieldRestrictions = !!(fieldMaskConfig || fieldPerms);
|
|
171
|
-
/** Apply both field mask and field-level permissions to a data item */
|
|
172
|
-
const applyPermissions = (data) => {
|
|
173
|
-
let result = fieldMaskConfig ? applyFieldMask(data, fieldMaskConfig) : data;
|
|
174
|
-
if (fieldPerms && result && typeof result === "object") {
|
|
175
|
-
if (Array.isArray(result)) result = result.map((item) => applyFieldReadPermissions(item, fieldPerms, effectiveRoles));
|
|
176
|
-
else result = applyFieldReadPermissions(result, fieldPerms, effectiveRoles);
|
|
177
|
-
}
|
|
178
|
-
return result;
|
|
179
|
-
};
|
|
180
|
-
if (response.headers) for (const [key, value] of Object.entries(response.headers)) reply.header(key, value);
|
|
181
|
-
if (response.data && typeof response.data === "object" && Array.isArray(response.data.data)) {
|
|
182
|
-
const payload = response.data;
|
|
183
|
-
const filteredRows = hasFieldRestrictions ? applyPermissions(payload.data) : payload.data;
|
|
184
|
-
let canonical;
|
|
185
|
-
if (isPaginatedResult(payload)) canonical = toCanonicalList({
|
|
186
|
-
...payload,
|
|
187
|
-
data: filteredRows
|
|
188
|
-
});
|
|
189
|
-
else if (typeof payload.page === "number" && typeof payload.limit === "number" && typeof payload.total === "number") canonical = toCanonicalList({
|
|
190
|
-
method: "offset",
|
|
191
|
-
data: filteredRows,
|
|
192
|
-
page: payload.page,
|
|
193
|
-
limit: payload.limit,
|
|
194
|
-
total: payload.total,
|
|
195
|
-
pages: payload.pages ?? Math.ceil(payload.total / Math.max(1, payload.limit)),
|
|
196
|
-
hasNext: payload.hasNext ?? false,
|
|
197
|
-
hasPrev: payload.hasPrev ?? false
|
|
198
|
-
});
|
|
199
|
-
else canonical = toCanonicalList(filteredRows);
|
|
200
|
-
reply.code(response.status ?? 200).send({
|
|
201
|
-
...canonical,
|
|
202
|
-
...response.meta ?? {},
|
|
203
|
-
...fieldCaps ? { fieldPermissions: fieldCaps } : {}
|
|
204
|
-
});
|
|
205
|
-
return;
|
|
206
|
-
}
|
|
207
|
-
const rawData = hasFieldRestrictions ? applyPermissions(response.data) : response.data;
|
|
208
|
-
const filteredData = rawData !== null && typeof rawData === "object" && typeof rawData.toJSON === "function" ? rawData.toJSON() : rawData;
|
|
209
|
-
const status = response.status ?? 200;
|
|
210
|
-
if (filteredData !== null && typeof filteredData === "object" && !Array.isArray(filteredData) && (response.meta || fieldCaps)) {
|
|
211
|
-
reply.code(status).send({
|
|
212
|
-
...filteredData,
|
|
213
|
-
...response.meta ?? {},
|
|
214
|
-
...fieldCaps ? { fieldPermissions: fieldCaps } : {}
|
|
215
|
-
});
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
reply.code(status).send(filteredData);
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Bridge an `IController` method (`(ctx: IRequestContext) => Promise<IControllerResponse<T>>`)
|
|
222
|
-
* into a Fastify route handler. Translates between arc's curated request
|
|
223
|
-
* context surface and Fastify's request/reply, and routes the envelope
|
|
224
|
-
* through `sendControllerResponse` so the pipeline applies field
|
|
225
|
-
* permissions, status, and meta uniformly. Internal plumbing, not a
|
|
226
|
-
* portability layer — arc is Fastify-only.
|
|
227
|
-
*
|
|
228
|
-
* @example
|
|
229
|
-
* ```typescript
|
|
230
|
-
* const controller = new BaseController(repository);
|
|
231
|
-
* const listHandler = createFastifyHandler(controller.list.bind(controller));
|
|
232
|
-
* fastify.get('/products', listHandler);
|
|
233
|
-
* ```
|
|
234
|
-
*/
|
|
235
|
-
function createFastifyHandler(controllerMethod) {
|
|
236
|
-
return async (req, reply) => {
|
|
237
|
-
sendControllerResponse(reply, await controllerMethod(createRequestContext(req)), req);
|
|
238
|
-
return reply;
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Create Fastify adapters for all CRUD methods of an IController
|
|
243
|
-
*
|
|
244
|
-
* Returns Fastify-compatible handlers for each CRUD operation
|
|
245
|
-
*
|
|
246
|
-
* @example
|
|
247
|
-
* ```typescript
|
|
248
|
-
* const controller = new BaseController(repository);
|
|
249
|
-
* const handlers = createCrudHandlers(controller);
|
|
250
|
-
*
|
|
251
|
-
* fastify.get('/', handlers.list);
|
|
252
|
-
* fastify.get('/:id', handlers.get);
|
|
253
|
-
* fastify.post('/', handlers.create);
|
|
254
|
-
* fastify.patch('/:id', handlers.update);
|
|
255
|
-
* fastify.delete('/:id', handlers.delete);
|
|
256
|
-
* ```
|
|
257
|
-
*/
|
|
258
|
-
function createCrudHandlers(controller) {
|
|
259
|
-
return {
|
|
260
|
-
list: createFastifyHandler(controller.list.bind(controller)),
|
|
261
|
-
get: createFastifyHandler(controller.get.bind(controller)),
|
|
262
|
-
create: createFastifyHandler(controller.create.bind(controller)),
|
|
263
|
-
update: createFastifyHandler(controller.update.bind(controller)),
|
|
264
|
-
delete: createFastifyHandler(controller.delete.bind(controller))
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
//#endregion
|
|
268
|
-
//#region src/core/middlewares/pipeline.ts
|
|
269
|
-
/**
|
|
270
|
-
* Resolve pipeline steps for a specific operation.
|
|
271
|
-
* Flat-array config applies to every op; map config applies per-op.
|
|
272
|
-
*/
|
|
273
|
-
function resolvePipelineSteps(pipeline, operation) {
|
|
274
|
-
if (!pipeline) return [];
|
|
275
|
-
if (Array.isArray(pipeline)) return pipeline;
|
|
276
|
-
return pipeline[operation] ?? [];
|
|
277
|
-
}
|
|
278
|
-
/**
|
|
279
|
-
* Wrap a controller method (one that takes `IRequestContext` and returns
|
|
280
|
-
* `IControllerResponse<T>`) with pipeline execution. Used by CRUD ops and
|
|
281
|
-
* string-handler custom routes.
|
|
282
|
-
*/
|
|
283
|
-
function buildPipelineHandler(controllerMethod, steps, operation, resourceName) {
|
|
284
|
-
return async (req, reply) => {
|
|
285
|
-
const pipeCtx = {
|
|
286
|
-
...createRequestContext(req),
|
|
287
|
-
resource: resourceName,
|
|
288
|
-
operation
|
|
289
|
-
};
|
|
290
|
-
sendControllerResponse(reply, await executePipeline(steps, pipeCtx, (ctx) => controllerMethod(ctx), operation), req);
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Wrap an action handler (one that takes `(id, data, req)` and returns a raw
|
|
295
|
-
* result) with pipeline execution. Returns a function that produces a full
|
|
296
|
-
* `IControllerResponse<unknown>` — the action router feeds this directly into
|
|
297
|
-
* `sendControllerResponse`, so field masking, custom status codes, `meta`,
|
|
298
|
-
* `details`, and structured error codes from pipeline interceptors flow
|
|
299
|
-
* through to the client unchanged.
|
|
300
|
-
*
|
|
301
|
-
* CRUD and actions now share the same parity invariant: a pipeline that
|
|
302
|
-
* returns `{ success: false, status: 422, error, details, meta }` reaches the
|
|
303
|
-
* client with all four fields intact. Previously the action path stringified
|
|
304
|
-
* failures into a generic `Error` and dropped everything except `statusCode`.
|
|
305
|
-
*
|
|
306
|
-
* Handler throws still bubble out — the caller's try/catch handles `onError`
|
|
307
|
-
* shaping and the generic `ACTION_FAILED` fallback.
|
|
308
|
-
*/
|
|
309
|
-
function buildActionPipelineHandler(handler, steps, operation, resourceName) {
|
|
310
|
-
if (steps.length === 0) return async (id, data, req) => ({
|
|
311
|
-
status: 200,
|
|
312
|
-
data: await handler(id, data, req)
|
|
313
|
-
});
|
|
314
|
-
return async (id, data, req) => {
|
|
315
|
-
const pipeCtx = {
|
|
316
|
-
...createRequestContext(req),
|
|
317
|
-
resource: resourceName,
|
|
318
|
-
operation
|
|
319
|
-
};
|
|
320
|
-
return executePipeline(steps, pipeCtx, async (_ctx) => ({
|
|
321
|
-
status: 200,
|
|
322
|
-
data: await handler(id, data, req)
|
|
323
|
-
}), operation);
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
//#endregion
|
|
327
|
-
//#region src/core/actionPermissions.ts
|
|
328
|
-
/**
|
|
329
|
-
* Return the effective `PermissionCheck` for a single action, or `undefined`
|
|
330
|
-
* when the resource declares no gate at any level.
|
|
331
|
-
*
|
|
332
|
-
* Callers decide what "no gate" means:
|
|
333
|
-
* - HTTP: boot-time throw in `normalizeActionsToRouterConfig`.
|
|
334
|
-
* - MCP: tool-generation throw in `resourceToTools` (mirrors HTTP — the
|
|
335
|
-
* two surfaces fail closed identically so MCP can't expose an
|
|
336
|
-
* unauthenticated mutating tool when the HTTP plugin lifecycle hasn't
|
|
337
|
-
* run).
|
|
338
|
-
* - OpenAPI: docs advertise the endpoint as unauthenticated.
|
|
339
|
-
*/
|
|
340
|
-
function resolveActionPermission(input) {
|
|
341
|
-
const { action, resourcePermissions, resourceActionPermissions, globalAuth } = input;
|
|
342
|
-
const explicit = typeof action !== "function" && action.permissions ? action.permissions : void 0;
|
|
343
|
-
if (explicit) return explicit;
|
|
344
|
-
if (resourceActionPermissions) return resourceActionPermissions;
|
|
345
|
-
if (globalAuth) return globalAuth;
|
|
346
|
-
const updateFallback = resourcePermissions?.update;
|
|
347
|
-
if (updateFallback) return updateFallback;
|
|
348
|
-
}
|
|
349
|
-
//#endregion
|
|
350
|
-
export { createCrudHandlers as a, getControllerContext as c, resolvePipelineSteps as i, getControllerScope as l, buildActionPipelineHandler as n, createFastifyHandler as o, buildPipelineHandler as r, createRequestContext as s, resolveActionPermission as t, sendControllerResponse as u };
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
import { _ as HealthCheck, a as ModuleContribution, f as ScheduleDefinition, n as ArcModuleInput, r as ArcModuleRegistry, s as ModuleSetupContext, t as ArcModule } from "./types-DfVDvkdP.mjs";
|
|
3
|
-
import { FastifyInstance } from "fastify";
|
|
4
|
-
//#region src/factory/module/contributions.d.ts
|
|
5
|
-
/**
|
|
6
|
-
* Collect every module's `healthChecks` in the caller's order. Fails at boot
|
|
7
|
-
* on a duplicate check `name` across modules, naming BOTH owning modules.
|
|
8
|
-
* The host's app-level checks are appended AFTER this (modules-first,
|
|
9
|
-
* host-last) by the caller.
|
|
10
|
-
*/
|
|
11
|
-
declare function collectModuleHealthChecks(modules: readonly ArcModule[]): HealthCheck[];
|
|
12
|
-
/**
|
|
13
|
-
* Collect every module's `workflows`. Resolves factory contributions (so a
|
|
14
|
-
* factory can close over a container the caller has already decorated on
|
|
15
|
-
* `fastify`) and flattens to a single opaque array. Arc core deliberately
|
|
16
|
-
* does NOT validate or interpret the values — the streamline integration
|
|
17
|
-
* owns name/shape checks and registration. Called by the integration at init
|
|
18
|
-
* time, not by arc's boot pipeline (workflows depend on the integration's
|
|
19
|
-
* container).
|
|
20
|
-
*/
|
|
21
|
-
declare function collectModuleWorkflows(fastify: FastifyInstance, modules: readonly ArcModule[]): Promise<unknown[]>;
|
|
22
|
-
/**
|
|
23
|
-
* Collect every module's `scheduledJobs`. Resolves factory contributions
|
|
24
|
-
* (after bootstraps) and fails at boot on a duplicate job `name` across
|
|
25
|
-
* modules. The caller passes the returned definitions to the canonical
|
|
26
|
-
* schedules plugin; factories must therefore be resolved only once.
|
|
27
|
-
*/
|
|
28
|
-
declare function collectModuleScheduledJobs(fastify: FastifyInstance, modules: readonly ArcModule[],
|
|
29
|
-
/**
|
|
30
|
-
* Optional sink for per-module counts, filled as each arm resolves.
|
|
31
|
-
*
|
|
32
|
-
* Exists so `arc.moduleDescriptors` can report real numbers without
|
|
33
|
-
* resolving the factories a SECOND time — a contribution factory closes over
|
|
34
|
-
* booted engines and may register or allocate, so running it twice is not a
|
|
35
|
-
* free read. One resolution feeds both activation and introspection.
|
|
36
|
-
*/
|
|
37
|
-
countsByModule?: Map<string, number>): Promise<ScheduleDefinition[]>;
|
|
38
|
-
//#endregion
|
|
39
|
-
//#region src/factory/module/extend.d.ts
|
|
40
|
-
/**
|
|
41
|
-
* What a caller may add. Every mergeable arm of `ArcModule`, plus `bootstrap`
|
|
42
|
-
* in wrapper form only.
|
|
43
|
-
*
|
|
44
|
-
* `name` is absent on purpose: renaming a module mid-composition breaks the
|
|
45
|
-
* `dependsOn` graph and the registry key its consumers read, and it is the one
|
|
46
|
-
* "extension" that is never additive.
|
|
47
|
-
*/
|
|
48
|
-
interface ModuleExtension<TExports = unknown> {
|
|
49
|
-
dependsOn?: readonly string[];
|
|
50
|
-
owns?: readonly string[] | "provided";
|
|
51
|
-
resources?: ArcModule<TExports>["resources"];
|
|
52
|
-
eventHandlers?: ArcModule<TExports>["eventHandlers"];
|
|
53
|
-
scheduledJobs?: ArcModule<TExports>["scheduledJobs"];
|
|
54
|
-
workflows?: ArcModule<TExports>["workflows"];
|
|
55
|
-
healthChecks?: ArcModule<TExports>["healthChecks"];
|
|
56
|
-
errorMappers?: ArcModule<TExports>["errorMappers"];
|
|
57
|
-
plugins?: ArcModule<TExports>["plugins"];
|
|
58
|
-
afterResources?: ArcModule<TExports>["afterResources"];
|
|
59
|
-
onClose?: ArcModule<TExports>["onClose"];
|
|
60
|
-
/**
|
|
61
|
-
* WRAP the module's bootstrap — it is never replaced.
|
|
62
|
-
*
|
|
63
|
-
* Bootstrap returns the module's exports, so two of them cannot be
|
|
64
|
-
* concatenated: one value has to win, and a host silently winning is the gym
|
|
65
|
-
* failure (`{ ...mod, bootstrap }` overwrote the factory's own, so the engine
|
|
66
|
-
* the module published was never created). The wrapper form makes the
|
|
67
|
-
* relationship explicit — call `inner` and decorate its result, or
|
|
68
|
-
* deliberately do not.
|
|
69
|
-
*
|
|
70
|
-
* `inner` is `undefined` when the module has no bootstrap of its own.
|
|
71
|
-
*/
|
|
72
|
-
bootstrap?: (inner: ((f: FastifyInstance, c: ModuleSetupContext) => TExports | Promise<TExports>) | undefined) => (f: FastifyInstance, c: ModuleSetupContext) => TExports | Promise<TExports>;
|
|
73
|
-
}
|
|
74
|
-
/**
|
|
75
|
-
* Merge a host's additions onto a module. Nothing the module declared is lost.
|
|
76
|
-
*
|
|
77
|
-
* ```ts
|
|
78
|
-
* return extendModule(createOrderModule({ … }), {
|
|
79
|
-
* eventHandlers: () => hostHandlers(), // appended after the module's
|
|
80
|
-
* dependsOn: ["outbox"], // unioned
|
|
81
|
-
* bootstrap: (inner) => async (f, c) => { // wraps, never replaces
|
|
82
|
-
* const exports = await inner?.(f, c);
|
|
83
|
-
* return decorate(exports);
|
|
84
|
-
* },
|
|
85
|
-
* });
|
|
86
|
-
* ```
|
|
87
|
-
*/
|
|
88
|
-
declare function extendModule<TExports = unknown>(mod: ArcModule<TExports>, extension: ModuleExtension<TExports>): ArcModule<TExports>;
|
|
89
|
-
//#endregion
|
|
90
|
-
//#region src/factory/module/lifecycle.d.ts
|
|
91
|
-
/**
|
|
92
|
-
* Subscribe every module's `eventHandlers` in dependency order (pass the
|
|
93
|
-
* `orderModules`-sorted list). Returns the ordered list of unsubscribe
|
|
94
|
-
* functions so the caller can tear them down at shutdown BEFORE module
|
|
95
|
-
* `onClose` (while module deps are still alive). Fails at boot on:
|
|
96
|
-
* - a duplicate NAMED handler across modules (attributing both owners);
|
|
97
|
-
* - a module declaring handlers while the event subsystem is unavailable.
|
|
98
|
-
* Resolves factory contributions (after bootstraps) so a handler can close over
|
|
99
|
-
* booted engines rather than a global getter.
|
|
100
|
-
*/
|
|
101
|
-
declare function subscribeModuleEventHandlers(fastify: FastifyInstance, modules: readonly ArcModule[],
|
|
102
|
-
/**
|
|
103
|
-
* Optional sink for per-module handler counts, filled as each arm resolves.
|
|
104
|
-
* Lets `arc.moduleDescriptors` report real numbers without resolving the
|
|
105
|
-
* factories a SECOND time — see the note on `collectModuleScheduledJobs`.
|
|
106
|
-
*/
|
|
107
|
-
countsByModule?: Map<string, number>): Promise<Array<() => void | Promise<void>>>;
|
|
108
|
-
//#endregion
|
|
109
|
-
//#region src/factory/module/order.d.ts
|
|
110
|
-
/**
|
|
111
|
-
* Order modules for composition by their `dependsOn` edges — a STABLE
|
|
112
|
-
* topological sort. Called once at the start of the bootstrap phase; every
|
|
113
|
-
* subsequent phase (bootstrap, resources, afterResources, and reverse-order
|
|
114
|
-
* onClose) iterates the returned list, so a module's declared dependencies are
|
|
115
|
-
* always composed before it.
|
|
116
|
-
*
|
|
117
|
-
* "Stable" = modules with no edge between them keep their original list order,
|
|
118
|
-
* so declaring `dependsOn` on one module never silently reorders an unrelated
|
|
119
|
-
* one. Backward compatible: a `modules` array with NO `dependsOn` anywhere is
|
|
120
|
-
* returned unchanged.
|
|
121
|
-
*
|
|
122
|
-
* Fail-fast — throws (never reorders past a broken contract) on:
|
|
123
|
-
* - duplicate module names (the name is the graph key)
|
|
124
|
-
* - a `dependsOn` name not present in the composed set
|
|
125
|
-
* - a self-reference (`dependsOn` includes the module's own name)
|
|
126
|
-
* - a dependency cycle (reports the concrete `a → b → … → a` path)
|
|
127
|
-
*/
|
|
128
|
-
declare function orderModules(modules: readonly ArcModule[]): ArcModule[];
|
|
129
|
-
//#endregion
|
|
130
|
-
//#region src/factory/module/resolve.d.ts
|
|
131
|
-
/**
|
|
132
|
-
* Resolve any module-input form to a concrete `ArcModule`. A thunk is invoked
|
|
133
|
-
* (this is where a dynamic `import()` fires); a promise (or plain module) is
|
|
134
|
-
* awaited. Called once per module at the start of the bootstrap phase.
|
|
135
|
-
*/
|
|
136
|
-
declare function resolveModule(input: ArcModuleInput): Promise<ArcModule>;
|
|
137
|
-
/** Resolve a `ModuleContribution` to a concrete array (array passthrough or factory call). */
|
|
138
|
-
declare function resolveContribution<T>(contribution: ModuleContribution<T> | undefined, fastify: FastifyInstance): Promise<readonly T[]>;
|
|
139
|
-
//#endregion
|
|
140
|
-
//#region src/factory/module/state.d.ts
|
|
141
|
-
type ModuleState = "resolved" | "bootstrapping" | "ready" | "closing" | "closed" | "failed";
|
|
142
|
-
/**
|
|
143
|
-
* Is `name` in the composed module graph? TRUE for resource-only modules and
|
|
144
|
-
* modules that exported nothing — the question `hasModuleExports()` cannot
|
|
145
|
-
* answer. FALSE before registerResources runs (no graph yet) and for
|
|
146
|
-
* uncomposed names.
|
|
147
|
-
*/
|
|
148
|
-
declare function hasModule(fastify: FastifyInstance, name: string): boolean;
|
|
149
|
-
/**
|
|
150
|
-
* The module's current lifecycle state, or `undefined` when the module is not
|
|
151
|
-
* composed (or no graph has been composed on this instance). See the state
|
|
152
|
-
* machine in the file header.
|
|
153
|
-
*/
|
|
154
|
-
declare function getModuleState(fastify: FastifyInstance, name: string): ModuleState | undefined;
|
|
155
|
-
//#endregion
|
|
156
|
-
//#region src/factory/module/index.d.ts
|
|
157
|
-
/**
|
|
158
|
-
* Identity helper for authoring a typed module — mirrors `defineResource`.
|
|
159
|
-
* Gives inference + a single obvious construction site; does no work.
|
|
160
|
-
*
|
|
161
|
-
* `TExports` is inferred from the `bootstrap` return value, so a module
|
|
162
|
-
* author gets a typed public export for free:
|
|
163
|
-
*
|
|
164
|
-
* ```ts
|
|
165
|
-
* export const accountingModule = (deps: Deps) =>
|
|
166
|
-
* defineModule({
|
|
167
|
-
* name: "accounting",
|
|
168
|
-
* bootstrap: async () => createAccountingEngine(deps), // TExports inferred
|
|
169
|
-
* });
|
|
170
|
-
* ```
|
|
171
|
-
*/
|
|
172
|
-
declare function defineModule<TExports = unknown>(module: ArcModule<TExports>): ArcModule<TExports>;
|
|
173
|
-
/**
|
|
174
|
-
* Typed accessor for a module's public export (its `bootstrap` return value),
|
|
175
|
-
* recorded at `fastify.arc.modules[name]`.
|
|
176
|
-
*
|
|
177
|
-
* Throwing, in line with the fail-fast contract (and `requireOrgId`-style
|
|
178
|
-
* accessors): a missing entry means the module either isn't composed, is
|
|
179
|
-
* composed AFTER the caller (list order = init order), or returned nothing —
|
|
180
|
-
* all wiring bugs that must surface at boot, not as `undefined` downstream.
|
|
181
|
-
*
|
|
182
|
-
* The type parameter is an assertion, not a proof. Two ways to type it:
|
|
183
|
-
* - augment `ArcModuleRegistry` once — then the name alone infers the type:
|
|
184
|
-
* `getModuleExports(f, "accounting")` → `AccountingEngine`.
|
|
185
|
-
* - or pass it inline: `getModuleExports<AccountingEngine>(f, "accounting")`.
|
|
186
|
-
*
|
|
187
|
-
* Siblings, for the two shapes this one is wrong for:
|
|
188
|
-
* - `getOptionalModuleExports` — the module is genuinely OPTIONAL.
|
|
189
|
-
* - `lazyModuleExports` / `lazyRequiredModuleExports` — the read happens at
|
|
190
|
-
* composition time, where boot order decides the answer.
|
|
191
|
-
*/
|
|
192
|
-
declare function getModuleExports<K extends keyof ArcModuleRegistry>(fastify: FastifyInstance, name: K): ArcModuleRegistry[K];
|
|
193
|
-
declare function getModuleExports<TExports = unknown>(fastify: FastifyInstance, name: string): TExports;
|
|
194
|
-
/**
|
|
195
|
-
* Non-throwing sibling of `getModuleExports`: `undefined` when the module is
|
|
196
|
-
* not composed (or has not bootstrapped yet).
|
|
197
|
-
*
|
|
198
|
-
* Exists because OPTIONAL sibling modules are a first-class composition shape —
|
|
199
|
-
* a deployment that turns `inventory` off still sells services, so its POS
|
|
200
|
-
* module must ask "is inventory here?" rather than fail-fast. Hosts were
|
|
201
|
-
* spelling that question as
|
|
202
|
-
*
|
|
203
|
-
* ```ts
|
|
204
|
-
* (fastify as { arc?: { modules?: Record<string, unknown> } })
|
|
205
|
-
* .arc?.modules?.inventory;
|
|
206
|
-
* ```
|
|
207
|
-
*
|
|
208
|
-
* — a double cast that (a) defeats `ArcModuleRegistry` inference, (b) silently
|
|
209
|
-
* survives a typo in the module name, and (c) is impossible to grep for. Use
|
|
210
|
-
* `getModuleExports` when the dependency is REQUIRED (boot must fail), this
|
|
211
|
-
* when it is genuinely optional.
|
|
212
|
-
*
|
|
213
|
-
* Prefer `lazyModuleExports` whenever the read happens at COMPOSITION time —
|
|
214
|
-
* see its doc for why a value read once can latch `undefined` forever.
|
|
215
|
-
*/
|
|
216
|
-
declare function getOptionalModuleExports<K extends keyof ArcModuleRegistry>(fastify: FastifyInstance, name: K): ArcModuleRegistry[K] | undefined;
|
|
217
|
-
declare function getOptionalModuleExports<TExports = unknown>(fastify: FastifyInstance, name: string): TExports | undefined;
|
|
218
|
-
/**
|
|
219
|
-
* Is a module's public export recorded yet? The predicate form of
|
|
220
|
-
* `getOptionalModuleExports`, for readiness gates (`ready: () => ...`) that
|
|
221
|
-
* should not have to reason about falsy-but-present exports.
|
|
222
|
-
*/
|
|
223
|
-
declare function hasModuleExports(fastify: FastifyInstance, name: string): boolean;
|
|
224
|
-
/**
|
|
225
|
-
* A DEFERRED reader of a sibling module's export: returns a getter that reads
|
|
226
|
-
* the registry at CALL time, not now.
|
|
227
|
-
*
|
|
228
|
-
* ── Why this exists (the bug it removes) ─────────────────────────────────────
|
|
229
|
-
*
|
|
230
|
-
* Module composition runs bootstraps in dependency/list order, but a lot of
|
|
231
|
-
* wiring is *registered* earlier than it *runs*: an `eventHandlers` factory, a
|
|
232
|
-
* bridge accessor handed to another module, a hook added during bootstrap and
|
|
233
|
-
* fired per request. Such a factory can execute BEFORE the sibling it needs has
|
|
234
|
-
* bootstrapped — so a sibling engine captured into a local at that moment is
|
|
235
|
-
* `undefined`, and stays `undefined` for the life of the process even though the
|
|
236
|
-
* sibling came up milliseconds later.
|
|
237
|
-
*
|
|
238
|
-
* That failure is SILENT by construction: the consumer sees a null bridge and
|
|
239
|
-
* treats it as "this deployment has no inventory / no revenue engine", which is
|
|
240
|
-
* a legitimate configuration. Real cost in this codebase: orders were created
|
|
241
|
-
* and never payment-confirmed (no entitlement, no card, no door) and nothing
|
|
242
|
-
* errored anywhere.
|
|
243
|
-
*
|
|
244
|
-
* The getter therefore:
|
|
245
|
-
* - re-reads while the module is ABSENT, so boot order cannot matter;
|
|
246
|
-
* - memoizes the FIRST resolved value, so the steady-state read is a closure
|
|
247
|
-
* variable rather than a registry lookup per request;
|
|
248
|
-
* - never memoizes absence — that is precisely the latch this replaces.
|
|
249
|
-
*
|
|
250
|
-
* ```ts
|
|
251
|
-
* const inventory = lazyModuleExports<FlowEngine>(fastify, "inventory");
|
|
252
|
-
* return {
|
|
253
|
-
* flowBridge: () => (inventory() ? createFlowEngineBridge({ getFlowEngine: () => inventory()! }) : null),
|
|
254
|
-
* ready: () => inventory() !== undefined,
|
|
255
|
-
* };
|
|
256
|
-
* ```
|
|
257
|
-
*/
|
|
258
|
-
declare function lazyModuleExports<K extends keyof ArcModuleRegistry>(fastify: FastifyInstance, name: K): () => ArcModuleRegistry[K] | undefined;
|
|
259
|
-
declare function lazyModuleExports<TExports = unknown>(fastify: FastifyInstance, name: string): () => TExports | undefined;
|
|
260
|
-
/**
|
|
261
|
-
* Required counterpart of `lazyModuleExports`: defers the read, then throws
|
|
262
|
-
* `getModuleExports`' error if the module is still absent at first use.
|
|
263
|
-
*
|
|
264
|
-
* The pair matters because "required" and "readable now" are different
|
|
265
|
-
* questions. A hook registered at bootstrap that needs the `order` engine at
|
|
266
|
-
* REQUEST time is a hard dependency, but reading it eagerly turns a correct
|
|
267
|
-
* composition into a boot crash purely because of list order. Deferring keeps
|
|
268
|
-
* the fail-fast guarantee and moves it to the first moment the answer is
|
|
269
|
-
* actually needed.
|
|
270
|
-
*
|
|
271
|
-
* Deferred does NOT mean unvalidated: a lazily-required module is still a
|
|
272
|
-
* HARD dependency, so PRESENCE is checked eagerly. When the composed module
|
|
273
|
-
* graph is known (any `createApp`/`registerResources` boot — the normal
|
|
274
|
-
* case), a name that is not in the graph throws HERE, at composition time,
|
|
275
|
-
* instead of surviving startup and failing on the first request or event.
|
|
276
|
-
* Only the EXPORT read (does bootstrap's return value exist yet?) is
|
|
277
|
-
* deferred to first call.
|
|
278
|
-
*
|
|
279
|
-
* Convention for hard sibling access: also declare the target in the
|
|
280
|
-
* consumer's `dependsOn`. That orders composition (the sibling's bootstrap
|
|
281
|
-
* runs first), while this accessor merely defers the read — the two are
|
|
282
|
-
* complementary, not alternatives:
|
|
283
|
-
* - hard dependency, read during bootstrap → `dependsOn` + `getModuleExports`
|
|
284
|
-
* - hard dependency, read at request/event time → `dependsOn` + this
|
|
285
|
-
* - optional integration → `lazyModuleExports` (no `dependsOn` needed)
|
|
286
|
-
*/
|
|
287
|
-
declare function lazyRequiredModuleExports<K extends keyof ArcModuleRegistry>(fastify: FastifyInstance, name: K): () => ArcModuleRegistry[K];
|
|
288
|
-
declare function lazyRequiredModuleExports<TExports = unknown>(fastify: FastifyInstance, name: string): () => TExports;
|
|
289
|
-
//#endregion
|
|
290
|
-
export { collectModuleScheduledJobs as _, lazyModuleExports as a, getModuleState as c, resolveModule as d, orderModules as f, collectModuleHealthChecks as g, extendModule as h, hasModuleExports as i, hasModule as l, ModuleExtension as m, getModuleExports as n, lazyRequiredModuleExports as o, subscribeModuleEventHandlers as p, getOptionalModuleExports as r, ModuleState as s, defineModule as t, resolveContribution as u, collectModuleWorkflows as v };
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/*! @classytic/arc v2.41.1 | MIT | https://classytic.com */
|
|
2
|
-
//#region src/idempotency/stores/interface.ts
|
|
3
|
-
/**
|
|
4
|
-
* Helper to create a result object
|
|
5
|
-
*/
|
|
6
|
-
function createIdempotencyResult(statusCode, body, headers, ttlMs) {
|
|
7
|
-
const now = /* @__PURE__ */ new Date();
|
|
8
|
-
return {
|
|
9
|
-
statusCode,
|
|
10
|
-
headers,
|
|
11
|
-
body,
|
|
12
|
-
createdAt: now,
|
|
13
|
-
expiresAt: new Date(now.getTime() + ttlMs)
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
//#endregion
|
|
17
|
-
export { createIdempotencyResult as t };
|