@classytic/arc 2.4.3 → 2.6.1
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.
- package/README.md +22 -6
- package/dist/{BaseController-CkM5dUh_.mjs → BaseController-AbbRx3e0.mjs} +5 -2
- package/dist/adapters/index.d.mts +2 -2
- package/dist/adapters/index.mjs +1 -1
- package/dist/{adapters-DTC4Ug66.mjs → adapters-CTn28N4y.mjs} +72 -11
- package/dist/audit/index.d.mts +1 -1
- package/dist/audit/index.mjs +11 -1
- package/dist/audit/mongodb.d.mts +1 -1
- package/dist/auth/index.d.mts +1 -1
- package/dist/auth/index.mjs +2 -2
- package/dist/cli/commands/init.mjs +12 -9
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +2 -2
- package/dist/{createApp-CBgVaFyh.mjs → createApp-Bol7DLUf.mjs} +404 -279
- package/dist/{defineResource-B22gcNvn.mjs → defineResource-bVKHjQzE.mjs} +116 -19
- package/dist/discovery/index.mjs +1 -1
- package/dist/docs/index.d.mts +1 -1
- package/dist/dynamic/index.d.mts +1 -1
- package/dist/dynamic/index.mjs +2 -2
- package/dist/{elevation-Ca_yveIO.d.mts → elevation-C_taLQrM.d.mts} +27 -1
- package/dist/{errorHandler-DMbGdzBG.mjs → errorHandler-r2595m8T.mjs} +1 -1
- package/dist/{errors-CPpvPHT0.d.mts → errors-CcVbl1-T.d.mts} +17 -1
- package/dist/{errors-rxhfP7Hf.mjs → errors-NoQKsbAT.mjs} +23 -1
- package/dist/{eventPlugin-iGrSEmwJ.d.mts → eventPlugin-DW45v4V5.d.mts} +30 -2
- package/dist/events/index.d.mts +2 -2
- package/dist/events/index.mjs +40 -10
- package/dist/factory/index.d.mts +44 -23
- package/dist/factory/index.mjs +136 -2
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.d.mts +3 -3
- package/dist/idempotency/mongodb.d.mts +1 -1
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-yhxyjqNb.d.mts → index-BIsZ_su5.d.mts} +4 -8
- package/dist/{index-BL8CaQih.d.mts → index-Cb3gtbg7.d.mts} +2 -2
- package/dist/{index-Diqcm14c.d.mts → index-NGZksqM5.d.mts} +30 -1
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +8 -7
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/mcp/index.d.mts +4 -2
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/{interface-DGmPxakH.d.mts → interface-DDW43OmS.d.mts} +194 -13
- package/dist/{mongodb-CUpYfxfD.d.mts → mongodb-kltrBPa1.d.mts} +10 -0
- package/dist/{mongodb-bga9AbkD.d.mts → mongodb-pMvOlR5_.d.mts} +1 -1
- package/dist/org/index.d.mts +1 -1
- package/dist/org/index.mjs +1 -1
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +2 -2
- package/dist/{permissions-Jk5x3sxz.mjs → permissions-C8ImI8gC.mjs} +44 -2
- package/dist/plugins/index.d.mts +1 -1
- package/dist/plugins/index.mjs +3 -3
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +1 -1
- package/dist/{presets-OMPaHMTY.mjs → presets-BMfdy34e.mjs} +2 -2
- package/dist/{redis-CQ5YxMC5.d.mts → redis-D0Qc-9EW.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/{resourceToTools-PMFE8HIv.mjs → resourceToTools-DH3c3e-T.mjs} +81 -7
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +2 -2
- package/dist/{sse-BkViJPlT.mjs → sse-BF7GR7IB.mjs} +1 -1
- package/dist/testing/index.d.mts +2 -2
- package/dist/testing/index.mjs +1 -1
- package/dist/types/index.d.mts +3 -3
- package/dist/types/index.mjs +23 -2
- package/dist/{types-C6TQjtdi.mjs → types-BhtYdxZU.mjs} +26 -1
- package/dist/{types-Dt0-AI6E.d.mts → types-D5hJ-k_3.d.mts} +189 -6
- package/dist/{types-BJmgxNbF.d.mts → types-D5rjsS_i.d.mts} +1 -1
- package/dist/utils/index.d.mts +2 -2
- package/dist/utils/index.mjs +1 -1
- package/package.json +6 -5
- package/skills/arc/SKILL.md +104 -4
- package/skills/arc/references/mcp.md +160 -2
- /package/dist/{interface-B4awm1RJ.d.mts → interface-gr-7qo9j.d.mts} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
-
import { n as PUBLIC_SCOPE } from "./types-
|
|
2
|
+
import { n as PUBLIC_SCOPE } from "./types-BhtYdxZU.mjs";
|
|
3
3
|
import Fastify from "fastify";
|
|
4
4
|
import qs from "qs";
|
|
5
5
|
//#region src/factory/presets.ts
|
|
@@ -120,6 +120,7 @@ const testingPreset = {
|
|
|
120
120
|
cors: false,
|
|
121
121
|
rateLimit: false,
|
|
122
122
|
underPressure: false,
|
|
123
|
+
arcPlugins: { gracefulShutdown: false },
|
|
123
124
|
sensible: true,
|
|
124
125
|
multipart: { limits: {
|
|
125
126
|
fileSize: 1024 * 1024,
|
|
@@ -181,49 +182,263 @@ function getPreset(name) {
|
|
|
181
182
|
}
|
|
182
183
|
}
|
|
183
184
|
//#endregion
|
|
184
|
-
//#region src/factory/
|
|
185
|
+
//#region src/factory/registerArcPlugins.ts
|
|
185
186
|
/**
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
* const primaryDb = await mongoose.connect(process.env.PRIMARY_DB);
|
|
213
|
-
* const analyticsDb = mongoose.createConnection(process.env.ANALYTICS_DB);
|
|
187
|
+
* Register Arc core plugin and event system.
|
|
188
|
+
* Returns loaded plugin modules for registerArcPlugins (avoids duplicate dynamic import).
|
|
189
|
+
*/
|
|
190
|
+
async function registerArcCore(fastify, config, trackPlugin) {
|
|
191
|
+
const { arcCorePlugin, requestIdPlugin, healthPlugin, gracefulShutdownPlugin } = await import("./plugins/index.mjs");
|
|
192
|
+
await fastify.register(arcCorePlugin, { emitEvents: config.arcPlugins?.emitEvents !== false });
|
|
193
|
+
trackPlugin("arc-core");
|
|
194
|
+
if (config.arcPlugins?.events !== false) {
|
|
195
|
+
const { default: eventPlugin } = await import("./eventPlugin-Ba00swHF.mjs").then((n) => n.n);
|
|
196
|
+
const eventOpts = typeof config.arcPlugins?.events === "object" ? config.arcPlugins.events : {};
|
|
197
|
+
await fastify.register(eventPlugin, {
|
|
198
|
+
...eventOpts,
|
|
199
|
+
transport: config.stores?.events
|
|
200
|
+
});
|
|
201
|
+
trackPlugin("arc-events", eventOpts);
|
|
202
|
+
fastify.log.debug(`Arc events plugin enabled (transport: ${fastify.events.transportName})`);
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
requestIdPlugin,
|
|
206
|
+
healthPlugin,
|
|
207
|
+
gracefulShutdownPlugin
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Register opt-in Arc plugins (requestId, health, gracefulShutdown,
|
|
212
|
+
* caching, queryCache, SSE, metrics, versioning).
|
|
214
213
|
*
|
|
215
|
-
*
|
|
216
|
-
|
|
217
|
-
|
|
214
|
+
* @param modules - Plugin modules loaded by registerArcCore (avoids re-importing)
|
|
215
|
+
*/
|
|
216
|
+
async function registerArcPlugins(fastify, config, trackPlugin, modules) {
|
|
217
|
+
const { requestIdPlugin, healthPlugin, gracefulShutdownPlugin } = modules;
|
|
218
|
+
if (config.arcPlugins?.requestId !== false) {
|
|
219
|
+
await fastify.register(requestIdPlugin);
|
|
220
|
+
trackPlugin("arc-request-id");
|
|
221
|
+
}
|
|
222
|
+
if (config.arcPlugins?.health !== false) {
|
|
223
|
+
await fastify.register(healthPlugin);
|
|
224
|
+
trackPlugin("arc-health");
|
|
225
|
+
}
|
|
226
|
+
if (config.arcPlugins?.gracefulShutdown !== false) {
|
|
227
|
+
await fastify.register(gracefulShutdownPlugin);
|
|
228
|
+
trackPlugin("arc-graceful-shutdown");
|
|
229
|
+
}
|
|
230
|
+
if (config.arcPlugins?.caching) {
|
|
231
|
+
const { default: cachingPlugin } = await import("./caching-BSXB-Xr7.mjs").then((n) => n.r);
|
|
232
|
+
const opts = config.arcPlugins.caching === true ? {} : config.arcPlugins.caching;
|
|
233
|
+
await fastify.register(cachingPlugin, opts);
|
|
234
|
+
trackPlugin("arc-caching", opts);
|
|
235
|
+
}
|
|
236
|
+
if (config.arcPlugins?.queryCache) {
|
|
237
|
+
const { queryCachePlugin } = await import("./queryCachePlugin-XtFplYO9.mjs").then((n) => n.n);
|
|
238
|
+
const opts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
|
|
239
|
+
const store = config.stores?.queryCache ?? new (await (import("./memory-BFAYkf8H.mjs").then((n) => n.n))).MemoryCacheStore();
|
|
240
|
+
await fastify.register(queryCachePlugin, {
|
|
241
|
+
store,
|
|
242
|
+
...opts
|
|
243
|
+
});
|
|
244
|
+
trackPlugin("arc-query-cache", opts);
|
|
245
|
+
}
|
|
246
|
+
if (config.arcPlugins?.sse) if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
|
|
247
|
+
else {
|
|
248
|
+
const { default: ssePlugin } = await import("./sse-BF7GR7IB.mjs").then((n) => n.r);
|
|
249
|
+
const opts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
|
|
250
|
+
await fastify.register(ssePlugin, opts);
|
|
251
|
+
trackPlugin("arc-sse", opts);
|
|
252
|
+
}
|
|
253
|
+
if (config.arcPlugins?.metrics) {
|
|
254
|
+
const { default: metricsPlugin } = await import("./metrics-Csh4nsvv.mjs").then((n) => n.r);
|
|
255
|
+
const opts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
|
|
256
|
+
await fastify.register(metricsPlugin, opts);
|
|
257
|
+
trackPlugin("arc-metrics", opts);
|
|
258
|
+
}
|
|
259
|
+
if (config.arcPlugins?.versioning) {
|
|
260
|
+
const { default: versioningPlugin } = await import("./versioning-BzfeHmhj.mjs").then((n) => n.r);
|
|
261
|
+
await fastify.register(versioningPlugin, config.arcPlugins.versioning);
|
|
262
|
+
trackPlugin("arc-versioning", config.arcPlugins.versioning);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/factory/registerAuth.ts
|
|
267
|
+
/**
|
|
268
|
+
* Decorate request.scope with PUBLIC_SCOPE default.
|
|
269
|
+
* Every request starts as public; auth hooks upgrade it.
|
|
270
|
+
*/
|
|
271
|
+
function decorateRequestScope(fastify) {
|
|
272
|
+
fastify.decorateRequest("scope", null);
|
|
273
|
+
fastify.addHook("onRequest", async (request) => {
|
|
274
|
+
if (!request.scope) request.scope = PUBLIC_SCOPE;
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Register the configured auth strategy (JWT, Better Auth, Custom, or Authenticator).
|
|
279
|
+
*/
|
|
280
|
+
async function registerAuth(fastify, config, trackPlugin) {
|
|
281
|
+
const authConfig = config.auth;
|
|
282
|
+
if (authConfig === false || !authConfig) {
|
|
283
|
+
fastify.log.debug("Authentication disabled");
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
switch (authConfig.type) {
|
|
287
|
+
case "betterAuth": {
|
|
288
|
+
const { plugin, openapi } = authConfig.betterAuth;
|
|
289
|
+
await fastify.register(plugin);
|
|
290
|
+
trackPlugin("auth-better-auth");
|
|
291
|
+
if (openapi && !fastify.arc.externalOpenApiPaths.includes(openapi)) fastify.arc.externalOpenApiPaths.push(openapi);
|
|
292
|
+
fastify.log.debug("Better Auth authentication enabled");
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
case "custom":
|
|
296
|
+
await fastify.register(authConfig.plugin);
|
|
297
|
+
trackPlugin("auth-custom");
|
|
298
|
+
fastify.log.debug("Custom authentication plugin enabled");
|
|
299
|
+
break;
|
|
300
|
+
case "authenticator": {
|
|
301
|
+
const { authenticate, optionalAuthenticate } = authConfig;
|
|
302
|
+
fastify.decorate("authenticate", async (request, reply) => {
|
|
303
|
+
await authenticate(request, reply);
|
|
304
|
+
});
|
|
305
|
+
if (!fastify.hasDecorator("optionalAuthenticate")) if (optionalAuthenticate) fastify.decorate("optionalAuthenticate", async (request, reply) => {
|
|
306
|
+
await optionalAuthenticate(request, reply);
|
|
307
|
+
});
|
|
308
|
+
else fastify.decorate("optionalAuthenticate", createOptionalAuthenticate(authenticate));
|
|
309
|
+
trackPlugin("auth-authenticator");
|
|
310
|
+
fastify.log.debug("Custom authenticator enabled");
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
case "jwt": {
|
|
314
|
+
const { authPlugin } = await import("./auth/index.mjs");
|
|
315
|
+
const { type: _, ...arcAuthOpts } = authConfig;
|
|
316
|
+
await fastify.register(authPlugin, arcAuthOpts);
|
|
317
|
+
trackPlugin("auth-jwt");
|
|
318
|
+
fastify.log.debug("Arc authentication plugin enabled");
|
|
319
|
+
break;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Register elevation plugin (opt-in, runs after auth).
|
|
325
|
+
*/
|
|
326
|
+
async function registerElevation(fastify, config, trackPlugin) {
|
|
327
|
+
if (!config.elevation) return;
|
|
328
|
+
const { elevationPlugin } = await import("./elevation-BEdACOLB.mjs").then((n) => n.r);
|
|
329
|
+
await fastify.register(elevationPlugin, config.elevation);
|
|
330
|
+
trackPlugin("arc-elevation", config.elevation);
|
|
331
|
+
fastify.log.debug("Elevation plugin enabled");
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Register error handler plugin (opt-out).
|
|
335
|
+
*/
|
|
336
|
+
async function registerErrorHandler(fastify, config, trackPlugin) {
|
|
337
|
+
if (config.errorHandler === false) return;
|
|
338
|
+
const { errorHandlerPlugin } = await import("./errorHandler-r2595m8T.mjs").then((n) => n.n);
|
|
339
|
+
const errorOpts = typeof config.errorHandler === "object" ? config.errorHandler : { includeStack: config.preset !== "production" };
|
|
340
|
+
await fastify.register(errorHandlerPlugin, errorOpts);
|
|
341
|
+
trackPlugin("arc-error-handler", errorOpts);
|
|
342
|
+
fastify.log.debug("Arc error handler enabled");
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* Create an optionalAuthenticate that wraps the main authenticate function.
|
|
346
|
+
* Intercepts 401/403 responses so unauthenticated requests proceed as public.
|
|
218
347
|
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
* });
|
|
348
|
+
* Uses a try/catch approach first; falls back to reply proxy only when
|
|
349
|
+
* the authenticator calls reply.code(401).send() instead of throwing.
|
|
222
350
|
*/
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
351
|
+
function createOptionalAuthenticate(authenticate) {
|
|
352
|
+
return async (request, reply) => {
|
|
353
|
+
let intercepted = false;
|
|
354
|
+
const proxyReply = new Proxy(reply, { get(target, prop) {
|
|
355
|
+
if (prop === "code") return (statusCode) => {
|
|
356
|
+
if (statusCode === 401 || statusCode === 403) {
|
|
357
|
+
intercepted = true;
|
|
358
|
+
return new Proxy(target, { get(_t, p) {
|
|
359
|
+
if (p === "send" || p === "type" || p === "header" || p === "headers") return () => proxyReply;
|
|
360
|
+
return Reflect.get(target, p, target);
|
|
361
|
+
} });
|
|
362
|
+
}
|
|
363
|
+
return target.code(statusCode);
|
|
364
|
+
};
|
|
365
|
+
if (prop === "send" && intercepted) return () => proxyReply;
|
|
366
|
+
if (prop === "sent") return intercepted ? false : target.sent;
|
|
367
|
+
return Reflect.get(target, prop, target);
|
|
368
|
+
} });
|
|
369
|
+
try {
|
|
370
|
+
await authenticate(request, proxyReply);
|
|
371
|
+
} catch {}
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
//#endregion
|
|
375
|
+
//#region src/factory/registerResources.ts
|
|
376
|
+
/** Register a single resource with descriptive error on failure. */
|
|
377
|
+
async function registerOne(parent, resource) {
|
|
378
|
+
const name = resource.name ?? "unknown";
|
|
379
|
+
try {
|
|
380
|
+
await parent.register(resource.toPlugin());
|
|
381
|
+
} catch (err) {
|
|
382
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
383
|
+
parent.log.error(`Failed to register resource "${name}": ${msg}`);
|
|
384
|
+
throw new Error(`Resource "${name}" failed to register: ${msg}. Check the resource definition, adapter, and permissions.`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Execute the full resource lifecycle:
|
|
389
|
+
* 1. plugins() — infra (DB, docs, webhooks)
|
|
390
|
+
* 2. bootstrap[] — domain init (singletons, event handlers)
|
|
391
|
+
* 3. resources[] — auto-discovered routes (split by prefix)
|
|
392
|
+
* 4. afterResources() — post-registration wiring
|
|
393
|
+
* 5. onReady/onClose — lifecycle hooks
|
|
394
|
+
*/
|
|
395
|
+
async function registerResources(fastify, config) {
|
|
396
|
+
if (config.plugins) {
|
|
397
|
+
await config.plugins(fastify);
|
|
398
|
+
fastify.log.debug("Custom plugins registered");
|
|
399
|
+
}
|
|
400
|
+
if (config.bootstrap?.length) {
|
|
401
|
+
for (const init of config.bootstrap) await init(fastify);
|
|
402
|
+
fastify.log.debug(`${config.bootstrap.length} bootstrap function(s) executed`);
|
|
403
|
+
}
|
|
404
|
+
if (config.resources?.length) {
|
|
405
|
+
const seen = /* @__PURE__ */ new Set();
|
|
406
|
+
for (const resource of config.resources) if (resource.name) {
|
|
407
|
+
if (seen.has(resource.name)) fastify.log.warn(`Duplicate resource name "${resource.name}" detected. This will cause route conflicts. Check your resources array and loadResources() output.`);
|
|
408
|
+
seen.add(resource.name);
|
|
409
|
+
}
|
|
410
|
+
const prefixed = [];
|
|
411
|
+
const root = [];
|
|
412
|
+
for (const resource of config.resources) if (resource.skipGlobalPrefix) root.push(resource);
|
|
413
|
+
else prefixed.push(resource);
|
|
414
|
+
for (const resource of root) await registerOne(fastify, resource);
|
|
415
|
+
if (prefixed.length) if (config.resourcePrefix) await fastify.register(async (scoped) => {
|
|
416
|
+
for (const resource of prefixed) await registerOne(scoped, resource);
|
|
417
|
+
}, { prefix: config.resourcePrefix });
|
|
418
|
+
else for (const resource of prefixed) await registerOne(fastify, resource);
|
|
419
|
+
const names = config.resources.map((r) => r.name ?? "?").join(", ");
|
|
420
|
+
const prefix = config.resourcePrefix ? ` (prefix: ${config.resourcePrefix})` : "";
|
|
421
|
+
fastify.log.info(`${config.resources.length} resource(s) registered${prefix}: ${names}`);
|
|
422
|
+
}
|
|
423
|
+
if (config.afterResources) {
|
|
424
|
+
await config.afterResources(fastify);
|
|
425
|
+
fastify.log.debug("afterResources hook executed");
|
|
426
|
+
}
|
|
427
|
+
if (config.onReady) {
|
|
428
|
+
const onReady = config.onReady;
|
|
429
|
+
fastify.addHook("onReady", async () => {
|
|
430
|
+
await onReady(fastify);
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
if (config.onClose) {
|
|
434
|
+
const onClose = config.onClose;
|
|
435
|
+
fastify.addHook("onClose", async () => {
|
|
436
|
+
await onClose(fastify);
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
//#endregion
|
|
441
|
+
//#region src/factory/registerSecurity.ts
|
|
227
442
|
const PLUGIN_REGISTRY = {
|
|
228
443
|
cors: {
|
|
229
444
|
package: "@fastify/cors",
|
|
@@ -256,6 +471,7 @@ const PLUGIN_REGISTRY = {
|
|
|
256
471
|
optional: true
|
|
257
472
|
}
|
|
258
473
|
};
|
|
474
|
+
/** Load a plugin from the registry with helpful error messages. */
|
|
259
475
|
async function loadPlugin(name, logger) {
|
|
260
476
|
const entry = PLUGIN_REGISTRY[name];
|
|
261
477
|
if (!entry) throw new Error(`Unknown plugin: ${name}`);
|
|
@@ -273,81 +489,10 @@ async function loadPlugin(name, logger) {
|
|
|
273
489
|
}
|
|
274
490
|
}
|
|
275
491
|
/**
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
* Security plugins are enabled by default (opt-out):
|
|
279
|
-
* - helmet (security headers)
|
|
280
|
-
* - cors (cross-origin requests)
|
|
281
|
-
* - rateLimit (DDoS protection)
|
|
282
|
-
* - underPressure (health monitoring)
|
|
283
|
-
*
|
|
284
|
-
* Note: Compression is not included due to known Fastify 5 issues.
|
|
285
|
-
* Use a reverse proxy (Nginx, Caddy) or CDN for compression.
|
|
286
|
-
*
|
|
287
|
-
* @param options - Application configuration
|
|
288
|
-
* @returns Configured Fastify instance
|
|
492
|
+
* Register security plugins (Helmet, CORS, Rate Limiting).
|
|
493
|
+
* All enabled by default — set to `false` to opt out.
|
|
289
494
|
*/
|
|
290
|
-
async function
|
|
291
|
-
if (options.debug !== void 0 && options.debug !== false) {
|
|
292
|
-
const { configureArcLogger } = await import("./logger-Dz3j1ItV.mjs").then((n) => n.r);
|
|
293
|
-
configureArcLogger({ debug: options.debug });
|
|
294
|
-
}
|
|
295
|
-
const authConfig = options.auth;
|
|
296
|
-
const isAuthDisabled = authConfig === false;
|
|
297
|
-
if (!isAuthDisabled && authConfig && authConfig.type === "jwt") {
|
|
298
|
-
if (!authConfig.jwt?.secret && !authConfig.authenticate) throw new Error("createApp: JWT secret required when Arc auth is enabled.\nProvide auth.jwt.secret, auth.authenticate, or set auth: false to disable.\nExample: auth: { type: 'jwt', jwt: { secret: process.env.JWT_SECRET } }");
|
|
299
|
-
}
|
|
300
|
-
const deferredWarnings = [];
|
|
301
|
-
if (options.runtime === "distributed") {
|
|
302
|
-
const MEMORY_NAMES = new Set(["memory", "memory-cache"]);
|
|
303
|
-
const missing = [];
|
|
304
|
-
const eventsTransport = options.stores?.events;
|
|
305
|
-
if (!eventsTransport || MEMORY_NAMES.has(eventsTransport.name)) missing.push("events transport");
|
|
306
|
-
if (options.arcPlugins?.caching) {
|
|
307
|
-
const cacheStore = options.stores?.cache;
|
|
308
|
-
if (!cacheStore || MEMORY_NAMES.has(cacheStore.name)) missing.push("cache store");
|
|
309
|
-
}
|
|
310
|
-
const idempotencyStore = options.stores?.idempotency;
|
|
311
|
-
if (idempotencyStore && MEMORY_NAMES.has(idempotencyStore.name)) missing.push("idempotency store (memory-backed in distributed mode)");
|
|
312
|
-
else if (!idempotencyStore) deferredWarnings.push("runtime: 'distributed' — no idempotency store configured. Write-path deduplication will be instance-local. If resources use the idempotency plugin, provide stores.idempotency with a Redis/MongoDB store.");
|
|
313
|
-
if (options.arcPlugins?.queryCache) {
|
|
314
|
-
const qcStore = options.stores?.queryCache;
|
|
315
|
-
if (!qcStore || MEMORY_NAMES.has(qcStore.name)) missing.push("queryCache store");
|
|
316
|
-
}
|
|
317
|
-
if (missing.length > 0) throw new Error(`[Arc] runtime: 'distributed' requires Redis/durable adapters.\nMissing: ${missing.join(", ")}.\nProvide Redis-backed stores or use runtime: 'memory' for development.`);
|
|
318
|
-
}
|
|
319
|
-
const config = {
|
|
320
|
-
...options.preset ? getPreset(options.preset) : {},
|
|
321
|
-
...options
|
|
322
|
-
};
|
|
323
|
-
const fastify = Fastify({
|
|
324
|
-
logger: config.logger ?? true,
|
|
325
|
-
trustProxy: config.trustProxy ?? false,
|
|
326
|
-
routerOptions: { querystringParser: (str) => qs.parse(str) },
|
|
327
|
-
ajv: { customOptions: {
|
|
328
|
-
coerceTypes: true,
|
|
329
|
-
useDefaults: true,
|
|
330
|
-
removeAdditional: false,
|
|
331
|
-
keywords: ["example", ...config.ajv?.keywords ?? []]
|
|
332
|
-
} }
|
|
333
|
-
});
|
|
334
|
-
for (const warning of deferredWarnings) fastify.log.warn(warning);
|
|
335
|
-
if (config.typeProvider === "typebox") try {
|
|
336
|
-
const { TypeBoxValidatorCompiler } = await import("@fastify/type-provider-typebox");
|
|
337
|
-
fastify.setValidatorCompiler(TypeBoxValidatorCompiler);
|
|
338
|
-
fastify.log.debug("TypeBox type provider enabled");
|
|
339
|
-
} catch {
|
|
340
|
-
fastify.log.warn("typeProvider: \"typebox\" requested but @fastify/type-provider-typebox is not installed. Install it with: npm install @sinclair/typebox @fastify/type-provider-typebox");
|
|
341
|
-
}
|
|
342
|
-
fastify.removeContentTypeParser("application/json");
|
|
343
|
-
fastify.addContentTypeParser("application/json", { parseAs: "string" }, (_req, body, done) => {
|
|
344
|
-
if (!body || body.length === 0) return done(null, void 0);
|
|
345
|
-
try {
|
|
346
|
-
done(null, JSON.parse(body));
|
|
347
|
-
} catch (err) {
|
|
348
|
-
done(err);
|
|
349
|
-
}
|
|
350
|
-
});
|
|
495
|
+
async function registerSecurityPlugins(fastify, config) {
|
|
351
496
|
if (config.helmet !== false) {
|
|
352
497
|
const helmet = await loadPlugin("helmet");
|
|
353
498
|
await fastify.register(helmet, config.helmet ?? {});
|
|
@@ -369,19 +514,22 @@ async function createApp(options) {
|
|
|
369
514
|
};
|
|
370
515
|
await fastify.register(rateLimit, rateLimitOpts);
|
|
371
516
|
if (!(typeof rateLimitOpts === "object" && "store" in rateLimitOpts)) {
|
|
372
|
-
if (config.runtime === "distributed") {
|
|
373
|
-
|
|
374
|
-
throw new Error("[Arc] runtime: 'distributed' with rate limiting requires a shared store.\nProvide rateLimit: { store: new RedisStore({ ... }) } or disable rate limiting: rateLimit: false");
|
|
375
|
-
} else if (config.preset === "production") fastify.log.warn("Rate limiting is using in-memory store. In multi-instance deployments, each instance tracks limits independently. Configure a Redis store for distributed rate limiting: rateLimit: { store: new RedisStore({ ... }) }");
|
|
517
|
+
if (config.runtime === "distributed") throw new Error("[Arc] runtime: 'distributed' with rate limiting requires a shared store.\nProvide rateLimit: { store: new RedisStore({ ... }) } or disable rate limiting: rateLimit: false");
|
|
518
|
+
else if (config.preset === "production") fastify.log.warn("Rate limiting is using in-memory store. In multi-instance deployments, each instance tracks limits independently. Configure a Redis store for distributed rate limiting.");
|
|
376
519
|
}
|
|
377
520
|
fastify.log.debug("Rate limiting enabled");
|
|
378
521
|
} else fastify.log.warn("Rate limiting disabled");
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Register performance and utility plugins (Under Pressure, Sensible, Multipart, Raw Body).
|
|
525
|
+
*/
|
|
526
|
+
async function registerUtilityPlugins(fastify, config) {
|
|
379
527
|
if (config.preset === "production") fastify.log.warn("Response compression is not enabled (Fastify 5 stream issues). Use a reverse proxy (Nginx, Caddy, Cloudflare) for gzip/brotli in production.");
|
|
380
528
|
if (config.underPressure !== false) {
|
|
381
529
|
const underPressure = await loadPlugin("underPressure");
|
|
382
530
|
await fastify.register(underPressure, config.underPressure ?? { exposeStatusRoute: true });
|
|
383
531
|
fastify.log.debug("Health monitoring (under-pressure) enabled");
|
|
384
|
-
}
|
|
532
|
+
}
|
|
385
533
|
if (config.sensible !== false) {
|
|
386
534
|
const sensible = await loadPlugin("sensible");
|
|
387
535
|
await fastify.register(sensible);
|
|
@@ -414,9 +562,135 @@ async function createApp(options) {
|
|
|
414
562
|
fastify.log.debug("Raw body parsing enabled");
|
|
415
563
|
}
|
|
416
564
|
}
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
565
|
+
}
|
|
566
|
+
//#endregion
|
|
567
|
+
//#region src/factory/createApp.ts
|
|
568
|
+
/**
|
|
569
|
+
* ArcFactory - Production-ready Fastify application factory
|
|
570
|
+
*
|
|
571
|
+
* Enforces security best practices by making plugins opt-out instead of opt-in.
|
|
572
|
+
* A developer must explicitly disable security features rather than forget to enable them.
|
|
573
|
+
*
|
|
574
|
+
* Note: Arc is database-agnostic. Connect your database separately and provide
|
|
575
|
+
* adapters when defining resources. This allows multiple databases, custom
|
|
576
|
+
* connection pooling, and full control over your data layer.
|
|
577
|
+
*
|
|
578
|
+
* @example
|
|
579
|
+
* // 1. Connect your database(s) separately
|
|
580
|
+
* import mongoose from 'mongoose';
|
|
581
|
+
* await mongoose.connect(process.env.MONGO_URI);
|
|
582
|
+
*
|
|
583
|
+
* // 2. Create Arc app with resources
|
|
584
|
+
* const app = await createApp({
|
|
585
|
+
* preset: 'production',
|
|
586
|
+
* auth: { type: 'jwt', jwt: { secret: process.env.JWT_SECRET } },
|
|
587
|
+
* cors: { origin: ['https://example.com'] },
|
|
588
|
+
* resources: [productResource, orderResource],
|
|
589
|
+
* });
|
|
590
|
+
*
|
|
591
|
+
* @example
|
|
592
|
+
* // Multiple databases example
|
|
593
|
+
* const primaryDb = await mongoose.connect(process.env.PRIMARY_DB);
|
|
594
|
+
* const analyticsDb = mongoose.createConnection(process.env.ANALYTICS_DB);
|
|
595
|
+
*
|
|
596
|
+
* const orderResource = defineResource({
|
|
597
|
+
* adapter: createMongooseAdapter({ model: OrderModel, repository: orderRepo }),
|
|
598
|
+
* });
|
|
599
|
+
*
|
|
600
|
+
* const analyticsResource = defineResource({
|
|
601
|
+
* adapter: createMongooseAdapter({ model: AnalyticsModel, repository: analyticsRepo }),
|
|
602
|
+
* });
|
|
603
|
+
*/
|
|
604
|
+
var createApp_exports = /* @__PURE__ */ __exportAll({
|
|
605
|
+
ArcFactory: () => ArcFactory,
|
|
606
|
+
createApp: () => createApp
|
|
607
|
+
});
|
|
608
|
+
const MEMORY_STORE_NAMES = new Set(["memory", "memory-cache"]);
|
|
609
|
+
function validateAuthOptions(options) {
|
|
610
|
+
const authConfig = options.auth;
|
|
611
|
+
if (authConfig === false || !authConfig) return;
|
|
612
|
+
if (authConfig.type === "jwt" && !authConfig.jwt?.secret && !authConfig.authenticate) throw new Error("createApp: JWT secret required when Arc auth is enabled.\nProvide auth.jwt.secret, auth.authenticate, or set auth: false to disable.\nExample: auth: { type: 'jwt', jwt: { secret: process.env.JWT_SECRET } }");
|
|
613
|
+
}
|
|
614
|
+
function validateDistributedRuntime(options) {
|
|
615
|
+
const deferredWarnings = [];
|
|
616
|
+
if (options.runtime !== "distributed") return deferredWarnings;
|
|
617
|
+
const missing = [];
|
|
618
|
+
const events = options.stores?.events;
|
|
619
|
+
if (!events || MEMORY_STORE_NAMES.has(events.name)) missing.push("events transport");
|
|
620
|
+
if (options.arcPlugins?.caching) {
|
|
621
|
+
const cache = options.stores?.cache;
|
|
622
|
+
if (!cache || MEMORY_STORE_NAMES.has(cache.name)) missing.push("cache store");
|
|
623
|
+
}
|
|
624
|
+
const idempotency = options.stores?.idempotency;
|
|
625
|
+
if (idempotency && MEMORY_STORE_NAMES.has(idempotency.name)) missing.push("idempotency store (memory-backed in distributed mode)");
|
|
626
|
+
else if (!idempotency) deferredWarnings.push("runtime: 'distributed' — no idempotency store configured. Write-path deduplication will be instance-local. If resources use the idempotency plugin, provide stores.idempotency with a Redis/MongoDB store.");
|
|
627
|
+
if (options.arcPlugins?.queryCache) {
|
|
628
|
+
const qc = options.stores?.queryCache;
|
|
629
|
+
if (!qc || MEMORY_STORE_NAMES.has(qc.name)) missing.push("queryCache store");
|
|
630
|
+
}
|
|
631
|
+
if (missing.length > 0) throw new Error(`[Arc] runtime: 'distributed' requires Redis/durable adapters.\nMissing: ${missing.join(", ")}.\nProvide Redis-backed stores or use runtime: 'memory' for development.`);
|
|
632
|
+
return deferredWarnings;
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Create a production-ready Fastify application with Arc framework.
|
|
636
|
+
*
|
|
637
|
+
* Boot order:
|
|
638
|
+
* ```
|
|
639
|
+
* 0. Logger, validation, preset merge
|
|
640
|
+
* 1. Create Fastify instance
|
|
641
|
+
* 2. Security plugins (Helmet, CORS, Rate Limit) — opt-out
|
|
642
|
+
* 3. Utility plugins (Under Pressure, Sensible, Multipart, Raw Body)
|
|
643
|
+
* 4. Arc core (fastify.arc, events)
|
|
644
|
+
* 5. Arc plugins (requestId, health, caching, SSE, metrics, versioning)
|
|
645
|
+
* 6. Auth (scope decoration, auth strategy, elevation, error handler)
|
|
646
|
+
* 7. plugins() — user infra (DB, docs, webhooks)
|
|
647
|
+
* 8. bootstrap[] — domain init (singletons, event handlers)
|
|
648
|
+
* 9. resources[] — auto-discovered routes (prefix + skipGlobalPrefix)
|
|
649
|
+
* 10. afterResources() — post-registration wiring
|
|
650
|
+
* 11. onReady/onClose — lifecycle hooks
|
|
651
|
+
* ```
|
|
652
|
+
*/
|
|
653
|
+
async function createApp(options) {
|
|
654
|
+
if (options.debug !== void 0 && options.debug !== false) {
|
|
655
|
+
const { configureArcLogger } = await import("./logger-Dz3j1ItV.mjs").then((n) => n.r);
|
|
656
|
+
configureArcLogger({ debug: options.debug });
|
|
657
|
+
}
|
|
658
|
+
validateAuthOptions(options);
|
|
659
|
+
const deferredWarnings = validateDistributedRuntime(options);
|
|
660
|
+
const config = {
|
|
661
|
+
...options.preset ? getPreset(options.preset) : {},
|
|
662
|
+
...options
|
|
663
|
+
};
|
|
664
|
+
const fastify = Fastify({
|
|
665
|
+
logger: config.logger ?? true,
|
|
666
|
+
trustProxy: config.trustProxy ?? false,
|
|
667
|
+
routerOptions: { querystringParser: (str) => qs.parse(str) },
|
|
668
|
+
ajv: { customOptions: {
|
|
669
|
+
coerceTypes: true,
|
|
670
|
+
useDefaults: true,
|
|
671
|
+
removeAdditional: false,
|
|
672
|
+
keywords: ["example", ...config.ajv?.keywords ?? []]
|
|
673
|
+
} }
|
|
674
|
+
});
|
|
675
|
+
for (const warning of deferredWarnings) fastify.log.warn(warning);
|
|
676
|
+
if (config.typeProvider === "typebox") try {
|
|
677
|
+
const { TypeBoxValidatorCompiler } = await import("@fastify/type-provider-typebox");
|
|
678
|
+
fastify.setValidatorCompiler(TypeBoxValidatorCompiler);
|
|
679
|
+
} catch {
|
|
680
|
+
fastify.log.warn("typeProvider: \"typebox\" requested but @fastify/type-provider-typebox is not installed.");
|
|
681
|
+
}
|
|
682
|
+
const sjp = await import("secure-json-parse");
|
|
683
|
+
fastify.removeContentTypeParser("application/json");
|
|
684
|
+
fastify.addContentTypeParser("application/json", { parseAs: "string" }, (_req, body, done) => {
|
|
685
|
+
if (!body || body.length === 0) return done(null, void 0);
|
|
686
|
+
try {
|
|
687
|
+
done(null, sjp.parse(body));
|
|
688
|
+
} catch (err) {
|
|
689
|
+
done(err);
|
|
690
|
+
}
|
|
691
|
+
});
|
|
692
|
+
await registerSecurityPlugins(fastify, config);
|
|
693
|
+
await registerUtilityPlugins(fastify, config);
|
|
420
694
|
const trackPlugin = (name, opts) => {
|
|
421
695
|
fastify.arc.plugins.set(name, {
|
|
422
696
|
name,
|
|
@@ -424,162 +698,13 @@ async function createApp(options) {
|
|
|
424
698
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
425
699
|
});
|
|
426
700
|
};
|
|
427
|
-
trackPlugin(
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
});
|
|
435
|
-
trackPlugin("arc-events", eventOpts);
|
|
436
|
-
fastify.log.debug(`Arc events plugin enabled (transport: ${fastify.events.transportName})`);
|
|
437
|
-
}
|
|
438
|
-
if (config.arcPlugins?.requestId !== false) {
|
|
439
|
-
await fastify.register(requestIdPlugin);
|
|
440
|
-
trackPlugin("arc-request-id");
|
|
441
|
-
fastify.log.debug("Arc requestId plugin enabled");
|
|
442
|
-
}
|
|
443
|
-
if (config.arcPlugins?.health !== false) {
|
|
444
|
-
await fastify.register(healthPlugin);
|
|
445
|
-
trackPlugin("arc-health");
|
|
446
|
-
fastify.log.debug("Arc health plugin enabled");
|
|
447
|
-
}
|
|
448
|
-
if (config.arcPlugins?.gracefulShutdown !== false) {
|
|
449
|
-
await fastify.register(gracefulShutdownPlugin);
|
|
450
|
-
trackPlugin("arc-graceful-shutdown");
|
|
451
|
-
fastify.log.debug("Arc gracefulShutdown plugin enabled");
|
|
452
|
-
}
|
|
453
|
-
if (config.arcPlugins?.caching) {
|
|
454
|
-
const { default: cachingPlugin } = await import("./caching-BSXB-Xr7.mjs").then((n) => n.r);
|
|
455
|
-
const cachingOpts = config.arcPlugins.caching === true ? {} : config.arcPlugins.caching;
|
|
456
|
-
await fastify.register(cachingPlugin, cachingOpts);
|
|
457
|
-
trackPlugin("arc-caching", cachingOpts);
|
|
458
|
-
fastify.log.debug("Arc caching plugin enabled");
|
|
459
|
-
}
|
|
460
|
-
if (config.arcPlugins?.queryCache) {
|
|
461
|
-
const { queryCachePlugin } = await import("./queryCachePlugin-XtFplYO9.mjs").then((n) => n.n);
|
|
462
|
-
const qcOpts = config.arcPlugins.queryCache === true ? {} : config.arcPlugins.queryCache;
|
|
463
|
-
const store = options.stores?.queryCache ?? new (await (import("./memory-BFAYkf8H.mjs").then((n) => n.n))).MemoryCacheStore();
|
|
464
|
-
await fastify.register(queryCachePlugin, {
|
|
465
|
-
store,
|
|
466
|
-
...qcOpts
|
|
467
|
-
});
|
|
468
|
-
trackPlugin("arc-query-cache", qcOpts);
|
|
469
|
-
fastify.log.debug("Arc queryCache plugin enabled");
|
|
470
|
-
}
|
|
471
|
-
if (config.arcPlugins?.sse) if (config.arcPlugins?.events === false) fastify.log.warn("SSE plugin requires events plugin (arcPlugins.events). SSE disabled.");
|
|
472
|
-
else {
|
|
473
|
-
const { default: ssePlugin } = await import("./sse-BkViJPlT.mjs").then((n) => n.r);
|
|
474
|
-
const sseOpts = config.arcPlugins.sse === true ? {} : config.arcPlugins.sse;
|
|
475
|
-
await fastify.register(ssePlugin, sseOpts);
|
|
476
|
-
trackPlugin("arc-sse", sseOpts);
|
|
477
|
-
fastify.log.debug("Arc SSE plugin enabled");
|
|
478
|
-
}
|
|
479
|
-
if (config.arcPlugins?.metrics) {
|
|
480
|
-
const { default: metricsPlugin } = await import("./metrics-Csh4nsvv.mjs").then((n) => n.r);
|
|
481
|
-
const metricsOpts = config.arcPlugins.metrics === true ? {} : config.arcPlugins.metrics;
|
|
482
|
-
await fastify.register(metricsPlugin, metricsOpts);
|
|
483
|
-
trackPlugin("arc-metrics", metricsOpts);
|
|
484
|
-
fastify.log.debug("Arc metrics plugin enabled");
|
|
485
|
-
}
|
|
486
|
-
if (config.arcPlugins?.versioning) {
|
|
487
|
-
const { default: versioningPlugin } = await import("./versioning-BzfeHmhj.mjs").then((n) => n.r);
|
|
488
|
-
await fastify.register(versioningPlugin, config.arcPlugins.versioning);
|
|
489
|
-
trackPlugin("arc-versioning", config.arcPlugins.versioning);
|
|
490
|
-
fastify.log.debug("Arc versioning plugin enabled");
|
|
491
|
-
}
|
|
492
|
-
fastify.decorateRequest("scope", null);
|
|
493
|
-
fastify.addHook("onRequest", async (request) => {
|
|
494
|
-
if (!request.scope) request.scope = PUBLIC_SCOPE;
|
|
495
|
-
});
|
|
496
|
-
if (isAuthDisabled) fastify.log.debug("Authentication disabled");
|
|
497
|
-
else if (authConfig) switch (authConfig.type) {
|
|
498
|
-
case "betterAuth": {
|
|
499
|
-
const { plugin, openapi } = authConfig.betterAuth;
|
|
500
|
-
await fastify.register(plugin);
|
|
501
|
-
trackPlugin("auth-better-auth");
|
|
502
|
-
if (openapi && !fastify.arc.externalOpenApiPaths.includes(openapi)) fastify.arc.externalOpenApiPaths.push(openapi);
|
|
503
|
-
fastify.log.debug("Better Auth authentication enabled");
|
|
504
|
-
break;
|
|
505
|
-
}
|
|
506
|
-
case "custom":
|
|
507
|
-
await fastify.register(authConfig.plugin);
|
|
508
|
-
trackPlugin("auth-custom");
|
|
509
|
-
fastify.log.debug("Custom authentication plugin enabled");
|
|
510
|
-
break;
|
|
511
|
-
case "authenticator": {
|
|
512
|
-
const { authenticate, optionalAuthenticate } = authConfig;
|
|
513
|
-
fastify.decorate("authenticate", async (request, reply) => {
|
|
514
|
-
await authenticate(request, reply);
|
|
515
|
-
});
|
|
516
|
-
if (!fastify.hasDecorator("optionalAuthenticate")) if (optionalAuthenticate) fastify.decorate("optionalAuthenticate", async (request, reply) => {
|
|
517
|
-
await optionalAuthenticate(request, reply);
|
|
518
|
-
});
|
|
519
|
-
else fastify.decorate("optionalAuthenticate", async (request, reply) => {
|
|
520
|
-
let intercepted = false;
|
|
521
|
-
const proxyReply = new Proxy(reply, { get(target, prop) {
|
|
522
|
-
if (prop === "code") return (statusCode) => {
|
|
523
|
-
if (statusCode === 401 || statusCode === 403) {
|
|
524
|
-
intercepted = true;
|
|
525
|
-
return new Proxy(target, { get(_t, p) {
|
|
526
|
-
if (p === "send" || p === "type" || p === "header" || p === "headers") return () => proxyReply;
|
|
527
|
-
return Reflect.get(target, p, target);
|
|
528
|
-
} });
|
|
529
|
-
}
|
|
530
|
-
return target.code(statusCode);
|
|
531
|
-
};
|
|
532
|
-
if (prop === "send" && intercepted) return () => proxyReply;
|
|
533
|
-
if (prop === "sent") return intercepted ? false : target.sent;
|
|
534
|
-
return Reflect.get(target, prop, target);
|
|
535
|
-
} });
|
|
536
|
-
try {
|
|
537
|
-
await authenticate(request, proxyReply);
|
|
538
|
-
} catch {}
|
|
539
|
-
});
|
|
540
|
-
trackPlugin("auth-authenticator");
|
|
541
|
-
fastify.log.debug("Custom authenticator enabled");
|
|
542
|
-
break;
|
|
543
|
-
}
|
|
544
|
-
case "jwt": {
|
|
545
|
-
const { authPlugin } = await import("./auth/index.mjs");
|
|
546
|
-
const { type: _, ...arcAuthOpts } = authConfig;
|
|
547
|
-
await fastify.register(authPlugin, arcAuthOpts);
|
|
548
|
-
trackPlugin("auth-jwt");
|
|
549
|
-
fastify.log.debug("Arc authentication plugin enabled");
|
|
550
|
-
break;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
if (config.elevation) {
|
|
554
|
-
const { elevationPlugin } = await import("./elevation-BEdACOLB.mjs").then((n) => n.r);
|
|
555
|
-
await fastify.register(elevationPlugin, config.elevation);
|
|
556
|
-
trackPlugin("arc-elevation", config.elevation);
|
|
557
|
-
fastify.log.debug("Elevation plugin enabled");
|
|
558
|
-
}
|
|
559
|
-
if (config.errorHandler !== false) {
|
|
560
|
-
const { errorHandlerPlugin } = await import("./errorHandler-DMbGdzBG.mjs").then((n) => n.n);
|
|
561
|
-
const errorOpts = typeof config.errorHandler === "object" ? config.errorHandler : { includeStack: config.preset !== "production" };
|
|
562
|
-
await fastify.register(errorHandlerPlugin, errorOpts);
|
|
563
|
-
trackPlugin("arc-error-handler", errorOpts);
|
|
564
|
-
fastify.log.debug("Arc error handler enabled");
|
|
565
|
-
}
|
|
566
|
-
if (config.plugins) {
|
|
567
|
-
await config.plugins(fastify);
|
|
568
|
-
fastify.log.debug("Custom plugins registered");
|
|
569
|
-
}
|
|
570
|
-
if (config.onReady) {
|
|
571
|
-
const onReady = config.onReady;
|
|
572
|
-
fastify.addHook("onReady", async () => {
|
|
573
|
-
await onReady(fastify);
|
|
574
|
-
});
|
|
575
|
-
}
|
|
576
|
-
if (config.onClose) {
|
|
577
|
-
const onClose = config.onClose;
|
|
578
|
-
fastify.addHook("onClose", async () => {
|
|
579
|
-
await onClose(fastify);
|
|
580
|
-
});
|
|
581
|
-
}
|
|
582
|
-
const authMode = isAuthDisabled ? "none" : authConfig ? authConfig.type : "none";
|
|
701
|
+
await registerArcPlugins(fastify, config, trackPlugin, await registerArcCore(fastify, config, trackPlugin));
|
|
702
|
+
decorateRequestScope(fastify);
|
|
703
|
+
await registerAuth(fastify, config, trackPlugin);
|
|
704
|
+
await registerElevation(fastify, config, trackPlugin);
|
|
705
|
+
await registerErrorHandler(fastify, config, trackPlugin);
|
|
706
|
+
await registerResources(fastify, config);
|
|
707
|
+
const authMode = config.auth === false ? "none" : config.auth ? config.auth.type : "none";
|
|
583
708
|
fastify.log.info({
|
|
584
709
|
preset: config.preset ?? "custom",
|
|
585
710
|
runtime: config.runtime ?? "memory",
|
|
@@ -614,4 +739,4 @@ const ArcFactory = {
|
|
|
614
739
|
}
|
|
615
740
|
};
|
|
616
741
|
//#endregion
|
|
617
|
-
export {
|
|
742
|
+
export { edgePreset as a, testingPreset as c, developmentPreset as i, createApp as n, getPreset as o, createApp_exports as r, productionPreset as s, ArcFactory as t };
|