@classytic/arc 2.20.0 → 2.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/dist/{BaseController-D-MHZ27_.mjs → BaseController-C6FlRWxF.mjs} +2 -2
  2. package/dist/{EventTransport-IEn7Tn-Q.d.mts → EventTransport-QmY7A258.d.mts} +0 -1
  3. package/dist/{QueryCache-TRfXau3W.d.mts → QueryCache-Z1B38koh.d.mts} +0 -1
  4. package/dist/{actionPermissions-C9RMiIpc.mjs → actionPermissions-ze70xXJi.mjs} +1 -1
  5. package/dist/audit/index.d.mts +9 -5
  6. package/dist/auth/audit.d.mts +7 -4
  7. package/dist/auth/audit.mjs +5 -0
  8. package/dist/auth/index.d.mts +35 -14
  9. package/dist/auth/index.mjs +6 -3
  10. package/dist/auth/redis-session.d.mts +1 -2
  11. package/dist/cache/index.d.mts +2 -3
  12. package/dist/cli/commands/describe.d.mts +1 -2
  13. package/dist/cli/commands/docs.mjs +1 -1
  14. package/dist/cli/commands/init.mjs +1 -1
  15. package/dist/cli/index.mjs +1 -1
  16. package/dist/context/index.d.mts +2 -2
  17. package/dist/core/index.d.mts +3 -3
  18. package/dist/core/index.mjs +4 -4
  19. package/dist/{core-CWhyGkiM.mjs → core-b6fgFgnd.mjs} +129 -17
  20. package/dist/{createActionRouter-HlQso0jO.mjs → createActionRouter-h1cEEaEX.mjs} +10 -6
  21. package/dist/{createAggregationRouter-5LzPzgw1.mjs → createAggregationRouter-B1C0kvTL.mjs} +3 -3
  22. package/dist/{createApp-D6uwzCKu.mjs → createApp-Ci7yG5gv.mjs} +390 -192
  23. package/dist/discovery/index.d.mts +0 -1
  24. package/dist/docs/index.d.mts +1 -2
  25. package/dist/docs/index.mjs +1 -1
  26. package/dist/{elevation-xGUBq6p_.d.mts → elevation-C9s5hFP_.d.mts} +0 -1
  27. package/dist/encryption/index.d.mts +2 -144
  28. package/dist/encryption/index.mjs +9 -5
  29. package/dist/{errorHandler-Dxf6P_kV.mjs → errorHandler-BTzXSwIg.mjs} +24 -3
  30. package/dist/{errorHandler-BtUUDPEa.d.mts → errorHandler-C6yv4ZTW.d.mts} +13 -1
  31. package/dist/{errors-DOHkjQlD.d.mts → errors-DHcHVAgk.d.mts} +0 -1
  32. package/dist/{eventPlugin-DbBFd3x_.d.mts → eventPlugin-A6EbthXR.d.mts} +10 -6
  33. package/dist/events/index.d.mts +9 -6
  34. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  35. package/dist/events/transports/redis.d.mts +1 -2
  36. package/dist/factory/index.d.mts +30 -9
  37. package/dist/factory/index.mjs +35 -4
  38. package/dist/{fastify-augmentation-CCQJEDgh.d.mts → fastify-augmentation-9_fuetB4.d.mts} +2 -3
  39. package/dist/fields-D3lhgzhD.d.mts +121 -0
  40. package/dist/health-ScyjIVo5.mjs +202 -0
  41. package/dist/hooks/index.d.mts +1 -1
  42. package/dist/idempotency/index.d.mts +4 -3
  43. package/dist/idempotency/index.mjs +2 -0
  44. package/dist/idempotency/redis.d.mts +1 -1
  45. package/dist/{types-BHMKlMBU.d.mts → index-BEu4wjbL.d.mts} +241 -80
  46. package/dist/{index-DUa8WHz3.d.mts → index-D8MKYGDa.d.mts} +88 -7
  47. package/dist/{index-_--GyzcO.d.mts → index-DbtgCOCj.d.mts} +1324 -1228
  48. package/dist/{index-CdFkIMEd.d.mts → index-ogIHvOjo.d.mts} +36 -7
  49. package/dist/index.d.mts +8 -7
  50. package/dist/index.mjs +6 -6
  51. package/dist/{init-YH_nslQR.mjs → init-DleK3p9f.mjs} +272 -139
  52. package/dist/integrations/event-gateway.d.mts +2 -3
  53. package/dist/integrations/index.d.mts +2 -2
  54. package/dist/integrations/jobs.d.mts +0 -1
  55. package/dist/integrations/mcp/index.d.mts +6 -7
  56. package/dist/integrations/mcp/index.mjs +1 -1
  57. package/dist/integrations/mcp/testing.d.mts +1 -2
  58. package/dist/integrations/mcp/testing.mjs +1 -1
  59. package/dist/integrations/streamline.d.mts +2 -2
  60. package/dist/integrations/webhooks.d.mts +8 -5
  61. package/dist/integrations/webhooks.mjs +9 -5
  62. package/dist/integrations/websocket-pushref-redis.d.mts +1 -2
  63. package/dist/integrations/websocket-redis.d.mts +1 -2
  64. package/dist/integrations/websocket.d.mts +2 -2
  65. package/dist/jsonBody-BlzAoGCe.mjs +43 -0
  66. package/dist/{loadResources-B8UmpITt.mjs → loadResources-DpU8pfjV.mjs} +4 -3
  67. package/dist/logger/index.d.mts +26 -1
  68. package/dist/logger/index.mjs +39 -1
  69. package/dist/{mcpPlugin-CuW6L_uD.mjs → mcpPlugin-ByA2tf6D.mjs} +8 -4
  70. package/dist/middleware/index.d.mts +1 -2
  71. package/dist/{module-JYH9V0I8.d.mts → module-B4u10iOJ.d.mts} +71 -1
  72. package/dist/{openapi-Dq6RbEGK.mjs → openapi-BRpMZD7C.mjs} +3 -3
  73. package/dist/permissions/index.d.mts +4 -3
  74. package/dist/permissions/index.mjs +2 -2
  75. package/dist/{permissions-CgbOLJZG.mjs → permissions-BJMuKMl7.mjs} +33 -1
  76. package/dist/pipeline/index.d.mts +1 -2
  77. package/dist/plugins/index.d.mts +96 -7
  78. package/dist/plugins/index.mjs +86 -268
  79. package/dist/plugins/response-cache.d.mts +5 -3
  80. package/dist/plugins/response-cache.mjs +1 -0
  81. package/dist/plugins/tracing-entry.d.mts +1 -1
  82. package/dist/plugins/tracing-entry.mjs +7 -4
  83. package/dist/presets/filesUpload.d.mts +4 -5
  84. package/dist/presets/filesUpload.mjs +1 -1
  85. package/dist/presets/index.d.mts +1 -2
  86. package/dist/presets/index.mjs +1 -1
  87. package/dist/presets/multiTenant.d.mts +1 -2
  88. package/dist/presets/search.d.mts +2 -3
  89. package/dist/presets/search.mjs +1 -1
  90. package/dist/{presets-DWsYQdbV.mjs → presets-gw0ciIUD.mjs} +1 -1
  91. package/dist/{pushref-registry-DnCqL3aa.d.mts → pushref-registry-BbcnpNTm.d.mts} +4 -2
  92. package/dist/{queryCachePlugin-xzFyKAjT.d.mts → queryCachePlugin-CfYWq-Ak.d.mts} +1 -2
  93. package/dist/rateLimitKey-BoWRH3e7.mjs +14 -0
  94. package/dist/{redis-C3W0qbz8.d.mts → redis-UDtLaNj6.d.mts} +0 -1
  95. package/dist/{redis-stream-DsP1vqZB.d.mts → redis-stream-iVR2l7aS.d.mts} +1 -2
  96. package/dist/{registerRoute-DGSREwPw.mjs → registerRoute-xHc42fwB.mjs} +38 -18
  97. package/dist/registry/index.d.mts +2 -2
  98. package/dist/requestId-5BhTVzBv.mjs +113 -0
  99. package/dist/schemaConverter-Ds5reSGs.mjs +168 -0
  100. package/dist/{schemaIR-ajetw3Fv.mjs → schemaIR-B-6SPe7g.mjs} +2 -2
  101. package/dist/schemas/index.d.mts +0 -1
  102. package/dist/scim/index.d.mts +0 -1
  103. package/dist/scim/index.mjs +6 -7
  104. package/dist/scope/index.d.mts +2 -3
  105. package/dist/scope/index.mjs +2 -76
  106. package/dist/scope-k15RXfcq.mjs +64 -0
  107. package/dist/{sessionManager-BXG-HGwG.d.mts → sessionManager-BHOM3N_0.d.mts} +10 -6
  108. package/dist/{storage-Co_PQpeQ.d.mts → storage-DLUZ04kK.d.mts} +8 -4
  109. package/dist/testing/index.d.mts +83 -4
  110. package/dist/testing/index.mjs +74 -4
  111. package/dist/testing/storageContract.d.mts +1 -2
  112. package/dist/{tracing-D8jlGXP7.d.mts → tracing-CsGKQJGg.d.mts} +0 -1
  113. package/dist/types/index.d.mts +4 -4
  114. package/dist/types/storage.d.mts +1 -1
  115. package/dist/{types-C9b2HR5W.d.mts → types-Bc42CUyk.d.mts} +1 -2
  116. package/dist/types-Cc91nXuA.d.mts +132 -0
  117. package/dist/{fields-C7ahdGYf.d.mts → types-Csxga7d3.d.mts} +2 -123
  118. package/dist/{types-LNebCoJx.d.mts → types-DH0GupI7.d.mts} +8 -4
  119. package/dist/usage/index.d.mts +142 -0
  120. package/dist/usage/index.mjs +0 -0
  121. package/dist/utils/index.d.mts +220 -144
  122. package/dist/utils/index.mjs +3 -3
  123. package/dist/{utils-ChjYQpjn.mjs → utils-FhJ88TJE.mjs} +129 -39
  124. package/dist/{versioning-D_eBCfNr.d.mts → versioning-DLwklPlG.d.mts} +1 -7
  125. package/dist/{websocket-MlYlrYq0.d.mts → websocket-uuWqM6UY.d.mts} +5 -4
  126. package/llms.txt +39 -0
  127. package/package.json +453 -447
  128. package/skills/arc/SKILL.md +14 -8
  129. package/skills/arc/references/api-reference.md +34 -0
  130. package/skills/arc/references/auth.md +21 -4
  131. package/skills/arc/references/mcp.md +1 -1
  132. package/skills/arc/references/production.md +44 -2
  133. package/skills/arc-code-review/SKILL.md +1 -1
  134. package/skills/arc-code-review/references/anti-patterns.md +71 -18
  135. package/skills/arc-code-review/references/arc-cheatsheet.md +15 -2
  136. package/skills/arc-code-review/references/migration-recipes.md +7 -7
  137. package/skills/arc-code-review/references/mongokit-migration.md +4 -3
  138. package/skills/arc-module-publishing/SKILL.md +243 -0
  139. package/skills/arc-module-publishing/references/authoring-modules.md +327 -0
  140. package/skills/arc-module-publishing/references/package-manifest.md +157 -0
  141. package/skills/arc-module-publishing/references/publishing-workflow.md +182 -0
  142. package/dist/preloadResources-CHw3_oA_.mjs +0 -45
  143. package/dist/schemaConverter-U0MnO9vd.mjs +0 -110
@@ -0,0 +1,202 @@
1
+ import fp from "fastify-plugin";
2
+ //#region src/plugins/health.ts
3
+ function createHttpMetrics() {
4
+ return {
5
+ requestsTotal: {},
6
+ requestDurations: [],
7
+ _ringIndex: 0,
8
+ startTime: Date.now()
9
+ };
10
+ }
11
+ const healthPlugin = async (fastify, opts = {}) => {
12
+ const { prefix = "/_health", checks = [], metrics = false, metricsCollector, version, collectHttpMetrics = metrics } = opts;
13
+ const httpMetrics = createHttpMetrics();
14
+ fastify.get(prefix, { schema: {
15
+ tags: ["Health"],
16
+ summary: "Liveness probe (alias for /live)",
17
+ description: "Returns 200 if the process is alive",
18
+ response: { 200: {
19
+ type: "object",
20
+ properties: {
21
+ status: {
22
+ type: "string",
23
+ enum: ["ok"]
24
+ },
25
+ timestamp: { type: "string" },
26
+ version: { type: "string" }
27
+ }
28
+ } }
29
+ } }, async () => ({
30
+ status: "ok",
31
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
32
+ ...version ? { version } : {}
33
+ }));
34
+ fastify.get(`${prefix}/live`, { schema: {
35
+ tags: ["Health"],
36
+ summary: "Liveness probe",
37
+ description: "Returns 200 if the process is alive",
38
+ response: { 200: {
39
+ type: "object",
40
+ properties: {
41
+ status: {
42
+ type: "string",
43
+ enum: ["ok"]
44
+ },
45
+ timestamp: { type: "string" },
46
+ version: { type: "string" }
47
+ }
48
+ } }
49
+ } }, async () => {
50
+ return {
51
+ status: "ok",
52
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
53
+ ...version ? { version } : {}
54
+ };
55
+ });
56
+ fastify.get(`${prefix}/ready`, { schema: {
57
+ tags: ["Health"],
58
+ summary: "Readiness probe",
59
+ description: "Returns 200 if all dependencies are healthy",
60
+ response: {
61
+ 200: {
62
+ type: "object",
63
+ properties: {
64
+ status: {
65
+ type: "string",
66
+ enum: ["ready", "not_ready"]
67
+ },
68
+ timestamp: { type: "string" },
69
+ checks: {
70
+ type: "array",
71
+ items: {
72
+ type: "object",
73
+ properties: {
74
+ name: { type: "string" },
75
+ healthy: { type: "boolean" },
76
+ duration: { type: "number" },
77
+ error: { type: "string" }
78
+ }
79
+ }
80
+ }
81
+ }
82
+ },
83
+ 503: {
84
+ type: "object",
85
+ properties: {
86
+ status: {
87
+ type: "string",
88
+ enum: ["not_ready"]
89
+ },
90
+ timestamp: { type: "string" },
91
+ checks: { type: "array" }
92
+ }
93
+ }
94
+ }
95
+ } }, async (_, reply) => {
96
+ const results = await runChecks(checks);
97
+ const criticalFailed = results.some((r) => !r.healthy && (checks.find((c) => c.name === r.name)?.critical ?? true));
98
+ const response = {
99
+ status: criticalFailed ? "not_ready" : "ready",
100
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
101
+ checks: results
102
+ };
103
+ if (criticalFailed) reply.code(503);
104
+ return response;
105
+ });
106
+ if (metrics) fastify.get(`${prefix}/metrics`, async (_, reply) => {
107
+ reply.type("text/plain; charset=utf-8");
108
+ if (metricsCollector) return await metricsCollector();
109
+ const uptime = process.uptime();
110
+ const memory = process.memoryUsage();
111
+ const cpu = process.cpuUsage();
112
+ const lines = [
113
+ "# HELP process_uptime_seconds Process uptime in seconds",
114
+ "# TYPE process_uptime_seconds gauge",
115
+ `process_uptime_seconds ${uptime.toFixed(2)}`,
116
+ "",
117
+ "# HELP process_memory_heap_bytes Heap memory usage in bytes",
118
+ "# TYPE process_memory_heap_bytes gauge",
119
+ `process_memory_heap_bytes{type="used"} ${memory.heapUsed}`,
120
+ `process_memory_heap_bytes{type="total"} ${memory.heapTotal}`,
121
+ "",
122
+ "# HELP process_memory_rss_bytes RSS memory in bytes",
123
+ "# TYPE process_memory_rss_bytes gauge",
124
+ `process_memory_rss_bytes ${memory.rss}`,
125
+ "",
126
+ "# HELP process_memory_external_bytes External memory in bytes",
127
+ "# TYPE process_memory_external_bytes gauge",
128
+ `process_memory_external_bytes ${memory.external}`,
129
+ "",
130
+ "# HELP process_cpu_user_microseconds User CPU time in microseconds",
131
+ "# TYPE process_cpu_user_microseconds counter",
132
+ `process_cpu_user_microseconds ${cpu.user}`,
133
+ "",
134
+ "# HELP process_cpu_system_microseconds System CPU time in microseconds",
135
+ "# TYPE process_cpu_system_microseconds counter",
136
+ `process_cpu_system_microseconds ${cpu.system}`,
137
+ ""
138
+ ];
139
+ if (collectHttpMetrics && Object.keys(httpMetrics.requestsTotal).length > 0) {
140
+ lines.push("# HELP http_requests_total Total HTTP requests by status code", "# TYPE http_requests_total counter");
141
+ for (const [status, count] of Object.entries(httpMetrics.requestsTotal)) lines.push(`http_requests_total{status="${status}"} ${count}`);
142
+ lines.push("");
143
+ if (httpMetrics.requestDurations.length > 0) {
144
+ const sorted = [...httpMetrics.requestDurations].sort((a, b) => a - b);
145
+ const p50 = sorted[Math.floor(sorted.length * .5)] || 0;
146
+ const p95 = sorted[Math.floor(sorted.length * .95)] || 0;
147
+ const p99 = sorted[Math.floor(sorted.length * .99)] || 0;
148
+ const sum = sorted.reduce((a, b) => a + b, 0);
149
+ lines.push("# HELP http_request_duration_milliseconds HTTP request duration", "# TYPE http_request_duration_milliseconds summary", `http_request_duration_milliseconds{quantile="0.5"} ${p50.toFixed(2)}`, `http_request_duration_milliseconds{quantile="0.95"} ${p95.toFixed(2)}`, `http_request_duration_milliseconds{quantile="0.99"} ${p99.toFixed(2)}`, `http_request_duration_milliseconds_sum ${sum.toFixed(2)}`, `http_request_duration_milliseconds_count ${sorted.length}`, "");
150
+ }
151
+ }
152
+ return lines.join("\n");
153
+ });
154
+ if (collectHttpMetrics) fastify.addHook("onResponse", async (_request, reply) => {
155
+ const duration = reply.elapsedTime;
156
+ const statusBucket = `${Math.floor(reply.statusCode / 100)}xx`;
157
+ httpMetrics.requestsTotal[statusBucket] = (httpMetrics.requestsTotal[statusBucket] || 0) + 1;
158
+ if (httpMetrics.requestDurations.length < 1e4) httpMetrics.requestDurations.push(duration);
159
+ else httpMetrics.requestDurations[httpMetrics._ringIndex % 1e4] = duration;
160
+ httpMetrics._ringIndex = httpMetrics._ringIndex + 1;
161
+ });
162
+ fastify.log?.debug?.(`Health plugin registered at ${prefix} (alias), ${prefix}/live, ${prefix}/ready${metrics ? `, ${prefix}/metrics` : ""}`);
163
+ };
164
+ /**
165
+ * Run all health checks with timeout
166
+ */
167
+ async function runChecks(checks) {
168
+ const results = [];
169
+ for (const check of checks) {
170
+ const start = Date.now();
171
+ const timeout = check.timeout ?? 5e3;
172
+ let timer;
173
+ try {
174
+ const checkPromise = Promise.resolve(check.check());
175
+ const timeoutPromise = new Promise((_, reject) => {
176
+ timer = setTimeout(() => reject(/* @__PURE__ */ new Error("Health check timeout")), timeout);
177
+ });
178
+ const healthy = await Promise.race([checkPromise, timeoutPromise]);
179
+ results.push({
180
+ name: check.name,
181
+ healthy: Boolean(healthy),
182
+ duration: Date.now() - start
183
+ });
184
+ } catch (err) {
185
+ results.push({
186
+ name: check.name,
187
+ healthy: false,
188
+ duration: Date.now() - start,
189
+ error: err.message
190
+ });
191
+ } finally {
192
+ if (timer) clearTimeout(timer);
193
+ }
194
+ }
195
+ return results;
196
+ }
197
+ var health_default = fp(healthPlugin, {
198
+ name: "arc-health",
199
+ fastify: "5.x"
200
+ });
201
+ //#endregion
202
+ export { health_default as n, healthPlugin as t };
@@ -1,2 +1,2 @@
1
- import { Cn as afterDelete, Dn as beforeUpdate, En as beforeDelete, On as createHookSystem, Sn as afterCreate, Tn as beforeCreate, _n as HookOperation, bn as HookSystem, gn as HookHandler, hn as HookContext, kn as defineHook, mn as DefineHookOptions, vn as HookPhase, wn as afterUpdate, xn as HookSystemOptions, yn as HookRegistration } from "../index-_--GyzcO.mjs";
1
+ import { Cn as afterDelete, Dn as beforeUpdate, En as beforeDelete, On as createHookSystem, Sn as afterCreate, Tn as beforeCreate, _n as HookOperation, bn as HookSystem, gn as HookHandler, hn as HookContext, kn as defineHook, mn as DefineHookOptions, vn as HookPhase, wn as afterUpdate, xn as HookSystemOptions, yn as HookRegistration } from "../index-DbtgCOCj.mjs";
2
2
  export { type DefineHookOptions, type HookContext, type HookHandler, type HookOperation, type HookPhase, type HookRegistration, HookSystem, type HookSystemOptions, afterCreate, afterDelete, afterUpdate, beforeCreate, beforeDelete, beforeUpdate, createHookSystem, defineHook };
@@ -1,8 +1,7 @@
1
1
  import { i as createIdempotencyResult, n as IdempotencyResult, r as IdempotencyStore, t as IdempotencyLock } from "../interface-Uk8ONJv7.mjs";
2
- import { i as RedisIdempotencyStoreOptions, n as RedisClient } from "../redis-C3W0qbz8.mjs";
2
+ import { i as RedisIdempotencyStoreOptions, n as RedisClient } from "../redis-UDtLaNj6.mjs";
3
3
  import { FastifyPluginAsync } from "fastify";
4
4
  import { RepositoryLike } from "@classytic/repo-core/adapter";
5
-
6
5
  //#region src/idempotency/idempotencyPlugin.d.ts
7
6
  interface IdempotencyPluginOptions {
8
7
  /** Enable idempotency (default: false) */
@@ -60,7 +59,9 @@ declare module "fastify" {
60
59
  interface FastifyInstance {
61
60
  /** Idempotency utilities */
62
61
  idempotency: {
63
- /** Manually invalidate an idempotency key */invalidate: (key: string) => Promise<void>; /** Check if a key has a cached response */
62
+ /** Manually invalidate an idempotency key */
63
+ invalidate: (key: string) => Promise<void>;
64
+ /** Check if a key has a cached response */
64
65
  has: (key: string) => Promise<boolean>;
65
66
  /**
66
67
  * Route-level preHandler for idempotency check + lock.
@@ -287,12 +287,14 @@ const idempotencyPlugin = async (fastify, opts = {}) => {
287
287
  });
288
288
  fastify.decorateRequest("idempotencyKey", void 0);
289
289
  fastify.decorateRequest("idempotencyReplayed", false);
290
+ fastify.decorateRequest("_idempotencyFullKey", void 0);
290
291
  fastify.log?.debug?.("Idempotency plugin disabled");
291
292
  return;
292
293
  }
293
294
  const methodSet = new Set(methods.map((m) => m.toUpperCase()));
294
295
  fastify.decorateRequest("idempotencyKey", void 0);
295
296
  fastify.decorateRequest("idempotencyReplayed", false);
297
+ fastify.decorateRequest("_idempotencyFullKey", void 0);
296
298
  /**
297
299
  * Check if this request should use idempotency
298
300
  */
@@ -1,2 +1,2 @@
1
- import { a as UpstashRedisLike, i as RedisIdempotencyStoreOptions, n as RedisClient, o as ioredisAsIdempotencyClient, r as RedisIdempotencyStore, s as upstashAsIdempotencyClient, t as IoredisLike } from "../redis-C3W0qbz8.mjs";
1
+ import { a as UpstashRedisLike, i as RedisIdempotencyStoreOptions, n as RedisClient, o as ioredisAsIdempotencyClient, r as RedisIdempotencyStore, s as upstashAsIdempotencyClient, t as IoredisLike } from "../redis-UDtLaNj6.mjs";
2
2
  export { type IoredisLike, type RedisClient, RedisIdempotencyStore, type RedisIdempotencyStoreOptions, type UpstashRedisLike, ioredisAsIdempotencyClient, upstashAsIdempotencyClient };
@@ -1,16 +1,15 @@
1
- import { Ut as Authenticator } from "./index-_--GyzcO.mjs";
1
+ import { Ut as Authenticator } from "./index-DbtgCOCj.mjs";
2
2
  import { r as CacheStore } from "./interface-C1_WLTMK.mjs";
3
- import { n as ElevationOptions } from "./elevation-xGUBq6p_.mjs";
4
- import { a as EventTransport } from "./EventTransport-IEn7Tn-Q.mjs";
3
+ import { n as ElevationOptions } from "./elevation-C9s5hFP_.mjs";
4
+ import { a as EventTransport } from "./EventTransport-QmY7A258.mjs";
5
5
  import { t as ExternalOpenApiPaths } from "./externalPaths-BE6wFpYv.mjs";
6
- import { r as QueryCachePluginOptions } from "./queryCachePlugin-xzFyKAjT.mjs";
7
- import { l as ResourceLike, n as ArcModuleInput } from "./module-JYH9V0I8.mjs";
8
- import { t as EventPluginOptions } from "./eventPlugin-DbBFd3x_.mjs";
9
- import { _ as HealthOptions, f as CachingOptions, l as SSEOptions, o as MetricsOptions, t as VersioningOptions } from "./versioning-D_eBCfNr.mjs";
10
- import { t as ErrorHandlerOptions } from "./errorHandler-BtUUDPEa.mjs";
6
+ import { r as QueryCachePluginOptions } from "./queryCachePlugin-CfYWq-Ak.mjs";
7
+ import { t as ErrorHandlerOptions } from "./errorHandler-C6yv4ZTW.mjs";
8
+ import { l as ResourceLike, n as ArcModuleInput } from "./module-B4u10iOJ.mjs";
9
+ import { t as EventPluginOptions } from "./eventPlugin-A6EbthXR.mjs";
10
+ import { _ as HealthOptions, f as CachingOptions, l as SSEOptions, o as MetricsOptions, t as VersioningOptions } from "./versioning-DLwklPlG.mjs";
11
11
  import { r as IdempotencyStore } from "./interface-Uk8ONJv7.mjs";
12
12
  import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, FastifyServerOptions } from "fastify";
13
-
14
13
  //#region src/testing/preloadResources.d.ts
15
14
  /** Eager glob result: `{ '/path/to/file.ts': resourceModule }` */
16
15
  type EagerGlobResult = Record<string, unknown>;
@@ -34,30 +33,7 @@ declare function preloadResources(globResult: EagerGlobResult): ResourceLike[];
34
33
  */
35
34
  declare function preloadResourcesAsync(globResult: LazyGlobResult): Promise<ResourceLike[]>;
36
35
  //#endregion
37
- //#region src/factory/types.d.ts
38
- type CorsOptions = Record<string, unknown> & {
39
- origin?: unknown;
40
- credentials?: boolean;
41
- methods?: string[];
42
- allowedHeaders?: string[];
43
- };
44
- type HelmetOptions = Record<string, unknown>;
45
- type RateLimitOpts = Record<string, unknown> & {
46
- max?: number;
47
- timeWindow?: string | number;
48
- /**
49
- * Path patterns to exempt from rate limiting. Supports exact match
50
- * (`/health`) or prefix match with trailing `*` (`/api/auth/*`).
51
- *
52
- * Implemented by synthesising a `@fastify/rate-limit` `allowList`
53
- * function. When combined with a user-supplied `allowList`, both
54
- * are OR-ed together (path OR ip/custom match skips the limit).
55
- *
56
- * Use this for endpoints that are hit frequently but independently
57
- * of user traffic — session heartbeat, webhooks, health probes.
58
- */
59
- skipPaths?: string[];
60
- };
36
+ //#region src/factory/types/auth.d.ts
61
37
  /**
62
38
  * Arc's built-in JWT auth
63
39
  *
@@ -95,11 +71,17 @@ interface JwtAuthOption {
95
71
  * If provided, jwt utilities are available in authenticator context
96
72
  */
97
73
  jwt?: {
98
- /** JWT secret (required for JWT features) */secret: string; /** Access token expiry (default: '15m') */
99
- expiresIn?: string; /** Refresh token secret (defaults to main secret) */
100
- refreshSecret?: string; /** Refresh token expiry (default: '7d') */
101
- refreshExpiresIn?: string; /** Additional @fastify/jwt sign options */
102
- sign?: Record<string, unknown>; /** Additional @fastify/jwt verify options */
74
+ /** JWT secret (required for JWT features) */
75
+ secret: string;
76
+ /** Access token expiry (default: '15m') */
77
+ expiresIn?: string;
78
+ /** Refresh token secret (defaults to main secret) */
79
+ refreshSecret?: string;
80
+ /** Refresh token expiry (default: '7d') */
81
+ refreshExpiresIn?: string;
82
+ /** Additional @fastify/jwt sign options */
83
+ sign?: Record<string, unknown>;
84
+ /** Additional @fastify/jwt verify options */
103
85
  verify?: Record<string, unknown>;
104
86
  };
105
87
  /**
@@ -156,13 +138,29 @@ interface JwtAuthOption {
156
138
  * });
157
139
  * ```
158
140
  */
141
+ /** The shape `createBetterAuthAdapter()` returns. */
142
+ interface BetterAuthAdapterResult {
143
+ plugin: FastifyPluginAsync;
144
+ openapi?: ExternalOpenApiPaths;
145
+ }
159
146
  interface BetterAuthOption {
160
147
  type: "betterAuth";
161
- /** Better Auth adapter — pass the result of createBetterAuthAdapter() */
162
- betterAuth: {
163
- plugin: FastifyPluginAsync;
164
- openapi?: ExternalOpenApiPaths;
165
- };
148
+ /**
149
+ * Better Auth adapter — the result of `createBetterAuthAdapter()`, or a
150
+ * sync/async THUNK returning it (2.22). The thunk resolves during auth
151
+ * registration — AFTER `beforeBoot` — for adapters whose construction
152
+ * needs a live DB (Better Auth's `mongodbAdapter(mongoose.connection
153
+ * .getClient().db())` requires an OPEN connection, so an eager value
154
+ * forces hosts back into connect-before-createApp ordering dances):
155
+ *
156
+ * ```typescript
157
+ * createApp({
158
+ * beforeBoot: () => connectDatabase(),
159
+ * auth: { type: 'betterAuth', betterAuth: () => createBetterAuthAdapter({ auth: getAuth() }) },
160
+ * });
161
+ * ```
162
+ */
163
+ betterAuth: BetterAuthAdapterResult | (() => BetterAuthAdapterResult | Promise<BetterAuthAdapterResult>);
166
164
  }
167
165
  /**
168
166
  * Custom auth plugin — full control over authentication setup
@@ -229,6 +227,112 @@ interface CustomAuthenticatorOption {
229
227
  * - `CustomAuthenticatorOption` — A bare authenticate function (`type: 'authenticator'`)
230
228
  */
231
229
  type AuthOption = false | JwtAuthOption | BetterAuthOption | CustomPluginAuthOption | CustomAuthenticatorOption;
230
+ //#endregion
231
+ //#region src/factory/types/plugin-options.d.ts
232
+ /**
233
+ * Plugin-specific option shapes for `createApp()` — under-pressure,
234
+ * multipart, and raw-body plugin configuration.
235
+ */
236
+ interface UnderPressureOptions {
237
+ /** Expose `/_status` route for health checks (default: false) */
238
+ exposeStatusRoute?: boolean;
239
+ /** Event loop lag threshold in ms — requests rejected above this (default: 1000) */
240
+ maxEventLoopDelay?: number;
241
+ /** V8 heap usage threshold in bytes — requests rejected above this */
242
+ maxHeapUsedBytes?: number;
243
+ /** RSS memory threshold in bytes — requests rejected above this */
244
+ maxRssBytes?: number;
245
+ }
246
+ interface MultipartOptions {
247
+ limits?: {
248
+ /** Max file size in bytes (default: Fastify default ~1MB) */
249
+ fileSize?: number;
250
+ /** Max number of files per request */
251
+ files?: number;
252
+ };
253
+ }
254
+ interface RawBodyOptions {
255
+ /** Body field name to store raw body on (default: 'rawBody') */
256
+ field?: string;
257
+ /** Apply to all routes globally (default: false) */
258
+ global?: boolean;
259
+ /** Encoding for raw body string (default: 'utf8') */
260
+ encoding?: string;
261
+ /** Parse raw body before other parsers (default: false) */
262
+ runFirst?: boolean;
263
+ }
264
+ //#endregion
265
+ //#region src/factory/types/security.d.ts
266
+ /**
267
+ * Security-plugin option shapes — CORS / Helmet / rate-limit config for
268
+ * `createApp()`, including plan-aware rate limiting (2.22).
269
+ */
270
+ type CorsOptions = Record<string, unknown> & {
271
+ origin?: unknown;
272
+ credentials?: boolean;
273
+ methods?: string[];
274
+ allowedHeaders?: string[];
275
+ };
276
+ type HelmetOptions = Record<string, unknown>;
277
+ type RateLimitOpts = Record<string, unknown> & {
278
+ max?: number;
279
+ timeWindow?: string | number;
280
+ /**
281
+ * Path patterns to exempt from rate limiting. Supports exact match
282
+ * (`/health`) or prefix match with trailing `*` (`/api/auth/*`).
283
+ *
284
+ * Implemented by synthesising a `@fastify/rate-limit` `allowList`
285
+ * function. When combined with a user-supplied `allowList`, both
286
+ * are OR-ed together (path OR ip/custom match skips the limit).
287
+ *
288
+ * Use this for endpoints that are hit frequently but independently
289
+ * of user traffic — session heartbeat, webhooks, health probes.
290
+ */
291
+ skipPaths?: string[];
292
+ /**
293
+ * Plan-aware limits (2.22) — resolve the caller's plan per request and
294
+ * apply that plan's ceiling instead of the global `max`:
295
+ *
296
+ * ```typescript
297
+ * rateLimit: {
298
+ * timeWindow: '1 minute',
299
+ * plan: {
300
+ * resolve: (req) => lookupPlan(req), // 'free' | 'pro' | ...
301
+ * limits: { free: { max: 60 }, pro: { max: 1000 }, enterprise: false },
302
+ * default: 'free',
303
+ * },
304
+ * }
305
+ * ```
306
+ *
307
+ * `false` = effectively unlimited. When `plan` is set and no
308
+ * `keyGenerator` is supplied, arc defaults to the tenant key chain
309
+ * (org → user → client → IP) so buckets follow actors, not addresses.
310
+ *
311
+ * Timing caveat: the limiter runs before route-level auth, so
312
+ * `req.scope` may be unresolved for token-authenticated calls —
313
+ * `resolve` receives the RAW request and may need to derive the plan
314
+ * from its own header/token/session lookup. Same caveat as
315
+ * `createTenantKeyGenerator`'s IP fallback (documented there).
316
+ */
317
+ plan?: RateLimitPlanConfig;
318
+ };
319
+ /** Plan-aware rate limiting (2.22) — see `rateLimit.plan` on CreateAppOptions. */
320
+ interface RateLimitPlanConfig {
321
+ /**
322
+ * Name the caller's plan from the raw request. Return `undefined` to
323
+ * fall back to `default`. Sync or async; a throwing resolver also
324
+ * falls back (limits must fail-safe, not fail-open to unlimited).
325
+ */
326
+ resolve: (req: import("fastify").FastifyRequest) => string | undefined | Promise<string | undefined>;
327
+ /** Per-plan ceilings within the shared `timeWindow`. `false` = effectively unlimited. */
328
+ limits: Record<string, {
329
+ max: number;
330
+ } | false>;
331
+ /** Plan used when `resolve` returns undefined/unknown or throws. Default: fall back to the global `max`. */
332
+ default?: string;
333
+ }
334
+ //#endregion
335
+ //#region src/factory/types/app-options.d.ts
232
336
  /**
233
337
  * CreateApp Options
234
338
  *
@@ -271,8 +375,18 @@ type AuthOption = false | JwtAuthOption | BetterAuthOption | CustomPluginAuthOpt
271
375
  * ```
272
376
  */
273
377
  interface CreateAppOptions {
274
- /** Environment preset: 'production', 'development', 'testing', or 'edge' */
275
- preset?: "production" | "development" | "testing" | "edge";
378
+ /** Environment preset: 'production', 'development', 'testing', 'edge', or 'worker' */
379
+ preset?: "production" | "development" | "testing" | "edge" | "worker";
380
+ /**
381
+ * Route-mounting switch (2.23) — the worker-role primitive. `false`
382
+ * registers every resource's shared runtime state (registry metadata +
383
+ * adapter, hooks, cache-invalidation rules) WITHOUT mounting any routes,
384
+ * so headless processes keep tenant-purge cascade, per-resource audit,
385
+ * and events metadata intact. Set by `preset: 'worker'` /
386
+ * `createWorker()`; hosts composing a custom worker shape may set it
387
+ * directly. Default: true (routes mount).
388
+ */
389
+ mountRoutes?: boolean;
276
390
  /**
277
391
  * Runtime profile for store backends.
278
392
  * - 'memory' (default): Uses in-memory stores. Suitable for single-instance deployments.
@@ -289,9 +403,13 @@ interface CreateAppOptions {
289
403
  * not memory-backed. Provide Redis or other durable adapters.
290
404
  */
291
405
  stores?: {
292
- /** Event transport (e.g., RedisEventTransport) */events?: EventTransport; /** Cache store (e.g., RedisCacheStore) */
293
- cache?: CacheStore; /** Idempotency store (e.g., RedisIdempotencyStore) */
294
- idempotency?: IdempotencyStore; /** QueryCache store (e.g., RedisCacheStore). Default: MemoryCacheStore. */
406
+ /** Event transport (e.g., RedisEventTransport) */
407
+ events?: EventTransport;
408
+ /** Cache store (e.g., RedisCacheStore) */
409
+ cache?: CacheStore;
410
+ /** Idempotency store (e.g., RedisIdempotencyStore) */
411
+ idempotency?: IdempotencyStore;
412
+ /** QueryCache store (e.g., RedisCacheStore). Default: MemoryCacheStore. */
295
413
  queryCache?: CacheStore;
296
414
  };
297
415
  /** Fastify logger configuration */
@@ -315,10 +433,50 @@ interface CreateAppOptions {
315
433
  * ```
316
434
  */
317
435
  debug?: boolean | string;
318
- /** Trust proxy headers (X-Forwarded-For, etc.) */
319
- trustProxy?: boolean;
436
+ /**
437
+ * Trust proxy headers (X-Forwarded-For, etc.). Pass-through to Fastify's
438
+ * `trustProxy` server option — all its forms are supported, not just the
439
+ * boolean:
440
+ *
441
+ * - `true` — trust ALL proxies (only safe when the app is never directly
442
+ * reachable; a spoofed `X-Forwarded-For` otherwise forges `request.ip`,
443
+ * which feeds rate-limit keys and audit logs)
444
+ * - `1` — trust exactly one hop (typical single LB / reverse proxy)
445
+ * - `'10.0.0.0/8'` / `['10.0.0.0/8', '172.16.0.0/12']` — trust only these
446
+ * CIDR ranges
447
+ *
448
+ * @example
449
+ * ```ts
450
+ * createApp({ preset: 'production', trustProxy: 1 }); // one LB hop
451
+ * createApp({ preset: 'production', trustProxy: '10.0.0.0/8' }); // VPC proxies
452
+ * ```
453
+ */
454
+ trustProxy?: boolean | string | string[] | number;
320
455
  /** Fastify plugin/onReady timeout in ms (default: 10_000). Raise for slow boot work (index materialisation, WAL replay, external warm-up). */
321
456
  pluginTimeout?: number;
457
+ /**
458
+ * Max time in ms the server waits to RECEIVE an entire request
459
+ * (headers + body). Pass-through to Fastify's `requestTimeout` (Node's
460
+ * `server.requestTimeout`). Default `0` defers to Node's own 300s bound
461
+ * — slow-loris requests are already cut off there, so arc doesn't
462
+ * override; tighten (e.g. `30_000`) for APIs that never accept slow
463
+ * uploads. Response duration (SSE, streaming) is NOT affected.
464
+ */
465
+ requestTimeout?: number;
466
+ /**
467
+ * Max time in ms to establish a connection before the socket is
468
+ * destroyed. Pass-through to Fastify's `connectionTimeout` (Node's
469
+ * `server.timeout`). Default `0` = no limit (Node's `headersTimeout`
470
+ * ~60s still bounds idle pre-request sockets).
471
+ */
472
+ connectionTimeout?: number;
473
+ /**
474
+ * Keep-alive idle timeout in ms (Node's `server.keepAliveTimeout`,
475
+ * default 72_000). MUST be longer than the idle timeout of any load
476
+ * balancer in front of the app (ALB default 60s) or the LB reuses
477
+ * sockets the server just closed → intermittent 502s.
478
+ */
479
+ keepAliveTimeout?: number;
322
480
  /**
323
481
  * Maximum JSON body size in bytes. Pass-through to Fastify's
324
482
  * server-level `bodyLimit` option; default is Fastify's 1 MiB
@@ -442,7 +600,8 @@ interface CreateAppOptions {
442
600
  rawBody?: RawBodyOptions | false;
443
601
  /** Enable Arc plugins (requestId, health, gracefulShutdown, events, caching, sse) */
444
602
  arcPlugins?: {
445
- /** Request ID tracking (default: true) */requestId?: boolean;
603
+ /** Request ID tracking (default: true) */
604
+ requestId?: boolean;
446
605
  /**
447
606
  * Health endpoints (default: true).
448
607
  *
@@ -469,8 +628,10 @@ interface CreateAppOptions {
469
628
  * }
470
629
  * ```
471
630
  */
472
- health?: boolean | HealthOptions; /** Graceful shutdown handling (default: true) */
473
- gracefulShutdown?: boolean; /** Emit events for CRUD operations (default: true) */
631
+ health?: boolean | HealthOptions;
632
+ /** Graceful shutdown handling (default: true) */
633
+ gracefulShutdown?: boolean;
634
+ /** Emit events for CRUD operations (default: true) */
474
635
  emitEvents?: boolean;
475
636
  /**
476
637
  * Event plugin configuration. Default: true (enabled with MemoryEventTransport).
@@ -851,6 +1012,32 @@ interface CreateAppOptions {
851
1012
  * @default false
852
1013
  */
853
1014
  strictResources?: boolean;
1015
+ /**
1016
+ * Pre-boot hook — awaited BEFORE arc does anything else: before the
1017
+ * Fastify instance exists, before module thunks resolve, before resource
1018
+ * discovery imports a single host file.
1019
+ *
1020
+ * The canonical slot for the DB connection and anything that
1021
+ * module-EVAL-time code depends on. Engines that register Mongoose models
1022
+ * at import time (top-level `createXEngine(...)`) trigger eager
1023
+ * `createIndex()` calls from schema plugins — if the connection isn't
1024
+ * open yet those buffer and die with `buffering timed out`. Hosts used to
1025
+ * hand-orchestrate this with `await connectDatabase()` + dynamic
1026
+ * `await import(...)` tricks before calling `createApp`; with `beforeBoot`
1027
+ * the ordering is arc's contract instead:
1028
+ *
1029
+ * ```ts
1030
+ * const app = await createApp({
1031
+ * beforeBoot: async () => { await connectDatabase(); },
1032
+ * modules: [() => import('#modules/accounting.js')], // thunks resolve AFTER beforeBoot
1033
+ * resources: async () => [...],
1034
+ * });
1035
+ * ```
1036
+ *
1037
+ * Runs after option validation (config bugs fail fast without touching
1038
+ * the DB). Cleanup stays host-owned — pair with `onClose`.
1039
+ */
1040
+ beforeBoot?: () => void | Promise<void>;
854
1041
  /**
855
1042
  * Custom plugin registration — runs after Arc core (security, auth, events)
856
1043
  * but before `bootstrap` and `resources`.
@@ -895,31 +1082,5 @@ interface CreateAppOptions {
895
1082
  /** Hook called when the app is shutting down */
896
1083
  onClose?: (fastify: FastifyInstance) => void | Promise<void>;
897
1084
  }
898
- interface UnderPressureOptions {
899
- /** Expose `/_status` route for health checks (default: false) */
900
- exposeStatusRoute?: boolean;
901
- /** Event loop lag threshold in ms — requests rejected above this (default: 1000) */
902
- maxEventLoopDelay?: number;
903
- /** V8 heap usage threshold in bytes — requests rejected above this */
904
- maxHeapUsedBytes?: number;
905
- /** RSS memory threshold in bytes — requests rejected above this */
906
- maxRssBytes?: number;
907
- }
908
- interface MultipartOptions {
909
- limits?: {
910
- /** Max file size in bytes (default: Fastify default ~1MB) */fileSize?: number; /** Max number of files per request */
911
- files?: number;
912
- };
913
- }
914
- interface RawBodyOptions {
915
- /** Body field name to store raw body on (default: 'rawBody') */
916
- field?: string;
917
- /** Apply to all routes globally (default: false) */
918
- global?: boolean;
919
- /** Encoding for raw body string (default: 'utf8') */
920
- encoding?: string;
921
- /** Parse raw body before other parsers (default: false) */
922
- runFirst?: boolean;
923
- }
924
1085
  //#endregion
925
- export { CustomPluginAuthOption as a, RawBodyOptions as c, preloadResourcesAsync as d, CustomAuthenticatorOption as i, UnderPressureOptions as l, BetterAuthOption as n, JwtAuthOption as o, CreateAppOptions as r, MultipartOptions as s, AuthOption as t, preloadResources as u };
1086
+ export { AuthOption as a, CustomPluginAuthOption as c, preloadResourcesAsync as d, UnderPressureOptions as i, JwtAuthOption as l, MultipartOptions as n, BetterAuthOption as o, RawBodyOptions as r, CustomAuthenticatorOption as s, CreateAppOptions as t, preloadResources as u };