@astrale-os/adapter-cloudflare 0.5.0-beta.4 → 0.5.0-beta.41

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 (180) hide show
  1. package/README.md +11 -0
  2. package/dist/adapter.d.ts +17 -0
  3. package/dist/adapter.js +178 -0
  4. package/dist/build/artifact.d.ts +7 -0
  5. package/dist/build/artifact.js +151 -0
  6. package/dist/build/bundle/admit.d.ts +1 -0
  7. package/dist/build/bundle/admit.js +9 -0
  8. package/dist/build/closure.d.ts +2 -0
  9. package/dist/build/closure.js +22 -0
  10. package/dist/build/codegen/execution.d.ts +3 -0
  11. package/dist/build/codegen/execution.js +2 -0
  12. package/dist/build/codegen/gateway.d.ts +1 -0
  13. package/dist/build/codegen/gateway.js +1 -0
  14. package/dist/build/codegen/merge.d.ts +0 -1
  15. package/dist/build/codegen/merge.js +0 -1
  16. package/dist/build/codegen/wrangler.d.ts +18 -1
  17. package/dist/build/codegen/wrangler.js +36 -17
  18. package/dist/build/configuration/addressing.d.ts +2 -0
  19. package/dist/build/configuration/addressing.js +11 -0
  20. package/dist/build/configuration/request-limit.d.ts +2 -0
  21. package/dist/build/configuration/request-limit.js +9 -0
  22. package/dist/build/configuration/router.d.ts +7 -0
  23. package/dist/build/configuration/router.js +11 -0
  24. package/dist/build/configuration/worker-name.d.ts +2 -0
  25. package/dist/build/configuration/worker-name.js +18 -0
  26. package/dist/build/configuration.d.ts +4 -0
  27. package/dist/build/configuration.js +4 -0
  28. package/dist/build/direct.d.ts +4 -0
  29. package/dist/build/direct.js +91 -0
  30. package/dist/build/frontend/routes.d.ts +2 -0
  31. package/dist/build/frontend/routes.js +11 -0
  32. package/dist/build/frontend-v1.d.ts +16 -0
  33. package/dist/build/frontend-v1.js +133 -0
  34. package/dist/build/gateway/prepare.d.ts +8 -0
  35. package/dist/build/gateway/prepare.js +33 -0
  36. package/dist/build/gateway/source.d.ts +1 -0
  37. package/dist/build/gateway/source.js +7 -0
  38. package/dist/build/index.d.ts +10 -16
  39. package/dist/build/index.js +9 -16
  40. package/dist/build/materialize.d.ts +4 -0
  41. package/dist/build/materialize.js +10 -0
  42. package/dist/build/metafile.d.ts +5 -0
  43. package/dist/build/metafile.js +14 -0
  44. package/dist/build/parse-output.d.ts +0 -1
  45. package/dist/build/parse-output.js +0 -1
  46. package/dist/build/qualification.d.ts +5 -0
  47. package/dist/build/qualification.js +7 -0
  48. package/dist/build/roots.d.ts +2 -0
  49. package/dist/build/roots.js +34 -0
  50. package/dist/build/worker/artifact.d.ts +39 -0
  51. package/dist/build/worker/artifact.js +1 -0
  52. package/dist/build/worker/artifact.load.d.ts +3 -0
  53. package/dist/build/worker/artifact.load.js +277 -0
  54. package/dist/build/worker/artifact.manifest.d.ts +47 -0
  55. package/dist/build/worker/artifact.manifest.js +87 -0
  56. package/dist/build/worker/artifact.paths.d.ts +4 -0
  57. package/dist/build/worker/artifact.paths.js +25 -0
  58. package/dist/build/worker/codegen/entry.d.ts +2 -0
  59. package/dist/build/worker/codegen/entry.js +28 -0
  60. package/dist/build/worker/codegen/service.d.ts +15 -0
  61. package/dist/build/worker/codegen/service.js +46 -0
  62. package/dist/build/worker/execution.prepare.d.ts +18 -0
  63. package/dist/build/worker/execution.prepare.js +108 -0
  64. package/dist/build/worker/index.d.ts +2 -0
  65. package/dist/build/worker/index.js +1 -0
  66. package/dist/build/worker/modules.d.ts +11 -0
  67. package/dist/build/worker/modules.js +26 -0
  68. package/dist/build/worker/runtime.d.ts +10 -0
  69. package/dist/build/worker/runtime.js +35 -0
  70. package/dist/build/worker/specifier.d.ts +1 -0
  71. package/dist/build/worker/specifier.js +5 -0
  72. package/dist/build/wrangler/bundle.d.ts +10 -0
  73. package/dist/build/wrangler/bundle.js +29 -0
  74. package/dist/build/wrangler/deploy.d.ts +23 -0
  75. package/dist/build/wrangler/deploy.js +35 -0
  76. package/dist/build/wrangler/development.d.ts +15 -0
  77. package/dist/build/wrangler/development.js +135 -0
  78. package/dist/build/wrangler/namespace.d.ts +10 -0
  79. package/dist/build/wrangler/namespace.js +26 -0
  80. package/dist/build/wrangler/process.d.ts +17 -0
  81. package/dist/build/wrangler/process.js +115 -0
  82. package/dist/build/wrangler/release.d.ts +11 -0
  83. package/dist/build/wrangler/release.js +27 -0
  84. package/dist/build/wrangler/secrets.d.ts +6 -0
  85. package/dist/build/wrangler/secrets.js +58 -0
  86. package/dist/build/wrangler/status.d.ts +14 -0
  87. package/dist/build/wrangler/status.js +56 -0
  88. package/dist/build/wrangler/type-generation.d.ts +6 -0
  89. package/dist/build/wrangler/type-generation.js +19 -0
  90. package/dist/build/wrangler-cli.d.ts +8 -86
  91. package/dist/build/wrangler-cli.js +8 -275
  92. package/dist/cloudflare.d.ts +1 -34
  93. package/dist/cloudflare.js +1 -205
  94. package/dist/deploy/deploy.d.ts +17 -0
  95. package/dist/deploy/deploy.js +234 -0
  96. package/dist/deploy/index.d.ts +1 -0
  97. package/dist/deploy/index.js +1 -0
  98. package/dist/identity/binding.d.ts +2 -0
  99. package/dist/identity/binding.js +2 -0
  100. package/dist/identity/material.d.ts +7 -0
  101. package/dist/identity/material.js +42 -0
  102. package/dist/identity/secret.d.ts +3 -0
  103. package/dist/identity/secret.js +7 -0
  104. package/dist/identity/source.d.ts +26 -0
  105. package/dist/identity/source.js +98 -0
  106. package/dist/index.d.ts +3 -4
  107. package/dist/index.js +3 -4
  108. package/dist/params.d.ts +24 -5
  109. package/dist/params.js +0 -1
  110. package/dist/release/index.d.ts +2 -0
  111. package/dist/release/index.js +2 -0
  112. package/dist/release/namespace.d.ts +2 -0
  113. package/dist/release/namespace.js +6 -0
  114. package/dist/release/target.d.ts +2 -0
  115. package/dist/release/target.js +9 -0
  116. package/dist/worker/entry.d.ts +39 -0
  117. package/dist/worker/entry.js +162 -0
  118. package/dist/worker/fetch.d.ts +11 -0
  119. package/dist/worker/fetch.js +31 -0
  120. package/dist/worker/gateway.d.ts +15 -0
  121. package/dist/worker/gateway.js +26 -0
  122. package/dist/worker/identity.d.ts +7 -0
  123. package/dist/worker/identity.js +23 -0
  124. package/dist/worker/index.d.ts +3 -59
  125. package/dist/worker/index.js +3 -209
  126. package/package.json +21 -18
  127. package/template/astrale.config.ts +19 -11
  128. package/dist/build/codegen/identity.d.ts +0 -10
  129. package/dist/build/codegen/identity.d.ts.map +0 -1
  130. package/dist/build/codegen/identity.js +0 -32
  131. package/dist/build/codegen/identity.js.map +0 -1
  132. package/dist/build/codegen/merge.d.ts.map +0 -1
  133. package/dist/build/codegen/merge.js.map +0 -1
  134. package/dist/build/codegen/worker.d.ts +0 -12
  135. package/dist/build/codegen/worker.d.ts.map +0 -1
  136. package/dist/build/codegen/worker.js +0 -40
  137. package/dist/build/codegen/worker.js.map +0 -1
  138. package/dist/build/codegen/wrangler.d.ts.map +0 -1
  139. package/dist/build/codegen/wrangler.js.map +0 -1
  140. package/dist/build/frontend.d.ts +0 -26
  141. package/dist/build/frontend.d.ts.map +0 -1
  142. package/dist/build/frontend.js +0 -263
  143. package/dist/build/frontend.js.map +0 -1
  144. package/dist/build/index.d.ts.map +0 -1
  145. package/dist/build/index.js.map +0 -1
  146. package/dist/build/parse-output.d.ts.map +0 -1
  147. package/dist/build/parse-output.js.map +0 -1
  148. package/dist/build/prepare.d.ts +0 -18
  149. package/dist/build/prepare.d.ts.map +0 -1
  150. package/dist/build/prepare.js +0 -117
  151. package/dist/build/prepare.js.map +0 -1
  152. package/dist/build/worker.d.ts +0 -15
  153. package/dist/build/worker.d.ts.map +0 -1
  154. package/dist/build/worker.js +0 -21
  155. package/dist/build/worker.js.map +0 -1
  156. package/dist/build/wrangler-cli.d.ts.map +0 -1
  157. package/dist/build/wrangler-cli.js.map +0 -1
  158. package/dist/cloudflare.d.ts.map +0 -1
  159. package/dist/cloudflare.js.map +0 -1
  160. package/dist/index.d.ts.map +0 -1
  161. package/dist/index.js.map +0 -1
  162. package/dist/params.d.ts.map +0 -1
  163. package/dist/params.js.map +0 -1
  164. package/dist/worker/index.d.ts.map +0 -1
  165. package/dist/worker/index.js.map +0 -1
  166. package/src/build/codegen/identity.ts +0 -40
  167. package/src/build/codegen/merge.ts +0 -92
  168. package/src/build/codegen/worker.ts +0 -53
  169. package/src/build/codegen/wrangler.ts +0 -127
  170. package/src/build/frontend.ts +0 -324
  171. package/src/build/index.ts +0 -29
  172. package/src/build/parse-output.ts +0 -60
  173. package/src/build/prepare.ts +0 -161
  174. package/src/build/worker.ts +0 -37
  175. package/src/build/wrangler-cli.ts +0 -362
  176. package/src/cloudflare.ts +0 -251
  177. package/src/index.ts +0 -21
  178. package/src/params.ts +0 -92
  179. package/src/worker/index.ts +0 -314
  180. package/src/worker/tsconfig.json +0 -10
@@ -0,0 +1,11 @@
1
+ import type { WorkerArtifact } from './artifact.js';
2
+ type WranglerModuleType = 'ESModule' | 'CompiledWasm' | 'Text' | 'Data';
3
+ /** Project one admitted Worker module inventory into Wrangler's relative module discovery. */
4
+ export declare function workerModules(worker: WorkerArtifact['release']): {
5
+ readonly baseDir: string;
6
+ readonly rules: readonly {
7
+ readonly type: WranglerModuleType;
8
+ readonly globs: readonly string[];
9
+ }[];
10
+ };
11
+ export {};
@@ -0,0 +1,26 @@
1
+ import { posix } from 'node:path';
2
+ const TYPES = Object.freeze({
3
+ esm: 'ESModule',
4
+ wasm: 'CompiledWasm',
5
+ text: 'Text',
6
+ data: 'Data',
7
+ });
8
+ /** Project one admitted Worker module inventory into Wrangler's relative module discovery. */
9
+ export function workerModules(worker) {
10
+ const directory = posix.dirname(worker.entrypoint);
11
+ const groups = new Map();
12
+ for (const module of worker.modules) {
13
+ if (module.path === worker.entrypoint)
14
+ continue;
15
+ const relative = posix.relative(directory, module.path);
16
+ if (relative === '' || relative === '..' || relative.startsWith('../')) {
17
+ throw new TypeError('Cloudflare Worker module escapes its entrypoint directory.');
18
+ }
19
+ const type = TYPES[module.kind];
20
+ const globs = groups.get(type) ?? [];
21
+ globs.push(relative);
22
+ groups.set(type, globs);
23
+ }
24
+ const rules = [...groups].map(([type, globs]) => Object.freeze({ type, globs: Object.freeze(globs) }));
25
+ return Object.freeze({ baseDir: `./${directory}`, rules: Object.freeze(rules) });
26
+ }
@@ -0,0 +1,10 @@
1
+ export interface WorkerRuntimeConfiguration {
2
+ readonly compatibilityDate: string;
3
+ readonly compatibilityFlags: readonly string[];
4
+ readonly vars: Readonly<Record<string, string>>;
5
+ readonly services: readonly {
6
+ readonly name: string;
7
+ readonly service: string;
8
+ }[];
9
+ }
10
+ export declare function admitWorkerRuntimeConfiguration(input: string): WorkerRuntimeConfiguration;
@@ -0,0 +1,35 @@
1
+ export function admitWorkerRuntimeConfiguration(input) {
2
+ const value = JSON.parse(input);
3
+ const compatibilityDate = value.compatibility_date;
4
+ const compatibilityFlags = value.compatibility_flags;
5
+ const vars = value.vars ?? {};
6
+ const services = value.services ?? [];
7
+ if (typeof compatibilityDate !== 'string' ||
8
+ !Array.isArray(compatibilityFlags) ||
9
+ compatibilityFlags.some((flag) => typeof flag !== 'string') ||
10
+ vars === null ||
11
+ typeof vars !== 'object' ||
12
+ Array.isArray(vars) ||
13
+ Object.values(vars).some((item) => typeof item !== 'string') ||
14
+ !Array.isArray(services)) {
15
+ invalidRuntime();
16
+ }
17
+ const admittedServices = services.map((input) => {
18
+ if (input === null || typeof input !== 'object' || Array.isArray(input))
19
+ invalidRuntime();
20
+ const binding = Reflect.get(input, 'binding');
21
+ const service = Reflect.get(input, 'service');
22
+ if (typeof binding !== 'string' || typeof service !== 'string')
23
+ invalidRuntime();
24
+ return Object.freeze({ name: binding, service });
25
+ });
26
+ return Object.freeze({
27
+ compatibilityDate,
28
+ compatibilityFlags: Object.freeze([...compatibilityFlags]),
29
+ vars: Object.freeze({ ...vars }),
30
+ services: Object.freeze(admittedServices),
31
+ });
32
+ }
33
+ function invalidRuntime() {
34
+ throw new TypeError('Generated Cloudflare runtime configuration is invalid.');
35
+ }
@@ -0,0 +1 @@
1
+ export declare function workerModuleSpecifier(): string;
@@ -0,0 +1,5 @@
1
+ import { fileURLToPath } from 'node:url';
2
+ const workerModule = import.meta.resolve('@astrale-os/adapter-cloudflare/worker');
3
+ export function workerModuleSpecifier() {
4
+ return fileURLToPath(workerModule);
5
+ }
@@ -0,0 +1,10 @@
1
+ export declare function runWranglerBundle(args: {
2
+ projectDir: string;
3
+ configPath: string;
4
+ outDir: string;
5
+ onLog?: (line: string) => void;
6
+ signal?: AbortSignal;
7
+ }): Promise<{
8
+ readonly bundlePath: string;
9
+ readonly metafilePath: string;
10
+ }>;
@@ -0,0 +1,29 @@
1
+ import { readdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { runCapture, wranglerCommand } from './process.js';
4
+ import { runWranglerTypes } from './type-generation.js';
5
+ export async function runWranglerBundle(args) {
6
+ await runWranglerTypes(args);
7
+ const { cmd, prefix } = wranglerCommand(args.projectDir);
8
+ const { code, out } = await runCapture(cmd, [
9
+ ...prefix,
10
+ 'deploy',
11
+ '--dry-run',
12
+ '--outdir',
13
+ args.outDir,
14
+ '--metafile',
15
+ '--config',
16
+ args.configPath,
17
+ ], args.projectDir, args.onLog, args.signal);
18
+ if (code !== 0)
19
+ throw new Error(`wrangler bundle failed (exit ${code}): ${out.slice(-400)}`);
20
+ const javascript = readdirSync(args.outDir).filter((file) => file.endsWith('.js'));
21
+ if (javascript.length === 0) {
22
+ throw new Error(`wrangler bundle produced no .js in ${args.outDir}`);
23
+ }
24
+ const preferred = javascript.find((file) => file.startsWith('worker')) ?? javascript[0];
25
+ return Object.freeze({
26
+ bundlePath: join(args.outDir, preferred),
27
+ metafilePath: join(args.outDir, 'bundle-meta.json'),
28
+ });
29
+ }
@@ -0,0 +1,23 @@
1
+ export interface DeployArgs {
2
+ configPath: string;
3
+ route?: string;
4
+ onLog?: (line: string) => void;
5
+ signal?: AbortSignal;
6
+ }
7
+ export declare class WranglerDeployError extends Error {
8
+ readonly applied?: {
9
+ readonly url?: string;
10
+ readonly deploymentVersion?: string;
11
+ };
12
+ constructor(message: string, applied?: {
13
+ readonly url?: string;
14
+ readonly deploymentVersion?: string;
15
+ });
16
+ }
17
+ export declare function runWranglerDeploy(args: DeployArgs & {
18
+ projectDir: string;
19
+ }): Promise<{
20
+ url?: string;
21
+ raw: string;
22
+ deploymentVersion: string;
23
+ }>;
@@ -0,0 +1,35 @@
1
+ import { parseDeployUrl, parseDeployVersionId } from '../parse-output.js';
2
+ import { runCapture, wranglerCommand } from './process.js';
3
+ import { runWranglerTypes } from './type-generation.js';
4
+ export class WranglerDeployError extends Error {
5
+ applied;
6
+ constructor(message, applied) {
7
+ super(message);
8
+ this.name = 'WranglerDeployError';
9
+ if (applied !== undefined)
10
+ this.applied = Object.freeze({ ...applied });
11
+ }
12
+ }
13
+ export async function runWranglerDeploy(args) {
14
+ await runWranglerTypes(args);
15
+ const { cmd, prefix } = wranglerCommand(args.projectDir);
16
+ const { code, out } = await runCapture(cmd, [...prefix, 'deploy', '--config', args.configPath], args.projectDir, args.onLog, args.signal);
17
+ const url = parseDeployUrl(out, args.route);
18
+ const deploymentVersion = parseDeployVersionId(out);
19
+ if (code !== 0) {
20
+ throw new WranglerDeployError(`wrangler deploy failed (code ${code}):\n${out.slice(-2000)}`, url === undefined && deploymentVersion === undefined
21
+ ? undefined
22
+ : {
23
+ ...(url === undefined ? {} : { url }),
24
+ ...(deploymentVersion === undefined ? {} : { deploymentVersion }),
25
+ });
26
+ }
27
+ if (deploymentVersion === undefined) {
28
+ throw new WranglerDeployError('wrangler deploy did not report its Version ID.', url === undefined ? undefined : { url });
29
+ }
30
+ return Object.freeze({
31
+ raw: out,
32
+ ...(url === undefined ? {} : { url }),
33
+ deploymentVersion,
34
+ });
35
+ }
@@ -0,0 +1,15 @@
1
+ export interface DevHandle {
2
+ url: string;
3
+ stop(): Promise<void>;
4
+ }
5
+ export declare function runWranglerDev(args: {
6
+ projectDir: string;
7
+ configPath: string;
8
+ port: number;
9
+ remote: boolean;
10
+ readonly ip?: string;
11
+ readonly autoPickPort?: boolean;
12
+ readonly secrets: Readonly<Record<string, string>>;
13
+ readonly onReload: () => void;
14
+ readonly onLog?: (line: string) => void;
15
+ }): Promise<DevHandle>;
@@ -0,0 +1,135 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { createServer } from 'node:net';
3
+ import { parseDevReadyUrl } from '../parse-output.js';
4
+ import { stopChild, wranglerCommand, wranglerProcessEnv } from './process.js';
5
+ import { createSecretEnvironment, secretRedactor } from './secrets.js';
6
+ import { runWranglerTypes } from './type-generation.js';
7
+ export async function runWranglerDev(args) {
8
+ await runWranglerTypes(args);
9
+ const { cmd, prefix } = wranglerCommand(args.projectDir);
10
+ const bindHost = args.ip ?? '127.0.0.1';
11
+ if (args.autoPickPort && !(await portFree(args.port, bindHost))) {
12
+ const picked = await ephemeralPort(bindHost);
13
+ args.onLog?.(`port ${args.port} is taken — using ${picked} instead (pass --port to pin one)`);
14
+ args = { ...args, port: picked };
15
+ }
16
+ const secretEnvironment = await createSecretEnvironment(args.secrets);
17
+ const redact = secretRedactor(args.secrets);
18
+ const wranglerArgs = [
19
+ ...prefix,
20
+ 'dev',
21
+ '--config',
22
+ args.configPath,
23
+ '--port',
24
+ String(args.port),
25
+ '--local-protocol',
26
+ 'http',
27
+ '--ip',
28
+ args.ip ?? '127.0.0.1',
29
+ ...(secretEnvironment === undefined ? [] : ['--env-file', secretEnvironment.path]),
30
+ ...(args.remote ? ['--remote'] : []),
31
+ ];
32
+ let child;
33
+ try {
34
+ child = spawn(cmd, wranglerArgs, {
35
+ cwd: args.projectDir,
36
+ stdio: ['ignore', 'pipe', 'pipe'],
37
+ env: wranglerProcessEnv(args.projectDir),
38
+ });
39
+ }
40
+ catch (cause) {
41
+ await secretEnvironment?.remove();
42
+ throw cause;
43
+ }
44
+ const fallbackUrl = `http://localhost:${args.port}`;
45
+ let resolved = false;
46
+ let buildErrorTail = '';
47
+ let outputTail = '';
48
+ let url;
49
+ try {
50
+ url = await new Promise((resolve, reject) => {
51
+ const onData = (buf) => {
52
+ const text = redact(buf.toString());
53
+ outputTail = (outputTail + text).slice(-3000);
54
+ for (const line of text.split('\n')) {
55
+ if (line.trim())
56
+ args.onLog?.(line);
57
+ }
58
+ if (/✘ \[ERROR\]|Build failed|Could not resolve "/.test(text)) {
59
+ buildErrorTail = (buildErrorTail + text).slice(-2000);
60
+ }
61
+ if (!resolved) {
62
+ const ready = parseDevReadyUrl(text, args.port);
63
+ if (ready) {
64
+ resolved = true;
65
+ resolve(ready);
66
+ }
67
+ }
68
+ else if (/Reloading|Detected changes|Updated/i.test(text)) {
69
+ args.onReload();
70
+ }
71
+ };
72
+ child.stdout?.on('data', onData);
73
+ child.stderr?.on('data', onData);
74
+ child.on('exit', (code) => {
75
+ if (!resolved) {
76
+ resolved = true;
77
+ reject(new Error(`wrangler dev exited before becoming ready (code ${code ?? 'null'}).\n${outputTail}`));
78
+ }
79
+ });
80
+ child.on('error', (error) => {
81
+ if (!resolved) {
82
+ resolved = true;
83
+ reject(error);
84
+ }
85
+ });
86
+ setTimeout(() => {
87
+ if (!resolved && child.exitCode === null) {
88
+ resolved = true;
89
+ if (buildErrorTail) {
90
+ void stopChild(child);
91
+ reject(new Error(`worker build failed — the dev server would hang silently.\n${buildErrorTail}`));
92
+ }
93
+ else {
94
+ resolve(fallbackUrl);
95
+ }
96
+ }
97
+ }, 15_000).unref?.();
98
+ });
99
+ }
100
+ catch (cause) {
101
+ await stopChild(child);
102
+ await secretEnvironment?.remove();
103
+ throw cause;
104
+ }
105
+ return {
106
+ url,
107
+ async stop() {
108
+ try {
109
+ await stopChild(child);
110
+ }
111
+ finally {
112
+ await secretEnvironment?.remove();
113
+ }
114
+ },
115
+ };
116
+ }
117
+ function portFree(port, host) {
118
+ return new Promise((resolve) => {
119
+ const server = createServer();
120
+ server.once('error', () => resolve(false));
121
+ server.once('listening', () => server.close(() => resolve(true)));
122
+ server.listen(port, host);
123
+ });
124
+ }
125
+ function ephemeralPort(host) {
126
+ return new Promise((resolve, reject) => {
127
+ const server = createServer();
128
+ server.once('error', reject);
129
+ server.listen(0, host, () => {
130
+ const address = server.address();
131
+ const port = typeof address === 'object' && address ? address.port : 0;
132
+ server.close(() => resolve(port));
133
+ });
134
+ });
135
+ }
@@ -0,0 +1,10 @@
1
+ export declare class WranglerDispatchNamespaceError extends Error {
2
+ readonly effect: 'none' | 'unknown';
3
+ constructor(message: string, effect: 'none' | 'unknown');
4
+ }
5
+ export declare function ensureWranglerDispatchNamespace(args: {
6
+ readonly projectDir: string;
7
+ readonly namespace: string;
8
+ readonly onLog?: (line: string) => void;
9
+ readonly signal?: AbortSignal;
10
+ }): Promise<void>;
@@ -0,0 +1,26 @@
1
+ import { runCapture, wranglerCommand } from './process.js';
2
+ const MISSING_DISPATCH_NAMESPACE = /\[code:\s*100119\]/u;
3
+ export class WranglerDispatchNamespaceError extends Error {
4
+ effect;
5
+ constructor(message, effect) {
6
+ super(message);
7
+ this.name = 'WranglerDispatchNamespaceError';
8
+ this.effect = effect;
9
+ }
10
+ }
11
+ export async function ensureWranglerDispatchNamespace(args) {
12
+ const { cmd, prefix } = wranglerCommand(args.projectDir);
13
+ const inspect = () => runCapture(cmd, [...prefix, 'dispatch-namespace', 'get', args.namespace], args.projectDir, args.onLog, args.signal);
14
+ const observed = await inspect();
15
+ if (observed.code === 0)
16
+ return;
17
+ if (!MISSING_DISPATCH_NAMESPACE.test(observed.out)) {
18
+ throw new WranglerDispatchNamespaceError(`wrangler dispatch namespace inspection failed (code ${observed.code}):\n${observed.out.slice(-2000)}`, 'none');
19
+ }
20
+ const created = await runCapture(cmd, [...prefix, 'dispatch-namespace', 'create', args.namespace], args.projectDir, args.onLog, args.signal);
21
+ if (created.code === 0)
22
+ return;
23
+ if ((await inspect()).code === 0)
24
+ return;
25
+ throw new WranglerDispatchNamespaceError(`wrangler dispatch namespace creation failed (code ${created.code}):\n${created.out.slice(-2000)}`, 'unknown');
26
+ }
@@ -0,0 +1,17 @@
1
+ import type { ChildProcess } from 'node:child_process';
2
+ export declare function wranglerCommand(projectDir: string, runtime?: {
3
+ readonly executable: string;
4
+ readonly bun: boolean;
5
+ }): {
6
+ cmd: string;
7
+ prefix: string[];
8
+ };
9
+ /** Keep Wrangler CLI diagnostic logs inside the generated project state. */
10
+ export declare function wranglerProcessEnv(projectDir: string): NodeJS.ProcessEnv;
11
+ export declare function runCapture(cmd: string, args: string[], cwd: string, onLog?: (line: string) => void, signal?: AbortSignal, redact?: (input: string) => string): Promise<{
12
+ code: number;
13
+ out: string;
14
+ stdout: string;
15
+ stderr: string;
16
+ }>;
17
+ export declare function stopChild(child: ChildProcess): Promise<void>;
@@ -0,0 +1,115 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { createRequire } from 'node:module';
4
+ import { dirname, join, resolve } from 'node:path';
5
+ export function wranglerCommand(projectDir, runtime = {
6
+ executable: process.execPath,
7
+ bun: process.versions.bun !== undefined,
8
+ }) {
9
+ const local = join(projectDir, 'node_modules', '.bin', 'wrangler');
10
+ if (existsSync(local))
11
+ return { cmd: local, prefix: [] };
12
+ const require = createRequire(import.meta.url);
13
+ const packagePath = require.resolve('wrangler/package.json');
14
+ const manifest = JSON.parse(readFileSync(packagePath, 'utf8'));
15
+ const executable = typeof manifest.bin === 'string' ? manifest.bin : manifest.bin?.wrangler;
16
+ if (!executable)
17
+ throw new TypeError('Installed Wrangler package has no wrangler executable.');
18
+ return {
19
+ cmd: runtime.bun ? 'node' : runtime.executable,
20
+ prefix: [resolve(dirname(packagePath), executable)],
21
+ };
22
+ }
23
+ /** Keep Wrangler CLI diagnostic logs inside the generated project state. */
24
+ export function wranglerProcessEnv(projectDir) {
25
+ return {
26
+ ...process.env,
27
+ WRANGLER_SEND_METRICS: 'false',
28
+ WRANGLER_LOG_PATH: process.env.WRANGLER_LOG_PATH || resolve(projectDir, '.astrale', 'logs', 'wrangler'),
29
+ };
30
+ }
31
+ export function runCapture(cmd, args, cwd, onLog, signal, redact = (input) => input) {
32
+ if (signal?.aborted)
33
+ return Promise.reject(new Error('Wrangler process cancelled.'));
34
+ return new Promise((resolve, reject) => {
35
+ const child = spawn(cmd, args, {
36
+ cwd,
37
+ stdio: ['ignore', 'pipe', 'pipe'],
38
+ env: wranglerProcessEnv(cwd),
39
+ });
40
+ let out = '';
41
+ let stdout = '';
42
+ let stderr = '';
43
+ let outputBytes = 0;
44
+ let terminating;
45
+ const timeout = setTimeout(() => terminate(new Error('Wrangler process timed out.')), 300_000);
46
+ const abort = () => terminate(new Error('Wrangler process cancelled.'));
47
+ signal?.addEventListener('abort', abort, { once: true });
48
+ let escalation;
49
+ const cleanup = () => {
50
+ clearTimeout(timeout);
51
+ if (escalation !== undefined)
52
+ clearTimeout(escalation);
53
+ signal?.removeEventListener('abort', abort);
54
+ };
55
+ function terminate(cause) {
56
+ if (terminating !== undefined)
57
+ return;
58
+ terminating = cause;
59
+ child.kill('SIGTERM');
60
+ escalation = setTimeout(() => {
61
+ if (child.exitCode === null && child.signalCode === null)
62
+ child.kill('SIGKILL');
63
+ }, 1_000);
64
+ }
65
+ const onData = (buffer, stream) => {
66
+ outputBytes += buffer.byteLength;
67
+ if (outputBytes > 1024 * 1024) {
68
+ terminate(new Error('Wrangler process exceeded its output limit.'));
69
+ return;
70
+ }
71
+ const chunk = buffer.toString();
72
+ out += chunk;
73
+ if (stream === 'stdout')
74
+ stdout += chunk;
75
+ else
76
+ stderr += chunk;
77
+ };
78
+ child.stdout?.on('data', (buffer) => onData(buffer, 'stdout'));
79
+ child.stderr?.on('data', (buffer) => onData(buffer, 'stderr'));
80
+ child.on('exit', (code) => {
81
+ cleanup();
82
+ if (terminating !== undefined)
83
+ reject(terminating);
84
+ else {
85
+ const safe = redact(out);
86
+ for (const line of safe.split('\n'))
87
+ if (line.trim())
88
+ onLog?.(line);
89
+ resolve({
90
+ code: code ?? 1,
91
+ out: safe,
92
+ stdout: redact(stdout),
93
+ stderr: redact(stderr),
94
+ });
95
+ }
96
+ });
97
+ child.on('error', (cause) => {
98
+ cleanup();
99
+ reject(terminating ?? cause);
100
+ });
101
+ });
102
+ }
103
+ export async function stopChild(child) {
104
+ if (child.exitCode !== null)
105
+ return;
106
+ await new Promise((resolve) => {
107
+ child.on('exit', () => resolve());
108
+ child.kill('SIGTERM');
109
+ setTimeout(() => {
110
+ if (child.exitCode === null)
111
+ child.kill('SIGKILL');
112
+ resolve();
113
+ }, 4000).unref?.();
114
+ });
115
+ }
@@ -0,0 +1,11 @@
1
+ export declare function runWranglerReleaseDeploy(args: {
2
+ readonly projectDir: string;
3
+ readonly configPath: string;
4
+ readonly namespace: string;
5
+ readonly secrets: Readonly<Record<string, string>>;
6
+ readonly onLog?: (line: string) => void;
7
+ readonly signal?: AbortSignal;
8
+ }): Promise<{
9
+ readonly raw: string;
10
+ readonly deploymentVersion: string;
11
+ }>;
@@ -0,0 +1,27 @@
1
+ import { parseDeployVersionId } from '../parse-output.js';
2
+ import { WranglerDeployError } from './deploy.js';
3
+ import { runCapture, wranglerCommand } from './process.js';
4
+ import { secretRedactor, withSecretsFile } from './secrets.js';
5
+ import { runWranglerTypes } from './type-generation.js';
6
+ export async function runWranglerReleaseDeploy(args) {
7
+ await runWranglerTypes(args);
8
+ const { cmd, prefix } = wranglerCommand(args.projectDir);
9
+ const execute = async (secretsFile) => runCapture(cmd, [
10
+ ...prefix,
11
+ 'deploy',
12
+ '--config',
13
+ args.configPath,
14
+ '--dispatch-namespace',
15
+ args.namespace,
16
+ ...(secretsFile === undefined ? [] : ['--secrets-file', secretsFile]),
17
+ ], args.projectDir, args.onLog, args.signal, secretRedactor(args.secrets));
18
+ const result = await withSecretsFile(args.secrets, execute);
19
+ const deploymentVersion = parseDeployVersionId(result.out);
20
+ if (result.code !== 0) {
21
+ throw new WranglerDeployError(`wrangler Release deploy failed (code ${result.code}):\n${result.out.slice(-2000)}`, deploymentVersion === undefined ? undefined : { deploymentVersion });
22
+ }
23
+ if (deploymentVersion === undefined) {
24
+ throw new WranglerDeployError('wrangler Release deploy did not report its Version ID.');
25
+ }
26
+ return Object.freeze({ raw: result.out, deploymentVersion });
27
+ }
@@ -0,0 +1,6 @@
1
+ export declare function withSecretsFile<Result>(secrets: Readonly<Record<string, string>>, operation: (path?: string) => Promise<Result>): Promise<Result>;
2
+ export declare function createSecretEnvironment(secrets: Readonly<Record<string, string>>): Promise<{
3
+ readonly path: string;
4
+ remove(): Promise<void>;
5
+ } | undefined>;
6
+ export declare function secretRedactor(secrets: Readonly<Record<string, string>>): (input: string) => string;
@@ -0,0 +1,58 @@
1
+ import { mkdtemp, rm, writeFile } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ export async function withSecretsFile(secrets, operation) {
5
+ if (Object.keys(secrets).length === 0)
6
+ return operation();
7
+ const directory = await mkdtemp(join(tmpdir(), 'astrale-secrets-'));
8
+ const path = join(directory, 'secrets.json');
9
+ try {
10
+ await writeFile(path, JSON.stringify(secrets), { mode: 0o600 });
11
+ return await operation(path);
12
+ }
13
+ finally {
14
+ await rm(directory, { recursive: true, force: true });
15
+ }
16
+ }
17
+ export async function createSecretEnvironment(secrets) {
18
+ if (Object.keys(secrets).length === 0)
19
+ return undefined;
20
+ const source = Object.entries(secrets)
21
+ .sort(([left], [right]) => left.localeCompare(right))
22
+ .map(([name, value]) => `${secretName(name)}=${dotenvSecret(value)}`)
23
+ .join('\n');
24
+ const directory = await mkdtemp(join(tmpdir(), 'astrale-dev-secrets-'));
25
+ const path = join(directory, '.dev.vars');
26
+ await writeFile(path, `${source}\n`, { mode: 0o600 });
27
+ return Object.freeze({
28
+ path,
29
+ remove: () => rm(directory, { recursive: true, force: true }),
30
+ });
31
+ }
32
+ export function secretRedactor(secrets) {
33
+ const values = Object.values(secrets)
34
+ .filter((value) => value.length > 0)
35
+ .flatMap((value) => [value, JSON.stringify(value).slice(1, -1)])
36
+ .filter((value, index, all) => all.indexOf(value) === index)
37
+ .sort((left, right) => right.length - left.length);
38
+ return (input) => values.reduce((output, value) => output.replaceAll(value, '[REDACTED]'), input);
39
+ }
40
+ function secretName(input) {
41
+ if (!/^[A-Za-z_]\w*$/u.test(input)) {
42
+ throw new TypeError(`Cloudflare secret name '${input}' is not a Worker binding name.`);
43
+ }
44
+ return input;
45
+ }
46
+ /** Encode without changing a byte under Wrangler's dotenv boundary. */
47
+ function dotenvSecret(input) {
48
+ if (!input.includes("'"))
49
+ return `'${input}'`;
50
+ if (!input.includes('`'))
51
+ return `\`${input}\``;
52
+ if (!input.includes('"') && !input.includes('\\n') && !input.includes('\\r')) {
53
+ return `"${input}"`;
54
+ }
55
+ if (input.trim() === input && !/[#\r\n]/u.test(input))
56
+ return input;
57
+ throw new TypeError('Cloudflare local secret cannot be represented losslessly as dotenv.');
58
+ }
@@ -0,0 +1,14 @@
1
+ export interface WranglerDeploymentStatus {
2
+ readonly deployment: string;
3
+ readonly versions: readonly {
4
+ readonly version: string;
5
+ readonly percentage: number;
6
+ }[];
7
+ }
8
+ export declare function runWranglerDeploymentStatus(args: {
9
+ readonly projectDir: string;
10
+ readonly configPath: string;
11
+ readonly workerName: string;
12
+ readonly signal?: AbortSignal;
13
+ }): Promise<WranglerDeploymentStatus>;
14
+ export declare function admitWranglerDeploymentStatus(output: string): WranglerDeploymentStatus;