@streamsy/serve 0.4.0 → 0.5.0

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.
@@ -1,4 +1,3 @@
1
- //#region src/action/contract.d.ts
2
1
  /**
3
2
  * The action-sink declaration.
4
3
  *
@@ -96,5 +95,4 @@ export interface CheckedActionSink<Payload, From extends ActionSinkRelation = Ac
96
95
  export declare function defineActionSink<Payload, From extends ActionSinkRelation>(spec: ActionSinkSpec<Payload, From>): CheckedActionSink<Payload, From>;
97
96
  /** The next backoff, in milliseconds, after `attempts` failed attempts. */
98
97
  export declare function backoffAfter(policy: ActionSinkDeliveryPolicy, attempts: number): number;
99
- export type PayloadOf<Sink> = Sink extends CheckedActionSink<infer Payload, infer _From> ? Payload : never;
100
- //#endregion
98
+ export type PayloadOf<Sink> = Sink extends CheckedActionSink<infer Payload, infer _From> ? Payload : never;
@@ -1,5 +1,6 @@
1
1
  import { contractFingerprint } from "../contract.js";
2
- //#region src/action/contract.ts
2
+ import { Array, Record } from "effect";
3
+ // src/action/contract.ts
3
4
  function defineActionSink(spec) {
4
5
  if (!Number.isInteger(spec.delivery.maxAttempts) || spec.delivery.maxAttempts < 1) throw new TypeError(`action sink ${spec.name} must declare at least one delivery attempt`);
5
6
  if (spec.delivery.backoffFactor < 1) throw new TypeError(`action sink ${spec.name} must not shrink its backoff`);
@@ -9,7 +10,7 @@ function defineActionSink(spec) {
9
10
  name: spec.name,
10
11
  key,
11
12
  handler: { ...spec.handler },
12
- delivery: Object.fromEntries(Object.entries(spec.delivery).map(([name, value]) => [name, Number.isFinite(value) ? value : String(value)]))
13
+ delivery: Record.fromEntries(Array.map(Record.toEntries(spec.delivery), ([name, value]) => [name, Number.isFinite(value) ? value : String(value)]))
13
14
  });
14
15
  return Object.freeze({
15
16
  ...spec,
@@ -24,5 +25,4 @@ function backoffAfter(policy, attempts) {
24
25
  const raw = policy.initialBackoffMs * policy.backoffFactor ** exponent;
25
26
  return Math.min(policy.maxBackoffMs, Math.round(raw));
26
27
  }
27
- //#endregion
28
28
  export { backoffAfter, defineActionSink };
@@ -1,6 +1,4 @@
1
- import { Schema } from "effect";
2
- import * as Cause$1 from "effect/Cause";
3
- //#region src/action/errors.d.ts
1
+ import { Cause, Schema } from "effect";
4
2
  export declare const ACTION_SINK_ERROR_TAGS: readonly ["ActionSinkDeliveryFailure", "OutboxUnavailable"];
5
3
  export type ActionSinkErrorTag = (typeof ACTION_SINK_ERROR_TAGS)[number];
6
4
  /** The durable outbox could not be read or written. Never a delivery outcome. */
@@ -8,7 +6,7 @@ type OutboxUnavailableSchema = Schema.TaggedStruct<"OutboxUnavailable", {
8
6
  readonly operation: Schema.String;
9
7
  readonly detail: Schema.String;
10
8
  }>;
11
- declare const OutboxUnavailableBase: Schema.Class<OutboxUnavailable, OutboxUnavailableSchema, Cause$1.YieldableError>;
9
+ declare const OutboxUnavailableBase: Schema.Class<OutboxUnavailable, OutboxUnavailableSchema, Cause.YieldableError>;
12
10
  export declare class OutboxUnavailable extends OutboxUnavailableBase {}
13
11
  /**
14
12
  * A handler refused one delivery.
@@ -23,7 +21,7 @@ type ActionSinkDeliveryFailureSchema = Schema.TaggedStruct<"ActionSinkDeliveryFa
23
21
  readonly detail: Schema.String;
24
22
  readonly retryable: Schema.Boolean;
25
23
  }>;
26
- declare const ActionSinkDeliveryFailureBase: Schema.Class<ActionSinkDeliveryFailure, ActionSinkDeliveryFailureSchema, Cause$1.YieldableError>;
24
+ declare const ActionSinkDeliveryFailureBase: Schema.Class<ActionSinkDeliveryFailure, ActionSinkDeliveryFailureSchema, Cause.YieldableError>;
27
25
  export declare class ActionSinkDeliveryFailure extends ActionSinkDeliveryFailureBase {
28
26
  static retryable(handler: string, idempotencyKey: string, detail: string): ActionSinkDeliveryFailure;
29
27
  static permanent(handler: string, idempotencyKey: string, detail: string): ActionSinkDeliveryFailure;
@@ -31,5 +29,4 @@ export declare class ActionSinkDeliveryFailure extends ActionSinkDeliveryFailure
31
29
  /** Why one outbox entry stopped being retried. */
32
30
  export declare const DEAD_LETTER_REASONS: readonly ["attempts-exhausted", "permanent", "payload-poison"];
33
31
  export type DeadLetterReason = (typeof DEAD_LETTER_REASONS)[number];
34
- //#endregion
35
32
  export {};
@@ -1,5 +1,5 @@
1
1
  import { Schema } from "effect";
2
- //#region src/action/errors.ts
2
+ // src/action/errors.ts
3
3
  /**
4
4
  * Typed action-sink failures.
5
5
  *
@@ -47,5 +47,4 @@ const DEAD_LETTER_REASONS = [
47
47
  "permanent",
48
48
  "payload-poison"
49
49
  ];
50
- //#endregion
51
50
  export { ACTION_SINK_ERROR_TAGS, ActionSinkDeliveryFailure, DEAD_LETTER_REASONS, OutboxUnavailable };
@@ -1,6 +1,5 @@
1
1
  import { DeadLetterReason, OutboxUnavailable } from "./errors.js";
2
2
  import { Context, Effect, Layer, Option } from "effect";
3
- //#region src/action/outbox.d.ts
4
3
  export type OutboxEntryState = "pending" | "delivered" | "dead";
5
4
  export interface OutboxEntry {
6
5
  /** Monotonic enqueue order. Delivery walks a lane in this order. */
@@ -63,5 +62,4 @@ export declare const outboxStoreLayer: (backing: OutboxBacking) => Layer.Layer<O
63
62
  * host behaves the same way on the durable one.
64
63
  */
65
64
  export declare function makeMemoryOutboxBacking(): OutboxBacking;
66
- //#endregion
67
65
  export {};
@@ -1,6 +1,6 @@
1
1
  import "./errors.js";
2
- import { Context, Effect, Layer, Option } from "effect";
3
- //#region src/action/outbox.ts
2
+ import { Array, Context, Effect, Layer, Option, Order } from "effect";
3
+ // src/action/outbox.ts
4
4
  /**
5
5
  * The durable outbox.
6
6
  *
@@ -35,7 +35,7 @@ function makeMemoryOutboxBacking() {
35
35
  ...patch
36
36
  });
37
37
  };
38
- const ordered = () => [...entries.values()].toSorted((left, right) => left.id - right.id);
38
+ const ordered = () => Array.sort([...entries.values()], Order.mapInput(Order.Number, (entry) => entry.id));
39
39
  return {
40
40
  enqueue: (drafts) => Effect.sync(() => {
41
41
  let enqueued = 0;
@@ -72,11 +72,11 @@ function makeMemoryOutboxBacking() {
72
72
  };
73
73
  }),
74
74
  nextDue: (sink, partitionId, inOrder = false) => Effect.sync(() => {
75
- const pending = ordered().filter((entry) => entry.sink === sink && entry.state === "pending" && (partitionId === void 0 || entry.partitionId === partitionId));
75
+ const pending = Array.filter(ordered(), (entry) => entry.sink === sink && entry.state === "pending" && (partitionId === void 0 || entry.partitionId === partitionId));
76
76
  const first = pending[0];
77
- return first === void 0 ? Option.none() : Option.some(inOrder ? first.nextAttemptAtMs : pending.reduce((at, entry) => Math.min(at, entry.nextAttemptAtMs), Infinity));
77
+ return first === void 0 ? Option.none() : Option.some(inOrder ? first.nextAttemptAtMs : Array.reduce(pending, Infinity, (at, entry) => Math.min(at, entry.nextAttemptAtMs)));
78
78
  }),
79
- claimDue: (sink, partitionId, nowMs, limit, inOrder = false) => Effect.sync(() => ordered().filter((entry) => entry.sink === sink && entry.state === "pending" && (inOrder || entry.nextAttemptAtMs <= nowMs) && (partitionId === void 0 || entry.partitionId === partitionId)).slice(0, limit)),
79
+ claimDue: (sink, partitionId, nowMs, limit, inOrder = false) => Effect.sync(() => Array.filter(ordered(), (entry) => entry.sink === sink && entry.state === "pending" && (inOrder || entry.nextAttemptAtMs <= nowMs) && (partitionId === void 0 || entry.partitionId === partitionId)).slice(0, limit)),
80
80
  markDelivered: (id, attempts, atMs) => Effect.sync(() => {
81
81
  replace(id, {
82
82
  state: "delivered",
@@ -102,8 +102,7 @@ function makeMemoryOutboxBacking() {
102
102
  settledAtMs: atMs
103
103
  });
104
104
  }),
105
- list: (sink, partitionId) => Effect.sync(() => ordered().filter((entry) => entry.sink === sink && (partitionId === void 0 || entry.partitionId === partitionId)))
105
+ list: (sink, partitionId) => Effect.sync(() => Array.filter(ordered(), (entry) => entry.sink === sink && (partitionId === void 0 || entry.partitionId === partitionId)))
106
106
  };
107
107
  }
108
- //#endregion
109
108
  export { OutboxStore, makeMemoryOutboxBacking, outboxStore, outboxStoreLayer };
@@ -1,9 +1,8 @@
1
1
  import { ActionSinkRelation, CheckedActionSink } from "./contract.js";
2
2
  import { ActionSinkDeliveryFailure, OutboxUnavailable } from "./errors.js";
3
3
  import { OutboxDraft, OutboxStore } from "./outbox.js";
4
- import { t as Wakeup } from "../wakeup-BtnMEjLv.js";
4
+ import { t as Wakeup } from "../wakeup--kIoqiIE.js";
5
5
  import { Effect } from "effect";
6
- //#region src/action/runtime.d.ts
7
6
  /** One attempt at one delivery, as the handler sees it. */
8
7
  export interface ActionSinkDelivery<Payload> {
9
8
  readonly entryId: number;
@@ -76,5 +75,4 @@ export declare const drain: <Payload, From extends ActionSinkRelation, Requireme
76
75
  */
77
76
  export declare const OutboxWakeup: {
78
77
  make: <Payload, From extends ActionSinkRelation, R = never>(sink: CheckedActionSink<Payload, From>, handler: ActionSinkHandler<Payload, R>, options?: DrainOptions) => Wakeup<OutboxStore | R>;
79
- };
80
- //#endregion
78
+ };
@@ -1,10 +1,10 @@
1
1
  import { backoffAfter } from "./contract.js";
2
2
  import { ActionSinkDeliveryFailure } from "./errors.js";
3
3
  import { OutboxStore } from "./outbox.js";
4
- import { n as Wakeup } from "../wakeup-CWKg8pts.js";
5
- import { Cause, Clock, Effect, Exit, Semaphore } from "effect";
4
+ import { n as Wakeup } from "../wakeup-CcJE8k3i.js";
5
+ import { Array, Cause, Clock, Effect, Exit, Option, Semaphore } from "effect";
6
6
  import { StorageFault } from "@streamsy/core";
7
- //#region src/action/runtime.ts
7
+ // src/action/runtime.ts
8
8
  /**
9
9
  * The delivery runtime: one bounded, serialized pass over a sink's outbox.
10
10
  *
@@ -41,7 +41,7 @@ function actionSinkHandler(sink, deliver) {
41
41
  const DEFAULT_DRAIN_LIMIT = 100;
42
42
  /** Lower declared payloads to outbox drafts. Pure, so a caller can write them transactionally. */
43
43
  function draftsFor(sink, payloads, enqueuedAtMs) {
44
- return payloads.map((payload) => ({
44
+ return Array.map(payloads, (payload) => ({
45
45
  sink: sink.name,
46
46
  partitionId: sink.partitionBy(payload),
47
47
  idempotencyKey: sink.idempotencyKey(payload),
@@ -147,8 +147,8 @@ function decodePayload(sink, entry) {
147
147
  }
148
148
  /** Every non-interrupt cause becomes one retryable refusal; a thrown handler is still a handler. */
149
149
  function failureOf(sink, entry, cause) {
150
- const declared = cause.reasons.find(Cause.isFailReason);
151
- if (declared !== void 0) return declared.error;
150
+ const declared = Array.findFirst(cause.reasons, Cause.isFailReason);
151
+ if (Option.isSome(declared)) return declared.value.error;
152
152
  return ActionSinkDeliveryFailure.retryable(sink.handler.name, entry.idempotencyKey, Cause.pretty(cause).slice(0, 500));
153
153
  }
154
154
  function deadLetterReason(sink, attempt, failure) {
@@ -178,5 +178,4 @@ function outboxWakeupName(sink, partitionId) {
178
178
  const lane = partitionId === void 0 ? "" : `/${encodeURIComponent(partitionId)}`;
179
179
  return `streamsy/outbox/${encodeURIComponent(sink)}${lane}`;
180
180
  }
181
- //#endregion
182
181
  export { DEFAULT_DRAIN_LIMIT, OutboxWakeup, actionSinkHandler, draftsFor, drain };
@@ -1,8 +1,6 @@
1
1
  import { OutboxUnavailable } from "./errors.js";
2
2
  import { OutboxBacking } from "./outbox.js";
3
3
  import { Effect } from "effect";
4
- import { SqlClient } from "effect/sql/SqlClient";
5
- //#region src/action/sql.d.ts
6
- export declare function migrateOutbox(sql: SqlClient): Effect.Effect<void, OutboxUnavailable>;
7
- export declare function createSqlOutboxBacking(sql: SqlClient): OutboxBacking;
8
- //#endregion
4
+ import { SqlClient } from "effect/sql";
5
+ export declare function migrateOutbox(sql: SqlClient.SqlClient): Effect.Effect<void, OutboxUnavailable>;
6
+ export declare function createSqlOutboxBacking(sql: SqlClient.SqlClient): OutboxBacking;
@@ -1,6 +1,6 @@
1
1
  import { OutboxUnavailable } from "./errors.js";
2
- import { Effect, Option, Predicate, Schema } from "effect";
3
- //#region src/action/sql.ts
2
+ import { Array, Effect, Option, Predicate, Schema } from "effect";
3
+ // src/action/sql.ts
4
4
  const TABLE = "streamsy_effect_outbox";
5
5
  /** MySQL key columns. Their sum fits the 3072-byte InnoDB index limit at 4 bytes a character. */
6
6
  const MYSQL_SINK_LENGTH = 255;
@@ -80,7 +80,7 @@ const unsupported = (operation) => Effect.fail(new OutboxUnavailable({
80
80
  }));
81
81
  /** The names of the outbox indexes that exist in the current MySQL database. */
82
82
  const mysqlIndexes = (sql) => sql`SELECT DISTINCT index_name AS name FROM information_schema.statistics
83
- WHERE table_schema = DATABASE() AND table_name = ${TABLE}`.pipe(Effect.map((rows) => new Set(rows.map(({ name }) => name))));
83
+ WHERE table_schema = DATABASE() AND table_name = ${TABLE}`.pipe(Effect.map((rows) => new Set(Array.map(rows, ({ name }) => name))));
84
84
  /** MySQL has no `CREATE INDEX IF NOT EXISTS`. A concurrent migration may win the create. */
85
85
  const migrateMysql = (sql) => Effect.gen(function* () {
86
86
  if (Option.isSome(yield* Effect.serviceOption(sql.transactionService))) return yield* new OutboxUnavailable({
@@ -94,7 +94,7 @@ const migrateMysql = (sql) => Effect.gen(function* () {
94
94
  }
95
95
  });
96
96
  function migrateOutbox(sql) {
97
- const statements = (dialect) => Effect.forEach([CREATE_TABLE[dialect], ...INDEXES.map((index) => createIndex(index, true))], (statement) => sql.unsafe(statement), { discard: true });
97
+ const statements = (dialect) => Effect.forEach([CREATE_TABLE[dialect], ...Array.map(INDEXES, (index) => createIndex(index, true))], (statement) => sql.unsafe(statement), { discard: true });
98
98
  return attempt("migrateOutbox", sql.onDialectOrElse({
99
99
  sqlite: () => statements("sqlite"),
100
100
  pg: () => statements("pg"),
@@ -106,7 +106,7 @@ function migrateOutbox(sql) {
106
106
  * MySQL readiness only reads the catalog. The backing may run inside a caller's
107
107
  * transaction, and DDL there would commit it early.
108
108
  */
109
- const mysqlReady = (sql) => Effect.flatMap(mysqlIndexes(sql), (names) => INDEXES.every((index) => names.has(index.name)) ? Effect.void : Effect.fail(new OutboxUnavailable({
109
+ const mysqlReady = (sql) => Effect.flatMap(mysqlIndexes(sql), (names) => Array.every(INDEXES, (index) => names.has(index.name)) ? Effect.void : Effect.fail(new OutboxUnavailable({
110
110
  operation: "ready",
111
111
  detail: `${TABLE} is not migrated; on MySQL, run migrateOutbox at placement start`
112
112
  })));
@@ -144,7 +144,7 @@ function createSqlOutboxBacking(sql) {
144
144
  }));
145
145
  const run = (operation, statement) => attempt(operation, Effect.andThen(ready, statement));
146
146
  const execute = (operation, statement) => run(operation, statement).pipe(Effect.asVoid);
147
- const rows = (operation, statement) => run(operation, statement).pipe(Effect.map((found) => found.map(decodeRow)));
147
+ const rows = (operation, statement) => run(operation, statement).pipe(Effect.map((found) => Array.map(found, (row) => decodeRow(row))));
148
148
  /** Whether the draft was new. A draft with a known sink and key changes nothing. */
149
149
  const insert = (draft) => {
150
150
  const row = sql.insert({
@@ -225,5 +225,4 @@ function decodeReason(value) {
225
225
  if (value === "attempts-exhausted" || value === "permanent" || value === "payload-poison") return value;
226
226
  throw new TypeError(`unknown outbox dead-letter reason ${value}`);
227
227
  }
228
- //#endregion
229
228
  export { createSqlOutboxBacking, migrateOutbox };
package/dist/alchemy.d.ts CHANGED
@@ -1,17 +1,15 @@
1
- import { a as alarmSlotLayer, i as AlarmSlot, n as WakeupProgress, r as Wakeups, t as Wakeup } from "./wakeup-BtnMEjLv.js";
2
- import { i as ObjectServices, n as rule, r as ObjectApp, t as Placement } from "./placement-BOM3E08P.js";
1
+ import { a as alarmSlotLayer, i as AlarmSlot, n as WakeupProgress, r as Wakeups, t as Wakeup } from "./wakeup--kIoqiIE.js";
2
+ import { i as ObjectServices, n as rule, r as ObjectApp, t as Placement } from "./placement-Cj3sd02S.js";
3
3
  import { Effect, Layer, Scope } from "effect";
4
- import { HttpServerRequest, HttpServerResponse } from "effect/http";
4
+ import { HttpServerError, HttpServerRequest, HttpServerResponse } from "effect/http";
5
5
  import { Storage, StorageFault } from "@streamsy/core";
6
6
  import { DurableObject } from "alchemy/Cloudflare";
7
- import { HttpServerError } from "effect/http/HttpServerError";
8
- //#region src/alchemy.d.ts
9
7
  /** An Alchemy HttpEffect; authenticate before evaluating this router. */
10
8
  export declare const router: (options: {
11
9
  readonly objects: Pick<DurableObject, "getByName">;
12
10
  readonly prefix?: `/${string}`;
13
11
  readonly placement?: Placement;
14
- }) => Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError, HttpServerRequest.HttpServerRequest>;
12
+ }) => Effect.Effect<HttpServerResponse.HttpServerResponse, HttpServerError.HttpServerError, HttpServerRequest.HttpServerRequest>;
15
13
  export type ObjectHandlers = {
16
14
  readonly fetch: Effect.Effect<HttpServerResponse.HttpServerResponse, never, HttpServerRequest.HttpServerRequest>;
17
15
  readonly alarm: () => Effect.Effect<void>;
@@ -21,5 +19,4 @@ export declare function objectHandlers<R = never>(options: {
21
19
  readonly layer: Layer.Layer<Storage | AlarmSlot | R, StorageFault>;
22
20
  readonly wakeups?: ReadonlyArray<Wakeup<ObjectServices<R>>>;
23
21
  }): Effect.Effect<ObjectHandlers, never, Scope.Scope>;
24
- //#endregion
25
22
  export { Placement, Wakeup, type WakeupProgress, Wakeups, alarmSlotLayer, rule };
package/dist/alchemy.js CHANGED
@@ -1,9 +1,9 @@
1
- import { a as alarmSlotLayer, n as Wakeup, r as Wakeups } from "./wakeup-CWKg8pts.js";
2
- import { a as acquireObject, i as rule, o as providedApp, r as Placement, s as unavailable, t as resolvePlacement } from "./router-DviIc1O7.js";
1
+ import { a as alarmSlotLayer, n as Wakeup, r as Wakeups } from "./wakeup-CcJE8k3i.js";
2
+ import { a as acquireObject, i as rule, o as providedApp, r as Placement, s as unavailable, t as resolvePlacement } from "./router-C2p1T25K.js";
3
3
  import { Effect, Option } from "effect";
4
4
  import { HttpRouter, HttpServerRequest, HttpServerRespondable, HttpServerResponse } from "effect/http";
5
5
  import { securityHeaders, streamPath } from "@streamsy/core/http";
6
- //#region src/alchemy.ts
6
+ // src/alchemy.ts
7
7
  /** An Alchemy HttpEffect; authenticate before evaluating this router. */
8
8
  const router = (options) => {
9
9
  const path = streamPath(options.prefix);
@@ -35,5 +35,4 @@ function objectHandlers(options) {
35
35
  };
36
36
  });
37
37
  }
38
- //#endregion
39
38
  export { Placement, Wakeup, Wakeups, alarmSlotLayer, objectHandlers, router, rule };
package/dist/bun.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { Duration } from "effect";
2
- //#region src/bun.d.ts
3
2
  export declare const DEFAULT_PORT = 3000;
4
3
  export declare const DEFAULT_HOSTNAME = "127.0.0.1";
5
4
  export interface ListenerOptions {
@@ -9,5 +8,4 @@ export interface ListenerOptions {
9
8
  readonly gracefulShutdownTimeout?: Duration.Input;
10
9
  }
11
10
  /** The Bun listener that keeps a parked long poll alive. */
12
- export declare const listener: (options?: ListenerOptions) => import("effect/Layer").Layer<import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("effect/http/HttpServer").HttpServer | import("@effect/platform-bun/BunServices").BunServices, import("effect/http/HttpServerError").ServeError, never>;
13
- //#endregion
11
+ export declare const listener: (options?: ListenerOptions) => import("effect/Layer").Layer<import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | import("effect/http/HttpServer").HttpServer | import("@effect/platform-bun/BunServices").BunServices, import("effect/http/HttpServerError").ServeError, never>;
package/dist/bun.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { BunHttpServer } from "@effect/platform-bun";
2
- //#region src/bun.ts
2
+ // src/bun.ts
3
3
  const DEFAULT_PORT = 3e3;
4
4
  const DEFAULT_HOSTNAME = "127.0.0.1";
5
5
  const listenerOptions = (options) => ({
@@ -10,5 +10,4 @@ const listenerOptions = (options) => ({
10
10
  });
11
11
  /** The Bun listener that keeps a parked long poll alive. */
12
12
  const listener = (options = {}) => BunHttpServer.layer(listenerOptions(options));
13
- //#endregion
14
13
  export { DEFAULT_HOSTNAME, DEFAULT_PORT, listener };
@@ -1,10 +1,9 @@
1
- import { a as alarmSlotLayer, n as WakeupProgress, r as Wakeups, t as Wakeup } from "./wakeup-BtnMEjLv.js";
2
- import { i as ObjectServices, n as rule, r as ObjectApp, t as Placement } from "./placement-BOM3E08P.js";
1
+ import { a as alarmSlotLayer, n as WakeupProgress, r as Wakeups, t as Wakeup } from "./wakeup--kIoqiIE.js";
2
+ import { i as ObjectServices, n as rule, r as ObjectApp, t as Placement } from "./placement-Cj3sd02S.js";
3
3
  import { Layer } from "effect";
4
4
  import { Storage, StorageFault } from "@streamsy/core";
5
5
  import { DurableObject } from "cloudflare:workers";
6
6
  import { AlarmInvocationInfo, DurableObjectNamespace, DurableObjectState, ExportedHandler } from "@cloudflare/workers-types";
7
- //#region src/cloudflare/object.d.ts
8
7
  interface ObjectConfiguration<Env, R = never> {
9
8
  readonly app: ObjectApp<R>;
10
9
  readonly wakeups?: ReadonlyArray<Wakeup<ObjectServices<R>>>;
@@ -23,13 +22,10 @@ declare class ObjectHost<Env, R> extends DurableObject<Env> {
23
22
  export declare const StreamsyObject: {
24
23
  make: <Env = unknown, R = never>(configuration: ObjectConfiguration<Env, R>) => new (state: DurableObjectState, env: Env) => ObjectHost<Env, R>;
25
24
  };
26
- //#endregion
27
- //#region src/cloudflare/router.d.ts
28
25
  interface RouterOptions<Env> {
29
26
  readonly namespace: (env: Env) => DurableObjectNamespace;
30
27
  readonly placement?: Placement;
31
28
  readonly prefix?: `/${string}`;
32
29
  }
33
30
  export declare const router: <Env>(options: RouterOptions<Env>) => ExportedHandler<Env>;
34
- //#endregion
35
31
  export { type ObjectConfiguration, type ObjectInstance, Placement, type RouterOptions, Wakeup, type WakeupProgress, Wakeups, alarmSlotLayer, rule };
@@ -1,9 +1,9 @@
1
- import { a as alarmSlotLayer, n as Wakeup, r as Wakeups } from "./wakeup-CWKg8pts.js";
2
- import { a as acquireObject, i as rule, n as router, o as providedApp, r as Placement, s as unavailable } from "./router-DviIc1O7.js";
1
+ import { a as alarmSlotLayer, n as Wakeup, r as Wakeups } from "./wakeup-CcJE8k3i.js";
2
+ import { a as acquireObject, i as rule, n as router, o as providedApp, r as Placement, s as unavailable } from "./router-C2p1T25K.js";
3
3
  import { Context, Effect, Layer, ManagedRuntime, Option } from "effect";
4
4
  import { HttpRouter, HttpServerResponse } from "effect/http";
5
5
  import { DurableObject } from "cloudflare:workers";
6
- //#region src/cloudflare/object.ts
6
+ // src/cloudflare/object.ts
7
7
  var ObjectOwner = class extends Context.Service()("streamsy/ObjectOwner") {};
8
8
  var ObjectHost = class extends DurableObject {
9
9
  #runtime;
@@ -36,5 +36,4 @@ const StreamsyObject = { make: (configuration) => class extends ObjectHost {
36
36
  super(state, env, configuration);
37
37
  }
38
38
  } };
39
- //#endregion
40
39
  export { Placement, StreamsyObject, Wakeup, Wakeups, alarmSlotLayer, router, rule };
@@ -1,4 +1,3 @@
1
- //#region src/contract.d.ts
2
1
  /**
3
2
  * Canonical encoding and the change-detection digests served routes need.
4
3
  *
@@ -63,5 +62,4 @@ declare const PUBLIC_ERROR_TAGS: readonly ["InvalidParams", "ProtocolVersionUnsu
63
62
  type PublicErrorTag = PublicError["_tag"];
64
63
  /** The browser's JSON error boundary; unknown fields are discarded. */
65
64
  declare function decodePublicError(value: unknown): PublicError;
66
- //#endregion
67
65
  export { PublicErrorTag as a, STATE_CONTRACT_HEADER as c, STREAM_VERSION_HEADER as d, VALUE_CONTRACT_HEADER as f, valueEtag as g, decodePublicError as h, PublicError as i, STATE_VERSION_HEADER as l, contractFingerprint as m, DURABLE_STATE_PROTOCOL as n, ReplayRecovery as o, canonicalJson as p, PUBLIC_ERROR_TAGS as r, ResumeRejectedReason as s, CanonicalValue as t, STREAM_CONTRACT_HEADER as u };
@@ -1,2 +1,2 @@
1
- import { a as PublicErrorTag, c as STATE_CONTRACT_HEADER, d as STREAM_VERSION_HEADER, f as VALUE_CONTRACT_HEADER, g as valueEtag, h as decodePublicError, i as PublicError, l as STATE_VERSION_HEADER, m as contractFingerprint, n as DURABLE_STATE_PROTOCOL, o as ReplayRecovery, p as canonicalJson, r as PUBLIC_ERROR_TAGS, s as ResumeRejectedReason, t as CanonicalValue, u as STREAM_CONTRACT_HEADER } from "./contract-BUWJ-Sy2.js";
1
+ import { a as PublicErrorTag, c as STATE_CONTRACT_HEADER, d as STREAM_VERSION_HEADER, f as VALUE_CONTRACT_HEADER, g as valueEtag, h as decodePublicError, i as PublicError, l as STATE_VERSION_HEADER, m as contractFingerprint, n as DURABLE_STATE_PROTOCOL, o as ReplayRecovery, p as canonicalJson, r as PUBLIC_ERROR_TAGS, s as ResumeRejectedReason, t as CanonicalValue, u as STREAM_CONTRACT_HEADER } from "./contract-CFoMXKuH.js";
2
2
  export { CanonicalValue, DURABLE_STATE_PROTOCOL, PUBLIC_ERROR_TAGS, PublicError, PublicErrorTag, ReplayRecovery, ResumeRejectedReason, STATE_CONTRACT_HEADER, STATE_VERSION_HEADER, STREAM_CONTRACT_HEADER, STREAM_VERSION_HEADER, VALUE_CONTRACT_HEADER, canonicalJson, contractFingerprint, decodePublicError, valueEtag };
package/dist/contract.js CHANGED
@@ -1,7 +1,19 @@
1
- //#region src/contract.ts
1
+ import { Array, Order, Record } from "effect";
2
+ // src/contract.ts
3
+ /**
4
+ * Canonical encoding and the change-detection digests served routes need.
5
+ *
6
+ * A contract fingerprint and a value ETag answer the same question — "is
7
+ * this still the thing you were holding?" — so both are computed from one
8
+ * canonical encoding. Object keys are sorted and array order is preserved, so
9
+ * two values that differ only in property order encode identically and cannot
10
+ * invalidate a consumer's cache for no reason.
11
+ *
12
+ * These are change-detection identities, not security digests.
13
+ */
2
14
  /** Encode a value with object keys sorted, recursively. Array order is preserved. */
3
15
  function canonicalJson(value) {
4
- if (isCanonicalArray(value)) return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
16
+ if (isCanonicalArray(value)) return `[${Array.join(Array.map(value, (item) => canonicalJson(item)), ",")}]`;
5
17
  if (value === null) return "null";
6
18
  if (value instanceof Object) return canonicalObject(value);
7
19
  if (typeof value === "number" && !Number.isFinite(value)) throw new TypeError("Expected finite JSON number");
@@ -16,16 +28,12 @@ function isCanonicalArray(value) {
16
28
  }
17
29
  function canonicalObject(value) {
18
30
  const entries = [];
19
- for (const key of Object.keys(value).toSorted(compareKeys)) {
31
+ for (const key of Array.sort(Record.keys(value), Order.String)) {
20
32
  const member = value[key];
21
33
  if (member === void 0) continue;
22
34
  entries.push(`${JSON.stringify(key)}:${canonicalJson(member)}`);
23
35
  }
24
- return `{${entries.join(",")}}`;
25
- }
26
- function compareKeys(left, right) {
27
- if (left === right) return 0;
28
- return left < right ? -1 : 1;
36
+ return `{${Array.join(entries, ",")}}`;
29
37
  }
30
38
  /** Eight lowercase hex characters of FNV-1a over the canonical encoding's UTF-16 code units. */
31
39
  function contractFingerprint(value) {
@@ -120,5 +128,4 @@ function decodePublicError(value) {
120
128
  default: throw new TypeError("Unknown public error tag");
121
129
  }
122
130
  }
123
- //#endregion
124
131
  export { DURABLE_STATE_PROTOCOL, PUBLIC_ERROR_TAGS, STATE_CONTRACT_HEADER, STATE_VERSION_HEADER, STREAM_CONTRACT_HEADER, STREAM_VERSION_HEADER, VALUE_CONTRACT_HEADER, canonicalJson, contractFingerprint, decodePublicError, valueEtag };
package/dist/deno.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { Duration, Effect } from "effect";
2
- //#region src/deno.d.ts
3
2
  export declare const DEFAULT_PORT = 3000;
4
3
  export declare const DEFAULT_HOSTNAME = "127.0.0.1";
5
4
  export interface ListenerOptions {
@@ -23,5 +22,4 @@ export declare const listener: (options?: ListenerOptions) => import("effect/Lay
23
22
  * );
24
23
  * ```
25
24
  */
26
- export declare const nodeSqliteCompat: Effect.Effect<void>;
27
- //#endregion
25
+ export declare const nodeSqliteCompat: Effect.Effect<void>;
package/dist/deno.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Effect } from "effect";
2
2
  import { DenoHttpServer } from "@effect/platform-deno";
3
- //#region src/deno.ts
3
+ // src/deno.ts
4
4
  const DEFAULT_PORT = 3e3;
5
5
  const DEFAULT_HOSTNAME = "127.0.0.1";
6
6
  const listenerOptions = (options) => ({
@@ -28,5 +28,4 @@ const nodeSqliteCompat = Effect.promise(() => import("node:sqlite")).pipe(Effect
28
28
  const prototype = DatabaseSync.prototype;
29
29
  if (prototype.enableLoadExtension === void 0) prototype.enableLoadExtension = () => {};
30
30
  })));
31
- //#endregion
32
31
  export { DEFAULT_HOSTNAME, DEFAULT_PORT, listener, nodeSqliteCompat };
package/dist/index.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- import { t as CanonicalValue } from "./contract-BUWJ-Sy2.js";
1
+ import { t as CanonicalValue } from "./contract-CFoMXKuH.js";
2
2
  import { Duration, Effect, Layer, Runtime, Schema } from "effect";
3
3
  import { HttpRouter, HttpServerRequest, HttpServerRespondable, HttpServerResponse } from "effect/http";
4
4
  import { Http, StreamAddress, Streamsy } from "@streamsy/core";
5
5
  import { StreamRoute } from "@streamsy/core/protocol";
6
6
  import { ShutdownSignal } from "@streamsy/core/http";
7
- //#region src/errors.d.ts
8
7
  /** The public error body. `cases` gives one validating constructor per tag. */
9
8
  export declare const PublicErrorSchema: Schema.TaggedUnion<{
10
9
  readonly ContractChanged: Schema.TaggedStruct<"ContractChanged", {
@@ -95,8 +94,6 @@ declare class WireEncodeFailed extends WireEncodeFailed_base {
95
94
  [HttpServerRespondable.symbol](): Effect.Effect<HttpServerResponse.HttpServerResponse, never, never>;
96
95
  }
97
96
  type ServeError = InvalidParams | ProtocolVersionUnsupported | ResumeRejected | ContractChanged | TransportUnavailable | WireEncodeFailed;
98
- //#endregion
99
- //#region src/host.d.ts
100
97
  type DefaultServeRequires<R> = HttpRouter.Request.Only<"Requires", R> | HttpRouter.Request.Only<"GlobalRequires", R>;
101
98
  /** The options of `HttpRouter.serve`, plus the drain bound. */
102
99
  type LayerOptions<R = never, HE = never, HR = DefaultServeRequires<R>> = NonNullable<Parameters<typeof HttpRouter.serve<unknown, unknown, R, HE, HR>>[1]> & {
@@ -155,8 +152,8 @@ declare function mount<P, A, K, RD, RE, const Path extends `/${string}`>(declara
155
152
  }, path: Path & ExactPath<Path, NoInfer<P>>, options?: ReadOptions): import("effect/Layer").Layer<never, never, HttpRouter.HttpRouter | HttpRouter.Request<"Error", InvalidParams> | HttpRouter.Request<"Error", ProtocolVersionUnsupported> | HttpRouter.Request<"Error", ResumeRejected> | HttpRouter.Request<"Error", TransportUnavailable> | HttpRouter.Request<"Error", WireEncodeFailed> | HttpRouter.Request.From<"Requires", Exclude<K, HttpRouter.Provided>>>;
156
153
  /** Durable Streams response for one resolved address. */
157
154
  declare function stream<A, K, RD, RE>(address: Streamsy.Address<A, K, RD, RE>, options?: ReadOptions): Effect.Effect<HttpServerResponse.HttpServerResponse, ProtocolVersionUnsupported | ResumeRejected | TransportUnavailable | WireEncodeFailed, K | HttpServerRequest.HttpServerRequest | HttpRouter.RouteContext>;
158
- /** Durable State change response with replay-from-start recovery. */
159
- declare function state<C extends StreamAddress.Collections, K, RD, RE>(address: StreamAddress.StateAddress<C, RD, RE> & Streamsy.Address<StreamAddress.CollectionsChange<C>, K, RD, RE>, options?: ReadOptions): Effect.Effect<HttpServerResponse.HttpServerResponse, ProtocolVersionUnsupported | ResumeRejected | TransportUnavailable | WireEncodeFailed, K | HttpServerRequest.HttpServerRequest | HttpRouter.RouteContext>;
155
+ /** Durable State response with replay-from-start recovery. Stored controls pass through. */
156
+ declare function state<C extends StreamAddress.Collections, K, RD, RE>(address: StreamAddress.StateAddress<C, RD, RE> & Streamsy.Address<StreamAddress.CollectionsEvent<C>, K, RD, RE>, options?: ReadOptions): Effect.Effect<HttpServerResponse.HttpServerResponse, ProtocolVersionUnsupported | ResumeRejected | TransportUnavailable | WireEncodeFailed, K | HttpServerRequest.HttpServerRequest | HttpRouter.RouteContext>;
160
157
  interface ValueOptions {
161
158
  readonly cache?: {
162
159
  readonly visibility: "private" | "public";
@@ -167,5 +164,4 @@ interface ValueOptions {
167
164
  }
168
165
  /** Canonical JSON response for any computation, including mapped projection state. */
169
166
  declare const value: <A, I extends CanonicalValue, E, R, RE>(schema: Schema.Codec<A, I, never, RE>, compute: Effect.Effect<A, E, R>, options?: ValueOptions | undefined) => Effect.Effect<HttpServerResponse.HttpServerResponse, E | ContractChanged | WireEncodeFailed, R | RE | HttpServerRequest.HttpServerRequest | HttpRouter.RouteContext>;
170
- //#endregion
171
167
  export { serve_d_exports as Serve };
package/dist/index.js CHANGED
@@ -1,11 +1,11 @@
1
- import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.js";
1
+ import { t as __exportAll } from "./rolldown-runtime-D8jQpDG7.js";
2
2
  import { STATE_CONTRACT_HEADER, STATE_VERSION_HEADER, STREAM_CONTRACT_HEADER, STREAM_VERSION_HEADER, VALUE_CONTRACT_HEADER, canonicalJson, contractFingerprint, valueEtag } from "./contract.js";
3
- import { Cause, Duration, Effect, Exit, Layer, Runtime, Schema } from "effect";
3
+ import { Array, Cause, Duration, Effect, Exit, Layer, Order, Record, Runtime, Schema } from "effect";
4
4
  import { HttpRouter, HttpServerRequest, HttpServerRespondable, HttpServerResponse } from "effect/http";
5
5
  import { Http, Offset } from "@streamsy/core";
6
6
  import { StreamsReader } from "@streamsy/core/protocol";
7
7
  import { ShutdownSignal, layerShutdownSignal } from "@streamsy/core/http";
8
- //#region src/errors.ts
8
+ // src/errors.ts
9
9
  /** The public error body. `cases` gives one validating constructor per tag. */
10
10
  const PublicErrorSchema = Schema.TaggedUnion({
11
11
  InvalidParams: {
@@ -92,8 +92,7 @@ function errorResponse(error, status, headers) {
92
92
  }
93
93
  });
94
94
  }
95
- //#endregion
96
- //#region src/host.ts
95
+ // src/host.ts
97
96
  const drain = (timeout = Duration.seconds(2)) => (serve) => {
98
97
  return Layer.effectDiscard(Effect.gen(function* () {
99
98
  const signal = yield* ShutdownSignal;
@@ -131,8 +130,7 @@ const layer = (routes, options) => HttpRouter.serve(routes, options).pipe(drain(
131
130
  * caused. In a `Layer.launch` main the fiber ends only on failure or a signal.
132
131
  */
133
132
  const teardown = (exit, onExit) => Exit.isFailure(exit) && Cause.hasInterruptsOnly(exit.cause) ? onExit(0) : Runtime.defaultTeardown(exit, onExit);
134
- //#endregion
135
- //#region src/serve.ts
133
+ // src/serve.ts
136
134
  var serve_exports = /* @__PURE__ */ __exportAll({
137
135
  ContractChanged: () => ContractChanged,
138
136
  InvalidParams: () => InvalidParams,
@@ -187,7 +185,7 @@ function mount(declaration, path, options) {
187
185
  function stream(address, options = {}) {
188
186
  return read(address, options, "stream", Effect.succeed(null));
189
187
  }
190
- /** Durable State change response with replay-from-start recovery. */
188
+ /** Durable State response with replay-from-start recovery. Stored controls pass through. */
191
189
  function state(address, options = {}) {
192
190
  return read(address, options, "state", collectionDescriptor(address.collections));
193
191
  }
@@ -198,7 +196,7 @@ function collectionDescriptor(value) {
198
196
  route: matchedRoute.path,
199
197
  detail: "Invalid collection metadata"
200
198
  })));
201
- return Object.fromEntries(Object.entries(collections).map(([type, collection]) => [type, collection.key]));
199
+ return Record.fromEntries(Array.map(Record.toEntries(collections), ([type, collection]) => [type, collection.key]));
202
200
  });
203
201
  }
204
202
  const read = Effect.fn("Serve.read")(function* (address, options, kind, descriptor) {
@@ -215,7 +213,7 @@ const read = Effect.fn("Serve.read")(function* (address, options, kind, descript
215
213
  source,
216
214
  id: address.id,
217
215
  collections,
218
- params: path.split("/").filter((segment) => segment.startsWith(":")).map((segment) => segment.slice(1)).toSorted(),
216
+ params: Array.sort(Array.map(Array.filter(path.split("/"), (segment) => segment.startsWith(":")), (segment) => segment.slice(1)), Order.String),
219
217
  version: 1,
220
218
  recovery: "replay-from-start",
221
219
  contract: options.contract ?? null
@@ -324,7 +322,8 @@ const value = Effect.fn("Serve.value")(function* (schema, compute, options = {})
324
322
  "cache-control": cacheControl,
325
323
  etag
326
324
  };
327
- if (request.headers["if-none-match"]?.split(",").some((tag) => tag.trim() === "*" || tag.trim().replace(/^W\//, "") === etag)) return HttpServerResponse.empty({
325
+ const ifNoneMatch = request.headers["if-none-match"];
326
+ if (ifNoneMatch !== void 0 && Array.some(ifNoneMatch.split(","), (tag) => tag.trim() === "*" || tag.trim().replace(/^W\//, "") === etag)) return HttpServerResponse.empty({
328
327
  status: 304,
329
328
  headers: responseHeaders
330
329
  });
@@ -333,5 +332,4 @@ const value = Effect.fn("Serve.value")(function* (schema, compute, options = {})
333
332
  "content-type": "application/json"
334
333
  } });
335
334
  });
336
- //#endregion
337
335
  export { PublicErrorSchema, serve_exports as Serve };
package/dist/node.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { Duration, Layer } from "effect";
2
2
  import { HttpServer } from "effect/http";
3
3
  import { ServerOptions } from "node:http";
4
- //#region src/node.d.ts
5
4
  export declare const DEFAULT_PORT = 3000;
6
5
  export declare const DEFAULT_HOSTNAME = "127.0.0.1";
7
6
  export interface ListenerOptions {
@@ -16,5 +15,4 @@ export interface ListenerOptions {
16
15
  readonly gracefulShutdownTimeout?: Duration.Input;
17
16
  }
18
17
  /** The Node listener for a Streamsy host. */
19
- export declare const listener: (options?: ListenerOptions) => Layer.Layer<import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices, import("effect/http/HttpServerError").ServeError, never>;
20
- //#endregion
18
+ export declare const listener: (options?: ListenerOptions) => Layer.Layer<import("effect/http/Etag").Generator | import("effect/http/HttpPlatform").HttpPlatform | HttpServer.HttpServer | import("@effect/platform-node/NodeServices").NodeServices, import("effect/http/HttpServerError").ServeError, never>;
package/dist/node.js CHANGED
@@ -2,7 +2,7 @@ import { Effect, Layer } from "effect";
2
2
  import { HttpServer, HttpServerResponse } from "effect/http";
3
3
  import { createServer } from "node:http";
4
4
  import { NodeHttpServer } from "@effect/platform-node";
5
- //#region src/node.ts
5
+ // src/node.ts
6
6
  const DEFAULT_PORT = 3e3;
7
7
  const DEFAULT_HOSTNAME = "127.0.0.1";
8
8
  const listenOptions = (options) => ({
@@ -54,5 +54,4 @@ const createUnavailableAwareServer = (options) => {
54
54
  };
55
55
  /** The Node listener for a Streamsy host. */
56
56
  const listener = (options = {}) => nodeServer.pipe(Layer.provideMerge(NodeHttpServer.layer(() => createUnavailableAwareServer(options.server ?? {}), listenOptions(options))));
57
- //#endregion
58
57
  export { DEFAULT_HOSTNAME, DEFAULT_PORT, listener };
@@ -1,12 +1,9 @@
1
- import { r as Wakeups } from "./wakeup-BtnMEjLv.js";
1
+ import { r as Wakeups } from "./wakeup--kIoqiIE.js";
2
2
  import { Context, Effect, Layer, Option, Scope } from "effect";
3
3
  import { HttpRouter, HttpServerRespondable, HttpServerResponse } from "effect/http";
4
4
  import { Storage, StorageFault } from "@streamsy/core";
5
- //#region src/cloudflare/object-runtime.d.ts
6
5
  type ObjectServices<R = never> = Storage | Wakeups | R;
7
6
  type ObjectApp<R = never> = Layer.Layer<never, never, HttpRouter.HttpRouter | HttpRouter.Request.From<"Requires", ObjectServices<R>> | HttpRouter.Request.From<"Error", HttpServerRespondable.Respondable>>;
8
- //#endregion
9
- //#region src/cloudflare/placement.d.ts
10
7
  interface Placement {
11
8
  readonly name: (streamPath: string) => string;
12
9
  }
@@ -56,5 +53,4 @@ declare const Placement: {
56
53
  */
57
54
  readonly byRoute: (rules: ReadonlyArray<ErasedOwnerRule>) => Placement;
58
55
  };
59
- //#endregion
60
56
  export { ObjectServices as i, rule as n, ObjectApp as r, Placement as t };
@@ -1,4 +1,4 @@
1
- //#region \0rolldown/runtime.js
1
+ // \0rolldown/runtime.js
2
2
  var __defProp = Object.defineProperty;
3
3
  var __exportAll = (all, no_symbols) => {
4
4
  let target = {};
@@ -9,5 +9,4 @@ var __exportAll = (all, no_symbols) => {
9
9
  if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
10
  return target;
11
11
  };
12
- //#endregion
13
12
  export { __exportAll as t };
@@ -1,9 +1,9 @@
1
- import { i as makeWakeups, r as Wakeups, t as RequestTouch } from "./wakeup-CWKg8pts.js";
1
+ import { i as makeWakeups, r as Wakeups, t as RequestTouch } from "./wakeup-CcJE8k3i.js";
2
2
  import { Context, Effect, Exit, Layer, Option, Scope } from "effect";
3
3
  import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/http";
4
4
  import { Http } from "@streamsy/core";
5
5
  import { securityHeaders, streamPath } from "@streamsy/core/http";
6
- //#region src/cloudflare/host-program.ts
6
+ // src/cloudflare/host-program.ts
7
7
  /**
8
8
  * Reconciliation belongs to the mutation's finalizer. A request interruption
9
9
  * can be pending when the mutation's own uninterruptible region ends, so a
@@ -11,8 +11,7 @@ import { securityHeaders, streamPath } from "@streamsy/core/http";
11
11
  * reconciliation is logged and does not replace the mutation's result.
12
12
  */
13
13
  const withMutationReconciliation = (effect, reconcile) => effect.pipe(Effect.ensuring(reconcile.pipe(Effect.uninterruptible, Effect.catchCause((cause) => Effect.logError("Wakeup reconciliation failed", cause)))));
14
- //#endregion
15
- //#region src/cloudflare/object-runtime.ts
14
+ // src/cloudflare/object-runtime.ts
16
15
  /** Both object hosts install this middleware, including around user routes. */
17
16
  function objectApp(app, context) {
18
17
  const middleware = HttpRouter.middleware((handler) => Effect.gen(function* () {
@@ -55,8 +54,7 @@ function acquireObject(layer, compile, wakeups) {
55
54
  function providedApp(app, context) {
56
55
  return objectApp(app, context).pipe(HttpRouter.provideRequest(Layer.succeedContext(context)));
57
56
  }
58
- //#endregion
59
- //#region src/cloudflare/placement.ts
57
+ // src/cloudflare/placement.ts
60
58
  /**
61
59
  * Declare one placement rule with its stream's parameters in scope.
62
60
  *
@@ -84,8 +82,7 @@ const Placement = {
84
82
  return "";
85
83
  } })
86
84
  };
87
- //#endregion
88
- //#region src/cloudflare/router.ts
85
+ // src/cloudflare/router.ts
89
86
  const badRequest = (message) => new Response(message, {
90
87
  status: 400,
91
88
  headers: securityHeaders
@@ -126,5 +123,4 @@ const router = (options) => {
126
123
  return namespace.get(namespace.idFromName(child.name)).fetch(request);
127
124
  } };
128
125
  };
129
- //#endregion
130
126
  export { acquireObject as a, rule as i, router as n, providedApp as o, Placement as r, unavailable as s, resolvePlacement as t };
@@ -1,7 +1,6 @@
1
1
  import { Context, Duration, Effect, Layer, Option } from "effect";
2
2
  import { Storage, StorageFault } from "@streamsy/core";
3
3
  import { DurableObjectStorage } from "@cloudflare/workers-types";
4
- //#region src/cloudflare/alarm.d.ts
5
4
  declare const AlarmSlot_base: Context.ServiceClass<AlarmSlot, "@streamsy/serve/cloudflare/AlarmSlot", {
6
5
  readonly current: Effect.Effect<Option.Option<number>>;
7
6
  readonly arm: (at: number) => Effect.Effect<void>;
@@ -9,8 +8,6 @@ declare const AlarmSlot_base: Context.ServiceClass<AlarmSlot, "@streamsy/serve/c
9
8
  }>;
10
9
  declare class AlarmSlot extends AlarmSlot_base {}
11
10
  declare const alarmSlotLayer: (storage: DurableObjectStorage) => Layer.Layer<AlarmSlot>;
12
- //#endregion
13
- //#region src/cloudflare/wakeup.d.ts
14
11
  /**
15
12
  * A bounded run that stopped at its bound returns `{ more: true }`. The
16
13
  * scheduler then re-arms at once and does not count the pass as a failure.
@@ -52,5 +49,4 @@ declare const Wakeups_base: Context.ServiceClass<Wakeups, "@streamsy/serve/cloud
52
49
  }>>;
53
50
  }>;
54
51
  declare class Wakeups extends Wakeups_base {}
55
- //#endregion
56
52
  export { alarmSlotLayer as a, AlarmSlot as i, WakeupProgress as n, Wakeups as r, Wakeup as t };
@@ -1,15 +1,14 @@
1
- import { Clock, Context, Duration, Effect, Exit, Layer, Option, Predicate, Semaphore } from "effect";
1
+ import { Array, Clock, Context, Duration, Effect, Exit, Layer, Option, Predicate, Semaphore } from "effect";
2
2
  import { Storage } from "@streamsy/core";
3
3
  import { Protocol } from "@streamsy/core/protocol";
4
- //#region src/cloudflare/alarm.ts
4
+ // src/cloudflare/alarm.ts
5
5
  var AlarmSlot = class extends Context.Service()("@streamsy/serve/cloudflare/AlarmSlot") {};
6
6
  const alarmSlotLayer = (storage) => Layer.succeed(AlarmSlot, {
7
7
  current: Effect.promise(() => storage.getAlarm()).pipe(Effect.map(Option.fromNullishOr)),
8
8
  arm: (at) => Effect.promise(() => storage.setAlarm(at)),
9
9
  clear: Effect.promise(() => storage.deleteAlarm())
10
10
  });
11
- //#endregion
12
- //#region src/cloudflare/wakeup.ts
11
+ // src/cloudflare/wakeup.ts
13
12
  const Permits = Context.Reference("@streamsy/serve/cloudflare/WakeupPermits", { defaultValue: () => void 0 });
14
13
  const Guarded = Symbol.for("@streamsy/serve/cloudflare/WakeupPermit");
15
14
  const reportsMore = (value) => Predicate.hasProperty(value, "more") && value.more;
@@ -35,16 +34,16 @@ const expiry = Wakeup.make({
35
34
  });
36
35
  /** Internal object owner. Its context closes over the same services as fetch. */
37
36
  const makeWakeups = (context, definitions = []) => Effect.gen(function* () {
38
- const wakeups = [expiry, ...definitions].map(Wakeup.make);
37
+ const wakeups = Array.map([expiry, ...definitions], (wakeup) => Wakeup.make(wakeup));
39
38
  const names = /* @__PURE__ */ new Set();
40
39
  for (const wakeup of wakeups) {
41
40
  if (names.has(wakeup.name)) return yield* Effect.die(/* @__PURE__ */ new Error(`Duplicate wakeup name: ${wakeup.name}`));
42
41
  names.add(wakeup.name);
43
42
  }
44
- const permits = new Map(wakeups.map((wakeup) => [wakeup.name, Semaphore.makeUnsafe(1)]));
43
+ const permits = new Map(Array.map(wakeups, (wakeup) => [wakeup.name, Semaphore.makeUnsafe(1)]));
45
44
  const reconcilePermit = yield* Semaphore.make(1);
46
45
  const turnPermit = yield* Semaphore.make(1);
47
- const states = wakeups.map((wakeup) => ({
46
+ const states = Array.map(wakeups, (wakeup) => ({
48
47
  wakeup,
49
48
  failures: 0,
50
49
  floor: 0,
@@ -121,5 +120,4 @@ const makeWakeups = (context, definitions = []) => Effect.gen(function* () {
121
120
  alarm: alarm.pipe(Effect.provide(complete))
122
121
  };
123
122
  });
124
- //#endregion
125
123
  export { alarmSlotLayer as a, makeWakeups as i, Wakeup as n, Wakeups as r, RequestTouch as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamsy/serve",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Effect HttpRouter routes, Bun, Node, Deno and Cloudflare hosts, and durable action delivery.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -87,7 +87,7 @@
87
87
  "test:dialects": "bun --bun vitest run test/action/outbox-dialects.test.ts"
88
88
  },
89
89
  "dependencies": {
90
- "@streamsy/core": "^0.4.0"
90
+ "@streamsy/core": "^0.5.0"
91
91
  },
92
92
  "devDependencies": {
93
93
  "@cloudflare/workers-types": "4.20251228.0",
@@ -98,7 +98,7 @@
98
98
  "@effect/sql-pg": "4.0.5",
99
99
  "@effect/sql-sqlite-do": "4.0.5",
100
100
  "@effect/vitest": "4.0.5",
101
- "@streamsy/storage": "0.4.0",
101
+ "@streamsy/storage": "0.5.0",
102
102
  "@types/bun": "^1.4.3",
103
103
  "alchemy": "2.0.0-beta.83",
104
104
  "effect": "4.0.5",