@classytic/arc 2.41.0 → 2.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{EventTransport-CbUKDzZZ.d.mts → EventTransport-CiicKz2Z.d.mts} +1 -1
- package/dist/{EventTransport-n7qO5Cz3.mjs → EventTransport-tzA7CAB4.mjs} +22 -17
- package/dist/{HookSystem-CxvZBo77.mjs → HookSystem-B6UaKRbF.mjs} +2 -2
- package/dist/{QueryCache-DFA-QrU9.d.mts → QueryCache--5uAuYPW.d.mts} +7 -3
- package/dist/{ResourceRegistry-Cu7uTJp9.mjs → ResourceRegistry-CuCARu2B.mjs} +44 -13
- package/dist/actionPermissions-BotX1YOa.mjs +776 -0
- package/dist/audit/index.d.mts +43 -5
- package/dist/audit/index.mjs +48 -26
- package/dist/auth/audit.mjs +1 -1
- package/dist/auth/index.d.mts +43 -13
- package/dist/auth/index.mjs +130 -155
- package/dist/auth/redis-session.mjs +1 -1
- package/dist/{authorizationDecision-2YY_Kaj1.mjs → authorizationDecision-DQYrkl5f.mjs} +7 -102
- package/dist/{base-CxrV1LmG.d.mts → base-CZyixEQR.d.mts} +27 -5
- package/dist/boundedMap-BULzfXYU.mjs +57 -0
- package/dist/{buildHandler-CUhjGHGZ.mjs → buildHandler-CRQ8EcX5.mjs} +4 -4
- package/dist/cache/index.d.mts +22 -34
- package/dist/cache/index.mjs +4 -4
- package/dist/{caching-DsWDjIxx.mjs → caching-Cc2M1DwO.mjs} +3 -3
- package/dist/claimed-B10RTfkM.mjs +8 -0
- package/dist/cleanup/index.d.mts +6 -10
- package/dist/cleanup/index.mjs +27 -73
- package/dist/cli/commands/describe.d.mts +4 -2
- package/dist/cli/commands/describe.mjs +7 -4
- package/dist/cli/commands/docs.mjs +3 -3
- package/dist/cli/commands/doctor.mjs +1 -1
- package/dist/cli/commands/generate.mjs +2 -2
- package/dist/cli/commands/init.mjs +2 -2
- package/dist/cli/commands/introspect.mjs +2 -2
- package/dist/cli/index.mjs +3 -3
- package/dist/{constants-BxLgNFF7.mjs → constants-Cu7Mx7_U.mjs} +4 -2
- package/dist/context/index.d.mts +2 -5
- package/dist/context/index.mjs +18 -38
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +8 -8
- package/dist/{core-iNGqYerO.mjs → core-Cb0LjccA.mjs} +5 -4
- package/dist/{createActionRouter-BBlbPs3k.mjs → createActionRouter-xjqonMRO.mjs} +87 -39
- package/dist/{createAggregationRouter-Dv5SuK2x.mjs → createAggregationRouter-zRYWw_5D.mjs} +11 -8
- package/dist/{preloadResources-Bbp4QJ8V.mjs → createApp-D-953ZAq.mjs} +299 -390
- package/dist/{createMcpServer-CbDWkZ1T.d.mts → createMcpServer-C9cxS8zt.d.mts} +1 -1
- package/dist/{defineAggregation-UyeurGkp.mjs → defineAggregation-Bm8RjMbq.mjs} +1 -1
- package/dist/{defineResource-DzGT-2LP.mjs → defineResource-DIC7JZGU.mjs} +118 -1024
- package/dist/discovery/index.mjs +1 -1
- package/dist/docs/index.d.mts +10 -4
- package/dist/docs/index.mjs +4 -4
- package/dist/{elevation-BXCfVcNG.d.mts → elevation-BW2J3tNj.d.mts} +1 -2
- package/dist/{elevation-BF-vhGxY.mjs → elevation-DG8SrLN-.mjs} +41 -12
- package/dist/encryption/index.mjs +3 -3
- package/dist/{errorHandler-CIMkCOWm.mjs → errorHandler-BnfDviL1.mjs} +26 -5
- package/dist/{errors-C_YhELhe.mjs → errors-CtrywMHP.mjs} +58 -23
- package/dist/{errors-BGdVwP25.d.mts → errors-JYc9vwqE.d.mts} +9 -0
- package/dist/{eventPlugin-B52abJ8W.d.mts → eventPlugin-BtmA4-3P.d.mts} +65 -9
- package/dist/{eventPlugin-CmXaPWLv.mjs → eventPlugin-Q6BhzHzs.mjs} +82 -21
- package/dist/{subscribe-helpers-AVYPgqTl.mjs → eventTypes-Cdhm6ZlV.mjs} +151 -17
- package/dist/events/index.d.mts +33 -8
- package/dist/events/index.mjs +49 -53
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +139 -157
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/events/transports/redis.mjs +2 -2
- package/dist/factory/index.d.mts +32 -17
- package/dist/factory/index.mjs +213 -32
- package/dist/{fastify-augmentation-Cq2nHVaW.d.mts → fastify-augmentation-DhUJ-mvo.d.mts} +11 -25
- package/dist/{fields-I_EeRWND.d.mts → fields-BBSpf8pS.d.mts} +1 -1
- package/dist/{fields-BTyPLHK0.mjs → fields-ToPYpMqS.mjs} +1 -1
- package/dist/filter-merge-IpGywvym.mjs +99 -0
- package/dist/{generate-D_YmiCNf.mjs → generate-DRTkLlIl.mjs} +2 -2
- package/dist/{headers-CNECDE6q.mjs → headers-BbkIF6vA.mjs} +2 -2
- package/dist/health-D-Ft6JM6.mjs +22 -0
- package/dist/{health-CxQzbJmr.mjs → health-DqbsG-_w.mjs} +115 -55
- package/dist/hooks/index.d.mts +2 -2
- package/dist/hooks/index.mjs +2 -2
- package/dist/idempotency/index.d.mts +31 -7
- package/dist/idempotency/index.mjs +155 -98
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +48 -11
- package/dist/{index-BHdZD8zK.d.mts → index-Bxriq3so.d.mts} +84 -21
- package/dist/{preloadResources-CjS2OfpU.d.mts → index-C8zVGYsk.d.mts} +37 -38
- package/dist/{index-D4u76eQW.d.mts → index-CRoPtISf.d.mts} +23 -73
- package/dist/{index-CbJiYrfv.d.mts → index-Chgz3d0i.d.mts} +1 -1
- package/dist/index-JdbZAIsm.d.mts +510 -0
- package/dist/{index-DHbyPo5j.d.mts → index-mqaqZNpe.d.mts} +253 -254
- package/dist/index.d.mts +8 -8
- package/dist/index.mjs +11 -11
- package/dist/{init-Y2MlSN9n.mjs → init-B_oUYDXI.mjs} +1 -1
- package/dist/integrations/event-gateway.d.mts +8 -3
- package/dist/integrations/event-gateway.mjs +4 -4
- package/dist/integrations/index.d.mts +3 -3
- package/dist/integrations/index.mjs +1 -1
- package/dist/integrations/jobs/index.d.mts +1 -1
- package/dist/integrations/jobs/index.mjs +18 -5
- package/dist/integrations/mcp/index.d.mts +72 -59
- package/dist/integrations/mcp/index.mjs +19 -4
- package/dist/integrations/mcp/testing.d.mts +2 -2
- package/dist/integrations/mcp/testing.mjs +2 -2
- package/dist/integrations/streamline.d.mts +78 -136
- package/dist/integrations/streamline.mjs +116 -54
- package/dist/integrations/webhooks.d.mts +60 -120
- package/dist/integrations/webhooks.mjs +36 -80
- package/dist/integrations/websocket-pushref-redis.d.mts +1 -1
- package/dist/integrations/websocket-pushref-redis.mjs +1 -1
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket-redis.mjs +1 -1
- package/dist/integrations/websocket.d.mts +3 -3
- package/dist/integrations/websocket.mjs +2 -2
- package/dist/{interface-DEWNOMza.d.mts → interface-C0395yUN.d.mts} +13 -1
- package/dist/{jsonBody-Cq9KYl2Z.mjs → jsonBody-B4YVGcql.mjs} +2 -2
- package/dist/{keys-Cmv4cBRa.mjs → keys-B2KtgMLJ.mjs} +7 -18
- package/dist/{loadResources-CblF7xt2.mjs → loadResources-Dn4lalw2.mjs} +3 -3
- package/dist/{loadResourcesFromEntry-99fIMC3p.mjs → loadResourcesFromEntry-3sLYbuse.mjs} +2 -2
- package/dist/logger/index.d.mts +1 -8
- package/dist/logger/index.mjs +3 -153
- package/dist/logger-DXjfIqjV.mjs +160 -0
- package/dist/{mcpPlugin-xF3Gw-HW.mjs → mcpPlugin-C08HalN5.mjs} +433 -170
- package/dist/{memory-CpDZ9NPw.mjs → memory-9Cp7611C.mjs} +36 -34
- package/dist/{metrics-BaKnq8ag.mjs → metrics-CWI11eRu.mjs} +39 -72
- package/dist/middleware/index.d.mts +2 -2
- package/dist/middleware/index.mjs +3 -3
- package/dist/migrations/index.mjs +2 -2
- package/dist/{module-BkQEesNQ.mjs → module-BSvOQC7E.mjs} +8 -8
- package/dist/multiTenant-cY0FkDiA.d.mts +104 -0
- package/dist/{multipartBody-C92aJ8am.mjs → multipartBody-CXM2a-Dy.mjs} +46 -7
- package/dist/{openapi-C3LFRzPq.mjs → openapi-DT_G4VhY.mjs} +48 -10
- package/dist/outbox-admin/index.d.mts +3 -3
- package/dist/outbox-admin/index.mjs +6 -18
- package/dist/{outbox-module-dsCyLxXH.mjs → outbox-module-Bjgzl6tg.mjs} +203 -33
- package/dist/{outbox-module-BHdUnuuK.d.mts → outbox-module-gZCHoE4H.d.mts} +79 -6
- package/dist/permissions/index.d.mts +5 -4
- package/dist/permissions/index.mjs +8 -6
- package/dist/{permissions-DiHXy8Ck.mjs → permissions-Cm0uYHvd.mjs} +149 -33
- package/dist/{pipe-CBVmUJ-j.mjs → pipe-BqeGT6UO.mjs} +2 -2
- package/dist/pipeline/index.d.mts +2 -2
- package/dist/pipeline/index.mjs +2 -2
- package/dist/plan-FVR6LP59.mjs +565 -0
- package/dist/{BaseController-DYJRZ0EJ.mjs → plugin-BccET1tL.mjs} +1256 -344
- package/dist/plugins/index.d.mts +14 -11
- package/dist/plugins/index.mjs +44 -22
- package/dist/plugins/response-cache.mjs +8 -14
- package/dist/plugins/tracing-entry.mjs +2 -2
- package/dist/{pluralize-2_U6QZQg.mjs → pluralize-CVIOcgMF.mjs} +1 -1
- package/dist/presets/filesUpload.d.mts +2 -2
- package/dist/presets/filesUpload.mjs +5 -5
- package/dist/presets/index.d.mts +26 -31
- package/dist/presets/index.mjs +4 -4
- package/dist/presets/multiTenant.d.mts +2 -107
- package/dist/presets/multiTenant.mjs +22 -162
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +2 -2
- package/dist/{presets-BdlugYTc.mjs → presets-Cpvj6fGO.mjs} +99 -107
- package/dist/{projection-Uvgp-EEj.mjs → projection-Ce4aQhQk.mjs} +2 -2
- package/dist/{pushref-registry-Dov9l4qE.d.mts → pushref-registry-UxCkoA4C.d.mts} +1 -1
- package/dist/{queryCachePlugin-BzqxconG.d.mts → queryCachePlugin-C2lZjTxk.d.mts} +1 -1
- package/dist/{queryCachePlugin-B5F09bYr.mjs → queryCachePlugin-CFWNH8aT.mjs} +12 -14
- package/dist/{rateLimitKey-CK-EU2L8.mjs → rateLimitKey-C4O-kPm0.mjs} +1 -1
- package/dist/{realtime-FnnD0n3Z.mjs → realtime-DmPE9S54.mjs} +25 -21
- package/dist/redis-stream-BMk-bpQS.d.mts +219 -0
- package/dist/{redis-Bzi1y3F9.d.mts → redis-vLvtcGy_.d.mts} +13 -24
- package/dist/redisShapes-CQksY67q.d.mts +55 -0
- package/dist/registry/index.d.mts +9 -4
- package/dist/registry/index.mjs +4 -4
- package/dist/{registry-D3y8VEJ1.mjs → registry-Bcz9G1bh.mjs} +10 -8
- package/dist/{renewingLease-B3VYh5aU.mjs → renewingLease-qODa7KJQ.mjs} +1 -1
- package/dist/{replyHelpers-DPHsdw-5.mjs → replyHelpers-CG_VtVgS.mjs} +2 -2
- package/dist/{requestContext-cSVKnYPk.mjs → requestContext-Dsu9P1bV.mjs} +1 -1
- package/dist/requirement-CbiLdQ1n.d.mts +22 -0
- package/dist/requirement-KDdoRcOP.mjs +35 -0
- package/dist/resourceRateLimit-Wf6d6FL9.d.mts +30 -0
- package/dist/resourceRateLimit-dSjVr1pk.mjs +180 -0
- package/dist/{rolldown-runtime-DjDuyglE.mjs → rolldown-runtime-D1w9Y1VJ.mjs} +1 -1
- package/dist/routeValidator-D36SW8qt.mjs +70 -0
- package/dist/{runtimeCapabilities-DSA_dH1_.mjs → runtimeCapabilities-0tccjRlj.mjs} +1 -1
- package/dist/{schedules-BktJb3Y3.mjs → schedules-C-OpmHBq.mjs} +46 -37
- package/dist/{schemaConverter-BLCSmNbr.mjs → schemaConverter-BORek3O-.mjs} +2 -2
- package/dist/{schemaIR-K16Bb5BX.mjs → schemaIR-C7Vbp71I.mjs} +2 -2
- package/dist/schemas/index.d.mts +10 -0
- package/dist/schemas/index.mjs +8 -3
- package/dist/scim/index.mjs +2 -2
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +6 -6
- package/dist/{scope-jSxuBTFM.mjs → scope-DQthe88b.mjs} +4 -4
- package/dist/shutdown-MqA81z_P.mjs +59 -0
- package/dist/{simpleEqualityMatcher-BiOnyflP.mjs → simpleEqualityMatcher-auW8QjXN.mjs} +1 -1
- package/dist/{sse-CH7Slxc1.mjs → sse-CYzQBVFe.mjs} +11 -15
- package/dist/{sseStream-CN-Lh1Tu.mjs → sseStream-B5_Afiqn.mjs} +2 -2
- package/dist/{store-helpers-9DX9-1j_.mjs → store-helpers-BOzk-CuX.mjs} +1 -1
- package/dist/{streaming-dvppnxsr.mjs → streaming-DjTQhZrN.mjs} +1 -1
- package/dist/subscriberIdentity-CW39uA8G.mjs +28 -0
- package/dist/tenancy--ed9eH_z.mjs +43 -0
- package/dist/tenantPreparation-BKon6Yox.mjs +67 -0
- package/dist/testing/cleanupStoreContract.mjs +1 -1
- package/dist/testing/index.d.mts +193 -175
- package/dist/testing/index.mjs +286 -212
- package/dist/testing/outboxStoreContract.mjs +137 -2
- package/dist/testing/requiredPorts.d.mts +17 -0
- package/dist/testing/requiredPorts.mjs +65 -0
- package/dist/testing/storageContract.mjs +1 -1
- package/dist/timing-qczZ0YC-.mjs +46 -0
- package/dist/{transactionContext-DCWMI6hV.mjs → transactionContext-DjFkrNG6.mjs} +1 -1
- package/dist/{typeGuards-D7tHFdH6.mjs → typeGuards-CySF12G-.mjs} +1 -1
- package/dist/types/index.d.mts +4 -4
- package/dist/types/index.mjs +1 -1
- package/dist/types/storage.mjs +1 -1
- package/dist/{types-CWQn9-IC.d.mts → types-B4Aq4Sdj.d.mts} +22 -9
- package/dist/{types-DfVDvkdP.d.mts → types-BJ-2XqVF.d.mts} +189 -204
- package/dist/{types-CDy3-zw1.mjs → types-Bk9NOgOC.mjs} +4 -4
- package/dist/usage/index.d.mts +2 -3
- package/dist/usage/index.mjs +5 -5
- package/dist/{userHelpers-DDMOZ_Pm.mjs → userHelpers-aklawRaK.mjs} +1 -1
- package/dist/utils/index.d.mts +84 -223
- package/dist/utils/index.mjs +13 -12
- package/dist/{utils-B-zj_b__.mjs → utils-ec1oTo1n.mjs} +315 -404
- package/dist/{validate-R0G_JrnK.mjs → validate-9yzHC1e2.mjs} +6 -14
- package/dist/{versioning-D6JIK8Tk.d.mts → versioning-Bsb51gGT.d.mts} +28 -13
- package/dist/{versioning-DuVWcu-F.mjs → versioning-fhhWHV74.mjs} +4 -4
- package/dist/{websocket-BOXVggMf.d.mts → websocket-Cs4awVyD.d.mts} +1 -1
- package/dist/{workScope-BhsWde4k.mjs → workScope-zpPyge4t.mjs} +2 -2
- package/package.json +578 -574
- 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-DPwPWPOV.mjs +0 -350
- package/dist/index-CFfLKMH4.d.mts +0 -290
- package/dist/interface-BIpgzbZl.mjs +0 -17
- package/dist/redis-stream-BPKscsTB.d.mts +0 -260
- package/dist/registerRoute-DRsSHPSM.mjs +0 -364
- package/dist/sync/changelog.d.mts +0 -3
- package/dist/sync/changelog.mjs +0 -3
|
@@ -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 };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
import { n as
|
|
2
|
+
import { n as UpstashKeyValueCommands, t as IoredisKeyValueCommands } from "./redisShapes-CQksY67q.mjs";
|
|
3
|
+
import { n as IdempotencyResult, r as IdempotencyStore } from "./interface-C0395yUN.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,7 @@ interface RedisIdempotencyStoreOptions {
|
|
|
26
27
|
}
|
|
27
28
|
declare class RedisIdempotencyStore implements IdempotencyStore {
|
|
28
29
|
readonly name = "redis";
|
|
30
|
+
readonly supportsFencedCompletion: boolean;
|
|
29
31
|
private client;
|
|
30
32
|
private prefix;
|
|
31
33
|
private lockPrefix;
|
|
@@ -36,6 +38,7 @@ declare class RedisIdempotencyStore implements IdempotencyStore {
|
|
|
36
38
|
get(key: string): Promise<IdempotencyResult | undefined>;
|
|
37
39
|
set(key: string, result: Omit<IdempotencyResult, "key">): Promise<void>;
|
|
38
40
|
tryLock(key: string, requestId: string, ttlMs: number): Promise<boolean>;
|
|
41
|
+
setIfLocked(key: string, requestId: string, result: Omit<IdempotencyResult, "key">): Promise<boolean>;
|
|
39
42
|
unlock(key: string, requestId: string): Promise<void>;
|
|
40
43
|
isLocked(key: string): Promise<boolean>;
|
|
41
44
|
delete(key: string): Promise<void>;
|
|
@@ -46,21 +49,12 @@ declare class RedisIdempotencyStore implements IdempotencyStore {
|
|
|
46
49
|
close(): Promise<void>;
|
|
47
50
|
}
|
|
48
51
|
/**
|
|
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`.
|
|
52
|
+
* Minimal ioredis shape the idempotency store depends on — the shared
|
|
53
|
+
* key/value commands (`utils/redisShapes.ts`, where the `unknown[]`-tail rule
|
|
54
|
+
* is explained) plus `exists` and lifecycle. Keeps this file peer-dep-free.
|
|
56
55
|
*/
|
|
57
|
-
interface IoredisLike {
|
|
58
|
-
get(key: string): Promise<string | null>;
|
|
59
|
-
set(...args: unknown[]): Promise<string | null>;
|
|
60
|
-
del(...keys: string[]): Promise<number>;
|
|
56
|
+
interface IoredisLike extends IoredisKeyValueCommands {
|
|
61
57
|
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
58
|
quit?(): Promise<string>;
|
|
65
59
|
disconnect?(): void;
|
|
66
60
|
}
|
|
@@ -86,17 +80,12 @@ interface IoredisLike {
|
|
|
86
80
|
declare function ioredisAsIdempotencyClient(client: IoredisLike): RedisClient & {
|
|
87
81
|
eval?: (script: string, numkeys: number, ...args: (string | number)[]) => Promise<unknown>;
|
|
88
82
|
};
|
|
89
|
-
/**
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Minimal `@upstash/redis` shape the idempotency store depends on (REST,
|
|
85
|
+
* edge-safe) — the shared key/value commands plus `exists`.
|
|
86
|
+
*/
|
|
87
|
+
interface UpstashRedisLike extends UpstashKeyValueCommands {
|
|
94
88
|
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
89
|
}
|
|
101
90
|
/**
|
|
102
91
|
* Wrap an `@upstash/redis` REST client as an idempotency `RedisClient`.
|
|
@@ -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-mqaqZNpe.mjs";
|
|
3
|
+
import "../fastify-augmentation-DhUJ-mvo.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
|
|
@@ -277,7 +277,6 @@ declare function getCascadingResourcesWithMetadata(registry: ResourceRegistry):
|
|
|
277
277
|
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,6 +325,12 @@ 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
|
|
@@ -345,4 +350,4 @@ declare function buildResourceManifest(resource: ResourceDefinition): ResourceMa
|
|
|
345
350
|
*/
|
|
346
351
|
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 TenantDataLeak, assertNoTenantData, buildResourceManifest, buildResourceManifestFromRegistry, cascadeDeleteForOrganization, getCascadingResources, getCascadingResourcesWithMetadata, _default as introspectionPlugin
|
|
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, assertNoTenantData, buildResourceManifest, buildResourceManifestFromRegistry, cascadeDeleteForOrganization, getCascadingResources, getCascadingResourcesWithMetadata, _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.43.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-Bcz9G1bh.mjs";
|
|
3
|
+
import { t as ResourceRegistry } from "../ResourceRegistry-CuCARu2B.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.43.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as CRUD_OPERATIONS } from "./constants-Cu7Mx7_U.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/registry/assertNoTenantData.ts
|
|
5
5
|
/**
|
|
@@ -442,8 +442,8 @@ var introspectionPlugin_default = fp(introspectionPlugin, { name: "arc-introspec
|
|
|
442
442
|
//#region src/registry/manifest.ts
|
|
443
443
|
/**
|
|
444
444
|
* Resource metadata for frontend code generators — `{ name, prefix, idField,
|
|
445
|
-
* crudOps, actions[] }`, where each action carries its `mount`
|
|
446
|
-
* `requiresId`.
|
|
445
|
+
* versionField?, crudOps, actions[] }`, where each action carries its `mount`
|
|
446
|
+
* and `requiresId`.
|
|
447
447
|
*
|
|
448
448
|
* A hand-rolled FE client needs a method per declarative action and drifts the
|
|
449
449
|
* moment a new one is declared. Arc owns the resource definition, so it can
|
|
@@ -502,7 +502,8 @@ function buildResourceManifest(resource) {
|
|
|
502
502
|
actions,
|
|
503
503
|
aggregations,
|
|
504
504
|
customRoutes,
|
|
505
|
-
...resource.tenantField !== void 0 ? { tenantField: resource.tenantField } : {}
|
|
505
|
+
...resource.tenantField !== void 0 ? { tenantField: resource.tenantField } : {},
|
|
506
|
+
...resource.versionField !== void 0 ? { versionField: resource.versionField } : {}
|
|
506
507
|
};
|
|
507
508
|
}
|
|
508
509
|
/**
|
|
@@ -544,14 +545,15 @@ function buildResourceManifestFromRegistry(entry) {
|
|
|
544
545
|
name: entry.name,
|
|
545
546
|
displayName: entry.displayName ?? entry.name,
|
|
546
547
|
prefix: entry.prefix,
|
|
547
|
-
idField: "_id",
|
|
548
|
+
idField: entry.idField ?? "_id",
|
|
548
549
|
updateMethod,
|
|
549
550
|
crudOps,
|
|
550
551
|
actions,
|
|
551
552
|
aggregations,
|
|
552
553
|
customRoutes,
|
|
553
|
-
...entry.tenantField !== void 0 ? { tenantField: entry.tenantField } : {}
|
|
554
|
+
...entry.tenantField !== void 0 ? { tenantField: entry.tenantField } : {},
|
|
555
|
+
...entry.versionField !== void 0 ? { versionField: entry.versionField } : {}
|
|
554
556
|
};
|
|
555
557
|
}
|
|
556
558
|
//#endregion
|
|
557
|
-
export {
|
|
559
|
+
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 { t as __exportAll } from "./rolldown-runtime-
|
|
1
|
+
/*! @classytic/arc v2.43.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as __exportAll } from "./rolldown-runtime-D1w9Y1VJ.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,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,35 @@
|
|
|
1
|
+
/*! @classytic/arc v2.43.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,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 };
|