@classytic/arc 1.1.0 → 2.1.3

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 (200) hide show
  1. package/README.md +247 -794
  2. package/bin/arc.js +91 -52
  3. package/dist/EventTransport-BkUDYZEb.d.mts +99 -0
  4. package/dist/HookSystem-BsGV-j2l.mjs +404 -0
  5. package/dist/ResourceRegistry-7Ic20ZMw.mjs +249 -0
  6. package/dist/adapters/index.d.mts +5 -0
  7. package/dist/adapters/index.mjs +3 -0
  8. package/dist/audit/index.d.mts +81 -0
  9. package/dist/audit/index.mjs +275 -0
  10. package/dist/audit/mongodb.d.mts +5 -0
  11. package/dist/audit/mongodb.mjs +3 -0
  12. package/dist/audited-CGdLiSlE.mjs +140 -0
  13. package/dist/auth/index.d.mts +188 -0
  14. package/dist/auth/index.mjs +1096 -0
  15. package/dist/auth/redis-session.d.mts +43 -0
  16. package/dist/auth/redis-session.mjs +75 -0
  17. package/dist/betterAuthOpenApi-DjWDddNc.mjs +249 -0
  18. package/dist/cache/index.d.mts +145 -0
  19. package/dist/cache/index.mjs +91 -0
  20. package/dist/caching-GSDJcA6-.mjs +93 -0
  21. package/dist/chunk-C7Uep-_p.mjs +20 -0
  22. package/dist/circuitBreaker-DYhWBW_D.mjs +1096 -0
  23. package/dist/cli/commands/describe.d.mts +18 -0
  24. package/dist/cli/commands/describe.mjs +238 -0
  25. package/dist/cli/commands/docs.d.mts +13 -0
  26. package/dist/cli/commands/docs.mjs +52 -0
  27. package/dist/cli/commands/{generate.d.ts → generate.d.mts} +3 -2
  28. package/dist/cli/commands/generate.mjs +357 -0
  29. package/dist/cli/commands/{init.d.ts → init.d.mts} +11 -8
  30. package/dist/cli/commands/{init.js → init.mjs} +807 -617
  31. package/dist/cli/commands/introspect.d.mts +10 -0
  32. package/dist/cli/commands/introspect.mjs +75 -0
  33. package/dist/cli/index.d.mts +16 -0
  34. package/dist/cli/index.mjs +156 -0
  35. package/dist/constants-DdXFXQtN.mjs +84 -0
  36. package/dist/core/index.d.mts +5 -0
  37. package/dist/core/index.mjs +4 -0
  38. package/dist/createApp-D2D5XXaV.mjs +559 -0
  39. package/dist/defineResource-PXzSJ15_.mjs +2197 -0
  40. package/dist/discovery/index.d.mts +46 -0
  41. package/dist/discovery/index.mjs +109 -0
  42. package/dist/docs/index.d.mts +162 -0
  43. package/dist/docs/index.mjs +74 -0
  44. package/dist/elevation-DGo5shaX.d.mts +87 -0
  45. package/dist/elevation-DSTbVvYj.mjs +113 -0
  46. package/dist/errorHandler-C3GY3_ow.mjs +108 -0
  47. package/dist/errorHandler-CW3OOeYq.d.mts +72 -0
  48. package/dist/errors-DAWRdiYP.d.mts +124 -0
  49. package/dist/errors-DBANPbGr.mjs +211 -0
  50. package/dist/eventPlugin-BEOvaDqo.mjs +229 -0
  51. package/dist/eventPlugin-H6wDDjGO.d.mts +124 -0
  52. package/dist/events/index.d.mts +53 -0
  53. package/dist/events/index.mjs +51 -0
  54. package/dist/events/transports/redis-stream-entry.d.mts +2 -0
  55. package/dist/events/transports/redis-stream-entry.mjs +177 -0
  56. package/dist/events/transports/redis.d.mts +76 -0
  57. package/dist/events/transports/redis.mjs +124 -0
  58. package/dist/externalPaths-SyPF2tgK.d.mts +50 -0
  59. package/dist/factory/index.d.mts +63 -0
  60. package/dist/factory/index.mjs +3 -0
  61. package/dist/fastifyAdapter-C8DlE0YH.d.mts +216 -0
  62. package/dist/fields-Bi_AVKSo.d.mts +109 -0
  63. package/dist/fields-CTd_CrKr.mjs +114 -0
  64. package/dist/hooks/index.d.mts +4 -0
  65. package/dist/hooks/index.mjs +3 -0
  66. package/dist/idempotency/index.d.mts +96 -0
  67. package/dist/idempotency/index.mjs +319 -0
  68. package/dist/idempotency/mongodb.d.mts +2 -0
  69. package/dist/idempotency/mongodb.mjs +114 -0
  70. package/dist/idempotency/redis.d.mts +2 -0
  71. package/dist/idempotency/redis.mjs +103 -0
  72. package/dist/index.d.mts +260 -0
  73. package/dist/index.mjs +104 -0
  74. package/dist/integrations/event-gateway.d.mts +46 -0
  75. package/dist/integrations/event-gateway.mjs +43 -0
  76. package/dist/integrations/index.d.mts +5 -0
  77. package/dist/integrations/index.mjs +1 -0
  78. package/dist/integrations/jobs.d.mts +103 -0
  79. package/dist/integrations/jobs.mjs +123 -0
  80. package/dist/integrations/streamline.d.mts +60 -0
  81. package/dist/integrations/streamline.mjs +125 -0
  82. package/dist/integrations/websocket.d.mts +82 -0
  83. package/dist/integrations/websocket.mjs +288 -0
  84. package/dist/interface-CSNjltAc.d.mts +77 -0
  85. package/dist/interface-DTbsvIWe.d.mts +54 -0
  86. package/dist/interface-e9XfSsUV.d.mts +1097 -0
  87. package/dist/introspectionPlugin-B3JkrjwU.mjs +53 -0
  88. package/dist/keys-DhqDRxv3.mjs +42 -0
  89. package/dist/logger-ByrvQWZO.mjs +78 -0
  90. package/dist/memory-B2v7KrCB.mjs +143 -0
  91. package/dist/migrations/index.d.mts +156 -0
  92. package/dist/migrations/index.mjs +260 -0
  93. package/dist/mongodb-ClykrfGo.d.mts +118 -0
  94. package/dist/mongodb-DNKEExbf.mjs +93 -0
  95. package/dist/mongodb-Dg8O_gvd.d.mts +71 -0
  96. package/dist/openapi-9nB_kiuR.mjs +525 -0
  97. package/dist/org/index.d.mts +68 -0
  98. package/dist/org/index.mjs +513 -0
  99. package/dist/org/types.d.mts +82 -0
  100. package/dist/org/types.mjs +1 -0
  101. package/dist/permissions/index.d.mts +278 -0
  102. package/dist/permissions/index.mjs +579 -0
  103. package/dist/plugins/index.d.mts +172 -0
  104. package/dist/plugins/index.mjs +522 -0
  105. package/dist/plugins/response-cache.d.mts +87 -0
  106. package/dist/plugins/response-cache.mjs +283 -0
  107. package/dist/plugins/tracing-entry.d.mts +2 -0
  108. package/dist/plugins/tracing-entry.mjs +185 -0
  109. package/dist/pluralize-CM-jZg7p.mjs +86 -0
  110. package/dist/policies/{index.d.ts → index.d.mts} +204 -170
  111. package/dist/policies/index.mjs +321 -0
  112. package/dist/presets/{index.d.ts → index.d.mts} +62 -131
  113. package/dist/presets/index.mjs +143 -0
  114. package/dist/presets/multiTenant.d.mts +24 -0
  115. package/dist/presets/multiTenant.mjs +113 -0
  116. package/dist/presets-BTeYbw7h.d.mts +57 -0
  117. package/dist/presets-CeFtfDR8.mjs +119 -0
  118. package/dist/prisma-C3iornoK.d.mts +274 -0
  119. package/dist/prisma-DJbMt3yf.mjs +627 -0
  120. package/dist/queryCachePlugin-B6R0d4av.mjs +138 -0
  121. package/dist/queryCachePlugin-Q6SYuHZ6.d.mts +71 -0
  122. package/dist/redis-UwjEp8Ea.d.mts +49 -0
  123. package/dist/redis-stream-CBg0upHI.d.mts +103 -0
  124. package/dist/registry/index.d.mts +11 -0
  125. package/dist/registry/index.mjs +4 -0
  126. package/dist/requestContext-xi6OKBL-.mjs +55 -0
  127. package/dist/schemaConverter-Dtg0Kt9T.mjs +98 -0
  128. package/dist/schemas/index.d.mts +63 -0
  129. package/dist/schemas/index.mjs +82 -0
  130. package/dist/scope/index.d.mts +21 -0
  131. package/dist/scope/index.mjs +65 -0
  132. package/dist/sessionManager-D_iEHjQl.d.mts +186 -0
  133. package/dist/sse-DkqQ1uxb.mjs +123 -0
  134. package/dist/testing/index.d.mts +907 -0
  135. package/dist/testing/index.mjs +1976 -0
  136. package/dist/tracing-8CEbhF0w.d.mts +70 -0
  137. package/dist/typeGuards-DwxA1t_L.mjs +9 -0
  138. package/dist/types/index.d.mts +946 -0
  139. package/dist/types/index.mjs +14 -0
  140. package/dist/types-B0dhNrnd.d.mts +445 -0
  141. package/dist/types-Beqn1Un7.mjs +38 -0
  142. package/dist/types-DelU6kln.mjs +25 -0
  143. package/dist/types-RLkFVgaw.d.mts +101 -0
  144. package/dist/utils/index.d.mts +747 -0
  145. package/dist/utils/index.mjs +6 -0
  146. package/package.json +194 -68
  147. package/dist/BaseController-DVAiHxEQ.d.ts +0 -233
  148. package/dist/adapters/index.d.ts +0 -237
  149. package/dist/adapters/index.js +0 -668
  150. package/dist/arcCorePlugin-CsShQdyP.d.ts +0 -273
  151. package/dist/audit/index.d.ts +0 -195
  152. package/dist/audit/index.js +0 -319
  153. package/dist/auth/index.d.ts +0 -47
  154. package/dist/auth/index.js +0 -174
  155. package/dist/cli/commands/docs.d.ts +0 -11
  156. package/dist/cli/commands/docs.js +0 -474
  157. package/dist/cli/commands/generate.js +0 -334
  158. package/dist/cli/commands/introspect.d.ts +0 -8
  159. package/dist/cli/commands/introspect.js +0 -338
  160. package/dist/cli/index.d.ts +0 -4
  161. package/dist/cli/index.js +0 -3269
  162. package/dist/core/index.d.ts +0 -220
  163. package/dist/core/index.js +0 -2786
  164. package/dist/createApp-Ce9wl8W9.d.ts +0 -77
  165. package/dist/docs/index.d.ts +0 -166
  166. package/dist/docs/index.js +0 -658
  167. package/dist/errors-8WIxGS_6.d.ts +0 -122
  168. package/dist/events/index.d.ts +0 -117
  169. package/dist/events/index.js +0 -89
  170. package/dist/factory/index.d.ts +0 -38
  171. package/dist/factory/index.js +0 -1652
  172. package/dist/hooks/index.d.ts +0 -4
  173. package/dist/hooks/index.js +0 -199
  174. package/dist/idempotency/index.d.ts +0 -323
  175. package/dist/idempotency/index.js +0 -500
  176. package/dist/index-B4t03KQ0.d.ts +0 -1366
  177. package/dist/index.d.ts +0 -135
  178. package/dist/index.js +0 -4756
  179. package/dist/migrations/index.d.ts +0 -185
  180. package/dist/migrations/index.js +0 -274
  181. package/dist/org/index.d.ts +0 -129
  182. package/dist/org/index.js +0 -220
  183. package/dist/permissions/index.d.ts +0 -144
  184. package/dist/permissions/index.js +0 -103
  185. package/dist/plugins/index.d.ts +0 -46
  186. package/dist/plugins/index.js +0 -1069
  187. package/dist/policies/index.js +0 -196
  188. package/dist/presets/index.js +0 -384
  189. package/dist/presets/multiTenant.d.ts +0 -39
  190. package/dist/presets/multiTenant.js +0 -112
  191. package/dist/registry/index.d.ts +0 -16
  192. package/dist/registry/index.js +0 -253
  193. package/dist/testing/index.d.ts +0 -618
  194. package/dist/testing/index.js +0 -48020
  195. package/dist/types/index.d.ts +0 -4
  196. package/dist/types/index.js +0 -8
  197. package/dist/types-B99TBmFV.d.ts +0 -76
  198. package/dist/types-BvckRbs2.d.ts +0 -143
  199. package/dist/utils/index.d.ts +0 -679
  200. package/dist/utils/index.js +0 -931
@@ -0,0 +1,404 @@
1
+ //#region src/hooks/HookSystem.ts
2
+ var HookSystem = class {
3
+ hooks;
4
+ logger;
5
+ warn;
6
+ constructor(options) {
7
+ this.hooks = /* @__PURE__ */ new Map();
8
+ this.logger = options?.logger ?? { error: (...args) => console.error(...args) };
9
+ this.warn = options?.logger?.warn ?? ((...args) => console.warn(...args));
10
+ }
11
+ /**
12
+ * Generate hook key
13
+ */
14
+ getKey(resource, operation, phase) {
15
+ return `${resource}:${operation}:${phase}`;
16
+ }
17
+ /**
18
+ * Register a hook
19
+ * Supports both object parameter and positional arguments
20
+ */
21
+ register(resourceOrOptions, operation, phase, handler, priority = 10) {
22
+ let hookName;
23
+ let resource;
24
+ let finalOperation;
25
+ let finalPhase;
26
+ let finalHandler;
27
+ let finalPriority;
28
+ let dependsOn;
29
+ if (typeof resourceOrOptions === "object") {
30
+ hookName = resourceOrOptions.name;
31
+ resource = resourceOrOptions.resource;
32
+ finalOperation = resourceOrOptions.operation;
33
+ finalPhase = resourceOrOptions.phase;
34
+ finalHandler = resourceOrOptions.handler;
35
+ finalPriority = resourceOrOptions.priority ?? 10;
36
+ dependsOn = resourceOrOptions.dependsOn;
37
+ } else {
38
+ resource = resourceOrOptions;
39
+ finalOperation = operation;
40
+ finalPhase = phase;
41
+ finalHandler = handler;
42
+ finalPriority = priority;
43
+ }
44
+ const key = this.getKey(resource, finalOperation, finalPhase);
45
+ if (!this.hooks.has(key)) this.hooks.set(key, []);
46
+ const registration = {
47
+ name: hookName,
48
+ resource,
49
+ operation: finalOperation,
50
+ phase: finalPhase,
51
+ handler: finalHandler,
52
+ priority: finalPriority,
53
+ dependsOn
54
+ };
55
+ const hooks = this.hooks.get(key);
56
+ hooks.push(registration);
57
+ hooks.sort((a, b) => a.priority - b.priority);
58
+ return () => {
59
+ const idx = hooks.indexOf(registration);
60
+ if (idx !== -1) hooks.splice(idx, 1);
61
+ };
62
+ }
63
+ /**
64
+ * Register before hook
65
+ */
66
+ before(resource, operation, handler, priority = 10) {
67
+ return this.register(resource, operation, "before", handler, priority);
68
+ }
69
+ /**
70
+ * Register after hook
71
+ */
72
+ after(resource, operation, handler, priority = 10) {
73
+ return this.register(resource, operation, "after", handler, priority);
74
+ }
75
+ /**
76
+ * Register around hook — wraps the core operation.
77
+ * Call `next()` inside the handler to proceed.
78
+ */
79
+ around(resource, operation, handler, priority = 10) {
80
+ return this.register(resource, operation, "around", handler, priority);
81
+ }
82
+ /**
83
+ * Execute around hooks as a nested middleware chain.
84
+ * Each around hook receives `next()` to call the next hook or the core operation.
85
+ */
86
+ async executeAround(resource, operation, data, execute, options) {
87
+ const key = this.getKey(resource, operation, "around");
88
+ const hooks = [...this.hooks.get(key) ?? []];
89
+ const wildcardKey = this.getKey("*", operation, "around");
90
+ const allHooks = [...this.hooks.get(wildcardKey) ?? [], ...hooks];
91
+ allHooks.sort((a, b) => a.priority - b.priority);
92
+ if (allHooks.length === 0) return execute();
93
+ let index = 0;
94
+ const next = async () => {
95
+ if (index < allHooks.length) {
96
+ const hook = allHooks[index++];
97
+ const ctx = {
98
+ resource,
99
+ operation,
100
+ phase: "around",
101
+ data,
102
+ user: options?.user,
103
+ context: options?.context,
104
+ meta: options?.meta
105
+ };
106
+ return hook.handler(ctx, next);
107
+ }
108
+ return execute();
109
+ };
110
+ return next();
111
+ }
112
+ /**
113
+ * Execute hooks for a given context
114
+ */
115
+ async execute(ctx) {
116
+ const key = this.getKey(ctx.resource, ctx.operation, ctx.phase);
117
+ const hooks = this.hooks.get(key) ?? [];
118
+ const wildcardKey = this.getKey("*", ctx.operation, ctx.phase);
119
+ let allHooks = [...this.hooks.get(wildcardKey) ?? [], ...hooks];
120
+ allHooks.sort((a, b) => a.priority - b.priority);
121
+ if (allHooks.some((h) => h.dependsOn?.length)) allHooks = this.topologicalSort(allHooks);
122
+ let result = ctx.data;
123
+ for (const hook of allHooks) {
124
+ const handlerContext = {
125
+ resource: ctx.resource,
126
+ operation: ctx.operation,
127
+ phase: ctx.phase,
128
+ data: result,
129
+ result: ctx.result,
130
+ user: ctx.user,
131
+ context: ctx.context,
132
+ meta: ctx.meta
133
+ };
134
+ const hookResult = await hook.handler(handlerContext);
135
+ if (hookResult !== void 0 && hookResult !== null) result = hookResult;
136
+ }
137
+ return result;
138
+ }
139
+ /**
140
+ * Execute before hooks
141
+ */
142
+ async executeBefore(resource, operation, data, options) {
143
+ return await this.execute({
144
+ resource,
145
+ operation,
146
+ phase: "before",
147
+ data,
148
+ user: options?.user,
149
+ context: options?.context,
150
+ meta: options?.meta
151
+ }) ?? data;
152
+ }
153
+ /**
154
+ * Execute after hooks
155
+ * Errors in after hooks are logged but don't fail the request
156
+ */
157
+ async executeAfter(resource, operation, result, options) {
158
+ try {
159
+ await this.execute({
160
+ resource,
161
+ operation,
162
+ phase: "after",
163
+ result,
164
+ user: options?.user,
165
+ context: options?.context,
166
+ meta: options?.meta
167
+ });
168
+ } catch (error) {
169
+ this.logger.error(`[HookSystem] Error in after hook for ${resource}:${operation}:`, error);
170
+ }
171
+ }
172
+ /**
173
+ * Topological sort with Kahn's algorithm.
174
+ * Hooks with `dependsOn` are ordered after their dependencies.
175
+ * Within the same dependency level, priority ordering is preserved.
176
+ * Hooks without names or dependencies pass through in their original order.
177
+ */
178
+ topologicalSort(hooks) {
179
+ const byName = /* @__PURE__ */ new Map();
180
+ const inDegree = /* @__PURE__ */ new Map();
181
+ const dependents = /* @__PURE__ */ new Map();
182
+ for (const hook of hooks) {
183
+ inDegree.set(hook, 0);
184
+ if (hook.name) byName.set(hook.name, hook);
185
+ }
186
+ for (const hook of hooks) if (hook.dependsOn) {
187
+ let resolvedDeps = 0;
188
+ for (const dep of hook.dependsOn) if (byName.has(dep)) {
189
+ resolvedDeps++;
190
+ if (!dependents.has(dep)) dependents.set(dep, []);
191
+ dependents.get(dep).push(hook);
192
+ } else this.warn(`[HookSystem] Hook '${hook.name ?? "<unnamed>"}' depends on '${dep}' which is not registered in the same phase/resource. Dependency will be ignored.`);
193
+ inDegree.set(hook, resolvedDeps);
194
+ }
195
+ const queue = [];
196
+ const result = [];
197
+ for (const hook of hooks) if (inDegree.get(hook) === 0) queue.push(hook);
198
+ queue.sort((a, b) => a.priority - b.priority);
199
+ while (queue.length > 0) {
200
+ const current = queue.shift();
201
+ result.push(current);
202
+ if (current.name && dependents.has(current.name)) {
203
+ const deps = dependents.get(current.name);
204
+ for (const dep of deps) {
205
+ const newDegree = inDegree.get(dep) - 1;
206
+ inDegree.set(dep, newDegree);
207
+ if (newDegree === 0) {
208
+ const insertIdx = queue.findIndex((q) => q.priority > dep.priority);
209
+ if (insertIdx === -1) queue.push(dep);
210
+ else queue.splice(insertIdx, 0, dep);
211
+ }
212
+ }
213
+ }
214
+ }
215
+ if (result.length < hooks.length) {
216
+ const missing = hooks.filter((h) => !result.includes(h));
217
+ const names = missing.map((h) => h.name ?? "<unnamed>").join(", ");
218
+ this.logger.error(`[HookSystem] Circular dependency detected in hooks: ${names}. These hooks will be appended in priority order.`);
219
+ missing.sort((a, b) => a.priority - b.priority);
220
+ result.push(...missing);
221
+ }
222
+ return result;
223
+ }
224
+ /**
225
+ * Get all registered hooks
226
+ */
227
+ getAll() {
228
+ const all = [];
229
+ for (const hooks of this.hooks.values()) all.push(...hooks);
230
+ return all;
231
+ }
232
+ /**
233
+ * Get hooks for a specific resource
234
+ */
235
+ getForResource(resource) {
236
+ const all = [];
237
+ for (const [key, hooks] of this.hooks.entries()) if (key.startsWith(`${resource}:`)) all.push(...hooks);
238
+ return all;
239
+ }
240
+ /**
241
+ * Get hooks matching filter criteria.
242
+ * Useful for debugging and testing specific hook combinations.
243
+ *
244
+ * @example
245
+ * ```typescript
246
+ * // Find all before-create hooks for products (including wildcards)
247
+ * const hooks = hookSystem.getRegistered({
248
+ * resource: 'product',
249
+ * operation: 'create',
250
+ * phase: 'before',
251
+ * });
252
+ * ```
253
+ */
254
+ getRegistered(filter) {
255
+ let results = this.getAll();
256
+ if (filter?.resource) results = results.filter((h) => h.resource === filter.resource || h.resource === "*");
257
+ if (filter?.operation) results = results.filter((h) => h.operation === filter.operation);
258
+ if (filter?.phase) results = results.filter((h) => h.phase === filter.phase);
259
+ return results;
260
+ }
261
+ /**
262
+ * Get a structured summary of all registered hooks for debugging.
263
+ *
264
+ * @example
265
+ * ```typescript
266
+ * const info = hookSystem.inspect();
267
+ * // { total: 12, resources: { product: [...], '*': [...] }, summary: [...] }
268
+ * ```
269
+ */
270
+ inspect() {
271
+ const all = this.getAll();
272
+ const byResource = /* @__PURE__ */ new Map();
273
+ for (const hook of all) {
274
+ const arr = byResource.get(hook.resource) ?? [];
275
+ arr.push(hook);
276
+ byResource.set(hook.resource, arr);
277
+ }
278
+ return {
279
+ total: all.length,
280
+ resources: Object.fromEntries(byResource),
281
+ summary: all.map((h) => ({
282
+ name: h.name,
283
+ key: `${h.resource}:${h.operation}:${h.phase}`,
284
+ priority: h.priority,
285
+ ...h.dependsOn?.length ? { dependsOn: h.dependsOn } : {}
286
+ }))
287
+ };
288
+ }
289
+ /**
290
+ * Check if any hooks exist for a specific resource/operation/phase combination.
291
+ */
292
+ has(resource, operation, phase) {
293
+ const key = this.getKey(resource, operation, phase);
294
+ return (this.hooks.get(key)?.length ?? 0) > 0;
295
+ }
296
+ /**
297
+ * Clear all hooks
298
+ */
299
+ clear() {
300
+ this.hooks.clear();
301
+ }
302
+ /**
303
+ * Clear hooks for a specific resource
304
+ */
305
+ clearResource(resource) {
306
+ for (const key of this.hooks.keys()) if (key.startsWith(`${resource}:`)) this.hooks.delete(key);
307
+ }
308
+ };
309
+ /**
310
+ * Create a new isolated HookSystem instance
311
+ *
312
+ * Use this for:
313
+ * - Test isolation (parallel test suites)
314
+ * - Multiple app instances with independent hooks
315
+ *
316
+ * @example
317
+ * const hooks = createHookSystem();
318
+ * await app.register(arcCorePlugin, { hookSystem: hooks });
319
+ *
320
+ * @example With custom logger
321
+ * const hooks = createHookSystem({ logger: fastify.log });
322
+ */
323
+ function createHookSystem(options) {
324
+ return new HookSystem(options);
325
+ }
326
+ /**
327
+ * Define a named hook with optional dependencies.
328
+ * Returns a registration object — call `register(hookSystem)` to activate.
329
+ *
330
+ * @example
331
+ * ```typescript
332
+ * const generateSlug = defineHook({
333
+ * name: 'generateSlug',
334
+ * resource: 'product', operation: 'create', phase: 'before',
335
+ * handler: (ctx) => ({ ...ctx.data, slug: slugify(ctx.data.name) }),
336
+ * });
337
+ *
338
+ * const validateUniqueSlug = defineHook({
339
+ * name: 'validateUniqueSlug',
340
+ * resource: 'product', operation: 'create', phase: 'before',
341
+ * dependsOn: ['generateSlug'],
342
+ * handler: async (ctx) => { // check uniqueness },
343
+ * });
344
+ *
345
+ * // Register on a hook system
346
+ * generateSlug.register(hooks);
347
+ * validateUniqueSlug.register(hooks);
348
+ * ```
349
+ */
350
+ function defineHook(options) {
351
+ return {
352
+ ...options,
353
+ register(hooks) {
354
+ return hooks.register({
355
+ name: options.name,
356
+ resource: options.resource,
357
+ operation: options.operation,
358
+ phase: options.phase,
359
+ handler: options.handler,
360
+ priority: options.priority,
361
+ dependsOn: options.dependsOn
362
+ });
363
+ }
364
+ };
365
+ }
366
+ /**
367
+ * Create a before-create hook registration for a given hook system
368
+ */
369
+ function beforeCreate(hooks, resource, handler, priority = 10) {
370
+ return hooks.before(resource, "create", handler, priority);
371
+ }
372
+ /**
373
+ * Create an after-create hook registration for a given hook system
374
+ */
375
+ function afterCreate(hooks, resource, handler, priority = 10) {
376
+ return hooks.after(resource, "create", handler, priority);
377
+ }
378
+ /**
379
+ * Create a before-update hook registration for a given hook system
380
+ */
381
+ function beforeUpdate(hooks, resource, handler, priority = 10) {
382
+ return hooks.before(resource, "update", handler, priority);
383
+ }
384
+ /**
385
+ * Create an after-update hook registration for a given hook system
386
+ */
387
+ function afterUpdate(hooks, resource, handler, priority = 10) {
388
+ return hooks.after(resource, "update", handler, priority);
389
+ }
390
+ /**
391
+ * Create a before-delete hook registration for a given hook system
392
+ */
393
+ function beforeDelete(hooks, resource, handler, priority = 10) {
394
+ return hooks.before(resource, "delete", handler, priority);
395
+ }
396
+ /**
397
+ * Create an after-delete hook registration for a given hook system
398
+ */
399
+ function afterDelete(hooks, resource, handler, priority = 10) {
400
+ return hooks.after(resource, "delete", handler, priority);
401
+ }
402
+
403
+ //#endregion
404
+ export { beforeCreate as a, createHookSystem as c, afterUpdate as i, defineHook as l, afterCreate as n, beforeDelete as o, afterDelete as r, beforeUpdate as s, HookSystem as t };
@@ -0,0 +1,249 @@
1
+ import { s as DEFAULT_UPDATE_METHOD, t as CRUD_OPERATIONS } from "./constants-DdXFXQtN.mjs";
2
+
3
+ //#region src/registry/ResourceRegistry.ts
4
+ var ResourceRegistry = class {
5
+ _resources;
6
+ _frozen;
7
+ constructor() {
8
+ this._resources = /* @__PURE__ */ new Map();
9
+ this._frozen = false;
10
+ }
11
+ /**
12
+ * Register a resource
13
+ */
14
+ register(resource, options = {}) {
15
+ if (this._frozen) throw new Error(`Registry frozen. Cannot register '${resource.name}' after startup.`);
16
+ if (this._resources.has(resource.name)) throw new Error(`Resource '${resource.name}' already registered.`);
17
+ const entry = {
18
+ name: resource.name,
19
+ displayName: resource.displayName,
20
+ tag: resource.tag,
21
+ prefix: resource.prefix,
22
+ module: options.module ?? void 0,
23
+ adapter: resource.adapter ? {
24
+ type: resource.adapter.type,
25
+ name: resource.adapter.name
26
+ } : null,
27
+ permissions: resource.permissions,
28
+ presets: resource._appliedPresets ?? [],
29
+ routes: [],
30
+ additionalRoutes: resource.additionalRoutes.map((r) => ({
31
+ method: r.method,
32
+ path: r.path,
33
+ handler: typeof r.handler === "string" ? r.handler : r.handler.name || "anonymous",
34
+ operation: r.operation,
35
+ summary: r.summary,
36
+ description: r.description,
37
+ permissions: r.permissions,
38
+ wrapHandler: r.wrapHandler,
39
+ schema: r.schema
40
+ })),
41
+ events: Object.keys(resource.events ?? {}),
42
+ registeredAt: (/* @__PURE__ */ new Date()).toISOString(),
43
+ disableDefaultRoutes: resource.disableDefaultRoutes,
44
+ updateMethod: resource.updateMethod,
45
+ disabledRoutes: resource.disabledRoutes,
46
+ openApiSchemas: options.openApiSchemas,
47
+ fieldPermissions: extractFieldPermissions(resource.fields),
48
+ pipelineSteps: extractPipelineSteps(resource.pipe),
49
+ rateLimit: resource.rateLimit,
50
+ plugin: resource.toPlugin()
51
+ };
52
+ this._resources.set(resource.name, entry);
53
+ return this;
54
+ }
55
+ /**
56
+ * Get resource by name
57
+ */
58
+ get(name) {
59
+ return this._resources.get(name);
60
+ }
61
+ /**
62
+ * Get all resources
63
+ */
64
+ getAll() {
65
+ return Array.from(this._resources.values());
66
+ }
67
+ /**
68
+ * Get resources by module
69
+ */
70
+ getByModule(moduleName) {
71
+ return this.getAll().filter((r) => r.module === moduleName);
72
+ }
73
+ /**
74
+ * Get resources by preset
75
+ */
76
+ getByPreset(presetName) {
77
+ return this.getAll().filter((r) => r.presets.includes(presetName));
78
+ }
79
+ /**
80
+ * Check if resource exists
81
+ */
82
+ has(name) {
83
+ return this._resources.has(name);
84
+ }
85
+ /**
86
+ * Get registry statistics
87
+ */
88
+ getStats() {
89
+ const resources = this.getAll();
90
+ const presetCounts = {};
91
+ for (const r of resources) for (const preset of r.presets) presetCounts[preset] = (presetCounts[preset] ?? 0) + 1;
92
+ return {
93
+ totalResources: resources.length,
94
+ byModule: this._groupBy(resources, "module"),
95
+ presetUsage: presetCounts,
96
+ totalRoutes: resources.reduce((sum, r) => {
97
+ if (r.disableDefaultRoutes) return sum + (r.additionalRoutes?.length ?? 0);
98
+ const disabledSet = new Set(r.disabledRoutes ?? []);
99
+ let defaultCount = CRUD_OPERATIONS.filter((route) => !disabledSet.has(route)).length;
100
+ if (!disabledSet.has("update") && r.updateMethod === "both") defaultCount += 1;
101
+ return sum + defaultCount + (r.additionalRoutes?.length ?? 0);
102
+ }, 0),
103
+ totalEvents: resources.reduce((sum, r) => sum + (r.events?.length ?? 0), 0)
104
+ };
105
+ }
106
+ /**
107
+ * Get full introspection data
108
+ */
109
+ getIntrospection() {
110
+ return {
111
+ resources: this.getAll().map((r) => {
112
+ const disabledSet = new Set(r.disabledRoutes ?? []);
113
+ const updateMethod = r.updateMethod ?? DEFAULT_UPDATE_METHOD;
114
+ const defaultRoutes = r.disableDefaultRoutes ? [] : [
115
+ ...!disabledSet.has("list") ? [{
116
+ method: "GET",
117
+ path: r.prefix,
118
+ operation: "list"
119
+ }] : [],
120
+ ...!disabledSet.has("get") ? [{
121
+ method: "GET",
122
+ path: `${r.prefix}/:id`,
123
+ operation: "get"
124
+ }] : [],
125
+ ...!disabledSet.has("create") ? [{
126
+ method: "POST",
127
+ path: r.prefix,
128
+ operation: "create"
129
+ }] : [],
130
+ ...!disabledSet.has("update") ? updateMethod === "both" ? [{
131
+ method: "PUT",
132
+ path: `${r.prefix}/:id`,
133
+ operation: "update"
134
+ }, {
135
+ method: "PATCH",
136
+ path: `${r.prefix}/:id`,
137
+ operation: "update"
138
+ }] : [{
139
+ method: updateMethod,
140
+ path: `${r.prefix}/:id`,
141
+ operation: "update"
142
+ }] : [],
143
+ ...!disabledSet.has("delete") ? [{
144
+ method: "DELETE",
145
+ path: `${r.prefix}/:id`,
146
+ operation: "delete"
147
+ }] : []
148
+ ];
149
+ return {
150
+ name: r.name,
151
+ displayName: r.displayName,
152
+ prefix: r.prefix,
153
+ module: r.module,
154
+ presets: r.presets,
155
+ permissions: r.permissions,
156
+ routes: [...defaultRoutes, ...r.additionalRoutes?.map((ar) => ({
157
+ method: ar.method,
158
+ path: `${r.prefix}${ar.path}`,
159
+ operation: ar.operation ?? (typeof ar.handler === "string" ? ar.handler : "custom"),
160
+ handler: typeof ar.handler === "string" ? ar.handler : void 0,
161
+ summary: ar.summary
162
+ })) ?? []],
163
+ events: r.events
164
+ };
165
+ }),
166
+ stats: this.getStats(),
167
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString()
168
+ };
169
+ }
170
+ /**
171
+ * Freeze registry (prevent further registrations)
172
+ */
173
+ freeze() {
174
+ this._frozen = true;
175
+ }
176
+ /**
177
+ * Check if frozen
178
+ */
179
+ isFrozen() {
180
+ return this._frozen;
181
+ }
182
+ /**
183
+ * Unfreeze registry (allow new registrations)
184
+ */
185
+ unfreeze() {
186
+ this._frozen = false;
187
+ }
188
+ /**
189
+ * Reset registry — clear all resources and unfreeze
190
+ */
191
+ reset() {
192
+ this._resources.clear();
193
+ this._frozen = false;
194
+ }
195
+ /** @internal Alias for unfreeze() */
196
+ _unfreeze() {
197
+ this.unfreeze();
198
+ }
199
+ /** @internal Alias for reset() */
200
+ _clear() {
201
+ this.reset();
202
+ }
203
+ /**
204
+ * Group by key
205
+ */
206
+ _groupBy(arr, key) {
207
+ const result = {};
208
+ for (const item of arr) {
209
+ const k = String(item[key] ?? "uncategorized");
210
+ result[k] = (result[k] ?? 0) + 1;
211
+ }
212
+ return result;
213
+ }
214
+ };
215
+ function extractFieldPermissions(fields) {
216
+ if (!fields || Object.keys(fields).length === 0) return void 0;
217
+ const result = {};
218
+ for (const [field, perm] of Object.entries(fields)) {
219
+ const entry = { type: perm._type };
220
+ if (perm.roles?.length) entry.roles = perm.roles;
221
+ if (perm.redactValue !== void 0) entry.redactValue = perm.redactValue;
222
+ result[field] = entry;
223
+ }
224
+ return result;
225
+ }
226
+ function extractPipelineSteps(pipe) {
227
+ if (!pipe) return void 0;
228
+ const steps = [];
229
+ if (Array.isArray(pipe)) steps.push(...pipe);
230
+ else {
231
+ const seen = /* @__PURE__ */ new Set();
232
+ for (const opSteps of Object.values(pipe)) if (Array.isArray(opSteps)) for (const step of opSteps) {
233
+ const key = `${step._type}:${step.name}`;
234
+ if (!seen.has(key)) {
235
+ seen.add(key);
236
+ steps.push(step);
237
+ }
238
+ }
239
+ }
240
+ if (steps.length === 0) return void 0;
241
+ return steps.map((s) => ({
242
+ type: s._type,
243
+ name: s.name,
244
+ operations: s.operations ? [...s.operations] : void 0
245
+ }));
246
+ }
247
+
248
+ //#endregion
249
+ export { ResourceRegistry as t };
@@ -0,0 +1,5 @@
1
+ import "../elevation-DGo5shaX.mjs";
2
+ import { a as RepositoryLike, i as RelationMetadata, n as DataAdapter, o as SchemaMetadata, r as FieldMetadata, s as ValidationResult, t as AdapterFactory } from "../interface-e9XfSsUV.mjs";
3
+ import "../types-RLkFVgaw.mjs";
4
+ import { a as PrismaQueryParserOptions, c as MongooseAdapterOptions, i as PrismaQueryParser, l as createMongooseAdapter, n as PrismaAdapterOptions, o as createPrismaAdapter, r as PrismaQueryOptions, s as MongooseAdapter, t as PrismaAdapter } from "../prisma-C3iornoK.mjs";
5
+ export { type AdapterFactory, type DataAdapter, type FieldMetadata, MongooseAdapter, type MongooseAdapterOptions, PrismaAdapter, type PrismaAdapterOptions, type PrismaQueryOptions, PrismaQueryParser, type PrismaQueryParserOptions, type RelationMetadata, type RepositoryLike, type SchemaMetadata, type ValidationResult, createMongooseAdapter, createPrismaAdapter };
@@ -0,0 +1,3 @@
1
+ import { a as createMongooseAdapter, i as MongooseAdapter, n as PrismaQueryParser, r as createPrismaAdapter, t as PrismaAdapter } from "../prisma-DJbMt3yf.mjs";
2
+
3
+ export { MongooseAdapter, PrismaAdapter, PrismaQueryParser, createMongooseAdapter, createPrismaAdapter };