@classytic/arc 1.1.0 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +247 -794
- package/bin/arc.js +91 -52
- package/dist/EventTransport-BD2U0BTc.d.mts +100 -0
- package/dist/EventTransport-BD2U0BTc.d.mts.map +1 -0
- package/dist/HookSystem-BsGV-j2l.mjs +405 -0
- package/dist/HookSystem-BsGV-j2l.mjs.map +1 -0
- package/dist/ResourceRegistry-DsN4KJjV.mjs +250 -0
- package/dist/ResourceRegistry-DsN4KJjV.mjs.map +1 -0
- package/dist/adapters/index.d.mts +5 -0
- package/dist/adapters/index.mjs +3 -0
- package/dist/audit/index.d.mts +82 -0
- package/dist/audit/index.d.mts.map +1 -0
- package/dist/audit/index.mjs +276 -0
- package/dist/audit/index.mjs.map +1 -0
- package/dist/audit/mongodb.d.mts +5 -0
- package/dist/audit/mongodb.mjs +3 -0
- package/dist/audited-C3T5DTUx.mjs +141 -0
- package/dist/audited-C3T5DTUx.mjs.map +1 -0
- package/dist/auth/index.d.mts +189 -0
- package/dist/auth/index.d.mts.map +1 -0
- package/dist/auth/index.mjs +1102 -0
- package/dist/auth/index.mjs.map +1 -0
- package/dist/auth/redis-session.d.mts +44 -0
- package/dist/auth/redis-session.d.mts.map +1 -0
- package/dist/auth/redis-session.mjs +76 -0
- package/dist/auth/redis-session.mjs.map +1 -0
- package/dist/betterAuthOpenApi-BrHKeSAx.mjs +250 -0
- package/dist/betterAuthOpenApi-BrHKeSAx.mjs.map +1 -0
- package/dist/cache/index.d.mts +146 -0
- package/dist/cache/index.d.mts.map +1 -0
- package/dist/cache/index.mjs +92 -0
- package/dist/cache/index.mjs.map +1 -0
- package/dist/caching-Bl28lYsR.mjs +94 -0
- package/dist/caching-Bl28lYsR.mjs.map +1 -0
- package/dist/chunk-C7Uep-_p.mjs +20 -0
- package/dist/circuitBreaker-DeY4FCjs.mjs +1097 -0
- package/dist/circuitBreaker-DeY4FCjs.mjs.map +1 -0
- package/dist/cli/commands/describe.d.mts +19 -0
- package/dist/cli/commands/describe.d.mts.map +1 -0
- package/dist/cli/commands/describe.mjs +239 -0
- package/dist/cli/commands/describe.mjs.map +1 -0
- package/dist/cli/commands/docs.d.mts +14 -0
- package/dist/cli/commands/docs.d.mts.map +1 -0
- package/dist/cli/commands/docs.mjs +53 -0
- package/dist/cli/commands/docs.mjs.map +1 -0
- package/dist/cli/commands/{generate.d.ts → generate.d.mts} +3 -1
- package/dist/cli/commands/generate.d.mts.map +1 -0
- package/dist/cli/commands/generate.mjs +358 -0
- package/dist/cli/commands/generate.mjs.map +1 -0
- package/dist/cli/commands/{init.d.ts → init.d.mts} +12 -8
- package/dist/cli/commands/init.d.mts.map +1 -0
- package/dist/cli/commands/{init.js → init.mjs} +807 -616
- package/dist/cli/commands/init.mjs.map +1 -0
- package/dist/cli/commands/introspect.d.mts +11 -0
- package/dist/cli/commands/introspect.d.mts.map +1 -0
- package/dist/cli/commands/introspect.mjs +76 -0
- package/dist/cli/commands/introspect.mjs.map +1 -0
- package/dist/cli/index.d.mts +17 -0
- package/dist/cli/index.d.mts.map +1 -0
- package/dist/cli/index.mjs +157 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/constants-DdXFXQtN.mjs +85 -0
- package/dist/constants-DdXFXQtN.mjs.map +1 -0
- package/dist/core/index.d.mts +5 -0
- package/dist/core/index.mjs +4 -0
- package/dist/createApp-CUgNqegw.mjs +560 -0
- package/dist/createApp-CUgNqegw.mjs.map +1 -0
- package/dist/defineResource-k0_BDn8v.mjs +2197 -0
- package/dist/defineResource-k0_BDn8v.mjs.map +1 -0
- package/dist/discovery/index.d.mts +47 -0
- package/dist/discovery/index.d.mts.map +1 -0
- package/dist/discovery/index.mjs +110 -0
- package/dist/discovery/index.mjs.map +1 -0
- package/dist/docs/index.d.mts +163 -0
- package/dist/docs/index.d.mts.map +1 -0
- package/dist/docs/index.mjs +73 -0
- package/dist/docs/index.mjs.map +1 -0
- package/dist/elevation-BRy3yFWT.mjs +113 -0
- package/dist/elevation-BRy3yFWT.mjs.map +1 -0
- package/dist/elevation-B_2dRLVP.d.mts +88 -0
- package/dist/elevation-B_2dRLVP.d.mts.map +1 -0
- package/dist/errorHandler-BbcgBmIH.d.mts +73 -0
- package/dist/errorHandler-BbcgBmIH.d.mts.map +1 -0
- package/dist/errorHandler-C1okiriz.mjs +109 -0
- package/dist/errorHandler-C1okiriz.mjs.map +1 -0
- package/dist/errors-B9bZok84.mjs +212 -0
- package/dist/errors-B9bZok84.mjs.map +1 -0
- package/dist/errors-ChKiFz62.d.mts +125 -0
- package/dist/errors-ChKiFz62.d.mts.map +1 -0
- package/dist/eventPlugin-CTrLH3mt.d.mts +125 -0
- package/dist/eventPlugin-CTrLH3mt.d.mts.map +1 -0
- package/dist/eventPlugin-DGR_B2on.mjs +230 -0
- package/dist/eventPlugin-DGR_B2on.mjs.map +1 -0
- package/dist/events/index.d.mts +54 -0
- package/dist/events/index.d.mts.map +1 -0
- package/dist/events/index.mjs +52 -0
- package/dist/events/index.mjs.map +1 -0
- package/dist/events/transports/redis-stream-entry.d.mts +2 -0
- package/dist/events/transports/redis-stream-entry.mjs +178 -0
- package/dist/events/transports/redis-stream-entry.mjs.map +1 -0
- package/dist/events/transports/redis.d.mts +77 -0
- package/dist/events/transports/redis.d.mts.map +1 -0
- package/dist/events/transports/redis.mjs +125 -0
- package/dist/events/transports/redis.mjs.map +1 -0
- package/dist/externalPaths-DlINfKbP.d.mts +51 -0
- package/dist/externalPaths-DlINfKbP.d.mts.map +1 -0
- package/dist/factory/index.d.mts +64 -0
- package/dist/factory/index.d.mts.map +1 -0
- package/dist/factory/index.mjs +3 -0
- package/dist/fastifyAdapter-BkrGrlFi.d.mts +217 -0
- package/dist/fastifyAdapter-BkrGrlFi.d.mts.map +1 -0
- package/dist/fields-DyaDVX4J.d.mts +110 -0
- package/dist/fields-DyaDVX4J.d.mts.map +1 -0
- package/dist/fields-iagOozy0.mjs +115 -0
- package/dist/fields-iagOozy0.mjs.map +1 -0
- package/dist/hooks/index.d.mts +4 -0
- package/dist/hooks/index.mjs +3 -0
- package/dist/idempotency/index.d.mts +97 -0
- package/dist/idempotency/index.d.mts.map +1 -0
- package/dist/idempotency/index.mjs +320 -0
- package/dist/idempotency/index.mjs.map +1 -0
- package/dist/idempotency/mongodb.d.mts +2 -0
- package/dist/idempotency/mongodb.mjs +115 -0
- package/dist/idempotency/mongodb.mjs.map +1 -0
- package/dist/idempotency/redis.d.mts +2 -0
- package/dist/idempotency/redis.mjs +104 -0
- package/dist/idempotency/redis.mjs.map +1 -0
- package/dist/index.d.mts +261 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +105 -0
- package/dist/index.mjs.map +1 -0
- package/dist/integrations/event-gateway.d.mts +47 -0
- package/dist/integrations/event-gateway.d.mts.map +1 -0
- package/dist/integrations/event-gateway.mjs +44 -0
- package/dist/integrations/event-gateway.mjs.map +1 -0
- package/dist/integrations/index.d.mts +5 -0
- package/dist/integrations/index.mjs +1 -0
- package/dist/integrations/jobs.d.mts +104 -0
- package/dist/integrations/jobs.d.mts.map +1 -0
- package/dist/integrations/jobs.mjs +124 -0
- package/dist/integrations/jobs.mjs.map +1 -0
- package/dist/integrations/streamline.d.mts +61 -0
- package/dist/integrations/streamline.d.mts.map +1 -0
- package/dist/integrations/streamline.mjs +126 -0
- package/dist/integrations/streamline.mjs.map +1 -0
- package/dist/integrations/websocket.d.mts +83 -0
- package/dist/integrations/websocket.d.mts.map +1 -0
- package/dist/integrations/websocket.mjs +289 -0
- package/dist/integrations/websocket.mjs.map +1 -0
- package/dist/interface-B01JvPVc.d.mts +78 -0
- package/dist/interface-B01JvPVc.d.mts.map +1 -0
- package/dist/interface-CZe8IkMf.d.mts +55 -0
- package/dist/interface-CZe8IkMf.d.mts.map +1 -0
- package/dist/interface-Ch8HU9uM.d.mts +1098 -0
- package/dist/interface-Ch8HU9uM.d.mts.map +1 -0
- package/dist/introspectionPlugin-rFdO8ZUa.mjs +54 -0
- package/dist/introspectionPlugin-rFdO8ZUa.mjs.map +1 -0
- package/dist/keys-BqNejWup.mjs +43 -0
- package/dist/keys-BqNejWup.mjs.map +1 -0
- package/dist/logger-Df2O2WsW.mjs +79 -0
- package/dist/logger-Df2O2WsW.mjs.map +1 -0
- package/dist/memory-cQgelFOj.mjs +144 -0
- package/dist/memory-cQgelFOj.mjs.map +1 -0
- package/dist/migrations/index.d.mts +157 -0
- package/dist/migrations/index.d.mts.map +1 -0
- package/dist/migrations/index.mjs +261 -0
- package/dist/migrations/index.mjs.map +1 -0
- package/dist/mongodb-BfJVlUJH.mjs +94 -0
- package/dist/mongodb-BfJVlUJH.mjs.map +1 -0
- package/dist/mongodb-CGzRbfAK.d.mts +119 -0
- package/dist/mongodb-CGzRbfAK.d.mts.map +1 -0
- package/dist/mongodb-JN-9JA7K.d.mts +72 -0
- package/dist/mongodb-JN-9JA7K.d.mts.map +1 -0
- package/dist/openapi-G3Cw7XuM.mjs +524 -0
- package/dist/openapi-G3Cw7XuM.mjs.map +1 -0
- package/dist/org/index.d.mts +69 -0
- package/dist/org/index.d.mts.map +1 -0
- package/dist/org/index.mjs +514 -0
- package/dist/org/index.mjs.map +1 -0
- package/dist/org/types.d.mts +83 -0
- package/dist/org/types.d.mts.map +1 -0
- package/dist/org/types.mjs +1 -0
- package/dist/permissions/index.d.mts +279 -0
- package/dist/permissions/index.d.mts.map +1 -0
- package/dist/permissions/index.mjs +579 -0
- package/dist/permissions/index.mjs.map +1 -0
- package/dist/plugins/index.d.mts +173 -0
- package/dist/plugins/index.d.mts.map +1 -0
- package/dist/plugins/index.mjs +523 -0
- package/dist/plugins/index.mjs.map +1 -0
- package/dist/plugins/response-cache.d.mts +88 -0
- package/dist/plugins/response-cache.d.mts.map +1 -0
- package/dist/plugins/response-cache.mjs +284 -0
- package/dist/plugins/response-cache.mjs.map +1 -0
- package/dist/plugins/tracing-entry.d.mts +2 -0
- package/dist/plugins/tracing-entry.mjs +186 -0
- package/dist/plugins/tracing-entry.mjs.map +1 -0
- package/dist/pluralize-CEweyOEm.mjs +87 -0
- package/dist/pluralize-CEweyOEm.mjs.map +1 -0
- package/dist/policies/{index.d.ts → index.d.mts} +204 -169
- package/dist/policies/index.d.mts.map +1 -0
- package/dist/policies/index.mjs +322 -0
- package/dist/policies/index.mjs.map +1 -0
- package/dist/presets/{index.d.ts → index.d.mts} +63 -131
- package/dist/presets/index.d.mts.map +1 -0
- package/dist/presets/index.mjs +144 -0
- package/dist/presets/index.mjs.map +1 -0
- package/dist/presets/multiTenant.d.mts +25 -0
- package/dist/presets/multiTenant.d.mts.map +1 -0
- package/dist/presets/multiTenant.mjs +114 -0
- package/dist/presets/multiTenant.mjs.map +1 -0
- package/dist/presets-BITljm96.mjs +120 -0
- package/dist/presets-BITljm96.mjs.map +1 -0
- package/dist/presets-DzSMwlKj.d.mts +58 -0
- package/dist/presets-DzSMwlKj.d.mts.map +1 -0
- package/dist/prisma-DJbMt3yf.mjs +628 -0
- package/dist/prisma-DJbMt3yf.mjs.map +1 -0
- package/dist/prisma-Dg9GoVdj.d.mts +275 -0
- package/dist/prisma-Dg9GoVdj.d.mts.map +1 -0
- package/dist/queryCachePlugin-7THaI5mt.d.mts +72 -0
- package/dist/queryCachePlugin-7THaI5mt.d.mts.map +1 -0
- package/dist/queryCachePlugin-DMBnp2Q0.mjs +139 -0
- package/dist/queryCachePlugin-DMBnp2Q0.mjs.map +1 -0
- package/dist/redis-D-JAeLtm.d.mts +50 -0
- package/dist/redis-D-JAeLtm.d.mts.map +1 -0
- package/dist/redis-stream-Bdh_vUU8.d.mts +104 -0
- package/dist/redis-stream-Bdh_vUU8.d.mts.map +1 -0
- package/dist/registry/index.d.mts +12 -0
- package/dist/registry/index.d.mts.map +1 -0
- package/dist/registry/index.mjs +4 -0
- package/dist/requestContext-QQD6ROJc.mjs +56 -0
- package/dist/requestContext-QQD6ROJc.mjs.map +1 -0
- package/dist/schemaConverter-BwrmWroW.mjs +99 -0
- package/dist/schemaConverter-BwrmWroW.mjs.map +1 -0
- package/dist/schemas/index.d.mts +64 -0
- package/dist/schemas/index.d.mts.map +1 -0
- package/dist/schemas/index.mjs +83 -0
- package/dist/schemas/index.mjs.map +1 -0
- package/dist/scope/index.d.mts +22 -0
- package/dist/scope/index.d.mts.map +1 -0
- package/dist/scope/index.mjs +66 -0
- package/dist/scope/index.mjs.map +1 -0
- package/dist/sessionManager-jPKLbHE0.d.mts +187 -0
- package/dist/sessionManager-jPKLbHE0.d.mts.map +1 -0
- package/dist/sse-B3c3_yZp.mjs +124 -0
- package/dist/sse-B3c3_yZp.mjs.map +1 -0
- package/dist/testing/index.d.mts +908 -0
- package/dist/testing/index.d.mts.map +1 -0
- package/dist/testing/index.mjs +1977 -0
- package/dist/testing/index.mjs.map +1 -0
- package/dist/tracing-Cc7vVQPp.d.mts +71 -0
- package/dist/tracing-Cc7vVQPp.d.mts.map +1 -0
- package/dist/typeGuards-DhMNLuvU.mjs +10 -0
- package/dist/typeGuards-DhMNLuvU.mjs.map +1 -0
- package/dist/types/index.d.mts +947 -0
- package/dist/types/index.d.mts.map +1 -0
- package/dist/types/index.mjs +15 -0
- package/dist/types/index.mjs.map +1 -0
- package/dist/types-Beqn1Un7.mjs +39 -0
- package/dist/types-Beqn1Un7.mjs.map +1 -0
- package/dist/types-CIgB7UUl.d.mts +446 -0
- package/dist/types-CIgB7UUl.d.mts.map +1 -0
- package/dist/types-aYB4V7uN.d.mts +87 -0
- package/dist/types-aYB4V7uN.d.mts.map +1 -0
- package/dist/utils/index.d.mts +748 -0
- package/dist/utils/index.d.mts.map +1 -0
- package/dist/utils/index.mjs +6 -0
- package/package.json +194 -68
- package/dist/BaseController-DVAiHxEQ.d.ts +0 -233
- package/dist/adapters/index.d.ts +0 -237
- package/dist/adapters/index.js +0 -668
- package/dist/arcCorePlugin-CsShQdyP.d.ts +0 -273
- package/dist/audit/index.d.ts +0 -195
- package/dist/audit/index.js +0 -319
- package/dist/auth/index.d.ts +0 -47
- package/dist/auth/index.js +0 -174
- package/dist/cli/commands/docs.d.ts +0 -11
- package/dist/cli/commands/docs.js +0 -474
- package/dist/cli/commands/generate.js +0 -334
- package/dist/cli/commands/introspect.d.ts +0 -8
- package/dist/cli/commands/introspect.js +0 -338
- package/dist/cli/index.d.ts +0 -4
- package/dist/cli/index.js +0 -3269
- package/dist/core/index.d.ts +0 -220
- package/dist/core/index.js +0 -2786
- package/dist/createApp-Ce9wl8W9.d.ts +0 -77
- package/dist/docs/index.d.ts +0 -166
- package/dist/docs/index.js +0 -658
- package/dist/errors-8WIxGS_6.d.ts +0 -122
- package/dist/events/index.d.ts +0 -117
- package/dist/events/index.js +0 -89
- package/dist/factory/index.d.ts +0 -38
- package/dist/factory/index.js +0 -1652
- package/dist/hooks/index.d.ts +0 -4
- package/dist/hooks/index.js +0 -199
- package/dist/idempotency/index.d.ts +0 -323
- package/dist/idempotency/index.js +0 -500
- package/dist/index-B4t03KQ0.d.ts +0 -1366
- package/dist/index.d.ts +0 -135
- package/dist/index.js +0 -4756
- package/dist/migrations/index.d.ts +0 -185
- package/dist/migrations/index.js +0 -274
- package/dist/org/index.d.ts +0 -129
- package/dist/org/index.js +0 -220
- package/dist/permissions/index.d.ts +0 -144
- package/dist/permissions/index.js +0 -103
- package/dist/plugins/index.d.ts +0 -46
- package/dist/plugins/index.js +0 -1069
- package/dist/policies/index.js +0 -196
- package/dist/presets/index.js +0 -384
- package/dist/presets/multiTenant.d.ts +0 -39
- package/dist/presets/multiTenant.js +0 -112
- package/dist/registry/index.d.ts +0 -16
- package/dist/registry/index.js +0 -253
- package/dist/testing/index.d.ts +0 -618
- package/dist/testing/index.js +0 -48020
- package/dist/types/index.d.ts +0 -4
- package/dist/types/index.js +0 -8
- package/dist/types-B99TBmFV.d.ts +0 -76
- package/dist/types-BvckRbs2.d.ts +0 -143
- package/dist/utils/index.d.ts +0 -679
- package/dist/utils/index.js +0 -931
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { i as EventTransport, n as EventHandler, r as EventLogger, t as DomainEvent } from "../../EventTransport-BD2U0BTc.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/events/transports/redis.d.ts
|
|
4
|
+
interface RedisLike {
|
|
5
|
+
publish(channel: string, message: string): Promise<number>;
|
|
6
|
+
subscribe(...channels: string[]): Promise<unknown>;
|
|
7
|
+
psubscribe(...patterns: string[]): Promise<unknown>;
|
|
8
|
+
on(event: string, handler: (...args: unknown[]) => void): unknown;
|
|
9
|
+
duplicate(): RedisLike;
|
|
10
|
+
quit(): Promise<unknown>;
|
|
11
|
+
}
|
|
12
|
+
interface RedisEventTransportOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Redis channel prefix for all events.
|
|
15
|
+
* Events are published to `<channel>:<event.type>`.
|
|
16
|
+
* @default 'arc-events'
|
|
17
|
+
*/
|
|
18
|
+
channel?: string;
|
|
19
|
+
/**
|
|
20
|
+
* If `true`, the transport will NOT call `quit()` on the Redis clients when
|
|
21
|
+
* `close()` is called. Useful when you manage the Redis lifecycle externally.
|
|
22
|
+
* @default false
|
|
23
|
+
*/
|
|
24
|
+
externalLifecycle?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Logger for error messages (default: console).
|
|
27
|
+
* Pass `fastify.log` to integrate with your application logger.
|
|
28
|
+
*/
|
|
29
|
+
logger?: EventLogger;
|
|
30
|
+
}
|
|
31
|
+
declare class RedisEventTransport implements EventTransport {
|
|
32
|
+
readonly name = "redis";
|
|
33
|
+
/** Publish-side client (the original client or a duplicate). */
|
|
34
|
+
private pub;
|
|
35
|
+
/** Subscribe-side client (always a duplicate — ioredis requires a dedicated connection for subscriptions). */
|
|
36
|
+
private sub;
|
|
37
|
+
/** Channel prefix. */
|
|
38
|
+
private channel;
|
|
39
|
+
/** Whether we own the Redis client lifecycle. */
|
|
40
|
+
private externalLifecycle;
|
|
41
|
+
/** Logger for error messages. */
|
|
42
|
+
private logger;
|
|
43
|
+
/** Registered handlers keyed by their *Redis* pattern (with channel prefix). */
|
|
44
|
+
private handlers;
|
|
45
|
+
/** Tracks whether the pmessage listener has been attached. */
|
|
46
|
+
private listenerAttached;
|
|
47
|
+
constructor(redis: RedisLike, options?: RedisEventTransportOptions);
|
|
48
|
+
publish(event: DomainEvent): Promise<void>;
|
|
49
|
+
subscribe(pattern: string, handler: EventHandler): Promise<() => void>;
|
|
50
|
+
close(): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* Attach the Redis message listeners exactly once.
|
|
53
|
+
*/
|
|
54
|
+
private ensureListener;
|
|
55
|
+
/**
|
|
56
|
+
* Dispatch an incoming Redis message to all registered handlers.
|
|
57
|
+
*/
|
|
58
|
+
private dispatch;
|
|
59
|
+
/**
|
|
60
|
+
* Convert an Arc event pattern to a Redis channel/pattern string.
|
|
61
|
+
*
|
|
62
|
+
* Arc patterns use `*` as a single-segment wildcard (e.g., `product.*`).
|
|
63
|
+
* Redis PSUBSCRIBE uses the same glob syntax, so we just prepend the
|
|
64
|
+
* channel prefix.
|
|
65
|
+
*
|
|
66
|
+
* Special case: bare `*` means "all events", which maps to
|
|
67
|
+
* `<channel>:*` in Redis.
|
|
68
|
+
*/
|
|
69
|
+
private toRedisPattern;
|
|
70
|
+
/**
|
|
71
|
+
* Returns true if the pattern contains glob characters.
|
|
72
|
+
*/
|
|
73
|
+
private isGlob;
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
export { RedisEventTransport, RedisEventTransport as default, RedisEventTransportOptions, RedisLike };
|
|
77
|
+
//# sourceMappingURL=redis.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.d.mts","names":[],"sources":["../../../src/events/transports/redis.ts"],"mappings":";;;UAwBiB,SAAA;EACf,OAAA,CAAQ,OAAA,UAAiB,OAAA,WAAkB,OAAA;EAC3C,SAAA,IAAa,QAAA,aAAqB,OAAA;EAClC,UAAA,IAAc,QAAA,aAAqB,OAAA;EACnC,EAAA,CAAG,KAAA,UAAe,OAAA,MAAa,IAAA;EAC/B,SAAA,IAAa,SAAA;EACb,IAAA,IAAQ,OAAA;AAAA;AAAA,UAOO,0BAAA;EATgB;;;;;EAe/B,OAAA;EAbe;;AAOjB;;;EAaE,iBAAA;EAPA;;;;EAaA,MAAA,GAAS,WAAA;AAAA;AAAA,cA8BE,mBAAA,YAA+B,cAAA;EAAA,SACjC,IAAA;;UAGD,GAAA;EAoB+B;EAAA,QAjB/B,GAAA;EAmC2B;EAAA,QAhC3B,OAAA;EAyCiD;EAAA,QAtCjD,iBAAA;EAbkC;EAAA,QAgBlC,MAAA;EAhBgD;EAAA,QAmBhD,QAAA;EAlBC;EAAA,QAqBD,gBAAA;cAEI,KAAA,EAAO,SAAA,EAAW,OAAA,GAAS,0BAAA;EAkBjC,OAAA,CAAQ,KAAA,EAAO,WAAA,GAAc,OAAA;EAS7B,SAAA,CAAU,OAAA,UAAiB,OAAA,EAAS,YAAA,GAAe,OAAA;EAmCnD,KAAA,CAAA,GAAS,OAAA;EAnEP;;;EAAA,QAyFA,cAAA;EApFI;;;EAAA,QAwGJ,QAAA;EAtFa;;;;;;;;;;EAAA,QA6Hb,cAAA;EAvCA;;;EAAA,QA8CA,MAAA;AAAA"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
//#region src/events/transports/redis.ts
|
|
2
|
+
/**
|
|
3
|
+
* DomainEvent contains a `Date` in `meta.timestamp`. We serialize it as an
|
|
4
|
+
* ISO string and revive it on the other end so subscribers always receive a
|
|
5
|
+
* proper Date object.
|
|
6
|
+
*/
|
|
7
|
+
function serialize(event) {
|
|
8
|
+
return JSON.stringify(event, (_key, value) => {
|
|
9
|
+
if (value instanceof Date) return value.toISOString();
|
|
10
|
+
return value;
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function deserialize(raw) {
|
|
14
|
+
return JSON.parse(raw, (key, value) => {
|
|
15
|
+
if (key === "timestamp" && typeof value === "string") return new Date(value);
|
|
16
|
+
return value;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
var RedisEventTransport = class {
|
|
20
|
+
name = "redis";
|
|
21
|
+
/** Publish-side client (the original client or a duplicate). */
|
|
22
|
+
pub;
|
|
23
|
+
/** Subscribe-side client (always a duplicate — ioredis requires a dedicated connection for subscriptions). */
|
|
24
|
+
sub;
|
|
25
|
+
/** Channel prefix. */
|
|
26
|
+
channel;
|
|
27
|
+
/** Whether we own the Redis client lifecycle. */
|
|
28
|
+
externalLifecycle;
|
|
29
|
+
/** Logger for error messages. */
|
|
30
|
+
logger;
|
|
31
|
+
/** Registered handlers keyed by their *Redis* pattern (with channel prefix). */
|
|
32
|
+
handlers = /* @__PURE__ */ new Map();
|
|
33
|
+
/** Tracks whether the pmessage listener has been attached. */
|
|
34
|
+
listenerAttached = false;
|
|
35
|
+
constructor(redis, options = {}) {
|
|
36
|
+
const { channel = "arc-events", externalLifecycle = false, logger = console } = options;
|
|
37
|
+
this.channel = channel;
|
|
38
|
+
this.externalLifecycle = externalLifecycle;
|
|
39
|
+
this.logger = logger;
|
|
40
|
+
this.pub = redis;
|
|
41
|
+
this.sub = redis.duplicate();
|
|
42
|
+
}
|
|
43
|
+
async publish(event) {
|
|
44
|
+
const redisChannel = `${this.channel}:${event.type}`;
|
|
45
|
+
await this.pub.publish(redisChannel, serialize(event));
|
|
46
|
+
}
|
|
47
|
+
async subscribe(pattern, handler) {
|
|
48
|
+
this.ensureListener();
|
|
49
|
+
const redisPattern = this.toRedisPattern(pattern);
|
|
50
|
+
if (!this.handlers.has(redisPattern)) {
|
|
51
|
+
this.handlers.set(redisPattern, /* @__PURE__ */ new Set());
|
|
52
|
+
if (this.isGlob(redisPattern)) await this.sub.psubscribe(redisPattern);
|
|
53
|
+
else await this.sub.subscribe(redisPattern);
|
|
54
|
+
}
|
|
55
|
+
this.handlers.get(redisPattern).add(handler);
|
|
56
|
+
return () => {
|
|
57
|
+
const set = this.handlers.get(redisPattern);
|
|
58
|
+
if (set) set.delete(handler);
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
async close() {
|
|
62
|
+
this.handlers.clear();
|
|
63
|
+
if (!this.externalLifecycle) await Promise.all([this.sub.quit(), this.pub.quit()]);
|
|
64
|
+
else await this.sub.quit();
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Attach the Redis message listeners exactly once.
|
|
68
|
+
*/
|
|
69
|
+
ensureListener() {
|
|
70
|
+
if (this.listenerAttached) return;
|
|
71
|
+
this.listenerAttached = true;
|
|
72
|
+
this.sub.on("pmessage", (...args) => {
|
|
73
|
+
const [redisPattern, , message] = args;
|
|
74
|
+
this.dispatch(redisPattern, message);
|
|
75
|
+
});
|
|
76
|
+
this.sub.on("message", (...args) => {
|
|
77
|
+
const [channel, message] = args;
|
|
78
|
+
this.dispatch(channel, message);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Dispatch an incoming Redis message to all registered handlers.
|
|
83
|
+
*/
|
|
84
|
+
dispatch(redisPatternOrChannel, raw) {
|
|
85
|
+
const handlers = this.handlers.get(redisPatternOrChannel);
|
|
86
|
+
if (!handlers || handlers.size === 0) return;
|
|
87
|
+
let event;
|
|
88
|
+
try {
|
|
89
|
+
event = deserialize(raw);
|
|
90
|
+
} catch {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
for (const handler of handlers) try {
|
|
94
|
+
const result = handler(event);
|
|
95
|
+
if (result && typeof result.catch === "function") result.catch((err) => {
|
|
96
|
+
this.logger.error(`[RedisEventTransport] Handler error for ${event.type}:`, err);
|
|
97
|
+
});
|
|
98
|
+
} catch (err) {
|
|
99
|
+
this.logger.error(`[RedisEventTransport] Handler error for ${event.type}:`, err);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Convert an Arc event pattern to a Redis channel/pattern string.
|
|
104
|
+
*
|
|
105
|
+
* Arc patterns use `*` as a single-segment wildcard (e.g., `product.*`).
|
|
106
|
+
* Redis PSUBSCRIBE uses the same glob syntax, so we just prepend the
|
|
107
|
+
* channel prefix.
|
|
108
|
+
*
|
|
109
|
+
* Special case: bare `*` means "all events", which maps to
|
|
110
|
+
* `<channel>:*` in Redis.
|
|
111
|
+
*/
|
|
112
|
+
toRedisPattern(pattern) {
|
|
113
|
+
return `${this.channel}:${pattern}`;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Returns true if the pattern contains glob characters.
|
|
117
|
+
*/
|
|
118
|
+
isGlob(pattern) {
|
|
119
|
+
return pattern.includes("*") || pattern.includes("?") || pattern.includes("[");
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
//#endregion
|
|
124
|
+
export { RedisEventTransport, RedisEventTransport as default };
|
|
125
|
+
//# sourceMappingURL=redis.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis.mjs","names":[],"sources":["../../../src/events/transports/redis.ts"],"sourcesContent":["/**\n * Redis Event Transport\n *\n * Uses Redis Pub/Sub for cross-process event delivery.\n * Optional dependency — only imported when explicitly used.\n *\n * @example\n * import { RedisEventTransport } from '@classytic/arc/events/redis';\n * import Redis from 'ioredis';\n *\n * const redis = new Redis();\n * const transport = new RedisEventTransport(redis, { channel: 'arc-events' });\n *\n * await app.register(eventPlugin, { transport });\n */\n\nimport type { EventTransport, DomainEvent, EventHandler, EventLogger } from '../EventTransport.js';\n\n// ---------------------------------------------------------------------------\n// Minimal Redis-like interface so consumers don't need ioredis at type level.\n// Any Redis client implementing these methods (ioredis, node-redis wrapper,\n// etc.) will work.\n// ---------------------------------------------------------------------------\n\nexport interface RedisLike {\n publish(channel: string, message: string): Promise<number>;\n subscribe(...channels: string[]): Promise<unknown>;\n psubscribe(...patterns: string[]): Promise<unknown>;\n on(event: string, handler: (...args: unknown[]) => void): unknown;\n duplicate(): RedisLike;\n quit(): Promise<unknown>;\n}\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface RedisEventTransportOptions {\n /**\n * Redis channel prefix for all events.\n * Events are published to `<channel>:<event.type>`.\n * @default 'arc-events'\n */\n channel?: string;\n\n /**\n * If `true`, the transport will NOT call `quit()` on the Redis clients when\n * `close()` is called. Useful when you manage the Redis lifecycle externally.\n * @default false\n */\n externalLifecycle?: boolean;\n\n /**\n * Logger for error messages (default: console).\n * Pass `fastify.log` to integrate with your application logger.\n */\n logger?: EventLogger;\n}\n\n// ---------------------------------------------------------------------------\n// Serialization helpers\n// ---------------------------------------------------------------------------\n\n/**\n * DomainEvent contains a `Date` in `meta.timestamp`. We serialize it as an\n * ISO string and revive it on the other end so subscribers always receive a\n * proper Date object.\n */\nfunction serialize(event: DomainEvent): string {\n return JSON.stringify(event, (_key, value) => {\n if (value instanceof Date) return value.toISOString();\n return value;\n });\n}\n\nfunction deserialize(raw: string): DomainEvent {\n return JSON.parse(raw, (key, value) => {\n if (key === 'timestamp' && typeof value === 'string') return new Date(value);\n return value;\n }) as DomainEvent;\n}\n\n// ---------------------------------------------------------------------------\n// Transport\n// ---------------------------------------------------------------------------\n\nexport class RedisEventTransport implements EventTransport {\n readonly name = 'redis';\n\n /** Publish-side client (the original client or a duplicate). */\n private pub: RedisLike;\n\n /** Subscribe-side client (always a duplicate — ioredis requires a dedicated connection for subscriptions). */\n private sub: RedisLike;\n\n /** Channel prefix. */\n private channel: string;\n\n /** Whether we own the Redis client lifecycle. */\n private externalLifecycle: boolean;\n\n /** Logger for error messages. */\n private logger: EventLogger;\n\n /** Registered handlers keyed by their *Redis* pattern (with channel prefix). */\n private handlers = new Map<string, Set<EventHandler>>();\n\n /** Tracks whether the pmessage listener has been attached. */\n private listenerAttached = false;\n\n constructor(redis: RedisLike, options: RedisEventTransportOptions = {}) {\n const { channel = 'arc-events', externalLifecycle = false, logger = console } = options;\n\n this.channel = channel;\n this.externalLifecycle = externalLifecycle;\n this.logger = logger;\n\n // Use the provided client for publishing, create a dedicated duplicate for subscribing.\n // ioredis requires separate connections for pub and sub because a client in\n // subscriber mode cannot issue regular commands.\n this.pub = redis;\n this.sub = redis.duplicate();\n }\n\n // -----------------------------------------------------------------------\n // EventTransport.publish\n // -----------------------------------------------------------------------\n\n async publish(event: DomainEvent): Promise<void> {\n const redisChannel = `${this.channel}:${event.type}`;\n await this.pub.publish(redisChannel, serialize(event));\n }\n\n // -----------------------------------------------------------------------\n // EventTransport.subscribe\n // -----------------------------------------------------------------------\n\n async subscribe(pattern: string, handler: EventHandler): Promise<() => void> {\n this.ensureListener();\n\n const redisPattern = this.toRedisPattern(pattern);\n\n if (!this.handlers.has(redisPattern)) {\n this.handlers.set(redisPattern, new Set());\n\n // Decide between exact SUBSCRIBE or pattern PSUBSCRIBE.\n if (this.isGlob(redisPattern)) {\n await this.sub.psubscribe(redisPattern);\n } else {\n await this.sub.subscribe(redisPattern);\n }\n }\n\n this.handlers.get(redisPattern)!.add(handler);\n\n // Return unsubscribe function.\n return () => {\n const set = this.handlers.get(redisPattern);\n if (set) {\n set.delete(handler);\n // Note: we intentionally do NOT punsubscribe/unsubscribe from Redis\n // when the last handler for a pattern is removed. This keeps the\n // implementation simple and avoids race conditions. The pattern stays\n // active until close() is called.\n }\n };\n }\n\n // -----------------------------------------------------------------------\n // EventTransport.close\n // -----------------------------------------------------------------------\n\n async close(): Promise<void> {\n this.handlers.clear();\n\n if (!this.externalLifecycle) {\n // The subscriber connection is always a duplicate we created, so we\n // always quit it. The publisher is the client the user passed in, so\n // we only quit it when externalLifecycle is false.\n await Promise.all([this.sub.quit(), this.pub.quit()]);\n } else {\n // Even with external lifecycle we quit the subscriber duplicate since we\n // created it ourselves.\n await this.sub.quit();\n }\n }\n\n // -----------------------------------------------------------------------\n // Internal helpers\n // -----------------------------------------------------------------------\n\n /**\n * Attach the Redis message listeners exactly once.\n */\n private ensureListener(): void {\n if (this.listenerAttached) return;\n this.listenerAttached = true;\n\n // Pattern-matched messages (from psubscribe)\n this.sub.on('pmessage', (...args: unknown[]) => {\n const [redisPattern, , message] = args as [string, string, string];\n this.dispatch(redisPattern, message);\n });\n\n // Exact-matched messages (from subscribe)\n this.sub.on('message', (...args: unknown[]) => {\n const [channel, message] = args as [string, string];\n this.dispatch(channel, message);\n });\n }\n\n /**\n * Dispatch an incoming Redis message to all registered handlers.\n */\n private dispatch(redisPatternOrChannel: string, raw: string): void {\n const handlers = this.handlers.get(redisPatternOrChannel);\n if (!handlers || handlers.size === 0) return;\n\n let event: DomainEvent;\n try {\n event = deserialize(raw);\n } catch {\n // Ignore malformed messages — they may come from non-Arc publishers\n // sharing the same Redis instance.\n return;\n }\n\n for (const handler of handlers) {\n try {\n const result = handler(event);\n // If the handler returns a promise, catch rejections so one failing\n // handler doesn't prevent the rest from executing.\n if (result && typeof (result as Promise<void>).catch === 'function') {\n (result as Promise<void>).catch((err) => {\n this.logger.error(`[RedisEventTransport] Handler error for ${event.type}:`, err);\n });\n }\n } catch (err) {\n this.logger.error(`[RedisEventTransport] Handler error for ${event.type}:`, err);\n }\n }\n }\n\n /**\n * Convert an Arc event pattern to a Redis channel/pattern string.\n *\n * Arc patterns use `*` as a single-segment wildcard (e.g., `product.*`).\n * Redis PSUBSCRIBE uses the same glob syntax, so we just prepend the\n * channel prefix.\n *\n * Special case: bare `*` means \"all events\", which maps to\n * `<channel>:*` in Redis.\n */\n private toRedisPattern(pattern: string): string {\n return `${this.channel}:${pattern}`;\n }\n\n /**\n * Returns true if the pattern contains glob characters.\n */\n private isGlob(pattern: string): boolean {\n return pattern.includes('*') || pattern.includes('?') || pattern.includes('[');\n }\n}\n\nexport default RedisEventTransport;\n"],"mappings":";;;;;;AAoEA,SAAS,UAAU,OAA4B;AAC7C,QAAO,KAAK,UAAU,QAAQ,MAAM,UAAU;AAC5C,MAAI,iBAAiB,KAAM,QAAO,MAAM,aAAa;AACrD,SAAO;GACP;;AAGJ,SAAS,YAAY,KAA0B;AAC7C,QAAO,KAAK,MAAM,MAAM,KAAK,UAAU;AACrC,MAAI,QAAQ,eAAe,OAAO,UAAU,SAAU,QAAO,IAAI,KAAK,MAAM;AAC5E,SAAO;GACP;;AAOJ,IAAa,sBAAb,MAA2D;CACzD,AAAS,OAAO;;CAGhB,AAAQ;;CAGR,AAAQ;;CAGR,AAAQ;;CAGR,AAAQ;;CAGR,AAAQ;;CAGR,AAAQ,2BAAW,IAAI,KAAgC;;CAGvD,AAAQ,mBAAmB;CAE3B,YAAY,OAAkB,UAAsC,EAAE,EAAE;EACtE,MAAM,EAAE,UAAU,cAAc,oBAAoB,OAAO,SAAS,YAAY;AAEhF,OAAK,UAAU;AACf,OAAK,oBAAoB;AACzB,OAAK,SAAS;AAKd,OAAK,MAAM;AACX,OAAK,MAAM,MAAM,WAAW;;CAO9B,MAAM,QAAQ,OAAmC;EAC/C,MAAM,eAAe,GAAG,KAAK,QAAQ,GAAG,MAAM;AAC9C,QAAM,KAAK,IAAI,QAAQ,cAAc,UAAU,MAAM,CAAC;;CAOxD,MAAM,UAAU,SAAiB,SAA4C;AAC3E,OAAK,gBAAgB;EAErB,MAAM,eAAe,KAAK,eAAe,QAAQ;AAEjD,MAAI,CAAC,KAAK,SAAS,IAAI,aAAa,EAAE;AACpC,QAAK,SAAS,IAAI,8BAAc,IAAI,KAAK,CAAC;AAG1C,OAAI,KAAK,OAAO,aAAa,CAC3B,OAAM,KAAK,IAAI,WAAW,aAAa;OAEvC,OAAM,KAAK,IAAI,UAAU,aAAa;;AAI1C,OAAK,SAAS,IAAI,aAAa,CAAE,IAAI,QAAQ;AAG7C,eAAa;GACX,MAAM,MAAM,KAAK,SAAS,IAAI,aAAa;AAC3C,OAAI,IACF,KAAI,OAAO,QAAQ;;;CAazB,MAAM,QAAuB;AAC3B,OAAK,SAAS,OAAO;AAErB,MAAI,CAAC,KAAK,kBAIR,OAAM,QAAQ,IAAI,CAAC,KAAK,IAAI,MAAM,EAAE,KAAK,IAAI,MAAM,CAAC,CAAC;MAIrD,OAAM,KAAK,IAAI,MAAM;;;;;CAWzB,AAAQ,iBAAuB;AAC7B,MAAI,KAAK,iBAAkB;AAC3B,OAAK,mBAAmB;AAGxB,OAAK,IAAI,GAAG,aAAa,GAAG,SAAoB;GAC9C,MAAM,CAAC,gBAAgB,WAAW;AAClC,QAAK,SAAS,cAAc,QAAQ;IACpC;AAGF,OAAK,IAAI,GAAG,YAAY,GAAG,SAAoB;GAC7C,MAAM,CAAC,SAAS,WAAW;AAC3B,QAAK,SAAS,SAAS,QAAQ;IAC/B;;;;;CAMJ,AAAQ,SAAS,uBAA+B,KAAmB;EACjE,MAAM,WAAW,KAAK,SAAS,IAAI,sBAAsB;AACzD,MAAI,CAAC,YAAY,SAAS,SAAS,EAAG;EAEtC,IAAI;AACJ,MAAI;AACF,WAAQ,YAAY,IAAI;UAClB;AAGN;;AAGF,OAAK,MAAM,WAAW,SACpB,KAAI;GACF,MAAM,SAAS,QAAQ,MAAM;AAG7B,OAAI,UAAU,OAAQ,OAAyB,UAAU,WACvD,CAAC,OAAyB,OAAO,QAAQ;AACvC,SAAK,OAAO,MAAM,2CAA2C,MAAM,KAAK,IAAI,IAAI;KAChF;WAEG,KAAK;AACZ,QAAK,OAAO,MAAM,2CAA2C,MAAM,KAAK,IAAI,IAAI;;;;;;;;;;;;;CAetF,AAAQ,eAAe,SAAyB;AAC9C,SAAO,GAAG,KAAK,QAAQ,GAAG;;;;;CAM5B,AAAQ,OAAO,SAA0B;AACvC,SAAO,QAAQ,SAAS,IAAI,IAAI,QAAQ,SAAS,IAAI,IAAI,QAAQ,SAAS,IAAI"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//#region src/docs/externalPaths.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* External OpenAPI Path Provider
|
|
4
|
+
*
|
|
5
|
+
* Generic interface for injecting non-resource paths into the OpenAPI spec.
|
|
6
|
+
* Used by auth adapters (Better Auth, custom auth), third-party integrations,
|
|
7
|
+
* or any system that registers routes outside Arc's resource registry.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import type { ExternalOpenApiPaths } from '@classytic/arc/docs';
|
|
12
|
+
*
|
|
13
|
+
* const authPaths: ExternalOpenApiPaths = {
|
|
14
|
+
* paths: {
|
|
15
|
+
* '/api/auth/sign-in': {
|
|
16
|
+
* post: { summary: 'Sign in', requestBody: { ... } },
|
|
17
|
+
* },
|
|
18
|
+
* },
|
|
19
|
+
* securitySchemes: {
|
|
20
|
+
* cookieAuth: { type: 'apiKey', in: 'cookie', name: 'session_token' },
|
|
21
|
+
* },
|
|
22
|
+
* tags: [{ name: 'Authentication' }],
|
|
23
|
+
* };
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
/** Pre-built OpenAPI path fragments ready to merge into a spec */
|
|
27
|
+
interface ExternalOpenApiPaths {
|
|
28
|
+
/** OpenAPI path items keyed by path (e.g. '/api/auth/sign-in') */
|
|
29
|
+
paths: Record<string, Record<string, unknown>>;
|
|
30
|
+
/** Additional component schemas to merge into components.schemas */
|
|
31
|
+
schemas?: Record<string, Record<string, unknown>>;
|
|
32
|
+
/** Additional security scheme definitions to merge into components.securitySchemes */
|
|
33
|
+
securitySchemes?: Record<string, Record<string, unknown>>;
|
|
34
|
+
/** Additional tags for grouping operations */
|
|
35
|
+
tags?: Array<{
|
|
36
|
+
name: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
}>;
|
|
39
|
+
/**
|
|
40
|
+
* Additional security alternatives for Arc resource paths.
|
|
41
|
+
* Each item is OR'd with bearerAuth. Keys within the same object are AND'd.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* // "bearer OR (api-key AND org-header)"
|
|
45
|
+
* resourceSecurity: [{ apiKeyAuth: [], orgHeader: [] }]
|
|
46
|
+
*/
|
|
47
|
+
resourceSecurity?: Array<Record<string, string[]>>;
|
|
48
|
+
}
|
|
49
|
+
//#endregion
|
|
50
|
+
export { ExternalOpenApiPaths as t };
|
|
51
|
+
//# sourceMappingURL=externalPaths-DlINfKbP.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"externalPaths-DlINfKbP.d.mts","names":[],"sources":["../src/docs/externalPaths.ts"],"mappings":";;AA0BA;;;;;;;;;;;;;;;;;;;;;;;;UAAiB,oBAAA;EAQA;EANf,KAAA,EAAO,MAAA,SAAe,MAAA;EAetB;EAbA,OAAA,GAAU,MAAA,SAAe,MAAA;EAaA;EAXzB,eAAA,GAAkB,MAAA,SAAe,MAAA;EAWF;EAT/B,IAAA,GAAO,KAAA;IAAQ,IAAA;IAAc,WAAA;EAAA;;;;;;;;;EAS7B,gBAAA,GAAmB,KAAA,CAAM,MAAA;AAAA"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import "../elevation-B_2dRLVP.mjs";
|
|
2
|
+
import "../interface-Ch8HU9uM.mjs";
|
|
3
|
+
import "../types-aYB4V7uN.mjs";
|
|
4
|
+
import "../queryCachePlugin-7THaI5mt.mjs";
|
|
5
|
+
import "../eventPlugin-CTrLH3mt.mjs";
|
|
6
|
+
import "../errorHandler-BbcgBmIH.mjs";
|
|
7
|
+
import { a as CustomPluginAuthOption, c as RawBodyOptions, i as CustomAuthenticatorOption, l as UnderPressureOptions, n as BetterAuthOption, o as JwtAuthOption, r as CreateAppOptions, s as MultipartOptions, t as AuthOption } from "../types-CIgB7UUl.mjs";
|
|
8
|
+
import { FastifyInstance } from "fastify";
|
|
9
|
+
|
|
10
|
+
//#region src/factory/createApp.d.ts
|
|
11
|
+
/**
|
|
12
|
+
* Create a production-ready Fastify application with Arc framework
|
|
13
|
+
*
|
|
14
|
+
* Security plugins are enabled by default (opt-out):
|
|
15
|
+
* - helmet (security headers)
|
|
16
|
+
* - cors (cross-origin requests)
|
|
17
|
+
* - rateLimit (DDoS protection)
|
|
18
|
+
* - underPressure (health monitoring)
|
|
19
|
+
*
|
|
20
|
+
* Note: Compression is not included due to known Fastify 5 issues.
|
|
21
|
+
* Use a reverse proxy (Nginx, Caddy) or CDN for compression.
|
|
22
|
+
*
|
|
23
|
+
* @param options - Application configuration
|
|
24
|
+
* @returns Configured Fastify instance
|
|
25
|
+
*/
|
|
26
|
+
declare function createApp(options: CreateAppOptions): Promise<FastifyInstance>;
|
|
27
|
+
/**
|
|
28
|
+
* Quick factory for common scenarios
|
|
29
|
+
*/
|
|
30
|
+
declare const ArcFactory: {
|
|
31
|
+
/**
|
|
32
|
+
* Create production app with strict security
|
|
33
|
+
*/
|
|
34
|
+
production(options: Omit<CreateAppOptions, "preset">): Promise<FastifyInstance>;
|
|
35
|
+
/**
|
|
36
|
+
* Create development app with relaxed security
|
|
37
|
+
*/
|
|
38
|
+
development(options: Omit<CreateAppOptions, "preset">): Promise<FastifyInstance>;
|
|
39
|
+
/**
|
|
40
|
+
* Create testing app with minimal setup
|
|
41
|
+
*/
|
|
42
|
+
testing(options: Omit<CreateAppOptions, "preset">): Promise<FastifyInstance>;
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/factory/presets.d.ts
|
|
46
|
+
/**
|
|
47
|
+
* Production preset - strict security, performance optimized
|
|
48
|
+
*/
|
|
49
|
+
declare const productionPreset: Partial<CreateAppOptions>;
|
|
50
|
+
/**
|
|
51
|
+
* Development preset - relaxed security, verbose logging
|
|
52
|
+
*/
|
|
53
|
+
declare const developmentPreset: Partial<CreateAppOptions>;
|
|
54
|
+
/**
|
|
55
|
+
* Testing preset - minimal setup, fast startup
|
|
56
|
+
*/
|
|
57
|
+
declare const testingPreset: Partial<CreateAppOptions>;
|
|
58
|
+
/**
|
|
59
|
+
* Get preset by name
|
|
60
|
+
*/
|
|
61
|
+
declare function getPreset(name: 'production' | 'development' | 'testing' | 'edge'): Partial<CreateAppOptions>;
|
|
62
|
+
//#endregion
|
|
63
|
+
export { ArcFactory, type AuthOption, type BetterAuthOption, type CreateAppOptions, type CustomAuthenticatorOption, type CustomPluginAuthOption, type JwtAuthOption, type MultipartOptions, type RawBodyOptions, type UnderPressureOptions, createApp, developmentPreset, getPreset, productionPreset, testingPreset };
|
|
64
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/factory/createApp.ts","../../src/factory/presets.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;iBAkHsB,SAAA,CAAU,OAAA,EAAS,gBAAA,GAAmB,OAAA,CAAQ,eAAA;;;;cAievD,UAAA;EAkBsE;;;sBAdvD,IAAA,CAAK,gBAAA,cAA8B,OAAA,CAAQ,eAAA;ECzkB1D;;;uBDglBgB,IAAA,CAAK,gBAAA,cAA8B,OAAA,CAAQ,eAAA;EChlBjB;AA8DvD;;mBDyhByB,IAAA,CAAK,gBAAA,cAA8B,OAAA,CAAQ,eAAA;AAAA;;;;AAnfpE;;cCpGa,gBAAA,EAAkB,OAAA,CAAQ,gBAAA;;;;cA8D1B,iBAAA,EAAmB,OAAA,CAAQ,gBAAA;;;;cA6C3B,aAAA,EAAe,OAAA,CAAQ,gBAAA;;;;iBAiEpB,SAAA,CAAU,IAAA,sDAA0D,OAAA,CAAQ,gBAAA"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { s as RequestScope } from "./elevation-B_2dRLVP.mjs";
|
|
2
|
+
import { b as IControllerResponse, x as IRequestContext, y as IController } from "./interface-Ch8HU9uM.mjs";
|
|
3
|
+
import { t as PermissionCheck } from "./types-aYB4V7uN.mjs";
|
|
4
|
+
import { CrudController, CrudRouterOptions, FastifyWithDecorators, RequestContext, RequestWithExtras } from "./types/index.mjs";
|
|
5
|
+
import { FastifyInstance, FastifyReply, FastifyRequest, RouteHandlerMethod } from "fastify";
|
|
6
|
+
|
|
7
|
+
//#region src/core/createCrudRouter.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Create CRUD routes for a controller
|
|
10
|
+
*
|
|
11
|
+
* @param fastify - Fastify instance with Arc decorators
|
|
12
|
+
* @param controller - CRUD controller with handler methods
|
|
13
|
+
* @param options - Router configuration
|
|
14
|
+
*/
|
|
15
|
+
declare function createCrudRouter<TDoc = unknown>(fastify: FastifyWithDecorators, controller: CrudController<TDoc> | undefined, options?: CrudRouterOptions): void;
|
|
16
|
+
/**
|
|
17
|
+
* Create permission middleware from PermissionCheck
|
|
18
|
+
* Useful for custom route registration
|
|
19
|
+
*/
|
|
20
|
+
declare function createPermissionMiddleware(permission: PermissionCheck, resourceName: string, action: string): RouteHandlerMethod | null;
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/core/createActionRouter.d.ts
|
|
23
|
+
/**
|
|
24
|
+
* Action handler function
|
|
25
|
+
* @param id - Resource ID
|
|
26
|
+
* @param data - Action-specific data from request body
|
|
27
|
+
* @param req - Full Fastify request object
|
|
28
|
+
* @returns Action result (will be wrapped in success response)
|
|
29
|
+
*/
|
|
30
|
+
type ActionHandler<TData = any, TResult = any> = (id: string, data: TData, req: RequestWithExtras) => Promise<TResult>;
|
|
31
|
+
/**
|
|
32
|
+
* Action router configuration
|
|
33
|
+
*/
|
|
34
|
+
interface ActionRouterConfig {
|
|
35
|
+
/**
|
|
36
|
+
* OpenAPI tag for grouping routes
|
|
37
|
+
*/
|
|
38
|
+
tag?: string;
|
|
39
|
+
/**
|
|
40
|
+
* Action handlers map
|
|
41
|
+
* @example { approve: (id, data, req) => service.approve(id), ... }
|
|
42
|
+
*/
|
|
43
|
+
actions: Record<string, ActionHandler>;
|
|
44
|
+
/**
|
|
45
|
+
* Per-action permission checks (PermissionCheck functions)
|
|
46
|
+
* @example { approve: requireRoles(['admin', 'manager']), cancel: requireRoles(['admin']) }
|
|
47
|
+
*/
|
|
48
|
+
actionPermissions?: Record<string, PermissionCheck>;
|
|
49
|
+
/**
|
|
50
|
+
* Per-action JSON schema for body validation
|
|
51
|
+
* @example { dispatch: { transport: { type: 'object' } } }
|
|
52
|
+
*/
|
|
53
|
+
actionSchemas?: Record<string, Record<string, any>>;
|
|
54
|
+
/**
|
|
55
|
+
* Global permission check applied to all actions (if action-specific not defined)
|
|
56
|
+
*/
|
|
57
|
+
globalAuth?: PermissionCheck;
|
|
58
|
+
/**
|
|
59
|
+
* Optional idempotency service
|
|
60
|
+
* If provided, will handle idempotency-key header
|
|
61
|
+
*/
|
|
62
|
+
idempotencyService?: IdempotencyService;
|
|
63
|
+
/**
|
|
64
|
+
* Custom error handler for action execution failures
|
|
65
|
+
* @param error - The error thrown by action handler
|
|
66
|
+
* @param action - The action that failed
|
|
67
|
+
* @param id - The resource ID
|
|
68
|
+
* @returns Status code and error response
|
|
69
|
+
*/
|
|
70
|
+
onError?: (error: Error, action: string, id: string) => {
|
|
71
|
+
statusCode: number;
|
|
72
|
+
error: string;
|
|
73
|
+
code?: string;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Idempotency service interface
|
|
78
|
+
* Apps can provide their own implementation
|
|
79
|
+
*/
|
|
80
|
+
interface IdempotencyService {
|
|
81
|
+
check(key: string, payload: any): Promise<{
|
|
82
|
+
isNew: boolean;
|
|
83
|
+
existingResult?: any;
|
|
84
|
+
}>;
|
|
85
|
+
complete(key: string | undefined, result: any): Promise<void>;
|
|
86
|
+
fail(key: string | undefined, error: Error): Promise<void>;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Create action-based state transition endpoint
|
|
90
|
+
*
|
|
91
|
+
* Registers: POST /:id/action
|
|
92
|
+
* Body: { action: string, ...actionData }
|
|
93
|
+
*
|
|
94
|
+
* @param fastify - Fastify instance
|
|
95
|
+
* @param config - Action router configuration
|
|
96
|
+
*/
|
|
97
|
+
declare function createActionRouter(fastify: FastifyInstance, config: ActionRouterConfig): void;
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/constants.d.ts
|
|
100
|
+
/**
|
|
101
|
+
* Arc Framework Constants — Single Source of Truth
|
|
102
|
+
*
|
|
103
|
+
* Every default value, magic string, and framework constant lives here.
|
|
104
|
+
* Import from this module instead of hard-coding values inline.
|
|
105
|
+
*
|
|
106
|
+
* All exported values are deeply frozen (Object.freeze) to prevent
|
|
107
|
+
* accidental mutation at runtime — inspired by Go's const blocks
|
|
108
|
+
* and Rust's immutable-by-default philosophy.
|
|
109
|
+
*/
|
|
110
|
+
/** Standard CRUD operation names */
|
|
111
|
+
declare const CRUD_OPERATIONS: readonly ["list", "get", "create", "update", "delete"];
|
|
112
|
+
type CrudOperation = (typeof CRUD_OPERATIONS)[number];
|
|
113
|
+
/** Mutation operations that emit events */
|
|
114
|
+
declare const MUTATION_OPERATIONS: readonly ["create", "update", "delete"];
|
|
115
|
+
type MutationOperation = (typeof MUTATION_OPERATIONS)[number];
|
|
116
|
+
/** Lifecycle hook phases */
|
|
117
|
+
declare const HOOK_PHASES: readonly ["before", "around", "after"];
|
|
118
|
+
type HookPhase = (typeof HOOK_PHASES)[number];
|
|
119
|
+
/** Hook operations (superset of CRUD — includes 'read' alias for 'get') */
|
|
120
|
+
declare const HOOK_OPERATIONS: readonly ["create", "update", "delete", "read", "list"];
|
|
121
|
+
type HookOperation = (typeof HOOK_OPERATIONS)[number];
|
|
122
|
+
/** Default items per page */
|
|
123
|
+
declare const DEFAULT_LIMIT: 20;
|
|
124
|
+
/** Maximum items per page (framework-wide ceiling) */
|
|
125
|
+
declare const DEFAULT_MAX_LIMIT: 1000;
|
|
126
|
+
/** Default sort field (descending creation date) */
|
|
127
|
+
declare const DEFAULT_SORT: "-createdAt";
|
|
128
|
+
/** Default primary key field name */
|
|
129
|
+
declare const DEFAULT_ID_FIELD: "_id";
|
|
130
|
+
/** Default multi-tenant scoping field */
|
|
131
|
+
declare const DEFAULT_TENANT_FIELD: "organizationId";
|
|
132
|
+
/** Default HTTP method for update routes */
|
|
133
|
+
declare const DEFAULT_UPDATE_METHOD: "PATCH";
|
|
134
|
+
/** System-managed fields that cannot be set via request body */
|
|
135
|
+
declare const SYSTEM_FIELDS: readonly ["_id", "__v", "createdAt", "updatedAt", "deletedAt"];
|
|
136
|
+
/** Maximum regex pattern length (ReDoS mitigation) */
|
|
137
|
+
declare const MAX_REGEX_LENGTH: 200;
|
|
138
|
+
/** Maximum search query length */
|
|
139
|
+
declare const MAX_SEARCH_LENGTH: 200;
|
|
140
|
+
/** Maximum filter nesting depth (prevents filter bombs) */
|
|
141
|
+
declare const MAX_FILTER_DEPTH: 10;
|
|
142
|
+
/**
|
|
143
|
+
* Query parameters consumed by the framework — never treated as filters.
|
|
144
|
+
* Shared by all query parsers (Arc built-in, Prisma, custom).
|
|
145
|
+
*/
|
|
146
|
+
declare const RESERVED_QUERY_PARAMS: Readonly<Set<string>>;
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/core/fastifyAdapter.d.ts
|
|
149
|
+
/**
|
|
150
|
+
* Create IRequestContext from Fastify request
|
|
151
|
+
*
|
|
152
|
+
* Extracts framework-agnostic context from Fastify-specific request object
|
|
153
|
+
*/
|
|
154
|
+
declare function createRequestContext(req: FastifyRequest): IRequestContext;
|
|
155
|
+
/**
|
|
156
|
+
* Get typed auth context from an IRequestContext.
|
|
157
|
+
* Use this in controller overrides to access request context.
|
|
158
|
+
*
|
|
159
|
+
* For org scope, use `getControllerScope(req)` instead.
|
|
160
|
+
*/
|
|
161
|
+
declare function getControllerContext(req: IRequestContext): RequestContext;
|
|
162
|
+
/**
|
|
163
|
+
* Get request scope from an IRequestContext.
|
|
164
|
+
* Returns the RequestScope set by auth adapters.
|
|
165
|
+
*/
|
|
166
|
+
declare function getControllerScope(req: IRequestContext): RequestScope;
|
|
167
|
+
/**
|
|
168
|
+
* Send IControllerResponse via Fastify reply
|
|
169
|
+
*
|
|
170
|
+
* Converts framework-agnostic response to Fastify response
|
|
171
|
+
* Applies field masking if specified in request
|
|
172
|
+
*/
|
|
173
|
+
declare function sendControllerResponse<T>(reply: FastifyReply, response: IControllerResponse<T>, request?: FastifyRequest): void;
|
|
174
|
+
/**
|
|
175
|
+
* Create Fastify route handler from IController method
|
|
176
|
+
*
|
|
177
|
+
* Wraps framework-agnostic controller method in Fastify-specific handler
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* const controller = new BaseController(repository);
|
|
182
|
+
*
|
|
183
|
+
* // Create Fastify handler
|
|
184
|
+
* const listHandler = createFastifyHandler(controller.list.bind(controller));
|
|
185
|
+
*
|
|
186
|
+
* // Register route
|
|
187
|
+
* fastify.get('/products', listHandler);
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
declare function createFastifyHandler<T>(controllerMethod: (req: IRequestContext) => Promise<IControllerResponse<T>>): (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
191
|
+
/**
|
|
192
|
+
* Create Fastify adapters for all CRUD methods of an IController
|
|
193
|
+
*
|
|
194
|
+
* Returns Fastify-compatible handlers for each CRUD operation
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* ```typescript
|
|
198
|
+
* const controller = new BaseController(repository);
|
|
199
|
+
* const handlers = createCrudHandlers(controller);
|
|
200
|
+
*
|
|
201
|
+
* fastify.get('/', handlers.list);
|
|
202
|
+
* fastify.get('/:id', handlers.get);
|
|
203
|
+
* fastify.post('/', handlers.create);
|
|
204
|
+
* fastify.patch('/:id', handlers.update);
|
|
205
|
+
* fastify.delete('/:id', handlers.delete);
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
declare function createCrudHandlers<TDoc>(controller: IController<TDoc>): {
|
|
209
|
+
list: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
210
|
+
get: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
211
|
+
create: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
212
|
+
update: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
213
|
+
delete: (req: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
214
|
+
};
|
|
215
|
+
//#endregion
|
|
216
|
+
export { createCrudRouter as A, MutationOperation as C, ActionRouterConfig as D, ActionHandler as E, IdempotencyService as O, MUTATION_OPERATIONS as S, SYSTEM_FIELDS as T, HookOperation as _, getControllerScope as a, MAX_REGEX_LENGTH as b, CrudOperation as c, DEFAULT_MAX_LIMIT as d, DEFAULT_SORT as f, HOOK_PHASES as g, HOOK_OPERATIONS as h, getControllerContext as i, createPermissionMiddleware as j, createActionRouter as k, DEFAULT_ID_FIELD as l, DEFAULT_UPDATE_METHOD as m, createFastifyHandler as n, sendControllerResponse as o, DEFAULT_TENANT_FIELD as p, createRequestContext as r, CRUD_OPERATIONS as s, createCrudHandlers as t, DEFAULT_LIMIT as u, HookPhase as v, RESERVED_QUERY_PARAMS as w, MAX_SEARCH_LENGTH as x, MAX_FILTER_DEPTH as y };
|
|
217
|
+
//# sourceMappingURL=fastifyAdapter-BkrGrlFi.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fastifyAdapter-BkrGrlFi.d.mts","names":[],"sources":["../src/core/createCrudRouter.ts","../src/core/createActionRouter.ts","../src/constants.ts","../src/core/fastifyAdapter.ts"],"mappings":";;;;;;;;;;;;;;iBA4WgB,gBAAA,gBAAA,CACd,OAAA,EAAS,qBAAA,EACT,UAAA,EAAY,cAAA,CAAe,IAAA,eAC3B,OAAA,GAAS,iBAAA;;;;AAmOX;iBAAgB,0BAAA,CACd,UAAA,EAAY,eAAA,EACZ,YAAA,UACA,MAAA,WACC,kBAAA;;;;;;;;;;KCjiBS,aAAA,gCACV,EAAA,UACA,IAAA,EAAM,KAAA,EACN,GAAA,EAAK,iBAAA,KACF,OAAA,CAAQ,OAAA;;;;UAKI,kBAAA;EALZ;;;EASH,GAAA;EAJe;;;;EAUf,OAAA,EAAS,MAAA,SAAe,aAAA;EAMW;;;;EAAnC,iBAAA,GAAoB,MAAA,SAAe,eAAA;EAiBd;;;;EAXrB,aAAA,GAAgB,MAAA,SAAe,MAAA;EAZ/B;;;EAiBA,UAAA,GAAa,eAAA;EAXO;;;;EAiBpB,kBAAA,GAAqB,kBAAA;EANrB;;;;;;;EAeA,OAAA,IACE,KAAA,EAAO,KAAA,EACP,MAAA,UACA,EAAA;IACK,UAAA;IAAoB,KAAA;IAAe,IAAA;EAAA;AAAA;;AAO5C;;;UAAiB,kBAAA;EACf,KAAA,CAAM,GAAA,UAAa,OAAA,QAAe,OAAA;IAAU,KAAA;IAAgB,cAAA;EAAA;EAC5D,QAAA,CAAS,GAAA,sBAAyB,MAAA,QAAc,OAAA;EAChD,IAAA,CAAK,GAAA,sBAAyB,KAAA,EAAO,KAAA,GAAQ,OAAA;AAAA;;;;;;;;;;iBAY/B,kBAAA,CAAmB,OAAA,EAAS,eAAA,EAAiB,MAAA,EAAQ,kBAAA;;;;;;;;;;AD0OrE;;;;cE5Va,eAAA;AAAA,KAGD,aAAA,WAAwB,eAAA;;cAGvB,mBAAA;AAAA,KAGD,iBAAA,WAA4B,mBAAA;;cAO3B,WAAA;AAAA,KAGD,SAAA,WAAoB,WAAA;;cAGnB,eAAA;AAAA,KAGD,aAAA,WAAwB,eAAA;;cAOvB,aAAA;;cAGA,iBAAA;AF+hBb;AAAA,cE5hBa,YAAA;;cAOA,gBAAA;;cAGA,oBAAA;;cAGA,qBAAA;;cAGA,aAAA;;cASA,gBAAA;;cAGA,iBAAA;AD7Bb;AAAA,cCgCa,gBAAA;;;;;cAUA,qBAAA,EAAqB,QAAA,CAAA,GAAA;;;;;;;;iBCAlB,oBAAA,CAAqB,GAAA,EAAK,cAAA,GAAiB,eAAA;;;;;;;iBA8D3C,oBAAA,CAAqB,GAAA,EAAK,eAAA,GAAkB,cAAA;;;;;iBAQ5C,kBAAA,CAAmB,GAAA,EAAK,eAAA,GAAkB,YAAA;;;;;;;iBAyC1C,sBAAA,GAAA,CACd,KAAA,EAAO,YAAA,EACP,QAAA,EAAU,mBAAA,CAAoB,CAAA,GAC9B,OAAA,GAAU,cAAA;;;;;;;AF5JZ;;;;;;;;;;iBEoQgB,oBAAA,GAAA,CACd,gBAAA,GAAmB,GAAA,EAAK,eAAA,KAAoB,OAAA,CAAQ,mBAAA,CAAoB,CAAA,MAE1D,GAAA,EAAK,cAAA,EAAgB,KAAA,EAAO,YAAA,KAAe,OAAA;;;;;;;;;;AF9P3D;;;;;;;;iBEsRgB,kBAAA,MAAA,CAAyB,UAAA,EAAY,WAAA,CAAY,IAAA;cAxB5C,cAAA,EAAc,KAAA,EAAS,YAAA,KAAe,OAAA;aAAtC,cAAA,EAAc,KAAA,EAAS,YAAA,KAAe,OAAA;gBAAtC,cAAA,EAAc,KAAA,EAAS,YAAA,KAAe,OAAA;gBAAtC,cAAA,EAAc,KAAA,EAAS,YAAA,KAAe,OAAA;gBAAtC,cAAA,EAAc,KAAA,EAAS,YAAA,KAAe,OAAA;AAAA"}
|