@astrale-os/adapter-cloudflare 0.5.0-beta.177 → 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 (123) hide show
  1. package/README.md +48 -83
  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 +3 -9
  10. package/dist/build/index.js +3 -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 +17 -9
  18. package/dist/hosting/activation.js +53 -15
  19. package/dist/hosting/client.d.ts +5 -0
  20. package/dist/hosting/client.js +37 -5
  21. package/dist/hosting/dispatcher/deployments.d.mts +14 -0
  22. package/dist/hosting/dispatcher/deployments.mjs +36 -11
  23. package/dist/hosting/dispatcher/dispatcher.d.mts +5 -31
  24. package/dist/hosting/dispatcher/dispatcher.mjs +5 -25
  25. package/dist/hosting/dispatcher/env.d.mts +6 -1
  26. package/dist/hosting/dispatcher/index.mjs +1 -3
  27. package/dist/hosting/dispatcher/modules.d.ts +1 -2
  28. package/dist/hosting/dispatcher/modules.js +1 -2
  29. package/dist/hosting/dispatcher/services.d.mts +1 -6
  30. package/dist/hosting/dispatcher/services.mjs +16 -45
  31. package/dist/hosting/index.d.ts +5 -4
  32. package/dist/hosting/index.js +4 -3
  33. package/dist/hosting/kv.d.ts +20 -19
  34. package/dist/hosting/kv.js +49 -44
  35. package/dist/hosting/listing.d.ts +7 -6
  36. package/dist/hosting/listing.js +6 -13
  37. package/dist/hosting/provision/dispatcher.d.ts +3 -5
  38. package/dist/hosting/provision/dispatcher.js +3 -5
  39. package/dist/hosting/provision/plan.d.ts +2 -2
  40. package/dist/hosting/refusal.d.ts +1 -1
  41. package/dist/hosting/retirement.d.ts +8 -6
  42. package/dist/hosting/retirement.js +11 -23
  43. package/dist/hosting/script.d.ts +15 -10
  44. package/dist/hosting/script.js +20 -8
  45. package/dist/hosting/testing/fake-cloudflare.d.ts +12 -2
  46. package/dist/hosting/testing/fake-cloudflare.js +67 -9
  47. package/dist/hosting/testing/index.d.ts +1 -1
  48. package/dist/hosting/testing/index.js +1 -1
  49. package/dist/index.d.ts +1 -3
  50. package/dist/index.js +1 -3
  51. package/dist/namespace/configure.js +0 -3
  52. package/dist/namespace/coordinates.d.ts +1 -7
  53. package/dist/namespace/coordinates.js +2 -20
  54. package/dist/namespace/deploy-release.js +45 -8
  55. package/dist/namespace/index.d.ts +1 -1
  56. package/dist/namespace/index.js +1 -1
  57. package/dist/namespace/list.js +2 -1
  58. package/dist/namespace/record.d.ts +21 -0
  59. package/dist/namespace/record.js +53 -0
  60. package/dist/params.d.ts +9 -94
  61. package/dist/worker/build-digest.d.ts +2 -8
  62. package/dist/worker/build-digest.js +1 -13
  63. package/dist/worker/durable.d.ts +2 -10
  64. package/dist/worker/durable.js +4 -23
  65. package/dist/worker/entry.d.ts +8 -3
  66. package/dist/worker/entry.js +6 -16
  67. package/dist/worker/front.d.ts +5 -25
  68. package/dist/worker/front.js +3 -8
  69. package/dist/worker/index.d.ts +1 -2
  70. package/dist/worker/index.js +1 -2
  71. package/dist/worker/settings.d.ts +2 -3
  72. package/package.json +2 -6
  73. package/template/astrale.config.ts +1 -1
  74. package/dist/build/worker/artifact.d.ts +0 -40
  75. package/dist/build/worker/artifact.js +0 -1
  76. package/dist/build/worker/artifact.load.d.ts +0 -3
  77. package/dist/build/worker/artifact.load.js +0 -308
  78. package/dist/build/worker/artifact.manifest.d.ts +0 -48
  79. package/dist/build/worker/artifact.manifest.js +0 -87
  80. package/dist/build/worker/artifact.paths.d.ts +0 -4
  81. package/dist/build/worker/artifact.paths.js +0 -25
  82. package/dist/build/worker/index.d.ts +0 -2
  83. package/dist/build/worker/index.js +0 -1
  84. package/dist/build/worker/runtime.d.ts +0 -10
  85. package/dist/build/worker/runtime.js +0 -35
  86. package/dist/legacy/direct/addressing.d.ts +0 -2
  87. package/dist/legacy/direct/addressing.js +0 -17
  88. package/dist/legacy/direct/deploy.d.ts +0 -17
  89. package/dist/legacy/direct/deploy.js +0 -241
  90. package/dist/legacy/direct/identity/secret.d.ts +0 -3
  91. package/dist/legacy/direct/identity/secret.js +0 -7
  92. package/dist/legacy/direct/identity/source.d.ts +0 -29
  93. package/dist/legacy/direct/identity/source.js +0 -54
  94. package/dist/legacy/direct/index.d.ts +0 -23
  95. package/dist/legacy/direct/index.js +0 -23
  96. package/dist/legacy/direct/materialize.d.ts +0 -4
  97. package/dist/legacy/direct/materialize.js +0 -10
  98. package/dist/legacy/direct/placement.d.ts +0 -4
  99. package/dist/legacy/direct/placement.js +0 -91
  100. package/dist/legacy/direct/prepare.d.ts +0 -7
  101. package/dist/legacy/direct/prepare.js +0 -158
  102. package/dist/legacy/direct/readiness.d.ts +0 -34
  103. package/dist/legacy/direct/readiness.js +0 -95
  104. package/dist/legacy/direct/vars.d.ts +0 -27
  105. package/dist/legacy/direct/vars.js +0 -53
  106. package/dist/legacy/direct/worker-name.d.ts +0 -2
  107. package/dist/legacy/direct/worker-name.js +0 -18
  108. package/dist/legacy/direct/worker.d.ts +0 -37
  109. package/dist/legacy/direct/worker.js +0 -58
  110. package/dist/legacy/direct/wrangler/deploy.d.ts +0 -24
  111. package/dist/legacy/direct/wrangler/deploy.js +0 -44
  112. package/dist/legacy/direct/wrangler/output.d.ts +0 -2
  113. package/dist/legacy/direct/wrangler/output.js +0 -10
  114. package/dist/legacy/direct/wrangler/rollout.d.ts +0 -27
  115. package/dist/legacy/direct/wrangler/rollout.js +0 -75
  116. package/dist/legacy/direct/wrangler/secrets.d.ts +0 -2
  117. package/dist/legacy/direct/wrangler/secrets.js +0 -24
  118. package/dist/legacy/direct/wrangler/status.d.ts +0 -18
  119. package/dist/legacy/direct/wrangler/status.js +0 -75
  120. package/dist/legacy/direct/wrangler/triggers.d.ts +0 -5
  121. package/dist/legacy/direct/wrangler/triggers.js +0 -12
  122. package/dist/legacy/direct/wrangler/versions.d.ts +0 -13
  123. 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,12 +73,15 @@ 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; it never holds a provider version or a secret digest. Readiness then waits, bounded,
79
- until the URL serves the sealed release from the provider version the upload created.
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
80
+ sealed release from the provider version the upload created.
80
81
  - **Reuse.** A redeploy of the same release finds the script under its label. It is reused only
81
- when its artifact and line tags and its record name this build and configuration (or, when an
82
- interrupted deploy left it unactivated, when it binds exactly this configuration). Then only that
82
+ when its artifact and line tags and its record name this build and configuration (only the
83
+ record's commit may differ: the deployment keeps the record it was first activated with), or,
84
+ when an interrupted deploy left it unactivated, when it binds exactly this configuration. Then only that
83
85
  script's declared secrets are rewritten in place, and readiness waits for the version the
84
86
  rewrite minted. Anything else is a `deployment-conflict`, refused with nothing written; no other
85
87
  deployment is ever touched. A `summary:<label>` an older adapter wrote is ignored.
@@ -112,67 +114,42 @@ Node and inside a Worker (`fetch` and WebCrypto only).
112
114
  - **Client.** `CloudflareClient`: bounded bodies, `Retry-After`/429 waits by policy
113
115
  (`DEFAULT_RATE_LIMIT`, `OPERATOR_RATE_LIMIT` or none), timeouts and a deadline, typed
114
116
  `CloudflareApiError`s (`kind`, `status`, `codes`), cursor and numbered pagination, multipart
115
- forms and raw KV text.
116
- - **Routing KV.** `RoutingKv` (by title or id) and the contract: `record:<label>` (canonical
117
- `DeploymentRecordV1`, written once; re-publication only for the same release, `sameRelease`),
118
- `retirement:<label>` (`withdrawn`), `current:<name>` (one of the name's labels), `cron:<name>`
119
- (canonical UTC crons). Every write is read back before it counts.
117
+ forms, raw KV text and bytes.
118
+ - **Routing KV.** `RoutingKv` (by title or id) and the contract: `record:<label>` (the deployment's
119
+ record, opaque bytes with their media type in the value's metadata, `{"contentType":...}`, or
120
+ `-` with `{"contentType":null}` for a deployment that publishes none; written once and kept only
121
+ for the same bytes and media type), `retirement:<label>` (`withdrawn`), `current:<name>` (one of
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.
120
126
  - **Scripts.** `deploymentBindings`, `deploymentTags` (with a host's extra tags),
121
127
  `uploadDeploymentScript` (create-only, every module kind, assets in parallel),
122
128
  `observeScript`, `scriptSettings`, `scriptSecretNames`, `putScriptSecret`, `deleteScript`, and
123
129
  `createSigningIdentity` (WebCrypto Ed25519).
124
- - **Lifecycle.** `activateDeployment`, `promoteDeployment`, `replaceSchedules`, two-phase retirement
130
+ - **Lifecycle.** `activateDeployment` (a label and its record, `WorkerRecord` of
131
+ `@astrale-os/sdk/deployment/worker`, or `null`), `readRecord`, `promoteDeployment`,
132
+ `replaceSchedules`, two-phase retirement
125
133
  (`markRetired`, then `completeRetirement` once `retirementVisible` sees the dispatcher answer
126
134
  `DEPLOYMENT_RETIRED`), `waitForReadiness` through the public URL or a dispatch binding, and
127
135
  `listLineDeployments`.
128
136
  - **Dispatcher.** Plain ES modules shipped with the package (`dispatcherModules()`), routing a label
129
- to its script, a name to `current:<name>`, and firing `cron:<name>` on schedule. A host prepends
130
- its own routing and schedule targets with `createDispatcher(extension)` in its own `index.mjs`,
131
- without forking it. The public never reaches `/__scheduled`; nothing in the generated Worker
132
- serves it yet.
137
+ to its script, a name to `current:<name>`, and firing `cron:<name>` on schedule. It serves a
138
+ label's record at `/.well-known/astrale/deployment.json`, its bytes and media type as published
139
+ (immutable, `405` but for `GET` and `HEAD`), and hands that path to a deployment that publishes
140
+ none. `createDispatcher()` owns routing and schedule admission. The public never reaches
141
+ `/__scheduled`; cron targets serve that internal POST endpoint.
133
142
  - **Provisioning.** `planHosting` reads; `applyHosting` refuses any blocker before its first write;
134
143
  `provisionHosting` does both.
135
144
  - **Testing.** `@astrale-os/adapter-cloudflare/hosting/testing` exports `FakeCloudflare`, one fake
136
145
  account that serves public requests with the real dispatcher.
137
146
 
138
- ## Legacy direct mode
139
-
140
- Deprecated: replaced by namespace mode, and deleted with the legacy deploy path (D10) once no
141
- configuration on any repository's main selects direct mode. Its consumers until they move: domains
142
- organization, whose `prod` binds an EU-jurisdiction R2 bucket that namespace mode does not admit
143
- yet, and the SDK's own e2e fixture (`__e2e__/flagship/fixtures/v1`).
144
-
145
- `astrale-domain deploy <environment>` builds locally and deploys the Worker and frontend
146
- remotely. Omitting `route` uses the observed stable `*.workers.dev` issuer.
147
-
148
- For an existing Worker, a deploy uploads a candidate, reconciles
149
- configured triggers, and stages the candidate at 0% while the previous version retains 100% of normal
150
- HTTP traffic. Exact-version probes verify Publication, Bundle, and serving identity before promotion;
151
- serving the Publication also initializes the Domain Runtime. After promotion, normal requests
152
- must prove that exact version again. A failed candidate remains at 0% and can be replaced on retry.
153
- Bundled View routes must also serve the prepared browser-file generation before readiness passes.
154
- This covers frontend-only changes without changing the Schema or reinstalling the Domain. HTML is
155
- revalidated on every use and transient asset misses are not cached; ordinary successful asset
156
- responses keep their cache policy. Frontend files are answered by the Worker's small startup module
157
- without loading the execution Service, which each such file starts loading in the background for the
158
- calls the View makes next.
159
- The first deployment uses ordinary Worker provisioning because no previous version exists to retain.
160
- An existing traffic-split rollout must be resolved before this single-version workflow can continue.
161
-
162
- A direct Cloudflare target shared across machines must still have one external writer, normally a
163
- CI/CD concurrency group. Live deployment checks detect competing changes but are not an atomic
164
- compare-and-swap or distributed lock. Use separate Worker targets for uncoordinated development.
165
- Routes, custom domains, schedules, and other non-versioned provider settings are reconciled through
166
- Wrangler before candidate probing; they can remain changed after a failed attempt. Candidate readiness
167
- protects the HTTP implementation, not an atomic rollback of external resources or trigger settings.
168
-
169
147
  ## Frontend files
170
148
 
171
149
  The Worker runs before Workers Static Assets on every request and owns frontend response headers
172
150
  and routing, for `cloudflare()` and `astrale()` deployments alike. Frontend preparation therefore
173
151
  refuses a built frontend whose root contains `_headers`, `_redirects`, or `.assetsignore`, in any
174
- letter case, as a file or a directory. Wrangler would apply them as asset configuration in a direct
175
- 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
176
153
  the artifact is prepared, before any provider change. Vite copies its public directory (`public/`
177
154
  by default) to that root: remove them there, or stop the plugin that emits them. The same names
178
155
  below the root, such as `docs/_headers`, remain ordinary files.
@@ -194,26 +171,18 @@ same bytes. Each deployment binds its own values as plain Worker bindings:
194
171
  | --- | --- |
195
172
  | `WORKER_URL` | serving origin of the deployment |
196
173
  | `ASTRALE_SETTINGS` | canonical JSON of `{ invocationTimeoutMs, maximumRequestBytes, router, tokenExchange }`, defaults resolved, with optional `durableOptions` |
197
- | `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 |
198
- | `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 |
199
175
 
200
176
  These names are adapter-owned: preparation refuses an author var, or a `wrangler.vars` entry, that
201
177
  takes one, and deployment refuses a secret that does. A host that deploys the Worker itself encodes
202
178
  the settings with `encodeWorkerSettings` from `@astrale-os/adapter-cloudflare/worker`. A generated
203
179
  Worker refuses execution without `ASTRALE_SETTINGS`.
204
180
 
205
- `ASTRALE_BUILD_DIGEST` selects what the Worker serves. Bound, the Worker is an immutable deployment:
206
- it seals the `DomainRelease` v4 of its code at its `WORKER_URL`, which is its issuer, and serves it
207
- at `/.well-known/astrale/release.json` beside its frozen v3 projection at `domain.json`; it admits
208
- only routing that names that exact release or projection. Unbound, it is a stable Worker and serves
209
- exactly the v3 Publication of today, answering 404 at `release.json`. The Worker refuses execution,
210
- instead of falling back to v3, when the digest is malformed, when it is bound without `WORKER_URL`,
211
- or beside a direct-mode binding or a configured `identityIssuer`.
212
-
213
- The two direct-mode bindings are deprecated. Their replacement is an immutable deployment, whose
214
- host binds `WORKER_URL` to the deployment URL, which is also its issuer (namespace mode). They are
215
- deleted with direct mode, once the Shell, Services, Issues, domains and ui configurations deploy in
216
- 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.
217
186
 
218
187
  The `wrangler` overlay reaches the deployment configuration, never the bundle. Preparation refuses
219
188
  an overlay key the bundler reads (`alias`, `build`, `define`, `jsx_factory`, `jsx_fragment`,
@@ -229,21 +198,13 @@ digest, and `deploy` places that same Bundle. Two installed versions of this pac
229
198
  copies of the bundler: when their code differs, `build` prints one build digest per copy and warns
230
199
  until the adapter versions are aligned.
231
200
 
232
- Services receives the placed modules as they are, so there the digest names the bytes the
233
- deployment runs. Direct mode runs `wrangler deploy`, which bundles the placed modules again: there
234
- the digest names the portable Worker the deployment was made from, not the bytes Cloudflare runs.
235
- A host that receives a Worker upload recomputes the digest from the exact upload with the same
236
- function:
237
-
238
- ```ts
239
- import { workerBuildManifest } from '@astrale-os/adapter-cloudflare/release'
240
- import { buildDigest } from '@astrale-os/sdk/deployment/build/manifest'
241
-
242
- const digest = buildDigest(workerBuildManifest({ origin, revision, bundle, entry, modules, assets }))
243
- ```
244
-
245
- Modules keep their uploaded name and an asset `/<path>` is the file `assets/<path>`. The bundler
246
- version names this layout, not a package release. Both subpaths are runtime-safe.
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.
247
208
 
248
209
  ## Material for a custom Worker entrypoint
249
210
 
@@ -260,8 +221,12 @@ await writeFile('src/build-material.gen.ts', generateBuildMaterialModule(build))
260
221
 
261
222
  The generated TypeScript module exports `loadBuildMaterial()`. Call it from the
262
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`.
263
228
  Such an entrypoint states its execution settings (`invocationTimeoutMs`, `maximumRequestBytes`,
264
- `tokenExchange`, `routeSubrequest`, `identityIssuer`) in its configuration, or reads them from its
229
+ `tokenExchange`, `routeSubrequest`) in its configuration, or reads them from its
265
230
  deployment bindings with `settings: deploymentSettings`; one Worker never takes both.
266
231
  It constructs the same verified, deduplicated material as the adapter's generated Service;
267
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,18 +1,12 @@
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';
6
+ export { encodeWorkerSettings, SETTINGS_BINDING } from '../worker/settings.js';
7
7
  export { GENERATED_WORKER_RUNTIME } from './codegen/wrangler.js';
8
8
  export { frontendFiles } from './frontend/files.js';
9
9
  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';
@@ -1,17 +1,12 @@
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
+ export { encodeWorkerSettings, SETTINGS_BINDING } from '../worker/settings.js';
6
7
  export { GENERATED_WORKER_RUNTIME } from './codegen/wrangler.js';
7
8
  export { frontendFiles } from './frontend/files.js';
8
9
  export { verifyWorkerFrontend } from './frontend/readiness.js';
9
10
  export { qualifyWorkerBundle } from './qualification.js';
10
11
  export { assertSingletonRuntimeRoots } from './roots.js';
11
12
  export { runWranglerBundle, runWranglerTypes } from '../wrangler/index.js';
12
- /**
13
- * @deprecated Legacy direct mode only (`../legacy/direct/index.ts`): preparation of a stable
14
- * Worker, its Wrangler deployment and its name. Replacement: `cloudflare({ namespace })`, which
15
- * deploys one immutable deployment per release. Deleted with direct mode (D10).
16
- */
17
- 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
- }