@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,7 @@
1
- import { C as ArcQueryParser, S as wrapResponse, _ as paginateWrapper, a as createCircuitBreaker, b as responses, c as deleteResponse, d as getListQueryParams, f as itemResponse, g as mutationResponse, h as messageWrapper, i as CircuitState, l as errorResponseSchema, m as listResponse, n as CircuitBreakerError, o as createCircuitBreakerRegistry, p as itemWrapper, r as CircuitBreakerRegistry, s as createStateMachine, t as CircuitBreaker, u as getDefaultCrudSchemas, v as paginationSchema, w as createQueryParser, x as successResponseSchema, y as queryParams } from "../circuitBreaker-CSS2VvL6.mjs";
2
- import { a as OrgAccessDeniedError, c as ServiceUnavailableError, d as createError, f as isArcError, i as NotFoundError, l as UnauthorizedError, n as ConflictError, o as OrgRequiredError, r as ForbiddenError, s as RateLimitError, t as ArcError, u as ValidationError } from "../errors-DBANPbGr.mjs";
3
- import { t as hasEvents } from "../typeGuards-DwxA1t_L.mjs";
4
- import { a as toJsonSchema, i as isZodSchema, n as convertRouteSchema, r as isJsonSchema, t as convertOpenApiSchemas } from "../schemaConverter-Dtg0Kt9T.mjs";
5
-
6
- export { ArcError, ArcQueryParser, CircuitBreaker, CircuitBreakerError, CircuitBreakerRegistry, CircuitState, ConflictError, ForbiddenError, NotFoundError, OrgAccessDeniedError, OrgRequiredError, RateLimitError, ServiceUnavailableError, UnauthorizedError, ValidationError, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createError, createQueryParser, createStateMachine, deleteResponse, errorResponseSchema, getDefaultCrudSchemas, getListQueryParams, hasEvents, isArcError, isJsonSchema, isZodSchema, itemResponse, itemWrapper, listResponse, messageWrapper, mutationResponse, paginateWrapper, paginationSchema, queryParams, responses, successResponseSchema, toJsonSchema, wrapResponse };
1
+ import { n as createQueryParser, t as ArcQueryParser } from "../queryParser-CgCtsjti.mjs";
2
+ import { a as createCircuitBreaker, i as CircuitState, n as CircuitBreakerError, o as createCircuitBreakerRegistry, r as CircuitBreakerRegistry, t as CircuitBreaker } from "../circuitBreaker-BOBOpN2w.mjs";
3
+ import { _ as defineCompensation, a as getListQueryParams, c as listResponse, d as paginateWrapper, f as paginationSchema, g as wrapResponse, h as successResponseSchema, i as getDefaultCrudSchemas, l as messageWrapper, m as responses, n as deleteResponse, o as itemResponse, p as queryParams, r as errorResponseSchema, s as itemWrapper, t as createStateMachine, u as mutationResponse, v as withCompensation } from "../utils-Dc0WhlIl.mjs";
4
+ import { a as OrgAccessDeniedError, c as ServiceUnavailableError, d as createError, f as isArcError, i as NotFoundError, l as UnauthorizedError, n as ConflictError, o as OrgRequiredError, r as ForbiddenError, s as RateLimitError, t as ArcError, u as ValidationError } from "../errors-rxhfP7Hf.mjs";
5
+ import { a as toJsonSchema, i as isZodSchema, n as convertRouteSchema, r as isJsonSchema, t as convertOpenApiSchemas } from "../schemaConverter-DjzHpFam.mjs";
6
+ import { t as hasEvents } from "../typeGuards-Cj5Rgvlg.mjs";
7
+ export { ArcError, ArcQueryParser, CircuitBreaker, CircuitBreakerError, CircuitBreakerRegistry, CircuitState, ConflictError, ForbiddenError, NotFoundError, OrgAccessDeniedError, OrgRequiredError, RateLimitError, ServiceUnavailableError, UnauthorizedError, ValidationError, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createError, createQueryParser, createStateMachine, defineCompensation, deleteResponse, errorResponseSchema, getDefaultCrudSchemas, getListQueryParams, hasEvents, isArcError, isJsonSchema, isZodSchema, itemResponse, itemWrapper, listResponse, messageWrapper, mutationResponse, paginateWrapper, paginationSchema, queryParams, responses, successResponseSchema, toJsonSchema, withCompensation, wrapResponse };
@@ -0,0 +1,594 @@
1
+ //#region src/utils/compensation.ts
2
+ /**
3
+ * Run steps in order with automatic compensation on failure.
4
+ *
5
+ * @typeParam TCtx - Context type shared across steps (defaults to Record<string, unknown>)
6
+ */
7
+ async function withCompensation(_name, steps, initialContext, hooks) {
8
+ const ctx = { ...initialContext };
9
+ const completedSteps = [];
10
+ const results = {};
11
+ const completed = [];
12
+ for (const step of steps) {
13
+ if (step.fireAndForget) {
14
+ completedSteps.push(step.name);
15
+ step.execute(ctx).then((result) => hooks?.onStepComplete?.(step.name, result), () => {});
16
+ continue;
17
+ }
18
+ try {
19
+ const result = await step.execute(ctx);
20
+ completedSteps.push(step.name);
21
+ results[step.name] = result;
22
+ completed.push({
23
+ step,
24
+ result
25
+ });
26
+ hooks?.onStepComplete?.(step.name, result);
27
+ } catch (err) {
28
+ const error = err instanceof Error ? err : new Error(String(err));
29
+ hooks?.onStepFailed?.(step.name, error);
30
+ const compensationErrors = await rollback(ctx, completed, hooks);
31
+ return {
32
+ success: false,
33
+ completedSteps,
34
+ results,
35
+ failedStep: step.name,
36
+ error: error.message,
37
+ ...compensationErrors.length > 0 ? { compensationErrors } : {}
38
+ };
39
+ }
40
+ }
41
+ return {
42
+ success: true,
43
+ completedSteps,
44
+ results
45
+ };
46
+ }
47
+ async function rollback(ctx, completed, hooks) {
48
+ const errors = [];
49
+ for (let i = completed.length - 1; i >= 0; i--) {
50
+ const entry = completed[i];
51
+ if (!entry?.step.compensate) continue;
52
+ const compensateFn = entry.step.compensate;
53
+ try {
54
+ await compensateFn(ctx, entry.result);
55
+ hooks?.onCompensate?.(entry.step.name);
56
+ } catch (err) {
57
+ errors.push({
58
+ step: entry.step.name,
59
+ error: err instanceof Error ? err.message : String(err)
60
+ });
61
+ }
62
+ }
63
+ return errors;
64
+ }
65
+ function defineCompensation(name, steps) {
66
+ return {
67
+ name,
68
+ execute: (initialContext, hooks) => withCompensation(name, steps, initialContext, hooks)
69
+ };
70
+ }
71
+ //#endregion
72
+ //#region src/utils/responseSchemas.ts
73
+ /**
74
+ * Base success response schema
75
+ */
76
+ const successResponseSchema = {
77
+ type: "object",
78
+ properties: { success: {
79
+ type: "boolean",
80
+ example: true
81
+ } },
82
+ required: ["success"]
83
+ };
84
+ /**
85
+ * Error response schema
86
+ */
87
+ const errorResponseSchema = {
88
+ type: "object",
89
+ properties: {
90
+ success: {
91
+ type: "boolean",
92
+ example: false
93
+ },
94
+ error: {
95
+ type: "string",
96
+ description: "Error message"
97
+ },
98
+ code: {
99
+ type: "string",
100
+ description: "Error code"
101
+ },
102
+ message: {
103
+ type: "string",
104
+ description: "Detailed message"
105
+ }
106
+ },
107
+ required: ["success", "error"]
108
+ };
109
+ /**
110
+ * Pagination schema - matches MongoKit/Arc runtime format
111
+ *
112
+ * Runtime format (flat fields):
113
+ * { page, limit, total, pages, hasNext, hasPrev }
114
+ */
115
+ const paginationSchema = {
116
+ type: "object",
117
+ properties: {
118
+ page: {
119
+ type: "integer",
120
+ example: 1
121
+ },
122
+ limit: {
123
+ type: "integer",
124
+ example: 20
125
+ },
126
+ total: {
127
+ type: "integer",
128
+ example: 100
129
+ },
130
+ pages: {
131
+ type: "integer",
132
+ example: 5
133
+ },
134
+ hasNext: {
135
+ type: "boolean",
136
+ example: true
137
+ },
138
+ hasPrev: {
139
+ type: "boolean",
140
+ example: false
141
+ }
142
+ },
143
+ required: [
144
+ "page",
145
+ "limit",
146
+ "total",
147
+ "pages",
148
+ "hasNext",
149
+ "hasPrev"
150
+ ]
151
+ };
152
+ /**
153
+ * Wrap a data schema in a success response
154
+ */
155
+ function wrapResponse(dataSchema) {
156
+ return {
157
+ type: "object",
158
+ properties: {
159
+ success: {
160
+ type: "boolean",
161
+ example: true
162
+ },
163
+ data: dataSchema
164
+ },
165
+ required: ["success", "data"],
166
+ additionalProperties: true
167
+ };
168
+ }
169
+ /**
170
+ * Create a list response schema with pagination - matches MongoKit/Arc runtime format
171
+ *
172
+ * Runtime format:
173
+ * { success, docs: [...], page, limit, total, pages, hasNext, hasPrev }
174
+ *
175
+ * Note: Uses 'docs' array (not 'data') with flat pagination fields
176
+ */
177
+ function listResponse(itemSchema) {
178
+ return {
179
+ type: "object",
180
+ properties: {
181
+ success: {
182
+ type: "boolean",
183
+ example: true
184
+ },
185
+ docs: {
186
+ type: "array",
187
+ items: itemSchema
188
+ },
189
+ page: {
190
+ type: "integer",
191
+ example: 1
192
+ },
193
+ limit: {
194
+ type: "integer",
195
+ example: 20
196
+ },
197
+ total: {
198
+ type: "integer",
199
+ example: 100
200
+ },
201
+ pages: {
202
+ type: "integer",
203
+ example: 5
204
+ },
205
+ hasNext: {
206
+ type: "boolean",
207
+ example: false
208
+ },
209
+ hasPrev: {
210
+ type: "boolean",
211
+ example: false
212
+ }
213
+ },
214
+ required: ["success", "docs"],
215
+ additionalProperties: true
216
+ };
217
+ }
218
+ /**
219
+ * Alias for listResponse - matches local responseSchemas.js naming
220
+ */
221
+ const paginateWrapper = listResponse;
222
+ /**
223
+ * Create a single item response schema
224
+ *
225
+ * Runtime format: { success, data: {...} }
226
+ */
227
+ function itemResponse(itemSchema) {
228
+ return wrapResponse(itemSchema);
229
+ }
230
+ /**
231
+ * Alias for itemResponse - matches local responseSchemas.js naming
232
+ */
233
+ const itemWrapper = itemResponse;
234
+ /**
235
+ * Create a create/update response schema
236
+ */
237
+ function mutationResponse(itemSchema) {
238
+ return {
239
+ type: "object",
240
+ properties: {
241
+ success: {
242
+ type: "boolean",
243
+ example: true
244
+ },
245
+ data: itemSchema,
246
+ message: {
247
+ type: "string",
248
+ example: "Created successfully"
249
+ }
250
+ },
251
+ required: ["success", "data"],
252
+ additionalProperties: true
253
+ };
254
+ }
255
+ /**
256
+ * Create a delete response schema
257
+ *
258
+ * Runtime format: { success, data: { message, id?, soft? } }
259
+ */
260
+ function deleteResponse() {
261
+ return {
262
+ type: "object",
263
+ properties: {
264
+ success: {
265
+ type: "boolean",
266
+ example: true
267
+ },
268
+ data: {
269
+ type: "object",
270
+ properties: {
271
+ message: {
272
+ type: "string",
273
+ example: "Deleted successfully"
274
+ },
275
+ id: {
276
+ type: "string",
277
+ example: "507f1f77bcf86cd799439011"
278
+ },
279
+ soft: {
280
+ type: "boolean",
281
+ example: false
282
+ }
283
+ },
284
+ required: ["message"]
285
+ }
286
+ },
287
+ required: ["success"],
288
+ additionalProperties: true
289
+ };
290
+ }
291
+ /**
292
+ * Alias for deleteResponse - matches local responseSchemas.js naming
293
+ */
294
+ const messageWrapper = deleteResponse;
295
+ const responses = {
296
+ 200: (schema) => ({
297
+ description: "Successful response",
298
+ content: { "application/json": { schema } }
299
+ }),
300
+ 201: (schema) => ({
301
+ description: "Created successfully",
302
+ content: { "application/json": { schema: mutationResponse(schema) } }
303
+ }),
304
+ 400: {
305
+ description: "Bad Request",
306
+ content: { "application/json": { schema: {
307
+ ...errorResponseSchema,
308
+ properties: {
309
+ ...errorResponseSchema.properties,
310
+ code: {
311
+ type: "string",
312
+ example: "VALIDATION_ERROR"
313
+ },
314
+ details: {
315
+ type: "object",
316
+ properties: { errors: {
317
+ type: "array",
318
+ items: {
319
+ type: "object",
320
+ properties: {
321
+ field: { type: "string" },
322
+ message: { type: "string" }
323
+ }
324
+ }
325
+ } }
326
+ }
327
+ }
328
+ } } }
329
+ },
330
+ 401: {
331
+ description: "Unauthorized",
332
+ content: { "application/json": { schema: {
333
+ ...errorResponseSchema,
334
+ properties: {
335
+ ...errorResponseSchema.properties,
336
+ code: {
337
+ type: "string",
338
+ example: "UNAUTHORIZED"
339
+ }
340
+ }
341
+ } } }
342
+ },
343
+ 403: {
344
+ description: "Forbidden",
345
+ content: { "application/json": { schema: {
346
+ ...errorResponseSchema,
347
+ properties: {
348
+ ...errorResponseSchema.properties,
349
+ code: {
350
+ type: "string",
351
+ example: "FORBIDDEN"
352
+ }
353
+ }
354
+ } } }
355
+ },
356
+ 404: {
357
+ description: "Not Found",
358
+ content: { "application/json": { schema: {
359
+ ...errorResponseSchema,
360
+ properties: {
361
+ ...errorResponseSchema.properties,
362
+ code: {
363
+ type: "string",
364
+ example: "NOT_FOUND"
365
+ }
366
+ }
367
+ } } }
368
+ },
369
+ 409: {
370
+ description: "Conflict",
371
+ content: { "application/json": { schema: {
372
+ ...errorResponseSchema,
373
+ properties: {
374
+ ...errorResponseSchema.properties,
375
+ code: {
376
+ type: "string",
377
+ example: "CONFLICT"
378
+ }
379
+ }
380
+ } } }
381
+ },
382
+ 500: {
383
+ description: "Internal Server Error",
384
+ content: { "application/json": { schema: {
385
+ ...errorResponseSchema,
386
+ properties: {
387
+ ...errorResponseSchema.properties,
388
+ code: {
389
+ type: "string",
390
+ example: "INTERNAL_ERROR"
391
+ }
392
+ }
393
+ } } }
394
+ }
395
+ };
396
+ const queryParams = {
397
+ pagination: {
398
+ page: {
399
+ type: "integer",
400
+ minimum: 1,
401
+ default: 1,
402
+ description: "Page number"
403
+ },
404
+ limit: {
405
+ type: "integer",
406
+ minimum: 1,
407
+ maximum: 100,
408
+ default: 20,
409
+ description: "Items per page"
410
+ }
411
+ },
412
+ sorting: { sort: {
413
+ type: "string",
414
+ description: "Sort field (prefix with - for descending)",
415
+ example: "-createdAt"
416
+ } },
417
+ filtering: {
418
+ select: {
419
+ description: "Fields to include (space-separated or object)",
420
+ example: "name email createdAt"
421
+ },
422
+ populate: {
423
+ description: "Relations to populate (comma-separated string or bracket-notation object)",
424
+ example: "author,category"
425
+ }
426
+ }
427
+ };
428
+ /**
429
+ * Get standard list query parameters schema
430
+ */
431
+ function getListQueryParams() {
432
+ return {
433
+ type: "object",
434
+ properties: {
435
+ ...queryParams.pagination,
436
+ ...queryParams.sorting,
437
+ ...queryParams.filtering
438
+ },
439
+ additionalProperties: true
440
+ };
441
+ }
442
+ /**
443
+ * Generic item schema that allows any properties.
444
+ * Used as default when no user schema is provided.
445
+ * Enables fast-json-stringify while still passing through all fields.
446
+ */
447
+ const genericItemSchema = {
448
+ type: "object",
449
+ additionalProperties: true
450
+ };
451
+ /**
452
+ * Recursively strip `example` keys from a schema object.
453
+ * The `example` keyword is OpenAPI metadata — not standard JSON Schema —
454
+ * and triggers Ajv strict mode errors when used on routes without the
455
+ * `keywords: ['example']` AJV config (e.g., raw Fastify without createApp).
456
+ */
457
+ function stripExamples(schema) {
458
+ if (schema === null || typeof schema !== "object") return schema;
459
+ if (Array.isArray(schema)) return schema.map(stripExamples);
460
+ const result = {};
461
+ for (const [key, value] of Object.entries(schema)) {
462
+ if (key === "example") continue;
463
+ result[key] = stripExamples(value);
464
+ }
465
+ return result;
466
+ }
467
+ /**
468
+ * Get default response schemas for all CRUD operations.
469
+ *
470
+ * When routes have response schemas, Fastify compiles them with
471
+ * fast-json-stringify for 2-3x faster serialization and prevents
472
+ * accidental field disclosure.
473
+ *
474
+ * These defaults use `additionalProperties: true` so all fields pass through.
475
+ * Override with specific schemas for full serialization performance + safety.
476
+ *
477
+ * Note: `example` properties are stripped from defaults so they work with
478
+ * any Fastify instance (not just createApp which adds `keywords: ['example']`).
479
+ */
480
+ function getDefaultCrudSchemas() {
481
+ return stripExamples({
482
+ list: {
483
+ querystring: getListQueryParams(),
484
+ response: { 200: listResponse(genericItemSchema) }
485
+ },
486
+ get: { response: { 200: itemResponse(genericItemSchema) } },
487
+ create: { response: { 201: mutationResponse(genericItemSchema) } },
488
+ update: { response: { 200: itemResponse(genericItemSchema) } },
489
+ delete: { response: { 200: deleteResponse() } }
490
+ });
491
+ }
492
+ //#endregion
493
+ //#region src/utils/stateMachine.ts
494
+ /**
495
+ * Create a state machine for validating transitions
496
+ *
497
+ * @param name - Name of the state machine (used in error messages)
498
+ * @param transitions - Map of actions to allowed source statuses
499
+ * @param options - Additional options (history, guards, actions)
500
+ * @returns State machine with can() and assert() methods
501
+ *
502
+ * @example
503
+ * // Basic usage
504
+ * const transferState = createStateMachine('Transfer', {
505
+ * approve: ['draft'],
506
+ * dispatch: ['approved'],
507
+ * receive: ['dispatched', 'in_transit'],
508
+ * cancel: ['draft', 'approved'],
509
+ * });
510
+ *
511
+ * @example
512
+ * // With guards and actions
513
+ * const orderState = createStateMachine('Order', {
514
+ * approve: {
515
+ * from: ['pending'],
516
+ * to: 'approved',
517
+ * guard: ({ data }) => data.paymentConfirmed,
518
+ * before: ({ from, to }) => console.log(`Approving order from ${from} to ${to}`),
519
+ * after: ({ data }) => sendApprovalEmail(data.customerId),
520
+ * },
521
+ * }, { trackHistory: true });
522
+ */
523
+ function createStateMachine(name, transitions = {}, options = {}) {
524
+ const normalized = /* @__PURE__ */ new Map();
525
+ const history = options.trackHistory ? [] : void 0;
526
+ Object.entries(transitions).forEach(([action, allowed]) => {
527
+ if (Array.isArray(allowed)) normalized.set(action, { from: new Set(allowed) });
528
+ else if (typeof allowed === "object" && "from" in allowed) normalized.set(action, {
529
+ from: new Set(Array.isArray(allowed.from) ? allowed.from : [allowed.from]),
530
+ to: allowed.to,
531
+ guard: allowed.guard,
532
+ before: allowed.before,
533
+ after: allowed.after
534
+ });
535
+ });
536
+ const can = (action, status) => {
537
+ const transition = normalized.get(action);
538
+ if (!transition || !status) return false;
539
+ return transition.from.has(status);
540
+ };
541
+ const canAsync = async (action, status, context) => {
542
+ const transition = normalized.get(action);
543
+ if (!transition || !status) return false;
544
+ if (!transition.from.has(status)) return false;
545
+ if (transition.guard) try {
546
+ return await transition.guard({
547
+ from: status,
548
+ to: transition.to || "",
549
+ action,
550
+ data: context
551
+ });
552
+ } catch {
553
+ return false;
554
+ }
555
+ return true;
556
+ };
557
+ const assert = (action, status, errorFactory, message) => {
558
+ if (can(action, status)) return;
559
+ const errorMessage = message || `${name} cannot '${action}' when status is '${status || "unknown"}'`;
560
+ if (typeof errorFactory === "function") throw errorFactory(errorMessage);
561
+ throw new Error(errorMessage);
562
+ };
563
+ const recordTransition = (from, to, action, metadata) => {
564
+ if (history) history.push({
565
+ from,
566
+ to,
567
+ action,
568
+ timestamp: /* @__PURE__ */ new Date(),
569
+ metadata
570
+ });
571
+ };
572
+ const getHistory = () => {
573
+ return history ? [...history] : [];
574
+ };
575
+ const clearHistory = () => {
576
+ if (history) history.length = 0;
577
+ };
578
+ const getAvailableActions = (status) => {
579
+ const actions = [];
580
+ for (const [action, transition] of normalized.entries()) if (transition.from.has(status)) actions.push(action);
581
+ return actions;
582
+ };
583
+ return {
584
+ can,
585
+ canAsync,
586
+ assert,
587
+ recordTransition,
588
+ getHistory,
589
+ clearHistory,
590
+ getAvailableActions
591
+ };
592
+ }
593
+ //#endregion
594
+ export { defineCompensation as _, getListQueryParams as a, listResponse as c, paginateWrapper as d, paginationSchema as f, wrapResponse as g, successResponseSchema as h, getDefaultCrudSchemas as i, messageWrapper as l, responses as m, deleteResponse as n, itemResponse as o, queryParams as p, errorResponseSchema as r, itemWrapper as s, createStateMachine as t, mutationResponse as u, withCompensation as v };
@@ -0,0 +1,37 @@
1
+ import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
2
+ import fp from "fastify-plugin";
3
+ //#region src/plugins/versioning.ts
4
+ var versioning_exports = /* @__PURE__ */ __exportAll({
5
+ default: () => versioning_default,
6
+ versioningPlugin: () => versioningPlugin
7
+ });
8
+ const PREFIX_REGEX = /^\/v(\d+)\//;
9
+ const versioningPlugin = async (fastify, opts) => {
10
+ const { type, defaultVersion = "1", headerName = "accept-version", responseHeader = "x-api-version", deprecated = [], sunset } = opts;
11
+ const deprecatedSet = new Set(deprecated);
12
+ fastify.decorateRequest("apiVersion", defaultVersion);
13
+ fastify.addHook("onRequest", async (request) => {
14
+ let version = defaultVersion;
15
+ if (type === "header") {
16
+ const headerValue = request.headers[headerName];
17
+ if (headerValue) version = String(headerValue);
18
+ } else if (type === "prefix") {
19
+ const match = request.url.match(PREFIX_REGEX);
20
+ if (match) version = match[1] ?? defaultVersion;
21
+ }
22
+ request.apiVersion = version;
23
+ });
24
+ fastify.addHook("onSend", async (request, reply) => {
25
+ reply.header(responseHeader, request.apiVersion);
26
+ if (deprecatedSet.has(request.apiVersion)) {
27
+ reply.header("deprecation", "true");
28
+ reply.header("sunset", sunset ?? new Date(Date.now() + 2160 * 60 * 60 * 1e3).toISOString());
29
+ }
30
+ });
31
+ };
32
+ var versioning_default = fp(versioningPlugin, {
33
+ name: "arc-versioning",
34
+ fastify: "5.x"
35
+ });
36
+ //#endregion
37
+ export { versioning_default as n, versioning_exports as r, versioningPlugin as t };