@lunora/container 1.0.0-alpha.52 → 1.0.0-alpha.54
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/index.d.mts +12 -5
- package/dist/index.d.ts +12 -5
- package/dist/index.mjs +1 -1
- package/dist/otel.d.mts +52 -9
- package/dist/otel.d.ts +52 -9
- package/dist/otel.mjs +1 -1
- package/dist/packem_shared/createContainerContext-DRyINB-P.mjs +1 -0
- package/dist/packem_shared/sampling-D21rpu_s.mjs +1 -0
- package/package.json +1 -1
- package/dist/packem_shared/createContainerContext-DApPtouZ.mjs +0 -1
package/dist/index.d.mts
CHANGED
|
@@ -314,11 +314,18 @@ interface ContainerBindingSpec {
|
|
|
314
314
|
* A missing binding doesn't throw here — only when the handle is actually used —
|
|
315
315
|
* so one unprovisioned container never breaks unrelated functions.
|
|
316
316
|
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
|
|
321
|
-
|
|
317
|
+
* The last two arguments carry the dispatch's telemetry verdict onto every
|
|
318
|
+
* outbound container `fetch`, both read off the inbound request by the DO:
|
|
319
|
+
*
|
|
320
|
+
* - `traceparent` — the W3C trace context, so the container's own spans stitch
|
|
321
|
+
* under the Worker's trace rather than starting a disconnected one.
|
|
322
|
+
* - `sampleErrors` — the tail-bias toggle, so the container exports what THIS
|
|
323
|
+
* dispatch decided instead of falling back to its own environment and agreeing
|
|
324
|
+
* with the other tiers only by coincidence. `undefined` (an alarm, a
|
|
325
|
+
* subscription re-run, a non-Lunora caller propagated no verdict) omits the
|
|
326
|
+
* header and leaves the container on its own configuration.
|
|
327
|
+
*/
|
|
328
|
+
declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>, jurisdiction?: DurableObjectJurisdiction, traceparent?: string, sampleErrors?: boolean) => Record<string, ContainerAccessor>;
|
|
322
329
|
/**
|
|
323
330
|
* A test handler: receives the request plus the targeted instance name.
|
|
324
331
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -314,11 +314,18 @@ interface ContainerBindingSpec {
|
|
|
314
314
|
* A missing binding doesn't throw here — only when the handle is actually used —
|
|
315
315
|
* so one unprovisioned container never breaks unrelated functions.
|
|
316
316
|
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
|
|
321
|
-
|
|
317
|
+
* The last two arguments carry the dispatch's telemetry verdict onto every
|
|
318
|
+
* outbound container `fetch`, both read off the inbound request by the DO:
|
|
319
|
+
*
|
|
320
|
+
* - `traceparent` — the W3C trace context, so the container's own spans stitch
|
|
321
|
+
* under the Worker's trace rather than starting a disconnected one.
|
|
322
|
+
* - `sampleErrors` — the tail-bias toggle, so the container exports what THIS
|
|
323
|
+
* dispatch decided instead of falling back to its own environment and agreeing
|
|
324
|
+
* with the other tiers only by coincidence. `undefined` (an alarm, a
|
|
325
|
+
* subscription re-run, a non-Lunora caller propagated no verdict) omits the
|
|
326
|
+
* header and leaves the container on its own configuration.
|
|
327
|
+
*/
|
|
328
|
+
declare const createContainerContext: (env: Record<string, unknown>, specs: ReadonlyArray<ContainerBindingSpec>, jurisdiction?: DurableObjectJurisdiction, traceparent?: string, sampleErrors?: boolean) => Record<string, ContainerAccessor>;
|
|
322
329
|
/**
|
|
323
330
|
* A test handler: receives the request plus the targeted instance name.
|
|
324
331
|
*/
|
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-DRyINB-P.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.d.mts
CHANGED
|
@@ -56,6 +56,24 @@ interface ContainerLogInput {
|
|
|
56
56
|
* Options for {@link createContainerTelemetry}.
|
|
57
57
|
*/
|
|
58
58
|
interface ContainerTelemetryOptions {
|
|
59
|
+
/**
|
|
60
|
+
* Keep a span that ERRORED even when the inbound `traceparent` says the trace
|
|
61
|
+
* was sampled out — the **tail bias**, the same rule `@lunora/runtime`'s
|
|
62
|
+
* `sampling.alwaysSampleErrors` and the shard apply.
|
|
63
|
+
*
|
|
64
|
+
* Normally you do not set this: the dispatch's verdict arrives on the
|
|
65
|
+
* inbound request (see {@link ContainerTelemetryOptions.request}), so all
|
|
66
|
+
* three tiers agree because they were told the same thing. Set it to
|
|
67
|
+
* override that. With neither, the `LUNORA_SAMPLE_ERRORS` env var applies
|
|
68
|
+
* (`"0"` turns it off) — which is what a one-shot container with no request
|
|
69
|
+
* to read has — and failing that, the tier default `true`.
|
|
70
|
+
*
|
|
71
|
+
* Per SPAN, not per trace: the container is a long-running process with no
|
|
72
|
+
* dispatch boundary to re-decide at, so a span that already settled `ok`
|
|
73
|
+
* before a sibling failed is not retro-exported. That is the same shape the
|
|
74
|
+
* worker applies to its own dispatch events.
|
|
75
|
+
*/
|
|
76
|
+
alwaysSampleErrors?: boolean;
|
|
59
77
|
/**
|
|
60
78
|
* Value of the `deployment.environment` resource attribute. Falls back to
|
|
61
79
|
* the `DEPLOYMENT_ENVIRONMENT` / `ENVIRONMENT` / `NODE_ENV` env vars **only
|
|
@@ -78,6 +96,26 @@ interface ContainerTelemetryOptions {
|
|
|
78
96
|
headers?: Record<string, string>;
|
|
79
97
|
/** Called with any send failure so the caller can surface it; the export itself always swallows. */
|
|
80
98
|
onError?: (error: unknown) => void;
|
|
99
|
+
/**
|
|
100
|
+
* The inbound container request, read for the telemetry context the caller's
|
|
101
|
+
* Worker dispatch propagated onto it: the W3C `traceparent` and the
|
|
102
|
+
* tail-bias toggle.
|
|
103
|
+
*
|
|
104
|
+
* The one-call form of the per-request pattern. A container that serves many
|
|
105
|
+
* requests must build a telemetry instance per request anyway (the trace
|
|
106
|
+
* context differs each call), and passing the request means the handler does
|
|
107
|
+
* not have to know which headers carry what — which is the only way the
|
|
108
|
+
* toggle's header name would otherwise reach user code.
|
|
109
|
+
*
|
|
110
|
+
* ```ts
|
|
111
|
+
* const telemetry = createContainerTelemetry({ request });
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
request?: {
|
|
115
|
+
headers: {
|
|
116
|
+
get: (name: string) => null | string;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
81
119
|
/** Additional resource attributes merged onto every signal. */
|
|
82
120
|
resourceAttributes?: Record<string, ContainerAttributeValue>;
|
|
83
121
|
/** `service.name` resource attribute; defaults to the `LUNORA_SERVICE_NAME` env var then `"lunora-container"`. */
|
|
@@ -105,23 +143,28 @@ interface ContainerTelemetryOptions {
|
|
|
105
143
|
* OBEYS rather than re-derives: a `traceparent` whose flags say the trace was
|
|
106
144
|
* sampled out (`…-00`) suppresses span export, because the worker and shard
|
|
107
145
|
* spans of that trace were dropped and shipping ours would leave the collector
|
|
108
|
-
* holding the middle of a trace.
|
|
146
|
+
* holding the middle of a trace. An ERRORED span is the exception — see
|
|
147
|
+
* {@link ContainerTelemetryOptions.alwaysSampleErrors}. Logs are never sampled
|
|
148
|
+
* and are unaffected.
|
|
109
149
|
*
|
|
110
|
-
* `@lunora/container` stamps this trace context
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
150
|
+
* `@lunora/container` stamps this trace context on every proxied fetch
|
|
151
|
+
* (`ctx.containers.<name>.…`), so a container that serves many requests
|
|
152
|
+
* should create a telemetry instance per request — the trace context differs
|
|
153
|
+
* each call, so a single process-lifetime instance can't carry it. Prefer
|
|
154
|
+
* {@link ContainerTelemetryOptions.request}, which takes this AND the
|
|
155
|
+
* tail-bias toggle off the same request:
|
|
115
156
|
*
|
|
116
157
|
* ```ts
|
|
117
158
|
* // inside the container's request handler
|
|
118
|
-
* const telemetry = createContainerTelemetry({
|
|
159
|
+
* const telemetry = createContainerTelemetry({ request });
|
|
119
160
|
* await telemetry.trace("transcode", () => transcode(job));
|
|
120
161
|
* await telemetry.flush();
|
|
121
162
|
* ```
|
|
122
163
|
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
164
|
+
* Pass this option explicitly only to override what the request carries. The
|
|
165
|
+
* `LUNORA_TRACEPARENT` env fallback fits a one-shot container that processes
|
|
166
|
+
* a single job per start, where there is no request to read (the value is
|
|
167
|
+
* fixed for the process).
|
|
125
168
|
*/
|
|
126
169
|
traceparent?: string;
|
|
127
170
|
}
|
package/dist/otel.d.ts
CHANGED
|
@@ -56,6 +56,24 @@ interface ContainerLogInput {
|
|
|
56
56
|
* Options for {@link createContainerTelemetry}.
|
|
57
57
|
*/
|
|
58
58
|
interface ContainerTelemetryOptions {
|
|
59
|
+
/**
|
|
60
|
+
* Keep a span that ERRORED even when the inbound `traceparent` says the trace
|
|
61
|
+
* was sampled out — the **tail bias**, the same rule `@lunora/runtime`'s
|
|
62
|
+
* `sampling.alwaysSampleErrors` and the shard apply.
|
|
63
|
+
*
|
|
64
|
+
* Normally you do not set this: the dispatch's verdict arrives on the
|
|
65
|
+
* inbound request (see {@link ContainerTelemetryOptions.request}), so all
|
|
66
|
+
* three tiers agree because they were told the same thing. Set it to
|
|
67
|
+
* override that. With neither, the `LUNORA_SAMPLE_ERRORS` env var applies
|
|
68
|
+
* (`"0"` turns it off) — which is what a one-shot container with no request
|
|
69
|
+
* to read has — and failing that, the tier default `true`.
|
|
70
|
+
*
|
|
71
|
+
* Per SPAN, not per trace: the container is a long-running process with no
|
|
72
|
+
* dispatch boundary to re-decide at, so a span that already settled `ok`
|
|
73
|
+
* before a sibling failed is not retro-exported. That is the same shape the
|
|
74
|
+
* worker applies to its own dispatch events.
|
|
75
|
+
*/
|
|
76
|
+
alwaysSampleErrors?: boolean;
|
|
59
77
|
/**
|
|
60
78
|
* Value of the `deployment.environment` resource attribute. Falls back to
|
|
61
79
|
* the `DEPLOYMENT_ENVIRONMENT` / `ENVIRONMENT` / `NODE_ENV` env vars **only
|
|
@@ -78,6 +96,26 @@ interface ContainerTelemetryOptions {
|
|
|
78
96
|
headers?: Record<string, string>;
|
|
79
97
|
/** Called with any send failure so the caller can surface it; the export itself always swallows. */
|
|
80
98
|
onError?: (error: unknown) => void;
|
|
99
|
+
/**
|
|
100
|
+
* The inbound container request, read for the telemetry context the caller's
|
|
101
|
+
* Worker dispatch propagated onto it: the W3C `traceparent` and the
|
|
102
|
+
* tail-bias toggle.
|
|
103
|
+
*
|
|
104
|
+
* The one-call form of the per-request pattern. A container that serves many
|
|
105
|
+
* requests must build a telemetry instance per request anyway (the trace
|
|
106
|
+
* context differs each call), and passing the request means the handler does
|
|
107
|
+
* not have to know which headers carry what — which is the only way the
|
|
108
|
+
* toggle's header name would otherwise reach user code.
|
|
109
|
+
*
|
|
110
|
+
* ```ts
|
|
111
|
+
* const telemetry = createContainerTelemetry({ request });
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
request?: {
|
|
115
|
+
headers: {
|
|
116
|
+
get: (name: string) => null | string;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
81
119
|
/** Additional resource attributes merged onto every signal. */
|
|
82
120
|
resourceAttributes?: Record<string, ContainerAttributeValue>;
|
|
83
121
|
/** `service.name` resource attribute; defaults to the `LUNORA_SERVICE_NAME` env var then `"lunora-container"`. */
|
|
@@ -105,23 +143,28 @@ interface ContainerTelemetryOptions {
|
|
|
105
143
|
* OBEYS rather than re-derives: a `traceparent` whose flags say the trace was
|
|
106
144
|
* sampled out (`…-00`) suppresses span export, because the worker and shard
|
|
107
145
|
* spans of that trace were dropped and shipping ours would leave the collector
|
|
108
|
-
* holding the middle of a trace.
|
|
146
|
+
* holding the middle of a trace. An ERRORED span is the exception — see
|
|
147
|
+
* {@link ContainerTelemetryOptions.alwaysSampleErrors}. Logs are never sampled
|
|
148
|
+
* and are unaffected.
|
|
109
149
|
*
|
|
110
|
-
* `@lunora/container` stamps this trace context
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
150
|
+
* `@lunora/container` stamps this trace context on every proxied fetch
|
|
151
|
+
* (`ctx.containers.<name>.…`), so a container that serves many requests
|
|
152
|
+
* should create a telemetry instance per request — the trace context differs
|
|
153
|
+
* each call, so a single process-lifetime instance can't carry it. Prefer
|
|
154
|
+
* {@link ContainerTelemetryOptions.request}, which takes this AND the
|
|
155
|
+
* tail-bias toggle off the same request:
|
|
115
156
|
*
|
|
116
157
|
* ```ts
|
|
117
158
|
* // inside the container's request handler
|
|
118
|
-
* const telemetry = createContainerTelemetry({
|
|
159
|
+
* const telemetry = createContainerTelemetry({ request });
|
|
119
160
|
* await telemetry.trace("transcode", () => transcode(job));
|
|
120
161
|
* await telemetry.flush();
|
|
121
162
|
* ```
|
|
122
163
|
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
164
|
+
* Pass this option explicitly only to override what the request carries. The
|
|
165
|
+
* `LUNORA_TRACEPARENT` env fallback fits a one-shot container that processes
|
|
166
|
+
* a single job per start, where there is no request to read (the value is
|
|
167
|
+
* fixed for the process).
|
|
125
168
|
*/
|
|
126
169
|
traceparent?: string;
|
|
127
170
|
}
|
package/dist/otel.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as
|
|
1
|
+
import{a as X}from"./packem_shared/abort-deadline-jwbW9Ala.mjs";import{S as G,s as K}from"./packem_shared/sampling-D21rpu_s.mjs";const k={debug:5,error:17,fatal:21,info:9,log:9,trace:1,warn:13},N=e=>`${String(Math.round(e))}000000`,z=Array.from({length:256},(e,t)=>t.toString(16).padStart(2,"0")),y=512,x=new Uint8Array(y);let T=y;const U=(e,t,r)=>{let n="";for(let o=0;o<r;o+=1)n+=z[e[t+o]];return n},P=e=>{if(e>y){const r=new Uint8Array(e);return crypto.getRandomValues(r),U(r,0,e)}T+e>y&&(crypto.getRandomValues(x),T=0);const t=U(x,T,e);return T+=e,t},A=/^[0-9a-f]+$/,W=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||!A.test(r)||r==="ff"||r==="00"&&t.length!==4||n===void 0||o===void 0||s===void 0||s.length!==2||!A.test(s)||n.length!==32||o.length!==16||!A.test(n)||!A.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}},D=e=>e===void 0?[]:Object.entries(e).map(([t,r])=>h(t,r)),q=(e,t,r)=>{const n={},o=new Map,s=(i,a)=>{const m=i.toLowerCase(),f=o.get(m);f===void 0?(o.set(m,i),n[i]=a):n[f]=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},C=e=>Array.isArray(e)?e:[e],H=(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))},J=(e,t,r,n)=>({resourceSpans:[{resource:{attributes:H(r,n)},scopeSpans:[{scope:{name:t},spans:C(e)}]}]}),Q=(e,t,r,n)=>({resourceLogs:[{resource:{attributes:H(r,n)},scopeLogs:[{logRecords:C(e),scope:{name:t}}]}]}),Z=512,ee=200,V=e=>{const t=e.maxItems??Z,r=e.maxDelayMs??ee;let n=[],o,s,i;const a=()=>{o!==void 0&&(clearTimeout(o),o=void 0)},m=async()=>{a();const d=n;n=[];const u=i;s=void 0,i=void 0;try{d.length>0&&await e.export(d)}catch{}finally{u?.()}},f=d=>{s===void 0&&(s=new Promise(u=>{i=u}),o=setTimeout(()=>{m()},r)),d?.(s)};return{add:(d,u)=>{for(n.push(d);n.length>t;)n.shift();f(u),n.length>=t&&m()},flush:async d=>{if(n.length===0){a();return}const u=m();return d?.(u),u},get size(){return n.length}}},te=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},re=(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},B=(...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},ne=e=>e===void 0||e!=="0",oe=1e4,$=1,E=e=>process.env[e],se=()=>{const e=typeof process.pid!="number"?void 0:process.pid;return B(te(E),re(E,e))},ie=e=>e??(typeof globalThis.fetch=="function"?globalThis.fetch:void 0),ce=(e,t)=>{const r=D(e.attributes);e.error?.type!==void 0&&r.push(h("error.type",e.error.type));const n={attributes:r,endTimeUnixNano:N(e.endMs),flags:t?.sampled??!0?$:0,kind:1,name:e.name,...t===void 0?{}:{parentSpanId:t.parentSpanId},spanId:P(8),startTimeUnixNano:N(e.startMs),status:e.error===void 0?{code:1}:{code:2,message:e.error.message},traceId:t?.traceId??P(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},ae=(e,t,r)=>{const n=e.level??"info";return{attributes:D(e.attributes),body:{stringValue:e.message},severityNumber:k[n],severityText:n.toUpperCase(),timeUnixNano:N(e.ts??t),...r===void 0?{}:{flags:r.sampled?$:0,spanId:r.parentSpanId,traceId:r.traceId}}},Ee=(e={})=>{const t=e.endpoint??E("LUNORA_OTLP_ENDPOINT"),r=t!==void 0&&t.length>0,n=e.token??E("LUNORA_OTLP_TOKEN"),o=e.serviceName??E("LUNORA_SERVICE_NAME")??"lunora-container",s=e.request?.headers,i=W(e.traceparent??s?.get("traceparent")??E("LUNORA_TRACEPARENT")),a=i?.sampled!==!1,m=e.alwaysSampleErrors??ne(s?.get(G)??E("LUNORA_SAMPLE_ERRORS")),f=e.timeoutMs??oe,d=ie(e.fetch),u=q({"content-type":"application/json"},e.headers,n),j=e.detectResources===!0?se():void 0,S={};e.serviceVersion!==void 0&&(S["service.version"]=e.serviceVersion),e.deploymentEnvironment!==void 0&&(S["deployment.environment"]=e.deploymentEnvironment),e.resourceAttributes!==void 0&&Object.assign(S,e.resourceAttributes);const M=B(j,S);let p=t??"";for(;p.endsWith("/");)p=p.slice(0,-1);const F=`${p}/v1/traces`,Y=`${p}/v1/logs`,O=new Set,I=(c,R)=>{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 l=X(void 0,f,()=>new DOMException(`OTLP export to ${c} timed out after ${String(f)}ms`,"TimeoutError"));try{const g=await d(c,{body:JSON.stringify(R),headers:u,method:"POST",signal:l.signal});g.ok||e.onError?.(new Error(`createContainerTelemetry: OTLP export to ${c} failed with status ${String(g.status)}.`));try{await g.body?.cancel()}catch{}}catch(g){e.onError?.(g)}finally{l.dispose()}})().finally(()=>{O.delete(v)});return O.add(v),v},L=V({export:c=>I(F,J(c,"@lunora/container",o,M))}),w=V({export:c=>I(Y,Q(c,"@lunora/container",o,M))}),b=c=>{!r||!K({isTraced:a,keepErrors:m},c.error!==void 0)||L.add(ce(c,i))};return{emitLog:c=>{r&&w.add(ae(c,Date.now(),i))},emitSpan:b,enabled:r,flush:async()=>{await Promise.allSettled([L.flush(),w.flush()]),await Promise.allSettled(O)},trace:async(c,R,_)=>{const v=Date.now();try{const l=await R();return b({attributes:_,endMs:Date.now(),name:c,startMs:v}),l}catch(l){throw b({attributes:_,endMs:Date.now(),error:{message:l instanceof Error?l.message:String(l),type:l instanceof Error?l.name:void 0},name:c,startMs:v}),l}}}};export{Ee as createContainerTelemetry};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as D}from"@lunora/errors";import{e as N,r as k}from"./exec-KlR4eMip.mjs";import{S as I}from"./sampling-D21rpu_s.mjs";import{containerBindingName as U}from"./containerBindingName-C7SonAIK.mjs";import{a as q}from"./jurisdiction-DtE9s70w.mjs";const p=3,C=3e4,W=3,j=500,Y="cf-container-target-port",v="__lunora",F=t=>t.split("/").find(e=>e!=="")??"",P=(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(F(r)===v||F(s)===v)throw new D("BAD_REQUEST",`${e}: \`/${v}/*\` is reserved for Lunora's own container routes and cannot be reached with \`fetch\`. Use \`exec\` to run a command.`)},L=(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(Y,String(o)),n?.traceparent!==void 0&&r.headers.set("traceparent",n.traceparent),n?.sampleErrors!==void 0&&r.headers.set(I,n.sampleErrors?"1":"0"),r},O=async t=>{t<=0||await new Promise(e=>{setTimeout(e,t)})},z=/no container instance|not listening|try again later|rate.?limit|provision/i,G="no Container instance available",K="Failed to start container:",V=1024,J=async t=>(await k(t.clone().body,V)).text,H=t=>t instanceof Error&&z.test(t.message),b=async t=>{if(t.status===429)return!0;if(t.status!==500&&t.status!==503)return!1;try{const e=await J(t);return e.includes(G)||e.startsWith(K)}catch{return!1}},T=t=>`ctx.containers.${t.exportName}`,S=(t,e,o={},n,r)=>{const s=Math.max(1,o.attempts??W),a=o.backoffMs??j,c=o.maxBackoffMs??C,g=async(l,y)=>{const E=typeof l=="string"?s:1;let d;for(let m=0;m<E;m+=1){const u=m===E-1;m>0&&await O(Math.min(a*2**(m-1),c));try{const f=await t(L(l,y,n,r));if(u||!await b(f))return f}catch(f){if(d=f,u||!H(f))throw f}}throw d instanceof Error?d:new Error("ctx.containers: cold-start retry exhausted")};return{exec:N(g,e),fetch:async(l,y)=>(P(l,e),g(l,y)),port:l=>S(t,e,o,l,r)}},M=(t,e,o,n,r)=>S(async s=>t.get(t.idFromName(e)).fetch(s),o,n,void 0,r),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])}),B=(t,e,o,n,r)=>{const s=()=>t.get(t.idFromName(o));return{...S(async a=>s().fetch(a),T(e),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)}},R=t=>`pool-${String(Math.floor(Math.random()*t))}`,X=t=>t.status>=500,x=(t,e,o={},n,r)=>{const s=o.size??e.maxInstances??p,a=Math.max(1,o.attempts??3),c=o.backoffMs??100,g=o.maxBackoffMs??C,l=`${T(e)}.pool()`,y=(d,m)=>async(u,f)=>{const A=typeof u=="string"?a:1;let _;for(let w=0;w<A;w+=1){w>0&&await O(Math.min(c*2**(w-1),g));const $=L(u,f,n,r);try{const h=await t.get(t.idFromName(R(s))).fetch($);if(w===A-1||!await d(h))return h}catch(h){if(_=h,!m(h))throw h}}throw _ instanceof Error?_:new Error(`ctx.containers.${e.exportName}.pool(): all ${String(A)} attempts failed`)},E=y(o.retryOn??X,()=>!0);return{exec:N(y(b,H),l),fetch:async(d,m)=>(P(d,l),E(d,m)),port:d=>x(t,e,o,d,r)}},Z=(t,e,o)=>({any:(n,r)=>M(t,R(n??e.maxInstances??p),T(e),r,o),get:(n,r)=>B(t,e,n,r,o),pool:n=>x(t,e,n,void 0,o)}),tt=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}},ct=(t,e,o,n,r)=>{const s={};for(const a of e){const c=t[a.binding];s[a.exportName]=c&&typeof c.idFromName=="function"&&typeof c.get=="function"?Z(q(c,o),a,{...r===void 0?{}:{sampleErrors:r},...n===void 0?{}:{traceparent:n}}):tt(a)}return s},et=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}},it=t=>{const e={};for(const[o,n]of Object.entries(t)){const r=et(n),s={binding:U(o),exportName:o};e[o]={any:(a,c)=>M(r,R(a??s.maxInstances??p),T(s),{attempts:1,...c}),get:(a,c)=>B(r,s,a,{attempts:1,...c}),pool:a=>x(r,s,{...a,attempts:1})}}return e};export{ct as createContainerContext,it as createContainerTestContext};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e="x-lunora-sample-errors",o=(r,s)=>r.isTraced||r.keepErrors&&s;export{e as S,o as s};
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
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};
|