@classytic/arc 2.41.1 → 2.44.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-DZWDsaPa.mjs} +22 -17
- package/dist/{HookSystem-CMF94SEA.mjs → HookSystem-yjEgn7Cm.mjs} +2 -2
- package/dist/{QueryCache-DFA-QrU9.d.mts → QueryCache--5uAuYPW.d.mts} +7 -3
- package/dist/{ResourceRegistry-MGpXoxHZ.mjs → ResourceRegistry-DJxHUpE4.mjs} +44 -13
- package/dist/acquire-BozJ-9yt.mjs +35 -0
- package/dist/actionPermissions-5N04zBL4.mjs +772 -0
- package/dist/audit/index.d.mts +46 -8
- package/dist/audit/index.mjs +48 -26
- package/dist/auth/audit.d.mts +8 -8
- package/dist/auth/audit.mjs +1 -1
- package/dist/auth/index.d.mts +46 -16
- package/dist/auth/index.mjs +134 -156
- package/dist/auth/redis-session.d.mts +4 -5
- package/dist/auth/redis-session.mjs +1 -1
- package/dist/{authorizationDecision-DawKGoXb.mjs → authorizationDecision-wX3SA9w_.mjs} +7 -102
- package/dist/{base-CxrV1LmG.d.mts → base-CZyixEQR.d.mts} +27 -5
- package/dist/boundedMap-DWiKW7lP.mjs +57 -0
- package/dist/{buildHandler-CvxKOc0m.mjs → buildHandler-Bswu_vNb.mjs} +4 -4
- package/dist/cache/index.d.mts +31 -43
- package/dist/cache/index.mjs +4 -4
- package/dist/{caching-DbCBxlgw.mjs → caching-Drp68DIS.mjs} +3 -3
- package/dist/claimed-DYvQoZLv.mjs +8 -0
- package/dist/cleanup/index.d.mts +18 -22
- package/dist/cleanup/index.mjs +27 -73
- package/dist/cli/commands/describe.d.mts +7 -5
- package/dist/cli/commands/describe.mjs +7 -4
- package/dist/cli/commands/docs.d.mts +2 -3
- package/dist/cli/commands/docs.mjs +3 -3
- package/dist/cli/commands/doctor.d.mts +2 -3
- package/dist/cli/commands/doctor.mjs +1 -1
- package/dist/cli/commands/generate.mjs +2 -3
- package/dist/cli/commands/init.mjs +2 -3
- package/dist/cli/commands/introspect.d.mts +2 -3
- package/dist/cli/commands/introspect.mjs +2 -2
- package/dist/cli/index.mjs +3 -3
- package/dist/{constants-D5Ubn1fe.mjs → constants-B2QHb6u6.mjs} +4 -2
- package/dist/context/index.d.mts +11 -14
- 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-COcqo0Cp.mjs} +15 -5
- package/dist/{createActionRouter-DNqoaK81.mjs → createActionRouter-zG13rpPJ.mjs} +97 -39
- package/dist/{createAggregationRouter-BcbQJf10.mjs → createAggregationRouter-CWRz96Vr.mjs} +11 -8
- package/dist/{preloadResources-f3boenVZ.mjs → createApp-BGLYtErt.mjs} +305 -394
- package/dist/{createMcpServer-CbDWkZ1T.d.mts → createMcpServer-DSztofa9.d.mts} +1 -1
- package/dist/{defineAggregation-D_eJjm5A.mjs → defineAggregation--3xguLkk.mjs} +1 -1
- package/dist/{defineResource-DxkMTjRn.mjs → defineResource-gUp9ug66.mjs} +118 -1024
- package/dist/discovery/index.d.mts +6 -7
- package/dist/discovery/index.mjs +1 -1
- package/dist/docs/index.d.mts +11 -5
- package/dist/docs/index.mjs +4 -4
- package/dist/{elevation-CkvG0LG2.mjs → elevation-BG4gnMsJ.mjs} +41 -12
- package/dist/{elevation-BXCfVcNG.d.mts → elevation-BW2J3tNj.d.mts} +1 -2
- package/dist/encryption/index.d.mts +5 -5
- package/dist/encryption/index.mjs +3 -3
- package/dist/{errorHandler-D1GHKGlV.mjs → errorHandler-COHw3N1b.mjs} +26 -5
- package/dist/{errors-BH7YU_9O.mjs → errors-CW4anJBy.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-BweHAVws.mjs} +82 -21
- package/dist/{subscribe-helpers-DatDkcuG.mjs → eventTypes-N4-omQFR.mjs} +151 -17
- package/dist/events/index.d.mts +43 -18
- 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 +5 -6
- package/dist/events/transports/redis.mjs +2 -2
- package/dist/factory/index.d.mts +42 -27
- package/dist/factory/index.mjs +213 -32
- package/dist/{fastify-augmentation-Cq2nHVaW.d.mts → fastify-augmentation-DRlCIWE9.d.mts} +11 -25
- package/dist/{fields-BHrkoF5y.mjs → fields-A5crhsJb.mjs} +1 -1
- package/dist/{fields-I_EeRWND.d.mts → fields-BBSpf8pS.d.mts} +1 -1
- package/dist/filter-merge-DGM-GQ1J.mjs +99 -0
- package/dist/{generate-BLRf2m3y.mjs → generate-BXKo2Gc8.mjs} +2 -2
- package/dist/{headers-BIXWKrzQ.mjs → headers-DKZQKL-Z.mjs} +2 -2
- package/dist/health-BYAwA6YS.mjs +22 -0
- package/dist/{health-ZRHeDHXH.mjs → health-CrqUp3sz.mjs} +115 -55
- package/dist/hooks/index.d.mts +2 -2
- package/dist/hooks/index.mjs +2 -3
- package/dist/idempotency/index.d.mts +36 -10
- package/dist/idempotency/index.mjs +230 -111
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +77 -12
- package/dist/{index-D4u76eQW.d.mts → index-Beg3aNnK.d.mts} +23 -101
- package/dist/{preloadResources-CjS2OfpU.d.mts → index-BkV55WlV.d.mts} +37 -38
- package/dist/{index-BHdZD8zK.d.mts → index-Bxriq3so.d.mts} +84 -21
- package/dist/index-CUJvoyw3.d.mts +510 -0
- package/dist/{index-CbJiYrfv.d.mts → index-Chgz3d0i.d.mts} +1 -1
- package/dist/{index-DHbyPo5j.d.mts → index-WH7xgvxS.d.mts} +304 -254
- package/dist/index.d.mts +9 -9
- package/dist/index.mjs +11 -11
- package/dist/{init-CpLkjU18.mjs → init-BPzK5Lnr.mjs} +1 -1
- package/dist/integrations/event-gateway.d.mts +11 -7
- 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 +102 -89
- package/dist/integrations/mcp/index.mjs +19 -4
- package/dist/integrations/mcp/testing.d.mts +8 -9
- package/dist/integrations/mcp/testing.mjs +2 -2
- package/dist/integrations/streamline.d.mts +85 -144
- package/dist/integrations/streamline.mjs +131 -55
- package/dist/integrations/webhooks.d.mts +83 -143
- package/dist/integrations/webhooks.mjs +36 -80
- package/dist/integrations/websocket-pushref-redis.d.mts +5 -6
- package/dist/integrations/websocket-pushref-redis.mjs +1 -1
- package/dist/integrations/websocket-redis.d.mts +5 -6
- 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-JJzwH4Ro.d.mts} +34 -2
- package/dist/{jsonBody-ELR9lfeU.mjs → jsonBody-YIq-vGLv.mjs} +2 -2
- package/dist/{keys-cpZCtcIE.mjs → keys-CpoiZ8hU.mjs} +7 -18
- package/dist/{loadResources-BPfWiI5s.mjs → loadResources-6jO52-RR.mjs} +3 -3
- package/dist/{loadResourcesFromEntry-ixtfL3J2.mjs → loadResourcesFromEntry-MP8xEppu.mjs} +2 -2
- package/dist/logger/index.d.mts +9 -17
- package/dist/logger/index.mjs +2 -153
- package/dist/logger-D4V1zGLI.mjs +160 -0
- package/dist/{mcpPlugin-BX5PujI0.mjs → mcpPlugin-srPc7ItN.mjs} +446 -170
- package/dist/{memory-Iotz0cCx.mjs → memory-CTa5pOCW.mjs} +36 -34
- package/dist/{metrics-NMsmpRmY.mjs → metrics-DsZrvnpB.mjs} +39 -72
- package/dist/middleware/index.d.mts +5 -5
- package/dist/middleware/index.mjs +3 -3
- package/dist/migrations/index.d.mts +13 -13
- package/dist/migrations/index.mjs +2 -2
- package/dist/{module-DpLJG2_N.mjs → module--L0iBIZz.mjs} +8 -8
- package/dist/mongodb-memory-server-CHhYSq5y.mjs +35539 -0
- package/dist/mongoose-CYJCLyi-.mjs +67451 -0
- package/dist/multiTenant-CAtYlW6H.d.mts +104 -0
- package/dist/{multipartBody-DtRWM6_U.mjs → multipartBody-Cc7eZ9cq.mjs} +46 -7
- package/dist/node-D8UZv_8c.mjs +41236 -0
- package/dist/{openapi-CmFf3yq_.mjs → openapi-CMegfsm8.mjs} +48 -10
- package/dist/outbox-admin/index.d.mts +5 -5
- package/dist/outbox-admin/index.mjs +6 -18
- package/dist/{outbox-module-D2z5WkDY.mjs → outbox-module-Ryk270jd.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-C5ijIz5Q.mjs} +149 -33
- package/dist/{pipe-FMWNheSO.mjs → pipe-D81zMtWs.mjs} +2 -2
- package/dist/pipeline/index.d.mts +7 -7
- package/dist/pipeline/index.mjs +2 -2
- package/dist/plan-CJGMKoui.mjs +565 -0
- package/dist/{BaseController-CYEwjyjN.mjs → plugin-DALd9dcF.mjs} +1269 -346
- package/dist/plugins/index.d.mts +17 -14
- package/dist/plugins/index.mjs +44 -22
- package/dist/plugins/response-cache.d.mts +5 -6
- package/dist/plugins/response-cache.mjs +8 -14
- package/dist/plugins/tracing-entry.mjs +2 -2
- package/dist/{pluralize-CXWZIzTv.mjs → pluralize-COUNYo3d.mjs} +1 -1
- package/dist/presets/filesUpload.d.mts +8 -8
- package/dist/presets/filesUpload.mjs +5 -5
- package/dist/presets/index.d.mts +35 -40
- 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 +9 -10
- package/dist/presets/search.mjs +2 -2
- package/dist/{presets-ZJSPgmT2.mjs → presets-CONf2Qem.mjs} +99 -107
- package/dist/{projection-BzIgzoKZ.mjs → projection-wFoLqdss.mjs} +2 -2
- package/dist/{pushref-registry-Dov9l4qE.d.mts → pushref-registry-UxCkoA4C.d.mts} +1 -1
- package/dist/{queryCachePlugin-DZHPOm9p.mjs → queryCachePlugin-BATECpyh.mjs} +12 -14
- package/dist/{queryCachePlugin-BzqxconG.d.mts → queryCachePlugin-C2lZjTxk.d.mts} +1 -1
- package/dist/{rateLimitKey-BvOvcBDi.mjs → rateLimitKey-DVEZ_UpV.mjs} +1 -1
- package/dist/{realtime-8tIQWZz4.mjs → realtime-xyubMKSB.mjs} +25 -21
- package/dist/{redis-Bzi1y3F9.d.mts → redis-BDgv9Glw.d.mts} +17 -24
- package/dist/redis-stream-BMk-bpQS.d.mts +219 -0
- package/dist/redisShapes-CQksY67q.d.mts +55 -0
- package/dist/registry/index.d.mts +15 -10
- package/dist/registry/index.mjs +4 -4
- package/dist/{registry-DNkgGQcK.mjs → registry-Bdzg-hdm.mjs} +13 -10
- package/dist/{renewingLease-yF8BdW4b.mjs → renewingLease-BFJxGPuh.mjs} +1 -1
- package/dist/{replyHelpers-CNdvMfM4.mjs → replyHelpers-Bnc6lJyy.mjs} +2 -2
- package/dist/{requestContext-cDkAUr-P.mjs → requestContext-C10UeFe5.mjs} +1 -1
- package/dist/requirement-Bu50QALH.mjs +35 -0
- package/dist/requirement-CbiLdQ1n.d.mts +22 -0
- package/dist/resourceRateLimit-Wf6d6FL9.d.mts +30 -0
- package/dist/resourceRateLimit-urWPJxrt.mjs +180 -0
- package/dist/rolldown-runtime-MYrLNGnq.mjs +45 -0
- package/dist/routeValidator-DHtpzrzk.mjs +70 -0
- package/dist/{runtimeCapabilities-CkhS6jn6.mjs → runtimeCapabilities-DIMKMV2G.mjs} +1 -1
- package/dist/{schedules-j6uXXAeJ.mjs → schedules-CKTs5ys8.mjs} +46 -37
- package/dist/{schemaConverter-BVA7cFRG.mjs → schemaConverter-BVjkQ5DD.mjs} +2 -2
- package/dist/{schemaIR-CTvPDoPB.mjs → schemaIR-BA0Hb21Y.mjs} +2 -2
- package/dist/schemas/index.d.mts +20 -10
- package/dist/schemas/index.mjs +8 -3
- package/dist/scim/index.d.mts +13 -13
- package/dist/scim/index.mjs +2 -2
- package/dist/scope/index.d.mts +4 -4
- package/dist/scope/index.mjs +6 -6
- package/dist/{scope-BUi1xV1I.mjs → scope-DINaj7Ax.mjs} +4 -4
- package/dist/shutdown-LpLMH8T1.mjs +59 -0
- package/dist/{simpleEqualityMatcher-C9bzq2gO.mjs → simpleEqualityMatcher-DbwIM1f1.mjs} +1 -1
- package/dist/{sse-BeqVKQKu.mjs → sse-BzJFPX_1.mjs} +11 -15
- package/dist/{sseStream-Ppzo-WUH.mjs → sseStream-DQChzGTs.mjs} +2 -2
- package/dist/{store-helpers-dW71XVWL.mjs → store-helpers-Coj9RhOZ.mjs} +1 -1
- package/dist/{streaming-IcRFjWQU.mjs → streaming-JzB5CAga.mjs} +1 -1
- package/dist/subscriberIdentity-DeyppIif.mjs +28 -0
- package/dist/tenancy-DxV8OuX2.mjs +43 -0
- package/dist/tenantPreparation-CBvvAcAj.mjs +67 -0
- package/dist/testing/cleanupStoreContract.d.mts +4 -5
- package/dist/testing/cleanupStoreContract.mjs +1 -1
- package/dist/testing/index.d.mts +215 -197
- package/dist/testing/index.mjs +289 -214
- package/dist/testing/outboxStoreContract.d.mts +4 -5
- package/dist/testing/outboxStoreContract.mjs +137 -2
- package/dist/testing/requiredPorts.d.mts +16 -0
- package/dist/testing/requiredPorts.mjs +65 -0
- package/dist/testing/storageContract.d.mts +4 -5
- package/dist/testing/storageContract.mjs +1 -1
- package/dist/timing-CntavTlR.mjs +46 -0
- package/dist/{transactionContext-CPbdlnVw.mjs → transactionContext-orvnGu4F.mjs} +1 -1
- package/dist/{typeGuards-BLjPgVBm.mjs → typeGuards-kEaP_WmO.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-DfVDvkdP.d.mts → types-BJ-2XqVF.d.mts} +189 -204
- package/dist/{types-CWQn9-IC.d.mts → types-D3Ixsh00.d.mts} +22 -9
- package/dist/{types-CgenB1gv.mjs → types-fvO6Ovzx.mjs} +4 -4
- package/dist/usage/index.d.mts +5 -6
- package/dist/usage/index.mjs +5 -5
- package/dist/{userHelpers-ChRvkuSZ.mjs → userHelpers-C76zD5pa.mjs} +1 -1
- package/dist/utils/index.d.mts +125 -264
- package/dist/utils/index.mjs +13 -12
- package/dist/{utils-CxcctrAh.mjs → utils-BpkVZ8rB.mjs} +315 -404
- package/dist/{validate-DrYWnOzI.mjs → validate-CWp-lsrm.mjs} +6 -14
- package/dist/{versioning-D6JIK8Tk.d.mts → versioning-Bsb51gGT.d.mts} +28 -13
- package/dist/{versioning-DKkT4hNJ.mjs → versioning-CHhL9P3o.mjs} +4 -4
- package/dist/{websocket-BOXVggMf.d.mts → websocket-Cs4awVyD.d.mts} +1 -1
- package/dist/{workScope-CxKqoIFn.mjs → workScope-n9a7d_lZ.mjs} +2 -2
- package/package.json +57 -59
- 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/rolldown-runtime-eysyzFAF.mjs +0 -15
- package/dist/sync/changelog.d.mts +0 -3
- package/dist/sync/changelog.mjs +0 -3
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as createTenantKeyGenerator } from "./rateLimitKey-DVEZ_UpV.mjs";
|
|
3
|
+
//#region src/permissions/resourceRateLimit.ts
|
|
4
|
+
function decision(allowed, max, remaining, ttlSeconds) {
|
|
5
|
+
const reset = Math.max(1, ttlSeconds);
|
|
6
|
+
return {
|
|
7
|
+
allowed,
|
|
8
|
+
retryAfterSeconds: reset,
|
|
9
|
+
headers: {
|
|
10
|
+
"x-ratelimit-limit": max,
|
|
11
|
+
"x-ratelimit-remaining": remaining,
|
|
12
|
+
"x-ratelimit-reset": reset,
|
|
13
|
+
...!allowed ? { "retry-after": reset } : {}
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** Policy namespacing must reach the store key, not merely the local limiter cache. */
|
|
18
|
+
function policyIdentity(resource, policy) {
|
|
19
|
+
if (!Number.isSafeInteger(policy.max) || policy.max <= 0) throw new Error("rateLimit.max must be a positive safe integer.");
|
|
20
|
+
return JSON.stringify([
|
|
21
|
+
"arc-resource",
|
|
22
|
+
resource,
|
|
23
|
+
policy.bucket ?? "resource",
|
|
24
|
+
policy.max,
|
|
25
|
+
resourceRateLimitWindowMs(policy.timeWindow)
|
|
26
|
+
]);
|
|
27
|
+
}
|
|
28
|
+
const RESOURCE_RATE_LIMITER = Symbol.for("arc.resourceRateLimiter");
|
|
29
|
+
const tenantKey = createTenantKeyGenerator();
|
|
30
|
+
function resourceRateLimitKey(resource, context) {
|
|
31
|
+
const actor = tenantKey({
|
|
32
|
+
scope: context.scope,
|
|
33
|
+
ip: context.ip ?? "anonymous"
|
|
34
|
+
});
|
|
35
|
+
const dimension = context.scope && context.scope.kind !== "public" && "organizationId" in context.scope && context.scope.organizationId ? "tenant" : context.scope?.kind ?? "public";
|
|
36
|
+
return JSON.stringify([
|
|
37
|
+
resource,
|
|
38
|
+
dimension,
|
|
39
|
+
actor
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
42
|
+
const RATE_LIMITING_DISABLED = Symbol.for("arc.rateLimitingDisabled");
|
|
43
|
+
/**
|
|
44
|
+
* The host said `rateLimit: false`. A resource's declared limit is then a recorded no-op, and
|
|
45
|
+
* the resources are named once at ready. Absent this mark, a declared limit with no limiter
|
|
46
|
+
* refuses to boot: nobody decided, so nothing would enforce it.
|
|
47
|
+
*/
|
|
48
|
+
function markRateLimitingDisabled(app) {
|
|
49
|
+
if (Reflect.get(app, RATE_LIMITING_DISABLED)) return;
|
|
50
|
+
const unenforced = /* @__PURE__ */ new Set();
|
|
51
|
+
Reflect.set(app, RATE_LIMITING_DISABLED, unenforced);
|
|
52
|
+
app.addHook("onReady", async () => {
|
|
53
|
+
if (unenforced.size === 0) return;
|
|
54
|
+
app.log.info({ resources: [...unenforced] }, `[arc] rate limiting is disabled for this app: declared rateLimit not enforced on ${[...unenforced].join(", ")}`);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/** Record a resource whose declared limit the host disabled; `false` when the host never decided. */
|
|
58
|
+
function recordUnenforcedRateLimit(app, resource) {
|
|
59
|
+
let owner = app;
|
|
60
|
+
while (owner) {
|
|
61
|
+
const unenforced = Reflect.get(owner, RATE_LIMITING_DISABLED);
|
|
62
|
+
if (unenforced instanceof Set) {
|
|
63
|
+
unenforced.add(resource);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
owner = Object.getPrototypeOf(owner);
|
|
67
|
+
}
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
/** Uses createRateLimit's store factory, preserving Redis/custom-store configuration. */
|
|
71
|
+
function resolveResourceRateLimiter(app, durability = "memory") {
|
|
72
|
+
if (!app.hasDecorator("createRateLimit")) return void 0;
|
|
73
|
+
let owner = app;
|
|
74
|
+
while (!Object.hasOwn(owner, "createRateLimit")) {
|
|
75
|
+
const parent = Object.getPrototypeOf(owner);
|
|
76
|
+
if (!parent || typeof parent !== "object" || !("createRateLimit" in parent)) break;
|
|
77
|
+
owner = parent;
|
|
78
|
+
}
|
|
79
|
+
const existing = Reflect.get(owner, RESOURCE_RATE_LIMITER);
|
|
80
|
+
if (existing) return existing;
|
|
81
|
+
const limiters = /* @__PURE__ */ new Map();
|
|
82
|
+
const limiter = {
|
|
83
|
+
durability,
|
|
84
|
+
async consume(resource, policy, context) {
|
|
85
|
+
const identity = policyIdentity(resource, policy);
|
|
86
|
+
let consume = limiters.get(identity);
|
|
87
|
+
if (!consume) {
|
|
88
|
+
consume = owner.createRateLimit({
|
|
89
|
+
max: policy.max,
|
|
90
|
+
timeWindow: resourceRateLimitWindowMs(policy.timeWindow),
|
|
91
|
+
allowList: [],
|
|
92
|
+
skipOnError: false,
|
|
93
|
+
keyGenerator: (req) => req.ip
|
|
94
|
+
});
|
|
95
|
+
limiters.set(identity, consume);
|
|
96
|
+
}
|
|
97
|
+
const req = {
|
|
98
|
+
ip: JSON.stringify([identity, resourceRateLimitKey(resource, context)]),
|
|
99
|
+
routeOptions: { config: {} }
|
|
100
|
+
};
|
|
101
|
+
const result = await consume(req);
|
|
102
|
+
return result.isAllowed ? decision(true, policy.max, policy.max, Math.ceil(resourceRateLimitWindowMs(policy.timeWindow) / 1e3)) : decision(!result.isExceeded, result.max, result.remaining, result.ttlInSeconds);
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
Reflect.set(owner, RESOURCE_RATE_LIMITER, limiter);
|
|
106
|
+
return limiter;
|
|
107
|
+
}
|
|
108
|
+
/** Standalone tools may use memory; wired distributed apps use the host's shared store. */
|
|
109
|
+
function createMemoryResourceRateLimiter(capacity = 1e4) {
|
|
110
|
+
if (!Number.isSafeInteger(capacity) || capacity <= 0) throw new Error("rateLimit memory capacity must be a positive safe integer.");
|
|
111
|
+
const windows = /* @__PURE__ */ new Map();
|
|
112
|
+
return {
|
|
113
|
+
durability: "memory",
|
|
114
|
+
async consume(resource, policy, context) {
|
|
115
|
+
const now = Date.now();
|
|
116
|
+
const windowMs = resourceRateLimitWindowMs(policy.timeWindow);
|
|
117
|
+
const key = JSON.stringify([policyIdentity(resource, policy), resourceRateLimitKey(resource, context)]);
|
|
118
|
+
let slot = windows.get(key);
|
|
119
|
+
if (!slot || slot.expires <= now) {
|
|
120
|
+
if (windows.size >= capacity) {
|
|
121
|
+
for (const [entry, window] of windows) if (window.expires <= now) windows.delete(entry);
|
|
122
|
+
if (!windows.has(key) && windows.size >= capacity) {
|
|
123
|
+
let retryMs = Number.POSITIVE_INFINITY;
|
|
124
|
+
for (const entry of windows.values()) retryMs = Math.min(retryMs, entry.expires - now);
|
|
125
|
+
return decision(false, policy.max, 0, Math.ceil(retryMs / 1e3));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
slot = {
|
|
129
|
+
expires: now + windowMs,
|
|
130
|
+
count: 0
|
|
131
|
+
};
|
|
132
|
+
windows.set(key, slot);
|
|
133
|
+
}
|
|
134
|
+
slot.count++;
|
|
135
|
+
return decision(slot.count <= policy.max, policy.max, Math.max(0, policy.max - slot.count), Math.ceil((slot.expires - now) / 1e3));
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function resourceRateLimitWindowMs(window) {
|
|
140
|
+
const match = /^\s*((?:\d+(?:\.\d+)?|\.\d+))\s*(ms|milliseconds?|msecs?|s|secs?|seconds?|m|mins?|minutes?|h|hrs?|hours?|d|days?|w|wks?|weeks?|y|yrs?|years?)?\s*$/i.exec(window);
|
|
141
|
+
const value = Number(match?.[1]) * ({
|
|
142
|
+
ms: 1,
|
|
143
|
+
millisecond: 1,
|
|
144
|
+
milliseconds: 1,
|
|
145
|
+
msec: 1,
|
|
146
|
+
msecs: 1,
|
|
147
|
+
s: 1e3,
|
|
148
|
+
sec: 1e3,
|
|
149
|
+
secs: 1e3,
|
|
150
|
+
second: 1e3,
|
|
151
|
+
seconds: 1e3,
|
|
152
|
+
m: 6e4,
|
|
153
|
+
min: 6e4,
|
|
154
|
+
mins: 6e4,
|
|
155
|
+
minute: 6e4,
|
|
156
|
+
minutes: 6e4,
|
|
157
|
+
h: 36e5,
|
|
158
|
+
hr: 36e5,
|
|
159
|
+
hrs: 36e5,
|
|
160
|
+
hour: 36e5,
|
|
161
|
+
hours: 36e5,
|
|
162
|
+
d: 864e5,
|
|
163
|
+
day: 864e5,
|
|
164
|
+
days: 864e5,
|
|
165
|
+
w: 6048e5,
|
|
166
|
+
wk: 6048e5,
|
|
167
|
+
wks: 6048e5,
|
|
168
|
+
week: 6048e5,
|
|
169
|
+
weeks: 6048e5,
|
|
170
|
+
y: 315576e5,
|
|
171
|
+
yr: 315576e5,
|
|
172
|
+
yrs: 315576e5,
|
|
173
|
+
year: 315576e5,
|
|
174
|
+
years: 315576e5
|
|
175
|
+
}[match?.[2]?.toLowerCase() ?? "ms"] ?? 0);
|
|
176
|
+
if (!Number.isSafeInteger(value) || value <= 0) throw new Error(`rateLimit.timeWindow '${window}' must be a positive duration in milliseconds.`);
|
|
177
|
+
return value;
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
180
|
+
export { resourceRateLimitWindowMs as a, resolveResourceRateLimiter as i, markRateLimitingDisabled as n, recordUnenforcedRateLimit as r, createMemoryResourceRateLimiter as t };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
//#region \0rolldown/runtime.js
|
|
4
|
+
var __create = Object.create;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __esmMin = (fn, res, err) => () => {
|
|
11
|
+
if (err) throw err[0];
|
|
12
|
+
try {
|
|
13
|
+
return fn && (res = fn(fn = 0)), res;
|
|
14
|
+
} catch (e) {
|
|
15
|
+
throw err = [e], e;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod, mod.exports), cb = null), mod.exports);
|
|
19
|
+
var __exportAll = (all, no_symbols) => {
|
|
20
|
+
let target = {};
|
|
21
|
+
for (var name in all) __defProp(target, name, {
|
|
22
|
+
get: all[name],
|
|
23
|
+
enumerable: true
|
|
24
|
+
});
|
|
25
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
26
|
+
return target;
|
|
27
|
+
};
|
|
28
|
+
var __copyProps = (to, from, except, desc) => {
|
|
29
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
30
|
+
key = keys[i];
|
|
31
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
32
|
+
get: ((k) => from[k]).bind(null, key),
|
|
33
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return to;
|
|
37
|
+
};
|
|
38
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
39
|
+
value: mod,
|
|
40
|
+
enumerable: true
|
|
41
|
+
}) : target, mod));
|
|
42
|
+
var __toCommonJS = (mod) => __hasOwnProp.call(mod, "module.exports") ? mod["module.exports"] : __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
43
|
+
var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
|
|
44
|
+
//#endregion
|
|
45
|
+
export { __toCommonJS as a, __require as i, __esmMin as n, __toESM as o, __exportAll as r, __commonJSMin as t };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { AjvCompiler } from "@fastify/ajv-compiler";
|
|
3
|
+
//#region src/core/routeValidator.ts
|
|
4
|
+
/**
|
|
5
|
+
* Arc's request validator — the AJV settings every route arc builds is validated with, on ANY host.
|
|
6
|
+
*
|
|
7
|
+
* Fastify's stock AJV runs `removeAdditional: true`, which turns `additionalProperties: false`
|
|
8
|
+
* into a silent STRIP: an unknown key in a strict action body or filter querystring reaches the
|
|
9
|
+
* handler as if it were never sent, and a dropped filter is a WIDER read. Arc's routes therefore
|
|
10
|
+
* carry their own `validatorCompiler` unless the instance was built by `createApp`, whose root
|
|
11
|
+
* AJV already runs these options (plus the host's `ajv.keywords`).
|
|
12
|
+
* Pinned by `tests/core/route-validator-bare-host.test.ts`.
|
|
13
|
+
*/
|
|
14
|
+
/** The AJV options `createApp` configures and arc's own routes are compiled with. */
|
|
15
|
+
function arcAjvCustomOptions(extraKeywords = []) {
|
|
16
|
+
return {
|
|
17
|
+
coerceTypes: true,
|
|
18
|
+
useDefaults: true,
|
|
19
|
+
removeAdditional: false,
|
|
20
|
+
strictTypes: false,
|
|
21
|
+
keywords: ["example", ...extraKeywords]
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/** Mark an instance whose ROOT AJV was configured with `arcAjvCustomOptions` (createApp only). */
|
|
25
|
+
function markArcRootValidator(fastify) {
|
|
26
|
+
fastify.decorate("arcRootValidator", true);
|
|
27
|
+
}
|
|
28
|
+
/** One pool for the process: `@fastify/ajv-compiler` caches an AJV per (shared schemas, options). */
|
|
29
|
+
const buildFromPool = AjvCompiler();
|
|
30
|
+
/**
|
|
31
|
+
* The `validatorCompiler` an arc-built route registers with — `undefined` under `createApp`
|
|
32
|
+
* (the root already validates this way). Schemas added with `fastify.addSchema` stay
|
|
33
|
+
* `$ref`-able: the shared pool is read when Fastify compiles the route.
|
|
34
|
+
*/
|
|
35
|
+
function arcRouteValidatorCompiler(fastify) {
|
|
36
|
+
if (fastify.arcRootValidator === true) return void 0;
|
|
37
|
+
return ({ schema, httpPart }) => {
|
|
38
|
+
return buildFromPool(sharedSchemas(fastify), { customOptions: arcAjvCustomOptions() })({ schema: httpPart === "headers" ? lowerCaseHeaderSchema(schema) : schema });
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** The instance's `addSchema` pool, in the shape AJV accepts (a schema is an object or boolean). */
|
|
42
|
+
function sharedSchemas(fastify) {
|
|
43
|
+
const out = {};
|
|
44
|
+
for (const [id, schema] of Object.entries(fastify.getSchemas())) if (isSchemaValue(schema)) out[id] = schema;
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
function isSchemaValue(value) {
|
|
48
|
+
return typeof value === "boolean" || typeof value === "object" && value !== null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Fastify lower-cases a headers schema's keys before compiling it with its OWN compiler and
|
|
52
|
+
* skips that for a custom one — so a route-level compiler must do it, or `Authorization` in a
|
|
53
|
+
* schema never matches the `authorization` header Node delivers.
|
|
54
|
+
*/
|
|
55
|
+
function lowerCaseHeaderSchema(schema) {
|
|
56
|
+
if (!isPlainObject(schema)) return schema;
|
|
57
|
+
const out = { ...schema };
|
|
58
|
+
if (Array.isArray(schema.required)) out.required = schema.required.map((h) => typeof h === "string" ? h.toLowerCase() : h);
|
|
59
|
+
if (isPlainObject(schema.properties)) {
|
|
60
|
+
const properties = {};
|
|
61
|
+
for (const [key, value] of Object.entries(schema.properties)) properties[key.toLowerCase()] = value;
|
|
62
|
+
out.properties = properties;
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
function isPlainObject(value) {
|
|
67
|
+
return typeof value === "object" && value !== null && Object.getPrototypeOf(value) === Object.prototype;
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { arcRouteValidatorCompiler as n, markArcRootValidator as r, arcAjvCustomOptions as t };
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { t as
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { r as __exportAll } from "./rolldown-runtime-MYrLNGnq.mjs";
|
|
3
|
+
import { a as runShutdown, i as runAdmittedWork, r as registerShutdownParticipant } from "./shutdown-LpLMH8T1.mjs";
|
|
4
|
+
import { t as runWorkScope } from "./workScope-n9a7d_lZ.mjs";
|
|
5
|
+
import { t as startRenewingLease } from "./renewingLease-BFJxGPuh.mjs";
|
|
5
6
|
import fp from "fastify-plugin";
|
|
6
7
|
//#region src/plugins/schedules.ts
|
|
7
8
|
var schedules_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -61,10 +62,10 @@ const schedulesPlugin = async (fastify, opts) => {
|
|
|
61
62
|
}
|
|
62
63
|
stat.runs++;
|
|
63
64
|
stat.lastRunAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
64
|
-
await runWorkScope({
|
|
65
|
+
await runAdmittedWork(() => runWorkScope({
|
|
65
66
|
kind: "job",
|
|
66
67
|
id: s.name
|
|
67
|
-
}, () => s.handler(fastify));
|
|
68
|
+
}, () => s.handler(fastify)));
|
|
68
69
|
stat.lastError = null;
|
|
69
70
|
} catch (err) {
|
|
70
71
|
stat.failures++;
|
|
@@ -105,37 +106,45 @@ const schedulesPlugin = async (fastify, opts) => {
|
|
|
105
106
|
}
|
|
106
107
|
if (stats.size > 0) fastify.log.debug(`[arc-schedules] ${stats.size} schedule(s) armed${lock ? ` (leader-safe, holder ${holderId})` : ""}`);
|
|
107
108
|
});
|
|
109
|
+
registerShutdownParticipant(fastify, {
|
|
110
|
+
name: "schedules",
|
|
111
|
+
stop: () => {
|
|
112
|
+
closing = true;
|
|
113
|
+
for (const timer of timers.values()) clearTimeout(timer);
|
|
114
|
+
timers.clear();
|
|
115
|
+
},
|
|
116
|
+
drain: async () => {
|
|
117
|
+
/**
|
|
118
|
+
* Let in-flight runs settle so shutdown never truncates a sweep mid-write —
|
|
119
|
+
* but BOUNDED, because a tick has no cancellation contract and its duration
|
|
120
|
+
* is not this plugin's to predict. An outbox relay whose subscriber retries
|
|
121
|
+
* on a backoff ladder holds one tick for minutes, and an unbounded await
|
|
122
|
+
* here contradicts the `unref` two functions up: pending scheduled work is
|
|
123
|
+
* already declared as something that must not hold the process.
|
|
124
|
+
*
|
|
125
|
+
* Waiting longer than the orchestrator's grace period buys nothing anyway —
|
|
126
|
+
* SIGKILL truncates the same write, without the log line naming what was
|
|
127
|
+
* still running.
|
|
128
|
+
*/
|
|
129
|
+
const pending = [...inFlight.entries()];
|
|
130
|
+
if (pending.length === 0) return;
|
|
131
|
+
let timeout;
|
|
132
|
+
const expired = Symbol("drain-timeout");
|
|
133
|
+
const budget = new Promise((resolve) => {
|
|
134
|
+
timeout = setTimeout(() => resolve(expired), drainTimeoutMs);
|
|
135
|
+
timeout.unref?.();
|
|
136
|
+
});
|
|
137
|
+
const settled = Promise.allSettled(pending.map(([, run]) => run)).then(() => void 0);
|
|
138
|
+
const outcome = await Promise.race([settled, budget]);
|
|
139
|
+
if (timeout) clearTimeout(timeout);
|
|
140
|
+
if (outcome === expired) fastify.log.warn({
|
|
141
|
+
schedules: pending.map(([name]) => name),
|
|
142
|
+
drainTimeoutMs
|
|
143
|
+
}, "[arc-schedules] shutdown drain budget exceeded — abandoning in-flight run(s). Durable work (outbox rows, leased sweeps) is retried on the next boot; raise `drainTimeoutMs` only if a listed job is genuinely non-resumable.");
|
|
144
|
+
}
|
|
145
|
+
});
|
|
108
146
|
fastify.addHook("onClose", async () => {
|
|
109
|
-
|
|
110
|
-
for (const timer of timers.values()) clearTimeout(timer);
|
|
111
|
-
timers.clear();
|
|
112
|
-
/**
|
|
113
|
-
* Let in-flight runs settle so shutdown never truncates a sweep mid-write —
|
|
114
|
-
* but BOUNDED, because a tick has no cancellation contract and its duration
|
|
115
|
-
* is not this plugin's to predict. An outbox relay whose subscriber retries
|
|
116
|
-
* on a backoff ladder holds one tick for minutes, and an unbounded await
|
|
117
|
-
* here contradicts the `unref` two functions up: pending scheduled work is
|
|
118
|
-
* already declared as something that must not hold the process.
|
|
119
|
-
*
|
|
120
|
-
* Waiting longer than the orchestrator's grace period buys nothing anyway —
|
|
121
|
-
* SIGKILL truncates the same write, without the log line naming what was
|
|
122
|
-
* still running.
|
|
123
|
-
*/
|
|
124
|
-
const pending = [...inFlight.entries()];
|
|
125
|
-
if (pending.length === 0) return;
|
|
126
|
-
let timeout;
|
|
127
|
-
const expired = Symbol("drain-timeout");
|
|
128
|
-
const budget = new Promise((resolve) => {
|
|
129
|
-
timeout = setTimeout(() => resolve(expired), drainTimeoutMs);
|
|
130
|
-
timeout.unref?.();
|
|
131
|
-
});
|
|
132
|
-
const settled = Promise.allSettled(pending.map(([, run]) => run)).then(() => void 0);
|
|
133
|
-
const outcome = await Promise.race([settled, budget]);
|
|
134
|
-
if (timeout) clearTimeout(timeout);
|
|
135
|
-
if (outcome === expired) fastify.log.warn({
|
|
136
|
-
schedules: pending.map(([name]) => name),
|
|
137
|
-
drainTimeoutMs
|
|
138
|
-
}, "[arc-schedules] shutdown drain budget exceeded — abandoning in-flight run(s). Durable work (outbox rows, leased sweeps) is retried on the next boot; raise `drainTimeoutMs` only if a listed job is genuinely non-resumable.");
|
|
147
|
+
await runShutdown(fastify);
|
|
139
148
|
});
|
|
140
149
|
};
|
|
141
150
|
var schedules_default = fp(schedulesPlugin, {
|
|
@@ -143,4 +152,4 @@ var schedules_default = fp(schedulesPlugin, {
|
|
|
143
152
|
fastify: "5.x"
|
|
144
153
|
});
|
|
145
154
|
//#endregion
|
|
146
|
-
export {
|
|
155
|
+
export { schedules_exports as n, schedules_default as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { arcLog } from "./logger
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as arcLog } from "./logger-D4V1zGLI.mjs";
|
|
3
3
|
//#region src/utils/schemaConverter.ts
|
|
4
4
|
/**
|
|
5
5
|
* Schema Converter — Detect-First, Convert-Only-When-Needed
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { o as toJsonSchema } from "./schemaConverter-
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { o as toJsonSchema } from "./schemaConverter-BVjkQ5DD.mjs";
|
|
3
3
|
//#region src/core/schemaIR.ts
|
|
4
4
|
/**
|
|
5
5
|
* Schema IR — one canonical representation, two adapters.
|
package/dist/schemas/index.d.mts
CHANGED
|
@@ -17,7 +17,7 @@ import { FastifyPluginAsyncTypebox, FastifyPluginCallbackTypebox, TypeBoxTypePro
|
|
|
17
17
|
* variant when your endpoint never emits the others. HTTP status
|
|
18
18
|
* discriminates success vs error — no `success` field.
|
|
19
19
|
*/
|
|
20
|
-
declare function ArcListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TUnion<[Type$1.TObject<{
|
|
20
|
+
export declare function ArcListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TUnion<[Type$1.TObject<{
|
|
21
21
|
method: Type$1.TLiteral<"offset">;
|
|
22
22
|
data: Type$1.TArray<T>;
|
|
23
23
|
page: Type$1.TInteger;
|
|
@@ -26,12 +26,17 @@ declare function ArcListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TUn
|
|
|
26
26
|
pages: Type$1.TInteger;
|
|
27
27
|
hasNext: Type$1.TBoolean;
|
|
28
28
|
hasPrev: Type$1.TBoolean;
|
|
29
|
+
totalIsEstimate: Type$1.TOptional<Type$1.TBoolean>;
|
|
30
|
+
countedAt: Type$1.TOptional<Type$1.TUnion<[Type$1.TString, Type$1.TNull]>>;
|
|
29
31
|
}>, Type$1.TObject<{
|
|
30
32
|
method: Type$1.TLiteral<"keyset">;
|
|
31
33
|
data: Type$1.TArray<T>;
|
|
32
34
|
limit: Type$1.TInteger;
|
|
33
35
|
hasMore: Type$1.TBoolean;
|
|
34
36
|
next: Type$1.TUnion<[Type$1.TString, Type$1.TNull]>;
|
|
37
|
+
prev: Type$1.TOptional<Type$1.TUnion<[Type$1.TString, Type$1.TNull]>>;
|
|
38
|
+
hasPrev: Type$1.TOptional<Type$1.TBoolean>;
|
|
39
|
+
end: Type$1.TOptional<Type$1.TUnion<[Type$1.TString, Type$1.TNull]>>;
|
|
35
40
|
}>, Type$1.TObject<{
|
|
36
41
|
method: Type$1.TLiteral<"aggregate">;
|
|
37
42
|
data: Type$1.TArray<T>;
|
|
@@ -45,7 +50,7 @@ declare function ArcListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TUn
|
|
|
45
50
|
data: Type$1.TArray<T>;
|
|
46
51
|
}>]>;
|
|
47
52
|
/** Offset variant — `{ method: 'offset', data, page, limit, total, pages, hasNext, hasPrev }`. */
|
|
48
|
-
declare function ArcOffsetListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
|
|
53
|
+
export declare function ArcOffsetListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
|
|
49
54
|
method: Type$1.TLiteral<"offset">;
|
|
50
55
|
data: Type$1.TArray<T>;
|
|
51
56
|
page: Type$1.TInteger;
|
|
@@ -54,17 +59,22 @@ declare function ArcOffsetListResponse<T extends TSchema$1>(itemSchema: T): Type
|
|
|
54
59
|
pages: Type$1.TInteger;
|
|
55
60
|
hasNext: Type$1.TBoolean;
|
|
56
61
|
hasPrev: Type$1.TBoolean;
|
|
62
|
+
totalIsEstimate: Type$1.TOptional<Type$1.TBoolean>;
|
|
63
|
+
countedAt: Type$1.TOptional<Type$1.TUnion<[Type$1.TString, Type$1.TNull]>>;
|
|
57
64
|
}>;
|
|
58
65
|
/** Keyset variant — `{ method: 'keyset', data, limit, hasMore, next: string | null }`. */
|
|
59
|
-
declare function ArcKeysetListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
|
|
66
|
+
export declare function ArcKeysetListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
|
|
60
67
|
method: Type$1.TLiteral<"keyset">;
|
|
61
68
|
data: Type$1.TArray<T>;
|
|
62
69
|
limit: Type$1.TInteger;
|
|
63
70
|
hasMore: Type$1.TBoolean;
|
|
64
71
|
next: Type$1.TUnion<[Type$1.TString, Type$1.TNull]>;
|
|
72
|
+
prev: Type$1.TOptional<Type$1.TUnion<[Type$1.TString, Type$1.TNull]>>;
|
|
73
|
+
hasPrev: Type$1.TOptional<Type$1.TBoolean>;
|
|
74
|
+
end: Type$1.TOptional<Type$1.TUnion<[Type$1.TString, Type$1.TNull]>>;
|
|
65
75
|
}>;
|
|
66
76
|
/** Aggregate variant — `{ method: 'aggregate', ...same as offset }`. */
|
|
67
|
-
declare function ArcAggregateListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
|
|
77
|
+
export declare function ArcAggregateListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
|
|
68
78
|
method: Type$1.TLiteral<"aggregate">;
|
|
69
79
|
data: Type$1.TArray<T>;
|
|
70
80
|
page: Type$1.TInteger;
|
|
@@ -75,13 +85,13 @@ declare function ArcAggregateListResponse<T extends TSchema$1>(itemSchema: T): T
|
|
|
75
85
|
hasPrev: Type$1.TBoolean;
|
|
76
86
|
}>;
|
|
77
87
|
/** Bare variant — `{ data }`, no `method` discriminant. */
|
|
78
|
-
declare function ArcBareListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
|
|
88
|
+
export declare function ArcBareListResponse<T extends TSchema$1>(itemSchema: T): Type$1.TObject<{
|
|
79
89
|
data: Type$1.TArray<T>;
|
|
80
90
|
}>;
|
|
81
91
|
/**
|
|
82
92
|
* Delete response — `{ message, id?, soft? }` raw at the top level.
|
|
83
93
|
*/
|
|
84
|
-
declare function ArcDeleteResponse(): Type$1.TObject<{
|
|
94
|
+
export declare function ArcDeleteResponse(): Type$1.TObject<{
|
|
85
95
|
message: Type$1.TString;
|
|
86
96
|
id: Type$1.TOptional<Type$1.TString>;
|
|
87
97
|
soft: Type$1.TOptional<Type$1.TBoolean>;
|
|
@@ -96,7 +106,7 @@ declare function ArcDeleteResponse(): Type$1.TObject<{
|
|
|
96
106
|
* Exported standalone so hosts can embed it in custom 422 / 409 response
|
|
97
107
|
* schemas.
|
|
98
108
|
*/
|
|
99
|
-
declare function ArcErrorDetail(): Type$1.TUnsafe<ErrorDetail>;
|
|
109
|
+
export declare function ArcErrorDetail(): Type$1.TUnsafe<ErrorDetail>;
|
|
100
110
|
/**
|
|
101
111
|
* Error response schema — `Type.Unsafe<ErrorContract>` over the canonical
|
|
102
112
|
* JSON Schema `errorContractSchema` from `@classytic/repo-core/errors`.
|
|
@@ -105,7 +115,7 @@ declare function ArcErrorDetail(): Type$1.TUnsafe<ErrorDetail>;
|
|
|
105
115
|
* (`errorContractSchema`) and this TypeBox helper cannot drift —
|
|
106
116
|
* literally one source.
|
|
107
117
|
*/
|
|
108
|
-
declare function ArcErrorResponse(): Type$1.TUnsafe<ErrorContract>;
|
|
118
|
+
export declare function ArcErrorResponse(): Type$1.TUnsafe<ErrorContract>;
|
|
109
119
|
/**
|
|
110
120
|
* Standard pagination + sorting + filtering query parameters.
|
|
111
121
|
* Matches Arc's list endpoint conventions.
|
|
@@ -121,7 +131,7 @@ declare function ArcErrorResponse(): Type$1.TUnsafe<ErrorContract>;
|
|
|
121
131
|
* projection forms at the request-validation gate even though arc passes
|
|
122
132
|
* them through unchanged.
|
|
123
133
|
*/
|
|
124
|
-
declare function ArcPaginationQuery(): Type$1.TObject<{
|
|
134
|
+
export declare function ArcPaginationQuery(): Type$1.TObject<{
|
|
125
135
|
page: Type$1.TOptional<Type$1.TInteger>;
|
|
126
136
|
limit: Type$1.TOptional<Type$1.TInteger>;
|
|
127
137
|
sort: Type$1.TOptional<Type$1.TString>;
|
|
@@ -129,4 +139,4 @@ declare function ArcPaginationQuery(): Type$1.TObject<{
|
|
|
129
139
|
populate: Type$1.TOptional<Type$1.TAny>;
|
|
130
140
|
}>;
|
|
131
141
|
//#endregion
|
|
132
|
-
export {
|
|
142
|
+
export { type FastifyPluginAsyncTypebox, type FastifyPluginCallbackTypebox, type Static, type TObject, type TSchema, Type, type TypeBoxTypeProvider };
|
package/dist/schemas/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
2
|
import { errorContractSchema, errorDetailSchema } from "@classytic/repo-core/errors";
|
|
3
3
|
import { Type, Type as Type$1 } from "typebox";
|
|
4
4
|
//#region src/schemas/index.ts
|
|
@@ -34,7 +34,9 @@ function ArcOffsetListResponse(itemSchema) {
|
|
|
34
34
|
total: Type$1.Integer(),
|
|
35
35
|
pages: Type$1.Integer(),
|
|
36
36
|
hasNext: Type$1.Boolean(),
|
|
37
|
-
hasPrev: Type$1.Boolean()
|
|
37
|
+
hasPrev: Type$1.Boolean(),
|
|
38
|
+
totalIsEstimate: Type$1.Optional(Type$1.Boolean()),
|
|
39
|
+
countedAt: Type$1.Optional(Type$1.Union([Type$1.String({ format: "date-time" }), Type$1.Null()]))
|
|
38
40
|
});
|
|
39
41
|
}
|
|
40
42
|
/** Keyset variant — `{ method: 'keyset', data, limit, hasMore, next: string | null }`. */
|
|
@@ -44,7 +46,10 @@ function ArcKeysetListResponse(itemSchema) {
|
|
|
44
46
|
data: Type$1.Array(itemSchema),
|
|
45
47
|
limit: Type$1.Integer(),
|
|
46
48
|
hasMore: Type$1.Boolean(),
|
|
47
|
-
next: Type$1.Union([Type$1.String(), Type$1.Null()])
|
|
49
|
+
next: Type$1.Union([Type$1.String(), Type$1.Null()]),
|
|
50
|
+
prev: Type$1.Optional(Type$1.Union([Type$1.String(), Type$1.Null()])),
|
|
51
|
+
hasPrev: Type$1.Optional(Type$1.Boolean()),
|
|
52
|
+
end: Type$1.Optional(Type$1.Union([Type$1.String(), Type$1.Null()]))
|
|
48
53
|
});
|
|
49
54
|
}
|
|
50
55
|
/** Aggregate variant — `{ method: 'aggregate', ...same as offset }`. */
|
package/dist/scim/index.d.mts
CHANGED
|
@@ -33,15 +33,15 @@ interface ScimResourceMapping {
|
|
|
33
33
|
/** Custom outbound transform — runs after attribute mapping. */
|
|
34
34
|
toScim?: (resource: Record<string, unknown>, mapped: Record<string, unknown>) => Record<string, unknown>;
|
|
35
35
|
}
|
|
36
|
-
declare const SCIM_USER_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:User";
|
|
37
|
-
declare const SCIM_GROUP_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:Group";
|
|
38
|
-
declare const SCIM_ENTERPRISE_USER_SCHEMA = "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User";
|
|
39
|
-
declare const DEFAULT_USER_MAPPING: ScimResourceMapping;
|
|
40
|
-
declare const DEFAULT_GROUP_MAPPING: ScimResourceMapping;
|
|
36
|
+
export declare const SCIM_USER_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:User";
|
|
37
|
+
export declare const SCIM_GROUP_SCHEMA = "urn:ietf:params:scim:schemas:core:2.0:Group";
|
|
38
|
+
export declare const SCIM_ENTERPRISE_USER_SCHEMA = "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User";
|
|
39
|
+
export declare const DEFAULT_USER_MAPPING: ScimResourceMapping;
|
|
40
|
+
export declare const DEFAULT_GROUP_MAPPING: ScimResourceMapping;
|
|
41
41
|
/** Translate inbound SCIM JSON → resource shape. */
|
|
42
|
-
declare function scimToResource(scim: Record<string, unknown>, mapping: ScimResourceMapping): Record<string, unknown>;
|
|
42
|
+
export declare function scimToResource(scim: Record<string, unknown>, mapping: ScimResourceMapping): Record<string, unknown>;
|
|
43
43
|
/** Translate resource → SCIM JSON. */
|
|
44
|
-
declare function resourceToScim(resource: Record<string, unknown>, mapping: ScimResourceMapping, baseUrl: string): Record<string, unknown>;
|
|
44
|
+
export declare function resourceToScim(resource: Record<string, unknown>, mapping: ScimResourceMapping, baseUrl: string): Record<string, unknown>;
|
|
45
45
|
//#endregion
|
|
46
46
|
//#region src/scim/types.d.ts
|
|
47
47
|
/**
|
|
@@ -138,7 +138,7 @@ interface ScimObservedEvent {
|
|
|
138
138
|
* ```
|
|
139
139
|
*/
|
|
140
140
|
type ScimType = "invalidFilter" | "tooMany" | "uniqueness" | "mutability" | "invalidSyntax" | "invalidPath" | "noTarget" | "invalidValue" | "invalidVers" | "sensitive";
|
|
141
|
-
declare class ScimError extends Error {
|
|
141
|
+
export declare class ScimError extends Error {
|
|
142
142
|
readonly statusCode: number;
|
|
143
143
|
readonly scimType?: ScimType;
|
|
144
144
|
constructor(statusCode: number, scimType: ScimType | undefined, detail: string);
|
|
@@ -197,9 +197,9 @@ type FilterNode = {
|
|
|
197
197
|
* `undefined` to deny (yields 400 invalidFilter). Use `IDENTITY_MAP` when
|
|
198
198
|
* the resource exposes SCIM-named attributes directly.
|
|
199
199
|
*/
|
|
200
|
-
declare function parseScimFilter(filter: string, mapAttr: (scimAttr: string) => string | undefined): Record<string, unknown>;
|
|
200
|
+
export declare function parseScimFilter(filter: string, mapAttr: (scimAttr: string) => string | undefined): Record<string, unknown>;
|
|
201
201
|
/** Pass-through mapper for resources that already use SCIM attribute names. */
|
|
202
|
-
declare const IDENTITY_MAP: (a: string) => string;
|
|
202
|
+
export declare const IDENTITY_MAP: (a: string) => string;
|
|
203
203
|
//#endregion
|
|
204
204
|
//#region src/scim/patch.d.ts
|
|
205
205
|
/**
|
|
@@ -248,17 +248,17 @@ interface ScimUpdate {
|
|
|
248
248
|
/** Field unset (remove without filter). */
|
|
249
249
|
$unset: Record<string, true>;
|
|
250
250
|
}
|
|
251
|
-
declare function parseScimPatch(req: ScimPatchRequest): ScimUpdate;
|
|
251
|
+
export declare function parseScimPatch(req: ScimPatchRequest): ScimUpdate;
|
|
252
252
|
/**
|
|
253
253
|
* Flatten a {@link ScimUpdate} into a plain `{ field: value }` object suitable
|
|
254
254
|
* for arc resource `PATCH` handlers that expect a partial document. Drops
|
|
255
255
|
* `$push` / `$pull` / `$unset` semantics — use {@link parseScimPatch} directly
|
|
256
256
|
* when the host needs the full op stream (e.g. to issue array mutations).
|
|
257
257
|
*/
|
|
258
|
-
declare function scimUpdateToFlatPatch(update: ScimUpdate): Record<string, unknown>;
|
|
258
|
+
export declare function scimUpdateToFlatPatch(update: ScimUpdate): Record<string, unknown>;
|
|
259
259
|
//#endregion
|
|
260
260
|
//#region src/scim/index.d.ts
|
|
261
261
|
declare const scimPlugin: FastifyPluginAsync<ScimPluginOptions>;
|
|
262
262
|
declare const _default: FastifyPluginAsync<ScimPluginOptions>;
|
|
263
263
|
//#endregion
|
|
264
|
-
export {
|
|
264
|
+
export { type FilterNode, type ScimObservedEvent, type ScimPatchRequest, type ScimPluginOptions, type ScimResourceBinding, type ScimResourceMapping, type ScimType, type ScimUpdate, _default as default, scimPlugin };
|
package/dist/scim/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as parseJsonBody } from "../jsonBody-
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as parseJsonBody } from "../jsonBody-YIq-vGLv.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/scim/errors.ts
|
|
5
5
|
var ScimError = class extends Error {
|