@zintrust/core 0.4.31 → 0.4.33
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/package.json +5 -1
- package/src/boot.d.ts +3 -0
- package/src/boot.d.ts.map +1 -0
- package/src/boot.js +1 -0
- package/src/cli/commands/D1ProxyCommand.d.ts.map +1 -1
- package/src/cli/commands/D1ProxyCommand.js +24 -159
- package/src/cli/commands/KvProxyCommand.d.ts.map +1 -1
- package/src/cli/commands/KvProxyCommand.js +23 -155
- package/src/cli/commands/ProxyScaffoldUtils.d.ts +31 -0
- package/src/cli/commands/ProxyScaffoldUtils.d.ts.map +1 -0
- package/src/cli/commands/ProxyScaffoldUtils.js +120 -0
- package/src/cli/commands/WranglerProxyCommandUtils.d.ts +26 -0
- package/src/cli/commands/WranglerProxyCommandUtils.d.ts.map +1 -0
- package/src/cli/commands/WranglerProxyCommandUtils.js +63 -0
- package/src/cli/scaffolding/GovernanceScaffolder.d.ts.map +1 -1
- package/src/cli/scaffolding/GovernanceScaffolder.js +3 -40
- package/src/cli/scaffolding/ProjectScaffolder.d.ts.map +1 -1
- package/src/cli/scaffolding/ProjectScaffolder.js +1 -34
- package/src/cli/scaffolding/ScaffoldingVersionUtils.d.ts +6 -0
- package/src/cli/scaffolding/ScaffoldingVersionUtils.d.ts.map +1 -0
- package/src/cli/scaffolding/ScaffoldingVersionUtils.js +34 -0
- package/src/config/middleware.d.ts +5 -2
- package/src/config/middleware.d.ts.map +1 -1
- package/src/config/middleware.js +56 -4
- package/src/index.js +3 -3
- package/src/proxy/kv/ZintrustKvProxy.js +1 -1
- package/src/templates/project/basic/src/boot/bootstrap.ts.tpl +3 -0
package/README.md
CHANGED
|
@@ -30,6 +30,21 @@ If you’re targeting a different runtime:
|
|
|
30
30
|
|
|
31
31
|
The canonical CLI is `zin`. `z` is a shorthand alias.
|
|
32
32
|
|
|
33
|
+
## Reuse The Stock Bootstrap
|
|
34
|
+
|
|
35
|
+
If your project needs a `src/boot/bootstrap.ts` entry for Docker, Node, or custom startup layout, you do not need to copy the full ZinTrust bootstrap implementation into your app.
|
|
36
|
+
|
|
37
|
+
Use a thin bootstrap file that delegates to the published core boot entrypoint:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
// src/boot/bootstrap.ts
|
|
41
|
+
import '@zintrust/core/boot';
|
|
42
|
+
|
|
43
|
+
export {};
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Use a full custom bootstrap only when you intentionally need behavior different from the stock ZinTrust bootstrap. If you replace it completely, you are also responsible for running any worker startup lifecycle that your app depends on.
|
|
47
|
+
|
|
33
48
|
## Install adapters (database/cache/etc.)
|
|
34
49
|
|
|
35
50
|
ZinTrust keeps the core package minimal. Integrations like database drivers are installed explicitly via adapter packages.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zintrust/core",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.33",
|
|
4
4
|
"description": "Production-grade TypeScript backend framework for JavaScript",
|
|
5
5
|
"homepage": "https://zintrust.com",
|
|
6
6
|
"repository": {
|
|
@@ -22,6 +22,10 @@
|
|
|
22
22
|
"types": "./src/start.d.ts",
|
|
23
23
|
"import": "./src/start.js"
|
|
24
24
|
},
|
|
25
|
+
"./boot": {
|
|
26
|
+
"types": "./src/boot.d.ts",
|
|
27
|
+
"import": "./src/boot.js"
|
|
28
|
+
},
|
|
25
29
|
"./cli": {
|
|
26
30
|
"types": "./src/cli.d.ts",
|
|
27
31
|
"import": "./src/cli.js"
|
package/src/boot.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boot.d.ts","sourceRoot":"","sources":["../../src/boot.ts"],"names":[],"mappings":"AAAA,OAAO,iBAAiB,CAAC;AAEzB,OAAO,EAAE,CAAC"}
|
package/src/boot.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './boot/bootstrap.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"D1ProxyCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/D1ProxyCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"D1ProxyCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/D1ProxyCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAmHrD,eAAO,MAAM,cAAc;cACf,YAAY;EAkBtB,CAAC;AAEH,eAAe,cAAc,CAAC"}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { SpawnUtil } from '../utils/spawn.js';
|
|
1
|
+
import { findQuotedValue, trimNonEmptyOption } from '../commands/ProxyScaffoldUtils.js';
|
|
2
|
+
import { addWranglerProxyBaseOptions, createWranglerProxyCommand, } from '../commands/WranglerProxyCommandUtils.js';
|
|
4
3
|
import { Env } from '../../config/env.js';
|
|
5
4
|
import { Logger } from '../../config/logger.js';
|
|
6
|
-
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
|
|
7
|
-
import { isNonEmptyString } from '../../helper/index.js';
|
|
8
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from '../../node-singletons/fs.js';
|
|
9
|
-
import { join } from '../../node-singletons/path.js';
|
|
10
5
|
const DEFAULT_CONFIG = 'wrangler.jsonc';
|
|
11
6
|
const DEFAULT_COMPATIBILITY_DATE = '2026-03-12';
|
|
12
7
|
const DEFAULT_BINDING = 'ZIN_DB';
|
|
@@ -14,76 +9,25 @@ const DEFAULT_DATABASE_NAME = 'd1-proxy-db';
|
|
|
14
9
|
const DEFAULT_DATABASE_ID = '<your-d1-database-id>';
|
|
15
10
|
const DEFAULT_MIGRATIONS_DIR = 'database/migrations/d1';
|
|
16
11
|
const DEFAULT_ENTRY_FILE = 'src/proxy/d1/ZintrustD1Proxy.ts';
|
|
12
|
+
const DEFAULT_ROUTE_PATTERN = 'd1-proxy.example.com';
|
|
17
13
|
const CORE_PROXY_MODULE = ['@zintrust', 'core', 'proxy'].join('/');
|
|
18
|
-
const trimOption = (value) => {
|
|
19
|
-
if (!isNonEmptyString(value))
|
|
20
|
-
return undefined;
|
|
21
|
-
const trimmed = value.trim();
|
|
22
|
-
return trimmed.length > 0 ? trimmed : undefined;
|
|
23
|
-
};
|
|
24
|
-
const resolveConfigPath = (raw) => {
|
|
25
|
-
const trimmed = trimOption(raw);
|
|
26
|
-
return trimmed ?? DEFAULT_CONFIG;
|
|
27
|
-
};
|
|
28
|
-
const isJsonWhitespace = (char) => {
|
|
29
|
-
return char === ' ' || char === '\n' || char === '\r' || char === '\t';
|
|
30
|
-
};
|
|
31
|
-
const findJsonKeyValueStart = (content, key) => {
|
|
32
|
-
const keyPosition = content.indexOf(`"${key}"`);
|
|
33
|
-
if (keyPosition < 0)
|
|
34
|
-
return -1;
|
|
35
|
-
let cursor = keyPosition + key.length + 2;
|
|
36
|
-
while (isJsonWhitespace(content[cursor]))
|
|
37
|
-
cursor++;
|
|
38
|
-
if (content[cursor] !== ':')
|
|
39
|
-
return -1;
|
|
40
|
-
cursor++;
|
|
41
|
-
while (isJsonWhitespace(content[cursor]))
|
|
42
|
-
cursor++;
|
|
43
|
-
return cursor;
|
|
44
|
-
};
|
|
45
|
-
const findQuotedValue = (content, key) => {
|
|
46
|
-
const valueStart = findJsonKeyValueStart(content, key);
|
|
47
|
-
if (valueStart < 0 || content[valueStart] !== '"')
|
|
48
|
-
return undefined;
|
|
49
|
-
const valueEnd = content.indexOf('"', valueStart + 1);
|
|
50
|
-
if (valueEnd < 0)
|
|
51
|
-
return undefined;
|
|
52
|
-
return trimOption(content.slice(valueStart + 1, valueEnd));
|
|
53
|
-
};
|
|
54
|
-
const hasEnvBlock = (content, envName) => {
|
|
55
|
-
const valueStart = findJsonKeyValueStart(content, envName);
|
|
56
|
-
return valueStart >= 0 && content[valueStart] === '{';
|
|
57
|
-
};
|
|
58
|
-
const findEnvObjectStart = (content) => {
|
|
59
|
-
const valueStart = findJsonKeyValueStart(content, 'env');
|
|
60
|
-
if (valueStart < 0 || content[valueStart] !== '{')
|
|
61
|
-
return -1;
|
|
62
|
-
return valueStart;
|
|
63
|
-
};
|
|
64
|
-
const isObjectEffectivelyEmpty = (content, objectStart) => {
|
|
65
|
-
let cursor = objectStart + 1;
|
|
66
|
-
while (isJsonWhitespace(content[cursor]))
|
|
67
|
-
cursor++;
|
|
68
|
-
return content[cursor] === '}';
|
|
69
|
-
};
|
|
70
14
|
const resolveConfigValues = (content, options) => {
|
|
71
15
|
const fileContent = content ?? '';
|
|
72
16
|
return {
|
|
73
|
-
binding:
|
|
74
|
-
|
|
17
|
+
binding: trimNonEmptyOption(options.binding) ??
|
|
18
|
+
trimNonEmptyOption(Env.get('D1_BINDING', '')) ??
|
|
75
19
|
findQuotedValue(fileContent, 'D1_BINDING') ??
|
|
76
20
|
findQuotedValue(fileContent, 'binding') ??
|
|
77
21
|
DEFAULT_BINDING,
|
|
78
|
-
databaseName:
|
|
79
|
-
|
|
22
|
+
databaseName: trimNonEmptyOption(options.databaseName) ??
|
|
23
|
+
trimNonEmptyOption(Env.get('D1_DATABASE_NAME', '')) ??
|
|
80
24
|
findQuotedValue(fileContent, 'database_name') ??
|
|
81
25
|
DEFAULT_DATABASE_NAME,
|
|
82
|
-
databaseId:
|
|
83
|
-
|
|
26
|
+
databaseId: trimNonEmptyOption(options.databaseId) ??
|
|
27
|
+
trimNonEmptyOption(Env.get('D1_DATABASE_ID', '')) ??
|
|
84
28
|
findQuotedValue(fileContent, 'database_id') ??
|
|
85
29
|
DEFAULT_DATABASE_ID,
|
|
86
|
-
migrationsDir:
|
|
30
|
+
migrationsDir: trimNonEmptyOption(options.migrationsDir) ??
|
|
87
31
|
findQuotedValue(fileContent, 'migrations_dir') ??
|
|
88
32
|
DEFAULT_MIGRATIONS_DIR,
|
|
89
33
|
};
|
|
@@ -111,81 +55,19 @@ const renderD1ProxyEnvBlock = (values) => {
|
|
|
111
55
|
` "database_id": "${values.databaseId}",`,
|
|
112
56
|
` "migrations_dir": "${values.migrationsDir}"`,
|
|
113
57
|
' }',
|
|
114
|
-
' ]',
|
|
58
|
+
' ],',
|
|
59
|
+
' // Add routes here when ready:',
|
|
60
|
+
` // "routes": [{ "pattern": "${DEFAULT_ROUTE_PATTERN}", "custom_domain": true }]`,
|
|
115
61
|
' }',
|
|
116
62
|
].join('\n');
|
|
117
63
|
};
|
|
118
|
-
const renderDefaultWranglerConfig = (values) => {
|
|
119
|
-
return [
|
|
120
|
-
'{',
|
|
121
|
-
' "name": "zintrust-api",',
|
|
122
|
-
' "main": "./src/functions/cloudflare.ts",',
|
|
123
|
-
` "compatibility_date": "${DEFAULT_COMPATIBILITY_DATE}",`,
|
|
124
|
-
' "compatibility_flags": ["nodejs_compat"],',
|
|
125
|
-
' "env": {',
|
|
126
|
-
renderD1ProxyEnvBlock(values),
|
|
127
|
-
' }',
|
|
128
|
-
'}',
|
|
129
|
-
'',
|
|
130
|
-
].join('\n');
|
|
131
|
-
};
|
|
132
|
-
const ensureProxyEntrypoint = (cwd) => {
|
|
133
|
-
const entryFilePath = join(cwd, DEFAULT_ENTRY_FILE);
|
|
134
|
-
if (existsSync(entryFilePath)) {
|
|
135
|
-
return { created: false, entryFilePath };
|
|
136
|
-
}
|
|
137
|
-
mkdirSync(join(cwd, 'src/proxy/d1'), { recursive: true });
|
|
138
|
-
writeFileSync(entryFilePath, [
|
|
139
|
-
`export { ZintrustD1Proxy } from '${CORE_PROXY_MODULE}';`,
|
|
140
|
-
`export { ZintrustD1Proxy as default } from '${CORE_PROXY_MODULE}';`,
|
|
141
|
-
'',
|
|
142
|
-
].join('\n'), 'utf-8');
|
|
143
|
-
return { created: true, entryFilePath };
|
|
144
|
-
};
|
|
145
|
-
const injectEnvBlock = (content, block) => {
|
|
146
|
-
if (hasEnvBlock(content, 'd1-proxy'))
|
|
147
|
-
return content;
|
|
148
|
-
const envObjectStart = findEnvObjectStart(content);
|
|
149
|
-
if (envObjectStart >= 0 && isObjectEffectivelyEmpty(content, envObjectStart)) {
|
|
150
|
-
const closingBraceIndex = content.indexOf('}', envObjectStart);
|
|
151
|
-
if (closingBraceIndex >= 0) {
|
|
152
|
-
return `${content.slice(0, envObjectStart)}{\n${block}\n }${content.slice(closingBraceIndex + 1)}`;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (envObjectStart >= 0) {
|
|
156
|
-
return `${content.slice(0, envObjectStart + 1)}\n${block},${content.slice(envObjectStart + 1)}`;
|
|
157
|
-
}
|
|
158
|
-
const closingIndex = content.lastIndexOf('}');
|
|
159
|
-
if (closingIndex < 0) {
|
|
160
|
-
throw ErrorFactory.createCliError('Invalid wrangler.jsonc: missing closing brace.');
|
|
161
|
-
}
|
|
162
|
-
const before = content.slice(0, closingIndex).trimEnd();
|
|
163
|
-
const suffix = before.endsWith('{') ? '\n' : ',\n';
|
|
164
|
-
return `${before}${suffix} "env": {\n${block}\n }\n}\n`;
|
|
165
|
-
};
|
|
166
|
-
const ensureWranglerConfig = (configPath, options) => {
|
|
167
|
-
if (!existsSync(configPath)) {
|
|
168
|
-
const values = resolveConfigValues(undefined, options);
|
|
169
|
-
writeFileSync(configPath, renderDefaultWranglerConfig(values), 'utf-8');
|
|
170
|
-
return { createdFile: true, insertedEnv: true, values };
|
|
171
|
-
}
|
|
172
|
-
const content = readFileSync(configPath, 'utf-8');
|
|
173
|
-
const values = resolveConfigValues(content, options);
|
|
174
|
-
const next = injectEnvBlock(content, renderD1ProxyEnvBlock(values));
|
|
175
|
-
if (next !== content) {
|
|
176
|
-
writeFileSync(configPath, next, 'utf-8');
|
|
177
|
-
return { createdFile: false, insertedEnv: true, values };
|
|
178
|
-
}
|
|
179
|
-
return { createdFile: false, insertedEnv: false, values };
|
|
180
|
-
};
|
|
181
64
|
const warnOnPlaceholderDatabaseId = (values) => {
|
|
182
65
|
if (values.databaseId !== DEFAULT_DATABASE_ID)
|
|
183
66
|
return;
|
|
184
67
|
Logger.warn('Could not resolve a D1 database id automatically. Update wrangler.jsonc or pass --database-id before relying on the generated d1-proxy environment.');
|
|
185
68
|
};
|
|
186
69
|
const addOptions = (command) => {
|
|
187
|
-
command
|
|
188
|
-
command.option('--watch', 'Auto-restart proxy on file changes');
|
|
70
|
+
addWranglerProxyBaseOptions(command, DEFAULT_CONFIG);
|
|
189
71
|
command.option('--binding <name>', 'D1 binding name', DEFAULT_BINDING);
|
|
190
72
|
command.option('--database-name <name>', 'Cloudflare D1 database name');
|
|
191
73
|
command.option('--database-id <id>', 'Cloudflare D1 database id');
|
|
@@ -193,37 +75,20 @@ const addOptions = (command) => {
|
|
|
193
75
|
};
|
|
194
76
|
export const D1ProxyCommand = Object.freeze({
|
|
195
77
|
create() {
|
|
196
|
-
return
|
|
78
|
+
return createWranglerProxyCommand({
|
|
197
79
|
name: 'proxy:d1',
|
|
198
80
|
aliases: ['d1:proxy'],
|
|
199
81
|
description: 'Start the local Cloudflare D1 proxy Worker via Wrangler and scaffold env.d1-proxy in wrangler.jsonc when missing',
|
|
82
|
+
envName: 'd1-proxy',
|
|
83
|
+
defaultConfig: DEFAULT_CONFIG,
|
|
84
|
+
compatibilityDate: DEFAULT_COMPATIBILITY_DATE,
|
|
85
|
+
entryFile: DEFAULT_ENTRY_FILE,
|
|
86
|
+
exportName: 'ZintrustD1Proxy',
|
|
87
|
+
moduleSpecifier: CORE_PROXY_MODULE,
|
|
200
88
|
addOptions,
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const entrypoint = ensureProxyEntrypoint(cwd);
|
|
205
|
-
const configPath = join(cwd, resolveConfigPath(options.config));
|
|
206
|
-
const result = ensureWranglerConfig(configPath, options);
|
|
207
|
-
if (entrypoint.created) {
|
|
208
|
-
Logger.info(`Created ${entrypoint.entryFilePath} from @zintrust/core proxy entrypoint.`);
|
|
209
|
-
}
|
|
210
|
-
if (result.createdFile) {
|
|
211
|
-
Logger.info(`Created ${configPath} with a default d1-proxy environment.`);
|
|
212
|
-
}
|
|
213
|
-
else if (result.insertedEnv) {
|
|
214
|
-
Logger.info(`Added env.d1-proxy to ${configPath}.`);
|
|
215
|
-
}
|
|
216
|
-
warnOnPlaceholderDatabaseId(result.values);
|
|
217
|
-
const exitCode = await SpawnUtil.spawnAndWait({
|
|
218
|
-
command: 'wrangler',
|
|
219
|
-
args: ['dev', '--config', configPath, '--env', 'd1-proxy'],
|
|
220
|
-
env: process.env,
|
|
221
|
-
forwardSignals: false,
|
|
222
|
-
});
|
|
223
|
-
if (exitCode !== 0) {
|
|
224
|
-
process.exit(exitCode);
|
|
225
|
-
}
|
|
226
|
-
},
|
|
89
|
+
resolveValues: resolveConfigValues,
|
|
90
|
+
renderEnvBlock: renderD1ProxyEnvBlock,
|
|
91
|
+
afterConfigResolved: warnOnPlaceholderDatabaseId,
|
|
227
92
|
});
|
|
228
93
|
},
|
|
229
94
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KvProxyCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/KvProxyCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"KvProxyCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/KvProxyCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAmGrD,eAAO,MAAM,cAAc;cACf,YAAY;EAkBtB,CAAC;AAEH,eAAe,cAAc,CAAC"}
|
|
@@ -1,83 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { SpawnUtil } from '../utils/spawn.js';
|
|
1
|
+
import { findQuotedValue, trimNonEmptyOption } from '../commands/ProxyScaffoldUtils.js';
|
|
2
|
+
import { addWranglerProxyBaseOptions, createWranglerProxyCommand, } from '../commands/WranglerProxyCommandUtils.js';
|
|
4
3
|
import { Env } from '../../config/env.js';
|
|
5
4
|
import { Logger } from '../../config/logger.js';
|
|
6
|
-
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
|
|
7
|
-
import { isNonEmptyString } from '../../helper/index.js';
|
|
8
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from '../../node-singletons/fs.js';
|
|
9
|
-
import { join } from '../../node-singletons/path.js';
|
|
10
5
|
const DEFAULT_CONFIG = 'wrangler.jsonc';
|
|
11
6
|
const DEFAULT_COMPATIBILITY_DATE = '2026-03-12';
|
|
12
7
|
const DEFAULT_BINDING = 'ZIN_KV';
|
|
13
8
|
const DEFAULT_NAMESPACE_ID = '<your-kv-namespace-id>';
|
|
14
9
|
const DEFAULT_ENTRY_FILE = 'src/proxy/kv/ZintrustKvProxy.ts';
|
|
10
|
+
const DEFAULT_ROUTE_PATTERN = 'kv-proxy.example.com';
|
|
15
11
|
const CORE_PROXY_MODULE = ['@zintrust', 'core', 'proxy'].join('/');
|
|
16
|
-
const trimOption = (value) => {
|
|
17
|
-
if (!isNonEmptyString(value))
|
|
18
|
-
return undefined;
|
|
19
|
-
const trimmed = value.trim();
|
|
20
|
-
return trimmed.length > 0 ? trimmed : undefined;
|
|
21
|
-
};
|
|
22
|
-
const resolveConfigPath = (raw) => trimOption(raw) ?? DEFAULT_CONFIG;
|
|
23
|
-
const isJsonWhitespace = (char) => {
|
|
24
|
-
return char === ' ' || char === '\n' || char === '\r' || char === '\t';
|
|
25
|
-
};
|
|
26
|
-
const findJsonKeyValueStart = (content, key) => {
|
|
27
|
-
const keyPosition = content.indexOf(`"${key}"`);
|
|
28
|
-
if (keyPosition < 0)
|
|
29
|
-
return -1;
|
|
30
|
-
let cursor = keyPosition + key.length + 2;
|
|
31
|
-
while (isJsonWhitespace(content[cursor]))
|
|
32
|
-
cursor++;
|
|
33
|
-
if (content[cursor] !== ':')
|
|
34
|
-
return -1;
|
|
35
|
-
cursor++;
|
|
36
|
-
while (isJsonWhitespace(content[cursor]))
|
|
37
|
-
cursor++;
|
|
38
|
-
return cursor;
|
|
39
|
-
};
|
|
40
|
-
const findQuotedValue = (content, key) => {
|
|
41
|
-
const valueStart = findJsonKeyValueStart(content, key);
|
|
42
|
-
if (valueStart < 0 || content[valueStart] !== '"')
|
|
43
|
-
return undefined;
|
|
44
|
-
const valueEnd = content.indexOf('"', valueStart + 1);
|
|
45
|
-
if (valueEnd < 0)
|
|
46
|
-
return undefined;
|
|
47
|
-
return trimOption(content.slice(valueStart + 1, valueEnd));
|
|
48
|
-
};
|
|
49
|
-
const hasEnvBlock = (content, envName) => {
|
|
50
|
-
const valueStart = findJsonKeyValueStart(content, envName);
|
|
51
|
-
return valueStart >= 0 && content[valueStart] === '{';
|
|
52
|
-
};
|
|
53
|
-
const findEnvObjectStart = (content) => {
|
|
54
|
-
const valueStart = findJsonKeyValueStart(content, 'env');
|
|
55
|
-
if (valueStart < 0 || content[valueStart] !== '{')
|
|
56
|
-
return -1;
|
|
57
|
-
return valueStart;
|
|
58
|
-
};
|
|
59
|
-
const isObjectEffectivelyEmpty = (content, objectStart) => {
|
|
60
|
-
let cursor = objectStart + 1;
|
|
61
|
-
while (isJsonWhitespace(content[cursor]))
|
|
62
|
-
cursor++;
|
|
63
|
-
return content[cursor] === '}';
|
|
64
|
-
};
|
|
65
12
|
const resolveConfigValues = (content, options) => {
|
|
66
13
|
const fileContent = content ?? '';
|
|
67
|
-
const namespaceId =
|
|
68
|
-
|
|
14
|
+
const namespaceId = trimNonEmptyOption(options.namespaceId) ??
|
|
15
|
+
trimNonEmptyOption(Env.get('KV_NAMESPACE_ID', '')) ??
|
|
69
16
|
findQuotedValue(fileContent, 'preview_id') ??
|
|
70
17
|
findQuotedValue(fileContent, 'id') ??
|
|
71
18
|
DEFAULT_NAMESPACE_ID;
|
|
72
19
|
return {
|
|
73
|
-
binding:
|
|
74
|
-
|
|
20
|
+
binding: trimNonEmptyOption(options.binding) ??
|
|
21
|
+
trimNonEmptyOption(Env.get('KV_NAMESPACE', '')) ??
|
|
75
22
|
findQuotedValue(fileContent, 'KV_NAMESPACE') ??
|
|
76
23
|
findQuotedValue(fileContent, 'binding') ??
|
|
77
24
|
DEFAULT_BINDING,
|
|
78
25
|
namespaceId,
|
|
79
|
-
previewId:
|
|
80
|
-
|
|
26
|
+
previewId: trimNonEmptyOption(options.previewId) ??
|
|
27
|
+
trimNonEmptyOption(Env.get('KV_NAMESPACE_PREVIEW_ID', '')) ??
|
|
81
28
|
findQuotedValue(fileContent, 'preview_id') ??
|
|
82
29
|
namespaceId,
|
|
83
30
|
};
|
|
@@ -100,118 +47,39 @@ const renderKvProxyEnvBlock = (values) => {
|
|
|
100
47
|
` "preview_id": "${values.previewId}",`,
|
|
101
48
|
' "remote": false',
|
|
102
49
|
' }',
|
|
103
|
-
' ]',
|
|
50
|
+
' ],',
|
|
51
|
+
' // Add routes here when ready:',
|
|
52
|
+
` // "routes": [{ "pattern": "${DEFAULT_ROUTE_PATTERN}", "custom_domain": true }]`,
|
|
104
53
|
' }',
|
|
105
54
|
].join('\n');
|
|
106
55
|
};
|
|
107
|
-
const renderDefaultWranglerConfig = (values) => {
|
|
108
|
-
return [
|
|
109
|
-
'{',
|
|
110
|
-
' "name": "zintrust-api",',
|
|
111
|
-
' "main": "./src/functions/cloudflare.ts",',
|
|
112
|
-
` "compatibility_date": "${DEFAULT_COMPATIBILITY_DATE}",`,
|
|
113
|
-
' "compatibility_flags": ["nodejs_compat"],',
|
|
114
|
-
' "env": {',
|
|
115
|
-
renderKvProxyEnvBlock(values),
|
|
116
|
-
' }',
|
|
117
|
-
'}',
|
|
118
|
-
'',
|
|
119
|
-
].join('\n');
|
|
120
|
-
};
|
|
121
|
-
const ensureProxyEntrypoint = (cwd) => {
|
|
122
|
-
const entryFilePath = join(cwd, DEFAULT_ENTRY_FILE);
|
|
123
|
-
if (existsSync(entryFilePath)) {
|
|
124
|
-
return { created: false, entryFilePath };
|
|
125
|
-
}
|
|
126
|
-
mkdirSync(join(cwd, 'src/proxy/kv'), { recursive: true });
|
|
127
|
-
writeFileSync(entryFilePath, [
|
|
128
|
-
`export { ZintrustKvProxy } from '${CORE_PROXY_MODULE}';`,
|
|
129
|
-
`export { ZintrustKvProxy as default } from '${CORE_PROXY_MODULE}';`,
|
|
130
|
-
'',
|
|
131
|
-
].join('\n'), 'utf-8');
|
|
132
|
-
return { created: true, entryFilePath };
|
|
133
|
-
};
|
|
134
|
-
const injectEnvBlock = (content, block) => {
|
|
135
|
-
if (hasEnvBlock(content, 'kv-proxy'))
|
|
136
|
-
return content;
|
|
137
|
-
const envObjectStart = findEnvObjectStart(content);
|
|
138
|
-
if (envObjectStart >= 0 && isObjectEffectivelyEmpty(content, envObjectStart)) {
|
|
139
|
-
const closingBraceIndex = content.indexOf('}', envObjectStart);
|
|
140
|
-
if (closingBraceIndex >= 0) {
|
|
141
|
-
return `${content.slice(0, envObjectStart)}{\n${block}\n }${content.slice(closingBraceIndex + 1)}`;
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
if (envObjectStart >= 0) {
|
|
145
|
-
return `${content.slice(0, envObjectStart + 1)}\n${block},${content.slice(envObjectStart + 1)}`;
|
|
146
|
-
}
|
|
147
|
-
const closingIndex = content.lastIndexOf('}');
|
|
148
|
-
if (closingIndex < 0) {
|
|
149
|
-
throw ErrorFactory.createCliError('Invalid wrangler.jsonc: missing closing brace.');
|
|
150
|
-
}
|
|
151
|
-
const before = content.slice(0, closingIndex).trimEnd();
|
|
152
|
-
const suffix = before.endsWith('{') ? '\n' : ',\n';
|
|
153
|
-
return `${before}${suffix} "env": {\n${block}\n }\n}\n`;
|
|
154
|
-
};
|
|
155
|
-
const ensureWranglerConfig = (configPath, options) => {
|
|
156
|
-
if (!existsSync(configPath)) {
|
|
157
|
-
const values = resolveConfigValues(undefined, options);
|
|
158
|
-
writeFileSync(configPath, renderDefaultWranglerConfig(values), 'utf-8');
|
|
159
|
-
return { createdFile: true, insertedEnv: true, values };
|
|
160
|
-
}
|
|
161
|
-
const content = readFileSync(configPath, 'utf-8');
|
|
162
|
-
const values = resolveConfigValues(content, options);
|
|
163
|
-
const next = injectEnvBlock(content, renderKvProxyEnvBlock(values));
|
|
164
|
-
if (next !== content) {
|
|
165
|
-
writeFileSync(configPath, next, 'utf-8');
|
|
166
|
-
return { createdFile: false, insertedEnv: true, values };
|
|
167
|
-
}
|
|
168
|
-
return { createdFile: false, insertedEnv: false, values };
|
|
169
|
-
};
|
|
170
56
|
const warnOnPlaceholderNamespaceId = (values) => {
|
|
171
57
|
if (values.namespaceId !== DEFAULT_NAMESPACE_ID)
|
|
172
58
|
return;
|
|
173
59
|
Logger.warn('Could not resolve a KV namespace id automatically. Update wrangler.jsonc or pass --namespace-id before relying on the generated kv-proxy environment.');
|
|
174
60
|
};
|
|
175
61
|
const addOptions = (command) => {
|
|
176
|
-
command
|
|
177
|
-
command.option('--watch', 'Auto-restart proxy on file changes');
|
|
62
|
+
addWranglerProxyBaseOptions(command, DEFAULT_CONFIG);
|
|
178
63
|
command.option('--binding <name>', 'KV binding name', DEFAULT_BINDING);
|
|
179
64
|
command.option('--namespace-id <id>', 'Cloudflare KV namespace id');
|
|
180
65
|
command.option('--preview-id <id>', 'Cloudflare KV preview namespace id');
|
|
181
66
|
};
|
|
182
67
|
export const KvProxyCommand = Object.freeze({
|
|
183
68
|
create() {
|
|
184
|
-
return
|
|
69
|
+
return createWranglerProxyCommand({
|
|
185
70
|
name: 'proxy:kv',
|
|
186
71
|
aliases: ['kv:proxy'],
|
|
187
72
|
description: 'Start the local Cloudflare KV proxy Worker via Wrangler and scaffold env.kv-proxy in wrangler.jsonc when missing',
|
|
73
|
+
envName: 'kv-proxy',
|
|
74
|
+
defaultConfig: DEFAULT_CONFIG,
|
|
75
|
+
compatibilityDate: DEFAULT_COMPATIBILITY_DATE,
|
|
76
|
+
entryFile: DEFAULT_ENTRY_FILE,
|
|
77
|
+
exportName: 'ZintrustKvProxy',
|
|
78
|
+
moduleSpecifier: CORE_PROXY_MODULE,
|
|
188
79
|
addOptions,
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
const entrypoint = ensureProxyEntrypoint(cwd);
|
|
193
|
-
const configPath = join(cwd, resolveConfigPath(options.config));
|
|
194
|
-
const result = ensureWranglerConfig(configPath, options);
|
|
195
|
-
if (entrypoint.created) {
|
|
196
|
-
Logger.info(`Created ${entrypoint.entryFilePath} from @zintrust/core proxy entrypoint.`);
|
|
197
|
-
}
|
|
198
|
-
if (result.createdFile) {
|
|
199
|
-
Logger.info(`Created ${configPath} with a default kv-proxy environment.`);
|
|
200
|
-
}
|
|
201
|
-
else if (result.insertedEnv) {
|
|
202
|
-
Logger.info(`Added env.kv-proxy to ${configPath}.`);
|
|
203
|
-
}
|
|
204
|
-
warnOnPlaceholderNamespaceId(result.values);
|
|
205
|
-
const exitCode = await SpawnUtil.spawnAndWait({
|
|
206
|
-
command: 'wrangler',
|
|
207
|
-
args: ['dev', '--config', configPath, '--env', 'kv-proxy'],
|
|
208
|
-
env: process.env,
|
|
209
|
-
forwardSignals: false,
|
|
210
|
-
});
|
|
211
|
-
if (exitCode !== 0) {
|
|
212
|
-
process.exit(exitCode);
|
|
213
|
-
}
|
|
214
|
-
},
|
|
80
|
+
resolveValues: resolveConfigValues,
|
|
81
|
+
renderEnvBlock: renderKvProxyEnvBlock,
|
|
82
|
+
afterConfigResolved: warnOnPlaceholderNamespaceId,
|
|
215
83
|
});
|
|
216
84
|
},
|
|
217
85
|
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
type EnsureProxyEntrypointOptions = {
|
|
2
|
+
cwd: string;
|
|
3
|
+
entryFile: string;
|
|
4
|
+
exportName: string;
|
|
5
|
+
moduleSpecifier: string;
|
|
6
|
+
};
|
|
7
|
+
type EnsureWranglerConfigOptions<TValues, TOptions> = {
|
|
8
|
+
configPath: string;
|
|
9
|
+
options: TOptions;
|
|
10
|
+
envName: string;
|
|
11
|
+
resolveValues: (content: string | undefined, options: TOptions) => TValues;
|
|
12
|
+
renderEnvBlock: (values: TValues) => string;
|
|
13
|
+
compatibilityDate: string;
|
|
14
|
+
};
|
|
15
|
+
type EnsureWranglerConfigResult<TValues> = {
|
|
16
|
+
createdFile: boolean;
|
|
17
|
+
insertedEnv: boolean;
|
|
18
|
+
values: TValues;
|
|
19
|
+
};
|
|
20
|
+
export declare const trimNonEmptyOption: (value: string | undefined) => string | undefined;
|
|
21
|
+
export declare const resolveConfigPath: (raw: string | undefined, fallback?: string) => string;
|
|
22
|
+
export declare const findQuotedValue: (content: string, key: string) => string | undefined;
|
|
23
|
+
export declare const injectEnvBlock: (content: string, envName: string, block: string) => string;
|
|
24
|
+
export declare const renderDefaultWranglerConfig: (envBlock: string, compatibilityDate: string) => string;
|
|
25
|
+
export declare const ensureProxyEntrypoint: (options: EnsureProxyEntrypointOptions) => {
|
|
26
|
+
created: boolean;
|
|
27
|
+
entryFilePath: string;
|
|
28
|
+
};
|
|
29
|
+
export declare const ensureWranglerConfig: <TValues, TOptions>(options: EnsureWranglerConfigOptions<TValues, TOptions>) => EnsureWranglerConfigResult<TValues>;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=ProxyScaffoldUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProxyScaffoldUtils.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/ProxyScaffoldUtils.ts"],"names":[],"mappings":"AAKA,KAAK,4BAA4B,GAAG;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,KAAK,2BAA2B,CAAC,OAAO,EAAE,QAAQ,IAAI;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,QAAQ,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC;IAC3E,cAAc,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM,CAAC;IAC5C,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,KAAK,0BAA0B,CAAC,OAAO,IAAI;IACzC,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAAI,OAAO,MAAM,GAAG,SAAS,KAAG,MAAM,GAAG,SAIvE,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,GAAG,SAAS,EAAE,iBAA2B,KAAG,MAExF,CAAC;AAmBF,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM,EAAE,KAAK,MAAM,KAAG,MAAM,GAAG,SAQvE,CAAC;AAmBF,eAAO,MAAM,cAAc,GAAI,SAAS,MAAM,EAAE,SAAS,MAAM,EAAE,OAAO,MAAM,KAAG,MAuBhF,CAAC;AAEF,eAAO,MAAM,2BAA2B,GACtC,UAAU,MAAM,EAChB,mBAAmB,MAAM,KACxB,MAaF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,SAAS,4BAA4B,KACpC;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAoB3C,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,OAAO,EAAE,QAAQ,EACpD,SAAS,2BAA2B,CAAC,OAAO,EAAE,QAAQ,CAAC,KACtD,0BAA0B,CAAC,OAAO,CAqBpC,CAAC"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
|
|
2
|
+
import { isNonEmptyString } from '../../helper/index.js';
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from '../../node-singletons/fs.js';
|
|
4
|
+
import { join } from '../../node-singletons/path.js';
|
|
5
|
+
export const trimNonEmptyOption = (value) => {
|
|
6
|
+
if (!isNonEmptyString(value))
|
|
7
|
+
return undefined;
|
|
8
|
+
const trimmed = value.trim();
|
|
9
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
10
|
+
};
|
|
11
|
+
export const resolveConfigPath = (raw, fallback = 'wrangler.jsonc') => {
|
|
12
|
+
return trimNonEmptyOption(raw) ?? fallback;
|
|
13
|
+
};
|
|
14
|
+
const isJsonWhitespace = (char) => {
|
|
15
|
+
return char === ' ' || char === '\n' || char === '\r' || char === '\t';
|
|
16
|
+
};
|
|
17
|
+
const findJsonKeyValueStart = (content, key) => {
|
|
18
|
+
const keyPosition = content.indexOf(`"${key}"`);
|
|
19
|
+
if (keyPosition < 0)
|
|
20
|
+
return -1;
|
|
21
|
+
let cursor = keyPosition + key.length + 2;
|
|
22
|
+
while (isJsonWhitespace(content[cursor]))
|
|
23
|
+
cursor++;
|
|
24
|
+
if (content[cursor] !== ':')
|
|
25
|
+
return -1;
|
|
26
|
+
cursor++;
|
|
27
|
+
while (isJsonWhitespace(content[cursor]))
|
|
28
|
+
cursor++;
|
|
29
|
+
return cursor;
|
|
30
|
+
};
|
|
31
|
+
export const findQuotedValue = (content, key) => {
|
|
32
|
+
const valueStart = findJsonKeyValueStart(content, key);
|
|
33
|
+
if (valueStart < 0 || content[valueStart] !== '"')
|
|
34
|
+
return undefined;
|
|
35
|
+
const valueEnd = content.indexOf('"', valueStart + 1);
|
|
36
|
+
if (valueEnd < 0)
|
|
37
|
+
return undefined;
|
|
38
|
+
return trimNonEmptyOption(content.slice(valueStart + 1, valueEnd));
|
|
39
|
+
};
|
|
40
|
+
const hasEnvBlock = (content, envName) => {
|
|
41
|
+
const valueStart = findJsonKeyValueStart(content, envName);
|
|
42
|
+
return valueStart >= 0 && content[valueStart] === '{';
|
|
43
|
+
};
|
|
44
|
+
const findEnvObjectStart = (content) => {
|
|
45
|
+
const valueStart = findJsonKeyValueStart(content, 'env');
|
|
46
|
+
if (valueStart < 0 || content[valueStart] !== '{')
|
|
47
|
+
return -1;
|
|
48
|
+
return valueStart;
|
|
49
|
+
};
|
|
50
|
+
const isObjectEffectivelyEmpty = (content, objectStart) => {
|
|
51
|
+
let cursor = objectStart + 1;
|
|
52
|
+
while (isJsonWhitespace(content[cursor]))
|
|
53
|
+
cursor++;
|
|
54
|
+
return content[cursor] === '}';
|
|
55
|
+
};
|
|
56
|
+
export const injectEnvBlock = (content, envName, block) => {
|
|
57
|
+
if (hasEnvBlock(content, envName))
|
|
58
|
+
return content;
|
|
59
|
+
const envObjectStart = findEnvObjectStart(content);
|
|
60
|
+
if (envObjectStart >= 0 && isObjectEffectivelyEmpty(content, envObjectStart)) {
|
|
61
|
+
const closingBraceIndex = content.indexOf('}', envObjectStart);
|
|
62
|
+
if (closingBraceIndex >= 0) {
|
|
63
|
+
return `${content.slice(0, envObjectStart)}{\n${block}\n }${content.slice(closingBraceIndex + 1)}`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (envObjectStart >= 0) {
|
|
67
|
+
return `${content.slice(0, envObjectStart + 1)}\n${block},${content.slice(envObjectStart + 1)}`;
|
|
68
|
+
}
|
|
69
|
+
const closingIndex = content.lastIndexOf('}');
|
|
70
|
+
if (closingIndex < 0) {
|
|
71
|
+
throw ErrorFactory.createCliError('Invalid wrangler.jsonc: missing closing brace.');
|
|
72
|
+
}
|
|
73
|
+
const before = content.slice(0, closingIndex).trimEnd();
|
|
74
|
+
const suffix = before.endsWith('{') ? '\n' : ',\n';
|
|
75
|
+
return `${before}${suffix} "env": {\n${block}\n }\n}\n`;
|
|
76
|
+
};
|
|
77
|
+
export const renderDefaultWranglerConfig = (envBlock, compatibilityDate) => {
|
|
78
|
+
return [
|
|
79
|
+
'{',
|
|
80
|
+
' "name": "zintrust-api",',
|
|
81
|
+
' "main": "./src/functions/cloudflare.ts",',
|
|
82
|
+
` "compatibility_date": "${compatibilityDate}",`,
|
|
83
|
+
' "compatibility_flags": ["nodejs_compat"],',
|
|
84
|
+
' "env": {',
|
|
85
|
+
envBlock,
|
|
86
|
+
' }',
|
|
87
|
+
'}',
|
|
88
|
+
'',
|
|
89
|
+
].join('\n');
|
|
90
|
+
};
|
|
91
|
+
export const ensureProxyEntrypoint = (options) => {
|
|
92
|
+
const entryFilePath = join(options.cwd, options.entryFile);
|
|
93
|
+
if (existsSync(entryFilePath)) {
|
|
94
|
+
return { created: false, entryFilePath };
|
|
95
|
+
}
|
|
96
|
+
const lastSlashIndex = entryFilePath.lastIndexOf('/');
|
|
97
|
+
const entryDir = lastSlashIndex > 0 ? entryFilePath.slice(0, lastSlashIndex) : options.cwd;
|
|
98
|
+
mkdirSync(entryDir, { recursive: true });
|
|
99
|
+
writeFileSync(entryFilePath, [
|
|
100
|
+
`export { ${options.exportName} } from '${options.moduleSpecifier}';`,
|
|
101
|
+
`export { ${options.exportName} as default } from '${options.moduleSpecifier}';`,
|
|
102
|
+
'',
|
|
103
|
+
].join('\n'), 'utf-8');
|
|
104
|
+
return { created: true, entryFilePath };
|
|
105
|
+
};
|
|
106
|
+
export const ensureWranglerConfig = (options) => {
|
|
107
|
+
if (!existsSync(options.configPath)) {
|
|
108
|
+
const values = options.resolveValues(undefined, options.options);
|
|
109
|
+
writeFileSync(options.configPath, renderDefaultWranglerConfig(options.renderEnvBlock(values), options.compatibilityDate), 'utf-8');
|
|
110
|
+
return { createdFile: true, insertedEnv: true, values };
|
|
111
|
+
}
|
|
112
|
+
const content = readFileSync(options.configPath, 'utf-8');
|
|
113
|
+
const values = options.resolveValues(content, options.options);
|
|
114
|
+
const next = injectEnvBlock(content, options.envName, options.renderEnvBlock(values));
|
|
115
|
+
if (next !== content) {
|
|
116
|
+
writeFileSync(options.configPath, next, 'utf-8');
|
|
117
|
+
return { createdFile: false, insertedEnv: true, values };
|
|
118
|
+
}
|
|
119
|
+
return { createdFile: false, insertedEnv: false, values };
|
|
120
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CommandOptions, IBaseCommand } from '../BaseCommand';
|
|
2
|
+
import type { Command } from 'commander';
|
|
3
|
+
export type WranglerProxyCommandOptions = CommandOptions & {
|
|
4
|
+
config?: string;
|
|
5
|
+
port?: string;
|
|
6
|
+
watch?: boolean;
|
|
7
|
+
};
|
|
8
|
+
type CreateWranglerProxyCommandInput<TValues, TOptions extends WranglerProxyCommandOptions> = {
|
|
9
|
+
name: string;
|
|
10
|
+
aliases: string[];
|
|
11
|
+
description: string;
|
|
12
|
+
envName: string;
|
|
13
|
+
defaultConfig: string;
|
|
14
|
+
compatibilityDate: string;
|
|
15
|
+
entryFile: string;
|
|
16
|
+
exportName: string;
|
|
17
|
+
moduleSpecifier: string;
|
|
18
|
+
addOptions: (command: Command) => void;
|
|
19
|
+
resolveValues: (content: string | undefined, options: TOptions) => TValues;
|
|
20
|
+
renderEnvBlock: (values: TValues) => string;
|
|
21
|
+
afterConfigResolved?: (values: TValues) => void;
|
|
22
|
+
};
|
|
23
|
+
export declare const addWranglerProxyBaseOptions: (command: Command, defaultConfig: string) => void;
|
|
24
|
+
export declare const createWranglerProxyCommand: <TValues, TOptions extends WranglerProxyCommandOptions>(input: CreateWranglerProxyCommandInput<TValues, TOptions>) => IBaseCommand;
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=WranglerProxyCommandUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WranglerProxyCommandUtils.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/WranglerProxyCommandUtils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAWrE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,MAAM,MAAM,2BAA2B,GAAG,cAAc,GAAG;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF,KAAK,+BAA+B,CAAC,OAAO,EAAE,QAAQ,SAAS,2BAA2B,IAAI;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC;IAC3E,cAAc,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM,CAAC;IAC5C,mBAAmB,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CACjD,CAAC;AAEF,eAAO,MAAM,2BAA2B,GAAI,SAAS,OAAO,EAAE,eAAe,MAAM,KAAG,IAIrF,CAAC;AAEF,eAAO,MAAM,0BAA0B,GAAI,OAAO,EAAE,QAAQ,SAAS,2BAA2B,EAC9F,OAAO,+BAA+B,CAAC,OAAO,EAAE,QAAQ,CAAC,KACxD,YA0DF,CAAC"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { BaseCommand } from '../BaseCommand.js';
|
|
2
|
+
import { maybeRunProxyWatchMode, parseIntOption } from '../commands/ProxyCommandUtils.js';
|
|
3
|
+
import { ensureProxyEntrypoint, ensureWranglerConfig, resolveConfigPath, } from '../commands/ProxyScaffoldUtils.js';
|
|
4
|
+
import { SpawnUtil } from '../utils/spawn.js';
|
|
5
|
+
import { Logger } from '../../config/logger.js';
|
|
6
|
+
import { join } from '../../node-singletons/path.js';
|
|
7
|
+
export const addWranglerProxyBaseOptions = (command, defaultConfig) => {
|
|
8
|
+
command.option('-c, --config <path>', 'Wrangler config file', defaultConfig);
|
|
9
|
+
command.option('--port <port>', 'Local Wrangler dev port');
|
|
10
|
+
command.option('--watch', 'Auto-restart proxy on file changes');
|
|
11
|
+
};
|
|
12
|
+
export const createWranglerProxyCommand = (input) => {
|
|
13
|
+
return BaseCommand.create({
|
|
14
|
+
name: input.name,
|
|
15
|
+
aliases: input.aliases,
|
|
16
|
+
description: input.description,
|
|
17
|
+
addOptions: input.addOptions,
|
|
18
|
+
execute: async (options) => {
|
|
19
|
+
const typedOptions = options;
|
|
20
|
+
await maybeRunProxyWatchMode(typedOptions.watch);
|
|
21
|
+
const port = parseIntOption(typedOptions.port, 'port');
|
|
22
|
+
const cwd = process.cwd();
|
|
23
|
+
const entrypoint = ensureProxyEntrypoint({
|
|
24
|
+
cwd,
|
|
25
|
+
entryFile: input.entryFile,
|
|
26
|
+
exportName: input.exportName,
|
|
27
|
+
moduleSpecifier: input.moduleSpecifier,
|
|
28
|
+
});
|
|
29
|
+
const configPath = join(cwd, resolveConfigPath(typedOptions.config, input.defaultConfig));
|
|
30
|
+
const result = ensureWranglerConfig({
|
|
31
|
+
configPath,
|
|
32
|
+
options: typedOptions,
|
|
33
|
+
envName: input.envName,
|
|
34
|
+
resolveValues: input.resolveValues,
|
|
35
|
+
renderEnvBlock: input.renderEnvBlock,
|
|
36
|
+
compatibilityDate: input.compatibilityDate,
|
|
37
|
+
});
|
|
38
|
+
if (entrypoint.created) {
|
|
39
|
+
Logger.info(`Created ${entrypoint.entryFilePath} from @zintrust/core proxy entrypoint.`);
|
|
40
|
+
}
|
|
41
|
+
if (result.createdFile) {
|
|
42
|
+
Logger.info(`Created ${configPath} with a default ${input.envName} environment.`);
|
|
43
|
+
}
|
|
44
|
+
else if (result.insertedEnv) {
|
|
45
|
+
Logger.info(`Added env.${input.envName} to ${configPath}.`);
|
|
46
|
+
}
|
|
47
|
+
input.afterConfigResolved?.(result.values);
|
|
48
|
+
const args = ['dev', '--config', configPath, '--env', input.envName];
|
|
49
|
+
if (port !== undefined) {
|
|
50
|
+
args.push('--port', String(port));
|
|
51
|
+
}
|
|
52
|
+
const exitCode = await SpawnUtil.spawnAndWait({
|
|
53
|
+
command: 'wrangler',
|
|
54
|
+
args,
|
|
55
|
+
env: process.env,
|
|
56
|
+
forwardSignals: false,
|
|
57
|
+
});
|
|
58
|
+
if (exitCode !== 0) {
|
|
59
|
+
process.exit(exitCode);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GovernanceScaffolder.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/GovernanceScaffolder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"GovernanceScaffolder.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/GovernanceScaffolder.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAqBH,MAAM,MAAM,yBAAyB,GAAG,QAAQ,CAAC;IAC/C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC,CAAC;AAsSH,eAAO,MAAM,oBAAoB;0BAEhB,MAAM,YACV,yBAAyB,GACjC,OAAO,CAAC,wBAAwB,CAAC;EA+DpC,CAAC;AAEH,eAAe,oBAAoB,CAAC"}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* - Architecture tests (Vitest)
|
|
7
7
|
*/
|
|
8
8
|
import { FileGenerator } from '../scaffolding/FileGenerator.js';
|
|
9
|
+
import { toCompatibleGovernanceVersion } from '../scaffolding/ScaffoldingVersionUtils.js';
|
|
9
10
|
import { VersionChecker } from '../services/VersionChecker.js';
|
|
10
11
|
import { SpawnUtil } from '../utils/spawn.js';
|
|
11
12
|
import { resolvePackageManager } from '../../common/index.js';
|
|
@@ -50,53 +51,15 @@ const ensureDevDependency = (devDependencies, name, version) => {
|
|
|
50
51
|
devDependencies[name] = version;
|
|
51
52
|
}
|
|
52
53
|
};
|
|
53
|
-
const extractMajorMinorVersion = (value) => {
|
|
54
|
-
const trimmed = value.trim();
|
|
55
|
-
let start = -1;
|
|
56
|
-
for (let index = 0; index < trimmed.length; index++) {
|
|
57
|
-
const char = trimmed[index];
|
|
58
|
-
if (char !== undefined && char >= '0' && char <= '9') {
|
|
59
|
-
start = index;
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (start < 0)
|
|
64
|
-
return undefined;
|
|
65
|
-
let end = start;
|
|
66
|
-
while (end < trimmed.length) {
|
|
67
|
-
const char = trimmed[end];
|
|
68
|
-
const isDigit = char !== undefined && char >= '0' && char <= '9';
|
|
69
|
-
if (!isDigit && char !== '.')
|
|
70
|
-
break;
|
|
71
|
-
end++;
|
|
72
|
-
}
|
|
73
|
-
const parts = trimmed.slice(start, end).split('.');
|
|
74
|
-
if (parts.length < 3)
|
|
75
|
-
return undefined;
|
|
76
|
-
const [major, minor, patch] = parts;
|
|
77
|
-
if (!major || !minor || !patch)
|
|
78
|
-
return undefined;
|
|
79
|
-
return { major, minor };
|
|
80
|
-
};
|
|
81
|
-
const toCompatibleGovernanceVersion = (value) => {
|
|
82
|
-
const parsed = extractMajorMinorVersion(value);
|
|
83
|
-
if (parsed === undefined)
|
|
84
|
-
return undefined;
|
|
85
|
-
return `^${parsed.major}.${parsed.minor}.0`;
|
|
86
|
-
};
|
|
87
54
|
const inferGovernanceVersion = (pkg) => {
|
|
88
55
|
const deps = getStringRecord(pkg.dependencies);
|
|
89
56
|
const core = deps?.['@zintrust/core'];
|
|
90
57
|
if (typeof core === 'string' && core.trim() !== '') {
|
|
91
|
-
|
|
92
|
-
if (compatibleFromCore !== undefined)
|
|
93
|
-
return compatibleFromCore;
|
|
58
|
+
return toCompatibleGovernanceVersion(core);
|
|
94
59
|
}
|
|
95
60
|
const currentVersion = VersionChecker.getCurrentVersion().trim();
|
|
96
61
|
if (currentVersion !== '' && currentVersion !== '0.0.0') {
|
|
97
|
-
|
|
98
|
-
if (compatibleFromCli !== undefined)
|
|
99
|
-
return compatibleFromCli;
|
|
62
|
+
return toCompatibleGovernanceVersion(currentVersion);
|
|
100
63
|
}
|
|
101
64
|
return '^0.4.0';
|
|
102
65
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectScaffolder.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/ProjectScaffolder.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"ProjectScaffolder.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/ProjectScaffolder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAYH,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,cAAc,GAAG,sBAAsB,CAAC;AAEpD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,cAAc,CAAC,OAAO,EAAE,sBAAsB,GAAG,IAAI,CAAC;IACtD,YAAY,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,eAAe,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAC;IACpE,cAAc,IAAI,MAAM,CAAC;IACzB,sBAAsB,IAAI,OAAO,CAAC;IAClC,iBAAiB,IAAI,MAAM,CAAC;IAC5B,WAAW,CAAC,OAAO,CAAC,EAAE,sBAAsB,GAAG,MAAM,CAAC;IACtD,gBAAgB,IAAI,OAAO,CAAC;IAC5B,aAAa,IAAI,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;CAC3E;AAodD,wBAAgB,qBAAqB,IAAI,MAAM,EAAE,CAEhD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAsBrE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG;IAChE,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CAsBA;AA8ID;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,GAAE,MAAsB,GAAG,kBAAkB,CAsB/F;AAED,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,qBAAqB,CAAC,CAEhC;AAED;;GAEG;AACH,eAAO,MAAM,iBAAiB;;;;;;EAM5B,CAAC"}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { EnvFileBackfill } from '../env/EnvFileBackfill.js';
|
|
6
6
|
import { EnvData } from '../scaffolding/env.js';
|
|
7
|
+
import { toCompatibleGovernanceVersion } from '../scaffolding/ScaffoldingVersionUtils.js';
|
|
7
8
|
import { Logger } from '../../config/logger.js';
|
|
8
9
|
import { ErrorFactory } from '../../exceptions/ZintrustError.js';
|
|
9
10
|
import { randomBytes } from '../../node-singletons/crypto.js';
|
|
@@ -20,40 +21,6 @@ const loadCoreVersion = () => {
|
|
|
20
21
|
return '0.0.0';
|
|
21
22
|
}
|
|
22
23
|
};
|
|
23
|
-
const extractMajorMinorVersion = (value) => {
|
|
24
|
-
const trimmed = value.trim();
|
|
25
|
-
let start = -1;
|
|
26
|
-
for (let index = 0; index < trimmed.length; index++) {
|
|
27
|
-
const char = trimmed[index];
|
|
28
|
-
if (char !== undefined && char >= '0' && char <= '9') {
|
|
29
|
-
start = index;
|
|
30
|
-
break;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
if (start < 0)
|
|
34
|
-
return undefined;
|
|
35
|
-
let end = start;
|
|
36
|
-
while (end < trimmed.length) {
|
|
37
|
-
const char = trimmed[end];
|
|
38
|
-
const isDigit = char !== undefined && char >= '0' && char <= '9';
|
|
39
|
-
if (!isDigit && char !== '.')
|
|
40
|
-
break;
|
|
41
|
-
end++;
|
|
42
|
-
}
|
|
43
|
-
const parts = trimmed.slice(start, end).split('.');
|
|
44
|
-
if (parts.length < 3)
|
|
45
|
-
return undefined;
|
|
46
|
-
const [major, minor, patch] = parts;
|
|
47
|
-
if (!major || !minor || !patch)
|
|
48
|
-
return undefined;
|
|
49
|
-
return { major, minor };
|
|
50
|
-
};
|
|
51
|
-
const toCompatibleGovernanceVersion = (version) => {
|
|
52
|
-
const parsed = extractMajorMinorVersion(version);
|
|
53
|
-
if (parsed !== undefined)
|
|
54
|
-
return `^${parsed.major}.${parsed.minor}.0`;
|
|
55
|
-
return '^0.4.0';
|
|
56
|
-
};
|
|
57
24
|
const createDirectories = (projectPath, directories) => {
|
|
58
25
|
let count = 0;
|
|
59
26
|
if (!fs.existsSync(projectPath)) {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const extractMajorMinorVersion: (value: string) => {
|
|
2
|
+
major: string;
|
|
3
|
+
minor: string;
|
|
4
|
+
} | undefined;
|
|
5
|
+
export declare const toCompatibleGovernanceVersion: (value: string, fallback?: string) => string;
|
|
6
|
+
//# sourceMappingURL=ScaffoldingVersionUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ScaffoldingVersionUtils.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/ScaffoldingVersionUtils.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB,GACnC,OAAO,MAAM,KACZ;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SA6BrC,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,OAAO,MAAM,EAAE,iBAAmB,KAAG,MAKlF,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const extractMajorMinorVersion = (value) => {
|
|
2
|
+
const trimmed = value.trim();
|
|
3
|
+
let start = -1;
|
|
4
|
+
for (let index = 0; index < trimmed.length; index++) {
|
|
5
|
+
const char = trimmed[index];
|
|
6
|
+
if (char !== undefined && char >= '0' && char <= '9') {
|
|
7
|
+
start = index;
|
|
8
|
+
break;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
if (start < 0)
|
|
12
|
+
return undefined;
|
|
13
|
+
let end = start;
|
|
14
|
+
while (end < trimmed.length) {
|
|
15
|
+
const char = trimmed[end];
|
|
16
|
+
const isDigit = char !== undefined && char >= '0' && char <= '9';
|
|
17
|
+
if (!isDigit && char !== '.')
|
|
18
|
+
break;
|
|
19
|
+
end++;
|
|
20
|
+
}
|
|
21
|
+
const parts = trimmed.slice(start, end).split('.');
|
|
22
|
+
if (parts.length < 3)
|
|
23
|
+
return undefined;
|
|
24
|
+
const [major, minor, patch] = parts;
|
|
25
|
+
if (!major || !minor || !patch)
|
|
26
|
+
return undefined;
|
|
27
|
+
return { major, minor };
|
|
28
|
+
};
|
|
29
|
+
export const toCompatibleGovernanceVersion = (value, fallback = '^0.4.0') => {
|
|
30
|
+
const parsed = extractMajorMinorVersion(value);
|
|
31
|
+
if (parsed === undefined)
|
|
32
|
+
return fallback;
|
|
33
|
+
return `^${parsed.major}.${parsed.minor}.0`;
|
|
34
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { MiddlewareConfigType } from './type';
|
|
2
|
-
import type { Middleware } from '../middleware/MiddlewareStack';
|
|
3
2
|
import type { MiddlewareFailureResponder } from '../middleware/MiddlewareFailureResponder';
|
|
3
|
+
import type { Middleware } from '../middleware/MiddlewareStack';
|
|
4
4
|
type SharedMiddlewares = {
|
|
5
5
|
log: Middleware;
|
|
6
6
|
error: Middleware;
|
|
@@ -68,7 +68,10 @@ export declare const MiddlewareKeys: Readonly<{
|
|
|
68
68
|
validateUserUpdate: true;
|
|
69
69
|
validateUserFill: true;
|
|
70
70
|
}>;
|
|
71
|
-
export type
|
|
71
|
+
export type ParameterizedRateLimitMiddlewareKey = `rateLimit:${number}:${number}`;
|
|
72
|
+
type StaticMiddlewareKey = keyof typeof MiddlewareKeys;
|
|
73
|
+
export type MiddlewareKey = StaticMiddlewareKey | ParameterizedRateLimitMiddlewareKey;
|
|
74
|
+
export declare const isKnownMiddlewareName: (value: string) => value is MiddlewareKey;
|
|
72
75
|
export declare function createMiddlewareConfig(): MiddlewareConfigType;
|
|
73
76
|
/**
|
|
74
77
|
* Clear the middleware configuration cache
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../src/config/middleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAUzD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../../src/config/middleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAUzD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,wCAAwC,CAAC;AACzF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAoC9D,KAAK,iBAAiB,GAAG;IACvB,GAAG,EAAE,UAAU,CAAC;IAChB,KAAK,EAAE,UAAU,CAAC;IAClB,QAAQ,EAAE,UAAU,CAAC;IACrB,SAAS,EAAE,UAAU,CAAC;IACtB,YAAY,EAAE,UAAU,CAAC;IACzB,aAAa,EAAE,UAAU,CAAC;IAC1B,aAAa,EAAE,UAAU,CAAC;IAC1B,qBAAqB,EAAE,UAAU,CAAC;IAClC,IAAI,EAAE,UAAU,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,GAAG,EAAE,UAAU,CAAC;IAChB,WAAW,EAAE,UAAU,CAAC;IACxB,aAAa,EAAE,UAAU,CAAC;IAC1B,gBAAgB,EAAE,UAAU,CAAC;IAC7B,iBAAiB,EAAE,UAAU,CAAC;IAC9B,kBAAkB,EAAE,UAAU,CAAC;IAC/B,gBAAgB,EAAE,UAAU,CAAC;CAC9B,CAAC;AAEF,KAAK,sBAAsB,GAAG,MAAM,iBAAiB,CAAC;AACtD,KAAK,yBAAyB,GAAG,OAAO,CACtC,MAAM,CAAC,sBAAsB,EAAE,0BAA0B,CAAC,CAC3D,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;CAKjB,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACjC,aAAa,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,aAAa,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAClE,qBAAqB,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,UAAU,CAAC,EAAE,yBAAyB,CAAC;IACvC,MAAM,CAAC,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;CACpC,CAAC;AAEF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;EAkBuB,CAAC;AAEnD,MAAM,MAAM,mCAAmC,GAAG,aAAa,MAAM,IAAI,MAAM,EAAE,CAAC;AAElF,KAAK,mBAAmB,GAAG,MAAM,OAAO,cAAc,CAAC;AAEvD,MAAM,MAAM,aAAa,GAAG,mBAAmB,GAAG,mCAAmC,CAAC;AAyBtF,eAAO,MAAM,qBAAqB,GAAI,OAAO,MAAM,KAAG,KAAK,IAAI,aAI9D,CAAC;AAuUF,wBAAgB,sBAAsB,IAAI,oBAAoB,CA4C7D;AAID;;;GAGG;AACH,eAAO,MAAM,0BAA0B,QAAO,IAE7C,CAAC;AAwBF,eAAO,MAAM,gBAAgB,EAAE,oBAY7B,CAAC;AAEH,eAAe,gBAAgB,CAAC"}
|
package/src/config/middleware.js
CHANGED
|
@@ -40,6 +40,24 @@ export const MiddlewareKeys = Object.freeze({
|
|
|
40
40
|
validateUserUpdate: true,
|
|
41
41
|
validateUserFill: true,
|
|
42
42
|
});
|
|
43
|
+
const parseParameterizedRateLimitKey = (value) => {
|
|
44
|
+
const match = /^rateLimit:(\d+):(\d+(?:\.\d+)?)$/.exec(value.trim());
|
|
45
|
+
if (!match)
|
|
46
|
+
return undefined;
|
|
47
|
+
const max = Number.parseInt(match[1] ?? '', 10);
|
|
48
|
+
const windowMinutes = Number.parseFloat(match[2] ?? '');
|
|
49
|
+
if (!Number.isInteger(max) || max < 1)
|
|
50
|
+
return undefined;
|
|
51
|
+
if (!Number.isFinite(windowMinutes) || windowMinutes <= 0)
|
|
52
|
+
return undefined;
|
|
53
|
+
const windowMs = Math.round(windowMinutes * 60_000);
|
|
54
|
+
if (!Number.isFinite(windowMs) || windowMs < 1)
|
|
55
|
+
return undefined;
|
|
56
|
+
return Object.freeze({ max, windowMs });
|
|
57
|
+
};
|
|
58
|
+
export const isKnownMiddlewareName = (value) => {
|
|
59
|
+
return (Object.hasOwn(MiddlewareKeys, value) || parseParameterizedRateLimitKey(value) !== undefined);
|
|
60
|
+
};
|
|
43
61
|
// Ensure `ValidationMiddleware` is strongly typed here to avoid `error`-typed values
|
|
44
62
|
// triggering `@typescript-eslint/no-unsafe-*` rules.
|
|
45
63
|
const Validation = ValidationMiddleware;
|
|
@@ -207,6 +225,42 @@ const applySharedMiddlewareOverrides = (shared, projectRoute) => {
|
|
|
207
225
|
}
|
|
208
226
|
return Object.freeze(overridden);
|
|
209
227
|
};
|
|
228
|
+
const createRouteMiddlewareRegistry = (resolvedShared, projectRoute, responders) => {
|
|
229
|
+
const target = Object.freeze({
|
|
230
|
+
...resolvedShared,
|
|
231
|
+
...projectRoute,
|
|
232
|
+
});
|
|
233
|
+
const dynamicRateLimits = new Map();
|
|
234
|
+
return new Proxy(target, {
|
|
235
|
+
get(currentTarget, prop, receiver) {
|
|
236
|
+
if (typeof prop !== 'string') {
|
|
237
|
+
return Reflect.get(currentTarget, prop, receiver);
|
|
238
|
+
}
|
|
239
|
+
const resolved = currentTarget[prop];
|
|
240
|
+
if (typeof resolved === 'function')
|
|
241
|
+
return resolved;
|
|
242
|
+
const cached = dynamicRateLimits.get(prop);
|
|
243
|
+
if (cached !== undefined)
|
|
244
|
+
return cached;
|
|
245
|
+
const parsed = parseParameterizedRateLimitKey(prop);
|
|
246
|
+
if (parsed === undefined)
|
|
247
|
+
return undefined;
|
|
248
|
+
const middleware = RateLimiter.create({
|
|
249
|
+
max: parsed.max,
|
|
250
|
+
windowMs: parsed.windowMs,
|
|
251
|
+
onFailure: responders.rateLimit,
|
|
252
|
+
});
|
|
253
|
+
dynamicRateLimits.set(prop, middleware);
|
|
254
|
+
return middleware;
|
|
255
|
+
},
|
|
256
|
+
ownKeys(currentTarget) {
|
|
257
|
+
return Reflect.ownKeys(currentTarget);
|
|
258
|
+
},
|
|
259
|
+
getOwnPropertyDescriptor(currentTarget, prop) {
|
|
260
|
+
return Reflect.getOwnPropertyDescriptor(currentTarget, prop);
|
|
261
|
+
},
|
|
262
|
+
});
|
|
263
|
+
};
|
|
210
264
|
export function createMiddlewareConfig() {
|
|
211
265
|
const loadMiddlewareConfig = StartupConfigFileRegistry.get(StartupConfigFile.Middleware) ?? {};
|
|
212
266
|
const skipPathsFromEnv = Env.get('CSRF_SKIP_PATHS', '')
|
|
@@ -224,6 +278,7 @@ export function createMiddlewareConfig() {
|
|
|
224
278
|
const projectGlobal = resolveProjectGlobalMiddlewares(effectiveMiddlewareConfig);
|
|
225
279
|
const projectRoute = resolveProjectRouteMiddlewares(effectiveMiddlewareConfig);
|
|
226
280
|
const resolvedShared = applySharedMiddlewareOverrides(shared, projectRoute);
|
|
281
|
+
const routeMiddlewareRegistry = createRouteMiddlewareRegistry(resolvedShared, projectRoute, responders);
|
|
227
282
|
const middlewareConfigObj = {
|
|
228
283
|
global: [
|
|
229
284
|
resolvedShared.log,
|
|
@@ -236,10 +291,7 @@ export function createMiddlewareConfig() {
|
|
|
236
291
|
resolvedShared.sanitizeBody,
|
|
237
292
|
...projectGlobal,
|
|
238
293
|
],
|
|
239
|
-
route:
|
|
240
|
-
...resolvedShared,
|
|
241
|
-
...projectRoute,
|
|
242
|
-
},
|
|
294
|
+
route: routeMiddlewareRegistry,
|
|
243
295
|
};
|
|
244
296
|
return Object.freeze(middlewareConfigObj);
|
|
245
297
|
}
|
package/src/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @zintrust/core v0.4.
|
|
2
|
+
* @zintrust/core v0.4.33
|
|
3
3
|
*
|
|
4
4
|
* ZinTrust Framework - Production-Grade TypeScript Backend
|
|
5
5
|
* Built for performance, type safety, and exceptional developer experience
|
|
6
6
|
*
|
|
7
7
|
* Build Information:
|
|
8
|
-
* Built: 2026-03-
|
|
8
|
+
* Built: 2026-03-29T18:23:05.480Z
|
|
9
9
|
* Node: >=20.0.0
|
|
10
10
|
* License: MIT
|
|
11
11
|
*
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* Available at runtime for debugging and health checks
|
|
22
22
|
*/
|
|
23
23
|
export const ZINTRUST_VERSION = '0.1.41';
|
|
24
|
-
export const ZINTRUST_BUILD_DATE = '2026-03-
|
|
24
|
+
export const ZINTRUST_BUILD_DATE = '2026-03-29T18:23:05.446Z'; // Replaced during build
|
|
25
25
|
export { Application } from './boot/Application.js';
|
|
26
26
|
export { AwsSigV4 } from './common/index.js';
|
|
27
27
|
export { SignedRequest } from './security/SignedRequest.js';
|
|
@@ -89,7 +89,7 @@ const verifySignedRequest = async (request, env, bodyBytes) => {
|
|
|
89
89
|
body: bodyBytes,
|
|
90
90
|
headers: request.headers,
|
|
91
91
|
windowMs,
|
|
92
|
-
getSecretForKeyId:
|
|
92
|
+
getSecretForKeyId: (_keyId) => secret,
|
|
93
93
|
verifyNonce: env.ZT_NONCES === undefined
|
|
94
94
|
? undefined
|
|
95
95
|
: async (keyId, nonce, ttlMs) => verifyNonceKv(env.ZT_NONCES, keyId, nonce, ttlMs),
|