@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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import { n as
|
|
2
|
+
import { n as UpstashKeyValueCommands, t as IoredisKeyValueCommands } from "./redisShapes-CQksY67q.mjs";
|
|
3
|
+
import { i as IdempotencyStore, r as IdempotencyResult, t as IdempotencyAcquisition } from "./interface-JJzwH4Ro.mjs";
|
|
3
4
|
//#region src/idempotency/stores/redis.d.ts
|
|
4
5
|
interface RedisClient {
|
|
5
6
|
get(key: string): Promise<string | null>;
|
|
@@ -26,6 +27,9 @@ interface RedisIdempotencyStoreOptions {
|
|
|
26
27
|
}
|
|
27
28
|
declare class RedisIdempotencyStore implements IdempotencyStore {
|
|
28
29
|
readonly name = "redis";
|
|
30
|
+
readonly supportsFencedCompletion: boolean;
|
|
31
|
+
/** The fenced completion script deletes the lock; it needs `eval`, as fencing does. */
|
|
32
|
+
readonly completionReleasesLock: boolean;
|
|
29
33
|
private client;
|
|
30
34
|
private prefix;
|
|
31
35
|
private lockPrefix;
|
|
@@ -34,8 +38,11 @@ declare class RedisIdempotencyStore implements IdempotencyStore {
|
|
|
34
38
|
private resultKey;
|
|
35
39
|
private lockKey;
|
|
36
40
|
get(key: string): Promise<IdempotencyResult | undefined>;
|
|
41
|
+
private parseResult;
|
|
37
42
|
set(key: string, result: Omit<IdempotencyResult, "key">): Promise<void>;
|
|
38
43
|
tryLock(key: string, requestId: string, ttlMs: number): Promise<boolean>;
|
|
44
|
+
acquire(key: string, requestId: string, lockTtlMs: number): Promise<IdempotencyAcquisition>;
|
|
45
|
+
setIfLocked(key: string, requestId: string, result: Omit<IdempotencyResult, "key">): Promise<boolean>;
|
|
39
46
|
unlock(key: string, requestId: string): Promise<void>;
|
|
40
47
|
isLocked(key: string): Promise<boolean>;
|
|
41
48
|
delete(key: string): Promise<void>;
|
|
@@ -46,21 +53,12 @@ declare class RedisIdempotencyStore implements IdempotencyStore {
|
|
|
46
53
|
close(): Promise<void>;
|
|
47
54
|
}
|
|
48
55
|
/**
|
|
49
|
-
* Minimal ioredis shape
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* every command as overloads whose tails mix literal tokens (`'MATCH'`),
|
|
53
|
-
* `Buffer` and an optional callback, and a narrower tail matches NONE of
|
|
54
|
-
* them — a real `Redis` instance then fails to assign (TS2345). Pinned by
|
|
55
|
-
* `tests/types/ioredis-assignability.test.ts`.
|
|
56
|
+
* Minimal ioredis shape the idempotency store depends on — the shared
|
|
57
|
+
* key/value commands (`utils/redisShapes.ts`, where the `unknown[]`-tail rule
|
|
58
|
+
* is explained) plus `exists` and lifecycle. Keeps this file peer-dep-free.
|
|
56
59
|
*/
|
|
57
|
-
interface IoredisLike {
|
|
58
|
-
get(key: string): Promise<string | null>;
|
|
59
|
-
set(...args: unknown[]): Promise<string | null>;
|
|
60
|
-
del(...keys: string[]): Promise<number>;
|
|
60
|
+
interface IoredisLike extends IoredisKeyValueCommands {
|
|
61
61
|
exists(...keys: string[]): Promise<number>;
|
|
62
|
-
scan(cursor: string | number, ...args: unknown[]): Promise<[string, string[]]>;
|
|
63
|
-
eval?(script: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
|
|
64
62
|
quit?(): Promise<string>;
|
|
65
63
|
disconnect?(): void;
|
|
66
64
|
}
|
|
@@ -86,17 +84,12 @@ interface IoredisLike {
|
|
|
86
84
|
declare function ioredisAsIdempotencyClient(client: IoredisLike): RedisClient & {
|
|
87
85
|
eval?: (script: string, numkeys: number, ...args: (string | number)[]) => Promise<unknown>;
|
|
88
86
|
};
|
|
89
|
-
/**
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Minimal `@upstash/redis` shape the idempotency store depends on (REST,
|
|
89
|
+
* edge-safe) — the shared key/value commands plus `exists`.
|
|
90
|
+
*/
|
|
91
|
+
interface UpstashRedisLike extends UpstashKeyValueCommands {
|
|
94
92
|
exists(...keys: string[]): Promise<number>;
|
|
95
|
-
scan(cursor: number | string, opts?: {
|
|
96
|
-
match?: string;
|
|
97
|
-
count?: number;
|
|
98
|
-
}): Promise<[number, string[]] | [string, string[]]>;
|
|
99
|
-
eval?(script: string, keys: string[], args: (string | number)[]): Promise<unknown>;
|
|
100
93
|
}
|
|
101
94
|
/**
|
|
102
95
|
* Wrap an `@upstash/redis` REST client as an idempotency `RedisClient`.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
|
|
2
|
+
import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "./EventTransport-CiicKz2Z.mjs";
|
|
3
|
+
//#region src/events/transports/redis-stream.d.ts
|
|
4
|
+
interface RedisStreamLike {
|
|
5
|
+
xadd(key: string, id: string, ...fieldValues: string[]): Promise<string | null>;
|
|
6
|
+
/**
|
|
7
|
+
* Per-handler progress for a partially failed message — which handlers already completed, so
|
|
8
|
+
* a retry (on any replica) runs only the rest. Optional for custom wrappers; without them a
|
|
9
|
+
* retry re-runs every handler and the transport warns once.
|
|
10
|
+
*/
|
|
11
|
+
hset?(key: string, ...fieldValues: string[]): Promise<unknown>;
|
|
12
|
+
hgetall?(key: string): Promise<Record<string, string>>;
|
|
13
|
+
del?(...keys: string[]): Promise<unknown>;
|
|
14
|
+
pexpire?(key: string, ms: number): Promise<unknown>;
|
|
15
|
+
xreadgroup(command: "GROUP", group: string, consumer: string, ...args: unknown[]): Promise<Array<[string, Array<[string, string[] | null]>]> | null>;
|
|
16
|
+
xack(key: string, group: string, ...ids: unknown[]): Promise<number>;
|
|
17
|
+
xgroup(command: string, key: string, group: string, ...args: unknown[]): Promise<unknown>;
|
|
18
|
+
xpending(key: string, group: string, ...args: unknown[]): Promise<unknown[]>;
|
|
19
|
+
xclaim(key: string, group: string, consumer: string, minIdleTime: number, ...ids: unknown[]): Promise<unknown[]>;
|
|
20
|
+
xlen(key: string): Promise<number>;
|
|
21
|
+
/**
|
|
22
|
+
* Lets the DLQ writer copy the original payload into the envelope, making
|
|
23
|
+
* dead-lettered events replayable. Without it the envelope keeps the error
|
|
24
|
+
* and attempt count but `event.payload` is `null` (warned once per process).
|
|
25
|
+
*/
|
|
26
|
+
xrange?(key: string, start: string, end: string): Promise<Array<[string, string[]]>>;
|
|
27
|
+
/** Graceful close — flushes queued commands then closes. ioredis-style. */
|
|
28
|
+
quit(): Promise<unknown>;
|
|
29
|
+
/**
|
|
30
|
+
* Force-disconnect, abandoning a live `XREADGROUP BLOCK`. When missing,
|
|
31
|
+
* `close()` falls back to `quit()` and may wait up to `blockTimeMs`.
|
|
32
|
+
*/
|
|
33
|
+
disconnect?(): void;
|
|
34
|
+
}
|
|
35
|
+
interface RedisStreamTransportOptions {
|
|
36
|
+
/**
|
|
37
|
+
* Redis stream key name.
|
|
38
|
+
* @default 'arc:events'
|
|
39
|
+
*/
|
|
40
|
+
stream?: string;
|
|
41
|
+
/**
|
|
42
|
+
* How long a partially failed message's per-handler progress is kept, in ms. Must outlast the
|
|
43
|
+
* retry window. @default 7 days
|
|
44
|
+
*/
|
|
45
|
+
handlerProgressTtlMs?: number;
|
|
46
|
+
/**
|
|
47
|
+
* Consumer group name. Each group receives every event independently.
|
|
48
|
+
* Multiple instances of the same service should share a group name.
|
|
49
|
+
* @default 'default'
|
|
50
|
+
*/
|
|
51
|
+
group?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Consumer name within the group. Must be unique per instance.
|
|
54
|
+
* @default 'consumer-<random>'
|
|
55
|
+
*/
|
|
56
|
+
consumer?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Block time in ms when waiting for new events.
|
|
59
|
+
* @default 5000
|
|
60
|
+
*/
|
|
61
|
+
blockTimeMs?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Max events to read per batch.
|
|
64
|
+
* @default 10
|
|
65
|
+
*/
|
|
66
|
+
batchSize?: number;
|
|
67
|
+
/**
|
|
68
|
+
* Max entries of a batch processed concurrently. `1` keeps strict stream
|
|
69
|
+
* order; higher values let entries complete OUT OF ORDER, so raise it only
|
|
70
|
+
* for order-independent handlers. Handlers for a single event always run
|
|
71
|
+
* sequentially.
|
|
72
|
+
* @default 1
|
|
73
|
+
*/
|
|
74
|
+
processingConcurrency?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Max delivery attempts before moving to dead letter stream.
|
|
77
|
+
* @default 5
|
|
78
|
+
*/
|
|
79
|
+
maxRetries?: number;
|
|
80
|
+
/**
|
|
81
|
+
* Idle time in ms before another consumer may claim a pending entry
|
|
82
|
+
* (crash recovery).
|
|
83
|
+
* @default 30000
|
|
84
|
+
*/
|
|
85
|
+
claimTimeoutMs?: number;
|
|
86
|
+
/**
|
|
87
|
+
* Dead letter stream name. Failed events are moved here after maxRetries.
|
|
88
|
+
* Set to `false` to disable DLQ (failed events are acked and dropped).
|
|
89
|
+
* @default 'arc:events:dlq'
|
|
90
|
+
*/
|
|
91
|
+
deadLetterStream?: string | false;
|
|
92
|
+
/**
|
|
93
|
+
* Max stream length (approximate, XADD MAXLEN ~). Trimming can drop entries still PENDING for a
|
|
94
|
+
* slow consumer, so it is opt-in: set it only with lag / oldest-pending monitoring in place.
|
|
95
|
+
* @default 0 (no trimming)
|
|
96
|
+
*/
|
|
97
|
+
maxLen?: number;
|
|
98
|
+
/**
|
|
99
|
+
* Where a NEW consumer group starts reading. `'latest'` (`$`) skips entries published before the
|
|
100
|
+
* group existed; `'beginning'` (`0`) replays the whole stream — including every side effect.
|
|
101
|
+
* @default 'latest'
|
|
102
|
+
*/
|
|
103
|
+
groupStart?: "latest" | "beginning";
|
|
104
|
+
/**
|
|
105
|
+
* Max serialized event size in bytes; `publish()` rejects larger events.
|
|
106
|
+
* @default 1_000_000 (1 MB)
|
|
107
|
+
*/
|
|
108
|
+
maxPayloadBytes?: number;
|
|
109
|
+
/**
|
|
110
|
+
* If `true`, `close()` never quits/disconnects the client — for a Redis
|
|
111
|
+
* connection shared with other transports or stores.
|
|
112
|
+
* @default false
|
|
113
|
+
*/
|
|
114
|
+
externalLifecycle?: boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Max time `close()` waits for the in-flight `XREADGROUP BLOCK` to drain.
|
|
117
|
+
* On timeout the client is force-disconnected, or — under
|
|
118
|
+
* `externalLifecycle` — the loop drains in the background (see `close()`).
|
|
119
|
+
* @default 1000
|
|
120
|
+
*/
|
|
121
|
+
closeTimeoutMs?: number;
|
|
122
|
+
/**
|
|
123
|
+
* Logger for error messages (default: console).
|
|
124
|
+
* Pass `fastify.log` to integrate with your application logger.
|
|
125
|
+
*/
|
|
126
|
+
logger?: EventLogger;
|
|
127
|
+
}
|
|
128
|
+
declare class RedisStreamTransport implements EventTransport {
|
|
129
|
+
readonly name = "redis-stream";
|
|
130
|
+
private redis;
|
|
131
|
+
private stream;
|
|
132
|
+
private group;
|
|
133
|
+
private consumer;
|
|
134
|
+
private blockTimeMs;
|
|
135
|
+
private batchSize;
|
|
136
|
+
private processingConcurrency;
|
|
137
|
+
private maxRetries;
|
|
138
|
+
private claimTimeoutMs;
|
|
139
|
+
private deadLetterStream;
|
|
140
|
+
private maxLen;
|
|
141
|
+
private groupStartId;
|
|
142
|
+
private maxPayloadBytes;
|
|
143
|
+
private logger;
|
|
144
|
+
private externalLifecycle;
|
|
145
|
+
private closeTimeoutMs;
|
|
146
|
+
private router;
|
|
147
|
+
private running;
|
|
148
|
+
private pollPromise;
|
|
149
|
+
/**
|
|
150
|
+
* Bumped whenever the poll loop should stop (last unsubscribe, `close()`).
|
|
151
|
+
* Each loop exits once its captured generation is stale, so a fast
|
|
152
|
+
* unsubscribe → resubscribe never leaves two loops on one consumer name.
|
|
153
|
+
*/
|
|
154
|
+
private generation;
|
|
155
|
+
private groupCreated;
|
|
156
|
+
/**
|
|
157
|
+
* Last handler failure per message id, so the DLQ envelope carries the real
|
|
158
|
+
* error. Cleared on ack / DLQ write; capped at {@link MAX_FAILURE_CONTEXT_ENTRIES}.
|
|
159
|
+
*/
|
|
160
|
+
private failureContext;
|
|
161
|
+
/** Set by `hold()`: subscriptions register, consumption waits for `start()`. */
|
|
162
|
+
private held;
|
|
163
|
+
/** Handler → a key stable across replicas: pattern, name, and order among same-named handlers. */
|
|
164
|
+
private handlerKeys;
|
|
165
|
+
private handlerKeyOwners;
|
|
166
|
+
private handlerPatterns;
|
|
167
|
+
private handlerProgressTtlMs;
|
|
168
|
+
private readonly tracksProgress;
|
|
169
|
+
private xrangeWarningEmitted;
|
|
170
|
+
constructor(redis: RedisStreamLike, options?: RedisStreamTransportOptions);
|
|
171
|
+
publish(event: DomainEvent): Promise<void>;
|
|
172
|
+
subscribe(pattern: string, handler: EventHandler): Promise<() => void>;
|
|
173
|
+
/**
|
|
174
|
+
* Register subscriptions without consuming until `start()`. arc holds consumption for the whole
|
|
175
|
+
* boot, so an entry for a module whose handler installs later is not acked with no handler.
|
|
176
|
+
*/
|
|
177
|
+
hold(): void;
|
|
178
|
+
/** Begin consuming — every handler registered while held sees the stream from here. */
|
|
179
|
+
start(): Promise<void>;
|
|
180
|
+
private startPolling;
|
|
181
|
+
/**
|
|
182
|
+
* Stop polling and release transport state. Waits up to `closeTimeoutMs`
|
|
183
|
+
* for the in-flight `XREADGROUP BLOCK`, then:
|
|
184
|
+
*
|
|
185
|
+
* - default: force-disconnects the client to break the BLOCK and awaits the
|
|
186
|
+
* loop — the transport is fully closed on return.
|
|
187
|
+
* - `externalLifecycle: true`: returns without touching the host's client;
|
|
188
|
+
* the loop drains in the background (up to `blockTimeMs` — keep it low)
|
|
189
|
+
* with its outcome silenced. Handlers are already cleared, so nothing
|
|
190
|
+
* dispatches meanwhile.
|
|
191
|
+
*
|
|
192
|
+
* A later `subscribe()` starts a fresh loop that never overlaps the stale one.
|
|
193
|
+
*/
|
|
194
|
+
close(): Promise<void>;
|
|
195
|
+
private ensureGroup;
|
|
196
|
+
private pollLoop;
|
|
197
|
+
private readNewMessages;
|
|
198
|
+
/** Process a batch with at most `processingConcurrency` entries in flight. */
|
|
199
|
+
private processEntries;
|
|
200
|
+
private claimPending;
|
|
201
|
+
private processEntry;
|
|
202
|
+
/**
|
|
203
|
+
* Uses the primitives router — the same glob rules (and dedup) as the memory
|
|
204
|
+
* transport. Don't swap in a private matcher: subscriptions must behave
|
|
205
|
+
* identically in-process and on the stream.
|
|
206
|
+
*/
|
|
207
|
+
private getMatchingHandlers;
|
|
208
|
+
private moveToDlq;
|
|
209
|
+
/**
|
|
210
|
+
* Build a `DeadLetteredEvent` from the original entry (via `xrange`) plus
|
|
211
|
+
* any local failure context. If the entry is unreadable (no `xrange`, error,
|
|
212
|
+
* or trimmed) the envelope keeps just the error and attempts. Returns `null`
|
|
213
|
+
* only when both are missing.
|
|
214
|
+
*/
|
|
215
|
+
private buildDlqEnvelope;
|
|
216
|
+
private sleep;
|
|
217
|
+
}
|
|
218
|
+
//#endregion
|
|
219
|
+
export { RedisStreamTransport as n, RedisStreamTransportOptions as r, RedisStreamLike as t };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/utils/redisShapes.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* The key/value command shapes arc's cache and idempotency stores share, for
|
|
5
|
+
* the two Redis clients hosts actually plug in: ioredis (TCP) and
|
|
6
|
+
* `@upstash/redis` (REST, edge-safe).
|
|
7
|
+
*
|
|
8
|
+
* Both stores used to declare their own copies of these, and they drifted the
|
|
9
|
+
* way copies do — the ioredis-assignability fix in 2.41 had to be applied to
|
|
10
|
+
* each separately. Each store's exported `IoredisLike` / `UpstashRedisLike`
|
|
11
|
+
* now EXTENDS the base here and adds only what that store uses beyond it, so
|
|
12
|
+
* the exported names are unchanged.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately NOT a single shape for every Redis user in arc. Streams
|
|
15
|
+
* (`xadd`/`xreadgroup`), pub/sub (`subscribe`/`duplicate`), sessions (sets),
|
|
16
|
+
* usage (hashes) and circuit counters (`incr`) share almost no commands with
|
|
17
|
+
* each other, and where they overlap the signatures differ on purpose. A
|
|
18
|
+
* shared superset would couple unrelated features and make every host wrapper
|
|
19
|
+
* implement commands it never serves.
|
|
20
|
+
*
|
|
21
|
+
* ## The one rule every ioredis shape must follow
|
|
22
|
+
*
|
|
23
|
+
* Variadic tails are `unknown[]`, never `(string | number)[]`. ioredis types
|
|
24
|
+
* each command as a set of OVERLOADS whose tails mix literal tokens (`'MATCH'`,
|
|
25
|
+
* `'PX'`, `'NX'`), `Buffer`, and an optional trailing callback. A narrower
|
|
26
|
+
* tail matches none of them, and `new Redis(url)` then fails to assign with
|
|
27
|
+
* TS2345. Pinned for every arc shape by `tests/types/ioredis-assignability.test.ts`.
|
|
28
|
+
*/
|
|
29
|
+
/** ioredis key/value commands shared by the cache and idempotency stores. */
|
|
30
|
+
interface IoredisKeyValueCommands {
|
|
31
|
+
get(key: string): Promise<string | null>;
|
|
32
|
+
set(...args: unknown[]): Promise<string | null>;
|
|
33
|
+
del(...keys: string[]): Promise<number>;
|
|
34
|
+
scan(cursor: string | number, ...args: unknown[]): Promise<[string, string[]]>;
|
|
35
|
+
eval?(script: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* `@upstash/redis` key/value commands shared by the cache and idempotency
|
|
39
|
+
* stores. REST-based, so it runs on edge runtimes (Cloudflare Workers, Vercel
|
|
40
|
+
* Edge, Deno Deploy) where TCP — and thus ioredis — is unavailable. Note the
|
|
41
|
+
* different call shapes: options objects rather than positional tokens, and
|
|
42
|
+
* `eval(script, keys[], args[])`.
|
|
43
|
+
*/
|
|
44
|
+
interface UpstashKeyValueCommands {
|
|
45
|
+
get(key: string): Promise<string | null | unknown>;
|
|
46
|
+
set(key: string, value: unknown, opts?: Record<string, unknown>): Promise<unknown>;
|
|
47
|
+
del(...keys: string[]): Promise<number>;
|
|
48
|
+
scan(cursor: number | string, opts?: {
|
|
49
|
+
match?: string;
|
|
50
|
+
count?: number;
|
|
51
|
+
}): Promise<[number, string[]] | [string, string[]]>;
|
|
52
|
+
eval?(script: string, keys: string[], args: (string | number)[]): Promise<unknown>;
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { UpstashKeyValueCommands as n, IoredisKeyValueCommands as t };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import {
|
|
3
|
-
import "../fastify-augmentation-
|
|
2
|
+
import { F as ResourceDefinition, G as CrudRouteKey, M as ResourceRegistry, N as RouteRow, S as IntrospectionPluginOptions, V as ResolvedTenantPurge, j as RegisterOptions, o as RegistryEntry } from "../index-WH7xgvxS.mjs";
|
|
3
|
+
import "../fastify-augmentation-DRlCIWE9.mjs";
|
|
4
4
|
import { TenantPurgeProgress, TenantPurgeStrategy } from "@classytic/repo-core/repository";
|
|
5
5
|
import { FastifyPluginAsync } from "fastify";
|
|
6
6
|
//#region src/registry/assertNoTenantData.d.ts
|
|
@@ -60,7 +60,7 @@ interface AssertNoTenantDataReport {
|
|
|
60
60
|
* });
|
|
61
61
|
* ```
|
|
62
62
|
*/
|
|
63
|
-
declare function assertNoTenantData(registry: ResourceRegistry, options: AssertNoTenantDataOptions): Promise<AssertNoTenantDataReport>;
|
|
63
|
+
export declare function assertNoTenantData(registry: ResourceRegistry, options: AssertNoTenantDataOptions): Promise<AssertNoTenantDataReport>;
|
|
64
64
|
//#endregion
|
|
65
65
|
//#region src/registry/purgeResource.d.ts
|
|
66
66
|
/**
|
|
@@ -232,7 +232,7 @@ interface CascadeCheckpoint {
|
|
|
232
232
|
* A resource is cascading when its resolved strategy isn't `disabled`
|
|
233
233
|
* (i.e. the host declared `onTenantDelete`).
|
|
234
234
|
*/
|
|
235
|
-
declare function getCascadingResources(registry: ResourceRegistry): readonly string[];
|
|
235
|
+
export declare function getCascadingResources(registry: ResourceRegistry): readonly string[];
|
|
236
236
|
/**
|
|
237
237
|
* Rich introspection — returns the resolved strategy + source per
|
|
238
238
|
* cascading resource. Use for audit dashboards that answer "what
|
|
@@ -245,7 +245,7 @@ declare function getCascadingResources(registry: ResourceRegistry): readonly str
|
|
|
245
245
|
* purge this" entry. Anonymize carries the field map. Custom carries
|
|
246
246
|
* the handler reference. Narrow on `.strategy.type` for typed access.
|
|
247
247
|
*/
|
|
248
|
-
declare function getCascadingResourcesWithMetadata(registry: ResourceRegistry): readonly {
|
|
248
|
+
export declare function getCascadingResourcesWithMetadata(registry: ResourceRegistry): readonly {
|
|
249
249
|
name: string;
|
|
250
250
|
tenantField: string;
|
|
251
251
|
strategy: TenantPurgeStrategy;
|
|
@@ -274,10 +274,9 @@ declare function getCascadingResourcesWithMetadata(registry: ResourceRegistry):
|
|
|
274
274
|
* @param registry The arc resource registry (`fastify.arc.registry`).
|
|
275
275
|
* @param options Org id + filters + logger + progress + signal.
|
|
276
276
|
*/
|
|
277
|
-
declare function cascadeDeleteForOrganization(registry: ResourceRegistry, options: CascadeOptions): Promise<CascadeReport>;
|
|
277
|
+
export declare function cascadeDeleteForOrganization(registry: ResourceRegistry, options: CascadeOptions): Promise<CascadeReport>;
|
|
278
278
|
//#endregion
|
|
279
279
|
//#region src/registry/introspectionPlugin.d.ts
|
|
280
|
-
declare const introspectionPlugin: FastifyPluginAsync<IntrospectionPluginOptions>;
|
|
281
280
|
declare const _default: FastifyPluginAsync<IntrospectionPluginOptions>;
|
|
282
281
|
//#endregion
|
|
283
282
|
//#region src/registry/manifest.d.ts
|
|
@@ -326,13 +325,19 @@ interface ResourceManifest {
|
|
|
326
325
|
readonly customRoutes: readonly CustomRouteManifestEntry[];
|
|
327
326
|
/** Tenant scoping field (when set) — surfaces for FE callers that need to send `x-organization-id`. */
|
|
328
327
|
readonly tenantField?: string | false;
|
|
328
|
+
/**
|
|
329
|
+
* Optimistic-concurrency version field — present only when the resource declares
|
|
330
|
+
* `concurrency`. A client sends `If-Match: "<doc[versionField]>"` on update and gets
|
|
331
|
+
* `412 arc.precondition_failed` for a stale one (arc-next: `createCrudApi(entity, { versionField })`).
|
|
332
|
+
*/
|
|
333
|
+
readonly versionField?: string;
|
|
329
334
|
}
|
|
330
335
|
/**
|
|
331
336
|
* Build a manifest from a `ResourceDefinition` (the value returned by
|
|
332
337
|
* `defineResource(...)`). Use at build time when you have direct access
|
|
333
338
|
* to the resource module — typically the simplest codegen path.
|
|
334
339
|
*/
|
|
335
|
-
declare function buildResourceManifest(resource: ResourceDefinition): ResourceManifest;
|
|
340
|
+
export declare function buildResourceManifest(resource: ResourceDefinition): ResourceManifest;
|
|
336
341
|
/**
|
|
337
342
|
* Build a manifest from a `RegistryEntry` — use when the resource is
|
|
338
343
|
* already registered (introspection endpoint, runtime audit script).
|
|
@@ -343,6 +348,6 @@ declare function buildResourceManifest(resource: ResourceDefinition): ResourceMa
|
|
|
343
348
|
* `/_resources` endpoint) gets the same output without dragging the resource
|
|
344
349
|
* module into the codegen path.
|
|
345
350
|
*/
|
|
346
|
-
declare function buildResourceManifestFromRegistry(entry: RegistryEntry): ResourceManifest;
|
|
351
|
+
export declare function buildResourceManifestFromRegistry(entry: RegistryEntry): ResourceManifest;
|
|
347
352
|
//#endregion
|
|
348
|
-
export { type ActionManifestEntry, type ActionMount, type AggregationManifestEntry, type AssertNoTenantDataOptions, type AssertNoTenantDataReport, type CascadeCheckpoint, type CascadeCheckpointState, type CascadeOptions, type CascadeReport, type CascadeResourceReport, type CustomRouteManifestEntry, type IntrospectionPluginOptions, type RegisterOptions, type ResourceManifest, ResourceRegistry, type
|
|
353
|
+
export { type ActionManifestEntry, type ActionMount, type AggregationManifestEntry, type AssertNoTenantDataOptions, type AssertNoTenantDataReport, type CascadeCheckpoint, type CascadeCheckpointState, type CascadeOptions, type CascadeReport, type CascadeResourceReport, type CustomRouteManifestEntry, type IntrospectionPluginOptions, type RegisterOptions, type ResourceManifest, ResourceRegistry, type RouteRow, type TenantDataLeak, _default as introspectionPlugin };
|
package/dist/registry/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { a as
|
|
3
|
-
import { t as ResourceRegistry } from "../ResourceRegistry-
|
|
4
|
-
export { ResourceRegistry, assertNoTenantData, buildResourceManifest, buildResourceManifestFromRegistry, cascadeDeleteForOrganization, getCascadingResources, getCascadingResourcesWithMetadata, introspectionPlugin_default as introspectionPlugin
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { a as getCascadingResources, i as cascadeDeleteForOrganization, n as buildResourceManifestFromRegistry, o as getCascadingResourcesWithMetadata, r as introspectionPlugin_default, s as assertNoTenantData, t as buildResourceManifest } from "../registry-Bdzg-hdm.mjs";
|
|
3
|
+
import { t as ResourceRegistry } from "../ResourceRegistry-DJxHUpE4.mjs";
|
|
4
|
+
export { ResourceRegistry, assertNoTenantData, buildResourceManifest, buildResourceManifestFromRegistry, cascadeDeleteForOrganization, getCascadingResources, getCascadingResourcesWithMetadata, introspectionPlugin_default as introspectionPlugin };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import { t as CRUD_OPERATIONS } from "./constants-
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as CRUD_OPERATIONS } from "./constants-B2QHb6u6.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/registry/assertNoTenantData.ts
|
|
5
5
|
/**
|
|
@@ -405,7 +405,7 @@ const introspectionPlugin = async (fastify, opts = {}) => {
|
|
|
405
405
|
const typedFastify = fastify;
|
|
406
406
|
const authMiddleware = authRoles.length > 0 && typedFastify.authenticate ? [typedFastify.authenticate, typedFastify.authorize?.(...authRoles)].filter(Boolean) : [];
|
|
407
407
|
const getRegistry = () => fastify.arc?.registry;
|
|
408
|
-
|
|
408
|
+
const introspectionRoutes = async (instance) => {
|
|
409
409
|
instance.get("/", { preHandler: authMiddleware }, async (_req, _reply) => {
|
|
410
410
|
return getRegistry()?.getIntrospection() ?? {
|
|
411
411
|
resources: [],
|
|
@@ -434,7 +434,8 @@ const introspectionPlugin = async (fastify, opts = {}) => {
|
|
|
434
434
|
if (!resource) return reply.code(404).send({ error: `Resource '${req.params.name}' not found` });
|
|
435
435
|
return resource;
|
|
436
436
|
});
|
|
437
|
-
}
|
|
437
|
+
};
|
|
438
|
+
await fastify.register(introspectionRoutes, { prefix });
|
|
438
439
|
fastify.log?.debug?.(`Introspection API at ${prefix}`);
|
|
439
440
|
};
|
|
440
441
|
var introspectionPlugin_default = fp(introspectionPlugin, { name: "arc-introspection" });
|
|
@@ -442,8 +443,8 @@ var introspectionPlugin_default = fp(introspectionPlugin, { name: "arc-introspec
|
|
|
442
443
|
//#region src/registry/manifest.ts
|
|
443
444
|
/**
|
|
444
445
|
* Resource metadata for frontend code generators — `{ name, prefix, idField,
|
|
445
|
-
* crudOps, actions[] }`, where each action carries its `mount`
|
|
446
|
-
* `requiresId`.
|
|
446
|
+
* versionField?, crudOps, actions[] }`, where each action carries its `mount`
|
|
447
|
+
* and `requiresId`.
|
|
447
448
|
*
|
|
448
449
|
* A hand-rolled FE client needs a method per declarative action and drifts the
|
|
449
450
|
* moment a new one is declared. Arc owns the resource definition, so it can
|
|
@@ -502,7 +503,8 @@ function buildResourceManifest(resource) {
|
|
|
502
503
|
actions,
|
|
503
504
|
aggregations,
|
|
504
505
|
customRoutes,
|
|
505
|
-
...resource.tenantField !== void 0 ? { tenantField: resource.tenantField } : {}
|
|
506
|
+
...resource.tenantField !== void 0 ? { tenantField: resource.tenantField } : {},
|
|
507
|
+
...resource.versionField !== void 0 ? { versionField: resource.versionField } : {}
|
|
506
508
|
};
|
|
507
509
|
}
|
|
508
510
|
/**
|
|
@@ -544,14 +546,15 @@ function buildResourceManifestFromRegistry(entry) {
|
|
|
544
546
|
name: entry.name,
|
|
545
547
|
displayName: entry.displayName ?? entry.name,
|
|
546
548
|
prefix: entry.prefix,
|
|
547
|
-
idField: "_id",
|
|
549
|
+
idField: entry.idField ?? "_id",
|
|
548
550
|
updateMethod,
|
|
549
551
|
crudOps,
|
|
550
552
|
actions,
|
|
551
553
|
aggregations,
|
|
552
554
|
customRoutes,
|
|
553
|
-
...entry.tenantField !== void 0 ? { tenantField: entry.tenantField } : {}
|
|
555
|
+
...entry.tenantField !== void 0 ? { tenantField: entry.tenantField } : {},
|
|
556
|
+
...entry.versionField !== void 0 ? { versionField: entry.versionField } : {}
|
|
554
557
|
};
|
|
555
558
|
}
|
|
556
559
|
//#endregion
|
|
557
|
-
export {
|
|
560
|
+
export { getCascadingResources as a, cascadeDeleteForOrganization as i, buildResourceManifestFromRegistry as n, getCascadingResourcesWithMetadata as o, introspectionPlugin_default as r, assertNoTenantData as s, buildResourceManifest as t };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/*! @classytic/arc v2.
|
|
2
|
-
import {
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
import { r as __exportAll } from "./rolldown-runtime-MYrLNGnq.mjs";
|
|
3
3
|
import { toCanonicalList } from "@classytic/repo-core/pagination";
|
|
4
4
|
import fp from "fastify-plugin";
|
|
5
5
|
//#region src/plugins/replyHelpers.ts
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/*! @classytic/arc v2.44.0 | MIT | https://classytic.com */
|
|
2
|
+
//#region src/idempotency/requirement.ts
|
|
3
|
+
/** What is wrong with a route's idempotency declaration, or `undefined` when it is sound. */
|
|
4
|
+
function idempotencyConfigProblem(config) {
|
|
5
|
+
const idem = config?.idempotency;
|
|
6
|
+
if (!idem) return void 0;
|
|
7
|
+
const ownerOk = typeof idem.owner === "string" && idem.owner.trim() !== "";
|
|
8
|
+
if (idem.dedup !== void 0 && idem.dedup !== "handler") return `dedup must be 'handler', got '${String(idem.dedup)}'`;
|
|
9
|
+
if (idem.dedup === "handler" && idem.required !== true) return "dedup: 'handler' needs required: true (the key is still demanded)";
|
|
10
|
+
if (idem.dedup === "handler" && !ownerOk) return "dedup: 'handler' needs a non-empty `owner` naming the runner that deduplicates";
|
|
11
|
+
if (idem.dedup === void 0 && idem.owner !== void 0) return "`owner` is only meaningful with dedup: 'handler'";
|
|
12
|
+
for (const [action, owner] of Object.entries(idem.handlerDedup ?? {})) if (!idem.requiredActions?.includes(action) || owner.trim() === "") return `handlerDedup '${action}' must name an action in requiredActions and a non-empty owner`;
|
|
13
|
+
}
|
|
14
|
+
/** True iff the route declares a fence for at least one call shape. */
|
|
15
|
+
function declaresIdempotencyFence(config) {
|
|
16
|
+
const idem = config?.idempotency;
|
|
17
|
+
return idem?.required === true || (idem?.requiredActions?.length ?? 0) > 0;
|
|
18
|
+
}
|
|
19
|
+
/** True iff THIS request's handler deduplicates itself (arc only demands and exposes the key). */
|
|
20
|
+
function requestDedupedByHandler(request) {
|
|
21
|
+
const idem = request.routeOptions.config?.idempotency;
|
|
22
|
+
if (idem?.required === true) return idem.dedup === "handler";
|
|
23
|
+
const action = request.body?.action;
|
|
24
|
+
return typeof action === "string" && idem?.handlerDedup?.[action] !== void 0;
|
|
25
|
+
}
|
|
26
|
+
/** True iff THIS request is fenced — a keyless call is refused, a keyed one deduplicated. */
|
|
27
|
+
function requestRequiresIdempotencyKey(request) {
|
|
28
|
+
const idem = request.routeOptions.config?.idempotency;
|
|
29
|
+
if (idem?.required === true) return true;
|
|
30
|
+
if (!idem?.requiredActions?.length) return false;
|
|
31
|
+
const action = request.body?.action;
|
|
32
|
+
return typeof action === "string" && idem.requiredActions.includes(action);
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { requestRequiresIdempotencyKey as i, idempotencyConfigProblem as n, requestDedupedByHandler as r, declaresIdempotencyFence as t };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
|
|
2
|
+
import { FastifyRequest } from "fastify";
|
|
3
|
+
//#region src/idempotency/requirement.d.ts
|
|
4
|
+
/** The `config.idempotency` slot of a Fastify route. */
|
|
5
|
+
interface RouteIdempotencyConfig {
|
|
6
|
+
/** Every call needs a key — any method, DELETE included. */
|
|
7
|
+
readonly required?: boolean;
|
|
8
|
+
/** Action route only: a call whose `body.action` is one of these needs a key. */
|
|
9
|
+
readonly requiredActions?: readonly string[];
|
|
10
|
+
/**
|
|
11
|
+
* `'handler'`: the handler owns at-most-once for this route (its own durable claim). Arc still
|
|
12
|
+
* refuses a keyless call and exposes the key on `request.idempotencyKey`, but stores, locks,
|
|
13
|
+
* fingerprints and replays nothing. Needs `required: true` and an `owner`.
|
|
14
|
+
*/
|
|
15
|
+
readonly dedup?: "handler";
|
|
16
|
+
/** Names the runner that owns deduplication under `dedup: 'handler'`. */
|
|
17
|
+
readonly owner?: string;
|
|
18
|
+
/** Action route only: action name -> owner, for actions in `requiredActions` that dedupe themselves. */
|
|
19
|
+
readonly handlerDedup?: Readonly<Record<string, string>>;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { RouteIdempotencyConfig as t };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
|
|
2
|
+
import { i as RequestScope } from "./types-Ddl9OjmE.mjs";
|
|
3
|
+
import { FastifyInstance } from "fastify";
|
|
4
|
+
//#region src/permissions/resourceRateLimit.d.ts
|
|
5
|
+
interface ResourceRateLimitPolicy {
|
|
6
|
+
max: number;
|
|
7
|
+
timeWindow: string;
|
|
8
|
+
/** Explicit route overrides own a separate budget. */
|
|
9
|
+
bucket?: string;
|
|
10
|
+
}
|
|
11
|
+
interface ResourceRateLimitContext {
|
|
12
|
+
scope?: RequestScope;
|
|
13
|
+
ip?: string;
|
|
14
|
+
}
|
|
15
|
+
interface ResourceRateLimitDecision {
|
|
16
|
+
readonly allowed: boolean;
|
|
17
|
+
readonly retryAfterSeconds: number;
|
|
18
|
+
readonly headers: Readonly<Record<string, number>>;
|
|
19
|
+
}
|
|
20
|
+
/** One resource budget across operations and transports, backed by the host's limiter. */
|
|
21
|
+
interface ResourceRateLimiter {
|
|
22
|
+
readonly durability: "memory" | "shared";
|
|
23
|
+
consume(resource: string, policy: ResourceRateLimitPolicy, context: ResourceRateLimitContext): Promise<ResourceRateLimitDecision>;
|
|
24
|
+
}
|
|
25
|
+
/** Uses createRateLimit's store factory, preserving Redis/custom-store configuration. */
|
|
26
|
+
declare function resolveResourceRateLimiter(app: FastifyInstance, durability?: ResourceRateLimiter["durability"]): ResourceRateLimiter | undefined;
|
|
27
|
+
/** Standalone tools may use memory; wired distributed apps use the host's shared store. */
|
|
28
|
+
declare function createMemoryResourceRateLimiter(capacity?: number): ResourceRateLimiter;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { createMemoryResourceRateLimiter as a, ResourceRateLimiter as i, ResourceRateLimitDecision as n, resolveResourceRateLimiter as o, ResourceRateLimitPolicy as r, ResourceRateLimitContext as t };
|