@classytic/arc 1.1.0 → 2.1.3

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.
Files changed (200) hide show
  1. package/README.md +247 -794
  2. package/bin/arc.js +91 -52
  3. package/dist/EventTransport-BkUDYZEb.d.mts +99 -0
  4. package/dist/HookSystem-BsGV-j2l.mjs +404 -0
  5. package/dist/ResourceRegistry-7Ic20ZMw.mjs +249 -0
  6. package/dist/adapters/index.d.mts +5 -0
  7. package/dist/adapters/index.mjs +3 -0
  8. package/dist/audit/index.d.mts +81 -0
  9. package/dist/audit/index.mjs +275 -0
  10. package/dist/audit/mongodb.d.mts +5 -0
  11. package/dist/audit/mongodb.mjs +3 -0
  12. package/dist/audited-CGdLiSlE.mjs +140 -0
  13. package/dist/auth/index.d.mts +188 -0
  14. package/dist/auth/index.mjs +1096 -0
  15. package/dist/auth/redis-session.d.mts +43 -0
  16. package/dist/auth/redis-session.mjs +75 -0
  17. package/dist/betterAuthOpenApi-DjWDddNc.mjs +249 -0
  18. package/dist/cache/index.d.mts +145 -0
  19. package/dist/cache/index.mjs +91 -0
  20. package/dist/caching-GSDJcA6-.mjs +93 -0
  21. package/dist/chunk-C7Uep-_p.mjs +20 -0
  22. package/dist/circuitBreaker-DYhWBW_D.mjs +1096 -0
  23. package/dist/cli/commands/describe.d.mts +18 -0
  24. package/dist/cli/commands/describe.mjs +238 -0
  25. package/dist/cli/commands/docs.d.mts +13 -0
  26. package/dist/cli/commands/docs.mjs +52 -0
  27. package/dist/cli/commands/{generate.d.ts → generate.d.mts} +3 -2
  28. package/dist/cli/commands/generate.mjs +357 -0
  29. package/dist/cli/commands/{init.d.ts → init.d.mts} +11 -8
  30. package/dist/cli/commands/{init.js → init.mjs} +807 -617
  31. package/dist/cli/commands/introspect.d.mts +10 -0
  32. package/dist/cli/commands/introspect.mjs +75 -0
  33. package/dist/cli/index.d.mts +16 -0
  34. package/dist/cli/index.mjs +156 -0
  35. package/dist/constants-DdXFXQtN.mjs +84 -0
  36. package/dist/core/index.d.mts +5 -0
  37. package/dist/core/index.mjs +4 -0
  38. package/dist/createApp-D2D5XXaV.mjs +559 -0
  39. package/dist/defineResource-PXzSJ15_.mjs +2197 -0
  40. package/dist/discovery/index.d.mts +46 -0
  41. package/dist/discovery/index.mjs +109 -0
  42. package/dist/docs/index.d.mts +162 -0
  43. package/dist/docs/index.mjs +74 -0
  44. package/dist/elevation-DGo5shaX.d.mts +87 -0
  45. package/dist/elevation-DSTbVvYj.mjs +113 -0
  46. package/dist/errorHandler-C3GY3_ow.mjs +108 -0
  47. package/dist/errorHandler-CW3OOeYq.d.mts +72 -0
  48. package/dist/errors-DAWRdiYP.d.mts +124 -0
  49. package/dist/errors-DBANPbGr.mjs +211 -0
  50. package/dist/eventPlugin-BEOvaDqo.mjs +229 -0
  51. package/dist/eventPlugin-H6wDDjGO.d.mts +124 -0
  52. package/dist/events/index.d.mts +53 -0
  53. package/dist/events/index.mjs +51 -0
  54. package/dist/events/transports/redis-stream-entry.d.mts +2 -0
  55. package/dist/events/transports/redis-stream-entry.mjs +177 -0
  56. package/dist/events/transports/redis.d.mts +76 -0
  57. package/dist/events/transports/redis.mjs +124 -0
  58. package/dist/externalPaths-SyPF2tgK.d.mts +50 -0
  59. package/dist/factory/index.d.mts +63 -0
  60. package/dist/factory/index.mjs +3 -0
  61. package/dist/fastifyAdapter-C8DlE0YH.d.mts +216 -0
  62. package/dist/fields-Bi_AVKSo.d.mts +109 -0
  63. package/dist/fields-CTd_CrKr.mjs +114 -0
  64. package/dist/hooks/index.d.mts +4 -0
  65. package/dist/hooks/index.mjs +3 -0
  66. package/dist/idempotency/index.d.mts +96 -0
  67. package/dist/idempotency/index.mjs +319 -0
  68. package/dist/idempotency/mongodb.d.mts +2 -0
  69. package/dist/idempotency/mongodb.mjs +114 -0
  70. package/dist/idempotency/redis.d.mts +2 -0
  71. package/dist/idempotency/redis.mjs +103 -0
  72. package/dist/index.d.mts +260 -0
  73. package/dist/index.mjs +104 -0
  74. package/dist/integrations/event-gateway.d.mts +46 -0
  75. package/dist/integrations/event-gateway.mjs +43 -0
  76. package/dist/integrations/index.d.mts +5 -0
  77. package/dist/integrations/index.mjs +1 -0
  78. package/dist/integrations/jobs.d.mts +103 -0
  79. package/dist/integrations/jobs.mjs +123 -0
  80. package/dist/integrations/streamline.d.mts +60 -0
  81. package/dist/integrations/streamline.mjs +125 -0
  82. package/dist/integrations/websocket.d.mts +82 -0
  83. package/dist/integrations/websocket.mjs +288 -0
  84. package/dist/interface-CSNjltAc.d.mts +77 -0
  85. package/dist/interface-DTbsvIWe.d.mts +54 -0
  86. package/dist/interface-e9XfSsUV.d.mts +1097 -0
  87. package/dist/introspectionPlugin-B3JkrjwU.mjs +53 -0
  88. package/dist/keys-DhqDRxv3.mjs +42 -0
  89. package/dist/logger-ByrvQWZO.mjs +78 -0
  90. package/dist/memory-B2v7KrCB.mjs +143 -0
  91. package/dist/migrations/index.d.mts +156 -0
  92. package/dist/migrations/index.mjs +260 -0
  93. package/dist/mongodb-ClykrfGo.d.mts +118 -0
  94. package/dist/mongodb-DNKEExbf.mjs +93 -0
  95. package/dist/mongodb-Dg8O_gvd.d.mts +71 -0
  96. package/dist/openapi-9nB_kiuR.mjs +525 -0
  97. package/dist/org/index.d.mts +68 -0
  98. package/dist/org/index.mjs +513 -0
  99. package/dist/org/types.d.mts +82 -0
  100. package/dist/org/types.mjs +1 -0
  101. package/dist/permissions/index.d.mts +278 -0
  102. package/dist/permissions/index.mjs +579 -0
  103. package/dist/plugins/index.d.mts +172 -0
  104. package/dist/plugins/index.mjs +522 -0
  105. package/dist/plugins/response-cache.d.mts +87 -0
  106. package/dist/plugins/response-cache.mjs +283 -0
  107. package/dist/plugins/tracing-entry.d.mts +2 -0
  108. package/dist/plugins/tracing-entry.mjs +185 -0
  109. package/dist/pluralize-CM-jZg7p.mjs +86 -0
  110. package/dist/policies/{index.d.ts → index.d.mts} +204 -170
  111. package/dist/policies/index.mjs +321 -0
  112. package/dist/presets/{index.d.ts → index.d.mts} +62 -131
  113. package/dist/presets/index.mjs +143 -0
  114. package/dist/presets/multiTenant.d.mts +24 -0
  115. package/dist/presets/multiTenant.mjs +113 -0
  116. package/dist/presets-BTeYbw7h.d.mts +57 -0
  117. package/dist/presets-CeFtfDR8.mjs +119 -0
  118. package/dist/prisma-C3iornoK.d.mts +274 -0
  119. package/dist/prisma-DJbMt3yf.mjs +627 -0
  120. package/dist/queryCachePlugin-B6R0d4av.mjs +138 -0
  121. package/dist/queryCachePlugin-Q6SYuHZ6.d.mts +71 -0
  122. package/dist/redis-UwjEp8Ea.d.mts +49 -0
  123. package/dist/redis-stream-CBg0upHI.d.mts +103 -0
  124. package/dist/registry/index.d.mts +11 -0
  125. package/dist/registry/index.mjs +4 -0
  126. package/dist/requestContext-xi6OKBL-.mjs +55 -0
  127. package/dist/schemaConverter-Dtg0Kt9T.mjs +98 -0
  128. package/dist/schemas/index.d.mts +63 -0
  129. package/dist/schemas/index.mjs +82 -0
  130. package/dist/scope/index.d.mts +21 -0
  131. package/dist/scope/index.mjs +65 -0
  132. package/dist/sessionManager-D_iEHjQl.d.mts +186 -0
  133. package/dist/sse-DkqQ1uxb.mjs +123 -0
  134. package/dist/testing/index.d.mts +907 -0
  135. package/dist/testing/index.mjs +1976 -0
  136. package/dist/tracing-8CEbhF0w.d.mts +70 -0
  137. package/dist/typeGuards-DwxA1t_L.mjs +9 -0
  138. package/dist/types/index.d.mts +946 -0
  139. package/dist/types/index.mjs +14 -0
  140. package/dist/types-B0dhNrnd.d.mts +445 -0
  141. package/dist/types-Beqn1Un7.mjs +38 -0
  142. package/dist/types-DelU6kln.mjs +25 -0
  143. package/dist/types-RLkFVgaw.d.mts +101 -0
  144. package/dist/utils/index.d.mts +747 -0
  145. package/dist/utils/index.mjs +6 -0
  146. package/package.json +194 -68
  147. package/dist/BaseController-DVAiHxEQ.d.ts +0 -233
  148. package/dist/adapters/index.d.ts +0 -237
  149. package/dist/adapters/index.js +0 -668
  150. package/dist/arcCorePlugin-CsShQdyP.d.ts +0 -273
  151. package/dist/audit/index.d.ts +0 -195
  152. package/dist/audit/index.js +0 -319
  153. package/dist/auth/index.d.ts +0 -47
  154. package/dist/auth/index.js +0 -174
  155. package/dist/cli/commands/docs.d.ts +0 -11
  156. package/dist/cli/commands/docs.js +0 -474
  157. package/dist/cli/commands/generate.js +0 -334
  158. package/dist/cli/commands/introspect.d.ts +0 -8
  159. package/dist/cli/commands/introspect.js +0 -338
  160. package/dist/cli/index.d.ts +0 -4
  161. package/dist/cli/index.js +0 -3269
  162. package/dist/core/index.d.ts +0 -220
  163. package/dist/core/index.js +0 -2786
  164. package/dist/createApp-Ce9wl8W9.d.ts +0 -77
  165. package/dist/docs/index.d.ts +0 -166
  166. package/dist/docs/index.js +0 -658
  167. package/dist/errors-8WIxGS_6.d.ts +0 -122
  168. package/dist/events/index.d.ts +0 -117
  169. package/dist/events/index.js +0 -89
  170. package/dist/factory/index.d.ts +0 -38
  171. package/dist/factory/index.js +0 -1652
  172. package/dist/hooks/index.d.ts +0 -4
  173. package/dist/hooks/index.js +0 -199
  174. package/dist/idempotency/index.d.ts +0 -323
  175. package/dist/idempotency/index.js +0 -500
  176. package/dist/index-B4t03KQ0.d.ts +0 -1366
  177. package/dist/index.d.ts +0 -135
  178. package/dist/index.js +0 -4756
  179. package/dist/migrations/index.d.ts +0 -185
  180. package/dist/migrations/index.js +0 -274
  181. package/dist/org/index.d.ts +0 -129
  182. package/dist/org/index.js +0 -220
  183. package/dist/permissions/index.d.ts +0 -144
  184. package/dist/permissions/index.js +0 -103
  185. package/dist/plugins/index.d.ts +0 -46
  186. package/dist/plugins/index.js +0 -1069
  187. package/dist/policies/index.js +0 -196
  188. package/dist/presets/index.js +0 -384
  189. package/dist/presets/multiTenant.d.ts +0 -39
  190. package/dist/presets/multiTenant.js +0 -112
  191. package/dist/registry/index.d.ts +0 -16
  192. package/dist/registry/index.js +0 -253
  193. package/dist/testing/index.d.ts +0 -618
  194. package/dist/testing/index.js +0 -48020
  195. package/dist/types/index.d.ts +0 -4
  196. package/dist/types/index.js +0 -8
  197. package/dist/types-B99TBmFV.d.ts +0 -76
  198. package/dist/types-BvckRbs2.d.ts +0 -143
  199. package/dist/utils/index.d.ts +0 -679
  200. package/dist/utils/index.js +0 -931
@@ -0,0 +1,76 @@
1
+ import { i as EventTransport, n as EventHandler, r as EventLogger, t as DomainEvent } from "../../EventTransport-BkUDYZEb.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 };
@@ -0,0 +1,124 @@
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 };
@@ -0,0 +1,50 @@
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 };
@@ -0,0 +1,63 @@
1
+ import "../elevation-DGo5shaX.mjs";
2
+ import "../interface-e9XfSsUV.mjs";
3
+ import "../types-RLkFVgaw.mjs";
4
+ import "../queryCachePlugin-Q6SYuHZ6.mjs";
5
+ import "../eventPlugin-H6wDDjGO.mjs";
6
+ import "../errorHandler-CW3OOeYq.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-B0dhNrnd.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 };
@@ -0,0 +1,3 @@
1
+ import { a as getPreset, i as developmentPreset, n as createApp, o as productionPreset, s as testingPreset, t as ArcFactory } from "../createApp-D2D5XXaV.mjs";
2
+
3
+ export { ArcFactory, createApp, developmentPreset, getPreset, productionPreset, testingPreset };
@@ -0,0 +1,216 @@
1
+ import { s as RequestScope } from "./elevation-DGo5shaX.mjs";
2
+ import { b as IControllerResponse, x as IRequestContext, y as IController } from "./interface-e9XfSsUV.mjs";
3
+ import { t as PermissionCheck } from "./types-RLkFVgaw.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 };
@@ -0,0 +1,109 @@
1
+ //#region src/permissions/fields.d.ts
2
+ /**
3
+ * Field-Level Permissions
4
+ *
5
+ * Control field visibility and writability per role.
6
+ * Integrated into the response path (read) and sanitization path (write).
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { fields, defineResource } from '@classytic/arc';
11
+ *
12
+ * const userResource = defineResource({
13
+ * name: 'user',
14
+ * adapter: userAdapter,
15
+ * fields: {
16
+ * salary: fields.visibleTo(['admin', 'hr']),
17
+ * internalNotes: fields.writableBy(['admin']),
18
+ * email: fields.redactFor(['viewer']),
19
+ * password: fields.hidden(),
20
+ * },
21
+ * });
22
+ * ```
23
+ */
24
+ type FieldPermissionType = 'hidden' | 'visibleTo' | 'writableBy' | 'redactFor';
25
+ interface FieldPermission {
26
+ readonly _type: FieldPermissionType;
27
+ readonly roles?: readonly string[];
28
+ readonly redactValue?: unknown;
29
+ }
30
+ type FieldPermissionMap = Record<string, FieldPermission>;
31
+ declare const fields: {
32
+ /**
33
+ * Field is never included in responses. Not writable via API.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * fields: { password: fields.hidden() }
38
+ * ```
39
+ */
40
+ hidden(): FieldPermission;
41
+ /**
42
+ * Field is only visible to users with specified roles.
43
+ * Other users don't see the field at all.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * fields: { salary: fields.visibleTo(['admin', 'hr']) }
48
+ * ```
49
+ */
50
+ visibleTo(roles: readonly string[]): FieldPermission;
51
+ /**
52
+ * Field is only writable by users with specified roles.
53
+ * All users can still read the field. Users without the role
54
+ * have the field silently stripped from write operations.
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * fields: { role: fields.writableBy(['admin']) }
59
+ * ```
60
+ */
61
+ writableBy(roles: readonly string[]): FieldPermission;
62
+ /**
63
+ * Field is redacted (replaced with a placeholder) for specified roles.
64
+ * Other users see the real value.
65
+ *
66
+ * @param roles - Roles that see the redacted value
67
+ * @param redactValue - Replacement value (default: '***')
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * fields: {
72
+ * email: fields.redactFor(['viewer']),
73
+ * ssn: fields.redactFor(['basic'], '***-**-****'),
74
+ * }
75
+ * ```
76
+ */
77
+ redactFor(roles: readonly string[], redactValue?: unknown): FieldPermission;
78
+ };
79
+ /**
80
+ * Apply field-level READ permissions to a response object.
81
+ * Strips hidden fields, enforces visibility, and applies redaction.
82
+ *
83
+ * @param data - The response object (mutated in place for performance)
84
+ * @param fieldPermissions - Field permission map from resource config
85
+ * @param userRoles - Current user's roles (empty array for unauthenticated)
86
+ * @returns The filtered object
87
+ */
88
+ declare function applyFieldReadPermissions<T extends Record<string, unknown>>(data: T, fieldPermissions: FieldPermissionMap, userRoles: readonly string[]): T;
89
+ /**
90
+ * Apply field-level WRITE permissions to request body.
91
+ * Strips fields that the user doesn't have permission to write.
92
+ *
93
+ * @param body - The request body (returns a new filtered copy)
94
+ * @param fieldPermissions - Field permission map from resource config
95
+ * @param userRoles - Current user's roles
96
+ * @returns Filtered body
97
+ */
98
+ declare function applyFieldWritePermissions<T extends Record<string, unknown>>(body: T, fieldPermissions: FieldPermissionMap, userRoles: readonly string[]): T;
99
+ /**
100
+ * Resolve effective roles by merging global user roles with org-level roles.
101
+ *
102
+ * Global roles come from `req.user.role` (normalized via getUserRoles()).
103
+ * Org roles come from `req.context.orgRoles` (set by BA adapter's org bridge).
104
+ *
105
+ * When no org context exists, returns global roles only — backward compatible.
106
+ */
107
+ declare function resolveEffectiveRoles(userRoles: readonly string[], orgRoles: readonly string[]): string[];
108
+ //#endregion
109
+ export { applyFieldWritePermissions as a, applyFieldReadPermissions as i, FieldPermissionMap as n, fields as o, FieldPermissionType as r, resolveEffectiveRoles as s, FieldPermission as t };