@classytic/arc 2.10.3 → 2.11.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 (153) hide show
  1. package/README.md +1 -1
  2. package/dist/{BaseController-CbKKIflT.mjs → BaseController-JNV08qOT.mjs} +595 -537
  3. package/dist/{queryCachePlugin-BKbWjgDG.d.mts → QueryCache-DOBNHBE0.d.mts} +2 -32
  4. package/dist/actionPermissions-C8YYU92K.mjs +22 -0
  5. package/dist/adapters/index.d.mts +2 -2
  6. package/dist/adapters/index.mjs +1 -1
  7. package/dist/{adapters-BXY4i-hw.mjs → adapters-D0tT2Tyo.mjs} +54 -0
  8. package/dist/audit/index.d.mts +2 -2
  9. package/dist/audit/index.mjs +15 -17
  10. package/dist/auth/index.d.mts +4 -4
  11. package/dist/auth/index.mjs +3 -3
  12. package/dist/auth/redis-session.d.mts +1 -1
  13. package/dist/{betterAuthOpenApi-BBRVhjQN.mjs → betterAuthOpenApi-DwxtK3uG.mjs} +1 -1
  14. package/dist/cache/index.d.mts +3 -2
  15. package/dist/cache/index.mjs +3 -3
  16. package/dist/cli/commands/docs.mjs +2 -2
  17. package/dist/cli/commands/generate.mjs +37 -27
  18. package/dist/cli/commands/init.mjs +47 -34
  19. package/dist/cli/commands/introspect.mjs +1 -1
  20. package/dist/context/index.d.mts +58 -0
  21. package/dist/context/index.mjs +2 -0
  22. package/dist/core/index.d.mts +3 -3
  23. package/dist/core/index.mjs +4 -3
  24. package/dist/core-DXdSSFW-.mjs +1037 -0
  25. package/dist/createActionRouter-BwaSM0No.mjs +166 -0
  26. package/dist/{createApp-BuvPma24.mjs → createApp-DvNYEhpb.mjs} +118 -36
  27. package/dist/docs/index.d.mts +2 -2
  28. package/dist/docs/index.mjs +1 -1
  29. package/dist/{elevation-C7hgL_aI.mjs → elevation-DOFoxoDs.mjs} +1 -1
  30. package/dist/errorHandler-Co3lnVmJ.d.mts +114 -0
  31. package/dist/{eventPlugin-DCUjuiQT.mjs → eventPlugin--5HIkdPU.mjs} +1 -1
  32. package/dist/{eventPlugin-CxWgpd6K.d.mts → eventPlugin-CUNjYYRY.d.mts} +1 -1
  33. package/dist/events/index.d.mts +4 -4
  34. package/dist/events/index.mjs +69 -51
  35. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  36. package/dist/events/transports/redis.d.mts +1 -1
  37. package/dist/factory/index.d.mts +1 -1
  38. package/dist/factory/index.mjs +2 -2
  39. package/dist/{fields-Lo1VUDpt.d.mts → fields-C8Y0XLAu.d.mts} +1 -1
  40. package/dist/hooks/index.d.mts +1 -1
  41. package/dist/hooks/index.mjs +1 -1
  42. package/dist/idempotency/index.d.mts +3 -3
  43. package/dist/idempotency/index.mjs +38 -27
  44. package/dist/idempotency/redis.d.mts +1 -1
  45. package/dist/{index-ChIw3776.d.mts → index-BYCqHCVu.d.mts} +4 -4
  46. package/dist/{index-Cl0uoKd5.d.mts → index-Cm0vUrr_.d.mts} +2100 -1688
  47. package/dist/{index-DStwgFUK.d.mts → index-DAushRTt.d.mts} +29 -10
  48. package/dist/index-DsJ1MNfC.d.mts +1179 -0
  49. package/dist/{index-8qw4y6ff.d.mts → index-t8pLpPFW.d.mts} +13 -10
  50. package/dist/index.d.mts +7 -251
  51. package/dist/index.mjs +8 -128
  52. package/dist/integrations/event-gateway.d.mts +2 -2
  53. package/dist/integrations/event-gateway.mjs +1 -1
  54. package/dist/integrations/index.d.mts +2 -2
  55. package/dist/integrations/mcp/index.d.mts +2 -2
  56. package/dist/integrations/mcp/index.mjs +1 -1
  57. package/dist/integrations/mcp/testing.d.mts +1 -1
  58. package/dist/integrations/mcp/testing.mjs +1 -1
  59. package/dist/integrations/streamline.d.mts +46 -5
  60. package/dist/integrations/streamline.mjs +50 -21
  61. package/dist/integrations/websocket-redis.d.mts +1 -1
  62. package/dist/integrations/websocket.d.mts +2 -154
  63. package/dist/integrations/websocket.mjs +292 -224
  64. package/dist/{keys-qcD-TVJl.mjs → keys-CARyUjiR.mjs} +2 -0
  65. package/dist/{loadResources-BAzJItAJ.mjs → loadResources-YNwKHvRA.mjs} +3 -1
  66. package/dist/logger/index.d.mts +81 -0
  67. package/dist/{logger-DLg8-Ueg.mjs → logger/index.mjs} +1 -6
  68. package/dist/middleware/index.d.mts +109 -0
  69. package/dist/middleware/index.mjs +70 -0
  70. package/dist/multipartBody-CvTR1Un6.mjs +123 -0
  71. package/dist/{openapi-B5F8AddX.mjs → openapi-C0L9ar7m.mjs} +9 -7
  72. package/dist/org/index.d.mts +2 -2
  73. package/dist/permissions/index.d.mts +2 -2
  74. package/dist/permissions/index.mjs +1 -3
  75. package/dist/{permissions-Dk6mshja.mjs → permissions-B4vU9L0Q.mjs} +220 -2
  76. package/dist/pipe-DVoIheVC.mjs +62 -0
  77. package/dist/pipeline/index.d.mts +62 -0
  78. package/dist/pipeline/index.mjs +53 -0
  79. package/dist/plugins/index.d.mts +25 -5
  80. package/dist/plugins/index.mjs +10 -10
  81. package/dist/plugins/response-cache.mjs +1 -1
  82. package/dist/plugins/tracing-entry.d.mts +1 -1
  83. package/dist/plugins/tracing-entry.mjs +42 -24
  84. package/dist/presets/filesUpload.d.mts +4 -4
  85. package/dist/presets/filesUpload.mjs +255 -1
  86. package/dist/presets/index.d.mts +1 -1
  87. package/dist/presets/index.mjs +2 -2
  88. package/dist/presets/multiTenant.d.mts +1 -1
  89. package/dist/presets/multiTenant.mjs +48 -8
  90. package/dist/presets/search.d.mts +2 -2
  91. package/dist/presets/search.mjs +1 -1
  92. package/dist/{presets-fLJVXdVn.mjs → presets-k604Lj99.mjs} +1 -1
  93. package/dist/queryCachePlugin-BUXBSm4F.d.mts +34 -0
  94. package/dist/{queryCachePlugin-DQCEfJis.mjs → queryCachePlugin-Bq6bO6vc.mjs} +3 -3
  95. package/dist/{redis-DqyeggCa.d.mts → redis-Cm1gnRDf.d.mts} +1 -1
  96. package/dist/{redis-stream-CakIQmwR.d.mts → redis-stream-CM8TXTix.d.mts} +1 -1
  97. package/dist/registry/index.d.mts +1 -1
  98. package/dist/registry/index.mjs +2 -2
  99. package/dist/{requestContext-xHIKedG6.mjs → requestContext-CfRkaxwf.mjs} +1 -1
  100. package/dist/{resourceToTools-BElv3xPT.mjs → resourceToTools--okX6QBr.mjs} +534 -415
  101. package/dist/routerShared-DeESFp4a.mjs +515 -0
  102. package/dist/schemaIR-BlG9bY7v.mjs +137 -0
  103. package/dist/scope/index.d.mts +2 -2
  104. package/dist/scope/index.mjs +1 -1
  105. package/dist/{sse-yBCgOLGu.mjs → sse-V7aXc3bW.mjs} +1 -1
  106. package/dist/{store-helpers-ZCSMJJAX.mjs → store-helpers-BhrzxvyQ.mjs} +4 -0
  107. package/dist/testing/index.d.mts +367 -711
  108. package/dist/testing/index.mjs +646 -1434
  109. package/dist/testing/storageContract.d.mts +1 -1
  110. package/dist/{tracing-65B51Dw3.d.mts → tracing-DokiEsuz.d.mts} +9 -4
  111. package/dist/types/index.d.mts +5 -5
  112. package/dist/types/index.mjs +1 -3
  113. package/dist/types/storage.d.mts +1 -1
  114. package/dist/{types-Co8k3NyS.d.mts → types-CgikqKAj.d.mts} +133 -21
  115. package/dist/{types-Btdda02s.d.mts → types-D9NqiYIw.d.mts} +1 -1
  116. package/dist/utils/index.d.mts +2 -898
  117. package/dist/utils/index.mjs +4 -5
  118. package/dist/utils-D3Yxnrwr.mjs +1639 -0
  119. package/dist/versioning-M9lNLhO8.d.mts +117 -0
  120. package/dist/websocket-CyJ1VIFI.d.mts +186 -0
  121. package/package.json +26 -8
  122. package/skills/arc/SKILL.md +124 -39
  123. package/skills/arc/references/testing.md +212 -183
  124. package/dist/applyPermissionResult-QhV1Pa-g.mjs +0 -37
  125. package/dist/core-CcR01lup.mjs +0 -1411
  126. package/dist/createActionRouter-Bp_5c_2b.mjs +0 -249
  127. package/dist/errorHandler-DRQ3EqfL.d.mts +0 -218
  128. package/dist/errors-CCSsMpXE.d.mts +0 -140
  129. package/dist/fields-bxkeltzz.mjs +0 -126
  130. package/dist/filesUpload-t21LS-py.mjs +0 -377
  131. package/dist/queryParser-DBqBB6AC.mjs +0 -352
  132. package/dist/types-Csi3FLfq.mjs +0 -27
  133. package/dist/utils-B2fNOD_i.mjs +0 -929
  134. /package/dist/{EventTransport-CUw5NNWe.d.mts → EventTransport-CfVEGaEl.d.mts} +0 -0
  135. /package/dist/{HookSystem-BNYKnrXF.mjs → HookSystem-CGsMd6oK.mjs} +0 -0
  136. /package/dist/{ResourceRegistry-BPd6NQDm.mjs → ResourceRegistry-DkAeAuTX.mjs} +0 -0
  137. /package/dist/{caching-CBpK_SCM.mjs → caching-CheW3m-S.mjs} +0 -0
  138. /package/dist/{elevation-C5SwtkAn.d.mts → elevation-s5ykdNHr.d.mts} +0 -0
  139. /package/dist/{errorHandler-Bb49BvPD.mjs → errorHandler-BQm8ZxTK.mjs} +0 -0
  140. /package/dist/{externalPaths-BQ8QijNH.d.mts → externalPaths-Bapitwvd.d.mts} +0 -0
  141. /package/dist/{interface-CSbZdv_3.d.mts → interface-CkkWm5uR.d.mts} +0 -0
  142. /package/dist/{interface-D218ikEo.d.mts → interface-Da0r7Lna.d.mts} +0 -0
  143. /package/dist/{memory-B5Amv9A1.mjs → memory-DikHSvWa.mjs} +0 -0
  144. /package/dist/{metrics-DuhiSEZI.mjs → metrics-Csh4nsvv.mjs} +0 -0
  145. /package/dist/{pluralize-A0tWEl1K.mjs → pluralize-BneOJkpi.mjs} +0 -0
  146. /package/dist/{registry-B3lRFBWo.mjs → registry-D63ee7fl.mjs} +0 -0
  147. /package/dist/{replyHelpers-CXtJDAZ0.mjs → replyHelpers-ByllIXXV.mjs} +0 -0
  148. /package/dist/{schemaConverter-BxFDdtXu.mjs → schemaConverter-B0oKLuqI.mjs} +0 -0
  149. /package/dist/{sessionManager-BkzVU8h2.d.mts → sessionManager-D-oNWHz3.d.mts} +0 -0
  150. /package/dist/{storage-CVk_SEn2.d.mts → storage-BwGQXUpd.d.mts} +0 -0
  151. /package/dist/{typeGuards-Cj5Rgvlg.mjs → typeGuards-CcFZXgU7.mjs} +0 -0
  152. /package/dist/{types-BD85MlEK.d.mts → types-tgR4Pt8F.d.mts} +0 -0
  153. /package/dist/{versioning-C2U_bLY0.mjs → versioning-CGPjkqAg.mjs} +0 -0
@@ -1,5 +1,4 @@
1
- import { r as CacheStore } from "./interface-D218ikEo.mjs";
2
- import { FastifyPluginAsync } from "fastify";
1
+ import { r as CacheStore } from "./interface-Da0r7Lna.mjs";
3
2
 
4
3
  //#region src/cache/QueryCache.d.ts
5
4
  /** Metadata wrapper stored in CacheStore */
@@ -39,33 +38,4 @@ declare class QueryCache {
39
38
  bumpTagVersion(tag: string): Promise<void>;
40
39
  }
41
40
  //#endregion
42
- //#region src/cache/queryCachePlugin.d.ts
43
- interface QueryCachePluginOptions {
44
- /** CacheStore instance. Default: MemoryCacheStore with default options. */
45
- store?: CacheStore;
46
- /** Global defaults for staleTime/gcTime (seconds) */
47
- defaults?: {
48
- staleTime?: number;
49
- gcTime?: number;
50
- };
51
- }
52
- interface QueryCacheDefaults {
53
- staleTime: number;
54
- gcTime: number;
55
- }
56
- /** Cross-resource invalidation rules collected from resource configs */
57
- interface CrossResourceRule {
58
- pattern: string;
59
- tags: string[];
60
- }
61
- declare module "fastify" {
62
- interface FastifyInstance {
63
- queryCache: QueryCache;
64
- queryCacheConfig: QueryCacheDefaults;
65
- /** Register cross-resource invalidation rules (called by defineResource) */
66
- registerCacheInvalidationRule?(rule: CrossResourceRule): void;
67
- }
68
- }
69
- declare const queryCachePlugin: FastifyPluginAsync<QueryCachePluginOptions>;
70
- //#endregion
71
- export { CacheEnvelope as a, QueryCache as c, queryCachePlugin as i, QueryCacheConfig as l, QueryCacheDefaults as n, CacheResult as o, QueryCachePluginOptions as r, CacheStatus as s, CrossResourceRule as t };
41
+ export { QueryCacheConfig as a, QueryCache as i, CacheResult as n, CacheStatus as r, CacheEnvelope as t };
@@ -0,0 +1,22 @@
1
+ //#region src/core/actionPermissions.ts
2
+ /**
3
+ * Return the effective `PermissionCheck` for a single action, or `undefined`
4
+ * when the resource declares no gate at any level.
5
+ *
6
+ * Callers decide what "no gate" means:
7
+ * - HTTP: boot-time throw in `normalizeActionsToRouterConfig`.
8
+ * - MCP: treated as allow (legacy) — but the HTTP fallback now fills the
9
+ * gap when `permissions.update` is set, so the MCP hole closes too.
10
+ * - OpenAPI: docs advertise the endpoint as unauthenticated.
11
+ */
12
+ function resolveActionPermission(input) {
13
+ const { action, resourcePermissions, resourceActionPermissions, globalAuth } = input;
14
+ const explicit = typeof action !== "function" && action.permissions ? action.permissions : void 0;
15
+ if (explicit) return explicit;
16
+ if (resourceActionPermissions) return resourceActionPermissions;
17
+ if (globalAuth) return globalAuth;
18
+ const updateFallback = resourcePermissions?.update;
19
+ if (updateFallback) return updateFallback;
20
+ }
21
+ //#endregion
22
+ export { resolveActionPermission as t };
@@ -1,3 +1,3 @@
1
- import { At as SchemaMetadata, Dt as FieldMetadata, Et as DataAdapter, Ot as RelationMetadata, jt as ValidationResult, kt as RepositoryLike, wt as AdapterFactory } from "../index-Cl0uoKd5.mjs";
2
- import { a as PrismaQueryParserOptions, c as MongooseAdapterOptions, d as DrizzleAdapterOptions, f as createDrizzleAdapter, i as PrismaQueryParser, l as createMongooseAdapter, n as PrismaAdapterOptions, o as createPrismaAdapter, r as PrismaQueryOptions, s as MongooseAdapter, t as PrismaAdapter, u as DrizzleAdapter } from "../index-DStwgFUK.mjs";
1
+ import { An as RelationMetadata, En as AdapterFactory, Mn as SchemaMetadata, Nn as ValidationResult, On as DataAdapter, jn as RepositoryLike, kn as FieldMetadata } from "../index-Cm0vUrr_.mjs";
2
+ import { a as PrismaQueryParserOptions, c as MongooseAdapterOptions, d as DrizzleAdapterOptions, f as createDrizzleAdapter, i as PrismaQueryParser, l as createMongooseAdapter, n as PrismaAdapterOptions, o as createPrismaAdapter, r as PrismaQueryOptions, s as MongooseAdapter, t as PrismaAdapter, u as DrizzleAdapter } from "../index-DAushRTt.mjs";
3
3
  export { AdapterFactory, DataAdapter, DrizzleAdapter, DrizzleAdapterOptions, FieldMetadata, MongooseAdapter, MongooseAdapterOptions, PrismaAdapter, PrismaAdapterOptions, PrismaQueryOptions, PrismaQueryParser, PrismaQueryParserOptions, RelationMetadata, RepositoryLike, SchemaMetadata, ValidationResult, createDrizzleAdapter, createMongooseAdapter, createPrismaAdapter };
@@ -1,2 +1,2 @@
1
- import { a as createMongooseAdapter, i as MongooseAdapter, n as PrismaQueryParser, o as DrizzleAdapter, r as createPrismaAdapter, s as createDrizzleAdapter, t as PrismaAdapter } from "../adapters-BXY4i-hw.mjs";
1
+ import { a as createMongooseAdapter, i as MongooseAdapter, n as PrismaQueryParser, o as DrizzleAdapter, r as createPrismaAdapter, s as createDrizzleAdapter, t as PrismaAdapter } from "../adapters-D0tT2Tyo.mjs";
2
2
  export { DrizzleAdapter, MongooseAdapter, PrismaAdapter, PrismaQueryParser, createDrizzleAdapter, createMongooseAdapter, createPrismaAdapter };
@@ -35,9 +35,58 @@ function mergeFieldRuleConstraints(schemas, schemaOptions) {
35
35
  if (rule.pattern != null && prop.pattern == null) prop.pattern = rule.pattern;
36
36
  if (rule.enum != null && prop.enum == null) prop.enum = rule.enum;
37
37
  if (rule.description != null && prop.description == null) prop.description = rule.description;
38
+ if (rule.nullable === true) applyNullable(prop);
38
39
  }
39
40
  }
40
41
  }
42
+ /**
43
+ * Widen a JSON Schema property to also accept `null`.
44
+ *
45
+ * Handles the three ways a property can be typed:
46
+ * - `type: 'string'` → `type: ['string', 'null']`
47
+ * - `type: [...]` → append `'null'` if missing
48
+ * - `anyOf: [...]` → append `{ type: 'null' }` branch if missing
49
+ *
50
+ * **Enum interaction:** when the widened prop also carries `enum: [...]`,
51
+ * `null` is appended to the enum list too. AJV's `enum` keyword rejects
52
+ * values not in the list regardless of the widened `type`, so
53
+ * `{ type: ['string','null'], enum: ['a','b'] }` alone would still reject
54
+ * `null`. The fix is `enum: ['a','b', null]`. (The `anyOf` branch dodges
55
+ * this entirely — each branch scopes its own enum.)
56
+ *
57
+ * No-op when the schema already admits null (don't double-wrap) or has
58
+ * no `type` / `anyOf` anchor to widen (e.g. Mixed — already accepts null).
59
+ *
60
+ * Mutates in place — callers already treat the slot schema as owned.
61
+ * Exported so adapters that walk `fieldRules` inline (mongoose fallback,
62
+ * drizzle post-process) can reuse the same widening logic.
63
+ */
64
+ function applyNullable(prop) {
65
+ if (Array.isArray(prop.anyOf)) {
66
+ if (!prop.anyOf.some((b) => b && typeof b === "object" && (b.type === "null" || b.const === null))) prop.anyOf.push({ type: "null" });
67
+ return;
68
+ }
69
+ if (Array.isArray(prop.type)) {
70
+ if (!prop.type.includes("null")) prop.type.push("null");
71
+ widenEnumToIncludeNull(prop);
72
+ return;
73
+ }
74
+ if (typeof prop.type === "string") {
75
+ prop.type = [prop.type, "null"];
76
+ widenEnumToIncludeNull(prop);
77
+ return;
78
+ }
79
+ }
80
+ /**
81
+ * Append `null` to `enum` when present. Required because AJV's `enum`
82
+ * keyword is independent of `type` — a value must appear in the enum
83
+ * array verbatim even if the widened type says null is allowed.
84
+ */
85
+ function widenEnumToIncludeNull(prop) {
86
+ if (!Array.isArray(prop.enum)) return;
87
+ if (prop.enum.includes(null)) return;
88
+ prop.enum = [...prop.enum, null];
89
+ }
41
90
  //#endregion
42
91
  //#region src/adapters/types.ts
43
92
  /**
@@ -306,6 +355,7 @@ var MongooseAdapter = class {
306
355
  if (rule.pattern != null && prop.pattern == null) prop.pattern = rule.pattern;
307
356
  if (rule.enum != null && prop.enum == null) prop.enum = rule.enum;
308
357
  if (rule.description != null && prop.description == null) prop.description = rule.description;
358
+ if (rule.nullable === true) applyNullable(prop);
309
359
  }
310
360
  if (typeInfo.isRequired && !optionalSet.has(fieldName) && !fieldRules[fieldName]?.optional) required.push(fieldName);
311
361
  }
@@ -432,6 +482,10 @@ var MongooseAdapter = class {
432
482
  baseType.type = "object";
433
483
  baseType.additionalProperties = true;
434
484
  }
485
+ if (options.default === null) {
486
+ applyNullable(baseType);
487
+ baseType.default = null;
488
+ }
435
489
  return baseType;
436
490
  }
437
491
  };
@@ -1,5 +1,5 @@
1
- import { d as UserBase } from "../fields-Lo1VUDpt.mjs";
2
- import { kt as RepositoryLike } from "../index-Cl0uoKd5.mjs";
1
+ import { jn as RepositoryLike } from "../index-Cm0vUrr_.mjs";
2
+ import { d as UserBase } from "../fields-C8Y0XLAu.mjs";
3
3
  import { FastifyPluginAsync } from "fastify";
4
4
 
5
5
  //#region src/audit/stores/interface.d.ts
@@ -1,12 +1,13 @@
1
1
  import fp from "fastify-plugin";
2
+ import { and, anyOf, eq, gte, lt, lte } from "@classytic/repo-core/filter";
2
3
  //#region src/audit/repository-audit-adapter.ts
3
4
  function repositoryAsAuditStore(repository) {
5
+ const idField = repository.idField ?? "_id";
4
6
  return {
5
7
  name: "repository",
6
8
  async log(entry) {
7
9
  const doc = {
8
- _id: entry.id,
9
- id: entry.id,
10
+ [idField]: entry.id,
10
11
  resource: entry.resource,
11
12
  documentId: entry.documentId,
12
13
  action: entry.action,
@@ -25,35 +26,32 @@ function repositoryAsAuditStore(repository) {
25
26
  },
26
27
  async purgeOlderThan(cutoff) {
27
28
  if (!repository.deleteMany) return 0;
28
- return (await repository.deleteMany({ timestamp: { $lt: cutoff } })).deletedCount ?? 0;
29
+ return (await repository.deleteMany(lt("timestamp", cutoff))).deletedCount ?? 0;
29
30
  },
30
31
  async query(opts = {}) {
31
32
  if (!repository.getAll) throw new Error("auditPlugin: repository.getAll is required for query(). It's on repo-core's MinimalRepo floor — every kit (mongokit, sqlitekit, custom) implements it.");
32
- const filter = {};
33
- if (opts.resource) filter.resource = opts.resource;
34
- if (opts.documentId) filter.documentId = opts.documentId;
35
- if (opts.userId) filter.userId = opts.userId;
36
- if (opts.organizationId) filter.organizationId = opts.organizationId;
33
+ const clauses = [];
34
+ if (opts.resource) clauses.push(eq("resource", opts.resource));
35
+ if (opts.documentId) clauses.push(eq("documentId", opts.documentId));
36
+ if (opts.userId) clauses.push(eq("userId", opts.userId));
37
+ if (opts.organizationId) clauses.push(eq("organizationId", opts.organizationId));
37
38
  if (opts.action) {
38
39
  const actions = Array.isArray(opts.action) ? opts.action : [opts.action];
39
- filter.action = actions.length === 1 ? actions[0] : { $in: actions };
40
- }
41
- if (opts.from || opts.to) {
42
- const range = {};
43
- if (opts.from) range.$gte = opts.from;
44
- if (opts.to) range.$lte = opts.to;
45
- filter.timestamp = range;
40
+ clauses.push(actions.length === 1 ? eq("action", actions[0]) : anyOf("action", actions));
46
41
  }
42
+ if (opts.from) clauses.push(gte("timestamp", opts.from));
43
+ if (opts.to) clauses.push(lte("timestamp", opts.to));
44
+ const filters = clauses.length > 0 ? and(...clauses) : void 0;
47
45
  const limit = opts.limit ?? 100;
48
46
  const page = Math.floor((opts.offset ?? 0) / limit) + 1;
49
47
  const result = await repository.getAll({
50
- filters: filter,
48
+ ...filters ? { filters } : {},
51
49
  sort: { timestamp: -1 },
52
50
  page,
53
51
  limit
54
52
  });
55
53
  return (Array.isArray(result) ? result : result.docs ?? []).map((d) => ({
56
- id: String(d._id ?? d.id ?? ""),
54
+ id: String(d[idField] ?? ""),
57
55
  resource: d.resource ?? "",
58
56
  documentId: d.documentId ?? "",
59
57
  action: d.action ?? "create",
@@ -1,7 +1,7 @@
1
- import { c as PermissionCheck } from "../fields-Lo1VUDpt.mjs";
2
- import { A as AuthHelpers, j as AuthPluginOptions } from "../index-Cl0uoKd5.mjs";
3
- import { t as ExternalOpenApiPaths } from "../externalPaths-BQ8QijNH.mjs";
4
- import { a as SessionManagerOptions, c as createSessionManager, i as SessionData, n as MemorySessionStoreOptions, o as SessionManagerResult, r as SessionCookieOptions, s as SessionStore, t as MemorySessionStore } from "../sessionManager-BkzVU8h2.mjs";
1
+ import { Ot as AuthHelpers, kt as AuthPluginOptions } from "../index-Cm0vUrr_.mjs";
2
+ import { c as PermissionCheck } from "../fields-C8Y0XLAu.mjs";
3
+ import { t as ExternalOpenApiPaths } from "../externalPaths-Bapitwvd.mjs";
4
+ import { a as SessionManagerOptions, c as createSessionManager, i as SessionData, n as MemorySessionStoreOptions, o as SessionManagerResult, r as SessionCookieOptions, s as SessionStore, t as MemorySessionStore } from "../sessionManager-D-oNWHz3.mjs";
5
5
  import { FastifyPluginAsync, FastifyReply as FastifyReply$1, FastifyRequest } from "fastify";
6
6
 
7
7
  //#region src/auth/authPlugin.d.ts
@@ -1,7 +1,7 @@
1
+ import { _ as requireTeamMembership, m as requireOrgRole, p as requireOrgMembership } from "../permissions-B4vU9L0Q.mjs";
1
2
  import { n as normalizeRoles, t as getUserRoles } from "../types-DV9WDfeg.mjs";
2
3
  import { t as ArcError } from "../errors-D5c-5BJL.mjs";
3
- import { _ as requireTeamMembership, m as requireOrgRole, p as requireOrgMembership } from "../permissions-Dk6mshja.mjs";
4
- import { n as extractBetterAuthOpenApi } from "../betterAuthOpenApi-BBRVhjQN.mjs";
4
+ import { n as extractBetterAuthOpenApi } from "../betterAuthOpenApi-DwxtK3uG.mjs";
5
5
  import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
6
6
  import fp from "fastify-plugin";
7
7
  //#region src/auth/authPlugin.ts
@@ -684,7 +684,7 @@ function createBetterAuthAdapter(options) {
684
684
  if (!fastify.hasDecorator("authenticate")) fastify.decorate("authenticate", authenticate);
685
685
  if (!fastify.hasDecorator("optionalAuthenticate")) fastify.decorate("optionalAuthenticate", optionalAuthenticate);
686
686
  if (!extractedOpenApi && openapiOpt !== false && auth.api && typeof auth.api === "object") {
687
- const { extractBetterAuthOpenApi } = await import("../betterAuthOpenApi-BBRVhjQN.mjs").then((n) => n.t);
687
+ const { extractBetterAuthOpenApi } = await import("../betterAuthOpenApi-DwxtK3uG.mjs").then((n) => n.t);
688
688
  extractedOpenApi = extractBetterAuthOpenApi(auth.api, {
689
689
  basePath,
690
690
  userFields
@@ -1,4 +1,4 @@
1
- import { i as SessionData, s as SessionStore } from "../sessionManager-BkzVU8h2.mjs";
1
+ import { i as SessionData, s as SessionStore } from "../sessionManager-D-oNWHz3.mjs";
2
2
 
3
3
  //#region src/auth/redis-session.d.ts
4
4
  /** Minimal Redis client interface — compatible with ioredis */
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
2
- import { a as toJsonSchema } from "./schemaConverter-BxFDdtXu.mjs";
2
+ import { a as toJsonSchema } from "./schemaConverter-B0oKLuqI.mjs";
3
3
  //#region src/auth/betterAuthOpenApi.ts
4
4
  var betterAuthOpenApi_exports = /* @__PURE__ */ __exportAll({ extractBetterAuthOpenApi: () => extractBetterAuthOpenApi });
5
5
  /**
@@ -1,5 +1,6 @@
1
- import { n as CacheStats, r as CacheStore, t as CacheLogger } from "../interface-D218ikEo.mjs";
2
- import { a as CacheEnvelope, c as QueryCache, i as queryCachePlugin, l as QueryCacheConfig, n as QueryCacheDefaults, o as CacheResult, r as QueryCachePluginOptions, s as CacheStatus, t as CrossResourceRule } from "../queryCachePlugin-BKbWjgDG.mjs";
1
+ import { n as CacheStats, r as CacheStore, t as CacheLogger } from "../interface-Da0r7Lna.mjs";
2
+ import { a as QueryCacheConfig, i as QueryCache, n as CacheResult, r as CacheStatus, t as CacheEnvelope } from "../QueryCache-DOBNHBE0.mjs";
3
+ import { i as queryCachePlugin, n as QueryCacheDefaults, r as QueryCachePluginOptions, t as CrossResourceRule } from "../queryCachePlugin-BUXBSm4F.mjs";
3
4
 
4
5
  //#region src/cache/keys.d.ts
5
6
  /**
@@ -1,6 +1,6 @@
1
- import { i as versionKey, n as hashParams, r as tagVersionKey, t as buildQueryKey } from "../keys-qcD-TVJl.mjs";
2
- import { t as MemoryCacheStore } from "../memory-B5Amv9A1.mjs";
3
- import { r as QueryCache, t as queryCachePlugin } from "../queryCachePlugin-DQCEfJis.mjs";
1
+ import { i as versionKey, n as hashParams, r as tagVersionKey, t as buildQueryKey } from "../keys-CARyUjiR.mjs";
2
+ import { t as MemoryCacheStore } from "../memory-DikHSvWa.mjs";
3
+ import { r as QueryCache, t as queryCachePlugin } from "../queryCachePlugin-Bq6bO6vc.mjs";
4
4
  //#region src/cache/redis.ts
5
5
  /**
6
6
  * Redis-backed cache store.
@@ -1,5 +1,5 @@
1
- import { t as ResourceRegistry } from "../../ResourceRegistry-BPd6NQDm.mjs";
2
- import { t as buildOpenApiSpec } from "../../openapi-B5F8AddX.mjs";
1
+ import { t as ResourceRegistry } from "../../ResourceRegistry-DkAeAuTX.mjs";
2
+ import { t as buildOpenApiSpec } from "../../openapi-C0L9ar7m.mjs";
3
3
  import { dirname, resolve } from "node:path";
4
4
  import { pathToFileURL } from "node:url";
5
5
  import { mkdirSync, writeFileSync } from "node:fs";
@@ -1,4 +1,4 @@
1
- import { t as pluralize } from "../../pluralize-A0tWEl1K.mjs";
1
+ import { t as pluralize } from "../../pluralize-BneOJkpi.mjs";
2
2
  import { join } from "node:path";
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
4
  //#region src/cli/commands/generate.ts
@@ -159,14 +159,18 @@ import { buildCrudSchemasFromModel } from '@classytic/mongokit/utils';
159
159
  const crudSchemas = buildCrudSchemasFromModel(${name}, {
160
160
  strictAdditionalProperties: true,
161
161
  fieldRules: {
162
- // systemManaged: excluded from create/update schemas
162
+ // systemManaged: framework stamps it — strip from body + required[]
163
163
  // deletedAt: { systemManaged: true },
164
164
  // immutable: cannot be updated after creation
165
165
  // slug: { immutable: true },
166
- // immutableAfterCreate: same as immutable (alias)
166
+ // immutableAfterCreate: alias for immutable
167
167
  // organizationId: { immutableAfterCreate: true },
168
- // optional: removed from required array
168
+ // optional: removed from required[] (properties kept)
169
169
  // description: { optional: true },
170
+ // nullable: widen JSON-Schema type to accept null (Zod .nullable() rescue)
171
+ // priceMode: { nullable: true },
172
+ // preserveForElevated: elevated admins keep the field on ingest (cross-tenant writes)
173
+ // organizationId: { systemManaged: true, preserveForElevated: true },
170
174
  },
171
175
  query: {
172
176
  filterableFields: {
@@ -274,42 +278,48 @@ ${ts ? "import { z } from 'zod';\n" : "const { z } = require('zod');\n"}
274
278
  return `/**
275
279
  * ${name} Tests
276
280
  * Generated by Arc CLI
281
+ *
282
+ * 2.11 testing surface:
283
+ * - createTestApp turnkey Fastify + in-memory Mongo + auth + fixtures
284
+ * - expectArc fluent envelope matchers (.ok, .unauthorized, .forbidden, ...)
285
+ * - ctx.auth unified TestAuthProvider — register a role, reuse .headers
277
286
  */
278
287
 
279
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
280
- import mongoose from 'mongoose';
281
- import { createMinimalTestApp } from '@classytic/arc/testing';
282
- ${ts ? "import type { FastifyInstance } from 'fastify';\n" : ""}import ${camel}Resource from '../src/resources/${fileName}/${fileName}.resource.js';
288
+ import { describe, it, beforeAll, afterAll } from 'vitest';
289
+ import { createTestApp, expectArc } from '@classytic/arc/testing';
290
+ import type { TestAppContext } from '@classytic/arc/testing';
291
+ import ${camel}Resource from '../src/resources/${fileName}/${fileName}.resource.js';
283
292
 
284
293
  describe('${name} Resource', () => {
285
- let app${ts ? ": FastifyInstance" : ""};
294
+ let ctx${ts ? ": TestAppContext" : ""};
286
295
 
287
296
  beforeAll(async () => {
288
- const testDbUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/arc-app-test';
289
- await mongoose.connect(testDbUri);
297
+ ctx = await createTestApp({
298
+ resources: [${camel}Resource],
299
+ authMode: 'jwt',
300
+ connectMongoose: true,
301
+ });
290
302
 
291
- app = createMinimalTestApp();
292
- await app.register(${camel}Resource.toPlugin());
293
- await app.ready();
303
+ ctx.auth${ts ? "!" : ""}.register('admin', {
304
+ user: { id: '1', roles: ['admin'] },
305
+ orgId: 'org-1',
306
+ });
294
307
  });
295
308
 
296
- afterAll(async () => {
297
- await app.close();
298
- await mongoose.connection.close();
299
- });
309
+ afterAll(() => ctx.close());
300
310
 
301
311
  describe('GET /${pluralize(fileName)}', () => {
302
- it('should return a list', async () => {
303
- const response = await app.inject({
304
- method: 'GET',
305
- url: '/${pluralize(fileName)}',
306
- });
307
-
308
- expect(response.statusCode).toBe(200);
309
- const body = JSON.parse(response.body);
310
- expect(body).toHaveProperty('docs');
312
+ it('should return a paginated list', async () => {
313
+ const res = await ctx.app.inject({ method: 'GET', url: '/${pluralize(fileName)}' });
314
+ expectArc(res).ok().paginated();
311
315
  });
312
316
  });
317
+
318
+ // More coverage patterns:
319
+ // - expectArc(res).unauthorized() for missing auth
320
+ // - expectArc(res).forbidden() for wrong role
321
+ // - ctx.auth.as('admin').headers for authenticated requests
322
+ // - ctx.fixtures.create('${fileName}', {...}) for seeded data
313
323
  });
314
324
  `;
315
325
  }
@@ -102,7 +102,7 @@ async function installDependencies(projectPath, config, pm) {
102
102
  ];
103
103
  if (config.auth === "better-auth") deps.push("better-auth@^1.6.0", "mongodb@latest");
104
104
  else deps.push("@fastify/jwt@latest", "bcryptjs@latest");
105
- if (config.adapter === "mongokit") deps.push("@classytic/mongokit@^3.10.2", "@classytic/repo-core@^0.1.0", "mongoose@^9.4.1");
105
+ if (config.adapter === "mongokit") deps.push("@classytic/mongokit@^3.11.0", "@classytic/repo-core@^0.2.0", "mongoose@^9.4.1");
106
106
  const devDeps = ["vitest@latest", "pino-pretty@latest"];
107
107
  if (config.typescript) devDeps.push("typescript@latest", "@types/node@latest", "tsx@latest");
108
108
  const installCmd = getInstallCommand(pm, deps, false);
@@ -309,7 +309,7 @@ function packageJsonTemplate(config) {
309
309
  "#utils/*": "./src/utils/*"
310
310
  },
311
311
  scripts,
312
- engines: { node: ">=20" }
312
+ engines: { node: ">=22" }
313
313
  }, null, 2);
314
314
  }
315
315
  function tsconfigTemplate() {
@@ -1746,8 +1746,12 @@ import { buildCrudSchemasFromModel } from '@classytic/mongokit/utils';
1746
1746
  const crudSchemas = buildCrudSchemasFromModel(Example, {
1747
1747
  strictAdditionalProperties: true,
1748
1748
  fieldRules: {
1749
- // Mark fields as system-managed (excluded from create/update)
1749
+ // Framework-injected fields strip from body + required[]
1750
1750
  // deletedAt: { systemManaged: true },
1751
+ // Legitimate null values (Zod .nullable() patterns) — widen JSON-Schema type
1752
+ // priceMode: { nullable: true },
1753
+ // Elevated-admin override for systemManaged fields (cross-tenant writes)
1754
+ // organizationId: { systemManaged: true, preserveForElevated: true },
1751
1755
  },
1752
1756
  query: {
1753
1757
  filterableFields: {
@@ -1781,61 +1785,70 @@ function exampleTestTemplate(config) {
1781
1785
  *
1782
1786
  * Run tests: npm test
1783
1787
  * Watch mode: npm run test:watch
1788
+ *
1789
+ * Uses arc's 2.11 testing surface:
1790
+ * - createTestApp — turnkey Fastify + in-memory Mongo + auth + fixtures
1791
+ * - expectArc — fluent envelope matchers (.ok, .unauthorized, .forbidden, ...)
1792
+ * - ctx.auth — unified TestAuthProvider, register a role once then reuse .headers
1784
1793
  */
1785
1794
 
1786
- import { describe, it, expect, beforeAll, afterAll } from 'vitest';
1787
- ${config.adapter === "mongokit" ? "import mongoose from 'mongoose';\n" : ""}import { createAppInstance } from '../src/app.js';
1788
- ${ts ? "import type { FastifyInstance } from 'fastify';\n" : ""}
1795
+ import { describe, it, beforeAll, afterAll } from 'vitest';
1796
+ import { createTestApp, expectArc } from '@classytic/arc/testing';
1797
+ import type { TestAppContext } from '@classytic/arc/testing';
1798
+ import { exampleResource } from '../src/resources/example/example.js';
1799
+
1789
1800
  describe('Example Resource', () => {
1790
- let app${ts ? ": FastifyInstance" : ""};
1801
+ let ctx${ts ? ": TestAppContext" : ""};
1791
1802
 
1792
1803
  beforeAll(async () => {
1793
- ${config.adapter === "mongokit" ? ` // Connect to test database
1794
- const testDbUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/${config.name}-test';
1795
- await mongoose.connect(testDbUri);
1796
- ` : ""}
1797
- // Create app instance
1798
- app = await createAppInstance();
1799
- await app.ready();
1804
+ ctx = await createTestApp({
1805
+ resources: [exampleResource],
1806
+ authMode: 'jwt',
1807
+ ${config.adapter === "mongokit" ? " connectMongoose: true,\n" : ""} });
1808
+
1809
+ ctx.auth${ts ? "!" : ""}.register('admin', {
1810
+ user: { id: '1', roles: ['admin'] },
1811
+ orgId: 'org-1',
1812
+ });
1800
1813
  });
1801
1814
 
1802
- afterAll(async () => {
1803
- await app.close();
1804
- ${config.adapter === "mongokit" ? " await mongoose.connection.close();" : ""}
1805
- });
1815
+ afterAll(() => ctx.close());
1806
1816
 
1807
1817
  describe('GET /examples', () => {
1808
- it('should return a list of examples', async () => {
1809
- const response = await app.inject({
1810
- method: 'GET',
1811
- url: '/examples',
1812
- });
1813
-
1814
- expect(response.statusCode).toBe(200);
1815
- const body = JSON.parse(response.body);
1816
- expect(body).toHaveProperty('docs');
1817
- expect(Array.isArray(body.docs)).toBe(true);
1818
+ it('should return a list of examples (public)', async () => {
1819
+ const res = await ctx.app.inject({ method: 'GET', url: '/examples' });
1820
+ expectArc(res).ok().paginated();
1818
1821
  });
1819
1822
  });
1820
1823
 
1821
1824
  describe('POST /examples', () => {
1822
1825
  it('should require authentication', async () => {
1823
- const response = await app.inject({
1826
+ const res = await ctx.app.inject({
1824
1827
  method: 'POST',
1825
1828
  url: '/examples',
1826
1829
  payload: { name: 'Test Example' },
1827
1830
  });
1831
+ expectArc(res).unauthorized();
1832
+ });
1828
1833
 
1829
- // Should fail without auth token
1830
- expect(response.statusCode).toBe(401);
1834
+ it('should create when admin is authenticated', async () => {
1835
+ const res = await ctx.app.inject({
1836
+ method: 'POST',
1837
+ url: '/examples',
1838
+ headers: ctx.auth${ts ? "!" : ""}.as('admin').headers,
1839
+ payload: { name: 'Test Example' },
1840
+ });
1841
+ expectArc(res).ok();
1831
1842
  });
1832
1843
  });
1833
1844
 
1834
1845
  // Add more tests as needed:
1835
- // - GET /examples/:id
1836
- // - PATCH /examples/:id
1837
- // - DELETE /examples/:id
1846
+ // - GET /examples/:id (expectArc(res).ok().hasData({ name: '...' }))
1847
+ // - PATCH /examples/:id (expectArc(res).ok())
1848
+ // - DELETE /examples/:id (expectArc(res).ok())
1838
1849
  // - Custom endpoints
1850
+ // - Permission denials (expectArc(res).forbidden().hasError(/reason/))
1851
+ // - Field hiding (expectArc(res).hidesField('password'))
1839
1852
  });
1840
1853
  `;
1841
1854
  }
@@ -1,4 +1,4 @@
1
- import { t as ResourceRegistry } from "../../ResourceRegistry-BPd6NQDm.mjs";
1
+ import { t as ResourceRegistry } from "../../ResourceRegistry-DkAeAuTX.mjs";
2
2
  import { resolve } from "node:path";
3
3
  import { pathToFileURL } from "node:url";
4
4
  //#region src/cli/commands/introspect.ts
@@ -0,0 +1,58 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+
3
+ //#region src/context/requestContext.d.ts
4
+ /**
5
+ * Shape of the request-scoped context store.
6
+ * Populated by Arc's onRequest hook in arcCorePlugin.
7
+ */
8
+ interface RequestStore {
9
+ /** Unique request identifier */
10
+ requestId?: string;
11
+ /** Authenticated user (if any) */
12
+ user?: {
13
+ id?: string;
14
+ _id?: string;
15
+ roles?: string[];
16
+ [key: string]: unknown;
17
+ } | null;
18
+ /** Active organization ID (multi-tenant) */
19
+ organizationId?: string;
20
+ /** Active team ID (team-scoped resources) */
21
+ teamId?: string;
22
+ /** Current resource name (set by arcDecorator in CRUD routes) */
23
+ resourceName?: string;
24
+ /** Request start time (for timing) */
25
+ startTime: number;
26
+ /** Additional context — extensible by app */
27
+ [key: string]: unknown;
28
+ }
29
+ /**
30
+ * Request context API.
31
+ *
32
+ * - `get()` — returns current store or undefined if outside request scope
33
+ * - `run(store, fn)` — run a function with a specific store (used by Arc internals)
34
+ * - `getStore()` — alias for get() (matches Node.js API naming)
35
+ */
36
+ declare const requestContext: {
37
+ /**
38
+ * Get the current request context.
39
+ * Returns undefined if called outside a request lifecycle.
40
+ */
41
+ get(): RequestStore | undefined;
42
+ /**
43
+ * Alias for get() — matches Node.js AsyncLocalStorage API naming.
44
+ */
45
+ getStore(): RequestStore | undefined;
46
+ /**
47
+ * Run a function within a specific request context.
48
+ * Used internally by Arc's onRequest hook.
49
+ */
50
+ run<T>(store: RequestStore, fn: () => T): T;
51
+ /**
52
+ * The underlying AsyncLocalStorage instance.
53
+ * Exposed for advanced use cases (testing, custom integrations).
54
+ */
55
+ storage: AsyncLocalStorage<RequestStore>;
56
+ };
57
+ //#endregion
58
+ export { type RequestStore, requestContext };
@@ -0,0 +1,2 @@
1
+ import { t as requestContext } from "../requestContext-CfRkaxwf.mjs";
2
+ export { requestContext };
@@ -1,3 +1,3 @@
1
- import { G as ResourceDefinition, K as defineResource, a as QueryResolverConfig, c as AccessControl, i as QueryResolver, l as AccessControlConfig, n as BaseController, o as BodySanitizer, r as BaseControllerOptions, s as BodySanitizerConfig } from "../index-Cl0uoKd5.mjs";
2
- import { C as MAX_REGEX_LENGTH, D as RESERVED_QUERY_PARAMS, E as MutationOperation, O as SYSTEM_FIELDS, S as MAX_FILTER_DEPTH, T as MUTATION_OPERATIONS, _ as DEFAULT_UPDATE_METHOD, a as getControllerScope, b as HookOperation, c as createCrudRouter, d as CrudOperation, f as DEFAULT_ID_FIELD, g as DEFAULT_TENANT_FIELD, h as DEFAULT_SORT, i as getControllerContext, l as createPermissionMiddleware, m as DEFAULT_MAX_LIMIT, n as createFastifyHandler, o as sendControllerResponse, p as DEFAULT_LIMIT, r as createRequestContext, s as defineResourceVariants, t as createCrudHandlers, u as CRUD_OPERATIONS, v as HOOK_OPERATIONS, w as MAX_SEARCH_LENGTH, x as HookPhase, y as HOOK_PHASES } from "../index-8qw4y6ff.mjs";
3
- export { AccessControl, AccessControlConfig, BaseController, BaseControllerOptions, BodySanitizer, BodySanitizerConfig, CRUD_OPERATIONS, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, QueryResolver, QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineResource, defineResourceVariants, getControllerContext, getControllerScope, sendControllerResponse };
1
+ import { B as ResourceDefinition, Gt as TreeMixin, Ht as SoftDeleteExt, Jt as BulkExt, Kt as SlugExt, Ut as SoftDeleteMixin, V as defineResource, Vt as BaseController, Wt as TreeExt, Yt as BulkMixin, an as QueryResolverConfig, cn as AccessControl, in as QueryResolver, ln as AccessControlConfig, nn as BaseCrudController, on as BodySanitizer, qt as SlugMixin, rn as ListResult, sn as BodySanitizerConfig, tn as BaseControllerOptions } from "../index-Cm0vUrr_.mjs";
2
+ import { C as MAX_REGEX_LENGTH, D as RESERVED_QUERY_PARAMS, E as MutationOperation, O as SYSTEM_FIELDS, S as MAX_FILTER_DEPTH, T as MUTATION_OPERATIONS, _ as DEFAULT_UPDATE_METHOD, a as getControllerScope, b as HookOperation, c as createCrudRouter, d as CrudOperation, f as DEFAULT_ID_FIELD, g as DEFAULT_TENANT_FIELD, h as DEFAULT_SORT, i as getControllerContext, l as createPermissionMiddleware, m as DEFAULT_MAX_LIMIT, n as createFastifyHandler, o as sendControllerResponse, p as DEFAULT_LIMIT, r as createRequestContext, s as defineResourceVariants, t as createCrudHandlers, u as CRUD_OPERATIONS, v as HOOK_OPERATIONS, w as MAX_SEARCH_LENGTH, x as HookPhase, y as HOOK_PHASES } from "../index-t8pLpPFW.mjs";
3
+ export { AccessControl, AccessControlConfig, BaseController, BaseControllerOptions, BaseCrudController, BodySanitizer, BodySanitizerConfig, BulkExt, BulkMixin, CRUD_OPERATIONS, CrudOperation, DEFAULT_ID_FIELD, DEFAULT_LIMIT, DEFAULT_MAX_LIMIT, DEFAULT_SORT, DEFAULT_TENANT_FIELD, DEFAULT_UPDATE_METHOD, HOOK_OPERATIONS, HOOK_PHASES, HookOperation, HookPhase, ListResult, MAX_FILTER_DEPTH, MAX_REGEX_LENGTH, MAX_SEARCH_LENGTH, MUTATION_OPERATIONS, MutationOperation, QueryResolver, QueryResolverConfig, RESERVED_QUERY_PARAMS, ResourceDefinition, SYSTEM_FIELDS, SlugExt, SlugMixin, SoftDeleteExt, SoftDeleteMixin, TreeExt, TreeMixin, createCrudHandlers, createCrudRouter, createFastifyHandler, createPermissionMiddleware, createRequestContext, defineResource, defineResourceVariants, getControllerContext, getControllerScope, sendControllerResponse };