@zintrust/core 1.8.4 → 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.
Files changed (43) hide show
  1. package/package.json +5 -5
  2. package/src/cli/CLI.d.ts.map +1 -1
  3. package/src/cli/CLI.js +2 -0
  4. package/src/cli/cloudflare/CloudflareSecretSync.d.ts +9 -4
  5. package/src/cli/cloudflare/CloudflareSecretSync.d.ts.map +1 -1
  6. package/src/cli/cloudflare/CloudflareSecretSync.js +121 -16
  7. package/src/cli/commands/EmailProxyCommand.d.ts +6 -0
  8. package/src/cli/commands/EmailProxyCommand.d.ts.map +1 -0
  9. package/src/cli/commands/EmailProxyCommand.js +108 -0
  10. package/src/cli/commands/ProxyCommand.d.ts +1 -0
  11. package/src/cli/commands/ProxyCommand.d.ts.map +1 -1
  12. package/src/cli/commands/ProxyCommand.js +6 -0
  13. package/src/cli/commands/PutCommand.d.ts.map +1 -1
  14. package/src/cli/commands/PutCommand.js +25 -1
  15. package/src/cli/commands/WranglerProxyCommandUtils.d.ts +1 -0
  16. package/src/cli/commands/WranglerProxyCommandUtils.d.ts.map +1 -1
  17. package/src/cli/commands/WranglerProxyCommandUtils.js +23 -2
  18. package/src/cli/scaffolding/env.d.ts.map +1 -1
  19. package/src/cli/scaffolding/env.js +4 -0
  20. package/src/config/env.d.ts +18 -0
  21. package/src/config/env.d.ts.map +1 -1
  22. package/src/config/env.js +19 -0
  23. package/src/index.js +3 -3
  24. package/src/proxy/email/ZintrustEmailProxy.d.ts +20 -0
  25. package/src/proxy/email/ZintrustEmailProxy.d.ts.map +1 -0
  26. package/src/proxy/email/ZintrustEmailProxy.js +129 -0
  27. package/src/proxy/email/register.d.ts +2 -0
  28. package/src/proxy/email/register.d.ts.map +1 -0
  29. package/src/proxy/email/register.js +5 -0
  30. package/src/proxy/smtp/SmtpProxyServer.d.ts.map +1 -1
  31. package/src/proxy/smtp/SmtpProxyServer.js +1 -1
  32. package/src/proxy.d.ts +1 -0
  33. package/src/proxy.d.ts.map +1 -1
  34. package/src/proxy.js +1 -0
  35. package/src/tools/mail/MailMessage.d.ts +18 -0
  36. package/src/tools/mail/MailMessage.d.ts.map +1 -0
  37. package/src/tools/mail/MailMessage.js +75 -0
  38. package/src/tools/mail/drivers/Cloudflare.d.ts +1 -1
  39. package/src/tools/mail/drivers/Cloudflare.d.ts.map +1 -1
  40. package/src/tools/mail/drivers/Cloudflare.js +61 -1
  41. package/src/tools/mail/drivers/Smtp.d.ts +2 -18
  42. package/src/tools/mail/drivers/Smtp.d.ts.map +1 -1
  43. package/src/tools/mail/drivers/Smtp.js +1 -65
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/core",
3
- "version": "1.8.4",
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",
@@ -1 +1 @@
1
- {"version":3,"file":"CLI.d.ts","sourceRoot":"","sources":["../../../src/cli/CLI.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA0EH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,MAAM,WAAW,IAAI;IACnB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,UAAU,IAAI,OAAO,CAAC;CACvB;AAqQD;;;;;;;GAOG;AACH,eAAO,MAAM,GAAG;cACJ,IAAI;EAed,CAAC"}
1
+ {"version":3,"file":"CLI.d.ts","sourceRoot":"","sources":["../../../src/cli/CLI.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA2EH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,MAAM,WAAW,IAAI;IACnB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,UAAU,IAAI,OAAO,CAAC;CACvB;AAsQD;;;;;;;GAOG;AACH,eAAO,MAAM,GAAG;cACJ,IAAI;EAed,CAAC"}
package/src/cli/CLI.js CHANGED
@@ -21,6 +21,7 @@ import { DeployContainerWorkersCommand } from './commands/DeployContainerWorkers
21
21
  import { DockerCommand } from './commands/DockerCommand.js';
22
22
  import { DockerPushCommand } from './commands/DockerPushCommand.js';
23
23
  import { DoctorArchitectureCommand } from './commands/DoctorArchitectureCommand.js';
24
+ import { EmailProxyCommand } from './commands/EmailProxyCommand.js';
24
25
  import { EnvKeyGenerateCommand } from './commands/EnvKeyGenerateCommand.js';
25
26
  import { FixCommand } from './commands/FixCommand.js';
26
27
  import { InitContainerCommand } from './commands/InitContainerCommand.js';
@@ -135,6 +136,7 @@ const buildCommandRegistry = () => {
135
136
  JwtDevCommand,
136
137
  ProxyCommand.create(),
137
138
  D1ProxyCommand.create(),
139
+ EmailProxyCommand.create(),
138
140
  KvProxyCommand.create(),
139
141
  MySqlProxyCommand.create(),
140
142
  PostgresProxyCommand.create(),
@@ -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,
@@ -0,0 +1,6 @@
1
+ import type { IBaseCommand } from '../BaseCommand';
2
+ export declare const EmailProxyCommand: Readonly<{
3
+ create(): IBaseCommand;
4
+ }>;
5
+ export default EmailProxyCommand;
6
+ //# sourceMappingURL=EmailProxyCommand.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailProxyCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/EmailProxyCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA4IrD,eAAO,MAAM,iBAAiB;cAClB,YAAY;EAmBtB,CAAC;AAEH,eAAe,iBAAiB,CAAC"}
@@ -0,0 +1,108 @@
1
+ import { findQuotedValue, trimNonEmptyOption } from '../commands/ProxyScaffoldUtils.js';
2
+ import { createWranglerProxyCommand, } from '../commands/WranglerProxyCommandUtils.js';
3
+ import { Env } from '../../config/env.js';
4
+ import { Logger } from '../../config/logger.js';
5
+ const DEFAULT_CONFIG = 'wrangler.jsonc';
6
+ const DEFAULT_COMPATIBILITY_DATE = '2026-03-12';
7
+ const DEFAULT_BINDING = 'SEND_EMAIL';
8
+ const DEFAULT_ENTRY_FILE = 'src/proxy/email/ZintrustEmailProxy.ts';
9
+ const DEFAULT_ROUTE_PATTERN = 'email-proxy.example.com';
10
+ const CORE_PROXY_MODULE = ['@zintrust', 'core', 'proxy'].join('/');
11
+ const parseCsv = (value) => {
12
+ const parsed = (value ?? '')
13
+ .split(',')
14
+ .map((item) => item.trim())
15
+ .filter((item, index, items) => item !== '' && items.indexOf(item) === index);
16
+ return parsed.length === 0 ? undefined : parsed;
17
+ };
18
+ const renderArray = (values) => {
19
+ return `[${values.map((value) => JSON.stringify(value)).join(', ')}]`;
20
+ };
21
+ const resolveConfigValues = (content, options) => {
22
+ const fileContent = content ?? '';
23
+ return {
24
+ binding: trimNonEmptyOption(options.binding) ??
25
+ trimNonEmptyOption(Env.get('MAIL_CLOUDFLARE_BINDING', '')) ??
26
+ findQuotedValue(fileContent, 'MAIL_CLOUDFLARE_BINDING') ??
27
+ findQuotedValue(fileContent, 'name') ??
28
+ DEFAULT_BINDING,
29
+ destinationAddress: trimNonEmptyOption(options.destinationAddress) ??
30
+ trimNonEmptyOption(findQuotedValue(fileContent, 'destination_address')),
31
+ allowedDestinationAddresses: parseCsv(options.allowedDestinationAddresses) ??
32
+ parseCsv(Env.get('MAIL_CLOUDFLARE_ALLOWED_DESTINATION_ADDRESSES', '')),
33
+ allowedSenderAddresses: parseCsv(options.allowedSenderAddresses) ??
34
+ parseCsv(Env.get('MAIL_CLOUDFLARE_ALLOWED_SENDER_ADDRESSES', '')),
35
+ };
36
+ };
37
+ const renderSendEmailBinding = (values) => {
38
+ const lines = [' {', ` "name": "${values.binding}"`];
39
+ if (values.destinationAddress !== undefined) {
40
+ lines.push(` ,"destination_address": "${values.destinationAddress}"`);
41
+ }
42
+ if (values.allowedDestinationAddresses !== undefined) {
43
+ lines.push(` ,"allowed_destination_addresses": ${renderArray(values.allowedDestinationAddresses)}`);
44
+ }
45
+ if (values.allowedSenderAddresses !== undefined) {
46
+ lines.push(` ,"allowed_sender_addresses": ${renderArray(values.allowedSenderAddresses)}`);
47
+ }
48
+ lines.push(' }');
49
+ return lines;
50
+ };
51
+ const renderEmailProxyEnvBlock = (values) => {
52
+ return [
53
+ ' "email-proxy": {',
54
+ ' "name": "zintrust-email-proxy",',
55
+ ' "main": "./src/proxy/email/ZintrustEmailProxy.ts",',
56
+ ' "compatibility_flags": ["nodejs_compat"],',
57
+ ` "compatibility_date": "${DEFAULT_COMPATIBILITY_DATE}",`,
58
+ ' "vars": {',
59
+ ` "MAIL_CLOUDFLARE_BINDING": "${values.binding}",`,
60
+ ' "ZT_PROXY_SIGNING_WINDOW_MS": "60000",',
61
+ ' "ZT_MAX_BODY_BYTES": "131072",',
62
+ ' "NODE_ENV": "development"',
63
+ ' },',
64
+ ' "send_email": [',
65
+ ...renderSendEmailBinding(values),
66
+ ' ],',
67
+ ' // Add routes here when ready:',
68
+ ` // "routes": [{ "pattern": "${DEFAULT_ROUTE_PATTERN}", "custom_domain": true }]`,
69
+ ' }',
70
+ ].join('\n');
71
+ };
72
+ const warnOnMissingSigningSecret = () => {
73
+ const directSecret = trimNonEmptyOption(Env.get('MAIL_CLOUDFLARE_PROXY_SECRET', ''));
74
+ const fallbackSecret = trimNonEmptyOption(Env.get('APP_KEY', ''));
75
+ if (directSecret !== undefined || fallbackSecret !== undefined)
76
+ return;
77
+ Logger.warn('Email proxy signing will fail: the resolved project env does not expose MAIL_CLOUDFLARE_PROXY_SECRET or APP_KEY to the Worker runtime. Signed requests will be rejected with 401 CONFIG_ERROR until one of those keys is set.');
78
+ };
79
+ const addOptions = (command) => {
80
+ command.option('-c, --config <path>', 'Wrangler config file', DEFAULT_CONFIG);
81
+ command.option('--port <port>', 'Local Wrangler dev port', '5777');
82
+ command.option('--watch', 'Auto-restart proxy on file changes');
83
+ command.option('--binding <name>', 'send_email binding name', DEFAULT_BINDING);
84
+ command.option('--destination-address <email>', 'Restrict the binding to one destination');
85
+ command.option('--allowed-destination-addresses <emails>', 'Comma-separated allowlist of destination addresses');
86
+ command.option('--allowed-sender-addresses <emails>', 'Comma-separated allowlist of sender addresses');
87
+ };
88
+ export const EmailProxyCommand = Object.freeze({
89
+ create() {
90
+ return createWranglerProxyCommand({
91
+ name: 'proxy:email',
92
+ aliases: ['email:proxy', 'proxy:cl:mail', 'proxy:cloudflare:mail'],
93
+ description: 'Start the local Cloudflare email proxy Worker via Wrangler and scaffold env.email-proxy in wrangler.jsonc when missing',
94
+ envName: 'email-proxy',
95
+ defaultPort: 5777,
96
+ defaultConfig: DEFAULT_CONFIG,
97
+ compatibilityDate: DEFAULT_COMPATIBILITY_DATE,
98
+ entryFile: DEFAULT_ENTRY_FILE,
99
+ exportName: 'ZintrustEmailProxy',
100
+ moduleSpecifier: CORE_PROXY_MODULE,
101
+ addOptions,
102
+ resolveValues: resolveConfigValues,
103
+ renderEnvBlock: renderEmailProxyEnvBlock,
104
+ afterConfigResolved: warnOnMissingSigningSecret,
105
+ });
106
+ },
107
+ });
108
+ export default EmailProxyCommand;
@@ -1,5 +1,6 @@
1
1
  import type { IBaseCommand } from '../BaseCommand';
2
2
  import '../../proxy/d1/register';
3
+ import '../../proxy/email/register';
3
4
  import '../../proxy/kv/register';
4
5
  import '../../proxy/mysql/register';
5
6
  import '../../proxy/postgres/register';
@@ -1 +1 @@
1
- {"version":3,"file":"ProxyCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/ProxyCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAMrE,OAAO,oBAAoB,CAAC;AAC5B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,0BAA0B,CAAC;AAClC,OAAO,uBAAuB,CAAC;AAC/B,OAAO,sBAAsB,CAAC;AA2D9B,eAAO,MAAM,YAAY;cACb,YAAY;EAyBtB,CAAC;AAEH,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"ProxyCommand.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/ProxyCommand.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAMrE,OAAO,oBAAoB,CAAC;AAC5B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,oBAAoB,CAAC;AAC5B,OAAO,uBAAuB,CAAC;AAC/B,OAAO,0BAA0B,CAAC;AAClC,OAAO,uBAAuB,CAAC;AAC/B,OAAO,sBAAsB,CAAC;AAgE9B,eAAO,MAAM,YAAY;cACb,YAAY;EAyBtB,CAAC;AAEH,eAAe,YAAY,CAAC"}
@@ -4,6 +4,7 @@ import { ErrorFactory } from '../../exceptions/ZintrustError.js';
4
4
  import * as path from '../../node-singletons/path.js';
5
5
  import { ProxyRegistry } from '../../proxy/ProxyRegistry.js';
6
6
  import '../../proxy/d1/register.js';
7
+ import '../../proxy/email/register.js';
7
8
  import '../../proxy/kv/register.js';
8
9
  import '../../proxy/mysql/register.js';
9
10
  import '../../proxy/postgres/register.js';
@@ -11,6 +12,11 @@ import '../../proxy/redis/register.js';
11
12
  import '../../proxy/smtp/register.js';
12
13
  const PROXY_TARGET_MAP = Object.freeze({
13
14
  d1: 'proxy:d1',
15
+ email: 'proxy:email',
16
+ cl: 'proxy:email',
17
+ 'cl:mail': 'proxy:email',
18
+ 'cloudflare:mail': 'proxy:email',
19
+ 'cloudflare-mail': 'proxy:email',
14
20
  kv: 'proxy:kv',
15
21
  mysql: 'proxy:mysql',
16
22
  my: 'proxy:mysql',
@@ -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);
@@ -10,6 +10,7 @@ type CreateWranglerProxyCommandInput<TValues, TOptions extends WranglerProxyComm
10
10
  aliases: string[];
11
11
  description: string;
12
12
  envName: string;
13
+ defaultPort?: number;
13
14
  defaultConfig: string;
14
15
  compatibilityDate: string;
15
16
  entryFile: string;
@@ -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,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,YAoFF,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,
@@ -23,7 +42,8 @@ export const createWranglerProxyCommand = (input) => {
23
42
  execute: async (options) => {
24
43
  const typedOptions = options;
25
44
  await maybeRunProxyWatchMode(typedOptions.watch);
26
- const port = parseIntOption(typedOptions.port, 'port');
45
+ const port = parseIntOption(typedOptions.port ??
46
+ (input.defaultPort === undefined ? undefined : String(input.defaultPort)), 'port');
27
47
  const cwd = process.cwd();
28
48
  const projectRoot = ensureProxyEnvLoadedForCwd(cwd);
29
49
  const entrypoint = ensureProxyEntrypoint({
@@ -65,6 +85,7 @@ export const createWranglerProxyCommand = (input) => {
65
85
  if (port !== undefined) {
66
86
  args.push('--port', String(port));
67
87
  }
88
+ const wranglerSpawnEnv = createWranglerDevSpawnEnv(process.env);
68
89
  const exitCode = await withWranglerDevVarsSnapshot({
69
90
  cwd: wranglerDevVarsCwd,
70
91
  projectRoot,
@@ -75,7 +96,7 @@ export const createWranglerProxyCommand = (input) => {
75
96
  return SpawnUtil.spawnAndWait({
76
97
  command: 'wrangler',
77
98
  args,
78
- env: process.env,
99
+ env: wranglerSpawnEnv,
79
100
  forwardSignals: false,
80
101
  });
81
102
  });
@@ -1 +1 @@
1
- {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/env.ts"],"names":[],"mappings":"AAsiBA,eAAO,MAAM,OAAO,GAClB,MAAM,MAAM,EACZ,MAAM,MAAM,EACZ,SAAS,MAAM,EACf,QAAQ,MAAM,EACd,oBAAoB,MAAM,EAC1B,SAAS,MAAM,EAAE,EACjB,cAAc,MAAM,KACnB,MAAM,EAWR,CAAC"}
1
+ {"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../../../src/cli/scaffolding/env.ts"],"names":[],"mappings":"AA0iBA,eAAO,MAAM,OAAO,GAClB,MAAM,MAAM,EACZ,MAAM,MAAM,EACZ,SAAS,MAAM,EACf,QAAQ,MAAM,EACd,oBAAoB,MAAM,EAC1B,SAAS,MAAM,EAAE,EACjB,cAAc,MAAM,KACnB,MAAM,EAWR,CAAC"}
@@ -431,6 +431,10 @@ const LoggingAndMail = () => [
431
431
  'MAIL_FROM_ADDRESS=',
432
432
  'MAIL_FROM_NAME=',
433
433
  'MAIL_CLOUDFLARE_BINDING=SEND_EMAIL',
434
+ 'MAIL_CLOUDFLARE_PROXY_URL=',
435
+ 'MAIL_CLOUDFLARE_PROXY_KEY_ID=',
436
+ 'MAIL_CLOUDFLARE_PROXY_SECRET=',
437
+ 'MAIL_CLOUDFLARE_PROXY_TIMEOUT_MS=30000',
434
438
  'MAIL_HOST=',
435
439
  'MAIL_PORT=587',
436
440
  'MAIL_USERNAME=',