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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/README.md +11 -0
  2. package/dist/adapter.d.ts +17 -0
  3. package/dist/adapter.js +140 -0
  4. package/dist/build/artifact.d.ts +8 -0
  5. package/dist/build/artifact.js +179 -0
  6. package/dist/build/closure.d.ts +2 -0
  7. package/dist/build/closure.js +22 -0
  8. package/dist/build/codegen/execution.d.ts +13 -0
  9. package/dist/build/codegen/execution.js +44 -0
  10. package/dist/build/codegen/gateway.d.ts +1 -0
  11. package/dist/build/codegen/gateway.js +7 -0
  12. package/dist/build/codegen/merge.d.ts +0 -1
  13. package/dist/build/codegen/merge.js +0 -1
  14. package/dist/build/codegen/wrangler.d.ts +10 -1
  15. package/dist/build/codegen/wrangler.js +22 -16
  16. package/dist/build/configuration.d.ts +9 -0
  17. package/dist/build/configuration.js +40 -0
  18. package/dist/build/frontend-v1.d.ts +16 -0
  19. package/dist/build/frontend-v1.js +133 -0
  20. package/dist/build/index.d.ts +8 -16
  21. package/dist/build/index.js +8 -16
  22. package/dist/build/metafile.d.ts +5 -0
  23. package/dist/build/metafile.js +14 -0
  24. package/dist/build/parse-output.d.ts +0 -1
  25. package/dist/build/parse-output.js +0 -1
  26. package/dist/build/qualification.d.ts +5 -0
  27. package/dist/build/qualification.js +7 -0
  28. package/dist/build/roots.d.ts +2 -0
  29. package/dist/build/roots.js +34 -0
  30. package/dist/build/wrangler-cli.d.ts +53 -34
  31. package/dist/build/wrangler-cli.js +284 -111
  32. package/dist/cloudflare.d.ts +1 -34
  33. package/dist/cloudflare.js +1 -205
  34. package/dist/deploy/deploy.d.ts +17 -0
  35. package/dist/deploy/deploy.js +207 -0
  36. package/dist/deploy/index.d.ts +1 -0
  37. package/dist/deploy/index.js +1 -0
  38. package/dist/identity/binding.d.ts +2 -0
  39. package/dist/identity/binding.js +2 -0
  40. package/dist/identity/material.d.ts +7 -0
  41. package/dist/identity/material.js +42 -0
  42. package/dist/identity/secret.d.ts +3 -0
  43. package/dist/identity/secret.js +7 -0
  44. package/dist/identity/source.d.ts +26 -0
  45. package/dist/identity/source.js +98 -0
  46. package/dist/index.d.ts +3 -4
  47. package/dist/index.js +3 -4
  48. package/dist/params.d.ts +18 -5
  49. package/dist/params.js +0 -1
  50. package/dist/release/index.d.ts +2 -0
  51. package/dist/release/index.js +2 -0
  52. package/dist/release/namespace.d.ts +2 -0
  53. package/dist/release/namespace.js +6 -0
  54. package/dist/release/target.d.ts +2 -0
  55. package/dist/release/target.js +9 -0
  56. package/dist/worker/entry.d.ts +34 -0
  57. package/dist/worker/entry.js +157 -0
  58. package/dist/worker/fetch.d.ts +11 -0
  59. package/dist/worker/fetch.js +31 -0
  60. package/dist/worker/gateway.d.ts +15 -0
  61. package/dist/worker/gateway.js +26 -0
  62. package/dist/worker/identity.d.ts +7 -0
  63. package/dist/worker/identity.js +23 -0
  64. package/dist/worker/index.d.ts +3 -48
  65. package/dist/worker/index.js +3 -186
  66. package/package.json +16 -18
  67. package/template/astrale.config.ts +19 -11
  68. package/dist/build/codegen/identity.d.ts +0 -10
  69. package/dist/build/codegen/identity.d.ts.map +0 -1
  70. package/dist/build/codegen/identity.js +0 -32
  71. package/dist/build/codegen/identity.js.map +0 -1
  72. package/dist/build/codegen/merge.d.ts.map +0 -1
  73. package/dist/build/codegen/merge.js.map +0 -1
  74. package/dist/build/codegen/worker.d.ts +0 -12
  75. package/dist/build/codegen/worker.d.ts.map +0 -1
  76. package/dist/build/codegen/worker.js +0 -40
  77. package/dist/build/codegen/worker.js.map +0 -1
  78. package/dist/build/codegen/wrangler.d.ts.map +0 -1
  79. package/dist/build/codegen/wrangler.js.map +0 -1
  80. package/dist/build/frontend.d.ts +0 -26
  81. package/dist/build/frontend.d.ts.map +0 -1
  82. package/dist/build/frontend.js +0 -263
  83. package/dist/build/frontend.js.map +0 -1
  84. package/dist/build/index.d.ts.map +0 -1
  85. package/dist/build/index.js.map +0 -1
  86. package/dist/build/parse-output.d.ts.map +0 -1
  87. package/dist/build/parse-output.js.map +0 -1
  88. package/dist/build/prepare.d.ts +0 -18
  89. package/dist/build/prepare.d.ts.map +0 -1
  90. package/dist/build/prepare.js +0 -117
  91. package/dist/build/prepare.js.map +0 -1
  92. package/dist/build/worker.d.ts +0 -15
  93. package/dist/build/worker.d.ts.map +0 -1
  94. package/dist/build/worker.js +0 -21
  95. package/dist/build/worker.js.map +0 -1
  96. package/dist/build/wrangler-cli.d.ts.map +0 -1
  97. package/dist/build/wrangler-cli.js.map +0 -1
  98. package/dist/cloudflare.d.ts.map +0 -1
  99. package/dist/cloudflare.js.map +0 -1
  100. package/dist/index.d.ts.map +0 -1
  101. package/dist/index.js.map +0 -1
  102. package/dist/params.d.ts.map +0 -1
  103. package/dist/params.js.map +0 -1
  104. package/dist/worker/index.d.ts.map +0 -1
  105. package/dist/worker/index.js.map +0 -1
  106. package/src/build/codegen/identity.ts +0 -40
  107. package/src/build/codegen/merge.ts +0 -92
  108. package/src/build/codegen/worker.ts +0 -53
  109. package/src/build/codegen/wrangler.ts +0 -127
  110. package/src/build/frontend.ts +0 -324
  111. package/src/build/index.ts +0 -29
  112. package/src/build/parse-output.ts +0 -60
  113. package/src/build/prepare.ts +0 -161
  114. package/src/build/worker.ts +0 -37
  115. package/src/build/wrangler-cli.ts +0 -362
  116. package/src/cloudflare.ts +0 -251
  117. package/src/index.ts +0 -21
  118. package/src/params.ts +0 -92
  119. package/src/worker/index.ts +0 -276
  120. package/src/worker/tsconfig.json +0 -10
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # `@astrale-os/adapter-cloudflare`
2
+
3
+ Cloudflare Worker deployment adapter for Astrale Domains.
4
+
5
+ During the SDK beta program, install the beta package set explicitly:
6
+
7
+ ```bash
8
+ npm install @astrale-os/sdk@beta @astrale-os/adapter-cloudflare@beta
9
+ ```
10
+
11
+ Bare installs return to stable releases when the package set is promoted and npm `latest` advances.
@@ -0,0 +1,17 @@
1
+ import type { Release } from '@astrale-os/sdk/deployment/release';
2
+ import { verifyReadiness } from '@astrale-os/sdk/deployment/verify';
3
+ import type { CloudflareParams } from './params.js';
4
+ import { artifact } from './build/artifact.js';
5
+ import { startFrontend } from './build/frontend-v1.js';
6
+ import { runWranglerDev } from './build/wrangler-cli.js';
7
+ import { type CloudflareDeployDependencies } from './deploy/index.js';
8
+ import { signingIdentity } from './identity/source.js';
9
+ export interface CloudflareDependencies extends CloudflareDeployDependencies {
10
+ readonly artifact: typeof artifact;
11
+ readonly runWranglerDev: typeof runWranglerDev;
12
+ readonly startFrontend: typeof startFrontend;
13
+ readonly signingIdentity: typeof signingIdentity;
14
+ }
15
+ export declare function cloudflare(environments: Readonly<Record<string, CloudflareParams>>): import("@astrale-os/sdk/deployment/adapter").Adapter<CloudflareParams>;
16
+ export declare function cloudflareWithDependencies(environments: Readonly<Record<string, CloudflareParams>>, dependencies: CloudflareDependencies): import("@astrale-os/sdk/deployment/adapter").Adapter<CloudflareParams>;
17
+ export declare function waitForReadiness(verify: typeof verifyReadiness, release: Release): Promise<void>;
@@ -0,0 +1,140 @@
1
+ import { defineAdapter } from '@astrale-os/sdk/deployment/adapter';
2
+ import { verifyReadiness } from '@astrale-os/sdk/deployment/verify';
3
+ import { writeFile } from 'node:fs/promises';
4
+ import { createRequire } from 'node:module';
5
+ import { join } from 'node:path';
6
+ import { artifact, materialize } from './build/artifact.js';
7
+ import { generateWranglerConfig } from './build/codegen/wrangler.js';
8
+ import { resolveRouter, workerName } from './build/configuration.js';
9
+ import { startFrontend } from './build/frontend-v1.js';
10
+ import { ensureWranglerDispatchNamespace, runWranglerDeploymentStatus, runWranglerDeploy, runWranglerDev, runWranglerReleaseDeploy, } from './build/wrangler-cli.js';
11
+ import { deployCloudflare } from './deploy/index.js';
12
+ import { signingIdentitySecrets } from './identity/secret.js';
13
+ import { signingIdentity } from './identity/source.js';
14
+ const packageVersion = createRequire(import.meta.url)('../package.json')
15
+ .version;
16
+ export function cloudflare(environments) {
17
+ return cloudflareWithDependencies(environments, {
18
+ artifact,
19
+ materialize,
20
+ ensureWranglerDispatchNamespace,
21
+ runWranglerDeploy,
22
+ runWranglerReleaseDeploy,
23
+ runWranglerDeploymentStatus,
24
+ runWranglerDev,
25
+ startFrontend,
26
+ signingIdentity,
27
+ waitForReadiness: (release) => waitForReadiness(verifyReadiness, release),
28
+ });
29
+ }
30
+ export function cloudflareWithDependencies(environments, dependencies) {
31
+ return defineAdapter({
32
+ name: 'cloudflare',
33
+ version: packageVersion,
34
+ environments,
35
+ prepare: dependencies.artifact,
36
+ async watch(parameters, context) {
37
+ const identity = await dependencies.signingIdentity({
38
+ projectDir: context.projectDir,
39
+ source: parameters.signingIdentity,
40
+ origin: context.artifact.build.schema.compiled.root.origin,
41
+ mode: 'local',
42
+ });
43
+ const secrets = signingIdentitySecrets(context.secrets, identity);
44
+ const directory = join(context.projectDir, '.astrale', 'runtime');
45
+ await dependencies.materialize(directory, context.artifact.files);
46
+ const frontend = await dependencies.startFrontend({
47
+ frontend: context.artifact.build.frontend,
48
+ projectDir: context.projectDir,
49
+ });
50
+ const name = workerName(parameters, context.artifact.build.schema.compiled.root.origin);
51
+ const router = resolveRouter(parameters.router);
52
+ const configPath = join(directory, 'wrangler.watch.jsonc');
53
+ await writeFile(configPath, generateWranglerConfig({
54
+ workerName: name,
55
+ vars: {
56
+ ...parameters.vars,
57
+ ...(parameters.host === undefined ? {} : { WORKER_URL: parameters.host }),
58
+ ...(frontend === undefined ? {} : { FRONTEND_DEV_URL: frontend.url }),
59
+ },
60
+ secretBindings: Object.keys(secrets).sort(),
61
+ ...(context.artifact.build.frontend?.source.kind === 'vite'
62
+ ? {
63
+ frontend: {
64
+ directory: './frontend',
65
+ routes: frontendRoutes(context.artifact),
66
+ dev: true,
67
+ },
68
+ }
69
+ : {}),
70
+ ...(router === undefined
71
+ ? {}
72
+ : { router: { binding: router.binding, service: router.service } }),
73
+ ...(parameters.wrangler === undefined ? {} : { wrangler: parameters.wrangler }),
74
+ selfBinding: true,
75
+ }));
76
+ let worker;
77
+ try {
78
+ worker = await dependencies.runWranglerDev({
79
+ projectDir: context.projectDir,
80
+ configPath,
81
+ port: parameters.port ?? 8787,
82
+ remote: parameters.remote ?? false,
83
+ secrets,
84
+ autoPickPort: parameters.port === undefined,
85
+ ...(parameters.host === undefined ? {} : { ip: '0.0.0.0' }),
86
+ onReload: context.onReload,
87
+ });
88
+ }
89
+ catch (cause) {
90
+ await frontend?.stop().catch(() => undefined);
91
+ throw cause;
92
+ }
93
+ return Object.freeze({
94
+ url: parameters.host ?? worker.url,
95
+ localUrl: worker.url,
96
+ async stop() {
97
+ const results = await Promise.allSettled([
98
+ worker.stop(),
99
+ frontend?.stop() ?? Promise.resolve(),
100
+ ]);
101
+ const failures = results.filter((result) => result.status === 'rejected');
102
+ if (failures.length === 1)
103
+ throw failures[0].reason;
104
+ if (failures.length > 1) {
105
+ throw new AggregateError(failures.map((failure) => failure.reason), 'Cloudflare development cleanup failed.');
106
+ }
107
+ },
108
+ });
109
+ },
110
+ deploy: (parameters, context) => deployCloudflare(parameters, context, dependencies),
111
+ secretsFile: (parameters) => parameters.secrets,
112
+ });
113
+ }
114
+ function frontendRoutes(artifact) {
115
+ const frontend = artifact.build.frontend;
116
+ if (frontend === undefined || frontend.source.kind !== 'vite')
117
+ return [];
118
+ return Object.freeze(frontend.routes
119
+ .flatMap((route) => frontend.source.kind === 'vite' && frontend.source.spa && route.path !== '/'
120
+ ? [route.path, `${route.path}/*`]
121
+ : route.path === '/'
122
+ ? ['/*']
123
+ : [route.path])
124
+ .sort());
125
+ }
126
+ export async function waitForReadiness(verify, release) {
127
+ const deadline = Date.now() + 90_000;
128
+ let last;
129
+ while (Date.now() < deadline) {
130
+ try {
131
+ await verify(release, { signal: AbortSignal.timeout(5_000) });
132
+ return;
133
+ }
134
+ catch (cause) {
135
+ last = cause;
136
+ await new Promise((resolve) => setTimeout(resolve, 1_000));
137
+ }
138
+ }
139
+ throw last instanceof Error ? last : new Error('Cloudflare Release did not become ready.');
140
+ }
@@ -0,0 +1,8 @@
1
+ import type { ArtifactSource, PrepareContext } from '@astrale-os/sdk/deployment/adapter';
2
+ import type { CloudflareParams } from '../params.js';
3
+ /** Prepare one revision Service and its stateless gateway without provider mutation. */
4
+ export declare function artifact(params: CloudflareParams, context: PrepareContext): Promise<ArtifactSource>;
5
+ export declare function materialize(root: string, files: readonly {
6
+ readonly path: string;
7
+ bytes(): Uint8Array;
8
+ }[]): Promise<void>;
@@ -0,0 +1,179 @@
1
+ import { material } from '@astrale-os/sdk/deployment/build';
2
+ import { addressing } from '@astrale-os/sdk/deployment/release';
3
+ import { readFile, writeFile, mkdir, rm } from 'node:fs/promises';
4
+ import { dirname, join, relative, resolve } from 'node:path';
5
+ import { pathToFileURL } from 'node:url';
6
+ import { SIGNING_IDENTITY_BINDING } from '../identity/binding.js';
7
+ import { releaseNamespace, releaseWorkerName } from '../release/index.js';
8
+ import { generateExecutionEntry } from './codegen/execution.js';
9
+ import { generateGatewayEntry } from './codegen/gateway.js';
10
+ import { generateWranglerConfig } from './codegen/wrangler.js';
11
+ import { deploymentAddressing, resolveRouter, workerName } from './configuration.js';
12
+ import { frontendFiles } from './frontend-v1.js';
13
+ import { qualifyWorkerBundle } from './qualification.js';
14
+ import { runWranglerBundle } from './wrangler-cli.js';
15
+ const encoder = new TextEncoder();
16
+ /** Prepare one revision Service and its stateless gateway without provider mutation. */
17
+ export async function artifact(params, context) {
18
+ const stateDirectory = join(context.projectDir, '.astrale');
19
+ const directory = join(stateDirectory, 'runtime');
20
+ await mkdir(directory, { recursive: true });
21
+ const runtimePath = resolve(context.projectDir, context.runtime.reference.path);
22
+ const runtimeSpecifier = moduleSpecifier(relative(directory, runtimePath));
23
+ const router = resolveRouter(params.router);
24
+ const name = workerName(params, context.build.schema.compiled.root.origin);
25
+ const revision = context.build.schema.compiled.root.revision;
26
+ const namespace = releaseNamespace(name);
27
+ const target = releaseWorkerName(revision);
28
+ const configuredIssuer = deploymentAddressing(params);
29
+ const source = generateExecutionEntry({
30
+ origin: context.build.schema.compiled.root.origin,
31
+ runtimeSpecifier,
32
+ material: material(context.build),
33
+ ...(configuredIssuer === undefined ? { providerAssignedWorker: name } : {}),
34
+ ...(router === undefined
35
+ ? {}
36
+ : {
37
+ router: {
38
+ binding: router.binding,
39
+ hostSuffix: router.hostSuffix,
40
+ minLabels: router.minLabels,
41
+ },
42
+ }),
43
+ });
44
+ const sourcePath = join(directory, 'worker.gen.ts');
45
+ await writeFile(sourcePath, source);
46
+ const configPath = join(directory, 'wrangler.prepare.jsonc');
47
+ await writeFile(configPath, generateWranglerConfig({
48
+ workerName: name,
49
+ ...(params.route === undefined ? {} : { route: params.route }),
50
+ vars: {
51
+ ...params.vars,
52
+ ...(configuredIssuer === undefined ? {} : { WORKER_URL: configuredIssuer }),
53
+ },
54
+ secretBindings: [SIGNING_IDENTITY_BINDING],
55
+ ...(router === undefined
56
+ ? {}
57
+ : { router: { binding: router.binding, service: router.service } }),
58
+ ...(params.wrangler === undefined ? {} : { wrangler: params.wrangler }),
59
+ selfBinding: false,
60
+ }));
61
+ const output = join(directory, 'dist-prepare');
62
+ await rm(output, { recursive: true, force: true });
63
+ const emitted = await runWranglerBundle({
64
+ projectDir: context.projectDir,
65
+ configPath,
66
+ outDir: output,
67
+ });
68
+ const frontend = await frontendFiles({
69
+ frontend: context.build.frontend,
70
+ projectDir: context.projectDir,
71
+ });
72
+ await qualifyWorkerBundle({
73
+ metafilePath: emitted.metafilePath,
74
+ inputRoot: dirname(configPath),
75
+ });
76
+ await admitBundle(emitted.bundlePath);
77
+ const bundle = await readFile(emitted.bundlePath);
78
+ const gatewaySource = join(directory, 'gateway.gen.ts');
79
+ await writeFile(gatewaySource, generateGatewayEntry(revision));
80
+ const gatewayPrepareConfig = join(directory, 'wrangler.gateway.prepare.jsonc');
81
+ await writeFile(gatewayPrepareConfig, generateWranglerConfig({
82
+ workerName: name,
83
+ main: './gateway.gen.ts',
84
+ ...(params.route === undefined ? {} : { route: params.route }),
85
+ releases: { binding: 'RELEASES', namespace },
86
+ selfBinding: false,
87
+ }));
88
+ const gatewayOutput = join(directory, 'dist-gateway');
89
+ await rm(gatewayOutput, { recursive: true, force: true });
90
+ const gateway = await runWranglerBundle({
91
+ projectDir: context.projectDir,
92
+ configPath: gatewayPrepareConfig,
93
+ outDir: gatewayOutput,
94
+ });
95
+ await qualifyWorkerBundle({
96
+ metafilePath: gateway.metafilePath,
97
+ inputRoot: dirname(gatewayPrepareConfig),
98
+ });
99
+ await admitBundle(gateway.bundlePath);
100
+ const gatewayBundle = await readFile(gateway.bundlePath);
101
+ const releaseConfig = generateWranglerConfig({
102
+ workerName: target,
103
+ main: './bundle/index.mjs',
104
+ vars: {
105
+ ...params.vars,
106
+ ...(configuredIssuer === undefined ? {} : { WORKER_URL: configuredIssuer }),
107
+ },
108
+ secretBindings: [SIGNING_IDENTITY_BINDING],
109
+ ...(context.build.frontend?.source.kind === 'vite'
110
+ ? {
111
+ frontend: {
112
+ directory: './frontend',
113
+ routes: frontendRoutes(context.build.frontend),
114
+ dev: false,
115
+ },
116
+ }
117
+ : {}),
118
+ ...(router === undefined
119
+ ? {}
120
+ : { router: { binding: router.binding, service: router.service } }),
121
+ ...(params.wrangler === undefined ? {} : { wrangler: params.wrangler }),
122
+ selfBinding: false,
123
+ });
124
+ const gatewayConfig = generateWranglerConfig({
125
+ workerName: name,
126
+ main: './gateway/index.mjs',
127
+ ...(params.route === undefined ? {} : { route: params.route }),
128
+ releases: { binding: 'RELEASES', namespace },
129
+ selfBinding: false,
130
+ });
131
+ const files = [
132
+ { path: 'wrangler.release.jsonc', content: encoder.encode(releaseConfig) },
133
+ { path: 'wrangler.gateway.jsonc', content: encoder.encode(gatewayConfig) },
134
+ { path: 'bundle/index.mjs', content: new Uint8Array(bundle) },
135
+ { path: 'gateway/index.mjs', content: new Uint8Array(gatewayBundle) },
136
+ ...frontend.map((file) => ({
137
+ path: `frontend/${file.path}`,
138
+ content: file.bytes(),
139
+ })),
140
+ ];
141
+ return Object.freeze({
142
+ entrypoint: 'bundle/index.mjs',
143
+ files: Object.freeze(files),
144
+ addressing: configuredIssuer === undefined
145
+ ? Object.freeze({ kind: 'provider-assigned', claim: name })
146
+ : Object.freeze({ kind: 'configured', addressing: addressing(configuredIssuer) }),
147
+ });
148
+ }
149
+ async function admitBundle(path) {
150
+ const loaded = (await import(`${pathToFileURL(path).href}?admit=${Date.now()}`));
151
+ if (loaded.default === null ||
152
+ typeof loaded.default !== 'object' ||
153
+ typeof Reflect.get(loaded.default, 'fetch') !== 'function') {
154
+ throw new TypeError('Generated Worker bundle does not export a Fetch handler.');
155
+ }
156
+ }
157
+ function frontendRoutes(frontend) {
158
+ if (frontend.source.kind !== 'vite')
159
+ return [];
160
+ return Object.freeze(frontend.routes
161
+ .flatMap((route) => frontend.source.kind === 'vite' && frontend.source.spa && route.path !== '/'
162
+ ? [route.path, `${route.path}/*`]
163
+ : route.path === '/'
164
+ ? ['/*']
165
+ : [route.path])
166
+ .sort());
167
+ }
168
+ export async function materialize(root, files) {
169
+ await rm(root, { recursive: true, force: true });
170
+ for (const file of files) {
171
+ const path = join(root, file.path);
172
+ await mkdir(dirname(path), { recursive: true });
173
+ await writeFile(path, file.bytes());
174
+ }
175
+ }
176
+ function moduleSpecifier(input) {
177
+ const normalized = input.split('\\').join('/');
178
+ return normalized.startsWith('.') ? normalized : `./${normalized}`;
179
+ }
@@ -0,0 +1,2 @@
1
+ /** Prove emitted Worker reachability excludes authoring, compilation, and broad Core Schema owners. */
2
+ export declare function assertRuntimeClosure(metafilePath: string): Promise<void>;
@@ -0,0 +1,22 @@
1
+ import { readWranglerMetafile } from './metafile.js';
2
+ const FORBIDDEN_RUNTIME_INPUTS = [
3
+ '/v1/builder/',
4
+ '/v1/compiled/compiler/',
5
+ '/node_modules/ajv/',
6
+ '/node_modules/re2js/',
7
+ '/kernel-core/domain/',
8
+ '/core/domain/',
9
+ '/kernel-core/schema/index.',
10
+ '/core/schema/index.',
11
+ ];
12
+ /** Prove emitted Worker reachability excludes authoring, compilation, and broad Core Schema owners. */
13
+ export async function assertRuntimeClosure(metafilePath) {
14
+ const metafile = await readWranglerMetafile(metafilePath);
15
+ const forbidden = Object.keys(metafile.inputs).filter((input) => {
16
+ const normalized = `/${input.replaceAll('\\', '/')}`.replace(/(\/node_modules\/@astrale-os\/kernel-[^/]+)\/dist\//gu, '$1/');
17
+ return FORBIDDEN_RUNTIME_INPUTS.some((fragment) => normalized.includes(fragment));
18
+ });
19
+ if (forbidden.length > 0) {
20
+ throw new Error(`Cloudflare Worker reaches build-side modules: ${forbidden.sort().join(', ')}.`);
21
+ }
22
+ }
@@ -0,0 +1,13 @@
1
+ import type { BuildMaterial } from '@astrale-os/sdk/deployment/build';
2
+ export interface ExecutionCodegenOptions {
3
+ readonly origin: string;
4
+ readonly runtimeSpecifier: string;
5
+ readonly material: BuildMaterial;
6
+ readonly providerAssignedWorker?: string;
7
+ readonly router?: {
8
+ readonly binding: string;
9
+ readonly hostSuffix: string;
10
+ readonly minLabels: number;
11
+ };
12
+ }
13
+ export declare function generateExecutionEntry(options: ExecutionCodegenOptions): string;
@@ -0,0 +1,44 @@
1
+ export function generateExecutionEntry(options) {
2
+ const router = options.router;
3
+ const routerPredicate = router === undefined
4
+ ? ''
5
+ : `
6
+ function isInstanceHost(host: string): boolean {
7
+ return host.endsWith(${JSON.stringify(router.hostSuffix)}) && host.split('.').length >= ${router.minLabels}
8
+ }
9
+ `;
10
+ const routeSubrequest = router === undefined
11
+ ? ''
12
+ : `
13
+ routeSubrequest: (url, env) => {
14
+ const labels = url.hostname.split('.')
15
+ if (env.DISPATCHER && labels[1] === 'svc') return env.DISPATCHER.get(labels[0]!)
16
+ if (env.${router.binding} && isInstanceHost(url.hostname)) return env.${router.binding}
17
+ return null
18
+ },`;
19
+ return `// AUTO-GENERATED by @astrale-os/adapter-cloudflare — do not edit.
20
+ // Domain origin: ${options.origin}
21
+ import type { BuildMaterial } from '@astrale-os/sdk/deployment/build'
22
+ import type { CloudflareAdapterBindings } from '@astrale-os/adapter-cloudflare/worker'
23
+
24
+ import { cloudflareWorkerEntry } from '@astrale-os/adapter-cloudflare/worker'
25
+ import runtime from ${JSON.stringify(options.runtimeSpecifier)}
26
+ import { decodeSigningIdentity, runtimeBindings } from '@astrale-os/adapter-cloudflare/worker'
27
+
28
+ type WorkerEnv = CloudflareEnv & CloudflareAdapterBindings & {
29
+ readonly ASTRALE_SIGNING_IDENTITY: string
30
+ }
31
+ type RuntimeEnv = Omit<WorkerEnv, 'ASTRALE_SIGNING_IDENTITY'>
32
+ const MATERIAL = ${JSON.stringify(options.material)} as const satisfies BuildMaterial
33
+ ${routerPredicate}
34
+ export default cloudflareWorkerEntry<WorkerEnv, RuntimeEnv>()({
35
+ runtime,
36
+ material: MATERIAL,
37
+ privateKey: (env) => decodeSigningIdentity(env.ASTRALE_SIGNING_IDENTITY),
38
+ resolveEnvironment: runtimeBindings,
39
+ resolveUrl: (env, requestOrigin) => env.WORKER_URL ?? env.IDENTITY_ISS ?? requestOrigin,
40
+ ${options.providerAssignedWorker === undefined ? '' : `providerAssignedWorker: ${JSON.stringify(options.providerAssignedWorker)},`}
41
+ selfBinding: (env) => env.SELF,${routeSubrequest}
42
+ })
43
+ `;
44
+ }
@@ -0,0 +1 @@
1
+ export declare function generateGatewayEntry(revision: string): string;
@@ -0,0 +1,7 @@
1
+ export function generateGatewayEntry(revision) {
2
+ return `// AUTO-GENERATED by @astrale-os/adapter-cloudflare — do not edit.
3
+ import { cloudflareGatewayEntry } from '@astrale-os/adapter-cloudflare/worker'
4
+
5
+ export default cloudflareGatewayEntry({ current: ${JSON.stringify(revision)} })
6
+ `;
7
+ }
@@ -17,4 +17,3 @@
17
17
  * the caller (`generateWranglerConfig`), not here — this stays a generic merge.
18
18
  */
19
19
  export declare function deepMergeConfig(base: Record<string, unknown>, overlay: Record<string, unknown>): Record<string, unknown>;
20
- //# sourceMappingURL=merge.d.ts.map
@@ -88,4 +88,3 @@ function isPlainObject(v) {
88
88
  function isPrimitive(v) {
89
89
  return v === null || (typeof v !== 'object' && typeof v !== 'function');
90
90
  }
91
- //# sourceMappingURL=merge.js.map
@@ -13,17 +13,27 @@
13
13
  */
14
14
  export interface WranglerCodegenOptions {
15
15
  workerName: string;
16
+ /** Provider-ready entrypoint relative to the generated config. */
17
+ main?: string;
16
18
  /** Full custom hostname for a routed deploy (e.g. 'crm.acme.dev'). */
17
19
  route?: string;
18
20
  /** Exact materialized local frontend; absence means no ASSETS binding. */
19
21
  frontend?: {
20
22
  readonly directory: string;
21
23
  readonly routes: readonly string[];
24
+ readonly dev: boolean;
22
25
  };
23
26
  /** Plain (non-secret) vars to bake in. */
24
27
  vars?: Record<string, string>;
28
+ /** Secret binding names required by generated Worker code. */
29
+ secretBindings?: readonly string[];
25
30
  /** Add the SELF service binding (autobinding). */
26
31
  selfBinding: boolean;
32
+ /** Bind the stable gateway to its private revision namespace. */
33
+ releases?: {
34
+ readonly binding: string;
35
+ readonly namespace: string;
36
+ };
27
37
  /**
28
38
  * Add a service binding to a router Worker for instance-host subrequest
29
39
  * routing (see `CloudflareParams.router`). Pairs with the worker
@@ -43,4 +53,3 @@ export interface WranglerCodegenOptions {
43
53
  wrangler?: Record<string, unknown>;
44
54
  }
45
55
  export declare function generateWranglerConfig(opts: WranglerCodegenOptions): string;
46
- //# sourceMappingURL=wrangler.d.ts.map
@@ -12,12 +12,12 @@
12
12
  * (extra bindings: KV, R2, D1, queues, …) — see `mergeUserConfig`.
13
13
  */
14
14
  import { deepMergeConfig } from './merge.js';
15
- // Wrangler 4.121.0's pinned workerd accepts dates through 2026-08-11.
15
+ // Keep the generated date on the runtime baseline qualified with Wrangler 4.123.0.
16
16
  const COMPATIBILITY_DATE = '2026-08-11';
17
17
  export function generateWranglerConfig(opts) {
18
18
  const config = {
19
19
  name: opts.workerName,
20
- main: './worker.gen.ts',
20
+ main: opts.main ?? './worker.gen.ts',
21
21
  compatibility_date: COMPATIBILITY_DATE,
22
22
  compatibility_flags: ['nodejs_compat'],
23
23
  workers_dev: true,
@@ -31,19 +31,19 @@ export function generateWranglerConfig(opts) {
31
31
  config.assets = {
32
32
  directory: opts.frontend.directory,
33
33
  binding: 'ASSETS',
34
- run_worker_first: [
35
- '/invoke',
36
- '/invoke/*',
37
- '/install',
38
- '/install/*',
39
- '/health',
40
- '/health/*',
41
- '/meta',
42
- '/meta/*',
43
- '/.well-known',
44
- '/.well-known/*',
45
- ...opts.frontend.routes,
46
- ],
34
+ run_worker_first: opts.frontend.dev
35
+ ? true
36
+ : [
37
+ '/releases',
38
+ '/releases/*',
39
+ '/install',
40
+ '/install/*',
41
+ '/health',
42
+ '/health/*',
43
+ '/.well-known',
44
+ '/.well-known/*',
45
+ ...opts.frontend.routes,
46
+ ],
47
47
  };
48
48
  }
49
49
  const services = [];
@@ -57,6 +57,12 @@ export function generateWranglerConfig(opts) {
57
57
  if (opts.vars && Object.keys(opts.vars).length > 0) {
58
58
  config.vars = opts.vars;
59
59
  }
60
+ if (opts.secretBindings && opts.secretBindings.length > 0) {
61
+ config.secrets = { required: [...opts.secretBindings] };
62
+ }
63
+ if (opts.releases !== undefined) {
64
+ config.dispatch_namespaces = [opts.releases];
65
+ }
60
66
  const merged = opts.wrangler ? mergeUserConfig(config, opts.wrangler) : config;
61
67
  return JSON.stringify(merged, null, 2) + '\n';
62
68
  }
@@ -68,6 +74,7 @@ const ADAPTER_OWNED_KEYS = [
68
74
  'routes',
69
75
  'compatibility_date',
70
76
  'version_metadata',
77
+ 'secrets',
71
78
  ];
72
79
  /**
73
80
  * Deep-merge the dev's raw wrangler overlay over the generated config, after
@@ -83,4 +90,3 @@ function mergeUserConfig(base, user) {
83
90
  }
84
91
  return deepMergeConfig(base, user);
85
92
  }
86
- //# sourceMappingURL=wrangler.js.map
@@ -0,0 +1,9 @@
1
+ import type { CloudflareParams } from '../params.js';
2
+ export declare function deploymentAddressing(parameters: CloudflareParams): string | undefined;
3
+ export declare function workerName(parameters: CloudflareParams, origin: string): string;
4
+ export declare function resolveRouter(input: CloudflareParams['router']): {
5
+ readonly binding: string;
6
+ readonly service: string;
7
+ readonly hostSuffix: string;
8
+ readonly minLabels: number;
9
+ } | undefined;
@@ -0,0 +1,40 @@
1
+ import { addressing } from '@astrale-os/sdk/deployment/release';
2
+ import { createHash } from 'node:crypto';
3
+ const WORKER_NAME = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/u;
4
+ export function deploymentAddressing(parameters) {
5
+ const routed = parameters.route === undefined ? undefined : addressing(`https://${parameters.route}`).issuer;
6
+ if (parameters.addressing === undefined)
7
+ return routed;
8
+ const configured = addressing(parameters.addressing).issuer;
9
+ if (routed !== undefined && configured !== routed) {
10
+ throw new TypeError('Cloudflare route and addressing identify different origins.');
11
+ }
12
+ return configured;
13
+ }
14
+ export function workerName(parameters, origin) {
15
+ if (parameters.workerName !== undefined) {
16
+ if (!WORKER_NAME.test(parameters.workerName) || parameters.workerName.length > 63) {
17
+ throw new TypeError('Cloudflare workerName is invalid.');
18
+ }
19
+ return parameters.workerName;
20
+ }
21
+ const readable = origin
22
+ .toLowerCase()
23
+ .replace(/[^a-z0-9]+/gu, '-')
24
+ .slice(0, 12)
25
+ .replace(/^-+|-+$/gu, '') || 'domain';
26
+ const hexadecimal = createHash('sha256').update(origin).digest('hex');
27
+ const identity = BigInt(`0x${hexadecimal}`).toString(36).padStart(50, '0');
28
+ return `${readable}-${identity}`;
29
+ }
30
+ export function resolveRouter(input) {
31
+ if (input === false)
32
+ return undefined;
33
+ const value = input ?? {};
34
+ return Object.freeze({
35
+ binding: value.binding ?? 'ROUTER',
36
+ service: value.service ?? 'admin-router',
37
+ hostSuffix: value.hostSuffix ?? '.astrale.ai',
38
+ minLabels: value.minLabels ?? 4,
39
+ });
40
+ }
@@ -0,0 +1,16 @@
1
+ import type { Build } from '@astrale-os/sdk/deployment/build';
2
+ import { frontend as frontendBuild } from '@astrale-os/sdk/deployment/build';
3
+ export interface FrontendDevHandle {
4
+ readonly url: string;
5
+ stop(): Promise<void>;
6
+ }
7
+ export declare function frontendFiles(input: {
8
+ readonly frontend: Build['frontend'];
9
+ readonly projectDir: string;
10
+ readonly onLog?: (line: string) => void;
11
+ }): Promise<readonly frontendBuild.FrontendFile[]>;
12
+ export declare function startFrontend(input: {
13
+ readonly frontend: Build['frontend'];
14
+ readonly projectDir: string;
15
+ readonly onLog?: (line: string) => void;
16
+ }): Promise<FrontendDevHandle | undefined>;