@classytic/arc 2.3.0 → 2.4.2

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 (175) 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-CgKOPhA4.mjs → createApp-ByWNRsZj.mjs} +64 -35
  39. package/dist/{defineResource-DWbpJYtm.mjs → defineResource-D9aY5Cy6.mjs} +108 -1157
  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-6b_eRDBw.d.mts → index-BL8CaQih.d.mts} +56 -57
  71. package/dist/index-Diqcm14c.d.mts +369 -0
  72. package/dist/{prisma-Dy5S5F5i.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.d.mts +113 -44
  99. package/dist/migrations/index.mjs +84 -102
  100. package/dist/{mongodb-DNKEExbf.mjs → mongodb-BuQ7fNTg.mjs} +1 -4
  101. package/dist/{mongodb-ClykrfGo.d.mts → mongodb-CUpYfxfD.d.mts} +2 -3
  102. package/dist/{mongodb-Dg8O_gvd.d.mts → mongodb-bga9AbkD.d.mts} +2 -2
  103. package/dist/{openapi-9nB_kiuR.mjs → openapi-CBmZ6EQN.mjs} +4 -21
  104. package/dist/org/index.d.mts +12 -14
  105. package/dist/org/index.mjs +92 -119
  106. package/dist/org/types.d.mts +2 -2
  107. package/dist/org/types.mjs +1 -1
  108. package/dist/permissions/index.d.mts +4 -278
  109. package/dist/permissions/index.mjs +4 -579
  110. package/dist/permissions-CA5zg0yK.mjs +751 -0
  111. package/dist/plugins/index.d.mts +104 -107
  112. package/dist/plugins/index.mjs +203 -313
  113. package/dist/plugins/response-cache.mjs +4 -69
  114. package/dist/plugins/tracing-entry.d.mts +1 -1
  115. package/dist/plugins/tracing-entry.mjs +24 -11
  116. package/dist/{pluralize-CM-jZg7p.mjs → pluralize-CcT6qF0a.mjs} +12 -13
  117. package/dist/policies/index.d.mts +2 -2
  118. package/dist/policies/index.mjs +80 -83
  119. package/dist/presets/index.d.mts +26 -19
  120. package/dist/presets/index.mjs +2 -142
  121. package/dist/presets/multiTenant.d.mts +1 -4
  122. package/dist/presets/multiTenant.mjs +4 -6
  123. package/dist/presets-C9QXJV1u.mjs +422 -0
  124. package/dist/{queryCachePlugin-B6R0d4av.mjs → queryCachePlugin-ClosZdNS.mjs} +6 -27
  125. package/dist/{queryCachePlugin-Q6SYuHZ6.d.mts → queryCachePlugin-DcmETvcB.d.mts} +3 -3
  126. package/dist/queryParser-CgCtsjti.mjs +352 -0
  127. package/dist/{redis-UwjEp8Ea.d.mts → redis-CQ5YxMC5.d.mts} +2 -2
  128. package/dist/{redis-stream-CBg0upHI.d.mts → redis-stream-BW9UKLZM.d.mts} +9 -2
  129. package/dist/registry/index.d.mts +1 -4
  130. package/dist/registry/index.mjs +3 -4
  131. package/dist/{introspectionPlugin-B3JkrjwU.mjs → registry-I-ogLgL9.mjs} +1 -8
  132. package/dist/{requestContext-xi6OKBL-.mjs → requestContext-DYtmNpm5.mjs} +1 -3
  133. package/dist/resourceToTools-PMFE8HIv.mjs +533 -0
  134. package/dist/rpc/index.d.mts +90 -0
  135. package/dist/rpc/index.mjs +248 -0
  136. package/dist/{schemaConverter-Dtg0Kt9T.mjs → schemaConverter-DjzHpFam.mjs} +1 -2
  137. package/dist/schemas/index.d.mts +30 -30
  138. package/dist/schemas/index.mjs +2 -4
  139. package/dist/scope/index.d.mts +13 -2
  140. package/dist/scope/index.mjs +18 -5
  141. package/dist/{sessionManager-D_iEHjQl.d.mts → sessionManager-wbkYj2HL.d.mts} +2 -2
  142. package/dist/{sse-DkqQ1uxb.mjs → sse-BkViJPlT.mjs} +4 -25
  143. package/dist/testing/index.d.mts +551 -567
  144. package/dist/testing/index.mjs +1744 -1799
  145. package/dist/{tracing-8CEbhF0w.d.mts → tracing-bz_U4EM1.d.mts} +6 -1
  146. package/dist/{typeGuards-DwxA1t_L.mjs → typeGuards-Cj5Rgvlg.mjs} +1 -2
  147. package/dist/types/index.d.mts +4 -946
  148. package/dist/types/index.mjs +2 -4
  149. package/dist/types-BJmgxNbF.d.mts +275 -0
  150. package/dist/{types-RLkFVgaw.d.mts → types-BNUccdcf.d.mts} +2 -2
  151. package/dist/{types-Beqn1Un7.mjs → types-C6TQjtdi.mjs} +30 -2
  152. package/dist/{types-tKwaViYB.d.mts → types-Dt0-AI6E.d.mts} +68 -27
  153. package/dist/{types-DelU6kln.mjs → types-ZUu_h0jp.mjs} +1 -2
  154. package/dist/utils/index.d.mts +254 -351
  155. package/dist/utils/index.mjs +7 -6
  156. package/dist/utils-Dc0WhlIl.mjs +594 -0
  157. package/dist/versioning-BzfeHmhj.mjs +37 -0
  158. package/package.json +44 -10
  159. package/skills/arc/SKILL.md +518 -0
  160. package/skills/arc/references/auth.md +250 -0
  161. package/skills/arc/references/events.md +272 -0
  162. package/skills/arc/references/integrations.md +385 -0
  163. package/skills/arc/references/mcp.md +431 -0
  164. package/skills/arc/references/production.md +610 -0
  165. package/skills/arc/references/testing.md +183 -0
  166. package/dist/audited-CGdLiSlE.mjs +0 -140
  167. package/dist/chunk-C7Uep-_p.mjs +0 -20
  168. package/dist/circuitBreaker-CSS2VvL6.mjs +0 -1109
  169. package/dist/errorHandler-CW3OOeYq.d.mts +0 -72
  170. package/dist/interface-BtdYtQUA.d.mts +0 -1114
  171. package/dist/presets-BTeYbw7h.d.mts +0 -57
  172. package/dist/presets-CeFtfDR8.mjs +0 -119
  173. /package/dist/{errors-DAWRdiYP.d.mts → errors-CPpvPHT0.d.mts} +0 -0
  174. /package/dist/{externalPaths-SyPF2tgK.d.mts → externalPaths-DpO-s7r8.d.mts} +0 -0
  175. /package/dist/{interface-DTbsvIWe.d.mts → interface-D_BWALyZ.d.mts} +0 -0
@@ -1,6 +1,42 @@
1
- import mongoose from "mongoose";
2
-
3
1
  //#region src/migrations/index.ts
2
+ /** Default logger that writes to stdout/stderr */
3
+ const defaultLogger = {
4
+ info: (msg) => process.stdout.write(`${msg}\n`),
5
+ error: (msg) => process.stderr.write(`${msg}\n`)
6
+ };
7
+ /**
8
+ * MongoDB-backed migration store.
9
+ *
10
+ * Uses a `_migrations` collection in the same database.
11
+ * The `db` parameter accepts any object with a `.collection()` method
12
+ * (Mongoose db, native MongoDB Db, etc.)
13
+ */
14
+ var MongoMigrationStore = class {
15
+ collectionName;
16
+ db;
17
+ constructor(db, opts) {
18
+ this.db = db;
19
+ this.collectionName = opts?.collectionName ?? "_migrations";
20
+ }
21
+ async getApplied() {
22
+ return await this.db.collection(this.collectionName).find({}).sort({ appliedAt: 1 }).toArray();
23
+ }
24
+ async record(migration, executionTime) {
25
+ await this.db.collection(this.collectionName).insertOne({
26
+ version: migration.version,
27
+ resource: migration.resource,
28
+ description: migration.description,
29
+ appliedAt: /* @__PURE__ */ new Date(),
30
+ executionTime
31
+ });
32
+ }
33
+ async remove(migration) {
34
+ await this.db.collection(this.collectionName).deleteOne({
35
+ version: migration.version,
36
+ resource: migration.resource
37
+ });
38
+ }
39
+ };
4
40
  /**
5
41
  * Define a migration
6
42
  */
@@ -10,77 +46,97 @@ function defineMigration(migration) {
10
46
  /**
11
47
  * Migration Runner
12
48
  *
13
- * Manages execution of migrations with tracking and rollback support.
49
+ * DB-agnostic. Manages execution of migrations with tracking and rollback.
50
+ * The `db` parameter is passed through to migration `up`/`down` functions
51
+ * as-is — the runner never touches it directly.
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * // MongoDB
56
+ * const runner = new MigrationRunner(mongoose.connection.db, {
57
+ * store: new MongoMigrationStore(mongoose.connection.db),
58
+ * });
59
+ *
60
+ * // Prisma
61
+ * const runner = new MigrationRunner(prisma, {
62
+ * store: new PrismaMigrationStore(prisma), // user-implemented
63
+ * });
64
+ *
65
+ * await runner.up(migrations);
66
+ * ```
14
67
  */
15
68
  var MigrationRunner = class {
16
- collectionName = "_migrations";
17
69
  db;
18
- constructor(db) {
70
+ store;
71
+ log;
72
+ constructor(db, opts) {
19
73
  this.db = db;
74
+ this.store = opts.store;
75
+ this.log = opts.logger ?? defaultLogger;
20
76
  }
21
77
  /**
22
78
  * Run all pending migrations
23
79
  */
24
80
  async up(migrations) {
25
- const applied = await this.getAppliedMigrations();
81
+ const applied = await this.store.getApplied();
26
82
  const appliedVersions = new Set(applied.map((m) => `${m.resource}:${m.version}`));
27
83
  const pending = migrations.filter((m) => !appliedVersions.has(`${m.resource}:${m.version}`)).sort((a, b) => a.version - b.version);
28
84
  if (pending.length === 0) {
29
- console.log("No pending migrations");
85
+ this.log.info("No pending migrations");
30
86
  return;
31
87
  }
32
- console.log(`Running ${pending.length} migration(s)...\n`);
88
+ this.log.info(`Running ${pending.length} migration(s)...`);
33
89
  for (const migration of pending) await this.runMigration(migration, "up");
34
- console.log("\nAll migrations completed successfully");
90
+ this.log.info("All migrations completed successfully");
35
91
  }
36
92
  /**
37
93
  * Rollback last migration
38
94
  */
39
95
  async down(migrations) {
40
- const applied = await this.getAppliedMigrations();
96
+ const applied = await this.store.getApplied();
41
97
  if (applied.length === 0) {
42
- console.log("No migrations to rollback");
98
+ this.log.info("No migrations to rollback");
43
99
  return;
44
100
  }
45
101
  const last = applied[applied.length - 1];
46
102
  if (!last) {
47
- console.log("No migrations to rollback");
103
+ this.log.info("No migrations to rollback");
48
104
  return;
49
105
  }
50
106
  const migration = migrations.find((m) => m.resource === last.resource && m.version === last.version);
51
107
  if (!migration) throw new Error(`Migration ${last.resource}:${last.version} not found in migration files`);
52
- console.log(`Rolling back ${migration.resource} v${migration.version}...`);
108
+ this.log.info(`Rolling back ${migration.resource} v${migration.version}...`);
53
109
  await this.runMigration(migration, "down", true);
54
- console.log("Rollback completed");
110
+ this.log.info("Rollback completed");
55
111
  }
56
112
  /**
57
113
  * Rollback to specific version
58
114
  */
59
115
  async downTo(migrations, targetVersion) {
60
- const toRollback = (await this.getAppliedMigrations()).filter((m) => m.version > targetVersion).reverse();
116
+ const toRollback = (await this.store.getApplied()).filter((m) => m.version > targetVersion).reverse();
61
117
  if (toRollback.length === 0) {
62
- console.log(`Already at or below version ${targetVersion}`);
118
+ this.log.info(`Already at or below version ${targetVersion}`);
63
119
  return;
64
120
  }
65
- console.log(`Rolling back ${toRollback.length} migration(s)...\n`);
121
+ this.log.info(`Rolling back ${toRollback.length} migration(s)...`);
66
122
  for (const record of toRollback) {
67
123
  const migration = migrations.find((m) => m.resource === record.resource && m.version === record.version);
68
124
  if (!migration) throw new Error(`Migration ${record.resource}:${record.version} not found`);
69
125
  await this.runMigration(migration, "down", true);
70
126
  }
71
- console.log("\nRollback completed");
127
+ this.log.info("Rollback completed");
72
128
  }
73
129
  /**
74
130
  * Get all applied migrations
75
131
  */
76
132
  async getAppliedMigrations() {
77
- return await this.db.collection(this.collectionName).find({}).sort({ appliedAt: 1 }).toArray();
133
+ return this.store.getApplied();
78
134
  }
79
135
  /**
80
136
  * Get pending migrations
81
137
  */
82
138
  async getPendingMigrations(migrations) {
83
- const applied = await this.getAppliedMigrations();
139
+ const applied = await this.store.getApplied();
84
140
  const appliedVersions = new Set(applied.map((m) => `${m.resource}:${m.version}`));
85
141
  return migrations.filter((m) => !appliedVersions.has(`${m.resource}:${m.version}`));
86
142
  }
@@ -96,46 +152,27 @@ var MigrationRunner = class {
96
152
  async runMigration(migration, direction, isRollback = false) {
97
153
  const start = Date.now();
98
154
  const action = direction === "up" ? "Applying" : "Rolling back";
99
- console.log(`${action} ${migration.resource} v${migration.version}${migration.description ? `: ${migration.description}` : ""}...`);
155
+ const label = `${migration.resource} v${migration.version}`;
156
+ const desc = migration.description ? `: ${migration.description}` : "";
157
+ this.log.info(`${action} ${label}${desc}...`);
100
158
  try {
101
159
  if (direction === "up") {
102
160
  await migration.up(this.db);
103
161
  if (migration.validate) {
104
162
  if (!await migration.validate(this.db)) throw new Error("Migration validation failed");
105
163
  }
106
- await this.recordMigration(migration, Date.now() - start);
164
+ await this.store.record(migration, Date.now() - start);
107
165
  } else {
108
166
  await migration.down(this.db);
109
- if (isRollback) await this.removeMigration(migration);
167
+ if (isRollback) await this.store.remove(migration);
110
168
  }
111
169
  const duration = Date.now() - start;
112
- console.log(`✅ ${migration.resource} v${migration.version} (${duration}ms)`);
170
+ this.log.info(`${label} completed (${duration}ms)`);
113
171
  } catch (error) {
114
- console.error(`❌ ${migration.resource} v${migration.version} failed:`, error.message);
172
+ this.log.error(`${label} failed: ${error.message}`);
115
173
  throw error;
116
174
  }
117
175
  }
118
- /**
119
- * Record a completed migration
120
- */
121
- async recordMigration(migration, executionTime) {
122
- await this.db.collection(this.collectionName).insertOne({
123
- version: migration.version,
124
- resource: migration.resource,
125
- description: migration.description,
126
- appliedAt: /* @__PURE__ */ new Date(),
127
- executionTime
128
- });
129
- }
130
- /**
131
- * Remove a migration record
132
- */
133
- async removeMigration(migration) {
134
- await this.db.collection(this.collectionName).deleteOne({
135
- version: migration.version,
136
- resource: migration.resource
137
- });
138
- }
139
176
  };
140
177
  /**
141
178
  * Add versioning to resource definition
@@ -201,60 +238,5 @@ var MigrationRegistry = class {
201
238
  this.migrations.clear();
202
239
  }
203
240
  };
204
- /**
205
- * Global migration registry instance
206
- */
207
- const migrationRegistry = new MigrationRegistry();
208
- /**
209
- * Common migration helpers
210
- */
211
- const migrationHelpers = {
212
- renameField: (collection, oldName, newName) => defineMigration({
213
- version: 0,
214
- resource: collection,
215
- description: `Rename ${oldName} to ${newName}`,
216
- up: async (db) => {
217
- await db.collection(collection).updateMany({}, { $rename: { [oldName]: newName } });
218
- },
219
- down: async (db) => {
220
- await db.collection(collection).updateMany({}, { $rename: { [newName]: oldName } });
221
- }
222
- }),
223
- addField: (collection, fieldName, defaultValue) => defineMigration({
224
- version: 0,
225
- resource: collection,
226
- description: `Add ${fieldName} field`,
227
- up: async (db) => {
228
- await db.collection(collection).updateMany({ [fieldName]: { $exists: false } }, { $set: { [fieldName]: defaultValue } });
229
- },
230
- down: async (db) => {
231
- await db.collection(collection).updateMany({}, { $unset: { [fieldName]: "" } });
232
- }
233
- }),
234
- removeField: (collection, fieldName) => defineMigration({
235
- version: 0,
236
- resource: collection,
237
- description: `Remove ${fieldName} field`,
238
- up: async (db) => {
239
- await db.collection(collection).updateMany({}, { $unset: { [fieldName]: "" } });
240
- },
241
- down: async (db) => {
242
- console.warn(`Cannot restore ${fieldName} field - data was deleted`);
243
- }
244
- }),
245
- createIndex: (collection, fields, options) => defineMigration({
246
- version: 0,
247
- resource: collection,
248
- description: `Create index on ${Object.keys(fields).join(", ")}`,
249
- up: async (db) => {
250
- await db.collection(collection).createIndex(fields, options);
251
- },
252
- down: async (db) => {
253
- const indexName = typeof options?.name === "string" ? options.name : Object.keys(fields).join("_");
254
- await db.collection(collection).dropIndex(indexName);
255
- }
256
- })
257
- };
258
-
259
241
  //#endregion
260
- export { MigrationRegistry, MigrationRunner, defineMigration, migrationHelpers, migrationRegistry, withSchemaVersion };
242
+ export { MigrationRegistry, MigrationRunner, MongoMigrationStore, defineMigration, withSchemaVersion };
@@ -4,9 +4,7 @@ var MongoAuditStore = class {
4
4
  collection;
5
5
  initialized = false;
6
6
  ttlDays;
7
- options;
8
7
  constructor(options) {
9
- this.options = options;
10
8
  const collectionName = options.collection ?? "audit_logs";
11
9
  this.collection = options.connection.collection(collectionName);
12
10
  this.ttlDays = options.ttlDays ?? 90;
@@ -88,6 +86,5 @@ var MongoAuditStore = class {
88
86
  }));
89
87
  }
90
88
  };
91
-
92
89
  //#endregion
93
- export { MongoAuditStore as t };
90
+ export { MongoAuditStore as t };
@@ -1,7 +1,7 @@
1
- import { i as UserBase } from "./types-RLkFVgaw.mjs";
1
+ import { i as UserBase } from "./types-BNUccdcf.mjs";
2
2
 
3
3
  //#region src/audit/stores/interface.d.ts
4
- type AuditAction = 'create' | 'update' | 'delete' | 'restore' | 'custom';
4
+ type AuditAction = "create" | "update" | "delete" | "restore" | "custom";
5
5
  interface AuditEntry {
6
6
  /** Unique audit log ID */
7
7
  id: string;
@@ -108,7 +108,6 @@ declare class MongoAuditStore implements AuditStore {
108
108
  private collection;
109
109
  private initialized;
110
110
  private ttlDays;
111
- private options;
112
111
  constructor(options: MongoAuditStoreOptions);
113
112
  private ensureIndexes;
114
113
  log(entry: AuditEntry): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { n as IdempotencyResult, r as IdempotencyStore } from "./interface-CSNjltAc.mjs";
1
+ import { n as IdempotencyResult, r as IdempotencyStore } from "./interface-B4awm1RJ.mjs";
2
2
 
3
3
  //#region src/idempotency/stores/mongodb.d.ts
4
4
  interface MongoConnection {
@@ -58,7 +58,7 @@ declare class MongoIdempotencyStore implements IdempotencyStore {
58
58
  private get collection();
59
59
  private ensureIndex;
60
60
  get(key: string): Promise<IdempotencyResult | undefined>;
61
- set(key: string, result: Omit<IdempotencyResult, 'key'>): Promise<void>;
61
+ set(key: string, result: Omit<IdempotencyResult, "key">): Promise<void>;
62
62
  tryLock(key: string, requestId: string, ttlMs: number): Promise<boolean>;
63
63
  unlock(key: string, requestId: string): Promise<void>;
64
64
  isLocked(key: string): Promise<boolean>;
@@ -1,23 +1,7 @@
1
- import { t as getUserRoles } from "./types-DelU6kln.mjs";
2
- import { n as convertRouteSchema } from "./schemaConverter-Dtg0Kt9T.mjs";
1
+ import { t as getUserRoles } from "./types-ZUu_h0jp.mjs";
2
+ import { n as convertRouteSchema } from "./schemaConverter-DjzHpFam.mjs";
3
3
  import fp from "fastify-plugin";
4
-
5
4
  //#region src/docs/openapi.ts
6
- /**
7
- * OpenAPI Spec Generator
8
- *
9
- * Auto-generates OpenAPI 3.0 specification from Arc resource registry.
10
- *
11
- * @example
12
- * import { openApiPlugin } from '@classytic/arc/docs';
13
- *
14
- * await fastify.register(openApiPlugin, {
15
- * title: 'My API',
16
- * version: '1.0.0',
17
- * });
18
- *
19
- * // Spec available at /_docs/openapi.json
20
- */
21
5
  const openApiPlugin = async (fastify, opts = {}) => {
22
6
  const { title = "Arc API", version = "1.0.0", description, serverUrl, prefix = "/_docs", apiPrefix = "", authRoles = [] } = opts;
23
7
  const buildSpec = () => {
@@ -481,7 +465,7 @@ function generateSchemas(resources) {
481
465
  if (fieldPerms && schemas[resource.name]?.properties) {
482
466
  const props = schemas[resource.name].properties;
483
467
  for (const [field, perm] of Object.entries(fieldPerms)) if (props[field]) {
484
- const desc = props[field].description ?? "";
468
+ const desc = props[field]?.description ?? "";
485
469
  const permDesc = formatFieldPermDescription(perm);
486
470
  props[field].description = desc ? `${desc} (${permDesc})` : permDesc;
487
471
  } else if (perm.type === "hidden") {}
@@ -520,6 +504,5 @@ var openapi_default = fp(openApiPlugin, {
520
504
  name: "arc-openapi",
521
505
  fastify: "5.x"
522
506
  });
523
-
524
507
  //#endregion
525
- export { openApiPlugin as n, openapi_default as r, buildOpenApiSpec as t };
508
+ export { openApiPlugin as n, openapi_default as r, buildOpenApiSpec as t };
@@ -1,10 +1,18 @@
1
- import "../elevation-DGo5shaX.mjs";
2
- import { S as RouteHandler } from "../interface-BtdYtQUA.mjs";
3
- import { i as UserBase } from "../types-RLkFVgaw.mjs";
4
- import "../types/index.mjs";
1
+ import { Pt as RouteHandler } from "../interface-DGmPxakH.mjs";
2
+ import { i as UserBase } from "../types-BNUccdcf.mjs";
5
3
  import { InvitationAdapter, InvitationDoc, MemberDoc, OrgAdapter, OrgDoc, OrgPermissionStatement, OrgRole, OrganizationPluginOptions } from "./types.mjs";
6
4
  import { FastifyPluginAsync, RouteHandlerMethod } from "fastify";
7
5
 
6
+ //#region src/org/organizationPlugin.d.ts
7
+ declare module "fastify" {
8
+ interface FastifyInstance {
9
+ /** Middleware: require the caller to hold one of the listed org roles */
10
+ requireOrgRole: (roles: string[]) => RouteHandlerMethod;
11
+ }
12
+ }
13
+ declare const organizationPlugin: FastifyPluginAsync<OrganizationPluginOptions>;
14
+ declare const _default: FastifyPluginAsync<OrganizationPluginOptions>;
15
+ //#endregion
8
16
  //#region src/org/orgGuard.d.ts
9
17
  interface OrgGuardOptions {
10
18
  /** Require organization context (default: true) */
@@ -55,14 +63,4 @@ declare function getUserOrgRoles(user: UserBase | undefined | null, orgId: strin
55
63
  */
56
64
  declare function hasOrgRole(user: UserBase | undefined | null, orgId: string | undefined | null, roles: string | string[], options?: OrgRolesOptions): boolean;
57
65
  //#endregion
58
- //#region src/org/organizationPlugin.d.ts
59
- declare module 'fastify' {
60
- interface FastifyInstance {
61
- /** Middleware: require the caller to hold one of the listed org roles */
62
- requireOrgRole: (roles: string[]) => RouteHandlerMethod;
63
- }
64
- }
65
- declare const organizationPlugin: FastifyPluginAsync<OrganizationPluginOptions>;
66
- declare const _default: FastifyPluginAsync<OrganizationPluginOptions>;
67
- //#endregion
68
66
  export { type InvitationAdapter, type InvitationDoc, type MemberDoc, type OrgAdapter, type OrgDoc, type OrgGuardOptions, type OrgMembershipOptions, type OrgPermissionStatement, type OrgRole, type OrgRolesOptions, type OrganizationPluginOptions, getUserOrgRoles, hasOrgRole, orgGuard, orgMembershipCheck, _default as organizationPlugin, organizationPlugin as organizationPluginFn, requireOrg, requireOrgRole };
@@ -1,122 +1,6 @@
1
- import { c as isElevated, i as getOrgRoles, l as isMember, n as PUBLIC_SCOPE, o as hasOrgAccess } from "../types-Beqn1Un7.mjs";
1
+ import { c as hasOrgAccess, d as isMember, i as getOrgRoles, n as PUBLIC_SCOPE, u as isElevated } from "../types-C6TQjtdi.mjs";
2
2
  import fp from "fastify-plugin";
3
-
4
- //#region src/org/orgGuard.ts
5
- /**
6
- * Create org guard middleware.
7
- * Reads `request.scope` for org context and roles.
8
- * Elevated scope always passes.
9
- */
10
- function orgGuard(options = {}) {
11
- const { requireOrgContext = true, roles = [] } = options;
12
- return async function orgGuardMiddleware(request, reply) {
13
- const scope = request.scope ?? PUBLIC_SCOPE;
14
- if (isElevated(scope)) return;
15
- if (requireOrgContext && !hasOrgAccess(scope)) {
16
- reply.code(403).send({
17
- success: false,
18
- error: "Organization context required",
19
- code: "ORG_CONTEXT_REQUIRED",
20
- message: "This endpoint requires an organization context. Please specify organization via x-organization-id header."
21
- });
22
- return;
23
- }
24
- if (roles.length > 0 && isMember(scope)) {
25
- const userOrgRoles = getOrgRoles(scope);
26
- if (!roles.some((role) => userOrgRoles.includes(role))) {
27
- reply.code(403).send({
28
- success: false,
29
- error: "Insufficient organization permissions",
30
- code: "ORG_ROLE_REQUIRED",
31
- message: `This action requires one of these organization roles: ${roles.join(", ")}`,
32
- required: roles,
33
- current: userOrgRoles
34
- });
35
- return;
36
- }
37
- }
38
- };
39
- }
40
- /**
41
- * Shorthand for requiring org context
42
- */
43
- function requireOrg() {
44
- return orgGuard({ requireOrgContext: true });
45
- }
46
- /**
47
- * Require org context with specific roles
48
- */
49
- function requireOrgRole(...roles) {
50
- return orgGuard({
51
- requireOrgContext: true,
52
- roles
53
- });
54
- }
55
-
56
- //#endregion
57
- //#region src/org/orgMembership.ts
58
- /**
59
- * Check if user is member of organization.
60
- * This is a low-level utility for checking membership from user object data.
61
- * For request-level checks, use `request.scope` (isMember/isElevated guards).
62
- */
63
- async function orgMembershipCheck(user, orgId, options = {}) {
64
- const { userOrgsPath = "organizations", validateFromDb } = options;
65
- if (!user || !orgId) return false;
66
- if ((user[userOrgsPath] ?? []).some((o) => {
67
- return (o.organizationId?.toString() ?? String(o)) === orgId.toString();
68
- })) return true;
69
- if (validateFromDb) {
70
- const userId = (user._id ?? user.id)?.toString();
71
- if (userId) return validateFromDb(userId, orgId);
72
- }
73
- return false;
74
- }
75
- /**
76
- * Get user's role in organization from user object data.
77
- * For request-level role checks, use `request.scope.orgRoles` (when scope is 'member').
78
- */
79
- function getUserOrgRoles(user, orgId, options = {}) {
80
- const { userOrgsPath = "organizations" } = options;
81
- if (!user || !orgId) return [];
82
- return (user[userOrgsPath] ?? []).find((o) => {
83
- return (o.organizationId?.toString() ?? String(o)) === orgId.toString();
84
- })?.roles ?? [];
85
- }
86
- /**
87
- * Check if user has specific role in organization from user object data.
88
- * For request-level role checks, use `requireOrgRole()` permission or `request.scope`.
89
- */
90
- function hasOrgRole(user, orgId, roles, options = {}) {
91
- const userOrgRoles = getUserOrgRoles(user, orgId, options);
92
- return (Array.isArray(roles) ? roles : [roles]).some((role) => userOrgRoles.includes(role));
93
- }
94
-
95
- //#endregion
96
3
  //#region src/org/organizationPlugin.ts
97
- /**
98
- * Organization Plugin -- Full org management with REST endpoints
99
- *
100
- * Creates these routes:
101
- * - POST /api/organizations -- Create org
102
- * - GET /api/organizations -- List user's orgs
103
- * - GET /api/organizations/:orgId -- Get org
104
- * - PATCH /api/organizations/:orgId -- Update org
105
- * - DELETE /api/organizations/:orgId -- Delete org
106
- * - GET /api/organizations/:orgId/members -- List members
107
- * - POST /api/organizations/:orgId/members -- Add member
108
- * - PATCH /api/organizations/:orgId/members/:userId -- Update role
109
- * - DELETE /api/organizations/:orgId/members/:userId -- Remove member
110
- *
111
- * @example
112
- * import { organizationPlugin } from '@classytic/arc/org';
113
- *
114
- * await fastify.register(organizationPlugin, {
115
- * adapter: myMongooseOrgAdapter,
116
- * basePath: '/api/organizations',
117
- * enableInvitations: false,
118
- * });
119
- */
120
4
  const DEFAULT_ROLES = [
121
5
  {
122
6
  name: "owner",
@@ -508,6 +392,95 @@ var organizationPlugin_default = fp(organizationPlugin, {
508
392
  name: "arc-organization",
509
393
  fastify: "5.x"
510
394
  });
511
-
512
395
  //#endregion
513
- export { getUserOrgRoles, hasOrgRole, orgGuard, orgMembershipCheck, organizationPlugin_default as organizationPlugin, organizationPlugin as organizationPluginFn, requireOrg, requireOrgRole };
396
+ //#region src/org/orgGuard.ts
397
+ /**
398
+ * Create org guard middleware.
399
+ * Reads `request.scope` for org context and roles.
400
+ * Elevated scope always passes.
401
+ */
402
+ function orgGuard(options = {}) {
403
+ const { requireOrgContext = true, roles = [] } = options;
404
+ return async function orgGuardMiddleware(request, reply) {
405
+ const scope = request.scope ?? PUBLIC_SCOPE;
406
+ if (isElevated(scope)) return;
407
+ if (requireOrgContext && !hasOrgAccess(scope)) {
408
+ reply.code(403).send({
409
+ success: false,
410
+ error: "Organization context required",
411
+ code: "ORG_CONTEXT_REQUIRED",
412
+ message: "This endpoint requires an organization context. Please specify organization via x-organization-id header."
413
+ });
414
+ return;
415
+ }
416
+ if (roles.length > 0 && isMember(scope)) {
417
+ const userOrgRoles = getOrgRoles(scope);
418
+ if (!roles.some((role) => userOrgRoles.includes(role))) {
419
+ reply.code(403).send({
420
+ success: false,
421
+ error: "Insufficient organization permissions",
422
+ code: "ORG_ROLE_REQUIRED",
423
+ message: `This action requires one of these organization roles: ${roles.join(", ")}`,
424
+ required: roles,
425
+ current: userOrgRoles
426
+ });
427
+ return;
428
+ }
429
+ }
430
+ };
431
+ }
432
+ /**
433
+ * Shorthand for requiring org context
434
+ */
435
+ function requireOrg() {
436
+ return orgGuard({ requireOrgContext: true });
437
+ }
438
+ /**
439
+ * Require org context with specific roles
440
+ */
441
+ function requireOrgRole(...roles) {
442
+ return orgGuard({
443
+ requireOrgContext: true,
444
+ roles
445
+ });
446
+ }
447
+ //#endregion
448
+ //#region src/org/orgMembership.ts
449
+ /**
450
+ * Check if user is member of organization.
451
+ * This is a low-level utility for checking membership from user object data.
452
+ * For request-level checks, use `request.scope` (isMember/isElevated guards).
453
+ */
454
+ async function orgMembershipCheck(user, orgId, options = {}) {
455
+ const { userOrgsPath = "organizations", validateFromDb } = options;
456
+ if (!user || !orgId) return false;
457
+ if ((user[userOrgsPath] ?? []).some((o) => {
458
+ return (o.organizationId?.toString() ?? String(o)) === orgId.toString();
459
+ })) return true;
460
+ if (validateFromDb) {
461
+ const userId = (user._id ?? user.id)?.toString();
462
+ if (userId) return validateFromDb(userId, orgId);
463
+ }
464
+ return false;
465
+ }
466
+ /**
467
+ * Get user's role in organization from user object data.
468
+ * For request-level role checks, use `request.scope.orgRoles` (when scope is 'member').
469
+ */
470
+ function getUserOrgRoles(user, orgId, options = {}) {
471
+ const { userOrgsPath = "organizations" } = options;
472
+ if (!user || !orgId) return [];
473
+ return (user[userOrgsPath] ?? []).find((o) => {
474
+ return (o.organizationId?.toString() ?? String(o)) === orgId.toString();
475
+ })?.roles ?? [];
476
+ }
477
+ /**
478
+ * Check if user has specific role in organization from user object data.
479
+ * For request-level role checks, use `requireOrgRole()` permission or `request.scope`.
480
+ */
481
+ function hasOrgRole(user, orgId, roles, options = {}) {
482
+ const userOrgRoles = getUserOrgRoles(user, orgId, options);
483
+ return (Array.isArray(roles) ? roles : [roles]).some((role) => userOrgRoles.includes(role));
484
+ }
485
+ //#endregion
486
+ export { getUserOrgRoles, hasOrgRole, orgGuard, orgMembershipCheck, organizationPlugin_default as organizationPlugin, organizationPlugin as organizationPluginFn, requireOrg, requireOrgRole };
@@ -28,7 +28,7 @@ interface InvitationDoc {
28
28
  email: string;
29
29
  role: string;
30
30
  invitedBy: string;
31
- status: 'pending' | 'accepted' | 'rejected' | 'expired';
31
+ status: "pending" | "accepted" | "rejected" | "expired";
32
32
  expiresAt: Date;
33
33
  createdAt?: Date;
34
34
  }
@@ -53,7 +53,7 @@ interface OrgAdapter {
53
53
  invitations?: InvitationAdapter;
54
54
  }
55
55
  interface InvitationAdapter {
56
- create(data: Omit<InvitationDoc, 'id' | 'createdAt'>): Promise<InvitationDoc>;
56
+ create(data: Omit<InvitationDoc, "id" | "createdAt">): Promise<InvitationDoc>;
57
57
  getByToken(token: string): Promise<InvitationDoc | null>;
58
58
  accept(id: string): Promise<void>;
59
59
  reject(id: string): Promise<void>;
@@ -1 +1 @@
1
- export { };
1
+ export {};