@classytic/arc 2.25.0 → 2.28.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 (48) hide show
  1. package/README.md +2 -0
  2. package/dist/{BaseController--M8EMzt0.mjs → BaseController-B65zbz9m.mjs} +4 -2
  3. package/dist/{applyPermissionResult-Tw4iXtlf.mjs → applyPermissionResult-BnyVE9eD.mjs} +10 -0
  4. package/dist/cleanup/index.d.mts +313 -0
  5. package/dist/cleanup/index.mjs +1267 -0
  6. package/dist/cli/commands/docs.mjs +1 -1
  7. package/dist/core/index.d.mts +1 -1
  8. package/dist/core/index.mjs +4 -3
  9. package/dist/core-BGa8TXJR.mjs +221 -0
  10. package/dist/{createActionRouter-B6GvAuAX.mjs → createActionRouter-C-N7z2EL.mjs} +2 -2
  11. package/dist/{preloadResources-DfyngIN9.mjs → createApp-Du5G54TM.mjs} +2 -74
  12. package/dist/{core-EUNu0u07.mjs → defineResource-2zvVVpxg.mjs} +6 -224
  13. package/dist/docs/index.mjs +1 -1
  14. package/dist/events/index.d.mts +28 -2
  15. package/dist/events/index.mjs +20 -4
  16. package/dist/factory/index.d.mts +4 -3
  17. package/dist/factory/index.mjs +3 -1
  18. package/dist/{fastify-augmentation-dPNBVeIj.d.mts → fastify-augmentation-OzYGIiU1.d.mts} +2 -1
  19. package/dist/index-CZY4EGga.d.mts +107 -0
  20. package/dist/{index-f4PTrk3B.d.mts → index-CjVg-Epg.d.mts} +1 -1
  21. package/dist/index.d.mts +2 -2
  22. package/dist/index.mjs +6 -5
  23. package/dist/integrations/mcp/index.mjs +1 -1
  24. package/dist/integrations/mcp/testing.mjs +1 -1
  25. package/dist/{mcpPlugin-Cb7olw0j.mjs → mcpPlugin-Cs6nRGCt.mjs} +3 -3
  26. package/dist/module-_-M7C5ES.mjs +29 -0
  27. package/dist/{openapi-DrLtog_B.mjs → openapi-BLV4OMXb.mjs} +1 -1
  28. package/dist/permissions/index.mjs +1 -1
  29. package/dist/{pipeline-BN5G05rd.mjs → pipeline-BGRnV-To.mjs} +1 -1
  30. package/dist/plugins/index.d.mts +2 -2
  31. package/dist/plugins/index.mjs +1 -1
  32. package/dist/plugins/tracing-entry.mjs +1 -1
  33. package/dist/{preloadResources-0oe-JSSS.d.mts → preloadResources-CM4uUaFv.d.mts} +2 -1
  34. package/dist/preloadResources-DSY5HGpS.mjs +45 -0
  35. package/dist/{realtime-Cuw_deA-.mjs → realtime-DEuDu9io.mjs} +1 -1
  36. package/dist/{registerRoute-v0Wb6Plt.mjs → registerRoute-DTp18Ah_.mjs} +1 -1
  37. package/dist/registry/index.d.mts +1 -1
  38. package/dist/registry/index.mjs +1 -1
  39. package/dist/{registry-vcOCtPij.mjs → registry-9km_w1aP.mjs} +15 -0
  40. package/dist/testing/cleanupStoreContract.d.mts +10 -0
  41. package/dist/testing/cleanupStoreContract.mjs +375 -0
  42. package/dist/testing/index.d.mts +3 -2
  43. package/dist/testing/index.mjs +4 -2
  44. package/dist/testing/outboxStoreContract.d.mts +13 -0
  45. package/dist/testing/outboxStoreContract.mjs +247 -0
  46. package/dist/types-C_ZZRcD6.d.mts +450 -0
  47. package/dist/{index-KUoxjIq1.d.mts → types-Cu6vVJmh.d.mts} +1 -105
  48. package/package.json +27 -12
@@ -72,6 +72,16 @@ interface MemoryEntry {
72
72
  declare class MemoryOutboxStore implements OutboxStore {
73
73
  private readonly entries;
74
74
  private readonly seenDedupeKeys;
75
+ /**
76
+ * Drop every entry (including delivered / failed / dead-lettered rows) and
77
+ * the dedupe-key memory.
78
+ *
79
+ * For tests only — it is the in-memory analogue of truncating the table, and
80
+ * exists so a suite can isolate cases against ONE store instance (the
81
+ * `reset` hook of `runOutboxStoreContract`). Not part of `OutboxStore`; a
82
+ * production store must never expose this.
83
+ */
84
+ clear(): void;
75
85
  save(event: DomainEvent, options?: OutboxWriteOptions): Promise<void>;
76
86
  getPending(limit: number): Promise<DomainEvent[]>;
77
87
  claimPending(options?: OutboxClaimOptions): Promise<DomainEvent[]>;
@@ -395,7 +405,23 @@ declare class EventOutbox {
395
405
  }
396
406
  //#endregion
397
407
  //#region src/events/repository-outbox-adapter.d.ts
398
- declare function repositoryAsOutboxStore(repository: RepositoryLike): OutboxStore;
408
+ interface RepositoryOutboxStoreOptions {
409
+ /**
410
+ * Column holding the "not claimable before" timestamp. Default
411
+ * `'visibleAt'`.
412
+ *
413
+ * Exists so a host with an EXISTING outbox table can adopt this adapter
414
+ * without renaming a column and rebuilding its claim index — the one thing
415
+ * that otherwise forces a migration on the table that guarantees delivery.
416
+ * A store using `nextVisibleAt` (with a `{status, nextVisibleAt, createdAt}`
417
+ * index) passes `{ visibleAtField: 'nextVisibleAt' }` and keeps both.
418
+ *
419
+ * The field is used in the claimable filter, on `save`, and on `fail`'s
420
+ * retry scheduling — all three stay in lockstep by construction.
421
+ */
422
+ readonly visibleAtField?: string;
423
+ }
424
+ declare function repositoryAsOutboxStore(repository: RepositoryLike, options?: RepositoryOutboxStoreOptions): OutboxStore;
399
425
  //#endregion
400
426
  //#region src/events/subscribe-helpers.d.ts
401
427
  /**
@@ -556,4 +582,4 @@ interface FastifyEventBus {
556
582
  };
557
583
  }
558
584
  //#endregion
559
- export { ARC_LIFECYCLE_EVENTS, type ArcLifecycleEvent, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, type CacheEvent, type CrudEventSuffix, type CustomValidator, type EventDefinitionInput, type EventDefinitionOutput, EventOutbox, type EventOutboxOptions, type EventPluginOptions, type EventRegistry, type EventRegistryOptions, type EventSchema, type ExponentialBackoffOptions, MemoryEventTransport, type MemoryEventTransportOptions, MemoryOutboxStore, type OutboxRelayErrorHandler, type OutboxRelayErrorKind, type PayloadOf, type RedisEventTransportOptions, type RedisLike, type RedisStreamLike, type RedisStreamTransportOptions, type RelayResult, type RetryOptions, type ValidationResult, type WrapWithBoundaryOptions, type WrapWithSchemaOptions, createDeadLetterPublisher, createEventRegistry, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, subscribeWithBoundary, subscribeWithSchema, withRetry, wrapWithBoundary, wrapWithSchema };
585
+ export { ARC_LIFECYCLE_EVENTS, type ArcLifecycleEvent, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, type CacheEvent, type CrudEventSuffix, type CustomValidator, type EventDefinitionInput, type EventDefinitionOutput, EventOutbox, type EventOutboxOptions, type EventPluginOptions, type EventRegistry, type EventRegistryOptions, type EventSchema, type ExponentialBackoffOptions, MemoryEventTransport, type MemoryEventTransportOptions, MemoryOutboxStore, type OutboxRelayErrorHandler, type OutboxRelayErrorKind, type PayloadOf, type RedisEventTransportOptions, type RedisLike, type RedisStreamLike, type RedisStreamTransportOptions, type RelayResult, type RepositoryOutboxStoreOptions, type RetryOptions, type ValidationResult, type WrapWithBoundaryOptions, type WrapWithSchemaOptions, createDeadLetterPublisher, createEventRegistry, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, subscribeWithBoundary, subscribeWithSchema, withRetry, wrapWithBoundary, wrapWithSchema };
@@ -251,6 +251,19 @@ const DEFAULT_LEASE_MS$2 = 3e4;
251
251
  var MemoryOutboxStore = class {
252
252
  entries = [];
253
253
  seenDedupeKeys = /* @__PURE__ */ new Set();
254
+ /**
255
+ * Drop every entry (including delivered / failed / dead-lettered rows) and
256
+ * the dedupe-key memory.
257
+ *
258
+ * For tests only — it is the in-memory analogue of truncating the table, and
259
+ * exists so a suite can isolate cases against ONE store instance (the
260
+ * `reset` hook of `runOutboxStoreContract`). Not part of `OutboxStore`; a
261
+ * production store must never expose this.
262
+ */
263
+ clear() {
264
+ this.entries.length = 0;
265
+ this.seenDedupeKeys.clear();
266
+ }
254
267
  async save(event, options) {
255
268
  if (!event?.type || typeof event.type !== "string") throw new InvalidOutboxEventError("event.type is required");
256
269
  if (!event.meta?.id || typeof event.meta.id !== "string") throw new InvalidOutboxEventError("event.meta.id is required");
@@ -467,7 +480,10 @@ function exponentialBackoff(options) {
467
480
  const DEFAULT_LEASE_MS$1 = 3e4;
468
481
  const DEFAULT_CLAIM_LIMIT = 100;
469
482
  const DEFAULT_PURGE_BATCH = 500;
470
- function repositoryAsOutboxStore(repository) {
483
+ /** Column this adapter reads/writes for the visibility timestamp. */
484
+ const DEFAULT_VISIBLE_AT_FIELD = "visibleAt";
485
+ function repositoryAsOutboxStore(repository, options) {
486
+ const visibleAtField = options?.visibleAtField ?? DEFAULT_VISIBLE_AT_FIELD;
471
487
  const missing = [];
472
488
  if (typeof repository.create !== "function") missing.push("create");
473
489
  if (typeof repository.getOne !== "function") missing.push("getOne");
@@ -494,7 +510,7 @@ function repositoryAsOutboxStore(repository) {
494
510
  * lease. Used by `getPending` and `claimPending` — defined once so the
495
511
  * two code paths stay in lockstep.
496
512
  */
497
- const claimableFilter = (now) => and(eq("status", "pending"), lte("visibleAt", now), or(eq("leaseOwner", null), lte("leaseExpiresAt", now)));
513
+ const claimableFilter = (now) => and(eq("status", "pending"), lte(visibleAtField, now), or(eq("leaseOwner", null), lte("leaseExpiresAt", now)));
498
514
  return {
499
515
  async save(event, options) {
500
516
  if (!event?.type || typeof event.type !== "string") throw new InvalidOutboxEventError("event.type is required");
@@ -506,7 +522,7 @@ function repositoryAsOutboxStore(repository) {
506
522
  type: event.type,
507
523
  status: "pending",
508
524
  attempts: 0,
509
- visibleAt: options?.visibleAt ?? now,
525
+ [visibleAtField]: options?.visibleAt ?? now,
510
526
  leaseOwner: null,
511
527
  leaseExpiresAt: null,
512
528
  deliveredAt: null,
@@ -592,7 +608,7 @@ function repositoryAsOutboxStore(repository) {
592
608
  const firstFailedAt = current.firstFailedAt ?? now;
593
609
  if (await r.findOneAndUpdate(filter, update({ set: {
594
610
  status: targetStatus,
595
- visibleAt,
611
+ [visibleAtField]: visibleAt,
596
612
  leaseOwner: null,
597
613
  leaseExpiresAt: null,
598
614
  lastFailedAt: now,
@@ -1,6 +1,7 @@
1
- import { _ as ResourceModule, a as orderModules, c as collectModuleScheduledJobs, d as ArcModuleInput, f as ArcModuleRegistry, g as ResourceLike, h as LoadResourcesOptions, i as resolveModule, l as collectModuleWorkflows, m as ModuleContribution, n as getModuleExports, o as subscribeModuleEventHandlers, p as EventHandlerDefinition, r as resolveContribution, s as collectModuleHealthChecks, t as defineModule, u as ArcModule, v as loadResources, w as HealthCheck } from "../index-KUoxjIq1.mjs";
2
- import "../fastify-augmentation-dPNBVeIj.mjs";
3
- import { a as RawBodyOptions, c as BetterAuthOption, d as JwtAuthOption, i as MultipartOptions, l as CustomAuthenticatorOption, n as preloadResourcesAsync, o as UnderPressureOptions, r as CreateAppOptions, s as AuthOption, t as preloadResources, u as CustomPluginAuthOption } from "../preloadResources-0oe-JSSS.mjs";
1
+ import { a as ModuleContribution, c as ResourceModule, h as HealthCheck, i as EventHandlerDefinition, l as loadResources, n as ArcModuleInput, o as LoadResourcesOptions, r as ArcModuleRegistry, s as ResourceLike, t as ArcModule } from "../types-Cu6vVJmh.mjs";
2
+ import { a as orderModules, c as collectModuleScheduledJobs, i as resolveModule, l as collectModuleWorkflows, n as getModuleExports, o as subscribeModuleEventHandlers, r as resolveContribution, s as collectModuleHealthChecks, t as defineModule } from "../index-CZY4EGga.mjs";
3
+ import "../fastify-augmentation-OzYGIiU1.mjs";
4
+ import { a as RawBodyOptions, c as BetterAuthOption, d as JwtAuthOption, i as MultipartOptions, l as CustomAuthenticatorOption, n as preloadResourcesAsync, o as UnderPressureOptions, r as CreateAppOptions, s as AuthOption, t as preloadResources, u as CustomPluginAuthOption } from "../preloadResources-CM4uUaFv.mjs";
4
5
  import { FastifyInstance, FastifyServerOptions } from "fastify";
5
6
  //#region src/factory/createApp.d.ts
6
7
  /**
@@ -1,6 +1,8 @@
1
1
  import { r as mergeHealthChecks, t as healthPlugin } from "../health-CB_m4S32.mjs";
2
- import { _ as collectModuleScheduledJobs, b as resolveModule, c as getPreset, d as workerPreset, f as defineModule, g as collectModuleHealthChecks, h as subscribeModuleEventHandlers, i as createApp, l as productionPreset, m as orderModules, n as preloadResourcesAsync, o as developmentPreset, p as getModuleExports, r as ArcFactory, s as edgePreset, t as preloadResources, u as testingPreset, v as collectModuleWorkflows, y as resolveContribution } from "../preloadResources-DfyngIN9.mjs";
2
+ import { a as edgePreset, c as testingPreset, d as subscribeModuleEventHandlers, f as collectModuleHealthChecks, g as resolveModule, h as resolveContribution, i as developmentPreset, l as workerPreset, m as collectModuleWorkflows, n as createApp, o as getPreset, p as collectModuleScheduledJobs, s as productionPreset, t as ArcFactory, u as orderModules } from "../createApp-Du5G54TM.mjs";
3
+ import { n as getModuleExports, t as defineModule } from "../module-_-M7C5ES.mjs";
3
4
  import { t as loadResources } from "../loadResources-DpU8pfjV.mjs";
5
+ import { n as preloadResourcesAsync, t as preloadResources } from "../preloadResources-DSY5HGpS.mjs";
4
6
  //#region src/factory/createWorker.ts
5
7
  async function createWorker(options, workerOptions = {}) {
6
8
  const { auth: _auth, helmet: _helmet, cors: _cors, rateLimit: _rateLimit, multipart: _multipart, rawBody: _rawBody, sensible: _sensible, underPressure: _underPressure, replyHelpers: _replyHelpers, ...runtimeOptions } = options;
@@ -1,6 +1,7 @@
1
1
  import { mn as HookSystem, z as ResourceRegistry } from "./index-DPRH0Cme.mjs";
2
2
  import { t as ExternalOpenApiPaths } from "./externalPaths-BE6wFpYv.mjs";
3
- import { f as ArcModuleRegistry, u as ArcModule, w as HealthCheck, y as ScheduleDefinition } from "./index-KUoxjIq1.mjs";
3
+ import { h as HealthCheck, r as ArcModuleRegistry, t as ArcModule, u as ScheduleDefinition } from "./types-Cu6vVJmh.mjs";
4
+ import "./index-CZY4EGga.mjs";
4
5
  import { FastifyPluginAsync } from "fastify";
5
6
  //#region src/core/arcCorePlugin.d.ts
6
7
  interface ArcCorePluginOptions {
@@ -0,0 +1,107 @@
1
+ import { a as ModuleContribution, h as HealthCheck, n as ArcModuleInput, r as ArcModuleRegistry, t as ArcModule, u as ScheduleDefinition } from "./types-Cu6vVJmh.mjs";
2
+ import { FastifyInstance } from "fastify";
3
+ //#region src/factory/module/contributions.d.ts
4
+ /**
5
+ * Collect every module's `healthChecks` in the caller's order. Fails at boot
6
+ * on a duplicate check `name` across modules, naming BOTH owning modules.
7
+ * The host's app-level checks are appended AFTER this (modules-first,
8
+ * host-last) by the caller.
9
+ */
10
+ declare function collectModuleHealthChecks(modules: readonly ArcModule[]): HealthCheck[];
11
+ /**
12
+ * Collect every module's `workflows`. Resolves factory contributions (so a
13
+ * factory can close over a container the caller has already decorated on
14
+ * `fastify`) and flattens to a single opaque array. Arc core deliberately
15
+ * does NOT validate or interpret the values — the streamline integration
16
+ * owns name/shape checks and registration. Called by the integration at init
17
+ * time, not by arc's boot pipeline (workflows depend on the integration's
18
+ * container).
19
+ */
20
+ declare function collectModuleWorkflows(fastify: FastifyInstance, modules: readonly ArcModule[]): Promise<unknown[]>;
21
+ /**
22
+ * Collect every module's `scheduledJobs`. Resolves factory contributions
23
+ * (after bootstraps) and fails at boot on a duplicate job `name` across
24
+ * modules. The caller passes the returned definitions to the canonical
25
+ * schedules plugin; factories must therefore be resolved only once.
26
+ */
27
+ declare function collectModuleScheduledJobs(fastify: FastifyInstance, modules: readonly ArcModule[]): Promise<ScheduleDefinition[]>;
28
+ //#endregion
29
+ //#region src/factory/module/lifecycle.d.ts
30
+ /**
31
+ * Subscribe every module's `eventHandlers` in dependency order (pass the
32
+ * `orderModules`-sorted list). Returns the ordered list of unsubscribe
33
+ * functions so the caller can tear them down at shutdown BEFORE module
34
+ * `onClose` (while module deps are still alive). Fails at boot on:
35
+ * - a duplicate NAMED handler across modules (attributing both owners);
36
+ * - a module declaring handlers while the event subsystem is unavailable.
37
+ * Resolves factory contributions (after bootstraps) so a handler can close over
38
+ * booted engines rather than a global getter.
39
+ */
40
+ declare function subscribeModuleEventHandlers(fastify: FastifyInstance, modules: readonly ArcModule[]): Promise<Array<() => void | Promise<void>>>;
41
+ //#endregion
42
+ //#region src/factory/module/order.d.ts
43
+ /**
44
+ * Order modules for composition by their `dependsOn` edges — a STABLE
45
+ * topological sort. Called once at the start of the bootstrap phase; every
46
+ * subsequent phase (bootstrap, resources, afterResources, and reverse-order
47
+ * onClose) iterates the returned list, so a module's declared dependencies are
48
+ * always composed before it.
49
+ *
50
+ * "Stable" = modules with no edge between them keep their original list order,
51
+ * so declaring `dependsOn` on one module never silently reorders an unrelated
52
+ * one. Backward compatible: a `modules` array with NO `dependsOn` anywhere is
53
+ * returned unchanged.
54
+ *
55
+ * Fail-fast — throws (never reorders past a broken contract) on:
56
+ * - duplicate module names (the name is the graph key)
57
+ * - a `dependsOn` name not present in the composed set
58
+ * - a self-reference (`dependsOn` includes the module's own name)
59
+ * - a dependency cycle (reports the concrete `a → b → … → a` path)
60
+ */
61
+ declare function orderModules(modules: readonly ArcModule[]): ArcModule[];
62
+ //#endregion
63
+ //#region src/factory/module/resolve.d.ts
64
+ /**
65
+ * Resolve any module-input form to a concrete `ArcModule`. A thunk is invoked
66
+ * (this is where a dynamic `import()` fires); a promise (or plain module) is
67
+ * awaited. Called once per module at the start of the bootstrap phase.
68
+ */
69
+ declare function resolveModule(input: ArcModuleInput): Promise<ArcModule>;
70
+ /** Resolve a `ModuleContribution` to a concrete array (array passthrough or factory call). */
71
+ declare function resolveContribution<T>(contribution: ModuleContribution<T> | undefined, fastify: FastifyInstance): Promise<readonly T[]>;
72
+ //#endregion
73
+ //#region src/factory/module/index.d.ts
74
+ /**
75
+ * Identity helper for authoring a typed module — mirrors `defineResource`.
76
+ * Gives inference + a single obvious construction site; does no work.
77
+ *
78
+ * `TExports` is inferred from the `bootstrap` return value, so a module
79
+ * author gets a typed public export for free:
80
+ *
81
+ * ```ts
82
+ * export const accountingModule = (deps: Deps) =>
83
+ * defineModule({
84
+ * name: "accounting",
85
+ * bootstrap: async () => createAccountingEngine(deps), // TExports inferred
86
+ * });
87
+ * ```
88
+ */
89
+ declare function defineModule<TExports = unknown>(module: ArcModule<TExports>): ArcModule<TExports>;
90
+ /**
91
+ * Typed accessor for a module's public export (its `bootstrap` return value),
92
+ * recorded at `fastify.arc.modules[name]`.
93
+ *
94
+ * Throwing, in line with the fail-fast contract (and `requireOrgId`-style
95
+ * accessors): a missing entry means the module either isn't composed, is
96
+ * composed AFTER the caller (list order = init order), or returned nothing —
97
+ * all wiring bugs that must surface at boot, not as `undefined` downstream.
98
+ *
99
+ * The type parameter is an assertion, not a proof. Two ways to type it:
100
+ * - augment `ArcModuleRegistry` once — then the name alone infers the type:
101
+ * `getModuleExports(f, "accounting")` → `AccountingEngine`.
102
+ * - or pass it inline: `getModuleExports<AccountingEngine>(f, "accounting")`.
103
+ */
104
+ declare function getModuleExports<K extends keyof ArcModuleRegistry>(fastify: FastifyInstance, name: K): ArcModuleRegistry[K];
105
+ declare function getModuleExports<TExports = unknown>(fastify: FastifyInstance, name: string): TExports;
106
+ //#endregion
107
+ export { orderModules as a, collectModuleScheduledJobs as c, resolveModule as i, collectModuleWorkflows as l, getModuleExports as n, subscribeModuleEventHandlers as o, resolveContribution as r, collectModuleHealthChecks as s, defineModule as t };
@@ -1,7 +1,7 @@
1
1
  import { C as RequestContext, Et as ActionDefinition, F as FastifyWithDecorators, H as CrudController, L as RequestWithExtras, Lt as IController, Rt as IControllerResponse, T as CrudRouterOptions, U as ResourceConfig, V as ResourceDefinition, et as RouteSchemaOptions, jt as RouteMcpConfig, tt as ArcFieldRule, ut as AggregationConfig, zt as IRequestContext } from "./index-DPRH0Cme.mjs";
2
2
  import { i as RequestScope } from "./types-DjC-KUCz.mjs";
3
3
  import { c as AnyRecord, t as PermissionCheck } from "./types-Dmw-6Qeo.mjs";
4
- import "./fastify-augmentation-dPNBVeIj.mjs";
4
+ import "./fastify-augmentation-OzYGIiU1.mjs";
5
5
  import { FastifyReply, FastifyRequest, RouteHandlerMethod } from "fastify";
6
6
  import { z } from "zod";
7
7
  import { DataAdapter } from "@classytic/repo-core/adapter";
package/dist/index.d.mts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { $t as SlugExt, A as RequestIdOptions, An as ControllerConfigurableOptions, B as defineResource, Bt as RouteHandler, C as RequestContext, D as HealthCheck, Dn as ArcListResult, E as GracefulShutdownOptions, En as ArcGetResult, F as FastifyWithDecorators, Ft as ControllerLike, H as CrudController, Jt as BaseController, L as RequestWithExtras, Lt as IController, Mn as ListResult, N as FastifyRequestExtras, Nt as RateLimitConfig, O as HealthOptions, On as ArcUpdateResult, P as FastifyWithAuth, Q as MiddlewareConfig, Qt as TreeMixin, Rt as IControllerResponse, S as QueryParserInterface, T as CrudRouterOptions, Tn as ArcDeleteResult, U as ResourceConfig, Ut as AuthPluginOptions, V as ResourceDefinition, X as CrudRouteKey, Xt as SoftDeleteMixin, Y as PresetResult, Yt as SoftDeleteExt, Z as CrudSchemas, Zt as TreeExt, a as InferAdapterDoc, c as TypedController, ct as AggMeasureShorthand, d as PaginationResult, dt as AggregationDateRangeRequirement, en as SlugMixin, et as RouteSchemaOptions, f as IntrospectionData, ft as AggregationIndexHint, g as ArcInternalMetadata, gt as AggregationsMap, h as ResourceMetadata, ht as AggregationRateLimit, i as ValidationResult, jn as ControllerConstructionOptions, k as IntrospectionPluginOptions, kn as BaseControllerOptions, l as TypedRepository, lt as AggregationCacheConfig, m as RegistryStats, mt as AggregationMaterializedResult, n as ConfigError, nn as BulkMixin, nt as FieldRule, o as InferDocType, ot as EventDefinition, p as RegistryEntry, pt as AggregationMaterializedContext, q as PresetFunction, r as ValidateOptions, rn as BaseCrudController, s as InferResourceDoc, st as AggMeasureInput, t as RouteHandlerMethod, tn as BulkExt, u as TypedResourceConfig, ut as AggregationConfig, w as ServiceContext, wn as ArcCreateResult, y as OwnershipCheck, zt as IRequestContext } from "./index-DPRH0Cme.mjs";
2
2
  import { c as AnyRecord, i as UserBase, l as ArcRequest, n as PermissionContext, p as UserOrganization, r as PermissionResult, t as PermissionCheck, u as JWTPayload } from "./types-Dmw-6Qeo.mjs";
3
3
  import { a as applyFieldWritePermissions, i as applyFieldReadPermissions, n as FieldPermissionMap, o as fields, t as FieldPermission } from "./fields-D3lhgzhD.mjs";
4
- import "./fastify-augmentation-dPNBVeIj.mjs";
5
- import { A as DEFAULT_ID_FIELD, B as MAX_FILTER_DEPTH, D as defineAggregation, F as DEFAULT_UPDATE_METHOD, G as RESERVED_QUERY_PARAMS, H as MAX_SEARCH_LENGTH, I as HOOK_OPERATIONS, K as SYSTEM_FIELDS, L as HOOK_PHASES, M as DEFAULT_MAX_LIMIT, N as DEFAULT_SORT, O as CRUD_OPERATIONS, P as DEFAULT_TENANT_FIELD, R as HookOperation, S as mergeResourceConfig, U as MUTATION_OPERATIONS, V as MAX_REGEX_LENGTH, W as MutationOperation, b as ResourceSeams, j as DEFAULT_LIMIT, k as CrudOperation, p as getControllerScope, v as defineResourceVariants, w as defineAction, x as SeamedResourceConfig, y as AdapterLike, z as HookPhase } from "./index-f4PTrk3B.mjs";
4
+ import "./fastify-augmentation-OzYGIiU1.mjs";
5
+ import { A as DEFAULT_ID_FIELD, B as MAX_FILTER_DEPTH, D as defineAggregation, F as DEFAULT_UPDATE_METHOD, G as RESERVED_QUERY_PARAMS, H as MAX_SEARCH_LENGTH, I as HOOK_OPERATIONS, K as SYSTEM_FIELDS, L as HOOK_PHASES, M as DEFAULT_MAX_LIMIT, N as DEFAULT_SORT, O as CRUD_OPERATIONS, P as DEFAULT_TENANT_FIELD, R as HookOperation, S as mergeResourceConfig, U as MUTATION_OPERATIONS, V as MAX_REGEX_LENGTH, W as MutationOperation, b as ResourceSeams, j as DEFAULT_LIMIT, k as CrudOperation, p as getControllerScope, v as defineResourceVariants, w as defineAction, x as SeamedResourceConfig, y as AdapterLike, z as HookPhase } from "./index-CjVg-Epg.mjs";
6
6
  import { A as allOf, E as DynamicPermissionMatrixConfig, F as requireAuth, I as requireOwnership, L as requireRoles, M as anyOf, N as denyAll, O as createDynamicPermissionMatrix, T as DynamicPermissionMatrix, c as publicReadAdminWrite, d as requireOrgMembership, f as requireOrgRole, h as requireTeamMembership, i as ownerWithAdminBypass, j as allowPublic, k as createOrgPermissions, l as readOnly, m as requireServiceScope, n as authenticated, o as presets_d_exports, p as requireScopeContext, r as fullPublic, s as publicRead, t as adminOnly, u as requireOrgInScope, z as when } from "./index-BMnbzvtO.mjs";
7
7
  import { a as NotFoundError, d as ValidationError, f as createDomainError, i as ForbiddenError, t as ArcError, u as UnauthorizedError } from "./errors-DHcHVAgk.mjs";
8
8
  //#region src/index.d.ts
package/dist/index.mjs CHANGED
@@ -1,11 +1,12 @@
1
1
  import { a as DEFAULT_SORT, c as HOOK_OPERATIONS, d as MAX_REGEX_LENGTH, f as MAX_SEARCH_LENGTH, h as SYSTEM_FIELDS, i as DEFAULT_MAX_LIMIT, l as HOOK_PHASES, m as RESERVED_QUERY_PARAMS, n as DEFAULT_ID_FIELD, o as DEFAULT_TENANT_FIELD, p as MUTATION_OPERATIONS, r as DEFAULT_LIMIT, s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS, u as MAX_FILTER_DEPTH } from "./constants-DNre0wRg.mjs";
2
2
  import { d as createDomainError, i as NotFoundError, l as UnauthorizedError, r as ForbiddenError, t as ArcError, u as ValidationError } from "./errors-DkO8Nk6-.mjs";
3
- import { a as BulkMixin, i as SlugMixin, n as TreeMixin, o as BaseCrudController, r as SoftDeleteMixin, t as BaseController } from "./BaseController--M8EMzt0.mjs";
4
- import { a as applyFieldWritePermissions, i as applyFieldReadPermissions, o as fields } from "./applyPermissionResult-Tw4iXtlf.mjs";
5
- import { a as defineResource, d as defineAction, i as defineResourceVariants, l as ResourceDefinition, m as defineAggregation, u as mergeResourceConfig } from "./core-EUNu0u07.mjs";
6
- import { c as getControllerScope } from "./pipeline-BN5G05rd.mjs";
3
+ import { a as BulkMixin, i as SlugMixin, n as TreeMixin, o as BaseCrudController, r as SoftDeleteMixin, t as BaseController } from "./BaseController-B65zbz9m.mjs";
4
+ import { a as applyFieldWritePermissions, i as applyFieldReadPermissions, o as fields } from "./applyPermissionResult-BnyVE9eD.mjs";
5
+ import { a as ResourceDefinition, t as defineResource } from "./defineResource-2zvVVpxg.mjs";
6
+ import { c as getControllerScope } from "./pipeline-BGRnV-To.mjs";
7
+ import { a as mergeResourceConfig, i as defineResourceVariants, o as defineAction, s as defineAggregation } from "./core-BGa8TXJR.mjs";
7
8
  import { A as requireAuth, D as anyOf, E as allowPublic, M as requireRoles, O as denyAll, P as when, T as allOf, _ as requireOrgMembership, b as requireServiceScope, c as publicReadAdminWrite, g as requireOrgInScope, h as createOrgPermissions, i as ownerWithAdminBypass, j as requireOwnership, l as readOnly, m as createDynamicPermissionMatrix, n as authenticated, o as presets_exports, r as fullPublic, s as publicRead, t as adminOnly, v as requireOrgRole, x as requireTeamMembership, y as requireScopeContext } from "./permissions-BrtiBKGs.mjs";
8
9
  //#region src/index.ts
9
- const version = "2.25.0";
10
+ const version = "2.28.0";
10
11
  //#endregion
11
12
  export { ArcError, BaseController, BaseCrudController, BulkMixin, CRUD_OPERATIONS, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, ForbiddenError, HOOK_OPERATIONS, HOOK_PHASES, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, NotFoundError, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, SlugMixin, SoftDeleteMixin, TreeMixin, UnauthorizedError, ValidationError, adminOnly, allOf, allowPublic, anyOf, applyFieldReadPermissions, applyFieldWritePermissions, authenticated, createDomainError, createDynamicPermissionMatrix, createOrgPermissions, defineAction, defineAggregation, defineResource, defineResourceVariants, denyAll, fields, fullPublic, getControllerScope, mergeResourceConfig, ownerWithAdminBypass, presets_exports as permissions, publicRead, publicReadAdminWrite, readOnly, requireAuth, requireOrgInScope, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireScopeContext, requireServiceScope, requireTeamMembership, version, when };
@@ -1,4 +1,4 @@
1
- import { a as defaultCrudDescription, c as mcpHandlerAdapter, d as toCallToolResult, f as toCallToolSuccess, h as resolveToolCollisions, i as fieldRulesToZod, l as permissionDeniedResult, m as createMcpServer, n as mcpPlugin, o as resolveCrudDescription, p as buildRequestContext, r as resourceToTools, s as invokeController, t as filterResourcesForMcp, u as toCallToolError } from "../../mcpPlugin-Cb7olw0j.mjs";
1
+ import { a as defaultCrudDescription, c as mcpHandlerAdapter, d as toCallToolResult, f as toCallToolSuccess, h as resolveToolCollisions, i as fieldRulesToZod, l as permissionDeniedResult, m as createMcpServer, n as mcpPlugin, o as resolveCrudDescription, p as buildRequestContext, r as resourceToTools, s as invokeController, t as filterResourcesForMcp, u as toCallToolError } from "../../mcpPlugin-Cs6nRGCt.mjs";
2
2
  //#region src/integrations/mcp/defineTool.ts
3
3
  /**
4
4
  * Define a type-safe MCP tool.
@@ -1,4 +1,4 @@
1
- import { m as createMcpServer, r as resourceToTools, t as filterResourcesForMcp } from "../../mcpPlugin-Cb7olw0j.mjs";
1
+ import { m as createMcpServer, r as resourceToTools, t as filterResourcesForMcp } from "../../mcpPlugin-Cs6nRGCt.mjs";
2
2
  //#region src/integrations/mcp/testing.ts
3
3
  /**
4
4
  * @classytic/arc/mcp/testing — MCP Test Utilities
@@ -1,10 +1,10 @@
1
1
  import { p as isArcError, t as ArcError } from "./errors-DkO8Nk6-.mjs";
2
2
  import { n as requireSingleHeaderValue } from "./headers-BvlQSDMB.mjs";
3
3
  import { b as isElevated, c as getOrgId, g as getUserId, x as isMember } from "./types-CSyCxl2V.mjs";
4
- import { t as BaseController } from "./BaseController--M8EMzt0.mjs";
5
- import { i as applyFieldReadPermissions, r as normalizePermissionResult, s as resolveEffectiveRoles } from "./applyPermissionResult-Tw4iXtlf.mjs";
4
+ import { t as BaseController } from "./BaseController-B65zbz9m.mjs";
5
+ import { i as applyFieldReadPermissions, r as normalizePermissionResult, s as resolveEffectiveRoles } from "./applyPermissionResult-BnyVE9eD.mjs";
6
6
  import { t as executePipeline } from "./pipe-BOLmGIxa.mjs";
7
- import { r as resolvePipelineSteps, u as buildRequestScopeProjection } from "./pipeline-BN5G05rd.mjs";
7
+ import { r as resolvePipelineSteps, u as buildRequestScopeProjection } from "./pipeline-BGRnV-To.mjs";
8
8
  import { n as validateAggregations } from "./validate-Bk44F2yT.mjs";
9
9
  import { n as executeAggregation } from "./buildHandler-Dtwd8fdz.mjs";
10
10
  import { t as pluralize } from "./pluralize-BhrgwZb9.mjs";
@@ -0,0 +1,29 @@
1
+ //#region src/factory/module/index.ts
2
+ /**
3
+ * Identity helper for authoring a typed module — mirrors `defineResource`.
4
+ * Gives inference + a single obvious construction site; does no work.
5
+ *
6
+ * `TExports` is inferred from the `bootstrap` return value, so a module
7
+ * author gets a typed public export for free:
8
+ *
9
+ * ```ts
10
+ * export const accountingModule = (deps: Deps) =>
11
+ * defineModule({
12
+ * name: "accounting",
13
+ * bootstrap: async () => createAccountingEngine(deps), // TExports inferred
14
+ * });
15
+ * ```
16
+ */
17
+ function defineModule(module) {
18
+ return module;
19
+ }
20
+ function getModuleExports(fastify, name) {
21
+ const modules = fastify.arc?.modules;
22
+ if (!modules || !Object.hasOwn(modules, name)) {
23
+ const available = modules ? Object.keys(modules) : [];
24
+ throw new Error(`[arc] no public export recorded for module "${name}".\nA module's export is its bootstrap() return value; it is readable only AFTER that module's bootstrap ran (dependsOn / list order = init order).\nModules with recorded exports: ${available.length ? available.join(", ") : "(none)"}.`);
25
+ }
26
+ return modules[name];
27
+ }
28
+ //#endregion
29
+ export { getModuleExports as n, defineModule as t };
@@ -2,7 +2,7 @@ import { n as getUserRoles } from "./userHelpers-Cg_y3fBz.mjs";
2
2
  import { n as convertRouteSchema } from "./schemaConverter-DJvodiHj.mjs";
3
3
  import { t as pluralize } from "./pluralize-BhrgwZb9.mjs";
4
4
  import { t as resolveActionPermission } from "./actionPermissions-CAphcmUG.mjs";
5
- import { t as buildActionBodySchema } from "./createActionRouter-B6GvAuAX.mjs";
5
+ import { t as buildActionBodySchema } from "./createActionRouter-C-N7z2EL.mjs";
6
6
  import fp from "fastify-plugin";
7
7
  //#region src/docs/openapi/canonical-schemas.ts
8
8
  /**
@@ -1,4 +1,4 @@
1
1
  import { n as getUserRoles, r as normalizeRoles } from "../userHelpers-Cg_y3fBz.mjs";
2
- import { a as applyFieldWritePermissions, i as applyFieldReadPermissions, o as fields, r as normalizePermissionResult, s as resolveEffectiveRoles, t as applyPermissionResult } from "../applyPermissionResult-Tw4iXtlf.mjs";
2
+ import { a as applyFieldWritePermissions, i as applyFieldReadPermissions, o as fields, r as normalizePermissionResult, s as resolveEffectiveRoles, t as applyPermissionResult } from "../applyPermissionResult-BnyVE9eD.mjs";
3
3
  import { A as requireAuth, C as requireDPoP, D as anyOf, E as allowPublic, M as requireRoles, N as roles, O as denyAll, P as when, S as requireAgentScope, T as allOf, _ as requireOrgMembership, a as permissionMatrix, b as requireServiceScope, c as publicReadAdminWrite, d as GRANT_MODES, f as modeSatisfies, g as requireOrgInScope, h as createOrgPermissions, i as ownerWithAdminBypass, j as requireOwnership, k as not, l as readOnly, m as createDynamicPermissionMatrix, n as authenticated, o as presets_exports, p as requireGrant, r as fullPublic, s as publicRead, t as adminOnly, u as createRoleHierarchy, v as requireOrgRole, w as requireMandate, x as requireTeamMembership, y as requireScopeContext } from "../permissions-BrtiBKGs.mjs";
4
4
  export { GRANT_MODES, adminOnly, allOf, allowPublic, anyOf, applyFieldReadPermissions, applyFieldWritePermissions, applyPermissionResult, authenticated, createDynamicPermissionMatrix, createOrgPermissions, createRoleHierarchy, denyAll, fields, fullPublic, getUserRoles, modeSatisfies, normalizePermissionResult, normalizeRoles, not, ownerWithAdminBypass, permissionMatrix, presets_exports as permissions, publicRead, publicReadAdminWrite, readOnly, requireAgentScope, requireAuth, requireDPoP, requireGrant, requireMandate, requireOrgInScope, requireOrgMembership, requireOrgRole, requireOwnership, requireRoles, requireScopeContext, requireServiceScope, requireTeamMembership, resolveEffectiveRoles, roles, when };
@@ -1,6 +1,6 @@
1
1
  import { n as getUserRoles } from "./userHelpers-Cg_y3fBz.mjs";
2
2
  import { b as isElevated, c as getOrgId, g as getUserId, n as PUBLIC_SCOPE, x as isMember } from "./types-CSyCxl2V.mjs";
3
- import { i as applyFieldReadPermissions, s as resolveEffectiveRoles } from "./applyPermissionResult-Tw4iXtlf.mjs";
3
+ import { i as applyFieldReadPermissions, s as resolveEffectiveRoles } from "./applyPermissionResult-BnyVE9eD.mjs";
4
4
  import { t as executePipeline } from "./pipe-BOLmGIxa.mjs";
5
5
  import { isPaginatedResult, toCanonicalList } from "@classytic/repo-core/pagination";
6
6
  //#region src/scope/projection.ts
@@ -1,8 +1,8 @@
1
1
  import { At as RouteDefinition, J as PresetHook, Q as MiddlewareConfig, et as RouteSchemaOptions } from "../index-DPRH0Cme.mjs";
2
2
  import { c as AnyRecord } from "../types-Dmw-6Qeo.mjs";
3
3
  import { i as errorHandlerPlugin, n as ErrorMapper, r as defaultIsDuplicateKeyError, t as ErrorHandlerOptions } from "../errorHandler-C6yv4ZTW.mjs";
4
- import { C as schedulesPlugin, D as healthPlugin, E as _default$3, S as _default$7, T as HealthOptions, b as ScheduleStats, w as HealthCheck, x as SchedulesPluginOptions, y as ScheduleDefinition } from "../index-KUoxjIq1.mjs";
5
- import { a as arcCorePlugin, i as _default, n as ArcCorePluginOptions, r as PluginMeta, t as ArcCore } from "../fastify-augmentation-dPNBVeIj.mjs";
4
+ import { _ as _default$3, d as ScheduleStats, f as SchedulesPluginOptions, g as HealthOptions, h as HealthCheck, m as schedulesPlugin, p as _default$7, u as ScheduleDefinition, v as healthPlugin } from "../types-Cu6vVJmh.mjs";
5
+ import { a as arcCorePlugin, i as _default, n as ArcCorePluginOptions, r as PluginMeta, t as ArcCore } from "../fastify-augmentation-OzYGIiU1.mjs";
6
6
  import { _ as CachingRule, a as MetricsCollector, c as metricsPlugin, d as _default$5, f as realtimePlugin, g as CachingOptions, h as ssePlugin, i as MetricEntry, l as ChangeOperation, m as _default$8, n as _default$9, o as MetricsOptions, p as SSEOptions, r as versioningPlugin, s as _default$4, t as VersioningOptions, u as RealtimeOptions, v as _default$1, y as cachingPlugin } from "../versioning-DXgY5BUm.mjs";
7
7
  import { t as TracingOptions } from "../tracing-CsGKQJGg.mjs";
8
8
  import { PaginatedResult } from "@classytic/repo-core/pagination";
@@ -8,7 +8,7 @@ import { a as requestIdPlugin, i as createRequestIdGenerator, n as health_defaul
8
8
  import { n as caching_default, t as cachingPlugin } from "../caching-_NJeDd1_.mjs";
9
9
  import { n as errorHandlerPlugin, t as defaultIsDuplicateKeyError } from "../errorHandler-Bq2OeEQL.mjs";
10
10
  import { n as metrics_default, t as metricsPlugin } from "../metrics-fdxI5mn_.mjs";
11
- import { n as realtime_default, t as realtimePlugin } from "../realtime-Cuw_deA-.mjs";
11
+ import { n as realtime_default, t as realtimePlugin } from "../realtime-DEuDu9io.mjs";
12
12
  import { t as replyHelpersPlugin } from "../replyHelpers-67tRawQZ.mjs";
13
13
  import { n as schedules_default, t as schedulesPlugin } from "../schedules-CjOd2MG9.mjs";
14
14
  import { n as sse_default, t as ssePlugin } from "../sse-BQ1w0dfl.mjs";
@@ -58,7 +58,7 @@ try {
58
58
  function createTracerProvider(options) {
59
59
  if (!isAvailable || !NodeTracerProvider || !BatchSpanProcessor || !OTLPTraceExporter) return null;
60
60
  const { serviceName = "@classytic/arc", serviceVersion, exporterUrl = "http://localhost:4318/v1/traces" } = options;
61
- const resolvedVersion = serviceVersion ?? "2.25.0";
61
+ const resolvedVersion = serviceVersion ?? "2.28.0";
62
62
  const exporter = new OTLPTraceExporter({ url: exporterUrl });
63
63
  const provider = new NodeTracerProvider({ resource: { attributes: {
64
64
  "service.name": serviceName,
@@ -5,7 +5,8 @@ import { a as EventTransport } from "./EventTransport-QmY7A258.mjs";
5
5
  import { t as ExternalOpenApiPaths } from "./externalPaths-BE6wFpYv.mjs";
6
6
  import { r as QueryCachePluginOptions } from "./queryCachePlugin-br4otiqF.mjs";
7
7
  import { t as ErrorHandlerOptions } from "./errorHandler-C6yv4ZTW.mjs";
8
- import { T as HealthOptions, d as ArcModuleInput, g as ResourceLike, x as SchedulesPluginOptions } from "./index-KUoxjIq1.mjs";
8
+ import { f as SchedulesPluginOptions, g as HealthOptions, n as ArcModuleInput, s as ResourceLike } from "./types-Cu6vVJmh.mjs";
9
+ import "./index-CZY4EGga.mjs";
9
10
  import { t as EventPluginOptions } from "./eventPlugin-Bg3cP2_u.mjs";
10
11
  import { g as CachingOptions, o as MetricsOptions, p as SSEOptions, t as VersioningOptions, u as RealtimeOptions } from "./versioning-DXgY5BUm.mjs";
11
12
  import { r as IdempotencyStore } from "./interface-Uk8ONJv7.mjs";
@@ -0,0 +1,45 @@
1
+ //#region src/factory/preloadResources.ts
2
+ /**
3
+ * Normalize an eager `import.meta.glob` result into a `ResourceLike[]`.
4
+ *
5
+ * Accepts either:
6
+ * - `{ import: 'default' }` form: values are the resource directly
7
+ * - default form: values are the full module — picks first export with `toPlugin()`
8
+ *
9
+ * Throws if any module doesn't yield a valid `ResourceLike`.
10
+ */
11
+ function preloadResources(globResult) {
12
+ const resources = [];
13
+ for (const [path, value] of Object.entries(globResult)) {
14
+ const resource = pickResource(value);
15
+ if (!resource) throw new Error(`preloadResources: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
16
+ resources.push(resource);
17
+ }
18
+ return resources.sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
19
+ }
20
+ /**
21
+ * Normalize a lazy `import.meta.glob` result into a `Promise<ResourceLike[]>`.
22
+ *
23
+ * Use this when resources depend on prior bootstrap (e.g., engine init) and
24
+ * cannot be evaluated at import time of the preload file.
25
+ */
26
+ async function preloadResourcesAsync(globResult) {
27
+ return (await Promise.all(Object.entries(globResult).map(async ([path, loader]) => {
28
+ const resource = pickResource(await loader());
29
+ if (!resource) throw new Error(`preloadResourcesAsync: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
30
+ return resource;
31
+ }))).sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
32
+ }
33
+ function pickResource(value) {
34
+ if (!value || typeof value !== "object") return void 0;
35
+ if (typeof value.toPlugin === "function") return value;
36
+ const mod = value;
37
+ const candidates = [
38
+ mod.default,
39
+ mod.resource,
40
+ ...Object.values(mod)
41
+ ];
42
+ for (const c of candidates) if (c && typeof c === "object" && typeof c.toPlugin === "function") return c;
43
+ }
44
+ //#endregion
45
+ export { preloadResourcesAsync as n, preloadResources as t };
@@ -3,7 +3,7 @@ import { arcLog } from "./logger/index.mjs";
3
3
  import { n as getUserRoles } from "./userHelpers-Cg_y3fBz.mjs";
4
4
  import { c as getOrgId } from "./types-CSyCxl2V.mjs";
5
5
  import { t as simpleEqualityMatcher } from "./simpleEqualityMatcher-Cg05HPsn.mjs";
6
- import { i as applyFieldReadPermissions, n as evaluateAndApplyPermission } from "./applyPermissionResult-Tw4iXtlf.mjs";
6
+ import { i as applyFieldReadPermissions, n as evaluateAndApplyPermission } from "./applyPermissionResult-BnyVE9eD.mjs";
7
7
  import { a as promoteStreamTokenToHeader } from "./streaming-BQcW6_Kq.mjs";
8
8
  import { t as openSseStream } from "./sseStream-BQ7Qaynp.mjs";
9
9
  import fp from "fastify-plugin";
@@ -1,5 +1,5 @@
1
1
  import { f as createError } from "./errors-DkO8Nk6-.mjs";
2
- import { n as evaluateAndApplyPermission } from "./applyPermissionResult-Tw4iXtlf.mjs";
2
+ import { n as evaluateAndApplyPermission } from "./applyPermissionResult-BnyVE9eD.mjs";
3
3
  //#region src/core/middlewares/permissions.ts
4
4
  /**
5
5
  * Build a PermissionContext from a Fastify request. Extracted so the CRUD
@@ -1,5 +1,5 @@
1
1
  import { K as ResolvedTenantPurge, R as RegisterOptions, V as ResourceDefinition, X as CrudRouteKey, k as IntrospectionPluginOptions, p as RegistryEntry, z as ResourceRegistry } from "../index-DPRH0Cme.mjs";
2
- import "../fastify-augmentation-dPNBVeIj.mjs";
2
+ import "../fastify-augmentation-OzYGIiU1.mjs";
3
3
  import { FastifyPluginAsync } from "fastify";
4
4
  import { TenantPurgeProgress, TenantPurgeStrategy } from "@classytic/repo-core/repository";
5
5
  //#region src/registry/assertNoTenantData.d.ts
@@ -1,3 +1,3 @@
1
- import { a as cascadeDeleteForOrganization, c as assertNoTenantData, i as introspectionPlugin_default, n as buildResourceManifestFromRegistry, o as getCascadingResources, r as introspectionPlugin, s as getCascadingResourcesWithMetadata, t as buildResourceManifest } from "../registry-vcOCtPij.mjs";
1
+ import { a as cascadeDeleteForOrganization, c as assertNoTenantData, i as introspectionPlugin_default, n as buildResourceManifestFromRegistry, o as getCascadingResources, r as introspectionPlugin, s as getCascadingResourcesWithMetadata, t as buildResourceManifest } from "../registry-9km_w1aP.mjs";
2
2
  import { t as ResourceRegistry } from "../ResourceRegistry-y9Hnypus.mjs";
3
3
  export { ResourceRegistry, assertNoTenantData, buildResourceManifest, buildResourceManifestFromRegistry, cascadeDeleteForOrganization, getCascadingResources, getCascadingResourcesWithMetadata, introspectionPlugin_default as introspectionPlugin, introspectionPlugin as introspectionPluginFn };
@@ -79,6 +79,9 @@ async function assertNoTenantData(registry, options) {
79
79
  *
80
80
  * - `skip` strategy: returns `path: 'skipped'` without touching the repo.
81
81
  * - `purgeByField` present: routes through it (chunked, progress, abort).
82
+ * - `purgeByField` absent + `requireChunked`: returns `path: 'unsupported'`
83
+ * (`arc.purge.chunked_required`) — authoritative cleanup never runs the
84
+ * unbounded fallback.
82
85
  * - `purgeByField` absent + hard strategy: legacy `deleteMany` fallback.
83
86
  * - `purgeByField` absent + non-hard strategy: returns `path: 'unsupported'`
84
87
  * with a clear error — soft/anonymize require the new primitive.
@@ -111,6 +114,18 @@ async function purgeResource(resourceName, tenantField, organizationId, resolved
111
114
  ...result.error ? { error: result.error } : {}
112
115
  };
113
116
  }
117
+ if (options.requireChunked) return {
118
+ resource: resourceName,
119
+ tenantField,
120
+ strategy,
121
+ processed: 0,
122
+ ok: false,
123
+ path: "unsupported",
124
+ error: {
125
+ code: "arc.purge.chunked_required",
126
+ message: `Resource '${resourceName}' requires chunked purge for authoritative cleanup, but its adapter repository does not implement \`purgeByField\`. The legacy unbounded \`deleteMany\` fallback is disabled here (no chunking / progress / abort). Upgrade the adapter (@classytic/mongokit ≥ 3.13.4 / @classytic/sqlitekit ≥ 0.3.4).`
127
+ }
128
+ };
114
129
  if (strategy.type !== "hard") return {
115
130
  resource: resourceName,
116
131
  tenantField,
@@ -0,0 +1,10 @@
1
+ import { D as CleanupRunStore } from "../types-C_ZZRcD6.mjs";
2
+ //#region src/testing/cleanupStoreContract.d.ts
3
+ interface CleanupRunStoreContractSetupResult {
4
+ store: CleanupRunStore;
5
+ teardown?: () => Promise<void>;
6
+ }
7
+ type CleanupRunStoreContractSetup = () => Promise<CleanupRunStoreContractSetupResult>;
8
+ declare function runCleanupRunStoreContract(name: string, setup: CleanupRunStoreContractSetup): void;
9
+ //#endregion
10
+ export { CleanupRunStoreContractSetup, CleanupRunStoreContractSetupResult, runCleanupRunStoreContract };