@zintrust/core 1.8.5 → 1.8.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/core",
3
- "version": "1.8.5",
3
+ "version": "1.8.6",
4
4
  "description": "Production-grade TypeScript backend framework for JavaScript",
5
5
  "homepage": "https://zintrust.com",
6
6
  "repository": {
@@ -59,14 +59,14 @@
59
59
  "dependencies": {
60
60
  "@cloudflare/containers": "^0.3.4",
61
61
  "bcryptjs": "^3.0.3",
62
- "bullmq": "^5.76.8",
62
+ "bullmq": "^5.76.10",
63
63
  "chalk": "^5.6.2",
64
64
  "commander": "^14.0.3",
65
65
  "inquirer": "^13.4.3",
66
66
  "jsonwebtoken": "^9.0.3",
67
67
  "mysql2": "^3.22.3",
68
- "pg": "^8.20.0",
69
- "tsx": "^4.22.0"
68
+ "pg": "^8.21.0",
69
+ "tsx": "^4.22.3"
70
70
  },
71
71
  "overrides": {
72
72
  "ajv": "^8.18.0",
@@ -76,8 +76,8 @@
76
76
  "@tootallnate/once": "3.0.1",
77
77
  "node-forge": "^1.4.0",
78
78
  "fast-uri": "3.1.2",
79
- "fast-xml-builder": "1.2.0",
80
79
  "fast-xml-parser": "5.8.0",
80
+ "protobufjs": "7.5.9",
81
81
  "brace-expansion": "^5.0.5",
82
82
  "picomatch": "^4.0.4",
83
83
  "cross-spawn": "^7.0.5",
@@ -15,20 +15,25 @@ type CloudflareSecretSyncArgs = {
15
15
  envPath: string;
16
16
  dryRun?: boolean;
17
17
  configGroups?: string[];
18
+ directKeys?: string[];
19
+ inlineValues?: Record<string, string>;
18
20
  configPath?: string;
19
21
  target?: string;
22
+ bulk?: boolean;
20
23
  requireSelection?: boolean;
21
24
  };
22
25
  export type CloudflareSecretSyncResult = {
23
26
  pushed: number;
27
+ pushedKeys: string[];
28
+ skippedEmptyKeys: string[];
24
29
  failures: CloudflareSecretSyncFailure[];
25
30
  selectedKeys: string[];
26
31
  };
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>;
32
+ export declare const reportCloudflareSecretSync: (log: CloudflareSecretLog, result: Pick<CloudflareSecretSyncResult, "pushed" | "skippedEmptyKeys" | "failures">) => void;
33
+ export declare const syncCloudflareSecrets: ({ log, cwd, wranglerEnvs, envPath, dryRun, configGroups, directKeys, inlineValues, configPath, target, bulk, requireSelection, }: CloudflareSecretSyncArgs) => Promise<CloudflareSecretSyncResult>;
29
34
  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;
35
+ syncCloudflareSecrets: ({ log, cwd, wranglerEnvs, envPath, dryRun, configGroups, directKeys, inlineValues, configPath, target, bulk, requireSelection, }: CloudflareSecretSyncArgs) => Promise<CloudflareSecretSyncResult>;
36
+ reportCloudflareSecretSync: (log: CloudflareSecretLog, result: Pick<CloudflareSecretSyncResult, "pushed" | "skippedEmptyKeys" | "failures">) => void;
32
37
  }>;
33
38
  export default _default;
34
39
  //# sourceMappingURL=CloudflareSecretSync.d.ts.map
@@ -1 +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,IAcF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAU,kGAUzC,wBAAwB,KAAG,OAAO,CAAC,0BAA0B,CAqC/D,CAAC;;8HArCC,wBAAwB,KAAG,OAAO,CAAC,0BAA0B,CAAC;sCA5B1D,mBAAmB,UAChB,IAAI,CAAC,0BAA0B,EAAE,QAAQ,GAAG,UAAU,CAAC,KAC9D,IAAI;;AAiEP,wBAGG"}
1
+ {"version":3,"file":"CloudflareSecretSync.d.ts","sourceRoot":"","sources":["../../../../src/cli/cloudflare/CloudflareSecretSync.ts"],"names":[],"mappings":"AAaA,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;AAcF,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,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,QAAQ,EAAE,2BAA2B,EAAE,CAAC;IACxC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AAuTF,eAAO,MAAM,0BAA0B,GACrC,KAAK,mBAAmB,EACxB,QAAQ,IAAI,CAAC,0BAA0B,EAAE,QAAQ,GAAG,kBAAkB,GAAG,UAAU,CAAC,KACnF,IAcF,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAU,kIAazC,wBAAwB,KAAG,OAAO,CAAC,0BAA0B,CAiD/D,CAAC;;8JAjDC,wBAAwB,KAAG,OAAO,CAAC,0BAA0B,CAAC;sCA/B1D,mBAAmB,UAChB,IAAI,CAAC,0BAA0B,EAAE,QAAQ,GAAG,kBAAkB,GAAG,UAAU,CAAC,KACnF,IAAI;;AAgFP,wBAGG"}
@@ -3,7 +3,8 @@ import { resolveNpmPath } from '../../common/index.js';
3
3
  import { appConfig } from '../../config/app.js';
4
4
  import { ErrorFactory } from '../../exceptions/ZintrustError.js';
5
5
  import { execFileSync } from '../../node-singletons/child-process.js';
6
- import { existsSync } from '../../node-singletons/fs.js';
6
+ import { existsSync, mkdtempSync, rmSync, writeFileSync } from '../../node-singletons/fs.js';
7
+ import { tmpdir } from '../../node-singletons/os.js';
7
8
  import * as path from '../../node-singletons/path.js';
8
9
  import { EnvFile } from '../../toolkit/Secrets/EnvFile.js';
9
10
  const uniq = (items) => {
@@ -29,6 +30,12 @@ const resolveValue = (key, envMap) => {
29
30
  const fromProcess = process.env[key];
30
31
  return fromFile ?? fromProcess ?? '';
31
32
  };
33
+ const resolveValueWithOverrides = (key, envMap, inlineValues) => {
34
+ const inlineValue = inlineValues[key];
35
+ if (typeof inlineValue === 'string')
36
+ return inlineValue;
37
+ return resolveValue(key, envMap);
38
+ };
32
39
  const getPutTimeoutMs = () => {
33
40
  const raw = process.env['ZT_PUT_TIMEOUT_MS'];
34
41
  if (typeof raw !== 'string')
@@ -38,13 +45,20 @@ const getPutTimeoutMs = () => {
38
45
  return 120000;
39
46
  return parsed;
40
47
  };
48
+ const describeWranglerEnv = (wranglerEnv) => wranglerEnv.trim() === '' ? 'top-level worker' : wranglerEnv;
41
49
  const putSecret = (wranglerEnv, key, value, configPath) => {
42
50
  const npmPath = resolveNpmPath();
43
51
  const args = ['exec', '--yes', '--', 'wrangler'];
44
52
  if (typeof configPath === 'string' && configPath.trim() !== '') {
45
53
  args.push('--config', configPath.trim());
46
54
  }
47
- args.push('secret', 'put', key, '--env', wranglerEnv);
55
+ args.push('secret', 'put', key);
56
+ if (wranglerEnv.trim() === '') {
57
+ args.push('--env=');
58
+ }
59
+ else {
60
+ args.push('--env', wranglerEnv);
61
+ }
48
62
  execFileSync(npmPath, args, {
49
63
  stdio: ['pipe', 'inherit', 'inherit'],
50
64
  input: value,
@@ -54,8 +68,33 @@ const putSecret = (wranglerEnv, key, value, configPath) => {
54
68
  env: appConfig.getSafeEnv(),
55
69
  });
56
70
  };
71
+ const putSecretBulk = (wranglerEnv, payloadPath, configPath) => {
72
+ const npmPath = resolveNpmPath();
73
+ const args = ['exec', '--yes', '--', 'wrangler'];
74
+ if (typeof configPath === 'string' && configPath.trim() !== '') {
75
+ args.push('--config', configPath.trim());
76
+ }
77
+ args.push('secret', 'bulk', payloadPath);
78
+ if (wranglerEnv.trim() === '') {
79
+ args.push('--env=');
80
+ }
81
+ else {
82
+ args.push('--env', wranglerEnv);
83
+ }
84
+ execFileSync(npmPath, args, {
85
+ stdio: ['ignore', 'inherit', 'inherit'],
86
+ encoding: 'utf8',
87
+ timeout: getPutTimeoutMs(),
88
+ killSignal: 'SIGTERM',
89
+ env: appConfig.getSafeEnv(),
90
+ });
91
+ };
57
92
  const getFailureReason = (error) => error instanceof Error ? error.message : String(error);
58
- const resolveSelectedKeys = ({ log, config, cwd, wranglerEnvs, configGroups = [], configPath, target, requireSelection, }) => {
93
+ const resolveSelectedKeys = ({ log, config, cwd, wranglerEnvs, configGroups = [], directKeys = [], configPath, target, requireSelection, }) => {
94
+ const selectedDirectKeys = uniq(directKeys);
95
+ if (selectedDirectKeys.length > 0) {
96
+ return selectedDirectKeys;
97
+ }
59
98
  const explicitKeys = uniq(configGroups.flatMap((groupKey) => {
60
99
  const keys = getConfigArray(config, groupKey);
61
100
  if (keys.length === 0) {
@@ -76,46 +115,109 @@ const resolveSelectedKeys = ({ log, config, cwd, wranglerEnvs, configGroups = []
76
115
  return selectedKeys;
77
116
  }
78
117
  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.'
118
+ ? 'No secret keys resolved from explicit keys or .zintrust.json cloudflare.shared_env/cloudflare.targets/cloudflare.wrangler_envs. Use --key/--keys, --var <group>, or add a Cloudflare env manifest.'
80
119
  : 'No secret keys resolved from selected groups.');
81
120
  };
82
- const processSecretSync = (log, wranglerEnvs, selectedKeys, envMap, dryRun, configPath) => {
121
+ const resolveBulkPayload = (log, wranglerEnv, selectedKeys, envMap, inlineValues) => {
122
+ const payload = {};
123
+ const includedKeys = [];
124
+ const skippedEmptyKeys = [];
125
+ const wranglerEnvLabel = describeWranglerEnv(wranglerEnv);
126
+ for (const key of selectedKeys) {
127
+ const value = resolveValueWithOverrides(key, envMap, inlineValues);
128
+ if (value.trim() === '') {
129
+ log.warn(`skip ${key} -> ${wranglerEnvLabel}: empty value`);
130
+ skippedEmptyKeys.push(key);
131
+ continue;
132
+ }
133
+ payload[key] = value;
134
+ includedKeys.push(key);
135
+ }
136
+ return { payload, includedKeys, skippedEmptyKeys };
137
+ };
138
+ const processSecretSync = (log, wranglerEnvs, selectedKeys, envMap, dryRun, configPath, inlineValues) => {
83
139
  let pushed = 0;
140
+ const pushedKeys = [];
141
+ const skippedEmptyKeys = [];
84
142
  const failures = [];
85
143
  for (const wranglerEnv of wranglerEnvs) {
144
+ const wranglerEnvLabel = describeWranglerEnv(wranglerEnv);
86
145
  for (const key of selectedKeys) {
87
- const value = resolveValue(key, envMap);
146
+ const value = resolveValueWithOverrides(key, envMap, inlineValues);
88
147
  if (value.trim() === '') {
89
- failures.push({ wranglerEnv, key, reason: 'empty value' });
148
+ log.warn(`skip ${key} -> ${wranglerEnvLabel}: empty value`);
149
+ skippedEmptyKeys.push(key);
90
150
  continue;
91
151
  }
92
152
  try {
93
153
  if (!dryRun) {
94
- log.info(`putting ${key} -> ${wranglerEnv}...`);
154
+ log.info(`putting ${key} -> ${wranglerEnvLabel}...`);
95
155
  putSecret(wranglerEnv, key, value, configPath);
96
156
  }
97
157
  pushed += 1;
98
- log.info(`${dryRun ? '[dry-run] ' : ''}put ${key} -> ${wranglerEnv}`);
158
+ pushedKeys.push(key);
159
+ log.info(`${dryRun ? '[dry-run] ' : ''}put ${key} -> ${wranglerEnvLabel}`);
99
160
  }
100
161
  catch (error) {
101
162
  failures.push({ wranglerEnv, key, reason: getFailureReason(error) });
102
163
  }
103
164
  }
104
165
  }
105
- return { pushed, failures };
166
+ return { pushed, pushedKeys, skippedEmptyKeys, failures };
167
+ };
168
+ const processSecretBulkSync = (log, wranglerEnvs, selectedKeys, envMap, dryRun, configPath, inlineValues) => {
169
+ let pushed = 0;
170
+ const pushedKeys = [];
171
+ const skippedEmptyKeys = [];
172
+ const failures = [];
173
+ for (const wranglerEnv of wranglerEnvs) {
174
+ const wranglerEnvLabel = describeWranglerEnv(wranglerEnv);
175
+ const { payload, includedKeys, skippedEmptyKeys: skippedForEnv, } = resolveBulkPayload(log, wranglerEnv, selectedKeys, envMap, inlineValues);
176
+ skippedEmptyKeys.push(...skippedForEnv);
177
+ if (includedKeys.length === 0) {
178
+ log.info(`skip bulk upload -> ${wranglerEnvLabel}: no non-empty keys`);
179
+ continue;
180
+ }
181
+ log.info(`${dryRun ? '[dry-run] ' : ''}bulk keys -> ${wranglerEnvLabel}: ${includedKeys.join(', ')}`);
182
+ if (dryRun) {
183
+ pushed += includedKeys.length;
184
+ pushedKeys.push(...includedKeys);
185
+ continue;
186
+ }
187
+ const tempDir = mkdtempSync(path.join(tmpdir(), 'zintrust-cloudflare-secret-bulk-'));
188
+ const payloadPath = path.join(tempDir, 'secrets.json');
189
+ try {
190
+ writeFileSync(payloadPath, JSON.stringify(payload, null, 2), 'utf8');
191
+ log.info(`bulk uploading ${includedKeys.length} key(s) -> ${wranglerEnvLabel}...`);
192
+ putSecretBulk(wranglerEnv, payloadPath, configPath);
193
+ pushed += includedKeys.length;
194
+ pushedKeys.push(...includedKeys);
195
+ log.info(`bulk put ${includedKeys.length} key(s) -> ${wranglerEnvLabel}`);
196
+ }
197
+ catch (error) {
198
+ const reason = getFailureReason(error);
199
+ for (const key of includedKeys) {
200
+ failures.push({ wranglerEnv, key, reason });
201
+ }
202
+ }
203
+ finally {
204
+ rmSync(tempDir, { recursive: true, force: true });
205
+ }
206
+ }
207
+ return { pushed, pushedKeys, skippedEmptyKeys, failures };
106
208
  };
107
209
  export const reportCloudflareSecretSync = (log, result) => {
108
210
  if (typeof log.success === 'function') {
109
- log.success(`Cloudflare secrets report: pushed=${result.pushed}, failed=${result.failures.length}`);
211
+ log.success(`Cloudflare secrets report: pushed=${result.pushed}, skipped_empty=${result.skippedEmptyKeys.length}, failed=${result.failures.length}`);
110
212
  }
111
213
  else {
112
- log.info(`Cloudflare secrets report: pushed=${result.pushed}, failed=${result.failures.length}`);
214
+ log.info(`Cloudflare secrets report: pushed=${result.pushed}, skipped_empty=${result.skippedEmptyKeys.length}, failed=${result.failures.length}`);
113
215
  }
114
216
  for (const item of result.failures) {
115
- log.warn(`${item.key} -> ${item.wranglerEnv}: ${item.reason}`);
217
+ log.warn(`${item.key} -> ${describeWranglerEnv(item.wranglerEnv)}: ${item.reason}`);
116
218
  }
117
219
  };
118
- export const syncCloudflareSecrets = async ({ log, cwd, wranglerEnvs, envPath, dryRun = false, configGroups = [], configPath, target, requireSelection = true, }) => {
220
+ export const syncCloudflareSecrets = async ({ log, cwd, wranglerEnvs, envPath, dryRun = false, configGroups = [], directKeys = [], inlineValues = {}, configPath, target, bulk = false, requireSelection = true, }) => {
119
221
  const normalizedConfigPath = typeof configPath === 'string' && configPath.trim() !== '' ? configPath.trim() : undefined;
120
222
  if (normalizedConfigPath !== undefined && !existsSync(path.join(cwd, normalizedConfigPath))) {
121
223
  throw ErrorFactory.createCliError(`Wrangler config not found: ${normalizedConfigPath}`);
@@ -127,15 +229,18 @@ export const syncCloudflareSecrets = async ({ log, cwd, wranglerEnvs, envPath, d
127
229
  cwd,
128
230
  wranglerEnvs,
129
231
  configGroups,
232
+ directKeys,
130
233
  configPath: normalizedConfigPath,
131
234
  target,
132
235
  requireSelection,
133
236
  });
134
237
  if (selectedKeys.length === 0) {
135
- return { pushed: 0, failures: [], selectedKeys: [] };
238
+ return { pushed: 0, pushedKeys: [], skippedEmptyKeys: [], failures: [], selectedKeys: [] };
136
239
  }
137
240
  const envMap = await EnvFile.read({ cwd, path: envPath });
138
- const syncResult = processSecretSync(log, wranglerEnvs, selectedKeys, envMap, dryRun, normalizedConfigPath);
241
+ const syncResult = bulk
242
+ ? processSecretBulkSync(log, wranglerEnvs, selectedKeys, envMap, dryRun, normalizedConfigPath, inlineValues)
243
+ : processSecretSync(log, wranglerEnvs, selectedKeys, envMap, dryRun, normalizedConfigPath, inlineValues);
139
244
  return {
140
245
  ...syncResult,
141
246
  selectedKeys,
@@ -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;AAqFvF,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;AAwHvF,eAAO,MAAM,UAAU;cACX,YAAY;EAWtB,CAAC;AAEH,eAAe,UAAU,CAAC"}
@@ -23,10 +23,25 @@ const uniq = (items) => {
23
23
  const resolveConfigGroups = (options) => {
24
24
  return uniq(toStringArray(options.var));
25
25
  };
26
+ const resolveDirectKeys = (options) => {
27
+ return uniq([...toStringArray(options.key), ...toStringArray(options.keys)]);
28
+ };
29
+ const resolveInlineValues = (options) => {
30
+ if (typeof options.value !== 'string')
31
+ return {};
32
+ const directKeys = resolveDirectKeys(options);
33
+ if (directKeys.length === 0) {
34
+ throw ErrorFactory.createCliError('`--value` requires `--key` or `--keys`.');
35
+ }
36
+ if (directKeys.length !== 1) {
37
+ throw ErrorFactory.createCliError('`--value` supports exactly one selected key.');
38
+ }
39
+ return { [directKeys[0]]: options.value };
40
+ };
26
41
  const resolveWranglerEnvs = (options) => {
27
42
  const requested = uniq(toStringArray(options.wg));
28
43
  if (requested.length === 0)
29
- return ['worker'];
44
+ return [''];
30
45
  return requested;
31
46
  };
32
47
  const parseEnvPath = (options) => {
@@ -40,9 +55,13 @@ const addOptions = (command) => {
40
55
  .argument('[provider]', 'Secret provider (cloudflare)', 'cloudflare')
41
56
  .option('--wg <env...>', 'Wrangler environment target(s), e.g. d1-proxy kv-proxy')
42
57
  .option('--var <configKey...>', 'Config array key(s) from .zintrust.json (e.g. d1_env kv_env)')
58
+ .option('--key <name...>', 'Upload selected secret key(s) directly without group expansion')
59
+ .option('--keys <name...>', 'Upload selected secret key(s) from env source without group expansion')
60
+ .option('--value <value>', 'Inline value for a single `--key` upload')
43
61
  .option('--target <id>', 'Cloudflare worker target key from .zintrust.json cloudflare.targets')
44
62
  .option('--env_path <path>', 'Path to env file used as source values', '.env')
45
63
  .option('-c, --config <path>', 'Wrangler config file to target (optional)')
64
+ .option('--bulk', 'Upload the final key set with one wrangler secret bulk call per target')
46
65
  .option('--dry-run', 'Show what would be uploaded without calling wrangler');
47
66
  };
48
67
  const ensureCloudflareProvider = (providerRaw) => {
@@ -53,6 +72,8 @@ const ensureCloudflareProvider = (providerRaw) => {
53
72
  const execute = async (cmd, options) => {
54
73
  ensureCloudflareProvider(String(options.args?.[0] ?? 'cloudflare'));
55
74
  const cwd = process.cwd();
75
+ const directKeys = resolveDirectKeys(options);
76
+ const inlineValues = resolveInlineValues(options);
56
77
  const result = await syncCloudflareSecrets({
57
78
  log: cmd,
58
79
  cwd,
@@ -60,8 +81,11 @@ const execute = async (cmd, options) => {
60
81
  envPath: parseEnvPath(options),
61
82
  dryRun: options.dryRun === true,
62
83
  configGroups: resolveConfigGroups(options),
84
+ directKeys,
85
+ inlineValues,
63
86
  configPath: typeof options.config === 'string' ? options.config.trim() : undefined,
64
87
  target: typeof options.target === 'string' ? options.target : undefined,
88
+ bulk: options.bulk === true,
65
89
  requireSelection: true,
66
90
  });
67
91
  reportCloudflareSecretSync(cmd, result);
@@ -1 +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;AAkBrE,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,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,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;AAMF,eAAO,MAAM,0BAA0B,GAAI,OAAO,EAAE,QAAQ,SAAS,2BAA2B,EAC9F,OAAO,+BAA+B,CAAC,OAAO,EAAE,QAAQ,CAAC,KACxD,YAwFF,CAAC"}
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;AAmBrE,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,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,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;AA+BF,eAAO,MAAM,0BAA0B,GAAI,OAAO,EAAE,QAAQ,SAAS,2BAA2B,EAC9F,OAAO,+BAA+B,CAAC,OAAO,EAAE,QAAQ,CAAC,KACxD,YA0FF,CAAC"}
@@ -4,6 +4,7 @@ import { ensureProxyEnvLoadedForCwd, maybeRunProxyWatchMode, parseIntOption, } f
4
4
  import { ensureProxyEntrypoint, ensureWranglerConfig, renderProxyWranglerDevConfig, resolveConfigPath, } from '../commands/ProxyScaffoldUtils.js';
5
5
  import { SpawnUtil } from '../utils/spawn.js';
6
6
  import { Logger } from '../../config/logger.js';
7
+ import { isNonEmptyString } from '../../helper/index.js';
7
8
  import { mkdirSync, writeFileSync } from '../../node-singletons/fs.js';
8
9
  import { dirname, join } from '../../node-singletons/path.js';
9
10
  export const addWranglerProxyBaseOptions = (command, defaultConfig) => {
@@ -14,6 +15,24 @@ export const addWranglerProxyBaseOptions = (command, defaultConfig) => {
14
15
  const toRootedProxyConfigContent = (content) => {
15
16
  return content.replaceAll('": "../../', '": "./');
16
17
  };
18
+ const isTruthyFlag = (value) => {
19
+ if (!isNonEmptyString(value))
20
+ return false;
21
+ const normalized = value.trim().toLowerCase();
22
+ return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
23
+ };
24
+ const createWranglerDevSpawnEnv = (runtimeEnv) => {
25
+ if (isTruthyFlag(runtimeEnv['ZIN_WRANGLER_DEV_KEEP_API_TOKEN'])) {
26
+ return runtimeEnv;
27
+ }
28
+ if (!isNonEmptyString(runtimeEnv['CLOUDFLARE_API_TOKEN'])) {
29
+ return runtimeEnv;
30
+ }
31
+ Logger.warn('Ignoring CLOUDFLARE_API_TOKEN for local wrangler dev. Wrangler 4.92+ blocks interactive OAuth login when that variable is exported. Set ZIN_WRANGLER_DEV_KEEP_API_TOKEN=true to preserve token-based auth.');
32
+ const env = { ...runtimeEnv };
33
+ delete env['CLOUDFLARE_API_TOKEN'];
34
+ return env;
35
+ };
17
36
  export const createWranglerProxyCommand = (input) => {
18
37
  return BaseCommand.create({
19
38
  name: input.name,
@@ -66,6 +85,7 @@ export const createWranglerProxyCommand = (input) => {
66
85
  if (port !== undefined) {
67
86
  args.push('--port', String(port));
68
87
  }
88
+ const wranglerSpawnEnv = createWranglerDevSpawnEnv(process.env);
69
89
  const exitCode = await withWranglerDevVarsSnapshot({
70
90
  cwd: wranglerDevVarsCwd,
71
91
  projectRoot,
@@ -76,7 +96,7 @@ export const createWranglerProxyCommand = (input) => {
76
96
  return SpawnUtil.spawnAndWait({
77
97
  command: 'wrangler',
78
98
  args,
79
- env: process.env,
99
+ env: wranglerSpawnEnv,
80
100
  forwardSignals: false,
81
101
  });
82
102
  });
package/src/index.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * @zintrust/core v1.8.5
2
+ * @zintrust/core v1.8.6
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-05-15T16:02:47.614Z
8
+ * Built: 2026-05-19T17:44:54.279Z
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-05-15T16:02:47.579Z'; // Replaced during build
24
+ export const ZINTRUST_BUILD_DATE = '2026-05-19T17:44:54.246Z'; // 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';
@@ -1 +1 @@
1
- {"version":3,"file":"SmtpProxyServer.d.ts","sourceRoot":"","sources":["../../../../src/proxy/smtp/SmtpProxyServer.ts"],"names":[],"mappings":"AA6BA,KAAK,cAAc,GAAG,OAAO,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC,CAAC;AA6UH,eAAO,MAAM,eAAe;sBACH,cAAc,GAAQ,OAAO,CAAC,IAAI,CAAC;EA2B1D,CAAC;AAEH,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"SmtpProxyServer.d.ts","sourceRoot":"","sources":["../../../../src/proxy/smtp/SmtpProxyServer.ts"],"names":[],"mappings":"AAyBA,KAAK,cAAc,GAAG,OAAO,CAAC;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,GAAG,MAAM,CAAC;IAC7B,cAAc,EAAE,OAAO,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC,CAAC;AA6UH,eAAO,MAAM,eAAe;sBACH,cAAc,GAAQ,OAAO,CAAC,IAAI,CAAC;EA2B1D,CAAC;AAEH,eAAe,eAAe,CAAC"}
@@ -3,7 +3,7 @@ import { Env } from '../../config/env.js';
3
3
  import { Logger } from '../../config/logger.js';
4
4
  import { ErrorFactory } from '../../exceptions/ZintrustError.js';
5
5
  import { isNonEmptyString, isObject } from '../../helper/index.js';
6
- import { SmtpDriver, } from '../../tools/mail/drivers/Smtp.js';
6
+ import { SmtpDriver } from '../../tools/mail/drivers/Smtp.js';
7
7
  import { ErrorHandler } from '../ErrorHandler.js';
8
8
  import { createProxyServer } from '../ProxyServer.js';
9
9
  import { resolveProxySigningConfig } from '../ProxySigningConfigResolver.js';