@lunora/container 1.0.0-alpha.5 → 1.0.0-alpha.51

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/LICENSE.md CHANGED
@@ -103,3 +103,29 @@ Unless required by applicable law or agreed to in writing, software distributed
103
103
  under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
104
104
  CONDITIONS OF ANY KIND, either express or implied. See the License for the
105
105
  specific language governing permissions and limitations under the License.
106
+
107
+ <!-- DEPENDENCIES -->
108
+
109
+ # Licenses of bundled dependencies
110
+ The published @lunora/container artifact additionally contains code with the following licenses:
111
+ MIT OR Apache-2.0
112
+
113
+ # Bundled dependencies:
114
+ ## @cloudflare/containers
115
+ License: MIT OR Apache-2.0
116
+ Repository: git+https://github.com/cloudflare/containers.git
117
+
118
+ <!-- /DEPENDENCIES -->
119
+
120
+ <!-- TYPE_DEPENDENCIES -->
121
+
122
+ # Licenses of bundled types
123
+ The published @lunora/container artifact additionally contains code with the following licenses:
124
+ MIT OR Apache-2.0
125
+
126
+ # Bundled types:
127
+ ## @cloudflare/containers
128
+ License: MIT OR Apache-2.0
129
+ Repository: git+https://github.com/cloudflare/containers.git
130
+
131
+ <!-- /TYPE_DEPENDENCIES -->
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">
@@ -170,12 +172,12 @@ export const api = defineContainer({
170
172
 
171
173
  ### Hard timeout
172
174
 
173
- `sleepAfter` caps _idle_ time; `hardTimeout` caps _total_ lifetime — a runaway-cost backstop measured from start, regardless of activity (same grammar as `sleepAfter`). When it elapses the generated class's `onHardTimeoutExpired` hook runs (default: `stop()`); the timer is run-generation-stamped so a stale timer from a slept/crashed run can't kill a fresh one.
175
+ `sleepAfter` caps _idle_ time; `hardTimeout` caps _total_ lifetime — a runaway-cost backstop measured from start, regardless of activity (same grammar as `sleepAfter`). When it elapses the generated class's `onHardTimeoutExpired` hook runs (default: `stop()`, i.e. SIGTERM with no escalation — a container that ignores SIGTERM outlives its cap unless you override the hook and follow up with `destroy()`); the timer is run-generation-stamped so a stale timer from a slept/crashed run can't kill a fresh one.
174
176
 
175
177
  ```ts
176
178
  export const job = defineContainer({
177
179
  image: "./containers/job",
178
- hardTimeout: "1h", // never run longer than an hour, busy or not
180
+ hardTimeout: "1h", // stopped after an hour, busy or not
179
181
  });
180
182
  ```
181
183
 
@@ -194,7 +196,7 @@ await lunora.mutation("jobs:markDone", { id: pending[0].id });
194
196
 
195
197
  The token is a bearer your Worker's `resolveIdentity` recognizes — pass it to the container as a `secret`. Non-JS containers can `POST /_lunora/rpc` with `{ functionPath, args }` directly.
196
198
 
197
- Secure the bridge in `resolveIdentity`: read `request.headers.get("authorization")`, strip the `Bearer ` prefix, and compare the token against a Worker secret (e.g. `env.LUNORA_CONTAINER_TOKEN`) you also forward to the container. Return a `{ userId }` identity only on a match and `null` otherwise — an unrecognised request then runs anonymously and is rejected by your functions' own authorization checks. See [Securing the bridge](https://lunora.sh/docs/addons/containers#securing-the-bridge) for the full example.
199
+ Secure the bridge in `resolveIdentity`: read `request.headers.get("authorization")`, strip the `Bearer ` prefix, and compare the token against a Worker secret (e.g. `env.LUNORA_CONTAINER_TOKEN`) you also forward to the container. Return a `{ userId }` identity only on a match and `null` otherwise — an unrecognised request then runs anonymously and is rejected by your functions' own authorization checks. See [Securing the bridge](https://lunora.sh/docs/packages/container#securing-the-bridge) for the full example.
198
200
 
199
201
  ### Entry points
200
202
 
@@ -202,7 +204,7 @@ Secure the bridge in `resolveIdentity`: read `request.headers.get("authorization
202
204
  - `@lunora/container/do` — workerd-only: the `LunoraContainer` base class the generated DO classes extend (pulls in `@cloudflare/containers`).
203
205
  - `@lunora/container/bridge` — runtime-agnostic: `createContainerBridge` for calling Lunora functions from inside a container.
204
206
 
205
- > This README covers the basics. For the full API, options, and guides, see the **[documentation](https://lunora.sh/docs/addons/containers)**.
207
+ > This README covers the basics. For the full API, options, and guides, see the **[documentation](https://lunora.sh/docs/packages/container)**.
206
208
 
207
209
  ### Known platform limitations
208
210
 
package/dist/bridge.d.mts CHANGED
@@ -1,4 +1,7 @@
1
- /** A `fetch` implementation defaults to the runtime global. */
1
+ import { LunoraError } from '@lunora/errors';
2
+ /**
3
+ * A `fetch` implementation — defaults to the runtime global.
4
+ */
2
5
  type FetchLike = (input: string, init: {
3
6
  body: string;
4
7
  headers: Record<string, string>;
@@ -9,34 +12,38 @@ type FetchLike = (input: string, init: {
9
12
  status: number;
10
13
  statusText?: string;
11
14
  }>;
15
+ /**
16
+ * `ContainerBridgeOptions` is part of the experimental `@lunora/container` API and may change without a major version bump.
17
+ */
12
18
  interface ContainerBridgeOptions {
13
19
  /**
14
- * Base URL of the deployed Lunora Worker (no trailing `/_lunora/rpc`), e.g.
15
- * `https://my-app.workers.dev`. In a Lunora container, surface it as an
16
- * `env` value on the definition.
17
- */
20
+ * Base URL of the deployed Lunora Worker (no trailing `/_lunora/rpc`), e.g.
21
+ * `https://my-app.workers.dev`. In a Lunora container, surface it as an
22
+ * `env` value on the definition.
23
+ */
18
24
  baseUrl: string;
19
25
  /** Injectable `fetch` (tests / non-global runtimes). Defaults to `globalThis.fetch`. */
20
26
  fetch?: FetchLike;
21
27
  /**
22
- * Bearer token sent as `Authorization: Bearer &lt;token>`. Your Worker's
23
- * `resolveIdentity` maps it to the identity the called functions run as.
24
- * Pass it to the container as a `secret`, never bake it into the image.
25
- */
28
+ * Bearer token sent as `Authorization: Bearer <token>`. Your Worker's
29
+ * `resolveIdentity` maps it to the identity the called functions run as.
30
+ * Pass it to the container as a `secret`, never bake it into the image.
31
+ */
26
32
  token?: string;
27
33
  }
28
- /** Thrown when a Lunora function returns an error envelope. Carries the wire `code`. */
29
- declare class ContainerBridgeError extends Error {
30
- readonly code: string;
34
+ /**
35
+ * Thrown when a Lunora function returns an error envelope. A `LunoraError` subclass carrying the wire `code`.
36
+ */
37
+ declare class ContainerBridgeError extends LunoraError {
31
38
  constructor(code: string, message: string);
32
39
  }
33
40
  /**
34
- * Structural mirror of `@lunora/client`'s `FunctionReference` — the typed
35
- * handle the generated `_generated/api` object carries. Declared locally (not
36
- * imported) so the bridge stays dependency-free and its `.d.ts` is
37
- * self-contained; the `__lunoraPhantom` shape matches, so a real `api.x.y`
38
- * reference is assignable and its arg/return types are inferable.
39
- */
41
+ * Structural mirror of `@lunora/client`'s `FunctionReference` — the typed
42
+ * handle the generated `_generated/api` object carries. Declared locally (not
43
+ * imported) so the bridge stays dependency-free and its `.d.ts` is
44
+ * self-contained; the `__lunoraPhantom` shape matches, so a real `api.x.y`
45
+ * reference is assignable and its arg/return types are inferable.
46
+ */
40
47
  interface BridgeFunctionReference<Args = unknown, Result = unknown> {
41
48
  readonly __lunoraPhantom?: {
42
49
  args: Args;
@@ -56,6 +63,9 @@ type ResultOfReference<Reference> = Reference extends {
56
63
  returns: infer Result;
57
64
  };
58
65
  } ? Result : never;
66
+ /**
67
+ * `ContainerBridge` is part of the experimental `@lunora/container` API and may change without a major version bump.
68
+ */
59
69
  interface ContainerBridge {
60
70
  /** Call an `action` by `namespace:fn` path. Alias of {@link ContainerBridge.call} for intent. */
61
71
  action: <Result = unknown>(functionPath: string, args?: Record<string, unknown>, shardKey?: string) => Promise<Result>;
@@ -66,25 +76,25 @@ interface ContainerBridge {
66
76
  /** Call a `query` by `namespace:fn` path. Alias of {@link ContainerBridge.call} for intent. */
67
77
  query: <Result = unknown>(functionPath: string, args?: Record<string, unknown>, shardKey?: string) => Promise<Result>;
68
78
  /**
69
- * Fully-typed call via a generated function reference. Pass a reference from
70
- * the project's `_generated/api` (e.g. `api.messages.list`) and the args +
71
- * result are inferred from it — the typed counterpart to {@link ContainerBridge.call}
72
- * for JS/TS containers that can import the generated `api`.
73
- */
79
+ * Fully-typed call via a generated function reference. Pass a reference from
80
+ * the project's `_generated/api` (e.g. `api.messages.list`) and the args +
81
+ * result are inferred from it — the typed counterpart to {@link ContainerBridge.call}
82
+ * for JS/TS containers that can import the generated `api`.
83
+ */
74
84
  run: <Reference extends BridgeFunctionReference>(reference: Reference, args: ArgsOfReference<Reference>, shardKey?: string) => Promise<ResultOfReference<Reference>>;
75
85
  }
76
86
  /**
77
- * Build a container→Lunora bridge bound to a Worker URL + token.
78
- *
79
- * ```ts
80
- * const lunora = createContainerBridge({ baseUrl: process.env.LUNORA_URL!, token: process.env.LUNORA_TOKEN });
81
- * const messages = await lunora.query("messages:list", { limit: 20 });
82
- * await lunora.mutation("messages:markProcessed", { id });
83
- * ```
84
- *
85
- * `query`/`mutation`/`action` are intent-revealing aliases of one `call` — the
86
- * wire is identical and the server dispatches by the function's registered
87
- * kind, so a query path called via `.mutation(...)` still runs as a query.
88
- */
87
+ * Build a container→Lunora bridge bound to a Worker URL + token.
88
+ *
89
+ * ```ts
90
+ * const lunora = createContainerBridge({ baseUrl: process.env.LUNORA_URL!, token: process.env.LUNORA_TOKEN });
91
+ * const messages = await lunora.query("messages:list", { limit: 20 });
92
+ * await lunora.mutation("messages:markProcessed", { id });
93
+ * ```
94
+ *
95
+ * `query`/`mutation`/`action` are intent-revealing aliases of one `call` — the
96
+ * wire is identical and the server dispatches by the function's registered
97
+ * kind, so a query path called via `.mutation(...)` still runs as a query.
98
+ */
89
99
  declare const createContainerBridge: (options: ContainerBridgeOptions) => ContainerBridge;
90
100
  export { type BridgeFunctionReference, type ContainerBridge, ContainerBridgeError, type ContainerBridgeOptions, type FetchLike, createContainerBridge };
package/dist/bridge.d.ts CHANGED
@@ -1,4 +1,7 @@
1
- /** A `fetch` implementation defaults to the runtime global. */
1
+ import { LunoraError } from '@lunora/errors';
2
+ /**
3
+ * A `fetch` implementation — defaults to the runtime global.
4
+ */
2
5
  type FetchLike = (input: string, init: {
3
6
  body: string;
4
7
  headers: Record<string, string>;
@@ -9,34 +12,38 @@ type FetchLike = (input: string, init: {
9
12
  status: number;
10
13
  statusText?: string;
11
14
  }>;
15
+ /**
16
+ * `ContainerBridgeOptions` is part of the experimental `@lunora/container` API and may change without a major version bump.
17
+ */
12
18
  interface ContainerBridgeOptions {
13
19
  /**
14
- * Base URL of the deployed Lunora Worker (no trailing `/_lunora/rpc`), e.g.
15
- * `https://my-app.workers.dev`. In a Lunora container, surface it as an
16
- * `env` value on the definition.
17
- */
20
+ * Base URL of the deployed Lunora Worker (no trailing `/_lunora/rpc`), e.g.
21
+ * `https://my-app.workers.dev`. In a Lunora container, surface it as an
22
+ * `env` value on the definition.
23
+ */
18
24
  baseUrl: string;
19
25
  /** Injectable `fetch` (tests / non-global runtimes). Defaults to `globalThis.fetch`. */
20
26
  fetch?: FetchLike;
21
27
  /**
22
- * Bearer token sent as `Authorization: Bearer &lt;token>`. Your Worker's
23
- * `resolveIdentity` maps it to the identity the called functions run as.
24
- * Pass it to the container as a `secret`, never bake it into the image.
25
- */
28
+ * Bearer token sent as `Authorization: Bearer <token>`. Your Worker's
29
+ * `resolveIdentity` maps it to the identity the called functions run as.
30
+ * Pass it to the container as a `secret`, never bake it into the image.
31
+ */
26
32
  token?: string;
27
33
  }
28
- /** Thrown when a Lunora function returns an error envelope. Carries the wire `code`. */
29
- declare class ContainerBridgeError extends Error {
30
- readonly code: string;
34
+ /**
35
+ * Thrown when a Lunora function returns an error envelope. A `LunoraError` subclass carrying the wire `code`.
36
+ */
37
+ declare class ContainerBridgeError extends LunoraError {
31
38
  constructor(code: string, message: string);
32
39
  }
33
40
  /**
34
- * Structural mirror of `@lunora/client`'s `FunctionReference` — the typed
35
- * handle the generated `_generated/api` object carries. Declared locally (not
36
- * imported) so the bridge stays dependency-free and its `.d.ts` is
37
- * self-contained; the `__lunoraPhantom` shape matches, so a real `api.x.y`
38
- * reference is assignable and its arg/return types are inferable.
39
- */
41
+ * Structural mirror of `@lunora/client`'s `FunctionReference` — the typed
42
+ * handle the generated `_generated/api` object carries. Declared locally (not
43
+ * imported) so the bridge stays dependency-free and its `.d.ts` is
44
+ * self-contained; the `__lunoraPhantom` shape matches, so a real `api.x.y`
45
+ * reference is assignable and its arg/return types are inferable.
46
+ */
40
47
  interface BridgeFunctionReference<Args = unknown, Result = unknown> {
41
48
  readonly __lunoraPhantom?: {
42
49
  args: Args;
@@ -56,6 +63,9 @@ type ResultOfReference<Reference> = Reference extends {
56
63
  returns: infer Result;
57
64
  };
58
65
  } ? Result : never;
66
+ /**
67
+ * `ContainerBridge` is part of the experimental `@lunora/container` API and may change without a major version bump.
68
+ */
59
69
  interface ContainerBridge {
60
70
  /** Call an `action` by `namespace:fn` path. Alias of {@link ContainerBridge.call} for intent. */
61
71
  action: <Result = unknown>(functionPath: string, args?: Record<string, unknown>, shardKey?: string) => Promise<Result>;
@@ -66,25 +76,25 @@ interface ContainerBridge {
66
76
  /** Call a `query` by `namespace:fn` path. Alias of {@link ContainerBridge.call} for intent. */
67
77
  query: <Result = unknown>(functionPath: string, args?: Record<string, unknown>, shardKey?: string) => Promise<Result>;
68
78
  /**
69
- * Fully-typed call via a generated function reference. Pass a reference from
70
- * the project's `_generated/api` (e.g. `api.messages.list`) and the args +
71
- * result are inferred from it — the typed counterpart to {@link ContainerBridge.call}
72
- * for JS/TS containers that can import the generated `api`.
73
- */
79
+ * Fully-typed call via a generated function reference. Pass a reference from
80
+ * the project's `_generated/api` (e.g. `api.messages.list`) and the args +
81
+ * result are inferred from it — the typed counterpart to {@link ContainerBridge.call}
82
+ * for JS/TS containers that can import the generated `api`.
83
+ */
74
84
  run: <Reference extends BridgeFunctionReference>(reference: Reference, args: ArgsOfReference<Reference>, shardKey?: string) => Promise<ResultOfReference<Reference>>;
75
85
  }
76
86
  /**
77
- * Build a container→Lunora bridge bound to a Worker URL + token.
78
- *
79
- * ```ts
80
- * const lunora = createContainerBridge({ baseUrl: process.env.LUNORA_URL!, token: process.env.LUNORA_TOKEN });
81
- * const messages = await lunora.query("messages:list", { limit: 20 });
82
- * await lunora.mutation("messages:markProcessed", { id });
83
- * ```
84
- *
85
- * `query`/`mutation`/`action` are intent-revealing aliases of one `call` — the
86
- * wire is identical and the server dispatches by the function's registered
87
- * kind, so a query path called via `.mutation(...)` still runs as a query.
88
- */
87
+ * Build a container→Lunora bridge bound to a Worker URL + token.
88
+ *
89
+ * ```ts
90
+ * const lunora = createContainerBridge({ baseUrl: process.env.LUNORA_URL!, token: process.env.LUNORA_TOKEN });
91
+ * const messages = await lunora.query("messages:list", { limit: 20 });
92
+ * await lunora.mutation("messages:markProcessed", { id });
93
+ * ```
94
+ *
95
+ * `query`/`mutation`/`action` are intent-revealing aliases of one `call` — the
96
+ * wire is identical and the server dispatches by the function's registered
97
+ * kind, so a query path called via `.mutation(...)` still runs as a query.
98
+ */
89
99
  declare const createContainerBridge: (options: ContainerBridgeOptions) => ContainerBridge;
90
100
  export { type BridgeFunctionReference, type ContainerBridge, ContainerBridgeError, type ContainerBridgeOptions, type FetchLike, createContainerBridge };
package/dist/bridge.mjs CHANGED
@@ -1,76 +1 @@
1
- const RPC_PATH = "/_lunora/rpc";
2
- class ContainerBridgeError extends Error {
3
- code;
4
- constructor(code, message) {
5
- super(message);
6
- this.name = "ContainerBridgeError";
7
- this.code = code;
8
- }
9
- }
10
- const joinUrl = (baseUrl, path) => {
11
- let base = baseUrl;
12
- while (base.endsWith("/")) {
13
- base = base.slice(0, -1);
14
- }
15
- return `${base}${path}`;
16
- };
17
- const statusError = (functionPath, response) => new Error(
18
- `createContainerBridge: request to "${functionPath}" failed (status ${String(response.status)}${response.statusText ? ` ${response.statusText}` : ""})`
19
- );
20
- const parseResponseBody = async (response, functionPath) => {
21
- try {
22
- return await response.json();
23
- } catch {
24
- if (!response.ok) {
25
- throw statusError(functionPath, response);
26
- }
27
- throw new Error(`createContainerBridge: request to "${functionPath}" returned a non-JSON response (status ${String(response.status)})`);
28
- }
29
- };
30
- const createContainerBridge = (options) => {
31
- if (typeof options.baseUrl !== "string" || options.baseUrl.length === 0) {
32
- throw new TypeError("createContainerBridge: `baseUrl` must be a non-empty Worker URL (e.g. https://my-app.workers.dev) — is the URL env var set?");
33
- }
34
- const fetchImpl = options.fetch ?? globalThis.fetch;
35
- const call = async (functionPath, args = {}, shardKey) => {
36
- if (typeof fetchImpl !== "function") {
37
- throw new TypeError("createContainerBridge: no `fetch` available — pass `fetch` in options for this runtime.");
38
- }
39
- const headers = { "content-type": "application/json" };
40
- if (options.token !== void 0) {
41
- headers.authorization = `Bearer ${options.token}`;
42
- }
43
- const response = await fetchImpl(joinUrl(options.baseUrl, RPC_PATH), {
44
- body: JSON.stringify({ args, functionPath, shardKey }),
45
- headers,
46
- method: "POST"
47
- });
48
- const body = await parseResponseBody(response, functionPath);
49
- if (typeof body === "object" && body !== null && "error" in body) {
50
- const { error } = body;
51
- if (typeof error === "object" && error !== null) {
52
- const { code, message } = error;
53
- if (typeof code === "string" && typeof message === "string") {
54
- throw new ContainerBridgeError(code, message);
55
- }
56
- }
57
- let detail;
58
- try {
59
- detail = JSON.stringify(error);
60
- } catch {
61
- detail = String(error);
62
- }
63
- throw new Error(
64
- `createContainerBridge: request to "${functionPath}" returned a malformed error envelope (status ${String(response.status)}): ${detail}`
65
- );
66
- }
67
- if (!response.ok) {
68
- throw statusError(functionPath, response);
69
- }
70
- return body.result;
71
- };
72
- const run = async (reference, args, shardKey) => call(reference.__lunoraRef, args, shardKey);
73
- return { action: call, call, mutation: call, query: call, run };
74
- };
75
-
76
- export { ContainerBridgeError, createContainerBridge };
1
+ import{LunoraError as p}from"@lunora/errors";const m=r=>{let t="";for(let c=0;c<r.length;c+=32768)t+=String.fromCharCode(...r.subarray(c,c+32768));return btoa(t)},R=r=>{const t=atob(r),o=new Uint8Array(t.length);for(let c=0;c<t.length;c+=1)o[c]=t.codePointAt(c)??0;return o},f="$lunora.wire$",w=64,E=1024,g="__proto__",j={BigInt64Array,BigUint64Array,Float32Array,Float64Array,Int8Array,Int16Array,Int32Array,Uint8Array,Uint8ClampedArray,Uint16Array,Uint32Array},O={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},U=r=>{if(r===null||typeof r!="object")return!1;const t=Object.getPrototypeOf(r);return t===null||t===Object.prototype},d=(r,t=0)=>{if(t>w)throw new RangeError(`wire-codec: value nesting exceeds the ${w}-level limit`);if(r===void 0)return[f,"undefined"];if(r===null)return null;const o=typeof r;if(o==="bigint")return[f,"bigint",r.toString()];if(o==="number"){const e=r;return Number.isNaN(e)?[f,"nan"]:e===1/0?[f,"inf"]:e===-1/0?[f,"-inf"]:e}if(o!=="object")return r;if(r instanceof Date)return[f,"date",d(r.getTime(),t+1)];if(r instanceof Error){const e=r,n={};for(const i of Object.keys(e)){if(e[i]===void 0)continue;const u=d(e[i],t+1);i===g?Object.defineProperty(n,i,{configurable:!0,enumerable:!0,value:u,writable:!0}):n[i]=u}const a=[f,"error",String(e.name),String(e.message),n];return e.cause!==void 0&&a.push(d(e.cause,t+1)),a}if(r instanceof URL)return[f,"url",r.href];if(r instanceof Map)return[f,"map",[...r.entries()].map(([e,n])=>[d(e,t+1),d(n,t+1)])];if(r instanceof Set)return[f,"set",[...r].map(e=>d(e,t+1))];if(r instanceof ArrayBuffer)return[f,"bytes",m(new Uint8Array(r)),"ArrayBuffer"];if(ArrayBuffer.isView(r)){const e=r,n=e.constructor.name,a=new Uint8Array(e.buffer,e.byteOffset,e.byteLength);return n==="Uint8Array"?[f,"bytes",m(a)]:[f,"bytes",m(a),n]}if(Array.isArray(r)){const e=r.map(n=>d(n,t+1));return e.length>0&&e[0]===f?[f,"arr",e]:e}if(!U(r)){const e=r.constructor?.name??"value";throw new TypeError(`wire-codec: cannot encode a ${e} over the Lunora wire — only plain objects, arrays, and the supported built-ins (Date, Error, URL, Map, Set, ArrayBuffer/typed arrays, bigint) round-trip`)}const c=r,s={};for(const e of Object.keys(c)){const n=c[e];if(n===void 0)continue;const a=d(n,t+1);e===g?Object.defineProperty(s,e,{configurable:!0,enumerable:!0,value:a,writable:!0}):s[e]=a}return s},b=(r,t=0)=>{if(t>w)throw new RangeError(`wire-codec: value nesting exceeds the ${w}-level limit`);if(r===null||typeof r!="object")return r;if(Array.isArray(r)){if(r[0]===f)switch(r[1]){case"-inf":return-1/0;case"arr":return r[2].map(e=>b(e,t+1));case"bigint":{const e=r[2];if(typeof e!="string"||e.length>E||!/^-?\d+$/.test(e))throw new RangeError(`wire-codec: invalid or over-long bigint (max ${E} digits)`);return BigInt(e)}case"date":{const e=b(r[2],t+1);if(typeof e!="number")throw new TypeError("wire-codec: malformed date — epoch must be a number");return new Date(e)}case"map":{const e=r[2];return new Map(e.map(n=>{if(!Array.isArray(n)||n.length!==2)throw new TypeError("wire-codec: malformed map entry — expected a [key, value] pair");return[b(n[0],t+1),b(n[1],t+1)]}))}case"set":return new Set(r[2].map(e=>b(e,t+1)));case"url":{const e=r[2];if(typeof e!="string")throw new TypeError("wire-codec: malformed url — href must be a string");return new URL(e)}case"error":{const e=r[2],n=r[3];if(typeof e!="string"||typeof n!="string")throw new TypeError("wire-codec: malformed error — name and message must be strings");const a=(Object.hasOwn(O,e)?O[e]:void 0)??Error,i=new a(n);i.name!==e&&Object.defineProperty(i,"name",{configurable:!0,value:e,writable:!0});const u=b(r[4],t+1);if(u===null||typeof u!="object"||Array.isArray(u))throw new TypeError("wire-codec: malformed error — props must be an object");for(const y of Object.keys(u))y===g?Object.defineProperty(i,y,{configurable:!0,enumerable:!0,value:u[y],writable:!0}):i[y]=u[y];return r.length>5&&Object.defineProperty(i,"cause",{configurable:!0,value:b(r[5],t+1),writable:!0}),i}case"bytes":{const e=r[2];if(typeof e!="string")throw new TypeError("wire-codec: malformed bytes — payload must be a base64 string");const n=R(e);if(m(n)!==e)throw new TypeError("wire-codec: malformed bytes — payload must be canonical padded base64");const a=r[3]??"Uint8Array";if(a==="ArrayBuffer")return n.buffer.byteLength===n.byteLength?n.buffer:n.slice().buffer;const i=Object.hasOwn(j,a)?j[a]:void 0;return i?new i(n.slice().buffer):n}case"inf":return 1/0;case"nan":return Number.NaN;case"undefined":return;default:return r.map(e=>b(e,t+1))}return r.map(s=>b(s,t+1))}const o=r,c={};for(const s of Object.keys(o)){const e=b(o[s],t+1);s===g?Object.defineProperty(c,s,{configurable:!0,enumerable:!0,value:e,writable:!0}):c[s]=e}return c},B="/_lunora/rpc";class S extends p{constructor(t,o){super(t,o,{name:"ContainerBridgeError"})}}const k=(r,t)=>{let o=r;for(;o.endsWith("/");)o=o.slice(0,-1);return`${o}${t}`},T=(r,t)=>new Error(`createContainerBridge: request to "${r}" failed (status ${String(t.status)}${t.statusText?` ${t.statusText}`:""})`),$=async(r,t)=>{try{return await r.json()}catch{throw r.ok?new p("INTERNAL",`createContainerBridge: request to "${t}" returned a non-JSON response (status ${String(r.status)})`):T(t,r)}},N=r=>{if(typeof r.baseUrl!="string"||r.baseUrl.length===0)throw new TypeError("createContainerBridge: `baseUrl` must be a non-empty Worker URL (e.g. https://my-app.workers.dev) — is the URL env var set?");const t=r.fetch??globalThis.fetch,o=async(s,e={},n)=>{if(typeof t!="function")throw new TypeError("createContainerBridge: no `fetch` available — pass `fetch` in options for this runtime.");const a={"content-type":"application/json"};r.token!==void 0&&(a.authorization=`Bearer ${r.token}`);const i=await t(k(r.baseUrl,B),{body:JSON.stringify({args:d(e),functionPath:s,shardKey:n}),headers:a,method:"POST"}),u=await $(i,s);if(typeof u=="object"&&u!==null&&"error"in u){const{error:y}=u;if(typeof y=="object"&&y!==null){const{code:h,message:A}=y;if(typeof h=="string"&&typeof A=="string")throw new S(h,A)}let l;try{l=JSON.stringify(y)}catch{l=String(y)}throw new p("INTERNAL",`createContainerBridge: request to "${s}" returned a malformed error envelope (status ${String(i.status)}): ${l}`)}if(!i.ok)throw T(s,i);return b(u.result)};return{action:o,call:o,mutation:o,query:o,run:async(s,e,n)=>o(s.__lunoraRef,e,n)}};export{S as ContainerBridgeError,N as createContainerBridge};