@astrale-os/adapter-cloudflare 0.5.0-beta.2 → 0.5.0-beta.21

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 (120) hide show
  1. package/README.md +11 -0
  2. package/dist/adapter.d.ts +17 -0
  3. package/dist/adapter.js +140 -0
  4. package/dist/build/artifact.d.ts +8 -0
  5. package/dist/build/artifact.js +179 -0
  6. package/dist/build/closure.d.ts +2 -0
  7. package/dist/build/closure.js +22 -0
  8. package/dist/build/codegen/execution.d.ts +13 -0
  9. package/dist/build/codegen/execution.js +44 -0
  10. package/dist/build/codegen/gateway.d.ts +1 -0
  11. package/dist/build/codegen/gateway.js +7 -0
  12. package/dist/build/codegen/merge.d.ts +0 -1
  13. package/dist/build/codegen/merge.js +0 -1
  14. package/dist/build/codegen/wrangler.d.ts +10 -1
  15. package/dist/build/codegen/wrangler.js +22 -16
  16. package/dist/build/configuration.d.ts +9 -0
  17. package/dist/build/configuration.js +40 -0
  18. package/dist/build/frontend-v1.d.ts +16 -0
  19. package/dist/build/frontend-v1.js +133 -0
  20. package/dist/build/index.d.ts +8 -16
  21. package/dist/build/index.js +8 -16
  22. package/dist/build/metafile.d.ts +5 -0
  23. package/dist/build/metafile.js +14 -0
  24. package/dist/build/parse-output.d.ts +0 -1
  25. package/dist/build/parse-output.js +0 -1
  26. package/dist/build/qualification.d.ts +5 -0
  27. package/dist/build/qualification.js +7 -0
  28. package/dist/build/roots.d.ts +2 -0
  29. package/dist/build/roots.js +34 -0
  30. package/dist/build/wrangler-cli.d.ts +53 -34
  31. package/dist/build/wrangler-cli.js +284 -111
  32. package/dist/cloudflare.d.ts +1 -34
  33. package/dist/cloudflare.js +1 -205
  34. package/dist/deploy/deploy.d.ts +17 -0
  35. package/dist/deploy/deploy.js +207 -0
  36. package/dist/deploy/index.d.ts +1 -0
  37. package/dist/deploy/index.js +1 -0
  38. package/dist/identity/binding.d.ts +2 -0
  39. package/dist/identity/binding.js +2 -0
  40. package/dist/identity/material.d.ts +7 -0
  41. package/dist/identity/material.js +42 -0
  42. package/dist/identity/secret.d.ts +3 -0
  43. package/dist/identity/secret.js +7 -0
  44. package/dist/identity/source.d.ts +26 -0
  45. package/dist/identity/source.js +98 -0
  46. package/dist/index.d.ts +3 -4
  47. package/dist/index.js +3 -4
  48. package/dist/params.d.ts +18 -5
  49. package/dist/params.js +0 -1
  50. package/dist/release/index.d.ts +2 -0
  51. package/dist/release/index.js +2 -0
  52. package/dist/release/namespace.d.ts +2 -0
  53. package/dist/release/namespace.js +6 -0
  54. package/dist/release/target.d.ts +2 -0
  55. package/dist/release/target.js +9 -0
  56. package/dist/worker/entry.d.ts +34 -0
  57. package/dist/worker/entry.js +157 -0
  58. package/dist/worker/fetch.d.ts +11 -0
  59. package/dist/worker/fetch.js +31 -0
  60. package/dist/worker/gateway.d.ts +15 -0
  61. package/dist/worker/gateway.js +26 -0
  62. package/dist/worker/identity.d.ts +7 -0
  63. package/dist/worker/identity.js +23 -0
  64. package/dist/worker/index.d.ts +3 -48
  65. package/dist/worker/index.js +3 -186
  66. package/package.json +21 -18
  67. package/template/astrale.config.ts +19 -11
  68. package/dist/build/codegen/identity.d.ts +0 -10
  69. package/dist/build/codegen/identity.d.ts.map +0 -1
  70. package/dist/build/codegen/identity.js +0 -32
  71. package/dist/build/codegen/identity.js.map +0 -1
  72. package/dist/build/codegen/merge.d.ts.map +0 -1
  73. package/dist/build/codegen/merge.js.map +0 -1
  74. package/dist/build/codegen/worker.d.ts +0 -12
  75. package/dist/build/codegen/worker.d.ts.map +0 -1
  76. package/dist/build/codegen/worker.js +0 -40
  77. package/dist/build/codegen/worker.js.map +0 -1
  78. package/dist/build/codegen/wrangler.d.ts.map +0 -1
  79. package/dist/build/codegen/wrangler.js.map +0 -1
  80. package/dist/build/frontend.d.ts +0 -26
  81. package/dist/build/frontend.d.ts.map +0 -1
  82. package/dist/build/frontend.js +0 -263
  83. package/dist/build/frontend.js.map +0 -1
  84. package/dist/build/index.d.ts.map +0 -1
  85. package/dist/build/index.js.map +0 -1
  86. package/dist/build/parse-output.d.ts.map +0 -1
  87. package/dist/build/parse-output.js.map +0 -1
  88. package/dist/build/prepare.d.ts +0 -18
  89. package/dist/build/prepare.d.ts.map +0 -1
  90. package/dist/build/prepare.js +0 -117
  91. package/dist/build/prepare.js.map +0 -1
  92. package/dist/build/worker.d.ts +0 -15
  93. package/dist/build/worker.d.ts.map +0 -1
  94. package/dist/build/worker.js +0 -21
  95. package/dist/build/worker.js.map +0 -1
  96. package/dist/build/wrangler-cli.d.ts.map +0 -1
  97. package/dist/build/wrangler-cli.js.map +0 -1
  98. package/dist/cloudflare.d.ts.map +0 -1
  99. package/dist/cloudflare.js.map +0 -1
  100. package/dist/index.d.ts.map +0 -1
  101. package/dist/index.js.map +0 -1
  102. package/dist/params.d.ts.map +0 -1
  103. package/dist/params.js.map +0 -1
  104. package/dist/worker/index.d.ts.map +0 -1
  105. package/dist/worker/index.js.map +0 -1
  106. package/src/build/codegen/identity.ts +0 -40
  107. package/src/build/codegen/merge.ts +0 -92
  108. package/src/build/codegen/worker.ts +0 -53
  109. package/src/build/codegen/wrangler.ts +0 -127
  110. package/src/build/frontend.ts +0 -324
  111. package/src/build/index.ts +0 -29
  112. package/src/build/parse-output.ts +0 -60
  113. package/src/build/prepare.ts +0 -161
  114. package/src/build/worker.ts +0 -37
  115. package/src/build/wrangler-cli.ts +0 -362
  116. package/src/cloudflare.ts +0 -251
  117. package/src/index.ts +0 -21
  118. package/src/params.ts +0 -92
  119. package/src/worker/index.ts +0 -276
  120. package/src/worker/tsconfig.json +0 -10
@@ -8,16 +8,32 @@
8
8
  */
9
9
  import { spawn } from 'node:child_process';
10
10
  import { existsSync, readdirSync } from 'node:fs';
11
+ import { readFileSync } from 'node:fs';
11
12
  import { mkdtemp, rm, writeFile } from 'node:fs/promises';
13
+ import { createRequire } from 'node:module';
12
14
  import { createServer } from 'node:net';
13
15
  import { tmpdir } from 'node:os';
14
- import { dirname, join } from 'node:path';
16
+ import { dirname, join, resolve } from 'node:path';
15
17
  import { parseDeployUrl, parseDeployVersionId, parseDevReadyUrl } from './parse-output.js';
16
18
  function wranglerCommand(projectDir) {
17
19
  const local = join(projectDir, 'node_modules', '.bin', 'wrangler');
18
20
  if (existsSync(local))
19
21
  return { cmd: local, prefix: [] };
20
- return { cmd: 'npx', prefix: ['--yes', 'wrangler'] };
22
+ const require = createRequire(import.meta.url);
23
+ const packagePath = require.resolve('wrangler/package.json');
24
+ const manifest = JSON.parse(readFileSync(packagePath, 'utf8'));
25
+ const executable = typeof manifest.bin === 'string' ? manifest.bin : manifest.bin?.wrangler;
26
+ if (!executable)
27
+ throw new TypeError('Installed Wrangler package has no wrangler executable.');
28
+ return { cmd: process.execPath, prefix: [resolve(dirname(packagePath), executable)] };
29
+ }
30
+ /** Keep Wrangler CLI diagnostic logs inside the generated project state. */
31
+ export function wranglerProcessEnv(projectDir) {
32
+ return {
33
+ ...process.env,
34
+ WRANGLER_SEND_METRICS: 'false',
35
+ WRANGLER_LOG_PATH: process.env.WRANGLER_LOG_PATH || resolve(projectDir, '.astrale', 'logs', 'wrangler'),
36
+ };
21
37
  }
22
38
  /**
23
39
  * True when `port` is free on `host`. A taken port previously made `wrangler
@@ -54,6 +70,7 @@ export async function runWranglerDev(args) {
54
70
  args.onLog?.(`port ${args.port} is taken — using ${picked} instead (pass --port to pin one)`);
55
71
  args = { ...args, port: picked };
56
72
  }
73
+ const secretEnvironment = await createSecretEnvironment(args.secrets);
57
74
  const wranglerArgs = [
58
75
  ...prefix,
59
76
  'dev',
@@ -65,13 +82,21 @@ export async function runWranglerDev(args) {
65
82
  'http',
66
83
  '--ip',
67
84
  args.ip ?? '127.0.0.1',
85
+ ...(secretEnvironment === undefined ? [] : ['--env-file', secretEnvironment.path]),
68
86
  ...(args.remote ? ['--remote'] : []),
69
87
  ];
70
- const child = spawn(cmd, wranglerArgs, {
71
- cwd: args.projectDir,
72
- stdio: ['ignore', 'pipe', 'pipe'],
73
- env: { ...process.env, WRANGLER_SEND_METRICS: 'false' },
74
- });
88
+ let child;
89
+ try {
90
+ child = spawn(cmd, wranglerArgs, {
91
+ cwd: args.projectDir,
92
+ stdio: ['ignore', 'pipe', 'pipe'],
93
+ env: wranglerProcessEnv(args.projectDir),
94
+ });
95
+ }
96
+ catch (cause) {
97
+ await secretEnvironment?.remove();
98
+ throw cause;
99
+ }
75
100
  const fallbackUrl = `http://localhost:${args.port}`;
76
101
  // (args.port may have been re-picked above — everything below reads args.port)
77
102
  let resolved = false;
@@ -80,135 +105,272 @@ export async function runWranglerDev(args) {
80
105
  // safety net can fail LOUD instead of resolving a dead URL.
81
106
  let buildErrorTail = '';
82
107
  let outputTail = '';
83
- const url = await new Promise((resolve, reject) => {
84
- const onData = (buf) => {
85
- const text = buf.toString();
86
- outputTail = (outputTail + text).slice(-3000);
87
- for (const line of text.split('\n')) {
88
- if (line.trim())
89
- args.onLog?.(line);
90
- }
91
- if (/✘ \[ERROR\]|Build failed|Could not resolve "/.test(text)) {
92
- buildErrorTail = (buildErrorTail + text).slice(-2000);
93
- }
94
- if (!resolved) {
95
- const ready = parseDevReadyUrl(text, args.port);
96
- if (ready) {
108
+ let url;
109
+ try {
110
+ url = await new Promise((resolve, reject) => {
111
+ const onData = (buf) => {
112
+ const text = buf.toString();
113
+ outputTail = (outputTail + text).slice(-3000);
114
+ for (const line of text.split('\n')) {
115
+ if (line.trim())
116
+ args.onLog?.(line);
117
+ }
118
+ if (/✘ \[ERROR\]|Build failed|Could not resolve "/.test(text)) {
119
+ buildErrorTail = (buildErrorTail + text).slice(-2000);
120
+ }
121
+ if (!resolved) {
122
+ const ready = parseDevReadyUrl(text, args.port);
123
+ if (ready) {
124
+ resolved = true;
125
+ resolve(ready);
126
+ }
127
+ }
128
+ else if (/Reloading|Detected changes|Updated/i.test(text)) {
129
+ args.onReload();
130
+ }
131
+ };
132
+ child.stdout?.on('data', onData);
133
+ child.stderr?.on('data', onData);
134
+ child.on('exit', (code) => {
135
+ if (!resolved) {
97
136
  resolved = true;
98
- resolve(ready);
137
+ reject(new Error(`wrangler dev exited before becoming ready (code ${code ?? 'null'}).\n${outputTail}`));
99
138
  }
100
- }
101
- else if (/Reloading|Detected changes|Updated/i.test(text)) {
102
- args.onReload();
103
- }
104
- };
105
- child.stdout?.on('data', onData);
106
- child.stderr?.on('data', onData);
107
- child.on('exit', (code) => {
108
- if (!resolved) {
109
- resolved = true;
110
- reject(new Error(`wrangler dev exited before becoming ready (code ${code ?? 'null'}).\n${outputTail}`));
111
- }
112
- });
113
- child.on('error', (err) => {
114
- if (!resolved) {
115
- resolved = true;
116
- reject(err);
117
- }
118
- });
119
- // Safety net: if we never matched a URL but the process is alive, assume the
120
- // conventional localhost URL after a grace period — UNLESS the build
121
- // failed, in which case a listener may be up but the worker will never
122
- // answer: fail loud with the build output instead of handing back a dead
123
- // URL.
124
- setTimeout(() => {
125
- if (!resolved && child.exitCode === null) {
126
- resolved = true;
127
- if (buildErrorTail) {
128
- void stopChild(child);
129
- reject(new Error(`worker build failed — the dev server would hang silently.\n${buildErrorTail}`));
139
+ });
140
+ child.on('error', (err) => {
141
+ if (!resolved) {
142
+ resolved = true;
143
+ reject(err);
130
144
  }
131
- else {
132
- resolve(fallbackUrl);
145
+ });
146
+ // Safety net: if we never matched a URL but the process is alive, assume the
147
+ // conventional localhost URL after a grace period — UNLESS the build
148
+ // failed, in which case a listener may be up but the worker will never
149
+ // answer: fail loud with the build output instead of handing back a dead
150
+ // URL.
151
+ setTimeout(() => {
152
+ if (!resolved && child.exitCode === null) {
153
+ resolved = true;
154
+ if (buildErrorTail) {
155
+ void stopChild(child);
156
+ reject(new Error(`worker build failed — the dev server would hang silently.\n${buildErrorTail}`));
157
+ }
158
+ else {
159
+ resolve(fallbackUrl);
160
+ }
133
161
  }
162
+ }, 15_000).unref?.();
163
+ });
164
+ }
165
+ catch (cause) {
166
+ await stopChild(child);
167
+ await secretEnvironment?.remove();
168
+ throw cause;
169
+ }
170
+ return {
171
+ url,
172
+ async stop() {
173
+ try {
174
+ await stopChild(child);
134
175
  }
135
- }, 15_000).unref?.();
136
- });
137
- return { url, stop: () => stopChild(child) };
176
+ finally {
177
+ await secretEnvironment?.remove();
178
+ }
179
+ },
180
+ };
181
+ }
182
+ export class WranglerDeployError extends Error {
183
+ applied;
184
+ constructor(message, applied) {
185
+ super(message);
186
+ this.name = 'WranglerDeployError';
187
+ if (applied !== undefined)
188
+ this.applied = Object.freeze({ ...applied });
189
+ }
138
190
  }
139
191
  export async function runWranglerDeploy(args) {
140
192
  await runWranglerTypes(args);
141
193
  const { cmd, prefix } = wranglerCommand(args.projectDir);
142
- const deploy = (configPath) => runCapture(cmd, [...prefix, 'deploy', '--config', configPath], args.projectDir, args.onLog);
143
- return deployWithSelfFallback(deploy, args);
144
- }
145
- /**
146
- * Deploy with a one-time SELF-binding first-deploy recovery. A fresh worker that
147
- * declares a `services: SELF` binding can't deploy (its own script doesn't exist
148
- * yet); on THAT specific failure we deploy the SELF-less fallback to create the
149
- * script, then redeploy WITH SELF so autobinding works. Steady state (every
150
- * deploy after the first) is a single pass. The `deploy` runner is injected so
151
- * the orchestration is unit-testable without shelling out.
152
- */
153
- export async function deployWithSelfFallback(deploy, args) {
154
- let { code, out } = await deploy(args.configPath);
155
- if (code !== 0 && args.fallbackConfigPath && isSelfBindingMissing(out, args.workerName)) {
156
- args.onLog?.('SELF service binding not resolvable yet (first deploy) — creating the worker, then re-binding SELF');
157
- const first = await deploy(args.fallbackConfigPath);
158
- if (first.code !== 0) {
159
- throw new Error(`wrangler deploy (no-self pass) failed (code ${first.code}):\n${first.out.slice(-2000)}`);
160
- }
161
- ;
162
- ({ code, out } = await deploy(args.configPath));
163
- }
194
+ const { code, out } = await runCapture(cmd, [...prefix, 'deploy', '--config', args.configPath], args.projectDir, args.onLog);
195
+ const url = parseDeployUrl(out, args.route);
196
+ const deploymentVersion = parseDeployVersionId(out);
164
197
  if (code !== 0) {
165
- throw new Error(`wrangler deploy failed (code ${code}):\n${out.slice(-2000)}`);
198
+ throw new WranglerDeployError(`wrangler deploy failed (code ${code}):\n${out.slice(-2000)}`, url === undefined && deploymentVersion === undefined
199
+ ? undefined
200
+ : {
201
+ ...(url === undefined ? {} : { url }),
202
+ ...(deploymentVersion === undefined ? {} : { deploymentVersion }),
203
+ });
166
204
  }
167
- const url = parseDeployUrl(out, args.route);
168
- if (!url) {
169
- throw new Error(`could not determine deployed URL from wrangler output:\n${out.slice(-2000)}`);
205
+ if (deploymentVersion === undefined) {
206
+ throw new WranglerDeployError('wrangler deploy did not report its Version ID.', url === undefined ? undefined : { url });
170
207
  }
171
- const deploymentVersion = parseDeployVersionId(out);
172
- return { url, raw: out, ...(deploymentVersion === undefined ? {} : { deploymentVersion }) };
208
+ return Object.freeze({
209
+ raw: out,
210
+ ...(url === undefined ? {} : { url }),
211
+ deploymentVersion,
212
+ });
173
213
  }
174
- /**
175
- * True when wrangler failed specifically because the worker's OWN `SELF` service
176
- * binding can't resolve yet (first deploy of a fresh script). Requires the
177
- * worker name so an unrelated missing-service error doesn't trigger the two-pass.
178
- */
179
- export function isSelfBindingMissing(out, workerName) {
180
- const bindingError = /could not resolve binding/i.test(out) ||
181
- /service\b[^\n]*\bnot found/i.test(out) ||
182
- /script\b[^\n]*\bnot found/i.test(out);
183
- if (!bindingError || !/\bSELF\b/.test(out))
184
- return false;
185
- return workerName ? out.includes(workerName) : true;
214
+ /** Upload one Schema-revision Worker without assigning it a public route. */
215
+ export async function runWranglerReleaseDeploy(args) {
216
+ await runWranglerTypes(args);
217
+ const { cmd, prefix } = wranglerCommand(args.projectDir);
218
+ const execute = async (secretsFile) => runCapture(cmd, [
219
+ ...prefix,
220
+ 'deploy',
221
+ '--config',
222
+ args.configPath,
223
+ '--dispatch-namespace',
224
+ args.namespace,
225
+ ...(secretsFile === undefined ? [] : ['--secrets-file', secretsFile]),
226
+ ], args.projectDir, args.onLog);
227
+ const result = await withSecretsFile(args.secrets, execute);
228
+ const deploymentVersion = parseDeployVersionId(result.out);
229
+ if (result.code !== 0) {
230
+ throw new WranglerDeployError(`wrangler Release deploy failed (code ${result.code}):\n${result.out.slice(-2000)}`, deploymentVersion === undefined ? undefined : { deploymentVersion });
231
+ }
232
+ if (deploymentVersion === undefined) {
233
+ throw new WranglerDeployError('wrangler Release deploy did not report its Version ID.');
234
+ }
235
+ return Object.freeze({ raw: result.out, deploymentVersion });
236
+ }
237
+ const MISSING_DISPATCH_NAMESPACE = /\[code:\s*100119\]/u;
238
+ export class WranglerDispatchNamespaceError extends Error {
239
+ effect;
240
+ constructor(message, effect) {
241
+ super(message);
242
+ this.name = 'WranglerDispatchNamespaceError';
243
+ this.effect = effect;
244
+ }
186
245
  }
187
- export async function bulkPutSecrets(args) {
188
- const names = Object.keys(args.secrets);
189
- if (names.length === 0)
246
+ /** Establish the private dispatch namespace without advancing the public gateway. */
247
+ export async function ensureWranglerDispatchNamespace(args) {
248
+ const { cmd, prefix } = wranglerCommand(args.projectDir);
249
+ const inspect = () => runCapture(cmd, [...prefix, 'dispatch-namespace', 'get', args.namespace], args.projectDir, args.onLog);
250
+ const observed = await inspect();
251
+ if (observed.code === 0)
252
+ return;
253
+ if (!MISSING_DISPATCH_NAMESPACE.test(observed.out)) {
254
+ throw new WranglerDispatchNamespaceError(`wrangler dispatch namespace inspection failed (code ${observed.code}):\n${observed.out.slice(-2000)}`, 'none');
255
+ }
256
+ const created = await runCapture(cmd, [...prefix, 'dispatch-namespace', 'create', args.namespace], args.projectDir, args.onLog);
257
+ if (created.code === 0)
190
258
  return;
259
+ // Another convergent deployment may have created the namespace after inspection.
260
+ if ((await inspect()).code === 0)
261
+ return;
262
+ throw new WranglerDispatchNamespaceError(`wrangler dispatch namespace creation failed (code ${created.code}):\n${created.out.slice(-2000)}`, 'unknown');
263
+ }
264
+ export async function runWranglerDeploymentStatus(args) {
191
265
  const { cmd, prefix } = wranglerCommand(args.projectDir);
192
- const dir = await mkdtemp(join(tmpdir(), 'astrale-secrets-'));
193
- const file = join(dir, 'secrets.json');
266
+ const result = await runCapture(cmd, [
267
+ ...prefix,
268
+ 'deployments',
269
+ 'status',
270
+ '--config',
271
+ args.configPath,
272
+ '--name',
273
+ args.workerName,
274
+ '--json',
275
+ ], args.projectDir);
276
+ if (result.code !== 0) {
277
+ throw new Error(`wrangler deployments status failed (code ${result.code}):\n${result.out.slice(-2000)}`);
278
+ }
279
+ return admitWranglerDeploymentStatus(result.out);
280
+ }
281
+ /** Admit Wrangler's current-deployment JSON before it becomes provider evidence. */
282
+ export function admitWranglerDeploymentStatus(output) {
283
+ let decoded;
194
284
  try {
195
- await writeFile(file, JSON.stringify(args.secrets));
196
- const { code, out } = await runCapture(cmd, [...prefix, 'secret', 'bulk', file, '--config', args.configPath], args.projectDir, args.onLog);
197
- if (code !== 0) {
198
- throw new Error(`wrangler secret bulk failed (code ${code}):\n${out.slice(-1500)}`);
285
+ decoded = JSON.parse(output);
286
+ }
287
+ catch (cause) {
288
+ throw new TypeError('wrangler deployments status returned invalid JSON.', { cause });
289
+ }
290
+ if (decoded === null || typeof decoded !== 'object' || Array.isArray(decoded))
291
+ invalidStatus();
292
+ const deployment = Reflect.get(decoded, 'id');
293
+ const traffic = Reflect.get(decoded, 'versions');
294
+ if (typeof deployment !== 'string' ||
295
+ deployment.length === 0 ||
296
+ !Array.isArray(traffic) ||
297
+ traffic.length === 0) {
298
+ invalidStatus();
299
+ }
300
+ const versions = traffic.map((entry) => {
301
+ if (entry === null || typeof entry !== 'object' || Array.isArray(entry))
302
+ invalidStatus();
303
+ const version = Reflect.get(entry, 'version_id');
304
+ const percentage = Reflect.get(entry, 'percentage');
305
+ if (typeof version !== 'string' ||
306
+ version.length === 0 ||
307
+ typeof percentage !== 'number' ||
308
+ !Number.isFinite(percentage) ||
309
+ percentage <= 0 ||
310
+ percentage > 100) {
311
+ invalidStatus();
199
312
  }
313
+ return Object.freeze({ version, percentage });
314
+ });
315
+ return Object.freeze({ deployment, versions: Object.freeze(versions) });
316
+ }
317
+ function invalidStatus() {
318
+ throw new TypeError('wrangler deployments status returned invalid deployment evidence.');
319
+ }
320
+ async function withSecretsFile(secrets, operation) {
321
+ if (Object.keys(secrets).length === 0)
322
+ return operation();
323
+ const directory = await mkdtemp(join(tmpdir(), 'astrale-secrets-'));
324
+ const path = join(directory, 'secrets.json');
325
+ try {
326
+ await writeFile(path, JSON.stringify(secrets), { mode: 0o600 });
327
+ return await operation(path);
200
328
  }
201
329
  finally {
202
- await rm(dir, { recursive: true, force: true });
330
+ await rm(directory, { recursive: true, force: true });
203
331
  }
204
332
  }
333
+ async function createSecretEnvironment(secrets) {
334
+ if (Object.keys(secrets).length === 0)
335
+ return undefined;
336
+ const source = Object.entries(secrets)
337
+ .sort(([left], [right]) => left.localeCompare(right))
338
+ .map(([name, value]) => `${secretName(name)}=${dotenvSecret(value)}`)
339
+ .join('\n');
340
+ const directory = await mkdtemp(join(tmpdir(), 'astrale-dev-secrets-'));
341
+ const path = join(directory, '.dev.vars');
342
+ await writeFile(path, `${source}\n`, { mode: 0o600 });
343
+ return Object.freeze({
344
+ path,
345
+ remove: () => rm(directory, { recursive: true, force: true }),
346
+ });
347
+ }
348
+ function secretName(input) {
349
+ if (!/^[A-Za-z_]\w*$/u.test(input)) {
350
+ throw new TypeError(`Cloudflare secret name '${input}' is not a Worker binding name.`);
351
+ }
352
+ return input;
353
+ }
354
+ /** Encode without changing a byte under Wrangler's dotenv boundary. */
355
+ function dotenvSecret(input) {
356
+ if (!input.includes("'"))
357
+ return `'${input}'`;
358
+ if (!input.includes('`'))
359
+ return `\`${input}\``;
360
+ if (!input.includes('"') && !input.includes('\\n') && !input.includes('\\r')) {
361
+ return `"${input}"`;
362
+ }
363
+ if (input.trim() === input && !/[#\r\n]/u.test(input))
364
+ return input;
365
+ throw new TypeError('Cloudflare local secret cannot be represented losslessly as dotenv.');
366
+ }
205
367
  // ── internals ──────────────────────────────────────────────────────────────
206
368
  function runCapture(cmd, args, cwd, onLog) {
207
369
  return new Promise((resolve, reject) => {
208
370
  const child = spawn(cmd, args, {
209
371
  cwd,
210
372
  stdio: ['ignore', 'pipe', 'pipe'],
211
- env: { ...process.env, WRANGLER_SEND_METRICS: 'false' },
373
+ env: wranglerProcessEnv(cwd),
212
374
  });
213
375
  let out = '';
214
376
  const onData = (buf) => {
@@ -245,14 +407,26 @@ async function stopChild(child) {
245
407
  export async function runWranglerBundle(args) {
246
408
  await runWranglerTypes(args);
247
409
  const { cmd, prefix } = wranglerCommand(args.projectDir);
248
- const { code, out } = await runCapture(cmd, [...prefix, 'deploy', '--dry-run', '--outdir', args.outDir, '--config', args.configPath], args.projectDir, args.onLog);
410
+ const { code, out } = await runCapture(cmd, [
411
+ ...prefix,
412
+ 'deploy',
413
+ '--dry-run',
414
+ '--outdir',
415
+ args.outDir,
416
+ '--metafile',
417
+ '--config',
418
+ args.configPath,
419
+ ], args.projectDir, args.onLog);
249
420
  if (code !== 0)
250
421
  throw new Error(`wrangler bundle failed (exit ${code}): ${out.slice(-400)}`);
251
422
  const js = readdirSync(args.outDir).filter((f) => f.endsWith('.js'));
252
423
  if (js.length === 0)
253
424
  throw new Error(`wrangler bundle produced no .js in ${args.outDir}`);
254
425
  const preferred = js.find((f) => f.startsWith('worker')) ?? js[0];
255
- return join(args.outDir, preferred);
426
+ return Object.freeze({
427
+ bundlePath: join(args.outDir, preferred),
428
+ metafilePath: join(args.outDir, 'bundle-meta.json'),
429
+ });
256
430
  }
257
431
  /** Generate the exact binding and runtime types selected by the generated config. */
258
432
  export async function runWranglerTypes(args) {
@@ -272,4 +446,3 @@ export async function runWranglerTypes(args) {
272
446
  }
273
447
  return outputPath;
274
448
  }
275
- //# sourceMappingURL=wrangler-cli.js.map
@@ -1,34 +1 @@
1
- /**
2
- * `cloudflare(envs)` — the Cloudflare deployment adapter.
3
- *
4
- * Owns everything target-specific: it codegens the Worker entry + wrangler
5
- * config (the dev never sees a `wrangler.jsonc`), materializes the optional frontend
6
- * SPA, and shells out to `wrangler`. `watch` → `wrangler dev` (local URL);
7
- * `deploy` → `wrangler deploy` (workers.dev or a custom-domain URL); secrets →
8
- * `wrangler secret bulk`. Both `watch` and `deploy` return the URL the CLI
9
- * prints and `astrale domain install <url>` consumes.
10
- */
11
- import type { DomainAdapter } from '@astrale-os/sdk';
12
- import type { CloudflareParams } from './params.js';
13
- import { bulkPutSecrets, buildCloudflareWorker, buildFrontend, logTo, prepare, runWranglerDeploy, runWranglerDev, runWranglerTypes, watchFrontend } from './build/index.js';
14
- export declare function cloudflare(envs: Record<string, CloudflareParams>): DomainAdapter<CloudflareParams>;
15
- interface CloudflareAdapterDependencies {
16
- readonly bulkPutSecrets: typeof bulkPutSecrets;
17
- readonly buildCloudflareWorker: typeof buildCloudflareWorker;
18
- readonly buildFrontend: typeof buildFrontend;
19
- readonly logTo: typeof logTo;
20
- readonly prepare: typeof prepare;
21
- readonly runWranglerDeploy: typeof runWranglerDeploy;
22
- readonly runWranglerDev: typeof runWranglerDev;
23
- readonly runWranglerTypes: typeof runWranglerTypes;
24
- readonly watchFrontend: typeof watchFrontend;
25
- readonly waitUntilLive: typeof waitUntilLive;
26
- }
27
- /** Exact orchestration seam used by adapter-level deployment qualification. */
28
- export declare function cloudflareWithDependencies(envs: Record<string, CloudflareParams>, dependencies: CloudflareAdapterDependencies): DomainAdapter<CloudflareParams>;
29
- /**
30
- * Poll until the deployed Worker health endpoint returns exactly 200.
31
- */
32
- declare function waitUntilLive(url: string, onLog: (line: string) => void, expectedDeploymentVersion?: string, timeoutMs?: number): Promise<void>;
33
- export {};
34
- //# sourceMappingURL=cloudflare.d.ts.map
1
+ export { cloudflare } from './adapter.js';