@lunora/container 1.0.0-alpha.11 → 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;
@@ -216,9 +240,13 @@ interface ContainerBindingSpec {
216
240
  * `traceparent` (the inbound RPC's W3C trace context, forwarded by the runtime
217
241
  * and read off the request by the DO) is stamped onto every outbound container
218
242
  * `fetch`, so the container's own spans stitch under the Worker's trace.
243
+ * @experimental
219
244
  */
220
245
  declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>, jurisdiction?: DurableObjectJurisdiction, traceparent?: string) => Record<string, ContainerAccessor>;
221
- /** A test handler: receives the request plus the targeted instance name. */
246
+ /**
247
+ * A test handler: receives the request plus the targeted instance name.
248
+ * @experimental
249
+ */
222
250
  type ContainerTestHandler = (request: Request, instance: {
223
251
  name: string;
224
252
  }) => Promise<Response> | Response;
@@ -232,6 +260,7 @@ type ContainerTestHandler = (request: Request, instance: {
232
260
  * transcoder: (request) => new Response("ok"),
233
261
  * });
234
262
  * ```
263
+ * @experimental
235
264
  */
236
265
  declare const createContainerTestContext: (handlers: Record<string, ContainerTestHandler>) => Record<string, ContainerAccessor>;
237
266
  /**
@@ -243,6 +272,7 @@ declare const createContainerTestContext: (handlers: Record<string, ContainerTes
243
272
  * `Dockerfile.dev` also counts) is used as-is with its directory as the build
244
273
  * context; any other path is treated as the build-context directory and the
245
274
  * Dockerfile is expected at `&lt;dir>/Dockerfile`.
275
+ * @experimental
246
276
  */
247
277
  declare const normalizeContainerImage: (image: ContainerImageSource) => NormalizedContainerImage;
248
278
  /**
@@ -250,6 +280,7 @@ declare const normalizeContainerImage: (image: ContainerImageSource) => Normaliz
250
280
  * `transcoder` → `TranscoderContainer`. wrangler's `containers[].class_name`
251
281
  * and the Durable Object binding's `class_name` both reference it, so codegen
252
282
  * and the config layer MUST derive it identically — always via this helper.
283
+ * @experimental
253
284
  */
254
285
  declare const containerClassName: (exportName: string) => string;
255
286
  /**
@@ -257,6 +288,7 @@ declare const containerClassName: (exportName: string) => string;
257
288
  * `CONTAINER_TRANSCODER`, `imageResizer` → `CONTAINER_IMAGE_RESIZER`. The
258
289
  * `CONTAINER_` prefix namespaces these away from `SHARD`/`SESSION`/`SCHEDULER`
259
290
  * so a container export can never collide with the built-in bindings.
291
+ * @experimental
260
292
  */
261
293
  declare const containerBindingName: (exportName: string) => string;
262
294
  /**
@@ -265,16 +297,25 @@ declare const containerBindingName: (exportName: string) => string;
265
297
  * it as the wrangler `containers[].image`, and `lunora deploy` builds that tag
266
298
  * with Railpack and `wrangler containers push`es it before deploying — so all
267
299
  * three derive the tag from this one helper and can never disagree.
300
+ * @experimental
268
301
  */
269
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
+ */
270
307
  declare const defineContainer: (config: ContainerConfig) => ContainerDefinition;
271
- /** 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
+ */
272
312
  declare const isContainerDefinition: (value: unknown) => value is ContainerDefinition;
273
313
  /**
274
314
  * The container's full environment at instance start: the static `env` block
275
315
  * plus every declared secret resolved from the Worker `env`. A declared secret
276
316
  * missing from the Worker env fails fast — starting the container without a
277
317
  * credential it was promised yields far worse errors downstream.
318
+ * @experimental
278
319
  */
279
320
  declare const resolveContainerEnvVariables: (definition: ContainerDefinition, workerEnv: Record<string, unknown>, exportName?: string) => Record<string, string>;
280
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;
@@ -216,9 +240,13 @@ interface ContainerBindingSpec {
216
240
  * `traceparent` (the inbound RPC's W3C trace context, forwarded by the runtime
217
241
  * and read off the request by the DO) is stamped onto every outbound container
218
242
  * `fetch`, so the container's own spans stitch under the Worker's trace.
243
+ * @experimental
219
244
  */
220
245
  declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>, jurisdiction?: DurableObjectJurisdiction, traceparent?: string) => Record<string, ContainerAccessor>;
221
- /** A test handler: receives the request plus the targeted instance name. */
246
+ /**
247
+ * A test handler: receives the request plus the targeted instance name.
248
+ * @experimental
249
+ */
222
250
  type ContainerTestHandler = (request: Request, instance: {
223
251
  name: string;
224
252
  }) => Promise<Response> | Response;
@@ -232,6 +260,7 @@ type ContainerTestHandler = (request: Request, instance: {
232
260
  * transcoder: (request) => new Response("ok"),
233
261
  * });
234
262
  * ```
263
+ * @experimental
235
264
  */
236
265
  declare const createContainerTestContext: (handlers: Record<string, ContainerTestHandler>) => Record<string, ContainerAccessor>;
237
266
  /**
@@ -243,6 +272,7 @@ declare const createContainerTestContext: (handlers: Record<string, ContainerTes
243
272
  * `Dockerfile.dev` also counts) is used as-is with its directory as the build
244
273
  * context; any other path is treated as the build-context directory and the
245
274
  * Dockerfile is expected at `&lt;dir>/Dockerfile`.
275
+ * @experimental
246
276
  */
247
277
  declare const normalizeContainerImage: (image: ContainerImageSource) => NormalizedContainerImage;
248
278
  /**
@@ -250,6 +280,7 @@ declare const normalizeContainerImage: (image: ContainerImageSource) => Normaliz
250
280
  * `transcoder` → `TranscoderContainer`. wrangler's `containers[].class_name`
251
281
  * and the Durable Object binding's `class_name` both reference it, so codegen
252
282
  * and the config layer MUST derive it identically — always via this helper.
283
+ * @experimental
253
284
  */
254
285
  declare const containerClassName: (exportName: string) => string;
255
286
  /**
@@ -257,6 +288,7 @@ declare const containerClassName: (exportName: string) => string;
257
288
  * `CONTAINER_TRANSCODER`, `imageResizer` → `CONTAINER_IMAGE_RESIZER`. The
258
289
  * `CONTAINER_` prefix namespaces these away from `SHARD`/`SESSION`/`SCHEDULER`
259
290
  * so a container export can never collide with the built-in bindings.
291
+ * @experimental
260
292
  */
261
293
  declare const containerBindingName: (exportName: string) => string;
262
294
  /**
@@ -265,16 +297,25 @@ declare const containerBindingName: (exportName: string) => string;
265
297
  * it as the wrangler `containers[].image`, and `lunora deploy` builds that tag
266
298
  * with Railpack and `wrangler containers push`es it before deploying — so all
267
299
  * three derive the tag from this one helper and can never disagree.
300
+ * @experimental
268
301
  */
269
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
+ */
270
307
  declare const defineContainer: (config: ContainerConfig) => ContainerDefinition;
271
- /** 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
+ */
272
312
  declare const isContainerDefinition: (value: unknown) => value is ContainerDefinition;
273
313
  /**
274
314
  * The container's full environment at instance start: the static `env` block
275
315
  * plus every declared secret resolved from the Worker `env`. A declared secret
276
316
  * missing from the Worker env fails fast — starting the container without a
277
317
  * credential it was promised yields far worse errors downstream.
318
+ * @experimental
278
319
  */
279
320
  declare const resolveContainerEnvVariables: (definition: ContainerDefinition, workerEnv: Record<string, unknown>, exportName?: string) => Record<string, string>;
280
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/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;
@@ -86,7 +99,10 @@ interface ContainerTelemetryOptions {
86
99
  */
87
100
  traceparent?: string;
88
101
  }
89
- /** The exporter handle {@link createContainerTelemetry} returns. */
102
+ /**
103
+ * The exporter handle {@link createContainerTelemetry} returns.
104
+ * @experimental
105
+ */
90
106
  interface ContainerTelemetry {
91
107
  /** Record one log line (no-op when disabled). */
92
108
  emitLog: (log: ContainerLogInput) => void;
@@ -113,6 +129,7 @@ interface ContainerTelemetry {
113
129
  * false`): `emitSpan`/`emitLog` no-op and `trace` still runs its work but records
114
130
  * nothing — so the same code runs unchanged locally and in the cloud.
115
131
  * @param options Exporter options; every field falls back to a `LUNORA_*` env var.
132
+ * @experimental
116
133
  */
117
134
  declare const createContainerTelemetry: (options?: ContainerTelemetryOptions) => ContainerTelemetry;
118
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;
@@ -86,7 +99,10 @@ interface ContainerTelemetryOptions {
86
99
  */
87
100
  traceparent?: string;
88
101
  }
89
- /** The exporter handle {@link createContainerTelemetry} returns. */
102
+ /**
103
+ * The exporter handle {@link createContainerTelemetry} returns.
104
+ * @experimental
105
+ */
90
106
  interface ContainerTelemetry {
91
107
  /** Record one log line (no-op when disabled). */
92
108
  emitLog: (log: ContainerLogInput) => void;
@@ -113,6 +129,7 @@ interface ContainerTelemetry {
113
129
  * false`): `emitSpan`/`emitLog` no-op and `trace` still runs its work but records
114
130
  * nothing — so the same code runs unchanged locally and in the cloud.
115
131
  * @param options Exporter options; every field falls back to a `LUNORA_*` env var.
132
+ * @experimental
116
133
  */
117
134
  declare const createContainerTelemetry: (options?: ContainerTelemetryOptions) => ContainerTelemetry;
118
135
  export { type ContainerAttributeValue, type ContainerLogInput, type ContainerSpanInput, type ContainerTelemetry, type ContainerTelemetryOptions, type OtelFetchLike, createContainerTelemetry };
@@ -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.11",
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"