@lunora/platform 1.0.0-alpha.55 → 1.0.0-alpha.57
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/conformance/index.d.mts +1 -1
- package/dist/conformance/index.d.ts +1 -1
- package/dist/conformance/suite.d.mts +1 -1
- package/dist/conformance/suite.d.ts +1 -1
- package/dist/conformance/suite.mjs +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/LUNORA_ROLE_PREFIX-Da37XHgu.mjs +1 -0
- package/dist/packem_shared/{socket-host.d-BsnGTydH.d.mts → socket-host.d-VUTw8RC_.d.mts} +8 -1
- package/dist/packem_shared/{socket-host.d-BsnGTydH.d.ts → socket-host.d-VUTw8RC_.d.ts} +8 -1
- package/package.json +1 -1
- package/dist/packem_shared/resolveShard-BzKOUEO4.mjs +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { type C as ConformanceHost, type a as ConformanceHostFactory, type R as ReferenceHost, type VitestApi, c as createReferenceHost, defineHostContractSuite } from "./suite.mjs";
|
|
2
|
-
import "../packem_shared/socket-host.d-
|
|
2
|
+
import "../packem_shared/socket-host.d-VUTw8RC_.mjs";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { type C as ConformanceHost, type a as ConformanceHostFactory, type R as ReferenceHost, type VitestApi, c as createReferenceHost, defineHostContractSuite } from "./suite.js";
|
|
2
|
-
import "../packem_shared/socket-host.d-
|
|
2
|
+
import "../packem_shared/socket-host.d-VUTw8RC_.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { e as ShardDirectory, i as ShardKvStore, n as SocketHandle, c as SchedulerHost, f as ShardHost, o as SocketHost } from "../packem_shared/socket-host.d-
|
|
1
|
+
import { e as ShardDirectory, i as ShardKvStore, n as SocketHandle, c as SchedulerHost, f as ShardHost, o as SocketHost } from "../packem_shared/socket-host.d-VUTw8RC_.mjs";
|
|
2
2
|
/**
|
|
3
3
|
* A conformance host bundles all four platform contracts so a single factory
|
|
4
4
|
* can stand up a complete, isolated test environment.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { e as ShardDirectory, i as ShardKvStore, n as SocketHandle, c as SchedulerHost, f as ShardHost, o as SocketHost } from "../packem_shared/socket-host.d-
|
|
1
|
+
import { e as ShardDirectory, i as ShardKvStore, n as SocketHandle, c as SchedulerHost, f as ShardHost, o as SocketHost } from "../packem_shared/socket-host.d-VUTw8RC_.js";
|
|
2
2
|
/**
|
|
3
3
|
* A conformance host bundles all four platform contracts so a single factory
|
|
4
4
|
* can stand up a complete, isolated test environment.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveShard as E}from"../packem_shared/resolveShard-BzKOUEO4.mjs";const f=/platform closed/u,h=d=>new Promise(k=>{setTimeout(k,d)}),b=(d,k,v)=>{const{describe:w,expect:a,it:o}=v;w(`host contract: ${d}`,()=>{const p=async()=>k(),l=t=>t.createSocket?.()??{},c=async t=>{const e=await p();try{await t(e)}finally{e.cleanup?.()}};w("ShardHost",()=>{o("serializes mutations so no two closures interleave",async()=>{a.assertions(1),await c(async t=>{const e=[];await Promise.all([t.shard.runSerialized(async()=>{e.push("a-start"),await new Promise(i=>{setTimeout(i,10)}),e.push("a-end")}),t.shard.runSerialized(async()=>{e.push("b-start"),await new Promise(i=>{setTimeout(i,5)}),e.push("b-end")})]);const s=e.join("").includes("a-starta-end"),r=e.join("").includes("b-startb-end");a(s&&r).toBe(!0)})}),o("rolls back a transaction that throws",async()=>{a.assertions(2),await c(async t=>{await t.shard.transaction(async()=>{t.shard.sql.exec("CREATE TABLE IF NOT EXISTS rollback_test (id INTEGER PRIMARY KEY)"),t.shard.sql.exec("INSERT INTO rollback_test (id) VALUES (1)")}),await a(t.shard.transaction(async()=>{throw t.shard.sql.exec("INSERT INTO rollback_test (id) VALUES (2)"),new Error("boom")})).rejects.toThrow("boom");const e=t.shard.sql.exec("SELECT id FROM rollback_test WHERE id = 2").toArray();a(e).toHaveLength(0)})}),o("never lets a task outside a mutation observe its uncommitted writes",async t=>{a.hasAssertions(),await c(async e=>{if(e.isolatesByDispatch===!0){t.skip(`${d} isolates concurrent tasks at the dispatch boundary, which no in-isolate read can stand in for`);return}e.shard.sql.exec("CREATE TABLE IF NOT EXISTS isolation_test (id INTEGER PRIMARY KEY)");let s;const r=new Promise(m=>{s=m}),i=e.shard.runSerialized(async()=>e.shard.transaction(async()=>{throw e.shard.sql.exec("INSERT INTO isolation_test (id) VALUES (1)"),s(),await h(20),new Error("boom")}));await r;let n,u;try{n=e.shard.sql.exec("SELECT id FROM isolation_test").toArray()}catch(m){n=[],u=m}a(n).toStrictEqual([]),u!==void 0&&a(u).toMatchObject({code:"SHARD_UNAVAILABLE",status:503,type:"VisulimaError"}),await a(i).rejects.toThrow("boom"),a(e.shard.sql.exec("SELECT id FROM isolation_test").toArray()).toStrictEqual([])})}),o("rejects with the value the closure threw, and stays usable",async()=>{a.assertions(3),await c(async t=>{const e=Object.assign(new Error("closure failed"),{code:"NOT_FOUND",status:404});await a(t.shard.transaction(()=>Promise.reject(e))).rejects.toBe(e),await a(t.shard.runSerialized(()=>Promise.reject(e))).rejects.toBe(e),await a(t.shard.runSerialized(async()=>t.shard.transaction(async()=>"still here"))).resolves.toBe("still here")})}),o("observes its own writes inside a transaction",async()=>{a.assertions(1),await c(async t=>{const e=await t.shard.transaction(async()=>(t.shard.sql.exec("CREATE TABLE IF NOT EXISTS ryw_test (id INTEGER PRIMARY KEY, value TEXT)"),t.shard.sql.exec("INSERT INTO ryw_test (id, value) VALUES (1, 'hello')"),t.shard.sql.exec("SELECT value FROM ryw_test WHERE id = 1").toArray()[0]?.value));a(e).toBe("hello")})}),o("keeps overlapping transactions atomic",async()=>{a.assertions(2),await c(async t=>{t.shard.sql.exec("CREATE TABLE IF NOT EXISTS overlap_test (id TEXT PRIMARY KEY)");const e=t.shard.transaction(async()=>{t.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('A')"),await new Promise(i=>{setTimeout(i,20)}),t.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('B')")}),s=t.shard.transaction(async()=>{t.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('C')")});await Promise.all([e,s]);const r=t.shard.sql.exec("SELECT id FROM overlap_test ORDER BY id").toArray();a(r.map(i=>i.id)).toStrictEqual(["A","B","C"]),await a(t.shard.transaction(async()=>{throw t.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('D')"),new Error("boom")})).rejects.toThrow("boom")})}),o("returns a cursor that buffers, yields one row, and iterates",async()=>{a.assertions(3),await c(async t=>{await t.shard.transaction(async()=>{t.shard.sql.exec("CREATE TABLE IF NOT EXISTS cursor_test (id INTEGER PRIMARY KEY)"),t.shard.sql.exec("INSERT INTO cursor_test (id) VALUES (1)"),t.shard.sql.exec("INSERT INTO cursor_test (id) VALUES (2)")}),a(t.shard.sql.exec("SELECT id FROM cursor_test ORDER BY id").toArray()).toHaveLength(2),a(t.shard.sql.exec("SELECT id FROM cursor_test WHERE id = 1").one()).toEqual({id:1}),a([...t.shard.sql.exec("SELECT id FROM cursor_test ORDER BY id")]).toHaveLength(2)})}),o("reports a pending alarm, and clears it once fired",async()=>{a.assertions(2),await c(async t=>{const e=Date.now()+50;if(await t.shard.alarms.set(e),a(await t.shard.alarms.get()).toBe(e),t.awaitAlarmFired===void 0){a(await t.shard.alarms.get()).toBe(e);return}await t.awaitAlarmFired(e),a(await t.shard.alarms.get()).toBeNull()})}),o("deletes a pending alarm",async()=>{a.assertions(1),await c(async t=>{await t.shard.alarms.set(Date.now()+1e4),await t.shard.alarms.delete(),a(await t.shard.alarms.get()).toBeNull()})})}),w("SocketHost",()=>{o("accepts a socket and can send/close",async()=>{await c(async t=>{const e=t.socket.accept(l(t),{user:"ada"});a(t.socket.idFor(e)).toBeDefined(),e.send("hello"),a(t.socket.getSockets().map(s=>t.socket.idFor(s))).toContain(t.socket.idFor(e)),t.readFrames!==void 0&&a(t.readFrames(e)).toStrictEqual(["hello"]),a(()=>{e.close(1e3,"done")}).not.toThrow()})}),o("round-trips an attachment on a live socket",async()=>{a.assertions(1),await c(async t=>{const e={roomId:"room-1",roles:["admin"]},s=t.socket.accept(l(t),e);a(s.deserializeAttachment()).toEqual(e)})}),o("round-trips attachments across a recycle",async t=>{await c(async e=>{if(e.simulateRecycle===void 0||e.restoreSocket===void 0){t.skip(`${d} does not implement simulateRecycle/restoreSocket`);return}a.assertions(1);const s={roomId:"room-1",roles:["admin"]},r=e.socket.accept(l(e),s);e.simulateRecycle();const i=e.restoreSocket(e.socket.idFor(r),s);a(i.deserializeAttachment()).toEqual(s)})}),o("keeps idFor stable across repeated calls within a wake",async()=>{a.assertions(1),await c(async t=>{const e=t.socket.accept(l(t),{}),s=t.socket.idFor(e),r=t.socket.idFor(e);a(r).toBe(s)})}),o("keeps idFor stable across a recycle",async t=>{await c(async e=>{if(e.simulateRecycle===void 0||e.restoreSocket===void 0){t.skip(`${d} does not implement simulateRecycle/restoreSocket`);return}a.assertions(1);const s={roomId:"room-1",roles:["admin"]},r=e.socket.accept(l(e),s),i=e.socket.idFor(r);e.simulateRecycle();const n=e.restoreSocket(i,s);a(e.socket.idFor(n)).toBe(i)})}),o("returns exactly the sockets carrying an accept-time tag",async()=>{a.assertions(4),await c(async t=>{const e=t.socket.accept(l(t),{},["room-a"]),s=t.socket.accept(l(t),{},["room-b"]),r=t.socket.accept(l(t),{}),i=n=>t.socket.idFor(n);a(t.socket.getSockets("room-a").map(i)).toStrictEqual([i(e)]),a(t.socket.getSockets("room-b").map(i)).toStrictEqual([i(s)]),a(t.socket.getSockets("room-c").map(i)).toStrictEqual([]),a(t.socket.getSockets().map(i)).toContain(i(r))})}),o("accepts the portable budget of nine caller tags",async()=>{a.assertions(9);const t=await p(),e=Array.from({length:9},(i,n)=>`tag-${String(n)}`),s=t.socket.accept(l(t),{},e),r=i=>t.socket.idFor(i);for(const i of e)a(t.socket.getSockets(i).map(r)).toStrictEqual([r(s)]);t.cleanup?.()}),o("resolves a raw socket back to its handle",async()=>{a.assertions(2),await c(async t=>{const e=l(t),s=t.socket.accept(e,{}),r=t.socket.handleFor(e);a(r!==void 0&&t.socket.idFor(r)).toBe(t.socket.idFor(s)),a(t.socket.handleFor(l(t))).toBeUndefined()})}),o("reports a plausible outbound queue depth, if any",async()=>{a.assertions(1),await c(async t=>{const e=t.socket.accept(l(t),{}),{bufferedAmount:s}=e;a(s===void 0||typeof s=="number"&&s>=0).toBe(!0)})}),o("retags a live socket when the host declares mutable tags",async t=>{await c(async e=>{if(e.socket.setTag===void 0){t.skip(`${d} does not implement mutable socket tags (setTag)`);return}a.assertions(2);const s=e.socket.accept(l(e),{});e.socket.setTag(s,"room-a"),a(e.socket.getSockets("room-a").map(r=>e.socket.idFor(r))).toStrictEqual([e.socket.idFor(s)]),e.socket.removeTag?.(s,"room-a"),a(e.socket.getSockets("room-a").map(r=>e.socket.idFor(r))).toStrictEqual([])})})}),w("ShardDirectory",()=>{o("resolves shard keys deterministically",async()=>{a.assertions(1),await c(async t=>{const e=await E(t.directory,"tenant-42").fetch(new Request("http://localhost/")),s=await E(t.directory,"tenant-42").fetch(new Request("http://localhost/"));await a(e.text()).resolves.toBe(await s.text())})}),o("dispatches fetch to a resolved stub",async()=>{a.assertions(1),await c(async t=>{const s=await E(t.directory,"tenant-42").fetch(new Request("http://localhost/"));a(s).toBeInstanceOf(Response)})})}),w("SchedulerHost",()=>{o("schedules a job for a future timestamp",async t=>{await c(async e=>{if(e.scheduler===void 0){t.skip(`${d} does not implement SchedulerHost`);return}a.assertions(2);const s=Date.now(),r=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:50});a(r.scheduledFor).toBeGreaterThanOrEqual(s+50),a(r.id).toBeDefined()})}),o("dispatches a scheduled job at least once",async t=>{await c(async e=>{if(e.scheduler===void 0){t.skip(`${d} does not implement SchedulerHost`);return}const s=e.scheduler.deadLetter!==void 0;if(!s&&e.awaitJobDispatched===void 0){t.skip(`${d} does not claim at-least-once delivery (no deadLetter) and supplies no awaitJobDispatched hook`);return}if(s&&a(e.awaitJobDispatched).toBeDefined(),e.awaitJobDispatched===void 0)return;const r=e.scheduler.list!==void 0;a.assertions(1+(s?1:0)+(r?1:0));const i=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:30});if(await a(e.awaitJobDispatched(i.id)).resolves.toBe(!0),r){const n=await e.scheduler.list?.();a(n?.some(u=>u.id===i.id)).toBe(!1)}})}),o("cancels a scheduled job",async t=>{await c(async e=>{if(e.scheduler===void 0){t.skip(`${d} does not implement SchedulerHost`);return}a.assertions(1);const s=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4}),r=await e.scheduler.cancel(s.id);a(r).toBe(!0)})}),o("reports a second cancel of the same job as false",async t=>{await c(async e=>{if(e.scheduler===void 0){t.skip(`${d} does not implement SchedulerHost`);return}a.assertions(2);const s=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4});a(await e.scheduler.cancel(s.id)).toBe(!0),a(await e.scheduler.cancel(s.id)).toBe(!1)})}),o("gives two identical schedules independently cancellable ids",async t=>{await c(async e=>{if(e.scheduler===void 0){t.skip(`${d} does not implement SchedulerHost`);return}a.assertions(3);const s=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:1e4}),r=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:1e4});a(r.id).not.toBe(s.id),a(await e.scheduler.cancel(s.id)).toBe(!0),a(await e.scheduler.cancel(r.id)).toBe(!0)})}),o("lists a pending job with a zero attempt count",async t=>{await c(async e=>{if(e.scheduler?.list===void 0){t.skip(`${d} does not implement SchedulerHost.list`);return}a.assertions(2);const s=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:1e4}),i=(await e.scheduler.list()).find(n=>n.id===s.id);a(i?.functionPath).toBe("tasks/remind"),a(i?.attempts).toBe(0)})}),o("keeps the pending and dead-letter listings disjoint",async t=>{await c(async e=>{if(e.scheduler?.list===void 0||e.scheduler.deadLetter===void 0||e.simulateDeadLetter===void 0){t.skip(`${d} does not implement scheduler.list/deadLetter/simulateDeadLetter`);return}a.assertions(2);const s=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4});await e.simulateDeadLetter(s.id);const r=await e.scheduler.list(),i=await e.scheduler.deadLetter.list();a(r.some(n=>n.id===s.id)).toBe(!1),a(i.some(n=>n.id===s.id)).toBe(!0)})}),o("returns a requeued job to the pending set with a fresh budget",async t=>{await c(async e=>{if(e.scheduler?.list===void 0||e.scheduler.deadLetter===void 0||e.simulateDeadLetter===void 0){t.skip(`${d} does not implement scheduler.list/deadLetter/simulateDeadLetter`);return}a.assertions(4);const s=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4});await e.simulateDeadLetter(s.id),a(await e.scheduler.deadLetter.requeue(s.id)).toBe(!0);const i=(await e.scheduler.list()).find(u=>u.id===s.id);a(i).toBeDefined(),a(i?.attempts).toBe(0);const n=await e.scheduler.deadLetter.list();a(n.some(u=>u.id===s.id)).toBe(!1)})}),o("reports a requeue of an unparked job as false",async t=>{await c(async e=>{if(e.scheduler?.deadLetter===void 0){t.skip(`${d} does not implement scheduler.deadLetter`);return}a.assertions(1),a(await e.scheduler.deadLetter.requeue("job-does-not-exist")).toBe(!1)})}),o("ticks a cron on schedule, and not before its next occurrence",async t=>{await c(async e=>{if(e.scheduler?.cron===void 0){t.skip(`${d} does not implement SchedulerHost.cron`);return}const{cronTicks:s}=e;if(s===void 0){a.fail(`${d} declares SchedulerHost.cron but no cronTicks — presence of cron is the claim that dynamic cron works, so it must be observable`);return}a.assertions(2),await e.scheduler.cron("* * * * * *","tasks/tick");const r=(new Date().getMonth()+6)%12+1;await e.scheduler.cron(`0 0 1 ${String(r)} *`,"tasks/far"),await new Promise(i=>{setTimeout(i,1200)}),a(s("tasks/tick")).toBeGreaterThanOrEqual(1),a(s("tasks/far")).toBe(0)})})}),w("ShardKvStore",()=>{o("reads back a written value",async t=>{await c(async e=>{if(e.kv===void 0){t.skip(`${d} does not implement ShardKvStore`);return}a.assertions(2),await e.kv.put("s:token-1",{userId:"ada"}),a(await e.kv.get("s:token-1")).toEqual({userId:"ada"}),a(await e.kv.get("s:missing")).toBeUndefined()})}),o("deletes a key idempotently",async t=>{await c(async e=>{if(e.kv===void 0){t.skip(`${d} does not implement ShardKvStore`);return}a.assertions(3),await e.kv.put("k",1),a(await e.kv.delete("k")).toBe(!0),a(await e.kv.delete("k")).toBe(!1),a(await e.kv.get("k")).toBeUndefined()})}),o("enumerates exactly the keys under a prefix",async t=>{await c(async e=>{if(e.kv===void 0){t.skip(`${d} does not implement ShardKvStore`);return}a.assertions(2),await e.kv.put("s:a",1),await e.kv.put("s:b",2),await e.kv.put("other",3);const s=await e.kv.list({prefix:"s:"}),r=await e.kv.list();a([...s.keys()].toSorted((i,n)=>i.localeCompare(n))).toStrictEqual(["s:a","s:b"]),a(r.size).toBe(3)})})}),w("post-dispose",()=>{o("fails closed on every documented surface once the host is terminally disposed",async t=>{const e=await p();if(e.disposeTerminally===void 0){t.skip(`${d} has no terminal dispose the suite can drive from inside a test`);return}const{removeTag:s,setTag:r}=e.socket,{scheduler:i}=e,n=e.socket.accept(l(e),{}),u=l(e);e.disposeTerminally();const m=async y=>{await a(async()=>{await y()}).rejects.toThrow(f)},S=[()=>e.shard.alarms.set(Date.now()+1e3),()=>e.shard.alarms.delete(),()=>e.socket.accept(u,{}),...r===void 0?[]:[()=>{r(n,"room-a")}],...s===void 0?[]:[()=>{s(n,"room-a")}],...i===void 0?[]:[()=>i.schedule("tasks/remind",{},{delayMs:10})]];a.assertions(S.length);for(const y of S)await m(y)})})})};export{b as defineHostContractSuite};
|
|
1
|
+
import{resolveShard as E}from"../packem_shared/LUNORA_ROLE_PREFIX-Da37XHgu.mjs";const f=/platform closed/u,h=d=>new Promise(k=>{setTimeout(k,d)}),b=(d,k,v)=>{const{describe:w,expect:a,it:o}=v;w(`host contract: ${d}`,()=>{const p=async()=>k(),l=t=>t.createSocket?.()??{},c=async t=>{const e=await p();try{await t(e)}finally{e.cleanup?.()}};w("ShardHost",()=>{o("serializes mutations so no two closures interleave",async()=>{a.assertions(1),await c(async t=>{const e=[];await Promise.all([t.shard.runSerialized(async()=>{e.push("a-start"),await new Promise(i=>{setTimeout(i,10)}),e.push("a-end")}),t.shard.runSerialized(async()=>{e.push("b-start"),await new Promise(i=>{setTimeout(i,5)}),e.push("b-end")})]);const s=e.join("").includes("a-starta-end"),r=e.join("").includes("b-startb-end");a(s&&r).toBe(!0)})}),o("rolls back a transaction that throws",async()=>{a.assertions(2),await c(async t=>{await t.shard.transaction(async()=>{t.shard.sql.exec("CREATE TABLE IF NOT EXISTS rollback_test (id INTEGER PRIMARY KEY)"),t.shard.sql.exec("INSERT INTO rollback_test (id) VALUES (1)")}),await a(t.shard.transaction(async()=>{throw t.shard.sql.exec("INSERT INTO rollback_test (id) VALUES (2)"),new Error("boom")})).rejects.toThrow("boom");const e=t.shard.sql.exec("SELECT id FROM rollback_test WHERE id = 2").toArray();a(e).toHaveLength(0)})}),o("never lets a task outside a mutation observe its uncommitted writes",async t=>{a.hasAssertions(),await c(async e=>{if(e.isolatesByDispatch===!0){t.skip(`${d} isolates concurrent tasks at the dispatch boundary, which no in-isolate read can stand in for`);return}e.shard.sql.exec("CREATE TABLE IF NOT EXISTS isolation_test (id INTEGER PRIMARY KEY)");let s;const r=new Promise(m=>{s=m}),i=e.shard.runSerialized(async()=>e.shard.transaction(async()=>{throw e.shard.sql.exec("INSERT INTO isolation_test (id) VALUES (1)"),s(),await h(20),new Error("boom")}));await r;let n,u;try{n=e.shard.sql.exec("SELECT id FROM isolation_test").toArray()}catch(m){n=[],u=m}a(n).toStrictEqual([]),u!==void 0&&a(u).toMatchObject({code:"SHARD_UNAVAILABLE",status:503,type:"VisulimaError"}),await a(i).rejects.toThrow("boom"),a(e.shard.sql.exec("SELECT id FROM isolation_test").toArray()).toStrictEqual([])})}),o("rejects with the value the closure threw, and stays usable",async()=>{a.assertions(3),await c(async t=>{const e=Object.assign(new Error("closure failed"),{code:"NOT_FOUND",status:404});await a(t.shard.transaction(()=>Promise.reject(e))).rejects.toBe(e),await a(t.shard.runSerialized(()=>Promise.reject(e))).rejects.toBe(e),await a(t.shard.runSerialized(async()=>t.shard.transaction(async()=>"still here"))).resolves.toBe("still here")})}),o("observes its own writes inside a transaction",async()=>{a.assertions(1),await c(async t=>{const e=await t.shard.transaction(async()=>(t.shard.sql.exec("CREATE TABLE IF NOT EXISTS ryw_test (id INTEGER PRIMARY KEY, value TEXT)"),t.shard.sql.exec("INSERT INTO ryw_test (id, value) VALUES (1, 'hello')"),t.shard.sql.exec("SELECT value FROM ryw_test WHERE id = 1").toArray()[0]?.value));a(e).toBe("hello")})}),o("keeps overlapping transactions atomic",async()=>{a.assertions(2),await c(async t=>{t.shard.sql.exec("CREATE TABLE IF NOT EXISTS overlap_test (id TEXT PRIMARY KEY)");const e=t.shard.transaction(async()=>{t.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('A')"),await new Promise(i=>{setTimeout(i,20)}),t.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('B')")}),s=t.shard.transaction(async()=>{t.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('C')")});await Promise.all([e,s]);const r=t.shard.sql.exec("SELECT id FROM overlap_test ORDER BY id").toArray();a(r.map(i=>i.id)).toStrictEqual(["A","B","C"]),await a(t.shard.transaction(async()=>{throw t.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('D')"),new Error("boom")})).rejects.toThrow("boom")})}),o("returns a cursor that buffers, yields one row, and iterates",async()=>{a.assertions(3),await c(async t=>{await t.shard.transaction(async()=>{t.shard.sql.exec("CREATE TABLE IF NOT EXISTS cursor_test (id INTEGER PRIMARY KEY)"),t.shard.sql.exec("INSERT INTO cursor_test (id) VALUES (1)"),t.shard.sql.exec("INSERT INTO cursor_test (id) VALUES (2)")}),a(t.shard.sql.exec("SELECT id FROM cursor_test ORDER BY id").toArray()).toHaveLength(2),a(t.shard.sql.exec("SELECT id FROM cursor_test WHERE id = 1").one()).toEqual({id:1}),a([...t.shard.sql.exec("SELECT id FROM cursor_test ORDER BY id")]).toHaveLength(2)})}),o("reports a pending alarm, and clears it once fired",async()=>{a.assertions(2),await c(async t=>{const e=Date.now()+50;if(await t.shard.alarms.set(e),a(await t.shard.alarms.get()).toBe(e),t.awaitAlarmFired===void 0){a(await t.shard.alarms.get()).toBe(e);return}await t.awaitAlarmFired(e),a(await t.shard.alarms.get()).toBeNull()})}),o("deletes a pending alarm",async()=>{a.assertions(1),await c(async t=>{await t.shard.alarms.set(Date.now()+1e4),await t.shard.alarms.delete(),a(await t.shard.alarms.get()).toBeNull()})})}),w("SocketHost",()=>{o("accepts a socket and can send/close",async()=>{await c(async t=>{const e=t.socket.accept(l(t),{user:"ada"});a(t.socket.idFor(e)).toBeDefined(),e.send("hello"),a(t.socket.getSockets().map(s=>t.socket.idFor(s))).toContain(t.socket.idFor(e)),t.readFrames!==void 0&&a(t.readFrames(e)).toStrictEqual(["hello"]),a(()=>{e.close(1e3,"done")}).not.toThrow()})}),o("round-trips an attachment on a live socket",async()=>{a.assertions(1),await c(async t=>{const e={roomId:"room-1",roles:["admin"]},s=t.socket.accept(l(t),e);a(s.deserializeAttachment()).toEqual(e)})}),o("round-trips attachments across a recycle",async t=>{await c(async e=>{if(e.simulateRecycle===void 0||e.restoreSocket===void 0){t.skip(`${d} does not implement simulateRecycle/restoreSocket`);return}a.assertions(1);const s={roomId:"room-1",roles:["admin"]},r=e.socket.accept(l(e),s);e.simulateRecycle();const i=e.restoreSocket(e.socket.idFor(r),s);a(i.deserializeAttachment()).toEqual(s)})}),o("keeps idFor stable across repeated calls within a wake",async()=>{a.assertions(1),await c(async t=>{const e=t.socket.accept(l(t),{}),s=t.socket.idFor(e),r=t.socket.idFor(e);a(r).toBe(s)})}),o("keeps idFor stable across a recycle",async t=>{await c(async e=>{if(e.simulateRecycle===void 0||e.restoreSocket===void 0){t.skip(`${d} does not implement simulateRecycle/restoreSocket`);return}a.assertions(1);const s={roomId:"room-1",roles:["admin"]},r=e.socket.accept(l(e),s),i=e.socket.idFor(r);e.simulateRecycle();const n=e.restoreSocket(i,s);a(e.socket.idFor(n)).toBe(i)})}),o("returns exactly the sockets carrying an accept-time tag",async()=>{a.assertions(4),await c(async t=>{const e=t.socket.accept(l(t),{},["room-a"]),s=t.socket.accept(l(t),{},["room-b"]),r=t.socket.accept(l(t),{}),i=n=>t.socket.idFor(n);a(t.socket.getSockets("room-a").map(i)).toStrictEqual([i(e)]),a(t.socket.getSockets("room-b").map(i)).toStrictEqual([i(s)]),a(t.socket.getSockets("room-c").map(i)).toStrictEqual([]),a(t.socket.getSockets().map(i)).toContain(i(r))})}),o("accepts the portable budget of nine caller tags",async()=>{a.assertions(9);const t=await p(),e=Array.from({length:9},(i,n)=>`tag-${String(n)}`),s=t.socket.accept(l(t),{},e),r=i=>t.socket.idFor(i);for(const i of e)a(t.socket.getSockets(i).map(r)).toStrictEqual([r(s)]);t.cleanup?.()}),o("resolves a raw socket back to its handle",async()=>{a.assertions(2),await c(async t=>{const e=l(t),s=t.socket.accept(e,{}),r=t.socket.handleFor(e);a(r!==void 0&&t.socket.idFor(r)).toBe(t.socket.idFor(s)),a(t.socket.handleFor(l(t))).toBeUndefined()})}),o("reports a plausible outbound queue depth, if any",async()=>{a.assertions(1),await c(async t=>{const e=t.socket.accept(l(t),{}),{bufferedAmount:s}=e;a(s===void 0||typeof s=="number"&&s>=0).toBe(!0)})}),o("retags a live socket when the host declares mutable tags",async t=>{await c(async e=>{if(e.socket.setTag===void 0){t.skip(`${d} does not implement mutable socket tags (setTag)`);return}a.assertions(2);const s=e.socket.accept(l(e),{});e.socket.setTag(s,"room-a"),a(e.socket.getSockets("room-a").map(r=>e.socket.idFor(r))).toStrictEqual([e.socket.idFor(s)]),e.socket.removeTag?.(s,"room-a"),a(e.socket.getSockets("room-a").map(r=>e.socket.idFor(r))).toStrictEqual([])})})}),w("ShardDirectory",()=>{o("resolves shard keys deterministically",async()=>{a.assertions(1),await c(async t=>{const e=await E(t.directory,"tenant-42").fetch(new Request("http://localhost/")),s=await E(t.directory,"tenant-42").fetch(new Request("http://localhost/"));await a(e.text()).resolves.toBe(await s.text())})}),o("dispatches fetch to a resolved stub",async()=>{a.assertions(1),await c(async t=>{const s=await E(t.directory,"tenant-42").fetch(new Request("http://localhost/"));a(s).toBeInstanceOf(Response)})})}),w("SchedulerHost",()=>{o("schedules a job for a future timestamp",async t=>{await c(async e=>{if(e.scheduler===void 0){t.skip(`${d} does not implement SchedulerHost`);return}a.assertions(2);const s=Date.now(),r=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:50});a(r.scheduledFor).toBeGreaterThanOrEqual(s+50),a(r.id).toBeDefined()})}),o("dispatches a scheduled job at least once",async t=>{await c(async e=>{if(e.scheduler===void 0){t.skip(`${d} does not implement SchedulerHost`);return}const s=e.scheduler.deadLetter!==void 0;if(!s&&e.awaitJobDispatched===void 0){t.skip(`${d} does not claim at-least-once delivery (no deadLetter) and supplies no awaitJobDispatched hook`);return}if(s&&a(e.awaitJobDispatched).toBeDefined(),e.awaitJobDispatched===void 0)return;const r=e.scheduler.list!==void 0;a.assertions(1+(s?1:0)+(r?1:0));const i=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:30});if(await a(e.awaitJobDispatched(i.id)).resolves.toBe(!0),r){const n=await e.scheduler.list?.();a(n?.some(u=>u.id===i.id)).toBe(!1)}})}),o("cancels a scheduled job",async t=>{await c(async e=>{if(e.scheduler===void 0){t.skip(`${d} does not implement SchedulerHost`);return}a.assertions(1);const s=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4}),r=await e.scheduler.cancel(s.id);a(r).toBe(!0)})}),o("reports a second cancel of the same job as false",async t=>{await c(async e=>{if(e.scheduler===void 0){t.skip(`${d} does not implement SchedulerHost`);return}a.assertions(2);const s=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4});a(await e.scheduler.cancel(s.id)).toBe(!0),a(await e.scheduler.cancel(s.id)).toBe(!1)})}),o("gives two identical schedules independently cancellable ids",async t=>{await c(async e=>{if(e.scheduler===void 0){t.skip(`${d} does not implement SchedulerHost`);return}a.assertions(3);const s=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:1e4}),r=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:1e4});a(r.id).not.toBe(s.id),a(await e.scheduler.cancel(s.id)).toBe(!0),a(await e.scheduler.cancel(r.id)).toBe(!0)})}),o("lists a pending job with a zero attempt count",async t=>{await c(async e=>{if(e.scheduler?.list===void 0){t.skip(`${d} does not implement SchedulerHost.list`);return}a.assertions(2);const s=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:1e4}),i=(await e.scheduler.list()).find(n=>n.id===s.id);a(i?.functionPath).toBe("tasks/remind"),a(i?.attempts).toBe(0)})}),o("keeps the pending and dead-letter listings disjoint",async t=>{await c(async e=>{if(e.scheduler?.list===void 0||e.scheduler.deadLetter===void 0||e.simulateDeadLetter===void 0){t.skip(`${d} does not implement scheduler.list/deadLetter/simulateDeadLetter`);return}a.assertions(2);const s=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4});await e.simulateDeadLetter(s.id);const r=await e.scheduler.list(),i=await e.scheduler.deadLetter.list();a(r.some(n=>n.id===s.id)).toBe(!1),a(i.some(n=>n.id===s.id)).toBe(!0)})}),o("returns a requeued job to the pending set with a fresh budget",async t=>{await c(async e=>{if(e.scheduler?.list===void 0||e.scheduler.deadLetter===void 0||e.simulateDeadLetter===void 0){t.skip(`${d} does not implement scheduler.list/deadLetter/simulateDeadLetter`);return}a.assertions(4);const s=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4});await e.simulateDeadLetter(s.id),a(await e.scheduler.deadLetter.requeue(s.id)).toBe(!0);const i=(await e.scheduler.list()).find(u=>u.id===s.id);a(i).toBeDefined(),a(i?.attempts).toBe(0);const n=await e.scheduler.deadLetter.list();a(n.some(u=>u.id===s.id)).toBe(!1)})}),o("reports a requeue of an unparked job as false",async t=>{await c(async e=>{if(e.scheduler?.deadLetter===void 0){t.skip(`${d} does not implement scheduler.deadLetter`);return}a.assertions(1),a(await e.scheduler.deadLetter.requeue("job-does-not-exist")).toBe(!1)})}),o("ticks a cron on schedule, and not before its next occurrence",async t=>{await c(async e=>{if(e.scheduler?.cron===void 0){t.skip(`${d} does not implement SchedulerHost.cron`);return}const{cronTicks:s}=e;if(s===void 0){a.fail(`${d} declares SchedulerHost.cron but no cronTicks — presence of cron is the claim that dynamic cron works, so it must be observable`);return}a.assertions(2),await e.scheduler.cron("* * * * * *","tasks/tick");const r=(new Date().getMonth()+6)%12+1;await e.scheduler.cron(`0 0 1 ${String(r)} *`,"tasks/far"),await new Promise(i=>{setTimeout(i,1200)}),a(s("tasks/tick")).toBeGreaterThanOrEqual(1),a(s("tasks/far")).toBe(0)})})}),w("ShardKvStore",()=>{o("reads back a written value",async t=>{await c(async e=>{if(e.kv===void 0){t.skip(`${d} does not implement ShardKvStore`);return}a.assertions(2),await e.kv.put("s:token-1",{userId:"ada"}),a(await e.kv.get("s:token-1")).toEqual({userId:"ada"}),a(await e.kv.get("s:missing")).toBeUndefined()})}),o("deletes a key idempotently",async t=>{await c(async e=>{if(e.kv===void 0){t.skip(`${d} does not implement ShardKvStore`);return}a.assertions(3),await e.kv.put("k",1),a(await e.kv.delete("k")).toBe(!0),a(await e.kv.delete("k")).toBe(!1),a(await e.kv.get("k")).toBeUndefined()})}),o("enumerates exactly the keys under a prefix",async t=>{await c(async e=>{if(e.kv===void 0){t.skip(`${d} does not implement ShardKvStore`);return}a.assertions(2),await e.kv.put("s:a",1),await e.kv.put("s:b",2),await e.kv.put("other",3);const s=await e.kv.list({prefix:"s:"}),r=await e.kv.list();a([...s.keys()].toSorted((i,n)=>i.localeCompare(n))).toStrictEqual(["s:a","s:b"]),a(r.size).toBe(3)})})}),w("post-dispose",()=>{o("fails closed on every documented surface once the host is terminally disposed",async t=>{const e=await p();if(e.disposeTerminally===void 0){t.skip(`${d} has no terminal dispose the suite can drive from inside a test`);return}const{removeTag:s,setTag:r}=e.socket,{scheduler:i}=e,n=e.socket.accept(l(e),{}),u=l(e);e.disposeTerminally();const m=async y=>{await a(async()=>{await y()}).rejects.toThrow(f)},S=[()=>e.shard.alarms.set(Date.now()+1e3),()=>e.shard.alarms.delete(),()=>e.socket.accept(u,{}),...r===void 0?[]:[()=>{r(n,"room-a")}],...s===void 0?[]:[()=>{s(n,"room-a")}],...i===void 0?[]:[()=>i.schedule("tasks/remind",{},{delayMs:10})]];a.assertions(S.length);for(const y of S)await m(y)})})})};export{b as defineHostContractSuite};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { type D as DirectShardDirectory, type S as ScheduleOptions, type a as ScheduledJob, type b as ScheduledJobStatus, type c as SchedulerHost, type d as ShardAlarms, type e as ShardDirectory, type f as ShardHost, type g as ShardJurisdiction, type h as ShardKvListOptions, type i as ShardKvStore, type j as ShardRegionHint, type k as ShardSqlCursor, type l as ShardSqlExec, type m as ShardStub, type n as SocketHandle, type o as SocketHost, type p as SqlRow, type T as TwoStepShardDirectory, r as resolveShard } from "./packem_shared/socket-host.d-
|
|
1
|
+
export { type D as DirectShardDirectory, L as LUNORA_ROLE_PREFIX, type S as ScheduleOptions, type a as ScheduledJob, type b as ScheduledJobStatus, type c as SchedulerHost, type d as ShardAlarms, type e as ShardDirectory, type f as ShardHost, type g as ShardJurisdiction, type h as ShardKvListOptions, type i as ShardKvStore, type j as ShardRegionHint, type k as ShardSqlCursor, type l as ShardSqlExec, type m as ShardStub, type n as SocketHandle, type o as SocketHost, type p as SqlRow, type T as TwoStepShardDirectory, r as resolveShard } from "./packem_shared/socket-host.d-VUTw8RC_.mjs";
|
|
2
2
|
/**
|
|
3
3
|
* The subset of the Cloudflare `ExecutionContext` the Lunora worker entry and
|
|
4
4
|
* the framework mount seams rely on — `waitUntil` for fire-and-forget work that
|
|
@@ -1190,9 +1190,9 @@ interface PlatformCapabilities {
|
|
|
1190
1190
|
* differs is which primitives exist, and that difference is exactly this
|
|
1191
1191
|
* matrix.
|
|
1192
1192
|
*
|
|
1193
|
-
* Ratings track celld **v0.6.
|
|
1193
|
+
* Ratings track celld **v0.6.1** and derive from its documented compatibility
|
|
1194
1194
|
* surface (`docs/cloudflare-compat.md`, `docs/services/*.md`,
|
|
1195
|
-
* `docs/limitations.md` in the celld repo; v0.6.0
|
|
1195
|
+
* `docs/limitations.md` in the celld repo; v0.6.0 was celld's first beta). The host contracts
|
|
1196
1196
|
* behind `shardedState`, `localSql`, `shardAlarms`, `commitOrderedTables` and
|
|
1197
1197
|
* `websocketHibernation` are also exercised by the conformance TCK against a
|
|
1198
1198
|
* live single-node celld (`@lunora/platform-celld`'s `celld` vitest project),
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { type D as DirectShardDirectory, type S as ScheduleOptions, type a as ScheduledJob, type b as ScheduledJobStatus, type c as SchedulerHost, type d as ShardAlarms, type e as ShardDirectory, type f as ShardHost, type g as ShardJurisdiction, type h as ShardKvListOptions, type i as ShardKvStore, type j as ShardRegionHint, type k as ShardSqlCursor, type l as ShardSqlExec, type m as ShardStub, type n as SocketHandle, type o as SocketHost, type p as SqlRow, type T as TwoStepShardDirectory, r as resolveShard } from "./packem_shared/socket-host.d-
|
|
1
|
+
export { type D as DirectShardDirectory, L as LUNORA_ROLE_PREFIX, type S as ScheduleOptions, type a as ScheduledJob, type b as ScheduledJobStatus, type c as SchedulerHost, type d as ShardAlarms, type e as ShardDirectory, type f as ShardHost, type g as ShardJurisdiction, type h as ShardKvListOptions, type i as ShardKvStore, type j as ShardRegionHint, type k as ShardSqlCursor, type l as ShardSqlExec, type m as ShardStub, type n as SocketHandle, type o as SocketHost, type p as SqlRow, type T as TwoStepShardDirectory, r as resolveShard } from "./packem_shared/socket-host.d-VUTw8RC_.js";
|
|
2
2
|
/**
|
|
3
3
|
* The subset of the Cloudflare `ExecutionContext` the Lunora worker entry and
|
|
4
4
|
* the framework mount seams rely on — `waitUntil` for fire-and-forget work that
|
|
@@ -1190,9 +1190,9 @@ interface PlatformCapabilities {
|
|
|
1190
1190
|
* differs is which primitives exist, and that difference is exactly this
|
|
1191
1191
|
* matrix.
|
|
1192
1192
|
*
|
|
1193
|
-
* Ratings track celld **v0.6.
|
|
1193
|
+
* Ratings track celld **v0.6.1** and derive from its documented compatibility
|
|
1194
1194
|
* surface (`docs/cloudflare-compat.md`, `docs/services/*.md`,
|
|
1195
|
-
* `docs/limitations.md` in the celld repo; v0.6.0
|
|
1195
|
+
* `docs/limitations.md` in the celld repo; v0.6.0 was celld's first beta). The host contracts
|
|
1196
1196
|
* behind `shardedState`, `localSql`, `shardAlarms`, `commitOrderedTables` and
|
|
1197
1197
|
* `websocketHibernation` are also exercised by the conformance TCK against a
|
|
1198
1198
|
* live single-node celld (`@lunora/platform-celld`'s `celld` vitest project),
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{NOOP_EXECUTION_CONTEXT as
|
|
1
|
+
import{NOOP_EXECUTION_CONTEXT as E}from"./packem_shared/NOOP_EXECUTION_CONTEXT-YmXqH-jH.mjs";import{LUNORA_ROLE_PREFIX as e,resolveShard as L}from"./packem_shared/LUNORA_ROLE_PREFIX-Da37XHgu.mjs";import{default as t}from"./packem_shared/CELLD_CAPABILITIES-BaeuWhr9.mjs";import{default as O}from"./packem_shared/CLOUDFLARE_CAPABILITIES-B0YUrj1a.mjs";import{default as C}from"./packem_shared/NODE_CAPABILITIES-C9ExnYOh.mjs";export{t as CELLD_CAPABILITIES,O as CLOUDFLARE_CAPABILITIES,e as LUNORA_ROLE_PREFIX,C as NODE_CAPABILITIES,E as NOOP_EXECUTION_CONTEXT,L as resolveShard};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const t="__lunora_do__:",_=(e,r,s)=>{if(r.startsWith(t))throw new Error(`shard key "${r}" uses the reserved "${t}" prefix`);return e.getByName!==void 0?e.getByName(r,s):e.get(e.idForName(r),s)};export{t as LUNORA_ROLE_PREFIX,_ as resolveShard};
|
|
@@ -304,6 +304,13 @@ interface TwoStepShardDirectory {
|
|
|
304
304
|
* closed when a jurisdiction is requested but the method is absent.
|
|
305
305
|
*/
|
|
306
306
|
type ShardDirectory = DirectShardDirectory | TwoStepShardDirectory;
|
|
307
|
+
/**
|
|
308
|
+
* The instance-name prefix reserved for framework roles that share the shard
|
|
309
|
+
* directory when an app merges its Durable Object classes (plan 462): the
|
|
310
|
+
* scheduler lives at `__lunora_do__:scheduler:<name>`. No shard key may carry
|
|
311
|
+
* it, so {@link resolveShard} refuses one on every host.
|
|
312
|
+
*/
|
|
313
|
+
declare const LUNORA_ROLE_PREFIX = "__lunora_do__:";
|
|
307
314
|
/**
|
|
308
315
|
* Resolve a shard key to a stub against either directory shape. Uses direct
|
|
309
316
|
* name lookup when the provider has it, and falls back to the two-step
|
|
@@ -685,4 +692,4 @@ interface SocketHost {
|
|
|
685
692
|
*/
|
|
686
693
|
setTag?: (socket: SocketHandle, tag: string) => void;
|
|
687
694
|
}
|
|
688
|
-
export { DirectShardDirectory as D, ScheduleOptions as S, TwoStepShardDirectory as T, ScheduledJob as a, ScheduledJobStatus as b, SchedulerHost as c, ShardAlarms as d, ShardDirectory as e, ShardHost as f, ShardJurisdiction as g, ShardKvListOptions as h, ShardKvStore as i, ShardRegionHint as j, ShardSqlCursor as k, ShardSqlExec as l, ShardStub as m, SocketHandle as n, SocketHost as o, SqlRow as p, resolveShard as r };
|
|
695
|
+
export { DirectShardDirectory as D, LUNORA_ROLE_PREFIX as L, ScheduleOptions as S, TwoStepShardDirectory as T, ScheduledJob as a, ScheduledJobStatus as b, SchedulerHost as c, ShardAlarms as d, ShardDirectory as e, ShardHost as f, ShardJurisdiction as g, ShardKvListOptions as h, ShardKvStore as i, ShardRegionHint as j, ShardSqlCursor as k, ShardSqlExec as l, ShardStub as m, SocketHandle as n, SocketHost as o, SqlRow as p, resolveShard as r };
|
|
@@ -304,6 +304,13 @@ interface TwoStepShardDirectory {
|
|
|
304
304
|
* closed when a jurisdiction is requested but the method is absent.
|
|
305
305
|
*/
|
|
306
306
|
type ShardDirectory = DirectShardDirectory | TwoStepShardDirectory;
|
|
307
|
+
/**
|
|
308
|
+
* The instance-name prefix reserved for framework roles that share the shard
|
|
309
|
+
* directory when an app merges its Durable Object classes (plan 462): the
|
|
310
|
+
* scheduler lives at `__lunora_do__:scheduler:<name>`. No shard key may carry
|
|
311
|
+
* it, so {@link resolveShard} refuses one on every host.
|
|
312
|
+
*/
|
|
313
|
+
declare const LUNORA_ROLE_PREFIX = "__lunora_do__:";
|
|
307
314
|
/**
|
|
308
315
|
* Resolve a shard key to a stub against either directory shape. Uses direct
|
|
309
316
|
* name lookup when the provider has it, and falls back to the two-step
|
|
@@ -685,4 +692,4 @@ interface SocketHost {
|
|
|
685
692
|
*/
|
|
686
693
|
setTag?: (socket: SocketHandle, tag: string) => void;
|
|
687
694
|
}
|
|
688
|
-
export { DirectShardDirectory as D, ScheduleOptions as S, TwoStepShardDirectory as T, ScheduledJob as a, ScheduledJobStatus as b, SchedulerHost as c, ShardAlarms as d, ShardDirectory as e, ShardHost as f, ShardJurisdiction as g, ShardKvListOptions as h, ShardKvStore as i, ShardRegionHint as j, ShardSqlCursor as k, ShardSqlExec as l, ShardStub as m, SocketHandle as n, SocketHost as o, SqlRow as p, resolveShard as r };
|
|
695
|
+
export { DirectShardDirectory as D, LUNORA_ROLE_PREFIX as L, ScheduleOptions as S, TwoStepShardDirectory as T, ScheduledJob as a, ScheduledJobStatus as b, SchedulerHost as c, ShardAlarms as d, ShardDirectory as e, ShardHost as f, ShardJurisdiction as g, ShardKvListOptions as h, ShardKvStore as i, ShardRegionHint as j, ShardSqlCursor as k, ShardSqlExec as l, ShardStub as m, SocketHandle as n, SocketHost as o, SqlRow as p, resolveShard as r };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/platform",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.57",
|
|
4
4
|
"description": "Provider-neutral host contracts for Lunora: shard/socket/directory/scheduler interfaces, binding projections, and the platform capability matrix",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const a=(e,g,N)=>e.getByName!==void 0?e.getByName(g,N):e.get(e.idForName(g),N);export{a as resolveShard};
|