@lunora/container 1.0.0-alpha.10 → 1.0.0-alpha.12

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
@@ -10,6 +10,8 @@
10
10
 
11
11
  <!-- END_PACKAGE_OG_IMAGE_PLACEHOLDER -->
12
12
 
13
+ > **Experimental** — this package is outside the Lunora 1.0 stability promise: its API may change in any release, without a major version bump.
14
+
13
15
  <br />
14
16
 
15
17
  <div align="center">
package/dist/bridge.d.mts CHANGED
@@ -1,5 +1,8 @@
1
1
  import { LunoraError } from '@lunora/errors';
2
- /** A `fetch` implementation — defaults to the runtime global. */
2
+ /**
3
+ * A `fetch` implementation — defaults to the runtime global.
4
+ * @experimental
5
+ */
3
6
  type FetchLike = (input: string, init: {
4
7
  body: string;
5
8
  headers: Record<string, string>;
@@ -10,6 +13,10 @@ type FetchLike = (input: string, init: {
10
13
  status: number;
11
14
  statusText?: string;
12
15
  }>;
16
+ /**
17
+ * `ContainerBridgeOptions` is part of the experimental `@lunora/container` API and may change without a major version bump.
18
+ * @experimental
19
+ */
13
20
  interface ContainerBridgeOptions {
14
21
  /**
15
22
  * Base URL of the deployed Lunora Worker (no trailing `/_lunora/rpc`), e.g.
@@ -26,7 +33,10 @@ interface ContainerBridgeOptions {
26
33
  */
27
34
  token?: string;
28
35
  }
29
- /** Thrown when a Lunora function returns an error envelope. A `LunoraError` subclass carrying the wire `code`. */
36
+ /**
37
+ * Thrown when a Lunora function returns an error envelope. A `LunoraError` subclass carrying the wire `code`.
38
+ * @experimental
39
+ */
30
40
  declare class ContainerBridgeError extends LunoraError {
31
41
  constructor(code: string, message: string);
32
42
  }
@@ -36,6 +46,7 @@ declare class ContainerBridgeError extends LunoraError {
36
46
  * imported) so the bridge stays dependency-free and its `.d.ts` is
37
47
  * self-contained; the `__lunoraPhantom` shape matches, so a real `api.x.y`
38
48
  * reference is assignable and its arg/return types are inferable.
49
+ * @experimental
39
50
  */
40
51
  interface BridgeFunctionReference<Args = unknown, Result = unknown> {
41
52
  readonly __lunoraPhantom?: {
@@ -56,6 +67,10 @@ type ResultOfReference<Reference> = Reference extends {
56
67
  returns: infer Result;
57
68
  };
58
69
  } ? Result : never;
70
+ /**
71
+ * `ContainerBridge` is part of the experimental `@lunora/container` API and may change without a major version bump.
72
+ * @experimental
73
+ */
59
74
  interface ContainerBridge {
60
75
  /** Call an `action` by `namespace:fn` path. Alias of {@link ContainerBridge.call} for intent. */
61
76
  action: <Result = unknown>(functionPath: string, args?: Record<string, unknown>, shardKey?: string) => Promise<Result>;
@@ -85,6 +100,7 @@ interface ContainerBridge {
85
100
  * `query`/`mutation`/`action` are intent-revealing aliases of one `call` — the
86
101
  * wire is identical and the server dispatches by the function's registered
87
102
  * kind, so a query path called via `.mutation(...)` still runs as a query.
103
+ * @experimental
88
104
  */
89
105
  declare const createContainerBridge: (options: ContainerBridgeOptions) => ContainerBridge;
90
106
  export { type BridgeFunctionReference, type ContainerBridge, ContainerBridgeError, type ContainerBridgeOptions, type FetchLike, createContainerBridge };
package/dist/bridge.d.ts CHANGED
@@ -1,5 +1,8 @@
1
1
  import { LunoraError } from '@lunora/errors';
2
- /** A `fetch` implementation — defaults to the runtime global. */
2
+ /**
3
+ * A `fetch` implementation — defaults to the runtime global.
4
+ * @experimental
5
+ */
3
6
  type FetchLike = (input: string, init: {
4
7
  body: string;
5
8
  headers: Record<string, string>;
@@ -10,6 +13,10 @@ type FetchLike = (input: string, init: {
10
13
  status: number;
11
14
  statusText?: string;
12
15
  }>;
16
+ /**
17
+ * `ContainerBridgeOptions` is part of the experimental `@lunora/container` API and may change without a major version bump.
18
+ * @experimental
19
+ */
13
20
  interface ContainerBridgeOptions {
14
21
  /**
15
22
  * Base URL of the deployed Lunora Worker (no trailing `/_lunora/rpc`), e.g.
@@ -26,7 +33,10 @@ interface ContainerBridgeOptions {
26
33
  */
27
34
  token?: string;
28
35
  }
29
- /** Thrown when a Lunora function returns an error envelope. A `LunoraError` subclass carrying the wire `code`. */
36
+ /**
37
+ * Thrown when a Lunora function returns an error envelope. A `LunoraError` subclass carrying the wire `code`.
38
+ * @experimental
39
+ */
30
40
  declare class ContainerBridgeError extends LunoraError {
31
41
  constructor(code: string, message: string);
32
42
  }
@@ -36,6 +46,7 @@ declare class ContainerBridgeError extends LunoraError {
36
46
  * imported) so the bridge stays dependency-free and its `.d.ts` is
37
47
  * self-contained; the `__lunoraPhantom` shape matches, so a real `api.x.y`
38
48
  * reference is assignable and its arg/return types are inferable.
49
+ * @experimental
39
50
  */
40
51
  interface BridgeFunctionReference<Args = unknown, Result = unknown> {
41
52
  readonly __lunoraPhantom?: {
@@ -56,6 +67,10 @@ type ResultOfReference<Reference> = Reference extends {
56
67
  returns: infer Result;
57
68
  };
58
69
  } ? Result : never;
70
+ /**
71
+ * `ContainerBridge` is part of the experimental `@lunora/container` API and may change without a major version bump.
72
+ * @experimental
73
+ */
59
74
  interface ContainerBridge {
60
75
  /** Call an `action` by `namespace:fn` path. Alias of {@link ContainerBridge.call} for intent. */
61
76
  action: <Result = unknown>(functionPath: string, args?: Record<string, unknown>, shardKey?: string) => Promise<Result>;
@@ -85,6 +100,7 @@ interface ContainerBridge {
85
100
  * `query`/`mutation`/`action` are intent-revealing aliases of one `call` — the
86
101
  * wire is identical and the server dispatches by the function's registered
87
102
  * kind, so a query path called via `.mutation(...)` still runs as a query.
103
+ * @experimental
88
104
  */
89
105
  declare const createContainerBridge: (options: ContainerBridgeOptions) => ContainerBridge;
90
106
  export { type BridgeFunctionReference, type ContainerBridge, ContainerBridgeError, type ContainerBridgeOptions, type FetchLike, createContainerBridge };
@@ -1,5 +1,5 @@
1
1
  import { DurableObject, WorkerEntrypoint } from 'cloudflare:workers';
2
- import { a as ContainerDefinition, D as DurableObjectJurisdiction } from "../packem_shared/jurisdiction.d-TwTGkgTg.mjs";
2
+ import { a as ContainerDefinition, D as DurableObjectJurisdiction } from "../packem_shared/jurisdiction.d-CdUpqfc-.mjs";
3
3
  /**
4
4
  * ContainerStartOptions as they come from worker types
5
5
  */
@@ -500,6 +500,7 @@ type DurableObjectContext = ConstructorParameters<typeof Container>[0];
500
500
  * }
501
501
  * }
502
502
  * ```
503
+ * @experimental
503
504
  */
504
505
  declare class LunoraContainer<Env = unknown> extends Container<Env> {
505
506
  /**
@@ -1,5 +1,5 @@
1
1
  import { DurableObject, WorkerEntrypoint } from 'cloudflare:workers';
2
- import { a as ContainerDefinition, D as DurableObjectJurisdiction } from "../packem_shared/jurisdiction.d-TwTGkgTg.js";
2
+ import { a as ContainerDefinition, D as DurableObjectJurisdiction } from "../packem_shared/jurisdiction.d-CdUpqfc-.js";
3
3
  /**
4
4
  * ContainerStartOptions as they come from worker types
5
5
  */
@@ -500,6 +500,7 @@ type DurableObjectContext = ConstructorParameters<typeof Container>[0];
500
500
  * }
501
501
  * }
502
502
  * ```
503
+ * @experimental
503
504
  */
504
505
  declare class LunoraContainer<Env = unknown> extends Container<Env> {
505
506
  /**
package/dist/index.d.mts CHANGED
@@ -1,6 +1,9 @@
1
- import { D as DurableObjectJurisdiction, C as ContainerConfig, a as ContainerDefinition, b as ContainerImageSource, N as NormalizedContainerImage } from "./packem_shared/jurisdiction.d-TwTGkgTg.mjs";
2
- export type { B as BuildImageSource, c as ContainerInstanceType, d as ContainerReadinessCheck, e as ContainerRollout, f as CustomContainerInstanceType, g as NamedContainerInstanceType, R as RegistryImageSource } from "./packem_shared/jurisdiction.d-TwTGkgTg.mjs";
3
- /** Options for explicitly starting an instance (mirrors `@cloudflare/containers`). */
1
+ import { D as DurableObjectJurisdiction, C as ContainerConfig, a as ContainerDefinition, b as ContainerImageSource, N as NormalizedContainerImage } from "./packem_shared/jurisdiction.d-CdUpqfc-.mjs";
2
+ export type { B as BuildImageSource, c as ContainerInstanceType, d as ContainerReadinessCheck, e as ContainerRollout, f as CustomContainerInstanceType, g as NamedContainerInstanceType, R as RegistryImageSource } from "./packem_shared/jurisdiction.d-CdUpqfc-.mjs";
3
+ /**
4
+ * Options for explicitly starting an instance (mirrors `@cloudflare/containers`).
5
+ * @experimental
6
+ */
4
7
  interface ContainerStartOptions {
5
8
  /** Override outbound internet access for this start. */
6
9
  enableInternet?: boolean;
@@ -11,7 +14,10 @@ interface ContainerStartOptions {
11
14
  /** Metadata labels attached for metrics/observability. */
12
15
  labels?: Record<string, string>;
13
16
  }
14
- /** A container instance's runtime state, as returned by `getState()`. Structural — the platform adds fields over time. */
17
+ /**
18
+ * A container instance's runtime state, as returned by `getState()`. Structural — the platform adds fields over time.
19
+ * @experimental
20
+ */
15
21
  interface ContainerInstanceState {
16
22
  [key: string]: unknown;
17
23
  /** Process exit code, present once the instance has `stopped_with_code`. */
@@ -36,7 +42,10 @@ interface ContainerStubLike {
36
42
  start?: (options?: ContainerStartOptions) => Promise<void>;
37
43
  stop?: (signal?: number | string) => Promise<void>;
38
44
  }
39
- /** What the client needs from a Durable Object namespace binding. */
45
+ /**
46
+ * What the client needs from a Durable Object namespace binding.
47
+ * @experimental
48
+ */
40
49
  interface ContainerNamespaceLike {
41
50
  get: (id: unknown) => ContainerStubLike;
42
51
  idFromName: (name: string) => unknown;
@@ -46,7 +55,10 @@ interface ContainerNamespaceLike {
46
55
  */
47
56
  jurisdiction?: (jurisdiction: DurableObjectJurisdiction) => ContainerNamespaceLike;
48
57
  }
49
- /** A handle on one container instance (one Durable Object). */
58
+ /**
59
+ * A handle on one container instance (one Durable Object).
60
+ * @experimental
61
+ */
50
62
  interface ContainerHandle {
51
63
  /**
52
64
  * Send an HTTP (or WebSocket-upgrade) request to the container. A path
@@ -70,6 +82,7 @@ interface ContainerHandle {
70
82
  * game, a job runner per id) often needs to tear down or inspect the instance
71
83
  * rather than wait for `sleepAfter`, so these wrap the container DO's
72
84
  * `start`/`stop`/`destroy`/`getState`.
85
+ * @experimental
73
86
  */
74
87
  interface ContainerInstanceHandle extends ContainerHandle {
75
88
  /** Stop and discard the instance (its ephemeral disk is lost). */
@@ -104,6 +117,7 @@ interface ContainerInstanceHandle extends ContainerHandle {
104
117
  * Each maps to the corresponding `@cloudflare/containers` `Container` RPC, so
105
118
  * an app can tighten or relax a single instance's allowed/denied hosts after
106
119
  * start without redeploying.
120
+ * @experimental
107
121
  */
108
122
  interface ContainerEgressControls {
109
123
  /** Add one hostname (or glob) to the allow-list. */
@@ -119,7 +133,10 @@ interface ContainerEgressControls {
119
133
  /** Replace the entire deny-list. */
120
134
  setDenied: (hosts: ReadonlyArray<string>) => Promise<void>;
121
135
  }
122
- /** The per-definition accessor exposed as `ctx.containers.&lt;exportName>`. */
136
+ /**
137
+ * The per-definition accessor exposed as `ctx.containers.&lt;exportName>`.
138
+ * @experimental
139
+ */
123
140
  interface ContainerAccessor {
124
141
  /**
125
142
  * A random instance from a fixed pool of `count` (defaults to the
@@ -159,7 +176,10 @@ interface ContainerAccessor {
159
176
  */
160
177
  pool: (options?: PoolOptions) => ContainerHandle;
161
178
  }
162
- /** Tuning for a pooled, retrying container handle. See {@link ContainerAccessor.pool}. */
179
+ /**
180
+ * Tuning for a pooled, retrying container handle. See {@link ContainerAccessor.pool}.
181
+ * @experimental
182
+ */
163
183
  interface PoolOptions {
164
184
  /** Total attempts before giving up (each on a freshly-picked instance). Default 3. */
165
185
  attempts?: number;
@@ -185,6 +205,7 @@ interface PoolOptions {
185
205
  * only on the platform's provisioning transients (no-instance / not-listening /
186
206
  * rate-limited — see {@link isColdStartTransient}), which is why it's safe by
187
207
  * default: those responses mean the request never reached the container.
208
+ * @experimental
188
209
  */
189
210
  interface InstanceRetryOptions {
190
211
  /**
@@ -198,7 +219,10 @@ interface InstanceRetryOptions {
198
219
  /** Upper bound on a single backoff sleep, in ms. Default {@link DEFAULT_MAX_BACKOFF_MS} (30s). */
199
220
  maxBackoffMs?: number;
200
221
  }
201
- /** Wiring info for one definition, emitted by codegen into the generated DO. */
222
+ /**
223
+ * Wiring info for one definition, emitted by codegen into the generated DO.
224
+ * @experimental
225
+ */
202
226
  interface ContainerBindingSpec {
203
227
  /** Durable Object binding name, e.g. `CONTAINER_TRANSCODER`. */
204
228
  binding: string;
@@ -209,13 +233,20 @@ interface ContainerBindingSpec {
209
233
  }
210
234
  /**
211
235
  * Build the `ctx.containers` record from the Worker `env`. Called by the
212
- * generated ShardDO with the specs codegen derived from
213
- * `lunora/containers.ts`. A missing binding doesn't throw here — only when the
214
- * handle is actually used — so one unprovisioned container never breaks
215
- * unrelated functions.
236
+ * generated ShardDO with the specs codegen derived from `lunora/containers.ts`.
237
+ * A missing binding doesn't throw here — only when the handle is actually used —
238
+ * so one unprovisioned container never breaks unrelated functions.
239
+ *
240
+ * `traceparent` (the inbound RPC's W3C trace context, forwarded by the runtime
241
+ * and read off the request by the DO) is stamped onto every outbound container
242
+ * `fetch`, so the container's own spans stitch under the Worker's trace.
243
+ * @experimental
244
+ */
245
+ declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>, jurisdiction?: DurableObjectJurisdiction, traceparent?: string) => Record<string, ContainerAccessor>;
246
+ /**
247
+ * A test handler: receives the request plus the targeted instance name.
248
+ * @experimental
216
249
  */
217
- declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>, jurisdiction?: DurableObjectJurisdiction) => Record<string, ContainerAccessor>;
218
- /** A test handler: receives the request plus the targeted instance name. */
219
250
  type ContainerTestHandler = (request: Request, instance: {
220
251
  name: string;
221
252
  }) => Promise<Response> | Response;
@@ -229,6 +260,7 @@ type ContainerTestHandler = (request: Request, instance: {
229
260
  * transcoder: (request) => new Response("ok"),
230
261
  * });
231
262
  * ```
263
+ * @experimental
232
264
  */
233
265
  declare const createContainerTestContext: (handlers: Record<string, ContainerTestHandler>) => Record<string, ContainerAccessor>;
234
266
  /**
@@ -240,6 +272,7 @@ declare const createContainerTestContext: (handlers: Record<string, ContainerTes
240
272
  * `Dockerfile.dev` also counts) is used as-is with its directory as the build
241
273
  * context; any other path is treated as the build-context directory and the
242
274
  * Dockerfile is expected at `&lt;dir>/Dockerfile`.
275
+ * @experimental
243
276
  */
244
277
  declare const normalizeContainerImage: (image: ContainerImageSource) => NormalizedContainerImage;
245
278
  /**
@@ -247,6 +280,7 @@ declare const normalizeContainerImage: (image: ContainerImageSource) => Normaliz
247
280
  * `transcoder` → `TranscoderContainer`. wrangler's `containers[].class_name`
248
281
  * and the Durable Object binding's `class_name` both reference it, so codegen
249
282
  * and the config layer MUST derive it identically — always via this helper.
283
+ * @experimental
250
284
  */
251
285
  declare const containerClassName: (exportName: string) => string;
252
286
  /**
@@ -254,6 +288,7 @@ declare const containerClassName: (exportName: string) => string;
254
288
  * `CONTAINER_TRANSCODER`, `imageResizer` → `CONTAINER_IMAGE_RESIZER`. The
255
289
  * `CONTAINER_` prefix namespaces these away from `SHARD`/`SESSION`/`SCHEDULER`
256
290
  * so a container export can never collide with the built-in bindings.
291
+ * @experimental
257
292
  */
258
293
  declare const containerBindingName: (exportName: string) => string;
259
294
  /**
@@ -262,16 +297,25 @@ declare const containerBindingName: (exportName: string) => string;
262
297
  * it as the wrangler `containers[].image`, and `lunora deploy` builds that tag
263
298
  * with Railpack and `wrangler containers push`es it before deploying — so all
264
299
  * three derive the tag from this one helper and can never disagree.
300
+ * @experimental
265
301
  */
266
302
  declare const containerBuildTag: (exportName: string) => string;
303
+ /**
304
+ * `defineContainer` is part of the experimental `@lunora/container` API and may change without a major version bump.
305
+ * @experimental
306
+ */
267
307
  declare const defineContainer: (config: ContainerConfig) => ContainerDefinition;
268
- /** True when a value is a `defineContainer` result (the runtime brand check). */
308
+ /**
309
+ * True when a value is a `defineContainer` result (the runtime brand check).
310
+ * @experimental
311
+ */
269
312
  declare const isContainerDefinition: (value: unknown) => value is ContainerDefinition;
270
313
  /**
271
314
  * The container's full environment at instance start: the static `env` block
272
315
  * plus every declared secret resolved from the Worker `env`. A declared secret
273
316
  * missing from the Worker env fails fast — starting the container without a
274
317
  * credential it was promised yields far worse errors downstream.
318
+ * @experimental
275
319
  */
276
320
  declare const resolveContainerEnvVariables: (definition: ContainerDefinition, workerEnv: Record<string, unknown>, exportName?: string) => Record<string, string>;
277
321
  export { type ContainerAccessor, type ContainerBindingSpec, type ContainerConfig, type ContainerDefinition, type ContainerEgressControls, type ContainerHandle, type ContainerImageSource, type ContainerInstanceHandle, type ContainerInstanceState, type ContainerNamespaceLike, type ContainerStartOptions, type ContainerTestHandler, type DurableObjectJurisdiction, type InstanceRetryOptions, type NormalizedContainerImage, type PoolOptions, containerBindingName, containerBuildTag, containerClassName, createContainerContext, createContainerTestContext, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVariables as resolveContainerEnvVars };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,9 @@
1
- import { D as DurableObjectJurisdiction, C as ContainerConfig, a as ContainerDefinition, b as ContainerImageSource, N as NormalizedContainerImage } from "./packem_shared/jurisdiction.d-TwTGkgTg.js";
2
- export type { B as BuildImageSource, c as ContainerInstanceType, d as ContainerReadinessCheck, e as ContainerRollout, f as CustomContainerInstanceType, g as NamedContainerInstanceType, R as RegistryImageSource } from "./packem_shared/jurisdiction.d-TwTGkgTg.js";
3
- /** Options for explicitly starting an instance (mirrors `@cloudflare/containers`). */
1
+ import { D as DurableObjectJurisdiction, C as ContainerConfig, a as ContainerDefinition, b as ContainerImageSource, N as NormalizedContainerImage } from "./packem_shared/jurisdiction.d-CdUpqfc-.js";
2
+ export type { B as BuildImageSource, c as ContainerInstanceType, d as ContainerReadinessCheck, e as ContainerRollout, f as CustomContainerInstanceType, g as NamedContainerInstanceType, R as RegistryImageSource } from "./packem_shared/jurisdiction.d-CdUpqfc-.js";
3
+ /**
4
+ * Options for explicitly starting an instance (mirrors `@cloudflare/containers`).
5
+ * @experimental
6
+ */
4
7
  interface ContainerStartOptions {
5
8
  /** Override outbound internet access for this start. */
6
9
  enableInternet?: boolean;
@@ -11,7 +14,10 @@ interface ContainerStartOptions {
11
14
  /** Metadata labels attached for metrics/observability. */
12
15
  labels?: Record<string, string>;
13
16
  }
14
- /** A container instance's runtime state, as returned by `getState()`. Structural — the platform adds fields over time. */
17
+ /**
18
+ * A container instance's runtime state, as returned by `getState()`. Structural — the platform adds fields over time.
19
+ * @experimental
20
+ */
15
21
  interface ContainerInstanceState {
16
22
  [key: string]: unknown;
17
23
  /** Process exit code, present once the instance has `stopped_with_code`. */
@@ -36,7 +42,10 @@ interface ContainerStubLike {
36
42
  start?: (options?: ContainerStartOptions) => Promise<void>;
37
43
  stop?: (signal?: number | string) => Promise<void>;
38
44
  }
39
- /** What the client needs from a Durable Object namespace binding. */
45
+ /**
46
+ * What the client needs from a Durable Object namespace binding.
47
+ * @experimental
48
+ */
40
49
  interface ContainerNamespaceLike {
41
50
  get: (id: unknown) => ContainerStubLike;
42
51
  idFromName: (name: string) => unknown;
@@ -46,7 +55,10 @@ interface ContainerNamespaceLike {
46
55
  */
47
56
  jurisdiction?: (jurisdiction: DurableObjectJurisdiction) => ContainerNamespaceLike;
48
57
  }
49
- /** A handle on one container instance (one Durable Object). */
58
+ /**
59
+ * A handle on one container instance (one Durable Object).
60
+ * @experimental
61
+ */
50
62
  interface ContainerHandle {
51
63
  /**
52
64
  * Send an HTTP (or WebSocket-upgrade) request to the container. A path
@@ -70,6 +82,7 @@ interface ContainerHandle {
70
82
  * game, a job runner per id) often needs to tear down or inspect the instance
71
83
  * rather than wait for `sleepAfter`, so these wrap the container DO's
72
84
  * `start`/`stop`/`destroy`/`getState`.
85
+ * @experimental
73
86
  */
74
87
  interface ContainerInstanceHandle extends ContainerHandle {
75
88
  /** Stop and discard the instance (its ephemeral disk is lost). */
@@ -104,6 +117,7 @@ interface ContainerInstanceHandle extends ContainerHandle {
104
117
  * Each maps to the corresponding `@cloudflare/containers` `Container` RPC, so
105
118
  * an app can tighten or relax a single instance's allowed/denied hosts after
106
119
  * start without redeploying.
120
+ * @experimental
107
121
  */
108
122
  interface ContainerEgressControls {
109
123
  /** Add one hostname (or glob) to the allow-list. */
@@ -119,7 +133,10 @@ interface ContainerEgressControls {
119
133
  /** Replace the entire deny-list. */
120
134
  setDenied: (hosts: ReadonlyArray<string>) => Promise<void>;
121
135
  }
122
- /** The per-definition accessor exposed as `ctx.containers.&lt;exportName>`. */
136
+ /**
137
+ * The per-definition accessor exposed as `ctx.containers.&lt;exportName>`.
138
+ * @experimental
139
+ */
123
140
  interface ContainerAccessor {
124
141
  /**
125
142
  * A random instance from a fixed pool of `count` (defaults to the
@@ -159,7 +176,10 @@ interface ContainerAccessor {
159
176
  */
160
177
  pool: (options?: PoolOptions) => ContainerHandle;
161
178
  }
162
- /** Tuning for a pooled, retrying container handle. See {@link ContainerAccessor.pool}. */
179
+ /**
180
+ * Tuning for a pooled, retrying container handle. See {@link ContainerAccessor.pool}.
181
+ * @experimental
182
+ */
163
183
  interface PoolOptions {
164
184
  /** Total attempts before giving up (each on a freshly-picked instance). Default 3. */
165
185
  attempts?: number;
@@ -185,6 +205,7 @@ interface PoolOptions {
185
205
  * only on the platform's provisioning transients (no-instance / not-listening /
186
206
  * rate-limited — see {@link isColdStartTransient}), which is why it's safe by
187
207
  * default: those responses mean the request never reached the container.
208
+ * @experimental
188
209
  */
189
210
  interface InstanceRetryOptions {
190
211
  /**
@@ -198,7 +219,10 @@ interface InstanceRetryOptions {
198
219
  /** Upper bound on a single backoff sleep, in ms. Default {@link DEFAULT_MAX_BACKOFF_MS} (30s). */
199
220
  maxBackoffMs?: number;
200
221
  }
201
- /** Wiring info for one definition, emitted by codegen into the generated DO. */
222
+ /**
223
+ * Wiring info for one definition, emitted by codegen into the generated DO.
224
+ * @experimental
225
+ */
202
226
  interface ContainerBindingSpec {
203
227
  /** Durable Object binding name, e.g. `CONTAINER_TRANSCODER`. */
204
228
  binding: string;
@@ -209,13 +233,20 @@ interface ContainerBindingSpec {
209
233
  }
210
234
  /**
211
235
  * Build the `ctx.containers` record from the Worker `env`. Called by the
212
- * generated ShardDO with the specs codegen derived from
213
- * `lunora/containers.ts`. A missing binding doesn't throw here — only when the
214
- * handle is actually used — so one unprovisioned container never breaks
215
- * unrelated functions.
236
+ * generated ShardDO with the specs codegen derived from `lunora/containers.ts`.
237
+ * A missing binding doesn't throw here — only when the handle is actually used —
238
+ * so one unprovisioned container never breaks unrelated functions.
239
+ *
240
+ * `traceparent` (the inbound RPC's W3C trace context, forwarded by the runtime
241
+ * and read off the request by the DO) is stamped onto every outbound container
242
+ * `fetch`, so the container's own spans stitch under the Worker's trace.
243
+ * @experimental
244
+ */
245
+ declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>, jurisdiction?: DurableObjectJurisdiction, traceparent?: string) => Record<string, ContainerAccessor>;
246
+ /**
247
+ * A test handler: receives the request plus the targeted instance name.
248
+ * @experimental
216
249
  */
217
- declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>, jurisdiction?: DurableObjectJurisdiction) => Record<string, ContainerAccessor>;
218
- /** A test handler: receives the request plus the targeted instance name. */
219
250
  type ContainerTestHandler = (request: Request, instance: {
220
251
  name: string;
221
252
  }) => Promise<Response> | Response;
@@ -229,6 +260,7 @@ type ContainerTestHandler = (request: Request, instance: {
229
260
  * transcoder: (request) => new Response("ok"),
230
261
  * });
231
262
  * ```
263
+ * @experimental
232
264
  */
233
265
  declare const createContainerTestContext: (handlers: Record<string, ContainerTestHandler>) => Record<string, ContainerAccessor>;
234
266
  /**
@@ -240,6 +272,7 @@ declare const createContainerTestContext: (handlers: Record<string, ContainerTes
240
272
  * `Dockerfile.dev` also counts) is used as-is with its directory as the build
241
273
  * context; any other path is treated as the build-context directory and the
242
274
  * Dockerfile is expected at `&lt;dir>/Dockerfile`.
275
+ * @experimental
243
276
  */
244
277
  declare const normalizeContainerImage: (image: ContainerImageSource) => NormalizedContainerImage;
245
278
  /**
@@ -247,6 +280,7 @@ declare const normalizeContainerImage: (image: ContainerImageSource) => Normaliz
247
280
  * `transcoder` → `TranscoderContainer`. wrangler's `containers[].class_name`
248
281
  * and the Durable Object binding's `class_name` both reference it, so codegen
249
282
  * and the config layer MUST derive it identically — always via this helper.
283
+ * @experimental
250
284
  */
251
285
  declare const containerClassName: (exportName: string) => string;
252
286
  /**
@@ -254,6 +288,7 @@ declare const containerClassName: (exportName: string) => string;
254
288
  * `CONTAINER_TRANSCODER`, `imageResizer` → `CONTAINER_IMAGE_RESIZER`. The
255
289
  * `CONTAINER_` prefix namespaces these away from `SHARD`/`SESSION`/`SCHEDULER`
256
290
  * so a container export can never collide with the built-in bindings.
291
+ * @experimental
257
292
  */
258
293
  declare const containerBindingName: (exportName: string) => string;
259
294
  /**
@@ -262,16 +297,25 @@ declare const containerBindingName: (exportName: string) => string;
262
297
  * it as the wrangler `containers[].image`, and `lunora deploy` builds that tag
263
298
  * with Railpack and `wrangler containers push`es it before deploying — so all
264
299
  * three derive the tag from this one helper and can never disagree.
300
+ * @experimental
265
301
  */
266
302
  declare const containerBuildTag: (exportName: string) => string;
303
+ /**
304
+ * `defineContainer` is part of the experimental `@lunora/container` API and may change without a major version bump.
305
+ * @experimental
306
+ */
267
307
  declare const defineContainer: (config: ContainerConfig) => ContainerDefinition;
268
- /** True when a value is a `defineContainer` result (the runtime brand check). */
308
+ /**
309
+ * True when a value is a `defineContainer` result (the runtime brand check).
310
+ * @experimental
311
+ */
269
312
  declare const isContainerDefinition: (value: unknown) => value is ContainerDefinition;
270
313
  /**
271
314
  * The container's full environment at instance start: the static `env` block
272
315
  * plus every declared secret resolved from the Worker `env`. A declared secret
273
316
  * missing from the Worker env fails fast — starting the container without a
274
317
  * credential it was promised yields far worse errors downstream.
318
+ * @experimental
275
319
  */
276
320
  declare const resolveContainerEnvVariables: (definition: ContainerDefinition, workerEnv: Record<string, unknown>, exportName?: string) => Record<string, string>;
277
321
  export { type ContainerAccessor, type ContainerBindingSpec, type ContainerConfig, type ContainerDefinition, type ContainerEgressControls, type ContainerHandle, type ContainerImageSource, type ContainerInstanceHandle, type ContainerInstanceState, type ContainerNamespaceLike, type ContainerStartOptions, type ContainerTestHandler, type DurableObjectJurisdiction, type InstanceRetryOptions, type NormalizedContainerImage, type PoolOptions, containerBindingName, containerBuildTag, containerClassName, createContainerContext, createContainerTestContext, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVariables as resolveContainerEnvVars };
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- export { createContainerContext, createContainerTestContext } from './packem_shared/createContainerContext-CIVzsY5m.mjs';
1
+ export { createContainerContext, createContainerTestContext } from './packem_shared/createContainerContext-Cg53QGdf.mjs';
2
2
  export { containerBindingName, containerBuildTag, containerClassName, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVars } from './packem_shared/containerBindingName-BiTrAF1J.mjs';
package/dist/otel.d.mts CHANGED
@@ -1,4 +1,7 @@
1
- /** An attribute value carried on a span or log. */
1
+ /**
2
+ * An attribute value carried on a span or log.
3
+ * @experimental
4
+ */
2
5
  type ContainerAttributeValue = boolean | number | string;
3
6
  /**
4
7
  * A `fetch` implementation — defaults to the runtime global. The exporter passes
@@ -6,6 +9,7 @@ type ContainerAttributeValue = boolean | number | string;
6
9
  * cancels the response `body` so Node/undici can release the socket for
7
10
  * keep-alive reuse instead of leaving it occupied by an unread stream. It reads
8
11
  * `ok`/`status` to detect a rejected export and nothing else from the response.
12
+ * @experimental
9
13
  */
10
14
  type OtelFetchLike = (input: string, init: {
11
15
  body: string;
@@ -19,7 +23,10 @@ type OtelFetchLike = (input: string, init: {
19
23
  ok: boolean;
20
24
  status: number;
21
25
  }>;
22
- /** A single span the container process asks the exporter to record. */
26
+ /**
27
+ * A single span the container process asks the exporter to record.
28
+ * @experimental
29
+ */
23
30
  interface ContainerSpanInput {
24
31
  /** Attributes attached to the span (rendered under the OTLP `attributes` list). */
25
32
  attributes?: Record<string, ContainerAttributeValue>;
@@ -35,7 +42,10 @@ interface ContainerSpanInput {
35
42
  /** Wall-clock millis when the operation started. */
36
43
  startMs: number;
37
44
  }
38
- /** A single log line the container process asks the exporter to record. */
45
+ /**
46
+ * A single log line the container process asks the exporter to record.
47
+ * @experimental
48
+ */
39
49
  interface ContainerLogInput {
40
50
  /** Attributes attached to the log record. */
41
51
  attributes?: Record<string, ContainerAttributeValue>;
@@ -46,7 +56,10 @@ interface ContainerLogInput {
46
56
  /** Wall-clock millis the line was emitted; defaults to now. */
47
57
  ts?: number;
48
58
  }
49
- /** Options for {@link createContainerTelemetry}. */
59
+ /**
60
+ * Options for {@link createContainerTelemetry}.
61
+ * @experimental
62
+ */
50
63
  interface ContainerTelemetryOptions {
51
64
  /** Base OTLP collector endpoint; defaults to the `LUNORA_OTLP_ENDPOINT` env var. */
52
65
  endpoint?: string;
@@ -62,8 +75,34 @@ interface ContainerTelemetryOptions {
62
75
  timeoutMs?: number;
63
76
  /** Bearer token sent as an `Authorization: Bearer` header; defaults to the `LUNORA_OTLP_TOKEN` env var. */
64
77
  token?: string;
78
+ /**
79
+ * W3C `traceparent` of the Worker RPC that invoked this container; defaults to
80
+ * the `LUNORA_TRACEPARENT` env var. When present (and well-formed) every span
81
+ * inherits its trace id and hangs off its span id, so container spans stitch
82
+ * under the Worker's trace instead of forming a fresh, disconnected trace.
83
+ *
84
+ * `@lunora/container` stamps this trace context as the **`traceparent` request
85
+ * header** on every proxied fetch (`ctx.containers.&lt;name>.…`), so a container
86
+ * that serves many requests should read it per request and create a telemetry
87
+ * instance scoped to that request — the trace context differs each call, so a
88
+ * single process-lifetime instance can't carry it:
89
+ *
90
+ * ```ts
91
+ * // inside the container's request handler
92
+ * const telemetry = createContainerTelemetry({ traceparent: request.headers.get("traceparent") ?? undefined });
93
+ * await telemetry.trace("transcode", () => transcode(job));
94
+ * await telemetry.flush();
95
+ * ```
96
+ *
97
+ * The `LUNORA_TRACEPARENT` env fallback fits a one-shot container that
98
+ * processes a single job per start (the value is fixed for the process).
99
+ */
100
+ traceparent?: string;
65
101
  }
66
- /** The exporter handle {@link createContainerTelemetry} returns. */
102
+ /**
103
+ * The exporter handle {@link createContainerTelemetry} returns.
104
+ * @experimental
105
+ */
67
106
  interface ContainerTelemetry {
68
107
  /** Record one log line (no-op when disabled). */
69
108
  emitLog: (log: ContainerLogInput) => void;
@@ -90,6 +129,7 @@ interface ContainerTelemetry {
90
129
  * false`): `emitSpan`/`emitLog` no-op and `trace` still runs its work but records
91
130
  * nothing — so the same code runs unchanged locally and in the cloud.
92
131
  * @param options Exporter options; every field falls back to a `LUNORA_*` env var.
132
+ * @experimental
93
133
  */
94
134
  declare const createContainerTelemetry: (options?: ContainerTelemetryOptions) => ContainerTelemetry;
95
135
  export { type ContainerAttributeValue, type ContainerLogInput, type ContainerSpanInput, type ContainerTelemetry, type ContainerTelemetryOptions, type OtelFetchLike, createContainerTelemetry };
package/dist/otel.d.ts CHANGED
@@ -1,4 +1,7 @@
1
- /** An attribute value carried on a span or log. */
1
+ /**
2
+ * An attribute value carried on a span or log.
3
+ * @experimental
4
+ */
2
5
  type ContainerAttributeValue = boolean | number | string;
3
6
  /**
4
7
  * A `fetch` implementation — defaults to the runtime global. The exporter passes
@@ -6,6 +9,7 @@ type ContainerAttributeValue = boolean | number | string;
6
9
  * cancels the response `body` so Node/undici can release the socket for
7
10
  * keep-alive reuse instead of leaving it occupied by an unread stream. It reads
8
11
  * `ok`/`status` to detect a rejected export and nothing else from the response.
12
+ * @experimental
9
13
  */
10
14
  type OtelFetchLike = (input: string, init: {
11
15
  body: string;
@@ -19,7 +23,10 @@ type OtelFetchLike = (input: string, init: {
19
23
  ok: boolean;
20
24
  status: number;
21
25
  }>;
22
- /** A single span the container process asks the exporter to record. */
26
+ /**
27
+ * A single span the container process asks the exporter to record.
28
+ * @experimental
29
+ */
23
30
  interface ContainerSpanInput {
24
31
  /** Attributes attached to the span (rendered under the OTLP `attributes` list). */
25
32
  attributes?: Record<string, ContainerAttributeValue>;
@@ -35,7 +42,10 @@ interface ContainerSpanInput {
35
42
  /** Wall-clock millis when the operation started. */
36
43
  startMs: number;
37
44
  }
38
- /** A single log line the container process asks the exporter to record. */
45
+ /**
46
+ * A single log line the container process asks the exporter to record.
47
+ * @experimental
48
+ */
39
49
  interface ContainerLogInput {
40
50
  /** Attributes attached to the log record. */
41
51
  attributes?: Record<string, ContainerAttributeValue>;
@@ -46,7 +56,10 @@ interface ContainerLogInput {
46
56
  /** Wall-clock millis the line was emitted; defaults to now. */
47
57
  ts?: number;
48
58
  }
49
- /** Options for {@link createContainerTelemetry}. */
59
+ /**
60
+ * Options for {@link createContainerTelemetry}.
61
+ * @experimental
62
+ */
50
63
  interface ContainerTelemetryOptions {
51
64
  /** Base OTLP collector endpoint; defaults to the `LUNORA_OTLP_ENDPOINT` env var. */
52
65
  endpoint?: string;
@@ -62,8 +75,34 @@ interface ContainerTelemetryOptions {
62
75
  timeoutMs?: number;
63
76
  /** Bearer token sent as an `Authorization: Bearer` header; defaults to the `LUNORA_OTLP_TOKEN` env var. */
64
77
  token?: string;
78
+ /**
79
+ * W3C `traceparent` of the Worker RPC that invoked this container; defaults to
80
+ * the `LUNORA_TRACEPARENT` env var. When present (and well-formed) every span
81
+ * inherits its trace id and hangs off its span id, so container spans stitch
82
+ * under the Worker's trace instead of forming a fresh, disconnected trace.
83
+ *
84
+ * `@lunora/container` stamps this trace context as the **`traceparent` request
85
+ * header** on every proxied fetch (`ctx.containers.&lt;name>.…`), so a container
86
+ * that serves many requests should read it per request and create a telemetry
87
+ * instance scoped to that request — the trace context differs each call, so a
88
+ * single process-lifetime instance can't carry it:
89
+ *
90
+ * ```ts
91
+ * // inside the container's request handler
92
+ * const telemetry = createContainerTelemetry({ traceparent: request.headers.get("traceparent") ?? undefined });
93
+ * await telemetry.trace("transcode", () => transcode(job));
94
+ * await telemetry.flush();
95
+ * ```
96
+ *
97
+ * The `LUNORA_TRACEPARENT` env fallback fits a one-shot container that
98
+ * processes a single job per start (the value is fixed for the process).
99
+ */
100
+ traceparent?: string;
65
101
  }
66
- /** The exporter handle {@link createContainerTelemetry} returns. */
102
+ /**
103
+ * The exporter handle {@link createContainerTelemetry} returns.
104
+ * @experimental
105
+ */
67
106
  interface ContainerTelemetry {
68
107
  /** Record one log line (no-op when disabled). */
69
108
  emitLog: (log: ContainerLogInput) => void;
@@ -90,6 +129,7 @@ interface ContainerTelemetry {
90
129
  * false`): `emitSpan`/`emitLog` no-op and `trace` still runs its work but records
91
130
  * nothing — so the same code runs unchanged locally and in the cloud.
92
131
  * @param options Exporter options; every field falls back to a `LUNORA_*` env var.
132
+ * @experimental
93
133
  */
94
134
  declare const createContainerTelemetry: (options?: ContainerTelemetryOptions) => ContainerTelemetry;
95
135
  export { type ContainerAttributeValue, type ContainerLogInput, type ContainerSpanInput, type ContainerTelemetry, type ContainerTelemetryOptions, type OtelFetchLike, createContainerTelemetry };
package/dist/otel.mjs CHANGED
@@ -20,6 +20,19 @@ const otlpRandomHex = (bytes) => {
20
20
  }
21
21
  return hex;
22
22
  };
23
+ const HEX_ONLY = /^[0-9a-f]+$/;
24
+ const parseTraceparent = (header) => {
25
+ if (header === null || header === void 0) {
26
+ return void 0;
27
+ }
28
+ const parts = header.trim().toLowerCase().split("-");
29
+ const [version, traceId, parentSpanId, flags] = parts;
30
+ if (parts.length < 4 || version === void 0 || version.length !== 2 || !HEX_ONLY.test(version) || version === "ff" || // Version 00 forbids trailing fields; only a future version may carry them.
31
+ version === "00" && parts.length !== 4 || traceId === void 0 || parentSpanId === void 0 || flags === void 0 || flags.length !== 2 || !HEX_ONLY.test(flags) || traceId.length !== 32 || parentSpanId.length !== 16 || !HEX_ONLY.test(traceId) || !HEX_ONLY.test(parentSpanId) || traceId === "00000000000000000000000000000000" || parentSpanId === "0000000000000000") {
32
+ return void 0;
33
+ }
34
+ return { parentSpanId, traceId };
35
+ };
23
36
  const encodeAttribute = (key, value) => {
24
37
  if (typeof value === "boolean") {
25
38
  return { key, value: { boolValue: value } };
@@ -96,7 +109,7 @@ const resolveFetch = (injected) => {
96
109
  }
97
110
  return void 0;
98
111
  };
99
- const traceBody = (span, serviceName) => {
112
+ const traceBody = (span, serviceName, parent) => {
100
113
  const attributes = encodeAttributes(span.attributes);
101
114
  if (span.error?.type !== void 0) {
102
115
  attributes.push(encodeAttribute("error.type", span.error.type));
@@ -107,11 +120,14 @@ const traceBody = (span, serviceName) => {
107
120
  // SPAN_KIND_INTERNAL — the container's own work, not a server/client edge.
108
121
  kind: 1,
109
122
  name: span.name,
123
+ // Always the span's own (child) id; with a parent, hang it off the parent
124
+ // span and inherit the parent's trace id so the spans stitch into one trace.
125
+ ...parent === void 0 ? {} : { parentSpanId: parent.parentSpanId },
110
126
  spanId: otlpRandomHex(8),
111
127
  startTimeUnixNano: otlpUnixNano(span.startMs),
112
128
  // STATUS_CODE_OK (1) / STATUS_CODE_ERROR (2).
113
129
  status: span.error === void 0 ? { code: 1 } : { code: 2, message: span.error.message },
114
- traceId: otlpRandomHex(16)
130
+ traceId: parent?.traceId ?? otlpRandomHex(16)
115
131
  };
116
132
  return wrapResourceSpans(otlpSpan, "@lunora/container", serviceName);
117
133
  };
@@ -131,6 +147,7 @@ const createContainerTelemetry = (options = {}) => {
131
147
  const enabled = endpoint !== void 0 && endpoint.length > 0;
132
148
  const token = options.token ?? readEnv("LUNORA_OTLP_TOKEN");
133
149
  const serviceName = options.serviceName ?? readEnv("LUNORA_SERVICE_NAME") ?? "lunora-container";
150
+ const parent = parseTraceparent(options.traceparent ?? readEnv("LUNORA_TRACEPARENT"));
134
151
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
135
152
  const fetchImpl = resolveFetch(options.fetch);
136
153
  const headers = mergeHeaders({ "content-type": "application/json" }, options.headers, token);
@@ -169,7 +186,7 @@ const createContainerTelemetry = (options = {}) => {
169
186
  if (!enabled) {
170
187
  return;
171
188
  }
172
- send(tracesUrl, traceBody(span, serviceName));
189
+ send(tracesUrl, traceBody(span, serviceName, parent));
173
190
  };
174
191
  const emitLog = (log) => {
175
192
  if (!enabled) {
@@ -7,11 +7,14 @@ const DEFAULT_MAX_BACKOFF_MS = 3e4;
7
7
  const DEFAULT_COLD_START_ATTEMPTS = 3;
8
8
  const DEFAULT_COLD_START_BACKOFF_MS = 500;
9
9
  const TARGET_PORT_HEADER = "cf-container-target-port";
10
- const toRequest = (input, init, port) => {
10
+ const toRequest = (input, init, port, traceparent) => {
11
11
  const request = typeof input === "string" && input.startsWith("/") ? new Request(`http://container${input}`, init) : new Request(input, init);
12
12
  if (port !== void 0) {
13
13
  request.headers.set(TARGET_PORT_HEADER, String(port));
14
14
  }
15
+ if (traceparent !== void 0) {
16
+ request.headers.set("traceparent", traceparent);
17
+ }
15
18
  return request;
16
19
  };
17
20
  const sleep = async (ms) => {
@@ -62,7 +65,7 @@ const isColdStartTransient = async (response) => {
62
65
  return false;
63
66
  }
64
67
  };
65
- const coldStartRetryingHandle = (send, options = {}, port) => {
68
+ const coldStartRetryingHandle = (send, options = {}, port, traceparent) => {
66
69
  const attempts = Math.max(1, options.attempts ?? DEFAULT_COLD_START_ATTEMPTS);
67
70
  const baseBackoff = options.backoffMs ?? DEFAULT_COLD_START_BACKOFF_MS;
68
71
  const maxBackoff = options.maxBackoffMs ?? DEFAULT_MAX_BACKOFF_MS;
@@ -76,7 +79,7 @@ const coldStartRetryingHandle = (send, options = {}, port) => {
76
79
  await sleep(Math.min(baseBackoff * 2 ** (attempt - 1), maxBackoff));
77
80
  }
78
81
  try {
79
- const response = await send(toRequest(input, init, port));
82
+ const response = await send(toRequest(input, init, port, traceparent));
80
83
  if (isLastAttempt || !await isColdStartTransient(response)) {
81
84
  return response;
82
85
  }
@@ -89,10 +92,10 @@ const coldStartRetryingHandle = (send, options = {}, port) => {
89
92
  }
90
93
  throw lastError instanceof Error ? lastError : new Error("ctx.containers: cold-start retry exhausted");
91
94
  },
92
- port: (targetPort) => coldStartRetryingHandle(send, options, targetPort)
95
+ port: (targetPort) => coldStartRetryingHandle(send, options, targetPort, traceparent)
93
96
  };
94
97
  };
95
- const handleFor = (namespace, instanceName, options) => coldStartRetryingHandle(async (request) => namespace.get(namespace.idFromName(instanceName)).fetch(request), options);
98
+ const handleFor = (namespace, instanceName, options, traceparent) => coldStartRetryingHandle(async (request) => namespace.get(namespace.idFromName(instanceName)).fetch(request), options, void 0, traceparent);
96
99
  const lifecycleCall = async (stub, method, binding, argument) => {
97
100
  const rpc = stub[method];
98
101
  if (typeof rpc !== "function") {
@@ -110,10 +113,10 @@ const egressControlsFor = (stub, binding) => {
110
113
  setDenied: async (hosts) => lifecycleCall(stub(), "setDeniedHosts", binding, [...hosts])
111
114
  };
112
115
  };
113
- const instanceHandleFor = (namespace, spec, instanceName, options) => {
116
+ const instanceHandleFor = (namespace, spec, instanceName, options, traceparent) => {
114
117
  const stub = () => namespace.get(namespace.idFromName(instanceName));
115
118
  return {
116
- ...coldStartRetryingHandle(async (request) => stub().fetch(request), options),
119
+ ...coldStartRetryingHandle(async (request) => stub().fetch(request), options, void 0, traceparent),
117
120
  destroy: async () => lifecycleCall(stub(), "destroy", spec.binding),
118
121
  egress: egressControlsFor(stub, spec.binding),
119
122
  getState: async () => lifecycleCall(stub(), "getState", spec.binding),
@@ -127,7 +130,7 @@ const randomPoolName = (size) => (
127
130
  `pool-${String(Math.floor(Math.random() * size))}`
128
131
  );
129
132
  const retryOnServerError = (response) => response.status >= 500;
130
- const poolHandleFor = (namespace, spec, options = {}, port) => {
133
+ const poolHandleFor = (namespace, spec, options = {}, port, traceparent) => {
131
134
  const size = options.size ?? spec.maxInstances ?? DEFAULT_POOL_SIZE;
132
135
  const attempts = Math.max(1, options.attempts ?? 3);
133
136
  const baseBackoff = options.backoffMs ?? 100;
@@ -141,7 +144,7 @@ const poolHandleFor = (namespace, spec, options = {}, port) => {
141
144
  if (attempt > 0) {
142
145
  await sleep(Math.min(baseBackoff * 2 ** (attempt - 1), maxBackoff));
143
146
  }
144
- const request = toRequest(input, init, port);
147
+ const request = toRequest(input, init, port, traceparent);
145
148
  try {
146
149
  const response = await namespace.get(namespace.idFromName(randomPoolName(size))).fetch(request);
147
150
  if (attempt === totalAttempts - 1 || !shouldRetry(response)) {
@@ -153,14 +156,14 @@ const poolHandleFor = (namespace, spec, options = {}, port) => {
153
156
  }
154
157
  throw lastError instanceof Error ? lastError : new Error(`ctx.containers.${spec.exportName}.pool(): all ${String(totalAttempts)} attempts failed`);
155
158
  },
156
- port: (targetPort) => poolHandleFor(namespace, spec, options, targetPort)
159
+ port: (targetPort) => poolHandleFor(namespace, spec, options, targetPort, traceparent)
157
160
  };
158
161
  };
159
- const accessorFor = (namespace, spec) => {
162
+ const accessorFor = (namespace, spec, traceparent) => {
160
163
  return {
161
- any: (count, options) => handleFor(namespace, randomPoolName(count ?? spec.maxInstances ?? DEFAULT_POOL_SIZE), options),
162
- get: (name, options) => instanceHandleFor(namespace, spec, name, options),
163
- pool: (options) => poolHandleFor(namespace, spec, options)
164
+ any: (count, options) => handleFor(namespace, randomPoolName(count ?? spec.maxInstances ?? DEFAULT_POOL_SIZE), options, traceparent),
165
+ get: (name, options) => instanceHandleFor(namespace, spec, name, options, traceparent),
166
+ pool: (options) => poolHandleFor(namespace, spec, options, void 0, traceparent)
164
167
  };
165
168
  };
166
169
  const missingBindingAccessor = (spec) => {
@@ -172,11 +175,11 @@ const missingBindingAccessor = (spec) => {
172
175
  };
173
176
  return { any: fail, get: fail, pool: fail };
174
177
  };
175
- const createContainerContext = (env, specs, jurisdiction) => {
178
+ const createContainerContext = (env, specs, jurisdiction, traceparent) => {
176
179
  const containers = {};
177
180
  for (const spec of specs) {
178
181
  const binding = env[spec.binding];
179
- containers[spec.exportName] = binding && typeof binding.idFromName === "function" && typeof binding.get === "function" ? accessorFor(applyJurisdiction(binding, jurisdiction), spec) : missingBindingAccessor(spec);
182
+ containers[spec.exportName] = binding && typeof binding.idFromName === "function" && typeof binding.get === "function" ? accessorFor(applyJurisdiction(binding, jurisdiction), spec, traceparent) : missingBindingAccessor(spec);
180
183
  }
181
184
  return containers;
182
185
  };
@@ -5,12 +5,16 @@
5
5
  * it is safe to import from Node tooling (codegen, the config layer) as well
6
6
  * as from worker code.
7
7
  */
8
- /** Named instance types Cloudflare Containers provides. */
8
+ /**
9
+ * Named instance types Cloudflare Containers provides.
10
+ * @experimental
11
+ */
9
12
  type NamedContainerInstanceType = "basic" | "lite" | "standard-1" | "standard-2" | "standard-3" | "standard-4";
10
13
  /**
11
14
  * A custom instance type. Cloudflare's bounds at the time of writing: up to
12
15
  * 4 vCPU, 12 GiB memory, 20 GB disk, ≥ 3 GiB memory per vCPU and ≤ 2 GB disk
13
16
  * per GiB memory. The config-layer validator enforces the documented ranges.
17
+ * @experimental
14
18
  */
15
19
  interface CustomContainerInstanceType {
16
20
  /** Disk in MB. Cloudflare's default is 2000 (2 GB). */
@@ -20,8 +24,15 @@ interface CustomContainerInstanceType {
20
24
  /** vCPU count. Cloudflare's default is 0.0625 (1/16 vCPU). */
21
25
  vcpu?: number;
22
26
  }
27
+ /**
28
+ * `ContainerInstanceType` is part of the experimental `@lunora/container` API and may change without a major version bump.
29
+ * @experimental
30
+ */
23
31
  type ContainerInstanceType = CustomContainerInstanceType | NamedContainerInstanceType;
24
- /** Rolling-deploy tuning for a container. */
32
+ /**
33
+ * Rolling-deploy tuning for a container.
34
+ * @experimental
35
+ */
25
36
  interface ContainerRollout {
26
37
  /** Seconds an active instance runs before it's eligible for update (wrangler `rollout_active_grace_period`). */
27
38
  gracePeriodSeconds?: number;
@@ -32,6 +43,7 @@ interface ContainerRollout {
32
43
  * A pre-built image pulled from a registry — the Cloudflare Registry, Docker
33
44
  * Hub, or Amazon ECR (the registries `wrangler deploy` supports). The
34
45
  * reference must be fully qualified, e.g. `docker.io/acme/transcoder:1.4`.
46
+ * @experimental
35
47
  */
36
48
  interface RegistryImageSource {
37
49
  registry: string;
@@ -41,6 +53,7 @@ interface RegistryImageSource {
41
53
  * source directory and `lunora deploy` builds an OCI image with Railpack
42
54
  * (needs a BuildKit instance) and pushes it to the Cloudflare Registry before
43
55
  * wrangler runs. Opt-in — the Dockerfile path is the zero-extra-deps default.
56
+ * @experimental
44
57
  */
45
58
  interface BuildImageSource {
46
59
  build: string;
@@ -50,6 +63,7 @@ interface BuildImageSource {
50
63
  * either a directory containing a `Dockerfile` (normalized to
51
64
  * `&lt;dir>/Dockerfile` with the directory as the build context) or a path to
52
65
  * the Dockerfile itself — while `{ registry }` is a pre-built image reference.
66
+ * @experimental
53
67
  */
54
68
  type ContainerImageSource = BuildImageSource | RegistryImageSource | string;
55
69
  /**
@@ -62,6 +76,7 @@ type ContainerImageSource = BuildImageSource | RegistryImageSource | string;
62
76
  * functions), so codegen and the config layer can read it without evaluating
63
77
  * code. (Upstream cloudflare/containers#188 expresses the same idea as handler
64
78
  * functions; the Lunora config is data-only, so it's modelled as descriptors.)
79
+ * @experimental
65
80
  */
66
81
  interface ContainerReadinessCheck {
67
82
  /** HTTP path probed on the container, e.g. `"/ready"` (a leading slash is optional). */
@@ -71,6 +86,10 @@ interface ContainerReadinessCheck {
71
86
  /** HTTP status that means "ready". Defaults to `200`. */
72
87
  status?: number;
73
88
  }
89
+ /**
90
+ * `ContainerConfig` is part of the experimental `@lunora/container` API and may change without a major version bump.
91
+ * @experimental
92
+ */
74
93
  interface ContainerConfig {
75
94
  /**
76
95
  * Hostnames the container may reach **even when {@link ContainerConfig.enableInternet}
@@ -227,12 +246,16 @@ interface ContainerConfig {
227
246
  /**
228
247
  * The value `defineContainer` returns: the validated config plus a brand the
229
248
  * codegen discovery and the generated Container DO class key on.
249
+ * @experimental
230
250
  */
231
251
  interface ContainerDefinition extends ContainerConfig {
232
252
  /** Brand marking a value as a Lunora container definition. */
233
253
  readonly isLunoraContainer: true;
234
254
  }
235
- /** A normalized image source, as written into `wrangler.jsonc`. */
255
+ /**
256
+ * A normalized image source, as written into `wrangler.jsonc`.
257
+ * @experimental
258
+ */
236
259
  type NormalizedContainerImage = {
237
260
  /** Build context directory (wrangler `image_build_context`). */
238
261
  buildContext: string;
@@ -251,6 +274,7 @@ type NormalizedContainerImage = {
251
274
  * Cloudflare Durable Object data-residency jurisdiction. Widening union —
252
275
  * Cloudflare adds values over time.
253
276
  * @see https://developers.cloudflare.com/durable-objects/reference/data-location/
277
+ * @experimental
254
278
  */
255
279
  type DurableObjectJurisdiction = "eu" | "fedramp" | "us";
256
280
  /**
@@ -5,12 +5,16 @@
5
5
  * it is safe to import from Node tooling (codegen, the config layer) as well
6
6
  * as from worker code.
7
7
  */
8
- /** Named instance types Cloudflare Containers provides. */
8
+ /**
9
+ * Named instance types Cloudflare Containers provides.
10
+ * @experimental
11
+ */
9
12
  type NamedContainerInstanceType = "basic" | "lite" | "standard-1" | "standard-2" | "standard-3" | "standard-4";
10
13
  /**
11
14
  * A custom instance type. Cloudflare's bounds at the time of writing: up to
12
15
  * 4 vCPU, 12 GiB memory, 20 GB disk, ≥ 3 GiB memory per vCPU and ≤ 2 GB disk
13
16
  * per GiB memory. The config-layer validator enforces the documented ranges.
17
+ * @experimental
14
18
  */
15
19
  interface CustomContainerInstanceType {
16
20
  /** Disk in MB. Cloudflare's default is 2000 (2 GB). */
@@ -20,8 +24,15 @@ interface CustomContainerInstanceType {
20
24
  /** vCPU count. Cloudflare's default is 0.0625 (1/16 vCPU). */
21
25
  vcpu?: number;
22
26
  }
27
+ /**
28
+ * `ContainerInstanceType` is part of the experimental `@lunora/container` API and may change without a major version bump.
29
+ * @experimental
30
+ */
23
31
  type ContainerInstanceType = CustomContainerInstanceType | NamedContainerInstanceType;
24
- /** Rolling-deploy tuning for a container. */
32
+ /**
33
+ * Rolling-deploy tuning for a container.
34
+ * @experimental
35
+ */
25
36
  interface ContainerRollout {
26
37
  /** Seconds an active instance runs before it's eligible for update (wrangler `rollout_active_grace_period`). */
27
38
  gracePeriodSeconds?: number;
@@ -32,6 +43,7 @@ interface ContainerRollout {
32
43
  * A pre-built image pulled from a registry — the Cloudflare Registry, Docker
33
44
  * Hub, or Amazon ECR (the registries `wrangler deploy` supports). The
34
45
  * reference must be fully qualified, e.g. `docker.io/acme/transcoder:1.4`.
46
+ * @experimental
35
47
  */
36
48
  interface RegistryImageSource {
37
49
  registry: string;
@@ -41,6 +53,7 @@ interface RegistryImageSource {
41
53
  * source directory and `lunora deploy` builds an OCI image with Railpack
42
54
  * (needs a BuildKit instance) and pushes it to the Cloudflare Registry before
43
55
  * wrangler runs. Opt-in — the Dockerfile path is the zero-extra-deps default.
56
+ * @experimental
44
57
  */
45
58
  interface BuildImageSource {
46
59
  build: string;
@@ -50,6 +63,7 @@ interface BuildImageSource {
50
63
  * either a directory containing a `Dockerfile` (normalized to
51
64
  * `&lt;dir>/Dockerfile` with the directory as the build context) or a path to
52
65
  * the Dockerfile itself — while `{ registry }` is a pre-built image reference.
66
+ * @experimental
53
67
  */
54
68
  type ContainerImageSource = BuildImageSource | RegistryImageSource | string;
55
69
  /**
@@ -62,6 +76,7 @@ type ContainerImageSource = BuildImageSource | RegistryImageSource | string;
62
76
  * functions), so codegen and the config layer can read it without evaluating
63
77
  * code. (Upstream cloudflare/containers#188 expresses the same idea as handler
64
78
  * functions; the Lunora config is data-only, so it's modelled as descriptors.)
79
+ * @experimental
65
80
  */
66
81
  interface ContainerReadinessCheck {
67
82
  /** HTTP path probed on the container, e.g. `"/ready"` (a leading slash is optional). */
@@ -71,6 +86,10 @@ interface ContainerReadinessCheck {
71
86
  /** HTTP status that means "ready". Defaults to `200`. */
72
87
  status?: number;
73
88
  }
89
+ /**
90
+ * `ContainerConfig` is part of the experimental `@lunora/container` API and may change without a major version bump.
91
+ * @experimental
92
+ */
74
93
  interface ContainerConfig {
75
94
  /**
76
95
  * Hostnames the container may reach **even when {@link ContainerConfig.enableInternet}
@@ -227,12 +246,16 @@ interface ContainerConfig {
227
246
  /**
228
247
  * The value `defineContainer` returns: the validated config plus a brand the
229
248
  * codegen discovery and the generated Container DO class key on.
249
+ * @experimental
230
250
  */
231
251
  interface ContainerDefinition extends ContainerConfig {
232
252
  /** Brand marking a value as a Lunora container definition. */
233
253
  readonly isLunoraContainer: true;
234
254
  }
235
- /** A normalized image source, as written into `wrangler.jsonc`. */
255
+ /**
256
+ * A normalized image source, as written into `wrangler.jsonc`.
257
+ * @experimental
258
+ */
236
259
  type NormalizedContainerImage = {
237
260
  /** Build context directory (wrangler `image_build_context`). */
238
261
  buildContext: string;
@@ -251,6 +274,7 @@ type NormalizedContainerImage = {
251
274
  * Cloudflare Durable Object data-residency jurisdiction. Widening union —
252
275
  * Cloudflare adds values over time.
253
276
  * @see https://developers.cloudflare.com/durable-objects/reference/data-location/
277
+ * @experimental
254
278
  */
255
279
  type DurableObjectJurisdiction = "eu" | "fedramp" | "us";
256
280
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/container",
3
- "version": "1.0.0-alpha.10",
3
+ "version": "1.0.0-alpha.12",
4
4
  "description": "Cloudflare Containers for Lunora: defineContainer, generated Container DO classes, and the ctx.containers action surface",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -56,7 +56,7 @@
56
56
  "access": "public"
57
57
  },
58
58
  "dependencies": {
59
- "@lunora/errors": "1.0.0-alpha.4"
59
+ "@lunora/errors": "1.0.0-alpha.5"
60
60
  },
61
61
  "engines": {
62
62
  "node": "^22.15.0 || >=24.11.0"