@classytic/arc 2.33.0 → 2.35.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 (184) hide show
  1. package/dist/{BaseController-CaQC5EXX.mjs → BaseController-C-pofd0v.mjs} +131 -16
  2. package/dist/{EventTransport-tq4CEB-q.mjs → EventTransport-BrPeGUil.mjs} +1 -1
  3. package/dist/{HookSystem-DqXkMXkB.mjs → HookSystem-LlPm3Anm.mjs} +6 -4
  4. package/dist/{ResourceRegistry-DoALUlOO.mjs → ResourceRegistry-yuD1Ra53.mjs} +2 -2
  5. package/dist/{pipeline-q69DZzgG.mjs → actionPermissions-LFuOra7W.mjs} +30 -7
  6. package/dist/audit/index.d.mts +2 -2
  7. package/dist/audit/index.mjs +2 -2
  8. package/dist/auth/audit.mjs +1 -1
  9. package/dist/auth/index.d.mts +2 -2
  10. package/dist/auth/index.mjs +6 -6
  11. package/dist/auth/redis-session.mjs +1 -1
  12. package/dist/{authorizationDecision-B3mP_QTy.mjs → authorizationDecision-DYXBBgtg.mjs} +6 -2
  13. package/dist/{base-Csjc2oF-.d.mts → base-GZU3LlzP.d.mts} +1 -1
  14. package/dist/{buildHandler-DLE9xX-V.mjs → buildHandler-Do9aMIvL.mjs} +4 -4
  15. package/dist/cache/index.mjs +4 -4
  16. package/dist/{caching-BcGmvnHh.mjs → caching-cN5X9VEL.mjs} +2 -2
  17. package/dist/cleanup/index.d.mts +2 -2
  18. package/dist/cleanup/index.mjs +4 -4
  19. package/dist/cli/commands/describe.d.mts +1 -1
  20. package/dist/cli/commands/describe.mjs +3 -3
  21. package/dist/cli/commands/docs.mjs +3 -3
  22. package/dist/cli/commands/doctor.mjs +1 -1
  23. package/dist/cli/commands/generate.mjs +2 -2
  24. package/dist/cli/commands/init.mjs +2 -2
  25. package/dist/cli/commands/introspect.mjs +2 -2
  26. package/dist/cli/index.mjs +3 -3
  27. package/dist/{constants-BxCoxmiN.mjs → constants-BhzLXCfN.mjs} +1 -1
  28. package/dist/context/index.d.mts +16 -1
  29. package/dist/context/index.mjs +64 -4
  30. package/dist/core/index.d.mts +3 -3
  31. package/dist/core/index.mjs +8 -8
  32. package/dist/{core-dxhcHhUD.mjs → core-DdEvmmkY.mjs} +3 -3
  33. package/dist/{createActionRouter-DVpoA0do.mjs → createActionRouter-EYWgd0ly.mjs} +27 -64
  34. package/dist/{createAggregationRouter-B_B48JZ-.mjs → createAggregationRouter-ByOK9Iev.mjs} +7 -7
  35. package/dist/defineAggregation-DoIijIFq.mjs +25 -0
  36. package/dist/{defineResource-DR9vsUYy.mjs → defineResource-BOrGeocE.mjs} +62 -66
  37. package/dist/discovery/index.mjs +1 -1
  38. package/dist/docs/index.d.mts +1 -1
  39. package/dist/docs/index.mjs +3 -3
  40. package/dist/{elevation-B0tlY8y1.mjs → elevation-tGIAldco.mjs} +4 -4
  41. package/dist/encryption/index.mjs +3 -3
  42. package/dist/{errorHandler-C7xBqHsI.mjs → errorHandler-BbQ1nQdk.mjs} +3 -3
  43. package/dist/{errors-Cd_yy7aW.mjs → errors-CLid6atr.mjs} +1 -1
  44. package/dist/{eventPlugin-DSNEZWfV.d.mts → eventPlugin-B47sfCJ8.d.mts} +1 -1
  45. package/dist/{eventPlugin-Dar8qRaj.mjs → eventPlugin-ClPn5kjn.mjs} +5 -5
  46. package/dist/events/index.d.mts +2 -2
  47. package/dist/events/index.mjs +5 -5
  48. package/dist/events/transports/redis-stream-entry.mjs +1 -1
  49. package/dist/events/transports/redis.mjs +1 -1
  50. package/dist/factory/index.d.mts +5 -5
  51. package/dist/factory/index.mjs +5 -6
  52. package/dist/{fastify-augmentation-DVsTAy6X.d.mts → fastify-augmentation-CMMTL3cj.d.mts} +3 -3
  53. package/dist/{fields-CojeUkS4.mjs → fields-UtpiJO5R.mjs} +1 -1
  54. package/dist/{generate-DiB0nHPR.mjs → generate-BoAVy3_b.mjs} +2 -2
  55. package/dist/{headers-CveNO9gR.mjs → headers-DODWf7aC.mjs} +2 -2
  56. package/dist/{health-CasG2A28.mjs → health-_Zs3R_Sb.mjs} +1 -1
  57. package/dist/hooks/index.d.mts +1 -1
  58. package/dist/hooks/index.mjs +2 -2
  59. package/dist/idempotency/index.mjs +23 -58
  60. package/dist/idempotency/redis.mjs +1 -1
  61. package/dist/{index-C5NnMEKF.d.mts → index-C50BUxKs.d.mts} +1 -1
  62. package/dist/{index-BcR2bFZQ.d.mts → index-C5K78SXo.d.mts} +33 -105
  63. package/dist/{index-ugr1tQlW.d.mts → index-CJCcbADG.d.mts} +1 -1
  64. package/dist/{index-B-DvUzsr.d.mts → index-K7auNNW0.d.mts} +2 -2
  65. package/dist/{index-Brin0ODU.d.mts → index-h-mTgMqy.d.mts} +21 -31
  66. package/dist/index.d.mts +6 -6
  67. package/dist/index.mjs +12 -12
  68. package/dist/{init-mdJ-v4Xp.mjs → init-Ce4R0ZGK.mjs} +1 -1
  69. package/dist/integrations/event-gateway.mjs +2 -2
  70. package/dist/integrations/index.d.mts +2 -2
  71. package/dist/integrations/index.mjs +1 -1
  72. package/dist/integrations/jobs/index.d.mts +1 -1
  73. package/dist/integrations/jobs/index.mjs +2 -2
  74. package/dist/integrations/mcp/index.d.mts +4 -4
  75. package/dist/integrations/mcp/index.mjs +2 -2
  76. package/dist/integrations/mcp/testing.d.mts +1 -1
  77. package/dist/integrations/mcp/testing.mjs +3 -3
  78. package/dist/integrations/streamline.mjs +3 -3
  79. package/dist/integrations/webhooks.d.mts +1 -1
  80. package/dist/integrations/webhooks.mjs +11 -24
  81. package/dist/integrations/websocket-pushref-redis.mjs +1 -1
  82. package/dist/integrations/websocket-redis.mjs +1 -1
  83. package/dist/integrations/websocket.mjs +1 -1
  84. package/dist/{interface-DznJrm9R.mjs → interface-my7sxjw5.mjs} +1 -1
  85. package/dist/{jsonBody-CfGH-GxO.mjs → jsonBody-CVKA7C5m.mjs} +2 -2
  86. package/dist/{keys-WeKvY1dE.mjs → keys-GlU7XpmG.mjs} +1 -1
  87. package/dist/{loadResources-Bj-aSsXi.mjs → loadResources-DdQb0qgV.mjs} +2 -2
  88. package/dist/{loadResourcesFromEntry-DwaxNcE2.mjs → loadResourcesFromEntry-s6aGs6-0.mjs} +2 -2
  89. package/dist/logger/index.d.mts +23 -4
  90. package/dist/logger/index.mjs +50 -7
  91. package/dist/{mcpPlugin-aJg5TqHi.mjs → mcpPlugin-DkzZ7Nfx.mjs} +15 -16
  92. package/dist/{memory-qYSB7rMQ.mjs → memory-BwedEXau.mjs} +2 -2
  93. package/dist/{metrics-D7uchLjN.mjs → metrics-5LNPvL3P.mjs} +2 -2
  94. package/dist/middleware/index.d.mts +1 -1
  95. package/dist/middleware/index.mjs +3 -3
  96. package/dist/migrations/index.mjs +7 -4
  97. package/dist/{module-CwKsgZXp.mjs → module-Bh5OC7ww.mjs} +1 -1
  98. package/dist/{multipartBody-4w-S3-PU.mjs → multipartBody-DjilSDAT.mjs} +1 -1
  99. package/dist/{openapi-BN5cQaO2.mjs → openapi-ItLH12_L.mjs} +6 -6
  100. package/dist/outbox-admin/index.d.mts +2 -2
  101. package/dist/outbox-admin/index.mjs +4 -4
  102. package/dist/{outbox-module-CXB79bvt.mjs → outbox-module-SGIZz6oS.mjs} +14 -5
  103. package/dist/{outbox-module-D-VFq37T.d.mts → outbox-module-b1TyDGGi.d.mts} +40 -3
  104. package/dist/permissions/index.d.mts +2 -2
  105. package/dist/permissions/index.mjs +5 -5
  106. package/dist/{permissions-BAfwgBxc.mjs → permissions-CpSBlFXZ.mjs} +6 -6
  107. package/dist/{pipe-BzzoEhHj.mjs → pipe-DNOuCfAs.mjs} +2 -2
  108. package/dist/pipeline/index.d.mts +1 -1
  109. package/dist/pipeline/index.mjs +2 -2
  110. package/dist/plugins/index.d.mts +92 -7
  111. package/dist/plugins/index.mjs +168 -17
  112. package/dist/plugins/response-cache.mjs +2 -2
  113. package/dist/plugins/tracing-entry.mjs +2 -2
  114. package/dist/{pluralize-B7xf8qZF.mjs → pluralize-DGAQQKL8.mjs} +1 -1
  115. package/dist/{preloadResources-g07ldtzh.d.mts → preloadResources--ZtY9pe9.d.mts} +54 -270
  116. package/dist/{createApp-CLGegqiG.mjs → preloadResources-DYMzmUk1.mjs} +112 -31
  117. package/dist/presets/filesUpload.d.mts +3 -3
  118. package/dist/presets/filesUpload.mjs +5 -5
  119. package/dist/presets/index.d.mts +2 -2
  120. package/dist/presets/index.mjs +2 -2
  121. package/dist/presets/multiTenant.d.mts +1 -1
  122. package/dist/presets/multiTenant.mjs +3 -3
  123. package/dist/presets/search.d.mts +9 -47
  124. package/dist/presets/search.mjs +2 -2
  125. package/dist/{presets-D-h0uWtP.mjs → presets-Bi0Z6Iq8.mjs} +4 -4
  126. package/dist/{projection-BKM0Ui-s.mjs → projection-aevMua7Q.mjs} +2 -2
  127. package/dist/{queryCachePlugin-DhsZ0ZIc.mjs → queryCachePlugin-BI7Z88Cx.mjs} +5 -5
  128. package/dist/{rateLimitKey-XngqVNEV.mjs → rateLimitKey-pyevMUVf.mjs} +1 -1
  129. package/dist/{realtime-CTkv3O59.mjs → realtime-Cfl2bVjj.mjs} +9 -9
  130. package/dist/{rateLimit-D4-ASwsI.mjs → registerRoute-u1opU66a.mjs} +114 -3
  131. package/dist/registry/index.d.mts +2 -2
  132. package/dist/registry/index.mjs +3 -3
  133. package/dist/{registry-CLxCR5LF.mjs → registry-B4sf9Rfm.mjs} +15 -56
  134. package/dist/{renewingLease-BnWtbA2U.mjs → renewingLease-5ESPMxp0.mjs} +14 -2
  135. package/dist/{replyHelpers-BSBVCwL3.mjs → replyHelpers-2jfmMnnV.mjs} +2 -2
  136. package/dist/{requestContext-uYE35JxI.mjs → requestContext-DzOUGNdY.mjs} +1 -1
  137. package/dist/{rolldown-runtime-DNg-KNCk.mjs → rolldown-runtime-CEvvVXTC.mjs} +1 -1
  138. package/dist/{runtimeCapabilities-Dm88DaTW.mjs → runtimeCapabilities-BagWc3TI.mjs} +1 -1
  139. package/dist/{schedules-CAcZgia0.mjs → schedules-BLpZLWGF.mjs} +6 -4
  140. package/dist/{schemaConverter-XH1RZRyu.mjs → schemaConverter-D2_bHGil.mjs} +19 -32
  141. package/dist/{schemaIR-CDvclAew.mjs → schemaIR-s3-ouZmP.mjs} +2 -2
  142. package/dist/schemas/index.mjs +1 -1
  143. package/dist/scim/index.mjs +2 -2
  144. package/dist/scope/index.d.mts +1 -1
  145. package/dist/scope/index.mjs +5 -5
  146. package/dist/{scope-BaWcqBvr.mjs → scope-DgepeCv6.mjs} +4 -4
  147. package/dist/simpleEqualityMatcher-BO-sFTQf.mjs +43 -0
  148. package/dist/{sse-Cf0jUHsh.mjs → sse-DUIP1XH6.mjs} +5 -5
  149. package/dist/{sseStream-Bvpi-UXQ.mjs → sseStream-ByFGyIVr.mjs} +2 -2
  150. package/dist/{store-helpers-B88p99Z2.mjs → store-helpers-kIJCY0wI.mjs} +1 -1
  151. package/dist/{streaming-DsT1B5zJ.mjs → streaming-DOpZVht6.mjs} +1 -1
  152. package/dist/{subscribe-helpers-CLIOHsOK.mjs → subscribe-helpers-Dh2BnzJf.mjs} +12 -46
  153. package/dist/sync/changelog.mjs +1 -1
  154. package/dist/testing/cleanupStoreContract.mjs +1 -1
  155. package/dist/testing/index.d.mts +6 -6
  156. package/dist/testing/index.mjs +10 -11
  157. package/dist/testing/outboxStoreContract.mjs +1 -1
  158. package/dist/testing/storageContract.mjs +1 -1
  159. package/dist/{transactionContext-BYtWnwAS.mjs → transactionContext-BTZzEcRo.mjs} +1 -1
  160. package/dist/{typeGuards-jjKJDN13.mjs → typeGuards-B-_jqaMi.mjs} +1 -1
  161. package/dist/types/index.d.mts +3 -3
  162. package/dist/types/index.mjs +1 -1
  163. package/dist/types/storage.mjs +1 -1
  164. package/dist/{types-BcTumAz_.d.mts → types-DDD5RKTu.d.mts} +30 -87
  165. package/dist/{types-BfC83KsP.d.mts → types-DSvHH0uR.d.mts} +1 -1
  166. package/dist/{types-ChuShpCP.d.mts → types-Ddl9OjmE.d.mts} +18 -121
  167. package/dist/{types-CoqIRrkY.mjs → types-GY4kATJb.mjs} +10 -53
  168. package/dist/usage/index.d.mts +2 -2
  169. package/dist/usage/index.mjs +0 -0
  170. package/dist/{userHelpers-CWF5pbZD.mjs → userHelpers-AKcB4V03.mjs} +1 -1
  171. package/dist/utils/index.d.mts +16 -42
  172. package/dist/utils/index.mjs +11 -11
  173. package/dist/{utils-h1V_sT-t.mjs → utils-CBK9HQOI.mjs} +3 -3
  174. package/dist/{validate-CdQlz52F.mjs → validate-dosAIvQU.mjs} +2 -2
  175. package/dist/{versioning-rZbyLVD-.mjs → versioning-BbTs4pwM.mjs} +3 -3
  176. package/package.json +8 -8
  177. package/skills/arc/SKILL.md +9 -9
  178. package/skills/arc/references/api-reference.md +1 -1
  179. package/dist/actionPermissions-CUeCmpUd.mjs +0 -25
  180. package/dist/defineAggregation-Nv-DwfOf.mjs +0 -48
  181. package/dist/preloadResources-BkyqeTDz.mjs +0 -46
  182. package/dist/registerRoute-DNnd3vkz.mjs +0 -113
  183. package/dist/repositoryFilter-DKny7PDn.mjs +0 -45
  184. package/dist/simpleEqualityMatcher-Se8VrsaP.mjs +0 -69
@@ -1,14 +1,14 @@
1
1
 
2
- import { It as Authenticator } from "./index-BcR2bFZQ.mjs";
2
+ import { It as Authenticator } from "./index-C5K78SXo.mjs";
3
3
  import { r as CacheStore } from "./interface-BOk3YnTz.mjs";
4
4
  import { n as ElevationOptions } from "./elevation-BXCfVcNG.mjs";
5
5
  import { a as EventTransport } from "./EventTransport-BEVP50ka.mjs";
6
6
  import { t as ExternalOpenApiPaths } from "./externalPaths-CmGOxOtD.mjs";
7
7
  import { r as QueryCachePluginOptions } from "./queryCachePlugin-BzqxconG.mjs";
8
8
  import { t as ErrorHandlerOptions } from "./errorHandler-DI5AN2b5.mjs";
9
- import { l as ResourceLike, m as SchedulesPluginOptions, n as ArcModuleInput, v as HealthOptions } from "./types-BcTumAz_.mjs";
10
- import "./index-C5NnMEKF.mjs";
11
- import { t as EventPluginOptions } from "./eventPlugin-DSNEZWfV.mjs";
9
+ import { l as ResourceLike, m as SchedulesPluginOptions, n as ArcModuleInput, v as HealthOptions } from "./types-DDD5RKTu.mjs";
10
+ import "./index-C50BUxKs.mjs";
11
+ import { t as EventPluginOptions } from "./eventPlugin-B47sfCJ8.mjs";
12
12
  import { g as CachingOptions, o as MetricsOptions, p as SSEOptions, t as VersioningOptions, u as RealtimeOptions } from "./versioning-DEs-_hFi.mjs";
13
13
  import { r as IdempotencyStore } from "./interface-DEWNOMza.mjs";
14
14
  import { FastifyInstance, FastifyPluginAsync, FastifyReply, FastifyRequest, FastifyServerOptions } from "fastify";
@@ -331,7 +331,7 @@ interface RawBodyOptions {
331
331
  //#region src/factory/types/security.d.ts
332
332
  /**
333
333
  * Security-plugin option shapes — CORS / Helmet / rate-limit config for
334
- * `createApp()`, including plan-aware rate limiting (2.22).
334
+ * `createApp()`, including plan-aware rate limiting.
335
335
  */
336
336
  type CorsOptions = Record<string, unknown> & {
337
337
  origin?: unknown;
@@ -400,43 +400,14 @@ interface RateLimitPlanConfig {
400
400
  //#endregion
401
401
  //#region src/factory/types/app-options.d.ts
402
402
  /**
403
- * CreateApp Options
404
- *
405
- * Configuration for creating an Arc application.
403
+ * Configuration for `createApp()`.
406
404
  *
407
405
  * @example
408
406
  * ```typescript
409
- * // Minimal setup
410
- * const app = await createApp({
411
- * preset: 'development',
412
- * auth: {
413
- * type: 'jwt',
414
- * jwt: { secret: process.env.JWT_SECRET },
415
- * },
416
- * });
417
- *
418
- * // With custom authenticator
419
407
  * const app = await createApp({
420
408
  * preset: 'production',
421
- * auth: {
422
- * type: 'jwt',
423
- * jwt: { secret: process.env.JWT_SECRET },
424
- * authenticate: async (request, { jwt }) => {
425
- * // Check API key first
426
- * const apiKey = request.headers['x-api-key'];
427
- * if (apiKey) {
428
- * const result = await apiKeyService.verify(apiKey);
429
- * if (result) return { _id: result.userId, isApiKey: true };
430
- * }
431
- * // Then check JWT
432
- * const token = request.headers.authorization?.split(' ')[1];
433
- * if (token) {
434
- * const decoded = jwt.verify(token);
435
- * return userRepo.findById(decoded.id);
436
- * }
437
- * return null;
438
- * },
439
- * },
409
+ * auth: { type: 'jwt', jwt: { secret: process.env.JWT_SECRET } },
410
+ * resources: [productResource],
440
411
  * });
441
412
  * ```
442
413
  */
@@ -540,13 +511,20 @@ interface CreateAppOptions {
540
511
  * proxy's address; a boot warning fires when the production preset's
541
512
  * default is inherited without an explicit choice.
542
513
  *
514
+ * ⚠ **A hop COUNT is no longer accepted.** Fastify 5.12.1 removed `number`
515
+ * from this option and made hop-count trust fail closed, because it cannot
516
+ * validate the immediate peer — a direct client could spoof `X-Forwarded-*`
517
+ * simply by supplying enough hops. Name the proxies instead; arc warns at
518
+ * boot if a number reaches it from untyped (JavaScript) config.
519
+ *
543
520
  * @example
544
521
  * ```ts
545
- * createApp({ preset: 'production', trustProxy: 1 }); // one LB hop
546
- * createApp({ preset: 'production', trustProxy: '10.0.0.0/8' }); // VPC proxies
522
+ * createApp({ preset: 'production', trustProxy: 'loopback' }); // local proxy
523
+ * createApp({ preset: 'production', trustProxy: '10.0.0.0/8' }); // VPC proxies
524
+ * createApp({ preset: 'production', trustProxy: true }); // every hop trusted
547
525
  * ```
548
526
  */
549
- trustProxy?: boolean | string | string[] | number;
527
+ trustProxy?: boolean | string | string[];
550
528
  /** Fastify plugin/onReady timeout in ms (default: 10_000). Raise for slow boot work (index materialisation, WAL replay, external warm-up). */
551
529
  pluginTimeout?: number;
552
530
  /**
@@ -559,102 +537,33 @@ interface CreateAppOptions {
559
537
  */
560
538
  requestTimeout?: number;
561
539
  /**
562
- * Max time in ms to establish a connection before the socket is
563
- * destroyed. Pass-through to Fastify's `connectionTimeout` (Node's
564
- * `server.timeout`). Default `0` = no limit (Node's `headersTimeout`
565
- * ~60s still bounds idle pre-request sockets).
540
+ * Connection-establishment timeout in ms (Fastify `connectionTimeout`).
541
+ * Default `0` = unbounded; Node's ~60s `headersTimeout` still caps idle
542
+ * pre-request sockets.
566
543
  */
567
544
  connectionTimeout?: number;
568
545
  /**
569
- * Keep-alive idle timeout in ms (Node's `server.keepAliveTimeout`,
570
- * default 72_000). MUST be longer than the idle timeout of any load
571
- * balancer in front of the app (ALB default 60s) or the LB reuses
572
- * sockets the server just closed → intermittent 502s.
546
+ * Keep-alive idle timeout in ms (default 72_000). MUST exceed the idle
547
+ * timeout of any LB in front (ALB default 60s), or the LB reuses sockets
548
+ * the server just closed — intermittent 502s.
573
549
  */
574
550
  keepAliveTimeout?: number;
575
551
  /**
576
- * Maximum JSON body size in bytes. Pass-through to Fastify's
577
- * server-level `bodyLimit` option; default is Fastify's 1 MiB
578
- * (1_048_576 bytes). Raise for hosts shipping bulk-import / CSV ingest
579
- * / JSON-RPC batch endpoints — without this, Fastify rejects oversized
580
- * payloads with `FST_ERR_CTP_BODY_TOO_LARGE` (413) before any route
581
- * handler runs. File uploads on `multipart` routes are governed
582
- * separately by `multipart.limits.fileSize`. (2.15.1)
552
+ * Max JSON body in bytes. Fastify default 1 MiB; over it is a 413
553
+ * (`FST_ERR_CTP_BODY_TOO_LARGE`) raised BEFORE any handler runs.
554
+ * Multipart uploads are bounded separately by `multipart.limits.fileSize`.
583
555
  */
584
556
  bodyLimit?: number;
585
557
  /**
586
- * Maximum length of a single URL path parameter (in characters).
587
- *
588
- * 2.16 — Fastify's default is `100`, which silently 404s modern signed-
589
- * token URLs: HMAC tracking tokens (~250 chars), JWT-in-URL, OAuth state
590
- * parameters, password-reset / magic-link tokens, etc. Arc's default
591
- * is **400** to match the everything-is-a-signed-token reality of
592
- * production apps; hosts that need longer params can raise it further
593
- * (`1024` for very long JWTs).
594
- *
595
- * Pass-through to Fastify's `maxParamLength` constructor option — see
596
- * https://fastify.dev/docs/latest/Reference/Server/#maxparamlength.
597
- *
598
- * @example
599
- * ```ts
600
- * // App expects long HMAC tokens in the URL path
601
- * createApp({ preset: 'production', maxParamLength: 1024 });
602
- *
603
- * // Constrain tighter than arc's default for audit reasons
604
- * createApp({ preset: 'production', maxParamLength: 256 });
605
- * ```
558
+ * Max characters in one URL path param. Arc defaults to 400; Fastify's own
559
+ * 100 silently 404s signed-token URLs (HMAC ~250 chars, JWT, OAuth state).
560
+ * Raise to ~1024 for long JWTs.
606
561
  */
607
562
  maxParamLength?: number;
608
563
  /**
609
- * Auth configuration
610
- *
611
- * Set to false to disable authentication entirely.
612
- * Each auth strategy requires a `type` discriminant field.
613
- *
614
- * @example
615
- * ```typescript
616
- * // Disable auth
617
- * auth: false,
618
- *
619
- * // Arc JWT
620
- * auth: {
621
- * type: 'jwt',
622
- * jwt: { secret: process.env.JWT_SECRET },
623
- * },
624
- *
625
- * // Arc JWT + custom authenticator
626
- * auth: {
627
- * type: 'jwt',
628
- * jwt: { secret: process.env.JWT_SECRET },
629
- * authenticate: async (request, { jwt }) => {
630
- * const token = request.headers.authorization?.split(' ')[1];
631
- * if (!token) return null;
632
- * const decoded = jwt.verify(token);
633
- * return userRepo.findById(decoded.id);
634
- * },
635
- * },
636
- *
637
- * // Better Auth adapter
638
- * auth: { type: 'betterAuth', betterAuth: createBetterAuthAdapter({ auth: myBetterAuth }) },
639
- *
640
- * // Custom auth plugin
641
- * auth: {
642
- * type: 'custom',
643
- * plugin: async (fastify) => {
644
- * fastify.decorate('authenticate', async (req, reply) => { ... });
645
- * },
646
- * },
647
- *
648
- * // Custom authenticator function
649
- * auth: {
650
- * type: 'authenticator',
651
- * authenticate: async (request, reply) => {
652
- * const session = await validateSession(request);
653
- * if (!session) reply.code(401).send({ error: 'Unauthorized' });
654
- * request.user = session.user;
655
- * },
656
- * },
657
- * ```
564
+ * Auth configuration, or `false` to disable entirely. Each strategy is a
565
+ * discriminated union on `type` (`'jwt'` | `'better-auth'` | custom); see
566
+ * [[auth]] for the strategy matrix and `authenticate` override.
658
567
  */
659
568
  auth?: AuthOption;
660
569
  /**
@@ -886,162 +795,37 @@ interface CreateAppOptions {
886
795
  */
887
796
  replyHelpers?: boolean;
888
797
  /**
889
- * Serialize `bigint` values in JSON responses.
890
- *
891
- * `JSON.stringify` throws on `bigint` by default. This option installs a
892
- * `preSerialization` hook that converts them on the way out:
893
- *
894
- * - `false` (default) — no conversion. JSON serialization throws if a
895
- * `bigint` reaches the wire. Safest when no codepath produces bigints.
896
- * - `'string'` — **recommended for IDs / money / counters / ledgers.**
897
- * Converts every `bigint` to a decimal string. Lossless: every digit
898
- * survives the wire. Clients parse with `BigInt(value)` to reconstitute.
899
- * - `'number'` — converts every `bigint` to `Number(value)`.
900
- * **Lossy above 2^53 - 1 (`Number.MAX_SAFE_INTEGER` = 9007199254740991).**
901
- * Use ONLY when you've audited the value range — e.g. small enums,
902
- * bounded counters that physically can't exceed the safe range. For
903
- * anything that could be an ID, monetary amount, or unbounded counter,
904
- * `Number()` corruption silently rounds digits and there is no
905
- * recovery. arc emits a one-shot startup warning when you opt into
906
- * this mode.
907
- * - `true` — back-compat alias for `'number'`. Will be removed in a
908
- * future major. Migrate to `'string'` (lossless) or explicit
909
- * `'number'` (acknowledges the precision risk).
910
- *
911
- * Default: `false` (opt-in — most apps don't use bigint).
798
+ * Serialize `bigint` in JSON responses — `JSON.stringify` throws on it.
912
799
  *
913
- * @example
914
- * ```typescript
915
- * // Lossless — recommended path
916
- * const app = await createApp({
917
- * serializeBigInt: 'string',
918
- * });
919
- * // { totalSatoshis: "9007199254740999" }
920
- *
921
- * // Lossy — only when value range is bounded
922
- * const app = await createApp({
923
- * serializeBigInt: 'number',
924
- * });
925
- * // { totalSatoshis: 9007199254740999 } // precision lost above MAX_SAFE_INTEGER
926
- * });
927
- * ```
800
+ * - `false` (default) — no conversion; a bigint reaching the wire throws.
801
+ * - `'string'` — lossless decimal string. The choice for IDs, money,
802
+ * counters, ledgers. Clients reconstitute with `BigInt(value)`.
803
+ * - `'number'` — LOSSY above `Number.MAX_SAFE_INTEGER` (2^53-1). Only for
804
+ * an audited, bounded range.
928
805
  */
929
806
  serializeBigInt?: boolean | "number" | "string";
930
807
  /**
931
- * Domain modules — compose whole domains into the app, one entry each.
932
- *
933
- * Where a `resources` entry is a single route group, a module is a self-
934
- * contained domain package's full contribution (engine init + resources +
935
- * post-wiring) bundled as one `ArcModule`. A package exports
936
- * `createXModule(deps): ArcModule`; the host lists it here instead of hand-
937
- * threading the package's pieces across `bootstrap` / `resources` /
938
- * `afterResources`.
939
- *
940
- * Modules are pure sugar over the existing lifecycle — each expands into the
941
- * SAME phases, in `dependsOn` order, running BEFORE the app-level entry in
942
- * every phase:
943
- * ```
944
- * app plugins() → modules[].plugins (infra registration)
945
- * modules[].bootstrap → options.bootstrap
946
- * modules[].resources → options.resources / resourceDir (see note ↓)
947
- * modules[].afterResources → options.afterResources
948
- * modules[].onClose (REVERSE order) → options.onClose (one hook; see below)
949
- * ```
950
- * RESOURCES — two orderings, both intentional: app `resources`/`resourceDir`
951
- * RESOLVE first (semantics untouched), but module resources REGISTER first
952
- * (prepended, so their routes mount ahead of app resources). Teardown runs
953
- * module `onClose` (reverse composition order) then app `onClose` in a single
954
- * close hook, so module teardown (flush outboxes, drain queues, close a
955
- * module-owned connection) sees live shared infra — the app/plugin
956
- * connections close last. Module resources flow through arc's normal
957
- * registration (prefix, dedup, docs, audit) — they are not special-cased.
958
- *
959
- * Entries may be eager modules, promises, or **thunks of dynamic imports**
960
- * (the `next/dynamic` idea, backend-shaped) — thunks resolve once at boot,
961
- * so region/tier-gated packs are only imported when selected:
962
- *
963
- * @example
964
- * ```ts
965
- * const taxPack = region === 'BD'
966
- * ? () => import('@classytic/bd-tax/module').then((m) => m.createBdTaxModule(deps))
967
- * : () => import('@classytic/us-tax/module').then((m) => m.createUsTaxModule(deps));
968
- *
969
- * const app = await createApp({
970
- * resourcePrefix: '/api/v1',
971
- * modules: [accountingModule({ permissions }), taxPack],
972
- * resources: [healthResource], // app-local resources still compose alongside
973
- * });
974
- * ```
808
+ * Domain modules — one entry per self-contained domain package.
975
809
  *
976
- * See `wiki/modules.md` for the full design (authoring convention, events/
977
- * outbox integration, microservices path).
810
+ * A `resources` entry is a route group; an `ArcModule` is a package's whole
811
+ * contribution (engine init + resources + post-wiring), so the host stops
812
+ * hand-threading pieces across `bootstrap` / `resources` / `afterResources`.
813
+ * Modules expand into the SAME lifecycle phases in `dependsOn` order,
814
+ * always before the app-level entry for that phase. See [[modules]].
978
815
  */
979
816
  modules?: ReadonlyArray<ArcModuleInput>;
980
817
  /**
981
- * Resources to register automatically. Accepts two shapes:
818
+ * Resources to register. An ARRAY is resolved at module import, so every
819
+ * adapter must be constructible synchronously. A FACTORY (sync or async,
820
+ * receiving the Fastify instance) is resolved after `bootstrap[]` and before
821
+ * routes are wired — the answer when an adapter depends on an engine that
822
+ * boots asynchronously, since `defineResource()` then runs with that engine
823
+ * already live. Boot order lives in CLAUDE.md; do not restate it here.
982
824
  *
983
- * 1. **Array** — each resource's `.toPlugin()` is called and registered.
984
- * Defined at module-import time, so the resource's adapter must be
985
- * constructible without any async state.
986
- *
987
- * 2. **Factory function** (sync or async) — called AFTER `bootstrap[]`
988
- * but BEFORE routes are wired. Use this when a resource's adapter
989
- * depends on an engine / singleton that boots asynchronously
990
- * (e.g. `await ensureCatalogEngine()` / `await createFlowEngine()`).
991
- * The factory receives the Fastify instance for symmetry with
992
- * `plugins` and `bootstrap`.
993
- *
994
- * Arc's lifecycle contract:
995
- * ```
996
- * 1. Arc core (security, auth, events)
997
- * 2. plugins() ← infra (DB, SSE, data)
998
- * 3. bootstrap[] ← domain init (engines, singletons)
999
- * 4. resources resolution ← (factory form: call it here)
1000
- * 5. resources registered ← plugins mounted on Fastify
1001
- * 6. afterResources() ← post-registration wiring
1002
- * ```
1003
- *
1004
- * The factory form is the canonical answer to "my repository lives in an
1005
- * engine that boots asynchronously." Before this shape existed, hosts had
1006
- * to write per-resource lazy-bridge adapters that awaited the engine on
1007
- * every CRUD call — pure boilerplate. With a factory, `defineResource(...)`
1008
- * runs with the engine already live, so `createMongooseAdapter(engine.models.X, engine.repositories.X)`
1009
- * works directly.
1010
- *
1011
- * @example Static array (most resources)
1012
- * ```ts
1013
- * const app = await createApp({
1014
- * resources: [productResource, orderResource, userResource],
1015
- * auth: { type: 'jwt', jwt: { secret: 'xxx' } },
1016
- * });
1017
- * ```
1018
- *
1019
- * @example Factory with async-booted engine
1020
- * ```ts
1021
- * import { createApp, defineResource } from '@classytic/arc';
1022
- * import { createMongooseAdapter } from '@classytic/mongokit/adapter';
1023
- *
1024
- * const app = await createApp({
1025
- * bootstrap: [async () => { await ensureCatalogEngine(); }],
1026
- * resources: async () => {
1027
- * const cat = await ensureCatalogEngine();
1028
- * return [
1029
- * defineResource({
1030
- * name: 'product',
1031
- * adapter: createMongooseAdapter(cat.models.Product, cat.repositories.product),
1032
- * // ...
1033
- * }),
1034
- * ];
1035
- * },
1036
- * });
1037
- * ```
1038
- *
1039
- * @example Factory delegating to auto-discovery
825
+ * @example
1040
826
  * ```ts
1041
- * const app = await createApp({
1042
- * bootstrap: [async () => { await ensureCatalogEngine(); }],
1043
- * resources: async () => loadResources(import.meta.url),
1044
- * });
827
+ * resources: [productResource] // static
828
+ * resources: async () => [defineResource({ ... })] // engine-dependent
1045
829
  * ```
1046
830
  */
1047
831
  resources?: ReadonlyArray<ResourceLike> | ((fastify: FastifyInstance) => ReadonlyArray<ResourceLike> | Promise<ReadonlyArray<ResourceLike>>);