@astrale-os/adapter-cloudflare 0.5.0-beta.13 → 0.5.0-beta.131

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 (196) hide show
  1. package/CHANGELOG.md +1515 -0
  2. package/README.md +46 -2
  3. package/dist/adapter.d.ts +14 -0
  4. package/dist/adapter.js +82 -0
  5. package/dist/build/artifact.d.ts +7 -0
  6. package/dist/build/artifact.js +165 -0
  7. package/dist/build/bundle/admit.d.ts +1 -0
  8. package/dist/build/bundle/admit.js +9 -0
  9. package/dist/build/closure.d.ts +2 -0
  10. package/dist/build/closure.js +22 -0
  11. package/dist/build/codegen/execution.d.ts +3 -0
  12. package/dist/build/codegen/execution.js +2 -0
  13. package/dist/build/codegen/merge.d.ts +0 -1
  14. package/dist/build/codegen/merge.js +0 -1
  15. package/dist/build/codegen/wrangler.d.ts +12 -3
  16. package/dist/build/codegen/wrangler.js +26 -18
  17. package/dist/build/configuration/addressing.d.ts +2 -0
  18. package/dist/build/configuration/addressing.js +11 -0
  19. package/dist/build/configuration/invocation-timeout.d.ts +2 -0
  20. package/dist/build/configuration/invocation-timeout.js +9 -0
  21. package/dist/build/configuration/request-limit.d.ts +2 -0
  22. package/dist/build/configuration/request-limit.js +9 -0
  23. package/dist/build/configuration/router.d.ts +7 -0
  24. package/dist/build/configuration/router.js +11 -0
  25. package/dist/build/configuration/worker-name.d.ts +2 -0
  26. package/dist/build/configuration/worker-name.js +18 -0
  27. package/dist/build/configuration.d.ts +5 -0
  28. package/dist/build/configuration.js +5 -0
  29. package/dist/build/direct.d.ts +4 -0
  30. package/dist/build/direct.js +90 -0
  31. package/dist/build/frontend/files.d.ts +9 -0
  32. package/dist/build/frontend/files.js +33 -0
  33. package/dist/build/frontend/readiness.d.ts +10 -0
  34. package/dist/build/frontend/readiness.js +21 -0
  35. package/dist/build/frontend/revision.d.ts +5 -0
  36. package/dist/build/frontend/revision.js +14 -0
  37. package/dist/build/frontend/vite.d.ts +8 -0
  38. package/dist/build/frontend/vite.js +32 -0
  39. package/dist/build/frontend/vite.process.d.ts +7 -0
  40. package/dist/build/frontend/vite.process.js +65 -0
  41. package/dist/build/frontend/vite.runner.d.ts +1 -0
  42. package/dist/build/frontend/vite.runner.js +120 -0
  43. package/dist/build/index.d.ts +9 -16
  44. package/dist/build/index.js +8 -16
  45. package/dist/build/materialize.d.ts +4 -0
  46. package/dist/build/materialize.js +10 -0
  47. package/dist/build/metafile.d.ts +5 -0
  48. package/dist/build/metafile.js +14 -0
  49. package/dist/build/qualification.d.ts +5 -0
  50. package/dist/build/qualification.js +7 -0
  51. package/dist/build/roots.d.ts +2 -0
  52. package/dist/build/roots.js +34 -0
  53. package/dist/build/worker/artifact.d.ts +40 -0
  54. package/dist/build/worker/artifact.js +1 -0
  55. package/dist/build/worker/artifact.load.d.ts +3 -0
  56. package/dist/build/worker/artifact.load.js +308 -0
  57. package/dist/build/worker/artifact.manifest.d.ts +48 -0
  58. package/dist/build/worker/artifact.manifest.js +87 -0
  59. package/dist/build/worker/artifact.paths.d.ts +4 -0
  60. package/dist/build/worker/artifact.paths.js +25 -0
  61. package/dist/build/worker/codegen/entry.d.ts +2 -0
  62. package/dist/build/worker/codegen/entry.js +28 -0
  63. package/dist/build/worker/codegen/service.d.ts +19 -0
  64. package/dist/build/worker/codegen/service.js +53 -0
  65. package/dist/build/worker/execution.prepare.d.ts +19 -0
  66. package/dist/build/worker/execution.prepare.js +115 -0
  67. package/dist/build/worker/index.d.ts +2 -0
  68. package/dist/build/worker/index.js +1 -0
  69. package/dist/build/worker/modules.d.ts +11 -0
  70. package/dist/build/worker/modules.js +26 -0
  71. package/dist/build/worker/runtime.d.ts +10 -0
  72. package/dist/build/worker/runtime.js +35 -0
  73. package/dist/build/worker/specifier.d.ts +1 -0
  74. package/dist/build/worker/specifier.js +5 -0
  75. package/dist/cloudflare.d.ts +1 -45
  76. package/dist/cloudflare.js +1 -202
  77. package/dist/deploy/deploy.d.ts +17 -0
  78. package/dist/deploy/deploy.js +221 -0
  79. package/dist/deploy/index.d.ts +1 -0
  80. package/dist/deploy/index.js +1 -0
  81. package/dist/deploy/output.d.ts +2 -0
  82. package/dist/deploy/output.js +10 -0
  83. package/dist/deploy/readiness.d.ts +5 -0
  84. package/dist/deploy/readiness.js +18 -0
  85. package/dist/identity/binding.d.ts +2 -0
  86. package/dist/identity/binding.js +2 -0
  87. package/dist/identity/index.d.ts +3 -0
  88. package/dist/identity/index.js +3 -0
  89. package/dist/identity/material.d.ts +7 -0
  90. package/dist/identity/material.js +42 -0
  91. package/dist/identity/secret.d.ts +3 -0
  92. package/dist/identity/secret.js +7 -0
  93. package/dist/identity/source.d.ts +26 -0
  94. package/dist/identity/source.js +127 -0
  95. package/dist/index.d.ts +10 -9
  96. package/dist/index.js +10 -9
  97. package/dist/params.d.ts +40 -36
  98. package/dist/params.js +0 -1
  99. package/dist/release/deployment.d.ts +44 -0
  100. package/dist/release/deployment.js +141 -0
  101. package/dist/release/index.d.ts +2 -0
  102. package/dist/release/index.js +2 -0
  103. package/dist/release/staged-worker-name.d.ts +3 -0
  104. package/dist/release/staged-worker-name.js +10 -0
  105. package/dist/worker/entry.d.ts +48 -0
  106. package/dist/worker/entry.js +157 -0
  107. package/dist/worker/fetch.d.ts +0 -1
  108. package/dist/worker/fetch.js +0 -1
  109. package/dist/worker/frontend.d.ts +4 -0
  110. package/dist/worker/frontend.js +46 -0
  111. package/dist/worker/identity.d.ts +7 -0
  112. package/dist/worker/identity.js +23 -0
  113. package/dist/worker/index.d.ts +2 -61
  114. package/dist/worker/index.js +2 -218
  115. package/dist/wrangler/bundle.d.ts +10 -0
  116. package/dist/wrangler/bundle.js +28 -0
  117. package/dist/wrangler/deploy.d.ts +24 -0
  118. package/dist/wrangler/deploy.js +44 -0
  119. package/dist/wrangler/index.d.ts +5 -0
  120. package/dist/wrangler/index.js +5 -0
  121. package/dist/wrangler/process.d.ts +15 -0
  122. package/dist/wrangler/process.js +102 -0
  123. package/dist/wrangler/rollout.d.ts +27 -0
  124. package/dist/wrangler/rollout.js +75 -0
  125. package/dist/wrangler/secrets.d.ts +2 -0
  126. package/dist/wrangler/secrets.js +24 -0
  127. package/dist/wrangler/status.d.ts +18 -0
  128. package/dist/wrangler/status.js +75 -0
  129. package/dist/wrangler/triggers.d.ts +5 -0
  130. package/dist/wrangler/triggers.js +12 -0
  131. package/dist/wrangler/type-generation.d.ts +6 -0
  132. package/dist/wrangler/type-generation.js +19 -0
  133. package/dist/wrangler/versions.d.ts +13 -0
  134. package/dist/wrangler/versions.js +38 -0
  135. package/package.json +34 -19
  136. package/template/astrale.config.ts +30 -31
  137. package/dist/build/codegen/identity.d.ts +0 -10
  138. package/dist/build/codegen/identity.d.ts.map +0 -1
  139. package/dist/build/codegen/identity.js +0 -32
  140. package/dist/build/codegen/identity.js.map +0 -1
  141. package/dist/build/codegen/merge.d.ts.map +0 -1
  142. package/dist/build/codegen/merge.js.map +0 -1
  143. package/dist/build/codegen/worker.d.ts +0 -12
  144. package/dist/build/codegen/worker.d.ts.map +0 -1
  145. package/dist/build/codegen/worker.js +0 -40
  146. package/dist/build/codegen/worker.js.map +0 -1
  147. package/dist/build/codegen/wrangler.d.ts.map +0 -1
  148. package/dist/build/codegen/wrangler.js.map +0 -1
  149. package/dist/build/frontend.d.ts +0 -27
  150. package/dist/build/frontend.d.ts.map +0 -1
  151. package/dist/build/frontend.js +0 -252
  152. package/dist/build/frontend.js.map +0 -1
  153. package/dist/build/index.d.ts.map +0 -1
  154. package/dist/build/index.js.map +0 -1
  155. package/dist/build/parse-output.d.ts +0 -27
  156. package/dist/build/parse-output.d.ts.map +0 -1
  157. package/dist/build/parse-output.js +0 -58
  158. package/dist/build/parse-output.js.map +0 -1
  159. package/dist/build/prepare.d.ts +0 -18
  160. package/dist/build/prepare.d.ts.map +0 -1
  161. package/dist/build/prepare.js +0 -138
  162. package/dist/build/prepare.js.map +0 -1
  163. package/dist/build/worker.d.ts +0 -15
  164. package/dist/build/worker.d.ts.map +0 -1
  165. package/dist/build/worker.js +0 -21
  166. package/dist/build/worker.js.map +0 -1
  167. package/dist/build/wrangler-cli.d.ts +0 -88
  168. package/dist/build/wrangler-cli.d.ts.map +0 -1
  169. package/dist/build/wrangler-cli.js +0 -283
  170. package/dist/build/wrangler-cli.js.map +0 -1
  171. package/dist/cloudflare.d.ts.map +0 -1
  172. package/dist/cloudflare.js.map +0 -1
  173. package/dist/index.d.ts.map +0 -1
  174. package/dist/index.js.map +0 -1
  175. package/dist/params.d.ts.map +0 -1
  176. package/dist/params.js.map +0 -1
  177. package/dist/worker/fetch.d.ts.map +0 -1
  178. package/dist/worker/fetch.js.map +0 -1
  179. package/dist/worker/index.d.ts.map +0 -1
  180. package/dist/worker/index.js.map +0 -1
  181. package/src/build/codegen/identity.ts +0 -40
  182. package/src/build/codegen/merge.ts +0 -92
  183. package/src/build/codegen/worker.ts +0 -53
  184. package/src/build/codegen/wrangler.ts +0 -128
  185. package/src/build/frontend.ts +0 -298
  186. package/src/build/index.ts +0 -29
  187. package/src/build/parse-output.ts +0 -60
  188. package/src/build/prepare.ts +0 -189
  189. package/src/build/worker.ts +0 -37
  190. package/src/build/wrangler-cli.ts +0 -372
  191. package/src/cloudflare.ts +0 -275
  192. package/src/index.ts +0 -21
  193. package/src/params.ts +0 -92
  194. package/src/worker/fetch.ts +0 -46
  195. package/src/worker/index.ts +0 -332
  196. package/src/worker/tsconfig.json +0 -10
@@ -0,0 +1,90 @@
1
+ import { isDeepStrictEqual } from 'node:util';
2
+ import { generateWranglerConfig } from './codegen/wrangler.js';
3
+ import { resolveRouter, workerName } from './configuration.js';
4
+ import { workerModules } from './worker/modules.js';
5
+ export function admitDirectPlacement(artifact, worker, parameters, environment = '') {
6
+ const name = workerName(parameters, worker.origin, environment);
7
+ const release = config(artifact, 'wrangler.jsonc');
8
+ const router = resolveRouter(parameters.router);
9
+ const frontend = artifact.build.frontend;
10
+ const hasViteFrontend = frontend?.source.kind === 'vite';
11
+ equal(worker.assets !== undefined, hasViteFrontend);
12
+ if (worker.assets !== undefined) {
13
+ equal(worker.assets.notFound, 'none');
14
+ equal(worker.assets.runWorkerFirst, true);
15
+ }
16
+ equal(release, generated({
17
+ workerName: name,
18
+ main: `./${worker.release.entrypoint}`,
19
+ ...(parameters.route === undefined ? {} : { route: parameters.route }),
20
+ vars: worker.bindings.vars,
21
+ secretBindings: worker.bindings.requiredSecrets,
22
+ modules: directModules(worker.release),
23
+ ...(hasViteFrontend
24
+ ? {
25
+ frontend: {
26
+ directory: './frontend',
27
+ },
28
+ }
29
+ : {}),
30
+ ...(router === undefined
31
+ ? {}
32
+ : { router: { binding: router.binding, service: router.service } }),
33
+ ...(parameters.wrangler === undefined ? {} : { wrangler: parameters.wrangler }),
34
+ selfBinding: false,
35
+ }));
36
+ equal(release.compatibility_date, worker.runtime.compatibilityDate);
37
+ equalStringCollection(release.compatibility_flags, worker.runtime.compatibilityFlags);
38
+ equal(release.services ?? [], worker.bindings.services.map(serviceBinding));
39
+ equal(record(release.assets).html_handling, worker.assets?.htmlHandling);
40
+ }
41
+ function directModules(worker) {
42
+ try {
43
+ return workerModules(worker);
44
+ }
45
+ catch {
46
+ mismatch();
47
+ }
48
+ }
49
+ function generated(input) {
50
+ return JSON.parse(generateWranglerConfig(input));
51
+ }
52
+ function config(artifact, path) {
53
+ const file = artifact.files.find((candidate) => candidate.path === path);
54
+ if (file === undefined)
55
+ throw new TypeError(`Direct Cloudflare artifact is missing ${path}.`);
56
+ try {
57
+ const value = JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(file.bytes()));
58
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
59
+ throw new Error();
60
+ return value;
61
+ }
62
+ catch (cause) {
63
+ throw new TypeError(`Direct Cloudflare artifact ${path} is invalid.`, { cause });
64
+ }
65
+ }
66
+ function serviceBinding(binding) {
67
+ return { binding: binding.name, service: binding.service };
68
+ }
69
+ function record(input) {
70
+ return input !== null && typeof input === 'object' && !Array.isArray(input)
71
+ ? input
72
+ : {};
73
+ }
74
+ function equal(actual, expected) {
75
+ if (!isDeepStrictEqual(actual, expected)) {
76
+ throw new TypeError('Direct Cloudflare placement differs from its admitted Worker artifact.', {
77
+ cause: { actual, expected },
78
+ });
79
+ }
80
+ }
81
+ function equalStringCollection(actual, expected) {
82
+ if (!Array.isArray(actual) ||
83
+ actual.some((value) => typeof value !== 'string') ||
84
+ !isDeepStrictEqual([...actual].sort(), [...expected].sort())) {
85
+ mismatch();
86
+ }
87
+ }
88
+ function mismatch() {
89
+ throw new TypeError('Direct Cloudflare placement differs from its admitted Worker artifact.');
90
+ }
@@ -0,0 +1,9 @@
1
+ import type { Build } from '@astrale-os/sdk/deployment/build';
2
+ import { frontend as frontendBuild } from '@astrale-os/sdk/deployment/build';
3
+ export declare function frontendFiles(input: {
4
+ readonly frontend: Build['frontend'];
5
+ readonly projectDir: string;
6
+ readonly output: string;
7
+ readonly recordSource?: (path: string) => void;
8
+ readonly signal: AbortSignal;
9
+ }): Promise<readonly frontendBuild.FrontendFile[]>;
@@ -0,0 +1,33 @@
1
+ import { frontend as frontendBuild } from '@astrale-os/sdk/deployment/build';
2
+ import { readFile, readdir, rm } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ import { buildViteFrontend } from './vite.js';
5
+ export async function frontendFiles(input) {
6
+ const frontend = input.frontend;
7
+ if (frontend === undefined)
8
+ return [];
9
+ if (frontend.source.kind === 'external')
10
+ return frontendBuild.files(frontend, []);
11
+ await rm(input.output, { recursive: true, force: true });
12
+ await buildViteFrontend({
13
+ projectDir: input.projectDir,
14
+ output: input.output,
15
+ ...(frontend.source.config === undefined ? {} : { config: frontend.source.config }),
16
+ recordSource: input.recordSource,
17
+ signal: input.signal,
18
+ });
19
+ return frontendBuild.files(frontend, await readFiles(input.output));
20
+ }
21
+ async function readFiles(root, current = '') {
22
+ const entries = await readdir(join(root, current), { withFileTypes: true });
23
+ const files = [];
24
+ for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
25
+ const path = current === '' ? entry.name : `${current}/${entry.name}`;
26
+ if (entry.isDirectory())
27
+ files.push(...(await readFiles(root, path)));
28
+ else if (entry.isFile()) {
29
+ files.push({ path, content: new Uint8Array(await readFile(join(root, path))) });
30
+ }
31
+ }
32
+ return files;
33
+ }
@@ -0,0 +1,10 @@
1
+ import type { Release } from '@astrale-os/sdk/deployment/release';
2
+ import type { verifyReadiness } from '@astrale-os/sdk/deployment/verify';
3
+ import type { WorkerArtifact } from '../worker/artifact.js';
4
+ type ReadinessFetch = NonNullable<NonNullable<Parameters<typeof verifyReadiness>[1]>['fetch']>;
5
+ /** Publication equality alone cannot prove a frontend-only deployment has propagated. */
6
+ export declare function verifyWorkerFrontend(release: Release, worker: Pick<WorkerArtifact, 'assets'>, options?: {
7
+ readonly fetch?: ReadinessFetch;
8
+ readonly signal?: AbortSignal;
9
+ }): Promise<void>;
10
+ export {};
@@ -0,0 +1,21 @@
1
+ import { frontendRevision } from './revision.js';
2
+ /** Publication equality alone cannot prove a frontend-only deployment has propagated. */
3
+ export async function verifyWorkerFrontend(release, worker, options = {}) {
4
+ const frontend = release.build.frontend;
5
+ if (frontend === undefined || frontend.source.kind === 'external')
6
+ return;
7
+ if (worker.assets === undefined)
8
+ throw new Error('Prepared Worker frontend assets are missing.');
9
+ const expected = frontendRevision(worker.assets.files);
10
+ const fetcher = options.fetch ?? globalThis.fetch;
11
+ for (const route of frontend.routes) {
12
+ const response = await fetcher(new URL(route.path, release.addressing.issuer).href, {
13
+ redirect: 'error',
14
+ ...(options.signal === undefined ? {} : { signal: options.signal }),
15
+ });
16
+ const matches = response.status === 200 && response.headers.get('X-Astrale-Frontend') === expected;
17
+ await response.body?.cancel();
18
+ if (!matches)
19
+ throw new Error(`Frontend ${route.path} is not serving the prepared browser build yet.`);
20
+ }
21
+ }
@@ -0,0 +1,5 @@
1
+ /** Bind the generated Worker to its exact browser files, independently of Schema revision. */
2
+ export declare function frontendRevision(files: readonly {
3
+ readonly path: string;
4
+ bytes(): Uint8Array;
5
+ }[]): string;
@@ -0,0 +1,14 @@
1
+ import { createHash } from 'node:crypto';
2
+ /** Bind the generated Worker to its exact browser files, independently of Schema revision. */
3
+ export function frontendRevision(files) {
4
+ const hash = createHash('sha256');
5
+ const ordered = files
6
+ .map((file) => ({ path: file.path.replace(/^\//u, ''), bytes: file.bytes }))
7
+ .sort((a, b) => (a.path < b.path ? -1 : a.path > b.path ? 1 : 0));
8
+ for (const file of ordered) {
9
+ const bytes = file.bytes();
10
+ hash.update(JSON.stringify([file.path, bytes.byteLength]));
11
+ hash.update(bytes);
12
+ }
13
+ return `sha256:${hash.digest('hex')}`;
14
+ }
@@ -0,0 +1,8 @@
1
+ /** Build a Vite frontend in an isolated process and report its authored inputs. */
2
+ export declare function buildViteFrontend(input: {
3
+ readonly projectDir: string;
4
+ readonly output: string;
5
+ readonly config?: string;
6
+ readonly recordSource?: (path: string) => void;
7
+ readonly signal: AbortSignal;
8
+ }): Promise<void>;
@@ -0,0 +1,32 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { mkdir, mkdtemp, readFile, rm } from 'node:fs/promises';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { runViteProcess } from './vite.process.js';
6
+ /** Build a Vite frontend in an isolated process and report its authored inputs. */
7
+ export async function buildViteFrontend(input) {
8
+ const parent = dirname(input.output);
9
+ await mkdir(parent, { recursive: true });
10
+ const scratch = await mkdtemp(join(parent, 'vite-run-'));
11
+ const evidence = join(scratch, 'sources.json');
12
+ try {
13
+ await runViteProcess({
14
+ cwd: input.projectDir,
15
+ arguments: [runnerPath(), input.output, evidence, input.config ?? ''],
16
+ signal: input.signal,
17
+ });
18
+ const sources = JSON.parse(await readFile(evidence, 'utf8'));
19
+ if (!Array.isArray(sources) || sources.some((path) => typeof path !== 'string')) {
20
+ throw new TypeError('Vite preparation source evidence is invalid.');
21
+ }
22
+ for (const path of sources)
23
+ input.recordSource?.(path);
24
+ }
25
+ finally {
26
+ await rm(scratch, { recursive: true, force: true });
27
+ }
28
+ }
29
+ function runnerPath() {
30
+ const built = fileURLToPath(new URL('./vite.runner.js', import.meta.url));
31
+ return existsSync(built) ? built : fileURLToPath(new URL('./vite.runner.ts', import.meta.url));
32
+ }
@@ -0,0 +1,7 @@
1
+ /** Run one bounded, cancellable Vite subprocess. */
2
+ export declare function runViteProcess(input: {
3
+ readonly cwd: string;
4
+ readonly arguments: readonly string[];
5
+ readonly signal: AbortSignal;
6
+ readonly timeoutMs?: number;
7
+ }): Promise<void>;
@@ -0,0 +1,65 @@
1
+ import { spawn } from 'node:child_process';
2
+ const MAXIMUM_OUTPUT_BYTES = 1024 * 1024;
3
+ const TERMINATION_GRACE_MS = 1_000;
4
+ const VITE_TIMEOUT_MS = 300_000;
5
+ /** Run one bounded, cancellable Vite subprocess. */
6
+ export function runViteProcess(input) {
7
+ if (input.signal.aborted)
8
+ return Promise.reject(abortReason(input.signal));
9
+ return new Promise((resolve, reject) => {
10
+ const child = spawn(process.execPath, input.arguments, {
11
+ cwd: input.cwd,
12
+ stdio: ['ignore', 'pipe', 'pipe'],
13
+ windowsHide: true,
14
+ });
15
+ let output = '';
16
+ let outputBytes = 0;
17
+ let terminating;
18
+ let escalation;
19
+ const timeout = setTimeout(() => terminate(new Error('Vite process timed out.')), input.timeoutMs ?? VITE_TIMEOUT_MS);
20
+ const abort = () => terminate(abortReason(input.signal));
21
+ input.signal.addEventListener('abort', abort, { once: true });
22
+ const cleanup = () => {
23
+ clearTimeout(timeout);
24
+ if (escalation !== undefined)
25
+ clearTimeout(escalation);
26
+ input.signal.removeEventListener('abort', abort);
27
+ };
28
+ function terminate(cause) {
29
+ if (terminating !== undefined)
30
+ return;
31
+ terminating = cause;
32
+ child.kill('SIGTERM');
33
+ escalation = setTimeout(() => {
34
+ if (child.exitCode === null && child.signalCode === null)
35
+ child.kill('SIGKILL');
36
+ }, TERMINATION_GRACE_MS);
37
+ }
38
+ const collect = (buffer) => {
39
+ outputBytes += buffer.byteLength;
40
+ if (outputBytes > MAXIMUM_OUTPUT_BYTES) {
41
+ terminate(new Error('Vite process exceeded its output limit.'));
42
+ return;
43
+ }
44
+ output += buffer.toString();
45
+ };
46
+ child.stdout.on('data', collect);
47
+ child.stderr.on('data', collect);
48
+ child.once('error', (cause) => {
49
+ cleanup();
50
+ reject(terminating ?? cause);
51
+ });
52
+ child.once('close', (code) => {
53
+ cleanup();
54
+ if (terminating !== undefined)
55
+ reject(terminating);
56
+ else if (code === 0)
57
+ resolve();
58
+ else
59
+ reject(new Error(`Vite failed (code ${code ?? 1}):\n${output.slice(-1500)}`));
60
+ });
61
+ });
62
+ }
63
+ function abortReason(signal) {
64
+ return signal.reason instanceof Error ? signal.reason : new Error('Vite process cancelled.');
65
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,120 @@
1
+ import { readdir, writeFile } from 'node:fs/promises';
2
+ import { createRequire } from 'node:module';
3
+ import { isAbsolute, join, resolve } from 'node:path';
4
+ import { fileURLToPath, pathToFileURL } from 'node:url';
5
+ const CONFIG_FILES = Object.freeze([
6
+ 'vite.config.js',
7
+ 'vite.config.mjs',
8
+ 'vite.config.ts',
9
+ 'vite.config.cjs',
10
+ 'vite.config.mts',
11
+ 'vite.config.cts',
12
+ ]);
13
+ await main();
14
+ async function main() {
15
+ const [output, evidence, configured] = process.argv.slice(2);
16
+ if (output === undefined || evidence === undefined) {
17
+ throw new TypeError('Vite preparation runner arguments are invalid.');
18
+ }
19
+ const projectDir = process.cwd();
20
+ const vite = await projectVite(projectDir);
21
+ const sources = new Set();
22
+ let publicDir = false;
23
+ let environment = {
24
+ directory: false,
25
+ mode: 'production',
26
+ };
27
+ const config = configured === '' || configured === undefined ? undefined : configured;
28
+ await vite.build({
29
+ logLevel: 'silent',
30
+ ...(config === undefined
31
+ ? {}
32
+ : { configFile: isAbsolute(config) ? config : resolve(projectDir, config) }),
33
+ build: { outDir: output, emptyOutDir: true },
34
+ plugins: [
35
+ {
36
+ name: 'astrale:preparation-sources',
37
+ configResolved(value) {
38
+ publicDir = value.publicDir;
39
+ environment = { directory: value.envDir, mode: value.mode };
40
+ for (const path of value.configFileDependencies)
41
+ sources.add(path);
42
+ },
43
+ transform(_source, id) {
44
+ const path = physicalModulePath(id);
45
+ if (path !== undefined)
46
+ sources.add(path);
47
+ },
48
+ },
49
+ ],
50
+ });
51
+ for (const path of configCandidates(projectDir, config))
52
+ sources.add(path);
53
+ for (const path of environmentCandidates(environment))
54
+ sources.add(path);
55
+ if (publicDir !== false)
56
+ await collectTree(publicDir, sources);
57
+ await writeFile(evidence, JSON.stringify([...sources]));
58
+ }
59
+ function environmentCandidates(input) {
60
+ const directory = input.directory;
61
+ if (directory === false)
62
+ return [];
63
+ return ['.env', '.env.local', `.env.${input.mode}`, `.env.${input.mode}.local`].map((name) => join(directory, name));
64
+ }
65
+ function physicalModulePath(id) {
66
+ if (id.startsWith('\0'))
67
+ return undefined;
68
+ const path = id.replace(/[?#].*$/u, '');
69
+ if (path.length === 0)
70
+ return undefined;
71
+ if (path.startsWith('file:')) {
72
+ try {
73
+ return fileURLToPath(path);
74
+ }
75
+ catch {
76
+ return undefined;
77
+ }
78
+ }
79
+ return isAbsolute(path) ? path : undefined;
80
+ }
81
+ async function projectVite(projectDir) {
82
+ try {
83
+ const manifest = createRequire(join(projectDir, 'package.json')).resolve('vite/package.json');
84
+ const entry = createRequire(manifest).resolve('vite');
85
+ return (await import(pathToFileURL(entry).href));
86
+ }
87
+ catch (cause) {
88
+ throw new TypeError('Vite frontend requires a project-local Vite package.', { cause });
89
+ }
90
+ }
91
+ function configCandidates(projectDir, configured) {
92
+ if (configured !== undefined) {
93
+ return [isAbsolute(configured) ? configured : resolve(projectDir, configured)];
94
+ }
95
+ return CONFIG_FILES.map((name) => join(projectDir, name));
96
+ }
97
+ async function collectTree(path, output) {
98
+ output.add(path);
99
+ let entries;
100
+ try {
101
+ entries = await readdir(path, { withFileTypes: true });
102
+ }
103
+ catch (cause) {
104
+ if (errorCode(cause) === 'ENOENT')
105
+ return;
106
+ throw cause;
107
+ }
108
+ for (const entry of entries) {
109
+ const child = join(path, entry.name);
110
+ if (entry.isDirectory())
111
+ await collectTree(child, output);
112
+ else if (entry.isFile())
113
+ output.add(child);
114
+ }
115
+ }
116
+ function errorCode(input) {
117
+ return input !== null && typeof input === 'object' && 'code' in input
118
+ ? String(Reflect.get(input, 'code'))
119
+ : undefined;
120
+ }
@@ -1,16 +1,9 @@
1
- /**
2
- * `@astrale-os/adapter-cloudflare/build` — the shared Cloudflare-Workers build
3
- * toolkit, exposed for sibling deploy adapters (e.g.
4
- * `@astrale-os/adapter-astrale`).
5
- *
6
- * This is the "compile a `defineDomain` into a Cloudflare Workers bundle + run
7
- * local dev" surface that every Cloudflare-Workers deploy strategy shares,
8
- * regardless of WHERE the bundle ultimately ships. It is NOT part of the public
9
- * adapter API (`@astrale-os/adapter-cloudflare`) — it's the seam two adapters
10
- * build on so the codegen/bundling/dev logic lives in exactly one place.
11
- */
12
- export { buildFrontend, FRONTEND_STAGE_DIR, startFrontendDev, type FrontendDevHandle, type ViteBuild, type ViteBuildInput, } from './frontend.js';
13
- export { logTo, prepare } from './prepare.js';
14
- export { buildCloudflareWorker, type CloudflareWorkerBuild } from './worker.js';
15
- export { bulkPutSecrets, runWranglerBundle, runWranglerDeploy, runWranglerDev, runWranglerTypes, } from './wrangler-cli.js';
16
- //# sourceMappingURL=index.d.ts.map
1
+ export { artifact, materialize, prepareWorkerArtifact } from './artifact.js';
2
+ export { loadWorkerArtifact } from './worker/index.js';
3
+ export type { WorkerArtifact } from './worker/index.js';
4
+ export { resolveRouter, workerName } from './configuration.js';
5
+ export { frontendFiles } from './frontend/files.js';
6
+ export { verifyWorkerFrontend } from './frontend/readiness.js';
7
+ export { qualifyWorkerBundle } from './qualification.js';
8
+ export { assertSingletonRuntimeRoots } from './roots.js';
9
+ export { runWranglerBundle, runWranglerDeploy, runWranglerDeploymentStatus, runWranglerTypes, } from '../wrangler/index.js';
@@ -1,16 +1,8 @@
1
- /**
2
- * `@astrale-os/adapter-cloudflare/build` — the shared Cloudflare-Workers build
3
- * toolkit, exposed for sibling deploy adapters (e.g.
4
- * `@astrale-os/adapter-astrale`).
5
- *
6
- * This is the "compile a `defineDomain` into a Cloudflare Workers bundle + run
7
- * local dev" surface that every Cloudflare-Workers deploy strategy shares,
8
- * regardless of WHERE the bundle ultimately ships. It is NOT part of the public
9
- * adapter API (`@astrale-os/adapter-cloudflare`) — it's the seam two adapters
10
- * build on so the codegen/bundling/dev logic lives in exactly one place.
11
- */
12
- export { buildFrontend, FRONTEND_STAGE_DIR, startFrontendDev, } from './frontend.js';
13
- export { logTo, prepare } from './prepare.js';
14
- export { buildCloudflareWorker } from './worker.js';
15
- export { bulkPutSecrets, runWranglerBundle, runWranglerDeploy, runWranglerDev, runWranglerTypes, } from './wrangler-cli.js';
16
- //# sourceMappingURL=index.js.map
1
+ export { artifact, materialize, prepareWorkerArtifact } from './artifact.js';
2
+ export { loadWorkerArtifact } from './worker/index.js';
3
+ export { resolveRouter, workerName } from './configuration.js';
4
+ export { frontendFiles } from './frontend/files.js';
5
+ export { verifyWorkerFrontend } from './frontend/readiness.js';
6
+ export { qualifyWorkerBundle } from './qualification.js';
7
+ export { assertSingletonRuntimeRoots } from './roots.js';
8
+ export { runWranglerBundle, runWranglerDeploy, runWranglerDeploymentStatus, runWranglerTypes, } from '../wrangler/index.js';
@@ -0,0 +1,4 @@
1
+ export declare function materialize(root: string, files: readonly {
2
+ readonly path: string;
3
+ bytes(): Uint8Array;
4
+ }[]): Promise<void>;
@@ -0,0 +1,10 @@
1
+ import { mkdir, rm, writeFile } from 'node:fs/promises';
2
+ import { dirname, join } from 'node:path';
3
+ export async function materialize(root, files) {
4
+ await rm(root, { recursive: true, force: true });
5
+ for (const file of files) {
6
+ const path = join(root, file.path);
7
+ await mkdir(dirname(path), { recursive: true });
8
+ await writeFile(path, file.bytes());
9
+ }
10
+ }
@@ -0,0 +1,5 @@
1
+ export interface WranglerMetafile {
2
+ readonly inputs: Readonly<Record<string, unknown>>;
3
+ }
4
+ /** Decode Wrangler's emitted esbuild graph at the provider-tooling boundary. */
5
+ export declare function readWranglerMetafile(path: string): Promise<WranglerMetafile>;
@@ -0,0 +1,14 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ /** Decode Wrangler's emitted esbuild graph at the provider-tooling boundary. */
3
+ export async function readWranglerMetafile(path) {
4
+ const source = await readFile(path, 'utf8');
5
+ const value = JSON.parse(source);
6
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) {
7
+ throw new TypeError('Wrangler metafile is not an object.');
8
+ }
9
+ const inputs = Reflect.get(value, 'inputs');
10
+ if (inputs === null || typeof inputs !== 'object' || Array.isArray(inputs)) {
11
+ throw new TypeError('Wrangler metafile has no input graph.');
12
+ }
13
+ return Object.freeze({ inputs: inputs });
14
+ }
@@ -0,0 +1,5 @@
1
+ /** Qualify Wrangler's emitted module, not generated source or an inferred graph. */
2
+ export declare function qualifyWorkerBundle(evidence: {
3
+ readonly metafilePath: string;
4
+ readonly inputRoot: string;
5
+ }): Promise<void>;
@@ -0,0 +1,7 @@
1
+ import { assertRuntimeClosure } from './closure.js';
2
+ import { assertSingletonRuntimeRoots } from './roots.js';
3
+ /** Qualify Wrangler's emitted module, not generated source or an inferred graph. */
4
+ export async function qualifyWorkerBundle(evidence) {
5
+ await assertRuntimeClosure(evidence.metafilePath);
6
+ await assertSingletonRuntimeRoots(evidence.metafilePath, evidence.inputRoot);
7
+ }
@@ -0,0 +1,2 @@
1
+ /** Prove that Wrangler emitted at most one physical root per runtime package. */
2
+ export declare function assertSingletonRuntimeRoots(metafilePath: string, inputRoot: string): Promise<void>;
@@ -0,0 +1,34 @@
1
+ import { realpath } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+ import { readWranglerMetafile } from './metafile.js';
4
+ const RUNTIME_PACKAGES = [
5
+ '@astrale-os/adapter-cloudflare',
6
+ '@astrale-os/sdk',
7
+ '@astrale-os/kernel-core',
8
+ '@astrale-os/kernel-dsl',
9
+ '@astrale-os/kernel-ports',
10
+ '@astrale-os/kernel-protocol',
11
+ '@astrale-os/kernel-server',
12
+ '@astrale-os/kernel-client',
13
+ ];
14
+ /** Prove that Wrangler emitted at most one physical root per runtime package. */
15
+ export async function assertSingletonRuntimeRoots(metafilePath, inputRoot) {
16
+ const metafile = await readWranglerMetafile(metafilePath);
17
+ for (const name of RUNTIME_PACKAGES) {
18
+ const roots = new Set();
19
+ for (const input of Object.keys(metafile.inputs)) {
20
+ const root = packageRoot(input, name);
21
+ if (root !== undefined)
22
+ roots.add(await realpath(resolve(inputRoot, root)));
23
+ }
24
+ if (roots.size > 1) {
25
+ throw new Error(`Cloudflare Worker contains duplicate physical ${name} roots: ${[...roots].join(', ')}.`);
26
+ }
27
+ }
28
+ }
29
+ function packageRoot(input, name) {
30
+ const normalized = input.replaceAll('\\', '/');
31
+ const marker = `node_modules/${name}/`;
32
+ const index = normalized.lastIndexOf(marker);
33
+ return index < 0 ? undefined : normalized.slice(0, index + marker.length - 1);
34
+ }
@@ -0,0 +1,40 @@
1
+ import type { WorkerDeploymentId } from '../../release/deployment.js';
2
+ export type WorkerModuleKind = 'esm' | 'wasm' | 'text' | 'data';
3
+ export interface WorkerArtifactModule {
4
+ readonly path: string;
5
+ readonly kind: WorkerModuleKind;
6
+ bytes(): Uint8Array;
7
+ }
8
+ export interface WorkerArtifactExecutable {
9
+ readonly entrypoint: string;
10
+ readonly modules: readonly WorkerArtifactModule[];
11
+ }
12
+ export interface WorkerArtifact {
13
+ readonly origin: string;
14
+ readonly revision: string;
15
+ readonly deployment: WorkerDeploymentId;
16
+ readonly release: WorkerArtifactExecutable;
17
+ readonly runtime: {
18
+ readonly compatibilityDate: string;
19
+ readonly compatibilityFlags: readonly string[];
20
+ };
21
+ readonly bindings: {
22
+ readonly vars: Readonly<Record<string, string>>;
23
+ readonly services: readonly {
24
+ readonly name: string;
25
+ readonly service: string;
26
+ }[];
27
+ readonly requiredSecrets: readonly string[];
28
+ };
29
+ readonly assets?: {
30
+ readonly files: readonly {
31
+ readonly path: string;
32
+ readonly artifactPath: string;
33
+ readonly contentType?: string;
34
+ bytes(): Uint8Array;
35
+ }[];
36
+ readonly notFound: 'none' | '404-page' | 'single-page-application';
37
+ readonly runWorkerFirst: boolean;
38
+ readonly htmlHandling?: 'none';
39
+ };
40
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import type { PreparedArtifact } from '@astrale-os/sdk/deployment/adapter';
2
+ import type { WorkerArtifact } from './artifact.js';
3
+ export declare function loadWorkerArtifact(artifact: PreparedArtifact): WorkerArtifact;