@zintrust/core 0.4.39 → 0.4.40

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/core",
3
- "version": "0.4.39",
3
+ "version": "0.4.40",
4
4
  "description": "Production-grade TypeScript backend framework for JavaScript",
5
5
  "homepage": "https://zintrust.com",
6
6
  "repository": {
@@ -58,7 +58,7 @@
58
58
  },
59
59
  "dependencies": {
60
60
  "@cloudflare/containers": "^0.2.0",
61
- "@zintrust/workers": "^0.4.39",
61
+ "@zintrust/workers": "^0.4.40",
62
62
  "bcryptjs": "^3.0.3",
63
63
  "bullmq": "^5.71.1",
64
64
  "chalk": "^5.6.2",
@@ -0,0 +1,37 @@
1
+ type ZintrustConfig = Record<string, unknown>;
2
+ export interface CloudflareEnvTargetConfig {
3
+ sharedEnv: string[];
4
+ wranglerEnvs: Record<string, string[]>;
5
+ targets: Record<string, string[]>;
6
+ }
7
+ type ResolveTargetArgs = {
8
+ projectRoot: string;
9
+ cwd: string;
10
+ configPath?: string;
11
+ };
12
+ export declare const readZintrustConfig: (cwd: string) => ZintrustConfig;
13
+ export declare const resolveCloudflareEnvTargetConfig: (config: ZintrustConfig) => CloudflareEnvTargetConfig;
14
+ export declare const inferCloudflareTarget: ({ projectRoot, cwd, configPath, }: ResolveTargetArgs) => string;
15
+ export declare const resolveCloudflareEnvKeys: (args: {
16
+ config: ZintrustConfig;
17
+ projectRoot: string;
18
+ cwd: string;
19
+ configPath?: string;
20
+ wranglerEnv?: string;
21
+ target?: string;
22
+ }) => string[];
23
+ declare const _default: Readonly<{
24
+ readZintrustConfig: (cwd: string) => ZintrustConfig;
25
+ resolveCloudflareEnvTargetConfig: (config: ZintrustConfig) => CloudflareEnvTargetConfig;
26
+ inferCloudflareTarget: ({ projectRoot, cwd, configPath, }: ResolveTargetArgs) => string;
27
+ resolveCloudflareEnvKeys: (args: {
28
+ config: ZintrustConfig;
29
+ projectRoot: string;
30
+ cwd: string;
31
+ configPath?: string;
32
+ wranglerEnv?: string;
33
+ target?: string;
34
+ }) => string[];
35
+ }>;
36
+ export default _default;
37
+ //# sourceMappingURL=CloudflareEnvTargetConfig.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloudflareEnvTargetConfig.d.ts","sourceRoot":"","sources":["../../../../src/cli/cloudflare/CloudflareEnvTargetConfig.ts"],"names":[],"mappings":"AAKA,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE9C,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACnC;AAED,KAAK,iBAAiB,GAAG;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAkCF,eAAO,MAAM,kBAAkB,GAAI,KAAK,MAAM,KAAG,cAahD,CAAC;AAEF,eAAO,MAAM,gCAAgC,GAC3C,QAAQ,cAAc,KACrB,yBAQF,CAAC;AAqBF,eAAO,MAAM,qBAAqB,GAAI,mCAInC,iBAAiB,KAAG,MActB,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAI,MAAM;IAC7C,MAAM,EAAE,cAAc,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,KAAG,MAAM,EAiBT,CAAC;;8BA1FsC,MAAM,KAAG,cAAc;+CAgBrD,cAAc,KACrB,yBAAyB;+DAiCzB,iBAAiB,KAAG,MAAM;qCAgBkB;QAC7C,MAAM,EAAE,cAAc,CAAC;QACvB,WAAW,EAAE,MAAM,CAAC;QACpB,GAAG,EAAE,MAAM,CAAC;QACZ,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,KAAG,MAAM,EAAE;;AAmBZ,wBAKG"}
@@ -0,0 +1,104 @@
1
+ import { ErrorFactory } from '../../exceptions/ZintrustError.js';
2
+ import { isArray, isNonEmptyString, isObject } from '../../helper/index.js';
3
+ import { existsSync, readFileSync } from '../../node-singletons/fs.js';
4
+ import * as path from '../../node-singletons/path.js';
5
+ const uniq = (items) => {
6
+ const seen = new Set();
7
+ const out = [];
8
+ for (const item of items) {
9
+ const normalized = item.trim();
10
+ if (normalized === '' || seen.has(normalized))
11
+ continue;
12
+ seen.add(normalized);
13
+ out.push(normalized);
14
+ }
15
+ return out;
16
+ };
17
+ const toStringArray = (value) => {
18
+ if (!isArray(value))
19
+ return [];
20
+ return uniq(value.filter(isNonEmptyString).map((item) => item.trim()));
21
+ };
22
+ const toStringArrayRecord = (value) => {
23
+ if (!isObject(value))
24
+ return {};
25
+ return Object.fromEntries(Object.entries(value)
26
+ .filter(([key]) => isNonEmptyString(key))
27
+ .map(([key, entry]) => [key.trim(), toStringArray(entry)]));
28
+ };
29
+ const isAbsolutePath = (value) => value.startsWith('/') || /^[A-Za-z]:[\\/]/.test(value);
30
+ export const readZintrustConfig = (cwd) => {
31
+ const filePath = path.join(cwd, '.zintrust.json');
32
+ if (!existsSync(filePath)) {
33
+ return {};
34
+ }
35
+ try {
36
+ const raw = readFileSync(filePath, 'utf8');
37
+ const parsed = JSON.parse(raw);
38
+ return isObject(parsed) ? parsed : {};
39
+ }
40
+ catch (error) {
41
+ throw ErrorFactory.createCliError('Failed to parse .zintrust.json', error);
42
+ }
43
+ };
44
+ export const resolveCloudflareEnvTargetConfig = (config) => {
45
+ const raw = isObject(config['cloudflare']) ? config['cloudflare'] : {};
46
+ return Object.freeze({
47
+ sharedEnv: toStringArray(raw['shared_env']),
48
+ wranglerEnvs: toStringArrayRecord(raw['wrangler_envs']),
49
+ targets: toStringArrayRecord(raw['targets']),
50
+ });
51
+ };
52
+ const normalizeConfigPath = (cwd, configPath) => {
53
+ if (!isNonEmptyString(configPath))
54
+ return undefined;
55
+ return isAbsolutePath(configPath) ? configPath : path.join(cwd, configPath);
56
+ };
57
+ const inferServiceTarget = (projectRoot, candidateDir) => {
58
+ const servicesRoot = path.join(projectRoot, 'src', 'services');
59
+ const relative = path.relative(servicesRoot, candidateDir);
60
+ if (relative === '' || relative.startsWith('..'))
61
+ return undefined;
62
+ const segments = relative
63
+ .split(path.sep)
64
+ .map((segment) => segment.trim())
65
+ .filter((segment) => segment !== '');
66
+ if (segments.length < 2)
67
+ return undefined;
68
+ return `${segments[0]}/${segments[1]}`;
69
+ };
70
+ export const inferCloudflareTarget = ({ projectRoot, cwd, configPath, }) => {
71
+ const normalizedConfigPath = normalizeConfigPath(cwd, configPath);
72
+ const configBase = normalizedConfigPath === undefined ? '' : path.basename(normalizedConfigPath);
73
+ if (configBase.startsWith('wrangler.containers-proxy.')) {
74
+ return 'containers-proxy';
75
+ }
76
+ const candidateDir = normalizedConfigPath === undefined ? cwd : path.dirname(normalizedConfigPath);
77
+ const serviceTarget = inferServiceTarget(projectRoot, candidateDir);
78
+ if (serviceTarget !== undefined)
79
+ return serviceTarget;
80
+ return 'worker';
81
+ };
82
+ export const resolveCloudflareEnvKeys = (args) => {
83
+ const cloudflare = resolveCloudflareEnvTargetConfig(args.config);
84
+ const target = isNonEmptyString(args.target)
85
+ ? args.target.trim()
86
+ : inferCloudflareTarget({
87
+ projectRoot: args.projectRoot,
88
+ cwd: args.cwd,
89
+ configPath: args.configPath,
90
+ });
91
+ const wranglerEnv = isNonEmptyString(args.wranglerEnv) ? args.wranglerEnv.trim() : undefined;
92
+ return uniq([
93
+ ...cloudflare.sharedEnv,
94
+ ...(cloudflare.targets['default'] ?? []),
95
+ ...(cloudflare.targets[target] ?? []),
96
+ ...(wranglerEnv === undefined ? [] : (cloudflare.wranglerEnvs[wranglerEnv] ?? [])),
97
+ ]);
98
+ };
99
+ export default Object.freeze({
100
+ readZintrustConfig,
101
+ resolveCloudflareEnvTargetConfig,
102
+ inferCloudflareTarget,
103
+ resolveCloudflareEnvKeys,
104
+ });
@@ -0,0 +1,34 @@
1
+ type CloudflareSecretLog = {
2
+ info: (message: string) => void;
3
+ warn: (message: string) => void;
4
+ success?: (message: string) => void;
5
+ };
6
+ export type CloudflareSecretSyncFailure = {
7
+ wranglerEnv: string;
8
+ key: string;
9
+ reason: string;
10
+ };
11
+ type CloudflareSecretSyncArgs = {
12
+ log: CloudflareSecretLog;
13
+ cwd: string;
14
+ wranglerEnvs: string[];
15
+ envPath: string;
16
+ dryRun?: boolean;
17
+ configGroups?: string[];
18
+ configPath?: string;
19
+ target?: string;
20
+ requireSelection?: boolean;
21
+ };
22
+ export type CloudflareSecretSyncResult = {
23
+ pushed: number;
24
+ failures: CloudflareSecretSyncFailure[];
25
+ selectedKeys: string[];
26
+ };
27
+ export declare const reportCloudflareSecretSync: (log: CloudflareSecretLog, result: Pick<CloudflareSecretSyncResult, "pushed" | "failures">) => void;
28
+ export declare const syncCloudflareSecrets: ({ log, cwd, wranglerEnvs, envPath, dryRun, configGroups, configPath, target, requireSelection, }: CloudflareSecretSyncArgs) => Promise<CloudflareSecretSyncResult>;
29
+ declare const _default: Readonly<{
30
+ syncCloudflareSecrets: ({ log, cwd, wranglerEnvs, envPath, dryRun, configGroups, configPath, target, requireSelection, }: CloudflareSecretSyncArgs) => Promise<CloudflareSecretSyncResult>;
31
+ reportCloudflareSecretSync: (log: CloudflareSecretLog, result: Pick<CloudflareSecretSyncResult, "pushed" | "failures">) => void;
32
+ }>;
33
+ export default _default;
34
+ //# sourceMappingURL=CloudflareSecretSync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CloudflareSecretSync.d.ts","sourceRoot":"","sources":["../../../../src/cli/cloudflare/CloudflareSecretSync.ts"],"names":[],"mappings":"AAYA,KAAK,mBAAmB,GAAG;IACzB,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAaF,KAAK,wBAAwB,GAAG;IAC9B,GAAG,EAAE,mBAAmB,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,2BAA2B,EAAE,CAAC;IACxC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAiJF,eAAO,MAAM,0BAA0B,GACrC,KAAK,mBAAmB,EACxB,QAAQ,IAAI,CAAC,0BAA0B,EAAE,QAAQ,GAAG,UAAU,CAAC,KAC9D,IAYF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAU,kGAUzC,wBAAwB,KAAG,OAAO,CAAC,0BAA0B,CAwC/D,CAAC;;8HAxCC,wBAAwB,KAAG,OAAO,CAAC,0BAA0B,CAAC;sCA1B1D,mBAAmB,UAChB,IAAI,CAAC,0BAA0B,EAAE,QAAQ,GAAG,UAAU,CAAC,KAC9D,IAAI;;AAkEP,wBAGG"}
@@ -0,0 +1,148 @@
1
+ import { readZintrustConfig, resolveCloudflareEnvKeys, } from '../cloudflare/CloudflareEnvTargetConfig.js';
2
+ import { resolveNpmPath } from '../../common/index.js';
3
+ import { appConfig } from '../../config/app.js';
4
+ import { ErrorFactory } from '../../exceptions/ZintrustError.js';
5
+ import { execFileSync } from '../../node-singletons/child-process.js';
6
+ import { existsSync } from '../../node-singletons/fs.js';
7
+ import * as path from '../../node-singletons/path.js';
8
+ import { EnvFile } from '../../toolkit/Secrets/EnvFile.js';
9
+ const uniq = (items) => {
10
+ const seen = new Set();
11
+ const out = [];
12
+ for (const item of items) {
13
+ const normalized = item.trim();
14
+ if (normalized === '' || seen.has(normalized))
15
+ continue;
16
+ seen.add(normalized);
17
+ out.push(normalized);
18
+ }
19
+ return out;
20
+ };
21
+ const getConfigArray = (config, key) => {
22
+ const raw = config[key];
23
+ if (!Array.isArray(raw))
24
+ return [];
25
+ return uniq(raw.filter((item) => typeof item === 'string'));
26
+ };
27
+ const resolveValue = (key, envMap) => {
28
+ const fromFile = envMap[key];
29
+ const fromProcess = process.env[key];
30
+ return fromFile ?? fromProcess ?? '';
31
+ };
32
+ const getPutTimeoutMs = () => {
33
+ const raw = process.env['ZT_PUT_TIMEOUT_MS'];
34
+ if (typeof raw !== 'string')
35
+ return 120000;
36
+ const parsed = Number.parseInt(raw, 10);
37
+ if (!Number.isFinite(parsed) || parsed <= 0)
38
+ return 120000;
39
+ return parsed;
40
+ };
41
+ const putSecret = (wranglerEnv, key, value, configPath) => {
42
+ const npmPath = resolveNpmPath();
43
+ const args = ['exec', '--yes', '--', 'wrangler'];
44
+ if (typeof configPath === 'string' && configPath.trim() !== '') {
45
+ args.push('--config', configPath.trim());
46
+ }
47
+ args.push('secret', 'put', key, '--env', wranglerEnv);
48
+ execFileSync(npmPath, args, {
49
+ stdio: ['pipe', 'inherit', 'inherit'],
50
+ input: value,
51
+ encoding: 'utf8',
52
+ timeout: getPutTimeoutMs(),
53
+ killSignal: 'SIGTERM',
54
+ env: appConfig.getSafeEnv(),
55
+ });
56
+ };
57
+ const getFailureReason = (error) => error instanceof Error ? error.message : String(error);
58
+ const resolveSelectedKeys = ({ log, config, cwd, wranglerEnvs, configGroups = [], configPath, target, requireSelection, }) => {
59
+ const explicitKeys = uniq(configGroups.flatMap((groupKey) => {
60
+ const keys = getConfigArray(config, groupKey);
61
+ if (keys.length === 0) {
62
+ log.warn(`Group \`${groupKey}\` is missing or empty in .zintrust.json`);
63
+ }
64
+ return keys;
65
+ }));
66
+ const manifestKeys = uniq(wranglerEnvs.flatMap((wranglerEnv) => resolveCloudflareEnvKeys({
67
+ config,
68
+ projectRoot: cwd,
69
+ cwd,
70
+ ...(configPath === undefined ? {} : { configPath }),
71
+ wranglerEnv,
72
+ ...(typeof target === 'string' && target.trim() !== '' ? { target: target.trim() } : {}),
73
+ })));
74
+ const selectedKeys = uniq([...explicitKeys, ...manifestKeys]);
75
+ if (selectedKeys.length > 0 || !requireSelection) {
76
+ return selectedKeys;
77
+ }
78
+ throw ErrorFactory.createCliError(configGroups.length === 0
79
+ ? 'No secret keys resolved from .zintrust.json cloudflare.shared_env/cloudflare.targets/cloudflare.wrangler_envs. Use --var <group> or add a Cloudflare env manifest.'
80
+ : 'No secret keys resolved from selected groups.');
81
+ };
82
+ const processSecretSync = (log, wranglerEnvs, selectedKeys, envMap, dryRun, configPath) => {
83
+ let pushed = 0;
84
+ const failures = [];
85
+ for (const wranglerEnv of wranglerEnvs) {
86
+ for (const key of selectedKeys) {
87
+ const value = resolveValue(key, envMap);
88
+ if (value.trim() === '') {
89
+ failures.push({ wranglerEnv, key, reason: 'empty value' });
90
+ continue;
91
+ }
92
+ try {
93
+ if (!dryRun) {
94
+ log.info(`putting ${key} -> ${wranglerEnv}...`);
95
+ putSecret(wranglerEnv, key, value, configPath);
96
+ }
97
+ pushed += 1;
98
+ log.info(`${dryRun ? '[dry-run] ' : ''}put ${key} -> ${wranglerEnv}`);
99
+ }
100
+ catch (error) {
101
+ failures.push({ wranglerEnv, key, reason: getFailureReason(error) });
102
+ }
103
+ }
104
+ }
105
+ return { pushed, failures };
106
+ };
107
+ export const reportCloudflareSecretSync = (log, result) => {
108
+ if (typeof log.success === 'function') {
109
+ log.success(`Cloudflare secrets report: pushed=${result.pushed}, failed=${result.failures.length}`);
110
+ }
111
+ else {
112
+ log.info(`Cloudflare secrets report: pushed=${result.pushed}, failed=${result.failures.length}`);
113
+ }
114
+ for (const item of result.failures) {
115
+ log.warn(`${item.key} -> ${item.wranglerEnv}: ${item.reason}`);
116
+ }
117
+ };
118
+ export const syncCloudflareSecrets = async ({ log, cwd, wranglerEnvs, envPath, dryRun = false, configGroups = [], configPath, target, requireSelection = true, }) => {
119
+ const normalizedConfigPath = typeof configPath === 'string' && configPath.trim() !== '' ? configPath.trim() : undefined;
120
+ if (normalizedConfigPath !== undefined &&
121
+ !existsSync(path.join(cwd, normalizedConfigPath))) {
122
+ throw ErrorFactory.createCliError(`Wrangler config not found: ${normalizedConfigPath}`);
123
+ }
124
+ const config = readZintrustConfig(cwd);
125
+ const selectedKeys = resolveSelectedKeys({
126
+ log,
127
+ config,
128
+ cwd,
129
+ wranglerEnvs,
130
+ configGroups,
131
+ configPath: normalizedConfigPath,
132
+ target,
133
+ requireSelection,
134
+ });
135
+ if (selectedKeys.length === 0) {
136
+ return { pushed: 0, failures: [], selectedKeys: [] };
137
+ }
138
+ const envMap = await EnvFile.read({ cwd, path: envPath });
139
+ const syncResult = processSecretSync(log, wranglerEnvs, selectedKeys, envMap, dryRun, normalizedConfigPath);
140
+ return {
141
+ ...syncResult,
142
+ selectedKeys,
143
+ };
144
+ };
145
+ export default Object.freeze({
146
+ syncCloudflareSecrets,
147
+ reportCloudflareSecretSync,
148
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"DeployCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/DeployCommand.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAyIrE;;;GAGG;AACH,eAAO,MAAM,aAAa;IACxB;;OAEG;cACO,YAAY;EAGtB,CAAC"}
1
+ {"version":3,"file":"DeployCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/DeployCommand.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAuLrE;;;GAGG;AACH,eAAO,MAAM,aAAa;IACxB;;OAEG;cACO,YAAY;EAGtB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { BaseCommand } from '../BaseCommand.js';
2
+ import { reportCloudflareSecretSync, syncCloudflareSecrets, } from '../cloudflare/CloudflareSecretSync.js';
2
3
  import { SpawnUtil } from '../utils/spawn.js';
3
4
  import { Logger } from '../../config/logger.js';
4
5
  import { ErrorFactory } from '../../exceptions/ZintrustError.js';
@@ -44,7 +45,23 @@ const deployProxyStack = async (label) => {
44
45
  await runCompose(['compose', '-f', composePath, 'up', '-d', '--build']);
45
46
  Logger.info(`✅ ${label} deployed.`);
46
47
  };
47
- const runDeploy = async (target, options) => {
48
+ const syncWranglerSecrets = async (cmd, cwd, wranglerEnv, options) => {
49
+ if (options.syncSecrets === false)
50
+ return;
51
+ const result = await syncCloudflareSecrets({
52
+ log: cmd,
53
+ cwd,
54
+ wranglerEnvs: [wranglerEnv],
55
+ envPath: typeof options.envPath === 'string' && options.envPath.trim() !== '' ? options.envPath : '.env',
56
+ configPath: typeof options.config === 'string' ? options.config.trim() : undefined,
57
+ target: typeof options.target === 'string' ? options.target : undefined,
58
+ requireSelection: false,
59
+ });
60
+ if (result.selectedKeys.length === 0)
61
+ return;
62
+ reportCloudflareSecretSync(cmd, result);
63
+ };
64
+ const runDeploy = async (cmd, target, options) => {
48
65
  const normalizedTarget = target.trim().toLowerCase();
49
66
  if (normalizedTarget === 'cw' || normalizedTarget === 'cwr') {
50
67
  const label = normalizedTarget === 'cwr'
@@ -70,6 +87,7 @@ const runDeploy = async (target, options) => {
70
87
  }
71
88
  }
72
89
  Logger.info(`Deploying to Cloudflare environment: ${environment}`);
90
+ await syncWranglerSecrets(cmd, process.cwd(), environment, options);
73
91
  const exitCode = await SpawnUtil.spawnAndWait({
74
92
  command: 'wrangler',
75
93
  args: ['deploy', ...(config.length > 0 ? ['--config', config] : []), '--env', environment],
@@ -79,7 +97,7 @@ const runDeploy = async (target, options) => {
79
97
  }
80
98
  };
81
99
  const createDeployCommand = () => {
82
- return BaseCommand.create({
100
+ const cmd = BaseCommand.create({
83
101
  name: 'deploy',
84
102
  description: 'Deploy ZinTrust to Cloudflare Workers',
85
103
  addOptions: (command) => {
@@ -87,6 +105,9 @@ const createDeployCommand = () => {
87
105
  .argument('[target]', 'Deployment target (worker, d1-proxy, kv-proxy, production, cw, cp)', 'worker')
88
106
  .option('-e, --env <env>', 'Wrangler environment (overrides target)');
89
107
  command.option('-c, --config <path>', 'Wrangler config file (e.g. wrangler.containers-proxy.jsonc)');
108
+ command.option('--env-path <path>', 'Path to env file used when syncing Cloudflare secrets', '.env');
109
+ command.option('--target <id>', 'Cloudflare worker target key from .zintrust.json cloudflare.targets');
110
+ command.option('--no-sync-secrets', 'Skip Cloudflare secret sync before wrangler deploy');
90
111
  },
91
112
  execute: async (options) => {
92
113
  // Note: BaseCommand.create sets up action handler that calls execute(options).
@@ -103,9 +124,10 @@ const createDeployCommand = () => {
103
124
  //
104
125
  // So 'target' will be available in options.args[0]
105
126
  const target = options.args?.[0] ?? 'worker';
106
- await runDeploy(target, options);
127
+ await runDeploy(cmd, target, options);
107
128
  },
108
129
  });
130
+ return cmd;
109
131
  };
110
132
  /**
111
133
  * Deploy Command Factory
@@ -1 +1 @@
1
- {"version":3,"file":"DeployContainersProxyCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/DeployContainersProxyCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA2CrE,eAAO,MAAM,4BAA4B;cAC7B,YAAY;EAatB,CAAC"}
1
+ {"version":3,"file":"DeployContainersProxyCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/DeployContainersProxyCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA6ErE,eAAO,MAAM,4BAA4B;cAC7B,YAAY;EAkBtB,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { BaseCommand } from '../BaseCommand.js';
2
+ import { reportCloudflareSecretSync, syncCloudflareSecrets, } from '../cloudflare/CloudflareSecretSync.js';
2
3
  import { SpawnUtil } from '../utils/spawn.js';
3
4
  import { Logger } from '../../config/logger.js';
4
5
  import { ErrorFactory } from '../../exceptions/ZintrustError.js';
@@ -17,11 +18,28 @@ const resolveEnv = (raw) => {
17
18
  const normalized = typeof raw === 'string' ? raw.trim() : '';
18
19
  return normalized.length > 0 ? normalized : 'production';
19
20
  };
20
- const execute = async (options) => {
21
+ const syncDeploySecrets = async (cmd, cwd, config, env, options) => {
22
+ if (options.syncSecrets === false)
23
+ return;
24
+ const result = await syncCloudflareSecrets({
25
+ log: cmd,
26
+ cwd,
27
+ wranglerEnvs: [env],
28
+ envPath: typeof options.envPath === 'string' && options.envPath.trim() !== '' ? options.envPath : '.env',
29
+ configPath: config,
30
+ target: typeof options.target === 'string' ? options.target : undefined,
31
+ requireSelection: false,
32
+ });
33
+ if (result.selectedKeys.length === 0)
34
+ return;
35
+ reportCloudflareSecretSync(cmd, result);
36
+ };
37
+ const execute = async (cmd, options) => {
21
38
  const cwd = process.cwd();
22
39
  const config = resolveConfig(cwd, options.config);
23
40
  const env = resolveEnv(options.env);
24
41
  Logger.info(`Deploying Containers proxy via Wrangler (env=${env})...`);
42
+ await syncDeploySecrets(cmd, cwd, config, env, options);
25
43
  const exitCode = await SpawnUtil.spawnAndWait({
26
44
  command: 'wrangler',
27
45
  args: ['deploy', '--config', config, '--env', env],
@@ -31,15 +49,19 @@ const execute = async (options) => {
31
49
  };
32
50
  export const DeployContainersProxyCommand = Object.freeze({
33
51
  create() {
34
- return BaseCommand.create({
52
+ const cmd = BaseCommand.create({
35
53
  name: 'deploy:ccp',
36
54
  aliases: ['deploy:containers-proxy', 'deploy:cf-containers-proxy', 'd:ccp', 'ccp:deploy'],
37
55
  description: 'Deploy Cloudflare Containers proxy Worker (wrangler.containers-proxy.jsonc)',
38
56
  addOptions: (command) => {
39
57
  command.option('-e, --env <name>', 'Wrangler environment name', 'production');
40
58
  command.option('-c, --config <path>', 'Wrangler config file', DEFAULT_CONFIG);
59
+ command.option('--env-path <path>', 'Path to env file used when syncing Cloudflare secrets', '.env');
60
+ command.option('--target <id>', 'Cloudflare worker target key from .zintrust.json cloudflare.targets');
61
+ command.option('--no-sync-secrets', 'Skip Cloudflare secret sync before wrangler deploy');
41
62
  },
42
- execute: async (options) => execute(options),
63
+ execute: async (options) => execute(cmd, options),
43
64
  });
65
+ return cmd;
44
66
  },
45
67
  });
@@ -1 +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"}
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;CAmB3C,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,OAAO,EAAE,QAAQ,EACpD,SAAS,2BAA2B,CAAC,OAAO,EAAE,QAAQ,CAAC,KACtD,0BAA0B,CAAC,OAAO,CAqBpC,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { ErrorFactory } from '../../exceptions/ZintrustError.js';
2
2
  import { isNonEmptyString } from '../../helper/index.js';
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from '../../node-singletons/fs.js';
4
- import { join } from '../../node-singletons/path.js';
4
+ import { dirname, join } from '../../node-singletons/path.js';
5
5
  export const trimNonEmptyOption = (value) => {
6
6
  if (!isNonEmptyString(value))
7
7
  return undefined;
@@ -93,8 +93,7 @@ export const ensureProxyEntrypoint = (options) => {
93
93
  if (existsSync(entryFilePath)) {
94
94
  return { created: false, entryFilePath };
95
95
  }
96
- const lastSlashIndex = entryFilePath.lastIndexOf('/');
97
- const entryDir = lastSlashIndex > 0 ? entryFilePath.slice(0, lastSlashIndex) : options.cwd;
96
+ const entryDir = dirname(entryFilePath);
98
97
  mkdirSync(entryDir, { recursive: true });
99
98
  writeFileSync(entryFilePath, [
100
99
  `export { ${options.exportName} } from '${options.moduleSpecifier}';`,
@@ -1 +1 @@
1
- {"version":3,"file":"PutCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/PutCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA0OvF,eAAO,MAAM,UAAU;cACX,YAAY;EAWtB,CAAC;AAEH,eAAe,UAAU,CAAC"}
1
+ {"version":3,"file":"PutCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/PutCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAqFvF,eAAO,MAAM,UAAU;cACX,YAAY;EAWtB,CAAC;AAEH,eAAe,UAAU,CAAC"}
@@ -1,11 +1,6 @@
1
1
  import { BaseCommand } from '../BaseCommand.js';
2
- import { resolveNpmPath } from '../../common/index.js';
3
- import { appConfig } from '../../config/app.js';
2
+ import { reportCloudflareSecretSync, syncCloudflareSecrets, } from '../cloudflare/CloudflareSecretSync.js';
4
3
  import { ErrorFactory } from '../../exceptions/ZintrustError.js';
5
- import { execFileSync } from '../../node-singletons/child-process.js';
6
- import { existsSync, readFileSync } from '../../node-singletons/fs.js';
7
- import * as path from '../../node-singletons/path.js';
8
- import { EnvFile } from '../../toolkit/Secrets/EnvFile.js';
9
4
  const toStringArray = (value) => {
10
5
  if (typeof value === 'string')
11
6
  return [value];
@@ -25,26 +20,6 @@ const uniq = (items) => {
25
20
  }
26
21
  return out;
27
22
  };
28
- const readZintrustConfig = (cwd) => {
29
- const filePath = path.join(cwd, '.zintrust.json');
30
- if (!existsSync(filePath)) {
31
- return {};
32
- }
33
- try {
34
- const raw = readFileSync(filePath, 'utf8');
35
- const parsed = JSON.parse(raw);
36
- return typeof parsed === 'object' && parsed !== null ? parsed : {};
37
- }
38
- catch (error) {
39
- throw ErrorFactory.createCliError('Failed to parse .zintrust.json', error);
40
- }
41
- };
42
- const getConfigArray = (config, key) => {
43
- const raw = config[key];
44
- if (!Array.isArray(raw))
45
- return [];
46
- return uniq(raw.filter((item) => typeof item === 'string'));
47
- };
48
23
  const resolveConfigGroups = (options) => {
49
24
  return uniq(toStringArray(options.var));
50
25
  };
@@ -60,41 +35,12 @@ const parseEnvPath = (options) => {
60
35
  return direct;
61
36
  return '.env';
62
37
  };
63
- const resolveValue = (key, envMap) => {
64
- const fromFile = envMap[key];
65
- const fromProcess = process.env[key];
66
- return fromFile ?? fromProcess ?? '';
67
- };
68
- const getPutTimeoutMs = () => {
69
- const raw = process.env['ZT_PUT_TIMEOUT_MS'];
70
- if (typeof raw !== 'string')
71
- return 120000;
72
- const parsed = Number.parseInt(raw, 10);
73
- if (!Number.isFinite(parsed) || parsed <= 0)
74
- return 120000;
75
- return parsed;
76
- };
77
- const putSecret = (wranglerEnv, key, value, configPath) => {
78
- const npmPath = resolveNpmPath();
79
- const args = ['exec', '--yes', '--', 'wrangler'];
80
- if (typeof configPath === 'string' && configPath.trim().length > 0) {
81
- args.push('--config', configPath.trim());
82
- }
83
- args.push('secret', 'put', key, '--env', wranglerEnv);
84
- execFileSync(npmPath, args, {
85
- stdio: ['pipe', 'inherit', 'inherit'],
86
- input: value,
87
- encoding: 'utf8',
88
- timeout: getPutTimeoutMs(),
89
- killSignal: 'SIGTERM',
90
- env: appConfig.getSafeEnv(),
91
- });
92
- };
93
38
  const addOptions = (command) => {
94
39
  command
95
40
  .argument('[provider]', 'Secret provider (cloudflare)', 'cloudflare')
96
41
  .option('--wg <env...>', 'Wrangler environment target(s), e.g. d1-proxy kv-proxy')
97
42
  .option('--var <configKey...>', 'Config array key(s) from .zintrust.json (e.g. d1_env kv_env)')
43
+ .option('--target <id>', 'Cloudflare worker target key from .zintrust.json cloudflare.targets')
98
44
  .option('--env_path <path>', 'Path to env file used as source values', '.env')
99
45
  .option('-c, --config <path>', 'Wrangler config file to target (optional)')
100
46
  .option('--dry-run', 'Show what would be uploaded without calling wrangler');
@@ -104,70 +50,21 @@ const ensureCloudflareProvider = (providerRaw) => {
104
50
  return;
105
51
  throw ErrorFactory.createCliError('Only cloudflare provider is supported for `zin put`');
106
52
  };
107
- const resolveSelectedKeys = (cmd, config, options) => {
108
- const configGroups = resolveConfigGroups(options);
109
- if (configGroups.length === 0) {
110
- throw ErrorFactory.createCliError('No config groups selected. Use --var <group>.');
111
- }
112
- const selectedKeys = uniq(configGroups.flatMap((groupKey) => {
113
- const keys = getConfigArray(config, groupKey);
114
- if (keys.length === 0) {
115
- cmd.warn(`Group \`${groupKey}\` is missing or empty in .zintrust.json`);
116
- }
117
- return keys;
118
- }));
119
- if (selectedKeys.length === 0) {
120
- throw ErrorFactory.createCliError('No secret keys resolved from selected groups.');
121
- }
122
- return selectedKeys;
123
- };
124
- const getFailureReason = (error) => error instanceof Error ? error.message : String(error);
125
- const reportResult = (cmd, pushed, failures) => {
126
- cmd.success(`Cloudflare secrets report: pushed=${pushed}, failed=${failures.length}`);
127
- for (const item of failures) {
128
- cmd.warn(`${item.key} -> ${item.wranglerEnv}: ${item.reason}`);
129
- }
130
- };
131
- const processPut = (cmd, wranglerEnvs, selectedKeys, envMap, dryRun, configPath) => {
132
- let pushed = 0;
133
- const failures = [];
134
- for (const wranglerEnv of wranglerEnvs) {
135
- for (const key of selectedKeys) {
136
- const value = resolveValue(key, envMap);
137
- if (value.trim() === '') {
138
- failures.push({ wranglerEnv, key, reason: 'empty value' });
139
- continue;
140
- }
141
- try {
142
- if (!dryRun) {
143
- cmd.info(`putting ${key} -> ${wranglerEnv}...`);
144
- putSecret(wranglerEnv, key, value, configPath);
145
- }
146
- pushed += 1;
147
- cmd.info(`${dryRun ? '[dry-run] ' : ''}put ${key} -> ${wranglerEnv}`);
148
- }
149
- catch (error) {
150
- failures.push({ wranglerEnv, key, reason: getFailureReason(error) });
151
- }
152
- }
153
- }
154
- return { pushed, failures };
155
- };
156
53
  const execute = async (cmd, options) => {
157
54
  ensureCloudflareProvider(String(options.args?.[0] ?? 'cloudflare'));
158
55
  const cwd = process.cwd();
159
- const config = readZintrustConfig(cwd);
160
- const selectedKeys = resolveSelectedKeys(cmd, config, options);
161
- const envFilePath = parseEnvPath(options);
162
- const envMap = await EnvFile.read({ cwd, path: envFilePath });
163
- const wranglerEnvs = resolveWranglerEnvs(options);
164
- const dryRun = options.dryRun === true;
165
- const configPath = typeof options.config === 'string' ? options.config.trim() : '';
166
- if (configPath !== '' && !existsSync(path.join(cwd, configPath))) {
167
- throw ErrorFactory.createCliError(`Wrangler config not found: ${configPath}`);
168
- }
169
- const result = processPut(cmd, wranglerEnvs, selectedKeys, envMap, dryRun, configPath === '' ? undefined : configPath);
170
- reportResult(cmd, result.pushed, result.failures);
56
+ const result = await syncCloudflareSecrets({
57
+ log: cmd,
58
+ cwd,
59
+ wranglerEnvs: resolveWranglerEnvs(options),
60
+ envPath: parseEnvPath(options),
61
+ dryRun: options.dryRun === true,
62
+ configGroups: resolveConfigGroups(options),
63
+ configPath: typeof options.config === 'string' ? options.config.trim() : undefined,
64
+ target: typeof options.target === 'string' ? options.target : undefined,
65
+ requireSelection: true,
66
+ });
67
+ reportCloudflareSecretSync(cmd, result);
171
68
  };
172
69
  export const PutCommand = Object.freeze({
173
70
  create() {
@@ -1 +1 @@
1
- {"version":3,"file":"StartCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/StartCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAmhCvF,eAAO,MAAM,YAAY;cACb,YAAY;;mCAr9BU,MAAM,KAAG,OAAO;4CAaP,MAAM,KAAG,MAAM;4CA4Bf,MAAM,KAAG,OAAO;sCAetB,MAAM,WAAW,MAAM,KAAG,OAAO;oCAmBnC,MAAM,KAAG,OAAO;;EAq7BjD,CAAC"}
1
+ {"version":3,"file":"StartCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/StartCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoC,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA6lCvF,eAAO,MAAM,YAAY;cACb,YAAY;;mCA3hCU,MAAM,KAAG,OAAO;4CAaP,MAAM,KAAG,MAAM;4CA4Bf,MAAM,KAAG,OAAO;sCAetB,MAAM,WAAW,MAAM,KAAG,OAAO;oCAmBnC,MAAM,KAAG,OAAO;;EA2/BjD,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { BaseCommand } from '../BaseCommand.js';
2
+ import { readZintrustConfig, resolveCloudflareEnvKeys, } from '../cloudflare/CloudflareEnvTargetConfig.js';
2
3
  import { createDenoRunnerSource, createLambdaRunnerSource } from '../commands/runner/index.js';
3
4
  import { EnvFileLoader } from '../utils/EnvFileLoader.js';
4
5
  import { SpawnUtil } from '../utils/spawn.js';
@@ -336,11 +337,31 @@ const buildStartEnv = (projectRoot) => ({
336
337
  ...process.env,
337
338
  ZINTRUST_PROJECT_ROOT: projectRoot,
338
339
  });
339
- const buildWorkerDevVarsContent = () => {
340
+ const WRANGLER_RUNTIME_ENV_KEYS = Object.freeze([
341
+ 'APP_PORT',
342
+ 'CLOUDFLARE_WORKER',
343
+ 'DOCKER_WORKER',
344
+ 'ENVIRONMENT',
345
+ 'HOST',
346
+ 'NODE_ENV',
347
+ 'PORT',
348
+ 'RUNTIME',
349
+ 'SERVICE_DOMAIN',
350
+ 'SERVICE_NAME',
351
+ 'SERVICE_PORT',
352
+ 'WORKER_ENABLED',
353
+ 'ZINTRUST_PROJECT_ROOT',
354
+ ]);
355
+ const buildWorkerDevVarsContent = (selectedKeys) => {
356
+ const allowedKeys = selectedKeys === undefined || selectedKeys.length === 0
357
+ ? undefined
358
+ : new Set([...WRANGLER_RUNTIME_ENV_KEYS, ...selectedKeys]);
340
359
  return (Object.entries(process.env)
341
360
  .filter((entry) => {
342
361
  const [key, value] = entry;
343
- return isWranglerVarName(key) && typeof value === 'string';
362
+ return (isWranglerVarName(key) &&
363
+ typeof value === 'string' &&
364
+ (allowedKeys === undefined || allowedKeys.has(key)));
344
365
  })
345
366
  .map(([key, value]) => `${key}=${JSON.stringify(value)}`)
346
367
  .join('\n') + '\n');
@@ -357,7 +378,7 @@ const reconcileWranglerEnvBackup = (targetPath, backupPath) => {
357
378
  }
358
379
  unlinkSync(backupPath);
359
380
  };
360
- async function withWranglerEnvSnapshot(cwd, envName, fn) {
381
+ async function withWranglerEnvSnapshot(cwd, envName, selectedKeys, fn) {
361
382
  const normalizedEnv = typeof envName === 'string' ? envName.trim() : '';
362
383
  const targetName = normalizedEnv === '' ? '.dev.vars' : `.dev.vars.${normalizedEnv}`;
363
384
  const targetPath = path.join(cwd, targetName);
@@ -372,7 +393,7 @@ async function withWranglerEnvSnapshot(cwd, envName, fn) {
372
393
  renameSync(targetPath, backupPath);
373
394
  }
374
395
  try {
375
- writeFileSync(targetPath, buildWorkerDevVarsContent(), 'utf-8');
396
+ writeFileSync(targetPath, buildWorkerDevVarsContent(selectedKeys), 'utf-8');
376
397
  return await fn();
377
398
  }
378
399
  finally {
@@ -514,42 +535,64 @@ const resolveNodeProdCommand = (cwd) => {
514
535
  }
515
536
  return { command: 'node', args: [compiled] };
516
537
  };
517
- const executeWranglerStart = async (cmd, context, port, runtime, envName, wranglerConfig) => {
518
- if (runtime !== undefined) {
519
- throw ErrorFactory.createCliError('Error: --runtime is not supported with --wrangler (Wrangler controls Workers runtime).');
520
- }
538
+ const resolveWranglerDevConfig = (context, wranglerConfig) => {
521
539
  const normalizedConfig = typeof wranglerConfig === 'string' ? wranglerConfig.trim() : '';
522
540
  const explicitConfigFullPath = normalizedConfig.length > 0 ? path.join(context.cwd, normalizedConfig) : undefined;
523
541
  const configPath = explicitConfigFullPath ?? findWranglerConfig(context.cwd);
524
542
  const entry = resolveWranglerEntry(context.cwd);
525
- if (explicitConfigFullPath !== undefined) {
526
- if (existsSync(explicitConfigFullPath)) {
527
- // ok
528
- }
529
- else {
530
- throw ErrorFactory.createCliError(`Error: Wrangler config not found: ${normalizedConfig}`);
531
- }
543
+ if (explicitConfigFullPath !== undefined && !existsSync(explicitConfigFullPath)) {
544
+ throw ErrorFactory.createCliError(`Error: Wrangler config not found: ${normalizedConfig}`);
532
545
  }
533
546
  if (configPath === undefined && entry === undefined) {
534
547
  throw ErrorFactory.createCliError("Error: wrangler config not found (wrangler.toml/json). Run 'wrangler init' first.");
535
548
  }
536
- warnOnUnsafeWranglerBootstrap(cmd, context.cwd, entry);
549
+ return { normalizedConfig, configPath, entry };
550
+ };
551
+ const buildWranglerDevArgs = (args) => {
537
552
  const wranglerArgs = ['dev'];
538
- if (normalizedConfig !== '') {
539
- wranglerArgs.push('--config', normalizedConfig);
553
+ if (args.normalizedConfig !== '') {
554
+ wranglerArgs.push('--config', args.normalizedConfig);
540
555
  }
541
- if (configPath === undefined && entry !== undefined) {
542
- wranglerArgs.push(entry);
556
+ if (args.configPath === undefined && args.entry !== undefined) {
557
+ wranglerArgs.push(args.entry);
543
558
  }
544
- if (typeof port === 'number') {
545
- wranglerArgs.push('--port', String(port));
559
+ if (typeof args.port === 'number') {
560
+ wranglerArgs.push('--port', String(args.port));
546
561
  }
547
- if (envName !== undefined && envName.trim() !== '') {
548
- wranglerArgs.push('--env', envName.trim());
562
+ if (args.envName !== undefined && args.envName.trim() !== '') {
563
+ wranglerArgs.push('--env', args.envName.trim());
564
+ }
565
+ return wranglerArgs;
566
+ };
567
+ const resolveWranglerSelectedEnvKeys = (context, configPath, envName) => {
568
+ const zintrustConfigPath = path.join(context.projectRoot, '.zintrust.json');
569
+ if (!existsSync(zintrustConfigPath))
570
+ return undefined;
571
+ return resolveCloudflareEnvKeys({
572
+ config: readZintrustConfig(context.projectRoot),
573
+ projectRoot: context.projectRoot,
574
+ cwd: context.cwd,
575
+ ...(configPath === undefined ? {} : { configPath }),
576
+ ...(envName === undefined ? {} : { wranglerEnv: envName }),
577
+ });
578
+ };
579
+ const executeWranglerStart = async (cmd, context, port, runtime, envName, wranglerConfig) => {
580
+ if (runtime !== undefined) {
581
+ throw ErrorFactory.createCliError('Error: --runtime is not supported with --wrangler (Wrangler controls Workers runtime).');
549
582
  }
583
+ const { normalizedConfig, configPath, entry } = resolveWranglerDevConfig(context, wranglerConfig);
584
+ warnOnUnsafeWranglerBootstrap(cmd, context.cwd, entry);
585
+ const wranglerArgs = buildWranglerDevArgs({
586
+ normalizedConfig,
587
+ configPath,
588
+ entry,
589
+ port,
590
+ envName,
591
+ });
550
592
  logMySqlProxyHint(cmd);
551
593
  cmd.info('Starting in Wrangler dev mode...');
552
- const exitCode = await withWranglerEnvSnapshot(context.cwd, envName, async () => {
594
+ const selectedEnvKeys = resolveWranglerSelectedEnvKeys(context, configPath, envName);
595
+ const exitCode = await withWranglerEnvSnapshot(context.cwd, envName, selectedEnvKeys, async () => {
553
596
  const startEnv = {
554
597
  ...buildStartEnv(context.projectRoot),
555
598
  WORKER_ENABLED: 'false',
@@ -1 +1 @@
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"}
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;AA4eD,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"}
@@ -93,6 +93,30 @@ const createProjectConfigFile = (projectPath, variables) => {
93
93
  server: {
94
94
  port: variables['port'] ?? 7777,
95
95
  },
96
+ cloudflare: {
97
+ shared_env: ['APP_KEY', 'JWT_SECRET', 'SESSION_SECRET'],
98
+ targets: {
99
+ worker: [],
100
+ 'containers-proxy': [
101
+ 'MYSQL_PROXY_KEY_ID',
102
+ 'MYSQL_PROXY_SECRET',
103
+ 'POSTGRES_PROXY_KEY_ID',
104
+ 'POSTGRES_PROXY_SECRET',
105
+ 'REDIS_PROXY_KEY_ID',
106
+ 'REDIS_PROXY_SECRET',
107
+ 'MONGODB_PROXY_KEY_ID',
108
+ 'MONGODB_PROXY_SECRET',
109
+ 'SQLSERVER_PROXY_KEY_ID',
110
+ 'SQLSERVER_PROXY_SECRET',
111
+ 'SMTP_PROXY_KEY_ID',
112
+ 'SMTP_PROXY_SECRET',
113
+ ],
114
+ },
115
+ wrangler_envs: {
116
+ 'd1-proxy': ['D1_REMOTE_KEY_ID', 'D1_REMOTE_SECRET'],
117
+ 'kv-proxy': ['KV_REMOTE_KEY_ID', 'KV_REMOTE_SECRET'],
118
+ },
119
+ },
96
120
  };
97
121
  fs.writeFileSync(fullPath, JSON.stringify(config, null, 2));
98
122
  return true;
@@ -1 +1 @@
1
- {"version":3,"file":"ServiceScaffolder.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/ServiceScaffolder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IACjC,IAAI,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC7C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAaD;;GAEG;AAEH;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAuB7F;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CAGnF;AAED;;GAEG;AAEH,wBAAgB,QAAQ,CACtB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,qBAAqB,CAAC,CAiDhC;AA+cD,eAAO,MAAM,iBAAiB;;;;EAI5B,CAAC"}
1
+ {"version":3,"file":"ServiceScaffolder.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/ServiceScaffolder.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IACjC,IAAI,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC7C,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;CACjB;AAeD;;GAEG;AAEH;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAuB7F;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,MAAM,CAGnF;AAED;;GAEG;AAEH,wBAAgB,QAAQ,CACtB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,qBAAqB,CAAC,CAkDhC;AA8eD,eAAO,MAAM,iBAAiB;;;;EAI5B,CAAC"}
@@ -4,12 +4,14 @@
4
4
  */
5
5
  import { FileGenerator } from '../scaffolding/FileGenerator.js';
6
6
  import { Logger } from '../../config/logger.js';
7
+ import { isArray, isObject } from '../../helper/index.js';
7
8
  import * as path from '../../node-singletons/path.js';
8
9
  const coreModuleSpecifier = ['@zintrust', 'core'].join('/');
9
10
  const coreStartModuleSpecifier = `${coreModuleSpecifier}/start`;
10
11
  const serviceManifestImportExpression = "import('./bootstrap/service-manifest.ts').catch(() => import('./bootstrap/service-manifest.js'))";
11
12
  const buildRouteImportExpression = (domain, serviceName) => `import('../services/${domain}/${serviceName}/routes/api.ts').catch(() => import('../services/${domain}/${serviceName}/routes/api.js'))`;
12
13
  const getServiceConfigRoot = (domain, serviceName) => `src/services/${domain}/${serviceName}/config`;
14
+ const getServiceId = (domain, serviceName) => `${domain}/${serviceName}`;
13
15
  /**
14
16
  * ServiceScaffolder generates microservices with all necessary files
15
17
  */
@@ -74,6 +76,7 @@ export function scaffold(projectRoot, options) {
74
76
  ensureProjectRuntimeFiles(projectRoot, options);
75
77
  const filesCreated = createServiceFiles(servicePath, options);
76
78
  updateServiceManifest(projectRoot, options);
79
+ ensureCloudflareServiceTarget(projectRoot, options);
77
80
  return Promise.resolve({
78
81
  success: true,
79
82
  serviceName: options.name,
@@ -95,7 +98,7 @@ export function scaffold(projectRoot, options) {
95
98
  }
96
99
  function ensureProjectRuntimeFiles(projectRoot, options) {
97
100
  const domain = options.domain ?? 'default';
98
- const serviceId = `${domain}/${options.name}`;
101
+ const serviceId = getServiceId(domain, options.name);
99
102
  const routeImportExpression = buildRouteImportExpression(domain, options.name);
100
103
  const bootstrapDir = path.join(projectRoot, 'src', 'bootstrap');
101
104
  FileGenerator.createDirectory(bootstrapDir);
@@ -138,7 +141,7 @@ export default Object.freeze({ serviceManifest });
138
141
  }
139
142
  function updateServiceManifest(projectRoot, options) {
140
143
  const domain = options.domain ?? 'default';
141
- const serviceId = `${domain}/${options.name}`;
144
+ const serviceId = getServiceId(domain, options.name);
142
145
  const routeImportExpression = buildRouteImportExpression(domain, options.name);
143
146
  const manifestPath = path.join(projectRoot, 'src', 'bootstrap', 'service-manifest.ts');
144
147
  if (!FileGenerator.fileExists(manifestPath))
@@ -168,6 +171,33 @@ function updateServiceManifest(projectRoot, options) {
168
171
  const next = `${current.slice(0, markerIndex)}${entry}${current.slice(markerIndex)}`;
169
172
  FileGenerator.writeFile(manifestPath, next, { overwrite: true });
170
173
  }
174
+ function ensureCloudflareServiceTarget(projectRoot, options) {
175
+ const configPath = path.join(projectRoot, '.zintrust.json');
176
+ if (!FileGenerator.fileExists(configPath))
177
+ return;
178
+ try {
179
+ const raw = FileGenerator.readFile(configPath);
180
+ const parsed = JSON.parse(raw);
181
+ if (!isObject(parsed))
182
+ return;
183
+ const config = { ...parsed };
184
+ const cloudflare = isObject(config['cloudflare']) ? { ...config['cloudflare'] } : {};
185
+ const targets = isObject(cloudflare['targets']) ? { ...cloudflare['targets'] } : {};
186
+ const domain = options.domain ?? 'default';
187
+ const serviceId = getServiceId(domain, options.name);
188
+ if (isArray(targets[serviceId]))
189
+ return;
190
+ targets[serviceId] = [];
191
+ cloudflare['targets'] = targets;
192
+ config['cloudflare'] = cloudflare;
193
+ FileGenerator.writeFile(`${configPath}`, `${JSON.stringify(config, null, 2)}\n`, {
194
+ overwrite: true,
195
+ });
196
+ }
197
+ catch (error) {
198
+ Logger.warn(`Unable to update .zintrust.json Cloudflare targets for ${options.domain ?? 'default'}/${options.name}: ${error instanceof Error ? error.message : String(error)}`);
199
+ }
200
+ }
171
201
  /**
172
202
  * Create service directory structure
173
203
  */
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @zintrust/core v0.4.39
2
+ * @zintrust/core v0.4.40
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-30T20:09:19.868Z
8
+ * Built: 2026-03-31T13:31:43.681Z
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-30T20:09:19.808Z'; // Replaced during build
24
+ export const ZINTRUST_BUILD_DATE = '2026-03-31T13:31:43.643Z'; // 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';