@lensmcp/cluster 1.18.4 → 1.18.6
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/basic-ssl.js +1 -241
- package/build-scope-patterns.js +1 -40
- package/create-webpack-dev.js +1 -186
- package/create-webpack-prod.js +1 -169
- package/executors/build/build.impl.js +1 -98
- package/executors/gateway/gateway-errors.js +1 -43
- package/executors/gateway/gateway.impl.js +1 -53
- package/executors/gateway/gateway.lib.js +1 -29
- package/executors/gateway/health-check.js +1 -66
- package/executors/gateway/jwks-verify.js +1 -121
- package/executors/gateway/main.prod-gateway.js +2 -573
- package/executors/gateway/main.rollout.js +11 -117
- package/executors/gateway/manifest.js +1 -374
- package/executors/gateway/metrics.js +1 -56
- package/executors/gateway/otel-tracing.js +1 -74
- package/executors/gateway/prod-gateway.lib.js +1 -22
- package/executors/gateway/prod-runtime/access-log.js +1 -24
- package/executors/gateway/prod-runtime/app.js +1 -123
- package/executors/gateway/prod-runtime/auth.js +1 -51
- package/executors/gateway/prod-runtime/cors.js +1 -40
- package/executors/gateway/prod-runtime/edge.js +1 -65
- package/executors/gateway/prod-runtime/handler.js +1 -226
- package/executors/gateway/prod-runtime/hooks.js +1 -42
- package/executors/gateway/prod-runtime/observability.js +1 -125
- package/executors/gateway/prod-runtime/rollout.js +1 -103
- package/executors/gateway/prod-runtime/routing.js +1 -40
- package/executors/gateway/prod-runtime/server.js +1 -79
- package/executors/gateway/prod-runtime/trust.js +1 -32
- package/executors/gateway/prod-runtime/types.js +1 -2
- package/executors/gateway/prod-runtime/upgrade.js +4 -116
- package/executors/gateway/prod-runtime/upstream.js +1 -21
- package/executors/gateway/providers-prod.js +1 -232
- package/executors/gateway/rate-limit.js +2 -75
- package/executors/gateway/registry-source.js +1 -131
- package/executors/gateway/rollout-ops.js +2 -167
- package/executors/gateway/runtime/auth.js +1 -64
- package/executors/gateway/runtime/chooser.js +12 -45
- package/executors/gateway/runtime/control.js +1 -128
- package/executors/gateway/runtime/dev-auth.js +1 -108
- package/executors/gateway/runtime/discovery.js +1 -123
- package/executors/gateway/runtime/edge.js +1 -47
- package/executors/gateway/runtime/handler.js +1 -183
- package/executors/gateway/runtime/hooks.js +1 -55
- package/executors/gateway/runtime/lens-children.js +1 -651
- package/executors/gateway/runtime/lifecycle.js +3 -842
- package/executors/gateway/runtime/observability.js +2 -148
- package/executors/gateway/runtime/pod-env.js +2 -89
- package/executors/gateway/runtime/proxy.js +1 -457
- package/executors/gateway/runtime/route-registry.js +1 -72
- package/executors/gateway/runtime/scope.js +1 -117
- package/executors/gateway/runtime/server.js +3 -487
- package/executors/gateway/runtime/service-keys.js +1 -49
- package/executors/gateway/runtime/types.js +1 -151
- package/executors/gateway/runtime/upgrade.js +1 -71
- package/executors/gateway/runtime/workspace-registry.js +1 -99
- package/executors/gateway/ssrf-guard.js +1 -190
- package/executors/serve/serve.impl.js +1 -280
- package/executors/trust/trust.impl.js +4 -162
- package/gateway.js +1 -35
- package/index.js +1 -16
- package/main.devserver.js +10 -1117
- package/package.json +4 -3
- package/tsgo-check-plugin.js +4 -364
- package/typecheck-bus.js +4 -256
package/create-webpack-prod.js
CHANGED
|
@@ -1,169 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createProdWebpackConfig = createProdWebpackConfig;
|
|
4
|
-
const tslib_1 = require("tslib");
|
|
5
|
-
const app_plugin_1 = require("@nx/webpack/app-plugin");
|
|
6
|
-
const fs = tslib_1.__importStar(require("node:fs"));
|
|
7
|
-
const path = tslib_1.__importStar(require("node:path"));
|
|
8
|
-
const glob_1 = require("glob");
|
|
9
|
-
const webpack_node_externals_1 = tslib_1.__importDefault(require("webpack-node-externals"));
|
|
10
|
-
const build_scope_patterns_1 = require("./build-scope-patterns");
|
|
11
|
-
function createProdWebpackConfig(options) {
|
|
12
|
-
const { appRoot, outputDir, main, tsConfig, workspaceRoot, assets = [], additionalEntryPoints = [], runtimeDependencies = [], memoryLimit = 8192, generatePackageJson = true, buildLibsFromSource = false, ormConfigPath, migrationsDir = './src/migrations', orgScopes = [], bundlePackages = [], nodeExternalsConfig: userNodeExternalsConfig, webpackConfigPath, } = options;
|
|
13
|
-
const { allowlistPatterns, scopePrefixes, scopePatterns } = (0, build_scope_patterns_1.buildScopePatterns)(orgScopes);
|
|
14
|
-
// Build combined allowlist: orgScopes + bundlePackages + user-provided
|
|
15
|
-
const bundlePatterns = bundlePackages.map((pkg) => new RegExp(`^${pkg.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(/|$)`));
|
|
16
|
-
const combinedAllowlist = [
|
|
17
|
-
...allowlistPatterns,
|
|
18
|
-
...bundlePatterns,
|
|
19
|
-
...(userNodeExternalsConfig?.allowlist || []),
|
|
20
|
-
];
|
|
21
|
-
// Detect ormconfig.ts for the app so we can build it for TypeORM CLI in production
|
|
22
|
-
let resolvedOrmConfigPath;
|
|
23
|
-
if (ormConfigPath !== undefined) {
|
|
24
|
-
resolvedOrmConfigPath = ormConfigPath ? path.join(appRoot, ormConfigPath) : null;
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
const candidate = path.join(appRoot, './src/ormconfig.ts');
|
|
28
|
-
resolvedOrmConfigPath = fs.existsSync(candidate) ? candidate : null;
|
|
29
|
-
}
|
|
30
|
-
const hasOrmconfig = resolvedOrmConfigPath && fs.existsSync(resolvedOrmConfigPath);
|
|
31
|
-
const webpackConfigs = [];
|
|
32
|
-
const externalsConfig = [
|
|
33
|
-
(0, webpack_node_externals_1.default)({
|
|
34
|
-
allowlist: combinedAllowlist,
|
|
35
|
-
additionalModuleDirs: userNodeExternalsConfig?.additionalModuleDirs || [],
|
|
36
|
-
...(userNodeExternalsConfig?.importType && { importType: userNodeExternalsConfig.importType }),
|
|
37
|
-
}),
|
|
38
|
-
({ request }, callback) => {
|
|
39
|
-
// Workspace-internal absolute requests are SOURCE regardless of the
|
|
40
|
-
// bucket layout (apps/, libs/, server/, shared/, web/, …) — bundle them.
|
|
41
|
-
if (request &&
|
|
42
|
-
path.isAbsolute(request) &&
|
|
43
|
-
request.startsWith(workspaceRoot) &&
|
|
44
|
-
!request.includes('node_modules')) {
|
|
45
|
-
return callback();
|
|
46
|
-
}
|
|
47
|
-
if (request &&
|
|
48
|
-
(request.startsWith(path.join(workspaceRoot, 'apps')) ||
|
|
49
|
-
request.startsWith(path.join(workspaceRoot, 'libs')))) {
|
|
50
|
-
return callback();
|
|
51
|
-
}
|
|
52
|
-
if (request && scopePrefixes.some((prefix) => request.startsWith(prefix))) {
|
|
53
|
-
return callback();
|
|
54
|
-
}
|
|
55
|
-
if (request && scopePatterns.some((pattern) => pattern.test(request))) {
|
|
56
|
-
return callback();
|
|
57
|
-
}
|
|
58
|
-
if (request && bundlePatterns.some((pattern) => pattern.test(request))) {
|
|
59
|
-
return callback();
|
|
60
|
-
}
|
|
61
|
-
if (request && !(request.startsWith('./') || request.startsWith('..'))) {
|
|
62
|
-
// Bare specifier: only a real node_modules package may stay external —
|
|
63
|
-
// tsconfig-path aliases (e.g. @org/contracts → workspace source) must bundle.
|
|
64
|
-
const rootSegment = request.startsWith('@')
|
|
65
|
-
? request.split('/').slice(0, 2).join('/')
|
|
66
|
-
: request.split('/')[0];
|
|
67
|
-
if (!fs.existsSync(path.join(workspaceRoot, 'node_modules', rootSegment))) {
|
|
68
|
-
return callback();
|
|
69
|
-
}
|
|
70
|
-
return callback(null, `commonjs ${request}`);
|
|
71
|
-
}
|
|
72
|
-
return callback();
|
|
73
|
-
},
|
|
74
|
-
];
|
|
75
|
-
const productionBuildConfig = {
|
|
76
|
-
externals: externalsConfig,
|
|
77
|
-
output: {
|
|
78
|
-
path: outputDir,
|
|
79
|
-
clean: false,
|
|
80
|
-
},
|
|
81
|
-
devtool: 'inline-source-map',
|
|
82
|
-
mode: 'production',
|
|
83
|
-
plugins: [
|
|
84
|
-
new app_plugin_1.NxAppWebpackPlugin({
|
|
85
|
-
target: 'node22',
|
|
86
|
-
compiler: 'tsc',
|
|
87
|
-
main,
|
|
88
|
-
additionalEntryPoints,
|
|
89
|
-
externalDependencies: [],
|
|
90
|
-
mergeExternals: true,
|
|
91
|
-
memoryLimit,
|
|
92
|
-
tsConfig,
|
|
93
|
-
assets,
|
|
94
|
-
namedChunks: true,
|
|
95
|
-
optimization: true,
|
|
96
|
-
outputHashing: 'none',
|
|
97
|
-
generatePackageJson,
|
|
98
|
-
runtimeDependencies,
|
|
99
|
-
buildLibsFromSource,
|
|
100
|
-
typeCheckOptions: {
|
|
101
|
-
async: false,
|
|
102
|
-
},
|
|
103
|
-
sourceMap: 'inline-source-map',
|
|
104
|
-
progress: false,
|
|
105
|
-
}),
|
|
106
|
-
],
|
|
107
|
-
};
|
|
108
|
-
webpackConfigs.push(productionBuildConfig);
|
|
109
|
-
if (hasOrmconfig && resolvedOrmConfigPath) {
|
|
110
|
-
const migrationsEntryPoints = [];
|
|
111
|
-
const migrationsGlobPath = path.join(appRoot, migrationsDir, '*.ts');
|
|
112
|
-
const migrationFiles = (0, glob_1.globSync)(migrationsGlobPath);
|
|
113
|
-
migrationFiles.forEach((filename) => {
|
|
114
|
-
const migrationName = path.basename(filename, '.ts');
|
|
115
|
-
migrationsEntryPoints.push({
|
|
116
|
-
entryName: `migrations/${migrationName}`,
|
|
117
|
-
entryPath: filename,
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
const ormconfigRelative = './' + path.relative(appRoot, resolvedOrmConfigPath);
|
|
121
|
-
const ormconfigBuildConfig = {
|
|
122
|
-
...productionBuildConfig,
|
|
123
|
-
output: {
|
|
124
|
-
...productionBuildConfig.output,
|
|
125
|
-
filename: (pathData) => {
|
|
126
|
-
if (pathData.runtime === 'main') {
|
|
127
|
-
return 'ormconfig.js';
|
|
128
|
-
}
|
|
129
|
-
return '[name].js';
|
|
130
|
-
},
|
|
131
|
-
library: {
|
|
132
|
-
type: 'commonjs',
|
|
133
|
-
},
|
|
134
|
-
clean: false,
|
|
135
|
-
},
|
|
136
|
-
devtool: false,
|
|
137
|
-
plugins: [
|
|
138
|
-
new app_plugin_1.NxAppWebpackPlugin({
|
|
139
|
-
target: 'node22',
|
|
140
|
-
compiler: 'tsc',
|
|
141
|
-
main: ormconfigRelative,
|
|
142
|
-
externalDependencies: [],
|
|
143
|
-
additionalEntryPoints: migrationsEntryPoints,
|
|
144
|
-
mergeExternals: true,
|
|
145
|
-
tsConfig,
|
|
146
|
-
assets: [],
|
|
147
|
-
namedChunks: true,
|
|
148
|
-
memoryLimit,
|
|
149
|
-
optimization: false,
|
|
150
|
-
outputHashing: 'none',
|
|
151
|
-
generatePackageJson: false,
|
|
152
|
-
buildLibsFromSource,
|
|
153
|
-
skipTypeChecking: true,
|
|
154
|
-
sourceMap: false,
|
|
155
|
-
progress: !process.env.CI,
|
|
156
|
-
}),
|
|
157
|
-
],
|
|
158
|
-
};
|
|
159
|
-
webpackConfigs.push(ormconfigBuildConfig);
|
|
160
|
-
}
|
|
161
|
-
// Apply user webpack overrides if configured
|
|
162
|
-
if (webpackConfigPath) {
|
|
163
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
164
|
-
const overrideModule = require(path.resolve(appRoot, webpackConfigPath));
|
|
165
|
-
const overrideFn = overrideModule.default || overrideModule;
|
|
166
|
-
return webpackConfigs.map((config) => overrideFn(config));
|
|
167
|
-
}
|
|
168
|
-
return webpackConfigs;
|
|
169
|
-
}
|
|
1
|
+
"use strict";var A=Object.defineProperty;var d=(o,n)=>A(o,"name",{value:n,configurable:!0});var F=Object.defineProperty,m=d((o,n)=>F(o,"name",{value:n,configurable:!0}),"m");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createProdWebpackConfig=createProdWebpackConfig;const tslib_1=require("tslib"),app_plugin_1=require("@nx/webpack/app-plugin"),fs=tslib_1.__importStar(require("node:fs")),path=tslib_1.__importStar(require("node:path")),glob_1=require("glob"),webpack_node_externals_1=tslib_1.__importDefault(require("webpack-node-externals")),build_scope_patterns_1=require("./build-scope-patterns");function createProdWebpackConfig(o){const{appRoot:n,outputDir:P,main:_,tsConfig:g,workspaceRoot:r,assets:k=[],additionalEntryPoints:x=[],runtimeDependencies:C=[],memoryLimit:b=8192,generatePackageJson:j=!0,buildLibsFromSource:f=!1,ormConfigPath:c,migrationsDir:W="./src/migrations",orgScopes:w=[],bundlePackages:S=[],nodeExternalsConfig:a,webpackConfigPath:h}=o,{allowlistPatterns:v,scopePrefixes:D,scopePatterns:q}=(0,build_scope_patterns_1.buildScopePatterns)(w),y=S.map(e=>new RegExp(`^${e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}(/|$)`)),E=[...v,...y,...a?.allowlist||[]];let s;if(c!==void 0)s=c?path.join(n,c):null;else{const e=path.join(n,"./src/ormconfig.ts");s=fs.existsSync(e)?e:null}const $=s&&fs.existsSync(s),p=[],u={externals:[(0,webpack_node_externals_1.default)({allowlist:E,additionalModuleDirs:a?.additionalModuleDirs||[],...a?.importType&&{importType:a.importType}}),({request:e},i)=>{if(e&&path.isAbsolute(e)&&e.startsWith(r)&&!e.includes("node_modules")||e&&(e.startsWith(path.join(r,"apps"))||e.startsWith(path.join(r,"libs")))||e&&D.some(t=>e.startsWith(t))||e&&q.some(t=>t.test(e))||e&&y.some(t=>t.test(e)))return i();if(e&&!(e.startsWith("./")||e.startsWith(".."))){const t=e.startsWith("@")?e.split("/").slice(0,2).join("/"):e.split("/")[0];return fs.existsSync(path.join(r,"node_modules",t))?i(null,`commonjs ${e}`):i()}return i()}],output:{path:P,clean:!1},devtool:"inline-source-map",mode:"production",plugins:[new app_plugin_1.NxAppWebpackPlugin({target:"node22",compiler:"tsc",main:_,additionalEntryPoints:x,externalDependencies:[],mergeExternals:!0,memoryLimit:b,tsConfig:g,assets:k,namedChunks:!0,optimization:!0,outputHashing:"none",generatePackageJson:j,runtimeDependencies:C,buildLibsFromSource:f,typeCheckOptions:{async:!1},sourceMap:"inline-source-map",progress:!1})]};if(p.push(u),$&&s){const e=[],i=path.join(n,W,"*.ts");(0,glob_1.globSync)(i).forEach(l=>{const M=path.basename(l,".ts");e.push({entryName:`migrations/${M}`,entryPath:l})});const t="./"+path.relative(n,s),L={...u,output:{...u.output,filename:m(l=>l.runtime==="main"?"ormconfig.js":"[name].js","filename"),library:{type:"commonjs"},clean:!1},devtool:!1,plugins:[new app_plugin_1.NxAppWebpackPlugin({target:"node22",compiler:"tsc",main:t,externalDependencies:[],additionalEntryPoints:e,mergeExternals:!0,tsConfig:g,assets:[],namedChunks:!0,memoryLimit:b,optimization:!1,outputHashing:"none",generatePackageJson:!1,buildLibsFromSource:f,skipTypeChecking:!0,sourceMap:!1,progress:!process.env.CI})]};p.push(L)}if(h){const e=require(path.resolve(n,h)),i=e.default||e;return p.map(t=>i(t))}return p}d(createProdWebpackConfig,"createProdWebpackConfig"),m(createProdWebpackConfig,"createProdWebpackConfig");
|
|
@@ -1,98 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const tslib_1 = require("tslib");
|
|
4
|
-
const fs = tslib_1.__importStar(require("node:fs"));
|
|
5
|
-
const path = tslib_1.__importStar(require("node:path"));
|
|
6
|
-
const create_webpack_prod_1 = require("../../create-webpack-prod");
|
|
7
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
8
|
-
const webpack = require('webpack');
|
|
9
|
-
/**
|
|
10
|
-
* Production webpack build executor for NestJS applications.
|
|
11
|
-
*/
|
|
12
|
-
async function* buildExecutor(options, context) {
|
|
13
|
-
process.env.NODE_ENV = 'production';
|
|
14
|
-
const projectConfig = context.projectsConfigurations.projects[context.projectName];
|
|
15
|
-
const workspaceRoot = context.root;
|
|
16
|
-
const projectRoot = path.join(workspaceRoot, projectConfig.root);
|
|
17
|
-
const outputPath = options.outputPath
|
|
18
|
-
? path.join(workspaceRoot, options.outputPath)
|
|
19
|
-
: path.join(workspaceRoot, 'dist', projectConfig.root);
|
|
20
|
-
// Resolve entry/tsconfig relative to project root (absolute paths avoid
|
|
21
|
-
// NxAppWebpackPlugin's normalizeRelativePaths collision with executor options)
|
|
22
|
-
const entryFile = options.entryFile || './src/deployments/service/main.ts';
|
|
23
|
-
const tsConfigFile = options.tsConfigFile || './tsconfig.app.json';
|
|
24
|
-
const resolvedMain = path.resolve(projectRoot, entryFile);
|
|
25
|
-
const resolvedTsConfig = path.resolve(projectRoot, tsConfigFile);
|
|
26
|
-
const configs = (0, create_webpack_prod_1.createProdWebpackConfig)({
|
|
27
|
-
appName: context.projectName,
|
|
28
|
-
appRoot: projectRoot,
|
|
29
|
-
outputDir: outputPath,
|
|
30
|
-
main: resolvedMain,
|
|
31
|
-
tsConfig: resolvedTsConfig,
|
|
32
|
-
assets: options.assets || [],
|
|
33
|
-
additionalEntryPoints: [
|
|
34
|
-
...(options.workers || []).map((w) => ({ entryName: w.name, entryPath: w.entryPath })),
|
|
35
|
-
...(options.additionalEntryPoints || []),
|
|
36
|
-
],
|
|
37
|
-
runtimeDependencies: options.runtimeDependencies || [],
|
|
38
|
-
memoryLimit: options.memoryLimit || 8192,
|
|
39
|
-
generatePackageJson: options.generatePackageJson !== false,
|
|
40
|
-
buildLibsFromSource: options.buildLibsFromSource || false,
|
|
41
|
-
ormConfigPath: options.ormConfigPath,
|
|
42
|
-
migrationsDir: options.migrationsDir || './src/migrations',
|
|
43
|
-
workspaceRoot,
|
|
44
|
-
orgScopes: options.orgScopes || [],
|
|
45
|
-
bundlePackages: options.bundlePackages || [],
|
|
46
|
-
nodeExternalsConfig: options.nodeExternalsConfig,
|
|
47
|
-
webpackConfigPath: options.webpackConfigPath,
|
|
48
|
-
});
|
|
49
|
-
const configArray = Array.isArray(configs) ? configs : [configs];
|
|
50
|
-
for (const config of configArray) {
|
|
51
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
52
|
-
const { watch: _watch, ...normalizedConfig } = config;
|
|
53
|
-
const compiler = webpack(normalizedConfig);
|
|
54
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
55
|
-
const stats = await new Promise((resolve, reject) => {
|
|
56
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
-
compiler.run((err, stats) => {
|
|
58
|
-
if (err)
|
|
59
|
-
return reject(err);
|
|
60
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
|
-
compiler.close((closeErr) => {
|
|
62
|
-
if (closeErr)
|
|
63
|
-
console.error('[build] webpack close error:', closeErr);
|
|
64
|
-
});
|
|
65
|
-
resolve(stats);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
console.info(stats.toString(config.stats || { colors: true, chunks: false }));
|
|
69
|
-
if (stats.hasErrors()) {
|
|
70
|
-
yield { success: false };
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
// Post-process: minify worker outputs (NxAppWebpackPlugin doesn't minify additional entries)
|
|
75
|
-
const workerEntries = options.workers || [];
|
|
76
|
-
if (workerEntries.length > 0) {
|
|
77
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
78
|
-
const { minify } = require('terser');
|
|
79
|
-
for (const worker of workerEntries) {
|
|
80
|
-
const filePath = path.join(outputPath, `${worker.name}.js`);
|
|
81
|
-
if (fs.existsSync(filePath)) {
|
|
82
|
-
const code = fs.readFileSync(filePath, 'utf-8');
|
|
83
|
-
const result = await minify(code, {
|
|
84
|
-
keep_classnames: true,
|
|
85
|
-
sourceMap: { content: 'inline', url: 'inline' },
|
|
86
|
-
});
|
|
87
|
-
if (result.code) {
|
|
88
|
-
fs.writeFileSync(filePath, result.code);
|
|
89
|
-
console.info(`[build] minified ${worker.name}.js`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
yield { success: true, outfile: path.join(outputPath, 'main.js') };
|
|
95
|
-
}
|
|
96
|
-
exports.default = buildExecutor;
|
|
97
|
-
module.exports = buildExecutor;
|
|
98
|
-
module.exports.default = buildExecutor;
|
|
1
|
+
"use strict";var x=Object.defineProperty;var d=(e,o)=>x(e,"name",{value:o,configurable:!0});var _=Object.defineProperty,p=d((e,o)=>_(e,"name",{value:o,configurable:!0}),"p");Object.defineProperty(exports,"__esModule",{value:!0});const tslib_1=require("tslib"),fs=tslib_1.__importStar(require("node:fs")),path=tslib_1.__importStar(require("node:path")),create_webpack_prod_1=require("../../create-webpack-prod"),webpack=require("webpack");async function*buildExecutor(e,o){process.env.NODE_ENV="production";const m=o.projectsConfigurations.projects[o.projectName],n=o.root,a=path.join(n,m.root),c=e.outputPath?path.join(n,e.outputPath):path.join(n,"dist",m.root),y=e.entryFile||"./src/deployments/service/main.ts",P=e.tsConfigFile||"./tsconfig.app.json",k=path.resolve(a,y),h=path.resolve(a,P),u=(0,create_webpack_prod_1.createProdWebpackConfig)({appName:o.projectName,appRoot:a,outputDir:c,main:k,tsConfig:h,assets:e.assets||[],additionalEntryPoints:[...(e.workers||[]).map(r=>({entryName:r.name,entryPath:r.entryPath})),...e.additionalEntryPoints||[]],runtimeDependencies:e.runtimeDependencies||[],memoryLimit:e.memoryLimit||8192,generatePackageJson:e.generatePackageJson!==!1,buildLibsFromSource:e.buildLibsFromSource||!1,ormConfigPath:e.ormConfigPath,migrationsDir:e.migrationsDir||"./src/migrations",workspaceRoot:n,orgScopes:e.orgScopes||[],bundlePackages:e.bundlePackages||[],nodeExternalsConfig:e.nodeExternalsConfig,webpackConfigPath:e.webpackConfigPath}),j=Array.isArray(u)?u:[u];for(const r of j){const{watch:l,...t}=r,s=webpack(t),i=await new Promise((w,C)=>{s.run((b,S)=>{if(b)return C(b);s.close(g=>{g&&console.error("[build] webpack close error:",g)}),w(S)})});if(console.info(i.toString(r.stats||{colors:!0,chunks:!1})),i.hasErrors()){yield{success:!1};return}}const f=e.workers||[];if(f.length>0){const{minify:r}=require("terser");for(const l of f){const t=path.join(c,`${l.name}.js`);if(fs.existsSync(t)){const s=fs.readFileSync(t,"utf-8"),i=await r(s,{keep_classnames:!0,sourceMap:{content:"inline",url:"inline"}});i.code&&(fs.writeFileSync(t,i.code),console.info(`[build] minified ${l.name}.js`))}}}yield{success:!0,outfile:path.join(c,"main.js")}}d(buildExecutor,"buildExecutor"),p(buildExecutor,"buildExecutor"),exports.default=buildExecutor,module.exports=buildExecutor,module.exports.default=buildExecutor;
|
|
@@ -1,43 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GATEWAY_ERROR_CONTENT_TYPE = exports.GATEWAY_ERROR_KEY = void 0;
|
|
4
|
-
exports.ensureRequestId = ensureRequestId;
|
|
5
|
-
exports.gatewayErrorBody = gatewayErrorBody;
|
|
6
|
-
/** Reason → stable, neutral localized key (the consumer translates the `gateway.*` namespace). */
|
|
7
|
-
exports.GATEWAY_ERROR_KEY = {
|
|
8
|
-
bad_request: 'gateway.badRequest',
|
|
9
|
-
unauthorized: 'gateway.unauthorized',
|
|
10
|
-
forbidden: 'gateway.forbidden',
|
|
11
|
-
not_found: 'gateway.notFound',
|
|
12
|
-
rate_limited: 'gateway.rateLimited',
|
|
13
|
-
unavailable: 'gateway.unavailable',
|
|
14
|
-
internal: 'gateway.internal',
|
|
15
|
-
};
|
|
16
|
-
const headerValue = (v) => typeof v === 'string' && v !== '' ? v : Array.isArray(v) ? v[0] : undefined;
|
|
17
|
-
const mintId = () => Date.now().toString(36) + Math.random().toString(36).slice(2, 12);
|
|
18
|
-
/**
|
|
19
|
-
* Ensure the request carries an `x-request-id` (mint one at the edge if absent) and return it. Because
|
|
20
|
-
* the gateway forwards `req.headers` to the upstream, the pod's request-context adopts THIS id as its
|
|
21
|
-
* `traceId`, and the gateway returns it on the response — one id, end-to-end, for ANY request.
|
|
22
|
-
*/
|
|
23
|
-
function ensureRequestId(req) {
|
|
24
|
-
const existing = headerValue(req.headers['x-request-id']);
|
|
25
|
-
if (existing)
|
|
26
|
-
return existing;
|
|
27
|
-
const rid = mintId();
|
|
28
|
-
req.headers['x-request-id'] = rid;
|
|
29
|
-
return rid;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Build the JSON edge-error body `{ key, status, traceId, message? }`. `detail` becomes the dev-facing
|
|
33
|
-
* `message` (e.g. the old plain-text reason), never surfaced to end users.
|
|
34
|
-
*/
|
|
35
|
-
function gatewayErrorBody(status, reason, traceId, detail) {
|
|
36
|
-
return JSON.stringify({
|
|
37
|
-
key: exports.GATEWAY_ERROR_KEY[reason],
|
|
38
|
-
status,
|
|
39
|
-
traceId,
|
|
40
|
-
...(detail ? { message: detail } : {}),
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
exports.GATEWAY_ERROR_CONTENT_TYPE = 'application/json; charset=utf-8';
|
|
1
|
+
"use strict";var i=Object.defineProperty;var n=(e,t)=>i(e,"name",{value:t,configurable:!0});var o=Object.defineProperty,r=n((e,t)=>o(e,"name",{value:t,configurable:!0}),"r");Object.defineProperty(exports,"__esModule",{value:!0}),exports.GATEWAY_ERROR_CONTENT_TYPE=exports.GATEWAY_ERROR_KEY=void 0,exports.ensureRequestId=ensureRequestId,exports.gatewayErrorBody=gatewayErrorBody,exports.GATEWAY_ERROR_KEY={bad_request:"gateway.badRequest",unauthorized:"gateway.unauthorized",forbidden:"gateway.forbidden",not_found:"gateway.notFound",rate_limited:"gateway.rateLimited",unavailable:"gateway.unavailable",internal:"gateway.internal"};const headerValue=r(e=>typeof e=="string"&&e!==""?e:Array.isArray(e)?e[0]:void 0,"headerValue"),mintId=r(()=>Date.now().toString(36)+Math.random().toString(36).slice(2,12),"mintId");function ensureRequestId(e){const t=headerValue(e.headers["x-request-id"]);if(t)return t;const a=mintId();return e.headers["x-request-id"]=a,a}n(ensureRequestId,"ensureRequestId"),r(ensureRequestId,"ensureRequestId");function gatewayErrorBody(e,t,a,s){return JSON.stringify({key:exports.GATEWAY_ERROR_KEY[t],status:e,traceId:a,...s?{message:s}:{}})}n(gatewayErrorBody,"gatewayErrorBody"),r(gatewayErrorBody,"gatewayErrorBody"),exports.GATEWAY_ERROR_CONTENT_TYPE="application/json; charset=utf-8";
|
|
@@ -1,53 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.default = gatewayExecutor;
|
|
4
|
-
const gateway_lib_1 = require("./gateway.lib");
|
|
5
|
-
const server_1 = require("./runtime/server");
|
|
6
|
-
/**
|
|
7
|
-
* `gateway` executor — ONE https front door for the whole workspace,
|
|
8
|
-
* assembled dynamically from every project's `cluster` declaration:
|
|
9
|
-
*
|
|
10
|
-
* "cluster": { "host": "api.tetros.ai.local", "service": "api", "prependPrefix": "/api" }
|
|
11
|
-
* "cluster": { "host": "tetros.ai.local", "port": 5173, "default": true }
|
|
12
|
-
*
|
|
13
|
-
* Socket-pool services get production-shaped treatment: the gateway is the
|
|
14
|
-
* load balancer, round-robining each request across the service's child
|
|
15
|
-
* sockets (the pods), with serverless lifecycle (eager/minPods/maxPods/
|
|
16
|
-
* idleKillSec) and an auto `internal.<host>` domain for east-west calls
|
|
17
|
-
* that bypass the middleware (JWT) — a full prod demo on localhost. All of
|
|
18
|
-
* it is published to the LensMCP bus (source `gateway`, category `cluster`).
|
|
19
|
-
*
|
|
20
|
-
* The runtime lives in `gateway.lib.ts` (`startGateway`) so it is testable
|
|
21
|
-
* without signals; this wrapper only adds the executor contract.
|
|
22
|
-
*/
|
|
23
|
-
async function gatewayExecutor(options, context) {
|
|
24
|
-
// LAST-RESORT netting for the long-running daemon (this executor runs for DAYS): a benign socket
|
|
25
|
-
// errno that escaped every layer's local handlers — e.g. a dependency emitting 'error' on a socket
|
|
26
|
-
// it forgot to guard — must cost the ONE connection, never the process. Scoped to exactly the
|
|
27
|
-
// read/write network errnos; anything else preserves crash semantics (print + exit 1, matching
|
|
28
|
-
// Node's default). Lives HERE, not in startGateway, so tests driving the lib keep real crashes loud.
|
|
29
|
-
process.on('uncaughtException', (err) => {
|
|
30
|
-
if ((err.syscall === 'read' || err.syscall === 'write') && server_1.BENIGN_SOCKET_ERRNOS.has(err.code ?? '')) {
|
|
31
|
-
console.warn(`[gateway] survived a stray socket ${err.syscall} error (${err.code}) — one connection dropped, the daemon lives`);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
console.error(err);
|
|
35
|
-
process.exit(1);
|
|
36
|
-
});
|
|
37
|
-
let handle;
|
|
38
|
-
try {
|
|
39
|
-
handle = await (0, gateway_lib_1.startGateway)(options, context);
|
|
40
|
-
}
|
|
41
|
-
catch (e) {
|
|
42
|
-
console.error(e.message);
|
|
43
|
-
return { success: false };
|
|
44
|
-
}
|
|
45
|
-
return new Promise((resolve) => {
|
|
46
|
-
const stop = () => void handle.stop().then(() => resolve({ success: true }));
|
|
47
|
-
process.on('SIGINT', stop);
|
|
48
|
-
process.on('SIGTERM', stop);
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
module.exports = gatewayExecutor;
|
|
52
|
-
module.exports.default = gatewayExecutor;
|
|
53
|
-
module.exports.startGateway = gateway_lib_1.startGateway;
|
|
1
|
+
"use strict";var u=Object.defineProperty;var s=(r,t)=>u(r,"name",{value:t,configurable:!0});var c=Object.defineProperty,o=s((r,t)=>c(r,"name",{value:t,configurable:!0}),"o");Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=gatewayExecutor;const gateway_lib_1=require("./gateway.lib"),server_1=require("./runtime/server");async function gatewayExecutor(r,t){process.on("uncaughtException",e=>{if((e.syscall==="read"||e.syscall==="write")&&server_1.BENIGN_SOCKET_ERRNOS.has(e.code??"")){console.warn(`[gateway] survived a stray socket ${e.syscall} error (${e.code}) \u2014 one connection dropped, the daemon lives`);return}console.error(e),process.exit(1)});let a;try{a=await(0,gateway_lib_1.startGateway)(r,t)}catch(e){return console.error(e.message),{success:!1}}return new Promise(e=>{const n=o(()=>{a.stop().then(()=>e({success:!0}))},"stop");process.on("SIGINT",n),process.on("SIGTERM",n)})}s(gatewayExecutor,"gatewayExecutor"),o(gatewayExecutor,"gatewayExecutor"),module.exports=gatewayExecutor,module.exports.default=gatewayExecutor,module.exports.startGateway=gateway_lib_1.startGateway;
|
|
@@ -1,29 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.discoverRoutes = exports.pickSock = exports.readLensScope = exports.deriveMcpHttpPort = exports.baseDomainOf = exports.stampIdentity = exports.verifyDevJwt = exports.startGateway = exports.hostMatches = void 0;
|
|
4
|
-
/**
|
|
5
|
-
* Public facade for the dev gateway runtime.
|
|
6
|
-
*
|
|
7
|
-
* The implementation now lives in `./runtime/*` as composable LAYERS — see
|
|
8
|
-
* `runtime/server.ts` for the composition root (`startGateway`), `runtime/types.ts`
|
|
9
|
-
* for the shared types + the customer **hooks** API, and the sibling modules for
|
|
10
|
-
* each layer (discovery, dev-auth, observability, lifecycle, lens-children, proxy,
|
|
11
|
-
* auth, hooks, handler, upgrade). This barrel preserves the original module's
|
|
12
|
-
* public surface so the executor wrapper (`gateway.impl.ts`) and the existing
|
|
13
|
-
* specs keep importing from one stable path, and additionally exposes the new
|
|
14
|
-
* hooks types so customers can author typed layers on top of the gateway.
|
|
15
|
-
*/
|
|
16
|
-
var manifest_1 = require("./manifest"); // back-compat re-export
|
|
17
|
-
Object.defineProperty(exports, "hostMatches", { enumerable: true, get: function () { return manifest_1.hostMatches; } });
|
|
18
|
-
var server_1 = require("./runtime/server");
|
|
19
|
-
Object.defineProperty(exports, "startGateway", { enumerable: true, get: function () { return server_1.startGateway; } });
|
|
20
|
-
var dev_auth_1 = require("./runtime/dev-auth");
|
|
21
|
-
Object.defineProperty(exports, "verifyDevJwt", { enumerable: true, get: function () { return dev_auth_1.verifyDevJwt; } });
|
|
22
|
-
Object.defineProperty(exports, "stampIdentity", { enumerable: true, get: function () { return dev_auth_1.stampIdentity; } });
|
|
23
|
-
var scope_1 = require("./runtime/scope");
|
|
24
|
-
Object.defineProperty(exports, "baseDomainOf", { enumerable: true, get: function () { return scope_1.baseDomainOf; } });
|
|
25
|
-
Object.defineProperty(exports, "deriveMcpHttpPort", { enumerable: true, get: function () { return scope_1.deriveMcpHttpPort; } });
|
|
26
|
-
Object.defineProperty(exports, "readLensScope", { enumerable: true, get: function () { return scope_1.readLensScope; } });
|
|
27
|
-
var discovery_1 = require("./runtime/discovery");
|
|
28
|
-
Object.defineProperty(exports, "pickSock", { enumerable: true, get: function () { return discovery_1.pickSock; } });
|
|
29
|
-
Object.defineProperty(exports, "discoverRoutes", { enumerable: true, get: function () { return discovery_1.discoverRoutes; } });
|
|
1
|
+
"use strict";var i=Object.defineProperty;var n=(t,r)=>i(t,"name",{value:r,configurable:!0});var o=Object.defineProperty,e=n((t,r)=>o(t,"name",{value:r,configurable:!0}),"e");Object.defineProperty(exports,"__esModule",{value:!0}),exports.discoverRoutes=exports.pickSock=exports.readLensScope=exports.deriveMcpHttpPort=exports.baseDomainOf=exports.stampIdentity=exports.verifyDevJwt=exports.startGateway=exports.hostMatches=void 0;var manifest_1=require("./manifest");Object.defineProperty(exports,"hostMatches",{enumerable:!0,get:e(function(){return manifest_1.hostMatches},"get")});var server_1=require("./runtime/server");Object.defineProperty(exports,"startGateway",{enumerable:!0,get:e(function(){return server_1.startGateway},"get")});var dev_auth_1=require("./runtime/dev-auth");Object.defineProperty(exports,"verifyDevJwt",{enumerable:!0,get:e(function(){return dev_auth_1.verifyDevJwt},"get")}),Object.defineProperty(exports,"stampIdentity",{enumerable:!0,get:e(function(){return dev_auth_1.stampIdentity},"get")});var scope_1=require("./runtime/scope");Object.defineProperty(exports,"baseDomainOf",{enumerable:!0,get:e(function(){return scope_1.baseDomainOf},"get")}),Object.defineProperty(exports,"deriveMcpHttpPort",{enumerable:!0,get:e(function(){return scope_1.deriveMcpHttpPort},"get")}),Object.defineProperty(exports,"readLensScope",{enumerable:!0,get:e(function(){return scope_1.readLensScope},"get")});var discovery_1=require("./runtime/discovery");Object.defineProperty(exports,"pickSock",{enumerable:!0,get:e(function(){return discovery_1.pickSock},"get")}),Object.defineProperty(exports,"discoverRoutes",{enumerable:!0,get:e(function(){return discovery_1.discoverRoutes},"get")});
|
|
@@ -1,66 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createHealthChecker = createHealthChecker;
|
|
4
|
-
function createHealthChecker(opts = {}) {
|
|
5
|
-
const path = opts.path ?? '/healthz';
|
|
6
|
-
const intervalMs = opts.intervalMs ?? 5000;
|
|
7
|
-
const timeoutMs = opts.timeoutMs ?? 2000;
|
|
8
|
-
const downAt = Math.max(1, opts.unhealthyThreshold ?? 2);
|
|
9
|
-
const upAt = Math.max(1, opts.healthyThreshold ?? 1);
|
|
10
|
-
const doFetch = opts.fetchImpl ?? globalThis.fetch;
|
|
11
|
-
const state = new Map();
|
|
12
|
-
let timer;
|
|
13
|
-
const probeUrl = (url) => url.replace(/\/+$/, '') + (path.startsWith('/') ? path : '/' + path);
|
|
14
|
-
const probe = async (url) => {
|
|
15
|
-
const st = state.get(url);
|
|
16
|
-
if (!st)
|
|
17
|
-
return;
|
|
18
|
-
let ok = false;
|
|
19
|
-
const ctl = new AbortController();
|
|
20
|
-
const t = setTimeout(() => ctl.abort(), timeoutMs);
|
|
21
|
-
try {
|
|
22
|
-
const res = await doFetch(probeUrl(url), { method: 'GET', signal: ctl.signal, redirect: 'manual' });
|
|
23
|
-
ok = res.status < 500; // answered → up (even 404); only 5xx counts as down
|
|
24
|
-
}
|
|
25
|
-
catch { /* network error / timeout → unhealthy (ok stays false) */ }
|
|
26
|
-
finally {
|
|
27
|
-
clearTimeout(t);
|
|
28
|
-
}
|
|
29
|
-
st.lastCheckedAt = Date.now();
|
|
30
|
-
if (ok) {
|
|
31
|
-
st.oks += 1;
|
|
32
|
-
st.fails = 0;
|
|
33
|
-
if (!st.healthy && st.oks >= upAt) {
|
|
34
|
-
st.healthy = true;
|
|
35
|
-
opts.onChange?.(url, true);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
st.fails += 1;
|
|
40
|
-
st.oks = 0;
|
|
41
|
-
if (st.healthy && st.fails >= downAt) {
|
|
42
|
-
st.healthy = false;
|
|
43
|
-
opts.onChange?.(url, false);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
};
|
|
47
|
-
const probeOnce = () => Promise.allSettled([...state.keys()].map(probe)).then(() => undefined);
|
|
48
|
-
return {
|
|
49
|
-
track(urls) {
|
|
50
|
-
const next = new Set(urls);
|
|
51
|
-
for (const u of urls)
|
|
52
|
-
if (!state.has(u))
|
|
53
|
-
state.set(u, { healthy: true, fails: 0, oks: 0, lastCheckedAt: null });
|
|
54
|
-
for (const u of [...state.keys()])
|
|
55
|
-
if (!next.has(u))
|
|
56
|
-
state.delete(u);
|
|
57
|
-
},
|
|
58
|
-
isHealthy: (url) => state.get(url)?.healthy ?? true,
|
|
59
|
-
snapshot: () => [...state.entries()].map(([url, s]) => ({ url, healthy: s.healthy, fails: s.fails, lastCheckedAt: s.lastCheckedAt })),
|
|
60
|
-
probeOnce,
|
|
61
|
-
start() { if (timer)
|
|
62
|
-
return; timer = setInterval(() => void probeOnce(), intervalMs); timer.unref?.(); void probeOnce(); },
|
|
63
|
-
stop() { if (timer)
|
|
64
|
-
clearInterval(timer); timer = undefined; },
|
|
65
|
-
};
|
|
66
|
-
}
|
|
1
|
+
"use strict";var g=Object.defineProperty;var c=(a,h)=>g(a,"name",{value:h,configurable:!0});var C=Object.defineProperty,n=c((a,h)=>C(a,"name",{value:h,configurable:!0}),"n");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createHealthChecker=createHealthChecker;function createHealthChecker(a={}){const h=a.path??"/healthz",f=a.intervalMs??5e3,y=a.timeoutMs??2e3,p=Math.max(1,a.unhealthyThreshold??2),u=Math.max(1,a.healthyThreshold??1),d=a.fetchImpl??globalThis.fetch,l=new Map;let r;const k=n(t=>t.replace(/\/+$/,"")+(h.startsWith("/")?h:"/"+h),"probeUrl"),m=n(async t=>{const e=l.get(t);if(!e)return;let s=!1;const i=new AbortController,b=setTimeout(()=>i.abort(),y);try{s=(await d(k(t),{method:"GET",signal:i.signal,redirect:"manual"})).status<500}catch{}finally{clearTimeout(b)}e.lastCheckedAt=Date.now(),s?(e.oks+=1,e.fails=0,!e.healthy&&e.oks>=u&&(e.healthy=!0,a.onChange?.(t,!0))):(e.fails+=1,e.oks=0,e.healthy&&e.fails>=p&&(e.healthy=!1,a.onChange?.(t,!1)))},"probe"),o=n(()=>Promise.allSettled([...l.keys()].map(m)).then(()=>{}),"probeOnce");return{track(t){const e=new Set(t);for(const s of t)l.has(s)||l.set(s,{healthy:!0,fails:0,oks:0,lastCheckedAt:null});for(const s of[...l.keys()])e.has(s)||l.delete(s)},isHealthy:n(t=>l.get(t)?.healthy??!0,"isHealthy"),snapshot:n(()=>[...l.entries()].map(([t,e])=>({url:t,healthy:e.healthy,fails:e.fails,lastCheckedAt:e.lastCheckedAt})),"snapshot"),probeOnce:o,start(){r||(r=setInterval(()=>{o()},f),r.unref?.(),o())},stop(){r&&clearInterval(r),r=void 0}}}c(createHealthChecker,"createHealthChecker"),n(createHealthChecker,"createHealthChecker");
|
|
@@ -1,121 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.createJwksVerifier = createJwksVerifier;
|
|
4
|
-
/**
|
|
5
|
-
* RS256/JWKS JWT verifier — the production identity source for the prod
|
|
6
|
-
* gateway's attribute-based routing (`identify`). Fetches the IdP's JWKS,
|
|
7
|
-
* caches public keys by `kid`, and verifies tokens SYNCHRONOUSLY against that
|
|
8
|
-
* cache so it slots into the gateway's per-request `identify(req)` and rule
|
|
9
|
-
* evaluation without making the hot path async.
|
|
10
|
-
*
|
|
11
|
-
* Freshness without blocking the request:
|
|
12
|
-
* - initial JWKS load is awaited at startup (`refresh()`),
|
|
13
|
-
* - a periodic refresh keeps keys current (rotation),
|
|
14
|
-
* - an unknown `kid` triggers a rate-limited background refresh and the
|
|
15
|
-
* CURRENT request fails closed (undefined) — the token verifies on a later
|
|
16
|
-
* request once the new key is cached.
|
|
17
|
-
*
|
|
18
|
-
* Security: only the configured algorithms are accepted (default RS256) — `none`
|
|
19
|
-
* and HS* are rejected (alg-confusion guard); `exp`/`nbf` honoured with a small
|
|
20
|
-
* clock tolerance; `iss`/`aud` checked when configured.
|
|
21
|
-
*
|
|
22
|
-
* Scope: RSA families (RS256/384/512). EC (ES*) needs JOSE→DER signature
|
|
23
|
-
* conversion — a documented follow-up; most IdPs default to RS256.
|
|
24
|
-
*/
|
|
25
|
-
const node_crypto_1 = require("node:crypto");
|
|
26
|
-
const ALG_TO_HASH = { RS256: 'sha256', RS384: 'sha384', RS512: 'sha512' };
|
|
27
|
-
const b64urlJsonSafe = (s) => JSON.parse(Buffer.from(s, 'base64url').toString('utf8'));
|
|
28
|
-
function createJwksVerifier(opts) {
|
|
29
|
-
const algorithms = (opts.algorithms ?? ['RS256']).filter((a) => a in ALG_TO_HASH);
|
|
30
|
-
const cacheMaxAgeMs = opts.cacheMaxAgeMs ?? 600_000;
|
|
31
|
-
const missRefreshMs = opts.missRefreshMs ?? 30_000;
|
|
32
|
-
const tol = opts.clockToleranceSec ?? 60;
|
|
33
|
-
const doFetch = opts.fetchImpl ?? globalThis.fetch;
|
|
34
|
-
const audiences = opts.audience === undefined ? undefined : (Array.isArray(opts.audience) ? opts.audience : [opts.audience]);
|
|
35
|
-
let keys = new Map(); // kid → public key
|
|
36
|
-
let inflight;
|
|
37
|
-
let lastMissAt = 0;
|
|
38
|
-
const load = async () => {
|
|
39
|
-
const res = await doFetch(opts.jwksUrl, { headers: { accept: 'application/json' } });
|
|
40
|
-
if (!res.ok)
|
|
41
|
-
throw new Error(`JWKS ${opts.jwksUrl} → HTTP ${res.status}`);
|
|
42
|
-
const body = (await res.json());
|
|
43
|
-
const next = new Map();
|
|
44
|
-
for (const jwk of body.keys ?? []) {
|
|
45
|
-
if (jwk.kty !== 'RSA' || !jwk.n || !jwk.e)
|
|
46
|
-
continue;
|
|
47
|
-
if (jwk.use && jwk.use !== 'sig')
|
|
48
|
-
continue;
|
|
49
|
-
try {
|
|
50
|
-
const key = (0, node_crypto_1.createPublicKey)({ key: jwk, format: 'jwk' });
|
|
51
|
-
next.set(jwk.kid ?? '__single__', key);
|
|
52
|
-
}
|
|
53
|
-
catch { /* skip an unparseable key, keep the rest */ }
|
|
54
|
-
}
|
|
55
|
-
if (next.size > 0)
|
|
56
|
-
keys = next; // never blank out a working set on a bad fetch
|
|
57
|
-
};
|
|
58
|
-
const refresh = () => {
|
|
59
|
-
if (!inflight)
|
|
60
|
-
inflight = load().finally(() => { inflight = undefined; });
|
|
61
|
-
return inflight;
|
|
62
|
-
};
|
|
63
|
-
const refreshOnMiss = (nowMs) => {
|
|
64
|
-
if (nowMs - lastMissAt < missRefreshMs)
|
|
65
|
-
return;
|
|
66
|
-
lastMissAt = nowMs;
|
|
67
|
-
void refresh().catch(() => undefined); // background; this request already failed closed
|
|
68
|
-
};
|
|
69
|
-
const timer = setInterval(() => void refresh().catch(() => undefined), cacheMaxAgeMs);
|
|
70
|
-
timer.unref?.();
|
|
71
|
-
const verify = (token) => {
|
|
72
|
-
const parts = token.split('.');
|
|
73
|
-
if (parts.length !== 3)
|
|
74
|
-
return undefined;
|
|
75
|
-
let header;
|
|
76
|
-
try {
|
|
77
|
-
header = b64urlJsonSafe(parts[0]);
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
if (!header.alg || !algorithms.includes(header.alg))
|
|
83
|
-
return undefined; // reject none/HS*/unconfigured
|
|
84
|
-
const key = keys.get(header.kid ?? '__single__') ?? (keys.size === 1 ? [...keys.values()][0] : undefined);
|
|
85
|
-
if (!key) {
|
|
86
|
-
refreshOnMiss(Date.now());
|
|
87
|
-
return undefined;
|
|
88
|
-
}
|
|
89
|
-
let ok;
|
|
90
|
-
try {
|
|
91
|
-
ok = (0, node_crypto_1.verify)(ALG_TO_HASH[header.alg], Buffer.from(`${parts[0]}.${parts[1]}`), key, Buffer.from(parts[2], 'base64url'));
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
return undefined;
|
|
95
|
-
}
|
|
96
|
-
if (!ok)
|
|
97
|
-
return undefined;
|
|
98
|
-
let payload;
|
|
99
|
-
try {
|
|
100
|
-
payload = b64urlJsonSafe(parts[1]);
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
return undefined;
|
|
104
|
-
}
|
|
105
|
-
const now = Date.now() / 1000;
|
|
106
|
-
if (typeof payload.exp === 'number' && now > payload.exp + tol)
|
|
107
|
-
return undefined;
|
|
108
|
-
if (typeof payload.nbf === 'number' && now < payload.nbf - tol)
|
|
109
|
-
return undefined;
|
|
110
|
-
if (opts.issuer && payload.iss !== opts.issuer)
|
|
111
|
-
return undefined;
|
|
112
|
-
if (audiences) {
|
|
113
|
-
const aud = payload.aud;
|
|
114
|
-
const audList = Array.isArray(aud) ? aud : [aud];
|
|
115
|
-
if (!audList.some((a) => audiences.includes(a)))
|
|
116
|
-
return undefined;
|
|
117
|
-
}
|
|
118
|
-
return payload;
|
|
119
|
-
};
|
|
120
|
-
return { verify, refresh, stop: () => clearInterval(timer) };
|
|
121
|
-
}
|
|
1
|
+
"use strict";var J=Object.defineProperty;var h=(e,n)=>J(e,"name",{value:n,configurable:!0});var v=Object.defineProperty,c=h((e,n)=>v(e,"name",{value:n,configurable:!0}),"c");Object.defineProperty(exports,"__esModule",{value:!0}),exports.createJwksVerifier=createJwksVerifier;const node_crypto_1=require("node:crypto"),ALG_TO_HASH={RS256:"sha256",RS384:"sha384",RS512:"sha512"},b64urlJsonSafe=c(e=>JSON.parse(Buffer.from(e,"base64url").toString("utf8")),"b64urlJsonSafe");function createJwksVerifier(e){const n=(e.algorithms??["RS256"]).filter(r=>r in ALG_TO_HASH),g=e.cacheMaxAgeMs??6e5,k=e.missRefreshMs??3e4,d=e.clockToleranceSec??60,S=e.fetchImpl??globalThis.fetch,p=e.audience===void 0?void 0:Array.isArray(e.audience)?e.audience:[e.audience];let o=new Map,f,w=0;const m=c(async()=>{const r=await S(e.jwksUrl,{headers:{accept:"application/json"}});if(!r.ok)throw new Error(`JWKS ${e.jwksUrl} \u2192 HTTP ${r.status}`);const a=await r.json(),s=new Map;for(const t of a.keys??[])if(!(t.kty!=="RSA"||!t.n||!t.e)&&!(t.use&&t.use!=="sig"))try{const u=(0,node_crypto_1.createPublicKey)({key:t,format:"jwk"});s.set(t.kid??"__single__",u)}catch{}s.size>0&&(o=s)},"load"),l=c(()=>(f||(f=m().finally(()=>{f=void 0})),f),"refresh"),A=c(r=>{r-w<k||(w=r,l().catch(()=>{}))},"refreshOnMiss"),_=setInterval(()=>{l().catch(()=>{})},g);return _.unref?.(),{verify:c(r=>{const a=r.split(".");if(a.length!==3)return;let s;try{s=b64urlJsonSafe(a[0])}catch{return}if(!s.alg||!n.includes(s.alg))return;const t=o.get(s.kid??"__single__")??(o.size===1?[...o.values()][0]:void 0);if(!t){A(Date.now());return}let u;try{u=(0,node_crypto_1.verify)(ALG_TO_HASH[s.alg],Buffer.from(`${a[0]}.${a[1]}`),t,Buffer.from(a[2],"base64url"))}catch{return}if(!u)return;let i;try{i=b64urlJsonSafe(a[1])}catch{return}const b=Date.now()/1e3;if(!(typeof i.exp=="number"&&b>i.exp+d)&&!(typeof i.nbf=="number"&&b<i.nbf-d)&&!(e.issuer&&i.iss!==e.issuer)){if(p){const y=i.aud;if(!(Array.isArray(y)?y:[y]).some(j=>p.includes(j)))return}return i}},"verify"),refresh:l,stop:c(()=>clearInterval(_),"stop")}}h(createJwksVerifier,"createJwksVerifier"),c(createJwksVerifier,"createJwksVerifier");
|