@lunora/container 1.0.0-alpha.41 → 1.0.0-alpha.42
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/do/index.d.mts +50 -0
- package/dist/do/index.d.ts +50 -0
- package/dist/do/index.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/dist/otel.mjs +1 -1
- package/dist/packem_shared/{containerBindingName-D01FopQt.mjs → containerBindingName-C7SonAIK.mjs} +1 -1
- package/dist/packem_shared/{createContainerContext-IYvMMEfq.mjs → createContainerContext-DApPtouZ.mjs} +1 -1
- package/package.json +1 -1
package/dist/do/index.d.mts
CHANGED
|
@@ -514,6 +514,8 @@ declare class LunoraContainer<Env = unknown> extends Container<Env> {
|
|
|
514
514
|
private readonly lunoraDefaultPort?;
|
|
515
515
|
/** Hard-cap lifetime in whole seconds (from the `hardTimeout` config), or `undefined`. */
|
|
516
516
|
private readonly lunoraHardTimeoutSeconds?;
|
|
517
|
+
/** In-flight `readyOn` gate for the current start; cleared when it fails. See `awaitReadinessGate`. */
|
|
518
|
+
private lunoraReadiness?;
|
|
517
519
|
/** Declarative readiness probes that gate request proxying (from the `readyOn` config). */
|
|
518
520
|
private readonly lunoraReadyOn;
|
|
519
521
|
/** Map of container env-var name → Worker Secrets Store binding name (from the `secretsStore` config). */
|
|
@@ -528,6 +530,13 @@ declare class LunoraContainer<Env = unknown> extends Container<Env> {
|
|
|
528
530
|
* request — a no-op when `secretsStore` is unset.
|
|
529
531
|
*/
|
|
530
532
|
override containerFetch(...args: Parameters<Container<Env>["containerFetch"]>): Promise<Response>;
|
|
533
|
+
/**
|
|
534
|
+
* The start path `containerFetch` takes (and the one an app can call itself).
|
|
535
|
+
* The base's last act is `blockConcurrencyWhile(… onStart())`, so this
|
|
536
|
+
* override resumes on the far side of that gate — which is where
|
|
537
|
+
* {@link afterContainerStart} has to run. See its docblock.
|
|
538
|
+
*/
|
|
539
|
+
override startAndWaitForPorts(...args: Parameters<Container<Env>["startAndWaitForPorts"]>): Promise<void>;
|
|
531
540
|
/**
|
|
532
541
|
* Explicit start (`ctx.containers.<name>.get(id).start()`). Resolves the
|
|
533
542
|
* `secretsStore` bindings into `envVars` first, mirroring
|
|
@@ -553,6 +562,47 @@ declare class LunoraContainer<Env = unknown> extends Container<Env> {
|
|
|
553
562
|
generation?: number;
|
|
554
563
|
}): Promise<void>;
|
|
555
564
|
override onStop(parameters: StopParams): Promise<void>;
|
|
565
|
+
/**
|
|
566
|
+
* Arm the hard timeout and block on the `readyOn` probes — the work that has
|
|
567
|
+
* to happen once per real start, **outside** the base's start gate.
|
|
568
|
+
*
|
|
569
|
+
* It cannot live in `onStart`, which is the obvious home for it: the base
|
|
570
|
+
* invokes that hook as `blockConcurrencyWhile(async () => { … onStart() })`
|
|
571
|
+
* (`@cloudflare/containers`, both `start()` and `startAndWaitForPorts()`),
|
|
572
|
+
* and workerd treats a *rejecting* `blockConcurrencyWhile` closure as
|
|
573
|
+
* unrecoverable — it aborts the Durable Object, discards its in-memory state
|
|
574
|
+
* and every hibernating socket on it, and flattens the error to a plain
|
|
575
|
+
* `Error`. A readiness timeout is an ordinary, diagnosable failure: it must
|
|
576
|
+
* surface as the `LunoraError` naming the check, the port and the budget,
|
|
577
|
+
* not cost the object its life and arrive as an opaque message. (The same
|
|
578
|
+
* reasoning, and the same settle-outside-the-gate remedy, is written up on
|
|
579
|
+
* `ShardHost.runSerialized` in `@lunora/platform-cloudflare`.) A 30-second
|
|
580
|
+
* wait also has no business inside a gate that blocks every other dispatch
|
|
581
|
+
* to the object.
|
|
582
|
+
*
|
|
583
|
+
* Both start entry points call this immediately after `super`, so it runs
|
|
584
|
+
* once per start. It does NOT gate proxying on its own: the base commits the
|
|
585
|
+
* healthy state inside the start gate, before this runs, so a concurrent
|
|
586
|
+
* request would sail past `containerFetch`'s status check. That is what
|
|
587
|
+
* `awaitReadinessGate` is for, and it is the seam the move cost us —
|
|
588
|
+
* the in-gate placement got this for free from `blockConcurrencyWhile`.
|
|
589
|
+
*/
|
|
590
|
+
private afterContainerStart;
|
|
591
|
+
/**
|
|
592
|
+
* Block until the `readyOn` probes for the current start have passed.
|
|
593
|
+
*
|
|
594
|
+
* Necessary because the base marks the container healthy *inside* the start
|
|
595
|
+
* gate — `startAndWaitForPorts` runs `setHealthy()` immediately before
|
|
596
|
+
* `onStart()` — while our probes run after it returns. `containerFetch`
|
|
597
|
+
* skips the start path entirely once it observes
|
|
598
|
+
* `container.running && status === "healthy"`, so without this a request
|
|
599
|
+
* arriving mid-probe would proxy to a container that never reported ready,
|
|
600
|
+
* and a request arriving after a *failed* probe would do so permanently.
|
|
601
|
+
* Holding `setHealthy` and the probes together the way the base does would
|
|
602
|
+
* mean putting the probes back inside the gate, which is the defect this
|
|
603
|
+
* whole path exists to avoid.
|
|
604
|
+
*/
|
|
605
|
+
private awaitReadinessGate;
|
|
556
606
|
/**
|
|
557
607
|
* Arm the hard-timeout kill via the base scheduler (so it integrates with
|
|
558
608
|
* the container's own alarm machinery instead of fighting it). Bumps the run
|
package/dist/do/index.d.ts
CHANGED
|
@@ -514,6 +514,8 @@ declare class LunoraContainer<Env = unknown> extends Container<Env> {
|
|
|
514
514
|
private readonly lunoraDefaultPort?;
|
|
515
515
|
/** Hard-cap lifetime in whole seconds (from the `hardTimeout` config), or `undefined`. */
|
|
516
516
|
private readonly lunoraHardTimeoutSeconds?;
|
|
517
|
+
/** In-flight `readyOn` gate for the current start; cleared when it fails. See `awaitReadinessGate`. */
|
|
518
|
+
private lunoraReadiness?;
|
|
517
519
|
/** Declarative readiness probes that gate request proxying (from the `readyOn` config). */
|
|
518
520
|
private readonly lunoraReadyOn;
|
|
519
521
|
/** Map of container env-var name → Worker Secrets Store binding name (from the `secretsStore` config). */
|
|
@@ -528,6 +530,13 @@ declare class LunoraContainer<Env = unknown> extends Container<Env> {
|
|
|
528
530
|
* request — a no-op when `secretsStore` is unset.
|
|
529
531
|
*/
|
|
530
532
|
override containerFetch(...args: Parameters<Container<Env>["containerFetch"]>): Promise<Response>;
|
|
533
|
+
/**
|
|
534
|
+
* The start path `containerFetch` takes (and the one an app can call itself).
|
|
535
|
+
* The base's last act is `blockConcurrencyWhile(… onStart())`, so this
|
|
536
|
+
* override resumes on the far side of that gate — which is where
|
|
537
|
+
* {@link afterContainerStart} has to run. See its docblock.
|
|
538
|
+
*/
|
|
539
|
+
override startAndWaitForPorts(...args: Parameters<Container<Env>["startAndWaitForPorts"]>): Promise<void>;
|
|
531
540
|
/**
|
|
532
541
|
* Explicit start (`ctx.containers.<name>.get(id).start()`). Resolves the
|
|
533
542
|
* `secretsStore` bindings into `envVars` first, mirroring
|
|
@@ -553,6 +562,47 @@ declare class LunoraContainer<Env = unknown> extends Container<Env> {
|
|
|
553
562
|
generation?: number;
|
|
554
563
|
}): Promise<void>;
|
|
555
564
|
override onStop(parameters: StopParams): Promise<void>;
|
|
565
|
+
/**
|
|
566
|
+
* Arm the hard timeout and block on the `readyOn` probes — the work that has
|
|
567
|
+
* to happen once per real start, **outside** the base's start gate.
|
|
568
|
+
*
|
|
569
|
+
* It cannot live in `onStart`, which is the obvious home for it: the base
|
|
570
|
+
* invokes that hook as `blockConcurrencyWhile(async () => { … onStart() })`
|
|
571
|
+
* (`@cloudflare/containers`, both `start()` and `startAndWaitForPorts()`),
|
|
572
|
+
* and workerd treats a *rejecting* `blockConcurrencyWhile` closure as
|
|
573
|
+
* unrecoverable — it aborts the Durable Object, discards its in-memory state
|
|
574
|
+
* and every hibernating socket on it, and flattens the error to a plain
|
|
575
|
+
* `Error`. A readiness timeout is an ordinary, diagnosable failure: it must
|
|
576
|
+
* surface as the `LunoraError` naming the check, the port and the budget,
|
|
577
|
+
* not cost the object its life and arrive as an opaque message. (The same
|
|
578
|
+
* reasoning, and the same settle-outside-the-gate remedy, is written up on
|
|
579
|
+
* `ShardHost.runSerialized` in `@lunora/platform-cloudflare`.) A 30-second
|
|
580
|
+
* wait also has no business inside a gate that blocks every other dispatch
|
|
581
|
+
* to the object.
|
|
582
|
+
*
|
|
583
|
+
* Both start entry points call this immediately after `super`, so it runs
|
|
584
|
+
* once per start. It does NOT gate proxying on its own: the base commits the
|
|
585
|
+
* healthy state inside the start gate, before this runs, so a concurrent
|
|
586
|
+
* request would sail past `containerFetch`'s status check. That is what
|
|
587
|
+
* `awaitReadinessGate` is for, and it is the seam the move cost us —
|
|
588
|
+
* the in-gate placement got this for free from `blockConcurrencyWhile`.
|
|
589
|
+
*/
|
|
590
|
+
private afterContainerStart;
|
|
591
|
+
/**
|
|
592
|
+
* Block until the `readyOn` probes for the current start have passed.
|
|
593
|
+
*
|
|
594
|
+
* Necessary because the base marks the container healthy *inside* the start
|
|
595
|
+
* gate — `startAndWaitForPorts` runs `setHealthy()` immediately before
|
|
596
|
+
* `onStart()` — while our probes run after it returns. `containerFetch`
|
|
597
|
+
* skips the start path entirely once it observes
|
|
598
|
+
* `container.running && status === "healthy"`, so without this a request
|
|
599
|
+
* arriving mid-probe would proxy to a container that never reported ready,
|
|
600
|
+
* and a request arriving after a *failed* probe would do so permanently.
|
|
601
|
+
* Holding `setHealthy` and the probes together the way the base does would
|
|
602
|
+
* mean putting the probes back inside the gate, which is the defect this
|
|
603
|
+
* whole path exists to avoid.
|
|
604
|
+
*/
|
|
605
|
+
private awaitReadinessGate;
|
|
556
606
|
/**
|
|
557
607
|
* Arm the hard-timeout kill via the base scheduler (so it integrates with
|
|
558
608
|
* the container's own alarm machinery instead of fighting it). Bumps the run
|
package/dist/do/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraError as u}from"@lunora/errors";import{a as
|
|
1
|
+
import{LunoraError as u}from"@lunora/errors";import{a as w}from"../packem_shared/abort-deadline-jwbW9Ala.mjs";import{resolveContainerEnvVars as y,parseDurationSeconds as f}from"../packem_shared/containerBindingName-C7SonAIK.mjs";import{a as g}from"../packem_shared/jurisdiction-DtE9s70w.mjs";import{Container as R}from"../packem_shared/ContainerProxy-fTW_pXWY.mjs";import{ContainerProxy as x,outboundParams as M}from"../packem_shared/ContainerProxy-fTW_pXWY.mjs";const E="lunora",N=(s,t,r,e)=>({container:s,event:r,instance:t,level:r==="error"?"error":"info",message:e,source:E,ts:Date.now(),type:"container"}),i=(s,t,r,e)=>{const n=N(s,t,r,e),o=JSON.stringify(n);return r==="error"?console.error(o):console.log(o),n},T="__lunora_admin__:recordContainerEvent",S="__root__",_=s=>{if(s===null||typeof s!="object")return!1;const t=s;return typeof t.get=="function"&&typeof t.idFromName=="function"},O=(s,t)=>{const r=g(s,t);return typeof r.getByName=="function"?r.getByName(S):r.get(r.idFromName(S))},I=async(s,t,r)=>{try{const e=s??{},n=e.SHARD;if(!_(n))return;const o=typeof e.LUNORA_ADMIN_TOKEN=="string"?e.LUNORA_ADMIN_TOKEN:void 0;if(!o||o.length===0)return;const a=new Request("https://shard.internal/rpc",{body:JSON.stringify({args:{event:t},functionPath:T}),headers:{authorization:`Bearer ${o}`,"content-type":"application/json"},method:"POST"});await O(n,r).fetch(a)}catch{}},v=500,m=3e4,d="__lunoraHardTimeoutGeneration";class $ extends R{lunoraJurisdiction;lunoraName;lunoraDefaultPort;lunoraHardTimeoutSeconds;lunoraReadiness;lunoraReadyOn;lunoraSecretsStore;lunoraSecretsStoreResolved;constructor(t,r,e,n,o){super(t,r,{defaultPort:e.defaultPort,entrypoint:e.entrypoint?[...e.entrypoint]:void 0,envVars:y(e,r,n),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=n??"container",this.lunoraJurisdiction=o,this.lunoraDefaultPort=e.defaultPort,this.lunoraReadyOn=e.readyOn?[...e.readyOn]:[],this.lunoraHardTimeoutSeconds=e.hardTimeout===void 0?void 0:f(e.hardTimeout),this.lunoraSecretsStore=e.secretsStore}async containerFetch(...t){return await this.resolveSecretsStoreEnv(),await this.awaitReadinessGate(),super.containerFetch(...t)}async startAndWaitForPorts(...t){await super.startAndWaitForPorts(...t),await this.afterContainerStart()}async start(...t){const[r]=t;r?.envVars===void 0&&await this.resolveSecretsStoreEnv(),await super.start(...t),await this.afterContainerStart()}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()}async onHardTimeoutExpired(t){const r=await this.ctx.storage.get(d);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),this.lunoraReadiness=void 0,await super.onStop(t)}async afterContainerStart(){const t=this.lunoraReadiness;if(t!==void 0){await t;return}const r=(async()=>{await this.armHardTimeout(),await this.awaitContainerReadiness()})();this.lunoraReadiness=r;try{await r}catch(e){throw this.lunoraReadiness=void 0,e}}async awaitReadinessGate(){if(this.lunoraReadyOn.length!==0){if(this.lunoraReadiness===void 0){const{status:t}=await this.getState();if(t!=="healthy")return;this.lunoraReadiness=this.awaitContainerReadiness()}try{await this.lunoraReadiness}catch(t){throw this.lunoraReadiness=void 0,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 r=this.env,e={};for(const[n,o]of Object.entries(t)){const a=r[o];if(a===void 0||typeof a.get!="function")throw new u("INTERNAL",`container "${this.lunoraName}": secretsStore env "${n}" points at binding "${o}", which is not a Secrets Store binding on the Worker env. Add a \`secrets_store_secrets\` entry binding "${o}".`);const c=await a.get();if(typeof c!="string")throw new TypeError(`container "${this.lunoraName}": Secrets Store binding "${o}" (env "${n}") did not resolve to a string value.`);e[n]=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()+m;await Promise.all(this.lunoraReadyOn.map(async e=>this.awaitReadinessCheck(t,e,r)))}async awaitReadinessCheck(t,r,e){const n=r.port??this.lunoraDefaultPort;if(n===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 o=r.status??200,a=r.path.startsWith("/")?r.path:`/${r.path}`,c=t.getTcpPort(n);for(;;){const h=Math.max(v,e-Date.now()),l=w(void 0,h,()=>new DOMException(`readiness probe timed out after ${String(h)}ms`,"TimeoutError"));try{if((await c.fetch(`http://container${a}`,{signal:l.signal})).status===o)return}catch{}finally{l.dispose()}if(Date.now()>=e)throw new u("INTERNAL",`container "${this.lunoraName}": readiness check "${r.path}" (port ${String(n)}) did not return ${String(o)} within ${String(m)}ms`);await new Promise(p=>{setTimeout(p,v)})}}surfaceInStudioLogs(t){I(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{x as ContainerProxy,$ as LunoraContainer,M as outboundParams};
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createContainerContext as o,createContainerTestContext as r}from"./packem_shared/createContainerContext-
|
|
1
|
+
import{createContainerContext as o,createContainerTestContext as r}from"./packem_shared/createContainerContext-DApPtouZ.mjs";import{containerBindingName as a,containerBuildTag as i,containerClassName as C,defineContainer as m,isContainerDefinition as s,normalizeContainerImage as c,resolveContainerEnvVars as f}from"./packem_shared/containerBindingName-C7SonAIK.mjs";import{C as l}from"./packem_shared/exec-KlR4eMip.mjs";export{l as CONTAINER_EXEC_PATH,a as containerBindingName,i as containerBuildTag,C as containerClassName,o as createContainerContext,r as createContainerTestContext,m as defineContainer,s as isContainerDefinition,c as normalizeContainerImage,f as resolveContainerEnvVars};
|
package/dist/otel.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as $}from"./packem_shared/abort-deadline-jwbW9Ala.mjs";const j={debug:5,error:17,fatal:21,info:9,log:9,trace:1,warn:13},N=e=>`${String(Math.round(e))}000000`,F=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,"0")),y=512,I=new Uint8Array(y);let T=y;const x=(e,t,r)=>{let n="";for(let o=0;o<r;o+=1)n+=F[e[t+o]];return n},L=e=>{if(e>y){const r=new Uint8Array(e);return crypto.getRandomValues(r),x(r,0,e)}T+e>y&&(crypto.getRandomValues(I),T=0);const t=x(I,T,e);return T+=e,t},S=/^[0-9a-f]+$/,Y=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||!S.test(r)||r==="ff"||r==="00"&&t.length!==4||n===void 0||o===void 0||s===void 0||s.length!==2||!S.test(s)||n.length!==32||o.length!==16||!S.test(n)||!S.test(o)||n==="00000000000000000000000000000000"||o==="0000000000000000"))return{parentSpanId:o,sampled:(Number.parseInt(s,16)&1)===1,traceId:n}},h=(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}},V=e=>e===void 0?[]:Object.entries(e).map(([t,r])=>h(t,r)),X=(e,t,r)=>{const n={},o=new Map,s=(i,d)=>{const m=i.toLowerCase(),f=o.get(m);f===void 0?(o.set(m,i),n[i]=d):n[f]=d};for(const[i,d]of Object.entries(e))s(i,d);for(const[i,d]of Object.entries(t??{}))s(i,d);return r!==void 0&&r.length>0&&s("authorization",`Bearer ${r}`),n},P=e=>Array.isArray(e)?e:[e],D=(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])=>h(n,o))},K=(e,t,r,n)=>({resourceSpans:[{resource:{attributes:D(r,n)},scopeSpans:[{scope:{name:t},spans:P(e)}]}]}),z=(e,t,r,n)=>({resourceLogs:[{resource:{attributes:D(r,n)},scopeLogs:[{logRecords:P(e),scope:{name:t}}]}]}),G=512,W=200,U=e=>{const t=e.maxItems??G,r=e.maxDelayMs??W;let n=[],o,s,i;const d=()=>{o!==void 0&&(clearTimeout(o),o=void 0)},m=async()=>{d();const a=n;n=[];const l=i;s=void 0,i=void 0;try{a.length>0&&await e.export(a)}catch{}finally{l?.()}},f=a=>{s===void 0&&(s=new Promise(l=>{i=l}),o=setTimeout(()=>{m()},r)),a?.(s)};return{add:(a,l)=>{for(n.push(a);n.length>t;)n.shift();f(l),n.length>=t&&m()},flush:async a=>{if(n.length===0){d();return}const l=m();return a?.(l),l},get size(){return n.length}}},k=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},J=(e,t)=>{const r={},n=e("HOSTNAME")??e("COMPUTERNAME");n!==void 0&&(r["host.name"]=n);const o=e("KUBERNETES_POD_NAME")??e("
|
|
1
|
+
import{a as $}from"./packem_shared/abort-deadline-jwbW9Ala.mjs";const j={debug:5,error:17,fatal:21,info:9,log:9,trace:1,warn:13},N=e=>`${String(Math.round(e))}000000`,F=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,"0")),y=512,I=new Uint8Array(y);let T=y;const x=(e,t,r)=>{let n="";for(let o=0;o<r;o+=1)n+=F[e[t+o]];return n},L=e=>{if(e>y){const r=new Uint8Array(e);return crypto.getRandomValues(r),x(r,0,e)}T+e>y&&(crypto.getRandomValues(I),T=0);const t=x(I,T,e);return T+=e,t},S=/^[0-9a-f]+$/,Y=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||!S.test(r)||r==="ff"||r==="00"&&t.length!==4||n===void 0||o===void 0||s===void 0||s.length!==2||!S.test(s)||n.length!==32||o.length!==16||!S.test(n)||!S.test(o)||n==="00000000000000000000000000000000"||o==="0000000000000000"))return{parentSpanId:o,sampled:(Number.parseInt(s,16)&1)===1,traceId:n}},h=(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}},V=e=>e===void 0?[]:Object.entries(e).map(([t,r])=>h(t,r)),X=(e,t,r)=>{const n={},o=new Map,s=(i,d)=>{const m=i.toLowerCase(),f=o.get(m);f===void 0?(o.set(m,i),n[i]=d):n[f]=d};for(const[i,d]of Object.entries(e))s(i,d);for(const[i,d]of Object.entries(t??{}))s(i,d);return r!==void 0&&r.length>0&&s("authorization",`Bearer ${r}`),n},P=e=>Array.isArray(e)?e:[e],D=(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])=>h(n,o))},K=(e,t,r,n)=>({resourceSpans:[{resource:{attributes:D(r,n)},scopeSpans:[{scope:{name:t},spans:P(e)}]}]}),z=(e,t,r,n)=>({resourceLogs:[{resource:{attributes:D(r,n)},scopeLogs:[{logRecords:P(e),scope:{name:t}}]}]}),G=512,W=200,U=e=>{const t=e.maxItems??G,r=e.maxDelayMs??W;let n=[],o,s,i;const d=()=>{o!==void 0&&(clearTimeout(o),o=void 0)},m=async()=>{d();const a=n;n=[];const l=i;s=void 0,i=void 0;try{a.length>0&&await e.export(a)}catch{}finally{l?.()}},f=a=>{s===void 0&&(s=new Promise(l=>{i=l}),o=setTimeout(()=>{m()},r)),a?.(s)};return{add:(a,l)=>{for(n.push(a);n.length>t;)n.shift();f(l),n.length>=t&&m()},flush:async a=>{if(n.length===0){d();return}const l=m();return a?.(l),l},get size(){return n.length}}},k=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},J=(e,t)=>{const r={},n=e("HOSTNAME")??e("COMPUTERNAME");n!==void 0&&(r["host.name"]=n);const o=e("KUBERNETES_POD_NAME")??(e("KUBERNETES_SERVICE_HOST")===void 0?void 0:e("HOSTNAME"));return o!==void 0&&(r["k8s.pod.name"]=o),t!==void 0&&Number.isFinite(t)&&(r["process.pid"]=t),r},C=(...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},q=1e4,g=e=>process.env[e],Q=()=>{const e=typeof process.pid!="number"?void 0:process.pid;return C(k(g),J(g,e))},Z=e=>e??(typeof globalThis.fetch=="function"?globalThis.fetch:void 0),ee=(e,t)=>{const r=V(e.attributes);e.error?.type!==void 0&&r.push(h("error.type",e.error.type));const n={attributes:r,endTimeUnixNano:N(e.endMs),kind:1,name:e.name,...t===void 0?{}:{parentSpanId:t.parentSpanId},spanId:L(8),startTimeUnixNano:N(e.startMs),status:e.error===void 0?{code:1}:{code:2,message:e.error.message},traceId:t?.traceId??L(16)};return e.error&&(n.events=[{attributes:[h("exception.type",e.error.type??"Error"),h("exception.message",e.error.message)],name:"exception",timeUnixNano:N(e.endMs)}]),n},te=(e,t)=>{const r=e.level??"info";return{attributes:V(e.attributes),body:{stringValue:e.message},severityNumber:j[r],severityText:r.toUpperCase(),timeUnixNano:N(e.ts??t)}},ie=(e={})=>{const t=e.endpoint??g("LUNORA_OTLP_ENDPOINT"),r=t!==void 0&&t.length>0,n=e.token??g("LUNORA_OTLP_TOKEN"),o=e.serviceName??g("LUNORA_SERVICE_NAME")??"lunora-container",s=Y(e.traceparent??g("LUNORA_TRACEPARENT")),i=e.timeoutMs??q,d=Z(e.fetch),m=X({"content-type":"application/json"},e.headers,n),f=e.detectResources===!0?Q():void 0,a={};e.serviceVersion!==void 0&&(a["service.version"]=e.serviceVersion),e.deploymentEnvironment!==void 0&&(a["deployment.environment"]=e.deploymentEnvironment),e.resourceAttributes!==void 0&&Object.assign(a,e.resourceAttributes);const l=C(f,a);let p=t??"";for(;p.endsWith("/");)p=p.slice(0,-1);const H=`${p}/v1/traces`,B=`${p}/v1/logs`,b=new Set,_=(c,A)=>{if(d===void 0)return e.onError?.(new TypeError("createContainerTelemetry: no `fetch` available — pass `fetch` in options for this runtime.")),Promise.resolve();const v=(async()=>{const u=$(void 0,i,()=>new DOMException(`OTLP export to ${c} timed out after ${String(i)}ms`,"TimeoutError"));try{const E=await d(c,{body:JSON.stringify(A),headers:m,method:"POST",signal:u.signal});E.ok||e.onError?.(new Error(`createContainerTelemetry: OTLP export to ${c} failed with status ${String(E.status)}.`));try{await E.body?.cancel()}catch{}}catch(E){e.onError?.(E)}finally{u.dispose()}})().finally(()=>{b.delete(v)});return b.add(v),v},R=U({export:c=>_(H,K(c,"@lunora/container",o,l))}),w=U({export:c=>_(B,z(c,"@lunora/container",o,l))}),O=c=>{r&&R.add(ee(c,s))};return{emitLog:c=>{r&&w.add(te(c,Date.now()))},emitSpan:O,enabled:r,flush:async()=>{await Promise.allSettled([R.flush(),w.flush()]),await Promise.allSettled(b)},trace:async(c,A,M)=>{const v=Date.now();try{const u=await A();return O({attributes:M,endMs:Date.now(),name:c,startMs:v}),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:v}),u}}}};export{ie as createContainerTelemetry};
|
package/dist/packem_shared/{containerBindingName-D01FopQt.mjs → containerBindingName-C7SonAIK.mjs}
RENAMED
|
@@ -1 +1 @@
|
|
|
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`,
|
|
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`,S=e=>`CONTAINER_${e.replaceAll(/(?<=[a-z0-9])(?=[A-Z])/g,"_").toUpperCase()}`,N=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)},P=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)})`);const n=e.rollout?.gracePeriodSeconds;if(n!==void 0&&(!Number.isInteger(n)||n<0))throw new TypeError(`defineContainer: \`rollout.gracePeriodSeconds\` must be a non-negative integer (got ${String(n)})`);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}},I=e=>typeof e=="object"&&e!==null&&e.isLunoraContainer===!0,A=(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{S as containerBindingName,N as containerBuildTag,k as containerClassName,P as defineContainer,I as isContainerDefinition,$ as normalizeContainerImage,g as parseDurationSeconds,A as resolveContainerEnvVars};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{LunoraError as R}from"@lunora/errors";import{containerBindingName as k}from"./containerBindingName-
|
|
1
|
+
import{LunoraError as R}from"@lunora/errors";import{containerBindingName as k}from"./containerBindingName-C7SonAIK.mjs";import{e as F,r as U}from"./exec-KlR4eMip.mjs";import{a as I}from"./jurisdiction-DtE9s70w.mjs";const N=3,D=3e4,q=3,W=500,j="cf-container-target-port",v="__lunora",p=t=>t.split("/").find(e=>e!=="")??"",C=(t,e)=>{const o=typeof t=="string"?t:t.url,n=URL.parse(o.startsWith("/")?`http://container${o}`:o);if(n===null)return;const{pathname:r}=n;let s=r;try{s=decodeURIComponent(r)}catch{}if(p(r)===v||p(s)===v)throw new R("BAD_REQUEST",`${e}: \`/${v}/*\` is reserved for Lunora's own container routes and cannot be reached with \`fetch\`. Use \`exec\` to run a command.`)},P=(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(j,String(o)),n!==void 0&&r.headers.set("traceparent",n),r},L=async t=>{t<=0||await new Promise(e=>{setTimeout(e,t)})},Y=/no container instance|not listening|try again later|rate.?limit|provision/i,z="no Container instance available",G="Failed to start container:",K=1024,V=async t=>(await U(t.clone().body,K)).text,O=t=>t instanceof Error&&Y.test(t.message),H=async t=>{if(t.status===429)return!0;if(t.status!==500&&t.status!==503)return!1;try{const e=await V(t);return e.includes(z)||e.startsWith(G)}catch{return!1}},w=t=>`ctx.containers.${t.exportName}`,S=(t,e,o={},n,r)=>{const s=Math.max(1,o.attempts??q),a=o.backoffMs??W,A=o.maxBackoffMs??D,g=async(i,f)=>{const T=typeof i=="string"?s:1;let l;for(let d=0;d<T;d+=1){const u=d===T-1;d>0&&await L(Math.min(a*2**(d-1),A));try{const m=await t(P(i,f,n,r));if(u||!await H(m))return m}catch(m){if(l=m,u||!O(m))throw m}}throw l instanceof Error?l:new Error("ctx.containers: cold-start retry exhausted")};return{exec:F(g,e),fetch:async(i,f)=>(C(i,e),g(i,f)),port:i=>S(t,e,o,i,r)}},x=(t,e,o,n,r)=>S(async s=>t.get(t.idFromName(e)).fetch(s),o,n,void 0,r),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)},J=(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])}),b=(t,e,o,n,r)=>{const s=()=>t.get(t.idFromName(o));return{...S(async a=>s().fetch(a),w(e),n,void 0,r),destroy:async()=>c(s(),"destroy",e.binding),egress:J(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)}},B=t=>`pool-${String(Math.floor(Math.random()*t))}`,Q=t=>t.status>=500,M=(t,e,o={},n,r)=>{const s=o.size??e.maxInstances??N,a=Math.max(1,o.attempts??3),A=o.backoffMs??100,g=o.maxBackoffMs??D,i=`${w(e)}.pool()`,f=(l,d)=>async(u,m)=>{const E=typeof u=="string"?a:1;let _;for(let h=0;h<E;h+=1){h>0&&await L(Math.min(A*2**(h-1),g));const $=P(u,m,n,r);try{const y=await t.get(t.idFromName(B(s))).fetch($);if(h===E-1||!await l(y))return y}catch(y){if(_=y,!d(y))throw y}}throw _ instanceof Error?_:new Error(`ctx.containers.${e.exportName}.pool(): all ${String(E)} attempts failed`)},T=f(o.retryOn??Q,()=>!0);return{exec:F(f(H,O),i),fetch:async(l,d)=>(C(l,i),T(l,d)),port:l=>M(t,e,o,l,r)}},X=(t,e,o)=>({any:(n,r)=>x(t,B(n??e.maxInstances??N),w(e),r,o),get:(n,r)=>b(t,e,n,r,o),pool:n=>M(t,e,n,void 0,o)}),Z=t=>{const e=()=>{throw new R("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}},st=(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"?X(I(a,o),s,n):Z(s)}return r},tt=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}},at=t=>{const e={};for(const[o,n]of Object.entries(t)){const r=tt(n),s={binding:k(o),exportName:o};e[o]={any:()=>x(r,"pool-0",w(s),{attempts:1}),get:a=>b(r,s,a,{attempts:1}),pool:()=>x(r,"pool-0",`${w(s)}.pool()`,{attempts:1})}}return e};export{st as createContainerContext,at as createContainerTestContext};
|
package/package.json
CHANGED