@sanoma-ai/connector-cloudflare 0.1.5 → 0.2.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 +35 -27
- package/dist/connector.d.ts +6 -2
- package/dist/connector.js +10 -3
- package/dist/connector.js.map +1 -1
- package/dist/driver.js +48 -35
- package/dist/driver.js.map +1 -1
- package/dist/fake.d.ts +20 -6
- package/dist/fake.js +84 -26
- package/dist/fake.js.map +1 -1
- package/dist/ops.d.ts +68 -24
- package/dist/ops.js +183 -59
- package/dist/ops.js.map +1 -1
- package/dist/registry.d.ts +15 -5
- package/dist/registry.js +44 -12
- package/dist/registry.js.map +1 -1
- package/dist/resources.d.ts +1 -0
- package/dist/steps.js +2 -2
- package/dist/steps.js.map +1 -1
- package/dist/worker.d.ts +1 -1
- package/dist/worker.js +1 -1
- package/dist/worker.js.map +1 -1
- package/features/cloudflare.feature +4 -4
- package/package.json +3 -3
- package/worker/instance.d.ts +14 -2
- package/worker/instance.js +68 -13
package/README.md
CHANGED
|
@@ -37,20 +37,20 @@ export const host = cloudflare.container({
|
|
|
37
37
|
export const wakeup = cloudflare.alarm({ host });
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
| Field | What it is
|
|
41
|
-
| ---------------------- |
|
|
42
|
-
| `name` | The company's name, its identity: lowercase letters, digits and dashes, up to 56; the Worker and its application are `sanoma-<name>`
|
|
43
|
-
| `image` | The Sanoma runtime image, pinned to a tag or digest, never `latest`: any registry's, which apply copies into the account's ([The image](#the-image)) |
|
|
44
|
-
| `source` | The company repository: a clone URL, or a declared `github.repository`
|
|
45
|
-
| `ref` | The branch it runs, never a commit: apply resolves it and sets the hidden `revision`, so a merge is drift on the container
|
|
46
|
-
| `port` | The port the runtime listens on in the container (its `PORT`)
|
|
47
|
-
| `secrets` | Environment variables, each a `secret(...)`: the Worker's secret bindings, write-only, never in an output, the ledger or a checkpoint
|
|
48
|
-
| `domains` | Optional: a public site's domain, by site name, passed as `SANOMA_DOMAINS
|
|
49
|
-
| `id`, `url`, `healthy` | Cloudflare's: the application's id, the Worker's workers.dev URL, and whether it answers
|
|
40
|
+
| Field | What it is |
|
|
41
|
+
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
42
|
+
| `name` | The company's name, its identity: lowercase letters, digits and dashes, up to 56; the Worker and its application are `sanoma-<name>` |
|
|
43
|
+
| `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)) |
|
|
44
|
+
| `source` | The company repository: a clone URL, or a declared `github.repository` |
|
|
45
|
+
| `ref` | The branch it runs, never a commit: apply resolves it and sets the hidden `revision`, so a merge is drift on the container |
|
|
46
|
+
| `port` | The port the runtime listens on in the container (its `PORT`) |
|
|
47
|
+
| `secrets` | Environment variables, each a `secret(...)`: the Worker's secret bindings, write-only, never in an output, the ledger or a checkpoint |
|
|
48
|
+
| `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)) |
|
|
49
|
+
| `id`, `url`, `healthy` | Cloudflare's: the application's id, the Worker's workers.dev URL, and whether it answers |
|
|
50
50
|
|
|
51
51
|
The host's outputs are `{ url, revision, healthy }`. 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.
|
|
52
52
|
|
|
53
|
-
The alarm's
|
|
53
|
+
The alarm's field is `host`, the container it wakes (its URL); its state is `{ host, at, rang, healthy }`, the wake it holds, when its alarm last rang the host's `/wake` for it, and whether the host's Worker took the company's token. Its optional `token`, a `secret("NAME")`, names the secret that holds the host's wakeup token, by default `SANOMA_WAKEUP_TOKEN`: a workflow that makes a host and its alarm for each of several companies, outside any data file, gives each its own (`token: secret("CLOUD_ACME_WAKEUP_TOKEN")`, the secret the host's `secrets` give as `SANOMA_WAKEUP_TOKEN`). `create` mints the token there when none is stored, and its state then carries the name as `token`, so a `read`, `update` or `delete` given that state sends the same token; one given only an id uses the default. A data file leaves `token` out: apply reads and deletes an alarm by its id alone, so `plan` refuses another name. `register` takes `{ at }`, an ISO 8601 time, which replaces the wake the Worker held: a wake is "resume if stopped", so one that rings late, twice or for a replaced registration starts a worker that finds nothing to do and stops again. It names no host: on the host, the worker reads the host's URL from `SANOMA_HOST_URL`, which the Worker gives the container; off it, from the credential `alarm.create` stored. A runtime whose data files declare no wakeup, but whose environment has both `SANOMA_HOST_URL` and `SANOMA_WAKEUP_TOKEN` (a host Sanoma Cloud made), registers at the same route itself, with no connector call (`envWakeup` in `@sanoma-ai/workflows`).
|
|
54
54
|
|
|
55
55
|
The logo is Cloudflare's cloud from [Simple Icons](https://simpleicons.org/?q=cloudflare) (CC0), there only to identify the service an operation calls.
|
|
56
56
|
|
|
@@ -58,19 +58,19 @@ The logo is Cloudflare's cloud from [Simple Icons](https://simpleicons.org/?q=cl
|
|
|
58
58
|
|
|
59
59
|
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:
|
|
60
60
|
|
|
61
|
-
- starts the container on a request at the
|
|
62
|
-
- 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
|
|
61
|
+
- starts the container on a request at the Worker's `SANOMA_REVISION` and the image its upload names (`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 and both as labels (the image's as a hash, since a label holds 64 bytes); a running container that does not carry both is destroyed and started again (a deploy);
|
|
62
|
+
- 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);
|
|
63
63
|
- answers `GET /__sanoma/health` with `{ revision, running }` without starting the container, and with `?start=1` once the container answers, which is what a create or update waits for;
|
|
64
64
|
- 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;
|
|
65
65
|
- at the alarm, starts the container and calls its `/wake`, then notes when (`rang`); a failure throws, so Cloudflare rings the alarm again.
|
|
66
66
|
|
|
67
|
-
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. A create uploads the Worker with the object's migration (`new_sqlite_classes`) and its class as a container's (`containers: [{ class_name }]`, as Wrangler sends it), turns its workers.dev URL on,
|
|
67
|
+
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 new revision. 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 secret's new value goes up with the next update for another reason (a plan compares secret names, never values: the Worker's settings never show them), and a running container keeps the old one until it starts again. A delete detaches the custom domains the Worker was declared to serve, 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.
|
|
68
68
|
|
|
69
69
|
## The image
|
|
70
70
|
|
|
71
|
-
A container
|
|
71
|
+
A container of the `durable_object` policy runs an image of its own account's registry pinned by digest, `registry.cloudflare.com/<account id>/<repository>@sha256:<digest>`, never a tag, which Cloudflare stores and pulls without a limit (Docker Hub's pulls are not cached and are limited per address). So a create or an update first copies the declared image there and pins the copy by its manifest's digest, and the object starts that: `ghcr.io/sanoma-ai/runtime:0.8.1` becomes `registry.cloudflare.com/<account id>/sanoma-ai-runtime@sha256:…` (the path with `-` for `/`, copied as `:0.8.1`). An image in the account's registry by a tag is not copied: its manifest's digest is asked for (`docker-content-digest`); one there pinned by digest runs as it is, with no registry request. The copy is registry to registry in Node, no Docker: the source pulled anonymously (a public image; the token its registry's challenge names), the target pushed to with temporary credentials from `containers.registries.credentials.generate` (push and pull, an hour, never stored or logged). Each blob the account lacks is streamed across in one request of its known length, then the manifest, unchanged; when the account's manifest for that tag already has the source's digest, nothing is copied, and a digest-pinned source must answer that digest. Docker Hub is not copied from: declare `ghcr.io/sanoma-ai/runtime`. The source must be one image manifest (OCI or Docker v2), not an index: the release pushes one (`provenance: false`). Then the account prepares the pinned image to run (`POST …/containers/image-preparations { image }`, `containers.images.prepare`), asked again every two seconds until it is `ready`, as Wrangler does: an `error` fails the apply with Cloudflare's `reason`, not retryable, and fifteen minutes without `ready` fails it as retryable. Both come before the upload, so a copy or a preparation that fails leaves the Worker as it was.
|
|
72
72
|
|
|
73
|
-
The Worker's `SANOMA_IMAGE` binding holds the declared image and the
|
|
73
|
+
The Worker's `SANOMA_IMAGE` binding holds the declared image and its upload the pinned copy, so a read reports the declared image, a plan and drift compare the declaration with it, and a declaration that names `ghcr.io` plans no change once applied. An image pushed by hand (`npx wrangler containers push`) is declared as the `registry.cloudflare.com/<account id>/...` reference Wrangler prints, and runs as it is, pinned; another account's is copied like any other registry's image.
|
|
74
74
|
|
|
75
75
|
## Scale to zero
|
|
76
76
|
|
|
@@ -79,7 +79,11 @@ The Worker's `SANOMA_IMAGE` binding holds the declared image and the application
|
|
|
79
79
|
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.
|
|
80
80
|
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.
|
|
81
81
|
|
|
82
|
-
To stop scaling to zero, delete the `wakeup` line and apply: the alarm is deleted, and the next request still starts the container.
|
|
82
|
+
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.
|
|
83
|
+
|
|
84
|
+
## Custom domains
|
|
85
|
+
|
|
86
|
+
Each hostname in `domains` is attached to the Worker as a [Workers custom domain](https://developers.cloudflare.com/workers/configuration/routing/custom-domains/) by a create or an update: Cloudflare then makes its DNS record and certificate and routes it to the Worker. The zone is the hostname's last two labels (`www.acme.example` is in `acme.example`), looked up in the account once per apex when a domain is to be attached (`GET /zones?name=&account.id=`); a hostname whose zone the account does not hold, or the token cannot read, is refused, naming the hostname and the zone, before anything changes. An update lists the Worker's attached domains first (`GET …/workers/domains?service=`), and only a missing one's zone is looked up; after the upload, each missing one is attached (`PUT …/workers/domains { zone_id, hostname, service }`), then the ones declared before and no longer are detached (`DELETE …/workers/domains/{id}`). A domain attached by hand and never declared is left alone. A read lists the Worker's attached domains too and shows only the declared ones attached, so a domain whose attach failed, or one detached by hand, is drift that the next apply attaches again. A delete detaches every declared one before the Worker goes. A container without `domains` makes none of these requests, unless it served some before, which are then detached. A zone such as `acme.co.uk`, whose apex is three labels, is not found yet.
|
|
83
87
|
|
|
84
88
|
## Driver
|
|
85
89
|
|
|
@@ -93,40 +97,44 @@ A read is the Worker's settings (its bindings: what it runs is its plain-text bi
|
|
|
93
97
|
|
|
94
98
|
It reads these on every call, never when the config loads:
|
|
95
99
|
|
|
96
|
-
| Variable | What it is
|
|
97
|
-
| ----------------------- |
|
|
98
|
-
| `CLOUDFLARE_API_TOKEN` | An API token with Workers Scripts Edit and Workers Containers Edit (Write, in the API's terms) |
|
|
99
|
-
| `CLOUDFLARE_ACCOUNT_ID` | The account's id, 32 hexadecimal digits, on its dashboard's overview
|
|
100
|
+
| Variable | What it is |
|
|
101
|
+
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
102
|
+
| `CLOUDFLARE_API_TOKEN` | An API token with Workers Scripts Edit and Workers Containers Edit (Write, in the API's terms), and Zone Read for `domains` (custom domains are attached and detached with Workers Scripts Edit) |
|
|
103
|
+
| `CLOUDFLARE_ACCOUNT_ID` | The account's id, 32 hexadecimal digits, on its dashboard's overview |
|
|
100
104
|
|
|
101
105
|
When either is unset, a call fails, not retryable, naming it. The driver declares both in its `env` ([Credentials](https://www.npmjs.com/package/@sanoma-ai/workflows#credentials)): the worker starts without them, and the app's connector page shows their status and lets you set them. The alarm's operations need neither: they call the Worker with `SANOMA_WAKEUP_TOKEN` (`alarm.create` mints it when none is stored), and `alarm.create` stores `SANOMA_HOST_URL` once the host answered, for a register made off the host; it keeps the first one stored, which the Worker's own binding overrides on the host. A read without the token reads the alarm as unhealthy, so a plan to delete it still goes on.
|
|
102
106
|
|
|
103
107
|
### Errors and retries
|
|
104
108
|
|
|
105
|
-
A 429, a 5xx, a timeout or no reply is retryable, the image's copy's requests too; any other 4xx is not. `status` and `vendorCode` (Cloudflare's first error `code`) are kept. The driver's own refusals are not retryable: `exists` (a Worker of that name that is not Sanoma's, which is neither overwritten nor deleted), `plan_changed` (an update whose plan changed since it was approved, or is not an update: a new name is a delete and a create), `no_token` and `token` (a host without the company's `SANOMA_WAKEUP_TOKEN`, or with another one), and a Worker that did not answer at its revision in time, which says what it answered.
|
|
109
|
+
A 429, a 5xx, a timeout or no reply is retryable, the image's copy's requests too; any other 4xx is not. `status` and `vendorCode` (Cloudflare's first error `code`) are kept. The driver's own refusals are not retryable: `exists` (a Worker of that name that is not Sanoma's, which is neither overwritten nor deleted), `plan_changed` (an update whose plan changed since it was approved, or is not an update: a new name is a delete and a create), `no_token` and `token` (a host without the company's `SANOMA_WAKEUP_TOKEN`, or with another one), `no_zone` (a domain whose zone the account does not hold), and a Worker that did not answer at its revision in time, which says what it answered.
|
|
106
110
|
|
|
107
111
|
### Plan
|
|
108
112
|
|
|
109
|
-
Containers need the Workers Paid plan. An instance is billed while it runs, by its `
|
|
113
|
+
Containers need the Workers Paid plan. An instance is billed while it runs, by its `standard-1` size, and not while it is stopped; the Worker and its object are billed per request.
|
|
110
114
|
|
|
111
115
|
### Testing the driver
|
|
112
116
|
|
|
113
|
-
`test/driver.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 is Wrangler's and not in the reference or the SDK's types: the live run is what checks
|
|
117
|
+
`test/driver.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 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.
|
|
114
118
|
|
|
115
119
|
### Running it live
|
|
116
120
|
|
|
117
|
-
`test/live.ts`, run by hand, runs the Host and Wakeup kits against a real account, then prints how long a cold wake took (a request to a stopped container until it answers). It makes a Worker and
|
|
121
|
+
`test/live.ts`, run by hand, runs the Host and Wakeup kits against a real account, then prints how long a cold wake took (a request to a stopped container until it answers). It makes a Worker and a `durable_object` application named `sanoma-host-check` (`CLOUDFLARE_TEST_NAME` changes it), takes about an hour (each stop is Cloudflare's own ten quiet minutes), and deletes both at the end. It is what checks what the fixtures cannot: the copy and its pin by digest, the image's preparation, the upload's `containers[].images`, the application's body, the object's `start` with the `standard-1` instance type, and a deploy check's new image started by the object:
|
|
118
122
|
|
|
119
123
|
```sh
|
|
120
124
|
pnpm build
|
|
121
125
|
SANOMA_LIVE=1 CLOUDFLARE_API_TOKEN=... CLOUDFLARE_ACCOUNT_ID=... \
|
|
122
126
|
CLOUDFLARE_TEST_CHECKOUT=~/src/host-check-company CLOUDFLARE_TEST_DATABASE_URL=postgresql://... \
|
|
127
|
+
CLOUDFLARE_TEST_SOURCE_KEY_FILE=~/.ssh/host-check-company_ed25519 \
|
|
123
128
|
node connectors/cloudflare/test/live.ts
|
|
124
129
|
```
|
|
125
130
|
|
|
131
|
+
The kits have not run on a real account yet: the connector's first real run was Sanoma's own back office (`sanoma-ops`, 2026-10-29), deployed from the app's Resources page. What it showed: the second upload above, the Host the proxy forwards, and a cold start of about six to eight minutes on `standard-1` (the clone, `pnpm install` and `sanoma serve` of a company with a public site), which is what a visitor waits for after ten quiet minutes until the container is kept running or the sites move to the Worker.
|
|
132
|
+
|
|
126
133
|
It needs:
|
|
127
134
|
|
|
128
135
|
- a published runtime image, `ghcr.io/sanoma-ai/runtime:<version>` (the `sanoma` package's version), which apply copies into the account's registry; `CLOUDFLARE_TEST_IMAGE` names another;
|
|
129
136
|
- `CLOUDFLARE_TEST_CHECKOUT`: a clone of a company repository whose `origin` Cloudflare can clone and this machine can push to (each deploy check pushes an empty commit), holding the kits' company: [`connectors/docker/test/fixtures/company`](../docker/test/fixtures/company) with `sanoma` from npm, its config registering `wakeupCheckWorkflows` (the host checks' workflows and a tick every minute);
|
|
137
|
+
- `CLOUDFLARE_TEST_SOURCE_KEY_FILE`, for a private repository: the private half of a read-only deploy key on it, passed as the host's `SANOMA_SOURCE_KEY` secret as go-live does (the private `Sanoma-AI/host-check-company` is that company, with such a key);
|
|
130
138
|
- `CLOUDFLARE_TEST_DATABASE_URL`: a Postgres both the container and this machine reach, empty, the company's;
|
|
131
139
|
- Postgres here for the operator that applies the host (`pnpm db:up`).
|
|
132
140
|
|
|
@@ -142,7 +150,7 @@ The connector carries steps for [scenarios](https://github.com/Sanoma-AI/sanoma/
|
|
|
142
150
|
|
|
143
151
|
## Testing
|
|
144
152
|
|
|
145
|
-
`@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 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), and a container that runs what `start` returns for the image and revision it was started at (by default a stub that answers `/healthz` and `/wake`). An upload restarts the object over its new bindings
|
|
153
|
+
`@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), 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.
|
|
146
154
|
|
|
147
155
|
```ts
|
|
148
156
|
import { fakeCloudflare } from "@sanoma-ai/connector-cloudflare/fake";
|
|
@@ -162,6 +170,6 @@ await fake.close();
|
|
|
162
170
|
|
|
163
171
|
## Later
|
|
164
172
|
|
|
165
|
-
|
|
173
|
+
Snapshots, static egress, outbound Workers, and reading the logs (the Worker's and the container's are turned on at the upload and the application, and readable in the account's observability) are not done here yet.
|
|
166
174
|
|
|
167
175
|
License: FSL-1.1-ALv2.
|
package/dist/connector.d.ts
CHANGED
|
@@ -6,7 +6,8 @@ import { z } from "zod";
|
|
|
6
6
|
* on a request and stops it ten minutes after the last (`Host`: stops when quiet, its URL public).
|
|
7
7
|
* What it runs is in the Worker's plain-text bindings, so a read tells its revision with no
|
|
8
8
|
* container running. A deploy uploads the Worker again; a new image is copied into the account's
|
|
9
|
-
* registry (
|
|
9
|
+
* registry (one there by a tag is pinned to its digest) and named by the upload, and the object
|
|
10
|
+
* starts the container again at it.
|
|
10
11
|
*/
|
|
11
12
|
export declare const container: import("@sanoma-ai/workflows").Resource<z.ZodObject<{
|
|
12
13
|
id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
@@ -31,13 +32,16 @@ export declare const container: import("@sanoma-ai/workflows").Resource<z.ZodObj
|
|
|
31
32
|
* the container at the time the worker registered and calls its `/wake`. The alarm
|
|
32
33
|
* is the host's object's, so it has no id of its own: its binding is the host's URL, and a new
|
|
33
34
|
* host is a new alarm. The host's `secrets` must carry `SANOMA_WAKEUP_TOKEN`, which `create`
|
|
34
|
-
* mints when none is stored
|
|
35
|
+
* mints when none is stored, under the store name `token` gives (`secret("NAME")`), by default
|
|
36
|
+
* `SANOMA_WAKEUP_TOKEN`: a workflow that makes hosts for several companies names one per company.
|
|
37
|
+
* The state keeps that name, so a `read`, `update` or `delete` given the state sends the same token.
|
|
35
38
|
*/
|
|
36
39
|
export declare const alarm: import("@sanoma-ai/workflows").Resource<z.ZodObject<{
|
|
37
40
|
host: z.ZodString;
|
|
38
41
|
at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
39
42
|
rang: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
40
43
|
healthy: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
|
|
44
|
+
token: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
41
45
|
}, z.core.$strip>, "cloudflare.alarm", {
|
|
42
46
|
readonly host: {
|
|
43
47
|
readonly type: readonly ["cloudflare.container"];
|
package/dist/connector.js
CHANGED
|
@@ -46,7 +46,8 @@ const containerSchema = z.object({
|
|
|
46
46
|
* on a request and stops it ten minutes after the last (`Host`: stops when quiet, its URL public).
|
|
47
47
|
* What it runs is in the Worker's plain-text bindings, so a read tells its revision with no
|
|
48
48
|
* container running. A deploy uploads the Worker again; a new image is copied into the account's
|
|
49
|
-
* registry (
|
|
49
|
+
* registry (one there by a tag is pinned to its digest) and named by the upload, and the object
|
|
50
|
+
* starts the container again at it.
|
|
50
51
|
*/
|
|
51
52
|
export const container = defineResource({
|
|
52
53
|
vendor: "cloudflare",
|
|
@@ -85,13 +86,19 @@ const alarmSchema = z.object({
|
|
|
85
86
|
.nullish()
|
|
86
87
|
.describe("When the alarm last rang the host's /wake for that registration, null until it has"),
|
|
87
88
|
healthy: z.boolean().nullish().describe("Whether the host's Worker answers its wake route with this company's token"),
|
|
89
|
+
token: z
|
|
90
|
+
.string()
|
|
91
|
+
.nullish()
|
|
92
|
+
.describe("The secret that holds the host's wakeup token, a `secret(\"NAME\")`: the one the host's secrets give as SANOMA_WAKEUP_TOKEN, by default SANOMA_WAKEUP_TOKEN itself. A state gives its name when it is another"),
|
|
88
93
|
});
|
|
89
94
|
/**
|
|
90
95
|
* `cloudflare.alarm({ host })`: the Durable Object alarm of the host's own Worker, which starts
|
|
91
96
|
* the container at the time the worker registered and calls its `/wake`. The alarm
|
|
92
97
|
* is the host's object's, so it has no id of its own: its binding is the host's URL, and a new
|
|
93
98
|
* host is a new alarm. The host's `secrets` must carry `SANOMA_WAKEUP_TOKEN`, which `create`
|
|
94
|
-
* mints when none is stored
|
|
99
|
+
* mints when none is stored, under the store name `token` gives (`secret("NAME")`), by default
|
|
100
|
+
* `SANOMA_WAKEUP_TOKEN`: a workflow that makes hosts for several companies names one per company.
|
|
101
|
+
* The state keeps that name, so a `read`, `update` or `delete` given the state sends the same token.
|
|
95
102
|
*/
|
|
96
103
|
export const alarm = defineResource({
|
|
97
104
|
vendor: "cloudflare",
|
|
@@ -101,7 +108,7 @@ export const alarm = defineResource({
|
|
|
101
108
|
fields: {
|
|
102
109
|
immutable: ["host"],
|
|
103
110
|
vendorOwned: ["at", "rang", "healthy"],
|
|
104
|
-
writeOnly: [],
|
|
111
|
+
writeOnly: ["token"],
|
|
105
112
|
references: { host: { type: ["cloudflare.container"], by: ["url"] } },
|
|
106
113
|
},
|
|
107
114
|
bindingId: (state) => String(state.host),
|
package/dist/connector.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connector.js","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,eAAe,EAAE,cAAc,EAAe,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAChH,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;GAKG;AAEH,8GAA8G;AAC9G,MAAM,IAAI,GAAG,wCAAwC,CAAC;AAEtD,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IAC/B,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,uDAAuD,CAAC;IAC1F,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,KAAK,CAAC,IAAI,EAAE,mDAAmD,CAAC;SAChE,QAAQ,CACP,0GAA0G,CAC3G;IACH,KAAK,EAAE,CAAC;SACL,MAAM,EAAE;SACR,KAAK,CAAC,oDAAoD,EAAE,oCAAoC,CAAC;SACjG,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,0CAA0C,CAAC;SACzF,QAAQ,CACP,8IAA8I,CAC/I;IACH,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,qEAAqE,CAAC;IACzG,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,oDAAoD,CAAC;IACrF,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,iDAAiD,CAAC;IAC1F,OAAO,EAAE,CAAC;SACP,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC;SAC1D,OAAO,EAAE;SACT,QAAQ,CACP,2HAA2H,CAC5H;IACH,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,+DAA+D,CAAC;IACnH,OAAO,EAAE,CAAC;SACP,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QAClE,6DAA6D;SAC5D,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,uCAAuC,CAAC;SAC7F,OAAO,EAAE;SACT,QAAQ,CAAC,sCAAsC,CAAC;IACnD,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,QAAQ,CAAC,wDAAwD,CAAC;IAC5F,OAAO,EAAE,CAAC;SACP,OAAO,EAAE;SACT,OAAO,EAAE;SACT,QAAQ,CAAC,oGAAoG,CAAC;CAClH,CAAC,CAAC;AAEH
|
|
1
|
+
{"version":3,"file":"connector.js","sourceRoot":"","sources":["../src/connector.ts"],"names":[],"mappings":"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"}
|
package/dist/driver.js
CHANGED
|
@@ -5,7 +5,9 @@ import { alarmOps, containerOps, refused } from "./ops.js";
|
|
|
5
5
|
import { CLOUDFLARE_REGISTRY, copyImage } from "./registry.js";
|
|
6
6
|
/** The environment variables the driver reads, on every call. */
|
|
7
7
|
const env = z.object({
|
|
8
|
-
CLOUDFLARE_API_TOKEN: z
|
|
8
|
+
CLOUDFLARE_API_TOKEN: z
|
|
9
|
+
.string()
|
|
10
|
+
.describe("an API token with Workers Scripts Edit and Workers Containers Edit, and Zone Read for custom domains"),
|
|
9
11
|
CLOUDFLARE_ACCOUNT_ID: z
|
|
10
12
|
.string()
|
|
11
13
|
.regex(/^[0-9a-f]{32}$/, "an account id is 32 hexadecimal digits")
|
|
@@ -40,11 +42,6 @@ function failure(what, { APIError, APIConnectionTimeoutError }, timeoutMs, error
|
|
|
40
42
|
const isStatus = (error, status) => error instanceof DriverError && error.status === status;
|
|
41
43
|
/** A request to a Worker, with a timeout: a redirect is no answer of the Worker's, and would carry the token elsewhere. */
|
|
42
44
|
const sender = (timeoutMs) => (url, init) => fetch(url, { ...init, redirect: "manual", signal: AbortSignal.timeout(timeoutMs) });
|
|
43
|
-
/** An application as the operations read it: its image, as its configuration names it. */
|
|
44
|
-
const application = (app) => ({
|
|
45
|
-
id: app.id,
|
|
46
|
-
image: app.configuration?.image ?? "",
|
|
47
|
-
});
|
|
48
45
|
/**
|
|
49
46
|
* The account at `CLOUDFLARE_ACCOUNT_ID` with `CLOUDFLARE_API_TOKEN`, read when an operation is
|
|
50
47
|
* called. `subdomains` keeps each account's workers.dev subdomain once read, for the driver's life.
|
|
@@ -99,15 +96,17 @@ function accountApi(timeoutMs, subdomains) {
|
|
|
99
96
|
}),
|
|
100
97
|
async upload(script, { modules, ...metadata }) {
|
|
101
98
|
await calling(`PUT workers/scripts/${script}`, async (cf) => {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
//
|
|
105
|
-
// `containers` is Wrangler's,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
99
|
+
// The form as Cloudflare reads it (and Wrangler sends it): one part `metadata`, the JSON, and
|
|
100
|
+
// one part per module, named after it. The SDK's `workers.scripts.update` flattens the
|
|
101
|
+
// metadata into `metadata[main_module]` parts instead, which Cloudflare refuses ("Could not
|
|
102
|
+
// read content for part 'metadata'", found live 2026-10-28). `containers` is Wrangler's,
|
|
103
|
+
// which the SDK's type does not have yet.
|
|
104
|
+
const form = new FormData();
|
|
105
|
+
form.append("metadata", new Blob([JSON.stringify(metadata)], { type: "application/json" }), "metadata.json");
|
|
106
|
+
for (const { name, content } of modules) {
|
|
107
|
+
form.append(name, new Blob([content], { type: "application/javascript+module" }), name);
|
|
108
|
+
}
|
|
109
|
+
await cf.put(`/accounts/${account_id}/workers/scripts/${script}`, { body: form });
|
|
111
110
|
});
|
|
112
111
|
},
|
|
113
112
|
async enableSubdomain(script) {
|
|
@@ -119,27 +118,27 @@ function accountApi(timeoutMs, subdomains) {
|
|
|
119
118
|
application: (name) => calling(`GET containers/applications?name=${name}`, async (cf) => {
|
|
120
119
|
const page = await cf.containers.applications.list({ account_id, name });
|
|
121
120
|
const app = page.result.find((a) => a.name === name);
|
|
122
|
-
return app &&
|
|
121
|
+
return app && { id: app.id };
|
|
123
122
|
}),
|
|
124
|
-
createApplication: (name,
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
max_instances: 1,
|
|
131
|
-
configuration: { image, instance_type: "basic" },
|
|
132
|
-
durable_objects: { namespace_id },
|
|
133
|
-
}))),
|
|
134
|
-
async rollout(id, image) {
|
|
135
|
-
await calling(`POST containers/applications/${id}/rollouts`, (cf) => cf.containers.applications.rollouts.create(id, {
|
|
123
|
+
createApplication: (name, namespace_id) => calling("POST containers/applications", async (cf) => {
|
|
124
|
+
// The `durable_object` policy, the one for new applications (`default` is refused: "This
|
|
125
|
+
// account cannot create applications with the "default" scheduling policy", found live
|
|
126
|
+
// 2026-10-28). The object starts the container with its image and instance type, so the
|
|
127
|
+
// body names neither.
|
|
128
|
+
const { id } = await cf.containers.applications.create({
|
|
136
129
|
account_id,
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
130
|
+
name,
|
|
131
|
+
scheduling_policy: "durable_object",
|
|
132
|
+
durable_objects: { namespace_id },
|
|
133
|
+
// The container's output goes to the account's logs: what a start that never answers
|
|
134
|
+
// was doing is readable there, and nowhere else.
|
|
135
|
+
observability: { logs: { enabled: true } },
|
|
136
|
+
});
|
|
137
|
+
return { id };
|
|
138
|
+
}),
|
|
139
|
+
async prepareImage(image) {
|
|
140
|
+
const { status, reason } = await calling("POST containers/image-preparations", (cf) => cf.containers.images.prepare({ account_id, image }));
|
|
141
|
+
return { status, ...(reason && { reason }) };
|
|
143
142
|
},
|
|
144
143
|
account: account_id,
|
|
145
144
|
async copyImage(image) {
|
|
@@ -149,11 +148,25 @@ function accountApi(timeoutMs, subdomains) {
|
|
|
149
148
|
permissions: ["push", "pull"],
|
|
150
149
|
expiration_minutes: 60,
|
|
151
150
|
}));
|
|
152
|
-
|
|
151
|
+
return copyImage(image, { account: account_id, username, password });
|
|
153
152
|
},
|
|
154
153
|
async deleteApplication(id) {
|
|
155
154
|
await found(`DELETE containers/applications/${id}`, (cf) => cf.containers.applications.delete(id, { account_id }));
|
|
156
155
|
},
|
|
156
|
+
zone: (name) => calling(`GET zones?name=${name}`, async (cf) => {
|
|
157
|
+
const page = await cf.zones.list({ name, account: { id: account_id } });
|
|
158
|
+
return page.result.find((zone) => zone.name === name)?.id;
|
|
159
|
+
}),
|
|
160
|
+
domains: (script) => calling(`GET workers/domains?service=${script}`, async (cf) => {
|
|
161
|
+
const page = await cf.workers.domains.list({ account_id, service: script });
|
|
162
|
+
return page.result.map(({ id, hostname }) => ({ id, hostname }));
|
|
163
|
+
}),
|
|
164
|
+
async attachDomain(script, hostname, zone_id) {
|
|
165
|
+
await calling(`PUT workers/domains ${hostname}`, (cf) => cf.workers.domains.update({ account_id, hostname, service: script, zone_id }));
|
|
166
|
+
},
|
|
167
|
+
async detachDomain(id) {
|
|
168
|
+
await found(`DELETE workers/domains/${id}`, (cf) => cf.workers.domains.delete(id, { account_id }));
|
|
169
|
+
},
|
|
157
170
|
send: sender(timeoutMs),
|
|
158
171
|
};
|
|
159
172
|
}
|
package/dist/driver.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"driver.js","sourceRoot":"","sources":["../src/driver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAElF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC/C,OAAO,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"driver.js","sourceRoot":"","sources":["../src/driver.ts"],"names":[],"mappings":"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"}
|
package/dist/fake.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface FakeContainer {
|
|
|
11
11
|
/** Each `/wake` it answered, in order, when it records them. */
|
|
12
12
|
wakes?: readonly FakeWake[];
|
|
13
13
|
}
|
|
14
|
-
/** Starts the company's runtime, as the container would: the image, the revision its label names, and the Worker's text bindings as its environment. */
|
|
14
|
+
/** Starts the company's runtime, as the container would: the image (the one its pinned copy was copied from), the revision its label names, and the Worker's text bindings as its environment. */
|
|
15
15
|
export type StartContainer = (input: {
|
|
16
16
|
image: string;
|
|
17
17
|
revision: string;
|
|
@@ -20,32 +20,46 @@ export type StartContainer = (input: {
|
|
|
20
20
|
export interface FakeCloudflareOptions extends FakeOptions {
|
|
21
21
|
/** What a container runs. Default: a stub that answers `/healthz` 200 and `/wake` 204. */
|
|
22
22
|
start?: StartContainer;
|
|
23
|
+
/** The zones the account holds, by name. Default: every one asked for, as a sandbox's account holds its domains. */
|
|
24
|
+
zones?: readonly string[];
|
|
23
25
|
}
|
|
24
26
|
export interface FakeCloudflareState extends Record<string, unknown> {
|
|
25
27
|
seq: number;
|
|
26
|
-
/** The Workers, by script name: their bindings, a secret's without its text. */
|
|
28
|
+
/** The Workers, by script name: their bindings, a secret's without its text, and the images their object may start, by name. */
|
|
27
29
|
scripts: Record<string, {
|
|
28
30
|
bindings: ReadBinding[];
|
|
29
31
|
subdomain: boolean;
|
|
32
|
+
images: Record<string, string>;
|
|
30
33
|
}>;
|
|
31
34
|
/** The container applications, by id. */
|
|
32
35
|
applications: Record<string, Application & {
|
|
33
36
|
name: string;
|
|
34
37
|
}>;
|
|
35
|
-
/** The account's registry: each image copied in, by its reference there, and the image it was copied from. */
|
|
38
|
+
/** The account's registry: each image copied in, by its pinned reference there, and the image it was copied from. */
|
|
36
39
|
images: Record<string, string>;
|
|
40
|
+
/** The account's zones looked up so far: each one's id, by name. */
|
|
41
|
+
zones: Record<string, string>;
|
|
42
|
+
/** The Workers' custom domains, by id: the hostname, the Worker it routes to and its zone's id. */
|
|
43
|
+
domains: Record<string, {
|
|
44
|
+
hostname: string;
|
|
45
|
+
service: string;
|
|
46
|
+
zone_id: string;
|
|
47
|
+
}>;
|
|
37
48
|
}
|
|
38
49
|
/**
|
|
39
50
|
* An in-memory Cloudflare account for tests, as the API answers: Workers in `state.scripts`,
|
|
40
51
|
* container applications in `state.applications`, the images copied into its registry in
|
|
41
|
-
* `state.images` (nothing is pulled),
|
|
52
|
+
* `state.images` (nothing is pulled), their custom domains in `state.domains` (every zone held
|
|
53
|
+
* unless `zones` names them), and the account's workers.dev URLs served in
|
|
42
54
|
* this process, one server on a port of this machine per Worker name (a name with no Worker
|
|
43
55
|
* answers workers.dev's 404). Each Worker runs the package's own object code
|
|
44
56
|
* (`worker/instance.js`) over a `ctx` in this process: its container runs `start`'s runtime
|
|
45
57
|
* (the image and the revision label it was started at, the Worker's text bindings as its
|
|
46
58
|
* environment), its storage is in memory, and its alarm rings on a timer, again after a throw.
|
|
47
|
-
* An upload restarts the object over the new bindings, its container, storage and
|
|
48
|
-
*
|
|
59
|
+
* An upload restarts the object over the new bindings and images, its container, storage and
|
|
60
|
+
* alarm kept: a new image starts the container again at the next request, as the object finds its
|
|
61
|
+
* label changed. `copyImage` pins its copy by a made-up digest, and `prepareImage` answers ready
|
|
62
|
+
* for an image pinned in the account's registry. `stop(name)` stops a company's
|
|
49
63
|
* container as ten quiet minutes would, `fire()` rings every alarm now, `wakes()` lists the
|
|
50
64
|
* `/wake`s the containers answered, `close()` (and `reset()`) stops everything.
|
|
51
65
|
*/
|