@bleedingdev/modern-js-server-core 3.2.0-ultramodern.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/LICENSE +21 -0
- package/README.md +26 -0
- package/dist/cjs/adapters/node/helper/index.js +55 -0
- package/dist/cjs/adapters/node/helper/loadCache.js +58 -0
- package/dist/cjs/adapters/node/helper/loadConfig.js +78 -0
- package/dist/cjs/adapters/node/helper/loadEnv.js +66 -0
- package/dist/cjs/adapters/node/helper/loadPlugin.js +47 -0
- package/dist/cjs/adapters/node/helper/utils.js +39 -0
- package/dist/cjs/adapters/node/hono.js +95 -0
- package/dist/cjs/adapters/node/index.js +87 -0
- package/dist/cjs/adapters/node/node.js +180 -0
- package/dist/cjs/adapters/node/plugins/index.js +72 -0
- package/dist/cjs/adapters/node/plugins/nodeServer.js +43 -0
- package/dist/cjs/adapters/node/plugins/resource.js +206 -0
- package/dist/cjs/adapters/node/plugins/static.js +379 -0
- package/dist/cjs/constants.js +77 -0
- package/dist/cjs/context.js +41 -0
- package/dist/cjs/helper.js +45 -0
- package/dist/cjs/hono.js +46 -0
- package/dist/cjs/index.js +247 -0
- package/dist/cjs/plugins/compat/hooks.js +80 -0
- package/dist/cjs/plugins/compat/index.js +64 -0
- package/dist/cjs/plugins/contractGateAutopilot.js +158 -0
- package/dist/cjs/plugins/contractGateSnapshotStore.js +239 -0
- package/dist/cjs/plugins/default.js +61 -0
- package/dist/cjs/plugins/favicon.js +48 -0
- package/dist/cjs/plugins/index.js +157 -0
- package/dist/cjs/plugins/log.js +92 -0
- package/dist/cjs/plugins/mfCache.js +78 -0
- package/dist/cjs/plugins/middlewares.js +49 -0
- package/dist/cjs/plugins/monitors.js +192 -0
- package/dist/cjs/plugins/processedBy.js +50 -0
- package/dist/cjs/plugins/render/csrRscRender.js +74 -0
- package/dist/cjs/plugins/render/dataHandler.js +53 -0
- package/dist/cjs/plugins/render/index.js +158 -0
- package/dist/cjs/plugins/render/inject.js +92 -0
- package/dist/cjs/plugins/render/render.js +273 -0
- package/dist/cjs/plugins/render/renderRscHandler.js +72 -0
- package/dist/cjs/plugins/render/serverActionHandler.js +47 -0
- package/dist/cjs/plugins/render/ssrCache.js +198 -0
- package/dist/cjs/plugins/render/ssrRender.js +96 -0
- package/dist/cjs/plugins/render/utils.js +49 -0
- package/dist/cjs/plugins/route.js +68 -0
- package/dist/cjs/plugins/telemetry.js +1283 -0
- package/dist/cjs/serverBase.js +176 -0
- package/dist/cjs/types/config/bff.js +18 -0
- package/dist/cjs/types/config/dev.js +18 -0
- package/dist/cjs/types/config/html.js +18 -0
- package/dist/cjs/types/config/index.js +93 -0
- package/dist/cjs/types/config/output.js +18 -0
- package/dist/cjs/types/config/security.js +18 -0
- package/dist/cjs/types/config/server.js +18 -0
- package/dist/cjs/types/config/share.js +18 -0
- package/dist/cjs/types/config/source.js +18 -0
- package/dist/cjs/types/config/tools.js +18 -0
- package/dist/cjs/types/index.js +79 -0
- package/dist/cjs/types/plugins/base.js +18 -0
- package/dist/cjs/types/plugins/index.js +58 -0
- package/dist/cjs/types/plugins/plugin.js +18 -0
- package/dist/cjs/types/render.js +18 -0
- package/dist/cjs/types/requestHandler.js +18 -0
- package/dist/cjs/types/server.js +18 -0
- package/dist/cjs/utils/entry.js +40 -0
- package/dist/cjs/utils/env.js +51 -0
- package/dist/cjs/utils/error.js +93 -0
- package/dist/cjs/utils/index.js +114 -0
- package/dist/cjs/utils/middlewareCollector.js +63 -0
- package/dist/cjs/utils/publicDir.js +92 -0
- package/dist/cjs/utils/request.js +86 -0
- package/dist/cjs/utils/serverConfig.js +43 -0
- package/dist/cjs/utils/storage.js +69 -0
- package/dist/cjs/utils/transformStream.js +65 -0
- package/dist/cjs/utils/warmup.js +40 -0
- package/dist/esm/adapters/node/helper/index.mjs +5 -0
- package/dist/esm/adapters/node/helper/loadCache.mjs +14 -0
- package/dist/esm/adapters/node/helper/loadConfig.mjs +31 -0
- package/dist/esm/adapters/node/helper/loadEnv.mjs +22 -0
- package/dist/esm/adapters/node/helper/loadPlugin.mjs +13 -0
- package/dist/esm/adapters/node/helper/utils.mjs +5 -0
- package/dist/esm/adapters/node/hono.mjs +55 -0
- package/dist/esm/adapters/node/index.mjs +4 -0
- package/dist/esm/adapters/node/node.mjs +130 -0
- package/dist/esm/adapters/node/plugins/index.mjs +3 -0
- package/dist/esm/adapters/node/plugins/nodeServer.mjs +9 -0
- package/dist/esm/adapters/node/plugins/resource.mjs +138 -0
- package/dist/esm/adapters/node/plugins/static.mjs +329 -0
- package/dist/esm/constants.mjs +28 -0
- package/dist/esm/context.mjs +4 -0
- package/dist/esm/helper.mjs +11 -0
- package/dist/esm/hono.mjs +2 -0
- package/dist/esm/index.mjs +12 -0
- package/dist/esm/plugins/compat/hooks.mjs +40 -0
- package/dist/esm/plugins/compat/index.mjs +27 -0
- package/dist/esm/plugins/contractGateAutopilot.mjs +124 -0
- package/dist/esm/plugins/contractGateSnapshotStore.mjs +180 -0
- package/dist/esm/plugins/default.mjs +27 -0
- package/dist/esm/plugins/favicon.mjs +14 -0
- package/dist/esm/plugins/index.mjs +11 -0
- package/dist/esm/plugins/log.mjs +58 -0
- package/dist/esm/plugins/mfCache.mjs +35 -0
- package/dist/esm/plugins/middlewares.mjs +15 -0
- package/dist/esm/plugins/monitors.mjs +149 -0
- package/dist/esm/plugins/processedBy.mjs +16 -0
- package/dist/esm/plugins/render/csrRscRender.mjs +40 -0
- package/dist/esm/plugins/render/dataHandler.mjs +19 -0
- package/dist/esm/plugins/render/index.mjs +84 -0
- package/dist/esm/plugins/render/inject.mjs +55 -0
- package/dist/esm/plugins/render/render.mjs +230 -0
- package/dist/esm/plugins/render/renderRscHandler.mjs +38 -0
- package/dist/esm/plugins/render/serverActionHandler.mjs +13 -0
- package/dist/esm/plugins/render/ssrCache.mjs +158 -0
- package/dist/esm/plugins/render/ssrRender.mjs +62 -0
- package/dist/esm/plugins/render/utils.mjs +15 -0
- package/dist/esm/plugins/route.mjs +34 -0
- package/dist/esm/plugins/telemetry.mjs +1195 -0
- package/dist/esm/rslib-runtime.mjs +18 -0
- package/dist/esm/serverBase.mjs +139 -0
- package/dist/esm/types/config/bff.mjs +0 -0
- package/dist/esm/types/config/dev.mjs +0 -0
- package/dist/esm/types/config/html.mjs +0 -0
- package/dist/esm/types/config/index.mjs +6 -0
- package/dist/esm/types/config/output.mjs +0 -0
- package/dist/esm/types/config/security.mjs +0 -0
- package/dist/esm/types/config/server.mjs +0 -0
- package/dist/esm/types/config/share.mjs +0 -0
- package/dist/esm/types/config/source.mjs +0 -0
- package/dist/esm/types/config/tools.mjs +0 -0
- package/dist/esm/types/index.mjs +4 -0
- package/dist/esm/types/plugins/base.mjs +0 -0
- package/dist/esm/types/plugins/index.mjs +1 -0
- package/dist/esm/types/plugins/plugin.mjs +0 -0
- package/dist/esm/types/render.mjs +0 -0
- package/dist/esm/types/requestHandler.mjs +0 -0
- package/dist/esm/types/server.mjs +0 -0
- package/dist/esm/utils/entry.mjs +3 -0
- package/dist/esm/utils/env.mjs +14 -0
- package/dist/esm/utils/error.mjs +53 -0
- package/dist/esm/utils/index.mjs +9 -0
- package/dist/esm/utils/middlewareCollector.mjs +26 -0
- package/dist/esm/utils/publicDir.mjs +33 -0
- package/dist/esm/utils/request.mjs +40 -0
- package/dist/esm/utils/serverConfig.mjs +9 -0
- package/dist/esm/utils/storage.mjs +35 -0
- package/dist/esm/utils/transformStream.mjs +28 -0
- package/dist/esm/utils/warmup.mjs +6 -0
- package/dist/esm-node/adapters/node/helper/index.mjs +6 -0
- package/dist/esm-node/adapters/node/helper/loadCache.mjs +15 -0
- package/dist/esm-node/adapters/node/helper/loadConfig.mjs +32 -0
- package/dist/esm-node/adapters/node/helper/loadEnv.mjs +23 -0
- package/dist/esm-node/adapters/node/helper/loadPlugin.mjs +14 -0
- package/dist/esm-node/adapters/node/helper/utils.mjs +6 -0
- package/dist/esm-node/adapters/node/hono.mjs +56 -0
- package/dist/esm-node/adapters/node/index.mjs +5 -0
- package/dist/esm-node/adapters/node/node.mjs +131 -0
- package/dist/esm-node/adapters/node/plugins/index.mjs +4 -0
- package/dist/esm-node/adapters/node/plugins/nodeServer.mjs +10 -0
- package/dist/esm-node/adapters/node/plugins/resource.mjs +139 -0
- package/dist/esm-node/adapters/node/plugins/static.mjs +330 -0
- package/dist/esm-node/constants.mjs +29 -0
- package/dist/esm-node/context.mjs +5 -0
- package/dist/esm-node/helper.mjs +12 -0
- package/dist/esm-node/hono.mjs +3 -0
- package/dist/esm-node/index.mjs +13 -0
- package/dist/esm-node/plugins/compat/hooks.mjs +41 -0
- package/dist/esm-node/plugins/compat/index.mjs +28 -0
- package/dist/esm-node/plugins/contractGateAutopilot.mjs +125 -0
- package/dist/esm-node/plugins/contractGateSnapshotStore.mjs +182 -0
- package/dist/esm-node/plugins/default.mjs +28 -0
- package/dist/esm-node/plugins/favicon.mjs +15 -0
- package/dist/esm-node/plugins/index.mjs +12 -0
- package/dist/esm-node/plugins/log.mjs +59 -0
- package/dist/esm-node/plugins/mfCache.mjs +36 -0
- package/dist/esm-node/plugins/middlewares.mjs +16 -0
- package/dist/esm-node/plugins/monitors.mjs +150 -0
- package/dist/esm-node/plugins/processedBy.mjs +17 -0
- package/dist/esm-node/plugins/render/csrRscRender.mjs +41 -0
- package/dist/esm-node/plugins/render/dataHandler.mjs +20 -0
- package/dist/esm-node/plugins/render/index.mjs +85 -0
- package/dist/esm-node/plugins/render/inject.mjs +56 -0
- package/dist/esm-node/plugins/render/render.mjs +231 -0
- package/dist/esm-node/plugins/render/renderRscHandler.mjs +39 -0
- package/dist/esm-node/plugins/render/serverActionHandler.mjs +14 -0
- package/dist/esm-node/plugins/render/ssrCache.mjs +159 -0
- package/dist/esm-node/plugins/render/ssrRender.mjs +63 -0
- package/dist/esm-node/plugins/render/utils.mjs +16 -0
- package/dist/esm-node/plugins/route.mjs +35 -0
- package/dist/esm-node/plugins/telemetry.mjs +1196 -0
- package/dist/esm-node/rslib-runtime.mjs +19 -0
- package/dist/esm-node/serverBase.mjs +140 -0
- package/dist/esm-node/types/config/bff.mjs +1 -0
- package/dist/esm-node/types/config/dev.mjs +1 -0
- package/dist/esm-node/types/config/html.mjs +1 -0
- package/dist/esm-node/types/config/index.mjs +7 -0
- package/dist/esm-node/types/config/output.mjs +1 -0
- package/dist/esm-node/types/config/security.mjs +1 -0
- package/dist/esm-node/types/config/server.mjs +1 -0
- package/dist/esm-node/types/config/share.mjs +1 -0
- package/dist/esm-node/types/config/source.mjs +1 -0
- package/dist/esm-node/types/config/tools.mjs +1 -0
- package/dist/esm-node/types/index.mjs +5 -0
- package/dist/esm-node/types/plugins/base.mjs +1 -0
- package/dist/esm-node/types/plugins/index.mjs +2 -0
- package/dist/esm-node/types/plugins/plugin.mjs +1 -0
- package/dist/esm-node/types/render.mjs +1 -0
- package/dist/esm-node/types/requestHandler.mjs +1 -0
- package/dist/esm-node/types/server.mjs +1 -0
- package/dist/esm-node/utils/entry.mjs +4 -0
- package/dist/esm-node/utils/env.mjs +15 -0
- package/dist/esm-node/utils/error.mjs +54 -0
- package/dist/esm-node/utils/index.mjs +10 -0
- package/dist/esm-node/utils/middlewareCollector.mjs +27 -0
- package/dist/esm-node/utils/publicDir.mjs +34 -0
- package/dist/esm-node/utils/request.mjs +41 -0
- package/dist/esm-node/utils/serverConfig.mjs +10 -0
- package/dist/esm-node/utils/storage.mjs +36 -0
- package/dist/esm-node/utils/transformStream.mjs +29 -0
- package/dist/esm-node/utils/warmup.mjs +7 -0
- package/dist/types/adapters/node/helper/index.d.ts +6 -0
- package/dist/types/adapters/node/helper/loadCache.d.ts +2 -0
- package/dist/types/adapters/node/helper/loadConfig.d.ts +3 -0
- package/dist/types/adapters/node/helper/loadEnv.d.ts +3 -0
- package/dist/types/adapters/node/helper/loadPlugin.d.ts +3 -0
- package/dist/types/adapters/node/helper/utils.d.ts +21 -0
- package/dist/types/adapters/node/hono.d.ts +19 -0
- package/dist/types/adapters/node/index.d.ts +5 -0
- package/dist/types/adapters/node/node.d.ts +17 -0
- package/dist/types/adapters/node/plugins/index.d.ts +3 -0
- package/dist/types/adapters/node/plugins/nodeServer.d.ts +6 -0
- package/dist/types/adapters/node/plugins/resource.d.ts +11 -0
- package/dist/types/adapters/node/plugins/static.d.ts +25 -0
- package/dist/types/constants.d.ts +26 -0
- package/dist/types/context.d.ts +3 -0
- package/dist/types/helper.d.ts +10 -0
- package/dist/types/hono.d.ts +3 -0
- package/dist/types/index.d.ts +14 -0
- package/dist/types/plugins/compat/hooks.d.ts +8 -0
- package/dist/types/plugins/compat/index.d.ts +3 -0
- package/dist/types/plugins/contractGateAutopilot.d.ts +35 -0
- package/dist/types/plugins/contractGateSnapshotStore.d.ts +57 -0
- package/dist/types/plugins/default.d.ts +7 -0
- package/dist/types/plugins/favicon.d.ts +2 -0
- package/dist/types/plugins/index.d.ts +11 -0
- package/dist/types/plugins/log.d.ts +2 -0
- package/dist/types/plugins/mfCache.d.ts +12 -0
- package/dist/types/plugins/middlewares.d.ts +2 -0
- package/dist/types/plugins/monitors.d.ts +6 -0
- package/dist/types/plugins/processedBy.d.ts +2 -0
- package/dist/types/plugins/render/csrRscRender.d.ts +2 -0
- package/dist/types/plugins/render/dataHandler.d.ts +5 -0
- package/dist/types/plugins/render/index.d.ts +3 -0
- package/dist/types/plugins/render/inject.d.ts +7 -0
- package/dist/types/plugins/render/render.d.ts +16 -0
- package/dist/types/plugins/render/renderRscHandler.d.ts +2 -0
- package/dist/types/plugins/render/serverActionHandler.d.ts +2 -0
- package/dist/types/plugins/render/ssrCache.d.ts +18 -0
- package/dist/types/plugins/render/ssrRender.d.ts +26 -0
- package/dist/types/plugins/render/utils.d.ts +3 -0
- package/dist/types/plugins/route.d.ts +2 -0
- package/dist/types/plugins/telemetry.d.ts +309 -0
- package/dist/types/serverBase.d.ts +38 -0
- package/dist/types/types/config/bff.d.ts +142 -0
- package/dist/types/types/config/dev.d.ts +4 -0
- package/dist/types/types/config/html.d.ts +15 -0
- package/dist/types/types/config/index.d.ts +35 -0
- package/dist/types/types/config/output.d.ts +20 -0
- package/dist/types/types/config/security.d.ts +4 -0
- package/dist/types/types/config/server.d.ts +402 -0
- package/dist/types/types/config/share.d.ts +3 -0
- package/dist/types/types/config/source.d.ts +7 -0
- package/dist/types/types/config/tools.d.ts +2 -0
- package/dist/types/types/index.d.ts +4 -0
- package/dist/types/types/plugins/base.d.ts +57 -0
- package/dist/types/types/plugins/index.d.ts +2 -0
- package/dist/types/types/plugins/plugin.d.ts +31 -0
- package/dist/types/types/render.d.ts +24 -0
- package/dist/types/types/requestHandler.d.ts +48 -0
- package/dist/types/types/server.d.ts +67 -0
- package/dist/types/utils/entry.d.ts +3 -0
- package/dist/types/utils/env.d.ts +2 -0
- package/dist/types/utils/error.d.ts +8 -0
- package/dist/types/utils/index.d.ts +9 -0
- package/dist/types/utils/middlewareCollector.d.ts +12 -0
- package/dist/types/utils/publicDir.d.ts +40 -0
- package/dist/types/utils/request.d.ts +17 -0
- package/dist/types/utils/serverConfig.d.ts +8 -0
- package/dist/types/utils/storage.d.ts +5 -0
- package/dist/types/utils/transformStream.d.ts +5 -0
- package/dist/types/utils/warmup.d.ts +1 -0
- package/package.json +103 -0
- package/rslib.config.mts +4 -0
- package/rstest.config.mts +7 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import { fileReader } from "@modern-js/runtime-utils/fileReader";
|
|
2
|
+
import { fs } from "@modern-js/utils";
|
|
3
|
+
import { getMimeType } from "hono/utils/mime";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { sortRoutes } from "../../../utils/index.mjs";
|
|
6
|
+
import { getPublicDirPatterns } from "../../../utils/publicDir.mjs";
|
|
7
|
+
const serverStaticPlugin = ()=>({
|
|
8
|
+
name: '@modern-js/plugin-server-static',
|
|
9
|
+
setup (api) {
|
|
10
|
+
api.onPrepare(()=>{
|
|
11
|
+
const { middlewares, distDirectory: pwd, routes } = api.getServerContext();
|
|
12
|
+
const config = api.getServerConfig();
|
|
13
|
+
const serverStaticMiddleware = createStaticMiddleware({
|
|
14
|
+
pwd: pwd,
|
|
15
|
+
routes,
|
|
16
|
+
output: config.output || {},
|
|
17
|
+
html: config.html || {},
|
|
18
|
+
server: config.server || {}
|
|
19
|
+
});
|
|
20
|
+
middlewares.push({
|
|
21
|
+
name: 'server-static',
|
|
22
|
+
handler: serverStaticMiddleware
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
const PRE_COMPRESSED_ASSET_EXTENSIONS = {
|
|
28
|
+
br: '.br',
|
|
29
|
+
gzip: '.gz'
|
|
30
|
+
};
|
|
31
|
+
const PRE_COMPRESSED_SUPPORTED_ENCODINGS = [
|
|
32
|
+
'br',
|
|
33
|
+
'gzip'
|
|
34
|
+
];
|
|
35
|
+
const parseAcceptEncoding = (value)=>value.split(',').map((item)=>item.trim()).filter(Boolean).map((item)=>{
|
|
36
|
+
const [rawName, ...params] = item.split(';');
|
|
37
|
+
const name = rawName.trim().toLowerCase();
|
|
38
|
+
let q = 1;
|
|
39
|
+
for (const param of params){
|
|
40
|
+
const [key, rawValue] = param.split('=').map((v)=>v.trim());
|
|
41
|
+
if ('q' !== key.toLowerCase() || null == rawValue) continue;
|
|
42
|
+
const parsedQ = Number(rawValue);
|
|
43
|
+
if (!Number.isNaN(parsedQ)) q = Math.max(0, Math.min(parsedQ, 1));
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
name,
|
|
47
|
+
q
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
const getAcceptedEncodings = (value)=>{
|
|
51
|
+
if (!value) return [];
|
|
52
|
+
const parsed = parseAcceptEncoding(value);
|
|
53
|
+
const qualityByEncoding = new Map();
|
|
54
|
+
let wildcardQuality;
|
|
55
|
+
for (const { name, q } of parsed){
|
|
56
|
+
if ('*' === name) {
|
|
57
|
+
wildcardQuality = q;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
qualityByEncoding.set(name, q);
|
|
61
|
+
}
|
|
62
|
+
const getQuality = (encoding)=>{
|
|
63
|
+
const explicit = qualityByEncoding.get(encoding);
|
|
64
|
+
if (void 0 !== explicit) return explicit;
|
|
65
|
+
return wildcardQuality ?? 0;
|
|
66
|
+
};
|
|
67
|
+
return PRE_COMPRESSED_SUPPORTED_ENCODINGS.map((encoding)=>({
|
|
68
|
+
encoding,
|
|
69
|
+
quality: getQuality(encoding)
|
|
70
|
+
})).filter((item)=>item.quality > 0).sort((a, b)=>b.quality - a.quality).map((item)=>item.encoding);
|
|
71
|
+
};
|
|
72
|
+
const appendVaryHeader = (c, value)=>{
|
|
73
|
+
const current = c.res.headers.get('Vary');
|
|
74
|
+
if (!current) return void c.header('Vary', value);
|
|
75
|
+
const values = current.split(',').map((item)=>item.trim().toLowerCase()).filter(Boolean);
|
|
76
|
+
if (!values.includes(value.toLowerCase())) c.header('Vary', `${current}, ${value}`);
|
|
77
|
+
};
|
|
78
|
+
const resolvePreCompressedAsset = async (c, filepath)=>{
|
|
79
|
+
const brPath = `${filepath}${PRE_COMPRESSED_ASSET_EXTENSIONS.br}`;
|
|
80
|
+
const gzipPath = `${filepath}${PRE_COMPRESSED_ASSET_EXTENSIONS.gzip}`;
|
|
81
|
+
const [hasBr, hasGzip] = await Promise.all([
|
|
82
|
+
fs.pathExists(brPath),
|
|
83
|
+
fs.pathExists(gzipPath)
|
|
84
|
+
]);
|
|
85
|
+
const hasVariant = hasBr || hasGzip;
|
|
86
|
+
if (!hasVariant) return {
|
|
87
|
+
selected: null,
|
|
88
|
+
hasVariant: false
|
|
89
|
+
};
|
|
90
|
+
const acceptedEncodings = getAcceptedEncodings(c.req.header('accept-encoding'));
|
|
91
|
+
for (const encoding of acceptedEncodings){
|
|
92
|
+
if ('br' === encoding && hasBr) return {
|
|
93
|
+
selected: {
|
|
94
|
+
filepath: brPath,
|
|
95
|
+
encoding
|
|
96
|
+
},
|
|
97
|
+
hasVariant: true
|
|
98
|
+
};
|
|
99
|
+
if ('gzip' === encoding && hasGzip) return {
|
|
100
|
+
selected: {
|
|
101
|
+
filepath: gzipPath,
|
|
102
|
+
encoding
|
|
103
|
+
},
|
|
104
|
+
hasVariant: true
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
selected: null,
|
|
109
|
+
hasVariant: true
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
function createPublicMiddleware({ pwd, routes }) {
|
|
113
|
+
return async (c, next)=>{
|
|
114
|
+
const route = matchPublicRoute(c.req, routes);
|
|
115
|
+
if (route) {
|
|
116
|
+
const { entryPath } = route;
|
|
117
|
+
const originFilename = path.join(pwd, entryPath);
|
|
118
|
+
const preCompressedAsset = await resolvePreCompressedAsset(c, originFilename);
|
|
119
|
+
const filename = preCompressedAsset.selected?.filepath ?? originFilename;
|
|
120
|
+
const data = await fileReader.readFile(filename, 'buffer');
|
|
121
|
+
const mimeType = getMimeType(originFilename);
|
|
122
|
+
if (null !== data) {
|
|
123
|
+
const body = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
124
|
+
if (mimeType) c.header('Content-Type', mimeType);
|
|
125
|
+
Object.entries(route.responseHeaders || {}).forEach(([k, v])=>{
|
|
126
|
+
c.header(k, v);
|
|
127
|
+
});
|
|
128
|
+
if (preCompressedAsset.hasVariant) appendVaryHeader(c, 'Accept-Encoding');
|
|
129
|
+
if (preCompressedAsset.selected) c.header('Content-Encoding', preCompressedAsset.selected.encoding);
|
|
130
|
+
c.header('Content-Length', String(data.byteLength));
|
|
131
|
+
return c.body(body, 200);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return await next();
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function matchPublicRoute(req, routes) {
|
|
138
|
+
for (const route of routes.sort(sortRoutes))if (!route.isSSR && route.entryPath.startsWith('public') && req.path.startsWith(route.urlPath)) return route;
|
|
139
|
+
}
|
|
140
|
+
const extractPathname = (url)=>{
|
|
141
|
+
try {
|
|
142
|
+
if (url.includes('://')) return new URL(url).pathname || '/';
|
|
143
|
+
if (url.startsWith('//')) return new URL(`http:${url}`).pathname || '/';
|
|
144
|
+
return url;
|
|
145
|
+
} catch (e) {
|
|
146
|
+
return url;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
const MODULE_FEDERATION_MANIFEST_FILE = 'mf-manifest.json';
|
|
150
|
+
const MODULE_FEDERATION_OPTIONAL_FILES = [
|
|
151
|
+
'mf-stats.json'
|
|
152
|
+
];
|
|
153
|
+
const trimLeadingSlash = (value)=>value.replace(/^\/+/, '');
|
|
154
|
+
const joinModuleFederationAssetPath = (assetPath, assetName)=>{
|
|
155
|
+
if (!assetName) return '';
|
|
156
|
+
return trimLeadingSlash(path.posix.join(assetPath || '', assetName));
|
|
157
|
+
};
|
|
158
|
+
const appendModuleFederationAsset = (set, assetPath)=>{
|
|
159
|
+
if (!assetPath) return;
|
|
160
|
+
set.add(trimLeadingSlash(assetPath));
|
|
161
|
+
};
|
|
162
|
+
const appendModuleFederationAssets = (set, assets)=>{
|
|
163
|
+
assets?.js?.sync?.forEach((asset)=>appendModuleFederationAsset(set, asset));
|
|
164
|
+
assets?.js?.async?.forEach((asset)=>appendModuleFederationAsset(set, asset));
|
|
165
|
+
assets?.css?.sync?.forEach((asset)=>appendModuleFederationAsset(set, asset));
|
|
166
|
+
assets?.css?.async?.forEach((asset)=>appendModuleFederationAsset(set, asset));
|
|
167
|
+
};
|
|
168
|
+
const hasAbsoluteProtocol = (value)=>/^https?:\/\//i.test(value) || value.startsWith('//');
|
|
169
|
+
const ensureLeadingSlash = (value)=>{
|
|
170
|
+
if ('' === value) return '/';
|
|
171
|
+
return value.startsWith('/') ? value : `/${value}`;
|
|
172
|
+
};
|
|
173
|
+
const ensureTrailingSlash = (value)=>value.endsWith('/') ? value : `${value}/`;
|
|
174
|
+
const patchModuleFederationManifestPublicPath = (c, manifestBuffer, pathPrefix)=>{
|
|
175
|
+
try {
|
|
176
|
+
const manifest = JSON.parse(manifestBuffer.toString('utf-8'));
|
|
177
|
+
const publicPath = manifest.metaData?.publicPath;
|
|
178
|
+
if (!publicPath || hasAbsoluteProtocol(publicPath)) return manifestBuffer;
|
|
179
|
+
const requestURL = new URL(c.req.url);
|
|
180
|
+
const prefixPath = ensureTrailingSlash(ensureLeadingSlash(pathPrefix || '/'));
|
|
181
|
+
manifest.metaData = {
|
|
182
|
+
...manifest.metaData,
|
|
183
|
+
publicPath: `${requestURL.origin}${prefixPath}`
|
|
184
|
+
};
|
|
185
|
+
return Buffer.from(JSON.stringify(manifest), 'utf-8');
|
|
186
|
+
} catch {
|
|
187
|
+
return manifestBuffer;
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
const patchModuleFederationRemoteEntryPublicPath = (c, remoteEntryBuffer, pathPrefix)=>{
|
|
191
|
+
const requestURL = new URL(c.req.url);
|
|
192
|
+
const prefixPath = ensureTrailingSlash(ensureLeadingSlash(pathPrefix || '/'));
|
|
193
|
+
const publicPath = `${requestURL.origin}${prefixPath}`;
|
|
194
|
+
const source = remoteEntryBuffer.toString('utf-8');
|
|
195
|
+
const patched = source.replace(/__webpack_require__\.p\s*=\s*(['"`])[^'"`]*\1;/, `__webpack_require__.p = ${JSON.stringify(publicPath)};`).replace(/__rspack_require__\.p\s*=\s*(['"`])[^'"`]*\1;/, `__rspack_require__.p = ${JSON.stringify(publicPath)};`);
|
|
196
|
+
if (patched === source) return remoteEntryBuffer;
|
|
197
|
+
return Buffer.from(patched, 'utf-8');
|
|
198
|
+
};
|
|
199
|
+
const getModuleFederationAssetList = async (pwd)=>{
|
|
200
|
+
const assets = new Set();
|
|
201
|
+
const manifestPath = path.join(pwd, MODULE_FEDERATION_MANIFEST_FILE);
|
|
202
|
+
if (!await fs.pathExists(manifestPath)) return {
|
|
203
|
+
assets,
|
|
204
|
+
remoteEntry: null
|
|
205
|
+
};
|
|
206
|
+
assets.add(MODULE_FEDERATION_MANIFEST_FILE);
|
|
207
|
+
const manifestBuffer = await fileReader.readFileFromSystem(manifestPath, 'buffer');
|
|
208
|
+
if (null === manifestBuffer) return {
|
|
209
|
+
assets,
|
|
210
|
+
remoteEntry: null
|
|
211
|
+
};
|
|
212
|
+
for (const filename of MODULE_FEDERATION_OPTIONAL_FILES)if (await fs.pathExists(path.join(pwd, filename))) assets.add(filename);
|
|
213
|
+
let remoteEntryFile = null;
|
|
214
|
+
try {
|
|
215
|
+
const manifest = JSON.parse(manifestBuffer.toString('utf-8'));
|
|
216
|
+
const remoteEntry = joinModuleFederationAssetPath(manifest.metaData?.remoteEntry?.path, manifest.metaData?.remoteEntry?.name);
|
|
217
|
+
const dtsZip = joinModuleFederationAssetPath(manifest.metaData?.types?.path, manifest.metaData?.types?.zip);
|
|
218
|
+
const dtsApi = joinModuleFederationAssetPath(manifest.metaData?.types?.path, manifest.metaData?.types?.api);
|
|
219
|
+
if (remoteEntry) {
|
|
220
|
+
assets.add(remoteEntry);
|
|
221
|
+
remoteEntryFile = remoteEntry;
|
|
222
|
+
}
|
|
223
|
+
if (dtsZip) assets.add(dtsZip);
|
|
224
|
+
if (dtsApi) assets.add(dtsApi);
|
|
225
|
+
manifest.shared?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
|
|
226
|
+
manifest.remotes?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
|
|
227
|
+
manifest.exposes?.forEach((item)=>appendModuleFederationAssets(assets, item.assets));
|
|
228
|
+
} catch {}
|
|
229
|
+
return {
|
|
230
|
+
assets,
|
|
231
|
+
remoteEntry: remoteEntryFile
|
|
232
|
+
};
|
|
233
|
+
};
|
|
234
|
+
function createStaticMiddleware(options) {
|
|
235
|
+
const { pwd, routes } = options;
|
|
236
|
+
const prefix = options.output.assetPrefix || '/';
|
|
237
|
+
const pathPrefix = extractPathname(prefix);
|
|
238
|
+
const { distPath: { css: cssPath, js: jsPath, media: mediaPath } = {} } = options.output;
|
|
239
|
+
const { favicon } = options.html;
|
|
240
|
+
const { publicDir } = options.server;
|
|
241
|
+
const favicons = prepareFavicons(favicon);
|
|
242
|
+
const staticFiles = [
|
|
243
|
+
cssPath,
|
|
244
|
+
jsPath,
|
|
245
|
+
mediaPath
|
|
246
|
+
].filter((v)=>Boolean(v));
|
|
247
|
+
const publicDirPatterns = getPublicDirPatterns(publicDir);
|
|
248
|
+
const staticReg = [
|
|
249
|
+
'static/',
|
|
250
|
+
'upload/',
|
|
251
|
+
...staticFiles,
|
|
252
|
+
...publicDirPatterns
|
|
253
|
+
];
|
|
254
|
+
const iconReg = [
|
|
255
|
+
'favicon.ico',
|
|
256
|
+
'icon.png',
|
|
257
|
+
...favicons
|
|
258
|
+
];
|
|
259
|
+
const regPrefix = pathPrefix.endsWith('/') ? pathPrefix : `${pathPrefix}/`;
|
|
260
|
+
const staticPathRegExp = new RegExp(`^${regPrefix}(${[
|
|
261
|
+
...staticReg,
|
|
262
|
+
...iconReg
|
|
263
|
+
].join('|')})`);
|
|
264
|
+
const publicMiddleware = createPublicMiddleware({
|
|
265
|
+
pwd,
|
|
266
|
+
routes: routes || []
|
|
267
|
+
});
|
|
268
|
+
let moduleFederationAssetsPromise = null;
|
|
269
|
+
const getModuleFederationAssets = async ()=>{
|
|
270
|
+
if (!moduleFederationAssetsPromise) moduleFederationAssetsPromise = getModuleFederationAssetList(pwd);
|
|
271
|
+
return moduleFederationAssetsPromise;
|
|
272
|
+
};
|
|
273
|
+
const serveFile = async (c, filepath, moduleFederationAsset = false, moduleFederationRemoteEntry = false, requestPath = '')=>{
|
|
274
|
+
if (moduleFederationAsset) {
|
|
275
|
+
c.header('Access-Control-Allow-Origin', '*');
|
|
276
|
+
c.header('Access-Control-Allow-Headers', '*');
|
|
277
|
+
c.header('Access-Control-Allow-Methods', 'GET,HEAD,OPTIONS');
|
|
278
|
+
}
|
|
279
|
+
const mimeType = getMimeType(filepath);
|
|
280
|
+
if (mimeType) c.header('Content-Type', mimeType);
|
|
281
|
+
const shouldPatchManifest = moduleFederationAsset && requestPath === MODULE_FEDERATION_MANIFEST_FILE;
|
|
282
|
+
const shouldPatchRemoteEntry = moduleFederationRemoteEntry;
|
|
283
|
+
const canUsePreCompressed = !shouldPatchManifest && !shouldPatchRemoteEntry;
|
|
284
|
+
const preCompressedAsset = canUsePreCompressed ? await resolvePreCompressedAsset(c, filepath) : {
|
|
285
|
+
selected: null,
|
|
286
|
+
hasVariant: false
|
|
287
|
+
};
|
|
288
|
+
const targetFilepath = preCompressedAsset.selected?.filepath ?? filepath;
|
|
289
|
+
const chunk = await fileReader.readFileFromSystem(targetFilepath, 'buffer');
|
|
290
|
+
if (null === chunk) return null;
|
|
291
|
+
const responseChunk = shouldPatchManifest ? patchModuleFederationManifestPublicPath(c, chunk, pathPrefix) : shouldPatchRemoteEntry ? patchModuleFederationRemoteEntryPublicPath(c, chunk, pathPrefix) : chunk;
|
|
292
|
+
if (preCompressedAsset.hasVariant) appendVaryHeader(c, 'Accept-Encoding');
|
|
293
|
+
if (preCompressedAsset.selected) c.header('Content-Encoding', preCompressedAsset.selected.encoding);
|
|
294
|
+
c.header('Content-Length', String(responseChunk.byteLength));
|
|
295
|
+
const body = new Uint8Array(responseChunk.buffer, responseChunk.byteOffset, responseChunk.byteLength);
|
|
296
|
+
return c.body(body, 200);
|
|
297
|
+
};
|
|
298
|
+
return async (c, next)=>{
|
|
299
|
+
const pageRoute = c.get('route');
|
|
300
|
+
const pathname = c.req.path;
|
|
301
|
+
if (pageRoute && '' === path.extname(pathname)) return next();
|
|
302
|
+
const hit = staticPathRegExp.test(pathname);
|
|
303
|
+
const requestPath = trimLeadingSlash(pathname.replace(pathPrefix, ()=>''));
|
|
304
|
+
if (requestPath.includes('..')) return next();
|
|
305
|
+
const moduleFederationAssetMeta = await getModuleFederationAssets();
|
|
306
|
+
const isModuleFederationAsset = moduleFederationAssetMeta.assets.has(requestPath);
|
|
307
|
+
const isModuleFederationRemoteEntry = moduleFederationAssetMeta.remoteEntry === requestPath;
|
|
308
|
+
const serveByPath = async (filepath, moduleFederationAsset = false, moduleFederationRemoteEntry = false)=>{
|
|
309
|
+
if (!await fs.pathExists(filepath)) return null;
|
|
310
|
+
return serveFile(c, filepath, moduleFederationAsset, moduleFederationRemoteEntry, requestPath);
|
|
311
|
+
};
|
|
312
|
+
if (hit) {
|
|
313
|
+
const response = await serveByPath(path.join(pwd, requestPath), isModuleFederationAsset, isModuleFederationRemoteEntry);
|
|
314
|
+
if (null !== response) return response;
|
|
315
|
+
return next();
|
|
316
|
+
}
|
|
317
|
+
if (isModuleFederationAsset) {
|
|
318
|
+
const response = await serveByPath(path.join(pwd, requestPath), true, isModuleFederationRemoteEntry);
|
|
319
|
+
if (null !== response) return response;
|
|
320
|
+
}
|
|
321
|
+
return publicMiddleware(c, next);
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
const prepareFavicons = (favicon)=>{
|
|
325
|
+
const faviconNames = [];
|
|
326
|
+
if (favicon && 'string' == typeof favicon) faviconNames.push(favicon.substring(favicon.lastIndexOf('/') + 1));
|
|
327
|
+
return faviconNames;
|
|
328
|
+
};
|
|
329
|
+
export { createPublicMiddleware, createStaticMiddleware, serverStaticPlugin };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
const AGGRED_DIR = {
|
|
2
|
+
mock: 'config/mock',
|
|
3
|
+
server: 'server',
|
|
4
|
+
api: 'api',
|
|
5
|
+
shared: 'shared',
|
|
6
|
+
lambda: 'lambda'
|
|
7
|
+
};
|
|
8
|
+
const REPLACE_REG = {
|
|
9
|
+
before: {
|
|
10
|
+
head: '<head\\b[^>]*>',
|
|
11
|
+
body: '<body\\b[^>]*>'
|
|
12
|
+
},
|
|
13
|
+
after: {
|
|
14
|
+
head: '</head>',
|
|
15
|
+
body: '</body>'
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
var constants_ServerTimings = /*#__PURE__*/ function(ServerTimings) {
|
|
19
|
+
ServerTimings["SERVER_HANDLE_REQUEST"] = "server-handle-request";
|
|
20
|
+
ServerTimings["SERVER_MIDDLEWARE"] = "server-middleware";
|
|
21
|
+
ServerTimings["SERVER_HOOK_AFTER_RENDER"] = "server-hook-after-render";
|
|
22
|
+
ServerTimings["SERVER_HOOK_AFTER_MATCH"] = "server-hook-after-match";
|
|
23
|
+
return ServerTimings;
|
|
24
|
+
}({});
|
|
25
|
+
const SERVER_TIMING = 'Server-Timing';
|
|
26
|
+
const X_RENDER_CACHE = 'x-render-cache';
|
|
27
|
+
const X_MODERNJS_RENDER = 'x-modernjs-render';
|
|
28
|
+
export { AGGRED_DIR, REPLACE_REG, SERVER_TIMING, X_MODERNJS_RENDER, X_RENDER_CACHE, constants_ServerTimings as ServerTimings };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const LOADER_CONTEXT = 'loaderContext';
|
|
2
|
+
function getLoaderCtx(c) {
|
|
3
|
+
const loaderContext = c.get(LOADER_CONTEXT);
|
|
4
|
+
if (loaderContext) return loaderContext;
|
|
5
|
+
{
|
|
6
|
+
const loaderContext = new Map();
|
|
7
|
+
c.set(LOADER_CONTEXT, loaderContext);
|
|
8
|
+
return loaderContext;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export { getLoaderCtx };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from "./plugins/index.mjs";
|
|
2
|
+
export * from "./types/config/index.mjs";
|
|
3
|
+
export * from "./types/plugins/index.mjs";
|
|
4
|
+
export * from "./types/render.mjs";
|
|
5
|
+
export * from "./types/requestHandler.mjs";
|
|
6
|
+
export { Hono } from "hono";
|
|
7
|
+
export { AGGRED_DIR } from "./constants.mjs";
|
|
8
|
+
export { run, useHonoContext } from "./context.mjs";
|
|
9
|
+
export { getLoaderCtx } from "./helper.mjs";
|
|
10
|
+
export { createServerBase } from "./serverBase.mjs";
|
|
11
|
+
export { ErrorDigest, createErrorHtml, onError } from "./utils/index.mjs";
|
|
12
|
+
export { getPublicDirConfig, getPublicDirPatterns, getPublicDirRoutePrefixes, normalizePublicDir, normalizePublicDirPath, resolvePublicDirPaths } from "./utils/publicDir.mjs";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
function getHookRunners(context) {
|
|
2
|
+
const { hooks } = context;
|
|
3
|
+
return {
|
|
4
|
+
config: (params)=>hooks.modifyConfig.call(params),
|
|
5
|
+
prepare: ()=>hooks.onPrepare.call(),
|
|
6
|
+
reset: (params)=>hooks.onReset.call(params),
|
|
7
|
+
fallback: (input)=>hooks.fallback.call(input),
|
|
8
|
+
prepareWebServer: (input)=>hooks.prepareWebServer.call(input),
|
|
9
|
+
prepareApiServer: (input)=>hooks.prepareApiServer.call(input),
|
|
10
|
+
afterMatch: (ctx)=>hooks.afterMatch.call(ctx),
|
|
11
|
+
afterRender: (ctx)=>hooks.afterRender.call(ctx),
|
|
12
|
+
afterStreamingRender: (ctx)=>hooks.afterStreamingRender.call(ctx)
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function transformHookRunner(hookRunnerName) {
|
|
16
|
+
switch(hookRunnerName){
|
|
17
|
+
case 'config':
|
|
18
|
+
return 'modifyConfig';
|
|
19
|
+
case 'prepare':
|
|
20
|
+
return 'onPrepare';
|
|
21
|
+
case 'reset':
|
|
22
|
+
return 'onReset';
|
|
23
|
+
default:
|
|
24
|
+
return hookRunnerName;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function handleSetupResult(setupResult, api) {
|
|
28
|
+
if (!setupResult) return;
|
|
29
|
+
Object.keys(setupResult).forEach((key)=>{
|
|
30
|
+
const fn = setupResult[key];
|
|
31
|
+
if ('function' == typeof fn) {
|
|
32
|
+
const newAPI = transformHookRunner(key);
|
|
33
|
+
if (api[newAPI]) api[newAPI]((...params)=>{
|
|
34
|
+
const res = fn(...params);
|
|
35
|
+
return res;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
export { getHookRunners, handleSetupResult, transformHookRunner };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createAsyncHook, createAsyncPipelineHook } from "@modern-js/plugin";
|
|
2
|
+
import { getHookRunners, handleSetupResult } from "./hooks.mjs";
|
|
3
|
+
const compatPlugin = ()=>({
|
|
4
|
+
name: '@modern-js/server-compat',
|
|
5
|
+
registryHooks: {
|
|
6
|
+
fallback: createAsyncHook(),
|
|
7
|
+
prepareWebServer: createAsyncPipelineHook(),
|
|
8
|
+
prepareApiServer: createAsyncPipelineHook(),
|
|
9
|
+
afterMatch: createAsyncPipelineHook(),
|
|
10
|
+
afterRender: createAsyncPipelineHook(),
|
|
11
|
+
afterStreamingRender: createAsyncPipelineHook()
|
|
12
|
+
},
|
|
13
|
+
_registryApi: (getServerContext, updateServerContext)=>{
|
|
14
|
+
const getInternalContext = ()=>getServerContext()._internalContext;
|
|
15
|
+
return {
|
|
16
|
+
useConfigContext: ()=>getInternalContext().config,
|
|
17
|
+
useAppContext: ()=>{
|
|
18
|
+
const { _internalContext, ...serverContext } = getServerContext();
|
|
19
|
+
return serverContext;
|
|
20
|
+
},
|
|
21
|
+
setAppContext: (context)=>updateServerContext(context),
|
|
22
|
+
useHookRunners: ()=>getHookRunners(getInternalContext())
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
setup: ()=>{}
|
|
26
|
+
});
|
|
27
|
+
export { compatPlugin, handleSetupResult };
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { createFileContractGateSnapshotStore } from "./contractGateSnapshotStore.mjs";
|
|
2
|
+
const DEFAULT_POLL_INTERVAL_MS = 15000;
|
|
3
|
+
const DEFAULT_GATE_STALE_AFTER_MS = 600000;
|
|
4
|
+
class ContractGateAutopilot {
|
|
5
|
+
async start() {
|
|
6
|
+
await this.syncOnce();
|
|
7
|
+
if (this.poller) return;
|
|
8
|
+
this.poller = setInterval(()=>{
|
|
9
|
+
this.syncOnce();
|
|
10
|
+
}, this.pollIntervalMs);
|
|
11
|
+
if ('function' == typeof this.poller.unref) this.poller.unref();
|
|
12
|
+
}
|
|
13
|
+
stop() {
|
|
14
|
+
if (this.poller) {
|
|
15
|
+
clearInterval(this.poller);
|
|
16
|
+
this.poller = void 0;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
async syncOnce() {
|
|
20
|
+
const snapshot = await this.loadSnapshot();
|
|
21
|
+
if (!snapshot) return 0;
|
|
22
|
+
const gates = this.normalizeSnapshot(snapshot);
|
|
23
|
+
let updatedCount = 0;
|
|
24
|
+
for (const gate of gates){
|
|
25
|
+
this.orchestrator.addRequiredContractGate(gate.name);
|
|
26
|
+
const fingerprint = `${gate.passed ? '1' : '0'}:${gate.reason || ''}`;
|
|
27
|
+
if (this.appliedGateFingerprints.get(gate.name) !== fingerprint) {
|
|
28
|
+
this.orchestrator.setContractGate(gate.name, gate.passed, gate.reason);
|
|
29
|
+
this.appliedGateFingerprints.set(gate.name, fingerprint);
|
|
30
|
+
updatedCount += 1;
|
|
31
|
+
this.logger?.info?.(`[telemetry.canary.autopilot] gate=${gate.name} passed=${String(gate.passed)} reason=${gate.reason || 'none'}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return updatedCount;
|
|
35
|
+
}
|
|
36
|
+
async loadSnapshot() {
|
|
37
|
+
try {
|
|
38
|
+
const snapshot = await this.gateSnapshotStore.readSnapshot();
|
|
39
|
+
if (!snapshot) return;
|
|
40
|
+
const fingerprint = JSON.stringify(snapshot);
|
|
41
|
+
if (fingerprint === this.lastSnapshotFingerprint) return;
|
|
42
|
+
this.lastSnapshotFingerprint = fingerprint;
|
|
43
|
+
return snapshot;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
const source = this.gateSnapshotPath || this.gateSnapshotStore.name || 'stateStore';
|
|
46
|
+
this.logger?.warn?.(`[telemetry.canary.autopilot] failed to load gate snapshot ${source}: ${error instanceof Error ? error.message : String(error)}`);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
normalizeSnapshot(snapshot) {
|
|
51
|
+
const now = Date.now();
|
|
52
|
+
const output = [];
|
|
53
|
+
const gates = snapshot.gates;
|
|
54
|
+
if (!gates || 'object' != typeof gates) return output;
|
|
55
|
+
for (const [name, value] of Object.entries(gates)){
|
|
56
|
+
const normalizedName = name.trim();
|
|
57
|
+
if (!normalizedName) continue;
|
|
58
|
+
const gate = this.normalizeGateValue(value, snapshot.updatedAt, now);
|
|
59
|
+
if (!gate) continue;
|
|
60
|
+
if ('number' == typeof gate.expiresAt && Number.isFinite(gate.expiresAt) && gate.expiresAt > 0 && now >= gate.expiresAt) {
|
|
61
|
+
output.push({
|
|
62
|
+
name: normalizedName,
|
|
63
|
+
passed: true,
|
|
64
|
+
reason: void 0,
|
|
65
|
+
updatedAt: gate.updatedAt,
|
|
66
|
+
expiresAt: gate.expiresAt
|
|
67
|
+
});
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
const isStale = this.gateStaleAfterMs > 0 && now - gate.updatedAt > this.gateStaleAfterMs;
|
|
71
|
+
if (isStale) {
|
|
72
|
+
output.push({
|
|
73
|
+
name: normalizedName,
|
|
74
|
+
passed: false,
|
|
75
|
+
reason: gate.reason || 'Gate snapshot is stale',
|
|
76
|
+
updatedAt: gate.updatedAt
|
|
77
|
+
});
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
output.push({
|
|
81
|
+
name: normalizedName,
|
|
82
|
+
passed: gate.passed,
|
|
83
|
+
reason: gate.reason,
|
|
84
|
+
updatedAt: gate.updatedAt
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return output;
|
|
88
|
+
}
|
|
89
|
+
normalizeGateValue(value, snapshotUpdatedAt, now) {
|
|
90
|
+
if ('boolean' == typeof value) return {
|
|
91
|
+
passed: value,
|
|
92
|
+
updatedAt: this.normalizeUpdatedAt(snapshotUpdatedAt, now)
|
|
93
|
+
};
|
|
94
|
+
if (!value || 'object' != typeof value) return;
|
|
95
|
+
const hasPassed = 'boolean' == typeof value.passed;
|
|
96
|
+
const passed = true === value.passed;
|
|
97
|
+
let reason = 'string' == typeof value.reason && value.reason.trim().length > 0 ? value.reason : void 0;
|
|
98
|
+
if (!hasPassed) reason = reason || 'Gate snapshot record is missing "passed" boolean';
|
|
99
|
+
return {
|
|
100
|
+
passed,
|
|
101
|
+
reason,
|
|
102
|
+
updatedAt: this.normalizeUpdatedAt(value.updatedAt ?? snapshotUpdatedAt, now),
|
|
103
|
+
expiresAt: this.normalizeExpiresAt(value.expiresAt)
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
normalizeUpdatedAt(value, fallback) {
|
|
107
|
+
if ('number' == typeof value && Number.isFinite(value) && value > 0) return value;
|
|
108
|
+
return fallback;
|
|
109
|
+
}
|
|
110
|
+
normalizeExpiresAt(value) {
|
|
111
|
+
if ('number' == typeof value && Number.isFinite(value) && value > 0) return value;
|
|
112
|
+
}
|
|
113
|
+
constructor(options){
|
|
114
|
+
this.appliedGateFingerprints = new Map();
|
|
115
|
+
this.orchestrator = options.orchestrator;
|
|
116
|
+
if (!options.gateSnapshotStore && !options.gateSnapshotPath) throw new Error('ContractGateAutopilot requires gateSnapshotPath or gateSnapshotStore');
|
|
117
|
+
this.gateSnapshotPath = options.gateSnapshotPath;
|
|
118
|
+
this.gateSnapshotStore = options.gateSnapshotStore || createFileContractGateSnapshotStore(options.gateSnapshotPath);
|
|
119
|
+
this.pollIntervalMs = Math.max(250, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
|
|
120
|
+
this.gateStaleAfterMs = Math.max(0, options.gateStaleAfterMs ?? DEFAULT_GATE_STALE_AFTER_MS);
|
|
121
|
+
this.logger = options.logger;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export { ContractGateAutopilot };
|