@lunora/container 1.0.0-alpha.30 → 1.0.0-alpha.32

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/dist/bridge.d.mts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { LunoraError } from '@lunora/errors';
2
2
  /**
3
3
  * A `fetch` implementation — defaults to the runtime global.
4
- * @experimental
5
4
  */
6
5
  type FetchLike = (input: string, init: {
7
6
  body: string;
@@ -15,7 +14,6 @@ type FetchLike = (input: string, init: {
15
14
  }>;
16
15
  /**
17
16
  * `ContainerBridgeOptions` is part of the experimental `@lunora/container` API and may change without a major version bump.
18
- * @experimental
19
17
  */
20
18
  interface ContainerBridgeOptions {
21
19
  /**
@@ -35,7 +33,6 @@ interface ContainerBridgeOptions {
35
33
  }
36
34
  /**
37
35
  * Thrown when a Lunora function returns an error envelope. A `LunoraError` subclass carrying the wire `code`.
38
- * @experimental
39
36
  */
40
37
  declare class ContainerBridgeError extends LunoraError {
41
38
  constructor(code: string, message: string);
@@ -46,7 +43,6 @@ declare class ContainerBridgeError extends LunoraError {
46
43
  * imported) so the bridge stays dependency-free and its `.d.ts` is
47
44
  * self-contained; the `__lunoraPhantom` shape matches, so a real `api.x.y`
48
45
  * reference is assignable and its arg/return types are inferable.
49
- * @experimental
50
46
  */
51
47
  interface BridgeFunctionReference<Args = unknown, Result = unknown> {
52
48
  readonly __lunoraPhantom?: {
@@ -69,7 +65,6 @@ type ResultOfReference<Reference> = Reference extends {
69
65
  } ? Result : never;
70
66
  /**
71
67
  * `ContainerBridge` is part of the experimental `@lunora/container` API and may change without a major version bump.
72
- * @experimental
73
68
  */
74
69
  interface ContainerBridge {
75
70
  /** Call an `action` by `namespace:fn` path. Alias of {@link ContainerBridge.call} for intent. */
@@ -100,7 +95,6 @@ interface ContainerBridge {
100
95
  * `query`/`mutation`/`action` are intent-revealing aliases of one `call` — the
101
96
  * wire is identical and the server dispatches by the function's registered
102
97
  * kind, so a query path called via `.mutation(...)` still runs as a query.
103
- * @experimental
104
98
  */
105
99
  declare const createContainerBridge: (options: ContainerBridgeOptions) => ContainerBridge;
106
100
  export { type BridgeFunctionReference, type ContainerBridge, ContainerBridgeError, type ContainerBridgeOptions, type FetchLike, createContainerBridge };
package/dist/bridge.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { LunoraError } from '@lunora/errors';
2
2
  /**
3
3
  * A `fetch` implementation — defaults to the runtime global.
4
- * @experimental
5
4
  */
6
5
  type FetchLike = (input: string, init: {
7
6
  body: string;
@@ -15,7 +14,6 @@ type FetchLike = (input: string, init: {
15
14
  }>;
16
15
  /**
17
16
  * `ContainerBridgeOptions` is part of the experimental `@lunora/container` API and may change without a major version bump.
18
- * @experimental
19
17
  */
20
18
  interface ContainerBridgeOptions {
21
19
  /**
@@ -35,7 +33,6 @@ interface ContainerBridgeOptions {
35
33
  }
36
34
  /**
37
35
  * Thrown when a Lunora function returns an error envelope. A `LunoraError` subclass carrying the wire `code`.
38
- * @experimental
39
36
  */
40
37
  declare class ContainerBridgeError extends LunoraError {
41
38
  constructor(code: string, message: string);
@@ -46,7 +43,6 @@ declare class ContainerBridgeError extends LunoraError {
46
43
  * imported) so the bridge stays dependency-free and its `.d.ts` is
47
44
  * self-contained; the `__lunoraPhantom` shape matches, so a real `api.x.y`
48
45
  * reference is assignable and its arg/return types are inferable.
49
- * @experimental
50
46
  */
51
47
  interface BridgeFunctionReference<Args = unknown, Result = unknown> {
52
48
  readonly __lunoraPhantom?: {
@@ -69,7 +65,6 @@ type ResultOfReference<Reference> = Reference extends {
69
65
  } ? Result : never;
70
66
  /**
71
67
  * `ContainerBridge` is part of the experimental `@lunora/container` API and may change without a major version bump.
72
- * @experimental
73
68
  */
74
69
  interface ContainerBridge {
75
70
  /** Call an `action` by `namespace:fn` path. Alias of {@link ContainerBridge.call} for intent. */
@@ -100,7 +95,6 @@ interface ContainerBridge {
100
95
  * `query`/`mutation`/`action` are intent-revealing aliases of one `call` — the
101
96
  * wire is identical and the server dispatches by the function's registered
102
97
  * kind, so a query path called via `.mutation(...)` still runs as a query.
103
- * @experimental
104
98
  */
105
99
  declare const createContainerBridge: (options: ContainerBridgeOptions) => ContainerBridge;
106
100
  export { type BridgeFunctionReference, type ContainerBridge, ContainerBridgeError, type ContainerBridgeOptions, type FetchLike, createContainerBridge };
package/dist/bridge.mjs CHANGED
@@ -1 +1 @@
1
- import{LunoraError as f}from"@lunora/errors";const g="/_lunora/rpc";class y extends f{constructor(r,e){super(r,e,{name:"ContainerBridgeError"})}}const w=(t,r)=>{let e=t;for(;e.endsWith("/");)e=e.slice(0,-1);return`${e}${r}`},d=(t,r)=>new Error(`createContainerBridge: request to "${t}" failed (status ${String(r.status)}${r.statusText?` ${r.statusText}`:""})`),m=async(t,r)=>{try{return await t.json()}catch{throw t.ok?new f("INTERNAL",`createContainerBridge: request to "${r}" returned a non-JSON response (status ${String(t.status)})`):d(r,t)}},b=t=>{if(typeof t.baseUrl!="string"||t.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 r=t.fetch??globalThis.fetch,e=async(n,i={},c)=>{if(typeof r!="function")throw new TypeError("createContainerBridge: no `fetch` available — pass `fetch` in options for this runtime.");const l={"content-type":"application/json"};t.token!==void 0&&(l.authorization=`Bearer ${t.token}`);const s=await r(w(t.baseUrl,g),{body:JSON.stringify({args:i,functionPath:n,shardKey:c}),headers:l,method:"POST"}),o=await m(s,n);if(typeof o=="object"&&o!==null&&"error"in o){const{error:a}=o;if(typeof a=="object"&&a!==null){const{code:h,message:p}=a;if(typeof h=="string"&&typeof p=="string")throw new y(h,p)}let u;try{u=JSON.stringify(a)}catch{u=String(a)}throw new f("INTERNAL",`createContainerBridge: request to "${n}" returned a malformed error envelope (status ${String(s.status)}): ${u}`)}if(!s.ok)throw d(n,s);return o.result};return{action:e,call:e,mutation:e,query:e,run:async(n,i,c)=>e(n.__lunoraRef,i,c)}};export{y as ContainerBridgeError,b as createContainerBridge};
1
+ import{LunoraError as u}from"@lunora/errors";const h="/_lunora/rpc";class w extends u{constructor(r,e){super(r,e,{name:"ContainerBridgeError"})}}const p=(t,r)=>{let e=t;for(;e.endsWith("/");)e=e.slice(0,-1);return`${e}${r}`},g=(t,r)=>new Error(`createContainerBridge: request to "${t}" failed (status ${String(r.status)}${r.statusText?` ${r.statusText}`:""})`),m=async(t,r)=>{try{return await t.json()}catch{throw t.ok?new u("INTERNAL",`createContainerBridge: request to "${r}" returned a non-JSON response (status ${String(t.status)})`):g(r,t)}},B=t=>{if(typeof t.baseUrl!="string"||t.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 r=t.fetch??globalThis.fetch,e=async(n,i={},c)=>{if(typeof r!="function")throw new TypeError("createContainerBridge: no `fetch` available — pass `fetch` in options for this runtime.");const d={"content-type":"application/json"};t.token!==void 0&&(d.authorization=`Bearer ${t.token}`);const s=await r(p(t.baseUrl,h),{body:JSON.stringify({args:i,functionPath:n,shardKey:c}),headers:d,method:"POST"}),o=await m(s,n);if(typeof o=="object"&&o!==null&&"error"in o){const{error:a}=o;if(typeof a=="object"&&a!==null){const{code:f,message:y}=a;if(typeof f=="string"&&typeof y=="string")throw new w(f,y)}let l;try{l=JSON.stringify(a)}catch{l=String(a)}throw new u("INTERNAL",`createContainerBridge: request to "${n}" returned a malformed error envelope (status ${String(s.status)}): ${l}`)}if(!s.ok)throw g(n,s);return o.result};return{action:e,call:e,mutation:e,query:e,run:async(n,i,c)=>e(n.__lunoraRef,i,c)}};export{w as ContainerBridgeError,B as createContainerBridge};
@@ -1,5 +1,5 @@
1
+ import { C as ContainerDefinition, D as DurableObjectJurisdiction } from "../packem_shared/jurisdiction.d-KcQu2ZT_.mjs";
1
2
  import { DurableObject, WorkerEntrypoint } from 'cloudflare:workers';
2
- import { a as ContainerDefinition, D as DurableObjectJurisdiction } from "../packem_shared/jurisdiction.d-DeQ3rtYK.mjs";
3
3
  /**
4
4
  * ContainerStartOptions as they come from worker types
5
5
  */
@@ -500,7 +500,6 @@ type DurableObjectContext = ConstructorParameters<typeof Container>[0];
500
500
  * }
501
501
  * }
502
502
  * ```
503
- * @experimental
504
503
  */
505
504
  declare class LunoraContainer<Env = unknown> extends Container<Env> {
506
505
  /**
@@ -1,5 +1,5 @@
1
+ import { C as ContainerDefinition, D as DurableObjectJurisdiction } from "../packem_shared/jurisdiction.d-KcQu2ZT_.js";
1
2
  import { DurableObject, WorkerEntrypoint } from 'cloudflare:workers';
2
- import { a as ContainerDefinition, D as DurableObjectJurisdiction } from "../packem_shared/jurisdiction.d-DeQ3rtYK.js";
3
3
  /**
4
4
  * ContainerStartOptions as they come from worker types
5
5
  */
@@ -500,7 +500,6 @@ type DurableObjectContext = ConstructorParameters<typeof Container>[0];
500
500
  * }
501
501
  * }
502
502
  * ```
503
- * @experimental
504
503
  */
505
504
  declare class LunoraContainer<Env = unknown> extends Container<Env> {
506
505
  /**
package/dist/do/index.mjs CHANGED
@@ -1 +1 @@
1
- import{Container as S}from"../packem_shared/ContainerProxy-BsQAwSNX.mjs";import{ContainerProxy as O,outboundParams as L}from"../packem_shared/ContainerProxy-BsQAwSNX.mjs";import{LunoraError as u}from"@lunora/errors";import{resolveContainerEnvVars as g,parseDurationSeconds as v}from"../packem_shared/containerBindingName-CP4v-2Cw.mjs";import{e as y}from"../packem_shared/jurisdiction-BKRNOTip.mjs";const w="lunora",N=(i,t,n,e)=>({container:i,event:n,instance:t,level:n==="error"?"error":"info",message:e,source:w,ts:Date.now(),type:"container"}),a=(i,t,n,e)=>{const o=N(i,t,n,e),r=JSON.stringify(o);return n==="error"?console.error(r):console.log(r),o},$="__lunora_admin__:recordContainerEvent",h="__root__",E=i=>{if(i===null||typeof i!="object")return!1;const t=i;return typeof t.get=="function"&&typeof t.idFromName=="function"},I=(i,t)=>{const n=y(i,t);return typeof n.getByName=="function"?n.getByName(h):n.get(n.idFromName(h))},T=async(i,t,n)=>{try{const e=i??{},o=e.SHARD;if(!E(o))return;const r=typeof e.LUNORA_ADMIN_TOKEN=="string"?e.LUNORA_ADMIN_TOKEN:void 0;if(!r||r.length===0)return;const s=new Request("https://shard.internal/rpc",{body:JSON.stringify({args:{event:t},functionPath:$}),headers:{authorization:`Bearer ${r}`,"content-type":"application/json"},method:"POST"});await I(o,n).fetch(s)}catch{}},l=500,p=3e4,d="__lunoraHardTimeoutGeneration";class _ extends S{lunoraJurisdiction;lunoraName;lunoraDefaultPort;lunoraHardTimeoutSeconds;lunoraReadyOn;lunoraSecretsStore;lunoraSecretsStoreResolved;constructor(t,n,e,o,r){super(t,n,{defaultPort:e.defaultPort,entrypoint:e.entrypoint?[...e.entrypoint]:void 0,envVars:g(e,n,o),sleepAfter:e.sleepAfter}),e.enableInternet!==void 0&&(this.enableInternet=e.enableInternet),e.requiredPorts!==void 0&&(this.requiredPorts=[...e.requiredPorts]),e.interceptHttps!==void 0&&(this.interceptHttps=e.interceptHttps),e.allowedHosts!==void 0&&(this.allowedHosts=[...e.allowedHosts]),e.deniedHosts!==void 0&&(this.deniedHosts=[...e.deniedHosts]),e.pingEndpoint!==void 0&&(this.pingEndpoint=e.pingEndpoint),e.labels!==void 0&&(this.labels={...e.labels}),this.lunoraName=o??"container",this.lunoraJurisdiction=r,this.lunoraDefaultPort=e.defaultPort,this.lunoraReadyOn=e.readyOn?[...e.readyOn]:[],this.lunoraHardTimeoutSeconds=e.hardTimeout===void 0?void 0:v(e.hardTimeout),this.lunoraSecretsStore=e.secretsStore}async containerFetch(...t){return await this.resolveSecretsStoreEnv(),super.containerFetch(...t)}async start(...t){const[n]=t;return n?.envVars===void 0&&await this.resolveSecretsStoreEnv(),super.start(...t)}async onActivityExpired(){const t=a(this.lunoraName,this.instanceId(),"sleep");this.surfaceInStudioLogs(t),await super.onActivityExpired()}onError(t){const n=a(this.lunoraName,this.instanceId(),"error",t instanceof Error?t.message:String(t));return this.surfaceInStudioLogs(n),super.onError(t)}async onStart(){const t=a(this.lunoraName,this.instanceId(),"start");this.surfaceInStudioLogs(t),await super.onStart(),await this.armHardTimeout(),await this.awaitContainerReadiness()}async onHardTimeoutExpired(t){const n=await this.ctx.storage.get(d);if(t?.generation!==void 0&&t.generation!==n||this.ctx.container?.running!==!0)return;const e=a(this.lunoraName,this.instanceId(),"stop","hard timeout reached");this.surfaceInStudioLogs(e),await this.stop()}async onStop(t){const n=a(this.lunoraName,this.instanceId(),"stop",`${t.reason} (exit ${String(t.exitCode)})`);this.surfaceInStudioLogs(n),await super.onStop(t)}async armHardTimeout(){if(this.lunoraHardTimeoutSeconds===void 0)return;const t=(await this.ctx.storage.get(d)??0)+1;await this.ctx.storage.put(d,t),await this.schedule(this.lunoraHardTimeoutSeconds,"onHardTimeoutExpired",{generation:t})}async resolveSecretsStoreEnv(){const t=this.lunoraSecretsStore;t!==void 0&&(this.lunoraSecretsStoreResolved??=(async()=>{const n=this.env,e={};for(const[o,r]of Object.entries(t)){const s=n[r];if(s===void 0||typeof s.get!="function")throw new u("INTERNAL",`container "${this.lunoraName}": secretsStore env "${o}" points at binding "${r}", which is not a Secrets Store binding on the Worker env. Add a \`secrets_store_secrets\` entry binding "${r}".`);const c=await s.get();if(typeof c!="string")throw new TypeError(`container "${this.lunoraName}": Secrets Store binding "${r}" (env "${o}") did not resolve to a string value.`);e[o]=c}this.envVars={...this.envVars,...e}})().catch(n=>{throw this.lunoraSecretsStoreResolved=void 0,n}),await this.lunoraSecretsStoreResolved)}async awaitContainerReadiness(){if(this.lunoraReadyOn.length===0)return;const{container:t}=this.ctx;if(t===void 0)return;const n=Date.now()+p;await Promise.all(this.lunoraReadyOn.map(async e=>this.awaitReadinessCheck(t,e,n)))}async awaitReadinessCheck(t,n,e){const o=n.port??this.lunoraDefaultPort;if(o===void 0)throw new u("INTERNAL",`container "${this.lunoraName}": readyOn check "${n.path}" has no port — set the check's \`port\` or the container \`defaultPort\`.`);const r=n.status??200,s=n.path.startsWith("/")?n.path:`/${n.path}`,c=t.getTcpPort(o);for(;;){const f=Math.max(l,e-Date.now());try{if((await c.fetch(`http://container${s}`,{signal:AbortSignal.timeout(f)})).status===r)return}catch{}if(Date.now()>=e)throw new u("INTERNAL",`container "${this.lunoraName}": readiness check "${n.path}" (port ${String(o)}) did not return ${String(r)} within ${String(p)}ms`);await new Promise(m=>{setTimeout(m,l)})}}surfaceInStudioLogs(t){T(this.env,t,this.lunoraJurisdiction).catch(()=>{})}instanceId(){try{const{id:t}=this.ctx;return typeof t?.toString=="function"?t.toString():"unknown"}catch{return"unknown"}}}export{O as ContainerProxy,_ as LunoraContainer,L as outboundParams};
1
+ import{LunoraError as u}from"@lunora/errors";import{resolveContainerEnvVars as m,parseDurationSeconds as y}from"../packem_shared/containerBindingName-D01FopQt.mjs";import{a as g}from"../packem_shared/jurisdiction-DtE9s70w.mjs";import{Container as f}from"../packem_shared/ContainerProxy-fTW_pXWY.mjs";import{ContainerProxy as b,outboundParams as x}from"../packem_shared/ContainerProxy-fTW_pXWY.mjs";const w="lunora",E=(s,t,r,e)=>({container:s,event:r,instance:t,level:r==="error"?"error":"info",message:e,source:w,ts:Date.now(),type:"container"}),i=(s,t,r,e)=>{const o=E(s,t,r,e),n=JSON.stringify(o);return r==="error"?console.error(n):console.log(n),o},N="__lunora_admin__:recordContainerEvent",d="__root__",R=s=>{if(s===null||typeof s!="object")return!1;const t=s;return typeof t.get=="function"&&typeof t.idFromName=="function"},_=(s,t)=>{const r=g(s,t);return typeof r.getByName=="function"?r.getByName(d):r.get(r.idFromName(d))},T=async(s,t,r)=>{try{const e=s??{},o=e.SHARD;if(!R(o))return;const n=typeof e.LUNORA_ADMIN_TOKEN=="string"?e.LUNORA_ADMIN_TOKEN:void 0;if(!n||n.length===0)return;const a=new Request("https://shard.internal/rpc",{body:JSON.stringify({args:{event:t},functionPath:N}),headers:{authorization:`Bearer ${n}`,"content-type":"application/json"},method:"POST"});await _(o,r).fetch(a)}catch{}},p=500,S=3e4,h="__lunoraHardTimeoutGeneration";class L extends f{lunoraJurisdiction;lunoraName;lunoraDefaultPort;lunoraHardTimeoutSeconds;lunoraReadyOn;lunoraSecretsStore;lunoraSecretsStoreResolved;constructor(t,r,e,o,n){super(t,r,{defaultPort:e.defaultPort,entrypoint:e.entrypoint?[...e.entrypoint]:void 0,envVars:m(e,r,o),sleepAfter:e.sleepAfter}),e.enableInternet!==void 0&&(this.enableInternet=e.enableInternet),e.requiredPorts!==void 0&&(this.requiredPorts=[...e.requiredPorts]),e.interceptHttps!==void 0&&(this.interceptHttps=e.interceptHttps),e.allowedHosts!==void 0&&(this.allowedHosts=[...e.allowedHosts]),e.deniedHosts!==void 0&&(this.deniedHosts=[...e.deniedHosts]),e.pingEndpoint!==void 0&&(this.pingEndpoint=e.pingEndpoint),e.labels!==void 0&&(this.labels={...e.labels}),this.lunoraName=o??"container",this.lunoraJurisdiction=n,this.lunoraDefaultPort=e.defaultPort,this.lunoraReadyOn=e.readyOn?[...e.readyOn]:[],this.lunoraHardTimeoutSeconds=e.hardTimeout===void 0?void 0:y(e.hardTimeout),this.lunoraSecretsStore=e.secretsStore}async containerFetch(...t){return await this.resolveSecretsStoreEnv(),super.containerFetch(...t)}async start(...t){const[r]=t;return r?.envVars===void 0&&await this.resolveSecretsStoreEnv(),super.start(...t)}async onActivityExpired(){const t=i(this.lunoraName,this.instanceId(),"sleep");this.surfaceInStudioLogs(t),await super.onActivityExpired()}onError(t){const r=i(this.lunoraName,this.instanceId(),"error",t instanceof Error?t.message:String(t));return this.surfaceInStudioLogs(r),super.onError(t)}async onStart(){const t=i(this.lunoraName,this.instanceId(),"start");this.surfaceInStudioLogs(t),await super.onStart(),await this.armHardTimeout(),await this.awaitContainerReadiness()}async onHardTimeoutExpired(t){const r=await this.ctx.storage.get(h);if(t?.generation!==void 0&&t.generation!==r||this.ctx.container?.running!==!0)return;const e=i(this.lunoraName,this.instanceId(),"stop","hard timeout reached");this.surfaceInStudioLogs(e),await this.stop()}async onStop(t){const r=i(this.lunoraName,this.instanceId(),"stop",`${t.reason} (exit ${String(t.exitCode)})`);this.surfaceInStudioLogs(r),await super.onStop(t)}async armHardTimeout(){if(this.lunoraHardTimeoutSeconds===void 0)return;const t=(await this.ctx.storage.get(h)??0)+1;await this.ctx.storage.put(h,t),await this.schedule(this.lunoraHardTimeoutSeconds,"onHardTimeoutExpired",{generation:t})}async resolveSecretsStoreEnv(){const t=this.lunoraSecretsStore;t!==void 0&&(this.lunoraSecretsStoreResolved??=(async()=>{const r=this.env,e={};for(const[o,n]of Object.entries(t)){const a=r[n];if(a===void 0||typeof a.get!="function")throw new u("INTERNAL",`container "${this.lunoraName}": secretsStore env "${o}" points at binding "${n}", which is not a Secrets Store binding on the Worker env. Add a \`secrets_store_secrets\` entry binding "${n}".`);const c=await a.get();if(typeof c!="string")throw new TypeError(`container "${this.lunoraName}": Secrets Store binding "${n}" (env "${o}") did not resolve to a string value.`);e[o]=c}this.envVars={...this.envVars,...e}})().catch(r=>{throw this.lunoraSecretsStoreResolved=void 0,r}),await this.lunoraSecretsStoreResolved)}async awaitContainerReadiness(){if(this.lunoraReadyOn.length===0)return;const{container:t}=this.ctx;if(t===void 0)return;const r=Date.now()+S;await Promise.all(this.lunoraReadyOn.map(async e=>this.awaitReadinessCheck(t,e,r)))}async awaitReadinessCheck(t,r,e){const o=r.port??this.lunoraDefaultPort;if(o===void 0)throw new u("INTERNAL",`container "${this.lunoraName}": readyOn check "${r.path}" has no port — set the check's \`port\` or the container \`defaultPort\`.`);const n=r.status??200,a=r.path.startsWith("/")?r.path:`/${r.path}`,c=t.getTcpPort(o);for(;;){const v=Math.max(p,e-Date.now());try{if((await c.fetch(`http://container${a}`,{signal:AbortSignal.timeout(v)})).status===n)return}catch{}if(Date.now()>=e)throw new u("INTERNAL",`container "${this.lunoraName}": readiness check "${r.path}" (port ${String(o)}) did not return ${String(n)} within ${String(S)}ms`);await new Promise(l=>{setTimeout(l,p)})}}surfaceInStudioLogs(t){T(this.env,t,this.lunoraJurisdiction).catch(()=>{})}instanceId(){try{const{id:t}=this.ctx;return typeof t?.toString=="function"?t.toString():"unknown"}catch{return"unknown"}}}export{b as ContainerProxy,L as LunoraContainer,x as outboundParams};
package/dist/index.d.mts CHANGED
@@ -1,8 +1,74 @@
1
- import { D as DurableObjectJurisdiction, C as ContainerConfig, a as ContainerDefinition, b as ContainerImageSource, N as NormalizedContainerImage } from "./packem_shared/jurisdiction.d-DeQ3rtYK.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-DeQ3rtYK.mjs";
1
+ import { D as DurableObjectJurisdiction, a as ContainerConfig, C as ContainerDefinition, b as ContainerImageSource, N as NormalizedContainerImage } from "./packem_shared/jurisdiction.d-KcQu2ZT_.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-KcQu2ZT_.mjs";
3
+ /**
4
+ * Per-call options for `ContainerHandle.exec`.
5
+ */
6
+ interface ContainerExecOptions {
7
+ /** Arguments passed to `command`, unshelled — the container must not concatenate them into a shell string. */
8
+ args?: ReadonlyArray<string>;
9
+ /** Working directory for the command, relative to the container's own root. */
10
+ cwd?: string;
11
+ /** Extra environment for this command only, merged over the container's env. */
12
+ env?: Readonly<Record<string, string>>;
13
+ /**
14
+ * Cap on the response body, in bytes. The whole `{code,stdout,stderr}`
15
+ * document has to be held in memory to be parsed, and a build box or job
16
+ * runner routinely writes tens of megabytes — a single unbounded `exec`
17
+ * that returns more than the isolate's memory limit kills the isolate and
18
+ * every other in-flight request sharing it, not just this call. So a
19
+ * runner that overruns the cap fails the call loudly rather than taking
20
+ * the shard down with it; raise this (or have the runner cap its own
21
+ * output) when a command legitimately produces more.
22
+ *
23
+ * Default {@link DEFAULT_EXEC_MAX_OUTPUT_BYTES} (1MB).
24
+ */
25
+ maxOutputBytes?: number;
26
+ /**
27
+ * Abort the call. Composed with {@link ContainerExecOptions.timeoutMs} when
28
+ * both are given — whichever fires first wins.
29
+ */
30
+ signal?: AbortSignal;
31
+ /**
32
+ * Give up after this many ms. Covers the whole call — the request *and*
33
+ * reading the response body, since `fetch` resolves on headers and a
34
+ * runner that answers `200` and then stalls mid-body would otherwise sit
35
+ * past the deadline. Sent to the container as well, so a well-behaved
36
+ * runner can kill the process rather than leak it when the caller walks
37
+ * away.
38
+ */
39
+ timeoutMs?: number;
40
+ }
41
+ /**
42
+ * The outcome of a `ContainerHandle.exec` call.
43
+ *
44
+ * A non-zero `code` is **not** an error: a command that ran and failed is a
45
+ * result, and the caller decides what to do with it. Only a failure to *run*
46
+ * the command — transport, a non-2xx from the runner, an unparseable body —
47
+ * throws.
48
+ */
49
+ interface ContainerExecResult {
50
+ /** Process exit code. Non-zero means the command ran and failed. */
51
+ code: number;
52
+ /** Everything the command wrote to stderr. */
53
+ stderr: string;
54
+ /** Everything the command wrote to stdout. */
55
+ stdout: string;
56
+ }
57
+ /**
58
+ * The route `ContainerHandle.exec` POSTs to. Namespaced under
59
+ * `/__lunora/` so it cannot collide with an application route the container
60
+ * already serves — the previous ad-hoc convention was a bare `/exec`, which an
61
+ * app could plausibly own for its own purposes.
62
+ *
63
+ * Exported because it is a *contract*, not an implementation detail: a
64
+ * container image has to serve exactly this route, and `@lunora/agent`'s
65
+ * human-in-the-loop gate has to recognise it. Both had it hard-coded as a
66
+ * second literal, which drifts silently — and on the gate's side, drift
67
+ * un-gates model-chosen command execution.
68
+ */
69
+ declare const CONTAINER_EXEC_PATH = "/__lunora/exec";
3
70
  /**
4
71
  * Options for explicitly starting an instance (mirrors `@cloudflare/containers`).
5
- * @experimental
6
72
  */
7
73
  interface ContainerStartOptions {
8
74
  /** Override outbound internet access for this start. */
@@ -16,7 +82,6 @@ interface ContainerStartOptions {
16
82
  }
17
83
  /**
18
84
  * A container instance's runtime state, as returned by `getState()`. Structural — the platform adds fields over time.
19
- * @experimental
20
85
  */
21
86
  interface ContainerInstanceState {
22
87
  [key: string]: unknown;
@@ -44,7 +109,6 @@ interface ContainerStubLike {
44
109
  }
45
110
  /**
46
111
  * What the client needs from a Durable Object namespace binding.
47
- * @experimental
48
112
  */
49
113
  interface ContainerNamespaceLike {
50
114
  get: (id: unknown) => ContainerStubLike;
@@ -57,9 +121,24 @@ interface ContainerNamespaceLike {
57
121
  }
58
122
  /**
59
123
  * A handle on one container instance (one Durable Object).
60
- * @experimental
61
124
  */
62
125
  interface ContainerHandle {
126
+ /**
127
+ * Run a command inside the container and return its exit code and output.
128
+ *
129
+ * A container is an HTTP server, so there is no platform-level exec to call:
130
+ * the command is POSTed to `/__lunora/exec` and the container app
131
+ * serves that route. What this method adds over hand-rolling that fetch is
132
+ * the **contract** — a pinned path, a typed request and response, and the
133
+ * distinction between "the command failed" (a `code`) and "the command could
134
+ * not be run" (a throw). Before it existed every caller invented its own
135
+ * `/exec` convention and read the raw body back as if it were output, which
136
+ * silently turned a runner's 500 into a successful-looking result.
137
+ *
138
+ * The container side must accept `{ args, command, cwd, env, timeoutMs }`
139
+ * and answer `{ code, stdout, stderr }` as JSON.
140
+ */
141
+ exec: (command: string, options?: ContainerExecOptions) => Promise<ContainerExecResult>;
63
142
  /**
64
143
  * Send an HTTP (or WebSocket-upgrade) request to the container. A path
65
144
  * string (`"/transcode"`) is resolved against a synthetic origin; a full
@@ -82,7 +161,6 @@ interface ContainerHandle {
82
161
  * game, a job runner per id) often needs to tear down or inspect the instance
83
162
  * rather than wait for `sleepAfter`, so these wrap the container DO's
84
163
  * `start`/`stop`/`destroy`/`getState`.
85
- * @experimental
86
164
  */
87
165
  interface ContainerInstanceHandle extends ContainerHandle {
88
166
  /** Stop and discard the instance (its ephemeral disk is lost). */
@@ -117,7 +195,6 @@ interface ContainerInstanceHandle extends ContainerHandle {
117
195
  * Each maps to the corresponding `@cloudflare/containers` `Container` RPC, so
118
196
  * an app can tighten or relax a single instance's allowed/denied hosts after
119
197
  * start without redeploying.
120
- * @experimental
121
198
  */
122
199
  interface ContainerEgressControls {
123
200
  /** Add one hostname (or glob) to the allow-list. */
@@ -135,7 +212,6 @@ interface ContainerEgressControls {
135
212
  }
136
213
  /**
137
214
  * The per-definition accessor exposed as `ctx.containers.<exportName>`.
138
- * @experimental
139
215
  */
140
216
  interface ContainerAccessor {
141
217
  /**
@@ -178,7 +254,6 @@ interface ContainerAccessor {
178
254
  }
179
255
  /**
180
256
  * Tuning for a pooled, retrying container handle. See {@link ContainerAccessor.pool}.
181
- * @experimental
182
257
  */
183
258
  interface PoolOptions {
184
259
  /** Total attempts before giving up (each on a freshly-picked instance). Default 3. */
@@ -195,6 +270,10 @@ interface PoolOptions {
195
270
  * Whether a *returned* response should be retried on another instance.
196
271
  * Defaults to retrying any `5xx`. A thrown error (network/start failure) is
197
272
  * always retried regardless of this predicate.
273
+ *
274
+ * Applies to `fetch` only. `exec` retries on the cold-start transients
275
+ * alone, because its caller never chose the request and a command that
276
+ * already ran must not be re-run just because the runner failed afterwards.
198
277
  */
199
278
  retryOn?: (response: Response) => boolean;
200
279
  /** Pool size to spread picks across. Defaults to the definition's `maxInstances`, else 3. */
@@ -205,7 +284,6 @@ interface PoolOptions {
205
284
  * only on the platform's provisioning transients (no-instance / not-listening /
206
285
  * rate-limited — see {@link isColdStartTransient}), which is why it's safe by
207
286
  * default: those responses mean the request never reached the container.
208
- * @experimental
209
287
  */
210
288
  interface InstanceRetryOptions {
211
289
  /**
@@ -221,7 +299,6 @@ interface InstanceRetryOptions {
221
299
  }
222
300
  /**
223
301
  * Wiring info for one definition, emitted by codegen into the generated DO.
224
- * @experimental
225
302
  */
226
303
  interface ContainerBindingSpec {
227
304
  /** Durable Object binding name, e.g. `CONTAINER_TRANSCODER`. */
@@ -240,12 +317,10 @@ interface ContainerBindingSpec {
240
317
  * `traceparent` (the inbound RPC's W3C trace context, forwarded by the runtime
241
318
  * and read off the request by the DO) is stamped onto every outbound container
242
319
  * `fetch`, so the container's own spans stitch under the Worker's trace.
243
- * @experimental
244
320
  */
245
321
  declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>, jurisdiction?: DurableObjectJurisdiction, traceparent?: string) => Record<string, ContainerAccessor>;
246
322
  /**
247
323
  * A test handler: receives the request plus the targeted instance name.
248
- * @experimental
249
324
  */
250
325
  type ContainerTestHandler = (request: Request, instance: {
251
326
  name: string;
@@ -260,7 +335,6 @@ type ContainerTestHandler = (request: Request, instance: {
260
335
  * transcoder: (request) => new Response("ok"),
261
336
  * });
262
337
  * ```
263
- * @experimental
264
338
  */
265
339
  declare const createContainerTestContext: (handlers: Record<string, ContainerTestHandler>) => Record<string, ContainerAccessor>;
266
340
  /**
@@ -272,7 +346,6 @@ declare const createContainerTestContext: (handlers: Record<string, ContainerTes
272
346
  * `Dockerfile.dev` also counts) is used as-is with its directory as the build
273
347
  * context; any other path is treated as the build-context directory and the
274
348
  * Dockerfile is expected at `<dir>/Dockerfile`.
275
- * @experimental
276
349
  */
277
350
  declare const normalizeContainerImage: (image: ContainerImageSource) => NormalizedContainerImage;
278
351
  /**
@@ -280,7 +353,6 @@ declare const normalizeContainerImage: (image: ContainerImageSource) => Normaliz
280
353
  * `transcoder` → `TranscoderContainer`. wrangler's `containers[].class_name`
281
354
  * and the Durable Object binding's `class_name` both reference it, so codegen
282
355
  * and the config layer MUST derive it identically — always via this helper.
283
- * @experimental
284
356
  */
285
357
  declare const containerClassName: (exportName: string) => string;
286
358
  /**
@@ -288,7 +360,6 @@ declare const containerClassName: (exportName: string) => string;
288
360
  * `CONTAINER_TRANSCODER`, `imageResizer` → `CONTAINER_IMAGE_RESIZER`. The
289
361
  * `CONTAINER_` prefix namespaces these away from `SHARD`/`SESSION`/`SCHEDULER`
290
362
  * so a container export can never collide with the built-in bindings.
291
- * @experimental
292
363
  */
293
364
  declare const containerBindingName: (exportName: string) => string;
294
365
  /**
@@ -297,17 +368,14 @@ declare const containerBindingName: (exportName: string) => string;
297
368
  * it as the wrangler `containers[].image`, and `lunora deploy` builds that tag
298
369
  * with Railpack and `wrangler containers push`es it before deploying — so all
299
370
  * three derive the tag from this one helper and can never disagree.
300
- * @experimental
301
371
  */
302
372
  declare const containerBuildTag: (exportName: string) => string;
303
373
  /**
304
374
  * `defineContainer` is part of the experimental `@lunora/container` API and may change without a major version bump.
305
- * @experimental
306
375
  */
307
376
  declare const defineContainer: (config: ContainerConfig) => ContainerDefinition;
308
377
  /**
309
378
  * True when a value is a `defineContainer` result (the runtime brand check).
310
- * @experimental
311
379
  */
312
380
  declare const isContainerDefinition: (value: unknown) => value is ContainerDefinition;
313
381
  /**
@@ -315,7 +383,138 @@ declare const isContainerDefinition: (value: unknown) => value is ContainerDefin
315
383
  * plus every declared secret resolved from the Worker `env`. A declared secret
316
384
  * missing from the Worker env fails fast — starting the container without a
317
385
  * credential it was promised yields far worse errors downstream.
318
- * @experimental
319
386
  */
320
387
  declare const resolveContainerEnvVariables: (definition: ContainerDefinition, workerEnv: Record<string, unknown>, exportName?: string) => Record<string, string>;
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 };
388
+ export { CONTAINER_EXEC_PATH,
389
+ /**
390
+ * `@lunora/container` — Cloudflare Containers for Lunora.
391
+ *
392
+ * This root export is Node-safe (no Cloudflare runtime imports): the
393
+ * `defineContainer` authoring surface, the naming/normalization helpers
394
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
395
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
396
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
397
+ * behind the `@lunora/container/do` subpath.
398
+ */
399
+ type ContainerAccessor,
400
+ /**
401
+ * `@lunora/container` — Cloudflare Containers for Lunora.
402
+ *
403
+ * This root export is Node-safe (no Cloudflare runtime imports): the
404
+ * `defineContainer` authoring surface, the naming/normalization helpers
405
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
406
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
407
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
408
+ * behind the `@lunora/container/do` subpath.
409
+ */
410
+ type ContainerBindingSpec, type ContainerConfig, type ContainerDefinition,
411
+ /**
412
+ * `@lunora/container` — Cloudflare Containers for Lunora.
413
+ *
414
+ * This root export is Node-safe (no Cloudflare runtime imports): the
415
+ * `defineContainer` authoring surface, the naming/normalization helpers
416
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
417
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
418
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
419
+ * behind the `@lunora/container/do` subpath.
420
+ */
421
+ type ContainerEgressControls, type ContainerExecOptions, type ContainerExecResult,
422
+ /**
423
+ * `@lunora/container` — Cloudflare Containers for Lunora.
424
+ *
425
+ * This root export is Node-safe (no Cloudflare runtime imports): the
426
+ * `defineContainer` authoring surface, the naming/normalization helpers
427
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
428
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
429
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
430
+ * behind the `@lunora/container/do` subpath.
431
+ */
432
+ type ContainerHandle, type ContainerImageSource,
433
+ /**
434
+ * `@lunora/container` — Cloudflare Containers for Lunora.
435
+ *
436
+ * This root export is Node-safe (no Cloudflare runtime imports): the
437
+ * `defineContainer` authoring surface, the naming/normalization helpers
438
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
439
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
440
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
441
+ * behind the `@lunora/container/do` subpath.
442
+ */
443
+ type ContainerInstanceHandle,
444
+ /**
445
+ * `@lunora/container` — Cloudflare Containers for Lunora.
446
+ *
447
+ * This root export is Node-safe (no Cloudflare runtime imports): the
448
+ * `defineContainer` authoring surface, the naming/normalization helpers
449
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
450
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
451
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
452
+ * behind the `@lunora/container/do` subpath.
453
+ */
454
+ type ContainerInstanceState,
455
+ /**
456
+ * `@lunora/container` — Cloudflare Containers for Lunora.
457
+ *
458
+ * This root export is Node-safe (no Cloudflare runtime imports): the
459
+ * `defineContainer` authoring surface, the naming/normalization helpers
460
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
461
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
462
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
463
+ * behind the `@lunora/container/do` subpath.
464
+ */
465
+ type ContainerNamespaceLike,
466
+ /**
467
+ * `@lunora/container` — Cloudflare Containers for Lunora.
468
+ *
469
+ * This root export is Node-safe (no Cloudflare runtime imports): the
470
+ * `defineContainer` authoring surface, the naming/normalization helpers
471
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
472
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
473
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
474
+ * behind the `@lunora/container/do` subpath.
475
+ */
476
+ type ContainerStartOptions,
477
+ /**
478
+ * `@lunora/container` — Cloudflare Containers for Lunora.
479
+ *
480
+ * This root export is Node-safe (no Cloudflare runtime imports): the
481
+ * `defineContainer` authoring surface, the naming/normalization helpers
482
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
483
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
484
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
485
+ * behind the `@lunora/container/do` subpath.
486
+ */
487
+ type ContainerTestHandler,
488
+ /**
489
+ * `@lunora/container` — Cloudflare Containers for Lunora.
490
+ *
491
+ * This root export is Node-safe (no Cloudflare runtime imports): the
492
+ * `defineContainer` authoring surface, the naming/normalization helpers
493
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
494
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
495
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
496
+ * behind the `@lunora/container/do` subpath.
497
+ */
498
+ type DurableObjectJurisdiction,
499
+ /**
500
+ * `@lunora/container` — Cloudflare Containers for Lunora.
501
+ *
502
+ * This root export is Node-safe (no Cloudflare runtime imports): the
503
+ * `defineContainer` authoring surface, the naming/normalization helpers
504
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
505
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
506
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
507
+ * behind the `@lunora/container/do` subpath.
508
+ */
509
+ type InstanceRetryOptions, type NormalizedContainerImage,
510
+ /**
511
+ * `@lunora/container` — Cloudflare Containers for Lunora.
512
+ *
513
+ * This root export is Node-safe (no Cloudflare runtime imports): the
514
+ * `defineContainer` authoring surface, the naming/normalization helpers
515
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
516
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
517
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
518
+ * behind the `@lunora/container/do` subpath.
519
+ */
520
+ type PoolOptions, containerBindingName, containerBuildTag, containerClassName, createContainerContext, createContainerTestContext, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVariables as resolveContainerEnvVars };