@classytic/arc 2.21.0 → 2.22.0
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/dist/{BaseController-BnLYgE9A.mjs → BaseController-C6FlRWxF.mjs} +1 -1
- package/dist/audit/index.d.mts +2 -2
- package/dist/auth/index.d.mts +30 -2
- package/dist/auth/index.mjs +5 -2
- package/dist/cli/commands/describe.d.mts +1 -1
- package/dist/cli/commands/docs.mjs +1 -1
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +2 -2
- package/dist/{core-DZ6Xe1jR.mjs → core-b6fgFgnd.mjs} +123 -66
- package/dist/{createActionRouter-BWMDW-dL.mjs → createActionRouter-h1cEEaEX.mjs} +9 -5
- package/dist/{createAggregationRouter-l5mNdYrZ.mjs → createAggregationRouter-B1C0kvTL.mjs} +3 -3
- package/dist/{createApp-Bi61XxqU.mjs → createApp-Ci7yG5gv.mjs} +242 -56
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +1 -1
- package/dist/encryption/index.mjs +9 -5
- package/dist/{errorHandler-Dxf6P_kV.mjs → errorHandler-BTzXSwIg.mjs} +24 -3
- package/dist/{errorHandler-B0e-bq-O.d.mts → errorHandler-C6yv4ZTW.d.mts} +13 -0
- package/dist/factory/index.d.mts +29 -8
- package/dist/factory/index.mjs +34 -3
- package/dist/{fastify-augmentation-DafigGQp.d.mts → fastify-augmentation-9_fuetB4.d.mts} +2 -2
- package/dist/fields-D3lhgzhD.d.mts +121 -0
- package/dist/health-ScyjIVo5.mjs +202 -0
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.mjs +2 -0
- package/dist/{types-DpxoNX5v.d.mts → index-BEu4wjbL.d.mts} +187 -66
- package/dist/{index-B5N-NaHs.d.mts → index-D8MKYGDa.d.mts} +3 -3
- package/dist/{index-Dtrj6XZk.d.mts → index-DbtgCOCj.d.mts} +1287 -1225
- package/dist/{index-B5PoNbfu.d.mts → index-ogIHvOjo.d.mts} +1 -1
- package/dist/index.d.mts +6 -5
- package/dist/index.mjs +3 -3
- package/dist/{init-0YhHkXGk.mjs → init-DleK3p9f.mjs} +118 -2
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/mcp/index.d.mts +3 -3
- 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/integrations/webhooks.d.mts +8 -4
- package/dist/integrations/webhooks.mjs +9 -5
- package/dist/jsonBody-BlzAoGCe.mjs +43 -0
- package/dist/logger/index.d.mts +26 -1
- package/dist/logger/index.mjs +39 -1
- package/dist/{mcpPlugin-B9XjjNVG.mjs → mcpPlugin-ByA2tf6D.mjs} +6 -2
- package/dist/middleware/index.d.mts +1 -1
- package/dist/{module-WeuU5jfd.d.mts → module-B4u10iOJ.d.mts} +26 -1
- package/dist/{openapi-Kyod_di6.mjs → openapi-BRpMZD7C.mjs} +2 -2
- package/dist/permissions/index.d.mts +3 -2
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/plugins/index.d.mts +22 -5
- package/dist/plugins/index.mjs +11 -280
- package/dist/plugins/response-cache.mjs +1 -0
- package/dist/plugins/tracing-entry.mjs +7 -4
- package/dist/presets/filesUpload.d.mts +2 -2
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/search.d.mts +2 -2
- package/dist/rateLimitKey-BoWRH3e7.mjs +14 -0
- package/dist/{registerRoute-oGAl0oA8.mjs → registerRoute-xHc42fwB.mjs} +37 -17
- package/dist/registry/index.d.mts +2 -2
- package/dist/requestId-5BhTVzBv.mjs +113 -0
- package/dist/schemaConverter-Ds5reSGs.mjs +168 -0
- package/dist/{schemaIR-ajetw3Fv.mjs → schemaIR-B-6SPe7g.mjs} +2 -2
- package/dist/scim/index.mjs +6 -7
- package/dist/scope/index.mjs +2 -76
- package/dist/scope-k15RXfcq.mjs +64 -0
- package/dist/testing/index.d.mts +83 -3
- package/dist/testing/index.mjs +70 -3
- package/dist/types/index.d.mts +2 -2
- package/dist/{types-DRj-Ut4n.d.mts → types-Bc42CUyk.d.mts} +1 -1
- package/dist/{fields-BGqrAa_u.d.mts → types-Csxga7d3.d.mts} +1 -121
- package/dist/usage/index.d.mts +142 -0
- package/dist/usage/index.mjs +0 -0
- package/dist/utils/index.d.mts +40 -18
- package/dist/utils/index.mjs +2 -2
- package/dist/{utils-CO8a3B_1.mjs → utils-FhJ88TJE.mjs} +59 -13
- package/dist/{versioning-DQ4mCvg0.d.mts → versioning-DLwklPlG.d.mts} +0 -5
- package/llms.txt +39 -0
- package/package.json +453 -447
- package/skills/arc/SKILL.md +14 -8
- package/skills/arc/references/api-reference.md +34 -0
- package/skills/arc/references/auth.md +21 -4
- package/skills/arc/references/mcp.md +1 -1
- package/skills/arc/references/production.md +44 -2
- package/skills/arc-code-review/SKILL.md +1 -1
- package/skills/arc-code-review/references/anti-patterns.md +71 -18
- package/skills/arc-code-review/references/arc-cheatsheet.md +15 -2
- package/skills/arc-code-review/references/migration-recipes.md +7 -7
- package/skills/arc-code-review/references/mongokit-migration.md +4 -3
- package/skills/arc-module-publishing/SKILL.md +2 -1
- package/skills/arc-module-publishing/references/authoring-modules.md +55 -0
- package/skills/arc-module-publishing/references/package-manifest.md +4 -2
- package/dist/preloadResources-CHw3_oA_.mjs +0 -45
- package/dist/schemaConverter-U0MnO9vd.mjs +0 -110
|
@@ -1,8 +1,55 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.mjs";
|
|
2
|
-
import { arcLog } from "./logger/index.mjs";
|
|
2
|
+
import { arcLog, configureArcLogger, createPinoWriter } from "./logger/index.mjs";
|
|
3
3
|
import { n as PUBLIC_SCOPE } from "./types-bHblZ9YN.mjs";
|
|
4
|
+
import { t as createTenantKeyGenerator } from "./rateLimitKey-BoWRH3e7.mjs";
|
|
5
|
+
import { t as createRequestIdGenerator } from "./requestId-5BhTVzBv.mjs";
|
|
6
|
+
import { t as parseJsonBody } from "./jsonBody-BlzAoGCe.mjs";
|
|
4
7
|
import Fastify from "fastify";
|
|
5
8
|
import qs from "qs";
|
|
9
|
+
//#region src/testing/preloadResources.ts
|
|
10
|
+
/**
|
|
11
|
+
* Normalize an eager `import.meta.glob` result into a `ResourceLike[]`.
|
|
12
|
+
*
|
|
13
|
+
* Accepts either:
|
|
14
|
+
* - `{ import: 'default' }` form: values are the resource directly
|
|
15
|
+
* - default form: values are the full module — picks first export with `toPlugin()`
|
|
16
|
+
*
|
|
17
|
+
* Throws if any module doesn't yield a valid `ResourceLike`.
|
|
18
|
+
*/
|
|
19
|
+
function preloadResources(globResult) {
|
|
20
|
+
const resources = [];
|
|
21
|
+
for (const [path, value] of Object.entries(globResult)) {
|
|
22
|
+
const resource = pickResource(value);
|
|
23
|
+
if (!resource) throw new Error(`preloadResources: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
|
|
24
|
+
resources.push(resource);
|
|
25
|
+
}
|
|
26
|
+
return resources.sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Normalize a lazy `import.meta.glob` result into a `Promise<ResourceLike[]>`.
|
|
30
|
+
*
|
|
31
|
+
* Use this when resources depend on prior bootstrap (e.g., engine init) and
|
|
32
|
+
* cannot be evaluated at import time of the preload file.
|
|
33
|
+
*/
|
|
34
|
+
async function preloadResourcesAsync(globResult) {
|
|
35
|
+
return (await Promise.all(Object.entries(globResult).map(async ([path, loader]) => {
|
|
36
|
+
const resource = pickResource(await loader());
|
|
37
|
+
if (!resource) throw new Error(`preloadResourcesAsync: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
|
|
38
|
+
return resource;
|
|
39
|
+
}))).sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
40
|
+
}
|
|
41
|
+
function pickResource(value) {
|
|
42
|
+
if (!value || typeof value !== "object") return void 0;
|
|
43
|
+
if (typeof value.toPlugin === "function") return value;
|
|
44
|
+
const mod = value;
|
|
45
|
+
const candidates = [
|
|
46
|
+
mod.default,
|
|
47
|
+
mod.resource,
|
|
48
|
+
...Object.values(mod)
|
|
49
|
+
];
|
|
50
|
+
for (const c of candidates) if (c && typeof c === "object" && typeof c.toPlugin === "function") return c;
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
6
53
|
//#region src/factory/module.ts
|
|
7
54
|
/**
|
|
8
55
|
* Resolve any module-input form to a concrete `ArcModule`. A thunk is invoked
|
|
@@ -310,8 +357,33 @@ const edgePreset = {
|
|
|
310
357
|
/**
|
|
311
358
|
* Get preset by name
|
|
312
359
|
*/
|
|
360
|
+
/**
|
|
361
|
+
* Worker preset (2.23) — the headless process role. Flips the HTTP surface
|
|
362
|
+
* off and enables the `mountRoutes: false` primitive; runtime tiers
|
|
363
|
+
* (events, jobs, schedules, caching, audit, usage) stay AS CONFIGURED by
|
|
364
|
+
* the host's options. Per the preset contract, explicit narrow settings
|
|
365
|
+
* win — `arcPlugins: { metrics: true }` re-enables Prometheus on workers,
|
|
366
|
+
* `auth: {...}` keeps auth. See wiki/factory.md § createWorker.
|
|
367
|
+
*/
|
|
368
|
+
const workerPreset = {
|
|
369
|
+
mountRoutes: false,
|
|
370
|
+
auth: false,
|
|
371
|
+
helmet: false,
|
|
372
|
+
cors: false,
|
|
373
|
+
rateLimit: false,
|
|
374
|
+
underPressure: false,
|
|
375
|
+
sensible: false,
|
|
376
|
+
arcPlugins: {
|
|
377
|
+
requestId: false,
|
|
378
|
+
health: false,
|
|
379
|
+
sse: false,
|
|
380
|
+
metrics: false,
|
|
381
|
+
gracefulShutdown: true
|
|
382
|
+
}
|
|
383
|
+
};
|
|
313
384
|
function getPreset(name) {
|
|
314
385
|
switch (name) {
|
|
386
|
+
case "worker": return workerPreset;
|
|
315
387
|
case "production": return productionPreset;
|
|
316
388
|
case "development": return developmentPreset;
|
|
317
389
|
case "testing": return testingPreset;
|
|
@@ -424,7 +496,7 @@ async function registerAuth(fastify, config, trackPlugin) {
|
|
|
424
496
|
}
|
|
425
497
|
switch (authConfig.type) {
|
|
426
498
|
case "betterAuth": {
|
|
427
|
-
const { plugin, openapi } = authConfig.betterAuth;
|
|
499
|
+
const { plugin, openapi } = typeof authConfig.betterAuth === "function" ? await authConfig.betterAuth() : authConfig.betterAuth;
|
|
428
500
|
await fastify.register(plugin);
|
|
429
501
|
trackPlugin("auth-better-auth");
|
|
430
502
|
const arc = fastify.arc;
|
|
@@ -480,7 +552,7 @@ async function registerElevation(fastify, config, trackPlugin) {
|
|
|
480
552
|
*/
|
|
481
553
|
async function registerErrorHandler(fastify, config, trackPlugin, moduleErrorMappers) {
|
|
482
554
|
if (config.errorHandler === false) return;
|
|
483
|
-
const { errorHandlerPlugin } = await import("./errorHandler-
|
|
555
|
+
const { errorHandlerPlugin } = await import("./errorHandler-BTzXSwIg.mjs").then((n) => n.r);
|
|
484
556
|
let errorOpts = typeof config.errorHandler === "object" ? config.errorHandler : { includeStack: config.preset !== "production" };
|
|
485
557
|
if (moduleErrorMappers && moduleErrorMappers.length > 0) errorOpts = {
|
|
486
558
|
...errorOpts,
|
|
@@ -526,10 +598,10 @@ function isResourcesFactory(value) {
|
|
|
526
598
|
return typeof value === "function";
|
|
527
599
|
}
|
|
528
600
|
/** Register a single resource with descriptive error on failure. */
|
|
529
|
-
async function registerOne(parent, resource) {
|
|
601
|
+
async function registerOne(parent, resource, mountRoutes) {
|
|
530
602
|
const name = resource.name ?? "unknown";
|
|
531
603
|
try {
|
|
532
|
-
await parent.register(resource.toPlugin());
|
|
604
|
+
await parent.register(resource.toPlugin(), mountRoutes === false ? { arcMountRoutes: false } : {});
|
|
533
605
|
} catch (err) {
|
|
534
606
|
const rawMsg = err instanceof Error ? err.message : String(err);
|
|
535
607
|
const stripped = rawMsg.replace(new RegExp(`^Resource "${name}"\\s*`), "").replace(/\.+\s*$/, "");
|
|
@@ -640,6 +712,16 @@ async function registerResources(fastify, config, preResolvedModules) {
|
|
|
640
712
|
}
|
|
641
713
|
resolvedResources = discovered;
|
|
642
714
|
}
|
|
715
|
+
if (modules.length && resolvedResources && resolvedResources.length > 0) {
|
|
716
|
+
const owned = /* @__PURE__ */ new Set();
|
|
717
|
+
for (const m of modules) for (const name of m.owns ?? []) owned.add(name);
|
|
718
|
+
if (owned.size > 0) {
|
|
719
|
+
const before = resolvedResources.length;
|
|
720
|
+
resolvedResources = resolvedResources.filter((r) => !(r.name != null && owned.has(r.name)));
|
|
721
|
+
const dropped = before - resolvedResources.length;
|
|
722
|
+
if (dropped > 0) fastify.log.debug(`[arc] ${dropped} app resource(s) superseded by module owns() — the module version registers instead`);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
643
725
|
if (modules.length) {
|
|
644
726
|
const moduleResources = [];
|
|
645
727
|
for (const m of modules) {
|
|
@@ -668,11 +750,11 @@ async function registerResources(fastify, config, preResolvedModules) {
|
|
|
668
750
|
const root = [];
|
|
669
751
|
for (const resource of resolvedResources) if (resource.skipGlobalPrefix) root.push(resource);
|
|
670
752
|
else prefixed.push(resource);
|
|
671
|
-
for (const resource of root) await registerOne(fastify, resource);
|
|
753
|
+
for (const resource of root) await registerOne(fastify, resource, config.mountRoutes);
|
|
672
754
|
if (prefixed.length) if (config.resourcePrefix) await fastify.register(async (scoped) => {
|
|
673
|
-
for (const resource of prefixed) await registerOne(scoped, resource);
|
|
755
|
+
for (const resource of prefixed) await registerOne(scoped, resource, config.mountRoutes);
|
|
674
756
|
}, { prefix: config.resourcePrefix });
|
|
675
|
-
else for (const resource of prefixed) await registerOne(fastify, resource);
|
|
757
|
+
else for (const resource of prefixed) await registerOne(fastify, resource, config.mountRoutes);
|
|
676
758
|
const names = resolvedResources.map((r) => r.name ?? "?").join(", ");
|
|
677
759
|
const prefix = config.resourcePrefix ? ` (prefix: ${config.resourcePrefix})` : "";
|
|
678
760
|
fastify.log.info(`${resolvedResources.length} resource(s) registered${prefix}: ${names}`);
|
|
@@ -709,42 +791,69 @@ async function registerResources(fastify, config, preResolvedModules) {
|
|
|
709
791
|
});
|
|
710
792
|
}
|
|
711
793
|
//#endregion
|
|
712
|
-
//#region src/factory/
|
|
794
|
+
//#region src/factory/security/cors.ts
|
|
713
795
|
/**
|
|
714
|
-
*
|
|
715
|
-
*
|
|
716
|
-
*
|
|
796
|
+
* Headers ARC ITSELF reads from cross-origin requests. Auto-merged into a
|
|
797
|
+
* host-declared CORS `allowedHeaders` allow-list (2.22) so hosts never
|
|
798
|
+
* enumerate framework internals. Lowercase — header names are
|
|
799
|
+
* case-insensitive and @fastify/cors joins them verbatim.
|
|
717
800
|
*/
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
801
|
+
const ARC_PROTOCOL_HEADERS = [
|
|
802
|
+
"x-organization-id",
|
|
803
|
+
"x-arc-scope",
|
|
804
|
+
"x-request-id"
|
|
805
|
+
];
|
|
806
|
+
/**
|
|
807
|
+
* Response headers ARC'S OWN auth protocol emits that cross-origin JS MUST
|
|
808
|
+
* be able to read. Better Auth's bearer plugin returns the session token in
|
|
809
|
+
* `set-auth-token` — without exposing it, cross-origin clients (Capacitor /
|
|
810
|
+
* Ionic WebViews at `https://localhost`, cross-domain SPAs) complete the
|
|
811
|
+
* login POST but can never READ the token, so bearer auth silently dies
|
|
812
|
+
* (login appears to succeed, then every request is unauthenticated).
|
|
813
|
+
* Unlike `allowedHeaders`, an UNSET `exposedHeaders` is NOT safe — CORS
|
|
814
|
+
* exposes nothing beyond the safelist by default — so this is always
|
|
815
|
+
* merged, creating the list when the host declared none.
|
|
816
|
+
*/
|
|
817
|
+
const ARC_EXPOSED_HEADERS = ["set-auth-token"];
|
|
818
|
+
/**
|
|
819
|
+
* Default `Access-Control-Max-Age` (seconds) when the host didn't set one.
|
|
820
|
+
* Arc's CORS method/header lists are static per deploy, so letting browsers
|
|
821
|
+
* cache the preflight kills the extra OPTIONS round-trip on every request —
|
|
822
|
+
* on high-latency links (mobile webviews) that's a visible chunk of every
|
|
823
|
+
* API call. Browsers clamp internally (Chromium 2h, Firefox 24h).
|
|
824
|
+
*/
|
|
825
|
+
const DEFAULT_CORS_MAX_AGE = 86400;
|
|
826
|
+
/**
|
|
827
|
+
* Resolve the host's CORS config into `@fastify/cors` options + warnings.
|
|
828
|
+
* Throws on the one configuration arc refuses to ship (see module header).
|
|
829
|
+
* Callers decide only HOW to surface warnings — the policy is all here.
|
|
830
|
+
*/
|
|
831
|
+
function resolveCorsOptions(config) {
|
|
832
|
+
const corsOptions = { ...config.cors ?? {} };
|
|
833
|
+
const warnings = [];
|
|
834
|
+
const originDeclared = "origin" in corsOptions && corsOptions.origin !== void 0;
|
|
835
|
+
if (config.preset === "production" && !originDeclared) warnings.push("CORS origin is not explicitly configured in production. Browser apps: set cors.origin to allowed domains (e.g. ['https://app.example.com']) with credentials: true. Server-to-server / API-key services: cors: { origin: '*', credentials: false } OR cors: false to disable. Tip: when wiring cors.origin from an env var, fail fast on missing (`if (!process.env.ALLOWED_ORIGINS) throw ...`) instead of letting `undefined` slip through.");
|
|
836
|
+
if (corsOptions.credentials === true && corsOptions.origin === "*") throw new Error("[Arc] cors: `origin: '*'` cannot be combined with `credentials: true`.\nReflecting arbitrary origins with credentials lets any website read authenticated responses (CSRF-adjacent data theft).\nFix one of:\n • cors: { origin: ['https://app.example.com'], credentials: true } — explicit allow-list\n • cors: { origin: /\\.example\\.com$/, credentials: true } — pattern allow-list\n • cors: { origin: '*', credentials: false } — public, uncredentialed API");
|
|
837
|
+
if (Array.isArray(corsOptions.allowedHeaders)) {
|
|
838
|
+
const allowed = [...corsOptions.allowedHeaders];
|
|
839
|
+
const declared = new Set(allowed.map((h) => h.toLowerCase()));
|
|
840
|
+
for (const arcHeader of ARC_PROTOCOL_HEADERS) if (!declared.has(arcHeader)) allowed.push(arcHeader);
|
|
841
|
+
corsOptions.allowedHeaders = allowed;
|
|
842
|
+
}
|
|
843
|
+
{
|
|
844
|
+
const exposed = Array.isArray(corsOptions.exposedHeaders) ? [...corsOptions.exposedHeaders] : typeof corsOptions.exposedHeaders === "string" ? corsOptions.exposedHeaders.split(",").map((h) => h.trim()) : [];
|
|
845
|
+
const declared = new Set(exposed.map((h) => h.toLowerCase()));
|
|
846
|
+
for (const arcHeader of ARC_EXPOSED_HEADERS) if (!declared.has(arcHeader)) exposed.push(arcHeader);
|
|
847
|
+
corsOptions.exposedHeaders = exposed;
|
|
848
|
+
}
|
|
849
|
+
if (corsOptions.maxAge === void 0) corsOptions.maxAge = DEFAULT_CORS_MAX_AGE;
|
|
732
850
|
return {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
};
|
|
736
|
-
}
|
|
737
|
-
function compilePathMatcher(patterns) {
|
|
738
|
-
const prefixes = [];
|
|
739
|
-
const exact = /* @__PURE__ */ new Set();
|
|
740
|
-
for (const p of patterns) if (p.endsWith("*")) prefixes.push(p.slice(0, -1));
|
|
741
|
-
else exact.add(p);
|
|
742
|
-
return (path) => {
|
|
743
|
-
if (exact.has(path)) return true;
|
|
744
|
-
for (const pre of prefixes) if (path.startsWith(pre)) return true;
|
|
745
|
-
return false;
|
|
851
|
+
options: corsOptions,
|
|
852
|
+
warnings
|
|
746
853
|
};
|
|
747
854
|
}
|
|
855
|
+
//#endregion
|
|
856
|
+
//#region src/factory/security/pluginLoader.ts
|
|
748
857
|
const PLUGIN_REGISTRY = {
|
|
749
858
|
cors: {
|
|
750
859
|
package: "@fastify/cors",
|
|
@@ -793,6 +902,77 @@ async function loadPlugin(name, logger) {
|
|
|
793
902
|
throw new Error(`Failed to load plugin '${name}': ${err.message}`);
|
|
794
903
|
}
|
|
795
904
|
}
|
|
905
|
+
//#endregion
|
|
906
|
+
//#region src/factory/security/rateLimit.ts
|
|
907
|
+
/** "Unlimited" as a number — @fastify/rate-limit's `max` fn must return one. */
|
|
908
|
+
const UNLIMITED_MAX = Number.MAX_SAFE_INTEGER;
|
|
909
|
+
/**
|
|
910
|
+
* Expand `plan` sugar (2.22) into `@fastify/rate-limit` primitives: a
|
|
911
|
+
* per-request async `max` that resolves the caller's plan once (cached
|
|
912
|
+
* per request), and — unless the host supplied one — the tenant
|
|
913
|
+
* keyGenerator so buckets follow actors, not IPs. Unknown plans and
|
|
914
|
+
* resolver errors fall back to `plan.default`, then the global `max`
|
|
915
|
+
* (fail-safe: never fail-open to unlimited).
|
|
916
|
+
*/
|
|
917
|
+
function buildPlanOpts(rest, plan) {
|
|
918
|
+
const globalMax = typeof rest.max === "number" ? rest.max : 100;
|
|
919
|
+
const fallbackLimit = plan.default !== void 0 ? plan.limits[plan.default] ?? { max: globalMax } : { max: globalMax };
|
|
920
|
+
const planCache = /* @__PURE__ */ new WeakMap();
|
|
921
|
+
const limitOf = (req) => {
|
|
922
|
+
let cached = planCache.get(req);
|
|
923
|
+
if (!cached) {
|
|
924
|
+
cached = Promise.resolve().then(() => plan.resolve(req)).then((name) => name !== void 0 ? plan.limits[name] ?? fallbackLimit : fallbackLimit).catch(() => fallbackLimit);
|
|
925
|
+
planCache.set(req, cached);
|
|
926
|
+
}
|
|
927
|
+
return cached;
|
|
928
|
+
};
|
|
929
|
+
return {
|
|
930
|
+
...rest,
|
|
931
|
+
max: async (req, _key) => {
|
|
932
|
+
const limit = await limitOf(req);
|
|
933
|
+
return limit === false ? UNLIMITED_MAX : limit.max;
|
|
934
|
+
},
|
|
935
|
+
keyGenerator: rest.keyGenerator ?? createTenantKeyGenerator()
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Translate `skipPaths` sugar into a `@fastify/rate-limit` `allowList`
|
|
940
|
+
* function. A user-supplied `allowList` (array of IPs or function) is
|
|
941
|
+
* preserved and OR-ed with the path match.
|
|
942
|
+
*/
|
|
943
|
+
function buildRateLimitOpts(input) {
|
|
944
|
+
const { skipPaths, allowList, plan, ...bare } = input;
|
|
945
|
+
const rest = plan ? buildPlanOpts(bare, plan) : bare;
|
|
946
|
+
if (!skipPaths || skipPaths.length === 0) return allowList === void 0 ? rest : {
|
|
947
|
+
...rest,
|
|
948
|
+
allowList
|
|
949
|
+
};
|
|
950
|
+
const matchesPath = compilePathMatcher(skipPaths);
|
|
951
|
+
const combined = async (req, key) => {
|
|
952
|
+
const path = (req.url ?? "").split("?", 1)[0] ?? "";
|
|
953
|
+
if (matchesPath(path)) return true;
|
|
954
|
+
if (typeof allowList === "function") return await allowList(req, key);
|
|
955
|
+
if (Array.isArray(allowList)) return allowList.includes(key);
|
|
956
|
+
return false;
|
|
957
|
+
};
|
|
958
|
+
return {
|
|
959
|
+
...rest,
|
|
960
|
+
allowList: combined
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
function compilePathMatcher(patterns) {
|
|
964
|
+
const prefixes = [];
|
|
965
|
+
const exact = /* @__PURE__ */ new Set();
|
|
966
|
+
for (const p of patterns) if (p.endsWith("*")) prefixes.push(p.slice(0, -1));
|
|
967
|
+
else exact.add(p);
|
|
968
|
+
return (path) => {
|
|
969
|
+
if (exact.has(path)) return true;
|
|
970
|
+
for (const pre of prefixes) if (path.startsWith(pre)) return true;
|
|
971
|
+
return false;
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
//#endregion
|
|
975
|
+
//#region src/factory/registerSecurity.ts
|
|
796
976
|
/**
|
|
797
977
|
* Register security plugins (Helmet, CORS, Rate Limiting).
|
|
798
978
|
* All enabled by default — set to `false` to opt out.
|
|
@@ -805,10 +985,8 @@ async function registerSecurityPlugins(fastify, config) {
|
|
|
805
985
|
} else fastify.log.warn("Helmet disabled - security headers not applied");
|
|
806
986
|
if (config.cors !== false) {
|
|
807
987
|
const cors = await loadPlugin("cors");
|
|
808
|
-
const
|
|
809
|
-
const
|
|
810
|
-
if (config.preset === "production" && !originDeclared) fastify.log.warn("CORS origin is not explicitly configured in production. Browser apps: set cors.origin to allowed domains (e.g. ['https://app.example.com']) with credentials: true. Server-to-server / API-key services: cors: { origin: '*', credentials: false } OR cors: false to disable. Tip: when wiring cors.origin from an env var, fail fast on missing (`if (!process.env.ALLOWED_ORIGINS) throw ...`) instead of letting `undefined` slip through.");
|
|
811
|
-
if (corsOptions.credentials && corsOptions.origin === "*") corsOptions.origin = true;
|
|
988
|
+
const { options: corsOptions, warnings } = resolveCorsOptions(config);
|
|
989
|
+
for (const warning of warnings) fastify.log.warn(warning);
|
|
812
990
|
await fastify.register(cors, corsOptions);
|
|
813
991
|
fastify.log.debug("CORS enabled");
|
|
814
992
|
} else fastify.log.warn("CORS disabled");
|
|
@@ -844,13 +1022,17 @@ async function registerUtilityPlugins(fastify, config) {
|
|
|
844
1022
|
if (config.multipart !== false) {
|
|
845
1023
|
const multipart = await loadPlugin("multipart", fastify.log);
|
|
846
1024
|
if (multipart) {
|
|
1025
|
+
const hostMultipart = typeof config.multipart === "object" && config.multipart !== null ? config.multipart : {};
|
|
847
1026
|
await fastify.register(multipart, {
|
|
1027
|
+
throwFileSizeLimit: true,
|
|
1028
|
+
...hostMultipart,
|
|
848
1029
|
limits: {
|
|
849
1030
|
fileSize: 10 * 1024 * 1024,
|
|
850
|
-
files: 10
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
1031
|
+
files: 10,
|
|
1032
|
+
fields: 100,
|
|
1033
|
+
parts: 120,
|
|
1034
|
+
...hostMultipart.limits
|
|
1035
|
+
}
|
|
854
1036
|
});
|
|
855
1037
|
fastify.log.debug("Multipart (file uploads) enabled");
|
|
856
1038
|
}
|
|
@@ -1030,10 +1212,7 @@ Missing:\n${lines}\n\nFix: pass durable adapters under \`stores\`, or use runtim
|
|
|
1030
1212
|
* ```
|
|
1031
1213
|
*/
|
|
1032
1214
|
async function createApp(options) {
|
|
1033
|
-
if (options.debug !== void 0 && options.debug !== false) {
|
|
1034
|
-
const { configureArcLogger } = await import("./logger/index.mjs");
|
|
1035
|
-
configureArcLogger({ debug: options.debug });
|
|
1036
|
-
}
|
|
1215
|
+
if (options.debug !== void 0 && options.debug !== false) configureArcLogger({ debug: options.debug });
|
|
1037
1216
|
validateAuthOptions(options);
|
|
1038
1217
|
const deferredWarnings = validateDistributedRuntime(options);
|
|
1039
1218
|
if (options.beforeBoot) await options.beforeBoot();
|
|
@@ -1043,11 +1222,16 @@ async function createApp(options) {
|
|
|
1043
1222
|
};
|
|
1044
1223
|
const resolvedModules = orderModules(await Promise.all((config.modules ?? []).map((m) => resolveModule(m))));
|
|
1045
1224
|
const moduleErrorMappers = resolvedModules.flatMap((m) => m.errorMappers ?? []);
|
|
1225
|
+
const resolvedLogger = resolveLoggerConfig(config.logger);
|
|
1046
1226
|
const fastify = Fastify({
|
|
1047
|
-
logger:
|
|
1227
|
+
logger: resolvedLogger,
|
|
1228
|
+
genReqId: createRequestIdGenerator(),
|
|
1048
1229
|
trustProxy: config.trustProxy ?? false,
|
|
1049
1230
|
pluginTimeout: config.pluginTimeout ?? 1e4,
|
|
1050
1231
|
...config.bodyLimit !== void 0 ? { bodyLimit: config.bodyLimit } : {},
|
|
1232
|
+
...config.requestTimeout !== void 0 ? { requestTimeout: config.requestTimeout } : {},
|
|
1233
|
+
...config.connectionTimeout !== void 0 ? { connectionTimeout: config.connectionTimeout } : {},
|
|
1234
|
+
...config.keepAliveTimeout !== void 0 ? { keepAliveTimeout: config.keepAliveTimeout } : {},
|
|
1051
1235
|
allowErrorHandlerOverride: true,
|
|
1052
1236
|
routerOptions: {
|
|
1053
1237
|
querystringParser: (str) => qs.parse(str, {
|
|
@@ -1066,6 +1250,10 @@ async function createApp(options) {
|
|
|
1066
1250
|
keywords: ["example", ...config.ajv?.keywords ?? []]
|
|
1067
1251
|
} }
|
|
1068
1252
|
});
|
|
1253
|
+
if (resolvedLogger !== false) configureArcLogger({
|
|
1254
|
+
...options.debug !== void 0 && options.debug !== false ? { debug: options.debug } : {},
|
|
1255
|
+
writer: createPinoWriter(fastify.log)
|
|
1256
|
+
});
|
|
1069
1257
|
for (const warning of deferredWarnings) fastify.log.warn(warning);
|
|
1070
1258
|
if (config.typeProvider === "typebox") try {
|
|
1071
1259
|
const { TypeBoxValidatorCompiler } = await import("@fastify/type-provider-typebox");
|
|
@@ -1073,12 +1261,10 @@ async function createApp(options) {
|
|
|
1073
1261
|
} catch {
|
|
1074
1262
|
fastify.log.warn("typeProvider: \"typebox\" requested but @fastify/type-provider-typebox is not installed.");
|
|
1075
1263
|
}
|
|
1076
|
-
const sjp = await import("secure-json-parse");
|
|
1077
1264
|
fastify.removeContentTypeParser("application/json");
|
|
1078
1265
|
fastify.addContentTypeParser("application/json", { parseAs: "string" }, (_req, body, done) => {
|
|
1079
|
-
if (!body || body.length === 0) return done(null, void 0);
|
|
1080
1266
|
try {
|
|
1081
|
-
done(null,
|
|
1267
|
+
done(null, parseJsonBody(body));
|
|
1082
1268
|
} catch (err) {
|
|
1083
1269
|
done(err);
|
|
1084
1270
|
}
|
|
@@ -1152,4 +1338,4 @@ const ArcFactory = {
|
|
|
1152
1338
|
}
|
|
1153
1339
|
};
|
|
1154
1340
|
//#endregion
|
|
1155
|
-
export { edgePreset as a, testingPreset as c,
|
|
1341
|
+
export { edgePreset as a, testingPreset as c, getModuleExports as d, orderModules as f, preloadResourcesAsync as h, developmentPreset as i, workerPreset as l, preloadResources as m, createApp as n, getPreset as o, resolveModule as p, createApp_exports as r, productionPreset as s, ArcFactory as t, defineModule as u };
|
package/dist/docs/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { p as RegistryEntry } from "../index-
|
|
1
|
+
import { p as RegistryEntry } from "../index-DbtgCOCj.mjs";
|
|
2
2
|
import { t as ExternalOpenApiPaths } from "../externalPaths-BE6wFpYv.mjs";
|
|
3
3
|
import { FastifyPluginAsync } from "fastify";
|
|
4
4
|
//#region src/docs/openapi/types.d.ts
|
package/dist/docs/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as getUserRoles } from "../types-XTApa8b6.mjs";
|
|
2
|
-
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-
|
|
2
|
+
import { n as openApiPlugin, r as openapi_default, t as buildOpenApiSpec } from "../openapi-BRpMZD7C.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
//#region src/docs/scalar.ts
|
|
5
5
|
const scalarPlugin = async (fastify, opts = {}) => {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { t as ArcError } from "../errors-DkO8Nk6-.mjs";
|
|
2
|
+
import { t as parseJsonBody } from "../jsonBody-BlzAoGCe.mjs";
|
|
1
3
|
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
2
4
|
import fp from "fastify-plugin";
|
|
3
5
|
//#region src/encryption/fieldCipher.ts
|
|
@@ -182,17 +184,19 @@ const encryptionPlugin = async (fastify, opts) => {
|
|
|
182
184
|
}
|
|
183
185
|
if (decryptRequests) fastify.addContentTypeParser(contentType, { parseAs: "string" }, async (request, body) => {
|
|
184
186
|
const provider = keyProvider;
|
|
185
|
-
|
|
186
|
-
request.requestDecrypted = true;
|
|
187
|
-
if (plaintext.length === 0) return void 0;
|
|
187
|
+
let plaintext;
|
|
188
188
|
try {
|
|
189
|
-
|
|
189
|
+
plaintext = await jweDecrypt(body, (kid) => provider.decryptionKey(kid, { request }), allowedAlgs, allowedEncs);
|
|
190
190
|
} catch (cause) {
|
|
191
|
-
throw
|
|
191
|
+
throw new ArcError("Invalid encrypted request body", {
|
|
192
|
+
code: "arc.bad_request",
|
|
192
193
|
statusCode: 400,
|
|
193
194
|
cause
|
|
194
195
|
});
|
|
195
196
|
}
|
|
197
|
+
request.requestDecrypted = true;
|
|
198
|
+
if (plaintext.length === 0) return void 0;
|
|
199
|
+
return parseJsonBody(plaintext, "Decrypted request body is not valid JSON");
|
|
196
200
|
});
|
|
197
201
|
fastify.addHook("preSerialization", async (request, reply, payload) => {
|
|
198
202
|
const directive = resolveDirective(request, defaultMode, fallback);
|
|
@@ -91,7 +91,7 @@ function mongooseValidationDetails(errors) {
|
|
|
91
91
|
}
|
|
92
92
|
async function errorHandlerPluginFn(fastify, options = {}) {
|
|
93
93
|
const isProduction = process.env.NODE_ENV === "production";
|
|
94
|
-
const { includeStack = !isProduction, onError, errorMap = {}, errorMappers = [], isDuplicateKeyError = defaultIsDuplicateKeyError } = options;
|
|
94
|
+
const { includeStack = !isProduction, exposeInternalMessages = !isProduction, onError, errorMap = {}, errorMappers = [], isDuplicateKeyError = defaultIsDuplicateKeyError } = options;
|
|
95
95
|
fastify.setErrorHandler(async (error, request, reply) => {
|
|
96
96
|
if (onError) try {
|
|
97
97
|
await onError(error, request);
|
|
@@ -112,6 +112,7 @@ async function errorHandlerPluginFn(fastify, options = {}) {
|
|
|
112
112
|
...Object.keys(meta).length > 0 ? { meta } : {}
|
|
113
113
|
};
|
|
114
114
|
const status = wire.status ?? 500;
|
|
115
|
+
if (status >= 500 && wire.code === "arc.internal_error" && !exposeInternalMessages) wire.message = "Internal Server Error";
|
|
115
116
|
if (status >= 500) request.log.error({
|
|
116
117
|
err: error,
|
|
117
118
|
status
|
|
@@ -120,11 +121,31 @@ async function errorHandlerPluginFn(fastify, options = {}) {
|
|
|
120
121
|
err: error,
|
|
121
122
|
status
|
|
122
123
|
}, "Client error");
|
|
123
|
-
if (isArcError(error) && error.cause)
|
|
124
|
+
if (isArcError(error) && error.cause) {
|
|
125
|
+
const causeLevel = status >= 500 ? "error" : "warn";
|
|
126
|
+
request.log[causeLevel]({ cause: error.cause }, "Error cause chain");
|
|
127
|
+
}
|
|
124
128
|
return reply.status(status).send(wire);
|
|
125
129
|
});
|
|
126
130
|
}
|
|
127
131
|
/**
|
|
132
|
+
* Is `code` a caller-defined DOMAIN error code that is safe to surface on the
|
|
133
|
+
* wire, as opposed to a framework/runtime code that must never leak?
|
|
134
|
+
*
|
|
135
|
+
* Rejects Fastify internals (`FST_*`) and Node system errnos (`ECONNREFUSED`,
|
|
136
|
+
* `ENOENT`, …). Domain codes in the wild use separators (`PURCHASE_NOT_FOUND`,
|
|
137
|
+
* `access.forbidden`) so they pass; a bare all-caps single word that collides
|
|
138
|
+
* with the Node-errno shape (e.g. `EXPIRED`) is conservatively treated as a
|
|
139
|
+
* runtime code — use a separatored code (`TOKEN_EXPIRED`) to surface it, or
|
|
140
|
+
* throw via `createDomainError` which bypasses this heuristic entirely.
|
|
141
|
+
*/
|
|
142
|
+
function isDomainErrorCode(code) {
|
|
143
|
+
if (typeof code !== "string" || code.length === 0) return false;
|
|
144
|
+
if (code.startsWith("FST_")) return false;
|
|
145
|
+
if (/^E[A-Z]{2,}$/.test(code)) return false;
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
128
149
|
* Single-pass error → `ErrorContract` classifier. Dispatch order is fixed:
|
|
129
150
|
*
|
|
130
151
|
* 1. Class-based mappers (`instanceof`) — user-registered domain errors
|
|
@@ -167,7 +188,7 @@ function classify(error, ctx) {
|
|
|
167
188
|
};
|
|
168
189
|
}
|
|
169
190
|
if (typeof fastifyErr.statusCode === "number") return {
|
|
170
|
-
code: statusToArcCode(fastifyErr.statusCode),
|
|
191
|
+
code: (isDomainErrorCode(fastifyErr.code) ? fastifyErr.code : void 0) ?? statusToArcCode(fastifyErr.statusCode),
|
|
171
192
|
message: error.message || "Error",
|
|
172
193
|
status: fastifyErr.statusCode
|
|
173
194
|
};
|
|
@@ -18,6 +18,19 @@ interface ErrorMapper<T extends Error = Error> {
|
|
|
18
18
|
interface ErrorHandlerOptions {
|
|
19
19
|
/** Include `meta.stack` on the wire (defaults to `NODE_ENV !== 'production'`). */
|
|
20
20
|
includeStack?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Ship the raw `error.message` of UNCLASSIFIED 500s on the wire
|
|
23
|
+
* (defaults to `NODE_ENV !== 'production'`).
|
|
24
|
+
*
|
|
25
|
+
* Only the `arc.internal_error` fallback is affected — an unclassified
|
|
26
|
+
* throw carries whatever some driver/library put in the message (connection
|
|
27
|
+
* strings, file paths, SQL fragments), which must not reach clients in
|
|
28
|
+
* production. Deliberate 5xx contracts are untouched: `ArcError` /
|
|
29
|
+
* domain-coded throws (`payment.gateway.timeout`, …) keep their message
|
|
30
|
+
* because the thrower chose it for the client. The raw message is always
|
|
31
|
+
* logged via `request.log.error({ err })` regardless of this flag.
|
|
32
|
+
*/
|
|
33
|
+
exposeInternalMessages?: boolean;
|
|
21
34
|
/** Custom callback fired for every error — log to Sentry / Datadog / etc. */
|
|
22
35
|
onError?: (error: Error, request: FastifyRequest) => void | Promise<void>;
|
|
23
36
|
/** Map by `error.name` string. Lower priority than `errorMappers`. */
|
package/dist/factory/index.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { a as getModuleExports, c as LoadResourcesOptions, d as loadResources, i as defineModule, l as ResourceLike, n as ArcModuleInput, o as orderModules, r as ArcModuleRegistry, s as resolveModule, t as ArcModule, u as ResourceModule } from "../module-
|
|
2
|
-
import "../fastify-augmentation-
|
|
3
|
-
import { a as
|
|
1
|
+
import { a as getModuleExports, c as LoadResourcesOptions, d as loadResources, i as defineModule, l as ResourceLike, n as ArcModuleInput, o as orderModules, r as ArcModuleRegistry, s as resolveModule, t as ArcModule, u as ResourceModule } from "../module-B4u10iOJ.mjs";
|
|
2
|
+
import "../fastify-augmentation-9_fuetB4.mjs";
|
|
3
|
+
import { a as AuthOption, c as CustomPluginAuthOption, d as preloadResourcesAsync, i as UnderPressureOptions, l as JwtAuthOption, n as MultipartOptions, o as BetterAuthOption, r as RawBodyOptions, s as CustomAuthenticatorOption, t as CreateAppOptions, u as preloadResources } from "../index-BEu4wjbL.mjs";
|
|
4
|
+
import { g as HealthCheck } from "../versioning-DLwklPlG.mjs";
|
|
4
5
|
import { FastifyInstance, FastifyServerOptions } from "fastify";
|
|
5
6
|
//#region src/factory/createApp.d.ts
|
|
6
7
|
/**
|
|
@@ -32,6 +33,29 @@ declare const ArcFactory: {
|
|
|
32
33
|
testing(options: Omit<CreateAppOptions, "preset">): Promise<FastifyInstance>;
|
|
33
34
|
};
|
|
34
35
|
//#endregion
|
|
36
|
+
//#region src/factory/createWorker.d.ts
|
|
37
|
+
interface WorkerHealthOptions {
|
|
38
|
+
/** Port for the worker's ONLY listener (liveness/readiness probes). */
|
|
39
|
+
port: number;
|
|
40
|
+
/** Bind host. Default `0.0.0.0`. */
|
|
41
|
+
host?: string;
|
|
42
|
+
/** Readiness checks — same contract as the API role's healthPlugin. */
|
|
43
|
+
checks?: HealthCheck[];
|
|
44
|
+
}
|
|
45
|
+
interface CreateWorkerOptions {
|
|
46
|
+
/** Opt-in probe listener. Omit = the worker binds NOTHING. */
|
|
47
|
+
health?: WorkerHealthOptions;
|
|
48
|
+
}
|
|
49
|
+
interface ArcWorker {
|
|
50
|
+
/** The underlying Fastify instance (decorations, inject, logs). */
|
|
51
|
+
app: FastifyInstance;
|
|
52
|
+
/** Typed module-export accessor — same contract as `getModuleExports`. */
|
|
53
|
+
exports<TExports = unknown>(name: string): TExports;
|
|
54
|
+
/** Close app → module onClose (reverse) → app onClose. Idempotent. */
|
|
55
|
+
close(): Promise<void>;
|
|
56
|
+
}
|
|
57
|
+
declare function createWorker(options: CreateAppOptions, workerOptions?: CreateWorkerOptions): Promise<ArcWorker>;
|
|
58
|
+
//#endregion
|
|
35
59
|
//#region src/factory/edge.d.ts
|
|
36
60
|
interface FetchHandlerOptions {
|
|
37
61
|
/**
|
|
@@ -91,9 +115,6 @@ declare const testingPreset: Partial<CreateAppOptions>;
|
|
|
91
115
|
* when using `toFetchHandler()` (routes through Fastify's `.inject()`).
|
|
92
116
|
*/
|
|
93
117
|
declare const edgePreset: Partial<CreateAppOptions>;
|
|
94
|
-
|
|
95
|
-
* Get preset by name
|
|
96
|
-
*/
|
|
97
|
-
declare function getPreset(name: "production" | "development" | "testing" | "edge"): Partial<CreateAppOptions>;
|
|
118
|
+
declare function getPreset(name: "production" | "development" | "testing" | "edge" | "worker"): Partial<CreateAppOptions>;
|
|
98
119
|
//#endregion
|
|
99
|
-
export { ArcFactory, type ArcModule, type ArcModuleInput, type ArcModuleRegistry, type AuthOption, type BetterAuthOption, type CreateAppOptions, type CustomAuthenticatorOption, type CustomPluginAuthOption, type FetchHandlerOptions, type JwtAuthOption, type LoadResourcesOptions, type MultipartOptions, type RawBodyOptions, type ResourceLike, type ResourceModule, type UnderPressureOptions, createApp, defineModule, developmentPreset, edgePreset, getModuleExports, getPreset, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveModule, testingPreset, toFetchHandler };
|
|
120
|
+
export { ArcFactory, type ArcModule, type ArcModuleInput, type ArcModuleRegistry, type ArcWorker, type AuthOption, type BetterAuthOption, type CreateAppOptions, type CreateWorkerOptions, type CustomAuthenticatorOption, type CustomPluginAuthOption, type FetchHandlerOptions, type JwtAuthOption, type LoadResourcesOptions, type MultipartOptions, type RawBodyOptions, type ResourceLike, type ResourceModule, type UnderPressureOptions, type WorkerHealthOptions, createApp, createWorker, defineModule, developmentPreset, edgePreset, getModuleExports, getPreset, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveModule, testingPreset, toFetchHandler };
|
package/dist/factory/index.mjs
CHANGED
|
@@ -1,6 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { a as edgePreset, c as testingPreset, d as getModuleExports, f as orderModules, h as preloadResourcesAsync, i as developmentPreset, l as workerPreset, m as preloadResources, n as createApp, o as getPreset, p as resolveModule, s as productionPreset, t as ArcFactory, u as defineModule } from "../createApp-Ci7yG5gv.mjs";
|
|
2
|
+
import { t as healthPlugin } from "../health-ScyjIVo5.mjs";
|
|
3
3
|
import { t as loadResources } from "../loadResources-DpU8pfjV.mjs";
|
|
4
|
+
//#region src/factory/createWorker.ts
|
|
5
|
+
async function createWorker(options, workerOptions = {}) {
|
|
6
|
+
const { auth: _auth, helmet: _helmet, cors: _cors, rateLimit: _rateLimit, multipart: _multipart, rawBody: _rawBody, sensible: _sensible, underPressure: _underPressure, replyHelpers: _replyHelpers, ...runtimeOptions } = options;
|
|
7
|
+
const app = await createApp({
|
|
8
|
+
...runtimeOptions,
|
|
9
|
+
preset: "worker",
|
|
10
|
+
...runtimeOptions.arcPlugins ? { arcPlugins: {
|
|
11
|
+
...workerPreset.arcPlugins,
|
|
12
|
+
...runtimeOptions.arcPlugins
|
|
13
|
+
} } : {}
|
|
14
|
+
});
|
|
15
|
+
if (workerOptions.health) {
|
|
16
|
+
const { port, host = "0.0.0.0", checks = [] } = workerOptions.health;
|
|
17
|
+
await app.register(healthPlugin, { checks });
|
|
18
|
+
await app.listen({
|
|
19
|
+
port,
|
|
20
|
+
host
|
|
21
|
+
});
|
|
22
|
+
} else await app.ready();
|
|
23
|
+
let closed = false;
|
|
24
|
+
return {
|
|
25
|
+
app,
|
|
26
|
+
exports: (name) => getModuleExports(app, name),
|
|
27
|
+
close: async () => {
|
|
28
|
+
if (closed) return;
|
|
29
|
+
closed = true;
|
|
30
|
+
await app.close();
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
4
35
|
//#region src/factory/edge.ts
|
|
5
36
|
/**
|
|
6
37
|
* Convert a Fastify app into a Web Standards fetch handler.
|
|
@@ -47,4 +78,4 @@ function toFetchHandler(app, options = {}) {
|
|
|
47
78
|
};
|
|
48
79
|
}
|
|
49
80
|
//#endregion
|
|
50
|
-
export { ArcFactory, createApp, defineModule, developmentPreset, edgePreset, getModuleExports, getPreset, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveModule, testingPreset, toFetchHandler };
|
|
81
|
+
export { ArcFactory, createApp, createWorker, defineModule, developmentPreset, edgePreset, getModuleExports, getPreset, loadResources, orderModules, preloadResources, preloadResourcesAsync, productionPreset, resolveModule, testingPreset, toFetchHandler };
|