@lunora/container 1.0.0-alpha.63 → 1.0.0-alpha.65
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/README.md +17 -0
- package/dist/do/index.d.mts +140 -18
- package/dist/do/index.d.ts +140 -18
- package/dist/do/index.mjs +1 -1
- package/dist/index.d.mts +160 -12
- package/dist/index.d.ts +160 -12
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/CONTAINER_EXEC_PATH-7Hcd6-zs.mjs +1 -0
- package/dist/packem_shared/{ContainerProxy-fTW_pXWY.mjs → ContainerProxy-CfXjS6ZI.mjs} +7 -7
- package/dist/packem_shared/DURABLE_OBJECT_POLICY_FORBIDDEN_KEYS-B9DkqZkf.mjs +1 -0
- package/dist/packem_shared/LunoraSandboxContainer-DPnhJUu1.mjs +1 -0
- package/dist/packem_shared/createContainerContext-CcW1olml.mjs +1 -0
- package/dist/packem_shared/exec-BYLW70sR.mjs +1 -0
- package/dist/packem_shared/{jurisdiction.d-DPvNG2h5.d.mts → jurisdiction.d-t4mtuqYW.d.mts} +228 -32
- package/dist/packem_shared/{jurisdiction.d-DPvNG2h5.d.ts → jurisdiction.d-t4mtuqYW.d.ts} +228 -32
- package/dist/packem_shared/sandbox-types.d-DXlHnsEg.d.mts +110 -0
- package/dist/packem_shared/sandbox-types.d-DXlHnsEg.d.ts +110 -0
- package/dist/sandbox/index.d.mts +66 -0
- package/dist/sandbox/index.d.ts +66 -0
- package/dist/sandbox/index.mjs +1 -0
- package/package.json +7 -2
- package/dist/packem_shared/CONTAINER_EXEC_PATH-ZfG5wgai.mjs +0 -1
- package/dist/packem_shared/containerBindingName-C7SonAIK.mjs +0 -1
- package/dist/packem_shared/createContainerContext-CLvjjJLs.mjs +0 -1
- package/dist/packem_shared/exec-WbCOLswq.mjs +0 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { SandboxFileStat, SandboxDirectoryEntry, DirectoryBackupRecord, S3MountInspection } from '@cloudflare/sandbox';
|
|
2
|
+
export { DirectoryBackupGateway, S3Gateway } from '@cloudflare/sandbox';
|
|
3
|
+
import { LunoraContainer } from "../do/index.js";
|
|
4
|
+
import { C as ContainerFileOptions, a as ContainerFileContent, b as ContainerBackupOptions, c as ContainerMountRequest } from "../packem_shared/sandbox-types.d-DXlHnsEg.js";
|
|
5
|
+
import { C as ContainerDefinition } from "../packem_shared/jurisdiction.d-t4mtuqYW.js";
|
|
6
|
+
import 'cloudflare:workers';
|
|
7
|
+
type DurableObjectContext = ConstructorParameters<typeof LunoraContainer>[0];
|
|
8
|
+
declare class LunoraSandboxContainer<Env = unknown> extends LunoraContainer<Env> {
|
|
9
|
+
#private;
|
|
10
|
+
constructor(context: DurableObjectContext, env: Env, definition: ContainerDefinition, exportName?: string, jurisdiction?: ConstructorParameters<typeof LunoraContainer>[4]);
|
|
11
|
+
lunoraReadFile(path: string, options?: ContainerFileOptions): Promise<Response>;
|
|
12
|
+
lunoraWriteFile(path: string, content: ContainerFileContent, options?: ContainerFileOptions): Promise<void>;
|
|
13
|
+
lunoraStat(path: string, options?: ContainerFileOptions): Promise<SandboxFileStat>;
|
|
14
|
+
lunoraReadDirectory(path: string, options?: ContainerFileOptions): Promise<SandboxDirectoryEntry[]>;
|
|
15
|
+
lunoraMkdir(path: string, options?: ContainerFileOptions & {
|
|
16
|
+
recursive?: boolean;
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
lunoraRename(source: string, destination: string, options?: ContainerFileOptions): Promise<void>;
|
|
19
|
+
lunoraRemove(path: string, options?: ContainerFileOptions & {
|
|
20
|
+
force?: boolean;
|
|
21
|
+
recursive?: boolean;
|
|
22
|
+
}): Promise<void>;
|
|
23
|
+
lunoraBackup(directory: string, options?: ContainerBackupOptions): Promise<DirectoryBackupRecord>;
|
|
24
|
+
lunoraRestore(backup: DirectoryBackupRecord, options?: {
|
|
25
|
+
directory?: string;
|
|
26
|
+
}): Promise<void>;
|
|
27
|
+
/** Needs no running container — it only deletes the object. */
|
|
28
|
+
lunoraDeleteBackup(backup: DirectoryBackupRecord): Promise<void>;
|
|
29
|
+
lunoraMount(request: ContainerMountRequest): Promise<void>;
|
|
30
|
+
lunoraInspectMount(path: string): Promise<S3MountInspection>;
|
|
31
|
+
lunoraUnmount(path: string): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Route backup traffic to its gateway before the base installs the egress
|
|
34
|
+
* policy's catch-all, which takes every hostname registered after it.
|
|
35
|
+
* Without an egress policy there is no catch-all, and each backup or
|
|
36
|
+
* restore registers its own route when it runs.
|
|
37
|
+
*/
|
|
38
|
+
protected override beforeContainerStart(): Promise<void>;
|
|
39
|
+
/** Run one helper operation on the running container, translating sandbox errors. */
|
|
40
|
+
private withContainer;
|
|
41
|
+
private files;
|
|
42
|
+
/** One `DirectoryBackup` per instance: it runs one operation at a time per container, which only holds if every call shares it. */
|
|
43
|
+
private backups;
|
|
44
|
+
private mounts;
|
|
45
|
+
/** A Worker entrypoint from `ctx.exports`, with the fix when the worker does not export it. */
|
|
46
|
+
private workerExport;
|
|
47
|
+
/** Resolve a mount's credentials from the Worker secrets they name. */
|
|
48
|
+
private mountCredentials;
|
|
49
|
+
private label;
|
|
50
|
+
}
|
|
51
|
+
export {
|
|
52
|
+
/**
|
|
53
|
+
* `@lunora/container/sandbox` — the workerd-only half of the Sandbox SDK
|
|
54
|
+
* helpers (`@cloudflare/sandbox`) a `defineContainer({ sandbox: true })`
|
|
55
|
+
* container uses. A separate subpath from `/do`, so an app that never opts in
|
|
56
|
+
* never loads `@cloudflare/sandbox` (or the `zod` it depends on).
|
|
57
|
+
*
|
|
58
|
+
* `LunoraSandboxContainer` is the base class codegen extends for a `sandbox:
|
|
59
|
+
* true` container: `LunoraContainer` plus the file, backup and bucket-mount
|
|
60
|
+
* RPCs the named-instance handle calls. `DirectoryBackupGateway` and
|
|
61
|
+
* `S3Gateway` are the `WorkerEntrypoint`s the backup and mount helpers route
|
|
62
|
+
* the container's storage traffic through (reached as `ctx.exports.<Name>`),
|
|
63
|
+
* so the deployed worker must export both; the generated
|
|
64
|
+
* `_generated/containers.ts` re-exports them from here.
|
|
65
|
+
*/
|
|
66
|
+
LunoraSandboxContainer };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{default as r}from"../packem_shared/LunoraSandboxContainer-DPnhJUu1.mjs";import{DirectoryBackupGateway as t,S3Gateway as n}from"@cloudflare/sandbox";export{t as DirectoryBackupGateway,r as LunoraSandboxContainer,n as S3Gateway};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/container",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.65",
|
|
4
4
|
"description": "Cloudflare Containers for Lunora: defineContainer, generated Container DO classes, and the ctx.containers action surface",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -50,13 +50,18 @@
|
|
|
50
50
|
"types": "./dist/otel.d.ts",
|
|
51
51
|
"import": "./dist/otel.mjs"
|
|
52
52
|
},
|
|
53
|
+
"./sandbox": {
|
|
54
|
+
"types": "./dist/sandbox/index.d.ts",
|
|
55
|
+
"import": "./dist/sandbox/index.mjs"
|
|
56
|
+
},
|
|
53
57
|
"./package.json": "./package.json"
|
|
54
58
|
},
|
|
55
59
|
"publishConfig": {
|
|
56
60
|
"access": "public"
|
|
57
61
|
},
|
|
58
62
|
"dependencies": {
|
|
59
|
-
"@
|
|
63
|
+
"@cloudflare/sandbox": "1.0.0",
|
|
64
|
+
"@lunora/errors": "1.0.0-alpha.48"
|
|
60
65
|
},
|
|
61
66
|
"engines": {
|
|
62
67
|
"node": "^22.15.0 || >=24.11.0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import"./abort-deadline-jwbW9Ala.mjs";import{a as s,C as t,e as A,p as C}from"./exec-WbCOLswq.mjs";export{s as CONTAINER_EXEC_HEADER,t as CONTAINER_EXEC_PATH,A as execViaFetch,C as pathMatchesAnyDecoding};
|
|
@@ -1 +0,0 @@
|
|
|
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 +0,0 @@
|
|
|
1
|
-
import{LunoraError as R}from"@lunora/errors";import{e as C,p as j,a as g,r as X}from"./exec-WbCOLswq.mjs";import{S as Y}from"./sampling-D21rpu_s.mjs";import{containerBindingName as q}from"./containerBindingName-C7SonAIK.mjs";import{a as z}from"./jurisdiction-DtE9s70w.mjs";const S=3,F=3e4,G=3,J=500,K="cf-container-target-port",O="__lunora",Q=t=>{const e=t.indexOf(";");return e===-1?t:t.slice(0,e)},V=t=>{const e=t.split("/").find(o=>o!=="")??"";return Q(e).toLowerCase()},L=(t,e)=>{const o=typeof t=="string"?t:t.url,r=URL.parse(o.startsWith("/")?`http://container${o}`:o);if(r!==null&&j(r.pathname,n=>V(n)===O))throw new R("BAD_REQUEST",`${e}: \`/${O}/*\` is reserved for Lunora's own container routes and cannot be reached with \`fetch\`. Use \`exec\` to run a command.`)},b=(t,e)=>{if(typeof t!="string"){const r=new Request(t,e);return r.headers.delete(g),[r,void 0]}if(e?.headers===void 0)return[t,e];const o=new Headers(e.headers);return o.delete(g),[t,{...e,headers:o}]},H=(t,e,o,r)=>{const n=typeof t=="string"&&t.startsWith("/")?new Request(`http://container${t}`,e):new Request(t,e);return o!==void 0&&n.headers.set(K,String(o)),r?.traceparent!==void 0&&n.headers.set("traceparent",r.traceparent),r?.sampleErrors!==void 0&&n.headers.set(Y,r.sampleErrors?"1":"0"),n},M=async t=>{t<=0||await new Promise(e=>{setTimeout(e,t)})},Z=/no container instance|not listening|try again later|rate.?limit|provision/i,tt="no Container instance available",et="Failed to start container:",ot=1024,rt=async t=>(await X(t.clone().body,ot)).text,$=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 rt(t);return e.includes(tt)||e.startsWith(et)}catch{return!1}},E=t=>`ctx.containers.${t.exportName}`,p=(t,e,o={},r,n)=>{const s=Math.max(1,o.attempts??G),a=o.backoffMs??J,c=o.maxBackoffMs??F,A=async(l,h)=>{const T=typeof l=="string"?s:1;let d;for(let f=0;f<T;f+=1){const u=f===T-1;f>0&&await M(Math.min(a*2**(f-1),c));try{const m=await t(H(l,h,r,n),h?.signal);if(u||!await B(m))return m}catch(m){if(d=m,u||!$(m))throw m}}throw d instanceof Error?d:new Error("ctx.containers: cold-start retry exhausted")};return{exec:C(A,e),fetch:async(l,h)=>(L(l,e),A(...b(l,h))),port:l=>p(t,e,o,l,n)}},nt=async(t,e)=>{if(e.aborted)throw t.catch(()=>{}),e.reason;const o=new AbortController,r=new Promise((n,s)=>{e.addEventListener("abort",()=>{s(e.reason)},{once:!0,signal:o.signal})});try{return await Promise.race([t,r])}finally{o.abort(),t.catch(()=>{})}},N=async(t,e,o)=>{if(!e.headers.has(g))return t.fetch(e);if(typeof t.lunoraExec!="function")throw new TypeError("ctx.containers: this container DO does not expose lunoraExec() — is @lunora/container/do up to date?");e.headers.delete(g);const r=t.lunoraExec(new Request(e.url,{body:await e.text(),headers:e.headers,method:e.method}));return o?nt(r,o):r},k=(t,e,o,r,n)=>p(async(s,a)=>N(t.get(t.idFromName(e)),s,a),o,r,void 0,n),i=async(t,e,o,r)=>{const n=t[e];if(typeof n!="function")throw new TypeError(`ctx.containers: the "${o}" container DO does not expose ${e}() — is @lunora/container/do up to date?`);return n(r)},st=(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])}),I=(t,e,o,r,n)=>{const s=()=>t.get(t.idFromName(o));return{...p(async(a,c)=>N(s(),a,c),E(e),r,void 0,n),destroy:async()=>i(s(),"destroy",e.binding),egress:st(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)}},v="pool-",P=t=>`${v}${String(Math.floor(Math.random()*t))}`,U=(t,e)=>{if(t.startsWith(v))throw new R("BAD_REQUEST",`${E(e)}.get(${JSON.stringify(t)}): the "${v}" prefix is reserved for the instances .any()/.pool() pick, so this name would share a container (disk, lifecycle) with a pool member. Pick a name that does not start with "${v}".`)},at=t=>t.status>=500,D=(t,e,o={},r,n)=>{const s=o.size??e.maxInstances??S,a=Math.max(1,o.attempts??3),c=o.backoffMs??100,A=o.maxBackoffMs??F,l=`${E(e)}.pool()`,h=(d,f)=>async(u,m)=>{const _=typeof u=="string"?a:1;let x;for(let w=0;w<_;w+=1){w>0&&await M(Math.min(c*2**(w-1),A));const W=H(u,m,r,n);try{const y=await N(t.get(t.idFromName(P(s))),W,m?.signal);if(w===_-1||!await d(y))return y}catch(y){if(x=y,!f(y))throw y}}throw x instanceof Error?x:new Error(`ctx.containers.${e.exportName}.pool(): all ${String(_)} attempts failed`)},T=h(o.retryOn??at,()=>!0);return{exec:C(h(B,$),l),fetch:async(d,f)=>(L(d,l),T(...b(d,f))),port:d=>D(t,e,o,d,n)}},ct=(t,e,o)=>({any:(r,n)=>k(t,P(r??e.maxInstances??S),E(e),n,o),get:(r,n)=>(U(r,e),I(t,e,r,n,o)),pool:r=>D(t,e,r,void 0,o)}),it=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}},ut=(t,e,o,r,n)=>{const s={};for(const a of e){const c=t[a.binding];s[a.exportName]=c&&typeof c.idFromName=="function"&&typeof c.get=="function"?ct(z(c,o),a,{...n===void 0?{}:{sampleErrors:n},...r===void 0?{}:{traceparent:r}}):it(a)}return s},lt=t=>{const e=o=>({allowHost:()=>Promise.resolve(),denyHost:()=>Promise.resolve(),destroy:()=>Promise.resolve(),fetch:r=>Promise.resolve(t(r,{name:o})),lunoraExec:r=>Promise.resolve(t(r,{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}},wt=t=>{const e={};for(const[o,r]of Object.entries(t)){const n=lt(r),s={binding:q(o),exportName:o};e[o]={any:(a,c)=>k(n,P(a??s.maxInstances??S),E(s),{attempts:1,...c}),get:(a,c)=>(U(a,s),I(n,s,a,{attempts:1,...c})),pool:a=>D(n,s,{...a,attempts:1})}}return e};export{ut as createContainerContext,wt as createContainerTestContext};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as d}from"@lunora/errors";import{a as w}from"./abort-deadline-jwbW9Ala.mjs";const E=async(o,t,r)=>{if(o===null)return{overflowed:!1,text:""};const e=o.getReader(),n=new TextDecoder,a=()=>{e.cancel().catch(()=>{})};r?.addEventListener("abort",a);let s="",c=0,u=!1;try{for(;r?.aborted!==!0;){const{done:f,value:i}=await e.read();if(f)break;if(c+i.byteLength>t){u=!0,s+=n.decode(i.subarray(0,t-c),{stream:!0});break}c+=i.byteLength,s+=n.decode(i,{stream:!0})}s+=n.decode()}finally{r?.removeEventListener("abort",a),e.cancel().catch(()=>{})}if(r?.aborted===!0)throw r.reason;return{overflowed:u,text:s}},l="/__lunora/exec",h="x-lunora-container-exec",x=5,b=o=>{try{return decodeURIComponent(o)}catch{return o.replaceAll(/%[\da-f]{2}/giu,t=>{try{return decodeURIComponent(t)}catch{return t}})}},O=(o,t)=>{let r=o;for(let e=0;e<=x;e+=1){if(t(r))return!0;const n=b(r);if(n===r)return!1;r=n}return!0},m=512,v=1e6,p="<redacted>",g=(o,t)=>{if(t===void 0)return o;let r=o;for(const e of Object.values(t))e.length>0&&(r=r.split(e).join(p));return r},T=o=>{if(o===null||typeof o!="object")return;const{code:t,stderr:r,stdout:e}=o;if(typeof t=="number")return{code:t,stderr:typeof r=="string"?r:"",stdout:typeof e=="string"?e:""}},A=(o,t)=>w(o,t,()=>new d("INTERNAL",`ctx.containers: exec timed out after ${String(t)}ms`)),R=(o,t)=>async(r,e={})=>{if(typeof r!="string"||r.length===0)throw new d("BAD_REQUEST",`${t}: exec requires a non-empty \`command\``);const n=e.maxOutputBytes??v;if(!Number.isSafeInteger(n)||n<=0)throw new d("BAD_REQUEST",`${t}: exec \`maxOutputBytes\` must be a positive whole number of bytes, got ${String(n)}`);const a=A(e.signal,e.timeoutMs);try{const s=await o(l,{body:JSON.stringify({args:e.args??[],command:r,...e.cwd===void 0?{}:{cwd:e.cwd},...e.env===void 0?{}:{env:e.env},...e.timeoutMs===void 0?{}:{timeoutMs:e.timeoutMs}}),headers:{"content-type":"application/json",[h]:"1"},method:"POST",...a.signal===void 0?{}:{signal:a.signal}});if(!s.ok){const i=await E(s.body,m,a.signal).then(y=>g(y.text,e.env),()=>"<unreadable body>");throw new d("INTERNAL",`${t}: exec failed — the container answered ${String(s.status)} for POST ${l}. Does it serve that route? Body: ${i}`)}const c=await E(s.body,n,a.signal);if(c.overflowed)throw new d("INTERNAL",`${t}: exec response exceeded ${String(n)} bytes and was abandoned — it is buffered whole to be parsed, so an unbounded one would exhaust the isolate. Cap the command's output, or raise \`maxOutputBytes\`.`);let u;try{u=JSON.parse(c.text)}catch{throw new d("INTERNAL",`${t}: exec response was not JSON — expected {"code","stdout","stderr"} from POST ${l}`)}const f=T(u);if(f===void 0)throw new d("INTERNAL",`${t}: exec response is missing a numeric \`code\` — expected {"code","stdout","stderr"} from POST ${l}`);return f}finally{a.dispose()}};export{l as C,h as a,R as e,O as p,E as r};
|