@bleedingdev/modern-js-server-core 3.8.3-ultramodern.6 → 3.9.0-ultramodern.10

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 (94) hide show
  1. package/dist/cjs/adapters/node/index.js +4 -0
  2. package/dist/cjs/adapters/node/plugins/static.js +107 -29
  3. package/dist/cjs/index.js +10 -34
  4. package/dist/cjs/plugins/compat/index.js +1 -0
  5. package/dist/cjs/serverBase.js +58 -17
  6. package/dist/cjs/utils/error.js +0 -10
  7. package/dist/esm/adapters/node/index.mjs +1 -0
  8. package/dist/esm/adapters/node/plugins/static.mjs +107 -32
  9. package/dist/esm/index.mjs +1 -1
  10. package/dist/esm/plugins/compat/index.mjs +1 -0
  11. package/dist/esm/plugins/render/render.mjs +4 -5
  12. package/dist/esm/serverBase.mjs +58 -17
  13. package/dist/esm/utils/error.mjs +0 -1
  14. package/dist/esm-node/adapters/node/index.mjs +1 -0
  15. package/dist/esm-node/adapters/node/plugins/static.mjs +107 -32
  16. package/dist/esm-node/index.mjs +1 -1
  17. package/dist/esm-node/plugins/compat/index.mjs +1 -0
  18. package/dist/esm-node/plugins/render/render.mjs +4 -3
  19. package/dist/esm-node/serverBase.mjs +58 -17
  20. package/dist/esm-node/utils/error.mjs +0 -1
  21. package/dist/types/adapters/node/helper/index.d.ts +6 -6
  22. package/dist/types/adapters/node/helper/loadCache.d.ts +1 -1
  23. package/dist/types/adapters/node/helper/loadConfig.d.ts +1 -1
  24. package/dist/types/adapters/node/helper/loadEnv.d.ts +1 -1
  25. package/dist/types/adapters/node/helper/loadPlugin.d.ts +1 -1
  26. package/dist/types/adapters/node/helper/utils.d.ts +1 -1
  27. package/dist/types/adapters/node/hono.d.ts +2 -2
  28. package/dist/types/adapters/node/index.d.ts +7 -5
  29. package/dist/types/adapters/node/node.d.ts +1 -1
  30. package/dist/types/adapters/node/plugins/index.d.ts +3 -3
  31. package/dist/types/adapters/node/plugins/nodeServer.d.ts +1 -1
  32. package/dist/types/adapters/node/plugins/resource.d.ts +1 -1
  33. package/dist/types/adapters/node/plugins/static.d.ts +43 -5
  34. package/dist/types/helper.d.ts +1 -1
  35. package/dist/types/index.d.ts +13 -14
  36. package/dist/types/plugins/compat/hooks.d.ts +1 -1
  37. package/dist/types/plugins/compat/index.d.ts +2 -2
  38. package/dist/types/plugins/default.d.ts +2 -2
  39. package/dist/types/plugins/favicon.d.ts +1 -1
  40. package/dist/types/plugins/index.d.ts +8 -8
  41. package/dist/types/plugins/log.d.ts +1 -1
  42. package/dist/types/plugins/middlewares.d.ts +1 -1
  43. package/dist/types/plugins/monitors.d.ts +1 -1
  44. package/dist/types/plugins/processedBy.d.ts +1 -1
  45. package/dist/types/plugins/render/csrRscRender.d.ts +1 -1
  46. package/dist/types/plugins/render/dataHandler.d.ts +1 -1
  47. package/dist/types/plugins/render/index.d.ts +2 -2
  48. package/dist/types/plugins/render/inject.d.ts +1 -1
  49. package/dist/types/plugins/render/render.d.ts +2 -2
  50. package/dist/types/plugins/render/renderRscHandler.d.ts +1 -1
  51. package/dist/types/plugins/render/serverActionHandler.d.ts +1 -1
  52. package/dist/types/plugins/render/ssrCache.d.ts +1 -1
  53. package/dist/types/plugins/render/ssrRender.d.ts +2 -2
  54. package/dist/types/plugins/render/utils.d.ts +2 -2
  55. package/dist/types/plugins/route.d.ts +1 -1
  56. package/dist/types/serverBase.d.ts +9 -3
  57. package/dist/types/types/config/bff.d.ts +4 -15
  58. package/dist/types/types/config/index.d.ts +14 -14
  59. package/dist/types/types/config/server.d.ts +1 -3
  60. package/dist/types/types/config/source.d.ts +1 -1
  61. package/dist/types/types/index.d.ts +4 -4
  62. package/dist/types/types/plugins/base.d.ts +3 -3
  63. package/dist/types/types/plugins/index.d.ts +2 -2
  64. package/dist/types/types/plugins/plugin.d.ts +16 -2
  65. package/dist/types/types/render.d.ts +1 -1
  66. package/dist/types/types/requestHandler.d.ts +3 -4
  67. package/dist/types/types/server.d.ts +1 -1
  68. package/dist/types/utils/error.d.ts +0 -2
  69. package/dist/types/utils/index.d.ts +9 -9
  70. package/dist/types/utils/publicDir.d.ts +1 -1
  71. package/dist/types/utils/serverConfig.d.ts +1 -1
  72. package/package.json +8 -9
  73. package/dist/cjs/adapters/node/plugins/staticModuleFederation.js +0 -173
  74. package/dist/cjs/adapters/node/plugins/staticPrecompressed.js +0 -152
  75. package/dist/cjs/adapters/node/plugins/staticServing.js +0 -206
  76. package/dist/cjs/types/config/bffRuntime.js +0 -18
  77. package/dist/cjs/types/config/serverTelemetry.js +0 -18
  78. package/dist/esm/adapters/node/plugins/staticModuleFederation.mjs +0 -104
  79. package/dist/esm/adapters/node/plugins/staticPrecompressed.mjs +0 -111
  80. package/dist/esm/adapters/node/plugins/staticServing.mjs +0 -152
  81. package/dist/esm/types/config/bffRuntime.mjs +0 -0
  82. package/dist/esm/types/config/serverTelemetry.mjs +0 -0
  83. package/dist/esm-node/adapters/node/plugins/staticModuleFederation.mjs +0 -105
  84. package/dist/esm-node/adapters/node/plugins/staticPrecompressed.mjs +0 -112
  85. package/dist/esm-node/adapters/node/plugins/staticServing.mjs +0 -153
  86. package/dist/esm-node/types/config/bffRuntime.mjs +0 -1
  87. package/dist/esm-node/types/config/serverTelemetry.mjs +0 -1
  88. package/dist/types/adapters/node/plugins/staticModuleFederation.d.ts +0 -13
  89. package/dist/types/adapters/node/plugins/staticPrecompressed.d.ts +0 -13
  90. package/dist/types/adapters/node/plugins/staticServing.d.ts +0 -25
  91. package/dist/types/types/config/bffRuntime.d.ts +0 -116
  92. package/dist/types/types/config/serverTelemetry.d.ts +0 -319
  93. /package/dist/esm/{rslib-runtime.mjs → rslib-runtime~1.mjs} +0 -0
  94. /package/dist/esm-node/{rslib-runtime.mjs → rslib-runtime~0.mjs} +0 -0
@@ -1,153 +0,0 @@
1
- import "node:module";
2
- import { fileReader } from "@modern-js/runtime-utils/fileReader";
3
- import { fs } from "@modern-js/utils";
4
- import { getMimeType } from "hono/utils/mime";
5
- import path from "path";
6
- import { applyModuleFederationAssetHeaders, getModuleFederationAssetList, getModuleFederationRequestPath, isBackendModuleFederationManifestRequest, isModuleFederationManifestRequest, patchModuleFederationManifestPublicPath, patchModuleFederationRemoteEntryPublicPath } from "./staticModuleFederation.mjs";
7
- import { applyPreCompressedAssetHeaders, resolvePreCompressedAsset } from "./staticPrecompressed.mjs";
8
- const MODULE_FEDERATION_ASSET_REFRESH_INTERVAL_MS = 1000;
9
- const getStaticMimeType = (filename)=>getMimeType(filename) ?? ('.cjs' === path.extname(filename).toLowerCase() ? "text/javascript; charset=UTF-8" : void 0);
10
- const servePreCompressedPublicRouteAsset = async (c, pwd, route)=>{
11
- const { entryPath } = route;
12
- const originFilename = path.join(pwd, entryPath);
13
- const preCompressedAsset = await resolvePreCompressedAsset(c, originFilename);
14
- if (!preCompressedAsset.acceptable) {
15
- applyPreCompressedAssetHeaders(c, preCompressedAsset);
16
- return c.body(null, 406);
17
- }
18
- const filename = preCompressedAsset.selected?.filepath ?? originFilename;
19
- const data = await fileReader.readFile(filename, 'buffer');
20
- const mimeType = getStaticMimeType(originFilename);
21
- if (null === data) return null;
22
- const body = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
23
- if (mimeType) c.header('Content-Type', mimeType);
24
- Object.entries(route.responseHeaders || {}).forEach(([k, v])=>{
25
- c.header(k, v);
26
- });
27
- applyPreCompressedAssetHeaders(c, preCompressedAsset);
28
- c.header('Content-Length', String(data.byteLength));
29
- return c.body(body, 200);
30
- };
31
- const isPathInside = (target, root)=>{
32
- const relative = path.relative(path.resolve(root), path.resolve(target));
33
- return '' === relative || !relative.startsWith(`..${path.sep}`) && '..' !== relative && !path.isAbsolute(relative);
34
- };
35
- const resolvePublicDirectoryAsset = async (pwd, pathname)=>{
36
- let decodedPathname;
37
- try {
38
- decodedPathname = decodeURIComponent(pathname).replace(/\\/gu, '/');
39
- } catch {
40
- return null;
41
- }
42
- if (decodedPathname.includes('\0') || decodedPathname.split('/').includes('..')) return null;
43
- const publicDirectory = path.join(pwd, 'public');
44
- const filepath = path.resolve(publicDirectory, decodedPathname.replace(/^\/+/u, ''));
45
- if (!isPathInside(filepath, publicDirectory)) return null;
46
- try {
47
- const [realPublicDirectory, realFilepath, stat] = await Promise.all([
48
- fs.realpath(publicDirectory),
49
- fs.realpath(filepath),
50
- fs.stat(filepath)
51
- ]);
52
- if (!stat.isFile() || !isPathInside(realFilepath, realPublicDirectory)) return null;
53
- return realFilepath;
54
- } catch {
55
- return null;
56
- }
57
- };
58
- const servePublicDirectoryAsset = async (c, pwd)=>{
59
- const method = c.req.raw.method.toUpperCase();
60
- if ('GET' !== method && 'HEAD' !== method) return null;
61
- const originFilename = await resolvePublicDirectoryAsset(pwd, c.req.path);
62
- if (null === originFilename) return null;
63
- const preCompressedAsset = await resolvePreCompressedAsset(c, originFilename);
64
- if (!preCompressedAsset.acceptable) {
65
- applyPreCompressedAssetHeaders(c, preCompressedAsset);
66
- return c.body(null, 406);
67
- }
68
- const selectedFilename = preCompressedAsset.selected?.filepath ?? originFilename;
69
- const publicDirectory = await fs.realpath(path.join(pwd, 'public'));
70
- let realSelectedFilename;
71
- try {
72
- realSelectedFilename = await fs.realpath(selectedFilename);
73
- } catch {
74
- return null;
75
- }
76
- if (!isPathInside(realSelectedFilename, publicDirectory)) return null;
77
- const data = await fileReader.readFileFromSystem(realSelectedFilename, 'buffer');
78
- if (null === data) return null;
79
- const mimeType = getStaticMimeType(originFilename);
80
- if (mimeType) c.header('Content-Type', mimeType);
81
- applyPreCompressedAssetHeaders(c, preCompressedAsset);
82
- c.header('Content-Length', String(data.byteLength));
83
- if ('HEAD' === method) return c.body(null, 200);
84
- const body = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
85
- return c.body(body, 200);
86
- };
87
- const createModuleFederationStaticServing = ({ pwd, pathPrefix })=>{
88
- let moduleFederationAssets = null;
89
- let moduleFederationAssetsExpiresAt = 0;
90
- let moduleFederationAssetsRefresh = null;
91
- const getModuleFederationAssets = async ()=>{
92
- if (moduleFederationAssets && Date.now() < moduleFederationAssetsExpiresAt) return moduleFederationAssets;
93
- if (!moduleFederationAssetsRefresh) moduleFederationAssetsRefresh = getModuleFederationAssetList(pwd).then((assets)=>{
94
- moduleFederationAssets = assets;
95
- moduleFederationAssetsExpiresAt = Date.now() + MODULE_FEDERATION_ASSET_REFRESH_INTERVAL_MS;
96
- return assets;
97
- }).finally(()=>{
98
- moduleFederationAssetsRefresh = null;
99
- });
100
- return moduleFederationAssetsRefresh;
101
- };
102
- const resolveRequest = async (pathname)=>{
103
- const requestPath = getModuleFederationRequestPath(pathname, pathPrefix);
104
- if (requestPath.includes('..')) return null;
105
- const moduleFederationAssetMeta = await getModuleFederationAssets();
106
- return {
107
- requestPath,
108
- isModuleFederationAsset: moduleFederationAssetMeta.assets.has(requestPath),
109
- isModuleFederationRemoteEntry: moduleFederationAssetMeta.remoteEntries.has(requestPath)
110
- };
111
- };
112
- const serveFile = async (c, filepath, moduleFederationAsset = false, moduleFederationRemoteEntry = false, requestPath = '')=>{
113
- if (moduleFederationAsset) applyModuleFederationAssetHeaders(c);
114
- const mimeType = getStaticMimeType(filepath);
115
- if (mimeType) c.header('Content-Type', mimeType);
116
- const shouldPatchManifest = moduleFederationAsset && isModuleFederationManifestRequest(requestPath) && !isBackendModuleFederationManifestRequest(requestPath);
117
- const shouldPatchRemoteEntry = moduleFederationRemoteEntry;
118
- const canUsePreCompressed = !shouldPatchManifest && !shouldPatchRemoteEntry;
119
- const preCompressedAsset = canUsePreCompressed ? await resolvePreCompressedAsset(c, filepath) : {
120
- selected: null,
121
- hasVariant: false,
122
- acceptable: true
123
- };
124
- if (!preCompressedAsset.acceptable) {
125
- applyPreCompressedAssetHeaders(c, preCompressedAsset);
126
- return c.body(null, 406);
127
- }
128
- const targetFilepath = preCompressedAsset.selected?.filepath ?? filepath;
129
- const chunk = await fileReader.readFileFromSystem(targetFilepath, 'buffer');
130
- if (null === chunk) return null;
131
- const responseChunk = shouldPatchManifest ? patchModuleFederationManifestPublicPath(c, chunk, pathPrefix) : shouldPatchRemoteEntry ? patchModuleFederationRemoteEntryPublicPath(c, chunk, pathPrefix) : chunk;
132
- applyPreCompressedAssetHeaders(c, preCompressedAsset);
133
- c.header('Content-Length', String(responseChunk.byteLength));
134
- const body = new Uint8Array(responseChunk.buffer, responseChunk.byteOffset, responseChunk.byteLength);
135
- return c.body(body, 200);
136
- };
137
- const serveByPath = async (c, filepath, request, moduleFederationAsset = false, moduleFederationRemoteEntry = false)=>{
138
- if (!isPathInside(filepath, pwd)) return null;
139
- if (!await fs.pathExists(filepath)) return null;
140
- return serveFile(c, filepath, moduleFederationAsset, moduleFederationRemoteEntry, request.requestPath);
141
- };
142
- const serveStaticHit = (c, request)=>serveByPath(c, path.join(pwd, request.requestPath), request, request.isModuleFederationAsset, request.isModuleFederationRemoteEntry);
143
- const serveModuleFederationAsset = (c, request)=>{
144
- if (!request.isModuleFederationAsset) return null;
145
- return serveByPath(c, path.join(pwd, request.requestPath), request, true, request.isModuleFederationRemoteEntry);
146
- };
147
- return {
148
- resolveRequest,
149
- serveStaticHit,
150
- serveModuleFederationAsset
151
- };
152
- };
153
- export { createModuleFederationStaticServing, servePreCompressedPublicRouteAsset, servePublicDirectoryAsset };
@@ -1 +0,0 @@
1
- import "node:module";
@@ -1 +0,0 @@
1
- import "node:module";
@@ -1,13 +0,0 @@
1
- import type { Middleware } from '../../../types';
2
- export declare const MODULE_FEDERATION_MANIFEST_FILE = "mf-manifest.json";
3
- export type ModuleFederationServeAssets = {
4
- assets: Set<string>;
5
- remoteEntries: Set<string>;
6
- };
7
- export declare const getModuleFederationRequestPath: (pathname: string, pathPrefix: string) => string;
8
- export declare const isModuleFederationManifestRequest: (requestPath: string) => boolean;
9
- export declare const isBackendModuleFederationManifestRequest: (requestPath: string) => requestPath is "backend-mf-manifest.json";
10
- export declare const applyModuleFederationAssetHeaders: (c: Parameters<Middleware>[0]) => void;
11
- export declare const patchModuleFederationManifestPublicPath: (c: Parameters<Middleware>[0], manifestBuffer: Buffer, pathPrefix: string) => Buffer<ArrayBufferLike>;
12
- export declare const patchModuleFederationRemoteEntryPublicPath: (c: Parameters<Middleware>[0], remoteEntryBuffer: Buffer, pathPrefix: string) => Buffer<ArrayBufferLike>;
13
- export declare const getModuleFederationAssetList: (pwd: string) => Promise<ModuleFederationServeAssets>;
@@ -1,13 +0,0 @@
1
- import type { Middleware } from '../../../types';
2
- type SupportedEncoding = 'br' | 'gzip';
3
- type ResolvePreCompressedAssetResult = {
4
- selected: {
5
- filepath: string;
6
- encoding: SupportedEncoding;
7
- } | null;
8
- hasVariant: boolean;
9
- acceptable: boolean;
10
- };
11
- export declare const resolvePreCompressedAsset: (c: Parameters<Middleware>[0], filepath: string) => Promise<ResolvePreCompressedAssetResult>;
12
- export declare const applyPreCompressedAssetHeaders: (c: Parameters<Middleware>[0], preCompressedAsset: ResolvePreCompressedAssetResult) => void;
13
- export {};
@@ -1,25 +0,0 @@
1
- import type { ServerRoute } from '@modern-js/types';
2
- import type { Middleware } from '../../../types';
3
- type MiddlewareContext = Parameters<Middleware>[0];
4
- type StaticServingRequest = {
5
- requestPath: string;
6
- isModuleFederationAsset: boolean;
7
- isModuleFederationRemoteEntry: boolean;
8
- };
9
- type StaticServingOptions = {
10
- pwd: string;
11
- pathPrefix: string;
12
- };
13
- export declare const servePreCompressedPublicRouteAsset: (c: MiddlewareContext, pwd: string, route: ServerRoute) => Promise<(Response & import("hono").TypedResponse<null, 406, "body">) | (Response & import("hono").TypedResponse<Uint8Array<ArrayBuffer>, 200, "body">) | null>;
14
- /**
15
- * Serves post-build convention assets generated under dist/public at their
16
- * root URL. This is intentionally independent of config/public and route.json:
17
- * the generator runs after the route manifest is built.
18
- */
19
- export declare const servePublicDirectoryAsset: (c: MiddlewareContext, pwd: string) => Promise<(Response & import("hono").TypedResponse<null, 200, "body">) | (Response & import("hono").TypedResponse<null, 406, "body">) | (Response & import("hono").TypedResponse<Uint8Array<ArrayBuffer>, 200, "body">) | null>;
20
- export declare const createModuleFederationStaticServing: ({ pwd, pathPrefix, }: StaticServingOptions) => {
21
- resolveRequest: (pathname: string) => Promise<StaticServingRequest | null>;
22
- serveStaticHit: (c: MiddlewareContext, request: StaticServingRequest) => Promise<(Response & import("hono").TypedResponse<null, 406, "body">) | (Response & import("hono").TypedResponse<Uint8Array<ArrayBuffer>, 200, "body">) | null>;
23
- serveModuleFederationAsset: (c: MiddlewareContext, request: StaticServingRequest) => Promise<(Response & import("hono").TypedResponse<null, 406, "body">) | (Response & import("hono").TypedResponse<Uint8Array<ArrayBuffer>, 200, "body">) | null> | null;
24
- };
25
- export {};
@@ -1,116 +0,0 @@
1
- export type BffRuntimeFramework = 'hono' | 'effect';
2
- export interface BffCrossProjectPolicyUserConfig {
3
- /**
4
- * Enable cross-project envelope and operation-context policy checks.
5
- *
6
- * @default false
7
- */
8
- enabled?: boolean;
9
- /**
10
- * Require cross-project envelope header when policy is enabled.
11
- *
12
- * @default true
13
- */
14
- requireEnvelope?: boolean;
15
- /**
16
- * Require operation-context header when policy is enabled.
17
- *
18
- * @default true
19
- */
20
- requireOperationContext?: boolean;
21
- /**
22
- * Require operation-context detail header carrying schema/version metadata.
23
- *
24
- * @default true
25
- */
26
- requireOperationContextDetails?: boolean;
27
- /**
28
- * Require operation schema hash in operation-context details.
29
- *
30
- * @default true
31
- */
32
- requireOperationSchemaHash?: boolean;
33
- /**
34
- * Require operation version in operation-context details.
35
- *
36
- * @default true
37
- */
38
- requireOperationVersion?: boolean;
39
- /**
40
- * Optional allowlist of producer namespaces derived from requestId.
41
- */
42
- allowedNamespaces?: string[];
43
- /**
44
- * Optional hook deriving a verified producer identity (namespace) from
45
- * request headers, e.g. an mTLS subject or gateway-verified JWT claim.
46
- * When provided, namespace checks bind to this verified value instead of
47
- * the client-asserted requestId namespace; returning `undefined` denies
48
- * the request. Without it the namespace checks are advisory only.
49
- */
50
- verifyProducerIdentity?: (headers: Record<string, unknown>) => string | undefined;
51
- /**
52
- * Optional operation-contract map keyed by:
53
- * - `${METHOD}:${routePath}`
54
- * - `operation:${requestId}:${operationId}`
55
- */
56
- expectedOperationContracts?: Record<string, {
57
- schemaHash?: string;
58
- operationVersion?: number;
59
- }>;
60
- /**
61
- * Allow operations missing from expectedOperationContracts.
62
- *
63
- * @default false
64
- */
65
- allowUnknownOperations?: boolean;
66
- /**
67
- * HTTP status code used for denied requests.
68
- *
69
- * @default 403
70
- */
71
- denyStatus?: number;
72
- }
73
- export type BffEffectOpenApiUserConfig = boolean | {
74
- path?: string;
75
- };
76
- export interface BffEffectDataPlatformSelectionUserConfig {
77
- maxDepth?: number;
78
- maxFields?: number;
79
- allowedLeafPaths?: string[];
80
- }
81
- export interface BffEffectDataPlatformBatchUserConfig {
82
- enabled?: boolean;
83
- endpoint?: `/${string}`;
84
- maxBatchSize?: number;
85
- maxBatchBytes?: number;
86
- flushIntervalMs?: number;
87
- maxConcurrency?: number;
88
- requestTimeoutMs?: number;
89
- allowedMethods?: string[];
90
- }
91
- export interface BffEffectDataPlatformUserConfig {
92
- enabled?: boolean;
93
- requireEnvelope?: boolean;
94
- envelopeHeader?: string;
95
- expectedNamespace?: string;
96
- validateOrigin?: boolean;
97
- requireTraceContext?: boolean;
98
- selection?: BffEffectDataPlatformSelectionUserConfig;
99
- batch?: BffEffectDataPlatformBatchUserConfig;
100
- }
101
- export interface BffEffectUserConfig {
102
- entry?: string;
103
- /**
104
- * Enforce Effect-native API/runtime modules instead of raw request handlers.
105
- *
106
- * When enabled, Effect API entries must export a `defineEffectBff(...)`
107
- * definition or a `{ api, layer }` HttpApi module. Raw `handler` exports,
108
- * default request handlers, and unbranded custom `createHandler` factories
109
- * are treated as legacy escape hatches.
110
- *
111
- * @default true
112
- */
113
- strictEffectApproach?: true;
114
- openapi?: BffEffectOpenApiUserConfig;
115
- dataPlatform?: BffEffectDataPlatformUserConfig;
116
- }
@@ -1,319 +0,0 @@
1
- export interface ServerTelemetryExporterOptions {
2
- enabled?: boolean;
3
- endpoint?: string;
4
- headers?: Record<string, string>;
5
- timeoutMs?: number;
6
- }
7
- export interface ServerTelemetryVictoriaMetricsOptions extends ServerTelemetryExporterOptions {
8
- metricPrefix?: string;
9
- }
10
- export interface ServerTelemetrySloUserConfig {
11
- /**
12
- * Queue utilization ratio threshold that emits SLO degradation alerts.
13
- *
14
- * @default 0.8
15
- */
16
- queueUtilizationWarnThreshold?: number;
17
- /**
18
- * Total dropped-envelope threshold that emits SLO degradation alerts.
19
- *
20
- * @default 1
21
- */
22
- queueDroppedWarnThreshold?: number;
23
- /**
24
- * Cooldown between SLO alert emissions for the same alert type.
25
- *
26
- * @default 60000
27
- */
28
- alertCooldownMs?: number;
29
- }
30
- export interface ServerTelemetryCanaryContractGateUserConfig {
31
- /**
32
- * Whether this contract gate currently passes.
33
- */
34
- passed: boolean;
35
- /**
36
- * Optional failure reason used for rollback diagnostics.
37
- */
38
- reason?: string;
39
- }
40
- export interface ServerTelemetryCanaryAutopilotUserConfig {
41
- /**
42
- * Enable automatic contract gate synchronization from a gate snapshot file.
43
- *
44
- * @default true
45
- */
46
- enabled?: boolean;
47
- /**
48
- * Path to contract gate snapshot JSON file.
49
- *
50
- * @default ".modern/contract-gates.json"
51
- */
52
- gateSnapshotPath?: string;
53
- /**
54
- * Poll interval for reading gate snapshot changes.
55
- *
56
- * @default 15000
57
- */
58
- pollIntervalMs?: number;
59
- /**
60
- * Marks gate entries as failed when they are older than this threshold.
61
- *
62
- * @default 600000
63
- */
64
- gateStaleAfterMs?: number;
65
- /**
66
- * Runtime MF fallback signal ingestion.
67
- */
68
- runtimeFallbackSignal?: ServerTelemetryCanaryRuntimeFallbackSignalUserConfig;
69
- /**
70
- * Optional pluggable state store backend for contract gate snapshots.
71
- * When omitted, snapshots are read/written from gateSnapshotPath on local disk.
72
- */
73
- stateStore?: ServerTelemetryCanaryAutopilotStateStoreUserConfig;
74
- }
75
- export interface ServerTelemetryCanaryAutopilotStateStoreUserConfig {
76
- /**
77
- * Path or package name of a module that exports
78
- * `createContractGateSnapshotStore(context)`.
79
- */
80
- module: string;
81
- /**
82
- * Optional adapter-specific configuration.
83
- */
84
- options?: Record<string, unknown>;
85
- }
86
- export interface ServerTelemetryCanaryRuntimeFallbackSignalUserConfig {
87
- /**
88
- * Enable runtime MF fallback signal ingestion endpoint.
89
- *
90
- * Opt-in: when enabled, `auth` must be configured with a token
91
- * (`expectedValue` or `expectedValueEnv`) or server setup throws.
92
- *
93
- * @default false
94
- */
95
- enabled?: boolean;
96
- /**
97
- * HTTP endpoint path for runtime fallback events.
98
- *
99
- * @default "/_modern/contract-gates/runtime-fallback"
100
- */
101
- endpoint?: string;
102
- /**
103
- * Contract gate name updated by runtime fallback events.
104
- *
105
- * @default "runtime-mf-fallback-health"
106
- */
107
- gateName?: string;
108
- /**
109
- * How long a runtime fallback signal should hold gate failure status.
110
- *
111
- * @default 300000
112
- */
113
- failureHoldMs?: number;
114
- /**
115
- * Maximum accepted request body size in bytes.
116
- *
117
- * @default 16384
118
- */
119
- maxBodyBytes?: number;
120
- /**
121
- * Optional runtime trust policy for fallback signal ingestion.
122
- * Use this to restrict who can mutate canary contract gates.
123
- */
124
- trustPolicy?: ServerTelemetryCanaryRuntimeFallbackSignalTrustPolicyUserConfig;
125
- /**
126
- * Optional request authentication for runtime fallback signal endpoint.
127
- */
128
- auth?: ServerTelemetryCanaryRuntimeFallbackSignalAuthUserConfig;
129
- }
130
- export interface ServerTelemetryCanaryRuntimeFallbackSignalAuthUserConfig {
131
- /**
132
- * Enable auth guard for runtime fallback signal endpoint.
133
- *
134
- * @default false
135
- */
136
- enabled?: boolean;
137
- /**
138
- * Header name carrying runtime fallback auth token.
139
- *
140
- * @default "x-modernjs-runtime-signal-token"
141
- */
142
- headerName?: string;
143
- /**
144
- * Expected token value. Prefer using expectedValueEnv in production.
145
- */
146
- expectedValue?: string;
147
- /**
148
- * Name of environment variable that stores expected token value.
149
- */
150
- expectedValueEnv?: string;
151
- }
152
- export interface ServerTelemetryCanaryRuntimeFallbackSignalTrustPolicyUserConfig {
153
- /**
154
- * Allowlist of app names accepted by runtime fallback signal endpoint.
155
- * Empty means no app-name allowlist check.
156
- */
157
- allowedApps?: string[];
158
- /**
159
- * Allowlist of entry origins accepted by runtime fallback signal endpoint.
160
- * Values should be URL origins (for example https://erp.example.com).
161
- * Empty means no entry-origin allowlist check.
162
- */
163
- allowedEntryOrigins?: string[];
164
- /**
165
- * Expected runtime digest per appName.
166
- */
167
- expectedRuntimeDigests?: Record<string, string>;
168
- /**
169
- * Require runtimeDigest to be present in signal payload metadata.
170
- *
171
- * @default false
172
- */
173
- enforceRuntimeDigest?: boolean;
174
- /**
175
- * Maximum accepted signals per app+origin window.
176
- *
177
- * @default 30
178
- */
179
- maxSignalsPerWindow?: number;
180
- /**
181
- * Sliding window size in milliseconds for maxSignalsPerWindow.
182
- *
183
- * @default 60000
184
- */
185
- windowMs?: number;
186
- /**
187
- * Drop duplicate fallback events with the same fingerprint during this window.
188
- *
189
- * @default 10000
190
- */
191
- dedupeWindowMs?: number;
192
- }
193
- export interface ServerTelemetryCanaryUserConfig {
194
- /**
195
- * Enable canary rollout/rollback orchestration.
196
- *
197
- * @default false
198
- */
199
- enabled?: boolean;
200
- /**
201
- * Periodic canary evaluation interval in milliseconds.
202
- *
203
- * @default 15000
204
- */
205
- evaluationIntervalMs?: number;
206
- /**
207
- * Required consecutive healthy evaluations before promotion.
208
- *
209
- * @default 3
210
- */
211
- minConsecutiveHealthyEvaluations?: number;
212
- /**
213
- * Consecutive failing evaluations before automated rollback.
214
- *
215
- * @default 2
216
- */
217
- rollbackConsecutiveFailures?: number;
218
- /**
219
- * Maximum queue utilization ratio allowed during canary.
220
- *
221
- * @default 0.8
222
- */
223
- maxQueueUtilization?: number;
224
- /**
225
- * Maximum allowed total dropped envelopes during canary.
226
- *
227
- * @default 0
228
- */
229
- maxTotalDropped?: number;
230
- /**
231
- * Maximum allowed unhealthy exporters during canary.
232
- *
233
- * @default 0
234
- */
235
- maxUnhealthyExporters?: number;
236
- /**
237
- * Contract gate map used in rollout decisions.
238
- * `true` means passing, `false` means failing.
239
- */
240
- contractGates?: Record<string, boolean | ServerTelemetryCanaryContractGateUserConfig>;
241
- /**
242
- * Contract-gate autopilot settings.
243
- */
244
- autopilot?: ServerTelemetryCanaryAutopilotUserConfig;
245
- }
246
- export interface ServerTelemetryUserConfig {
247
- /**
248
- * Enable framework telemetry envelope emission.
249
- * @default false
250
- */
251
- enabled?: boolean;
252
- /**
253
- * Logical service name attached to every telemetry envelope.
254
- * @default server.metaName
255
- */
256
- service?: string;
257
- /**
258
- * Logical module name attached to every telemetry envelope.
259
- * @default "server"
260
- */
261
- module?: string;
262
- /**
263
- * Environment attached to every telemetry envelope.
264
- * @default process.env.NODE_ENV || "development"
265
- */
266
- environment?: string;
267
- /**
268
- * Sampling rate for monitor events.
269
- * @default 1
270
- */
271
- samplingRate?: number;
272
- /**
273
- * Flush window in milliseconds for exporter batches.
274
- * @default 1000
275
- */
276
- flushIntervalMs?: number;
277
- /**
278
- * Maximum envelopes in one emitted batch.
279
- * @default 50
280
- */
281
- maxBatchSize?: number;
282
- /**
283
- * Maximum envelopes buffered before backpressure drops oldest.
284
- * @default 1000
285
- */
286
- maxQueueSize?: number;
287
- /**
288
- * Envelope attribute keys that should be redacted.
289
- */
290
- redactionKeys?: string[];
291
- /**
292
- * Control startup exporter health probe behavior.
293
- * When enabled (default), server initialization emits a startup probe and
294
- * marks exporters healthy/unhealthy before serving traffic.
295
- * When fail-loud mode is enabled (default), initialization throws if at
296
- * least one configured exporter is unhealthy.
297
- *
298
- * @default true
299
- */
300
- failLoudStartup?: boolean;
301
- /**
302
- * Queue backpressure/degradation SLO alert thresholds.
303
- */
304
- slo?: ServerTelemetrySloUserConfig;
305
- /**
306
- * Canary rollout and automated rollback orchestration policy.
307
- */
308
- canary?: ServerTelemetryCanaryUserConfig;
309
- exporters?: {
310
- /**
311
- * OpenTelemetry HTTP exporter.
312
- */
313
- otlp?: ServerTelemetryExporterOptions;
314
- /**
315
- * VictoriaMetrics Prometheus import exporter.
316
- */
317
- victoriaMetrics?: ServerTelemetryVictoriaMetricsOptions;
318
- };
319
- }