@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
@@ -1,319 +0,0 @@
1
- import fp from 'fastify-plugin';
2
-
3
- // src/audit/auditPlugin.ts
4
-
5
- // src/audit/stores/interface.ts
6
- function createAuditEntry(resource, documentId, action, context, data) {
7
- const changes = data?.before && data?.after ? detectChanges(data.before, data.after) : void 0;
8
- return {
9
- id: generateAuditId(),
10
- resource,
11
- documentId,
12
- action,
13
- userId: context.user?._id?.toString() ?? context.user?.id,
14
- organizationId: context.organizationId,
15
- before: data?.before,
16
- after: data?.after,
17
- changes,
18
- requestId: context.requestId,
19
- ipAddress: context.ipAddress,
20
- userAgent: context.userAgent,
21
- metadata: data?.metadata,
22
- timestamp: /* @__PURE__ */ new Date()
23
- };
24
- }
25
- function detectChanges(before, after) {
26
- const changes = [];
27
- const allKeys = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
28
- for (const key of allKeys) {
29
- if (key.startsWith("_") || key === "updatedAt") continue;
30
- const oldVal = JSON.stringify(before[key]);
31
- const newVal = JSON.stringify(after[key]);
32
- if (oldVal !== newVal) {
33
- changes.push(key);
34
- }
35
- }
36
- return changes;
37
- }
38
- function generateAuditId() {
39
- const timestamp = Date.now().toString(36);
40
- const random = Math.random().toString(36).substring(2, 10);
41
- return `aud_${timestamp}${random}`;
42
- }
43
-
44
- // src/audit/stores/memory.ts
45
- var MemoryAuditStore = class {
46
- name = "memory";
47
- entries = [];
48
- maxEntries;
49
- constructor(options = {}) {
50
- this.maxEntries = options.maxEntries ?? 1e3;
51
- }
52
- async log(entry) {
53
- this.entries.unshift(entry);
54
- if (this.entries.length > this.maxEntries) {
55
- this.entries = this.entries.slice(0, this.maxEntries);
56
- }
57
- }
58
- async query(options = {}) {
59
- let results = [...this.entries];
60
- if (options.resource) {
61
- results = results.filter((e) => e.resource === options.resource);
62
- }
63
- if (options.documentId) {
64
- results = results.filter((e) => e.documentId === options.documentId);
65
- }
66
- if (options.userId) {
67
- results = results.filter((e) => e.userId === options.userId);
68
- }
69
- if (options.organizationId) {
70
- results = results.filter((e) => e.organizationId === options.organizationId);
71
- }
72
- if (options.action) {
73
- const actions = Array.isArray(options.action) ? options.action : [options.action];
74
- results = results.filter((e) => actions.includes(e.action));
75
- }
76
- if (options.from) {
77
- results = results.filter((e) => e.timestamp >= options.from);
78
- }
79
- if (options.to) {
80
- results = results.filter((e) => e.timestamp <= options.to);
81
- }
82
- const offset = options.offset ?? 0;
83
- const limit = options.limit ?? 100;
84
- results = results.slice(offset, offset + limit);
85
- return results;
86
- }
87
- async close() {
88
- this.entries = [];
89
- }
90
- /** Get all entries (for testing) */
91
- getAll() {
92
- return [...this.entries];
93
- }
94
- /** Clear all entries (for testing) */
95
- clear() {
96
- this.entries = [];
97
- }
98
- };
99
-
100
- // src/audit/stores/mongodb.ts
101
- var MongoAuditStore = class {
102
- constructor(options) {
103
- this.options = options;
104
- const collectionName = options.collection ?? "audit_logs";
105
- this.collection = options.connection.collection(collectionName);
106
- this.ttlDays = options.ttlDays ?? 90;
107
- }
108
- name = "mongodb";
109
- collection;
110
- initialized = false;
111
- ttlDays;
112
- async ensureIndexes() {
113
- if (this.initialized) return;
114
- try {
115
- await this.collection.createIndex({
116
- resource: 1,
117
- documentId: 1,
118
- timestamp: -1
119
- });
120
- await this.collection.createIndex({ userId: 1, timestamp: -1 });
121
- await this.collection.createIndex({ organizationId: 1, timestamp: -1 });
122
- if (this.ttlDays > 0) {
123
- await this.collection.createIndex(
124
- { timestamp: 1 },
125
- { expireAfterSeconds: this.ttlDays * 24 * 60 * 60 }
126
- );
127
- }
128
- this.initialized = true;
129
- } catch {
130
- this.initialized = true;
131
- }
132
- }
133
- async log(entry) {
134
- await this.ensureIndexes();
135
- await this.collection.insertOne({
136
- _id: entry.id,
137
- resource: entry.resource,
138
- documentId: entry.documentId,
139
- action: entry.action,
140
- userId: entry.userId,
141
- organizationId: entry.organizationId,
142
- before: entry.before,
143
- after: entry.after,
144
- changes: entry.changes,
145
- requestId: entry.requestId,
146
- ipAddress: entry.ipAddress,
147
- userAgent: entry.userAgent,
148
- metadata: entry.metadata,
149
- timestamp: entry.timestamp
150
- });
151
- }
152
- async query(options = {}) {
153
- await this.ensureIndexes();
154
- const query = {};
155
- if (options.resource) {
156
- query.resource = options.resource;
157
- }
158
- if (options.documentId) {
159
- query.documentId = options.documentId;
160
- }
161
- if (options.userId) {
162
- query.userId = options.userId;
163
- }
164
- if (options.organizationId) {
165
- query.organizationId = options.organizationId;
166
- }
167
- if (options.action) {
168
- const actions = Array.isArray(options.action) ? options.action : [options.action];
169
- query.action = actions.length === 1 ? actions[0] : { $in: actions };
170
- }
171
- if (options.from || options.to) {
172
- query.timestamp = {};
173
- if (options.from) {
174
- query.timestamp.$gte = options.from;
175
- }
176
- if (options.to) {
177
- query.timestamp.$lte = options.to;
178
- }
179
- }
180
- const offset = options.offset ?? 0;
181
- const limit = options.limit ?? 100;
182
- const docs = await this.collection.find(query).sort({ timestamp: -1 }).skip(offset).limit(limit).toArray();
183
- return docs.map((doc) => ({
184
- id: String(doc._id),
185
- resource: doc.resource,
186
- documentId: doc.documentId,
187
- action: doc.action,
188
- userId: doc.userId,
189
- organizationId: doc.organizationId,
190
- before: doc.before,
191
- after: doc.after,
192
- changes: doc.changes,
193
- requestId: doc.requestId,
194
- ipAddress: doc.ipAddress,
195
- userAgent: doc.userAgent,
196
- metadata: doc.metadata,
197
- timestamp: doc.timestamp
198
- }));
199
- }
200
- };
201
-
202
- // src/audit/auditPlugin.ts
203
- var auditPlugin = async (fastify, opts = {}) => {
204
- const {
205
- enabled = false,
206
- stores: storeTypes = ["memory"],
207
- mongoConnection,
208
- mongoCollection = "audit_logs",
209
- ttlDays = 90,
210
- customStores = []
211
- } = opts;
212
- if (!enabled) {
213
- fastify.decorate("audit", createNoopLogger());
214
- fastify.decorateRequest("auditContext", void 0);
215
- fastify.log?.debug?.("Audit plugin disabled");
216
- return;
217
- }
218
- const stores = [...customStores];
219
- for (const type of storeTypes) {
220
- switch (type) {
221
- case "memory":
222
- stores.push(new MemoryAuditStore());
223
- break;
224
- case "mongodb":
225
- if (!mongoConnection) {
226
- throw new Error("Audit: mongoConnection required for mongodb store");
227
- }
228
- stores.push(new MongoAuditStore({
229
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
230
- connection: mongoConnection,
231
- collection: mongoCollection,
232
- ttlDays
233
- }));
234
- break;
235
- }
236
- }
237
- if (stores.length === 0) {
238
- throw new Error("Audit: at least one store must be configured");
239
- }
240
- async function logToStores(entry) {
241
- await Promise.all(stores.map((store) => store.log(entry)));
242
- }
243
- const audit = {
244
- async create(resource, documentId, data, context) {
245
- const entry = createAuditEntry(resource, documentId, "create", context ?? {}, {
246
- after: data
247
- });
248
- await logToStores(entry);
249
- },
250
- async update(resource, documentId, before, after, context) {
251
- const entry = createAuditEntry(resource, documentId, "update", context ?? {}, {
252
- before,
253
- after
254
- });
255
- await logToStores(entry);
256
- },
257
- async delete(resource, documentId, data, context) {
258
- const entry = createAuditEntry(resource, documentId, "delete", context ?? {}, {
259
- before: data
260
- });
261
- await logToStores(entry);
262
- },
263
- async restore(resource, documentId, data, context) {
264
- const entry = createAuditEntry(resource, documentId, "restore", context ?? {}, {
265
- after: data
266
- });
267
- await logToStores(entry);
268
- },
269
- async custom(resource, documentId, action, data, context) {
270
- const entry = createAuditEntry(resource, documentId, "custom", context ?? {}, {
271
- metadata: { customAction: action, ...data }
272
- });
273
- await logToStores(entry);
274
- },
275
- async query(options) {
276
- for (const store of stores) {
277
- if (store.query) {
278
- return store.query(options);
279
- }
280
- }
281
- return [];
282
- }
283
- };
284
- fastify.decorate("audit", audit);
285
- fastify.decorateRequest("auditContext", void 0);
286
- fastify.addHook("onRequest", async (request) => {
287
- const user = request.user;
288
- const context = request.context;
289
- request.auditContext = {
290
- user,
291
- organizationId: context?.organizationId ?? void 0,
292
- requestId: request.id,
293
- ipAddress: request.ip,
294
- userAgent: request.headers["user-agent"]
295
- };
296
- });
297
- fastify.addHook("onClose", async () => {
298
- await Promise.all(stores.map((store) => store.close?.()));
299
- });
300
- fastify.log?.info?.({ stores: storeTypes }, "Audit plugin enabled");
301
- };
302
- function createNoopLogger() {
303
- const noop = async () => {
304
- };
305
- return {
306
- create: noop,
307
- update: noop,
308
- delete: noop,
309
- restore: noop,
310
- custom: noop,
311
- query: async () => []
312
- };
313
- }
314
- var auditPlugin_default = fp(auditPlugin, {
315
- name: "arc-audit",
316
- fastify: "5.x"
317
- });
318
-
319
- export { MemoryAuditStore, MongoAuditStore, auditPlugin_default as auditPlugin, auditPlugin as auditPluginFn, createAuditEntry };
@@ -1,47 +0,0 @@
1
- import { FastifyPluginAsync } from 'fastify';
2
- import { g as AuthHelpers, h as AuthPluginOptions } from '../index-B4t03KQ0.js';
3
- import 'mongoose';
4
- import '../types-B99TBmFV.js';
5
-
6
- /**
7
- * Auth Plugin - Flexible, Database-Agnostic Authentication
8
- *
9
- * Arc provides JWT infrastructure and calls your authenticator.
10
- * You control ALL authentication logic.
11
- *
12
- * Design principles:
13
- * - Arc handles plumbing (JWT sign/verify utilities)
14
- * - App handles business logic (how to authenticate, where users live)
15
- * - Works with any database (Prisma, MongoDB, Postgres, none)
16
- * - Supports multiple auth strategies (JWT, API keys, sessions, etc.)
17
- *
18
- * @example
19
- * ```typescript
20
- * // In createApp
21
- * auth: {
22
- * jwt: { secret: process.env.JWT_SECRET },
23
- * authenticate: async (request, { jwt }) => {
24
- * // Your auth logic - Arc never touches your database
25
- * const token = request.headers.authorization?.split(' ')[1];
26
- * if (!token) return null;
27
- * const decoded = jwt.verify(token);
28
- * return userRepo.findById(decoded.id);
29
- * },
30
- * }
31
- * ```
32
- */
33
-
34
- declare module 'fastify' {
35
- interface FastifyInstance {
36
- /** Authenticate middleware - use in preHandler for protected routes */
37
- authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
38
- /** Authorize middleware factory - checks if user has required roles */
39
- authorize: (...roles: string[]) => (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
40
- /** Auth helpers - issueTokens, jwt utilities */
41
- auth: AuthHelpers;
42
- }
43
- }
44
- declare const authPlugin: FastifyPluginAsync<AuthPluginOptions>;
45
- declare const _default: FastifyPluginAsync<AuthPluginOptions>;
46
-
47
- export { AuthPluginOptions, _default as authPlugin, authPlugin as authPluginFn };
@@ -1,174 +0,0 @@
1
- import fp from 'fastify-plugin';
2
-
3
- // src/auth/authPlugin.ts
4
- function parseExpiresIn(input, defaultValue) {
5
- if (!input) return defaultValue;
6
- if (/^\d+$/.test(input)) return parseInt(input, 10);
7
- const match = /^(\d+)\s*([smhd])$/i.exec(input);
8
- if (!match) return defaultValue;
9
- const value = parseInt(match[1], 10);
10
- const unit = match[2].toLowerCase();
11
- const multipliers = { s: 1, m: 60, h: 3600, d: 86400 };
12
- return value * (multipliers[unit] ?? 1);
13
- }
14
- function extractBearerToken(request) {
15
- const auth = request.headers.authorization;
16
- if (!auth?.startsWith("Bearer ")) return null;
17
- return auth.slice(7);
18
- }
19
- var authPlugin = async (fastify, opts = {}) => {
20
- const { jwt: jwtConfig, authenticate: appAuthenticator, onFailure, userProperty = "user" } = opts;
21
- let jwtContext = null;
22
- if (jwtConfig?.secret) {
23
- if (jwtConfig.secret.length < 32) {
24
- throw new Error(
25
- `JWT secret must be at least 32 characters (current: ${jwtConfig.secret.length}).
26
- Use a strong random secret for production.`
27
- );
28
- }
29
- const jwtPlugin = await import('@fastify/jwt');
30
- await fastify.register(jwtPlugin.default ?? jwtPlugin, {
31
- secret: jwtConfig.secret,
32
- sign: {
33
- expiresIn: jwtConfig.expiresIn ?? "15m",
34
- ...jwtConfig.sign ?? {}
35
- },
36
- verify: { ...jwtConfig.verify ?? {} }
37
- });
38
- const fastifyWithJwt = fastify;
39
- jwtContext = {
40
- verify: (token) => {
41
- return fastifyWithJwt.jwt.verify(token);
42
- },
43
- sign: (payload, options) => {
44
- return fastifyWithJwt.jwt.sign(payload, options);
45
- },
46
- decode: (token) => {
47
- try {
48
- return fastifyWithJwt.jwt.decode(token);
49
- } catch {
50
- return null;
51
- }
52
- }
53
- };
54
- fastify.log.info("Auth: JWT infrastructure enabled");
55
- }
56
- const authContext = {
57
- jwt: jwtContext,
58
- fastify
59
- };
60
- const authenticate = async (request, reply) => {
61
- try {
62
- let user = null;
63
- if (appAuthenticator) {
64
- user = await appAuthenticator(request, authContext);
65
- } else if (jwtContext) {
66
- const token = extractBearerToken(request);
67
- if (token) {
68
- const decoded = jwtContext.verify(token);
69
- user = decoded;
70
- }
71
- } else {
72
- throw new Error(
73
- "No authenticator configured. Provide auth.authenticate function or auth.jwt.secret."
74
- );
75
- }
76
- if (!user) {
77
- throw new Error("Authentication required");
78
- }
79
- request[userProperty] = user;
80
- } catch (err) {
81
- const error = err instanceof Error ? err : new Error(String(err));
82
- if (onFailure) {
83
- await onFailure(request, reply, error);
84
- return;
85
- }
86
- const message = process.env.NODE_ENV === "production" ? "Authentication required" : error.message;
87
- reply.code(401).send({
88
- success: false,
89
- error: "Unauthorized",
90
- message
91
- });
92
- }
93
- };
94
- const refreshSecret = jwtConfig?.refreshSecret ?? jwtConfig?.secret;
95
- const accessExpiresIn = jwtConfig?.expiresIn ?? "15m";
96
- const refreshExpiresIn = jwtConfig?.refreshExpiresIn ?? "7d";
97
- const issueTokens = (payload, options) => {
98
- if (!jwtContext) {
99
- throw new Error("JWT not configured. Provide auth.jwt.secret to use issueTokens.");
100
- }
101
- const accessTtl = options?.expiresIn ?? accessExpiresIn;
102
- const refreshTtl = options?.refreshExpiresIn ?? refreshExpiresIn;
103
- const accessToken = jwtContext.sign(payload, { expiresIn: accessTtl });
104
- const refreshPayload = payload.id ? { id: payload.id, type: "refresh" } : payload._id ? { id: payload._id, type: "refresh" } : { ...payload, type: "refresh" };
105
- let refreshToken;
106
- if (refreshSecret) {
107
- const fastifyWithJwt = fastify;
108
- refreshToken = fastifyWithJwt.jwt.sign(refreshPayload, {
109
- expiresIn: refreshTtl,
110
- // Use refresh secret if different from main secret
111
- ...refreshSecret !== jwtConfig?.secret ? { secret: refreshSecret } : {}
112
- });
113
- }
114
- return {
115
- accessToken,
116
- refreshToken,
117
- expiresIn: parseExpiresIn(accessTtl, 900),
118
- refreshExpiresIn: refreshToken ? parseExpiresIn(refreshTtl, 604800) : void 0,
119
- tokenType: "Bearer"
120
- };
121
- };
122
- const verifyRefreshToken = (token) => {
123
- if (!jwtContext) {
124
- throw new Error("JWT not configured. Provide auth.jwt.secret to use verifyRefreshToken.");
125
- }
126
- const fastifyWithJwt = fastify;
127
- return fastifyWithJwt.jwt.verify(token, {
128
- ...refreshSecret !== jwtConfig?.secret ? { secret: refreshSecret } : {}
129
- });
130
- };
131
- const authorize = (...allowedRoles) => {
132
- return async (request, reply) => {
133
- const user = request[userProperty];
134
- if (!user) {
135
- reply.code(401).send({
136
- success: false,
137
- error: "Unauthorized",
138
- message: "No user context"
139
- });
140
- return;
141
- }
142
- const userRoles = user.roles ?? [];
143
- if (allowedRoles.length === 1 && allowedRoles[0] === "*") {
144
- return;
145
- }
146
- const hasRole = allowedRoles.some((role) => userRoles.includes(role));
147
- if (!hasRole) {
148
- reply.code(403).send({
149
- success: false,
150
- error: "Forbidden",
151
- message: `Requires one of: ${allowedRoles.join(", ")}`
152
- });
153
- return;
154
- }
155
- };
156
- };
157
- const authHelpers = {
158
- jwt: jwtContext,
159
- issueTokens,
160
- verifyRefreshToken
161
- };
162
- fastify.decorate("authenticate", authenticate);
163
- fastify.decorate("authorize", authorize);
164
- fastify.decorate("auth", authHelpers);
165
- fastify.log.info(
166
- `Auth: Plugin registered (jwt=${!!jwtContext}, customAuth=${!!appAuthenticator})`
167
- );
168
- };
169
- var authPlugin_default = fp(authPlugin, {
170
- name: "arc-auth",
171
- fastify: "5.x"
172
- });
173
-
174
- export { authPlugin_default as authPlugin, authPlugin as authPluginFn };
@@ -1,11 +0,0 @@
1
- /**
2
- * Arc CLI - Docs Command
3
- *
4
- * Export OpenAPI specification from registered resources
5
- */
6
- declare function exportDocs(args: string[]): Promise<void>;
7
- declare const _default: {
8
- exportDocs: typeof exportDocs;
9
- };
10
-
11
- export { _default as default, exportDocs };