@classytic/arc 2.2.5 → 2.4.1

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 (174) hide show
  1. package/README.md +187 -18
  2. package/bin/arc.js +11 -3
  3. package/dist/BaseController-CkM5dUh_.mjs +1031 -0
  4. package/dist/{EventTransport-BkUDYZEb.d.mts → EventTransport-wc5hSLik.d.mts} +1 -1
  5. package/dist/{HookSystem-BsGV-j2l.mjs → HookSystem-COkyWztM.mjs} +2 -3
  6. package/dist/{ResourceRegistry-7Ic20ZMw.mjs → ResourceRegistry-DeCIFlix.mjs} +8 -5
  7. package/dist/adapters/index.d.mts +3 -5
  8. package/dist/adapters/index.mjs +2 -3
  9. package/dist/{prisma-DJbMt3yf.mjs → adapters-DTC4Ug66.mjs} +45 -12
  10. package/dist/audit/index.d.mts +4 -7
  11. package/dist/audit/index.mjs +2 -29
  12. package/dist/audit/mongodb.d.mts +1 -4
  13. package/dist/audit/mongodb.mjs +2 -3
  14. package/dist/auth/index.d.mts +7 -9
  15. package/dist/auth/index.mjs +65 -63
  16. package/dist/auth/redis-session.d.mts +1 -1
  17. package/dist/auth/redis-session.mjs +1 -2
  18. package/dist/{betterAuthOpenApi-DjWDddNc.mjs → betterAuthOpenApi-lz0IRbXJ.mjs} +4 -6
  19. package/dist/cache/index.d.mts +23 -23
  20. package/dist/cache/index.mjs +4 -6
  21. package/dist/{caching-GSDJcA6-.mjs → caching-BSXB-Xr7.mjs} +2 -24
  22. package/dist/chunk-BpYLSNr0.mjs +14 -0
  23. package/dist/circuitBreaker-BOBOpN2w.mjs +284 -0
  24. package/dist/circuitBreaker-JP2GdJ4b.d.mts +206 -0
  25. package/dist/cli/commands/describe.mjs +24 -7
  26. package/dist/cli/commands/docs.mjs +6 -7
  27. package/dist/cli/commands/doctor.d.mts +10 -0
  28. package/dist/cli/commands/doctor.mjs +156 -0
  29. package/dist/cli/commands/generate.mjs +66 -17
  30. package/dist/cli/commands/init.mjs +315 -45
  31. package/dist/cli/commands/introspect.mjs +2 -4
  32. package/dist/cli/index.d.mts +1 -10
  33. package/dist/cli/index.mjs +4 -153
  34. package/dist/{constants-DdXFXQtN.mjs → constants-Cxde4rpC.mjs} +1 -2
  35. package/dist/core/index.d.mts +3 -5
  36. package/dist/core/index.mjs +5 -4
  37. package/dist/core-C1XCMtqM.mjs +185 -0
  38. package/dist/{createApp-BKHSl2nT.mjs → createApp-ByWNRsZj.mjs} +65 -36
  39. package/dist/{defineResource-DO9ONe_D.mjs → defineResource-D9aY5Cy6.mjs} +154 -1165
  40. package/dist/discovery/index.mjs +37 -5
  41. package/dist/docs/index.d.mts +6 -9
  42. package/dist/docs/index.mjs +3 -21
  43. package/dist/dynamic/index.d.mts +93 -0
  44. package/dist/dynamic/index.mjs +122 -0
  45. package/dist/{elevation-DSTbVvYj.mjs → elevation-BEdACOLB.mjs} +5 -36
  46. package/dist/{elevation-DGo5shaX.d.mts → elevation-Ca_yveIO.d.mts} +41 -7
  47. package/dist/{errorHandler-C3GY3_ow.mjs → errorHandler--zp54tGc.mjs} +3 -5
  48. package/dist/errorHandler-Do4vVQ1f.d.mts +139 -0
  49. package/dist/{errors-DBANPbGr.mjs → errors-rxhfP7Hf.mjs} +1 -2
  50. package/dist/{eventPlugin-BEOvaDqo.mjs → eventPlugin-Ba00swHF.mjs} +25 -27
  51. package/dist/{eventPlugin-H6wDDjGO.d.mts → eventPlugin-iGrSEmwJ.d.mts} +105 -5
  52. package/dist/events/index.d.mts +72 -7
  53. package/dist/events/index.mjs +216 -4
  54. package/dist/events/transports/redis-stream-entry.d.mts +1 -1
  55. package/dist/events/transports/redis-stream-entry.mjs +19 -7
  56. package/dist/events/transports/redis.d.mts +1 -1
  57. package/dist/events/transports/redis.mjs +3 -4
  58. package/dist/factory/index.d.mts +23 -9
  59. package/dist/factory/index.mjs +48 -3
  60. package/dist/{fields-Bi_AVKSo.d.mts → fields-DFwdaWCq.d.mts} +1 -1
  61. package/dist/{fields-CTd_CrKr.mjs → fields-ipsbIRPK.mjs} +1 -2
  62. package/dist/hooks/index.d.mts +1 -3
  63. package/dist/hooks/index.mjs +2 -3
  64. package/dist/idempotency/index.d.mts +5 -5
  65. package/dist/idempotency/index.mjs +3 -7
  66. package/dist/idempotency/mongodb.d.mts +1 -1
  67. package/dist/idempotency/mongodb.mjs +4 -5
  68. package/dist/idempotency/redis.d.mts +1 -1
  69. package/dist/idempotency/redis.mjs +2 -5
  70. package/dist/{fastifyAdapter-CyAA2zlB.d.mts → index-BL8CaQih.d.mts} +56 -57
  71. package/dist/index-Diqcm14c.d.mts +369 -0
  72. package/dist/{prisma-xjhMEq_S.d.mts → index-yhxyjqNb.d.mts} +4 -5
  73. package/dist/index.d.mts +100 -105
  74. package/dist/index.mjs +85 -58
  75. package/dist/integrations/event-gateway.d.mts +1 -1
  76. package/dist/integrations/event-gateway.mjs +8 -4
  77. package/dist/integrations/index.d.mts +4 -2
  78. package/dist/integrations/index.mjs +1 -1
  79. package/dist/integrations/jobs.d.mts +2 -2
  80. package/dist/integrations/jobs.mjs +63 -14
  81. package/dist/integrations/mcp/index.d.mts +219 -0
  82. package/dist/integrations/mcp/index.mjs +572 -0
  83. package/dist/integrations/mcp/testing.d.mts +53 -0
  84. package/dist/integrations/mcp/testing.mjs +104 -0
  85. package/dist/integrations/streamline.mjs +39 -19
  86. package/dist/integrations/webhooks.d.mts +56 -0
  87. package/dist/integrations/webhooks.mjs +139 -0
  88. package/dist/integrations/websocket-redis.d.mts +46 -0
  89. package/dist/integrations/websocket-redis.mjs +50 -0
  90. package/dist/integrations/websocket.d.mts +68 -2
  91. package/dist/integrations/websocket.mjs +96 -13
  92. package/dist/{interface-CSNjltAc.d.mts → interface-B4awm1RJ.d.mts} +2 -2
  93. package/dist/interface-DGmPxakH.d.mts +2213 -0
  94. package/dist/{keys-DhqDRxv3.mjs → keys-qcD-TVJl.mjs} +3 -4
  95. package/dist/{logger-ByrvQWZO.mjs → logger-Dz3j1ItV.mjs} +2 -4
  96. package/dist/{memory-B2v7KrCB.mjs → memory-Cb_7iy9e.mjs} +2 -4
  97. package/dist/metrics-Csh4nsvv.mjs +224 -0
  98. package/dist/migrations/index.mjs +3 -7
  99. package/dist/{mongodb-DNKEExbf.mjs → mongodb-BuQ7fNTg.mjs} +1 -4
  100. package/dist/{mongodb-ClykrfGo.d.mts → mongodb-CUpYfxfD.d.mts} +2 -3
  101. package/dist/{mongodb-Dg8O_gvd.d.mts → mongodb-bga9AbkD.d.mts} +2 -2
  102. package/dist/{openapi-9nB_kiuR.mjs → openapi-CBmZ6EQN.mjs} +4 -21
  103. package/dist/org/index.d.mts +12 -14
  104. package/dist/org/index.mjs +92 -119
  105. package/dist/org/types.d.mts +2 -2
  106. package/dist/org/types.mjs +1 -1
  107. package/dist/permissions/index.d.mts +4 -278
  108. package/dist/permissions/index.mjs +4 -579
  109. package/dist/permissions-CA5zg0yK.mjs +751 -0
  110. package/dist/plugins/index.d.mts +104 -107
  111. package/dist/plugins/index.mjs +203 -313
  112. package/dist/plugins/response-cache.mjs +4 -69
  113. package/dist/plugins/tracing-entry.d.mts +1 -1
  114. package/dist/plugins/tracing-entry.mjs +24 -11
  115. package/dist/{pluralize-CM-jZg7p.mjs → pluralize-CcT6qF0a.mjs} +12 -13
  116. package/dist/policies/index.d.mts +2 -2
  117. package/dist/policies/index.mjs +80 -83
  118. package/dist/presets/index.d.mts +26 -19
  119. package/dist/presets/index.mjs +2 -142
  120. package/dist/presets/multiTenant.d.mts +1 -4
  121. package/dist/presets/multiTenant.mjs +4 -6
  122. package/dist/presets-C9QXJV1u.mjs +422 -0
  123. package/dist/{queryCachePlugin-B6R0d4av.mjs → queryCachePlugin-ClosZdNS.mjs} +6 -27
  124. package/dist/{queryCachePlugin-Q6SYuHZ6.d.mts → queryCachePlugin-DcmETvcB.d.mts} +3 -3
  125. package/dist/queryParser-CgCtsjti.mjs +352 -0
  126. package/dist/{redis-UwjEp8Ea.d.mts → redis-CQ5YxMC5.d.mts} +2 -2
  127. package/dist/{redis-stream-CBg0upHI.d.mts → redis-stream-BW9UKLZM.d.mts} +9 -2
  128. package/dist/registry/index.d.mts +1 -4
  129. package/dist/registry/index.mjs +3 -4
  130. package/dist/{introspectionPlugin-B3JkrjwU.mjs → registry-I-ogLgL9.mjs} +1 -8
  131. package/dist/{requestContext-xi6OKBL-.mjs → requestContext-DYtmNpm5.mjs} +1 -3
  132. package/dist/resourceToTools-B6ZN9Ing.mjs +489 -0
  133. package/dist/rpc/index.d.mts +90 -0
  134. package/dist/rpc/index.mjs +248 -0
  135. package/dist/{schemaConverter-Dtg0Kt9T.mjs → schemaConverter-DjzHpFam.mjs} +1 -2
  136. package/dist/schemas/index.d.mts +30 -30
  137. package/dist/schemas/index.mjs +4 -6
  138. package/dist/scope/index.d.mts +13 -2
  139. package/dist/scope/index.mjs +18 -5
  140. package/dist/{sessionManager-D_iEHjQl.d.mts → sessionManager-wbkYj2HL.d.mts} +2 -2
  141. package/dist/{sse-DkqQ1uxb.mjs → sse-BkViJPlT.mjs} +4 -25
  142. package/dist/testing/index.d.mts +551 -567
  143. package/dist/testing/index.mjs +1744 -1799
  144. package/dist/{tracing-8CEbhF0w.d.mts → tracing-bz_U4EM1.d.mts} +6 -1
  145. package/dist/{typeGuards-DwxA1t_L.mjs → typeGuards-Cj5Rgvlg.mjs} +1 -2
  146. package/dist/types/index.d.mts +4 -946
  147. package/dist/types/index.mjs +2 -4
  148. package/dist/types-BJmgxNbF.d.mts +275 -0
  149. package/dist/{types-RLkFVgaw.d.mts → types-BNUccdcf.d.mts} +2 -2
  150. package/dist/{types-Beqn1Un7.mjs → types-C6TQjtdi.mjs} +30 -2
  151. package/dist/{types-DMSBMkaZ.d.mts → types-Dt0-AI6E.d.mts} +85 -27
  152. package/dist/{types-DelU6kln.mjs → types-ZUu_h0jp.mjs} +1 -2
  153. package/dist/utils/index.d.mts +255 -352
  154. package/dist/utils/index.mjs +7 -6
  155. package/dist/utils-Dc0WhlIl.mjs +594 -0
  156. package/dist/versioning-BzfeHmhj.mjs +37 -0
  157. package/package.json +46 -12
  158. package/skills/arc/SKILL.md +506 -0
  159. package/skills/arc/references/auth.md +250 -0
  160. package/skills/arc/references/events.md +272 -0
  161. package/skills/arc/references/integrations.md +385 -0
  162. package/skills/arc/references/mcp.md +386 -0
  163. package/skills/arc/references/production.md +610 -0
  164. package/skills/arc/references/testing.md +183 -0
  165. package/dist/audited-CGdLiSlE.mjs +0 -140
  166. package/dist/chunk-C7Uep-_p.mjs +0 -20
  167. package/dist/circuitBreaker-DYhWBW_D.mjs +0 -1096
  168. package/dist/errorHandler-CW3OOeYq.d.mts +0 -72
  169. package/dist/interface-DZYNK9bb.d.mts +0 -1112
  170. package/dist/presets-BTeYbw7h.d.mts +0 -57
  171. package/dist/presets-CeFtfDR8.mjs +0 -119
  172. /package/dist/{errors-DAWRdiYP.d.mts → errors-CPpvPHT0.d.mts} +0 -0
  173. /package/dist/{externalPaths-SyPF2tgK.d.mts → externalPaths-DpO-s7r8.d.mts} +0 -0
  174. /package/dist/{interface-DTbsvIWe.d.mts → interface-D_BWALyZ.d.mts} +0 -0
@@ -94,6 +94,6 @@ declare class MemoryEventTransport implements EventTransport {
94
94
  /**
95
95
  * Create a domain event with auto-generated metadata
96
96
  */
97
- declare function createEvent<T>(type: string, payload: T, meta?: Partial<DomainEvent['meta']>): DomainEvent<T>;
97
+ declare function createEvent<T>(type: string, payload: T, meta?: Partial<DomainEvent["meta"]>): DomainEvent<T>;
98
98
  //#endregion
99
99
  export { MemoryEventTransport as a, EventTransport as i, EventHandler as n, MemoryEventTransportOptions as o, EventLogger as r, createEvent as s, DomainEvent as t };
@@ -188,7 +188,7 @@ var HookSystem = class {
188
188
  for (const dep of hook.dependsOn) if (byName.has(dep)) {
189
189
  resolvedDeps++;
190
190
  if (!dependents.has(dep)) dependents.set(dep, []);
191
- dependents.get(dep).push(hook);
191
+ dependents.get(dep)?.push(hook);
192
192
  } else this.warn(`[HookSystem] Hook '${hook.name ?? "<unnamed>"}' depends on '${dep}' which is not registered in the same phase/resource. Dependency will be ignored.`);
193
193
  inDegree.set(hook, resolvedDeps);
194
194
  }
@@ -399,6 +399,5 @@ function beforeDelete(hooks, resource, handler, priority = 10) {
399
399
  function afterDelete(hooks, resource, handler, priority = 10) {
400
400
  return hooks.after(resource, "delete", handler, priority);
401
401
  }
402
-
403
402
  //#endregion
404
- export { beforeCreate as a, createHookSystem as c, afterUpdate as i, defineHook as l, afterCreate as n, beforeDelete as o, afterDelete as r, beforeUpdate as s, HookSystem as t };
403
+ export { beforeCreate as a, createHookSystem as c, afterUpdate as i, defineHook as l, afterCreate as n, beforeDelete as o, afterDelete as r, beforeUpdate as s, HookSystem as t };
@@ -1,6 +1,10 @@
1
- import { s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS } from "./constants-DdXFXQtN.mjs";
2
-
1
+ import { t as CRUD_OPERATIONS } from "./constants-Cxde4rpC.mjs";
3
2
  //#region src/registry/ResourceRegistry.ts
3
+ /**
4
+ * Resource Registry
5
+ *
6
+ * Singleton that tracks all registered resources for introspection.
7
+ */
4
8
  var ResourceRegistry = class {
5
9
  _resources;
6
10
  _frozen;
@@ -110,7 +114,7 @@ var ResourceRegistry = class {
110
114
  return {
111
115
  resources: this.getAll().map((r) => {
112
116
  const disabledSet = new Set(r.disabledRoutes ?? []);
113
- const updateMethod = r.updateMethod ?? DEFAULT_UPDATE_METHOD;
117
+ const updateMethod = r.updateMethod ?? "PATCH";
114
118
  const defaultRoutes = r.disableDefaultRoutes ? [] : [
115
119
  ...!disabledSet.has("list") ? [{
116
120
  method: "GET",
@@ -244,6 +248,5 @@ function extractPipelineSteps(pipe) {
244
248
  operations: s.operations ? [...s.operations] : void 0
245
249
  }));
246
250
  }
247
-
248
251
  //#endregion
249
- export { ResourceRegistry as t };
252
+ export { ResourceRegistry as t };
@@ -1,5 +1,3 @@
1
- import "../elevation-DGo5shaX.mjs";
2
- import { a as RepositoryLike, i as RelationMetadata, n as DataAdapter, o as SchemaMetadata, r as FieldMetadata, s as ValidationResult, t as AdapterFactory } from "../interface-DZYNK9bb.mjs";
3
- import "../types-RLkFVgaw.mjs";
4
- import { a as PrismaQueryParserOptions, c as MongooseAdapterOptions, i as PrismaQueryParser, l as createMongooseAdapter, n as PrismaAdapterOptions, o as createPrismaAdapter, r as PrismaQueryOptions, s as MongooseAdapter, t as PrismaAdapter } from "../prisma-xjhMEq_S.mjs";
5
- export { type AdapterFactory, type DataAdapter, type FieldMetadata, MongooseAdapter, type MongooseAdapterOptions, PrismaAdapter, type PrismaAdapterOptions, type PrismaQueryOptions, PrismaQueryParser, type PrismaQueryParserOptions, type RelationMetadata, type RepositoryLike, type SchemaMetadata, type ValidationResult, createMongooseAdapter, createPrismaAdapter };
1
+ import { a as RepositoryLike, i as RelationMetadata, n as DataAdapter, o as SchemaMetadata, r as FieldMetadata, s as ValidationResult, t as AdapterFactory } from "../interface-DGmPxakH.mjs";
2
+ import { a as PrismaQueryParserOptions, c as MongooseAdapterOptions, i as PrismaQueryParser, l as createMongooseAdapter, n as PrismaAdapterOptions, o as createPrismaAdapter, r as PrismaQueryOptions, s as MongooseAdapter, t as PrismaAdapter } from "../index-yhxyjqNb.mjs";
3
+ export { AdapterFactory, DataAdapter, FieldMetadata, MongooseAdapter, MongooseAdapterOptions, PrismaAdapter, PrismaAdapterOptions, PrismaQueryOptions, PrismaQueryParser, PrismaQueryParserOptions, RelationMetadata, RepositoryLike, SchemaMetadata, ValidationResult, createMongooseAdapter, createPrismaAdapter };
@@ -1,3 +1,2 @@
1
- import { a as createMongooseAdapter, i as MongooseAdapter, n as PrismaQueryParser, r as createPrismaAdapter, t as PrismaAdapter } from "../prisma-DJbMt3yf.mjs";
2
-
3
- export { MongooseAdapter, PrismaAdapter, PrismaQueryParser, createMongooseAdapter, createPrismaAdapter };
1
+ import { a as createMongooseAdapter, i as MongooseAdapter, n as PrismaQueryParser, r as createPrismaAdapter, t as PrismaAdapter } from "../adapters-DTC4Ug66.mjs";
2
+ export { MongooseAdapter, PrismaAdapter, PrismaQueryParser, createMongooseAdapter, createPrismaAdapter };
@@ -1,5 +1,4 @@
1
- import { h as SYSTEM_FIELDS, i as DEFAULT_MAX_LIMIT, m as RESERVED_QUERY_PARAMS, r as DEFAULT_LIMIT } from "./constants-DdXFXQtN.mjs";
2
-
1
+ import { h as SYSTEM_FIELDS, m as RESERVED_QUERY_PARAMS } from "./constants-Cxde4rpC.mjs";
3
2
  //#region src/adapters/types.ts
4
3
  /**
5
4
  * Check if value is a Mongoose model
@@ -13,7 +12,6 @@ function isMongooseModel(value) {
13
12
  function isRepository(value) {
14
13
  return typeof value === "object" && value !== null && "getAll" in value && "getById" in value && "create" in value && "update" in value && "delete" in value;
15
14
  }
16
-
17
15
  //#endregion
18
16
  //#region src/adapters/mongoose.ts
19
17
  /**
@@ -175,10 +173,46 @@ function createMongooseAdapter(modelOrOptions, repository) {
175
173
  }
176
174
  return new MongooseAdapter(modelOrOptions);
177
175
  }
178
-
179
176
  //#endregion
180
177
  //#region src/adapters/prisma.ts
181
178
  /**
179
+ * Prisma Adapter - PostgreSQL/MySQL/SQLite Implementation
180
+ *
181
+ * @experimental This adapter is implemented but has no integration tests yet.
182
+ * Use in production at your own risk. The Mongoose adapter is the recommended
183
+ * and battle-tested path.
184
+ *
185
+ * Bridges Prisma Client with Arc's DataAdapter interface.
186
+ * Supports Prisma 5+ with all database providers.
187
+ *
188
+ * Implemented features:
189
+ * - Schema generation (OpenAPI docs from DMMF)
190
+ * - Health checks (database connectivity)
191
+ * - Query parsing (URL params → Prisma where/orderBy)
192
+ * - Policy filter translation
193
+ * - Soft delete preset support
194
+ *
195
+ * Known gaps:
196
+ * - No integration test coverage
197
+ * - Multi-tenant isolation relies on caller-provided policyFilters (no auto-enforcement)
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * import { PrismaClient, Prisma } from '@prisma/client';
202
+ * import { createPrismaAdapter, PrismaQueryParser } from '@classytic/arc/adapters';
203
+ *
204
+ * const prisma = new PrismaClient();
205
+ *
206
+ * const userAdapter = createPrismaAdapter({
207
+ * client: prisma,
208
+ * modelName: 'user',
209
+ * repository: new UserRepository(prisma),
210
+ * dmmf: Prisma.dmmf, // For schema generation
211
+ * queryParser: new PrismaQueryParser(), // Optional: custom parser
212
+ * });
213
+ * ```
214
+ */
215
+ /**
182
216
  * Prisma Query Parser - Converts URL parameters to Prisma query format
183
217
  *
184
218
  * Translates Arc's query format to Prisma's where/orderBy/take/skip structure.
@@ -217,8 +251,8 @@ var PrismaQueryParser = class {
217
251
  $exists: void 0
218
252
  };
219
253
  constructor(options = {}) {
220
- this.maxLimit = options.maxLimit ?? DEFAULT_MAX_LIMIT;
221
- this.defaultLimit = options.defaultLimit ?? DEFAULT_LIMIT;
254
+ this.maxLimit = options.maxLimit ?? 1e3;
255
+ this.defaultLimit = options.defaultLimit ?? 20;
222
256
  this.softDeleteEnabled = options.softDeleteEnabled ?? true;
223
257
  this.softDeleteField = options.softDeleteField ?? "deletedAt";
224
258
  }
@@ -446,7 +480,7 @@ var PrismaAdapter = class {
446
480
  unique: true
447
481
  }))
448
482
  };
449
- } catch (err) {
483
+ } catch (_err) {
450
484
  return null;
451
485
  }
452
486
  }
@@ -472,7 +506,7 @@ var PrismaAdapter = class {
472
506
  errors
473
507
  };
474
508
  }
475
- } catch (err) {}
509
+ } catch (_err) {}
476
510
  return { valid: true };
477
511
  }
478
512
  async healthCheck() {
@@ -482,14 +516,14 @@ var PrismaAdapter = class {
482
516
  if (!delegate) return false;
483
517
  await delegate.findMany({ take: 1 });
484
518
  return true;
485
- } catch (err) {
519
+ } catch (_err) {
486
520
  return false;
487
521
  }
488
522
  }
489
523
  async close() {
490
524
  try {
491
525
  await this.client.$disconnect();
492
- } catch (err) {}
526
+ } catch (_err) {}
493
527
  }
494
528
  buildEntitySchema(model, options) {
495
529
  const properties = {};
@@ -622,6 +656,5 @@ var PrismaAdapter = class {
622
656
  function createPrismaAdapter(options) {
623
657
  return new PrismaAdapter(options);
624
658
  }
625
-
626
659
  //#endregion
627
- export { createMongooseAdapter as a, MongooseAdapter as i, PrismaQueryParser as n, createPrismaAdapter as r, PrismaAdapter as t };
660
+ export { createMongooseAdapter as a, MongooseAdapter as i, PrismaQueryParser as n, createPrismaAdapter as r, PrismaAdapter as t };
@@ -1,7 +1,4 @@
1
- import "../elevation-DGo5shaX.mjs";
2
- import "../interface-DZYNK9bb.mjs";
3
- import "../types-RLkFVgaw.mjs";
4
- import { a as AuditContext, c as AuditStore, i as AuditAction, l as AuditStoreOptions, n as MongoAuditStoreOptions, o as AuditEntry, r as MongoConnection, s as AuditQueryOptions, u as createAuditEntry } from "../mongodb-ClykrfGo.mjs";
1
+ import { a as AuditContext, c as AuditStore, i as AuditAction, l as AuditStoreOptions, n as MongoAuditStoreOptions, o as AuditEntry, r as MongoConnection, s as AuditQueryOptions, u as createAuditEntry } from "../mongodb-CUpYfxfD.mjs";
5
2
  import { FastifyPluginAsync } from "fastify";
6
3
 
7
4
  //#region src/audit/auditPlugin.d.ts
@@ -9,7 +6,7 @@ interface AuditPluginOptions {
9
6
  /** Enable audit logging (default: false) */
10
7
  enabled?: boolean;
11
8
  /** Storage backends to use */
12
- stores?: ('memory' | 'mongodb')[];
9
+ stores?: ("memory" | "mongodb")[];
13
10
  /** MongoDB connection (required if using mongodb store) */
14
11
  mongoConnection?: MongoConnection;
15
12
  /** MongoDB collection name (default: 'audit_logs') */
@@ -28,11 +25,11 @@ interface AuditPluginOptions {
28
25
  * - `false`: Disable auto-audit (manual calls only)
29
26
  */
30
27
  autoAudit?: boolean | {
31
- operations?: ('create' | 'update' | 'delete')[];
28
+ operations?: ("create" | "update" | "delete")[];
32
29
  exclude?: string[];
33
30
  };
34
31
  }
35
- declare module 'fastify' {
32
+ declare module "fastify" {
36
33
  interface FastifyInstance {
37
34
  /** Log an audit entry */
38
35
  audit: AuditLogger;
@@ -1,6 +1,5 @@
1
- import { t as MongoAuditStore } from "../mongodb-DNKEExbf.mjs";
1
+ import { t as MongoAuditStore } from "../mongodb-BuQ7fNTg.mjs";
2
2
  import fp from "fastify-plugin";
3
-
4
3
  //#region src/audit/stores/interface.ts
5
4
  /**
6
5
  * Create audit entry from context
@@ -42,7 +41,6 @@ function detectChanges(before, after) {
42
41
  function generateAuditId() {
43
42
  return `aud_${Date.now().toString(36)}${Math.random().toString(36).substring(2, 10)}`;
44
43
  }
45
-
46
44
  //#endregion
47
45
  //#region src/audit/stores/memory.ts
48
46
  var MemoryAuditStore = class {
@@ -85,32 +83,8 @@ var MemoryAuditStore = class {
85
83
  this.entries = [];
86
84
  }
87
85
  };
88
-
89
86
  //#endregion
90
87
  //#region src/audit/auditPlugin.ts
91
- /**
92
- * Audit Plugin
93
- *
94
- * Optional audit trail with flexible storage options.
95
- * Disabled by default - enable explicitly for enterprise use cases.
96
- *
97
- * @example
98
- * import { auditPlugin } from '@classytic/arc/audit';
99
- *
100
- * // Development: in-memory
101
- * await fastify.register(auditPlugin, {
102
- * enabled: true,
103
- * stores: ['memory'],
104
- * });
105
- *
106
- * // Production: MongoDB with TTL
107
- * await fastify.register(auditPlugin, {
108
- * enabled: true,
109
- * stores: ['mongodb'],
110
- * mongoConnection: mongoose.connection,
111
- * ttlDays: 90,
112
- * });
113
- */
114
88
  const auditPlugin = async (fastify, opts = {}) => {
115
89
  const { enabled = false, stores: storeTypes = ["memory"], mongoConnection, mongoCollection = "audit_logs", ttlDays = 90, customStores = [] } = opts;
116
90
  if (!enabled) {
@@ -270,6 +244,5 @@ var auditPlugin_default = fp(auditPlugin, {
270
244
  fastify: "5.x",
271
245
  dependencies: ["arc-core"]
272
246
  });
273
-
274
247
  //#endregion
275
- export { MemoryAuditStore, auditPlugin_default as auditPlugin, auditPlugin as auditPluginFn, createAuditEntry };
248
+ export { MemoryAuditStore, auditPlugin_default as auditPlugin, auditPlugin as auditPluginFn, createAuditEntry };
@@ -1,5 +1,2 @@
1
- import "../elevation-DGo5shaX.mjs";
2
- import "../interface-DZYNK9bb.mjs";
3
- import "../types-RLkFVgaw.mjs";
4
- import { n as MongoAuditStoreOptions, t as MongoAuditStore } from "../mongodb-ClykrfGo.mjs";
1
+ import { n as MongoAuditStoreOptions, t as MongoAuditStore } from "../mongodb-CUpYfxfD.mjs";
5
2
  export { MongoAuditStore, type MongoAuditStoreOptions };
@@ -1,3 +1,2 @@
1
- import { t as MongoAuditStore } from "../mongodb-DNKEExbf.mjs";
2
-
3
- export { MongoAuditStore };
1
+ import { t as MongoAuditStore } from "../mongodb-BuQ7fNTg.mjs";
2
+ export { MongoAuditStore };
@@ -1,13 +1,11 @@
1
- import "../elevation-DGo5shaX.mjs";
2
- import "../interface-DZYNK9bb.mjs";
3
- import { t as PermissionCheck } from "../types-RLkFVgaw.mjs";
4
- import { AuthHelpers, AuthPluginOptions } from "../types/index.mjs";
5
- import { t as ExternalOpenApiPaths } from "../externalPaths-SyPF2tgK.mjs";
6
- 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_iEHjQl.mjs";
1
+ import { m as AuthPluginOptions, p as AuthHelpers } from "../interface-DGmPxakH.mjs";
2
+ import { t as PermissionCheck } from "../types-BNUccdcf.mjs";
3
+ import { t as ExternalOpenApiPaths } from "../externalPaths-DpO-s7r8.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-wbkYj2HL.mjs";
7
5
  import { FastifyPluginAsync, FastifyReply as FastifyReply$1, FastifyRequest as FastifyRequest$1 } from "fastify";
8
6
 
9
7
  //#region src/auth/authPlugin.d.ts
10
- declare module 'fastify' {
8
+ declare module "fastify" {
11
9
  interface FastifyInstance {
12
10
  /** Authenticate middleware - use in preHandler for protected routes */
13
11
  authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
@@ -23,7 +21,7 @@ declare const authPlugin: FastifyPluginAsync<AuthPluginOptions>;
23
21
  declare const _default: FastifyPluginAsync<AuthPluginOptions>;
24
22
  //#endregion
25
23
  //#region src/auth/betterAuth.d.ts
26
- declare module 'fastify' {
24
+ declare module "fastify" {
27
25
  interface FastifyRequest {
28
26
  /** Raw request body (from @fastify/raw-body plugin, if registered) */
29
27
  rawBody?: Buffer | string;
@@ -104,7 +102,7 @@ interface BetterAuthAdapterResult {
104
102
  /** OpenAPI paths extracted from Better Auth endpoints (undefined if openapi: false) */
105
103
  openapi?: ExternalOpenApiPaths;
106
104
  }
107
- declare module 'fastify' {
105
+ declare module "fastify" {
108
106
  interface FastifyInstance {
109
107
  /**
110
108
  * Authenticate middleware (Better Auth variant).
@@ -1,40 +1,11 @@
1
- import { t as AUTHENTICATED_SCOPE } from "../types-Beqn1Un7.mjs";
2
- import { n as normalizeRoles, t as getUserRoles } from "../types-DelU6kln.mjs";
3
- import { t as ArcError } from "../errors-DBANPbGr.mjs";
4
- import { requireOrgMembership, requireOrgRole, requireTeamMembership } from "../permissions/index.mjs";
5
- import { n as extractBetterAuthOpenApi } from "../betterAuthOpenApi-DjWDddNc.mjs";
1
+ import { n as normalizeRoles, t as getUserRoles } from "../types-ZUu_h0jp.mjs";
2
+ import { t as ArcError } from "../errors-rxhfP7Hf.mjs";
3
+ import { c as requireOrgMembership, f as requireTeamMembership, l as requireOrgRole } from "../permissions-CA5zg0yK.mjs";
4
+ import { n as extractBetterAuthOpenApi } from "../betterAuthOpenApi-lz0IRbXJ.mjs";
6
5
  import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
7
6
  import fp from "fastify-plugin";
8
-
9
7
  //#region src/auth/authPlugin.ts
10
8
  /**
11
- * Auth Plugin - Flexible, Database-Agnostic Authentication
12
- *
13
- * Arc provides JWT infrastructure and calls your authenticator.
14
- * You control ALL authentication logic.
15
- *
16
- * Design principles:
17
- * - Arc handles plumbing (JWT sign/verify utilities)
18
- * - App handles business logic (how to authenticate, where users live)
19
- * - Works with any database (Prisma, MongoDB, Postgres, none)
20
- * - Supports multiple auth strategies (JWT, API keys, sessions, etc.)
21
- *
22
- * @example
23
- * ```typescript
24
- * // In createApp
25
- * auth: {
26
- * jwt: { secret: process.env.JWT_SECRET },
27
- * authenticate: async (request, { jwt }) => {
28
- * // Your auth logic - Arc never touches your database
29
- * const token = request.headers.authorization?.split(' ')[1];
30
- * if (!token) return null;
31
- * const decoded = jwt.verify(token);
32
- * return userRepo.findById(decoded.id);
33
- * },
34
- * }
35
- * ```
36
- */
37
- /**
38
9
  * Parse expiration string to seconds
39
10
  */
40
11
  function parseExpiresIn(input, defaultValue) {
@@ -47,7 +18,7 @@ function parseExpiresIn(input, defaultValue) {
47
18
  m: 60,
48
19
  h: 3600,
49
20
  d: 86400
50
- }[match[2].toLowerCase()] ?? 1);
21
+ }[match[2]?.toLowerCase() ?? "s"] ?? 1);
51
22
  }
52
23
  /**
53
24
  * Extract Bearer token from Authorization header
@@ -58,7 +29,12 @@ function extractBearerToken(request) {
58
29
  return auth.slice(7);
59
30
  }
60
31
  const authPlugin = async (fastify, opts = {}) => {
61
- const { jwt: jwtConfig, authenticate: appAuthenticator, onFailure, userProperty = "user", exposeAuthErrors = false } = opts;
32
+ const { jwt: jwtConfig, authenticate: appAuthenticator, onFailure, userProperty = "user", exposeAuthErrors = false, tokenExtractor, isRevoked } = opts;
33
+ /** Extract token from request — uses custom extractor if provided, else Bearer header */
34
+ const resolveToken = (request) => {
35
+ if (tokenExtractor) return tokenExtractor(request);
36
+ return extractBearerToken(request);
37
+ };
62
38
  let jwtContext = null;
63
39
  if (jwtConfig?.secret) {
64
40
  if (jwtConfig.secret.length < 32) throw new Error(`JWT secret must be at least 32 characters (current: ${jwtConfig.secret.length}).\nUse a strong random secret for production.`);
@@ -104,7 +80,7 @@ const authPlugin = async (fastify, opts = {}) => {
104
80
  let user = null;
105
81
  if (appAuthenticator) user = await appAuthenticator(request, authContext);
106
82
  else if (jwtContext) {
107
- const token = extractBearerToken(request);
83
+ const token = resolveToken(request);
108
84
  if (token) {
109
85
  const decoded = jwtContext.verify(token);
110
86
  if (decoded.type === "refresh") throw new Error("Refresh tokens cannot be used for authentication");
@@ -112,17 +88,31 @@ const authPlugin = async (fastify, opts = {}) => {
112
88
  }
113
89
  } else throw new Error("No authenticator configured. Provide auth.authenticate function or auth.jwt.secret.");
114
90
  if (!user) throw new Error("Authentication required");
91
+ if (isRevoked) try {
92
+ if (await isRevoked(user)) throw new Error("Token has been revoked");
93
+ } catch (revokeErr) {
94
+ if (revokeErr instanceof Error && revokeErr.message === "Token has been revoked") throw revokeErr;
95
+ throw new Error("Token revocation check failed");
96
+ }
115
97
  const reqRecord = request;
116
98
  reqRecord.user = user;
117
99
  reqRecord[userProperty] = user;
118
100
  if (!request.scope || request.scope.kind === "public") {
119
101
  const userRecord = user;
102
+ const userId = String(userRecord.id ?? userRecord._id ?? userRecord.sub ?? "") || void 0;
103
+ const userRoles = normalizeRoles(userRecord.role);
120
104
  if (userRecord.organizationId) request.scope = {
121
105
  kind: "member",
106
+ userId,
107
+ userRoles,
122
108
  organizationId: String(userRecord.organizationId),
123
109
  orgRoles: Array.isArray(userRecord.orgRoles) ? userRecord.orgRoles : []
124
110
  };
125
- else request.scope = AUTHENTICATED_SCOPE;
111
+ else request.scope = {
112
+ kind: "authenticated",
113
+ userId,
114
+ userRoles
115
+ };
126
116
  }
127
117
  } catch (err) {
128
118
  const error = err instanceof Error ? err : new Error(String(err));
@@ -152,25 +142,38 @@ const authPlugin = async (fastify, opts = {}) => {
152
142
  let user = null;
153
143
  if (appAuthenticator) user = await appAuthenticator(request, authContext);
154
144
  else if (jwtContext) {
155
- const token = extractBearerToken(request);
145
+ const token = resolveToken(request);
156
146
  if (token) {
157
147
  const decoded = jwtContext.verify(token);
158
148
  if (decoded.type === "refresh") return;
159
149
  user = decoded;
160
150
  }
161
151
  }
152
+ if (user && isRevoked) try {
153
+ if (await isRevoked(user)) return;
154
+ } catch {
155
+ return;
156
+ }
162
157
  if (user) {
163
158
  const reqRecord = request;
164
159
  reqRecord.user = user;
165
160
  reqRecord[userProperty] = user;
166
161
  if (!request.scope || request.scope.kind === "public") {
167
162
  const userRecord = user;
163
+ const userId = String(userRecord.id ?? userRecord._id ?? userRecord.sub ?? "") || void 0;
164
+ const userRoles = normalizeRoles(userRecord.role);
168
165
  if (userRecord.organizationId) request.scope = {
169
166
  kind: "member",
167
+ userId,
168
+ userRoles,
170
169
  organizationId: String(userRecord.organizationId),
171
170
  orgRoles: Array.isArray(userRecord.orgRoles) ? userRecord.orgRoles : []
172
171
  };
173
- else request.scope = AUTHENTICATED_SCOPE;
172
+ else request.scope = {
173
+ kind: "authenticated",
174
+ userId,
175
+ userRoles
176
+ };
174
177
  }
175
178
  }
176
179
  } catch {}
@@ -269,26 +272,9 @@ var authPlugin_default = fp(authPlugin, {
269
272
  name: "arc-auth",
270
273
  fastify: "5.x"
271
274
  });
272
-
273
275
  //#endregion
274
276
  //#region src/auth/betterAuth.ts
275
277
  /**
276
- * Better Auth Adapter for Arc/Fastify
277
- *
278
- * Bridges Fastify <-> Better Auth's Fetch API (Request/Response).
279
- * Better Auth is the USER's dependency -- Arc only provides this thin adapter.
280
- *
281
- * @example
282
- * import { betterAuth } from 'better-auth';
283
- * import { createBetterAuthAdapter } from '@classytic/arc/auth';
284
- *
285
- * const auth = betterAuth({ ... });
286
- *
287
- * const app = await createApp({
288
- * auth: { type: 'betterAuth', betterAuth: createBetterAuthAdapter({ auth }) },
289
- * });
290
- */
291
- /**
292
278
  * Convert a Fastify request into a Fetch API Request.
293
279
  *
294
280
  * Better Auth expects standard Web API Request objects.
@@ -567,7 +553,14 @@ function createBetterAuthAdapter(options) {
567
553
  const req = request;
568
554
  req.user = sessionData.user;
569
555
  req.session = sessionData.session;
570
- req.scope = AUTHENTICATED_SCOPE;
556
+ const baUser = sessionData.user;
557
+ const userId = String(baUser.id ?? baUser._id ?? "") || void 0;
558
+ const userRoles = normalizeRoles(baUser.role);
559
+ req.scope = {
560
+ kind: "authenticated",
561
+ userId,
562
+ userRoles
563
+ };
571
564
  if (orgEnabled) {
572
565
  const session = sessionData.session;
573
566
  const activeOrgId = session?.activeOrganizationId || request.headers["x-organization-id"];
@@ -578,6 +571,8 @@ function createBetterAuthAdapter(options) {
578
571
  if (orgRoles) {
579
572
  const scope = {
580
573
  kind: "member",
574
+ userId,
575
+ userRoles,
581
576
  organizationId: activeOrgId,
582
577
  orgRoles
583
578
  };
@@ -596,7 +591,7 @@ function createBetterAuthAdapter(options) {
596
591
  teams = Array.isArray(teamsData) ? teamsData : teamsData?.teams ?? [];
597
592
  }
598
593
  }
599
- if (teams && teams.some((t) => t.id === activeTeamId)) scope.teamId = activeTeamId;
594
+ if (teams?.some((t) => t.id === activeTeamId)) scope.teamId = activeTeamId;
600
595
  }
601
596
  req.scope = scope;
602
597
  }
@@ -637,7 +632,14 @@ function createBetterAuthAdapter(options) {
637
632
  const req = request;
638
633
  req.user = sessionData.user;
639
634
  req.session = sessionData.session;
640
- req.scope = AUTHENTICATED_SCOPE;
635
+ const optUser = sessionData.user;
636
+ const optUserId = String(optUser.id ?? optUser._id ?? "") || void 0;
637
+ const optUserRoles = normalizeRoles(optUser.role);
638
+ req.scope = {
639
+ kind: "authenticated",
640
+ userId: optUserId,
641
+ userRoles: optUserRoles
642
+ };
641
643
  if (orgEnabled) {
642
644
  const activeOrgId = sessionData.session?.activeOrganizationId || request.headers["x-organization-id"];
643
645
  if (activeOrgId) {
@@ -646,6 +648,8 @@ function createBetterAuthAdapter(options) {
646
648
  if (!orgRoles) orgRoles = await resolveOrgRoles(auth, request.protocol ?? "http", request.hostname ?? "localhost", normalizedBase, headers, activeOrgId);
647
649
  if (orgRoles) req.scope = {
648
650
  kind: "member",
651
+ userId: optUserId,
652
+ userRoles: optUserRoles,
649
653
  organizationId: activeOrgId,
650
654
  orgRoles
651
655
  };
@@ -672,7 +676,7 @@ function createBetterAuthAdapter(options) {
672
676
  if (!fastify.hasDecorator("authenticate")) fastify.decorate("authenticate", authenticate);
673
677
  if (!fastify.hasDecorator("optionalAuthenticate")) fastify.decorate("optionalAuthenticate", optionalAuthenticate);
674
678
  if (!extractedOpenApi && openapiOpt !== false && auth.api && typeof auth.api === "object") {
675
- const { extractBetterAuthOpenApi } = await import("../betterAuthOpenApi-DjWDddNc.mjs").then((n) => n.t);
679
+ const { extractBetterAuthOpenApi } = await import("../betterAuthOpenApi-lz0IRbXJ.mjs").then((n) => n.t);
676
680
  extractedOpenApi = extractBetterAuthOpenApi(auth.api, {
677
681
  basePath,
678
682
  userFields
@@ -699,7 +703,6 @@ function createBetterAuthAdapter(options) {
699
703
  openapi: extractedOpenApi
700
704
  };
701
705
  }
702
-
703
706
  //#endregion
704
707
  //#region src/auth/sessionManager.ts
705
708
  /**
@@ -1091,6 +1094,5 @@ function createSessionManager(options) {
1091
1094
  requireFresh
1092
1095
  };
1093
1096
  }
1094
-
1095
1097
  //#endregion
1096
- export { MemorySessionStore, authPlugin_default as authPlugin, authPlugin as authPluginFn, createBetterAuthAdapter, createSessionManager, extractBetterAuthOpenApi };
1098
+ export { MemorySessionStore, authPlugin_default as authPlugin, authPlugin as authPluginFn, createBetterAuthAdapter, createSessionManager, extractBetterAuthOpenApi };
@@ -1,4 +1,4 @@
1
- import { i as SessionData, s as SessionStore } from "../sessionManager-D_iEHjQl.mjs";
1
+ import { i as SessionData, s as SessionStore } from "../sessionManager-wbkYj2HL.mjs";
2
2
 
3
3
  //#region src/auth/redis-session.d.ts
4
4
  /** Minimal Redis client interface — compatible with ioredis */
@@ -70,6 +70,5 @@ var RedisSessionStore = class {
70
70
  }
71
71
  }
72
72
  };
73
-
74
73
  //#endregion
75
- export { RedisSessionStore };
74
+ export { RedisSessionStore };
@@ -1,6 +1,5 @@
1
- import { t as __exportAll } from "./chunk-C7Uep-_p.mjs";
2
- import { a as toJsonSchema } from "./schemaConverter-Dtg0Kt9T.mjs";
3
-
1
+ import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
2
+ import { a as toJsonSchema } from "./schemaConverter-DjzHpFam.mjs";
4
3
  //#region src/auth/betterAuthOpenApi.ts
5
4
  var betterAuthOpenApi_exports = /* @__PURE__ */ __exportAll({ extractBetterAuthOpenApi: () => extractBetterAuthOpenApi });
6
5
  /**
@@ -160,7 +159,7 @@ function extractBetterAuthOpenApi(authApi, options = {}) {
160
159
  }
161
160
  };
162
161
  if (userFields) {
163
- const userProps = schemas.User.properties;
162
+ const userProps = schemas.User?.properties;
164
163
  for (const [name, field] of Object.entries(userFields)) {
165
164
  const prop = { type: field.type };
166
165
  if (field.description) prop.description = field.description;
@@ -244,6 +243,5 @@ function mergeUserFieldsIntoRequestBody(requestBody, userFields, endpointPath) {
244
243
  if (isRequired && !required.includes(name)) required.push(name);
245
244
  }
246
245
  }
247
-
248
246
  //#endregion
249
- export { extractBetterAuthOpenApi as n, betterAuthOpenApi_exports as t };
247
+ export { extractBetterAuthOpenApi as n, betterAuthOpenApi_exports as t };