@lunora/container 1.0.0-alpha.3 → 1.0.0-alpha.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +26 -0
- package/README.md +99 -1
- package/dist/bridge.d.mts +51 -35
- package/dist/bridge.d.ts +51 -35
- package/dist/bridge.mjs +1 -76
- package/dist/do/index.d.mts +580 -45
- package/dist/do/index.d.ts +580 -45
- package/dist/do/index.mjs +1 -138
- package/dist/index.d.mts +367 -114
- package/dist/index.d.ts +367 -114
- package/dist/index.mjs +1 -2
- package/dist/otel.d.mts +161 -0
- package/dist/otel.d.ts +161 -0
- package/dist/otel.mjs +1 -0
- package/dist/packem_shared/ContainerProxy-fTW_pXWY.mjs +27 -0
- package/dist/packem_shared/containerBindingName-D01FopQt.mjs +1 -0
- package/dist/packem_shared/createContainerContext-C8pmlFAX.mjs +1 -0
- package/dist/packem_shared/jurisdiction-DtE9s70w.mjs +1 -0
- package/dist/packem_shared/jurisdiction.d-DeQ3rtYK.d.mts +281 -0
- package/dist/packem_shared/jurisdiction.d-DeQ3rtYK.d.ts +281 -0
- package/package.json +6 -2
- package/dist/packem_shared/containerBindingName-BGdSdFNA.mjs +0 -116
- package/dist/packem_shared/createContainerContext-CTpyUQ4J.mjs +0 -133
- package/dist/packem_shared/types.d-D2l2SYol.d.mts +0 -140
- package/dist/packem_shared/types.d-D2l2SYol.d.ts +0 -140
package/dist/do/index.mjs
CHANGED
|
@@ -1,138 +1 @@
|
|
|
1
|
-
import { Container } from
|
|
2
|
-
import { resolveContainerEnvVars as resolveContainerEnvVariables } from '../packem_shared/containerBindingName-BGdSdFNA.mjs';
|
|
3
|
-
|
|
4
|
-
const LUNORA_EVENT_SOURCE = "lunora";
|
|
5
|
-
const buildContainerLifecycleEvent = (container, instance, event, message) => {
|
|
6
|
-
return {
|
|
7
|
-
container,
|
|
8
|
-
event,
|
|
9
|
-
instance,
|
|
10
|
-
level: event === "error" ? "error" : "info",
|
|
11
|
-
message,
|
|
12
|
-
source: LUNORA_EVENT_SOURCE,
|
|
13
|
-
ts: Date.now(),
|
|
14
|
-
type: "container"
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
const emitContainerLifecycle = (container, instance, event, message) => {
|
|
18
|
-
const envelope = buildContainerLifecycleEvent(container, instance, event, message);
|
|
19
|
-
const line = JSON.stringify(envelope);
|
|
20
|
-
if (event === "error") {
|
|
21
|
-
console.error(line);
|
|
22
|
-
} else {
|
|
23
|
-
console.log(line);
|
|
24
|
-
}
|
|
25
|
-
return envelope;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const RECORD_CONTAINER_EVENT_OP = "__lunora_admin__:recordContainerEvent";
|
|
29
|
-
const ROOT_SHARD_NAME = "__root__";
|
|
30
|
-
const applyJurisdiction = (namespace, jurisdiction) => {
|
|
31
|
-
if (jurisdiction === void 0) {
|
|
32
|
-
return namespace;
|
|
33
|
-
}
|
|
34
|
-
if (typeof namespace.jurisdiction !== "function") {
|
|
35
|
-
throw new TypeError(
|
|
36
|
-
`@lunora/container: Durable Object namespace does not support jurisdiction("${jurisdiction}") — update @cloudflare/workers-types or remove the jurisdiction option`
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
return namespace.jurisdiction(jurisdiction);
|
|
40
|
-
};
|
|
41
|
-
const isShardNamespace = (value) => {
|
|
42
|
-
if (value === null || typeof value !== "object") {
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
const candidate = value;
|
|
46
|
-
return typeof candidate.get === "function" && typeof candidate.idFromName === "function";
|
|
47
|
-
};
|
|
48
|
-
const resolveRootShard = (namespace, jurisdiction) => {
|
|
49
|
-
const pinned = applyJurisdiction(namespace, jurisdiction);
|
|
50
|
-
if (typeof pinned.getByName === "function") {
|
|
51
|
-
return pinned.getByName(ROOT_SHARD_NAME);
|
|
52
|
-
}
|
|
53
|
-
return pinned.get(pinned.idFromName(ROOT_SHARD_NAME));
|
|
54
|
-
};
|
|
55
|
-
const reportContainerLifecycle = async (env, envelope, jurisdiction) => {
|
|
56
|
-
try {
|
|
57
|
-
const envRecord = env ?? {};
|
|
58
|
-
const namespace = envRecord["SHARD"];
|
|
59
|
-
if (!isShardNamespace(namespace)) {
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
const adminBearer = typeof envRecord["LUNORA_ADMIN_TOKEN"] === "string" ? envRecord["LUNORA_ADMIN_TOKEN"] : void 0;
|
|
63
|
-
if (!adminBearer || adminBearer.length === 0) {
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
const request = new Request("https://shard.internal/rpc", {
|
|
67
|
-
body: JSON.stringify({ args: { event: envelope }, functionPath: RECORD_CONTAINER_EVENT_OP }),
|
|
68
|
-
headers: { authorization: `Bearer ${adminBearer}`, "content-type": "application/json" },
|
|
69
|
-
method: "POST"
|
|
70
|
-
});
|
|
71
|
-
await resolveRootShard(namespace, jurisdiction).fetch(request);
|
|
72
|
-
} catch {
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
class LunoraContainer extends Container {
|
|
77
|
-
/**
|
|
78
|
-
* Data-residency jurisdiction the app's DOs are pinned to (codegen passes the
|
|
79
|
-
* schema's `.jurisdiction("…")`). Used to pin the best-effort lifecycle report
|
|
80
|
-
* to the same region as the root shard. `undefined` ⇒ un-pinned.
|
|
81
|
-
*/
|
|
82
|
-
lunoraJurisdiction;
|
|
83
|
-
/** The `lunora/containers.ts` export name, for lifecycle log correlation. */
|
|
84
|
-
lunoraName;
|
|
85
|
-
constructor(context, env, definition, exportName, jurisdiction) {
|
|
86
|
-
super(context, env, {
|
|
87
|
-
defaultPort: definition.defaultPort,
|
|
88
|
-
envVars: resolveContainerEnvVariables(definition, env, exportName),
|
|
89
|
-
sleepAfter: definition.sleepAfter
|
|
90
|
-
});
|
|
91
|
-
if (definition.enableInternet !== void 0) {
|
|
92
|
-
this.enableInternet = definition.enableInternet;
|
|
93
|
-
}
|
|
94
|
-
this.lunoraName = exportName ?? "container";
|
|
95
|
-
this.lunoraJurisdiction = jurisdiction;
|
|
96
|
-
}
|
|
97
|
-
onError(error) {
|
|
98
|
-
const envelope = emitContainerLifecycle(this.lunoraName, this.instanceId(), "error", error instanceof Error ? error.message : String(error));
|
|
99
|
-
this.surfaceInStudioLogs(envelope);
|
|
100
|
-
return super.onError(error);
|
|
101
|
-
}
|
|
102
|
-
async onStart() {
|
|
103
|
-
const envelope = emitContainerLifecycle(this.lunoraName, this.instanceId(), "start");
|
|
104
|
-
this.surfaceInStudioLogs(envelope);
|
|
105
|
-
await super.onStart();
|
|
106
|
-
}
|
|
107
|
-
async onStop(parameters) {
|
|
108
|
-
const envelope = emitContainerLifecycle(this.lunoraName, this.instanceId(), "stop", `${parameters.reason} (exit ${String(parameters.exitCode)})`);
|
|
109
|
-
this.surfaceInStudioLogs(envelope);
|
|
110
|
-
await super.onStop(parameters);
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Best-effort push of `envelope` into the root ShardDO's log buffer so it
|
|
114
|
-
* also appears in the Studio Logs panel (the terminal already has it via
|
|
115
|
-
* `emitContainerLifecycle`). Fire-and-forget and fully swallowed: a missing
|
|
116
|
-
* `SHARD` binding, a missing admin token, or a fetch failure NEVER throws
|
|
117
|
-
* out of a lifecycle hook — the `console` path stays the source of truth.
|
|
118
|
-
*/
|
|
119
|
-
surfaceInStudioLogs(envelope) {
|
|
120
|
-
reportContainerLifecycle(this.env, envelope, this.lunoraJurisdiction).catch(() => {
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Per-instance correlation id: the Durable Object id, which Cloudflare also
|
|
125
|
-
* injects into the container as `CLOUDFLARE_DURABLE_OBJECT_ID`. Read
|
|
126
|
-
* defensively — the id shape varies and isn't worth crashing a hook over.
|
|
127
|
-
*/
|
|
128
|
-
instanceId() {
|
|
129
|
-
try {
|
|
130
|
-
const { id } = this.ctx;
|
|
131
|
-
return typeof id?.toString === "function" ? id.toString() : "unknown";
|
|
132
|
-
} catch {
|
|
133
|
-
return "unknown";
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export { LunoraContainer as default };
|
|
1
|
+
import{LunoraError as u}from"@lunora/errors";import{resolveContainerEnvVars as m,parseDurationSeconds as y}from"../packem_shared/containerBindingName-D01FopQt.mjs";import{a as g}from"../packem_shared/jurisdiction-DtE9s70w.mjs";import{Container as f}from"../packem_shared/ContainerProxy-fTW_pXWY.mjs";import{ContainerProxy as b,outboundParams as x}from"../packem_shared/ContainerProxy-fTW_pXWY.mjs";const w="lunora",E=(s,t,r,e)=>({container:s,event:r,instance:t,level:r==="error"?"error":"info",message:e,source:w,ts:Date.now(),type:"container"}),i=(s,t,r,e)=>{const o=E(s,t,r,e),n=JSON.stringify(o);return r==="error"?console.error(n):console.log(n),o},N="__lunora_admin__:recordContainerEvent",d="__root__",R=s=>{if(s===null||typeof s!="object")return!1;const t=s;return typeof t.get=="function"&&typeof t.idFromName=="function"},_=(s,t)=>{const r=g(s,t);return typeof r.getByName=="function"?r.getByName(d):r.get(r.idFromName(d))},T=async(s,t,r)=>{try{const e=s??{},o=e.SHARD;if(!R(o))return;const n=typeof e.LUNORA_ADMIN_TOKEN=="string"?e.LUNORA_ADMIN_TOKEN:void 0;if(!n||n.length===0)return;const a=new Request("https://shard.internal/rpc",{body:JSON.stringify({args:{event:t},functionPath:N}),headers:{authorization:`Bearer ${n}`,"content-type":"application/json"},method:"POST"});await _(o,r).fetch(a)}catch{}},p=500,S=3e4,h="__lunoraHardTimeoutGeneration";class L extends f{lunoraJurisdiction;lunoraName;lunoraDefaultPort;lunoraHardTimeoutSeconds;lunoraReadyOn;lunoraSecretsStore;lunoraSecretsStoreResolved;constructor(t,r,e,o,n){super(t,r,{defaultPort:e.defaultPort,entrypoint:e.entrypoint?[...e.entrypoint]:void 0,envVars:m(e,r,o),sleepAfter:e.sleepAfter}),e.enableInternet!==void 0&&(this.enableInternet=e.enableInternet),e.requiredPorts!==void 0&&(this.requiredPorts=[...e.requiredPorts]),e.interceptHttps!==void 0&&(this.interceptHttps=e.interceptHttps),e.allowedHosts!==void 0&&(this.allowedHosts=[...e.allowedHosts]),e.deniedHosts!==void 0&&(this.deniedHosts=[...e.deniedHosts]),e.pingEndpoint!==void 0&&(this.pingEndpoint=e.pingEndpoint),e.labels!==void 0&&(this.labels={...e.labels}),this.lunoraName=o??"container",this.lunoraJurisdiction=n,this.lunoraDefaultPort=e.defaultPort,this.lunoraReadyOn=e.readyOn?[...e.readyOn]:[],this.lunoraHardTimeoutSeconds=e.hardTimeout===void 0?void 0:y(e.hardTimeout),this.lunoraSecretsStore=e.secretsStore}async containerFetch(...t){return await this.resolveSecretsStoreEnv(),super.containerFetch(...t)}async start(...t){const[r]=t;return r?.envVars===void 0&&await this.resolveSecretsStoreEnv(),super.start(...t)}async onActivityExpired(){const t=i(this.lunoraName,this.instanceId(),"sleep");this.surfaceInStudioLogs(t),await super.onActivityExpired()}onError(t){const r=i(this.lunoraName,this.instanceId(),"error",t instanceof Error?t.message:String(t));return this.surfaceInStudioLogs(r),super.onError(t)}async onStart(){const t=i(this.lunoraName,this.instanceId(),"start");this.surfaceInStudioLogs(t),await super.onStart(),await this.armHardTimeout(),await this.awaitContainerReadiness()}async onHardTimeoutExpired(t){const r=await this.ctx.storage.get(h);if(t?.generation!==void 0&&t.generation!==r||this.ctx.container?.running!==!0)return;const e=i(this.lunoraName,this.instanceId(),"stop","hard timeout reached");this.surfaceInStudioLogs(e),await this.stop()}async onStop(t){const r=i(this.lunoraName,this.instanceId(),"stop",`${t.reason} (exit ${String(t.exitCode)})`);this.surfaceInStudioLogs(r),await super.onStop(t)}async armHardTimeout(){if(this.lunoraHardTimeoutSeconds===void 0)return;const t=(await this.ctx.storage.get(h)??0)+1;await this.ctx.storage.put(h,t),await this.schedule(this.lunoraHardTimeoutSeconds,"onHardTimeoutExpired",{generation:t})}async resolveSecretsStoreEnv(){const t=this.lunoraSecretsStore;t!==void 0&&(this.lunoraSecretsStoreResolved??=(async()=>{const r=this.env,e={};for(const[o,n]of Object.entries(t)){const a=r[n];if(a===void 0||typeof a.get!="function")throw new u("INTERNAL",`container "${this.lunoraName}": secretsStore env "${o}" points at binding "${n}", which is not a Secrets Store binding on the Worker env. Add a \`secrets_store_secrets\` entry binding "${n}".`);const c=await a.get();if(typeof c!="string")throw new TypeError(`container "${this.lunoraName}": Secrets Store binding "${n}" (env "${o}") did not resolve to a string value.`);e[o]=c}this.envVars={...this.envVars,...e}})().catch(r=>{throw this.lunoraSecretsStoreResolved=void 0,r}),await this.lunoraSecretsStoreResolved)}async awaitContainerReadiness(){if(this.lunoraReadyOn.length===0)return;const{container:t}=this.ctx;if(t===void 0)return;const r=Date.now()+S;await Promise.all(this.lunoraReadyOn.map(async e=>this.awaitReadinessCheck(t,e,r)))}async awaitReadinessCheck(t,r,e){const o=r.port??this.lunoraDefaultPort;if(o===void 0)throw new u("INTERNAL",`container "${this.lunoraName}": readyOn check "${r.path}" has no port — set the check's \`port\` or the container \`defaultPort\`.`);const n=r.status??200,a=r.path.startsWith("/")?r.path:`/${r.path}`,c=t.getTcpPort(o);for(;;){const v=Math.max(p,e-Date.now());try{if((await c.fetch(`http://container${a}`,{signal:AbortSignal.timeout(v)})).status===n)return}catch{}if(Date.now()>=e)throw new u("INTERNAL",`container "${this.lunoraName}": readiness check "${r.path}" (port ${String(o)}) did not return ${String(n)} within ${String(S)}ms`);await new Promise(l=>{setTimeout(l,p)})}}surfaceInStudioLogs(t){T(this.env,t,this.lunoraJurisdiction).catch(()=>{})}instanceId(){try{const{id:t}=this.ctx;return typeof t?.toString=="function"?t.toString():"unknown"}catch{return"unknown"}}}export{b as ContainerProxy,L as LunoraContainer,x as outboundParams};
|