@astrale-os/adapter-cloudflare 0.5.0-beta.178 → 0.5.0-beta.179

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 (111) hide show
  1. package/README.md +33 -72
  2. package/dist/adapter.d.ts +4 -18
  3. package/dist/adapter.js +23 -28
  4. package/dist/build/configuration/router.js +7 -1
  5. package/dist/build/configuration/settings.d.ts +1 -1
  6. package/dist/build/configuration/settings.js +1 -3
  7. package/dist/build/frontend/readiness.d.ts +10 -4
  8. package/dist/build/frontend/readiness.js +2 -2
  9. package/dist/build/index.d.ts +2 -9
  10. package/dist/build/index.js +2 -8
  11. package/dist/build/worker/codegen/service.js +7 -9
  12. package/dist/build/worker/media-type.d.ts +1 -0
  13. package/dist/build/worker/media-type.js +20 -0
  14. package/dist/build/worker/modules.d.ts +5 -2
  15. package/dist/build/worker/upload.d.ts +1 -17
  16. package/dist/build/worker/upload.js +1 -39
  17. package/dist/hosting/activation.d.ts +3 -2
  18. package/dist/hosting/activation.js +15 -14
  19. package/dist/hosting/dispatcher/deployments.d.mts +14 -0
  20. package/dist/hosting/dispatcher/deployments.mjs +18 -24
  21. package/dist/hosting/dispatcher/dispatcher.d.mts +5 -31
  22. package/dist/hosting/dispatcher/dispatcher.mjs +5 -25
  23. package/dist/hosting/dispatcher/env.d.mts +2 -2
  24. package/dist/hosting/dispatcher/index.mjs +1 -3
  25. package/dist/hosting/dispatcher/modules.d.ts +1 -2
  26. package/dist/hosting/dispatcher/modules.js +1 -2
  27. package/dist/hosting/dispatcher/services.d.mts +1 -6
  28. package/dist/hosting/dispatcher/services.mjs +16 -45
  29. package/dist/hosting/index.d.ts +1 -1
  30. package/dist/hosting/kv.d.ts +4 -3
  31. package/dist/hosting/kv.js +4 -3
  32. package/dist/hosting/provision/dispatcher.d.ts +3 -5
  33. package/dist/hosting/provision/dispatcher.js +3 -5
  34. package/dist/hosting/provision/plan.d.ts +2 -2
  35. package/dist/hosting/retirement.d.ts +3 -2
  36. package/dist/hosting/retirement.js +3 -2
  37. package/dist/hosting/testing/fake-cloudflare.d.ts +4 -3
  38. package/dist/hosting/testing/fake-cloudflare.js +3 -2
  39. package/dist/hosting/testing/index.d.ts +1 -1
  40. package/dist/hosting/testing/index.js +1 -1
  41. package/dist/index.d.ts +1 -3
  42. package/dist/index.js +1 -3
  43. package/dist/namespace/configure.js +0 -3
  44. package/dist/namespace/coordinates.d.ts +1 -7
  45. package/dist/namespace/coordinates.js +2 -20
  46. package/dist/namespace/index.d.ts +1 -1
  47. package/dist/namespace/index.js +1 -1
  48. package/dist/params.d.ts +9 -94
  49. package/dist/worker/build-digest.d.ts +2 -8
  50. package/dist/worker/build-digest.js +1 -13
  51. package/dist/worker/durable.d.ts +2 -10
  52. package/dist/worker/durable.js +4 -23
  53. package/dist/worker/entry.d.ts +8 -3
  54. package/dist/worker/entry.js +6 -16
  55. package/dist/worker/front.d.ts +5 -25
  56. package/dist/worker/front.js +3 -8
  57. package/dist/worker/index.d.ts +1 -2
  58. package/dist/worker/index.js +1 -2
  59. package/dist/worker/settings.d.ts +2 -3
  60. package/package.json +2 -2
  61. package/template/astrale.config.ts +1 -1
  62. package/dist/build/worker/artifact.d.ts +0 -40
  63. package/dist/build/worker/artifact.js +0 -1
  64. package/dist/build/worker/artifact.load.d.ts +0 -3
  65. package/dist/build/worker/artifact.load.js +0 -308
  66. package/dist/build/worker/artifact.manifest.d.ts +0 -48
  67. package/dist/build/worker/artifact.manifest.js +0 -87
  68. package/dist/build/worker/artifact.paths.d.ts +0 -4
  69. package/dist/build/worker/artifact.paths.js +0 -25
  70. package/dist/build/worker/index.d.ts +0 -2
  71. package/dist/build/worker/index.js +0 -1
  72. package/dist/build/worker/runtime.d.ts +0 -10
  73. package/dist/build/worker/runtime.js +0 -35
  74. package/dist/legacy/direct/addressing.d.ts +0 -2
  75. package/dist/legacy/direct/addressing.js +0 -17
  76. package/dist/legacy/direct/deploy.d.ts +0 -17
  77. package/dist/legacy/direct/deploy.js +0 -241
  78. package/dist/legacy/direct/identity/secret.d.ts +0 -3
  79. package/dist/legacy/direct/identity/secret.js +0 -7
  80. package/dist/legacy/direct/identity/source.d.ts +0 -29
  81. package/dist/legacy/direct/identity/source.js +0 -54
  82. package/dist/legacy/direct/index.d.ts +0 -23
  83. package/dist/legacy/direct/index.js +0 -23
  84. package/dist/legacy/direct/materialize.d.ts +0 -4
  85. package/dist/legacy/direct/materialize.js +0 -10
  86. package/dist/legacy/direct/placement.d.ts +0 -4
  87. package/dist/legacy/direct/placement.js +0 -91
  88. package/dist/legacy/direct/prepare.d.ts +0 -7
  89. package/dist/legacy/direct/prepare.js +0 -158
  90. package/dist/legacy/direct/readiness.d.ts +0 -34
  91. package/dist/legacy/direct/readiness.js +0 -95
  92. package/dist/legacy/direct/vars.d.ts +0 -27
  93. package/dist/legacy/direct/vars.js +0 -53
  94. package/dist/legacy/direct/worker-name.d.ts +0 -2
  95. package/dist/legacy/direct/worker-name.js +0 -18
  96. package/dist/legacy/direct/worker.d.ts +0 -37
  97. package/dist/legacy/direct/worker.js +0 -58
  98. package/dist/legacy/direct/wrangler/deploy.d.ts +0 -24
  99. package/dist/legacy/direct/wrangler/deploy.js +0 -44
  100. package/dist/legacy/direct/wrangler/output.d.ts +0 -2
  101. package/dist/legacy/direct/wrangler/output.js +0 -10
  102. package/dist/legacy/direct/wrangler/rollout.d.ts +0 -27
  103. package/dist/legacy/direct/wrangler/rollout.js +0 -75
  104. package/dist/legacy/direct/wrangler/secrets.d.ts +0 -2
  105. package/dist/legacy/direct/wrangler/secrets.js +0 -24
  106. package/dist/legacy/direct/wrangler/status.d.ts +0 -18
  107. package/dist/legacy/direct/wrangler/status.js +0 -75
  108. package/dist/legacy/direct/wrangler/triggers.d.ts +0 -5
  109. package/dist/legacy/direct/wrangler/triggers.js +0 -12
  110. package/dist/legacy/direct/wrangler/versions.d.ts +0 -13
  111. package/dist/legacy/direct/wrangler/versions.js +0 -38
package/README.md CHANGED
@@ -10,11 +10,10 @@ npm install @astrale-os/sdk@beta @astrale-os/adapter-cloudflare@beta
10
10
 
11
11
  Bare installs return to stable releases when the package set is promoted and npm `latest` advances.
12
12
 
13
- One `cloudflare()` value configures one Project Environment, in one of two modes. With
14
- `namespace`, each release becomes one immutable deployment in a Workers for Platforms dispatch
15
- namespace, on your own Cloudflare account or on a platform's. Without it, the Environment is on the
16
- legacy direct mode: one stable Worker, replaced in place by each deploy. A deploy never installs:
17
- install the URL it prints with `astrale domain install <url> --direct -i <instance>`.
13
+ One `cloudflare()` value configures one Project Environment in a Workers for Platforms dispatch
14
+ namespace. Each release becomes one immutable deployment on your own Cloudflare account or on a
15
+ platform's. A deploy never installs: install the URL it prints with
16
+ `astrale domain install <url> -i <instance>`.
18
17
 
19
18
  Generated Worker modules are minified by default, including separately loaded runtime modules.
20
19
  The Environment's `wrangler` overlay cannot change minification: every Environment of one source
@@ -74,10 +73,10 @@ Every line is readable. Namespace mode is a thin layer over the hosting kit belo
74
73
  secrets and `ASTRALE_SIGNING_IDENTITY`, a key generated for that deployment and kept nowhere
75
74
  else. Its tags name its artifact (build digest), line and key; never `astrale-service`, which is
76
75
  Services'.
77
- - **Activation.** The deploy writes `record:<label>`, whose presence makes the dispatcher route the
78
- label: the canonical `DeploymentRecordV1`, published through the kit's opaque record with its
79
- media type, which the dispatcher serves at `/.well-known/astrale/deployment.json`. It never holds
80
- a provider version or a secret digest. Readiness then waits, bounded, until the URL serves the
76
+ - **Activation.** The deploy writes `record:<label>` with its media type metadata, whose presence
77
+ makes the dispatcher route the label: the canonical `DeploymentRecordV1`, published through the
78
+ kit's opaque record, which the dispatcher serves at `/.well-known/astrale/deployment.json`. It
79
+ never holds a provider version or a secret digest. Readiness then waits, bounded, until the URL serves the
81
80
  sealed release from the provider version the upload created.
82
81
  - **Reuse.** A redeploy of the same release finds the script under its label. It is reused only
83
82
  when its artifact and line tags and its record name this build and configuration (only the
@@ -120,9 +119,10 @@ Node and inside a Worker (`fetch` and WebCrypto only).
120
119
  record, opaque bytes with their media type in the value's metadata, `{"contentType":...}`, or
121
120
  `-` with `{"contentType":null}` for a deployment that publishes none; written once and kept only
122
121
  for the same bytes and media type), `retirement:<label>` (`withdrawn`), `current:<name>` (one of
123
- the name's labels), `cron:<name>` (canonical UTC crons). A record written before records were
124
- opaque has no metadata: it is a canonical `DeploymentRecordV1`, read and served as one. Every
125
- write is read back before it counts.
122
+ the name's labels), `cron:<name>` (canonical UTC crons). The record's metadata is mandatory: a
123
+ `record:<label>` without it activates nothing at the dispatcher (every path of the label answers
124
+ as not active yet, and no cron fires on it) and the kit refuses it (`record-invalid`) instead of
125
+ reading it as any record. Every write is read back before it counts.
126
126
  - **Scripts.** `deploymentBindings`, `deploymentTags` (with a host's extra tags),
127
127
  `uploadDeploymentScript` (create-only, every module kind, assets in parallel),
128
128
  `observeScript`, `scriptSettings`, `scriptSecretNames`, `putScriptSecret`, `deleteScript`, and
@@ -137,53 +137,19 @@ Node and inside a Worker (`fetch` and WebCrypto only).
137
137
  to its script, a name to `current:<name>`, and firing `cron:<name>` on schedule. It serves a
138
138
  label's record at `/.well-known/astrale/deployment.json`, its bytes and media type as published
139
139
  (immutable, `405` but for `GET` and `HEAD`), and hands that path to a deployment that publishes
140
- none. A host prepends
141
- its own routing and schedule targets with `createDispatcher(extension)` in its own `index.mjs`,
142
- without forking it. The public never reaches `/__scheduled`; nothing in the generated Worker
143
- serves it yet.
140
+ none. `createDispatcher()` owns routing and schedule admission. The public never reaches
141
+ `/__scheduled`; cron targets serve that internal POST endpoint.
144
142
  - **Provisioning.** `planHosting` reads; `applyHosting` refuses any blocker before its first write;
145
143
  `provisionHosting` does both.
146
144
  - **Testing.** `@astrale-os/adapter-cloudflare/hosting/testing` exports `FakeCloudflare`, one fake
147
145
  account that serves public requests with the real dispatcher.
148
146
 
149
- ## Legacy direct mode
150
-
151
- Deprecated: replaced by namespace mode, and deleted with the legacy deploy path (D10) once no
152
- configuration on any repository's main selects direct mode. Its consumers until they move: domains
153
- organization, whose `prod` binds an EU-jurisdiction R2 bucket that namespace mode does not admit
154
- yet, and the SDK's own e2e fixture (`__e2e__/flagship/fixtures/v1`).
155
-
156
- `astrale-domain deploy <environment>` builds locally and deploys the Worker and frontend
157
- remotely. Omitting `route` uses the observed stable `*.workers.dev` issuer.
158
-
159
- For an existing Worker, a deploy uploads a candidate, reconciles
160
- configured triggers, and stages the candidate at 0% while the previous version retains 100% of normal
161
- HTTP traffic. Exact-version probes verify Publication, Bundle, and serving identity before promotion;
162
- serving the Publication also initializes the Domain Runtime. After promotion, normal requests
163
- must prove that exact version again. A failed candidate remains at 0% and can be replaced on retry.
164
- Bundled View routes must also serve the prepared browser-file generation before readiness passes.
165
- This covers frontend-only changes without changing the Schema or reinstalling the Domain. HTML is
166
- revalidated on every use and transient asset misses are not cached; ordinary successful asset
167
- responses keep their cache policy. Frontend files are answered by the Worker's small startup module
168
- without loading the execution Service, which each such file starts loading in the background for the
169
- calls the View makes next.
170
- The first deployment uses ordinary Worker provisioning because no previous version exists to retain.
171
- An existing traffic-split rollout must be resolved before this single-version workflow can continue.
172
-
173
- A direct Cloudflare target shared across machines must still have one external writer, normally a
174
- CI/CD concurrency group. Live deployment checks detect competing changes but are not an atomic
175
- compare-and-swap or distributed lock. Use separate Worker targets for uncoordinated development.
176
- Routes, custom domains, schedules, and other non-versioned provider settings are reconciled through
177
- Wrangler before candidate probing; they can remain changed after a failed attempt. Candidate readiness
178
- protects the HTTP implementation, not an atomic rollback of external resources or trigger settings.
179
-
180
147
  ## Frontend files
181
148
 
182
149
  The Worker runs before Workers Static Assets on every request and owns frontend response headers
183
150
  and routing, for `cloudflare()` and `astrale()` deployments alike. Frontend preparation therefore
184
151
  refuses a built frontend whose root contains `_headers`, `_redirects`, or `.assetsignore`, in any
185
- letter case, as a file or a directory. Wrangler would apply them as asset configuration in a direct
186
- deployment, while a managed deployment would serve them as public files. The refusal happens while
152
+ letter case, as a file or a directory. Wrangler and managed deployments would otherwise interpret these files differently. The refusal happens while
187
153
  the artifact is prepared, before any provider change. Vite copies its public directory (`public/`
188
154
  by default) to that root: remove them there, or stop the plugin that emits them. The same names
189
155
  below the root, such as `docs/_headers`, remain ordinary files.
@@ -205,26 +171,18 @@ same bytes. Each deployment binds its own values as plain Worker bindings:
205
171
  | --- | --- |
206
172
  | `WORKER_URL` | serving origin of the deployment |
207
173
  | `ASTRALE_SETTINGS` | canonical JSON of `{ invocationTimeoutMs, maximumRequestBytes, router, tokenExchange }`, defaults resolved, with optional `durableOptions` |
208
- | `ASTRALE_BUILD_DIGEST` | build digest the host recomputed from the uploaded bytes, bound only by hosts that deploy immutable Workers; never by a stable Worker |
209
- | `IDENTITY_ISS`, `ASTRALE_PROVIDER_ASSIGNED_WORKER` | **legacy direct mode only** (deprecated): a stable issuer behind another route, and the workers.dev claim |
174
+ | `ASTRALE_BUILD_DIGEST` | required build digest the host recomputed from the uploaded bytes |
210
175
 
211
176
  These names are adapter-owned: preparation refuses an author var, or a `wrangler.vars` entry, that
212
177
  takes one, and deployment refuses a secret that does. A host that deploys the Worker itself encodes
213
178
  the settings with `encodeWorkerSettings` from `@astrale-os/adapter-cloudflare/worker`. A generated
214
179
  Worker refuses execution without `ASTRALE_SETTINGS`.
215
180
 
216
- `ASTRALE_BUILD_DIGEST` selects what the Worker serves. Bound, the Worker is an immutable deployment:
217
- it seals the `DomainRelease` v4 of its code at its `WORKER_URL`, which is its issuer, and serves it
218
- at `/.well-known/astrale/release.json` beside its frozen v3 projection at `domain.json`; it admits
219
- only routing that names that exact release or projection. Unbound, it is a stable Worker and serves
220
- exactly the v3 Publication of today, answering 404 at `release.json`. The Worker refuses execution,
221
- instead of falling back to v3, when the digest is malformed, when it is bound without `WORKER_URL`,
222
- or beside a direct-mode binding or a configured `identityIssuer`.
223
-
224
- The two direct-mode bindings are deprecated. Their replacement is an immutable deployment, whose
225
- host binds `WORKER_URL` to the deployment URL, which is also its issuer (namespace mode). They are
226
- deleted with direct mode, once the Shell, Services, Issues, domains and ui configurations deploy in
227
- namespace mode and no installation pins a direct Worker.
181
+ Every Worker requires `WORKER_URL` and `ASTRALE_BUILD_DIGEST`. It seals the `DomainRelease` v4
182
+ of its code at that serving URL, which is also its issuer, and serves it at
183
+ `/.well-known/astrale/release.json`. Routing must name that exact Release. The Worker refuses
184
+ execution when either binding is missing or invalid; a foreign request origin is refused before
185
+ loading the Service. `domain.json` serves no compatibility projection.
228
186
 
229
187
  The `wrangler` overlay reaches the deployment configuration, never the bundle. Preparation refuses
230
188
  an overlay key the bundler reads (`alias`, `build`, `define`, `jsx_factory`, `jsx_fragment`,
@@ -240,14 +198,13 @@ digest, and `deploy` places that same Bundle. Two installed versions of this pac
240
198
  copies of the bundler: when their code differs, `build` prints one build digest per copy and warns
241
199
  until the adapter versions are aligned.
242
200
 
243
- Services receives the placed modules as they are, so there the digest names the bytes the
244
- deployment runs. Direct mode runs `wrangler deploy`, which bundles the placed modules again: there
245
- the digest names the portable Worker the deployment was made from, not the bytes Cloudflare runs.
246
- A host that receives a Worker upload recomputes the digest from the exact upload with
247
- `workerBuildDigest` (`@astrale-os/sdk/deployment/worker`), given the members of the Bundle's build
248
- manifest its deployer declares (`format`, `version`, `origin`, `schema`, `bundler`): modules keep
249
- their uploaded name and an asset `/<path>` is the file `assets/<path>`, so the digest is the
250
- Bundle's own. The bundler version names this layout, not a package release.
201
+ Services and namespace deployments upload the placed modules unchanged, so the digest names the
202
+ bytes the deployment runs. A host with its own Worker entrypoint describes its final upload with
203
+ `workerBuildManifest` and `WorkerUpload` from `@astrale-os/adapter-cloudflare/build`, then computes
204
+ its digest with `buildDigest` from `@astrale-os/sdk/deployment/build/manifest`. Modules retain
205
+ their uploaded names and an asset `/<path>` becomes the file `assets/<path>`. The host binds that
206
+ digest as `ASTRALE_BUILD_DIGEST` and uploads those exact bytes without bundling them again.
207
+ The bundler version names this layout, not a package release.
251
208
 
252
209
  ## Material for a custom Worker entrypoint
253
210
 
@@ -264,8 +221,12 @@ await writeFile('src/build-material.gen.ts', generateBuildMaterialModule(build))
264
221
 
265
222
  The generated TypeScript module exports `loadBuildMaterial()`. Call it from the
266
223
  `load` callback of `generatedCloudflareWorkerEntry` alongside the Domain's Runtime.
224
+ That callback loads deterministic local modules once per entrypoint. Concurrent execution and
225
+ `prepare()` calls share the same result, including a rejection; subsequent calls do not retry it.
226
+ A newly constructed entrypoint starts independently. Keep remote preparation that may recover
227
+ from transient failures in its own lifecycle, rather than putting it in `load`.
267
228
  Such an entrypoint states its execution settings (`invocationTimeoutMs`, `maximumRequestBytes`,
268
- `tokenExchange`, `routeSubrequest`, `identityIssuer`) in its configuration, or reads them from its
229
+ `tokenExchange`, `routeSubrequest`) in its configuration, or reads them from its
269
230
  deployment bindings with `settings: deploymentSettings`; one Worker never takes both.
270
231
  It constructs the same verified, deduplicated material as the adapter's generated Service;
271
232
  allocation is deferred until the factory is called and the SDK deep-freezes it before use.
package/dist/adapter.d.ts CHANGED
@@ -1,25 +1,11 @@
1
1
  import type { Deployment } from '@astrale-os/sdk/deployment';
2
2
  import type { Adapter } from '@astrale-os/sdk/deployment/adapter';
3
- import type { CloudflareDeployDependencies } from './legacy/direct/index.js';
4
3
  import type { NamespaceDependencies } from './namespace/index.js';
5
4
  import type { CloudflareParams } from './params.js';
6
- import { artifact, signingIdentity } from './legacy/direct/index.js';
7
- export { waitForReadiness, type ReadinessCheck } from './legacy/direct/index.js';
8
5
  export type { NamespaceDependencies } from './namespace/index.js';
9
- export interface CloudflareDependencies extends CloudflareDeployDependencies {
10
- readonly artifact: typeof artifact;
11
- readonly signingIdentity: typeof signingIdentity;
12
- /** The effects of namespace mode. Default: `fetch`, `process.env` and a new WebCrypto key. */
13
- readonly namespace?: NamespaceDependencies;
6
+ export interface CloudflareDependencies {
7
+ readonly namespace: NamespaceDependencies;
14
8
  }
15
- /**
16
- * Deploy one Project Environment through Cloudflare Workers.
17
- *
18
- * With `namespace`, each release becomes one immutable deployment in that dispatch namespace
19
- * (adapter interface v2), `astrale-domain list` lists them, and the adapter's commands
20
- * `astrale-domain cloudflare plan|init <environment>` provision the namespace on your own account.
21
- * Without it, the Environment stays on the legacy direct mode: one stable Worker replaced in place
22
- * by each deploy (CT33).
23
- */
24
- export declare function cloudflare(parameters?: CloudflareParams): Deployment<CloudflareParams, Adapter<CloudflareParams>>;
9
+ /** Deploy each release as one immutable deployment in a Cloudflare dispatch namespace. */
10
+ export declare function cloudflare(parameters: CloudflareParams): Deployment<CloudflareParams, Adapter<CloudflareParams>>;
25
11
  export declare function cloudflareWithDependencies(parameters: CloudflareParams, dependencies: CloudflareDependencies): Deployment<CloudflareParams, Adapter<CloudflareParams>>;
package/dist/adapter.js CHANGED
@@ -1,33 +1,17 @@
1
1
  import { defineAdapter, defineDeployment } from '@astrale-os/sdk/deployment/adapter';
2
2
  import { createRequire } from 'node:module';
3
3
  import { cloudflareWorkerBundler } from './build/bundler.js';
4
+ import { workerSettings } from './build/configuration.js';
4
5
  import { createSigningIdentity } from './hosting/identity.js';
5
- import { artifact, deployCloudflare, directReadiness, materialize, runWranglerDeploymentStatus, runWranglerRollout, signingIdentity, } from './legacy/direct/index.js';
6
- import { admitNamespace, configureNamespace, deployNamespaceRelease, listNamespaceDeployments, hostingCommands, placeNamespace, } from './namespace/index.js';
7
- export { waitForReadiness } from './legacy/direct/index.js';
6
+ import { configureNamespace, deployNamespaceRelease, listNamespaceDeployments, hostingCommands, namespaceCoordinates, placeNamespace, } from './namespace/index.js';
8
7
  const packageVersion = createRequire(import.meta.url)('../package.json')
9
8
  .version;
10
- /**
11
- * Deploy one Project Environment through Cloudflare Workers.
12
- *
13
- * With `namespace`, each release becomes one immutable deployment in that dispatch namespace
14
- * (adapter interface v2), `astrale-domain list` lists them, and the adapter's commands
15
- * `astrale-domain cloudflare plan|init <environment>` provision the namespace on your own account.
16
- * Without it, the Environment stays on the legacy direct mode: one stable Worker replaced in place
17
- * by each deploy (CT33).
18
- */
19
- export function cloudflare(parameters = {}) {
20
- return cloudflareWithDependencies(parameters, {
21
- artifact,
22
- materialize,
23
- runWranglerRollout,
24
- runWranglerDeploymentStatus,
25
- signingIdentity,
26
- waitForReadiness: directReadiness,
27
- });
9
+ /** Deploy each release as one immutable deployment in a Cloudflare dispatch namespace. */
10
+ export function cloudflare(parameters) {
11
+ return cloudflareWithDependencies(parameters, { namespace: platformNamespaceDependencies() });
28
12
  }
29
13
  export function cloudflareWithDependencies(parameters, dependencies) {
30
- const namespace = dependencies.namespace ?? platformNamespaceDependencies();
14
+ const namespace = dependencies.namespace;
31
15
  const adapter = defineAdapter({
32
16
  name: 'cloudflare',
33
17
  version: packageVersion,
@@ -38,17 +22,28 @@ export function cloudflareWithDependencies(parameters, dependencies) {
38
22
  deployRelease: (parameters, context) => deployNamespaceRelease(parameters, context, namespace),
39
23
  list: (parameters, context) => listNamespaceDeployments(parameters, context, namespace),
40
24
  commands: hostingCommands(namespace),
41
- // Legacy direct mode (CT33 `legacy-direct`), deleted with it (D10).
42
- prepare: dependencies.artifact,
43
- deploy: (parameters, context) => deployCloudflare(parameters, context, dependencies),
44
25
  secretsFile: (parameters) => parameters.secrets,
45
26
  });
46
27
  return defineDeployment(adapter, parameters);
47
28
  }
48
- /** Refuse a malformed `namespace` when the Project is defined, before any build. */
29
+ /** Validate the deployment contract when the Project loads, before any build or provider effect. */
49
30
  function admitCloudflare(parameters) {
50
- if (parameters.namespace !== undefined)
51
- admitNamespace(parameters.namespace);
31
+ const allowed = [
32
+ 'namespace',
33
+ 'secrets',
34
+ 'vars',
35
+ 'maximumRequestBytes',
36
+ 'invocationTimeoutMs',
37
+ 'tokenExchange',
38
+ 'durable',
39
+ 'wrangler',
40
+ 'router',
41
+ ];
42
+ if (Reflect.ownKeys(parameters).some((key) => !allowed.includes(key))) {
43
+ throw new TypeError('Cloudflare deployment parameters contain unknown fields.');
44
+ }
45
+ namespaceCoordinates(parameters);
46
+ workerSettings(parameters);
52
47
  }
53
48
  function platformNamespaceDependencies() {
54
49
  return Object.freeze({
@@ -1,9 +1,15 @@
1
1
  export function resolveRouter(input) {
2
2
  if (input === false || input === undefined)
3
3
  return undefined;
4
+ if (input === null ||
5
+ typeof input !== 'object' ||
6
+ Array.isArray(input) ||
7
+ Reflect.ownKeys(input).some((key) => !['service', 'hostSuffix', 'minLabels'].includes(String(key)))) {
8
+ throw new TypeError('Cloudflare router parameters contain unknown fields.');
9
+ }
4
10
  const value = input;
5
11
  return Object.freeze({
6
- binding: value.binding ?? 'ROUTER',
12
+ binding: 'ROUTER',
7
13
  service: value.service ?? 'admin-router',
8
14
  hostSuffix: value.hostSuffix ?? '.astrale.ai',
9
15
  minLabels: value.minLabels ?? 4,
@@ -3,4 +3,4 @@ import type { WorkerSettings } from '../../worker/settings.js';
3
3
  /** Whether a binding name is one the adapter owns, so no author var or secret may take it. */
4
4
  export declare function isAdapterVar(name: string): boolean;
5
5
  /** Resolve one Environment's execution settings, defaults included, as its deployment binds them. */
6
- export declare function workerSettings(parameters: CloudflareParams): WorkerSettings;
6
+ export declare function workerSettings(parameters: Pick<CloudflareParams, 'durable' | 'maximumRequestBytes' | 'invocationTimeoutMs' | 'tokenExchange' | 'router'>): WorkerSettings;
@@ -6,14 +6,12 @@ import { workerRequestLimit } from './request-limit.js';
6
6
  import { resolveRouter } from './router.js';
7
7
  /**
8
8
  * Plain bindings the adapter owns on a deployment, bound or not; no author var or secret takes
9
- * these names. `IDENTITY_ISS` and `ASTRALE_PROVIDER_ASSIGNED_WORKER` belong to legacy direct mode.
9
+ * these names.
10
10
  */
11
11
  const ADAPTER_VARS = new Set([
12
12
  'WORKER_URL',
13
13
  BUILD_DIGEST_BINDING,
14
14
  SETTINGS_BINDING,
15
- 'IDENTITY_ISS',
16
- 'ASTRALE_PROVIDER_ASSIGNED_WORKER',
17
15
  ]);
18
16
  /** Whether a binding name is one the adapter owns, so no author var or secret may take it. */
19
17
  export function isAdapterVar(name) {
@@ -1,9 +1,15 @@
1
- import type { legacy } from '@astrale-os/sdk/deployment/release';
1
+ import type { Release } from '@astrale-os/sdk/deployment/release';
2
2
  import type { verifyReadiness } from '@astrale-os/sdk/deployment/verify';
3
- import type { WorkerArtifact } from '../worker/artifact.js';
4
3
  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: legacy.Release, worker: Pick<WorkerArtifact, 'assets'>, options?: {
4
+ /** Release equality alone cannot prove a frontend-only deployment has propagated. */
5
+ export declare function verifyWorkerFrontend(release: Release, worker: {
6
+ readonly assets?: {
7
+ readonly files: readonly {
8
+ readonly path: string;
9
+ bytes(): Uint8Array;
10
+ }[];
11
+ };
12
+ }, options?: {
7
13
  readonly fetch?: ReadinessFetch;
8
14
  readonly signal?: AbortSignal;
9
15
  }): Promise<void>;
@@ -1,5 +1,5 @@
1
1
  import { frontendRevision } from './revision.js';
2
- /** Publication equality alone cannot prove a frontend-only deployment has propagated. */
2
+ /** Release equality alone cannot prove a frontend-only deployment has propagated. */
3
3
  export async function verifyWorkerFrontend(release, worker, options = {}) {
4
4
  const frontend = release.build.frontend;
5
5
  if (frontend === undefined || frontend.source.kind === 'external')
@@ -9,7 +9,7 @@ export async function verifyWorkerFrontend(release, worker, options = {}) {
9
9
  const expected = frontendRevision(worker.assets.files);
10
10
  const fetcher = options.fetch ?? globalThis.fetch;
11
11
  for (const route of frontend.routes) {
12
- const response = await fetcher(new URL(route.path, release.addressing.publicUrl).href, {
12
+ const response = await fetcher(new URL(route.path, release.url).href, {
13
13
  redirect: 'error',
14
14
  ...(options.signal === undefined ? {} : { signal: options.signal }),
15
15
  });
@@ -1,8 +1,7 @@
1
1
  export { generateBuildMaterialModule } from './material.js';
2
2
  export { cloudflareWorkerBundler } from './bundler.js';
3
- export { loadWorkerArtifact } from './worker/index.js';
4
- export type { WorkerArtifact } from './worker/index.js';
5
- export { bundleUpload, uploadBuildDigest, uploadBuildEvidence, workerUpload, type BundleUpload, } from './worker/upload.js';
3
+ export { bundleUpload, type BundleUpload } from './worker/upload.js';
4
+ export { workerBuildManifest, type WorkerUpload } from '../release/build.js';
6
5
  export { isAdapterVar, resolveRouter, workerSettings } from './configuration.js';
7
6
  export { encodeWorkerSettings, SETTINGS_BINDING } from '../worker/settings.js';
8
7
  export { GENERATED_WORKER_RUNTIME } from './codegen/wrangler.js';
@@ -11,9 +10,3 @@ export { verifyWorkerFrontend } from './frontend/readiness.js';
11
10
  export { qualifyWorkerBundle } from './qualification.js';
12
11
  export { assertSingletonRuntimeRoots } from './roots.js';
13
12
  export { runWranglerBundle, runWranglerTypes } from '../wrangler/index.js';
14
- /**
15
- * @deprecated Legacy direct mode only (`../legacy/direct/index.ts`): preparation of a stable
16
- * Worker, its Wrangler deployment and its name. Replacement: `cloudflare({ namespace })`, which
17
- * deploys one immutable deployment per release. Deleted with direct mode (D10).
18
- */
19
- export { artifact, materialize, prepareWorkerArtifact, runWranglerDeploy, runWranglerDeploymentStatus, workerName, } from '../legacy/direct/index.js';
@@ -1,7 +1,7 @@
1
1
  export { generateBuildMaterialModule } from './material.js';
2
2
  export { cloudflareWorkerBundler } from './bundler.js';
3
- export { loadWorkerArtifact } from './worker/index.js';
4
- export { bundleUpload, uploadBuildDigest, uploadBuildEvidence, workerUpload, } from './worker/upload.js';
3
+ export { bundleUpload } from './worker/upload.js';
4
+ export { workerBuildManifest } from '../release/build.js';
5
5
  export { isAdapterVar, resolveRouter, workerSettings } from './configuration.js';
6
6
  export { encodeWorkerSettings, SETTINGS_BINDING } from '../worker/settings.js';
7
7
  export { GENERATED_WORKER_RUNTIME } from './codegen/wrangler.js';
@@ -10,9 +10,3 @@ export { verifyWorkerFrontend } from './frontend/readiness.js';
10
10
  export { qualifyWorkerBundle } from './qualification.js';
11
11
  export { assertSingletonRuntimeRoots } from './roots.js';
12
12
  export { runWranglerBundle, runWranglerTypes } from '../wrangler/index.js';
13
- /**
14
- * @deprecated Legacy direct mode only (`../legacy/direct/index.ts`): preparation of a stable
15
- * Worker, its Wrangler deployment and its name. Replacement: `cloudflare({ namespace })`, which
16
- * deploys one immutable deployment per release. Deleted with direct mode (D10).
17
- */
18
- export { artifact, materialize, prepareWorkerArtifact, runWranglerDeploy, runWranglerDeploymentStatus, workerName, } from '../legacy/direct/index.js';
@@ -10,8 +10,8 @@ export function generateExecutionService(options) {
10
10
  verifySharedLiteral(material, generated);
11
11
  const materialDeclarations = material.declarations.map((line) => ` ${line}\n`).join('');
12
12
  const durableImports = options.durable === true
13
- ? `import { RESTATE_IDENTITY_KEYS_BINDING, restateServing } from ${JSON.stringify(workerSpecifier)}
14
- import { isCanonicalDurableRequest, isLegacyDurableRequest } from '@astrale-os/sdk/execution'
13
+ ? `import { restateServing } from ${JSON.stringify(workerSpecifier)}
14
+ import { isCanonicalDurableRequest } from '@astrale-os/sdk/execution'
15
15
  // A variable specifier preserves the engine as a separate deployed module.
16
16
  const RESTATE_SPECIFIER = ${JSON.stringify(options.restateSpecifier ?? './restate.mjs')}
17
17
  `
@@ -22,14 +22,13 @@ const RESTATE_SPECIFIER = ${JSON.stringify(options.restateSpecifier ?? './restat
22
22
  return `// AUTO-GENERATED by @astrale-os/adapter-cloudflare — do not edit.
23
23
  // Domain origin: ${options.origin}
24
24
  import type { BuildMaterial, GeneratedBuildMaterial } from '@astrale-os/sdk/deployment/build'
25
- import type { CloudflareAdapterBindings${options.durable === true ? ', DurableBindings' : ''} } from ${JSON.stringify(workerSpecifier)}
25
+ import type { CloudflareAdapterBindings } from ${JSON.stringify(workerSpecifier)}
26
26
 
27
27
  import { generatedCloudflareWorkerEntry } from ${JSON.stringify(workerSpecifier)}
28
28
  import { decodeSigningIdentity, deploymentSettings, runtimeBindings } from ${JSON.stringify(workerSpecifier)}
29
- import { withDirectIssuer } from ${JSON.stringify(workerSpecifier)}
30
29
 
31
30
  ${durableImports}
32
- type WorkerEnv = CloudflareEnv & CloudflareAdapterBindings${options.durable === true ? ' & DurableBindings' : ''} & {
31
+ type WorkerEnv = CloudflareEnv & CloudflareAdapterBindings & {
33
32
  readonly ASTRALE_SIGNING_IDENTITY: string
34
33
  }
35
34
  type RuntimeEnv = Omit<WorkerEnv, 'ASTRALE_SIGNING_IDENTITY'>
@@ -43,14 +42,13 @@ ${materialDeclarations} return {
43
42
  },
44
43
  privateKey: (env) => decodeSigningIdentity(env.ASTRALE_SIGNING_IDENTITY),
45
44
  resolveEnvironment: runtimeBindings,
46
- // Legacy direct mode: a bound IDENTITY_ISS signs instead of the serving origin.
47
- settings: withDirectIssuer(deploymentSettings),
45
+ settings: deploymentSettings,
48
46
  selfBinding: (env) => env.SELF,${options.durable === true
49
47
  ? `
50
48
  durable: async (env, request, options) => {
51
- if (!isCanonicalDurableRequest(request) && !(isLegacyDurableRequest(request) && env[RESTATE_IDENTITY_KEYS_BINDING]?.trim())) return undefined
49
+ if (!isCanonicalDurableRequest(request)) return undefined
52
50
  const restate = await import(RESTATE_SPECIFIER)
53
- return restateServing(restate, env, options, request)
51
+ return restateServing(restate, request, options)
54
52
  },`
55
53
  : ''}
56
54
  })
@@ -0,0 +1 @@
1
+ export declare function inferMediaType(path: string): string | undefined;
@@ -0,0 +1,20 @@
1
+ export function inferMediaType(path) {
2
+ const extension = path.split('.').at(-1)?.toLowerCase();
3
+ return extension === undefined ? undefined : MEDIA_TYPES[extension];
4
+ }
5
+ const MEDIA_TYPES = Object.freeze({
6
+ html: 'text/html; charset=utf-8',
7
+ js: 'text/javascript; charset=utf-8',
8
+ mjs: 'text/javascript; charset=utf-8',
9
+ css: 'text/css; charset=utf-8',
10
+ json: 'application/json; charset=utf-8',
11
+ svg: 'image/svg+xml',
12
+ png: 'image/png',
13
+ jpg: 'image/jpeg',
14
+ jpeg: 'image/jpeg',
15
+ webp: 'image/webp',
16
+ ico: 'image/x-icon',
17
+ woff: 'font/woff',
18
+ woff2: 'font/woff2',
19
+ wasm: 'application/wasm',
20
+ });
@@ -1,4 +1,4 @@
1
- import type { WorkerArtifact } from './artifact.js';
1
+ import type { WorkerModuleKind } from '@astrale-os/sdk/deployment/worker';
2
2
  type WranglerModuleType = 'ESModule' | 'CompiledWasm' | 'Text' | 'Data';
3
3
  /**
4
4
  * Project one admitted Worker module inventory into Wrangler's relative module discovery. Rules
@@ -6,7 +6,10 @@ type WranglerModuleType = 'ESModule' | 'CompiledWasm' | 'Text' | 'Data';
6
6
  */
7
7
  export declare function workerModules(worker: {
8
8
  readonly entrypoint: string;
9
- readonly modules: readonly Pick<WorkerArtifact['release']['modules'][number], 'path' | 'kind'>[];
9
+ readonly modules: readonly {
10
+ readonly path: string;
11
+ readonly kind: WorkerModuleKind;
12
+ }[];
10
13
  }): {
11
14
  readonly baseDir: string;
12
15
  readonly rules: readonly {
@@ -1,10 +1,5 @@
1
- import type { Bundle, PreparedArtifact } from '@astrale-os/sdk/deployment/adapter';
2
- import type { Build } from '@astrale-os/sdk/deployment/build';
3
- import type { BuildDigest } from '@astrale-os/sdk/deployment/build/manifest';
4
- import type { WorkerArtifact } from './artifact.js';
1
+ import type { Bundle } from '@astrale-os/sdk/deployment/adapter';
5
2
  import { type WorkerUpload } from '../../release/build.js';
6
- /** The portable upload of one prepared Worker: exactly the modules and assets Services receives. */
7
- export declare function workerUpload(artifact: PreparedArtifact, worker: WorkerArtifact): WorkerUpload;
8
3
  /**
9
4
  * The portable upload of one Bundle of the Cloudflare Worker bundler, as an immutable deployment's
10
5
  * host receives it: the entry and the modules it loads (ES modules and their WASM), and the
@@ -26,14 +21,3 @@ export interface BundleUpload extends WorkerUpload {
26
21
  readonly contentType?: string;
27
22
  }[];
28
23
  }
29
- /** Build digest of the portable upload of one prepared Worker. */
30
- export declare function uploadBuildDigest(artifact: PreparedArtifact, worker: WorkerArtifact): BuildDigest;
31
- /** Record that preparation placed the CLI's Bundle for this Build. */
32
- export declare function recordPlacedBundle(build: Build): void;
33
- /**
34
- * The `build` field of one prepared Worker's provider evidence: the build digest of its portable
35
- * upload, present only when its preparation placed the CLI's Bundle.
36
- */
37
- export declare function uploadBuildEvidence(artifact: PreparedArtifact, worker: WorkerArtifact): {
38
- readonly build?: BuildDigest;
39
- };
@@ -1,25 +1,8 @@
1
1
  import { buildDigest } from '@astrale-os/sdk/deployment/build/manifest';
2
2
  import { workerBuildManifest } from '../../release/build.js';
3
3
  import { bundledWorker } from '../bundler.js';
4
- import { inferMediaType } from './artifact.manifest.js';
5
4
  import { RELEASE_ENTRY } from './execution.prepare.js';
6
- /** The portable upload of one prepared Worker: exactly the modules and assets Services receives. */
7
- export function workerUpload(artifact, worker) {
8
- const root = artifact.build.schema.compiled.root;
9
- if (worker.origin !== root.origin || worker.revision !== root.revision) {
10
- throw new TypeError('Worker artifact differs from its prepared Build.');
11
- }
12
- return Object.freeze({
13
- origin: root.origin,
14
- revision: root.revision,
15
- bundle: artifact.build.schema.bundle.ref.digest,
16
- entry: worker.release.entrypoint,
17
- modules: worker.release.modules.map((module) => ({ name: module.path, bytes: module.bytes() })),
18
- ...(worker.assets === undefined
19
- ? {}
20
- : { assets: worker.assets.files.map((file) => ({ path: file.path, bytes: file.bytes() })) }),
21
- });
22
- }
5
+ import { inferMediaType } from './media-type.js';
23
6
  /**
24
7
  * The portable upload of one Bundle of the Cloudflare Worker bundler, as an immutable deployment's
25
8
  * host receives it: the entry and the modules it loads (ES modules and their WASM), and the
@@ -57,24 +40,3 @@ export function bundleUpload(bundle) {
57
40
  }
58
41
  return upload;
59
42
  }
60
- /** Build digest of the portable upload of one prepared Worker. */
61
- export function uploadBuildDigest(artifact, worker) {
62
- return buildDigest(workerBuildManifest(workerUpload(artifact, worker)));
63
- }
64
- /**
65
- * Builds whose preparation placed the Bundle `astrale-domain deploy` passed (`PrepareContext.bundle`).
66
- * Only a CLI that passes a Bundle reads `ProviderDeploymentEvidence.build`: an older one refuses an
67
- * evidence field it does not know, after the provider has applied the upload.
68
- */
69
- const placedBuilds = new WeakSet();
70
- /** Record that preparation placed the CLI's Bundle for this Build. */
71
- export function recordPlacedBundle(build) {
72
- placedBuilds.add(build);
73
- }
74
- /**
75
- * The `build` field of one prepared Worker's provider evidence: the build digest of its portable
76
- * upload, present only when its preparation placed the CLI's Bundle.
77
- */
78
- export function uploadBuildEvidence(artifact, worker) {
79
- return placedBuilds.has(artifact.build) ? { build: uploadBuildDigest(artifact, worker) } : {};
80
- }
@@ -11,8 +11,9 @@ import type { RoutingKv } from './kv.js';
11
11
  export declare function activateDeployment(kv: RoutingKv, label: string, record: WorkerRecord | null): Promise<WorkerRecord | null>;
12
12
  /**
13
13
  * The record a label was activated with: the record, `null` when the deployment publishes none,
14
- * or undefined when the label is not activated. A record written before records were opaque,
15
- * without metadata, is the `DeploymentRecordV1` it holds, with that media type.
14
+ * or undefined when the label is not activated. A value whose metadata is not exactly
15
+ * `{"contentType":<media type or null>}` (none at all included) is no record the kit writes, and
16
+ * is refused (`record-invalid`) rather than read as any record.
16
17
  */
17
18
  export declare function readRecord(kv: RoutingKv, label: string): Promise<WorkerRecord | null | undefined>;
18
19
  /** Whether two activations publish the same record (or both none). */