@classytic/arc 2.3.0 → 2.4.1
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 +187 -18
- package/bin/arc.js +11 -3
- package/dist/BaseController-CkM5dUh_.mjs +1031 -0
- package/dist/{EventTransport-BkUDYZEb.d.mts → EventTransport-wc5hSLik.d.mts} +1 -1
- package/dist/{HookSystem-BsGV-j2l.mjs → HookSystem-COkyWztM.mjs} +2 -3
- package/dist/{ResourceRegistry-7Ic20ZMw.mjs → ResourceRegistry-DeCIFlix.mjs} +8 -5
- package/dist/adapters/index.d.mts +3 -5
- package/dist/adapters/index.mjs +2 -3
- package/dist/{prisma-DJbMt3yf.mjs → adapters-DTC4Ug66.mjs} +45 -12
- package/dist/audit/index.d.mts +4 -7
- package/dist/audit/index.mjs +2 -29
- package/dist/audit/mongodb.d.mts +1 -4
- package/dist/audit/mongodb.mjs +2 -3
- package/dist/auth/index.d.mts +7 -9
- package/dist/auth/index.mjs +65 -63
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/auth/redis-session.mjs +1 -2
- package/dist/{betterAuthOpenApi-DjWDddNc.mjs → betterAuthOpenApi-lz0IRbXJ.mjs} +4 -6
- package/dist/cache/index.d.mts +23 -23
- package/dist/cache/index.mjs +4 -6
- package/dist/{caching-GSDJcA6-.mjs → caching-BSXB-Xr7.mjs} +2 -24
- package/dist/chunk-BpYLSNr0.mjs +14 -0
- package/dist/circuitBreaker-BOBOpN2w.mjs +284 -0
- package/dist/circuitBreaker-JP2GdJ4b.d.mts +206 -0
- package/dist/cli/commands/describe.mjs +24 -7
- package/dist/cli/commands/docs.mjs +6 -7
- package/dist/cli/commands/doctor.d.mts +10 -0
- package/dist/cli/commands/doctor.mjs +156 -0
- package/dist/cli/commands/generate.mjs +66 -17
- package/dist/cli/commands/init.mjs +315 -45
- package/dist/cli/commands/introspect.mjs +2 -4
- package/dist/cli/index.d.mts +1 -10
- package/dist/cli/index.mjs +4 -153
- package/dist/{constants-DdXFXQtN.mjs → constants-Cxde4rpC.mjs} +1 -2
- package/dist/core/index.d.mts +3 -5
- package/dist/core/index.mjs +5 -4
- package/dist/core-C1XCMtqM.mjs +185 -0
- package/dist/{createApp-CgKOPhA4.mjs → createApp-ByWNRsZj.mjs} +64 -35
- package/dist/{defineResource-DWbpJYtm.mjs → defineResource-D9aY5Cy6.mjs} +108 -1157
- package/dist/discovery/index.mjs +37 -5
- package/dist/docs/index.d.mts +6 -9
- package/dist/docs/index.mjs +3 -21
- package/dist/dynamic/index.d.mts +93 -0
- package/dist/dynamic/index.mjs +122 -0
- package/dist/{elevation-DSTbVvYj.mjs → elevation-BEdACOLB.mjs} +5 -36
- package/dist/{elevation-DGo5shaX.d.mts → elevation-Ca_yveIO.d.mts} +41 -7
- package/dist/{errorHandler-C3GY3_ow.mjs → errorHandler--zp54tGc.mjs} +3 -5
- package/dist/errorHandler-Do4vVQ1f.d.mts +139 -0
- package/dist/{errors-DBANPbGr.mjs → errors-rxhfP7Hf.mjs} +1 -2
- package/dist/{eventPlugin-BEOvaDqo.mjs → eventPlugin-Ba00swHF.mjs} +25 -27
- package/dist/{eventPlugin-H6wDDjGO.d.mts → eventPlugin-iGrSEmwJ.d.mts} +105 -5
- package/dist/events/index.d.mts +72 -7
- package/dist/events/index.mjs +216 -4
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +19 -7
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/events/transports/redis.mjs +3 -4
- package/dist/factory/index.d.mts +23 -9
- package/dist/factory/index.mjs +48 -3
- package/dist/{fields-Bi_AVKSo.d.mts → fields-DFwdaWCq.d.mts} +1 -1
- package/dist/{fields-CTd_CrKr.mjs → fields-ipsbIRPK.mjs} +1 -2
- package/dist/hooks/index.d.mts +1 -3
- package/dist/hooks/index.mjs +2 -3
- package/dist/idempotency/index.d.mts +5 -5
- package/dist/idempotency/index.mjs +3 -7
- package/dist/idempotency/mongodb.d.mts +1 -1
- package/dist/idempotency/mongodb.mjs +4 -5
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +2 -5
- package/dist/{fastifyAdapter-6b_eRDBw.d.mts → index-BL8CaQih.d.mts} +56 -57
- package/dist/index-Diqcm14c.d.mts +369 -0
- package/dist/{prisma-Dy5S5F5i.d.mts → index-yhxyjqNb.d.mts} +4 -5
- package/dist/index.d.mts +100 -105
- package/dist/index.mjs +85 -58
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +8 -4
- package/dist/integrations/index.d.mts +4 -2
- package/dist/integrations/index.mjs +1 -1
- package/dist/integrations/jobs.d.mts +2 -2
- package/dist/integrations/jobs.mjs +63 -14
- package/dist/integrations/mcp/index.d.mts +219 -0
- package/dist/integrations/mcp/index.mjs +572 -0
- package/dist/integrations/mcp/testing.d.mts +53 -0
- package/dist/integrations/mcp/testing.mjs +104 -0
- package/dist/integrations/streamline.mjs +39 -19
- package/dist/integrations/webhooks.d.mts +56 -0
- package/dist/integrations/webhooks.mjs +139 -0
- package/dist/integrations/websocket-redis.d.mts +46 -0
- package/dist/integrations/websocket-redis.mjs +50 -0
- package/dist/integrations/websocket.d.mts +68 -2
- package/dist/integrations/websocket.mjs +96 -13
- package/dist/{interface-CSNjltAc.d.mts → interface-B4awm1RJ.d.mts} +2 -2
- package/dist/interface-DGmPxakH.d.mts +2213 -0
- package/dist/{keys-DhqDRxv3.mjs → keys-qcD-TVJl.mjs} +3 -4
- package/dist/{logger-ByrvQWZO.mjs → logger-Dz3j1ItV.mjs} +2 -4
- package/dist/{memory-B2v7KrCB.mjs → memory-Cb_7iy9e.mjs} +2 -4
- package/dist/metrics-Csh4nsvv.mjs +224 -0
- package/dist/migrations/index.mjs +3 -7
- package/dist/{mongodb-DNKEExbf.mjs → mongodb-BuQ7fNTg.mjs} +1 -4
- package/dist/{mongodb-ClykrfGo.d.mts → mongodb-CUpYfxfD.d.mts} +2 -3
- package/dist/{mongodb-Dg8O_gvd.d.mts → mongodb-bga9AbkD.d.mts} +2 -2
- package/dist/{openapi-9nB_kiuR.mjs → openapi-CBmZ6EQN.mjs} +4 -21
- package/dist/org/index.d.mts +12 -14
- package/dist/org/index.mjs +92 -119
- package/dist/org/types.d.mts +2 -2
- package/dist/org/types.mjs +1 -1
- package/dist/permissions/index.d.mts +4 -278
- package/dist/permissions/index.mjs +4 -579
- package/dist/permissions-CA5zg0yK.mjs +751 -0
- package/dist/plugins/index.d.mts +104 -107
- package/dist/plugins/index.mjs +203 -313
- package/dist/plugins/response-cache.mjs +4 -69
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +24 -11
- package/dist/{pluralize-CM-jZg7p.mjs → pluralize-CcT6qF0a.mjs} +12 -13
- package/dist/policies/index.d.mts +2 -2
- package/dist/policies/index.mjs +80 -83
- package/dist/presets/index.d.mts +26 -19
- package/dist/presets/index.mjs +2 -142
- package/dist/presets/multiTenant.d.mts +1 -4
- package/dist/presets/multiTenant.mjs +4 -6
- package/dist/presets-C9QXJV1u.mjs +422 -0
- package/dist/{queryCachePlugin-B6R0d4av.mjs → queryCachePlugin-ClosZdNS.mjs} +6 -27
- package/dist/{queryCachePlugin-Q6SYuHZ6.d.mts → queryCachePlugin-DcmETvcB.d.mts} +3 -3
- package/dist/queryParser-CgCtsjti.mjs +352 -0
- package/dist/{redis-UwjEp8Ea.d.mts → redis-CQ5YxMC5.d.mts} +2 -2
- package/dist/{redis-stream-CBg0upHI.d.mts → redis-stream-BW9UKLZM.d.mts} +9 -2
- package/dist/registry/index.d.mts +1 -4
- package/dist/registry/index.mjs +3 -4
- package/dist/{introspectionPlugin-B3JkrjwU.mjs → registry-I-ogLgL9.mjs} +1 -8
- package/dist/{requestContext-xi6OKBL-.mjs → requestContext-DYtmNpm5.mjs} +1 -3
- package/dist/resourceToTools-B6ZN9Ing.mjs +489 -0
- package/dist/rpc/index.d.mts +90 -0
- package/dist/rpc/index.mjs +248 -0
- package/dist/{schemaConverter-Dtg0Kt9T.mjs → schemaConverter-DjzHpFam.mjs} +1 -2
- package/dist/schemas/index.d.mts +30 -30
- package/dist/schemas/index.mjs +2 -4
- package/dist/scope/index.d.mts +13 -2
- package/dist/scope/index.mjs +18 -5
- package/dist/{sessionManager-D_iEHjQl.d.mts → sessionManager-wbkYj2HL.d.mts} +2 -2
- package/dist/{sse-DkqQ1uxb.mjs → sse-BkViJPlT.mjs} +4 -25
- package/dist/testing/index.d.mts +551 -567
- package/dist/testing/index.mjs +1744 -1799
- package/dist/{tracing-8CEbhF0w.d.mts → tracing-bz_U4EM1.d.mts} +6 -1
- package/dist/{typeGuards-DwxA1t_L.mjs → typeGuards-Cj5Rgvlg.mjs} +1 -2
- package/dist/types/index.d.mts +4 -946
- package/dist/types/index.mjs +2 -4
- package/dist/types-BJmgxNbF.d.mts +275 -0
- package/dist/{types-RLkFVgaw.d.mts → types-BNUccdcf.d.mts} +2 -2
- package/dist/{types-Beqn1Un7.mjs → types-C6TQjtdi.mjs} +30 -2
- package/dist/{types-tKwaViYB.d.mts → types-Dt0-AI6E.d.mts} +68 -27
- package/dist/{types-DelU6kln.mjs → types-ZUu_h0jp.mjs} +1 -2
- package/dist/utils/index.d.mts +254 -351
- package/dist/utils/index.mjs +7 -6
- package/dist/utils-Dc0WhlIl.mjs +594 -0
- package/dist/versioning-BzfeHmhj.mjs +37 -0
- package/package.json +44 -10
- package/skills/arc/SKILL.md +506 -0
- package/skills/arc/references/auth.md +250 -0
- package/skills/arc/references/events.md +272 -0
- package/skills/arc/references/integrations.md +385 -0
- package/skills/arc/references/mcp.md +386 -0
- package/skills/arc/references/production.md +610 -0
- package/skills/arc/references/testing.md +183 -0
- package/dist/audited-CGdLiSlE.mjs +0 -140
- package/dist/chunk-C7Uep-_p.mjs +0 -20
- package/dist/circuitBreaker-CSS2VvL6.mjs +0 -1109
- package/dist/errorHandler-CW3OOeYq.d.mts +0 -72
- package/dist/interface-BtdYtQUA.d.mts +0 -1114
- package/dist/presets-BTeYbw7h.d.mts +0 -57
- package/dist/presets-CeFtfDR8.mjs +0 -119
- /package/dist/{errors-DAWRdiYP.d.mts → errors-CPpvPHT0.d.mts} +0 -0
- /package/dist/{externalPaths-SyPF2tgK.d.mts → externalPaths-DpO-s7r8.d.mts} +0 -0
- /package/dist/{interface-DTbsvIWe.d.mts → interface-D_BWALyZ.d.mts} +0 -0
package/dist/cache/index.d.mts
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
|
-
import { i as CacheStore, n as CacheSetOptions, r as CacheStats, t as CacheLogger } from "../interface-
|
|
2
|
-
import { a as CacheEnvelope, c as QueryCache, i as queryCachePlugin, l as QueryCacheConfig, n as QueryCacheDefaults, o as CacheResult, r as QueryCachePluginOptions, s as CacheStatus, t as CrossResourceRule } from "../queryCachePlugin-
|
|
1
|
+
import { i as CacheStore, n as CacheSetOptions, r as CacheStats, t as CacheLogger } from "../interface-D_BWALyZ.mjs";
|
|
2
|
+
import { a as CacheEnvelope, c as QueryCache, i as queryCachePlugin, l as QueryCacheConfig, n as QueryCacheDefaults, o as CacheResult, r as QueryCachePluginOptions, s as CacheStatus, t as CrossResourceRule } from "../queryCachePlugin-DcmETvcB.mjs";
|
|
3
3
|
|
|
4
|
+
//#region src/cache/keys.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Cache Key Utilities
|
|
7
|
+
*
|
|
8
|
+
* Deterministic, scope-safe key generation for QueryCache.
|
|
9
|
+
* Keys include resource version, operation, params hash, and user/org scope
|
|
10
|
+
* to ensure multi-tenant isolation and O(1) version-based invalidation.
|
|
11
|
+
*/
|
|
12
|
+
/** Build a deterministic cache key for a query */
|
|
13
|
+
declare function buildQueryKey(resource: string, operation: string, resourceVersion: number, params: Record<string, unknown>, userId?: string, orgId?: string): string;
|
|
14
|
+
/** Resource version key — stored in CacheStore, bumped on mutations */
|
|
15
|
+
declare function versionKey(resource: string): string;
|
|
16
|
+
/** Tag version key — stored in CacheStore, bumped on cross-resource invalidation */
|
|
17
|
+
declare function tagVersionKey(tag: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Stable hash for query params.
|
|
20
|
+
* Sorts keys recursively, serializes to JSON, then applies djb2 hash.
|
|
21
|
+
* Returns hex string.
|
|
22
|
+
*/
|
|
23
|
+
declare function hashParams(params: Record<string, unknown>): string;
|
|
24
|
+
//#endregion
|
|
4
25
|
//#region src/cache/memory.d.ts
|
|
5
26
|
interface MemoryCacheStoreOptions {
|
|
6
27
|
/** Default TTL in milliseconds (default: 60_000) */
|
|
@@ -121,25 +142,4 @@ declare class RedisCacheStore<TValue = unknown> implements CacheStore<TValue> {
|
|
|
121
142
|
private withPrefix;
|
|
122
143
|
}
|
|
123
144
|
//#endregion
|
|
124
|
-
//#region src/cache/keys.d.ts
|
|
125
|
-
/**
|
|
126
|
-
* Cache Key Utilities
|
|
127
|
-
*
|
|
128
|
-
* Deterministic, scope-safe key generation for QueryCache.
|
|
129
|
-
* Keys include resource version, operation, params hash, and user/org scope
|
|
130
|
-
* to ensure multi-tenant isolation and O(1) version-based invalidation.
|
|
131
|
-
*/
|
|
132
|
-
/** Build a deterministic cache key for a query */
|
|
133
|
-
declare function buildQueryKey(resource: string, operation: string, resourceVersion: number, params: Record<string, unknown>, userId?: string, orgId?: string): string;
|
|
134
|
-
/** Resource version key — stored in CacheStore, bumped on mutations */
|
|
135
|
-
declare function versionKey(resource: string): string;
|
|
136
|
-
/** Tag version key — stored in CacheStore, bumped on cross-resource invalidation */
|
|
137
|
-
declare function tagVersionKey(tag: string): string;
|
|
138
|
-
/**
|
|
139
|
-
* Stable hash for query params.
|
|
140
|
-
* Sorts keys recursively, serializes to JSON, then applies djb2 hash.
|
|
141
|
-
* Returns hex string.
|
|
142
|
-
*/
|
|
143
|
-
declare function hashParams(params: Record<string, unknown>): string;
|
|
144
|
-
//#endregion
|
|
145
145
|
export { type CacheEnvelope, type CacheLogger, type CacheResult, type CacheSetOptions, type CacheStats, type CacheStatus, type CacheStore, type CrossResourceRule, MemoryCacheStore, type MemoryCacheStoreOptions, QueryCache, type QueryCacheConfig, type QueryCacheDefaults, type QueryCachePluginOptions, type RedisCacheClient, RedisCacheStore, type RedisCacheStoreOptions, type RedisPipeline, buildQueryKey, hashParams, queryCachePlugin, tagVersionKey, versionKey };
|
package/dist/cache/index.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { i as versionKey, n as hashParams, r as tagVersionKey, t as buildQueryKey } from "../keys-
|
|
2
|
-
import { t as MemoryCacheStore } from "../memory-
|
|
3
|
-
import { r as QueryCache, t as queryCachePlugin } from "../queryCachePlugin-
|
|
4
|
-
|
|
1
|
+
import { i as versionKey, n as hashParams, r as tagVersionKey, t as buildQueryKey } from "../keys-qcD-TVJl.mjs";
|
|
2
|
+
import { t as MemoryCacheStore } from "../memory-Cb_7iy9e.mjs";
|
|
3
|
+
import { r as QueryCache, t as queryCachePlugin } from "../queryCachePlugin-ClosZdNS.mjs";
|
|
5
4
|
//#region src/cache/redis.ts
|
|
6
5
|
/**
|
|
7
6
|
* Redis-backed cache store.
|
|
@@ -86,6 +85,5 @@ var RedisCacheStore = class {
|
|
|
86
85
|
return `${this.prefix}${key}`;
|
|
87
86
|
}
|
|
88
87
|
};
|
|
89
|
-
|
|
90
88
|
//#endregion
|
|
91
|
-
export { MemoryCacheStore, QueryCache, RedisCacheStore, buildQueryKey, hashParams, queryCachePlugin, tagVersionKey, versionKey };
|
|
89
|
+
export { MemoryCacheStore, QueryCache, RedisCacheStore, buildQueryKey, hashParams, queryCachePlugin, tagVersionKey, versionKey };
|
|
@@ -1,27 +1,6 @@
|
|
|
1
|
-
import { t as __exportAll } from "./chunk-
|
|
1
|
+
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
2
|
import fp from "fastify-plugin";
|
|
3
|
-
|
|
4
3
|
//#region src/plugins/caching.ts
|
|
5
|
-
/**
|
|
6
|
-
* Caching Plugin
|
|
7
|
-
*
|
|
8
|
-
* Adds ETag and Cache-Control headers to GET/HEAD responses.
|
|
9
|
-
* Supports conditional requests (304 Not Modified) for bandwidth savings.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* import { cachingPlugin } from '@classytic/arc/plugins';
|
|
13
|
-
*
|
|
14
|
-
* // Basic — ETag + conditional requests, no browser caching
|
|
15
|
-
* await fastify.register(cachingPlugin);
|
|
16
|
-
*
|
|
17
|
-
* // With cache rules per path
|
|
18
|
-
* await fastify.register(cachingPlugin, {
|
|
19
|
-
* rules: [
|
|
20
|
-
* { match: '/api/products', maxAge: 60 },
|
|
21
|
-
* { match: '/api/categories', maxAge: 300, staleWhileRevalidate: 60 },
|
|
22
|
-
* ],
|
|
23
|
-
* });
|
|
24
|
-
*/
|
|
25
4
|
var caching_exports = /* @__PURE__ */ __exportAll({
|
|
26
5
|
cachingPlugin: () => cachingPlugin,
|
|
27
6
|
default: () => caching_default
|
|
@@ -88,6 +67,5 @@ var caching_default = fp(cachingPlugin, {
|
|
|
88
67
|
name: "arc-caching",
|
|
89
68
|
fastify: "5.x"
|
|
90
69
|
});
|
|
91
|
-
|
|
92
70
|
//#endregion
|
|
93
|
-
export { caching_default as n, caching_exports as r, cachingPlugin as t };
|
|
71
|
+
export { caching_default as n, caching_exports as r, cachingPlugin as t };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import "node:module";
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __exportAll = (all, no_symbols) => {
|
|
5
|
+
let target = {};
|
|
6
|
+
for (var name in all) __defProp(target, name, {
|
|
7
|
+
get: all[name],
|
|
8
|
+
enumerable: true
|
|
9
|
+
});
|
|
10
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
11
|
+
return target;
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
export { __exportAll as t };
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
//#region src/utils/circuitBreaker.ts
|
|
2
|
+
/**
|
|
3
|
+
* Circuit Breaker Pattern
|
|
4
|
+
*
|
|
5
|
+
* Wraps external service calls with failure protection.
|
|
6
|
+
* Prevents cascading failures by "opening" the circuit when
|
|
7
|
+
* a service is failing, allowing it time to recover.
|
|
8
|
+
*
|
|
9
|
+
* States:
|
|
10
|
+
* - CLOSED: Normal operation, requests pass through
|
|
11
|
+
* - OPEN: Too many failures, all requests fail fast
|
|
12
|
+
* - HALF_OPEN: Testing if service recovered, limited requests
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* import { CircuitBreaker } from '@classytic/arc/utils';
|
|
16
|
+
*
|
|
17
|
+
* const paymentBreaker = new CircuitBreaker(async (amount) => {
|
|
18
|
+
* return await stripe.charges.create({ amount });
|
|
19
|
+
* }, {
|
|
20
|
+
* failureThreshold: 5,
|
|
21
|
+
* resetTimeout: 30000,
|
|
22
|
+
* timeout: 5000,
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* try {
|
|
26
|
+
* const result = await paymentBreaker.call(100);
|
|
27
|
+
* } catch (error) {
|
|
28
|
+
* // Handle failure or circuit open
|
|
29
|
+
* }
|
|
30
|
+
*/
|
|
31
|
+
const CircuitState = {
|
|
32
|
+
CLOSED: "CLOSED",
|
|
33
|
+
OPEN: "OPEN",
|
|
34
|
+
HALF_OPEN: "HALF_OPEN"
|
|
35
|
+
};
|
|
36
|
+
var CircuitBreakerError = class extends Error {
|
|
37
|
+
state;
|
|
38
|
+
constructor(message, state) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = "CircuitBreakerError";
|
|
41
|
+
this.state = state;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var CircuitBreaker = class {
|
|
45
|
+
state = CircuitState.CLOSED;
|
|
46
|
+
failures = 0;
|
|
47
|
+
successes = 0;
|
|
48
|
+
totalCalls = 0;
|
|
49
|
+
nextAttempt = 0;
|
|
50
|
+
lastCallAt = null;
|
|
51
|
+
openedAt = null;
|
|
52
|
+
failureThreshold;
|
|
53
|
+
resetTimeout;
|
|
54
|
+
timeout;
|
|
55
|
+
successThreshold;
|
|
56
|
+
fallback;
|
|
57
|
+
onStateChange;
|
|
58
|
+
onError;
|
|
59
|
+
name;
|
|
60
|
+
fn;
|
|
61
|
+
constructor(fn, options = {}) {
|
|
62
|
+
this.fn = fn;
|
|
63
|
+
this.failureThreshold = options.failureThreshold ?? 5;
|
|
64
|
+
this.resetTimeout = options.resetTimeout ?? 6e4;
|
|
65
|
+
this.timeout = options.timeout ?? 1e4;
|
|
66
|
+
this.successThreshold = options.successThreshold ?? 1;
|
|
67
|
+
this.fallback = options.fallback;
|
|
68
|
+
this.onStateChange = options.onStateChange;
|
|
69
|
+
this.onError = options.onError;
|
|
70
|
+
this.name = options.name ?? "CircuitBreaker";
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Call the wrapped function with circuit breaker protection
|
|
74
|
+
*/
|
|
75
|
+
async call(...args) {
|
|
76
|
+
this.totalCalls++;
|
|
77
|
+
this.lastCallAt = Date.now();
|
|
78
|
+
if (this.state === CircuitState.OPEN) {
|
|
79
|
+
if (Date.now() < this.nextAttempt) {
|
|
80
|
+
const error = new CircuitBreakerError(`Circuit breaker is OPEN for ${this.name}`, CircuitState.OPEN);
|
|
81
|
+
if (this.fallback) return this.fallback(...args);
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
this.setState(CircuitState.HALF_OPEN);
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const result = await this.executeWithTimeout(args);
|
|
88
|
+
this.onSuccess();
|
|
89
|
+
return result;
|
|
90
|
+
} catch (err) {
|
|
91
|
+
this.onFailure(err instanceof Error ? err : new Error(String(err)));
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Execute function with timeout
|
|
97
|
+
*/
|
|
98
|
+
async executeWithTimeout(args) {
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
const timeoutId = setTimeout(() => {
|
|
101
|
+
reject(/* @__PURE__ */ new Error(`Request timeout after ${this.timeout}ms`));
|
|
102
|
+
}, this.timeout);
|
|
103
|
+
this.fn(...args).then((result) => {
|
|
104
|
+
clearTimeout(timeoutId);
|
|
105
|
+
resolve(result);
|
|
106
|
+
}).catch((error) => {
|
|
107
|
+
clearTimeout(timeoutId);
|
|
108
|
+
reject(error);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Handle successful call
|
|
114
|
+
*/
|
|
115
|
+
onSuccess() {
|
|
116
|
+
this.failures = 0;
|
|
117
|
+
this.successes++;
|
|
118
|
+
if (this.state === CircuitState.HALF_OPEN) {
|
|
119
|
+
if (this.successes >= this.successThreshold) {
|
|
120
|
+
this.setState(CircuitState.CLOSED);
|
|
121
|
+
this.successes = 0;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Handle failed call
|
|
127
|
+
*/
|
|
128
|
+
onFailure(error) {
|
|
129
|
+
this.failures++;
|
|
130
|
+
this.successes = 0;
|
|
131
|
+
if (this.onError) this.onError(error);
|
|
132
|
+
if (this.state === CircuitState.HALF_OPEN || this.failures >= this.failureThreshold) {
|
|
133
|
+
this.setState(CircuitState.OPEN);
|
|
134
|
+
this.nextAttempt = Date.now() + this.resetTimeout;
|
|
135
|
+
this.openedAt = Date.now();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Change circuit state
|
|
140
|
+
*/
|
|
141
|
+
setState(newState) {
|
|
142
|
+
const oldState = this.state;
|
|
143
|
+
if (oldState !== newState) {
|
|
144
|
+
this.state = newState;
|
|
145
|
+
if (this.onStateChange) this.onStateChange(oldState, newState);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Manually open the circuit
|
|
150
|
+
*/
|
|
151
|
+
open() {
|
|
152
|
+
this.setState(CircuitState.OPEN);
|
|
153
|
+
this.nextAttempt = Date.now() + this.resetTimeout;
|
|
154
|
+
this.openedAt = Date.now();
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Manually close the circuit
|
|
158
|
+
*/
|
|
159
|
+
close() {
|
|
160
|
+
this.failures = 0;
|
|
161
|
+
this.successes = 0;
|
|
162
|
+
this.setState(CircuitState.CLOSED);
|
|
163
|
+
this.openedAt = null;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Get current statistics
|
|
167
|
+
*/
|
|
168
|
+
getStats() {
|
|
169
|
+
return {
|
|
170
|
+
name: this.name,
|
|
171
|
+
state: this.state,
|
|
172
|
+
failures: this.failures,
|
|
173
|
+
successes: this.successes,
|
|
174
|
+
totalCalls: this.totalCalls,
|
|
175
|
+
openedAt: this.openedAt,
|
|
176
|
+
lastCallAt: this.lastCallAt
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get current state
|
|
181
|
+
*/
|
|
182
|
+
getState() {
|
|
183
|
+
return this.state;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Check if circuit is open
|
|
187
|
+
*/
|
|
188
|
+
isOpen() {
|
|
189
|
+
return this.state === CircuitState.OPEN;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Check if circuit is closed
|
|
193
|
+
*/
|
|
194
|
+
isClosed() {
|
|
195
|
+
return this.state === CircuitState.CLOSED;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Reset statistics
|
|
199
|
+
*/
|
|
200
|
+
reset() {
|
|
201
|
+
this.failures = 0;
|
|
202
|
+
this.successes = 0;
|
|
203
|
+
this.totalCalls = 0;
|
|
204
|
+
this.lastCallAt = null;
|
|
205
|
+
this.openedAt = null;
|
|
206
|
+
this.setState(CircuitState.CLOSED);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
/**
|
|
210
|
+
* Create a circuit breaker with sensible defaults
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* const emailBreaker = createCircuitBreaker(
|
|
214
|
+
* async (to, subject, body) => sendEmail(to, subject, body),
|
|
215
|
+
* { name: 'email-service' }
|
|
216
|
+
* );
|
|
217
|
+
*/
|
|
218
|
+
function createCircuitBreaker(fn, options) {
|
|
219
|
+
return new CircuitBreaker(fn, options);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Circuit breaker registry for managing multiple breakers
|
|
223
|
+
*/
|
|
224
|
+
var CircuitBreakerRegistry = class {
|
|
225
|
+
breakers = /* @__PURE__ */ new Map();
|
|
226
|
+
/**
|
|
227
|
+
* Register a circuit breaker
|
|
228
|
+
*/
|
|
229
|
+
register(name, fn, options) {
|
|
230
|
+
const breaker = new CircuitBreaker(fn, {
|
|
231
|
+
...options,
|
|
232
|
+
name
|
|
233
|
+
});
|
|
234
|
+
this.breakers.set(name, breaker);
|
|
235
|
+
return breaker;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Get a circuit breaker by name
|
|
239
|
+
*/
|
|
240
|
+
get(name) {
|
|
241
|
+
return this.breakers.get(name);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Get all breakers
|
|
245
|
+
*/
|
|
246
|
+
getAll() {
|
|
247
|
+
return this.breakers;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Get statistics for all breakers
|
|
251
|
+
*/
|
|
252
|
+
getAllStats() {
|
|
253
|
+
const stats = {};
|
|
254
|
+
for (const [name, breaker] of this.breakers.entries()) stats[name] = breaker.getStats();
|
|
255
|
+
return stats;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Reset all breakers
|
|
259
|
+
*/
|
|
260
|
+
resetAll() {
|
|
261
|
+
for (const breaker of this.breakers.values()) breaker.reset();
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Open all breakers
|
|
265
|
+
*/
|
|
266
|
+
openAll() {
|
|
267
|
+
for (const breaker of this.breakers.values()) breaker.open();
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Close all breakers
|
|
271
|
+
*/
|
|
272
|
+
closeAll() {
|
|
273
|
+
for (const breaker of this.breakers.values()) breaker.close();
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
/**
|
|
277
|
+
* Create a new CircuitBreakerRegistry instance.
|
|
278
|
+
* Use this instead of a global singleton — attach to fastify.arc or pass explicitly.
|
|
279
|
+
*/
|
|
280
|
+
function createCircuitBreakerRegistry() {
|
|
281
|
+
return new CircuitBreakerRegistry();
|
|
282
|
+
}
|
|
283
|
+
//#endregion
|
|
284
|
+
export { createCircuitBreaker as a, CircuitState as i, CircuitBreakerError as n, createCircuitBreakerRegistry as o, CircuitBreakerRegistry as r, CircuitBreaker as t };
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
//#region src/utils/circuitBreaker.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Circuit Breaker Pattern
|
|
4
|
+
*
|
|
5
|
+
* Wraps external service calls with failure protection.
|
|
6
|
+
* Prevents cascading failures by "opening" the circuit when
|
|
7
|
+
* a service is failing, allowing it time to recover.
|
|
8
|
+
*
|
|
9
|
+
* States:
|
|
10
|
+
* - CLOSED: Normal operation, requests pass through
|
|
11
|
+
* - OPEN: Too many failures, all requests fail fast
|
|
12
|
+
* - HALF_OPEN: Testing if service recovered, limited requests
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* import { CircuitBreaker } from '@classytic/arc/utils';
|
|
16
|
+
*
|
|
17
|
+
* const paymentBreaker = new CircuitBreaker(async (amount) => {
|
|
18
|
+
* return await stripe.charges.create({ amount });
|
|
19
|
+
* }, {
|
|
20
|
+
* failureThreshold: 5,
|
|
21
|
+
* resetTimeout: 30000,
|
|
22
|
+
* timeout: 5000,
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* try {
|
|
26
|
+
* const result = await paymentBreaker.call(100);
|
|
27
|
+
* } catch (error) {
|
|
28
|
+
* // Handle failure or circuit open
|
|
29
|
+
* }
|
|
30
|
+
*/
|
|
31
|
+
declare const CircuitState: {
|
|
32
|
+
readonly CLOSED: "CLOSED";
|
|
33
|
+
readonly OPEN: "OPEN";
|
|
34
|
+
readonly HALF_OPEN: "HALF_OPEN";
|
|
35
|
+
};
|
|
36
|
+
type CircuitState = (typeof CircuitState)[keyof typeof CircuitState];
|
|
37
|
+
interface CircuitBreakerOptions {
|
|
38
|
+
/**
|
|
39
|
+
* Number of failures before opening circuit
|
|
40
|
+
* @default 5
|
|
41
|
+
*/
|
|
42
|
+
failureThreshold?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Time in ms before attempting to close circuit
|
|
45
|
+
* @default 60000 (60 seconds)
|
|
46
|
+
*/
|
|
47
|
+
resetTimeout?: number;
|
|
48
|
+
/**
|
|
49
|
+
* Request timeout in ms
|
|
50
|
+
* @default 10000 (10 seconds)
|
|
51
|
+
*/
|
|
52
|
+
timeout?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Number of successful requests in HALF_OPEN before closing
|
|
55
|
+
* @default 1
|
|
56
|
+
*/
|
|
57
|
+
successThreshold?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Fallback function when circuit is open.
|
|
60
|
+
* Receives the same arguments as the wrapped function.
|
|
61
|
+
*/
|
|
62
|
+
fallback?: (...args: unknown[]) => Promise<unknown>;
|
|
63
|
+
/**
|
|
64
|
+
* Callback when state changes
|
|
65
|
+
*/
|
|
66
|
+
onStateChange?: (from: CircuitState, to: CircuitState) => void;
|
|
67
|
+
/**
|
|
68
|
+
* Callback on error
|
|
69
|
+
*/
|
|
70
|
+
onError?: (error: Error) => void;
|
|
71
|
+
/**
|
|
72
|
+
* Name for logging/monitoring
|
|
73
|
+
*/
|
|
74
|
+
name?: string;
|
|
75
|
+
}
|
|
76
|
+
interface CircuitBreakerStats {
|
|
77
|
+
name?: string;
|
|
78
|
+
state: CircuitState;
|
|
79
|
+
failures: number;
|
|
80
|
+
successes: number;
|
|
81
|
+
totalCalls: number;
|
|
82
|
+
openedAt: number | null;
|
|
83
|
+
lastCallAt: number | null;
|
|
84
|
+
}
|
|
85
|
+
declare class CircuitBreakerError extends Error {
|
|
86
|
+
state: CircuitState;
|
|
87
|
+
constructor(message: string, state: CircuitState);
|
|
88
|
+
}
|
|
89
|
+
declare class CircuitBreaker<T extends (...args: any[]) => Promise<any>> {
|
|
90
|
+
private state;
|
|
91
|
+
private failures;
|
|
92
|
+
private successes;
|
|
93
|
+
private totalCalls;
|
|
94
|
+
private nextAttempt;
|
|
95
|
+
private lastCallAt;
|
|
96
|
+
private openedAt;
|
|
97
|
+
private readonly failureThreshold;
|
|
98
|
+
private readonly resetTimeout;
|
|
99
|
+
private readonly timeout;
|
|
100
|
+
private readonly successThreshold;
|
|
101
|
+
private readonly fallback?;
|
|
102
|
+
private readonly onStateChange?;
|
|
103
|
+
private readonly onError?;
|
|
104
|
+
private readonly name;
|
|
105
|
+
private readonly fn;
|
|
106
|
+
constructor(fn: T, options?: CircuitBreakerOptions);
|
|
107
|
+
/**
|
|
108
|
+
* Call the wrapped function with circuit breaker protection
|
|
109
|
+
*/
|
|
110
|
+
call(...args: Parameters<T>): Promise<ReturnType<T>>;
|
|
111
|
+
/**
|
|
112
|
+
* Execute function with timeout
|
|
113
|
+
*/
|
|
114
|
+
private executeWithTimeout;
|
|
115
|
+
/**
|
|
116
|
+
* Handle successful call
|
|
117
|
+
*/
|
|
118
|
+
private onSuccess;
|
|
119
|
+
/**
|
|
120
|
+
* Handle failed call
|
|
121
|
+
*/
|
|
122
|
+
private onFailure;
|
|
123
|
+
/**
|
|
124
|
+
* Change circuit state
|
|
125
|
+
*/
|
|
126
|
+
private setState;
|
|
127
|
+
/**
|
|
128
|
+
* Manually open the circuit
|
|
129
|
+
*/
|
|
130
|
+
open(): void;
|
|
131
|
+
/**
|
|
132
|
+
* Manually close the circuit
|
|
133
|
+
*/
|
|
134
|
+
close(): void;
|
|
135
|
+
/**
|
|
136
|
+
* Get current statistics
|
|
137
|
+
*/
|
|
138
|
+
getStats(): CircuitBreakerStats;
|
|
139
|
+
/**
|
|
140
|
+
* Get current state
|
|
141
|
+
*/
|
|
142
|
+
getState(): CircuitState;
|
|
143
|
+
/**
|
|
144
|
+
* Check if circuit is open
|
|
145
|
+
*/
|
|
146
|
+
isOpen(): boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Check if circuit is closed
|
|
149
|
+
*/
|
|
150
|
+
isClosed(): boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Reset statistics
|
|
153
|
+
*/
|
|
154
|
+
reset(): void;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Create a circuit breaker with sensible defaults
|
|
158
|
+
*
|
|
159
|
+
* @example
|
|
160
|
+
* const emailBreaker = createCircuitBreaker(
|
|
161
|
+
* async (to, subject, body) => sendEmail(to, subject, body),
|
|
162
|
+
* { name: 'email-service' }
|
|
163
|
+
* );
|
|
164
|
+
*/
|
|
165
|
+
declare function createCircuitBreaker<T extends (...args: any[]) => Promise<any>>(fn: T, options?: CircuitBreakerOptions): CircuitBreaker<T>;
|
|
166
|
+
/**
|
|
167
|
+
* Circuit breaker registry for managing multiple breakers
|
|
168
|
+
*/
|
|
169
|
+
declare class CircuitBreakerRegistry {
|
|
170
|
+
private breakers;
|
|
171
|
+
/**
|
|
172
|
+
* Register a circuit breaker
|
|
173
|
+
*/
|
|
174
|
+
register<T extends (...args: any[]) => Promise<any>>(name: string, fn: T, options?: Omit<CircuitBreakerOptions, "name">): CircuitBreaker<T>;
|
|
175
|
+
/**
|
|
176
|
+
* Get a circuit breaker by name
|
|
177
|
+
*/
|
|
178
|
+
get(name: string): CircuitBreaker<any> | undefined;
|
|
179
|
+
/**
|
|
180
|
+
* Get all breakers
|
|
181
|
+
*/
|
|
182
|
+
getAll(): Map<string, CircuitBreaker<any>>;
|
|
183
|
+
/**
|
|
184
|
+
* Get statistics for all breakers
|
|
185
|
+
*/
|
|
186
|
+
getAllStats(): Record<string, CircuitBreakerStats>;
|
|
187
|
+
/**
|
|
188
|
+
* Reset all breakers
|
|
189
|
+
*/
|
|
190
|
+
resetAll(): void;
|
|
191
|
+
/**
|
|
192
|
+
* Open all breakers
|
|
193
|
+
*/
|
|
194
|
+
openAll(): void;
|
|
195
|
+
/**
|
|
196
|
+
* Close all breakers
|
|
197
|
+
*/
|
|
198
|
+
closeAll(): void;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Create a new CircuitBreakerRegistry instance.
|
|
202
|
+
* Use this instead of a global singleton — attach to fastify.arc or pass explicitly.
|
|
203
|
+
*/
|
|
204
|
+
declare function createCircuitBreakerRegistry(): CircuitBreakerRegistry;
|
|
205
|
+
//#endregion
|
|
206
|
+
export { CircuitBreakerStats as a, createCircuitBreakerRegistry as c, CircuitBreakerRegistry as i, CircuitBreakerError as n, CircuitState as o, CircuitBreakerOptions as r, createCircuitBreaker as s, CircuitBreaker as t };
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { t as CRUD_OPERATIONS } from "../../constants-
|
|
1
|
+
import { t as CRUD_OPERATIONS } from "../../constants-Cxde4rpC.mjs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { pathToFileURL } from "node:url";
|
|
4
|
-
|
|
5
4
|
//#region src/cli/commands/describe.ts
|
|
6
5
|
/**
|
|
7
6
|
* Arc CLI - Describe Command
|
|
@@ -144,7 +143,8 @@ function describeEvents(resourceName, events) {
|
|
|
144
143
|
return Object.entries(events).map(([action, def]) => ({
|
|
145
144
|
name: `${resourceName}:${action}`,
|
|
146
145
|
description: def.description,
|
|
147
|
-
hasSchema: !!def.schema
|
|
146
|
+
hasSchema: !!def.schema,
|
|
147
|
+
hasHandler: !!def.handler
|
|
148
148
|
}));
|
|
149
149
|
}
|
|
150
150
|
function describeMiddlewares(middlewares) {
|
|
@@ -179,7 +179,7 @@ async function describe(args) {
|
|
|
179
179
|
try {
|
|
180
180
|
const flags = new Set(args.filter((a) => a.startsWith("--")));
|
|
181
181
|
const positional = args.filter((a) => !a.startsWith("--"));
|
|
182
|
-
const pretty = flags.has("--pretty") || !flags.has("--json");
|
|
182
|
+
const pretty = flags.has("--pretty") || !flags.has("--json") && (process.stdout.isTTY ?? true);
|
|
183
183
|
const filterResource = positional[1];
|
|
184
184
|
const entryPath = positional[0];
|
|
185
185
|
if (!entryPath) {
|
|
@@ -196,11 +196,18 @@ async function describe(args) {
|
|
|
196
196
|
}
|
|
197
197
|
const entryModule = await import(pathToFileURL(resolve(process.cwd(), entryPath)).href);
|
|
198
198
|
const resources = [];
|
|
199
|
+
let eventRegistry;
|
|
199
200
|
function tryCollect(value) {
|
|
200
201
|
if (value && typeof value === "object" && "name" in value && "_registryMeta" in value && "toPlugin" in value) resources.push(value);
|
|
201
202
|
}
|
|
203
|
+
function tryCollectRegistry(value) {
|
|
204
|
+
if (value && typeof value === "object" && "catalog" in value && "register" in value && "validate" in value && typeof value.catalog === "function") eventRegistry = value;
|
|
205
|
+
}
|
|
202
206
|
for (const exported of Object.values(entryModule)) if (Array.isArray(exported)) exported.forEach(tryCollect);
|
|
203
|
-
else
|
|
207
|
+
else {
|
|
208
|
+
tryCollect(exported);
|
|
209
|
+
tryCollectRegistry(exported);
|
|
210
|
+
}
|
|
204
211
|
if (resources.length === 0) throw new Error("No resource definitions found in entry file.\nMake sure your file exports defineResource() results:\n export const productResource = defineResource({ ... });");
|
|
205
212
|
const filtered = filterResource ? resources.filter((r) => r.name === filterResource) : resources;
|
|
206
213
|
if (filterResource && filtered.length === 0) throw new Error(`Resource '${filterResource}' not found.\nAvailable: ${resources.map((r) => r.name).join(", ")}`);
|
|
@@ -213,14 +220,25 @@ async function describe(args) {
|
|
|
213
220
|
if (res.pipeline) totalPipelineSteps += res.pipeline.guards.length + res.pipeline.transforms.length + res.pipeline.interceptors.length;
|
|
214
221
|
if (res.fields) totalFields += Object.keys(res.fields).length;
|
|
215
222
|
}
|
|
223
|
+
let eventCatalog;
|
|
224
|
+
if (eventRegistry) eventCatalog = eventRegistry.catalog().map((e) => ({
|
|
225
|
+
name: e.name,
|
|
226
|
+
version: e.version,
|
|
227
|
+
description: e.description,
|
|
228
|
+
hasSchema: !!e.schema,
|
|
229
|
+
schemaFields: e.schema?.properties ? Object.keys(e.schema.properties) : [],
|
|
230
|
+
requiredFields: e.schema?.required ?? []
|
|
231
|
+
}));
|
|
216
232
|
const output = {
|
|
217
233
|
$schema: "arc-describe/v1",
|
|
218
234
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
219
235
|
resources: described,
|
|
236
|
+
...eventCatalog?.length ? { eventCatalog } : {},
|
|
220
237
|
stats: {
|
|
221
238
|
totalResources: described.length,
|
|
222
239
|
totalRoutes: described.reduce((sum, r) => sum + r.routes.length, 0),
|
|
223
240
|
totalEvents: described.reduce((sum, r) => sum + r.events.length, 0),
|
|
241
|
+
totalCatalogedEvents: eventCatalog?.length ?? 0,
|
|
224
242
|
totalFields,
|
|
225
243
|
presetUsage: presetCounts,
|
|
226
244
|
pipelineSteps: totalPipelineSteps
|
|
@@ -233,6 +251,5 @@ async function describe(args) {
|
|
|
233
251
|
throw new Error(String(error));
|
|
234
252
|
}
|
|
235
253
|
}
|
|
236
|
-
|
|
237
254
|
//#endregion
|
|
238
|
-
export { describe as default, describe };
|
|
255
|
+
export { describe as default, describe };
|