@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.
- package/README.md +11 -0
- package/dist/adapter.d.ts +17 -0
- package/dist/adapter.js +140 -0
- package/dist/build/artifact.d.ts +8 -0
- package/dist/build/artifact.js +179 -0
- package/dist/build/closure.d.ts +2 -0
- package/dist/build/closure.js +22 -0
- package/dist/build/codegen/execution.d.ts +13 -0
- package/dist/build/codegen/execution.js +44 -0
- package/dist/build/codegen/gateway.d.ts +1 -0
- package/dist/build/codegen/gateway.js +7 -0
- package/dist/build/codegen/merge.d.ts +0 -1
- package/dist/build/codegen/merge.js +0 -1
- package/dist/build/codegen/wrangler.d.ts +10 -1
- package/dist/build/codegen/wrangler.js +22 -16
- package/dist/build/configuration.d.ts +9 -0
- package/dist/build/configuration.js +40 -0
- package/dist/build/frontend-v1.d.ts +16 -0
- package/dist/build/frontend-v1.js +133 -0
- package/dist/build/index.d.ts +8 -16
- package/dist/build/index.js +8 -16
- package/dist/build/metafile.d.ts +5 -0
- package/dist/build/metafile.js +14 -0
- package/dist/build/parse-output.d.ts +0 -1
- package/dist/build/parse-output.js +0 -1
- package/dist/build/qualification.d.ts +5 -0
- package/dist/build/qualification.js +7 -0
- package/dist/build/roots.d.ts +2 -0
- package/dist/build/roots.js +34 -0
- package/dist/build/wrangler-cli.d.ts +53 -34
- package/dist/build/wrangler-cli.js +284 -111
- package/dist/cloudflare.d.ts +1 -34
- package/dist/cloudflare.js +1 -205
- package/dist/deploy/deploy.d.ts +17 -0
- package/dist/deploy/deploy.js +207 -0
- package/dist/deploy/index.d.ts +1 -0
- package/dist/deploy/index.js +1 -0
- package/dist/identity/binding.d.ts +2 -0
- package/dist/identity/binding.js +2 -0
- package/dist/identity/material.d.ts +7 -0
- package/dist/identity/material.js +42 -0
- package/dist/identity/secret.d.ts +3 -0
- package/dist/identity/secret.js +7 -0
- package/dist/identity/source.d.ts +26 -0
- package/dist/identity/source.js +98 -0
- package/dist/index.d.ts +3 -4
- package/dist/index.js +3 -4
- package/dist/params.d.ts +18 -5
- package/dist/params.js +0 -1
- package/dist/release/index.d.ts +2 -0
- package/dist/release/index.js +2 -0
- package/dist/release/namespace.d.ts +2 -0
- package/dist/release/namespace.js +6 -0
- package/dist/release/target.d.ts +2 -0
- package/dist/release/target.js +9 -0
- package/dist/worker/entry.d.ts +34 -0
- package/dist/worker/entry.js +157 -0
- package/dist/worker/fetch.d.ts +11 -0
- package/dist/worker/fetch.js +31 -0
- package/dist/worker/gateway.d.ts +15 -0
- package/dist/worker/gateway.js +26 -0
- package/dist/worker/identity.d.ts +7 -0
- package/dist/worker/identity.js +23 -0
- package/dist/worker/index.d.ts +3 -48
- package/dist/worker/index.js +3 -186
- package/package.json +21 -18
- package/template/astrale.config.ts +19 -11
- package/dist/build/codegen/identity.d.ts +0 -10
- package/dist/build/codegen/identity.d.ts.map +0 -1
- package/dist/build/codegen/identity.js +0 -32
- package/dist/build/codegen/identity.js.map +0 -1
- package/dist/build/codegen/merge.d.ts.map +0 -1
- package/dist/build/codegen/merge.js.map +0 -1
- package/dist/build/codegen/worker.d.ts +0 -12
- package/dist/build/codegen/worker.d.ts.map +0 -1
- package/dist/build/codegen/worker.js +0 -40
- package/dist/build/codegen/worker.js.map +0 -1
- package/dist/build/codegen/wrangler.d.ts.map +0 -1
- package/dist/build/codegen/wrangler.js.map +0 -1
- package/dist/build/frontend.d.ts +0 -26
- package/dist/build/frontend.d.ts.map +0 -1
- package/dist/build/frontend.js +0 -263
- package/dist/build/frontend.js.map +0 -1
- package/dist/build/index.d.ts.map +0 -1
- package/dist/build/index.js.map +0 -1
- package/dist/build/parse-output.d.ts.map +0 -1
- package/dist/build/parse-output.js.map +0 -1
- package/dist/build/prepare.d.ts +0 -18
- package/dist/build/prepare.d.ts.map +0 -1
- package/dist/build/prepare.js +0 -117
- package/dist/build/prepare.js.map +0 -1
- package/dist/build/worker.d.ts +0 -15
- package/dist/build/worker.d.ts.map +0 -1
- package/dist/build/worker.js +0 -21
- package/dist/build/worker.js.map +0 -1
- package/dist/build/wrangler-cli.d.ts.map +0 -1
- package/dist/build/wrangler-cli.js.map +0 -1
- package/dist/cloudflare.d.ts.map +0 -1
- package/dist/cloudflare.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/params.d.ts.map +0 -1
- package/dist/params.js.map +0 -1
- package/dist/worker/index.d.ts.map +0 -1
- package/dist/worker/index.js.map +0 -1
- package/src/build/codegen/identity.ts +0 -40
- package/src/build/codegen/merge.ts +0 -92
- package/src/build/codegen/worker.ts +0 -53
- package/src/build/codegen/wrangler.ts +0 -127
- package/src/build/frontend.ts +0 -324
- package/src/build/index.ts +0 -29
- package/src/build/parse-output.ts +0 -60
- package/src/build/prepare.ts +0 -161
- package/src/build/worker.ts +0 -37
- package/src/build/wrangler-cli.ts +0 -362
- package/src/cloudflare.ts +0 -251
- package/src/index.ts +0 -21
- package/src/params.ts +0 -92
- package/src/worker/index.ts +0 -276
- 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
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (
|
|
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
|
-
|
|
137
|
+
reject(new Error(`wrangler dev exited before becoming ready (code ${code ?? 'null'}).\n${outputTail}`));
|
|
99
138
|
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
132
|
-
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
|
143
|
-
|
|
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
|
|
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
|
-
|
|
168
|
-
|
|
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
|
-
|
|
172
|
-
|
|
208
|
+
return Object.freeze({
|
|
209
|
+
raw: out,
|
|
210
|
+
...(url === undefined ? {} : { url }),
|
|
211
|
+
deploymentVersion,
|
|
212
|
+
});
|
|
173
213
|
}
|
|
174
|
-
/**
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
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
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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
|
|
193
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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(
|
|
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:
|
|
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, [
|
|
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
|
|
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
|
package/dist/cloudflare.d.ts
CHANGED
|
@@ -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';
|