@lunora/platform 1.0.0-alpha.10 → 1.0.0-alpha.11
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.mjs +1 -1
- package/dist/conformance/suite.mjs +1 -1
- package/dist/index.d.mts +29 -1
- package/dist/index.d.ts +29 -1
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/createReferenceHost-baEGrNOu.mjs +1 -0
- package/dist/packem_shared/resolveShard-BzKOUEO4.mjs +1 -0
- package/package.json +1 -1
- package/dist/packem_shared/createReferenceHost-NGFPX8d2.mjs +0 -1
- package/dist/packem_shared/resolveShard-BPCXp8pD.mjs +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createReferenceHost as o}from"../packem_shared/createReferenceHost-
|
|
1
|
+
import{createReferenceHost as o}from"../packem_shared/createReferenceHost-baEGrNOu.mjs";import{defineHostContractSuite as f}from"./suite.mjs";export{o as createReferenceHost,f as defineHostContractSuite};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolveShard as h}from"../packem_shared/resolveShard-BPCXp8pD.mjs";const E=(n,m,k)=>{const{describe:u,expect:t,it:i}=k;u(`host contract: ${n}`,()=>{const w=async()=>m(),l=s=>s.createSocket?.()??{},c=async s=>{const e=await w();try{await s(e)}finally{e.cleanup?.()}};u("ShardHost",()=>{i("serializes mutations so no two closures interleave",async()=>{t.assertions(1),await c(async s=>{const e=[];await Promise.all([s.shard.runSerialized(async()=>{e.push("a-start"),await new Promise(r=>{setTimeout(r,10)}),e.push("a-end")}),s.shard.runSerialized(async()=>{e.push("b-start"),await new Promise(r=>{setTimeout(r,5)}),e.push("b-end")})]);const a=e.join("").includes("a-starta-end"),o=e.join("").includes("b-startb-end");t(a&&o).toBe(!0)})}),i("rolls back a transaction that throws",async()=>{t.assertions(2),await c(async s=>{await s.shard.transaction(async()=>{s.shard.sql.exec("CREATE TABLE IF NOT EXISTS rollback_test (id INTEGER PRIMARY KEY)"),s.shard.sql.exec("INSERT INTO rollback_test (id) VALUES (1)")}),await t(s.shard.transaction(async()=>{throw s.shard.sql.exec("INSERT INTO rollback_test (id) VALUES (2)"),new Error("boom")})).rejects.toThrow("boom");const e=s.shard.sql.exec("SELECT id FROM rollback_test WHERE id = 2").toArray();t(e).toHaveLength(0)})}),i("rejects with the value the closure threw, and stays usable",async()=>{t.assertions(3),await c(async s=>{const e=Object.assign(new Error("closure failed"),{code:"NOT_FOUND",status:404});await t(s.shard.transaction(()=>Promise.reject(e))).rejects.toBe(e),await t(s.shard.runSerialized(()=>Promise.reject(e))).rejects.toBe(e),await t(s.shard.runSerialized(async()=>s.shard.transaction(async()=>"still here"))).resolves.toBe("still here")})}),i("observes its own writes inside a transaction",async()=>{t.assertions(1),await c(async s=>{const e=await s.shard.transaction(async()=>(s.shard.sql.exec("CREATE TABLE IF NOT EXISTS ryw_test (id INTEGER PRIMARY KEY, value TEXT)"),s.shard.sql.exec("INSERT INTO ryw_test (id, value) VALUES (1, 'hello')"),s.shard.sql.exec("SELECT value FROM ryw_test WHERE id = 1").toArray()[0]?.value));t(e).toBe("hello")})}),i("keeps overlapping transactions atomic",async()=>{t.assertions(2),await c(async s=>{s.shard.sql.exec("CREATE TABLE IF NOT EXISTS overlap_test (id TEXT PRIMARY KEY)");const e=s.shard.transaction(async()=>{s.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('A')"),await new Promise(r=>{setTimeout(r,20)}),s.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('B')")}),a=s.shard.transaction(async()=>{s.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('C')")});await Promise.all([e,a]);const o=s.shard.sql.exec("SELECT id FROM overlap_test ORDER BY id").toArray();t(o.map(r=>r.id)).toStrictEqual(["A","B","C"]),await t(s.shard.transaction(async()=>{throw s.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('D')"),new Error("boom")})).rejects.toThrow("boom")})}),i("returns a cursor that buffers, yields one row, and iterates",async()=>{t.assertions(3),await c(async s=>{await s.shard.transaction(async()=>{s.shard.sql.exec("CREATE TABLE IF NOT EXISTS cursor_test (id INTEGER PRIMARY KEY)"),s.shard.sql.exec("INSERT INTO cursor_test (id) VALUES (1)"),s.shard.sql.exec("INSERT INTO cursor_test (id) VALUES (2)")}),t(s.shard.sql.exec("SELECT id FROM cursor_test ORDER BY id").toArray()).toHaveLength(2),t(s.shard.sql.exec("SELECT id FROM cursor_test WHERE id = 1").one()).toEqual({id:1}),t([...s.shard.sql.exec("SELECT id FROM cursor_test ORDER BY id")]).toHaveLength(2)})}),i("reports a pending alarm, and clears it once fired",async()=>{t.assertions(2),await c(async s=>{const e=Date.now()+50;if(await s.shard.alarms.set(e),t(await s.shard.alarms.get()).toBe(e),s.awaitAlarmFired===void 0){t(await s.shard.alarms.get()).toBe(e);return}await s.awaitAlarmFired(e),t(await s.shard.alarms.get()).toBeNull()})}),i("deletes a pending alarm",async()=>{t.assertions(1),await c(async s=>{await s.shard.alarms.set(Date.now()+1e4),await s.shard.alarms.delete(),t(await s.shard.alarms.get()).toBeNull()})})}),u("SocketHost",()=>{i("accepts a socket and can send/close",async()=>{await c(async s=>{const e=s.socket.accept(l(s),{user:"ada"});t(s.socket.idFor(e)).toBeDefined(),e.send("hello"),t(s.socket.getSockets().map(a=>s.socket.idFor(a))).toContain(s.socket.idFor(e)),s.readFrames!==void 0&&t(s.readFrames(e)).toStrictEqual(["hello"]),t(()=>{e.close(1e3,"done")}).not.toThrow()})}),i("round-trips an attachment on a live socket",async()=>{t.assertions(1),await c(async s=>{const e={roomId:"room-1",roles:["admin"]},a=s.socket.accept(l(s),e);t(a.deserializeAttachment()).toEqual(e)})}),i("round-trips attachments across a recycle",async s=>{await c(async e=>{if(e.simulateRecycle===void 0||e.restoreSocket===void 0){s.skip(`${n} does not implement simulateRecycle/restoreSocket`);return}t.assertions(1);const a={roomId:"room-1",roles:["admin"]},o=e.socket.accept(l(e),a);e.simulateRecycle();const r=e.restoreSocket(e.socket.idFor(o),a);t(r.deserializeAttachment()).toEqual(a)})}),i("keeps idFor stable across repeated calls within a wake",async()=>{t.assertions(1),await c(async s=>{const e=s.socket.accept(l(s),{}),a=s.socket.idFor(e),o=s.socket.idFor(e);t(o).toBe(a)})}),i("keeps idFor stable across a recycle",async s=>{await c(async e=>{if(e.simulateRecycle===void 0||e.restoreSocket===void 0){s.skip(`${n} does not implement simulateRecycle/restoreSocket`);return}t.assertions(1);const a={roomId:"room-1",roles:["admin"]},o=e.socket.accept(l(e),a),r=e.socket.idFor(o);e.simulateRecycle();const d=e.restoreSocket(r,a);t(e.socket.idFor(d)).toBe(r)})}),i("returns exactly the sockets carrying an accept-time tag",async()=>{t.assertions(4),await c(async s=>{const e=s.socket.accept(l(s),{},["room-a"]),a=s.socket.accept(l(s),{},["room-b"]),o=s.socket.accept(l(s),{}),r=d=>s.socket.idFor(d);t(s.socket.getSockets("room-a").map(r)).toStrictEqual([r(e)]),t(s.socket.getSockets("room-b").map(r)).toStrictEqual([r(a)]),t(s.socket.getSockets("room-c").map(r)).toStrictEqual([]),t(s.socket.getSockets().map(r)).toContain(r(o))})}),i("accepts the portable budget of nine caller tags",async()=>{t.assertions(9);const s=await w(),e=Array.from({length:9},(r,d)=>`tag-${String(d)}`),a=s.socket.accept(l(s),{},e),o=r=>s.socket.idFor(r);for(const r of e)t(s.socket.getSockets(r).map(o)).toStrictEqual([o(a)]);s.cleanup?.()}),i("resolves a raw socket back to its handle",async()=>{t.assertions(2),await c(async s=>{const e=l(s),a=s.socket.accept(e,{}),o=s.socket.handleFor(e);t(o!==void 0&&s.socket.idFor(o)).toBe(s.socket.idFor(a)),t(s.socket.handleFor(l(s))).toBeUndefined()})}),i("reports a plausible outbound queue depth, if any",async()=>{t.assertions(1),await c(async s=>{const e=s.socket.accept(l(s),{}),{bufferedAmount:a}=e;t(a===void 0||typeof a=="number"&&a>=0).toBe(!0)})}),i("retags a live socket when the host declares mutable tags",async s=>{await c(async e=>{if(e.socket.setTag===void 0){s.skip(`${n} does not implement mutable socket tags (setTag)`);return}t.assertions(2);const a=e.socket.accept(l(e),{});e.socket.setTag(a,"room-a"),t(e.socket.getSockets("room-a").map(o=>e.socket.idFor(o))).toStrictEqual([e.socket.idFor(a)]),e.socket.removeTag?.(a,"room-a"),t(e.socket.getSockets("room-a").map(o=>e.socket.idFor(o))).toStrictEqual([])})})}),u("ShardDirectory",()=>{i("resolves shard keys deterministically",async()=>{t.assertions(1),await c(async s=>{const e=await h(s.directory,"tenant-42").fetch(new Request("http://localhost/")),a=await h(s.directory,"tenant-42").fetch(new Request("http://localhost/"));await t(e.text()).resolves.toBe(await a.text())})}),i("dispatches fetch to a resolved stub",async()=>{t.assertions(1),await c(async s=>{const e=await h(s.directory,"tenant-42").fetch(new Request("http://localhost/"));t(e).toBeInstanceOf(Response)})})}),u("SchedulerHost",()=>{i("schedules a job for a future timestamp",async s=>{await c(async e=>{if(e.scheduler===void 0){s.skip(`${n} does not implement SchedulerHost`);return}t.assertions(2);const a=Date.now(),o=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:50});t(o.scheduledFor).toBeGreaterThanOrEqual(a+50),t(o.id).toBeDefined()})}),i("dispatches a scheduled job at least once",async s=>{await c(async e=>{if(e.scheduler===void 0){s.skip(`${n} does not implement SchedulerHost`);return}const a=e.scheduler.deadLetter!==void 0;if(!a&&e.awaitJobDispatched===void 0){s.skip(`${n} does not claim at-least-once delivery (no deadLetter) and supplies no awaitJobDispatched hook`);return}if(a&&t(e.awaitJobDispatched).toBeDefined(),e.awaitJobDispatched===void 0)return;const o=e.scheduler.list!==void 0;t.assertions(1+(a?1:0)+(o?1:0));const r=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:30});if(await t(e.awaitJobDispatched(r.id)).resolves.toBe(!0),o){const d=await e.scheduler.list?.();t(d?.some(y=>y.id===r.id)).toBe(!1)}})}),i("cancels a scheduled job",async s=>{await c(async e=>{if(e.scheduler===void 0){s.skip(`${n} does not implement SchedulerHost`);return}t.assertions(1);const a=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4}),o=await e.scheduler.cancel(a.id);t(o).toBe(!0)})}),i("reports a second cancel of the same job as false",async s=>{await c(async e=>{if(e.scheduler===void 0){s.skip(`${n} does not implement SchedulerHost`);return}t.assertions(2);const a=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4});t(await e.scheduler.cancel(a.id)).toBe(!0),t(await e.scheduler.cancel(a.id)).toBe(!1)})}),i("gives two identical schedules independently cancellable ids",async s=>{await c(async e=>{if(e.scheduler===void 0){s.skip(`${n} does not implement SchedulerHost`);return}t.assertions(3);const a=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:1e4}),o=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:1e4});t(o.id).not.toBe(a.id),t(await e.scheduler.cancel(a.id)).toBe(!0),t(await e.scheduler.cancel(o.id)).toBe(!0)})}),i("lists a pending job with a zero attempt count",async s=>{await c(async e=>{if(e.scheduler?.list===void 0){s.skip(`${n} does not implement SchedulerHost.list`);return}t.assertions(2);const a=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:1e4}),o=(await e.scheduler.list()).find(r=>r.id===a.id);t(o?.functionPath).toBe("tasks/remind"),t(o?.attempts).toBe(0)})}),i("keeps the pending and dead-letter listings disjoint",async s=>{await c(async e=>{if(e.scheduler?.list===void 0||e.scheduler.deadLetter===void 0||e.simulateDeadLetter===void 0){s.skip(`${n} does not implement scheduler.list/deadLetter/simulateDeadLetter`);return}t.assertions(2);const a=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4});await e.simulateDeadLetter(a.id);const o=await e.scheduler.list(),r=await e.scheduler.deadLetter.list();t(o.some(d=>d.id===a.id)).toBe(!1),t(r.some(d=>d.id===a.id)).toBe(!0)})}),i("returns a requeued job to the pending set with a fresh budget",async s=>{await c(async e=>{if(e.scheduler?.list===void 0||e.scheduler.deadLetter===void 0||e.simulateDeadLetter===void 0){s.skip(`${n} does not implement scheduler.list/deadLetter/simulateDeadLetter`);return}t.assertions(4);const a=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4});await e.simulateDeadLetter(a.id),t(await e.scheduler.deadLetter.requeue(a.id)).toBe(!0);const o=(await e.scheduler.list()).find(d=>d.id===a.id);t(o).toBeDefined(),t(o?.attempts).toBe(0);const r=await e.scheduler.deadLetter.list();t(r.some(d=>d.id===a.id)).toBe(!1)})}),i("reports a requeue of an unparked job as false",async s=>{await c(async e=>{if(e.scheduler?.deadLetter===void 0){s.skip(`${n} does not implement scheduler.deadLetter`);return}t.assertions(1),t(await e.scheduler.deadLetter.requeue("job-does-not-exist")).toBe(!1)})})}),u("ShardKvStore",()=>{i("reads back a written value",async s=>{await c(async e=>{if(e.kv===void 0){s.skip(`${n} does not implement ShardKvStore`);return}t.assertions(2),await e.kv.put("s:token-1",{userId:"ada"}),t(await e.kv.get("s:token-1")).toEqual({userId:"ada"}),t(await e.kv.get("s:missing")).toBeUndefined()})}),i("deletes a key idempotently",async s=>{await c(async e=>{if(e.kv===void 0){s.skip(`${n} does not implement ShardKvStore`);return}t.assertions(3),await e.kv.put("k",1),t(await e.kv.delete("k")).toBe(!0),t(await e.kv.delete("k")).toBe(!1),t(await e.kv.get("k")).toBeUndefined()})}),i("enumerates exactly the keys under a prefix",async s=>{await c(async e=>{if(e.kv===void 0){s.skip(`${n} does not implement ShardKvStore`);return}t.assertions(2),await e.kv.put("s:a",1),await e.kv.put("s:b",2),await e.kv.put("other",3);const a=await e.kv.list({prefix:"s:"}),o=await e.kv.list();t([...a.keys()].toSorted((r,d)=>r.localeCompare(d))).toStrictEqual(["s:a","s:b"]),t(o.size).toBe(3)})})})})};export{E as defineHostContractSuite};
|
|
1
|
+
import{resolveShard as k}from"../packem_shared/resolveShard-BzKOUEO4.mjs";const E=(n,m,y)=>{const{describe:u,expect:t,it:c}=y;u(`host contract: ${n}`,()=>{const p=async()=>m(),l=a=>a.createSocket?.()??{},o=async a=>{const e=await p();try{await a(e)}finally{e.cleanup?.()}};u("ShardHost",()=>{c("serializes mutations so no two closures interleave",async()=>{t.assertions(1),await o(async a=>{const e=[];await Promise.all([a.shard.runSerialized(async()=>{e.push("a-start"),await new Promise(i=>{setTimeout(i,10)}),e.push("a-end")}),a.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");t(s&&r).toBe(!0)})}),c("rolls back a transaction that throws",async()=>{t.assertions(2),await o(async a=>{await a.shard.transaction(async()=>{a.shard.sql.exec("CREATE TABLE IF NOT EXISTS rollback_test (id INTEGER PRIMARY KEY)"),a.shard.sql.exec("INSERT INTO rollback_test (id) VALUES (1)")}),await t(a.shard.transaction(async()=>{throw a.shard.sql.exec("INSERT INTO rollback_test (id) VALUES (2)"),new Error("boom")})).rejects.toThrow("boom");const e=a.shard.sql.exec("SELECT id FROM rollback_test WHERE id = 2").toArray();t(e).toHaveLength(0)})}),c("rejects with the value the closure threw, and stays usable",async()=>{t.assertions(3),await o(async a=>{const e=Object.assign(new Error("closure failed"),{code:"NOT_FOUND",status:404});await t(a.shard.transaction(()=>Promise.reject(e))).rejects.toBe(e),await t(a.shard.runSerialized(()=>Promise.reject(e))).rejects.toBe(e),await t(a.shard.runSerialized(async()=>a.shard.transaction(async()=>"still here"))).resolves.toBe("still here")})}),c("observes its own writes inside a transaction",async()=>{t.assertions(1),await o(async a=>{const e=await a.shard.transaction(async()=>(a.shard.sql.exec("CREATE TABLE IF NOT EXISTS ryw_test (id INTEGER PRIMARY KEY, value TEXT)"),a.shard.sql.exec("INSERT INTO ryw_test (id, value) VALUES (1, 'hello')"),a.shard.sql.exec("SELECT value FROM ryw_test WHERE id = 1").toArray()[0]?.value));t(e).toBe("hello")})}),c("keeps overlapping transactions atomic",async()=>{t.assertions(2),await o(async a=>{a.shard.sql.exec("CREATE TABLE IF NOT EXISTS overlap_test (id TEXT PRIMARY KEY)");const e=a.shard.transaction(async()=>{a.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('A')"),await new Promise(i=>{setTimeout(i,20)}),a.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('B')")}),s=a.shard.transaction(async()=>{a.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('C')")});await Promise.all([e,s]);const r=a.shard.sql.exec("SELECT id FROM overlap_test ORDER BY id").toArray();t(r.map(i=>i.id)).toStrictEqual(["A","B","C"]),await t(a.shard.transaction(async()=>{throw a.shard.sql.exec("INSERT INTO overlap_test (id) VALUES ('D')"),new Error("boom")})).rejects.toThrow("boom")})}),c("returns a cursor that buffers, yields one row, and iterates",async()=>{t.assertions(3),await o(async a=>{await a.shard.transaction(async()=>{a.shard.sql.exec("CREATE TABLE IF NOT EXISTS cursor_test (id INTEGER PRIMARY KEY)"),a.shard.sql.exec("INSERT INTO cursor_test (id) VALUES (1)"),a.shard.sql.exec("INSERT INTO cursor_test (id) VALUES (2)")}),t(a.shard.sql.exec("SELECT id FROM cursor_test ORDER BY id").toArray()).toHaveLength(2),t(a.shard.sql.exec("SELECT id FROM cursor_test WHERE id = 1").one()).toEqual({id:1}),t([...a.shard.sql.exec("SELECT id FROM cursor_test ORDER BY id")]).toHaveLength(2)})}),c("reports a pending alarm, and clears it once fired",async()=>{t.assertions(2),await o(async a=>{const e=Date.now()+50;if(await a.shard.alarms.set(e),t(await a.shard.alarms.get()).toBe(e),a.awaitAlarmFired===void 0){t(await a.shard.alarms.get()).toBe(e);return}await a.awaitAlarmFired(e),t(await a.shard.alarms.get()).toBeNull()})}),c("deletes a pending alarm",async()=>{t.assertions(1),await o(async a=>{await a.shard.alarms.set(Date.now()+1e4),await a.shard.alarms.delete(),t(await a.shard.alarms.get()).toBeNull()})})}),u("SocketHost",()=>{c("accepts a socket and can send/close",async()=>{await o(async a=>{const e=a.socket.accept(l(a),{user:"ada"});t(a.socket.idFor(e)).toBeDefined(),e.send("hello"),t(a.socket.getSockets().map(s=>a.socket.idFor(s))).toContain(a.socket.idFor(e)),a.readFrames!==void 0&&t(a.readFrames(e)).toStrictEqual(["hello"]),t(()=>{e.close(1e3,"done")}).not.toThrow()})}),c("round-trips an attachment on a live socket",async()=>{t.assertions(1),await o(async a=>{const e={roomId:"room-1",roles:["admin"]},s=a.socket.accept(l(a),e);t(s.deserializeAttachment()).toEqual(e)})}),c("round-trips attachments across a recycle",async a=>{await o(async e=>{if(e.simulateRecycle===void 0||e.restoreSocket===void 0){a.skip(`${n} does not implement simulateRecycle/restoreSocket`);return}t.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);t(i.deserializeAttachment()).toEqual(s)})}),c("keeps idFor stable across repeated calls within a wake",async()=>{t.assertions(1),await o(async a=>{const e=a.socket.accept(l(a),{}),s=a.socket.idFor(e),r=a.socket.idFor(e);t(r).toBe(s)})}),c("keeps idFor stable across a recycle",async a=>{await o(async e=>{if(e.simulateRecycle===void 0||e.restoreSocket===void 0){a.skip(`${n} does not implement simulateRecycle/restoreSocket`);return}t.assertions(1);const s={roomId:"room-1",roles:["admin"]},r=e.socket.accept(l(e),s),i=e.socket.idFor(r);e.simulateRecycle();const d=e.restoreSocket(i,s);t(e.socket.idFor(d)).toBe(i)})}),c("returns exactly the sockets carrying an accept-time tag",async()=>{t.assertions(4),await o(async a=>{const e=a.socket.accept(l(a),{},["room-a"]),s=a.socket.accept(l(a),{},["room-b"]),r=a.socket.accept(l(a),{}),i=d=>a.socket.idFor(d);t(a.socket.getSockets("room-a").map(i)).toStrictEqual([i(e)]),t(a.socket.getSockets("room-b").map(i)).toStrictEqual([i(s)]),t(a.socket.getSockets("room-c").map(i)).toStrictEqual([]),t(a.socket.getSockets().map(i)).toContain(i(r))})}),c("accepts the portable budget of nine caller tags",async()=>{t.assertions(9);const a=await p(),e=Array.from({length:9},(i,d)=>`tag-${String(d)}`),s=a.socket.accept(l(a),{},e),r=i=>a.socket.idFor(i);for(const i of e)t(a.socket.getSockets(i).map(r)).toStrictEqual([r(s)]);a.cleanup?.()}),c("resolves a raw socket back to its handle",async()=>{t.assertions(2),await o(async a=>{const e=l(a),s=a.socket.accept(e,{}),r=a.socket.handleFor(e);t(r!==void 0&&a.socket.idFor(r)).toBe(a.socket.idFor(s)),t(a.socket.handleFor(l(a))).toBeUndefined()})}),c("reports a plausible outbound queue depth, if any",async()=>{t.assertions(1),await o(async a=>{const e=a.socket.accept(l(a),{}),{bufferedAmount:s}=e;t(s===void 0||typeof s=="number"&&s>=0).toBe(!0)})}),c("retags a live socket when the host declares mutable tags",async a=>{await o(async e=>{if(e.socket.setTag===void 0){a.skip(`${n} does not implement mutable socket tags (setTag)`);return}t.assertions(2);const s=e.socket.accept(l(e),{});e.socket.setTag(s,"room-a"),t(e.socket.getSockets("room-a").map(r=>e.socket.idFor(r))).toStrictEqual([e.socket.idFor(s)]),e.socket.removeTag?.(s,"room-a"),t(e.socket.getSockets("room-a").map(r=>e.socket.idFor(r))).toStrictEqual([])})})}),u("ShardDirectory",()=>{c("resolves shard keys deterministically",async()=>{t.assertions(1),await o(async a=>{const e=await k(a.directory,"tenant-42").fetch(new Request("http://localhost/")),s=await k(a.directory,"tenant-42").fetch(new Request("http://localhost/"));await t(e.text()).resolves.toBe(await s.text())})}),c("dispatches fetch to a resolved stub",async()=>{t.assertions(1),await o(async a=>{const s=await k(a.directory,"tenant-42").fetch(new Request("http://localhost/"));t(s).toBeInstanceOf(Response)})})}),u("SchedulerHost",()=>{c("schedules a job for a future timestamp",async a=>{await o(async e=>{if(e.scheduler===void 0){a.skip(`${n} does not implement SchedulerHost`);return}t.assertions(2);const s=Date.now(),r=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:50});t(r.scheduledFor).toBeGreaterThanOrEqual(s+50),t(r.id).toBeDefined()})}),c("dispatches a scheduled job at least once",async a=>{await o(async e=>{if(e.scheduler===void 0){a.skip(`${n} does not implement SchedulerHost`);return}const s=e.scheduler.deadLetter!==void 0;if(!s&&e.awaitJobDispatched===void 0){a.skip(`${n} does not claim at-least-once delivery (no deadLetter) and supplies no awaitJobDispatched hook`);return}if(s&&t(e.awaitJobDispatched).toBeDefined(),e.awaitJobDispatched===void 0)return;const r=e.scheduler.list!==void 0;t.assertions(1+(s?1:0)+(r?1:0));const i=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:30});if(await t(e.awaitJobDispatched(i.id)).resolves.toBe(!0),r){const d=await e.scheduler.list?.();t(d?.some(w=>w.id===i.id)).toBe(!1)}})}),c("cancels a scheduled job",async a=>{await o(async e=>{if(e.scheduler===void 0){a.skip(`${n} does not implement SchedulerHost`);return}t.assertions(1);const s=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4}),r=await e.scheduler.cancel(s.id);t(r).toBe(!0)})}),c("reports a second cancel of the same job as false",async a=>{await o(async e=>{if(e.scheduler===void 0){a.skip(`${n} does not implement SchedulerHost`);return}t.assertions(2);const s=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4});t(await e.scheduler.cancel(s.id)).toBe(!0),t(await e.scheduler.cancel(s.id)).toBe(!1)})}),c("gives two identical schedules independently cancellable ids",async a=>{await o(async e=>{if(e.scheduler===void 0){a.skip(`${n} does not implement SchedulerHost`);return}t.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});t(r.id).not.toBe(s.id),t(await e.scheduler.cancel(s.id)).toBe(!0),t(await e.scheduler.cancel(r.id)).toBe(!0)})}),c("lists a pending job with a zero attempt count",async a=>{await o(async e=>{if(e.scheduler?.list===void 0){a.skip(`${n} does not implement SchedulerHost.list`);return}t.assertions(2);const s=await e.scheduler.schedule("tasks/remind",{user:"ada"},{delayMs:1e4}),i=(await e.scheduler.list()).find(d=>d.id===s.id);t(i?.functionPath).toBe("tasks/remind"),t(i?.attempts).toBe(0)})}),c("keeps the pending and dead-letter listings disjoint",async a=>{await o(async e=>{if(e.scheduler?.list===void 0||e.scheduler.deadLetter===void 0||e.simulateDeadLetter===void 0){a.skip(`${n} does not implement scheduler.list/deadLetter/simulateDeadLetter`);return}t.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();t(r.some(d=>d.id===s.id)).toBe(!1),t(i.some(d=>d.id===s.id)).toBe(!0)})}),c("returns a requeued job to the pending set with a fresh budget",async a=>{await o(async e=>{if(e.scheduler?.list===void 0||e.scheduler.deadLetter===void 0||e.simulateDeadLetter===void 0){a.skip(`${n} does not implement scheduler.list/deadLetter/simulateDeadLetter`);return}t.assertions(4);const s=await e.scheduler.schedule("tasks/remind",{},{delayMs:1e4});await e.simulateDeadLetter(s.id),t(await e.scheduler.deadLetter.requeue(s.id)).toBe(!0);const i=(await e.scheduler.list()).find(w=>w.id===s.id);t(i).toBeDefined(),t(i?.attempts).toBe(0);const d=await e.scheduler.deadLetter.list();t(d.some(w=>w.id===s.id)).toBe(!1)})}),c("reports a requeue of an unparked job as false",async a=>{await o(async e=>{if(e.scheduler?.deadLetter===void 0){a.skip(`${n} does not implement scheduler.deadLetter`);return}t.assertions(1),t(await e.scheduler.deadLetter.requeue("job-does-not-exist")).toBe(!1)})})}),u("ShardKvStore",()=>{c("reads back a written value",async a=>{await o(async e=>{if(e.kv===void 0){a.skip(`${n} does not implement ShardKvStore`);return}t.assertions(2),await e.kv.put("s:token-1",{userId:"ada"}),t(await e.kv.get("s:token-1")).toEqual({userId:"ada"}),t(await e.kv.get("s:missing")).toBeUndefined()})}),c("deletes a key idempotently",async a=>{await o(async e=>{if(e.kv===void 0){a.skip(`${n} does not implement ShardKvStore`);return}t.assertions(3),await e.kv.put("k",1),t(await e.kv.delete("k")).toBe(!0),t(await e.kv.delete("k")).toBe(!1),t(await e.kv.get("k")).toBeUndefined()})}),c("enumerates exactly the keys under a prefix",async a=>{await o(async e=>{if(e.kv===void 0){a.skip(`${n} does not implement ShardKvStore`);return}t.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();t([...s.keys()].toSorted((i,d)=>i.localeCompare(d))).toStrictEqual(["s:a","s:b"]),t(r.size).toBe(3)})})})})};export{E as defineHostContractSuite};
|
package/dist/index.d.mts
CHANGED
|
@@ -596,4 +596,32 @@ declare const CLOUDFLARE_CAPABILITIES: PlatformCapabilities;
|
|
|
596
596
|
* Both are argued in detail in `plans/234-node-host-findings.md`.
|
|
597
597
|
*/
|
|
598
598
|
declare const NODE_CAPABILITIES: PlatformCapabilities;
|
|
599
|
-
export { type AnalyticsEngineDataPoint, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, CLOUDFLARE_CAPABILITIES, type Capability, type CapabilityLevel, type D1DatabaseLike, type D1PreparedStatementLike, type D1SessionLike,
|
|
599
|
+
export { type AnalyticsEngineDataPoint, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, CLOUDFLARE_CAPABILITIES, type Capability, type CapabilityLevel, type D1DatabaseLike, type D1PreparedStatementLike, type D1SessionLike,
|
|
600
|
+
/**
|
|
601
|
+
* `@lunora/platform` — provider-neutral host contracts for Lunora.
|
|
602
|
+
*
|
|
603
|
+
* This package defines the structural interfaces that separate the Lunora
|
|
604
|
+
* engine from any specific host (Cloudflare Workers, AWS, Rivet, Node, etc.).
|
|
605
|
+
* It contains **types and capability metadata only** — near-zero runtime code.
|
|
606
|
+
*
|
|
607
|
+
* The contracts fall into four groups:
|
|
608
|
+
*
|
|
609
|
+
* 1. **Shard host** (`ShardHost`) — single-writer execution, transactions,
|
|
610
|
+
* local SQL, alarms, and background continuation per shard key.
|
|
611
|
+
* 2. **Socket host** (`SocketHost`) — hibernated WebSocket subscriptions with
|
|
612
|
+
* durable attachments and tagged fan-out.
|
|
613
|
+
* 3. **Shard directory** (`ShardDirectory`) — deterministic placement and RPC
|
|
614
|
+
* dispatch from shard keys to stubs.
|
|
615
|
+
* 4. **Scheduler host** (`SchedulerHost`) — durable delayed jobs, cron, and
|
|
616
|
+
* at-least-once dispatch.
|
|
617
|
+
*
|
|
618
|
+
* Plus canonical binding projections (`KVNamespaceLike`, `R2BucketLike`,
|
|
619
|
+
* `QueueBindingLike`, `D1DatabaseLike`, `VectorizeIndexLike`, …) and the
|
|
620
|
+
* `PlatformCapabilities` matrix that codegen uses to tailor emitted types per
|
|
621
|
+
* target.
|
|
622
|
+
*
|
|
623
|
+
* This package is **zero-dependency** and safe on every runtime (browser,
|
|
624
|
+
* workerd, Node). It is intended to be the leaf dependency every other
|
|
625
|
+
* `@lunora/*` package can import without creating cycles.
|
|
626
|
+
*/
|
|
627
|
+
type ExecutionContextLike, type KVNamespaceLike, type KvGetOptions, type KvListKey, type KvNamespaceListResult, type KvNamespacePutOptions, type KvValue, type KvValueType, type MessageBatchLike, type MessageLike, type MessageSendRequestLike, NODE_CAPABILITIES, NOOP_EXECUTION_CONTEXT, type PlatformCapabilities, type QueueBindingLike, type QueueContentType, type QueueMessageLike, type QueueRetryOptions, type QueueSendBatchOptions, type QueueSendOptions, type QueueSendOptionsLike, type QueueSendRequestLike, type R2BucketLike, type R2MultipartUploadLike, type R2ObjectBodyLike, type R2ObjectLike, type R2RangeLike, type R2UploadedPartLike, type VectorMatchLike, type VectorMetric, type VectorRecordLike, type VectorizeDeleteMutation, type VectorizeIndexDetails, type VectorizeIndexLike, type VectorizeMatch, type VectorizeMatches, type VectorizeQueryOptions, type VectorizeUpsertMutation, type VectorizeVector };
|
package/dist/index.d.ts
CHANGED
|
@@ -596,4 +596,32 @@ declare const CLOUDFLARE_CAPABILITIES: PlatformCapabilities;
|
|
|
596
596
|
* Both are argued in detail in `plans/234-node-host-findings.md`.
|
|
597
597
|
*/
|
|
598
598
|
declare const NODE_CAPABILITIES: PlatformCapabilities;
|
|
599
|
-
export { type AnalyticsEngineDataPoint, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, CLOUDFLARE_CAPABILITIES, type Capability, type CapabilityLevel, type D1DatabaseLike, type D1PreparedStatementLike, type D1SessionLike,
|
|
599
|
+
export { type AnalyticsEngineDataPoint, type AnalyticsEngineDataPointLike, type AnalyticsEngineDatasetLike, CLOUDFLARE_CAPABILITIES, type Capability, type CapabilityLevel, type D1DatabaseLike, type D1PreparedStatementLike, type D1SessionLike,
|
|
600
|
+
/**
|
|
601
|
+
* `@lunora/platform` — provider-neutral host contracts for Lunora.
|
|
602
|
+
*
|
|
603
|
+
* This package defines the structural interfaces that separate the Lunora
|
|
604
|
+
* engine from any specific host (Cloudflare Workers, AWS, Rivet, Node, etc.).
|
|
605
|
+
* It contains **types and capability metadata only** — near-zero runtime code.
|
|
606
|
+
*
|
|
607
|
+
* The contracts fall into four groups:
|
|
608
|
+
*
|
|
609
|
+
* 1. **Shard host** (`ShardHost`) — single-writer execution, transactions,
|
|
610
|
+
* local SQL, alarms, and background continuation per shard key.
|
|
611
|
+
* 2. **Socket host** (`SocketHost`) — hibernated WebSocket subscriptions with
|
|
612
|
+
* durable attachments and tagged fan-out.
|
|
613
|
+
* 3. **Shard directory** (`ShardDirectory`) — deterministic placement and RPC
|
|
614
|
+
* dispatch from shard keys to stubs.
|
|
615
|
+
* 4. **Scheduler host** (`SchedulerHost`) — durable delayed jobs, cron, and
|
|
616
|
+
* at-least-once dispatch.
|
|
617
|
+
*
|
|
618
|
+
* Plus canonical binding projections (`KVNamespaceLike`, `R2BucketLike`,
|
|
619
|
+
* `QueueBindingLike`, `D1DatabaseLike`, `VectorizeIndexLike`, …) and the
|
|
620
|
+
* `PlatformCapabilities` matrix that codegen uses to tailor emitted types per
|
|
621
|
+
* target.
|
|
622
|
+
*
|
|
623
|
+
* This package is **zero-dependency** and safe on every runtime (browser,
|
|
624
|
+
* workerd, Node). It is intended to be the leaf dependency every other
|
|
625
|
+
* `@lunora/*` package can import without creating cycles.
|
|
626
|
+
*/
|
|
627
|
+
type ExecutionContextLike, type KVNamespaceLike, type KvGetOptions, type KvListKey, type KvNamespaceListResult, type KvNamespacePutOptions, type KvValue, type KvValueType, type MessageBatchLike, type MessageLike, type MessageSendRequestLike, NODE_CAPABILITIES, NOOP_EXECUTION_CONTEXT, type PlatformCapabilities, type QueueBindingLike, type QueueContentType, type QueueMessageLike, type QueueRetryOptions, type QueueSendBatchOptions, type QueueSendOptions, type QueueSendOptionsLike, type QueueSendRequestLike, type R2BucketLike, type R2MultipartUploadLike, type R2ObjectBodyLike, type R2ObjectLike, type R2RangeLike, type R2UploadedPartLike, type VectorMatchLike, type VectorMetric, type VectorRecordLike, type VectorizeDeleteMutation, type VectorizeIndexDetails, type VectorizeIndexLike, type VectorizeMatch, type VectorizeMatches, type VectorizeQueryOptions, type VectorizeUpsertMutation, type VectorizeVector };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{NOOP_EXECUTION_CONTEXT as E}from"./packem_shared/NOOP_EXECUTION_CONTEXT-YmXqH-jH.mjs";import{CLOUDFLARE_CAPABILITIES as O,NODE_CAPABILITIES as e}from"./packem_shared/CLOUDFLARE_CAPABILITIES-DXNmuRst.mjs";import{resolveShard as C}from"./packem_shared/resolveShard-
|
|
1
|
+
import{NOOP_EXECUTION_CONTEXT as E}from"./packem_shared/NOOP_EXECUTION_CONTEXT-YmXqH-jH.mjs";import{CLOUDFLARE_CAPABILITIES as O,NODE_CAPABILITIES as e}from"./packem_shared/CLOUDFLARE_CAPABILITIES-DXNmuRst.mjs";import{resolveShard as C}from"./packem_shared/resolveShard-BzKOUEO4.mjs";export{O as CLOUDFLARE_CAPABILITIES,e as NODE_CAPABILITIES,E as NOOP_EXECUTION_CONTEXT,C as resolveShard};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{DatabaseSync as J}from"node:sqlite";let S=0,x=0;const L=()=>(S+=1,`socket-${S}`),P=()=>(x+=1,`job-${x}`),z=s=>s===void 0?null:s,I=s=>typeof s=="string"?new TextEncoder().encode(s).buffer:s instanceof ArrayBuffer?s:ArrayBuffer.isView(s)?s.buffer.slice(s.byteOffset,s.byteOffset+s.byteLength):new ArrayBuffer(0),W=()=>{const s=new J(":memory:"),a={alarmAt:null,alarmTimeout:null,pending:[],running:!1},i=new Map,h=new Map,d=new Map,M={exec:(e,...t)=>{const n=s.prepare(e),r=t.map(z),l=e.trim().toLowerCase().startsWith("select")?n.all(...r):(n.run(...r),[]);return{[Symbol.iterator]:()=>l[Symbol.iterator](),one:()=>{if(l.length!==1)throw new Error(`expected exactly one row, got ${String(l.length)}`);return l[0]},toArray:()=>[...l]}}},F={all:async(e,t)=>s.prepare(e).all(...t),run:async(e,t)=>{const r=s.prepare(e).run(...t);return{rowsAffected:Number(r.changes)}}},g=()=>{if(a.running||a.pending.length===0)return;const e=a.pending.shift();e!==void 0&&(a.running=!0,e.function_().then(e.resolve,e.reject).finally(()=>{a.running=!1,g()}))},j=e=>new Promise((t,n)=>{a.pending.push({function_:e,reject:r=>{n(r)},resolve:r=>{t(r)}}),g()});let w=Promise.resolve();const y=async e=>{s.exec("BEGIN");try{const t=await e();return s.exec("COMMIT"),t}catch(t){throw s.exec("ROLLBACK"),t}},k={alarms:{delete:()=>{a.alarmAt=null,a.alarmTimeout!==null&&(clearTimeout(a.alarmTimeout),a.alarmTimeout=null)},get:()=>a.alarmAt,set:e=>{const t=typeof e=="number"?e:e.getTime();a.alarmAt=t,a.alarmTimeout!==null&&clearTimeout(a.alarmTimeout);const n=Math.max(0,t-Date.now());a.alarmTimeout=setTimeout(()=>{a.alarmAt=null,a.alarmTimeout=null},n)}},asyncSql:F,runSerialized:j,sql:M,transaction:e=>{const t=w.then(()=>y(e),()=>y(e));return w=t.then(()=>{},()=>{}),t},waitUntil:()=>{}},u=new WeakMap,p=e=>{const t={bufferedAmount:e.bufferedAmount,close:(n,r)=>{e.closed=!0},deserializeAttachment:()=>e.attachment,send:n=>{e.received.push(typeof n=="string"?n:I(n))},serializeAttachment:n=>{e.attachment=n,h.set(e.id,n)}};return e.handle=t,u.set(t,e.id),t},D={accept:(e,t,n)=>{const r=L(),c={attachment:t,bufferedAmount:0,closed:!1,raw:e,handle:null,id:r,received:[],tags:new Set(n)};return i.set(r,c),d.set(r,new Set(n)),t!==void 0&&h.set(r,t),p(c)},getSockets:e=>{const t=[...i.values()];return(e===void 0?t:t.filter(r=>r.tags.has(e))).map(r=>r.handle)},handleFor:e=>[...i.values()].find(t=>t.raw===e)?.handle,idFor:e=>{const t=u.get(e);if(t===void 0)throw new Error("reference host: idFor called with a handle this host never issued");return t},removeTag:(e,t)=>{const n=u.get(e)??"",r=i.get(n);r!==void 0&&(t===void 0?r.tags.clear():r.tags.delete(t),d.set(n,new Set(r.tags)))},setTag:(e,t)=>{const n=u.get(e)??"",r=i.get(n);r!==void 0&&(r.tags.add(t),d.set(n,new Set(r.tags)))}},b={get:e=>({fetch:async()=>new Response(String(e))}),getByName:e=>({fetch:async()=>new Response(e)}),idForName:e=>`shard:${e}`,jurisdiction:e=>b},m=new Map,B={delete:async e=>m.delete(e),get:async e=>m.get(e),list:async e=>{const t=e?.prefix??"",n=new Map;for(const[r,c]of m)r.startsWith(t)&&n.set(r,c);return n},put:async(e,t)=>{m.set(e,structuredClone(t))}},o=new Map,f=new Map,v=new Set,T=(e,t)=>({attempts:t.attempts,functionPath:t.functionPath,id:e,scheduledFor:t.scheduledFor});return{awaitAlarmFired:async e=>{await new Promise(t=>{setTimeout(t,Math.max(0,e-Date.now())+30)})},awaitJobDispatched:async e=>{const t=o.get(e);return t!==void 0&&await new Promise(n=>{setTimeout(n,Math.max(0,t.scheduledFor-Date.now())+30)}),v.has(e)},cleanup:()=>{s.close(),a.alarmTimeout!==null&&clearTimeout(a.alarmTimeout);for(const e of o.values())clearTimeout(e.timer)},directory:b,kv:B,readFrames:e=>(i.get(u.get(e)??"")?.received??[]).filter(t=>typeof t=="string"),restoreSocket:(e,t)=>{const n={attachment:t,bufferedAmount:0,closed:!1,handle:null,id:e,raw:void 0,received:[],tags:new Set(d.get(e))};return i.set(e,n),p(n)},scheduler:{cancel:async e=>{const t=o.get(e);return t===void 0?!1:(clearTimeout(t.timer),o.delete(e),!0)},cron:async()=>{},deadLetter:{list:async()=>[...f].map(([e,t])=>T(e,t)),requeue:async e=>{const t=f.get(e);return t===void 0?!1:(f.delete(e),o.set(e,{...t,attempts:0,timer:void 0}),!0)}},list:async()=>[...o].map(([e,t])=>T(e,t)),schedule:async(e,t,n)=>{const r=P();let c;n?.at===void 0?c=Date.now()+(n?.delayMs??0):c=typeof n.at=="number"?n.at:n.at.getTime();const l=Math.max(0,c-Date.now()),C=setTimeout(()=>{const A=o.get(r);A!==void 0&&(A.attempts+=1),v.add(r),o.delete(r)},l);return o.set(r,{args:t,attempts:0,functionPath:e,options:n??{},scheduledFor:c,timer:C}),{id:r,scheduledFor:c}}},simulateDeadLetter:async e=>{const t=o.get(e);return t===void 0?!1:(clearTimeout(t.timer),o.delete(e),f.set(e,{...t,attempts:(t.options.retry?.maxAttempts??5)+1,timer:void 0}),!0)},shard:k,simulateRecycle:()=>{i.clear()},socket:D}};export{W as createReferenceHost};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const a=(e,g,N)=>e.getByName!==void 0?e.getByName(g,N):e.get(e.idForName(g),N);export{a as resolveShard};
|
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.11",
|
|
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
|
-
import{DatabaseSync as R}from"node:sqlite";let b=0,S=0;const j=()=>(b+=1,`socket-${b}`),C=()=>(S+=1,`job-${S}`),q=n=>n===void 0?null:n,E=n=>typeof n=="string"?new TextEncoder().encode(n).buffer:n instanceof ArrayBuffer?n:ArrayBuffer.isView(n)?n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength):new ArrayBuffer(0),O=()=>{const n=new R(":memory:"),s={alarmAt:null,alarmTimeout:null,pending:[],running:!1},l=new Map,f=new Map,u=new Map,x={exec:(e,...t)=>{const a=n.prepare(e),r=t.map(q),o=e.trim().toLowerCase().startsWith("select")?a.all(...r):(a.run(...r),[]);return{[Symbol.iterator]:()=>o[Symbol.iterator](),one:()=>{if(o.length!==1)throw new Error(`expected exactly one row, got ${String(o.length)}`);return o[0]},toArray:()=>[...o]}}},M={all:async(e,t)=>n.prepare(e).all(...t),run:async(e,t)=>{const a=n.prepare(e).run(...t);return{rowsAffected:Number(a.changes)}}},h=()=>{if(s.running||s.pending.length===0)return;const e=s.pending.shift();e!==void 0&&(s.running=!0,e.function_().then(e.resolve,e.reject).finally(()=>{s.running=!1,h()}))},F=e=>new Promise((t,a)=>{s.pending.push({function_:e,reject:r=>{a(r)},resolve:r=>{t(r)}}),h()});let g=Promise.resolve();const p=async e=>{n.exec("BEGIN");try{const t=await e();return n.exec("COMMIT"),t}catch(t){throw n.exec("ROLLBACK"),t}},D={alarms:{delete:()=>{s.alarmAt=null,s.alarmTimeout!==null&&(clearTimeout(s.alarmTimeout),s.alarmTimeout=null)},get:()=>s.alarmAt,set:e=>{const t=typeof e=="number"?e:e.getTime();s.alarmAt=t,s.alarmTimeout!==null&&clearTimeout(s.alarmTimeout);const a=Math.max(0,t-Date.now());s.alarmTimeout=setTimeout(()=>{s.alarmAt=null,s.alarmTimeout=null},a)}},asyncSql:M,runSerialized:F,sql:x,transaction:e=>{const t=g.then(()=>p(e),()=>p(e));return g=t.then(()=>{},()=>{}),t},waitUntil:()=>{}},c=new WeakMap,w=e=>{const t={bufferedAmount:e.bufferedAmount,close:(a,r)=>{e.closed=!0},deserializeAttachment:()=>e.attachment,send:a=>{e.received.push(typeof a=="string"?a:E(a))},serializeAttachment:a=>{e.attachment=a,f.set(e.id,a)}};return e.handle=t,c.set(t,e.id),t},P={accept:(e,t,a)=>{const r=j(),o={attachment:t,bufferedAmount:0,closed:!1,raw:e,handle:null,id:r,received:[],tags:new Set(a)};return l.set(r,o),u.set(r,new Set(a)),t!==void 0&&f.set(r,t),w(o)},getSockets:e=>{const t=[...l.values()];return(e===void 0?t:t.filter(a=>a.tags.has(e))).map(a=>a.handle)},handleFor:e=>[...l.values()].find(t=>t.raw===e)?.handle,idFor:e=>{const t=c.get(e);if(t===void 0)throw new Error("reference host: idFor called with a handle this host never issued");return t},removeTag:(e,t)=>{const a=c.get(e)??"",r=l.get(a);r!==void 0&&(t===void 0?r.tags.clear():r.tags.delete(t),u.set(a,new Set(r.tags)))},setTag:(e,t)=>{const a=c.get(e)??"",r=l.get(a);r!==void 0&&(r.tags.add(t),u.set(a,new Set(r.tags)))}},y={get:e=>({fetch:async()=>new Response(String(e))}),getByName:e=>({fetch:async()=>new Response(e)}),idForName:e=>`shard:${e}`,jurisdiction:e=>y},d=new Map,k={delete:async e=>d.delete(e),get:async e=>d.get(e),list:async e=>{const t=e?.prefix??"",a=new Map;for(const[r,o]of d)r.startsWith(t)&&a.set(r,o);return a},put:async(e,t)=>{d.set(e,structuredClone(t))}},i=new Map,m=new Map,v=new Set,T=(e,t)=>({attempts:t.attempts,functionPath:t.functionPath,id:e,scheduledFor:t.scheduledFor});return{awaitAlarmFired:async e=>{await new Promise(t=>{setTimeout(t,Math.max(0,e-Date.now())+30)})},awaitJobDispatched:async e=>{const t=i.get(e);return t!==void 0&&await new Promise(a=>{setTimeout(a,Math.max(0,t.scheduledFor-Date.now())+30)}),v.has(e)},cleanup:()=>{n.close(),s.alarmTimeout!==null&&clearTimeout(s.alarmTimeout);for(const e of i.values())clearTimeout(e.timer)},directory:y,kv:k,readFrames:e=>(l.get(c.get(e)??"")?.received??[]).filter(t=>typeof t=="string"),restoreSocket:(e,t)=>{const a={attachment:t,bufferedAmount:0,closed:!1,handle:null,id:e,raw:void 0,received:[],tags:new Set(u.get(e))};return l.set(e,a),w(a)},scheduler:{cancel:async e=>{const t=i.get(e);return t===void 0?!1:(clearTimeout(t.timer),i.delete(e),!0)},cron:async()=>{},deadLetter:{list:async()=>[...m].map(([e,t])=>T(e,t)),requeue:async e=>{const t=m.get(e);return t===void 0?!1:(m.delete(e),i.set(e,{...t,attempts:0,timer:void 0}),!0)}},list:async()=>[...i].map(([e,t])=>T(e,t)),schedule:async(e,t,a)=>{const r=C();let o;a?.at===void 0?o=Date.now()+(a?.delayMs??0):o=typeof a.at=="number"?a.at:a.at.getTime();const B=Math.max(0,o-Date.now()),L=setTimeout(()=>{const A=i.get(r);A!==void 0&&(A.attempts+=1),v.add(r),i.delete(r)},B);return i.set(r,{args:t,attempts:0,functionPath:e,options:a??{},scheduledFor:o,timer:L}),{id:r,scheduledFor:o}}},simulateDeadLetter:async e=>{const t=i.get(e);return t===void 0?!1:(clearTimeout(t.timer),i.delete(e),m.set(e,{...t,attempts:(t.options.retry?.maxAttempts??5)+1,timer:void 0}),!0)},shard:D,simulateRecycle:()=>{l.clear()},socket:P}};export{O as createReferenceHost};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
const a=(e,o,t)=>e.getByName!==void 0?e.getByName(o,t):e.get(e.idForName(o),t);export{a as resolveShard};
|