@sanoma-ai/connector-cloudflare 0.4.0 → 0.6.0

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.
package/README.md CHANGED
@@ -8,49 +8,67 @@
8
8
  npm install @sanoma-ai/connector-cloudflare
9
9
  ```
10
10
 
11
- The three types are writable: `read` and `import` (effect `read`), and the change operations only the apply workflow may call, `plan` (effect `read`), `create`, `update` and `delete` (effect `write`). The container is a host ([Hosts](https://github.com/Sanoma-AI/sanoma/blob/main/packages/workflows/README.md#hosts)) and the alarm a wakeup, with one more operation, `register`, which the worker calls:
11
+ The four types are writable: `read` and `import` (effect `read`), and the change operations only the apply workflow may call, `plan` (effect `read`), `create`, `update` and `delete` (effect `write`). The host is a host ([Hosts](https://github.com/Sanoma-AI/sanoma/blob/main/packages/workflows/README.md#hosts)) and the alarm a wakeup, with one more operation, `register`, which the worker calls. `container` is the host's earlier form, deprecated, the same Worker with every field declared:
12
12
 
13
13
  | Type | Operations | Import id |
14
14
  | ----------- | ----------------------------------------------------------------------------------------- | --------------- |
15
+ | `host` | `cloudflare.host.read`, `.import`, `.plan`, `.create`, `.update`, `.delete` | its name |
15
16
  | `container` | `cloudflare.container.read`, `.import`, `.plan`, `.create`, `.update`, `.delete` | its name |
16
17
  | `alarm` | `cloudflare.alarm.read`, `.import`, `.plan`, `.create`, `.update`, `.delete`, `.register` | its host's URL |
17
18
  | `records` | `cloudflare.records.read`, `.import`, `.plan`, `.create`, `.update`, `.delete` | `<zone>/<name>` |
18
19
 
19
- A data file declares them with the constructors in `@sanoma-ai/connector-cloudflare/resources`. Declaring the alarm is the whole switch for scaling to zero: without it, nothing starts a stopped container at a deadline, and a sleeping run waits for the next request.
20
+ A data file declares them with the constructors in `@sanoma-ai/connector-cloudflare/resources`. The host says only the decisions: its name, its hostname and the database it runs on. Declaring the alarm is the whole switch for scaling to zero: without it, nothing starts a stopped container at a deadline, and a sleeping run waits for the next request.
20
21
 
21
22
  ```ts
22
- // resources/infra.ts
23
+ // resources/sanoma/infra.ts
23
24
  import { cloudflare } from "@sanoma-ai/connector-cloudflare/resources";
24
- import { secret } from "sanoma/resources";
25
+ import { neon } from "@sanoma-ai/connector-neon/resources";
25
26
 
26
- export const host = cloudflare.container({
27
+ export const store = neon.project({ name: "acme", region: "aws-us-east-1" });
28
+ export const host = cloudflare.host({
27
29
  name: "acme", // the Worker sanoma-acme, at https://sanoma-acme.<subdomain>.workers.dev
28
- image: "ghcr.io/sanoma-ai/runtime:0.7.0", // copied into the account's registry at apply
29
- source: repo, // a declared github.repository, or a clone URL
30
- ref: "main",
31
- port: 3000,
32
30
  hostname: "app.acme.example", // optional: the app at its own domain, instead of workers.dev
33
- secrets: {
34
- SANOMA_DATABASE_URL: secret("DATABASE_URL"),
35
- SANOMA_SECRETS_KEY: secret("SECRETS_KEY"),
36
- SANOMA_WAKEUP_TOKEN: secret("SANOMA_WAKEUP_TOKEN", { generate: true }), // made at the first apply
37
- },
31
+ database: store, // the declared database it runs on: apply gives the host its URL
38
32
  });
39
33
  export const wakeup = cloudflare.alarm({ host });
40
34
  ```
41
35
 
42
- | Field | What it is |
43
- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
44
- | `name` | The company's name, its identity: lowercase letters, digits and dashes, up to 56; the Worker and its application are `sanoma-<name>` |
45
- | `image` | The Sanoma runtime image, pinned to a tag or digest, never `latest`: any registry's, which apply copies into the account's and pins by digest ([The image](#the-image)) |
46
- | `source` | The company repository: a clone URL, or a declared `github.repository` |
47
- | `ref` | The branch it runs, never a commit: apply resolves it and sets the hidden `revision`, so a merge is drift on the container |
48
- | `configurationRevision` | Optional non-secret configuration version: 1–63 ASCII letters, digits, `.`, `_` or `-`; advance it to restart with rotated bootstrap or environment values at the same source revision and image |
49
- | `port` | The port the runtime listens on in the container (its `PORT`) |
50
- | `secrets` | Environment variables, each a `secret(...)`: the Worker's secret bindings, write-only, never in an output, the ledger or a checkpoint |
51
- | `domains` | Optional: a public site's domain, by site name, passed as `SANOMA_DOMAINS` and attached to the Worker by apply as a custom domain ([Custom domains](#custom-domains)) |
52
- | `hostname` | Optional: the hostname the app is served at (`internal.acme.example`), attached like a site's domain; the host's `url` is then `https://<hostname>` |
53
- | `id`, `url`, `worker_url`, `healthy` | Cloudflare's: the application's id, where the app answers (`https://<hostname>` once attached, else the Worker's workers.dev URL), the workers.dev URL, and whether it answers |
36
+ Apply gives the host the rest before it plans (`deriveHosts` in `@sanoma-ai/workflows`), and a data file never writes it:
37
+
38
+ | Derived | From |
39
+ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
40
+ | `name` | The name it is bound by, else the repository's name, made lower-case letters, digits and dashes; declare `name` to override it |
41
+ | `image` | `ghcr.io/sanoma-ai/runtime:<version>` of the `sanoma` the repository installs; declare `image` to override it |
42
+ | `source` | The repository the app runs from: the `github.repository` the data files declare that is its `origin` (else the one its own data file declares), else `origin`'s URL; declare `source` to override it |
43
+ | `ref` | The repository's default branch, which apply resolves with the revision (`sanoma.source.resolve`); declare `ref` to override it |
44
+ | `port` | The image's own, 3000 |
45
+ | `domains` | Each public site's domain, `defineSite({ public: true, domain })`: attached in production, passed as `SANOMA_DOMAINS` ([Custom domains](#custom-domains)) |
46
+ | `secrets` | Markers only, each a `secret("NAME")` its driver resolves at the call: `SANOMA_DATABASE_URL` (the declared database's stored URL), `SANOMA_SIGNING_KEY` (the config's binding when it has one, else the host's own, `SANOMA_HOST_SIGNING_KEY`, generated into the store at its create), with the built-in store `SANOMA_SECRETS_KEY` (always bound: it unlocks the store), `SANOMA_SOURCE_KEY` (where the config has the GitHub connector), the wakeup's token, the credentials of the vendors its data file declares and the login's sender's, and the GitHub App's stored keys. A credential a driver reads from this machine's environment that nothing binds or stores stops the plan: bind it `{ env }` in `sanoma.config.ts` |
47
+ | the deploy key | For a GitHub repository, `<file>#host.sourceKey`, a read-only `github.repository_deploy_key` whose key is the public half of `SANOMA_SOURCE_KEY`, which apply mints at the host's first apply and keeps in the store. Found by the repository and the key, so a key GitHub holds already (a data file's `deployKey` of before) is bound, its binding taken over, never made again. Without the GitHub connector the host clones over HTTPS, a public repository only; a private one stops the plan |
48
+
49
+ A `cloudflare.container` in the lockfile binds a `cloudflare.host` declaration of the same export: renaming the constructor recreates nothing, and the next change apply writes records the new type.
50
+
51
+ ### Moving from `cloudflare.container`
52
+
53
+ As sanoma-ops moves, in one commit, then one apply from Resources:
54
+
55
+ 1. **The host.** Replace `cloudflare.container({ … })` with `cloudflare.host({ name, hostname, database: store })` (`name` may go too: it defaults to the repository's), and drop `image`, `source`, `ref`, `port`, `secrets` and `domains`.
56
+ 2. **The domains.** Put each one of `domains` on its site, `defineSite({ public: true, domain: "www.sanoma.work" })` in `sites/www/site.ts`. While the Worker has domains attached and no site declares one, the plan says so and changes nothing on the host, so a partial move never detaches them.
57
+ 3. **The deploy key.** Remove the `deployKey` export (and from the default list). Keep `SANOMA_SOURCE_KEY` where it is (sanoma-ops binds it `{ env }`): the derived `#host.sourceKey` has the same public half, so apply finds the key GitHub holds by its text and takes over the `#deployKey` binding (a `bind` step `from` it, one lockfile commit), and makes no second key.
58
+ 4. **The credentials.** Each credential the host is given must reach it from a binding or the store: one only in `.env` stops the plan, naming it, until it is bound `{ env }` in `sanoma.config.ts` (sanoma-ops binds `GITHUB_TOKEN`, `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID`, `RESEND_API_KEY`, `SANOMA_SOURCE_KEY` and the GitHub App's keys already). `SANOMA_SIGNING_KEY` keeps its `{ bootstrap }` binding, so the host keeps its key and sessions; with none, the host gets its own, generated at its create.
59
+
60
+ The plan then binds `#host.sourceKey` from `#deployKey` and updates the host in place: the same Worker, its type recorded as `cloudflare.host`.
61
+
62
+ | Field | What it is |
63
+ | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
64
+ | `name` | The company's name, its identity: lowercase letters, digits and dashes, up to 56; the Worker and its application are `sanoma-<name>` |
65
+ | `hostname` | Optional: the hostname the app is served at (`internal.acme.example`), attached like a site's domain; the host's `url` is then `https://<hostname>` |
66
+ | `database` | The declared database it runs on (a `neon.project` or `supabase.project`): apply gives the host its URL |
67
+ | `image`, `source`, `ref` | Optional overrides of what apply derives (above). `image` is pinned to a tag or digest, never `latest`: any registry's, which apply copies into the account's and pins by digest ([The image](#the-image)) |
68
+ | `configurationRevision` | Optional non-secret configuration version: 1–63 ASCII letters, digits, `.`, `_` or `-`; advance it to restart with rotated bootstrap or environment values at the same source revision and image |
69
+ | `id`, `url`, `worker_url`, `healthy` | Cloudflare's: the application's id, where the app answers (`https://<hostname>` once attached, else the Worker's workers.dev URL), the workers.dev URL, and whether it answers |
70
+
71
+ `cloudflare.container`, deprecated, declares every field itself: `image`, `source`, `ref`, `port`, `secrets` (each a `secret(...)`) and `domains` (by site name). Workflows that make a host for each of several companies (Sanoma Cloud's) call its operations with those fields filled.
54
72
 
55
73
  The host's outputs are `{ url, wakeUrl, revision, healthy }` (`wakeUrl` its `worker_url`). Its capabilities: stops when quiet, a public URL, it runs an image rather than building one, no fixed egress. `healthy` is the application existing and the Worker answering `/__sanoma/health`, which does not start a stopped container: a stopped host is healthy, and a read or a drift check leaves it stopped.
56
74
 
@@ -64,11 +82,12 @@ The logo is Cloudflare's cloud from [Simple Icons](https://simpleicons.org/?q=cl
64
82
 
65
83
  The package ships the Worker it uploads, `worker/`: `index.js`, which exports the Durable Object class `SanomaInstance` over `DurableObject` from `cloudflare:workers`, and `instance.js`, the object's code with no imports (typed by `instance.d.ts`), which the fake runs in Node so both kits check the code Cloudflare runs. Every request to the Worker goes to its one object, which:
66
84
 
67
- - starts the container on a request at the Worker's source revision, configuration revision and uploaded image (`ctx.container.images.runtime`: the copy, pinned by digest), as a `standard-1` instance (½ vCPU, 4 GiB: the policy refuses `basic`), with the Worker's text bindings (plain and secret) as its environment. Container labels hold the two non-secret revisions and an image hash; a running container with different labels is destroyed and started again. Secret values never enter these labels;
85
+ - starts the container on a request at the Worker's source revision, configuration revision and uploaded image (`ctx.container.images.runtime`: the copy, pinned by digest), as a `standard-1` instance (½ vCPU, 4 GiB: the policy refuses `basic`), with the Worker's text bindings (plain and secret) as its environment. Container labels hold the two non-secret revisions, an image hash and a hash of `SANOMA_DOMAINS` (so a change of the sites' domains alone starts it again, its environment and its sites bundle with them); a running container with different labels is destroyed and started again. Secret values never enter these labels;
68
86
  - waits for the container's `/healthz` (up to ten minutes, the clone and install included), sets its inactivity timeout to ten minutes, and proxies the request to its `port` with the Host it was asked for and `x-forwarded-proto` (the port's fetch reaches the container whatever the URL's host, which becomes the request's Host header: so the company serves the public site declared on a domain);
69
87
  - answers `GET /__sanoma/health` with the source revision, configuration revision when set, and running/readiness state without starting the container. With `?start=1`, it waits until the container answers; a create or update checks both revisions before completing;
70
88
  - holds the company's one wake at `/__sanoma/wake`, with `Authorization: Bearer <SANOMA_WAKEUP_TOKEN>`: `GET` shows `{ at, rang? }`, `PUT { at }` replaces it and sets the object's alarm, `DELETE` clears it. Without the token among the host's secrets it answers 404, with another one 401;
71
- - at the alarm, starts the container and calls its `/wake`, then notes when (`rang`); a failure throws, so Cloudflare rings the alarm again.
89
+ - at the alarm, starts the container and calls its `/wake`, then notes when (`rang`); a failure throws, so Cloudflare rings the alarm again;
90
+ - over RPC, `startNow()` (the sites router's call, [Public sites on the Worker](#public-sites-on-the-worker)): sets the alarm now, unless one is due sooner, and the alarm starts the container without the caller waiting and sends it `/wake` (its worker may have stopped itself while the container stayed up); a wake held for later is set again once the start is made, and one that rang never rings twice.
72
91
 
73
92
  Cloudflare stops the container after ten quiet minutes; the object, its storage and its alarm stay, and the next request or alarm starts the container again. The application is of the `durable_object` scheduling policy (public beta, the one Cloudflare documents for new applications; it refused `default` on the account the connector was first run on): the object starts its container, with the image and the instance type it names, so the application is only a name and the object's namespace, and has no image, versions or rollouts. A create, in Wrangler's order, copies and pins the image and waits until Cloudflare prepared it ([The image](#the-image)), uploads the Worker with the object's migration (`new_sqlite_classes`) and its class as a container's with that image (`containers: [{ name, class_name, images: { runtime } }]`, as Wrangler sends it), turns its workers.dev URL on, makes the application for the object's namespace (`{ name, scheduling_policy: "durable_object", durable_objects: { namespace_id } }`, the `namespace_id` the Worker's settings then show), and uploads the Worker once more: the object learns which application backs its namespace only from a deployment made after the application exists, and without that upload every start answers "There is no container application assigned to this Durable Object namespace", for good (found on the first real deploy, 2026-10-29); an update does the same, except that it leaves the application as it is: a new image is a new upload naming it, and the object destroys a container started at another and starts it again at the next request. Either waits for the Worker to answer at the requested source and configuration revisions. The policy is fixed once an application is made, and the object cannot start a container under another: one of the `default` policy under that name (none can be made any more) is deleted by hand before the apply. A plan compares secret binding names, never values: the Worker's settings never show them. Advance `configurationRevision` to upload rotated values and restart a running container with them; a value change alone does not trigger an update. A delete detaches the custom domains the Worker was declared to serve (its `hostname` too), deletes the application, then the Worker with its object's storage. A Worker of that name that is not Sanoma's (no `SANOMA_INSTANCE` binding) reads as gone, and a create or a delete refuses to touch it.
74
93
 
@@ -82,10 +101,10 @@ The Worker's `SANOMA_IMAGE` binding holds the declared image and its upload the
82
101
 
83
102
  1. **The connector.** Add `cloudflare` to the connectors in `sanoma.config.ts`: the worker on the host calls `cloudflare.alarm.register` through it.
84
103
  2. **Credentials.** On the connector's page, `CLOUDFLARE_API_TOKEN` and `CLOUDFLARE_ACCOUNT_ID` (below).
85
- 3. **Go live.** `infra.ts` declares the host with `SANOMA_WAKEUP_TOKEN: secret("SANOMA_WAKEUP_TOKEN", { generate: true })` among its secrets (apply makes it), and, after it, `export const wakeup = cloudflare.alarm({ host })`. One apply makes the Worker, waits for it, then the alarm, which checks the Worker holds the token.
104
+ 3. **Go live.** `infra.ts` declares the host and, after it, `export const wakeup = cloudflare.alarm({ host })`: apply mints `SANOMA_WAKEUP_TOKEN` when none is stored and gives it to the host. One apply makes the Worker, waits for it, then the alarm, which checks the Worker holds the token.
86
105
  4. **A sleeping run.** Start a workflow that sleeps past ten quiet minutes. Before the container stops the worker registers the sleep's end; at `at` the object's alarm starts the container and calls `/wake`, and the run continues.
87
106
 
88
- To stop scaling to zero, delete the `wakeup` line and `SANOMA_WAKEUP_TOKEN` from the host's secrets, and apply: the alarm is deleted, and the next request still starts the container. With the token left among the host's secrets, the runtime registers with the Worker's wake route itself (`envWakeup`), as a Sanoma Cloud company's does.
107
+ To stop scaling to zero, delete the `wakeup` line (from a `cloudflare.container`, its `SANOMA_WAKEUP_TOKEN` too), and apply: the alarm is deleted, and the next request still starts the container. With the token left among the host's secrets, the runtime registers with the Worker's wake route itself (`envWakeup`), as a Sanoma Cloud company's does.
89
108
 
90
109
  ## Custom domains
91
110
 
@@ -93,6 +112,12 @@ Each hostname in `domains` is attached to the Worker as a [Workers custom domain
93
112
 
94
113
  `hostname`, the app's own domain, is attached, detached and read the same way, in the same zone lookup, and the Worker's `SANOMA_HOSTNAME` binding records it (as `SANOMA_DOMAINS` records the sites'), so one no longer declared is detached and one detached by hand is drift. With it attached, the host's `url` is `https://<hostname>` (a read of one not attached gives the workers.dev URL, so a failed attach or one detached by hand never points at a name that does not route). The wake path stays on workers.dev, which stays on: the Worker's `SANOMA_HOST_URL` binding, the host's `wakeUrl` (its `worker_url`, which apply gives the alarm as its `host`) and the health checks, so a wake never waits on the hostname's DNS or certificate, and declaring or dropping a hostname leaves the alarm as it is. A `hostname` that is also one of `domains`' values is refused, `invalid`, at a plan and a create, before any request: the app answers at every hostname that is not a site's. A renamed `hostname` is detached before the upload and the new one attached after it, as a site's domain is: in between, a short window, no hostname is attached and the app answers on workers.dev alone (the host's `url` reads as it), and a failure there leaves the bindings naming the old one, detached, or the new one, not yet attached, which the next apply attaches.
95
114
 
115
+ ## Public sites on the Worker
116
+
117
+ A public site without a login, on a declared domain, is served by the Worker itself, so a visitor never waits for a cold container (forty seconds to minutes). With `domains` and `SANOMA_WAKEUP_TOKEN` among the host's secrets, a create or an update, once the Worker answers at the new revision, asks the container for its sites bundle (`GET /__sanoma/sites-bundle` with `Authorization: Bearer <SANOMA_WAKEUP_TOKEN>`): the runtime builds it on that first ask (`buildWorker` in `@sanoma-ai/app/build`: the sites' pages, `callFn` and `getActor`, the company's workflows and the runtime bundled for `workerd`, the pages no call is made from prerendered), answering 202 until it is built (each answer marked `x-sanoma-sites-bundle`), asked again every poll for up to the start's ten minutes, and through the object's 503 while the container starts again; a 401 or a 404 without the mark is a runtime from before the route (its staff login's sign-in page, or its app's not-found), and any other answer without it (an error of Cloudflare's, a 5xx), or a body cut off, is asked again too, until the start's time is up, which fails the apply, retryably. Then the bundle's assets (the client build and the prerendered pages) go in through an [assets upload session](https://developers.cloudflare.com/workers/static-assets/direct-upload/) (`POST …/scripts/{name}/assets-upload-session { manifest }`, then each bucket it asks for to `POST …/workers/assets/upload?base64=true` with the session's jwt), and the Worker is uploaded again as it was, plus `worker/sites.js` as its entry, the bundle's modules under `sites/`, the `ASSETS` binding and `assets: { jwt, config }`: `run_worker_first: ["/*", "!/assets/*"]` (the client build straight from the assets), `html_handling` and `not_found_handling` `none`. Its revision, image and bindings are the deploy's, so the container keeps running.
118
+
119
+ The router hands a request the bundle serves (a site's page on its domain, but `/__sanoma`, `/api`, `/auth`, `/webhooks`, `/setup`, `/wake` and `/healthz`) to it: a file of the client build at its path, the page prerendered for it when the request has no query, else the page rendered on the Worker over the company's database (`SANOMA_DATABASE_URL`, the Worker's binding), a run it starts queued for the container's worker, which `startNow` starts. Everything else goes to the object, as before. A container with no such site answers 404, and the Worker is uploaded again as the deploy uploaded it; one whose build failed answers 500 with why, which fails the apply (the Worker then serves the sites from the container until the next apply). The deploy's upload carries a `SANOMA_SITES_PENDING` binding, which either upload after it leaves out: a Worker whose sites upload failed keeps it, a read sees it, and the next plan is an update, which, when the Worker otherwise runs what is declared, makes the sites' upload alone (no upload of the Worker as deployed, no domain attached, no wait for its revision). A site with a login stays on the container: Better Auth needs its database adapter on the Worker first.
120
+
96
121
  ## DNS records
97
122
 
98
123
  `cloudflare.records({ zone, name, records })` is a set of DNS records in one of the account's zones, compared as a set, in no order. A sending domain's records are the case it is made for: `records` names a declared `resend.domain`, whose `dns_records` it then takes ([a field taken from another resource's state](https://github.com/Sanoma-AI/sanoma/blob/main/packages/workflows/README.md#resources)), or is a list written out.
@@ -127,7 +152,7 @@ export const mailDns = cloudflare.records({ zone: "sanoma.work", name: "resend",
127
152
 
128
153
  `./driver` (`cloudflareDriver`; `cloudflareDriver({ timeoutMs, startTimeoutMs, pollMs })`: 30 s a request, ten minutes for the Worker to answer at a new revision, two seconds between tries; each request of the image's copy may take ten minutes) and `./fake` (`fakeCloudflare`) are internal entries, for tests.
129
154
 
130
- A read is the Worker's settings (its bindings: what it runs is its plain-text bindings, never a secret's value), the application of its name (whether it exists, its id) and the account's subdomain (its URL); a plan compares the declared image, source, ref, revision, port, domains and hostname (each left out too) and secret names with them. The requests are made one after another, in the order the fixtures hold.
155
+ A read is the Worker's settings (its bindings: what it runs is its plain-text bindings, never a secret's value), the application of its name (whether it exists, its id) and the account's subdomain (its URL); a plan compares the declared image, source, ref, revision, port, domains and hostname (each left out too) and secret names with them, and plans an update for a Worker still marked `SANOMA_SITES_PENDING`. The requests are made one after another, in the order the fixtures hold.
131
156
 
132
157
  ### Environment
133
158
 
@@ -150,7 +175,7 @@ Containers need the Workers Paid plan. An instance is billed while it runs, by i
150
175
 
151
176
  ### Testing the driver
152
177
 
153
- `test/driver.test.ts`, `test/records.test.ts` and the features replay Cloudflare's replies (`test/fixtures`) with [`@sanoma-ai/testing/replay`](https://github.com/Sanoma-AI/sanoma/blob/main/packages/testing/README.md#replaying-a-vendors-api): `pnpm vitest run connectors/cloudflare` needs no account. The fixtures are **written from Cloudflare's API reference, not recorded**: no account was at hand when the connector was written. A recording replaces them (`SANOMA_LIVE=1 SANOMA_RECORD=1` on the features test), losing the account's id and subdomain, ids and tokens; until then, a reply the reference gets wrong is wrong here too. The upload's `containers` field (its `name`, `class_name` and `images`) is Wrangler's and not in the reference or the SDK's types, and the object's `ctx.container.images` and `start({ image, instance })` are the policy's: the live run is what checks them. The DNS records' fixtures (`records-*`) are written from the [DNS records reference](https://developers.cloudflare.com/api/resources/dns/subresources/records/) the same way, on `sanoma.work` with a set named `check` (`check-*.sanoma.work`), to be recorded on their first live run: Cloudflare's TXT content is quoted in them, which a read takes off. That Cloudflare shows a TXT record written unquoted with quotes is the reference's examples', not checked; a read compares it either way. The image's copy is tested against two small registries in this process (`test/registry.test.ts`), and the driver's call for its credentials over replies written from the SDK's types and the OCI distribution spec; that Cloudflare's registry takes the copy as written is what the live run checks. The driver tests only replay.
178
+ `test/driver.test.ts`, `test/records.test.ts` and the features replay Cloudflare's replies (`test/fixtures`) with [`@sanoma-ai/testing/replay`](https://github.com/Sanoma-AI/sanoma/blob/main/packages/testing/README.md#replaying-a-vendors-api): `pnpm vitest run connectors/cloudflare` needs no account. The fixtures are **written from Cloudflare's API reference, not recorded**: no account was at hand when the connector was written. A recording replaces them (`SANOMA_LIVE=1 SANOMA_RECORD=1` on the features test), losing the account's id and subdomain, ids and tokens; until then, a reply the reference gets wrong is wrong here too. The upload's `containers` field (its `name`, `class_name` and `images`) is Wrangler's and not in the reference or the SDK's types, and the object's `ctx.container.images` and `start({ image, instance })` are the policy's: the live run is what checks them. The DNS records' fixtures (`records-*`) are written from the [DNS records reference](https://developers.cloudflare.com/api/resources/dns/subresources/records/) the same way, on `sanoma.work` with a set named `check` (`check-*.sanoma.work`), to be recorded on their first live run: Cloudflare's TXT content is quoted in them, which a read takes off. That Cloudflare shows a TXT record written unquoted with quotes is the reference's examples', not checked; a read compares it either way. The `sites` fixture's assets session and bucket upload are written from the [direct upload guide](https://developers.cloudflare.com/workers/static-assets/direct-upload/) and the SDK's types the same way, with the guide's example hash (sha256 over an asset's base64 and its extension, 32 hex digits) and each part's type from its extension; that Cloudflare takes them, and serves `/assets/*` before the Worker, is the live run's to check. The image's copy is tested against two small registries in this process (`test/registry.test.ts`), and the driver's call for its credentials over replies written from the SDK's types and the OCI distribution spec; that Cloudflare's registry takes the copy as written is what the live run checks. The driver tests only replay.
154
179
 
155
180
  ### Running it live
156
181
 
@@ -186,7 +211,7 @@ The connector carries steps for [scenarios](https://github.com/Sanoma-AI/sanoma/
186
211
 
187
212
  ## Testing
188
213
 
189
- `@sanoma-ai/connector-cloudflare/fake` is an in-memory Cloudflare account, the fake the connector carries: each sandbox run makes one of its own. Its registry is in memory too: a copy records the image under its reference there and pulls nothing. Its custom domains are in `state.domains` (hostname, Worker and zone), each zone's DNS records in `state.dns`, by the zone's name (a test adds one by hand there; a batch is all or nothing, and refuses a record identical to one the zone holds (its type, content and priority) or one beside a CNAME, as Cloudflare does), and it holds every zone asked for unless `fakeCloudflare({ zones })` names the ones it holds. A copy pins the image by a made-up digest, and a preparation answers ready for an image pinned in its registry. Its Workers answer on ports of this machine, one per Worker name, and run the package's own object code over a `ctx` in this process: storage in memory, an alarm on a timer (again after a throw), `ctx.container.images` from the upload's `containers[].images`, and a container that runs what `start` returns for the image (the one its pinned copy was copied from) and revision it was started at (by default a stub that answers `/healthz` and `/wake`); `start` refuses an image the upload did not name or an instance type that is not Cloudflare's. An upload restarts the object over its new bindings and images: a new image starts the container again at the next request, as the object finds its label changed.
214
+ `@sanoma-ai/connector-cloudflare/fake` is an in-memory Cloudflare account, the fake the connector carries: each sandbox run makes one of its own. Its registry is in memory too: a copy records the image under its reference there and pulls nothing. Its custom domains are in `state.domains` (hostname, Worker and zone), each zone's DNS records in `state.dns`, by the zone's name (a test adds one by hand there; a batch is all or nothing, and refuses a record identical to one the zone holds (its type, content and priority) or one beside a CNAME, as Cloudflare does), and it holds every zone asked for unless `fakeCloudflare({ zones })` names the ones it holds. A copy pins the image by a made-up digest, and a preparation answers ready for an image pinned in its registry. Its Workers answer on ports of this machine, one per Worker name, and run the package's own object code over a `ctx` in this process: storage in memory, an alarm on a timer (again after a throw), `ctx.container.images` from the upload's `containers[].images`, and a container that runs what `start` returns for the image (the one its pinned copy was copied from) and revision it was started at (by default a stub that answers `/healthz` and `/wake`); `start` refuses an image the upload did not name or an instance type that is not Cloudflare's. An upload restarts the object over its new bindings and images: a new image starts the container again at the next request, as the object finds its label changed. Its assets upload session asks, in one bucket, for the hashes `state.assets` lacks, and an upload that redeems the completion jwt keeps each path's hash on the Worker (`state.scripts[name].assets`, beside its `main_module` and `modules`); its Workers still send every request to the object, as it runs no sites bundle.
190
215
 
191
216
  ```ts
192
217
  import { fakeCloudflare } from "@sanoma-ai/connector-cloudflare/fake";
@@ -8,6 +8,9 @@ import { z } from "zod";
8
8
  * container running. A deploy uploads the Worker again; a new image is copied into the account's
9
9
  * registry (one there by a tag is pinned to its digest) and named by the upload, and the object
10
10
  * starts the container again at it.
11
+ *
12
+ * Deprecated: declare `cloudflare.host`, the same Worker, which takes the decisions alone. A
13
+ * declaration renamed to it keeps its lockfile binding, and nothing is made again.
11
14
  */
12
15
  export declare const container: import("@sanoma-ai/workflows").Resource<z.ZodObject<{
13
16
  id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
@@ -30,6 +33,41 @@ export declare const container: import("@sanoma-ai/workflows").Resource<z.ZodObj
30
33
  readonly by: readonly ["http_clone_url"];
31
34
  };
32
35
  }, true>;
36
+ /**
37
+ * `cloudflare.host({ name, hostname, database })`: the company's Worker, as `cloudflare.container`
38
+ * runs it, declared by its decisions alone. `name` is the Worker's (`sanoma-<name>`; by default the
39
+ * repository's name, which apply fills, its identity `UNNAMED` until then), `hostname`
40
+ * the app's, `database` the declared database it runs on. Apply gives it the rest (`deriveHosts`):
41
+ * the installed sanoma's runtime image, the repository the app runs from at its default branch
42
+ * (`image`, `source` and `ref` are overrides), the image's port, every public site's domain, and
43
+ * as secrets its database's URL, its keys, the source key it clones with (whose public half apply
44
+ * declares on the repository) and the credentials it deploys itself with: names only, never a value.
45
+ */
46
+ export declare const host: import("@sanoma-ai/workflows").Resource<z.ZodObject<{
47
+ id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
48
+ revision: z.ZodOptional<z.ZodNullable<z.ZodString>>;
49
+ configurationRevision: z.ZodOptional<z.ZodNullable<z.ZodString>>;
50
+ hostname: z.ZodOptional<z.ZodNullable<z.ZodString>>;
51
+ url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
52
+ worker_url: z.ZodOptional<z.ZodNullable<z.ZodString>>;
53
+ healthy: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
54
+ name: z.ZodOptional<z.ZodNullable<z.ZodString>>;
55
+ image: z.ZodOptional<z.ZodNullable<z.ZodString>>;
56
+ source: z.ZodOptional<z.ZodNullable<z.ZodString>>;
57
+ ref: z.ZodOptional<z.ZodNullable<z.ZodString>>;
58
+ port: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
59
+ database: z.ZodOptional<z.ZodNullable<z.ZodString>>;
60
+ secrets: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodString>>>;
61
+ domains: z.ZodOptional<z.ZodNullable<z.ZodRecord<z.ZodString, z.ZodString>>>;
62
+ }, z.core.$strip>, "cloudflare.host", {
63
+ readonly source: {
64
+ readonly type: "github.repository";
65
+ readonly by: readonly ["http_clone_url"];
66
+ };
67
+ readonly database: {
68
+ readonly type: readonly ["neon.project", "supabase.project"];
69
+ };
70
+ }, true>;
33
71
  /**
34
72
  * `cloudflare.alarm({ host })`: the Durable Object alarm of the host's own Worker, which starts
35
73
  * the container at the time the worker registered and calls its `/wake`. The alarm
@@ -47,7 +85,7 @@ export declare const alarm: import("@sanoma-ai/workflows").Resource<z.ZodObject<
47
85
  token: z.ZodOptional<z.ZodNullable<z.ZodString>>;
48
86
  }, z.core.$strip>, "cloudflare.alarm", {
49
87
  readonly host: {
50
- readonly type: readonly ["cloudflare.container"];
88
+ readonly type: readonly ["cloudflare.host", "cloudflare.container"];
51
89
  readonly by: readonly ["worker_url", "url"];
52
90
  };
53
91
  }, true>;
@@ -125,6 +163,7 @@ declare const registerSpec: {
125
163
  };
126
164
  /** The connector's specs: each type's operations, and `register` in the alarm's group. */
127
165
  type CloudflareSpecs = {
166
+ host: (typeof host)["ops"];
128
167
  container: (typeof container)["ops"];
129
168
  records: (typeof records)["ops"];
130
169
  alarm: (typeof alarm)["ops"] & typeof registerSpec;
@@ -134,6 +173,6 @@ type CloudflareSpecs = {
134
173
  * `delete`) and `alarm.register` in the alarm's group (Supabase's pattern).
135
174
  */
136
175
  export declare const cloudflareOps: Connector<"cloudflare", CloudflareSpecs>;
137
- /** The connector's type: the three types, with `alarm.register`. */
176
+ /** The connector's type: the four types, with `alarm.register`. */
138
177
  export type CloudflareOps = typeof cloudflareOps;
139
178
  export {};
package/dist/connector.js CHANGED
@@ -1,10 +1,11 @@
1
- import { compareDeclared, defineConnector, defineResource, dnsRecord, vendorInfo, } from "@sanoma-ai/workflows";
1
+ import { compareDeclared, RUNTIME_PORT, UNNAMED, defineConnector, defineResource, dnsRecord, vendorInfo, } from "@sanoma-ai/workflows";
2
2
  import { DOMAIN_NAME, SITE_KEY } from "@sanoma-ai/workflows/shared";
3
3
  import { z } from "zod";
4
4
  /*
5
5
  * Cloudflare, over its official SDK (`cloudflare`): one Worker per company that runs the company
6
- * in a Container behind its one Durable Object (`cloudflare.container`, a host), and the same
7
- * object's alarm, which wakes it (`cloudflare.alarm`, a wakeup). Cloudflare's OpenTofu provider
6
+ * in a Container behind its one Durable Object (`cloudflare.host`, a host; `cloudflare.container`
7
+ * is its earlier, deprecated form), and the same object's alarm, which wakes it (`cloudflare.alarm`,
8
+ * a wakeup). Cloudflare's OpenTofu provider
8
9
  * has no container resource, so the schemas are a cut of the API's own fields, by hand.
9
10
  */
10
11
  /** A company's name as its Worker's is made of it (`sanoma-<name>`): a DNS label of 63 characters at most. */
@@ -57,6 +58,50 @@ const containerSchema = z.object({
57
58
  .nullish()
58
59
  .describe("Whether the application exists and the Worker answers /__sanoma/health, which starts the container"),
59
60
  });
61
+ /**
62
+ * `cloudflare.host`'s fields: the decisions a data file makes (`name`, `hostname`, `database`),
63
+ * and what apply fills when it is left out (`image`, `source`, `ref`, `port`) or always
64
+ * (`revision`, `secrets`, `domains`), which a data file never declares (`deriveHosts`).
65
+ */
66
+ const hostSchema = containerSchema.extend({
67
+ name: containerSchema.shape.name
68
+ .nullish()
69
+ .describe("The company's name: the Worker and its application are `sanoma-<name>`; by default the repository's name"),
70
+ image: containerSchema.shape.image
71
+ .nullish()
72
+ .describe("The runtime image, an override: by default `ghcr.io/sanoma-ai/runtime:<version>` of the installed sanoma"),
73
+ source: z
74
+ .string()
75
+ .min(1)
76
+ .nullish()
77
+ .describe("The company repository, an override: by default the one the app runs from (its `origin`, or the GitHub repository its data file declares)"),
78
+ ref: z
79
+ .string()
80
+ .min(1)
81
+ .nullish()
82
+ .describe("The branch it runs, an override: by default the repository's default branch"),
83
+ port: z
84
+ .number()
85
+ .int()
86
+ .min(1)
87
+ .max(65_535)
88
+ .nullish()
89
+ .describe(`The port the runtime listens on, the image's own (${RUNTIME_PORT}): never declared`),
90
+ database: z
91
+ .string()
92
+ .min(1)
93
+ .nullish()
94
+ .describe("The declared database it runs on, such as `store`: apply gives the host its URL"),
95
+ secrets: containerSchema.shape.secrets.describe("What apply gives the host, never declared: its database's URL, its keys and the credentials it deploys with, each a `secret(...)` naming a stored value"),
96
+ domains: containerSchema.shape.domains.describe("Each public site's domain, by site name, never declared: from `defineSite({ public: true, domain })`"),
97
+ });
98
+ /** The host contract both types meet, of which `cloudflare.host` derives what it is not told. */
99
+ const capabilities = {
100
+ lifetime: "stops-when-quiet",
101
+ publicUrl: true,
102
+ buildsFromGit: false,
103
+ staticEgress: false,
104
+ };
60
105
  /**
61
106
  * `cloudflare.container`: the company's Worker, `sanoma-<name>`, on the account's workers.dev
62
107
  * subdomain, whose one Durable Object (`SanomaInstance`) starts the runtime image in a Container
@@ -65,11 +110,14 @@ const containerSchema = z.object({
65
110
  * container running. A deploy uploads the Worker again; a new image is copied into the account's
66
111
  * registry (one there by a tag is pinned to its digest) and named by the upload, and the object
67
112
  * starts the container again at it.
113
+ *
114
+ * Deprecated: declare `cloudflare.host`, the same Worker, which takes the decisions alone. A
115
+ * declaration renamed to it keeps its lockfile binding, and nothing is made again.
68
116
  */
69
117
  export const container = defineResource({
70
118
  vendor: "cloudflare",
71
119
  type: "container",
72
- title: "Container",
120
+ title: "Container (deprecated: cloudflare.host)",
73
121
  identity: "name",
74
122
  schema: containerSchema,
75
123
  fields: {
@@ -82,7 +130,7 @@ export const container = defineResource({
82
130
  bindingId: (state) => String(state.name),
83
131
  writable: true,
84
132
  host: {
85
- capabilities: { lifetime: "stops-when-quiet", publicUrl: true, buildsFromGit: false, staticEgress: false },
133
+ capabilities,
86
134
  inputs: {
87
135
  source: "source",
88
136
  ref: "ref",
@@ -101,6 +149,53 @@ export const container = defineResource({
101
149
  }),
102
150
  },
103
151
  });
152
+ /**
153
+ * `cloudflare.host({ name, hostname, database })`: the company's Worker, as `cloudflare.container`
154
+ * runs it, declared by its decisions alone. `name` is the Worker's (`sanoma-<name>`; by default the
155
+ * repository's name, which apply fills, its identity `UNNAMED` until then), `hostname`
156
+ * the app's, `database` the declared database it runs on. Apply gives it the rest (`deriveHosts`):
157
+ * the installed sanoma's runtime image, the repository the app runs from at its default branch
158
+ * (`image`, `source` and `ref` are overrides), the image's port, every public site's domain, and
159
+ * as secrets its database's URL, its keys, the source key it clones with (whose public half apply
160
+ * declares on the repository) and the credentials it deploys itself with: names only, never a value.
161
+ */
162
+ export const host = defineResource({
163
+ vendor: "cloudflare",
164
+ type: "host",
165
+ title: "Host",
166
+ identity: "name",
167
+ schema: hostSchema,
168
+ // A `cloudflare.container` of the lockfile is this Worker: renamed, the declaration keeps its binding.
169
+ formerly: ["cloudflare.container"],
170
+ fields: {
171
+ immutable: ["name"],
172
+ vendorOwned: ["id", "url", "worker_url", "healthy"],
173
+ writeOnly: ["secrets", "database"],
174
+ references: {
175
+ source: { type: "github.repository", by: ["http_clone_url"] },
176
+ database: { type: ["neon.project", "supabase.project"] },
177
+ },
178
+ },
179
+ find: ({ name }) => name ?? UNNAMED,
180
+ bindingId: (state) => String(state.name),
181
+ writable: true,
182
+ host: {
183
+ capabilities,
184
+ derives: true,
185
+ inputs: {
186
+ name: "name",
187
+ source: "source",
188
+ ref: "ref",
189
+ image: "image",
190
+ database: "database",
191
+ secrets: "secrets",
192
+ domains: "domains",
193
+ hostname: "hostname",
194
+ },
195
+ revision: "revision",
196
+ outputs: container.host.outputs,
197
+ },
198
+ });
104
199
  const alarmSchema = z.object({
105
200
  host: z
106
201
  .string()
@@ -136,7 +231,7 @@ export const alarm = defineResource({
136
231
  vendorOwned: ["at", "rang", "healthy"],
137
232
  writeOnly: ["token"],
138
233
  // The host's wake URL (`worker_url`, its outputs' `wakeUrl`), as apply gives it: its `url` is its hostname once attached.
139
- references: { host: { type: ["cloudflare.container"], by: ["worker_url", "url"] } },
234
+ references: { host: { type: ["cloudflare.host", "cloudflare.container"], by: ["worker_url", "url"] } },
140
235
  },
141
236
  bindingId: (state) => String(state.host),
142
237
  writable: true,
@@ -246,7 +341,7 @@ export const records = defineResource({
246
341
  // Records hold no data: a set deleted by hand is written again without asking.
247
342
  stateless: true,
248
343
  });
249
- const types = defineConnector("cloudflare", { container, alarm, records }, vendorInfo(import.meta.url));
344
+ const types = defineConnector("cloudflare", { host, container, alarm, records }, vendorInfo(import.meta.url));
250
345
  /** `alarm.register`, in the alarm type's own group, beside its resource operations. */
251
346
  const registerSpec = {
252
347
  register: {
@@ -1 +1 @@
1
- {"version":3,"file":"connector.js","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connector.js","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"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"}
package/dist/driver.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { defineDriver, DriverError, retryableStatus } from "@sanoma-ai/workflows";
2
2
  import { z } from "zod";
3
- import { cloudflareOps } from "./connector.js";
4
- import { alarmOps, containerOps, refused } from "./ops.js";
3
+ import { cloudflareOps, host } from "./connector.js";
4
+ import { refused } from "./errors.js";
5
+ import { alarmOps, containerOps } from "./ops.js";
5
6
  import { recordsOps } from "./records.js";
6
7
  import { CLOUDFLARE_REGISTRY, copyImage } from "./registry.js";
7
8
  /** The environment variables the driver reads, on every call. */
@@ -40,6 +41,13 @@ function failure(what, { APIError, APIConnectionTimeoutError }, timeoutMs, error
40
41
  }
41
42
  return new DriverError(`cloudflare: ${what}: no reply`, { retryable: true, cause: error });
42
43
  }
44
+ /** How an upload types each kind of module's part. */
45
+ const MODULE_TYPES = {
46
+ esm: "application/javascript+module",
47
+ wasm: "application/wasm",
48
+ text: "text/plain",
49
+ data: "application/octet-stream",
50
+ };
43
51
  const isStatus = (error, status) => error instanceof DriverError && error.status === status;
44
52
  /** A request to a Worker, with a timeout: a redirect is no answer of the Worker's, and would carry the token elsewhere. */
45
53
  const sender = (timeoutMs) => (url, init) => fetch(url, { ...init, redirect: "manual", signal: AbortSignal.timeout(timeoutMs) });
@@ -104,12 +112,33 @@ function accountApi(timeoutMs, subdomains) {
104
112
  // which the SDK's type does not have yet.
105
113
  const form = new FormData();
106
114
  form.append("metadata", new Blob([JSON.stringify(metadata)], { type: "application/json" }), "metadata.json");
107
- for (const { name, content } of modules) {
108
- form.append(name, new Blob([content], { type: "application/javascript+module" }), name);
115
+ for (const { name, content, type = "esm" } of modules) {
116
+ form.append(name, new Blob([content], { type: MODULE_TYPES[type] }), name);
109
117
  }
110
118
  await cf.put(`/accounts/${account_id}/workers/scripts/${script}`, { body: form });
111
119
  });
112
120
  },
121
+ async assetsSession(script, manifest) {
122
+ const { jwt, buckets } = await calling(`POST workers/scripts/${script}/assets-upload-session`, (cf) => cf.workers.scripts.assets.upload.create(script, { account_id, manifest }));
123
+ if (!jwt)
124
+ throw refused(`${script}: the assets upload session answered no jwt`);
125
+ return { jwt, buckets: buckets ?? [] };
126
+ },
127
+ async uploadAssets(jwt, files) {
128
+ // One part per asset, named by its hash, its base64 the content and its type the one it is
129
+ // served with, as the API reference's example sends them; the session's jwt, not the
130
+ // account's token, authorizes it.
131
+ const form = new FormData();
132
+ for (const { hash, base64, contentType } of files) {
133
+ form.append(hash, new File([base64], hash, { type: contentType }), hash);
134
+ }
135
+ const answer = await calling("POST workers/assets/upload", (cf) => cf.post(`/accounts/${account_id}/workers/assets/upload`, {
136
+ query: { base64: true },
137
+ body: form,
138
+ headers: { authorization: `Bearer ${jwt}` },
139
+ }));
140
+ return answer.result?.jwt ?? undefined;
141
+ },
113
142
  async enableSubdomain(script) {
114
143
  await calling(`POST workers/scripts/${script}/subdomain`, (cf) => cf.workers.scripts.subdomain.create(script, { account_id, enabled: true }));
115
144
  },
@@ -215,6 +244,7 @@ export function cloudflareDriver(options = {}) {
215
244
  // The alarm only calls the Worker, so it needs no account.
216
245
  const worker = { send: sender(timeoutMs) };
217
246
  return defineDriver(cloudflareOps, {
247
+ host: containerOps(api, { startTimeoutMs, pollMs }, host),
218
248
  container: containerOps(api, { startTimeoutMs, pollMs }),
219
249
  alarm: alarmOps(() => worker),
220
250
  records: recordsOps(api),