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

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.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 { DurableObject, WorkerEntrypoint } from 'cloudflare:workers';
2
1
  import { a as ContainerDefinition, D as DurableObjectJurisdiction } from "../packem_shared/jurisdiction.d-DeQ3rtYK.mjs";
2
+ import { DurableObject, WorkerEntrypoint } from 'cloudflare:workers';
3
3
  /**
4
4
  * ContainerStartOptions as they come from worker types
5
5
  */
@@ -1,5 +1,5 @@
1
- import { DurableObject, WorkerEntrypoint } from 'cloudflare:workers';
2
1
  import { a as ContainerDefinition, D as DurableObjectJurisdiction } from "../packem_shared/jurisdiction.d-DeQ3rtYK.js";
2
+ import { DurableObject, WorkerEntrypoint } from 'cloudflare:workers';
3
3
  /**
4
4
  * ContainerStartOptions as they come from worker types
5
5
  */
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
@@ -318,4 +318,136 @@ declare const isContainerDefinition: (value: unknown) => value is ContainerDefin
318
318
  * @experimental
319
319
  */
320
320
  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 };
321
+ export {
322
+ /**
323
+ * `@lunora/container` — Cloudflare Containers for Lunora.
324
+ *
325
+ * This root export is Node-safe (no Cloudflare runtime imports): the
326
+ * `defineContainer` authoring surface, the naming/normalization helpers
327
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
328
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
329
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
330
+ * behind the `@lunora/container/do` subpath.
331
+ */
332
+ type ContainerAccessor,
333
+ /**
334
+ * `@lunora/container` — Cloudflare Containers for Lunora.
335
+ *
336
+ * This root export is Node-safe (no Cloudflare runtime imports): the
337
+ * `defineContainer` authoring surface, the naming/normalization helpers
338
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
339
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
340
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
341
+ * behind the `@lunora/container/do` subpath.
342
+ */
343
+ type ContainerBindingSpec, type ContainerConfig, type ContainerDefinition,
344
+ /**
345
+ * `@lunora/container` — Cloudflare Containers for Lunora.
346
+ *
347
+ * This root export is Node-safe (no Cloudflare runtime imports): the
348
+ * `defineContainer` authoring surface, the naming/normalization helpers
349
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
350
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
351
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
352
+ * behind the `@lunora/container/do` subpath.
353
+ */
354
+ type ContainerEgressControls,
355
+ /**
356
+ * `@lunora/container` — Cloudflare Containers for Lunora.
357
+ *
358
+ * This root export is Node-safe (no Cloudflare runtime imports): the
359
+ * `defineContainer` authoring surface, the naming/normalization helpers
360
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
361
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
362
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
363
+ * behind the `@lunora/container/do` subpath.
364
+ */
365
+ type ContainerHandle, type ContainerImageSource,
366
+ /**
367
+ * `@lunora/container` — Cloudflare Containers for Lunora.
368
+ *
369
+ * This root export is Node-safe (no Cloudflare runtime imports): the
370
+ * `defineContainer` authoring surface, the naming/normalization helpers
371
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
372
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
373
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
374
+ * behind the `@lunora/container/do` subpath.
375
+ */
376
+ type ContainerInstanceHandle,
377
+ /**
378
+ * `@lunora/container` — Cloudflare Containers for Lunora.
379
+ *
380
+ * This root export is Node-safe (no Cloudflare runtime imports): the
381
+ * `defineContainer` authoring surface, the naming/normalization helpers
382
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
383
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
384
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
385
+ * behind the `@lunora/container/do` subpath.
386
+ */
387
+ type ContainerInstanceState,
388
+ /**
389
+ * `@lunora/container` — Cloudflare Containers for Lunora.
390
+ *
391
+ * This root export is Node-safe (no Cloudflare runtime imports): the
392
+ * `defineContainer` authoring surface, the naming/normalization helpers
393
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
394
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
395
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
396
+ * behind the `@lunora/container/do` subpath.
397
+ */
398
+ type ContainerNamespaceLike,
399
+ /**
400
+ * `@lunora/container` — Cloudflare Containers for Lunora.
401
+ *
402
+ * This root export is Node-safe (no Cloudflare runtime imports): the
403
+ * `defineContainer` authoring surface, the naming/normalization helpers
404
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
405
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
406
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
407
+ * behind the `@lunora/container/do` subpath.
408
+ */
409
+ type ContainerStartOptions,
410
+ /**
411
+ * `@lunora/container` — Cloudflare Containers for Lunora.
412
+ *
413
+ * This root export is Node-safe (no Cloudflare runtime imports): the
414
+ * `defineContainer` authoring surface, the naming/normalization helpers
415
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
416
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
417
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
418
+ * behind the `@lunora/container/do` subpath.
419
+ */
420
+ type ContainerTestHandler,
421
+ /**
422
+ * `@lunora/container` — Cloudflare Containers for Lunora.
423
+ *
424
+ * This root export is Node-safe (no Cloudflare runtime imports): the
425
+ * `defineContainer` authoring surface, the naming/normalization helpers
426
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
427
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
428
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
429
+ * behind the `@lunora/container/do` subpath.
430
+ */
431
+ type DurableObjectJurisdiction,
432
+ /**
433
+ * `@lunora/container` — Cloudflare Containers for Lunora.
434
+ *
435
+ * This root export is Node-safe (no Cloudflare runtime imports): the
436
+ * `defineContainer` authoring surface, the naming/normalization helpers
437
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
438
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
439
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
440
+ * behind the `@lunora/container/do` subpath.
441
+ */
442
+ type InstanceRetryOptions, type NormalizedContainerImage,
443
+ /**
444
+ * `@lunora/container` — Cloudflare Containers for Lunora.
445
+ *
446
+ * This root export is Node-safe (no Cloudflare runtime imports): the
447
+ * `defineContainer` authoring surface, the naming/normalization helpers
448
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
449
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
450
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
451
+ * behind the `@lunora/container/do` subpath.
452
+ */
453
+ type PoolOptions, containerBindingName, containerBuildTag, containerClassName, createContainerContext, createContainerTestContext, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVariables as resolveContainerEnvVars };
package/dist/index.d.ts CHANGED
@@ -318,4 +318,136 @@ declare const isContainerDefinition: (value: unknown) => value is ContainerDefin
318
318
  * @experimental
319
319
  */
320
320
  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 };
321
+ export {
322
+ /**
323
+ * `@lunora/container` — Cloudflare Containers for Lunora.
324
+ *
325
+ * This root export is Node-safe (no Cloudflare runtime imports): the
326
+ * `defineContainer` authoring surface, the naming/normalization helpers
327
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
328
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
329
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
330
+ * behind the `@lunora/container/do` subpath.
331
+ */
332
+ type ContainerAccessor,
333
+ /**
334
+ * `@lunora/container` — Cloudflare Containers for Lunora.
335
+ *
336
+ * This root export is Node-safe (no Cloudflare runtime imports): the
337
+ * `defineContainer` authoring surface, the naming/normalization helpers
338
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
339
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
340
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
341
+ * behind the `@lunora/container/do` subpath.
342
+ */
343
+ type ContainerBindingSpec, type ContainerConfig, type ContainerDefinition,
344
+ /**
345
+ * `@lunora/container` — Cloudflare Containers for Lunora.
346
+ *
347
+ * This root export is Node-safe (no Cloudflare runtime imports): the
348
+ * `defineContainer` authoring surface, the naming/normalization helpers
349
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
350
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
351
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
352
+ * behind the `@lunora/container/do` subpath.
353
+ */
354
+ type ContainerEgressControls,
355
+ /**
356
+ * `@lunora/container` — Cloudflare Containers for Lunora.
357
+ *
358
+ * This root export is Node-safe (no Cloudflare runtime imports): the
359
+ * `defineContainer` authoring surface, the naming/normalization helpers
360
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
361
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
362
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
363
+ * behind the `@lunora/container/do` subpath.
364
+ */
365
+ type ContainerHandle, type ContainerImageSource,
366
+ /**
367
+ * `@lunora/container` — Cloudflare Containers for Lunora.
368
+ *
369
+ * This root export is Node-safe (no Cloudflare runtime imports): the
370
+ * `defineContainer` authoring surface, the naming/normalization helpers
371
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
372
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
373
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
374
+ * behind the `@lunora/container/do` subpath.
375
+ */
376
+ type ContainerInstanceHandle,
377
+ /**
378
+ * `@lunora/container` — Cloudflare Containers for Lunora.
379
+ *
380
+ * This root export is Node-safe (no Cloudflare runtime imports): the
381
+ * `defineContainer` authoring surface, the naming/normalization helpers
382
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
383
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
384
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
385
+ * behind the `@lunora/container/do` subpath.
386
+ */
387
+ type ContainerInstanceState,
388
+ /**
389
+ * `@lunora/container` — Cloudflare Containers for Lunora.
390
+ *
391
+ * This root export is Node-safe (no Cloudflare runtime imports): the
392
+ * `defineContainer` authoring surface, the naming/normalization helpers
393
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
394
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
395
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
396
+ * behind the `@lunora/container/do` subpath.
397
+ */
398
+ type ContainerNamespaceLike,
399
+ /**
400
+ * `@lunora/container` — Cloudflare Containers for Lunora.
401
+ *
402
+ * This root export is Node-safe (no Cloudflare runtime imports): the
403
+ * `defineContainer` authoring surface, the naming/normalization helpers
404
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
405
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
406
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
407
+ * behind the `@lunora/container/do` subpath.
408
+ */
409
+ type ContainerStartOptions,
410
+ /**
411
+ * `@lunora/container` — Cloudflare Containers for Lunora.
412
+ *
413
+ * This root export is Node-safe (no Cloudflare runtime imports): the
414
+ * `defineContainer` authoring surface, the naming/normalization helpers
415
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
416
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
417
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
418
+ * behind the `@lunora/container/do` subpath.
419
+ */
420
+ type ContainerTestHandler,
421
+ /**
422
+ * `@lunora/container` — Cloudflare Containers for Lunora.
423
+ *
424
+ * This root export is Node-safe (no Cloudflare runtime imports): the
425
+ * `defineContainer` authoring surface, the naming/normalization helpers
426
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
427
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
428
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
429
+ * behind the `@lunora/container/do` subpath.
430
+ */
431
+ type DurableObjectJurisdiction,
432
+ /**
433
+ * `@lunora/container` — Cloudflare Containers for Lunora.
434
+ *
435
+ * This root export is Node-safe (no Cloudflare runtime imports): the
436
+ * `defineContainer` authoring surface, the naming/normalization helpers
437
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
438
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
439
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
440
+ * behind the `@lunora/container/do` subpath.
441
+ */
442
+ type InstanceRetryOptions, type NormalizedContainerImage,
443
+ /**
444
+ * `@lunora/container` — Cloudflare Containers for Lunora.
445
+ *
446
+ * This root export is Node-safe (no Cloudflare runtime imports): the
447
+ * `defineContainer` authoring surface, the naming/normalization helpers
448
+ * codegen and `@lunora/config` share, the `ctx.containers` client wiring, and
449
+ * a Docker-free test double. The workerd-only `LunoraContainer` base class
450
+ * (which pulls in `@cloudflare/containers` → `cloudflare:workers`) lives
451
+ * behind the `@lunora/container/do` subpath.
452
+ */
453
+ type PoolOptions, containerBindingName, containerBuildTag, containerClassName, createContainerContext, createContainerTestContext, defineContainer, isContainerDefinition, normalizeContainerImage, resolveContainerEnvVariables as resolveContainerEnvVars };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{createContainerContext as t,createContainerTestContext as a}from"./packem_shared/createContainerContext-BXarJmWd.mjs";import{containerBindingName as o,containerBuildTag as r,containerClassName as C,defineContainer as m,isContainerDefinition as s,normalizeContainerImage as c,resolveContainerEnvVars as f}from"./packem_shared/containerBindingName-CP4v-2Cw.mjs";export{o as containerBindingName,r as containerBuildTag,C as containerClassName,t as createContainerContext,a as createContainerTestContext,m as defineContainer,s as isContainerDefinition,c as normalizeContainerImage,f as resolveContainerEnvVars};
1
+ import{createContainerContext as t,createContainerTestContext as a}from"./packem_shared/createContainerContext-C8pmlFAX.mjs";import{containerBindingName as o,containerBuildTag as r,containerClassName as C,defineContainer as m,isContainerDefinition as s,normalizeContainerImage as c,resolveContainerEnvVars as f}from"./packem_shared/containerBindingName-D01FopQt.mjs";export{o as containerBindingName,r as containerBuildTag,C as containerClassName,t as createContainerContext,a as createContainerTestContext,m as defineContainer,s as isContainerDefinition,c as normalizeContainerImage,f as resolveContainerEnvVars};
package/dist/otel.mjs CHANGED
@@ -1 +1 @@
1
- const L={debug:5,error:17,fatal:21,info:9,log:9,trace:1,warn:13},b=e=>`${String(Math.round(e))}000000`,A=e=>{const t=new Uint8Array(e);crypto.getRandomValues(t);let r="";for(const o of t)r+=o.toString(16).padStart(2,"0");return r},y=/^[0-9a-f]+$/,C=e=>{if(e==null)return;const t=e.trim().toLowerCase().split("-"),[r,o,n,s]=t;if(!(t.length<4||r===void 0||r.length!==2||!y.test(r)||r==="ff"||r==="00"&&t.length!==4||o===void 0||n===void 0||s===void 0||s.length!==2||!y.test(s)||o.length!==32||n.length!==16||!y.test(o)||!y.test(n)||o==="00000000000000000000000000000000"||n==="0000000000000000"))return{parentSpanId:n,sampled:(Number.parseInt(s,16)&1)===1,traceId:o}},v=(e,t)=>typeof t=="boolean"?{key:e,value:{boolValue:t}}:typeof t=="number"?Number.isFinite(t)?Number.isSafeInteger(t)?{key:e,value:{intValue:String(t)}}:{key:e,value:{doubleValue:t}}:{key:e,value:{stringValue:String(t)}}:{key:e,value:{stringValue:t}},M=e=>e===void 0?[]:Object.entries(e).map(([t,r])=>v(t,r)),U=(e,t,r)=>{const o={},n=new Map,s=(a,i)=>{const E=a.toLowerCase(),f=n.get(E);f===void 0?(n.set(E,a),o[a]=i):o[f]=i};for(const[a,i]of Object.entries(e))s(a,i);for(const[a,i]of Object.entries(t??{}))s(a,i);return r!==void 0&&r.length>0&&s("authorization",`Bearer ${r}`),o},R=e=>Array.isArray(e)?e:[e],_=(e,t)=>{const r={"service.name":e};for(const[o,n]of Object.entries(t??{}))r[o]=n;return Object.entries(r).map(([o,n])=>v(o,n))},$=(e,t,r,o)=>({resourceSpans:[{resource:{attributes:_(r,o)},scopeSpans:[{scope:{name:t},spans:R(e)}]}]}),x=(e,t,r,o)=>({resourceLogs:[{resource:{attributes:_(r,o)},scopeLogs:[{logRecords:R(e),scope:{name:t}}]}]}),D=e=>{const t={},r=e("SERVICE_VERSION")??e("CF_VERSION_METADATA")??e("VERCEL_GIT_COMMIT_SHA")??e("GITHUB_SHA")??e("COMMIT_SHA");r!==void 0&&(t["service.version"]=r);const o=e("DEPLOYMENT_ENVIRONMENT")??e("ENVIRONMENT")??e("NODE_ENV");return o!==void 0&&(t["deployment.environment"]=o),t},P=(e,t)=>{const r={},o=e("HOSTNAME")??e("COMPUTERNAME");o!==void 0&&(r["host.name"]=o);const n=e("KUBERNETES_POD_NAME")??e("HOSTNAME");return n!==void 0&&e("KUBERNETES_SERVICE_HOST")!==void 0&&(r["k8s.pod.name"]=n),t!==void 0&&Number.isFinite(t)&&(r["process.pid"]=t),r},I=(...e)=>{const t={};for(const r of e)if(r!==void 0)for(const[o,n]of Object.entries(r))t[o]=n;return t},j=1e4,l=e=>process.env[e],k=()=>{const e=typeof process.pid!="number"?void 0:process.pid;return I(D(l),P(l,e))},H=e=>e??(typeof globalThis.fetch=="function"?globalThis.fetch:void 0),B=(e,t,r,o)=>{const n=M(e.attributes);e.error?.type!==void 0&&n.push(v("error.type",e.error.type));const s={attributes:n,endTimeUnixNano:b(e.endMs),kind:1,name:e.name,...r===void 0?{}:{parentSpanId:r.parentSpanId},spanId:A(8),startTimeUnixNano:b(e.startMs),status:e.error===void 0?{code:1}:{code:2,message:e.error.message},traceId:r?.traceId??A(16)};return e.error&&(s.events=[{attributes:[v("exception.type",e.error.type??"Error"),v("exception.message",e.error.message)],name:"exception",timeUnixNano:b(e.endMs)}]),$(s,"@lunora/container",t,o)},F=(e,t,r,o)=>{const n=e.level??"info",s={attributes:M(e.attributes),body:{stringValue:e.message},severityNumber:L[n],severityText:n.toUpperCase(),timeUnixNano:b(e.ts??r)};return x(s,"@lunora/container",t,o)},K=(e={})=>{const t=e.endpoint??l("LUNORA_OTLP_ENDPOINT"),r=t!==void 0&&t.length>0,o=e.token??l("LUNORA_OTLP_TOKEN"),n=e.serviceName??l("LUNORA_SERVICE_NAME")??"lunora-container",s=C(e.traceparent??l("LUNORA_TRACEPARENT")),a=e.timeoutMs??j,i=H(e.fetch),E=U({"content-type":"application/json"},e.headers,o),f=e.detectResources===!0?k():void 0,g={};e.serviceVersion!==void 0&&(g["service.version"]=e.serviceVersion),e.deploymentEnvironment!==void 0&&(g["deployment.environment"]=e.deploymentEnvironment),e.resourceAttributes!==void 0&&Object.assign(g,e.resourceAttributes);const T=I(f,g);let p=t??"";for(;p.endsWith("/");)p=p.slice(0,-1);const w=`${p}/v1/traces`,V=`${p}/v1/logs`,N=new Set,h=(c,S)=>{if(i===void 0){e.onError?.(new TypeError("createContainerTelemetry: no `fetch` available — pass `fetch` in options for this runtime."));return}const m=(async()=>{try{const d=await i(c,{body:JSON.stringify(S),headers:E,method:"POST",signal:AbortSignal.timeout(a)});d.ok||e.onError?.(new Error(`createContainerTelemetry: OTLP export to ${c} failed with status ${String(d.status)}.`));try{await d.body?.cancel()}catch{}}catch(d){e.onError?.(d)}})().finally(()=>{N.delete(m)});N.add(m)},O=c=>{r&&h(w,B(c,n,s,T))};return{emitLog:c=>{r&&h(V,F(c,n,Date.now(),T))},emitSpan:O,enabled:r,flush:async()=>{await Promise.allSettled(N)},trace:async(c,S,m)=>{const d=Date.now();try{const u=await S();return O({attributes:m,endMs:Date.now(),name:c,startMs:d}),u}catch(u){throw O({attributes:m,endMs:Date.now(),error:{message:u instanceof Error?u.message:String(u),type:u instanceof Error?u.name:void 0},name:c,startMs:d}),u}}}};export{K as createContainerTelemetry};
1
+ const L={debug:5,error:17,fatal:21,info:9,log:9,trace:1,warn:13},b=e=>`${String(Math.round(e))}000000`,A=e=>{const t=new Uint8Array(e);crypto.getRandomValues(t);let r="";for(const n of t)r+=n.toString(16).padStart(2,"0");return r},p=/^[0-9a-f]+$/,U=e=>{if(e==null)return;const t=e.trim().toLowerCase().split("-"),[r,n,o,s]=t;if(!(t.length<4||r===void 0||r.length!==2||!p.test(r)||r==="ff"||r==="00"&&t.length!==4||n===void 0||o===void 0||s===void 0||s.length!==2||!p.test(s)||n.length!==32||o.length!==16||!p.test(n)||!p.test(o)||n==="00000000000000000000000000000000"||o==="0000000000000000"))return{parentSpanId:o,sampled:(Number.parseInt(s,16)&1)===1,traceId:n}},m=(e,t)=>typeof t=="boolean"?{key:e,value:{boolValue:t}}:typeof t=="number"?Number.isFinite(t)?Number.isSafeInteger(t)?{key:e,value:{intValue:String(t)}}:{key:e,value:{doubleValue:t}}:{key:e,value:{stringValue:String(t)}}:{key:e,value:{stringValue:t}},R=e=>e===void 0?[]:Object.entries(e).map(([t,r])=>m(t,r)),C=(e,t,r)=>{const n={},o=new Map,s=(i,a)=>{const v=i.toLowerCase(),E=o.get(v);E===void 0?(o.set(v,i),n[i]=a):n[E]=a};for(const[i,a]of Object.entries(e))s(i,a);for(const[i,a]of Object.entries(t??{}))s(i,a);return r!==void 0&&r.length>0&&s("authorization",`Bearer ${r}`),n},M=e=>Array.isArray(e)?e:[e],_=(e,t)=>{const r={"service.name":e};for(const[n,o]of Object.entries(t??{}))r[n]=o;return Object.entries(r).map(([n,o])=>m(n,o))},x=(e,t,r,n)=>({resourceSpans:[{resource:{attributes:_(r,n)},scopeSpans:[{scope:{name:t},spans:M(e)}]}]}),H=(e,t,r,n)=>({resourceLogs:[{resource:{attributes:_(r,n)},scopeLogs:[{logRecords:M(e),scope:{name:t}}]}]}),P=e=>{const t={},r=e("SERVICE_VERSION")??e("CF_VERSION_METADATA")??e("VERCEL_GIT_COMMIT_SHA")??e("GITHUB_SHA")??e("COMMIT_SHA");r!==void 0&&(t["service.version"]=r);const n=e("DEPLOYMENT_ENVIRONMENT")??e("ENVIRONMENT")??e("NODE_ENV");return n!==void 0&&(t["deployment.environment"]=n),t},D=(e,t)=>{const r={},n=e("HOSTNAME")??e("COMPUTERNAME");n!==void 0&&(r["host.name"]=n);const o=e("KUBERNETES_POD_NAME")??e("HOSTNAME");return o!==void 0&&e("KUBERNETES_SERVICE_HOST")!==void 0&&(r["k8s.pod.name"]=o),t!==void 0&&Number.isFinite(t)&&(r["process.pid"]=t),r},I=(...e)=>{const t={};for(const r of e)if(r!==void 0)for(const[n,o]of Object.entries(r))t[n]=o;return t},j=1e4,d=e=>process.env[e],$=()=>{const e=typeof process.pid!="number"?void 0:process.pid;return I(P(d),D(d,e))},B=e=>e??(typeof globalThis.fetch=="function"?globalThis.fetch:void 0),F=(e,t,r,n)=>{const o=R(e.attributes);e.error?.type!==void 0&&o.push(m("error.type",e.error.type));const s={attributes:o,endTimeUnixNano:b(e.endMs),kind:1,name:e.name,...r===void 0?{}:{parentSpanId:r.parentSpanId},spanId:A(8),startTimeUnixNano:b(e.startMs),status:e.error===void 0?{code:1}:{code:2,message:e.error.message},traceId:r?.traceId??A(16)};return e.error&&(s.events=[{attributes:[m("exception.type",e.error.type??"Error"),m("exception.message",e.error.message)],name:"exception",timeUnixNano:b(e.endMs)}]),x(s,"@lunora/container",t,n)},K=(e,t,r,n)=>{const o=e.level??"info",s={attributes:R(e.attributes),body:{stringValue:e.message},severityNumber:L[o],severityText:o.toUpperCase(),timeUnixNano:b(e.ts??r)};return H(s,"@lunora/container",t,n)},z=(e={})=>{const t=e.endpoint??d("LUNORA_OTLP_ENDPOINT"),r=t!==void 0&&t.length>0,n=e.token??d("LUNORA_OTLP_TOKEN"),o=e.serviceName??d("LUNORA_SERVICE_NAME")??"lunora-container",s=U(e.traceparent??d("LUNORA_TRACEPARENT")),i=e.timeoutMs??j,a=B(e.fetch),v=C({"content-type":"application/json"},e.headers,n),E=e.detectResources===!0?$():void 0,g={};e.serviceVersion!==void 0&&(g["service.version"]=e.serviceVersion),e.deploymentEnvironment!==void 0&&(g["deployment.environment"]=e.deploymentEnvironment),e.resourceAttributes!==void 0&&Object.assign(g,e.resourceAttributes);const S=I(E,g);let l=t??"";for(;l.endsWith("/");)l=l.slice(0,-1);const w=`${l}/v1/traces`,V=`${l}/v1/logs`,N=new Set,O=(u,T)=>{if(a===void 0){e.onError?.(new TypeError("createContainerTelemetry: no `fetch` available — pass `fetch` in options for this runtime."));return}const f=(async()=>{try{const c=await a(u,{body:JSON.stringify(T),headers:v,method:"POST",signal:AbortSignal.timeout(i)});c.ok||e.onError?.(new Error(`createContainerTelemetry: OTLP export to ${u} failed with status ${String(c.status)}.`));try{await c.body?.cancel()}catch{}}catch(c){e.onError?.(c)}})().finally(()=>{N.delete(f)});N.add(f)},y=u=>{r&&O(w,F(u,o,s,S))};return{emitLog:u=>{r&&O(V,K(u,o,Date.now(),S))},emitSpan:y,enabled:r,flush:async()=>{await Promise.allSettled(N)},trace:async(u,T,h)=>{const f=Date.now();try{const c=await T();return y({attributes:h,endMs:Date.now(),name:u,startMs:f}),c}catch(c){throw y({attributes:h,endMs:Date.now(),error:{message:c instanceof Error?c.message:String(c),type:c instanceof Error?c.name:void 0},name:u,startMs:f}),c}}}};export{z as createContainerTelemetry};
@@ -1,4 +1,4 @@
1
- import{DurableObject as F,WorkerEntrypoint as $}from"cloudflare:workers";function D(i=9){const t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",e=new Uint8Array(i);crypto.getRandomValues(e);let n="";for(let s=0;s<i;s++)n+=t[e[s]%t.length];return n}function W(i){if(typeof i=="number")return i;if(typeof i=="string"){const t=i.match(/^(\d+)([smh])$/);if(!t)throw new Error(`invalid time expression ${i}`);const e=parseInt(t[1]),n=t[2];switch(n){case"s":return e;case"m":return e*60;case"h":return e*60*60;default:throw new Error(`unknown time unit ${n}`)}}throw new Error(`invalid type for a time expression: ${typeof i}`)}const B="there is no container instance that can be provided to this durable object",_="you are requesting too many containers per second",E="runtime signalled the container to exit:",C="container exited with unexpected exit code:",j="container is not listening",T="__CF_CONTAINER_STATE",k="OUTBOUND_CONFIGURATION",U=3,O=100,G=180*1e3,P=5e3,V="10m",v=300,H=8e3,R=2e4,N=33;function et(i,t){return t}const g=new Map,S=new Map,A=new Map,K={SIGINT:2,SIGTERM:15,SIGKILL:9};function m(i,t){return(i instanceof Error?i.message:String(i)).toLowerCase().includes(t)}const I=i=>m(i,B),X=i=>m(i,_),J=i=>m(i,E),M=i=>m(i,j),Y=i=>m(i,C);function z(i){return i instanceof Error?J(i)?+i.message.toLowerCase().slice(i.message.toLowerCase().indexOf(E)+E.length+1):Y(i)?+i.message.toLowerCase().slice(i.message.toLowerCase().indexOf(C)+C.length+1):null:null}function L(i,t){const e=new AbortController;if(i?.aborted)return e.abort(),e.signal;i?.addEventListener("abort",()=>e.abort());const n=setTimeout(()=>e.abort(),t);return e.signal.addEventListener("abort",()=>clearTimeout(n)),e.signal}function x(i,t){const e=i.split("*");if(e.length===1)return i===t;if(!t.startsWith(e[0])||!t.endsWith(e[e.length-1]))return!1;let n=e[0].length;for(let s=1;s<e.length-1;s++){const o=t.indexOf(e[s],n);if(o===-1)return!1;n=o+e[s].length}return n<=t.length-e[e.length-1].length}function q(i,t){return t.some(e=>x(e,i))}function Q(i){let t=i.length;for(;t>0&&i[t-1]===".";)t--;return i.slice(0,t)}class Z{storage;status;constructor(t){this.storage=t}async setRunning(){await this.setStatusAndupdate("running")}async setHealthy(){await this.setStatusAndupdate("healthy")}async setStopping(){await this.setStatusAndupdate("stopping")}async setStopped(){await this.setStatusAndupdate("stopped")}async setStoppedIfUnchanged(t){this.status===t&&await this.setStopped()}async setStoppedWithCode(t){this.status={status:"stopped_with_code",lastChange:Date.now(),exitCode:t},await this.update()}async getState(){if(!this.status){const t=await this.storage.get(T);t?this.status=t:(this.status={status:"stopped",lastChange:Date.now()},await this.update())}return this.status}async setStatusAndupdate(t){this.status={status:t,lastChange:Date.now()},await this.update()}async update(){if(!this.status)throw new Error("status should be init");await this.storage.put(T,this.status)}}class nt extends ${async fetch(t){const e=new URL(t.url),n=Q(e.hostname),{className:s,containerId:o,outboundByHostOverrides:a,outboundHandlerOverride:c,enableInternet:d,allowedHosts:r,deniedHosts:l,interceptAll:f}=this.ctx.props,p={containerId:o,className:s};if(l&&q(n,l))return new Response("Origin is disallowed",{status:520});if(r&&!q(n,r))return new Response("Origin is disallowed",{status:520});const y=g.get(s);if(a&&y){const w=a[n]??Object.entries(a).find(([b])=>b!==n&&x(b,n))?.[1];if(w&&y[w.method])return y[w.method](t,this.env,{...p,params:w.params})}const u=A.get(s);if(u){const w=u[n]??Object.entries(u).find(([b])=>b!==n&&x(b,n))?.[1];if(w)return w(t,this.env,p)}if(!f)return r||d?fetch(t):new Response("Origin is disallowed",{status:520});if(c&&y?.[c.method])return y[c.method](t,this.env,{...p,params:c.params});const h=S.get(s);return h&&y?.[h]?y[h](t,this.env,p):r||d?fetch(t):new Response("Origin is disallowed",{status:520})}}class st extends F{static get outboundByHost(){return A.get(this.name)}static set outboundByHost(t){A.set(this.name,t)}static get outboundHandlers(){return g.get(this.name)}static set outboundHandlers(t){const e=g.get(this.name)??{};g.set(this.name,{...e,...t})}static get outbound(){const t=S.get(this.name);if(t)return g.get(this.name)?.[t]}static set outbound(t){const e="__outbound__",n=g.get(this.name)??{};g.set(this.name,{...n,[e]:t}),S.set(this.name,e)}static get outboundProxies(){return this.outboundHandlers}static set outboundProxies(t){this.outboundHandlers=t}static get outboundProxy(){return this.outbound}static set outboundProxy(t){this.outbound=t}defaultPort;requiredPorts;sleepAfter=V;envVars={};entrypoint;enableInternet=!0;labels={};interceptHttps=!1;allowedHosts;deniedHosts;pingEndpoint="ping";applyOutboundInterceptionPromise=Promise.resolve();usingInterception=!1;constructor(t,e,n){if(super(t,e),t.container===void 0)throw new Error("Containers have not been enabled for this Durable Object class. Have you correctly setup your Wrangler config? More info: https://developers.cloudflare.com/containers/get-started/#configuration");this.state=new Z(this.ctx.storage);const s=this.restoreOutboundConfiguration();this.ctx.blockConcurrencyWhile(async()=>{await this.scheduleNextAlarm(),this.renewActivityTimeout();const o=this.constructor;(s!==void 0||o.outboundByHost!==void 0||o.outbound!==void 0||o.outboundHandlers!==void 0||this.effectiveAllowedHosts!==void 0||this.effectiveDeniedHosts!==void 0)&&(this.usingInterception=!0),this.container.running&&(this.applyOutboundInterceptionPromise=this.applyOutboundInterception())}),this.container=t.container,n&&(n.defaultPort!==void 0&&(this.defaultPort=n.defaultPort),n.sleepAfter!==void 0&&(this.sleepAfter=n.sleepAfter),n.envVars!==void 0&&(this.envVars=n.envVars),n.entrypoint!==void 0&&(this.entrypoint=n.entrypoint),n.enableInternet!==void 0&&(this.enableInternet=n.enableInternet)),this.sql`
1
+ import{WorkerEntrypoint as B,DurableObject as D}from"cloudflare:workers";function $(i=9){const t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",e=new Uint8Array(i);crypto.getRandomValues(e);let n="";for(let s=0;s<i;s++)n+=t[e[s]%t.length];return n}function W(i){if(typeof i=="number")return i;if(typeof i=="string"){const t=i.match(/^(\d+)([smh])$/);if(!t)throw new Error(`invalid time expression ${i}`);const e=parseInt(t[1]),n=t[2];switch(n){case"s":return e;case"m":return e*60;case"h":return e*60*60;default:throw new Error(`unknown time unit ${n}`)}}throw new Error(`invalid type for a time expression: ${typeof i}`)}const F="there is no container instance that can be provided to this durable object",U="you are requesting too many containers per second",H="runtime signalled the container to exit:",T="container exited with unexpected exit code:",q="container is not listening",R="__CF_CONTAINER_STATE",x="OUTBOUND_CONFIGURATION",j=3,E=100,G=180*1e3,N=5e3,V="10m",v=300,O=8e3,P=2e4,M=33;function et(i,t){return t}const w=new Map,S=new Map,C=new Map,K={SIGINT:2,SIGTERM:15,SIGKILL:9};function m(i,t){return(i instanceof Error?i.message:String(i)).toLowerCase().includes(t)}const I=i=>m(i,F),X=i=>m(i,U),J=i=>m(i,H),k=i=>m(i,q),Y=i=>m(i,T);function z(i){return i instanceof Error?J(i)?+i.message.toLowerCase().slice(i.message.toLowerCase().indexOf(H)+H.length+1):Y(i)?+i.message.toLowerCase().slice(i.message.toLowerCase().indexOf(T)+T.length+1):null:null}function _(i,t){const e=new AbortController;if(i?.aborted)return e.abort(),e.signal;i?.addEventListener("abort",()=>e.abort());const n=setTimeout(()=>e.abort(),t);return e.signal.addEventListener("abort",()=>clearTimeout(n)),e.signal}function A(i,t){const e=i.split("*");if(e.length===1)return i===t;if(!t.startsWith(e[0])||!t.endsWith(e[e.length-1]))return!1;let n=e[0].length;for(let s=1;s<e.length-1;s++){const o=t.indexOf(e[s],n);if(o===-1)return!1;n=o+e[s].length}return n<=t.length-e[e.length-1].length}function L(i,t){return t.some(e=>A(e,i))}function Z(i){let t=i.length;for(;t>0&&i[t-1]===".";)t--;return i.slice(0,t)}class Q{storage;status;constructor(t){this.storage=t}async setRunning(){await this.setStatusAndupdate("running")}async setHealthy(){await this.setStatusAndupdate("healthy")}async setStopping(){await this.setStatusAndupdate("stopping")}async setStopped(){await this.setStatusAndupdate("stopped")}async setStoppedIfUnchanged(t){this.status===t&&await this.setStopped()}async setStoppedWithCode(t){this.status={status:"stopped_with_code",lastChange:Date.now(),exitCode:t},await this.update()}async getState(){if(!this.status){const t=await this.storage.get(R);t?this.status=t:(this.status={status:"stopped",lastChange:Date.now()},await this.update())}return this.status}async setStatusAndupdate(t){this.status={status:t,lastChange:Date.now()},await this.update()}async update(){if(!this.status)throw new Error("status should be init");await this.storage.put(R,this.status)}}class nt extends B{async fetch(t){const e=new URL(t.url),n=Z(e.hostname),{className:s,containerId:o,outboundByHostOverrides:a,outboundHandlerOverride:c,enableInternet:d,allowedHosts:r,deniedHosts:l,interceptAll:p}=this.ctx.props,f={containerId:o,className:s};if(l&&L(n,l))return new Response("Origin is disallowed",{status:520});if(r&&!L(n,r))return new Response("Origin is disallowed",{status:520});const g=w.get(s);if(a&&g){const y=a[n]??Object.entries(a).find(([b])=>b!==n&&A(b,n))?.[1];if(y&&g[y.method])return g[y.method](t,this.env,{...f,params:y.params})}const h=C.get(s);if(h){const y=h[n]??Object.entries(h).find(([b])=>b!==n&&A(b,n))?.[1];if(y)return y(t,this.env,f)}if(!p)return r||d?fetch(t):new Response("Origin is disallowed",{status:520});if(c&&g?.[c.method])return g[c.method](t,this.env,{...f,params:c.params});const u=S.get(s);return u&&g?.[u]?g[u](t,this.env,f):r||d?fetch(t):new Response("Origin is disallowed",{status:520})}}class st extends D{static get outboundByHost(){return C.get(this.name)}static set outboundByHost(t){C.set(this.name,t)}static get outboundHandlers(){return w.get(this.name)}static set outboundHandlers(t){const e=w.get(this.name)??{};w.set(this.name,{...e,...t})}static get outbound(){const t=S.get(this.name);if(t)return w.get(this.name)?.[t]}static set outbound(t){const e="__outbound__",n=w.get(this.name)??{};w.set(this.name,{...n,[e]:t}),S.set(this.name,e)}static get outboundProxies(){return this.outboundHandlers}static set outboundProxies(t){this.outboundHandlers=t}static get outboundProxy(){return this.outbound}static set outboundProxy(t){this.outbound=t}defaultPort;requiredPorts;sleepAfter=V;envVars={};entrypoint;enableInternet=!0;labels={};interceptHttps=!1;allowedHosts;deniedHosts;pingEndpoint="ping";applyOutboundInterceptionPromise=Promise.resolve();usingInterception=!1;constructor(t,e,n){if(super(t,e),t.container===void 0)throw new Error("Containers have not been enabled for this Durable Object class. Have you correctly setup your Wrangler config? More info: https://developers.cloudflare.com/containers/get-started/#configuration");this.state=new Q(this.ctx.storage);const s=this.restoreOutboundConfiguration();this.ctx.blockConcurrencyWhile(async()=>{await this.scheduleNextAlarm(),this.renewActivityTimeout();const o=this.constructor;(s!==void 0||o.outboundByHost!==void 0||o.outbound!==void 0||o.outboundHandlers!==void 0||this.effectiveAllowedHosts!==void 0||this.effectiveDeniedHosts!==void 0)&&(this.usingInterception=!0),this.container.running&&(this.applyOutboundInterceptionPromise=this.applyOutboundInterception())}),this.container=t.container,n&&(n.defaultPort!==void 0&&(this.defaultPort=n.defaultPort),n.sleepAfter!==void 0&&(this.sleepAfter=n.sleepAfter),n.envVars!==void 0&&(this.envVars=n.envVars),n.entrypoint!==void 0&&(this.entrypoint=n.entrypoint),n.enableInternet!==void 0&&(this.enableInternet=n.enableInternet)),this.sql`
2
2
  CREATE TABLE IF NOT EXISTS container_schedules (
3
3
  id TEXT PRIMARY KEY NOT NULL DEFAULT (randomblob(9)),
4
4
  callback TEXT NOT NULL,
@@ -8,7 +8,7 @@ import{DurableObject as F,WorkerEntrypoint as $}from"cloudflare:workers";functio
8
8
  delayInSeconds INTEGER,
9
9
  created_at INTEGER DEFAULT (unixepoch())
10
10
  )
11
- `,this.container.running&&(this.monitor=this.container.monitor(),this.setupMonitorCallbacks())}async getState(){return{...await this.state.getState()}}async setOutboundHandler(t,...e){this.validateOutboundHandlerMethodName(t),this.outboundHandlerOverride=e.length===0?{method:t}:{method:t,params:e[0]},await this.refreshOutboundInterception()}async setOutboundByHost(t,e,...n){this.validateOutboundHandlerMethodName(e),this.outboundByHostOverrides[t]=n.length===0?{method:e}:{method:e,params:n[0]},await this.refreshOutboundInterception()}async removeOutboundByHost(t){delete this.outboundByHostOverrides[t],await this.refreshOutboundInterception()}async setOutboundByHosts(t){for(const e of Object.values(t)){const n=typeof e=="string"?e:e.method;this.validateOutboundHandlerMethodName(n)}this.outboundByHostOverrides=Object.fromEntries(Object.entries(t).map(([e,n])=>[e,typeof n=="string"?{method:n}:n])),await this.refreshOutboundInterception()}async setAllowedHosts(t){this.allowedHostsOverride=[...t],this.usingInterception=!0,await this.refreshOutboundInterception()}async setDeniedHosts(t){this.deniedHostsOverride=[...t],this.usingInterception=!0,await this.refreshOutboundInterception()}async allowHost(t){const e=this.effectiveAllowedHosts??[];e.includes(t)||(this.allowedHostsOverride=[...e,t]),this.usingInterception=!0,await this.refreshOutboundInterception()}async denyHost(t){const e=this.effectiveDeniedHosts??[];e.includes(t)||(this.deniedHostsOverride=[...e,t]),this.usingInterception=!0,await this.refreshOutboundInterception()}async removeAllowedHost(t){this.allowedHostsOverride=(this.effectiveAllowedHosts??[]).filter(e=>e!==t),await this.refreshOutboundInterception()}async removeDeniedHost(t){this.deniedHostsOverride=(this.effectiveDeniedHosts??[]).filter(e=>e!==t),await this.refreshOutboundInterception()}async start(t,e){const n=e?.portToCheck??this.defaultPort??(this.requiredPorts?this.requiredPorts[0]:N),s=e?.waitInterval??v;await this.startContainerIfNotRunning({signal:e?.signal,waitInterval:s,retries:e?.retries??Math.ceil(H/s),portToCheck:n},t),this.setupMonitorCallbacks(),await this.ctx.blockConcurrencyWhile(async()=>{await this.onStart()})}async startAndWaitForPorts(t,e,n){let s,o,a;typeof t=="object"&&t!==null&&!Array.isArray(t)?(s=t.ports,o=t.cancellationOptions,a=t.startOptions):(s=t,o=e,a=n);const c=await this.getPortsToCheck(s);await this.syncPendingStoppedEvents(),o??={};const d=o.instanceGetTimeoutMS??H,r=o.waitInterval??v,l=Math.ceil(d/r),f={signal:o.abort,retries:l,waitInterval:r,portToCheck:c[0]},p=await this.startContainerIfNotRunning(f,a);let y=Math.ceil((o.portReadyTimeoutMS??R)/r)-p;for(const u of c)y=await this.waitForPort({signal:o.abort,waitInterval:r,retries:y,portToCheck:u});this.setupMonitorCallbacks(),await this.ctx.blockConcurrencyWhile(async()=>{await this.state.setHealthy(),await this.onStart()})}async waitForPort(t){const e=t.portToCheck,n=this.container.getTcpPort(e),s=new Promise(c=>{t.signal?.addEventListener("abort",()=>{c(!0)})}),o=t.waitInterval??v,a=t.retries??Math.ceil(R/o);for(let c=0;c<a;c++)try{const d=L(t.signal,P);await n.fetch(`http://${this.pingEndpoint}`,{signal:d});break}catch(d){const r=d instanceof Error?d.message:String(d);if(!this.container.running){try{await this.onError(new Error("Container crashed while checking for ports, did you start the container and setup the entrypoint correctly?"))}catch{}throw d}if(c===a-1){try{await this.onError(`Failed to verify port ${e} is available after ${(c+1)*o}ms, last error: ${r}`)}catch{}throw d}if(await Promise.any([new Promise(l=>setTimeout(l,o)),s]),t.signal?.aborted)throw new Error("Container request aborted.",{cause:d})}return a}async stop(t="SIGTERM"){this.container.running&&this.container.signal(typeof t=="string"?K[t]:t),await this.syncPendingStoppedEvents()}async destroy(){await this.container.destroy()}onStart(){}onStop(t){}async onActivityExpired(){console.log("Activity expired, signalling container to stop"),this.container.running&&await this.stop()}onError(t){throw console.error("Container error:",t),t}renewActivityTimeout(){const t=W(this.sleepAfter)*1e3;this.sleepAfterMs=Date.now()+t}decrementInflight(){this.inflightRequests=Math.max(0,this.inflightRequests-1),this.inflightRequests===0&&this.renewActivityTimeout()}async schedule(t,e,n){const s=D(9);if(typeof e!="string")throw new Error("Callback must be a string (method name)");if(typeof this[e]!="function")throw new Error(`this.${e} is not a function`);if(t instanceof Date){const o=Math.floor(t.getTime()/1e3);return this.sql`
11
+ `,this.container.running&&(this.monitor=this.container.monitor(),this.setupMonitorCallbacks())}async getState(){return{...await this.state.getState()}}async setOutboundHandler(t,...e){this.validateOutboundHandlerMethodName(t),this.outboundHandlerOverride=e.length===0?{method:t}:{method:t,params:e[0]},await this.refreshOutboundInterception()}async setOutboundByHost(t,e,...n){this.validateOutboundHandlerMethodName(e),this.outboundByHostOverrides[t]=n.length===0?{method:e}:{method:e,params:n[0]},await this.refreshOutboundInterception()}async removeOutboundByHost(t){delete this.outboundByHostOverrides[t],await this.refreshOutboundInterception()}async setOutboundByHosts(t){for(const e of Object.values(t)){const n=typeof e=="string"?e:e.method;this.validateOutboundHandlerMethodName(n)}this.outboundByHostOverrides=Object.fromEntries(Object.entries(t).map(([e,n])=>[e,typeof n=="string"?{method:n}:n])),await this.refreshOutboundInterception()}async setAllowedHosts(t){this.allowedHostsOverride=[...t],this.usingInterception=!0,await this.refreshOutboundInterception()}async setDeniedHosts(t){this.deniedHostsOverride=[...t],this.usingInterception=!0,await this.refreshOutboundInterception()}async allowHost(t){const e=this.effectiveAllowedHosts??[];e.includes(t)||(this.allowedHostsOverride=[...e,t]),this.usingInterception=!0,await this.refreshOutboundInterception()}async denyHost(t){const e=this.effectiveDeniedHosts??[];e.includes(t)||(this.deniedHostsOverride=[...e,t]),this.usingInterception=!0,await this.refreshOutboundInterception()}async removeAllowedHost(t){this.allowedHostsOverride=(this.effectiveAllowedHosts??[]).filter(e=>e!==t),await this.refreshOutboundInterception()}async removeDeniedHost(t){this.deniedHostsOverride=(this.effectiveDeniedHosts??[]).filter(e=>e!==t),await this.refreshOutboundInterception()}async start(t,e){const n=e?.portToCheck??this.defaultPort??(this.requiredPorts?this.requiredPorts[0]:M),s=e?.waitInterval??v;await this.startContainerIfNotRunning({signal:e?.signal,waitInterval:s,retries:e?.retries??Math.ceil(O/s),portToCheck:n},t),this.setupMonitorCallbacks(),await this.ctx.blockConcurrencyWhile(async()=>{await this.onStart()})}async startAndWaitForPorts(t,e,n){let s,o,a;typeof t=="object"&&t!==null&&!Array.isArray(t)?(s=t.ports,o=t.cancellationOptions,a=t.startOptions):(s=t,o=e,a=n);const c=await this.getPortsToCheck(s);await this.syncPendingStoppedEvents(),o??={};const d=o.instanceGetTimeoutMS??O,r=o.waitInterval??v,l=Math.ceil(d/r),p={signal:o.abort,retries:l,waitInterval:r,portToCheck:c[0]},f=await this.startContainerIfNotRunning(p,a);let h=Math.ceil((o.portReadyTimeoutMS??P)/r)-f;for(const u of c)h=await this.waitForPort({signal:o.abort,waitInterval:r,retries:h,portToCheck:u});this.setupMonitorCallbacks(),await this.ctx.blockConcurrencyWhile(async()=>{await this.state.setHealthy(),await this.onStart()})}async waitForPort(t){const e=t.portToCheck,n=this.container.getTcpPort(e),s=new Promise(c=>{t.signal?.addEventListener("abort",()=>{c(!0)})}),o=t.waitInterval??v,a=t.retries??Math.ceil(P/o);for(let c=0;c<a;c++)try{const d=_(t.signal,N);await n.fetch(`http://${this.pingEndpoint}`,{signal:d});break}catch(d){const r=d instanceof Error?d.message:String(d);if(!this.container.running){try{await this.onError(new Error("Container crashed while checking for ports, did you start the container and setup the entrypoint correctly?"))}catch{}throw d}if(c===a-1){try{await this.onError(`Failed to verify port ${e} is available after ${(c+1)*o}ms, last error: ${r}`)}catch{}throw d}if(await Promise.any([new Promise(l=>setTimeout(l,o)),s]),t.signal?.aborted)throw new Error("Container request aborted.",{cause:d})}return a}async stop(t="SIGTERM"){this.container.running&&this.container.signal(typeof t=="string"?K[t]:t),await this.syncPendingStoppedEvents()}async destroy(){await this.container.destroy()}onStart(){}onStop(t){}async onActivityExpired(){console.log("Activity expired, signalling container to stop"),this.container.running&&await this.stop()}onError(t){throw console.error("Container error:",t),t}renewActivityTimeout(){const t=W(this.sleepAfter)*1e3;this.sleepAfterMs=Date.now()+t}decrementInflight(){this.inflightRequests=Math.max(0,this.inflightRequests-1),this.inflightRequests===0&&this.renewActivityTimeout()}async schedule(t,e,n){const s=$(9);if(typeof e!="string")throw new Error("Callback must be a string (method name)");if(typeof this[e]!="function")throw new Error(`this.${e} is not a function`);if(t instanceof Date){const o=Math.floor(t.getTime()/1e3);return this.sql`
12
12
  INSERT OR REPLACE INTO container_schedules (id, callback, payload, type, time)
13
13
  VALUES (${s}, ${e}, ${JSON.stringify(n)}, 'scheduled', ${o})
14
14
  `,await this.scheduleNextAlarm(),{taskId:s,callback:e,payload:n,time:o,type:"scheduled"}}if(typeof t=="number"){const o=Math.floor(Date.now()/1e3+t);return this.sql`
@@ -16,11 +16,11 @@ import{DurableObject as F,WorkerEntrypoint as $}from"cloudflare:workers";functio
16
16
  VALUES (${s}, ${e}, ${JSON.stringify(n)}, 'delayed', ${t}, ${o})
17
17
  `,await this.scheduleNextAlarm(),{taskId:s,callback:e,payload:n,delayInSeconds:t,time:o,type:"delayed"}}throw new Error("Invalid schedule type. 'when' must be a Date or number of seconds")}async containerFetch(t,e,n){const{request:s,port:o}=this.requestAndPortFromContainerFetchArgs(t,e,n),a=await this.state.getState();if(!this.container.running||a.status!=="healthy")try{await this.startAndWaitForPorts(o,{abort:s.signal})}catch(r){return I(r)?new Response(`There is no Container instance available at this time.
18
18
  This is likely because you have reached your max concurrent instance count (set in wrangler config) or are you currently provisioning the Container.
19
- If you are deploying your Container for the first time, check your dashboard to see provisioning status, this may take a few minutes.`,{status:503}):X(r)?new Response(r instanceof Error?r.message:String(r),{status:429}):new Response(`Failed to start container: ${r instanceof Error?r.message:String(r)}`,{status:500})}const c=this.container.getTcpPort(o),d=s.url.replace("https:","http:");this.inflightRequests++;try{this.renewActivityTimeout();const r=await c.fetch(d,s);if(r.webSocket!==null){const l=r.webSocket,[f,p]=Object.values(new WebSocketPair);let y=!1;const u=()=>{y||(y=!0,this.decrementInflight())};return l.accept(),p.accept(),p.addEventListener("message",async h=>{this.renewActivityTimeout();try{const w=h.data instanceof Blob?await h.data.arrayBuffer():h.data;l.send(w)}catch{p.close(1011,"Failed to forward message to container")}}),l.addEventListener("message",async h=>{this.renewActivityTimeout();try{const w=h.data instanceof Blob?await h.data.arrayBuffer():h.data;p.send(w)}catch{l.close(1011,"Failed to forward message to client")}}),p.addEventListener("close",h=>{u();const w=h.code===1005||h.code===1006?1e3:h.code;l.close(w,h.reason)}),l.addEventListener("close",h=>{u();const w=h.code===1005||h.code===1006?1e3:h.code;p.close(w,h.reason)}),p.addEventListener("error",()=>{u(),l.close(1011,"Client WebSocket error")}),l.addEventListener("error",()=>{u(),p.close(1011,"Container WebSocket error")}),new Response(null,{status:r.status,webSocket:f,headers:r.headers})}if(r.body!==null){const{readable:l,writable:f}=new IdentityTransformStream;return r.body?.pipeTo(f).finally(()=>{this.decrementInflight()}),new Response(l,r)}return this.decrementInflight(),r}catch(r){if(this.decrementInflight(),!(r instanceof Error))throw r;return r.message.includes("Network connection lost.")?new Response("Container suddenly disconnected, try again",{status:500}):(console.error(`Error proxying request to container ${this.ctx.id}:`,r),new Response(`Error proxying request to container: ${r instanceof Error?r.message:String(r)}`,{status:500}))}}async fetch(t){if(this.defaultPort===void 0&&!t.headers.has("cf-container-target-port"))throw new Error("No port configured for this container. Set the `defaultPort` in your Container subclass, or specify a port with `container.fetch(switchPort(request, port))`.");let e=this.defaultPort;if(t.headers.has("cf-container-target-port")){const n=parseInt(t.headers.get("cf-container-target-port")??"");if(isNaN(n))throw new Error("port value from switchPort is not a number");e=n}return await this.containerFetch(t,e)}container;onStopCalled=!1;state;monitor;startInFlight;monitoredPromise;sleepAfterMs=0;inflightRequests=0;outboundByHostOverrides={};outboundHandlerOverride;allowedHostsOverride;deniedHostsOverride;hasInterceptAllRegistration=!1;validateOutboundHandlerMethodName(t){const e=g.get(this.constructor.name);if(!e||!(t in e))throw new Error(`Outbound handler method '${t}' not found in outboundHandlers for ${this.constructor.name}`)}get effectiveAllowedHosts(){return this.allowedHostsOverride??this.allowedHosts}get effectiveDeniedHosts(){return this.deniedHostsOverride??this.deniedHosts}getOutboundConfiguration(){return{outboundByHostOverrides:Object.keys(this.outboundByHostOverrides).length>0?this.outboundByHostOverrides:void 0,outboundHandlerOverride:this.outboundHandlerOverride,allowedHosts:this.effectiveAllowedHosts,deniedHosts:this.effectiveDeniedHosts,hasInterceptAllRegistration:this.hasInterceptAllRegistration||void 0}}persistOutboundConfiguration(t){this.ctx.storage.kv.put(k,{...t,allowedHosts:this.allowedHostsOverride,deniedHosts:this.deniedHostsOverride})}restoreOutboundConfiguration(){const t=this.ctx.storage.kv.get(k);if(t){if(this.outboundHandlerOverride=void 0,t.outboundHandlerOverride!==void 0)try{this.validateOutboundHandlerMethodName(t.outboundHandlerOverride.method),this.outboundHandlerOverride=t.outboundHandlerOverride}catch(e){console.warn("Ignoring invalid persisted outbound handler override:",e)}this.outboundByHostOverrides={};for(const[e,n]of Object.entries(t.outboundByHostOverrides??{}))try{this.validateOutboundHandlerMethodName(n.method),this.outboundByHostOverrides[e]=n}catch(s){console.warn(`Ignoring invalid persisted outbound override for ${e}:`,s)}return this.hasInterceptAllRegistration=t.hasInterceptAllRegistration===!0,t.allowedHosts&&(this.allowedHostsOverride=t.allowedHosts),t.deniedHosts&&(this.deniedHostsOverride=t.deniedHosts),this.getOutboundConfiguration()}}needsCatchAllInterception(){return this.constructor.outbound!==void 0||this.outboundHandlerOverride!==void 0}hasMutableOutboundConfiguration(){return Object.keys(this.outboundByHostOverrides).length>0||this.allowedHostsOverride!==void 0||this.deniedHostsOverride!==void 0}shouldInterceptAllOutbound(){return this.hasInterceptAllRegistration||this.needsCatchAllInterception()||this.effectiveAllowedHosts!==void 0||this.effectiveDeniedHosts!==void 0||this.hasMutableOutboundConfiguration()}getStaticOutboundByHostKeys(){const t=this.constructor;return t.outboundByHost?Object.keys(t.outboundByHost):[]}getHostsToIntercept(){const t=new Set,e=this.constructor;if(e.outboundByHost)for(const n of Object.keys(e.outboundByHost))t.add(n);for(const n of Object.keys(this.outboundByHostOverrides))t.add(n);return[...t]}async refreshOutboundInterception(){this.usingInterception&&(this.applyOutboundInterceptionPromise=this.applyOutboundInterception(),await this.applyOutboundInterceptionPromise)}async applyOutboundInterception(){const t=this.ctx;if(t.exports===void 0)throw new Error("ctx.exports is undefined, please try to update your compatibility date or export ContainerProxy from the containers package in your worker entrypoint");if(t.exports.ContainerProxy===void 0)throw new Error("ctx.exports.ContainerProxy is undefined, export ContainerProxy from the containers package in your worker entrypoint");const e=this.shouldInterceptAllOutbound();e&&(this.hasInterceptAllRegistration=e);const n=this.getOutboundConfiguration();this.persistOutboundConfiguration(n);const s=this.getHostsToIntercept(),o={enableInternet:this.enableInternet,containerId:this.ctx.id.toString(),className:this.constructor.name,outboundByHostOverrides:n.outboundByHostOverrides,outboundHandlerOverride:n.outboundHandlerOverride,allowedHosts:n.allowedHosts,deniedHosts:n.deniedHosts,interceptAll:e},a=t.exports.ContainerProxy({props:o});if(e){for(const c of this.getStaticOutboundByHostKeys())await this.container.interceptOutboundHttp(c,a),this.interceptHttps&&await this.container.interceptOutboundHttps(c,a);this.interceptHttps&&await this.container.interceptOutboundHttps("*",a),await this.container.interceptAllOutboundHttp(a)}else for(const c of s)await this.container.interceptOutboundHttp(c,a),this.interceptHttps&&await this.container.interceptOutboundHttps(c,a)}sql(t,...e){const n=t.reduce((s,o,a)=>s+o+(a<e.length?"?":""),"");return[...this.ctx.storage.sql.exec(n,...e)]}requestAndPortFromContainerFetchArgs(t,e,n){let s,o;if(t instanceof Request)s=t,o=typeof e=="number"?e:void 0;else{const a=typeof t=="string"?t:t.toString(),c=typeof e=="number"?{}:e||{};o=typeof e=="number"?e:typeof n=="number"?n:void 0,s=new Request(a,c)}if(o??=this.defaultPort,o===void 0)throw new Error("No port specified for container fetch. Set defaultPort or specify a port parameter.");return{request:s,port:o}}async getPortsToCheck(t){return t!==void 0?Array.isArray(t)?t:[t]:this.requiredPorts&&this.requiredPorts.length>0?[...this.requiredPorts]:[this.defaultPort??N]}async startContainerIfNotRunning(t,e){if(this.startInFlight)return this.startInFlight;if(this.container.running)return this.monitor||(this.monitor=this.container.monitor()),0;const n=this.doStartContainer(t,e);this.startInFlight=n;try{return await n}finally{this.startInFlight===n&&(this.startInFlight=void 0)}}async doStartContainer(t,e){const n=new Promise(a=>{t.signal?.addEventListener("abort",()=>{a(!0)})}),s=t.waitInterval??v,o=t.retries??Math.ceil(H/s);for(let a=0;a<o;a++){const c=e?.envVars??this.envVars,d=e?.entrypoint??this.entrypoint,r=e?.enableInternet??this.enableInternet,l=e?.labels??this.labels,f={enableInternet:r};c&&Object.keys(c).length>0&&(f.env=c),d&&(f.entrypoint=d),l&&Object.keys(l).length>0&&(f.labels=l),this.renewActivityTimeout();const p=async()=>{const u=await this.monitor?.catch(h=>h);if(typeof u=="number"){const h=new Error(`Container exited before we could determine the container health, exit code: ${u}`);await this.state.setStoppedWithCode(u),this.monitor=void 0;try{await this.onError(h)}catch{}throw h}else if(!I(u)){await this.state.setStopped(),this.monitor=void 0;try{await this.onError(u)}catch{}throw u}};a>0&&!this.container.running&&await p(),await this.scheduleNextAlarm(),this.container.running?await this.scheduleNextAlarm():(await this.refreshOutboundInterception(),this.container.start(f),this.monitor=this.container.monitor(),await this.state.setRunning()),this.renewActivityTimeout();const y=this.container.getTcpPort(t.portToCheck);try{const u=L(t.signal,P);return await y.fetch("http://containerstarthealthcheck",{signal:u}),a}catch(u){if(M(u)&&this.container.running)return a;if(!this.container.running&&M(u)&&await p(),await Promise.any([new Promise(h=>setTimeout(h,t.waitInterval)),n]),t.signal?.aborted)throw new Error("Aborted waiting for container to start as we received a cancellation signal",{cause:u});if(o===a+1)throw u instanceof Error&&u.message.includes("Network connection lost")&&this.ctx.abort(),await p(),await this.state.setStopped(),this.monitor=void 0,new Error(B,{cause:u});continue}}throw new Error(`Container did not start after ${o*s}ms`)}setupMonitorCallbacks(){const t=this.monitor;!t||this.monitoredPromise===t||(this.monitoredPromise=t,t.then(async()=>{await this.ctx.blockConcurrencyWhile(async()=>{this.monitor===t&&await this.state.setStoppedWithCode(0)})}).catch(async e=>{if(this.monitor!==t)return;if(I(e)){await this.ctx.blockConcurrencyWhile(async()=>{this.monitor===t&&await this.state.setStopped()});return}const n=z(e);if(n!==null){await this.ctx.blockConcurrencyWhile(async()=>{this.monitor===t&&await this.state.setStoppedWithCode(n)});return}if(await this.ctx.blockConcurrencyWhile(async()=>{this.monitor===t&&await this.state.setStopped()}),this.monitor===t)try{await this.onError(e)}catch{}}).finally(()=>{this.monitor===t&&(this.monitoredPromise=void 0,this.monitor=void 0)}))}deleteSchedules(t){this.sql`DELETE FROM container_schedules WHERE callback = ${t}`}async alarm(t){if(t!==void 0&&t.isRetry&&t.retryCount>U){((Number(this.sql`SELECT COUNT(*) as count FROM container_schedules`[0]?.count)||0)>0||this.container.running)&&await this.scheduleNextAlarm();return}const e=this.sql`
19
+ If you are deploying your Container for the first time, check your dashboard to see provisioning status, this may take a few minutes.`,{status:503}):X(r)?new Response(r instanceof Error?r.message:String(r),{status:429}):new Response(`Failed to start container: ${r instanceof Error?r.message:String(r)}`,{status:500})}const c=this.container.getTcpPort(o),d=s.url.replace("https:","http:");this.inflightRequests++;try{this.renewActivityTimeout();const r=await c.fetch(d,s);if(r.webSocket!==null){const l=r.webSocket,[p,f]=Object.values(new WebSocketPair);let g=!1;const h=()=>{g||(g=!0,this.decrementInflight())};return l.accept(),f.accept(),f.addEventListener("message",async u=>{this.renewActivityTimeout();try{const y=u.data instanceof Blob?await u.data.arrayBuffer():u.data;l.send(y)}catch{f.close(1011,"Failed to forward message to container")}}),l.addEventListener("message",async u=>{this.renewActivityTimeout();try{const y=u.data instanceof Blob?await u.data.arrayBuffer():u.data;f.send(y)}catch{l.close(1011,"Failed to forward message to client")}}),f.addEventListener("close",u=>{h();const y=u.code===1005||u.code===1006?1e3:u.code;l.close(y,u.reason)}),l.addEventListener("close",u=>{h();const y=u.code===1005||u.code===1006?1e3:u.code;f.close(y,u.reason)}),f.addEventListener("error",()=>{h(),l.close(1011,"Client WebSocket error")}),l.addEventListener("error",()=>{h(),f.close(1011,"Container WebSocket error")}),new Response(null,{status:r.status,webSocket:p,headers:r.headers})}if(r.body!==null){const{readable:l,writable:p}=new IdentityTransformStream;return r.body?.pipeTo(p).finally(()=>{this.decrementInflight()}),new Response(l,r)}return this.decrementInflight(),r}catch(r){if(this.decrementInflight(),!(r instanceof Error))throw r;return r.message.includes("Network connection lost.")?new Response("Container suddenly disconnected, try again",{status:500}):(console.error(`Error proxying request to container ${this.ctx.id}:`,r),new Response(`Error proxying request to container: ${r instanceof Error?r.message:String(r)}`,{status:500}))}}async fetch(t){if(this.defaultPort===void 0&&!t.headers.has("cf-container-target-port"))throw new Error("No port configured for this container. Set the `defaultPort` in your Container subclass, or specify a port with `container.fetch(switchPort(request, port))`.");let e=this.defaultPort;if(t.headers.has("cf-container-target-port")){const n=parseInt(t.headers.get("cf-container-target-port")??"");if(isNaN(n))throw new Error("port value from switchPort is not a number");e=n}return await this.containerFetch(t,e)}container;onStopCalled=!1;state;monitor;startInFlight;monitoredPromise;sleepAfterMs=0;inflightRequests=0;outboundByHostOverrides={};outboundHandlerOverride;allowedHostsOverride;deniedHostsOverride;hasInterceptAllRegistration=!1;validateOutboundHandlerMethodName(t){const e=w.get(this.constructor.name);if(!e||!(t in e))throw new Error(`Outbound handler method '${t}' not found in outboundHandlers for ${this.constructor.name}`)}get effectiveAllowedHosts(){return this.allowedHostsOverride??this.allowedHosts}get effectiveDeniedHosts(){return this.deniedHostsOverride??this.deniedHosts}getOutboundConfiguration(){return{outboundByHostOverrides:Object.keys(this.outboundByHostOverrides).length>0?this.outboundByHostOverrides:void 0,outboundHandlerOverride:this.outboundHandlerOverride,allowedHosts:this.effectiveAllowedHosts,deniedHosts:this.effectiveDeniedHosts,hasInterceptAllRegistration:this.hasInterceptAllRegistration||void 0}}persistOutboundConfiguration(t){this.ctx.storage.kv.put(x,{...t,allowedHosts:this.allowedHostsOverride,deniedHosts:this.deniedHostsOverride})}restoreOutboundConfiguration(){const t=this.ctx.storage.kv.get(x);if(t){if(this.outboundHandlerOverride=void 0,t.outboundHandlerOverride!==void 0)try{this.validateOutboundHandlerMethodName(t.outboundHandlerOverride.method),this.outboundHandlerOverride=t.outboundHandlerOverride}catch(e){console.warn("Ignoring invalid persisted outbound handler override:",e)}this.outboundByHostOverrides={};for(const[e,n]of Object.entries(t.outboundByHostOverrides??{}))try{this.validateOutboundHandlerMethodName(n.method),this.outboundByHostOverrides[e]=n}catch(s){console.warn(`Ignoring invalid persisted outbound override for ${e}:`,s)}return this.hasInterceptAllRegistration=t.hasInterceptAllRegistration===!0,t.allowedHosts&&(this.allowedHostsOverride=t.allowedHosts),t.deniedHosts&&(this.deniedHostsOverride=t.deniedHosts),this.getOutboundConfiguration()}}needsCatchAllInterception(){return this.constructor.outbound!==void 0||this.outboundHandlerOverride!==void 0}hasMutableOutboundConfiguration(){return Object.keys(this.outboundByHostOverrides).length>0||this.allowedHostsOverride!==void 0||this.deniedHostsOverride!==void 0}shouldInterceptAllOutbound(){return this.hasInterceptAllRegistration||this.needsCatchAllInterception()||this.effectiveAllowedHosts!==void 0||this.effectiveDeniedHosts!==void 0||this.hasMutableOutboundConfiguration()}getStaticOutboundByHostKeys(){const t=this.constructor;return t.outboundByHost?Object.keys(t.outboundByHost):[]}getHostsToIntercept(){const t=new Set,e=this.constructor;if(e.outboundByHost)for(const n of Object.keys(e.outboundByHost))t.add(n);for(const n of Object.keys(this.outboundByHostOverrides))t.add(n);return[...t]}async refreshOutboundInterception(){this.usingInterception&&(this.applyOutboundInterceptionPromise=this.applyOutboundInterception(),await this.applyOutboundInterceptionPromise)}async applyOutboundInterception(){const t=this.ctx;if(t.exports===void 0)throw new Error("ctx.exports is undefined, please try to update your compatibility date or export ContainerProxy from the containers package in your worker entrypoint");if(t.exports.ContainerProxy===void 0)throw new Error("ctx.exports.ContainerProxy is undefined, export ContainerProxy from the containers package in your worker entrypoint");const e=this.shouldInterceptAllOutbound();e&&(this.hasInterceptAllRegistration=e);const n=this.getOutboundConfiguration();this.persistOutboundConfiguration(n);const s=this.getHostsToIntercept(),o={enableInternet:this.enableInternet,containerId:this.ctx.id.toString(),className:this.constructor.name,outboundByHostOverrides:n.outboundByHostOverrides,outboundHandlerOverride:n.outboundHandlerOverride,allowedHosts:n.allowedHosts,deniedHosts:n.deniedHosts,interceptAll:e},a=t.exports.ContainerProxy({props:o});if(e){for(const c of this.getStaticOutboundByHostKeys())await this.container.interceptOutboundHttp(c,a),this.interceptHttps&&await this.container.interceptOutboundHttps(c,a);this.interceptHttps&&await this.container.interceptOutboundHttps("*",a),await this.container.interceptAllOutboundHttp(a)}else for(const c of s)await this.container.interceptOutboundHttp(c,a),this.interceptHttps&&await this.container.interceptOutboundHttps(c,a)}sql(t,...e){const n=t.reduce((s,o,a)=>s+o+(a<e.length?"?":""),"");return[...this.ctx.storage.sql.exec(n,...e)]}requestAndPortFromContainerFetchArgs(t,e,n){let s,o;if(t instanceof Request)s=t,o=typeof e=="number"?e:void 0;else{const a=typeof t=="string"?t:t.toString(),c=typeof e=="number"?{}:e||{};o=typeof e=="number"?e:typeof n=="number"?n:void 0,s=new Request(a,c)}if(o??=this.defaultPort,o===void 0)throw new Error("No port specified for container fetch. Set defaultPort or specify a port parameter.");return{request:s,port:o}}async getPortsToCheck(t){return t!==void 0?Array.isArray(t)?t:[t]:this.requiredPorts&&this.requiredPorts.length>0?[...this.requiredPorts]:[this.defaultPort??M]}async startContainerIfNotRunning(t,e){if(this.startInFlight)return this.startInFlight;if(this.container.running)return this.monitor||(this.monitor=this.container.monitor()),0;const n=this.doStartContainer(t,e);this.startInFlight=n;try{return await n}finally{this.startInFlight===n&&(this.startInFlight=void 0)}}async doStartContainer(t,e){const n=new Promise(a=>{t.signal?.addEventListener("abort",()=>{a(!0)})}),s=t.waitInterval??v,o=t.retries??Math.ceil(O/s);for(let a=0;a<o;a++){const c=e?.envVars??this.envVars,d=e?.entrypoint??this.entrypoint,r=e?.enableInternet??this.enableInternet,l=e?.labels??this.labels,p={enableInternet:r};c&&Object.keys(c).length>0&&(p.env=c),d&&(p.entrypoint=d),l&&Object.keys(l).length>0&&(p.labels=l),this.renewActivityTimeout();const f=async()=>{const h=await this.monitor?.catch(u=>u);if(typeof h=="number"){const u=new Error(`Container exited before we could determine the container health, exit code: ${h}`);await this.state.setStoppedWithCode(h),this.monitor=void 0;try{await this.onError(u)}catch{}throw u}else if(!I(h)){await this.state.setStopped(),this.monitor=void 0;try{await this.onError(h)}catch{}throw h}};a>0&&!this.container.running&&await f(),await this.scheduleNextAlarm(),this.container.running?await this.scheduleNextAlarm():(await this.refreshOutboundInterception(),this.container.start(p),this.monitor=this.container.monitor(),await this.state.setRunning()),this.renewActivityTimeout();const g=this.container.getTcpPort(t.portToCheck);try{const h=_(t.signal,N);return await g.fetch("http://containerstarthealthcheck",{signal:h}),a}catch(h){if(k(h)&&this.container.running)return a;if(!this.container.running&&k(h)&&await f(),await Promise.any([new Promise(u=>setTimeout(u,t.waitInterval)),n]),t.signal?.aborted)throw new Error("Aborted waiting for container to start as we received a cancellation signal",{cause:h});if(o===a+1)throw h instanceof Error&&h.message.includes("Network connection lost")&&this.ctx.abort(),await f(),await this.state.setStopped(),this.monitor=void 0,new Error(F,{cause:h});continue}}throw new Error(`Container did not start after ${o*s}ms`)}setupMonitorCallbacks(){const t=this.monitor;!t||this.monitoredPromise===t||(this.monitoredPromise=t,t.then(async()=>{await this.ctx.blockConcurrencyWhile(async()=>{this.monitor===t&&await this.state.setStoppedWithCode(0)})}).catch(async e=>{if(this.monitor!==t)return;if(I(e)){await this.ctx.blockConcurrencyWhile(async()=>{this.monitor===t&&await this.state.setStopped()});return}const n=z(e);if(n!==null){await this.ctx.blockConcurrencyWhile(async()=>{this.monitor===t&&await this.state.setStoppedWithCode(n)});return}if(await this.ctx.blockConcurrencyWhile(async()=>{this.monitor===t&&await this.state.setStopped()}),this.monitor===t)try{await this.onError(e)}catch{}}).finally(()=>{this.monitor===t&&(this.monitoredPromise=void 0,this.monitor=void 0)}))}deleteSchedules(t){this.sql`DELETE FROM container_schedules WHERE callback = ${t}`}async alarm(t){if(t!==void 0&&t.isRetry&&t.retryCount>j){((Number(this.sql`SELECT COUNT(*) as count FROM container_schedules`[0]?.count)||0)>0||this.container.running)&&await this.scheduleNextAlarm();return}const e=this.sql`
20
20
  SELECT * FROM container_schedules;
21
- `;let n=Date.now()+G;const s=Date.now()/1e3;for(const d of e){if(d.time>s)continue;const r=this[d.callback];if(!r||typeof r!="function"){console.error(`Callback ${d.callback} not found or is not a function`);continue}const l=this.getSchedule(d.id);try{const f=d.payload?JSON.parse(d.payload):void 0;await r.call(this,f,await l)}catch(f){console.error(`Error executing scheduled callback "${d.callback}":`,f)}this.sql`DELETE FROM container_schedules WHERE id = ${d.id}`}const o=this.sql`
21
+ `;let n=Date.now()+G;const s=Date.now()/1e3;for(const d of e){if(d.time>s)continue;const r=this[d.callback];if(!r||typeof r!="function"){console.error(`Callback ${d.callback} not found or is not a function`);continue}const l=this.getSchedule(d.id);try{const p=d.payload?JSON.parse(d.payload):void 0;await r.call(this,p,await l)}catch(p){console.error(`Error executing scheduled callback "${d.callback}":`,p)}this.sql`DELETE FROM container_schedules WHERE id = ${d.id}`}const o=this.sql`
22
22
  SELECT * FROM container_schedules;
23
- `,a=Math.min(...o.map(d=>d.time*1e3));if(!this.container.running){await this.syncPendingStoppedEvents(),o.length==0?await this.ctx.storage.deleteAlarm():await this.ctx.storage.setAlarm(a);return}if(this.isActivityExpired()){await this.onActivityExpired(),this.renewActivityTimeout(),await this.ctx.storage.setAlarm(Date.now()+O);return}n=Math.min(a,n,this.sleepAfterMs);const c=Math.max(n,Date.now()+O);await this.ctx.storage.setAlarm(c)}async syncPendingStoppedEvents(){const t=await this.state.getState();if(!this.container.running&&(t.status==="healthy"||t.status==="running")){await this.callOnStop({exitCode:0,reason:"exit"},t);return}if(!this.container.running&&t.status==="stopped_with_code"){await this.callOnStop({exitCode:t.exitCode??0,reason:"exit"},t);return}}async callOnStop(t,e){if(this.onStopCalled)return;this.onStopCalled=!0;const n=this.onStop(t);n instanceof Promise?await n.finally(()=>{this.onStopCalled=!1}):this.onStopCalled=!1,await this.state.setStoppedIfUnchanged(e)}async scheduleNextAlarm(t=1e3){const e=Date.now()+Math.max(t,O),n=await this.ctx.storage.getAlarm();n!==null&&n<=e||(await this.ctx.storage.setAlarm(e),await this.ctx.storage.sync())}async listSchedules(t){const e=this.sql`
23
+ `,a=Math.min(...o.map(d=>d.time*1e3));if(!this.container.running){await this.syncPendingStoppedEvents(),o.length==0?await this.ctx.storage.deleteAlarm():await this.ctx.storage.setAlarm(a);return}if(this.isActivityExpired()){await this.onActivityExpired(),this.renewActivityTimeout(),await this.ctx.storage.setAlarm(Date.now()+E);return}n=Math.min(a,n,this.sleepAfterMs);const c=Math.max(n,Date.now()+E);await this.ctx.storage.setAlarm(c)}async syncPendingStoppedEvents(){const t=await this.state.getState();if(!this.container.running&&(t.status==="healthy"||t.status==="running")){await this.callOnStop({exitCode:0,reason:"exit"},t);return}if(!this.container.running&&t.status==="stopped_with_code"){await this.callOnStop({exitCode:t.exitCode??0,reason:"exit"},t);return}}async callOnStop(t,e){if(this.onStopCalled)return;this.onStopCalled=!0;const n=this.onStop(t);n instanceof Promise?await n.finally(()=>{this.onStopCalled=!1}):this.onStopCalled=!1,await this.state.setStoppedIfUnchanged(e)}async scheduleNextAlarm(t=1e3){const e=Date.now()+Math.max(t,E),n=await this.ctx.storage.getAlarm();n!==null&&n<=e||(await this.ctx.storage.setAlarm(e),await this.ctx.storage.sync())}async listSchedules(t){const e=this.sql`
24
24
  SELECT * FROM container_schedules WHERE callback = ${t} LIMIT 1
25
25
  `;return!e||e.length===0?[]:e.map(this.toSchedule)}toSchedule(t){let e;try{e=JSON.parse(t.payload)}catch(n){console.error(`Error parsing payload for schedule ${t.id}:`,n),e=void 0}return t.type==="delayed"?{taskId:t.id,callback:t.callback,payload:e,type:"delayed",time:t.time,delayInSeconds:t.delayInSeconds}:{taskId:t.id,callback:t.callback,payload:e,type:"scheduled",time:t.time}}async getSchedule(t){const e=this.sql`
26
26
  SELECT * FROM container_schedules WHERE id = ${t} LIMIT 1
@@ -1 +1 @@
1
- import{LunoraError as c}from"@lunora/errors";const d=new Set(["basic","lite","standard-1","standard-2","standard-3","standard-4"]),o=/^[A-Z_]\w*$/i,l=/^\d+[smh]$/,u={h:3600,m:60,s:1},E=e=>{if(typeof e=="number")return Math.floor(e);if(l.exec(e)===null)throw new TypeError(`Invalid duration "${e}" — expected a number of seconds or "<n>[smh]"`);return Number(e.slice(0,-1))*(u[e.slice(-1)]??1)},m=e=>{const t=e.endsWith("/")?e.slice(0,-1):e,n=t.lastIndexOf("/");return n===-1?t:t.slice(n+1)},y=e=>{const t=e.lastIndexOf("/");return t===-1?".":e.slice(0,t)||"/"},$=e=>{if(typeof e!="string")return"build"in e?{buildDir:e.build.endsWith("/")?e.build.slice(0,-1):e.build,kind:"build"}:{kind:"registry",reference:e.registry};if(m(e).startsWith("Dockerfile"))return{buildContext:y(e),dockerfilePath:e,kind:"dockerfile"};const t=e.endsWith("/")?e.slice(0,-1):e;return{buildContext:t,dockerfilePath:`${t}/Dockerfile`,kind:"dockerfile"}},k=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}Container`,I=e=>`CONTAINER_${e.replaceAll(/(?<=[a-z0-9])(?=[A-Z])/g,"_").toUpperCase()}`,S=e=>`lunora-${e.replaceAll(/(?<=[a-z0-9])(?=[A-Z])/g,"-").toLowerCase()}:build`,g=e=>{if(typeof e=="string"){if(e.length===0)throw new TypeError("defineContainer: `image` must be a non-empty path or a { registry } reference");if(e.includes(":"))throw new TypeError(`defineContainer: \`image\` string "${e}" looks like a registry reference — pass it as { registry: "${e}" } instead. Plain strings are local Dockerfile paths.`);return}if("build"in e){if(typeof e.build!="string"||e.build.length===0)throw new TypeError("defineContainer: `image.build` must be a non-empty source directory for Railpack to build");return}if(typeof e.registry!="string"||e.registry.length===0)throw new TypeError("defineContainer: `image.registry` must be a non-empty fully-qualified image reference")},h=(e,t,n)=>{for(const[r,i]of Object.entries(e.secretsStore??{})){if(!o.test(r))throw new TypeError(`defineContainer: secretsStore env name "${r}" is not a valid environment variable name`);if(typeof i!="string"||i.trim().length===0)throw new TypeError(`defineContainer: \`secretsStore["${r}"]\` must be a non-empty Secrets Store binding name`);if(t.has(r)||n.has(r))throw new TypeError(`defineContainer: "${r}" is declared in both \`secretsStore\` and \`env\`/\`secrets\` — pick one source for the value`)}},w=e=>{for(const r of Object.keys(e.env??{}))if(!o.test(r))throw new TypeError(`defineContainer: env variable name "${r}" is not a valid environment variable name`);for(const r of Object.keys(e.buildArgs??{}))if(!o.test(r))throw new TypeError(`defineContainer: buildArg name "${r}" is not a valid environment variable name`);const t=new Set(Object.keys(e.env??{})),n=new Set(e.secrets);for(const r of e.secrets??[]){if(!o.test(r))throw new TypeError(`defineContainer: secret name "${r}" is not a valid environment variable name`);if(t.has(r))throw new TypeError(`defineContainer: "${r}" is declared in both \`env\` and \`secrets\` — a secret would silently overwrite the static env value; pick one`)}h(e,t,n)},s=(e,t)=>{if(!Number.isInteger(e)||e<1||e>65535)throw new TypeError(`defineContainer: \`${t}\` must be an integer in 1–65535 (got ${String(e)})`)},b=e=>{for(const t of e.readyOn??[]){if(typeof t.path!="string"||t.path.trim().length===0)throw new TypeError("defineContainer: `readyOn[].path` must be a non-empty HTTP path string");if(t.path!==t.path.trim())throw new TypeError("defineContainer: `readyOn[].path` must not have leading or trailing whitespace");if(t.port!==void 0&&s(t.port,"readyOn[].port"),t.status!==void 0&&(!Number.isInteger(t.status)||t.status<100||t.status>599))throw new TypeError(`defineContainer: \`readyOn[].status\` must be an HTTP status code in 100–599 (got ${String(t.status)})`)}},f=(e,t)=>{if(e!==void 0){if(typeof e=="string"){if(!l.test(e))throw new TypeError(`defineContainer: \`${t}\` string "${e}" must be a number of seconds followed by a unit, e.g. "30s", "5m", or "1h"`)}else if(!Number.isInteger(e)||e<1)throw new TypeError(`defineContainer: \`${t}\` must be a positive integer number of seconds or a duration string like "5m" (got ${String(e)})`)}},v=e=>{for(const t of["allowedHosts","deniedHosts"])if(e[t]?.some(n=>typeof n!="string"||n.trim().length===0))throw new TypeError(`defineContainer: \`${t}\` must be an array of non-empty hostname patterns`);if(e.interceptHttps!==void 0&&typeof e.interceptHttps!="boolean")throw new TypeError("defineContainer: `interceptHttps` must be a boolean, or omitted")},C=e=>{if(e.requiredPorts!==void 0){if(e.requiredPorts.length===0)throw new TypeError("defineContainer: `requiredPorts` must be a non-empty array of ports, or omitted");for(const t of e.requiredPorts)s(t,"requiredPorts[]")}if(e.entrypoint!==void 0&&(e.entrypoint.length===0||e.entrypoint.some(t=>typeof t!="string"||t.trim().length===0)))throw new TypeError("defineContainer: `entrypoint` must be a non-empty array of non-empty strings, or omitted");if(v(e),e.pingEndpoint!==void 0&&(typeof e.pingEndpoint!="string"||e.pingEndpoint.trim().length===0))throw new TypeError("defineContainer: `pingEndpoint` must be a non-empty path string");for(const[t,n]of Object.entries(e.labels??{}))if(t.trim().length===0||typeof n!="string")throw new TypeError("defineContainer: `labels` must be a record of non-empty keys to string values");b(e)},P=e=>{g(e.image),e.defaultPort!==void 0&&s(e.defaultPort,"defaultPort");const t=e.rollout?.stepPercentage;if(t!==void 0&&(!Number.isInteger(t)||t<1||t>100))throw new TypeError(`defineContainer: \`rollout.stepPercentage\` must be an integer in 1–100 (got ${String(t)})`);if(e.maxInstances!==void 0&&(!Number.isInteger(e.maxInstances)||e.maxInstances<1))throw new TypeError(`defineContainer: \`maxInstances\` must be a positive integer (got ${String(e.maxInstances)})`);if(typeof e.instanceType=="string"&&!d.has(e.instanceType))throw new TypeError(`defineContainer: unknown \`instanceType\` "${e.instanceType}" — use one of ${[...d].join(", ")}, or a custom { vcpu, memoryMib, diskMb } object`);return f(e.sleepAfter,"sleepAfter"),f(e.hardTimeout,"hardTimeout"),w(e),C(e),{...e,isLunoraContainer:!0}},A=e=>typeof e=="object"&&e!==null&&e.isLunoraContainer===!0,N=(e,t,n)=>{const r={...e.env};for(const i of e.secrets??[]){const a=t[i];if(typeof a!="string"){const p=n===void 0?"container":`container "${n}"`;throw new c("INTERNAL",`${p}: declared secret "${i}" is not set on the Worker environment. Add it to .dev.vars for local dev and run \`wrangler secret put ${i}\` for production.`)}r[i]=a}return r};export{I as containerBindingName,S as containerBuildTag,k as containerClassName,P as defineContainer,A as isContainerDefinition,$ as normalizeContainerImage,E as parseDurationSeconds,N as resolveContainerEnvVars};
1
+ import{LunoraError as f}from"@lunora/errors";const d=new Set(["basic","lite","standard-1","standard-2","standard-3","standard-4"]),s=/^[A-Z_]\w*$/i,c=/^\d+[smh]$/,u={h:3600,m:60,s:1},g=e=>{if(typeof e=="number")return Math.floor(e);if(c.exec(e)===null)throw new TypeError(`Invalid duration "${e}" — expected a number of seconds or "<n>[smh]"`);return Number(e.slice(0,-1))*(u[e.slice(-1)]??1)},y=e=>{const t=e.endsWith("/")?e.slice(0,-1):e,n=t.lastIndexOf("/");return n===-1?t:t.slice(n+1)},m=e=>{const t=e.lastIndexOf("/");return t===-1?".":e.slice(0,t)||"/"},$=e=>{if(typeof e!="string")return"build"in e?{buildDir:e.build.endsWith("/")?e.build.slice(0,-1):e.build,kind:"build"}:{kind:"registry",reference:e.registry};if(y(e).startsWith("Dockerfile"))return{buildContext:m(e),dockerfilePath:e,kind:"dockerfile"};const t=e.endsWith("/")?e.slice(0,-1):e;return{buildContext:t,dockerfilePath:`${t}/Dockerfile`,kind:"dockerfile"}},k=e=>`${e.charAt(0).toUpperCase()}${e.slice(1)}Container`,N=e=>`CONTAINER_${e.replaceAll(/(?<=[a-z0-9])(?=[A-Z])/g,"_").toUpperCase()}`,S=e=>`lunora-${e.replaceAll(/(?<=[a-z0-9])(?=[A-Z])/g,"-").toLowerCase()}:build`,h=e=>{if(typeof e=="string"){if(e.length===0)throw new TypeError("defineContainer: `image` must be a non-empty path or a { registry } reference");if(e.includes(":"))throw new TypeError(`defineContainer: \`image\` string "${e}" looks like a registry reference — pass it as { registry: "${e}" } instead. Plain strings are local Dockerfile paths.`);return}if("build"in e){if(typeof e.build!="string"||e.build.length===0)throw new TypeError("defineContainer: `image.build` must be a non-empty source directory for Railpack to build");return}if(typeof e.registry!="string"||e.registry.length===0)throw new TypeError("defineContainer: `image.registry` must be a non-empty fully-qualified image reference")},b=(e,t,n)=>{for(const[r,o]of Object.entries(e.secretsStore??{})){if(!s.test(r))throw new TypeError(`defineContainer: secretsStore env name "${r}" is not a valid environment variable name`);if(typeof o!="string"||o.trim().length===0)throw new TypeError(`defineContainer: \`secretsStore["${r}"]\` must be a non-empty Secrets Store binding name`);if(t.has(r)||n.has(r))throw new TypeError(`defineContainer: "${r}" is declared in both \`secretsStore\` and \`env\`/\`secrets\` — pick one source for the value`)}},w=e=>{for(const r of Object.keys(e.env??{}))if(!s.test(r))throw new TypeError(`defineContainer: env variable name "${r}" is not a valid environment variable name`);for(const r of Object.keys(e.buildArgs??{}))if(!s.test(r))throw new TypeError(`defineContainer: buildArg name "${r}" is not a valid environment variable name`);const t=new Set(Object.keys(e.env??{})),n=new Set(e.secrets);for(const r of e.secrets??[]){if(!s.test(r))throw new TypeError(`defineContainer: secret name "${r}" is not a valid environment variable name`);if(t.has(r))throw new TypeError(`defineContainer: "${r}" is declared in both \`env\` and \`secrets\` — a secret would silently overwrite the static env value; pick one`)}b(e,t,n)},i=(e,t)=>{if(!Number.isInteger(e)||e<1||e>65535)throw new TypeError(`defineContainer: \`${t}\` must be an integer in 1–65535 (got ${String(e)})`)},v=e=>{for(const t of e.readyOn??[]){if(typeof t.path!="string"||t.path.trim().length===0)throw new TypeError("defineContainer: `readyOn[].path` must be a non-empty HTTP path string");if(t.path!==t.path.trim())throw new TypeError("defineContainer: `readyOn[].path` must not have leading or trailing whitespace");if(t.port!==void 0&&i(t.port,"readyOn[].port"),t.status!==void 0&&(!Number.isInteger(t.status)||t.status<100||t.status>599))throw new TypeError(`defineContainer: \`readyOn[].status\` must be an HTTP status code in 100–599 (got ${String(t.status)})`)}},l=(e,t)=>{if(e!==void 0){if(typeof e=="string"){if(!c.test(e))throw new TypeError(`defineContainer: \`${t}\` string "${e}" must be a number of seconds followed by a unit, e.g. "30s", "5m", or "1h"`)}else if(!Number.isInteger(e)||e<1)throw new TypeError(`defineContainer: \`${t}\` must be a positive integer number of seconds or a duration string like "5m" (got ${String(e)})`)}},E=e=>{for(const t of["allowedHosts","deniedHosts"])if(e[t]?.some(r=>typeof r!="string"||r.trim().length===0))throw new TypeError(`defineContainer: \`${t}\` must be an array of non-empty hostname patterns`);if(e.interceptHttps!==void 0&&typeof e.interceptHttps!="boolean")throw new TypeError("defineContainer: `interceptHttps` must be a boolean, or omitted")},T=e=>{if(e.requiredPorts!==void 0){if(e.requiredPorts.length===0)throw new TypeError("defineContainer: `requiredPorts` must be a non-empty array of ports, or omitted");for(const t of e.requiredPorts)i(t,"requiredPorts[]")}if(e.entrypoint!==void 0&&(e.entrypoint.length===0||e.entrypoint.some(t=>typeof t!="string"||t.trim().length===0)))throw new TypeError("defineContainer: `entrypoint` must be a non-empty array of non-empty strings, or omitted");if(E(e),e.pingEndpoint!==void 0&&(typeof e.pingEndpoint!="string"||e.pingEndpoint.trim().length===0))throw new TypeError("defineContainer: `pingEndpoint` must be a non-empty path string");for(const[t,n]of Object.entries(e.labels??{}))if(t.trim().length===0||typeof n!="string")throw new TypeError("defineContainer: `labels` must be a record of non-empty keys to string values");v(e)},I=e=>{h(e.image),e.defaultPort!==void 0&&i(e.defaultPort,"defaultPort");const t=e.rollout?.stepPercentage;if(t!==void 0&&(!Number.isInteger(t)||t<1||t>100))throw new TypeError(`defineContainer: \`rollout.stepPercentage\` must be an integer in 1–100 (got ${String(t)})`);if(e.maxInstances!==void 0&&(!Number.isInteger(e.maxInstances)||e.maxInstances<1))throw new TypeError(`defineContainer: \`maxInstances\` must be a positive integer (got ${String(e.maxInstances)})`);if(typeof e.instanceType=="string"&&!d.has(e.instanceType))throw new TypeError(`defineContainer: unknown \`instanceType\` "${e.instanceType}" — use one of ${[...d].join(", ")}, or a custom { vcpu, memoryMib, diskMb } object`);return l(e.sleepAfter,"sleepAfter"),l(e.hardTimeout,"hardTimeout"),w(e),T(e),{...e,isLunoraContainer:!0}},A=e=>typeof e=="object"&&e!==null&&e.isLunoraContainer===!0,P=(e,t,n)=>{const r={...e.env};for(const o of e.secrets??[]){const a=t[o];if(typeof a!="string"){const p=n===void 0?"container":`container "${n}"`;throw new f("INTERNAL",`${p}: declared secret "${o}" is not set on the Worker environment. Add it to .dev.vars for local dev and run \`wrangler secret put ${o}\` for production.`)}r[o]=a}return r};export{N as containerBindingName,S as containerBuildTag,k as containerClassName,I as defineContainer,A as isContainerDefinition,$ as normalizeContainerImage,g as parseDurationSeconds,P as resolveContainerEnvVars};
@@ -0,0 +1 @@
1
+ import{LunoraError as F}from"@lunora/errors";import{containerBindingName as p}from"./containerBindingName-D01FopQt.mjs";import{a as N}from"./jurisdiction-DtE9s70w.mjs";const h=3,v=3e4,C=3,O=500,P="cf-container-target-port",_=(t,e,o,n)=>{const r=typeof t=="string"&&t.startsWith("/")?new Request(`http://container${t}`,e):new Request(t,e);return o!==void 0&&r.headers.set(P,String(o)),n!==void 0&&r.headers.set("traceparent",n),r},E=async t=>{t<=0||await new Promise(e=>{setTimeout(e,t)})},H=/no container instance|not listening|try again later|rate.?limit|provision/i,L="no Container instance available",b="Failed to start container:",B=1024,M=async t=>{const e=t.clone().body;if(e===null)return"";const o=e.getReader(),n=new TextDecoder;let r="";try{for(;r.length<B;){const{done:s,value:a}=await o.read();if(s)break;r+=n.decode(a,{stream:!0})}}finally{await o.cancel()}return r},k=t=>t instanceof Error&&H.test(t.message),$=async t=>{if(t.status===429)return!0;if(t.status!==500&&t.status!==503)return!1;try{const e=await M(t);return e.includes(L)||e.startsWith(b)}catch{return!1}},A=(t,e={},o,n)=>{const r=Math.max(1,e.attempts??C),s=e.backoffMs??O,a=e.maxBackoffMs??v;return{fetch:async(m,T)=>{const u=typeof m=="string"?r:1;let l;for(let d=0;d<u;d+=1){const f=d===u-1;d>0&&await E(Math.min(s*2**(d-1),a));try{const i=await t(_(m,T,o,n));if(f||!await $(i))return i}catch(i){if(l=i,f||!k(i))throw i}}throw l instanceof Error?l:new Error("ctx.containers: cold-start retry exhausted")},port:m=>A(t,e,m,n)}},w=(t,e,o,n)=>A(async r=>t.get(t.idFromName(e)).fetch(r),o,void 0,n),c=async(t,e,o,n)=>{const r=t[e];if(typeof r!="function")throw new TypeError(`ctx.containers: the "${o}" container DO does not expose ${e}() — is @lunora/container/do up to date?`);return r(n)},I=(t,e)=>({allow:async o=>c(t(),"allowHost",e,o),deny:async o=>c(t(),"denyHost",e,o),removeAllowed:async o=>c(t(),"removeAllowedHost",e,o),removeDenied:async o=>c(t(),"removeDeniedHost",e,o),setAllowed:async o=>c(t(),"setAllowedHosts",e,[...o]),setDenied:async o=>c(t(),"setDeniedHosts",e,[...o])}),S=(t,e,o,n,r)=>{const s=()=>t.get(t.idFromName(o));return{...A(async a=>s().fetch(a),n,void 0,r),destroy:async()=>c(s(),"destroy",e.binding),egress:I(s,e.binding),getState:async()=>c(s(),"getState",e.binding),renewActivityTimeout:async()=>c(s(),"renewActivityTimeout",e.binding),start:async a=>c(s(),"start",e.binding,a),stop:async a=>c(s(),"stop",e.binding,a)}},x=t=>`pool-${String(Math.floor(Math.random()*t))}`,q=t=>t.status>=500,R=(t,e,o={},n,r)=>{const s=o.size??e.maxInstances??h,a=Math.max(1,o.attempts??3),m=o.backoffMs??100,T=o.maxBackoffMs??v,u=o.retryOn??q;return{fetch:async(l,d)=>{const f=typeof l=="string"?a:1;let i;for(let y=0;y<f;y+=1){y>0&&await E(Math.min(m*2**(y-1),T));const D=_(l,d,n,r);try{const g=await t.get(t.idFromName(x(s))).fetch(D);if(y===f-1||!u(g))return g}catch(g){i=g}}throw i instanceof Error?i:new Error(`ctx.containers.${e.exportName}.pool(): all ${String(f)} attempts failed`)},port:l=>R(t,e,o,l,r)}},U=(t,e,o)=>({any:(n,r)=>w(t,x(n??e.maxInstances??h),r,o),get:(n,r)=>S(t,e,n,r,o),pool:n=>R(t,e,n,void 0,o)}),j=t=>{const e=()=>{throw new F("INTERNAL",`ctx.containers.${t.exportName}: no "${t.binding}" Durable Object binding found. Run \`lunora dev\` (or \`lunora deploy\`) to reconcile wrangler.jsonc, and make sure the worker entry re-exports the generated container classes.`)};return{any:e,get:e,pool:e}},G=(t,e,o,n)=>{const r={};for(const s of e){const a=t[s.binding];r[s.exportName]=a&&typeof a.idFromName=="function"&&typeof a.get=="function"?U(N(a,o),s,n):j(s)}return r},Y=t=>{const e=o=>({allowHost:()=>Promise.resolve(),denyHost:()=>Promise.resolve(),destroy:()=>Promise.resolve(),fetch:n=>Promise.resolve(t(n,{name:o})),getState:()=>Promise.resolve({lastChange:0}),removeAllowedHost:()=>Promise.resolve(),removeDeniedHost:()=>Promise.resolve(),renewActivityTimeout:()=>Promise.resolve(),setAllowedHosts:()=>Promise.resolve(),setDeniedHosts:()=>Promise.resolve(),start:()=>Promise.resolve(),stop:()=>Promise.resolve()});return{get:o=>e(String(o)),idFromName:o=>o}},J=t=>{const e={};for(const[o,n]of Object.entries(t)){const r=Y(n),s={binding:p(o)};e[o]={any:()=>w(r,"pool-0",{attempts:1}),get:a=>S(r,s,a,{attempts:1}),pool:()=>w(r,"pool-0",{attempts:1})}}return e};export{G as createContainerContext,J as createContainerTestContext};
@@ -0,0 +1 @@
1
+ const t=(o,r)=>{if(r===void 0)return o;if(typeof o.jurisdiction!="function")throw new TypeError(`@lunora/container: Durable Object namespace does not support jurisdiction("${r}") — update @cloudflare/workers-types or remove the jurisdiction option`);return o.jurisdiction(r)};export{t as a};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/container",
3
- "version": "1.0.0-alpha.30",
3
+ "version": "1.0.0-alpha.31",
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.21"
59
+ "@lunora/errors": "1.0.0-alpha.22"
60
60
  },
61
61
  "engines": {
62
62
  "node": "^22.15.0 || >=24.11.0"
@@ -1 +0,0 @@
1
- import{LunoraError as D}from"@lunora/errors";import{containerBindingName as E}from"./containerBindingName-CP4v-2Cw.mjs";import{e as $}from"./jurisdiction-BKRNOTip.mjs";const v=3,x=3e4,k=3,S=500,T="cf-container-target-port",b=(t,e,o,n)=>{const r=typeof t=="string"&&t.startsWith("/")?new Request(`http://container${t}`,e):new Request(t,e);return o!==void 0&&r.headers.set(T,String(o)),n!==void 0&&r.headers.set("traceparent",n),r},P=async t=>{t<=0||await new Promise(e=>{setTimeout(e,t)})},C=/no container instance|not listening|try again later|rate.?limit|provision/i,F="no Container instance available",R="Failed to start container:",j=1024,B=async t=>{const e=t.clone().body;if(e===null)return"";const o=e.getReader(),n=new TextDecoder;let r="";try{for(;r.length<j;){const{done:s,value:a}=await o.read();if(s)break;r+=n.decode(a,{stream:!0})}}finally{await o.cancel()}return r},I=t=>t instanceof Error&&C.test(t.message),O=async t=>{if(t.status===429)return!0;if(t.status!==500&&t.status!==503)return!1;try{const e=await B(t);return e.includes(F)||e.startsWith(R)}catch{return!1}},h=(t,e={},o,n)=>{const r=Math.max(1,e.attempts??k),s=e.backoffMs??S,a=e.maxBackoffMs??x;return{fetch:async(d,g)=>{const u=typeof d=="string"?r:1;let l;for(let m=0;m<u;m+=1){const f=m===u-1;m>0&&await P(Math.min(s*2**(m-1),a));try{const c=await t(b(d,g,o,n));if(f||!await O(c))return c}catch(c){if(l=c,f||!I(c))throw c}}throw l instanceof Error?l:new Error("ctx.containers: cold-start retry exhausted")},port:d=>h(t,e,d,n)}},w=(t,e,o,n)=>h(async r=>t.get(t.idFromName(e)).fetch(r),o,void 0,n),i=async(t,e,o,n)=>{const r=t[e];if(typeof r!="function")throw new TypeError(`ctx.containers: the "${o}" container DO does not expose ${e}() — is @lunora/container/do up to date?`);return r(n)},q=(t,e)=>({allow:async o=>i(t(),"allowHost",e,o),deny:async o=>i(t(),"denyHost",e,o),removeAllowed:async o=>i(t(),"removeAllowedHost",e,o),removeDenied:async o=>i(t(),"removeDeniedHost",e,o),setAllowed:async o=>i(t(),"setAllowedHosts",e,[...o]),setDenied:async o=>i(t(),"setDeniedHosts",e,[...o])}),H=(t,e,o,n,r)=>{const s=()=>t.get(t.idFromName(o));return{...h(async a=>s().fetch(a),n,void 0,r),destroy:async()=>i(s(),"destroy",e.binding),egress:q(s,e.binding),getState:async()=>i(s(),"getState",e.binding),renewActivityTimeout:async()=>i(s(),"renewActivityTimeout",e.binding),start:async a=>i(s(),"start",e.binding,a),stop:async a=>i(s(),"stop",e.binding,a)}},N=t=>`pool-${String(Math.floor(Math.random()*t))}`,L=t=>t.status>=500,A=(t,e,o={},n,r)=>{const s=o.size??e.maxInstances??v,a=Math.max(1,o.attempts??3),d=o.backoffMs??100,g=o.maxBackoffMs??x,u=o.retryOn??L;return{fetch:async(l,m)=>{const f=typeof l=="string"?a:1;let c;for(let y=0;y<f;y+=1){y>0&&await P(Math.min(d*2**(y-1),g));const M=b(l,m,n,r);try{const p=await t.get(t.idFromName(N(s))).fetch(M);if(y===f-1||!u(p))return p}catch(p){c=p}}throw c instanceof Error?c:new Error(`ctx.containers.${e.exportName}.pool(): all ${String(f)} attempts failed`)},port:l=>A(t,e,o,l,r)}},z=(t,e,o)=>({any:(n,r)=>w(t,N(n??e.maxInstances??v),r,o),get:(n,r)=>H(t,e,n,r,o),pool:n=>A(t,e,n,void 0,o)}),W=t=>{const e=()=>{throw new D("INTERNAL",`ctx.containers.${t.exportName}: no "${t.binding}" Durable Object binding found. Run \`lunora dev\` (or \`lunora deploy\`) to reconcile wrangler.jsonc, and make sure the worker entry re-exports the generated container classes.`)};return{any:e,get:e,pool:e}},G=(t,e,o,n)=>{const r={};for(const s of e){const a=t[s.binding];r[s.exportName]=a&&typeof a.idFromName=="function"&&typeof a.get=="function"?z($(a,o),s,n):W(s)}return r},J=t=>{const e=o=>({allowHost:()=>Promise.resolve(),denyHost:()=>Promise.resolve(),destroy:()=>Promise.resolve(),fetch:n=>Promise.resolve(t(n,{name:o})),getState:()=>Promise.resolve({lastChange:0}),removeAllowedHost:()=>Promise.resolve(),removeDeniedHost:()=>Promise.resolve(),renewActivityTimeout:()=>Promise.resolve(),setAllowedHosts:()=>Promise.resolve(),setDeniedHosts:()=>Promise.resolve(),start:()=>Promise.resolve(),stop:()=>Promise.resolve()});return{get:o=>e(String(o)),idFromName:o=>o}},K=t=>{const e={};for(const[o,n]of Object.entries(t)){const r=J(n),s={binding:E(o)};e[o]={any:()=>w(r,"pool-0",{attempts:1}),get:a=>H(r,s,a,{attempts:1}),pool:()=>w(r,"pool-0",{attempts:1})}}return e};export{G as createContainerContext,K as createContainerTestContext};
@@ -1 +0,0 @@
1
- const r=(o,e)=>{if(e===void 0)return o;if(typeof o.jurisdiction!="function")throw new TypeError(`@lunora/container: Durable Object namespace does not support jurisdiction("${e}") — update @cloudflare/workers-types or remove the jurisdiction option`);return o.jurisdiction(e)};export{r as e};