@zintrust/core 0.4.30 → 0.4.32
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/index.js +3 -3
- package/src/proxy/d1/ZintrustD1Proxy.d.ts +46 -1
- package/src/proxy/d1/ZintrustD1Proxy.d.ts.map +1 -1
- package/src/proxy/d1/ZintrustD1Proxy.js +356 -31
- package/src/proxy/kv/ZintrustKvProxy.d.ts +42 -1
- package/src/proxy/kv/ZintrustKvProxy.d.ts.map +1 -1
- package/src/proxy/kv/ZintrustKvProxy.js +297 -34
- 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.32",
|
|
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"}
|