@lunora/platform-node 1.0.0-alpha.155 → 1.0.0-alpha.157

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -4,6 +4,7 @@ import Database from 'better-sqlite3';
4
4
  import { ShardKvStore, QueueBindingLike, MessageBatchLike, SchedulerHost, ShardHost, SocketHost, DirectShardDirectory, PlatformCapabilities, ShardDirectory, R2BucketLike, SocketHandle } from '@lunora/platform';
5
5
  import { WorkflowBindingLike } from '@lunora/workflow';
6
6
  import { WorkflowRuntime, WorkflowStore } from '@visulima/workflow';
7
+ import { ShardNamespaceLike } from '@lunora/runtime';
7
8
  /** One function call the route has authenticated and parsed. */
8
9
  interface NodeDispatchInvocation {
9
10
  args: Record<string, unknown>;
@@ -97,6 +98,32 @@ interface NodeGlobalStore {
97
98
  * which is the whole point of the dialect seam.
98
99
  */
99
100
  declare const createNodeGlobalStore: (options?: NodeGlobalStoreOptions) => NodeGlobalStore;
101
+ /** Serve `options.fetch` over `node:http`. Resolves once the server is listening. */
102
+ declare const createNodeHttpServer: (options: NodeHttpServerOptions) => Promise<NodeHttpServer>;
103
+ /** Options for {@link createNodeHttpServer}. */
104
+ interface NodeHttpServerOptions {
105
+ /** The app worker's `fetch`, already bound to its env and context. */
106
+ fetch: (request: Request) => Promise<Response> | Response;
107
+ /** Host to bind. Defaults to the loopback interface. */
108
+ host?: string;
109
+ /**
110
+ * The public base URL the app is served at, e.g. `https://app.example`. Every request URL uses its origin, so origin-sensitive
111
+ * behaviour (CSRF, WebSocket origin checks, cookie rules) sees the public scheme behind a TLS proxy. The client's `Host` header is
112
+ * never used. Defaults to the bound `http://host:port`, which is only allowed for a loopback `host`: binding a non-loopback address
113
+ * (e.g. `0.0.0.0` or a LAN IP) without `origin` makes {@link createNodeHttpServer} reject, because browsers would send that address
114
+ * as `Origin` and fail the CSRF check. Set `origin` to the URL users open.
115
+ */
116
+ origin?: string;
117
+ /** Port to bind. `0` (the default) picks a free ephemeral port. */
118
+ port?: number;
119
+ }
120
+ /** A listening server. */
121
+ interface NodeHttpServer {
122
+ /** Stop accepting connections and end every open one, streams included. */
123
+ close: () => Promise<void>;
124
+ /** The `http://host:port` origin the server answers on. */
125
+ origin: string;
126
+ }
100
127
  /** Build a `ShardKvStore` over a `better-sqlite3` table in the shard's database. */
101
128
  declare const createNodeShardKvStore: (database: Database.Database) => ShardKvStore;
102
129
  /** Options for {@link createNodeQueueHost}. */
@@ -462,8 +489,8 @@ interface NodePlatform<Queues extends Record<string, {
462
489
  * The scheduler dispatch route, served in-process: `Request` in, shard
463
490
  * response out, no listener. It answers the path `@lunora/dispatch` and
464
491
  * `@lunora/scheduler` POST to. Pass it to `dispatchQueueBatch` as
465
- * `fetchImpl` so a queue handler's `message.run` / `ctx.run` reaches the
466
- * shard; the env you pass there must carry `LUNORA_ORIGIN_URL` and the same
492
+ * `queueFetchImpl` on `createWorker` so a queue handler's `message.run` /
493
+ * `ctx.run` reaches the shard; the env you pass there must carry `LUNORA_ORIGIN_URL` and the same
467
494
  * `LUNORA_ADMIN_TOKEN` as `dispatch.adminToken`. Every request is refused
468
495
  * unless `dispatch` supplies a secret.
469
496
  */
@@ -570,9 +597,12 @@ type NodePlatformOptions<Queues extends Record<string, {
570
597
  */
571
598
  objectStorageDirectory?: string;
572
599
  /**
573
- * Deliver one assembled queue batch — wire this to `dispatchQueueBatch`.
574
- * Required alongside `queues`; without it the messages would be stored
575
- * and never consumed.
600
+ * Deliver one assembled queue batch — wire this to the app worker's `queue`
601
+ * export from `createWorker`, not to bare `dispatchQueueBatch`. That export
602
+ * is the only path that hands the consumer `ctx.live`, and it takes
603
+ * `queueFetchImpl` (this platform's `dispatch`, wrapped as a `fetch`) so
604
+ * `ctx.run` reaches the shard here rather than `LUNORA_ORIGIN_URL`. Required
605
+ * alongside `queues`; without it the messages would be stored and never consumed.
576
606
  */
577
607
  onQueueBatch?: NodeQueueHostOptions<Queues>["onBatch"];
578
608
  /** The app's `defineQueue` results, keyed by export name. Omit when the app declares no queues. */
@@ -604,6 +634,15 @@ interface NodeR2BucketOptions {
604
634
  * maps directly onto a file operation.
605
635
  */
606
636
  declare const createNodeR2Bucket: (options: NodeR2BucketOptions) => R2BucketLike;
637
+ /**
638
+ * Present the node shard registry as the `ShardNamespaceLike` a worker is built
639
+ * over (`createWorker({ shardDO })`).
640
+ *
641
+ * The registry names its id derivation `idForName`, which is the
642
+ * `ShardDirectory` spelling; the worker reads Cloudflare's `idFromName`. Both
643
+ * return the key itself here, so a stub from either route reaches the same shard.
644
+ */
645
+ declare const createNodeShardNamespace: (directory: DirectShardDirectory) => ShardNamespaceLike;
607
646
  /**
608
647
  * The `DurableObjectState` subset `ShardDO` consumes.
609
648
  *
@@ -709,7 +748,11 @@ export { NODE_SCHEDULER_DISPATCH_PATH,
709
748
  *
710
749
  * **Still missing:** a dev server. There is no `lunora dev --target node`, and
711
750
  * nothing here owns a timer, so queue delivery is driven by an explicit
712
- * `poll()`. Also absent are the Cloudflare product bindings with no local
751
+ * `poll()`. What exists is the listener a dev server would sit on:
752
+ * `createNodeHttpServer` serves an app worker's `fetch` over `node:http`,
753
+ * streaming the response body, and `createNodeShardNamespace` presents the
754
+ * registry as the `shardDO` namespace that worker routes to. Also absent are
755
+ * the Cloudflare product bindings with no local
713
756
  * equivalent — Vectorize, Workers AI, Browser Rendering, Containers, Analytics
714
757
  * Engine, Pipelines, Secrets Store, Hyperdrive.
715
758
  *
@@ -751,7 +794,11 @@ type NodeDispatchInvocation,
751
794
  *
752
795
  * **Still missing:** a dev server. There is no `lunora dev --target node`, and
753
796
  * nothing here owns a timer, so queue delivery is driven by an explicit
754
- * `poll()`. Also absent are the Cloudflare product bindings with no local
797
+ * `poll()`. What exists is the listener a dev server would sit on:
798
+ * `createNodeHttpServer` serves an app worker's `fetch` over `node:http`,
799
+ * streaming the response body, and `createNodeShardNamespace` presents the
800
+ * registry as the `shardDO` namespace that worker routes to. Also absent are
801
+ * the Cloudflare product bindings with no local
755
802
  * equivalent — Vectorize, Workers AI, Browser Rendering, Containers, Analytics
756
803
  * Engine, Pipelines, Secrets Store, Hyperdrive.
757
804
  *
@@ -762,4 +809,4 @@ type NodeDispatchInvocation,
762
809
  * server, which is why both statements above hold at once. See
763
810
  * `plans/234-node-host-findings.md`.
764
811
  */
765
- type NodeDispatchRouteOptions, type NodeGlobalContextDatabaseOptions, type NodeGlobalStore, type NodeGlobalStoreOptions, type NodePlatform, type NodePlatformDispatchOptions, type NodePlatformOptions, type NodeProfileHandler, type NodeProfileHandlerOptions, type NodeQueueHost, type NodeQueueHostOptions, type NodeR2BucketOptions, type NodeSchedulerHost, type NodeSchedulerHostOptions, type NodeShard, type NodeShardHostOptions, type NodeShardRegistry, type NodeShardRegistryOptions, type NodeShardState, type NodeSocketHost, type NodeWorkflowHost, type NodeWorkflowHostOptions, createNodeDispatchRoute, createNodeGlobalStore, createNodePlatform, createNodeProfileHandler, createNodeQueueHost, createNodeR2Bucket, createNodeSchedulerHost, createNodeShardHost, createNodeShardKvStore, createNodeShardRegistry, createNodeShardState, createNodeSocketHost, createNodeSqlExec, createNodeWorkflowHost, createNodeWorkflowStore };
812
+ type NodeDispatchRouteOptions, type NodeGlobalContextDatabaseOptions, type NodeGlobalStore, type NodeGlobalStoreOptions, type NodeHttpServer, type NodeHttpServerOptions, type NodePlatform, type NodePlatformDispatchOptions, type NodePlatformOptions, type NodeProfileHandler, type NodeProfileHandlerOptions, type NodeQueueHost, type NodeQueueHostOptions, type NodeR2BucketOptions, type NodeSchedulerHost, type NodeSchedulerHostOptions, type NodeShard, type NodeShardHostOptions, type NodeShardRegistry, type NodeShardRegistryOptions, type NodeShardState, type NodeSocketHost, type NodeWorkflowHost, type NodeWorkflowHostOptions, createNodeDispatchRoute, createNodeGlobalStore, createNodeHttpServer, createNodePlatform, createNodeProfileHandler, createNodeQueueHost, createNodeR2Bucket, createNodeSchedulerHost, createNodeShardHost, createNodeShardKvStore, createNodeShardNamespace, createNodeShardRegistry, createNodeShardState, createNodeSocketHost, createNodeSqlExec, createNodeWorkflowHost, createNodeWorkflowStore };
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import Database from 'better-sqlite3';
4
4
  import { ShardKvStore, QueueBindingLike, MessageBatchLike, SchedulerHost, ShardHost, SocketHost, DirectShardDirectory, PlatformCapabilities, ShardDirectory, R2BucketLike, SocketHandle } from '@lunora/platform';
5
5
  import { WorkflowBindingLike } from '@lunora/workflow';
6
6
  import { WorkflowRuntime, WorkflowStore } from '@visulima/workflow';
7
+ import { ShardNamespaceLike } from '@lunora/runtime';
7
8
  /** One function call the route has authenticated and parsed. */
8
9
  interface NodeDispatchInvocation {
9
10
  args: Record<string, unknown>;
@@ -97,6 +98,32 @@ interface NodeGlobalStore {
97
98
  * which is the whole point of the dialect seam.
98
99
  */
99
100
  declare const createNodeGlobalStore: (options?: NodeGlobalStoreOptions) => NodeGlobalStore;
101
+ /** Serve `options.fetch` over `node:http`. Resolves once the server is listening. */
102
+ declare const createNodeHttpServer: (options: NodeHttpServerOptions) => Promise<NodeHttpServer>;
103
+ /** Options for {@link createNodeHttpServer}. */
104
+ interface NodeHttpServerOptions {
105
+ /** The app worker's `fetch`, already bound to its env and context. */
106
+ fetch: (request: Request) => Promise<Response> | Response;
107
+ /** Host to bind. Defaults to the loopback interface. */
108
+ host?: string;
109
+ /**
110
+ * The public base URL the app is served at, e.g. `https://app.example`. Every request URL uses its origin, so origin-sensitive
111
+ * behaviour (CSRF, WebSocket origin checks, cookie rules) sees the public scheme behind a TLS proxy. The client's `Host` header is
112
+ * never used. Defaults to the bound `http://host:port`, which is only allowed for a loopback `host`: binding a non-loopback address
113
+ * (e.g. `0.0.0.0` or a LAN IP) without `origin` makes {@link createNodeHttpServer} reject, because browsers would send that address
114
+ * as `Origin` and fail the CSRF check. Set `origin` to the URL users open.
115
+ */
116
+ origin?: string;
117
+ /** Port to bind. `0` (the default) picks a free ephemeral port. */
118
+ port?: number;
119
+ }
120
+ /** A listening server. */
121
+ interface NodeHttpServer {
122
+ /** Stop accepting connections and end every open one, streams included. */
123
+ close: () => Promise<void>;
124
+ /** The `http://host:port` origin the server answers on. */
125
+ origin: string;
126
+ }
100
127
  /** Build a `ShardKvStore` over a `better-sqlite3` table in the shard's database. */
101
128
  declare const createNodeShardKvStore: (database: Database.Database) => ShardKvStore;
102
129
  /** Options for {@link createNodeQueueHost}. */
@@ -462,8 +489,8 @@ interface NodePlatform<Queues extends Record<string, {
462
489
  * The scheduler dispatch route, served in-process: `Request` in, shard
463
490
  * response out, no listener. It answers the path `@lunora/dispatch` and
464
491
  * `@lunora/scheduler` POST to. Pass it to `dispatchQueueBatch` as
465
- * `fetchImpl` so a queue handler's `message.run` / `ctx.run` reaches the
466
- * shard; the env you pass there must carry `LUNORA_ORIGIN_URL` and the same
492
+ * `queueFetchImpl` on `createWorker` so a queue handler's `message.run` /
493
+ * `ctx.run` reaches the shard; the env you pass there must carry `LUNORA_ORIGIN_URL` and the same
467
494
  * `LUNORA_ADMIN_TOKEN` as `dispatch.adminToken`. Every request is refused
468
495
  * unless `dispatch` supplies a secret.
469
496
  */
@@ -570,9 +597,12 @@ type NodePlatformOptions<Queues extends Record<string, {
570
597
  */
571
598
  objectStorageDirectory?: string;
572
599
  /**
573
- * Deliver one assembled queue batch — wire this to `dispatchQueueBatch`.
574
- * Required alongside `queues`; without it the messages would be stored
575
- * and never consumed.
600
+ * Deliver one assembled queue batch — wire this to the app worker's `queue`
601
+ * export from `createWorker`, not to bare `dispatchQueueBatch`. That export
602
+ * is the only path that hands the consumer `ctx.live`, and it takes
603
+ * `queueFetchImpl` (this platform's `dispatch`, wrapped as a `fetch`) so
604
+ * `ctx.run` reaches the shard here rather than `LUNORA_ORIGIN_URL`. Required
605
+ * alongside `queues`; without it the messages would be stored and never consumed.
576
606
  */
577
607
  onQueueBatch?: NodeQueueHostOptions<Queues>["onBatch"];
578
608
  /** The app's `defineQueue` results, keyed by export name. Omit when the app declares no queues. */
@@ -604,6 +634,15 @@ interface NodeR2BucketOptions {
604
634
  * maps directly onto a file operation.
605
635
  */
606
636
  declare const createNodeR2Bucket: (options: NodeR2BucketOptions) => R2BucketLike;
637
+ /**
638
+ * Present the node shard registry as the `ShardNamespaceLike` a worker is built
639
+ * over (`createWorker({ shardDO })`).
640
+ *
641
+ * The registry names its id derivation `idForName`, which is the
642
+ * `ShardDirectory` spelling; the worker reads Cloudflare's `idFromName`. Both
643
+ * return the key itself here, so a stub from either route reaches the same shard.
644
+ */
645
+ declare const createNodeShardNamespace: (directory: DirectShardDirectory) => ShardNamespaceLike;
607
646
  /**
608
647
  * The `DurableObjectState` subset `ShardDO` consumes.
609
648
  *
@@ -709,7 +748,11 @@ export { NODE_SCHEDULER_DISPATCH_PATH,
709
748
  *
710
749
  * **Still missing:** a dev server. There is no `lunora dev --target node`, and
711
750
  * nothing here owns a timer, so queue delivery is driven by an explicit
712
- * `poll()`. Also absent are the Cloudflare product bindings with no local
751
+ * `poll()`. What exists is the listener a dev server would sit on:
752
+ * `createNodeHttpServer` serves an app worker's `fetch` over `node:http`,
753
+ * streaming the response body, and `createNodeShardNamespace` presents the
754
+ * registry as the `shardDO` namespace that worker routes to. Also absent are
755
+ * the Cloudflare product bindings with no local
713
756
  * equivalent — Vectorize, Workers AI, Browser Rendering, Containers, Analytics
714
757
  * Engine, Pipelines, Secrets Store, Hyperdrive.
715
758
  *
@@ -751,7 +794,11 @@ type NodeDispatchInvocation,
751
794
  *
752
795
  * **Still missing:** a dev server. There is no `lunora dev --target node`, and
753
796
  * nothing here owns a timer, so queue delivery is driven by an explicit
754
- * `poll()`. Also absent are the Cloudflare product bindings with no local
797
+ * `poll()`. What exists is the listener a dev server would sit on:
798
+ * `createNodeHttpServer` serves an app worker's `fetch` over `node:http`,
799
+ * streaming the response body, and `createNodeShardNamespace` presents the
800
+ * registry as the `shardDO` namespace that worker routes to. Also absent are
801
+ * the Cloudflare product bindings with no local
755
802
  * equivalent — Vectorize, Workers AI, Browser Rendering, Containers, Analytics
756
803
  * Engine, Pipelines, Secrets Store, Hyperdrive.
757
804
  *
@@ -762,4 +809,4 @@ type NodeDispatchInvocation,
762
809
  * server, which is why both statements above hold at once. See
763
810
  * `plans/234-node-host-findings.md`.
764
811
  */
765
- type NodeDispatchRouteOptions, type NodeGlobalContextDatabaseOptions, type NodeGlobalStore, type NodeGlobalStoreOptions, type NodePlatform, type NodePlatformDispatchOptions, type NodePlatformOptions, type NodeProfileHandler, type NodeProfileHandlerOptions, type NodeQueueHost, type NodeQueueHostOptions, type NodeR2BucketOptions, type NodeSchedulerHost, type NodeSchedulerHostOptions, type NodeShard, type NodeShardHostOptions, type NodeShardRegistry, type NodeShardRegistryOptions, type NodeShardState, type NodeSocketHost, type NodeWorkflowHost, type NodeWorkflowHostOptions, createNodeDispatchRoute, createNodeGlobalStore, createNodePlatform, createNodeProfileHandler, createNodeQueueHost, createNodeR2Bucket, createNodeSchedulerHost, createNodeShardHost, createNodeShardKvStore, createNodeShardRegistry, createNodeShardState, createNodeSocketHost, createNodeSqlExec, createNodeWorkflowHost, createNodeWorkflowStore };
812
+ type NodeDispatchRouteOptions, type NodeGlobalContextDatabaseOptions, type NodeGlobalStore, type NodeGlobalStoreOptions, type NodeHttpServer, type NodeHttpServerOptions, type NodePlatform, type NodePlatformDispatchOptions, type NodePlatformOptions, type NodeProfileHandler, type NodeProfileHandlerOptions, type NodeQueueHost, type NodeQueueHostOptions, type NodeR2BucketOptions, type NodeSchedulerHost, type NodeSchedulerHostOptions, type NodeShard, type NodeShardHostOptions, type NodeShardRegistry, type NodeShardRegistryOptions, type NodeShardState, type NodeSocketHost, type NodeWorkflowHost, type NodeWorkflowHostOptions, createNodeDispatchRoute, createNodeGlobalStore, createNodeHttpServer, createNodePlatform, createNodeProfileHandler, createNodeQueueHost, createNodeR2Bucket, createNodeSchedulerHost, createNodeShardHost, createNodeShardKvStore, createNodeShardNamespace, createNodeShardRegistry, createNodeShardState, createNodeSocketHost, createNodeSqlExec, createNodeWorkflowHost, createNodeWorkflowStore };
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{NODE_SCHEDULER_DISPATCH_PATH as r,createNodeDispatchRoute as t}from"./packem_shared/NODE_SCHEDULER_DISPATCH_PATH-CNBAtztq.mjs";import{createNodeGlobalStore as d,createNodeSqlExec as c}from"./packem_shared/createNodeGlobalStore-DhHyIT8I.mjs";import{createNodeShardKvStore as N}from"./packem_shared/createNodeShardKvStore-DVi1OsvH.mjs";import{createNodePlatform as p}from"./packem_shared/createNodePlatform-D-vCAW6Z.mjs";import{createNodeProfileHandler as S}from"./packem_shared/createNodeProfileHandler-MX5xKPg0.mjs";import{createNodeQueueHost as H}from"./packem_shared/createNodeQueueHost-CttDb5sY.mjs";import{createNodeR2Bucket as h}from"./packem_shared/createNodeR2Bucket-6I2HobcB.mjs";import{createNodeSchedulerHost as k}from"./packem_shared/createNodeSchedulerHost-BIJ4H38-.mjs";import{createNodeShardHost as E}from"./packem_shared/createNodeShardHost-B6mt1S-h.mjs";import{createNodeShardRegistry as R}from"./packem_shared/createNodeShardRegistry-q-46Oj8M.mjs";import{createNodeShardState as _}from"./packem_shared/createNodeShardState-D6XMWbFs.mjs";import{createNodeSocketHost as A}from"./packem_shared/createNodeSocketHost-DQs85G7W.mjs";import{createNodeWorkflowHost as T}from"./packem_shared/createNodeWorkflowHost-B76TKiw_.mjs";import{createNodeWorkflowStore as b}from"./packem_shared/createNodeWorkflowStore-CL7RYP6H.mjs";export{r as NODE_SCHEDULER_DISPATCH_PATH,t as createNodeDispatchRoute,d as createNodeGlobalStore,p as createNodePlatform,S as createNodeProfileHandler,H as createNodeQueueHost,h as createNodeR2Bucket,k as createNodeSchedulerHost,E as createNodeShardHost,N as createNodeShardKvStore,R as createNodeShardRegistry,_ as createNodeShardState,A as createNodeSocketHost,c as createNodeSqlExec,T as createNodeWorkflowHost,b as createNodeWorkflowStore};
1
+ import{NODE_SCHEDULER_DISPATCH_PATH as r,createNodeDispatchRoute as t}from"./packem_shared/NODE_SCHEDULER_DISPATCH_PATH-CNBAtztq.mjs";import{createNodeGlobalStore as d,createNodeSqlExec as c}from"./packem_shared/createNodeGlobalStore-DhHyIT8I.mjs";import{createNodeHttpServer as p}from"./packem_shared/createNodeHttpServer-BmIbUCl7.mjs";import{createNodeShardKvStore as m}from"./packem_shared/createNodeShardKvStore-DVi1OsvH.mjs";import{createNodePlatform as S}from"./packem_shared/createNodePlatform-bL7EdY_5.mjs";import{createNodeProfileHandler as l}from"./packem_shared/createNodeProfileHandler-MX5xKPg0.mjs";import{createNodeQueueHost as h}from"./packem_shared/createNodeQueueHost-CttDb5sY.mjs";import{createNodeR2Bucket as k}from"./packem_shared/createNodeR2Bucket-6I2HobcB.mjs";import{createNodeSchedulerHost as E}from"./packem_shared/createNodeSchedulerHost-BIJ4H38-.mjs";import{createNodeShardHost as R}from"./packem_shared/createNodeShardHost-B6mt1S-h.mjs";import{createNodeShardNamespace as _}from"./packem_shared/createNodeShardNamespace-BgQjDv7V.mjs";import{createNodeShardRegistry as w}from"./packem_shared/createNodeShardRegistry-q-46Oj8M.mjs";import{createNodeShardState as C}from"./packem_shared/createNodeShardState-D6XMWbFs.mjs";import{createNodeSocketHost as W}from"./packem_shared/createNodeSocketHost-DQs85G7W.mjs";import{createNodeWorkflowHost as g}from"./packem_shared/createNodeWorkflowHost-B76TKiw_.mjs";import{createNodeWorkflowStore as q}from"./packem_shared/createNodeWorkflowStore-CL7RYP6H.mjs";export{r as NODE_SCHEDULER_DISPATCH_PATH,t as createNodeDispatchRoute,d as createNodeGlobalStore,p as createNodeHttpServer,S as createNodePlatform,l as createNodeProfileHandler,h as createNodeQueueHost,k as createNodeR2Bucket,E as createNodeSchedulerHost,R as createNodeShardHost,m as createNodeShardKvStore,_ as createNodeShardNamespace,w as createNodeShardRegistry,C as createNodeShardState,W as createNodeSocketHost,c as createNodeSqlExec,g as createNodeWorkflowHost,q as createNodeWorkflowStore};
@@ -0,0 +1 @@
1
+ import{createServer as f}from"node:http";const i=e=>e.includes(":")&&!e.startsWith("[")?`[${e}]`:e,p=e=>{const t=new URL(e);if(t.protocol!=="http:"&&t.protocol!=="https:")throw new RangeError(`origin must be an http or https URL, got scheme ${t.protocol}`);return t.origin},w=/^(?:127\.\d+\.\d+\.\d+|::1|localhost)$/,u=1048576,b=5e3,y=(e,t)=>{const n=e[Symbol.asyncIterator]();return new ReadableStream({cancel(){if(t.headersSent){e.destroy();return}if(e.complete){e.resume();return}let s=0,c=!1;const o=()=>{if(!e.complete){if(c=!0,t.writableFinished){e.destroy();return}e.removeAllListeners("readable"),e.pause()}},r=setTimeout(o,b),a=()=>{clearTimeout(r)};t.once("finish",()=>{c&&e.destroy()}),e.once("end",a),e.once("close",a),e.removeAllListeners("readable"),e.on("readable",()=>{let d=e.read();for(;d!==null;){if(s+=d.byteLength,s>u){o();return}d=e.read()}})},async pull(s){const c=await n.next();c.done?s.close():s.enqueue(c.value)}})},v=(e,t)=>{const n=new URL(e??"/",t),s=new URL(t);return s.pathname=n.pathname,s.search=n.search,s},R=(e,t,n,s)=>{const c=new Headers;for(const[a,d]of Object.entries(e.headers))if(Array.isArray(d))for(const l of d)c.append(a,l);else d!==void 0&&c.set(a,d);const o=e.method??"GET",r={headers:c,method:o,signal:s};return o!=="GET"&&o!=="HEAD"&&(r.body=y(e,t),r.duplex="half"),new Request(v(e.url,n),r)},S=e=>e.destroyed,E=async(e,t)=>{if(t.on("error",()=>{t.destroy()}),S(t)){await e.body?.cancel().catch(()=>{});return}const n={};e.headers.forEach((r,a)=>{a!=="set-cookie"&&(n[a]=r)});const s=e.headers.getSetCookie();if(s.length>0&&(n["set-cookie"]=s),t.writeHead(e.status,n),e.body===null){t.end();return}const c=e.body.getReader();t.once("close",()=>{c.cancel().catch(()=>{})});let o=await c.read();for(;!o.done&&!t.destroyed;)t.write(o.value)||await new Promise(r=>{const a=()=>{t.off("drain",a),t.off("close",a),r()};t.on("drain",a),t.on("close",a)}),o=await c.read();t.end()},H=async e=>{const t=e.host??"127.0.0.1",n=e.origin===void 0?void 0:p(e.origin);if(n===void 0&&!w.test(t))throw new Error(`createNodeHttpServer: host "${t}" is not a loopback address, so set origin to the public URL (e.g. https://app.example). Without it, browser requests fail the CSRF origin check and signed URLs point at the bound address.`);const s=f();s.on("request",(o,r)=>{const a=n??`http://${i(t)}:${String(s.address().port)}`,d=new AbortController;r.once("close",()=>{r.writableFinished||d.abort()});let l;try{l=R(o,r,a,d.signal)}catch{r.writeHead(400,{"content-type":"text/plain; charset=utf-8"}),r.end("Bad Request");return}Promise.resolve().then(()=>e.fetch(l)).then(h=>E(h,r),()=>{r.headersSent||r.writeHead(500,{"content-type":"text/plain; charset=utf-8"}),r.end("Internal Server Error")}).catch(()=>{r.destroy()})}),await new Promise((o,r)=>{s.once("error",r),s.listen(e.port??0,t,()=>{s.off("error",r),o()})});const{port:c}=s.address();return{close:()=>new Promise((o,r)=>{s.closeAllConnections(),s.close(a=>{a===void 0?o():r(a)})}),origin:`http://${i(t)}:${String(c)}`}};export{H as createNodeHttpServer};
@@ -1 +1 @@
1
- import{LunoraError as k}from"@lunora/errors";import{NODE_CAPABILITIES as N}from"@lunora/platform";import{createNodeDispatchRoute as b,createShardInvoke as g}from"./NODE_SCHEDULER_DISPATCH_PATH-CNBAtztq.mjs";import{createNodeGlobalStore as y}from"./createNodeGlobalStore-DhHyIT8I.mjs";import{createNodeShardKvStore as R}from"./createNodeShardKvStore-DVi1OsvH.mjs";import{createNodeQueueHost as T}from"./createNodeQueueHost-CttDb5sY.mjs";import{createNodeR2Bucket as q}from"./createNodeR2Bucket-6I2HobcB.mjs";import{createNodeSchedulerHost as B}from"./createNodeSchedulerHost-BIJ4H38-.mjs";import{createNodeShardHost as I}from"./createNodeShardHost-B6mt1S-h.mjs";import{createNodeShardRegistry as _}from"./createNodeShardRegistry-q-46Oj8M.mjs";import{createNodeSocketHost as D}from"./createNodeSocketHost-DQs85G7W.mjs";import{createNodeWorkflowHost as H}from"./createNodeWorkflowHost-B76TKiw_.mjs";import{createNodeWorkflowStore as P}from"./createNodeWorkflowStore-CL7RYP6H.mjs";const z=(e={})=>{const o=[],{database:r,dispose:u,drain:i,host:l}=I(e);o.push(u);try{const a=R(r),t=_(e);o.push(()=>{t.close()});const{directory:d}=t,m=b({adminToken:e.dispatch?.adminToken,defaultShardKey:"__root__",invoke:g(d,e.onFetch!==void 0),schedulerSecret:e.dispatch?.schedulerSecret}),{socket:f}=D(r),{dispose:n,scheduler:p}=B(r,e);o.push(n);const S=e.queues===void 0?void 0:T(r,{onBatch:e.onQueueBatch??(()=>{throw new k("VALIDATION_ERROR","@lunora/platform-node: createNodePlatform was given `queues` without `onQueueBatch`, so a delivered batch has nowhere to go — pass dispatchQueueBatch from @lunora/queue")}),queues:e.queues}),v=e.objectStorageDirectory===void 0?void 0:q({directory:e.objectStorageDirectory}),c=e.globalTablesPath===void 0?void 0:y({path:e.globalTablesPath});c!==void 0&&o.push(()=>{c.dispose()});const w=e.workflows===void 0?void 0:H({store:P(r),workflows:e.workflows}),h=()=>{for(const s of o.toReversed())s()};return{capabilities:N,close:h,directory:d,dispatch:s=>m(s),drain:i,globalTables:c,kv:a,objectStorage:v,queues:S,scheduler:p,shard:l,sockets:f,workflows:w,[Symbol.dispose]:h}}catch(a){for(const t of o.toReversed())try{t()}catch{}throw a}};export{z as createNodePlatform};
1
+ import{LunoraError as k}from"@lunora/errors";import{NODE_CAPABILITIES as N}from"@lunora/platform";import{createNodeDispatchRoute as b,createShardInvoke as g}from"./NODE_SCHEDULER_DISPATCH_PATH-CNBAtztq.mjs";import{createNodeGlobalStore as y}from"./createNodeGlobalStore-DhHyIT8I.mjs";import{createNodeShardKvStore as R}from"./createNodeShardKvStore-DVi1OsvH.mjs";import{createNodeQueueHost as T}from"./createNodeQueueHost-CttDb5sY.mjs";import{createNodeR2Bucket as q}from"./createNodeR2Bucket-6I2HobcB.mjs";import{createNodeSchedulerHost as I}from"./createNodeSchedulerHost-BIJ4H38-.mjs";import{createNodeShardHost as _}from"./createNodeShardHost-B6mt1S-h.mjs";import{createNodeShardRegistry as B}from"./createNodeShardRegistry-q-46Oj8M.mjs";import{createNodeSocketHost as D}from"./createNodeSocketHost-DQs85G7W.mjs";import{createNodeWorkflowHost as H}from"./createNodeWorkflowHost-B76TKiw_.mjs";import{createNodeWorkflowStore as P}from"./createNodeWorkflowStore-CL7RYP6H.mjs";const z=(e={})=>{const o=[],{database:r,dispose:i,drain:u,host:m}=_(e);o.push(i);try{const c=R(r),t=B(e);o.push(()=>{t.close()});const{directory:d}=t,l=b({adminToken:e.dispatch?.adminToken,defaultShardKey:"__root__",invoke:g(d,e.onFetch!==void 0),schedulerSecret:e.dispatch?.schedulerSecret}),{socket:f}=D(r),{dispose:n,scheduler:p}=I(r,e);o.push(n);const S=e.queues===void 0?void 0:T(r,{onBatch:e.onQueueBatch??(()=>{throw new k("VALIDATION_ERROR","@lunora/platform-node: createNodePlatform was given `queues` without `onQueueBatch`, so a delivered batch has nowhere to go — pass the worker's queue export from createWorker")}),queues:e.queues}),v=e.objectStorageDirectory===void 0?void 0:q({directory:e.objectStorageDirectory}),a=e.globalTablesPath===void 0?void 0:y({path:e.globalTablesPath});a!==void 0&&o.push(()=>{a.dispose()});const w=e.workflows===void 0?void 0:H({store:P(r),workflows:e.workflows}),h=()=>{for(const s of o.toReversed())s()};return{capabilities:N,close:h,directory:d,dispatch:s=>l(s),drain:u,globalTables:a,kv:c,objectStorage:v,queues:S,scheduler:p,shard:m,sockets:f,workflows:w,[Symbol.dispose]:h}}catch(c){for(const t of o.toReversed())try{t()}catch{}throw c}};export{z as createNodePlatform};
@@ -0,0 +1 @@
1
+ import{LunoraError as r}from"@lunora/errors";const t=o=>({get:e=>{if(o.get===void 0)throw new r("INTERNAL","@lunora/platform-node: this shard directory has no id-based lookup");return o.get(e)},getByName:e=>o.getByName(e),idFromName:e=>o.idForName?.(e)??e});export{t as createNodeShardNamespace};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/platform-node",
3
- "version": "1.0.0-alpha.155",
3
+ "version": "1.0.0-alpha.157",
4
4
  "description": "Node implementation of the Lunora platform contracts: ShardHost, SocketHost, ShardDirectory, ShardKvStore, SchedulerHost over better-sqlite3 and an in-process socket registry (dev/test target)",
5
5
  "keywords": [
6
6
  "better-sqlite3",
@@ -46,13 +46,13 @@
46
46
  "access": "public"
47
47
  },
48
48
  "dependencies": {
49
- "@lunora/d1": "1.0.0-alpha.186",
49
+ "@lunora/d1": "1.0.0-alpha.188",
50
50
  "@lunora/errors": ">=1.0.0-alpha.51 <2.0.0-0",
51
- "@lunora/platform": "1.0.0-alpha.67",
52
- "@lunora/queue": "1.0.0-alpha.112",
53
- "@lunora/runtime": "1.0.0-alpha.214",
54
- "@lunora/sql-store": "1.0.0-alpha.187",
55
- "@lunora/workflow": "1.0.0-alpha.81",
51
+ "@lunora/platform": "1.0.0-alpha.68",
52
+ "@lunora/queue": "1.0.0-alpha.114",
53
+ "@lunora/runtime": "1.0.0-alpha.216",
54
+ "@lunora/sql-store": "1.0.0-alpha.189",
55
+ "@lunora/workflow": "1.0.0-alpha.83",
56
56
  "@visulima/cron-parser": "2.0.0",
57
57
  "@visulima/workflow": "3.0.0",
58
58
  "better-sqlite3": "^13.0.3",