@astrale-os/adapter-cloudflare 0.5.0-beta.8 → 0.5.0-beta.80

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 (192) hide show
  1. package/CHANGELOG.md +983 -0
  2. package/README.md +20 -2
  3. package/dist/adapter.d.ts +15 -0
  4. package/dist/adapter.js +76 -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 +13 -1
  16. package/dist/build/codegen/wrangler.js +35 -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 +93 -0
  31. package/dist/build/frontend/files.d.ts +7 -0
  32. package/dist/build/frontend/files.js +60 -0
  33. package/dist/build/frontend/routes.d.ts +2 -0
  34. package/dist/build/frontend/routes.js +11 -0
  35. package/dist/build/index.d.ts +10 -16
  36. package/dist/build/index.js +9 -16
  37. package/dist/build/materialize.d.ts +4 -0
  38. package/dist/build/materialize.js +10 -0
  39. package/dist/build/metafile.d.ts +5 -0
  40. package/dist/build/metafile.js +14 -0
  41. package/dist/build/qualification.d.ts +5 -0
  42. package/dist/build/qualification.js +7 -0
  43. package/dist/build/roots.d.ts +2 -0
  44. package/dist/build/roots.js +34 -0
  45. package/dist/build/worker/artifact.d.ts +40 -0
  46. package/dist/build/worker/artifact.js +1 -0
  47. package/dist/build/worker/artifact.load.d.ts +3 -0
  48. package/dist/build/worker/artifact.load.js +308 -0
  49. package/dist/build/worker/artifact.manifest.d.ts +48 -0
  50. package/dist/build/worker/artifact.manifest.js +87 -0
  51. package/dist/build/worker/artifact.paths.d.ts +4 -0
  52. package/dist/build/worker/artifact.paths.js +25 -0
  53. package/dist/build/worker/codegen/entry.d.ts +2 -0
  54. package/dist/build/worker/codegen/entry.js +28 -0
  55. package/dist/build/worker/codegen/service.d.ts +16 -0
  56. package/dist/build/worker/codegen/service.js +52 -0
  57. package/dist/build/worker/execution.prepare.d.ts +18 -0
  58. package/dist/build/worker/execution.prepare.js +109 -0
  59. package/dist/build/worker/index.d.ts +2 -0
  60. package/dist/build/worker/index.js +1 -0
  61. package/dist/build/worker/modules.d.ts +11 -0
  62. package/dist/build/worker/modules.js +26 -0
  63. package/dist/build/worker/runtime.d.ts +10 -0
  64. package/dist/build/worker/runtime.js +35 -0
  65. package/dist/build/worker/specifier.d.ts +1 -0
  66. package/dist/build/worker/specifier.js +5 -0
  67. package/dist/cloudflare.d.ts +1 -34
  68. package/dist/cloudflare.js +1 -205
  69. package/dist/deploy/deploy.d.ts +16 -0
  70. package/dist/deploy/deploy.js +204 -0
  71. package/dist/deploy/index.d.ts +1 -0
  72. package/dist/deploy/index.js +1 -0
  73. package/dist/deploy/output.d.ts +2 -0
  74. package/dist/deploy/output.js +10 -0
  75. package/dist/development/develop.d.ts +15 -0
  76. package/dist/development/develop.js +267 -0
  77. package/dist/development/frontend.d.ts +11 -0
  78. package/dist/development/frontend.js +109 -0
  79. package/dist/development/host.d.ts +2 -0
  80. package/dist/development/host.js +46 -0
  81. package/dist/development/port.d.ts +1 -0
  82. package/dist/development/port.js +12 -0
  83. package/dist/development/readiness.d.ts +14 -0
  84. package/dist/development/readiness.js +71 -0
  85. package/dist/development/resolved-fetch.d.ts +17 -0
  86. package/dist/development/resolved-fetch.js +80 -0
  87. package/dist/development/worker-output.d.ts +2 -0
  88. package/dist/development/worker-output.js +25 -0
  89. package/dist/development/worker.d.ts +17 -0
  90. package/dist/development/worker.js +147 -0
  91. package/dist/identity/binding.d.ts +2 -0
  92. package/dist/identity/binding.js +2 -0
  93. package/dist/identity/material.d.ts +7 -0
  94. package/dist/identity/material.js +42 -0
  95. package/dist/identity/secret.d.ts +3 -0
  96. package/dist/identity/secret.js +7 -0
  97. package/dist/identity/source.d.ts +26 -0
  98. package/dist/identity/source.js +127 -0
  99. package/dist/index.d.ts +5 -5
  100. package/dist/index.js +5 -5
  101. package/dist/params.d.ts +32 -7
  102. package/dist/params.js +0 -1
  103. package/dist/release/deployment.d.ts +44 -0
  104. package/dist/release/deployment.js +141 -0
  105. package/dist/release/index.d.ts +2 -0
  106. package/dist/release/index.js +2 -0
  107. package/dist/release/staged-worker-name.d.ts +3 -0
  108. package/dist/release/staged-worker-name.js +10 -0
  109. package/dist/worker/entry.d.ts +43 -0
  110. package/dist/worker/entry.js +181 -0
  111. package/dist/worker/fetch.d.ts +0 -1
  112. package/dist/worker/fetch.js +0 -1
  113. package/dist/worker/identity.d.ts +7 -0
  114. package/dist/worker/identity.js +23 -0
  115. package/dist/worker/index.d.ts +2 -61
  116. package/dist/worker/index.js +2 -212
  117. package/dist/wrangler/bundle.d.ts +10 -0
  118. package/dist/wrangler/bundle.js +27 -0
  119. package/dist/wrangler/deploy.d.ts +24 -0
  120. package/dist/wrangler/deploy.js +44 -0
  121. package/dist/wrangler/index.d.ts +5 -0
  122. package/dist/wrangler/index.js +5 -0
  123. package/dist/wrangler/process.d.ts +17 -0
  124. package/dist/wrangler/process.js +115 -0
  125. package/dist/wrangler/secrets.d.ts +6 -0
  126. package/dist/wrangler/secrets.js +58 -0
  127. package/dist/wrangler/status.d.ts +14 -0
  128. package/dist/wrangler/status.js +56 -0
  129. package/dist/wrangler/type-generation.d.ts +6 -0
  130. package/dist/wrangler/type-generation.js +19 -0
  131. package/package.json +30 -19
  132. package/template/astrale.config.ts +25 -17
  133. package/dist/build/codegen/identity.d.ts +0 -10
  134. package/dist/build/codegen/identity.d.ts.map +0 -1
  135. package/dist/build/codegen/identity.js +0 -32
  136. package/dist/build/codegen/identity.js.map +0 -1
  137. package/dist/build/codegen/merge.d.ts.map +0 -1
  138. package/dist/build/codegen/merge.js.map +0 -1
  139. package/dist/build/codegen/worker.d.ts +0 -12
  140. package/dist/build/codegen/worker.d.ts.map +0 -1
  141. package/dist/build/codegen/worker.js +0 -40
  142. package/dist/build/codegen/worker.js.map +0 -1
  143. package/dist/build/codegen/wrangler.d.ts.map +0 -1
  144. package/dist/build/codegen/wrangler.js.map +0 -1
  145. package/dist/build/frontend.d.ts +0 -26
  146. package/dist/build/frontend.d.ts.map +0 -1
  147. package/dist/build/frontend.js +0 -263
  148. package/dist/build/frontend.js.map +0 -1
  149. package/dist/build/index.d.ts.map +0 -1
  150. package/dist/build/index.js.map +0 -1
  151. package/dist/build/parse-output.d.ts +0 -27
  152. package/dist/build/parse-output.d.ts.map +0 -1
  153. package/dist/build/parse-output.js +0 -58
  154. package/dist/build/parse-output.js.map +0 -1
  155. package/dist/build/prepare.d.ts +0 -18
  156. package/dist/build/prepare.d.ts.map +0 -1
  157. package/dist/build/prepare.js +0 -117
  158. package/dist/build/prepare.js.map +0 -1
  159. package/dist/build/worker.d.ts +0 -15
  160. package/dist/build/worker.d.ts.map +0 -1
  161. package/dist/build/worker.js +0 -21
  162. package/dist/build/worker.js.map +0 -1
  163. package/dist/build/wrangler-cli.d.ts +0 -88
  164. package/dist/build/wrangler-cli.d.ts.map +0 -1
  165. package/dist/build/wrangler-cli.js +0 -283
  166. package/dist/build/wrangler-cli.js.map +0 -1
  167. package/dist/cloudflare.d.ts.map +0 -1
  168. package/dist/cloudflare.js.map +0 -1
  169. package/dist/index.d.ts.map +0 -1
  170. package/dist/index.js.map +0 -1
  171. package/dist/params.d.ts.map +0 -1
  172. package/dist/params.js.map +0 -1
  173. package/dist/worker/fetch.d.ts.map +0 -1
  174. package/dist/worker/fetch.js.map +0 -1
  175. package/dist/worker/index.d.ts.map +0 -1
  176. package/dist/worker/index.js.map +0 -1
  177. package/src/build/codegen/identity.ts +0 -40
  178. package/src/build/codegen/merge.ts +0 -92
  179. package/src/build/codegen/worker.ts +0 -53
  180. package/src/build/codegen/wrangler.ts +0 -127
  181. package/src/build/frontend.ts +0 -324
  182. package/src/build/index.ts +0 -29
  183. package/src/build/parse-output.ts +0 -60
  184. package/src/build/prepare.ts +0 -161
  185. package/src/build/worker.ts +0 -37
  186. package/src/build/wrangler-cli.ts +0 -372
  187. package/src/cloudflare.ts +0 -251
  188. package/src/index.ts +0 -21
  189. package/src/params.ts +0 -92
  190. package/src/worker/fetch.ts +0 -46
  191. package/src/worker/index.ts +0 -325
  192. package/src/worker/tsconfig.json +0 -10
@@ -0,0 +1,44 @@
1
+ declare const WORKER_DEPLOYMENT_ID: unique symbol;
2
+ /** Provider-local identity for one immutable staged Cloudflare implementation. */
3
+ export type WorkerDeploymentId = `sha256:${string}` & {
4
+ readonly [WORKER_DEPLOYMENT_ID]: true;
5
+ };
6
+ export interface WorkerDeploymentInput {
7
+ readonly release: {
8
+ readonly entrypoint: string;
9
+ readonly modules: readonly {
10
+ readonly path: string;
11
+ readonly kind: 'esm' | 'wasm' | 'text' | 'data';
12
+ readonly content: Uint8Array;
13
+ }[];
14
+ };
15
+ readonly runtime: {
16
+ readonly compatibilityDate: string;
17
+ readonly compatibilityFlags: readonly string[];
18
+ };
19
+ readonly bindings: {
20
+ readonly vars: Readonly<Record<string, string>>;
21
+ readonly services: readonly {
22
+ readonly name: string;
23
+ readonly service: string;
24
+ }[];
25
+ readonly requiredSecrets: readonly string[];
26
+ };
27
+ readonly assets?: {
28
+ readonly files: readonly {
29
+ readonly path: string;
30
+ readonly content: Uint8Array;
31
+ readonly contentType?: string;
32
+ }[];
33
+ readonly notFound: 'none' | '404-page' | 'single-page-application';
34
+ readonly runWorkerFirst: boolean;
35
+ readonly htmlHandling?: 'none';
36
+ };
37
+ }
38
+ /** Canonical provider material used only for staging, promotion, rollback, and cleanup. */
39
+ export declare function workerDeploymentMaterial(input: WorkerDeploymentInput): Uint8Array;
40
+ /** Identify one immutable Cloudflare implementation without changing SDK or Publication identity. */
41
+ export declare function workerDeployment(input: WorkerDeploymentInput): WorkerDeploymentId;
42
+ /** Admit a serialized provider deployment identity at a Cloudflare boundary. */
43
+ export declare function acceptWorkerDeployment(input: unknown): WorkerDeploymentId;
44
+ export {};
@@ -0,0 +1,141 @@
1
+ import { sha256 } from '@noble/hashes/sha2.js';
2
+ const DEPLOYMENT = /^sha256:[0-9a-f]{64}$/u;
3
+ /** Canonical provider material used only for staging, promotion, rollback, and cleanup. */
4
+ export function workerDeploymentMaterial(input) {
5
+ const release = executable(input.release);
6
+ const compatibilityFlags = uniqueStrings(input.runtime.compatibilityFlags, 'compatibility flag');
7
+ const vars = Object.fromEntries(Object.entries(input.bindings.vars)
8
+ .map(([name, value]) => {
9
+ stable(name, 'Variable binding name');
10
+ if (typeof value !== 'string')
11
+ invalid();
12
+ return [name, value];
13
+ })
14
+ .sort(([left], [right]) => compare(left, right)));
15
+ const services = [...input.bindings.services]
16
+ .map((binding) => ({
17
+ name: stable(binding.name, 'Service binding name'),
18
+ service: stable(binding.service, 'Service binding target'),
19
+ }))
20
+ .sort((left, right) => compare(left.name, right.name));
21
+ unique(services.map((binding) => binding.name), 'Service binding name');
22
+ const requiredSecrets = uniqueStrings(input.bindings.requiredSecrets, 'required secret');
23
+ unique([...Object.keys(vars), ...services.map((binding) => binding.name), ...requiredSecrets], 'Worker binding name');
24
+ const assets = input.assets === undefined ? undefined : canonicalAssets(input.assets);
25
+ return new TextEncoder().encode(`${JSON.stringify({
26
+ format: 'astrale.cloudflare.deployment',
27
+ version: 1,
28
+ release,
29
+ runtime: {
30
+ compatibilityDate: stable(input.runtime.compatibilityDate, 'compatibility date'),
31
+ compatibilityFlags,
32
+ },
33
+ bindings: { vars, services, requiredSecrets },
34
+ ...(assets === undefined ? {} : { assets }),
35
+ })}\n`);
36
+ }
37
+ /** Identify one immutable Cloudflare implementation without changing SDK or Publication identity. */
38
+ export function workerDeployment(input) {
39
+ let hexadecimal = '';
40
+ for (const byte of sha256(workerDeploymentMaterial(input))) {
41
+ hexadecimal += byte.toString(16).padStart(2, '0');
42
+ }
43
+ return `sha256:${hexadecimal}`;
44
+ }
45
+ /** Admit a serialized provider deployment identity at a Cloudflare boundary. */
46
+ export function acceptWorkerDeployment(input) {
47
+ if (typeof input !== 'string' || !DEPLOYMENT.test(input)) {
48
+ throw new TypeError('Cloudflare Worker deployment identity is invalid.');
49
+ }
50
+ return input;
51
+ }
52
+ function executable(input) {
53
+ const entrypoint = stablePath(input.entrypoint, 'Worker release entrypoint');
54
+ const modules = input.modules
55
+ .map((module) => ({
56
+ path: stablePath(module.path, 'Worker release module path'),
57
+ kind: module.kind,
58
+ digest: content(module.content),
59
+ }))
60
+ .sort((left, right) => compare(left.path, right.path));
61
+ unique(modules.map((module) => module.path), 'Worker release module path');
62
+ if (!modules.some((module) => module.path === entrypoint && module.kind === 'esm')) {
63
+ throw new TypeError('Worker release entrypoint must be an ES module in its module set.');
64
+ }
65
+ return { entrypoint, modules };
66
+ }
67
+ function canonicalAssets(input) {
68
+ const files = input.files
69
+ .map((file) => ({
70
+ path: stableAssetPath(file.path),
71
+ digest: content(file.content),
72
+ ...(file.contentType === undefined
73
+ ? {}
74
+ : { contentType: stable(file.contentType, 'Worker asset content type') }),
75
+ }))
76
+ .sort((left, right) => compare(left.path, right.path));
77
+ unique(files.map((file) => file.path), 'Worker asset path');
78
+ if (!['none', '404-page', 'single-page-application'].includes(input.notFound))
79
+ invalid();
80
+ if (input.htmlHandling !== undefined && input.htmlHandling !== 'none')
81
+ invalid();
82
+ return {
83
+ files,
84
+ notFound: input.notFound,
85
+ runWorkerFirst: input.runWorkerFirst,
86
+ htmlHandling: input.htmlHandling ?? 'none',
87
+ };
88
+ }
89
+ function uniqueStrings(input, label) {
90
+ const values = input.map((value) => stable(value, label)).sort(compare);
91
+ unique(values, label);
92
+ return values;
93
+ }
94
+ function unique(values, label) {
95
+ if (new Set(values).size !== values.length) {
96
+ throw new TypeError(`${label} must be unique.`);
97
+ }
98
+ }
99
+ function content(value) {
100
+ if (!(value instanceof Uint8Array))
101
+ invalid();
102
+ let hexadecimal = '';
103
+ for (const byte of sha256(value))
104
+ hexadecimal += byte.toString(16).padStart(2, '0');
105
+ return `sha256:${hexadecimal}`;
106
+ }
107
+ function stablePath(value, label) {
108
+ const admitted = stable(value, label);
109
+ if (admitted.startsWith('/') ||
110
+ admitted.includes('\\') ||
111
+ admitted.split('/').some((part) => part.length === 0 || part === '.' || part === '..')) {
112
+ invalid();
113
+ }
114
+ return admitted;
115
+ }
116
+ function stableAssetPath(value) {
117
+ const admitted = stable(value, 'Worker asset path');
118
+ if (!admitted.startsWith('/') ||
119
+ admitted.includes('\\') ||
120
+ admitted.includes('?') ||
121
+ admitted.includes('#') ||
122
+ admitted
123
+ .slice(1)
124
+ .split('/')
125
+ .some((part) => part.length === 0 || part === '.' || part === '..')) {
126
+ invalid();
127
+ }
128
+ return admitted;
129
+ }
130
+ function stable(value, label) {
131
+ if (typeof value !== 'string' || value.length === 0 || value.trim() !== value) {
132
+ throw new TypeError(`${label} must be stable non-empty text.`);
133
+ }
134
+ return value;
135
+ }
136
+ function compare(left, right) {
137
+ return left < right ? -1 : left > right ? 1 : 0;
138
+ }
139
+ function invalid() {
140
+ throw new TypeError('Cloudflare Worker deployment material is invalid.');
141
+ }
@@ -0,0 +1,2 @@
1
+ export { acceptWorkerDeployment, workerDeployment, workerDeploymentMaterial, type WorkerDeploymentId, type WorkerDeploymentInput, } from './deployment.js';
2
+ export { stagedWorkerName } from './staged-worker-name.js';
@@ -0,0 +1,2 @@
1
+ export { acceptWorkerDeployment, workerDeployment, workerDeploymentMaterial, } from './deployment.js';
2
+ export { stagedWorkerName } from './staged-worker-name.js';
@@ -0,0 +1,3 @@
1
+ import type { WorkerDeploymentId } from './deployment.js';
2
+ /** Provider deployment identities select immutable Workers inside the private namespace. */
3
+ export declare function stagedWorkerName(deployment: WorkerDeploymentId): string;
@@ -0,0 +1,10 @@
1
+ import { acceptWorkerDeployment } from './deployment.js';
2
+ const DEPLOYMENT = /^sha256:([0-9a-f]{64})$/u;
3
+ /** Provider deployment identities select immutable Workers inside the private namespace. */
4
+ export function stagedWorkerName(deployment) {
5
+ const digest = DEPLOYMENT.exec(acceptWorkerDeployment(deployment))?.[1];
6
+ if (digest === undefined)
7
+ throw new TypeError('Cloudflare Worker deployment identity is invalid.');
8
+ const encoded = BigInt(`0x${digest}`).toString(36).padStart(50, '0');
9
+ return `r-${encoded}`;
10
+ }
@@ -0,0 +1,43 @@
1
+ import type { JsonWebKey } from '@astrale-os/sdk/auth';
2
+ import type { BuildMaterial, GeneratedBuildMaterial } from '@astrale-os/sdk/deployment/build/material';
3
+ import type { ExecutionContext } from '@astrale-os/sdk/execution';
4
+ import type { Runtime } from '@astrale-os/sdk/runtime';
5
+ export interface Fetcher {
6
+ fetch(request: Request): Response | Promise<Response>;
7
+ }
8
+ export interface CloudflareAdapterBindings {
9
+ readonly ASTRALE_SIGNING_IDENTITY?: string;
10
+ readonly WORKER_URL?: string;
11
+ readonly IDENTITY_ISS?: string;
12
+ readonly ASSETS?: Fetcher;
13
+ readonly SELF?: Fetcher;
14
+ readonly FRONTEND_DEV_URL?: string;
15
+ readonly DISPATCHER?: {
16
+ get(name: string): Fetcher;
17
+ };
18
+ }
19
+ export interface CloudflareWorkerConfig<RuntimeValue extends Runtime, Bindings extends CloudflareAdapterBindings, Environment, Material = BuildMaterial> {
20
+ /** Generated frontend evidence available without restoring the execution Build. */
21
+ readonly frontend?: BuildMaterial['frontend'];
22
+ /** Load the exact Runtime and Build once, after the isolate has accepted a request. */
23
+ readonly load: () => CloudflareWorkerSource<RuntimeValue, Material> | Promise<CloudflareWorkerSource<RuntimeValue, Material>>;
24
+ readonly privateKey: JsonWebKey | ((bindings: Bindings) => JsonWebKey);
25
+ readonly resolveEnvironment?: (bindings: Bindings) => Environment;
26
+ readonly resolveUrl?: (bindings: Bindings, requestOrigin: string) => string;
27
+ readonly providerAssignedWorker?: string;
28
+ readonly selfBinding?: (bindings: Bindings) => Fetcher | null | undefined;
29
+ readonly routeSubrequest?: (url: URL, bindings: Bindings) => Fetcher | null | undefined;
30
+ readonly invocationTimeoutMs?: number;
31
+ readonly maximumRequestBytes?: number;
32
+ }
33
+ export interface CloudflareWorkerSource<RuntimeValue extends Runtime, Material = BuildMaterial> {
34
+ readonly runtime: RuntimeValue;
35
+ readonly material: Material;
36
+ }
37
+ export interface CloudflareWorker<Bindings> {
38
+ fetch(request: Request, environment: Bindings, executionContext?: ExecutionContext): Response | Promise<Response>;
39
+ }
40
+ /** Compose one Service implementation behind its provider-owned stable address. */
41
+ export declare function cloudflareWorkerEntry<Bindings extends CloudflareAdapterBindings, Environment = Bindings>(): <RuntimeValue extends Runtime>(config: CloudflareWorkerConfig<RuntimeValue, Bindings, Environment, BuildMaterial>) => CloudflareWorker<Bindings>;
42
+ /** Compose generated execution code whose exact Build material carries build-owner evidence. */
43
+ export declare function generatedCloudflareWorkerEntry<Bindings extends CloudflareAdapterBindings, Environment = Bindings>(): <RuntimeValue extends Runtime>(config: CloudflareWorkerConfig<RuntimeValue, Bindings, Environment, GeneratedBuildMaterial>) => CloudflareWorker<Bindings>;
@@ -0,0 +1,181 @@
1
+ import { issuer } from '@astrale-os/sdk/auth';
2
+ import { restoreBuild, restoreGeneratedBuild } from '@astrale-os/sdk/deployment/build/material';
3
+ import { addressing, assemble } from '@astrale-os/sdk/deployment/release/runtime';
4
+ import { createIdentity, serve } from '@astrale-os/sdk/execution';
5
+ import { dispatchCloudflareRequest, prepareCloudflareRequest } from './fetch.js';
6
+ /** Compose one Service implementation behind its provider-owned stable address. */
7
+ export function cloudflareWorkerEntry() {
8
+ return createCloudflareWorkerEntry(restoreBuild);
9
+ }
10
+ /** Compose generated execution code whose exact Build material carries build-owner evidence. */
11
+ export function generatedCloudflareWorkerEntry() {
12
+ return createCloudflareWorkerEntry(restoreGeneratedBuild);
13
+ }
14
+ function createCloudflareWorkerEntry(restore) {
15
+ return function (config) {
16
+ let currentIssuer;
17
+ let realized;
18
+ const realize = () => {
19
+ if (realized !== undefined)
20
+ return realized;
21
+ realized = Promise.resolve()
22
+ .then(config.load)
23
+ .then((source) => {
24
+ const build = restore(source.material, source.runtime);
25
+ const application = serve({
26
+ runtime: source.runtime,
27
+ load(environment) {
28
+ if (currentIssuer !== environment.issuer) {
29
+ throw new TypeError('Cloudflare Worker request origin differs from its current Release.');
30
+ }
31
+ return assemble(build, addressing(environment.issuer));
32
+ },
33
+ initialize: (environment) => config.resolveEnvironment === undefined
34
+ ? environment.bindings
35
+ : config.resolveEnvironment(environment.bindings),
36
+ identity(environment) {
37
+ return createIdentity({
38
+ issuer: issuer.accept(environment.issuer),
39
+ subject: build.schema.compiled.root.origin,
40
+ privateKey: typeof config.privateKey === 'function'
41
+ ? config.privateKey(environment.bindings)
42
+ : config.privateKey,
43
+ fetch: routedFetch(application, environment, config),
44
+ ...(new URL(environment.issuer).protocol === 'http:'
45
+ ? { allowInsecureHttp: true }
46
+ : {}),
47
+ });
48
+ },
49
+ server: {
50
+ health: { probe: { check: () => true } },
51
+ limits: {
52
+ maximumRequestBytes: config.maximumRequestBytes ?? 1024 * 1024,
53
+ maximumPublicationBytes: 1024 * 1024,
54
+ maximumBundleBytes: 8 * 1024 * 1024,
55
+ maximumDeliveryBytes: 8 * 1024 * 1024,
56
+ invocationTimeoutMs: config.invocationTimeoutMs ?? 30_000,
57
+ },
58
+ },
59
+ });
60
+ return Object.freeze({ build, application });
61
+ });
62
+ return realized;
63
+ };
64
+ return Object.freeze({
65
+ async fetch(request, bindings, context) {
66
+ const url = new URL(request.url);
67
+ const issuer = canonicalServingUrl(bindings.WORKER_URL ??
68
+ bindings.IDENTITY_ISS ??
69
+ config.resolveUrl?.(bindings, requestOrigin(url, request)) ??
70
+ requestOrigin(url, request));
71
+ if (config.providerAssignedWorker !== undefined &&
72
+ bindings.WORKER_URL === undefined &&
73
+ bindings.IDENTITY_ISS === undefined &&
74
+ !matchesWorkersDevClaim(issuer, config.providerAssignedWorker)) {
75
+ return new Response('Request origin differs from provider-assigned Addressing.', {
76
+ status: 421,
77
+ });
78
+ }
79
+ if (currentIssuer !== undefined && currentIssuer !== issuer) {
80
+ return new Response('Request origin differs from the current Release.', { status: 421 });
81
+ }
82
+ currentIssuer = issuer;
83
+ if (config.frontend !== undefined) {
84
+ const frontend = await frontendAsset(config.frontend, bindings, url, request);
85
+ if (frontend !== undefined)
86
+ return frontend;
87
+ }
88
+ const { application, build } = await realize();
89
+ if (config.frontend === undefined) {
90
+ const frontend = await frontendAsset(build.frontend, bindings, url, request);
91
+ if (frontend !== undefined)
92
+ return frontend;
93
+ }
94
+ return application.fetch(request, Object.freeze({ bindings, issuer }), context);
95
+ },
96
+ });
97
+ };
98
+ }
99
+ function routedFetch(application, environment, config) {
100
+ return async (input, init) => {
101
+ const { request, rejectRedirect } = prepareCloudflareRequest(input, init);
102
+ const destination = new URL(request.url);
103
+ if (destination.origin === environment.issuer) {
104
+ const self = config.selfBinding?.(environment.bindings);
105
+ return dispatchCloudflareRequest(request, rejectRedirect, (selected) => self === null || self === undefined
106
+ ? application.fetch(selected, environment)
107
+ : self.fetch(selected));
108
+ }
109
+ const route = config.routeSubrequest?.(destination, environment.bindings);
110
+ return dispatchCloudflareRequest(request, rejectRedirect, (selected) => route === null || route === undefined ? fetch(selected) : route.fetch(selected));
111
+ };
112
+ }
113
+ async function frontendAsset(frontend, environment, url, request) {
114
+ if (frontend === undefined || frontend.source.kind === 'external')
115
+ return undefined;
116
+ if (reserved(url.pathname) || (request.method !== 'GET' && request.method !== 'HEAD')) {
117
+ return undefined;
118
+ }
119
+ const development = environment.FRONTEND_DEV_URL?.replace(/\/+$/u, '');
120
+ if (development !== undefined && development.length > 0) {
121
+ return fetch(new Request(new URL(`${url.pathname}${url.search}`, `${development}/`), request));
122
+ }
123
+ const assetPath = url.pathname.startsWith('/assets/');
124
+ const route = assetPath
125
+ ? undefined
126
+ : frontend.routes
127
+ .filter((candidate) => candidate.path === url.pathname ||
128
+ (frontend.source.kind === 'vite' &&
129
+ frontend.source.spa &&
130
+ (candidate.path === '/' || url.pathname.startsWith(`${candidate.path}/`))))
131
+ .sort((left, right) => right.path.length - left.path.length)[0];
132
+ const assets = environment.ASSETS;
133
+ if (assets === undefined)
134
+ return undefined;
135
+ if (route === undefined) {
136
+ const response = await assets.fetch(request);
137
+ return assetPath || response.status !== 404 ? response : undefined;
138
+ }
139
+ if (route.path !== url.pathname) {
140
+ const response = await assets.fetch(request);
141
+ if (response.status !== 404)
142
+ return response;
143
+ }
144
+ return assets.fetch(new Request(new URL(`/${route.document}${url.search}`, url.origin), request));
145
+ }
146
+ function reserved(path) {
147
+ return ['/releases', '/install', '/health', '/meta', '/.well-known'].some((prefix) => path === prefix || path.startsWith(`${prefix}/`));
148
+ }
149
+ function requestOrigin(url, request) {
150
+ if (url.protocol !== 'http:')
151
+ return url.origin;
152
+ const forwarded = request.headers.get('x-forwarded-proto')?.split(',')[0]?.trim().toLowerCase();
153
+ return forwarded === 'https' ? `https://${url.host}` : url.origin;
154
+ }
155
+ function canonicalServingUrl(input) {
156
+ let url;
157
+ try {
158
+ url = new URL(input);
159
+ }
160
+ catch (cause) {
161
+ throw new TypeError('Cloudflare Worker serving URL is invalid.', { cause });
162
+ }
163
+ if ((url.protocol !== 'https:' && url.protocol !== 'http:') ||
164
+ url.username !== '' ||
165
+ url.password !== '' ||
166
+ url.pathname !== '/' ||
167
+ url.search !== '' ||
168
+ url.hash !== '') {
169
+ throw new TypeError('Cloudflare Worker serving URL must be one HTTP(S) origin.');
170
+ }
171
+ return url.origin;
172
+ }
173
+ function matchesWorkersDevClaim(issuer, expectedWorker) {
174
+ const url = new URL(issuer);
175
+ const labels = url.hostname.split('.');
176
+ return (url.protocol === 'https:' &&
177
+ labels.length >= 4 &&
178
+ labels[0] === expectedWorker &&
179
+ labels.at(-2) === 'workers' &&
180
+ labels.at(-1) === 'dev');
181
+ }
@@ -9,4 +9,3 @@ export declare function prepareCloudflareRequest(input: Parameters<typeof fetch>
9
9
  export declare function dispatchCloudflareRequest(request: Request, rejectRedirect: boolean, dispatch: RequestDispatcher): Promise<Response>;
10
10
  /** Restore the requested URL omitted by Cloudflare service-binding Responses. */
11
11
  export declare function adaptCloudflareResponse(request: Request, response: Response): Response;
12
- //# sourceMappingURL=fetch.d.ts.map
@@ -29,4 +29,3 @@ export function adaptCloudflareResponse(request, response) {
29
29
  },
30
30
  });
31
31
  }
32
- //# sourceMappingURL=fetch.js.map
@@ -0,0 +1,7 @@
1
+ import type { JsonWebKey } from '@astrale-os/sdk/auth';
2
+ /** Decode the secret representation at the Worker binding boundary. */
3
+ export declare function decodeSigningIdentity(input: string): JsonWebKey;
4
+ /** Remove adapter-owned signing material before Runtime initialization. */
5
+ export declare function runtimeBindings<Bindings extends {
6
+ readonly ASTRALE_SIGNING_IDENTITY: string;
7
+ }>(bindings: Bindings): Omit<Bindings, 'ASTRALE_SIGNING_IDENTITY'>;
@@ -0,0 +1,23 @@
1
+ /** Decode the secret representation at the Worker binding boundary. */
2
+ export function decodeSigningIdentity(input) {
3
+ if (typeof input !== 'string' || input.length === 0)
4
+ invalid();
5
+ let decoded;
6
+ try {
7
+ decoded = JSON.parse(input);
8
+ }
9
+ catch (cause) {
10
+ throw new TypeError('Cloudflare signing identity secret is invalid JSON.', { cause });
11
+ }
12
+ if (decoded === null || typeof decoded !== 'object' || Array.isArray(decoded))
13
+ invalid();
14
+ return decoded;
15
+ }
16
+ /** Remove adapter-owned signing material before Runtime initialization. */
17
+ export function runtimeBindings(bindings) {
18
+ const { ASTRALE_SIGNING_IDENTITY: _identity, ...runtime } = bindings;
19
+ return runtime;
20
+ }
21
+ function invalid() {
22
+ throw new TypeError('Cloudflare signing identity secret is invalid.');
23
+ }
@@ -1,61 +1,2 @@
1
- import type { schema } from '@astrale-os/kernel-dsl/v1';
2
- import type { DomainDefinition } from '@astrale-os/sdk';
3
- import type { Application, RemoteDeploymentInput, RemoteIdentityInput } from '@astrale-os/sdk/server';
4
- import type { ExecutionContext } from 'hono';
5
- /** SDK-owned Publication manifest passed through without adapter interpretation. */
6
- export type CloudflareManifest = NonNullable<RemoteDeploymentInput['manifest']>;
7
- export interface Fetcher {
8
- fetch(request: Request): Response | Promise<Response>;
9
- }
10
- /** Adapter-owned bindings generated into every Cloudflare deployment. */
11
- export interface CloudflareAdapterBindings {
12
- /** Native Cloudflare deployment metadata (direct Wrangler deployments). */
13
- readonly ASTRALE_VERSION_METADATA?: {
14
- readonly id: string;
15
- readonly tag: string;
16
- readonly timestamp: string;
17
- };
18
- /** Content-derived generation supplied by providers without version metadata. */
19
- readonly ASTRALE_DEPLOYMENT_VERSION?: string;
20
- readonly WORKER_URL?: string;
21
- readonly ASSETS?: Fetcher;
22
- readonly SELF?: Fetcher;
23
- readonly FRONTEND_DEV_URL?: string;
24
- readonly DISPATCHER?: {
25
- get(name: string): Fetcher;
26
- };
27
- }
28
- interface CloudflareWorkerSharedConfig<Bindings extends CloudflareAdapterBindings, Environment> {
29
- readonly privateKey: RemoteIdentityInput['privateKey'] | ((bindings: Bindings) => RemoteIdentityInput['privateKey']);
30
- /** Compose provider bindings into the exact Domain environment once per deployment generation. */
31
- readonly resolveEnvironment?: (bindings: Bindings) => Environment | Promise<Environment>;
32
- readonly manifest?: CloudflareManifest;
33
- /** Maximum time allowed for one Domain invocation. Defaults to 30 seconds. */
34
- readonly invocationTimeoutMs?: number;
35
- readonly resolveUrl?: (bindings: Bindings, requestOrigin: string) => string;
36
- readonly selfBinding?: (bindings: Bindings) => Fetcher | null | undefined;
37
- readonly routeSubrequest?: (url: URL, bindings: Bindings) => Fetcher | null | undefined;
38
- }
39
- /**
40
- * Resolve a Domain lazily when its module graph is too expensive for provider startup admission.
41
- * The adapter resolves at most once concurrently and retries after a rejected resolution.
42
- */
43
- export type CloudflareWorkerConfig<S extends schema.DomainSchema, Bindings extends CloudflareAdapterBindings, Environment> = CloudflareWorkerSharedConfig<Bindings, Environment> & ({
44
- readonly definition: DomainDefinition<S>;
45
- readonly resolveDefinition?: never;
46
- } | {
47
- readonly definition?: never;
48
- readonly resolveDefinition: (bindings: Bindings) => DomainDefinition<S> | Promise<DomainDefinition<S>>;
49
- });
50
- export interface CloudflareWorker<Environment> {
51
- fetch(request: Request, environment: Environment, executionContext?: ExecutionContext): Response | Promise<Response>;
52
- }
53
- /**
54
- * Compose one Cloudflare module Worker without request-global mutation.
55
- * Environment bindings are captured once per immutable serving URL/version generation.
56
- */
57
- export declare function cloudflareWorkerEntry<Bindings extends CloudflareAdapterBindings, Environment = Bindings>(): <const S extends schema.DomainSchema>(config: CloudflareWorkerConfig<S, Bindings, Environment>) => CloudflareWorker<Bindings>;
58
- /** Close an evicted Application before its environment bindings leave the cache. */
59
- export declare function closeEvictedApplication(application: Pick<Application, 'close'>, now?: () => number): Promise<void>;
60
- export {};
61
- //# sourceMappingURL=index.d.ts.map
1
+ export * from './entry.js';
2
+ export { decodeSigningIdentity, runtimeBindings } from './identity.js';