@lunora/container 1.0.0-alpha.8 → 1.0.0-alpha.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +24 -5
  2. package/dist/bridge.d.mts +43 -33
  3. package/dist/bridge.d.ts +43 -33
  4. package/dist/bridge.mjs +1 -79
  5. package/dist/do/index.d.mts +353 -91
  6. package/dist/do/index.d.ts +353 -91
  7. package/dist/do/index.mjs +1 -328
  8. package/dist/index.d.mts +584 -153
  9. package/dist/index.d.ts +584 -153
  10. package/dist/index.mjs +1 -2
  11. package/dist/otel.d.mts +217 -0
  12. package/dist/otel.d.ts +217 -0
  13. package/dist/otel.mjs +1 -0
  14. package/dist/packem_shared/CONTAINER_EXEC_PATH-7Hcd6-zs.mjs +1 -0
  15. package/dist/packem_shared/CUSTOM_INSTANCE_MIN_MEMORY_MIB_PER_VCPU-D8Hy9Rxv.mjs +1 -0
  16. package/dist/packem_shared/ContainerProxy-CfXjS6ZI.mjs +27 -0
  17. package/dist/packem_shared/DURABLE_OBJECT_POLICY_FORBIDDEN_KEYS-gYi8RBUO.mjs +1 -0
  18. package/dist/packem_shared/LunoraSandboxContainer-DPnhJUu1.mjs +1 -0
  19. package/dist/packem_shared/abort-deadline-jwbW9Ala.mjs +1 -0
  20. package/dist/packem_shared/createContainerContext-p6QxWjsm.mjs +1 -0
  21. package/dist/packem_shared/exec-BYLW70sR.mjs +1 -0
  22. package/dist/packem_shared/jurisdiction-DtE9s70w.mjs +1 -0
  23. package/dist/packem_shared/jurisdiction.d-BwpauCoI.d.mts +471 -0
  24. package/dist/packem_shared/jurisdiction.d-BwpauCoI.d.ts +471 -0
  25. package/dist/packem_shared/sampling-D21rpu_s.mjs +1 -0
  26. package/dist/packem_shared/sandbox-types.d-DXlHnsEg.d.mts +110 -0
  27. package/dist/packem_shared/sandbox-types.d-DXlHnsEg.d.ts +110 -0
  28. package/dist/sandbox/index.d.mts +66 -0
  29. package/dist/sandbox/index.d.ts +66 -0
  30. package/dist/sandbox/index.mjs +1 -0
  31. package/package.json +12 -2
  32. package/dist/packem_shared/ContainerProxy-DWqUX_re.mjs +0 -1474
  33. package/dist/packem_shared/containerBindingName-D5KTrblH.mjs +0 -218
  34. package/dist/packem_shared/createContainerContext-Cibbh7AE.mjs +0 -228
  35. package/dist/packem_shared/types.d-BlNwNY44.d.mts +0 -250
  36. package/dist/packem_shared/types.d-BlNwNY44.d.ts +0 -250
@@ -0,0 +1 @@
1
+ import{LunoraError as d}from"@lunora/errors";import{a as w}from"./abort-deadline-jwbW9Ala.mjs";const E=async(o,t,r)=>{if(o===null)return{overflowed:!1,text:""};const e=o.getReader(),n=new TextDecoder,a=()=>{e.cancel().catch(()=>{})};r?.addEventListener("abort",a);let s="",c=0,i=!1;try{for(;r?.aborted!==!0;){const{done:f,value:u}=await e.read();if(f)break;if(c+u.byteLength>t){i=!0,s+=n.decode(u.subarray(0,t-c),{stream:!0});break}c+=u.byteLength,s+=n.decode(u,{stream:!0})}s+=n.decode()}finally{r?.removeEventListener("abort",a),e.cancel().catch(()=>{})}if(r?.aborted===!0)throw r.reason;return{overflowed:i,text:s}},l="/__lunora/exec",h="x-lunora-container-exec",x=5,m=o=>{try{return decodeURIComponent(o)}catch{return o.replaceAll(/%[\da-f]{2}/giu,t=>{try{return decodeURIComponent(t)}catch{return t}})}},_=(o,t)=>{let r=o;for(let e=0;e<=x;e+=1){if(t(r))return!0;const n=m(r);if(n===r)return!1;r=n}return!0},b=512,p=1e6,v="<redacted>",g=(o,t)=>{if(t===void 0)return o;let r=o;for(const e of Object.values(t))e.length>0&&(r=r.split(e).join(v));return r},T=o=>{if(o===null||typeof o!="object")return;const{code:t,stderr:r,stdout:e}=o;if(typeof t=="number")return{code:t,stderr:typeof r=="string"?r:"",stdout:typeof e=="string"?e:""}},A=(o,t)=>w(o,t,()=>new d("INTERNAL",`ctx.containers: exec timed out after ${String(t)}ms`)),R=(o,t)=>async(r,e={})=>{if(typeof r!="string"||r.length===0)throw new d("BAD_REQUEST",`${t}: exec requires a non-empty \`command\``);const n=e.maxOutputBytes??p;if(!Number.isSafeInteger(n)||n<=0)throw new d("BAD_REQUEST",`${t}: exec \`maxOutputBytes\` must be a positive whole number of bytes, got ${String(n)}`);const a=A(e.signal,e.timeoutMs);try{const s=await o(l,{body:JSON.stringify({args:e.args??[],command:r,...e.cwd===void 0?{}:{cwd:e.cwd},...e.env===void 0?{}:{env:e.env},maxOutputBytes:n,...e.timeoutMs===void 0?{}:{timeoutMs:e.timeoutMs}}),headers:{"content-type":"application/json",[h]:"1"},method:"POST",...a.signal===void 0?{}:{signal:a.signal}});if(!s.ok){const u=await E(s.body,b,a.signal).then(y=>g(y.text,e.env),()=>"<unreadable body>");throw new d("INTERNAL",`${t}: exec failed — the container answered ${String(s.status)} for POST ${l}. Does it serve that route? Body: ${u}`)}const c=await E(s.body,n,a.signal);if(c.overflowed)throw new d("INTERNAL",`${t}: exec response exceeded ${String(n)} bytes and was abandoned — it is buffered whole to be parsed, so an unbounded one would exhaust the isolate. Cap the command's output, or raise \`maxOutputBytes\`.`);let i;try{i=JSON.parse(c.text)}catch{throw new d("INTERNAL",`${t}: exec response was not JSON — expected {"code","stdout","stderr"} from POST ${l}`)}const f=T(i);if(f===void 0)throw new d("INTERNAL",`${t}: exec response is missing a numeric \`code\` — expected {"code","stdout","stderr"} from POST ${l}`);return f}finally{a.dispose()}};export{l as C,p as D,h as a,R as e,_ as p,E as r};
@@ -0,0 +1 @@
1
+ const t=(o,r)=>{if(r===void 0)return o;if(typeof o.jurisdiction!="function")throw new TypeError(`@lunora/container: Durable Object namespace does not support jurisdiction("${r}") — update @cloudflare/workers-types or remove the jurisdiction option`);return o.jurisdiction(r)};export{t as a};
@@ -0,0 +1,471 @@
1
+ /**
2
+ * The `handle.spawn()` contract: start a process in a named container
3
+ * instance and stream it, rather than buffer it the way `exec` does.
4
+ *
5
+ * Node-safe (no runtime imports): the Durable Object half lives in
6
+ * `do/spawn.ts`. This module owns the request shape both halves agree on and
7
+ * turns the RPC result into a {@link ContainerProcess}.
8
+ */
9
+ /** Per-call options for `handle.spawn()`. */
10
+ interface ContainerSpawnOptions {
11
+ /** Arguments passed to `command`, unshelled. */
12
+ args?: ReadonlyArray<string>;
13
+ /** Working directory for the process. */
14
+ cwd?: string;
15
+ /** Extra environment for this process, merged over the container's start env. */
16
+ env?: Readonly<Record<string, string>>;
17
+ /**
18
+ * Run the process on a pseudo-terminal of this size (defaults `80×24`).
19
+ * Under a PTY, stderr is merged into `stdout` and `resize()` works — what an
20
+ * interactive shell or a browser terminal needs.
21
+ */
22
+ pty?: {
23
+ cols?: number;
24
+ rows?: number;
25
+ };
26
+ /** Kill the process when this signal aborts. */
27
+ signal?: AbortSignal;
28
+ /**
29
+ * Give the process a stdin: `true` returns a writable `stdin` stream, a
30
+ * `ReadableStream` is piped in as the process's whole input. Omitted or
31
+ * `false` leaves stdin closed.
32
+ */
33
+ stdin?: ReadableStream<Uint8Array> | boolean;
34
+ /**
35
+ * Kill the process after this many ms. Unlike `exec`, a spawned process has
36
+ * no default deadline: it runs until it exits, is killed, or the container
37
+ * stops.
38
+ */
39
+ timeoutMs?: number;
40
+ }
41
+ /** The request `lunoraSpawn` takes over RPC. An `AbortSignal` cannot cross RPC, so it stays on the caller's side. */
42
+ interface ContainerSpawnRequest extends Omit<ContainerSpawnOptions, "args" | "env" | "signal"> {
43
+ args?: string[];
44
+ command: string;
45
+ env?: Record<string, string>;
46
+ }
47
+ /** The control stub `lunoraSpawn` returns, as the caller sees it over RPC. */
48
+ interface ContainerProcessControlStub {
49
+ exitCode: () => Promise<number>;
50
+ kill: (signal?: number) => Promise<void> | void;
51
+ resize: (cols: number, rows: number) => Promise<void> | void;
52
+ }
53
+ /** `lunoraSpawn`'s RPC result. */
54
+ interface SpawnResult {
55
+ control: ContainerProcessControlStub;
56
+ isPty: boolean;
57
+ pid: number;
58
+ stderr: ReadableStream<Uint8Array> | null;
59
+ stdin: WritableStream<Uint8Array> | null;
60
+ stdout: ReadableStream<Uint8Array> | null;
61
+ }
62
+ /**
63
+ * A running process in a container instance, returned by `handle.spawn()`.
64
+ *
65
+ * The container finishes a process's output, and settles `exitCode`, only
66
+ * while its output is being drained. Lunora drains `stdout` and `stderr` into
67
+ * a 1 MiB buffer each, so they can be read in either order, or after awaiting
68
+ * `exitCode`, while the stream you are not reading stays under 1 MiB. Past
69
+ * that the process stalls until you read it: for a process that may write more
70
+ * to one stream, read both concurrently, or cancel the one you do not need.
71
+ */
72
+ interface ContainerProcess {
73
+ /** Resolves with the exit code once the process ends. */
74
+ exitCode: Promise<number>;
75
+ /** Whether the process runs on a PTY (`spawn({ pty })`). */
76
+ isPty: boolean;
77
+ /** Send `signal` (default `SIGTERM`). Resolves once the signal is sent, not when the process exits. */
78
+ kill: (signal?: number) => Promise<void>;
79
+ /** The process id inside the container, or `-1` when the runtime does not report one. */
80
+ pid: number;
81
+ /** Resize the PTY. Rejects for a process spawned without `pty`. */
82
+ resize: (cols: number, rows: number) => Promise<void>;
83
+ /** The process's stderr, or `null` under `pty` (merged into `stdout`). */
84
+ stderr: ReadableStream<Uint8Array> | null;
85
+ /** The process's stdin when spawned with `stdin: true`, else `null`. */
86
+ stdin: WritableStream<Uint8Array> | null;
87
+ /** The process's stdout (and, under `pty`, its stderr too). */
88
+ stdout: ReadableStream<Uint8Array> | null;
89
+ }
90
+ /**
91
+ * Public configuration types for `@lunora/container`.
92
+ *
93
+ * Everything in this module is pure data — no Cloudflare runtime imports — so
94
+ * it is safe to import from Node tooling (codegen, the config layer) as well
95
+ * as from worker code.
96
+ */
97
+ /**
98
+ * Named instance types Cloudflare Containers provides.
99
+ */
100
+ type NamedContainerInstanceType = "basic" | "lite" | "standard-1" | "standard-2" | "standard-3" | "standard-4";
101
+ /**
102
+ * A custom instance type, available to every Containers account. Cloudflare's
103
+ * documented bounds: 1–4 vCPU, up to 12 GiB memory, up to 20 GB disk at any
104
+ * memory size, and ≥ 3 GiB memory per vCPU. For less than 1 vCPU use the named
105
+ * `lite` or `basic` types. The bounds live in `CUSTOM_INSTANCE_TYPE_LIMITS`
106
+ * (`./instance-limits`), which both `defineContainer` (a `durable_object`
107
+ * container's runtime size) and `@lunora/config`'s wrangler validator
108
+ * (`containers[].instance_type`) enforce.
109
+ */
110
+ interface CustomContainerInstanceType {
111
+ /** Disk in MB, up to 20000 (20 GB) at any memory size. Cloudflare's default is 2000 (2 GB). */
112
+ diskMb?: number;
113
+ /** Memory in MiB, up to 12288 (12 GiB) and ≥ 3072 MiB per vCPU. Cloudflare's default is 256. */
114
+ memoryMib?: number;
115
+ /** vCPU count, 1–4 when set. Cloudflare's default is 0.0625 (1/16 vCPU). */
116
+ vcpu?: number;
117
+ }
118
+ /**
119
+ * `ContainerInstanceType` is part of the experimental `@lunora/container` API and may change without a major version bump.
120
+ */
121
+ type ContainerInstanceType = CustomContainerInstanceType | NamedContainerInstanceType;
122
+ /**
123
+ * An instance size a Durable Object can pick at start under the
124
+ * `durable_object` scheduling policy. The runtime does not accept `basic` (or
125
+ * the legacy `dev`/`standard` aliases), and a custom size must set all three
126
+ * fields — it is passed to `ctx.container.start({ instance })` as-is.
127
+ */
128
+ type ContainerRuntimeInstanceType = Exclude<NamedContainerInstanceType, "basic"> | Required<CustomContainerInstanceType>;
129
+ /**
130
+ * One named image under the `durable_object` scheduling policy. A `string` is
131
+ * a local Dockerfile path or directory, built and uploaded by `wrangler deploy`
132
+ * (wrangler `dockerfile` + `build_context`); `{ registry }` must be a
133
+ * digest-pinned (`@sha256:…`) reference under `registry.cloudflare.com/`,
134
+ * the Cloudflare managed registry — Cloudflare does not accept Docker Hub, ECR
135
+ * or Artifact Registry references here.
136
+ */
137
+ type ContainerNamedImageSource = RegistryImageSource | string;
138
+ /**
139
+ * Rolling-deploy tuning for a container.
140
+ */
141
+ interface ContainerRollout {
142
+ /** Seconds an active instance runs before it's eligible for update (wrangler `rollout_active_grace_period`). */
143
+ gracePeriodSeconds?: number;
144
+ /** Percentage of instances updated per rollout step, 1–100 (wrangler `rollout_step_percentage`). */
145
+ stepPercentage?: number;
146
+ }
147
+ /**
148
+ * A pre-built image pulled from a registry — the Cloudflare Registry, Docker
149
+ * Hub, or Amazon ECR (the registries `wrangler deploy` supports). The
150
+ * reference must be fully qualified, e.g. `docker.io/acme/transcoder:1.4`.
151
+ */
152
+ interface RegistryImageSource {
153
+ registry: string;
154
+ }
155
+ /**
156
+ * A Dockerfile-less build via [Railpack](https://railpack.com): point at a
157
+ * source directory and `lunora deploy` builds an OCI image with Railpack
158
+ * (needs a BuildKit instance) and pushes it to the Cloudflare Registry before
159
+ * wrangler runs. Opt-in — the Dockerfile path is the zero-extra-deps default.
160
+ */
161
+ interface BuildImageSource {
162
+ build: string;
163
+ }
164
+ /**
165
+ * Where the container image comes from. A `string` is a **local path** —
166
+ * either a directory containing a `Dockerfile` (normalized to
167
+ * `<dir>/Dockerfile` with the directory as the build context) or a path to
168
+ * the Dockerfile itself — while `{ registry }` is a pre-built image reference.
169
+ */
170
+ type ContainerImageSource = BuildImageSource | RegistryImageSource | string;
171
+ /**
172
+ * An application-level readiness probe that gates request proxying. Layered on
173
+ * top of the platform's own port/`pingEndpoint` health wait, it lets you hold
174
+ * traffic back until the app inside the container is *functionally* ready —
175
+ * migrations applied, caches warmed — which an open-port check can't see.
176
+ *
177
+ * Declarative on purpose: a `defineContainer` value stays pure data (no handler
178
+ * functions), so codegen and the config layer can read it without evaluating
179
+ * code. (Upstream cloudflare/containers#188 expresses the same idea as handler
180
+ * functions; the Lunora config is data-only, so it's modelled as descriptors.)
181
+ */
182
+ interface ContainerReadinessCheck {
183
+ /** HTTP path probed on the container, e.g. `"/ready"` (a leading slash is optional). */
184
+ path: string;
185
+ /** Port to probe. Defaults to {@link ContainerConfigBase.defaultPort}. */
186
+ port?: number;
187
+ /** HTTP status that means "ready". Defaults to `200`. */
188
+ status?: number;
189
+ }
190
+ /**
191
+ * The fields every container accepts, whichever scheduling policy it uses.
192
+ */
193
+ /**
194
+ * Where `handle.backup()` stores directory backups for a `sandbox: true`
195
+ * container: an R2 bucket binding and an optional key prefix.
196
+ */
197
+ interface ContainerBackupStorage {
198
+ /** Name of an R2 bucket binding on the Worker env (`r2_buckets[].binding`). */
199
+ bucket: string;
200
+ /** Key prefix for the backup objects. A non-empty prefix ends in `/`. */
201
+ prefix?: string;
202
+ }
203
+ interface ContainerConfigBase {
204
+ /**
205
+ * Hostnames the container may reach **even when {@link ContainerConfigBase.enableInternet}
206
+ * is `false`** — an egress allow-list (Cloudflare's `allowedHosts`). Glob
207
+ * patterns like `*.stripe.com` are supported. Pair with `enableInternet:
208
+ * false` to deny all egress except these hosts (the firewall pattern
209
+ * upstream issue cloudflare/containers#30 asked for). The interception path
210
+ * needs the `ContainerProxy` worker entrypoint, which codegen re-exports
211
+ * from the generated container file automatically; the named-instance
212
+ * handle's `egress` controls adjust the lists at runtime.
213
+ */
214
+ allowedHosts?: ReadonlyArray<string>;
215
+ /**
216
+ * Where `handle.backup()` saves directory backups (requires `sandbox: true`).
217
+ * The backup and restore traffic goes through the `DirectoryBackupGateway`
218
+ * Worker entrypoint, which hands the container a grant for one object per
219
+ * operation, so the container never holds bucket credentials.
220
+ */
221
+ backups?: ContainerBackupStorage;
222
+ /**
223
+ * Build-time variables for a Dockerfile/Railpack image — wrangler's
224
+ * `image_vars` (equivalent to `docker build --build-arg`). For *runtime*
225
+ * values use {@link ContainerConfigBase.env} / {@link ContainerConfigBase.secrets}.
226
+ * Ignored for a pre-built `{ registry }` image.
227
+ */
228
+ buildArgs?: Readonly<Record<string, string>>;
229
+ /**
230
+ * The port the container listens on. Worker → container requests target
231
+ * this port. Locally the Dockerfile must also `EXPOSE` it. For a
232
+ * multi-port container also declare {@link ContainerConfigBase.requiredPorts}
233
+ * and route per request with the handle's `.port(n)`.
234
+ */
235
+ defaultPort?: number;
236
+ /**
237
+ * Hostnames the container may **never** reach — an egress deny-list
238
+ * (Cloudflare's `deniedHosts`). Overrides everything else, including
239
+ * `enableInternet: true` and {@link ContainerConfigBase.allowedHosts}. Glob
240
+ * patterns like `*.evil.com` are supported.
241
+ */
242
+ deniedHosts?: ReadonlyArray<string>;
243
+ /**
244
+ * Whether the container may open outbound internet connections. Defaults
245
+ * to `true` — the platform default. Note that container egress is billed
246
+ * per GB by Cloudflare. Combine with {@link ContainerConfigBase.allowedHosts} /
247
+ * {@link ContainerConfigBase.deniedHosts} for a precise egress firewall.
248
+ */
249
+ enableInternet?: boolean;
250
+ /**
251
+ * Default command to run inside the container, overriding the image's
252
+ * `ENTRYPOINT`/`CMD` (Cloudflare's `entrypoint`). A per-start override is
253
+ * still available via the named-instance handle's `start({ entrypoint })`.
254
+ */
255
+ entrypoint?: ReadonlyArray<string>;
256
+ /**
257
+ * Static environment variables passed to the container on every start.
258
+ * For secret values use {@link ContainerConfigBase.secrets} instead so they
259
+ * flow through Worker Secrets rather than source code.
260
+ */
261
+ env?: Readonly<Record<string, string>>;
262
+ /**
263
+ * Hard cap on how long an instance may run, measured from start regardless
264
+ * of activity — a runaway-cost backstop on top of the idle
265
+ * {@link ContainerConfigBase.sleepAfter}. Same grammar as `sleepAfter`
266
+ * (`"30s"`, `"5m"`, `"1h"`, or a plain number of seconds). When it elapses,
267
+ * the `LunoraContainer.onHardTimeoutExpired` hook runs (default: `stop()`,
268
+ * which sends SIGTERM and does not escalate — a container that ignores the
269
+ * signal outlives its cap unless the hook is overridden to follow up with
270
+ * `destroy()`). (Upstream cloudflare/containers#85.)
271
+ */
272
+ hardTimeout?: number | string;
273
+ /**
274
+ * Intercept the container's outbound **HTTPS** traffic so the egress
275
+ * allow/deny lists apply to TLS connections too (Cloudflare's
276
+ * `interceptHttps`). Requires the image to trust the Cloudflare CA at
277
+ * `/etc/cloudflare/certs/cloudflare-containers-ca.crt`. Defaults to `false`
278
+ * (HTTP egress is gated regardless).
279
+ */
280
+ interceptHttps?: boolean;
281
+ /**
282
+ * Key-value metadata attached to every instance for metrics/observability
283
+ * (Cloudflare's container `labels`), e.g. `{ tenant: "acme", env: "prod" }`.
284
+ * A per-start override is available via the named-instance handle's
285
+ * `start({ labels })`.
286
+ */
287
+ labels?: Readonly<Record<string, string>>;
288
+ /**
289
+ * Override for the wrangler `containers[].name` identifier. Defaults to
290
+ * wrangler's own default (worker name + class name + environment).
291
+ */
292
+ name?: string;
293
+ /**
294
+ * HTTP path Cloudflare polls to decide an instance is healthy
295
+ * (Cloudflare's `pingEndpoint`). Defaults to upstream's slash-less `"ping"`;
296
+ * either `"ping"` or `"/healthz"`-style paths are accepted. Set this when
297
+ * the container exposes its readiness check under a different route.
298
+ */
299
+ pingEndpoint?: string;
300
+ /**
301
+ * Application-level readiness probes that gate request proxying: a
302
+ * `ctx.containers.<name>` fetch waits until every probe responds with its
303
+ * expected status before the request reaches the container — on top of the
304
+ * platform's port/`pingEndpoint` health wait. All probes run in parallel.
305
+ * Use these for readiness an open-port check can't see (migrations applied,
306
+ * caches warm). (Upstream cloudflare/containers#188.)
307
+ */
308
+ readyOn?: ReadonlyArray<ContainerReadinessCheck>;
309
+ /**
310
+ * Ports the container must be listening on before it's considered ready
311
+ * (Cloudflare's `requiredPorts`) — for multi-port containers. Start-up
312
+ * waits for every listed port, and the handle's `.port(n)` routes a request
313
+ * to any of them; {@link ContainerConfigBase.defaultPort} is the target when a
314
+ * request doesn't pick one.
315
+ */
316
+ requiredPorts?: ReadonlyArray<number>;
317
+ /**
318
+ * Opt in to the Sandbox SDK helpers (`@cloudflare/sandbox`): structured
319
+ * file operations, directory backups to R2 and S3-compatible bucket mounts
320
+ * on a named instance. The image must ship the `sandbox-shim` helper at
321
+ * `/usr/local/bin/sandbox-shim`, copied from Cloudflare's shim-only
322
+ * `cloudflare/sandbox` image (`COPY --from=…`). Turning it on also makes codegen export the `S3Gateway` and
323
+ * `DirectoryBackupGateway` entrypoints from the worker, which the helpers
324
+ * route the container's storage traffic through. Must be a `true`/`false`
325
+ * literal, because codegen reads it. Defaults to `false`.
326
+ */
327
+ sandbox?: boolean;
328
+ /**
329
+ * Names of Worker secrets (from `wrangler secret` / `.dev.vars`) forwarded
330
+ * into the container's environment at instance start. Each declared name
331
+ * must exist on the Worker `env` — a missing one fails fast with a
332
+ * directed error instead of starting the container without it.
333
+ */
334
+ secrets?: ReadonlyArray<string>;
335
+ /**
336
+ * Cloudflare **Secrets Store** secrets forwarded into the container's
337
+ * environment, as a map of *container env-var name → Worker Secrets Store
338
+ * binding name*. Each binding is resolved with its async `.get()` the first
339
+ * time the instance starts, then injected as that env var — e.g.
340
+ * `{ STRIPE_KEY: "STRIPE_SECRET" }` runs `env.STRIPE_SECRET.get()` and sets
341
+ * `STRIPE_KEY` inside the container. Unlike {@link ContainerConfigBase.secrets}
342
+ * (plain Worker text secrets), this pulls from a `secrets_store_secrets`
343
+ * binding. A name already used by `env`/`secrets` is rejected at authoring
344
+ * time; a missing binding or unreadable value fails the start. Applies
345
+ * to the default start (the `ctx.containers` proxy path and a bare
346
+ * `start()`); a persisted per-instance `start({ envVars })` replaces the env
347
+ * set wholesale, as it does for `env`/`secrets`. (Upstream
348
+ * cloudflare/containers#96.)
349
+ */
350
+ secretsStore?: Readonly<Record<string, string>>;
351
+ /**
352
+ * Idle timeout after which the instance is put to sleep, e.g. `"5m"`,
353
+ * `"30s"`, or a number of seconds. Cloudflare's default is `"10m"`.
354
+ */
355
+ sleepAfter?: number | string;
356
+ }
357
+ /**
358
+ * A container under the `default` scheduling policy: one application-wide
359
+ * image and instance size, set in wrangler and rolled out by Cloudflare on
360
+ * deploy.
361
+ */
362
+ interface DefaultScheduledContainerConfig extends ContainerConfigBase {
363
+ /** Image source — a local Dockerfile path/directory or a registry reference. */
364
+ image: ContainerImageSource;
365
+ /**
366
+ * Resource class for each instance: a named Cloudflare instance type or a
367
+ * custom `{ vcpu, memoryMib, diskMb }` object.
368
+ */
369
+ instanceType?: ContainerInstanceType;
370
+ /**
371
+ * Maximum number of concurrently *running* instances. Stopped (slept)
372
+ * containers don't count. Also the default pool size for `.any()`.
373
+ */
374
+ maxInstances?: number;
375
+ /**
376
+ * Rolling-deploy tuning. `stepPercentage` is the share of instances updated
377
+ * per rollout step (wrangler `rollout_step_percentage`); `gracePeriodSeconds`
378
+ * is how long an active instance is left running before it's eligible for
379
+ * update (wrangler `rollout_active_grace_period`).
380
+ */
381
+ rollout?: ContainerRollout;
382
+ /**
383
+ * Where the image and instance size are configured. `"default"` (the
384
+ * default) sets them once in wrangler; `"durable_object"` is the
385
+ * per-instance policy.
386
+ * Immutable once deployed: switching policies means a new container
387
+ * application, which replaces every running instance.
388
+ */
389
+ schedulingPolicy?: "default";
390
+ }
391
+ /**
392
+ * A container under the `durable_object` scheduling policy (Cloudflare public
393
+ * beta): each instance picks its image and size when it starts, via
394
+ * `ctx.containers.<name>.get(id).start({ image, instanceType })`, and can save
395
+ * and restore its filesystem with `snapshot()` / `start({ snapshot })`.
396
+ * Instances keep the image they started with across deploys — there is no
397
+ * application-wide rollout, and no `maxInstances` cap (running instances count
398
+ * against the account limit).
399
+ */
400
+ interface DurableObjectScheduledContainerConfig extends ContainerConfigBase {
401
+ /**
402
+ * The image a start uses when it names none — including the implicit start a
403
+ * `fetch`/`exec` triggers. Either a key of `images`
404
+ * or a Cloudflare-managed image such as `"cloudflare/debian-trixie"`.
405
+ * Without it, an instance must be started explicitly with an `image` (or a
406
+ * `snapshot`) before it can serve requests.
407
+ */
408
+ image?: string;
409
+ /**
410
+ * Named images an instance can start from, keyed by the name `start({ image })`
411
+ * takes (wrangler `containers[].images`). Up to 100, each name 1–128
412
+ * characters. Updating the map does not restart running instances.
413
+ */
414
+ images?: Readonly<Record<string, ContainerNamedImageSource>>;
415
+ /**
416
+ * The instance size a start uses when it names none. Cloudflare's runtime
417
+ * default is `"lite"`.
418
+ */
419
+ instanceType?: ContainerRuntimeInstanceType;
420
+ /** Selects the per-instance policy. Immutable once deployed. */
421
+ schedulingPolicy: "durable_object";
422
+ }
423
+ /**
424
+ * `ContainerConfig` is part of the experimental `@lunora/container` API and may change without a major version bump.
425
+ */
426
+ type ContainerConfig = DefaultScheduledContainerConfig | DurableObjectScheduledContainerConfig;
427
+ /**
428
+ * The value `defineContainer` returns: the validated config plus a brand the
429
+ * codegen discovery and the generated Container DO class key on.
430
+ */
431
+ type ContainerDefinition = ContainerConfig & {
432
+ /** Brand marking a value as a Lunora container definition. */
433
+ readonly isLunoraContainer: true;
434
+ };
435
+ /**
436
+ * A saved container filesystem, as `snapshot()` returns it. Plain data: store
437
+ * it anywhere and pass it back to `start({ snapshot })`, from the same or
438
+ * another instance of the same container. Tied to the image it was taken
439
+ * from; Cloudflare keeps it 30 days, refreshed on each restore.
440
+ */
441
+ interface ContainerSnapshot {
442
+ id: string;
443
+ name?: string;
444
+ /** Size in bytes. */
445
+ size: number;
446
+ }
447
+ /**
448
+ * A normalized image source, as written into `wrangler.jsonc`.
449
+ */
450
+ type NormalizedContainerImage = {
451
+ /** Build context directory (wrangler `image_build_context`). */
452
+ buildContext: string;
453
+ /** Path to the Dockerfile (wrangler `image`). */
454
+ dockerfilePath: string;
455
+ kind: "dockerfile";
456
+ } | {
457
+ /** Railpack source directory built + pushed at deploy time. */
458
+ buildDir: string;
459
+ kind: "build";
460
+ } | {
461
+ kind: "registry";
462
+ /** Fully-qualified image reference (wrangler `image`). */
463
+ reference: string;
464
+ };
465
+ /**
466
+ * Cloudflare Durable Object data-residency jurisdiction. Widening union —
467
+ * Cloudflare adds values over time.
468
+ * @see https://developers.cloudflare.com/durable-objects/reference/data-location/
469
+ */
470
+ type DurableObjectJurisdiction = "eu" | "fedramp" | "us";
471
+ export { BuildImageSource as B, ContainerDefinition as C, DurableObjectJurisdiction as D, NormalizedContainerImage as N, RegistryImageSource as R, SpawnResult as S, ContainerSpawnOptions as a, ContainerSnapshot as b, ContainerProcess as c, ContainerRuntimeInstanceType as d, ContainerSpawnRequest as e, DefaultScheduledContainerConfig as f, DurableObjectScheduledContainerConfig as g, ContainerImageSource as h, ContainerBackupStorage as i, ContainerConfig as j, ContainerConfigBase as k, ContainerInstanceType as l, ContainerNamedImageSource as m, ContainerReadinessCheck as n, ContainerRollout as o, CustomContainerInstanceType as p, NamedContainerInstanceType as q };