@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
@@ -29,14 +29,13 @@ function hashParams(params) {
29
29
  function stableStringify(value) {
30
30
  if (value === null || value === void 0) return "";
31
31
  if (typeof value !== "object") return String(value);
32
- if (Array.isArray(value)) return "[" + value.map(stableStringify).join(",") + "]";
33
- return "{" + Object.keys(value).sort().map((k) => k + ":" + stableStringify(value[k])).join(",") + "}";
32
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
33
+ return "{" + Object.keys(value).sort().map((k) => `${k}:${stableStringify(value[k])}`).join(",") + "}";
34
34
  }
35
35
  function djb2(str) {
36
36
  let hash = 5381;
37
37
  for (let i = 0; i < str.length; i++) hash = (hash << 5) + hash + str.charCodeAt(i) >>> 0;
38
38
  return hash;
39
39
  }
40
-
41
40
  //#endregion
42
- export { versionKey as i, hashParams as n, tagVersionKey as r, buildQueryKey as t };
41
+ export { versionKey as i, hashParams as n, tagVersionKey as r, buildQueryKey as t };
@@ -1,5 +1,4 @@
1
- import { t as __exportAll } from "./chunk-C7Uep-_p.mjs";
2
-
1
+ import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
3
2
  //#region src/logger/index.ts
4
3
  var logger_exports = /* @__PURE__ */ __exportAll({
5
4
  arcLog: () => arcLog,
@@ -73,6 +72,5 @@ function isSuppressed() {
73
72
  const env = typeof process !== "undefined" ? process.env?.ARC_SUPPRESS_WARNINGS : void 0;
74
73
  return env === "1" || env === "true";
75
74
  }
76
-
77
75
  //#endregion
78
- export { configureArcLogger as n, logger_exports as r, arcLog as t };
76
+ export { configureArcLogger as n, logger_exports as r, arcLog as t };
@@ -1,5 +1,4 @@
1
- import { t as __exportAll } from "./chunk-C7Uep-_p.mjs";
2
-
1
+ import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
3
2
  //#region src/cache/memory.ts
4
3
  var memory_exports = /* @__PURE__ */ __exportAll({
5
4
  MemoryCacheStore: () => MemoryCacheStore,
@@ -138,6 +137,5 @@ var MemoryCacheStore = class {
138
137
  function clamp(value, min, max) {
139
138
  return Math.min(max, Math.max(min, value));
140
139
  }
141
-
142
140
  //#endregion
143
- export { memory_exports as n, MemoryCacheStore as t };
141
+ export { memory_exports as n, MemoryCacheStore as t };
@@ -0,0 +1,224 @@
1
+ import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
2
+ import fp from "fastify-plugin";
3
+ //#region src/plugins/metrics.ts
4
+ var metrics_exports = /* @__PURE__ */ __exportAll({
5
+ default: () => metrics_default,
6
+ metricsPlugin: () => metricsPlugin
7
+ });
8
+ var Counter = class {
9
+ data = /* @__PURE__ */ new Map();
10
+ inc(labels, value = 1) {
11
+ const key = labelKey(labels);
12
+ const existing = this.data.get(key);
13
+ if (existing) existing.value += value;
14
+ else this.data.set(key, {
15
+ labels: { ...labels },
16
+ value
17
+ });
18
+ }
19
+ values() {
20
+ return [...this.data.values()];
21
+ }
22
+ reset() {
23
+ this.data.clear();
24
+ }
25
+ };
26
+ var Histogram = class {
27
+ data = /* @__PURE__ */ new Map();
28
+ observe(labels, value) {
29
+ const key = labelKey(labels);
30
+ const existing = this.data.get(key);
31
+ if (existing) {
32
+ existing.sum += value;
33
+ existing.count += 1;
34
+ } else this.data.set(key, {
35
+ labels: { ...labels },
36
+ sum: value,
37
+ count: 1
38
+ });
39
+ }
40
+ values() {
41
+ return [...this.data.values()].flatMap(({ labels, sum, count }) => [{
42
+ labels: {
43
+ ...labels,
44
+ le: "sum"
45
+ },
46
+ value: sum
47
+ }, {
48
+ labels: {
49
+ ...labels,
50
+ le: "count"
51
+ },
52
+ value: count
53
+ }]);
54
+ }
55
+ reset() {
56
+ this.data.clear();
57
+ }
58
+ };
59
+ var Gauge = class {
60
+ data = /* @__PURE__ */ new Map();
61
+ set(labels, value) {
62
+ const key = labelKey(labels);
63
+ this.data.set(key, {
64
+ labels: { ...labels },
65
+ value
66
+ });
67
+ }
68
+ values() {
69
+ return [...this.data.values()];
70
+ }
71
+ reset() {
72
+ this.data.clear();
73
+ }
74
+ };
75
+ function labelKey(labels) {
76
+ return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join(",");
77
+ }
78
+ function toPrometheusText(metrics) {
79
+ const lines = [];
80
+ for (const m of metrics) {
81
+ lines.push(`# HELP ${m.name} ${m.help}`);
82
+ lines.push(`# TYPE ${m.name} ${m.type}`);
83
+ for (const v of m.values) {
84
+ const labelStr = Object.entries(v.labels).map(([k, val]) => `${k}="${val}"`).join(",");
85
+ lines.push(`${m.name}{${labelStr}} ${v.value}`);
86
+ }
87
+ }
88
+ return `${lines.join("\n")}\n`;
89
+ }
90
+ const metricsPlugin = async (fastify, opts = {}) => {
91
+ const path = opts.path ?? "/_metrics";
92
+ const prefix = opts.prefix ?? "arc";
93
+ const httpRequestsTotal = new Counter();
94
+ const crudOpsTotal = new Counter();
95
+ const cacheHitsTotal = new Counter();
96
+ const cacheMissesTotal = new Counter();
97
+ const eventsPublishedTotal = new Counter();
98
+ const eventsConsumedTotal = new Counter();
99
+ const httpDuration = new Histogram();
100
+ const crudDuration = new Histogram();
101
+ const circuitBreakerState = new Gauge();
102
+ const collector = {
103
+ collect() {
104
+ const metrics = [
105
+ {
106
+ name: `${prefix}_http_requests_total`,
107
+ type: "counter",
108
+ help: "Total HTTP requests",
109
+ values: httpRequestsTotal.values()
110
+ },
111
+ {
112
+ name: `${prefix}_http_request_duration_seconds`,
113
+ type: "histogram",
114
+ help: "HTTP request duration in seconds",
115
+ values: httpDuration.values()
116
+ },
117
+ {
118
+ name: `${prefix}_crud_operations_total`,
119
+ type: "counter",
120
+ help: "Total CRUD operations by resource",
121
+ values: crudOpsTotal.values()
122
+ },
123
+ {
124
+ name: `${prefix}_crud_operation_duration_seconds`,
125
+ type: "histogram",
126
+ help: "CRUD operation duration in seconds",
127
+ values: crudDuration.values()
128
+ },
129
+ {
130
+ name: `${prefix}_cache_hits_total`,
131
+ type: "counter",
132
+ help: "Total cache hits by resource",
133
+ values: cacheHitsTotal.values()
134
+ },
135
+ {
136
+ name: `${prefix}_cache_misses_total`,
137
+ type: "counter",
138
+ help: "Total cache misses by resource",
139
+ values: cacheMissesTotal.values()
140
+ },
141
+ {
142
+ name: `${prefix}_events_published_total`,
143
+ type: "counter",
144
+ help: "Total events published",
145
+ values: eventsPublishedTotal.values()
146
+ },
147
+ {
148
+ name: `${prefix}_events_consumed_total`,
149
+ type: "counter",
150
+ help: "Total events consumed",
151
+ values: eventsConsumedTotal.values()
152
+ },
153
+ {
154
+ name: `${prefix}_circuit_breaker_state`,
155
+ type: "gauge",
156
+ help: "Circuit breaker state (0=closed, 1=open, 2=half-open)",
157
+ values: circuitBreakerState.values()
158
+ }
159
+ ];
160
+ opts.onCollect?.(metrics);
161
+ return metrics;
162
+ },
163
+ reset() {
164
+ httpRequestsTotal.reset();
165
+ httpDuration.reset();
166
+ crudOpsTotal.reset();
167
+ crudDuration.reset();
168
+ cacheHitsTotal.reset();
169
+ cacheMissesTotal.reset();
170
+ eventsPublishedTotal.reset();
171
+ eventsConsumedTotal.reset();
172
+ circuitBreakerState.reset();
173
+ },
174
+ recordOperation(resource, operation, status, durationMs) {
175
+ crudOpsTotal.inc({
176
+ resource,
177
+ operation,
178
+ status: String(status)
179
+ });
180
+ crudDuration.observe({
181
+ resource,
182
+ operation
183
+ }, durationMs / 1e3);
184
+ },
185
+ recordCacheHit(resource) {
186
+ cacheHitsTotal.inc({ resource });
187
+ },
188
+ recordCacheMiss(resource) {
189
+ cacheMissesTotal.inc({ resource });
190
+ },
191
+ recordEventPublish(eventType) {
192
+ eventsPublishedTotal.inc({ event_type: eventType });
193
+ },
194
+ recordEventConsume(eventType) {
195
+ eventsConsumedTotal.inc({ event_type: eventType });
196
+ },
197
+ recordCircuitBreakerState(service, state) {
198
+ const stateValue = state === "open" ? 1 : state === "half-open" ? 2 : 0;
199
+ circuitBreakerState.set({ service }, stateValue);
200
+ }
201
+ };
202
+ fastify.decorate("metrics", collector);
203
+ fastify.addHook("onResponse", async (request, reply) => {
204
+ if (request.url === path) return;
205
+ const duration = reply.elapsedTime / 1e3;
206
+ const labels = {
207
+ method: request.method,
208
+ route: request.routeOptions?.url ?? request.url,
209
+ status: String(reply.statusCode)
210
+ };
211
+ httpRequestsTotal.inc(labels);
212
+ httpDuration.observe(labels, duration);
213
+ });
214
+ fastify.get(path, async (_request, reply) => {
215
+ const text = toPrometheusText(collector.collect());
216
+ return reply.type("text/plain; charset=utf-8").send(text);
217
+ });
218
+ };
219
+ var metrics_default = fp(metricsPlugin, {
220
+ name: "arc-metrics",
221
+ fastify: "5.x"
222
+ });
223
+ //#endregion
224
+ export { metrics_default as n, metrics_exports as r, metricsPlugin as t };
@@ -1,6 +1,41 @@
1
- import mongoose from "mongoose";
2
-
3
1
  //#region src/migrations/index.d.ts
2
+ /**
3
+ * Schema Versioning and Migrations System
4
+ *
5
+ * Manages database schema changes over time with version tracking.
6
+ * Supports forward migrations, rollbacks, and schema compatibility layers.
7
+ *
8
+ * DB-agnostic: the `db` parameter is typed as `unknown` — the user passes
9
+ * whatever connection object their adapter uses (Mongoose db, Prisma client,
10
+ * Knex instance, etc.) and their `up`/`down` functions cast it internally.
11
+ *
12
+ * @example
13
+ * import { defineMigration, MigrationRunner } from '@classytic/arc/migrations';
14
+ *
15
+ * const productV2 = defineMigration({
16
+ * version: 2,
17
+ * resource: 'product',
18
+ * up: async (db) => {
19
+ * const mongo = db as import('mongoose').mongo.Db;
20
+ * await mongo.collection('products').updateMany(
21
+ * {},
22
+ * { $rename: { 'oldField': 'newField' } }
23
+ * );
24
+ * },
25
+ * down: async (db) => {
26
+ * const mongo = db as import('mongoose').mongo.Db;
27
+ * await mongo.collection('products').updateMany(
28
+ * {},
29
+ * { $rename: { 'newField': 'oldField' } }
30
+ * );
31
+ * },
32
+ * });
33
+ *
34
+ * const runner = new MigrationRunner(mongoose.connection.db, {
35
+ * store: new MongoMigrationStore(mongoose.connection.db),
36
+ * });
37
+ * await runner.up(migrations);
38
+ */
4
39
  interface Migration {
5
40
  /** Migration version (sequential number) */
6
41
  version: number;
@@ -9,17 +44,18 @@ interface Migration {
9
44
  /** Description of the migration */
10
45
  description?: string;
11
46
  /**
12
- * Forward migration (apply schema change)
47
+ * Forward migration (apply schema change).
48
+ * The `db` parameter is whatever connection object you pass to the runner.
13
49
  */
14
- up: (db: mongoose.mongo.Db) => Promise<void>;
50
+ up: (db: unknown) => Promise<void>;
15
51
  /**
16
- * Backward migration (revert schema change)
52
+ * Backward migration (revert schema change).
17
53
  */
18
- down: (db: mongoose.mongo.Db) => Promise<void>;
54
+ down: (db: unknown) => Promise<void>;
19
55
  /**
20
56
  * Optional validation that data is compatible after migration
21
57
  */
22
- validate?: (db: mongoose.mongo.Db) => Promise<boolean>;
58
+ validate?: (db: unknown) => Promise<boolean>;
23
59
  }
24
60
  interface MigrationRecord {
25
61
  version: number;
@@ -28,6 +64,54 @@ interface MigrationRecord {
28
64
  appliedAt: Date;
29
65
  executionTime: number;
30
66
  }
67
+ /**
68
+ * DB-agnostic migration store interface.
69
+ *
70
+ * Users implement this for their database:
71
+ * - MongoMigrationStore (uses a `_migrations` collection)
72
+ * - PrismaMigrationStore (uses a `_migrations` table)
73
+ * - or any custom store
74
+ */
75
+ interface MigrationStore {
76
+ /** Get all applied migration records, sorted by appliedAt ascending */
77
+ getApplied(): Promise<MigrationRecord[]>;
78
+ /** Record a completed migration */
79
+ record(migration: Migration, executionTime: number): Promise<void>;
80
+ /** Remove a migration record (for rollback) */
81
+ remove(migration: Migration): Promise<void>;
82
+ }
83
+ /**
84
+ * Minimal logger interface — matches Fastify's logger, pino, console, etc.
85
+ */
86
+ interface MigrationLogger {
87
+ info(msg: string): void;
88
+ error(msg: string): void;
89
+ }
90
+ /**
91
+ * MongoDB-backed migration store.
92
+ *
93
+ * Uses a `_migrations` collection in the same database.
94
+ * The `db` parameter accepts any object with a `.collection()` method
95
+ * (Mongoose db, native MongoDB Db, etc.)
96
+ */
97
+ declare class MongoMigrationStore implements MigrationStore {
98
+ private readonly collectionName;
99
+ private readonly db;
100
+ constructor(db: {
101
+ collection(name: string): any;
102
+ }, opts?: {
103
+ collectionName?: string;
104
+ });
105
+ getApplied(): Promise<MigrationRecord[]>;
106
+ record(migration: Migration, executionTime: number): Promise<void>;
107
+ remove(migration: Migration): Promise<void>;
108
+ }
109
+ interface MigrationRunnerOptions {
110
+ /** Migration store (required — use MongoMigrationStore or implement your own) */
111
+ store: MigrationStore;
112
+ /** Logger (defaults to process.stdout/stderr) */
113
+ logger?: MigrationLogger;
114
+ }
31
115
  /**
32
116
  * Define a migration
33
117
  */
@@ -35,12 +119,30 @@ declare function defineMigration(migration: Migration): Migration;
35
119
  /**
36
120
  * Migration Runner
37
121
  *
38
- * Manages execution of migrations with tracking and rollback support.
122
+ * DB-agnostic. Manages execution of migrations with tracking and rollback.
123
+ * The `db` parameter is passed through to migration `up`/`down` functions
124
+ * as-is — the runner never touches it directly.
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * // MongoDB
129
+ * const runner = new MigrationRunner(mongoose.connection.db, {
130
+ * store: new MongoMigrationStore(mongoose.connection.db),
131
+ * });
132
+ *
133
+ * // Prisma
134
+ * const runner = new MigrationRunner(prisma, {
135
+ * store: new PrismaMigrationStore(prisma), // user-implemented
136
+ * });
137
+ *
138
+ * await runner.up(migrations);
139
+ * ```
39
140
  */
40
141
  declare class MigrationRunner {
41
- private readonly collectionName;
42
142
  private readonly db;
43
- constructor(db: mongoose.mongo.Db);
143
+ private readonly store;
144
+ private readonly log;
145
+ constructor(db: unknown, opts: MigrationRunnerOptions);
44
146
  /**
45
147
  * Run all pending migrations
46
148
  */
@@ -69,14 +171,6 @@ declare class MigrationRunner {
69
171
  * Run a single migration
70
172
  */
71
173
  private runMigration;
72
- /**
73
- * Record a completed migration
74
- */
75
- private recordMigration;
76
- /**
77
- * Remove a migration record
78
- */
79
- private removeMigration;
80
174
  }
81
175
  /**
82
176
  * Schema version definition for resources
@@ -127,30 +221,5 @@ declare class MigrationRegistry {
127
221
  */
128
222
  clear(): void;
129
223
  }
130
- /**
131
- * Global migration registry instance
132
- */
133
- declare const migrationRegistry: MigrationRegistry;
134
- /**
135
- * Common migration helpers
136
- */
137
- declare const migrationHelpers: {
138
- /**
139
- * Rename a field across all documents
140
- */
141
- renameField: (collection: string, oldName: string, newName: string) => Migration;
142
- /**
143
- * Add a new field with default value
144
- */
145
- addField: (collection: string, fieldName: string, defaultValue: unknown) => Migration;
146
- /**
147
- * Remove a field
148
- */
149
- removeField: (collection: string, fieldName: string) => Migration;
150
- /**
151
- * Create an index
152
- */
153
- createIndex: (collection: string, fields: Record<string, 1 | -1>, options?: Record<string, unknown>) => Migration;
154
- };
155
224
  //#endregion
156
- export { Migration, MigrationRecord, MigrationRegistry, MigrationRunner, SchemaVersion, defineMigration, migrationHelpers, migrationRegistry, withSchemaVersion };
225
+ export { Migration, MigrationLogger, MigrationRecord, MigrationRegistry, MigrationRunner, MigrationRunnerOptions, MigrationStore, MongoMigrationStore, SchemaVersion, defineMigration, withSchemaVersion };