@shivaedev/effect-contract 0.5.1 → 0.6.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0 - 2026-10-18
4
+
5
+ ### Added
6
+
7
+ - Contracts attach command invalidation metadata and a host-served change subscription. `serverMetadata.ts` exports `CommandInvalidation` and `changesRpc(name)` for server adapters.
8
+ - Bindings expose the `LiveConnection` symbol from `automaticLive.ts`; React session boundaries mount its `connection` atom and track the binding's query keys. `LiveBound` describes its connection and status atoms; `automaticLive(service, name)` and `ChangesClient` expose the underlying tracked coordination. `live` also accepts a function for `resyncKeys`.
9
+
10
+ ### Fixed
11
+
12
+ - Live server hints contain only changed keys intersecting the subscription, so one item's change does not refetch unrelated item queries.
13
+
14
+ ### Breaking
15
+
16
+ - Contracts reserve the `<contract>.$changes` RPC name for automatic subscriptions. Use Platform's database host and NDJSON transport for automatic live queries, and remove application subscription handlers and `live()` mounting when using a React session boundary.
17
+
3
18
  ## 0.5.1 - 2026-10-17
4
19
 
5
20
  ### Fixed
package/README.md CHANGED
@@ -131,7 +131,7 @@ export const NotesHandlers = Notes.toLayer(Effect.gen(function* () {
131
131
  }));
132
132
  ```
133
133
 
134
- Save this module as `handlers.ts`. This in-memory layer is the same shape as the package's native RPC fixture. An application can put repository and service calls in these handlers. The contract checks handler result and rejection types; it does not implement persistence, authorization or transactions.
134
+ Save this module as `handlers.ts`. An application can put repository and service calls in these handlers. The contract checks handler result and rejection types; it does not implement persistence, authorization or transactions.
135
135
 
136
136
  Native `.middleware(M)` returns a contract that retains its declarations. For example, a native middleware service can supply request identity:
137
137
 
@@ -180,7 +180,7 @@ export class NotesClient extends AtomRpc.Service<NotesClient>()("app/NotesClient
180
180
  export const api = bind(Notes, NotesClient);
181
181
  ```
182
182
 
183
- Save the client module as `client.ts`. The [native RPC guide](https://github.com/ShivaeDev/platform/blob/main/docs/framework/native-rpc.md) covers hosting; the [order example](https://github.com/ShivaeDev/platform/blob/main/docs/framework/order-example.md) composes handlers, request identity, SQL and a rendered client.
183
+ Save the client module as `client.ts`. The [native RPC guide](https://github.com/ShivaeDev/platform/blob/main/docs/framework/native-rpc.md) covers hosting; the [reference composition](https://github.com/ShivaeDev/platform/blob/main/docs/framework/composed-example.md) composes handlers, request identity, SQL and a rendered client.
184
184
 
185
185
  ### 3. Every day: read and change
186
186
 
@@ -226,11 +226,15 @@ The dependency rule is deliberately asymmetric:
226
226
 
227
227
  `readKeys` and `invalidationKeys` produce these native keys and remove duplicates. An item change refreshes that item and its list, preserving other item queries. A list-only invalidation refreshes list queries and leaves item queries alone. These keys match native Reactivity's record-key hashing of `{ notes: [1] }`.
228
228
 
229
- Client invalidation follows RPC success. For server invalidation at the SQL commit boundary, use `transact` from [`effect-sql`](https://github.com/ShivaeDev/platform/tree/main/packages/effect-sql); commit-bound changes belong to [`effect-changes`](https://github.com/ShivaeDev/platform/tree/main/packages/effect-changes).
229
+ Client invalidation follows RPC success. Platform database hosts also publish declared command invalidations automatically after the handler succeeds, excluding keys already announced by repository commits, using `transact` from [`effect-sql`](https://github.com/ShivaeDev/platform/tree/main/packages/effect-sql); commit-bound changes belong to [`effect-changes`](https://github.com/ShivaeDev/platform/tree/main/packages/effect-changes).
230
230
 
231
- ### 4. When needed: live invalidation and resume
231
+ ### 4. Live invalidation and resume
232
232
 
233
- Declare subscriptions with `stream` in the same contract. Its `success` schema describes each element, and its `rejections` become the native stream error schema. `bind` exposes `run(payload)` as a native `Stream`, preserving declared rejections, middleware errors and RPC client errors. Completion and client interruption release the server subscription in the real HTTP tests. Compiler checks reject a unary client for a streaming operation.
233
+ Platform database hosts serve the contract's reserved `<contract>.$changes` stream automatically. With NDJSON transport, `bind` and a React `SessionBoundary` connect query atoms without an application subscribe operation or `live()` call. The boundary finds bound APIs returned directly by `connect` or as properties such as `client.api`. See [automatic changes](https://github.com/ShivaeDev/platform/blob/main/docs/framework/changes.md) for defaults.
234
+
235
+ For a custom transport or explicit pause/status controls, the native live APIs remain available:
236
+
237
+ Declare subscriptions with `stream` in the same contract. Its `success` schema describes each element, and its `rejections` become the native stream error schema. `bind` exposes `run(payload)` as a native `Stream`, preserving declared rejections, middleware errors and RPC client errors. Completion and client interruption release the server subscription. The compiler rejects a unary client for a streaming operation.
234
238
 
235
239
  Every query that should reconcile together must read the same explicit scope. This variant of the feature adds `workspace.list` alongside each query's item or list key:
236
240
 
@@ -259,7 +263,7 @@ export const LiveNotes = contract("notes", { queries: [LiveGet, LiveList], comma
259
263
 
260
264
  Save the shared module as `live-notes.ts`. A collection list key alone does not cover its item queries. The common `workspace.list` key expresses that the caller intends to reconcile all queries that read it.
261
265
 
262
- Use `LiveNotes` as the server group and implement `notes.subscribe` with a native Stream. For streaming HTTP, the tested fixture uses `RpcSerialization.layerNdjson` on both sides. This client mounts live coordination alongside its queries:
266
+ Use `LiveNotes` as the server group and implement `notes.subscribe` with a native Stream. For streaming HTTP, use `RpcSerialization.layerNdjson` on both sides. This client mounts live coordination alongside its queries:
263
267
 
264
268
  ```ts
265
269
  import { Layer } from "effect";
@@ -298,7 +302,7 @@ const releaseConnection = registry.mount(updates.connection);
298
302
  | `Resync` | The exact `resyncKeys` read scope. |
299
303
  | First hint on a new or reconnected stream | The full explicit scope, even if the hint is targeted. |
300
304
 
301
- `LiveHint` and `Key` are schemas. The package's schema test rejects unknown hint tags and item keys with an invalid ID type.
305
+ `LiveHint` and `Key` are schemas. Decoding rejects unknown hint tags and item keys with an invalid ID type.
302
306
 
303
307
  ```ts
304
308
  registry.set(updates.paused, true);
@@ -306,11 +310,11 @@ registry.set(updates.paused, false);
306
310
  const status = registry.get(updates.status);
307
311
  ```
308
312
 
309
- While paused, the HTTP consumer keeps observing hints and suppresses automatic refresh. `status.pending` saturates at 256 observations. Unpausing reconciles the explicit scope and resets the count. A resume signal also requests reconciliation; the injected native source and visible browser `online` and `visibilitychange` events are tested, while hidden events do not request refresh.
313
+ While paused, live coordination keeps observing hints and suppresses automatic refresh. `status.pending` saturates at 256 observations. Unpausing reconciles the explicit scope and resets the count. A resume signal also requests reconciliation: an injected native source and visible browser `online` and `visibilitychange` events request it, while hidden events do not.
310
314
 
311
- `status` exposes `connection`, `failure`, `needsResync` and `pending`. The real HTTP tests cover typed stream failure, reconnection, missed changes and clearing the stream failure after successful delivery. They also show native query atoms retaining previous successful data during a read failure and rejecting stale HTTP responses after invalidation. Query failures remain in their own `AsyncResult`; stream failures appear in `status.failure`.
315
+ `status` exposes `connection`, `failure`, `needsResync` and `pending`. A reconnection reconciles changes missed while disconnected, and successful delivery clears a typed stream failure. Native query atoms keep previous successful data during a read failure and reject stale responses after invalidation. Query failures remain in their own `AsyncResult`; stream failures appear in `status.failure`.
312
316
 
313
- The resume signal is a scoped native atom. Its browser listeners and injected source cleanup are tested on registry disposal, including a resume source used only through `live`. The HTTP fixture also verifies release of the stream subscription when the registry is disposed:
317
+ The resume signal is a scoped native atom. Disposing the registry removes its browser listeners, cleans up an injected source, including one used only through `live`, and releases the stream subscription:
314
318
 
315
319
  ```ts
316
320
  releaseQuery();
@@ -320,6 +324,10 @@ registry.dispose();
320
324
 
321
325
  Call the release functions and dispose the registry when the application's root no longer owns them.
322
326
 
327
+ ### Server hint streams
328
+
329
+ Import `makeLiveServer` from `@shivaedev/effect-contract/makeLiveServer.ts` and create it once with the server's `Reactivity` service. After application authorization, return `hints.stream([orders.item(id)])` from the native streaming RPC handler. Each subscription starts with `Resync`; invalidating its read keys produces `Changed` with those declared keys. `hints.resync` sends `Resync` to every live stream owned by that helper, including streams for otherwise unrelated keys. Independent helpers keep their resync scopes separate even when they share Reactivity. The native stream owns registration and interruption. Keep this server helper separate from the browser's `live` coordinator.
330
+
323
331
  ### API by module
324
332
 
325
333
  Import the module that defines the name; the package has no root entry.
@@ -332,21 +340,10 @@ Import the module that defines the name; the package has no root entry.
332
340
  | `keys.ts` | `collection(name, idSchema)`; `Key` schema; `readKeys(keys)` and `invalidationKeys(keys)`. |
333
341
  | `bind.ts` | `bind(contract, atomRpcService)`, exposing `{ query, run }` for queries, `{ run }` Effects for commands and `{ run }` native Streams for streams. |
334
342
  | `live.ts` | `LiveHint` schema; `live(runtime, { stream, resyncKeys, resume?, retryDelay? })`, exposing `connection`, `paused` and `status`. |
343
+ | `makeLiveServer.ts` | `makeLiveServer`, an Effect requiring `Reactivity` that creates scoped server hint streams and a `resync` Effect. |
335
344
  | `resume.ts` | `resumeSignal({ window?, native? })`, returning a native numeric signal atom. |
336
345
 
337
- `QueryOptions` is the query atom's optional second argument: `headers?: Headers.Input`, `timeToLive?: Duration.Input` and `serializationKey?: string`. For example, `api.get.query({ id: 1 }, { timeToLive: "1 minute" })` uses the native query options interface.
338
-
339
- | Module | Exported types |
340
- | --- | --- |
341
- | `operation.ts` | `Query`, `Command`, `Streaming`, `QueryShape`, `CommandShape`, `StreamShape`, `OperationShape`, `PayloadSchema`. |
342
- | `contract.ts` | `Contract`, `Declared`, `OperationRpc`, `Tag`. |
343
- | `rejection.ts` | `FieldRejection`, `MatchingTags`, `Reject`, `RejectedBy`, `RejectionClass`, `RejectionSpecs`, `Rejections`, `RejectionSet`, `RejectionUnion`, `RejectionValue`, `TaggedRejection`. |
344
- | `keys.ts` | `Collection`, `Identity`, `ItemKey`, `ListKey`, `Key`. |
345
- | `bind.ts` | `Bound`, `BoundQuery`, `BoundCommand`, `BoundStream`, `Failure`, `RunFailure`, `QueryOptions`. |
346
- | `live.ts` | `LiveHint`, `LiveOptions`, `LiveStatus`. |
347
- | `resume.ts` | `ResumeOptions`, `ResumeSource`, `ResumeWindow`. |
348
-
349
- The shape types describe operation declarations, the bound types describe client members, and the rejection types derive classes and failures from declared specs. A native resume source receives a callback and returns a cleanup function. `ResumeWindow` describes the browser event and visibility methods the signal uses.
346
+ A query atom takes native query options as its optional second argument, for example `api.get.query({ id: 1 }, { timeToLive: "1 minute" })`. A native resume source receives a callback and returns a cleanup function.
350
347
 
351
348
  ### Install, setup and limits
352
349
 
@@ -354,16 +351,10 @@ The shape types describe operation declarations, the bound types describe client
354
351
  pnpm add @shivaedev/effect-contract effect@4.0.4
355
352
  ```
356
353
 
357
- `effect` is a peer. The repository's dependency catalog uses Effect `4.0.4`, and the package declares Node.js 24 or newer. Choose the native server host separately; this package supplies no server process or HTTP transport implementation.
354
+ `effect` is a peer, and the package needs Node.js 24 or newer. Choose the native server host separately; this package supplies no server process or HTTP transport implementation.
358
355
 
359
356
  - Put schema declarations where server and browser can share them. Put service, authorization and transaction implementations on the server. React rendering and editable forms belong to `effect-react` and `effect-form`.
360
357
  - Run command Effects with the Reactivity instance that owns the queries to refresh, as in the registry example.
361
358
  - Choose a common read key for every query in a full reconciliation scope. A list key does not implicitly cover item queries.
362
- - Supply browser or native resume sources explicitly. Synthetic visibility and native-source tests establish signal behavior; they do not establish physical tab transitions or Capacitor lifecycle behavior.
363
- - Live hints carry refresh requests, not authoritative data or durable history. Applications own dependency discovery, authenticated subscriptions, delivery across processes, SSR and deployment behavior. Those boundaries need their own tests.
364
-
365
- The package's design priorities are in [docs/north-star.md](docs/north-star.md), and package work and maintainer questions are in [docs/roadmap.md](docs/roadmap.md).
366
-
367
- ### Server hint streams
368
-
369
- Import `makeLiveServer` from `@shivaedev/effect-contract/makeLiveServer.ts` and create it once with the server's `Reactivity` service. After application authorization, return `hints.stream([orders.item(id)])` from the native streaming RPC handler. Each subscription starts with `Resync`; invalidating its read keys produces `Changed` with those declared keys. `hints.resync` sends `Resync` to every live stream owned by that helper, including streams for otherwise unrelated keys. Independent helpers keep their resync scopes separate even when they share Reactivity. The native stream owns registration and interruption. Keep this server helper separate from the browser's `live` coordinator.
359
+ - Supply browser or native resume sources explicitly.
360
+ - Live hints carry refresh requests, not authoritative data or durable history. Applications own dependency discovery, authenticated subscriptions, delivery across processes, SSR and deployment behavior.
@@ -0,0 +1,22 @@
1
+ import type * as Atom from "effect/reactivity/Atom";
2
+ import type * as AtomRpc from "effect/reactivity/AtomRpc";
3
+ import { type Key } from "./keys.ts";
4
+ import { type LiveStatus } from "./live.ts";
5
+ import type { changesRpc } from "./serverMetadata.ts";
6
+ export declare const LiveConnection: unique symbol;
7
+ export interface LiveBound {
8
+ readonly [LiveConnection]: {
9
+ readonly connection: Atom.Atom<unknown>;
10
+ readonly status: Atom.Atom<LiveStatus<unknown>>;
11
+ };
12
+ }
13
+ export type ChangesClient = AtomRpc.AtomRpcClient<unknown, string, ReturnType<typeof changesRpc>>;
14
+ export declare function automaticLive(service: ChangesClient, name: string): {
15
+ subscription: {
16
+ connection: Atom.Atom<import("effect/reactivity/AsyncResult").AsyncResult<void, never>>;
17
+ paused: Atom.Writable<boolean, boolean>;
18
+ status: Atom.Writable<LiveStatus<import("effect/rpc/RpcClientError").RpcClientError>, LiveStatus<import("effect/rpc/RpcClientError").RpcClientError>>;
19
+ };
20
+ track: (reads: readonly Key[]) => void;
21
+ };
22
+ //# sourceMappingURL=automaticLive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automaticLive.d.ts","sourceRoot":"","sources":["../src/automaticLive.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,IAAI,MAAM,wBAAwB,CAAC;AACpD,OAAO,KAAK,KAAK,OAAO,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,KAAK,GAAG,EAAY,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,KAAK,UAAU,EAAQ,MAAM,WAAW,CAAC;AAElD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAItD,eAAO,MAAM,cAAc,eAA0D,CAAC;AACtF,MAAM,WAAW,SAAS;IACzB,QAAQ,CAAC,CAAC,cAAc,CAAC,EAAE;QAAE,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAA;KAAE,CAAC;CACxH;AAED,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC;AAElG,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM;;;;;;mBAG3C,SAAS,GAAG,EAAE;EAuBpC"}
@@ -0,0 +1,29 @@
1
+ import { Effect, Stream, SubscriptionRef } from "effect";
2
+ import { readKeys } from "./keys.js";
3
+ import { live } from "./live.js";
4
+ import { resumeSignal } from "./resume.js";
5
+ export const LiveConnection = Symbol.for("@shivaedev/effect-contract/LiveConnection");
6
+ export function automaticLive(service, name) {
7
+ const keys = Effect.runSync(SubscriptionRef.make([]));
8
+ const known = new Set();
9
+ function track(reads) {
10
+ const fresh = reads.filter((key) => {
11
+ const id = readKeys([key])[0] ?? "";
12
+ if (known.has(id)) {
13
+ return false;
14
+ }
15
+ known.add(id);
16
+ return true;
17
+ });
18
+ if (fresh.length > 0) {
19
+ Effect.runSync(SubscriptionRef.update(keys, (current) => [...current, ...fresh]));
20
+ }
21
+ }
22
+ const subscription = live(service.runtime, {
23
+ resume: resumeSignal(typeof window === "undefined" ? {} : { window }),
24
+ resyncKeys: () => Effect.runSync(SubscriptionRef.get(keys)),
25
+ stream: SubscriptionRef.changes(keys).pipe(Stream.switchMap((current) => current.length === 0 ? Stream.never : Stream.unwrap(service.use((client) => Effect.succeed(client(`${name}.$changes`, { keys: current })))))),
26
+ });
27
+ return { subscription, track };
28
+ }
29
+ //# sourceMappingURL=automaticLive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automaticLive.js","sourceRoot":"","sources":["../src/automaticLive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAGzD,OAAO,EAAY,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAmB,IAAI,EAAE,MAAM,WAAW,CAAC;AAClD,OAAO,EAAqB,YAAY,EAAE,MAAM,aAAa,CAAC;AAK9D,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;AAOtF,MAAM,UAAU,aAAa,CAAC,OAAsB,EAAE,IAAY;IACjE,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,IAAI,CAAiB,EAAE,CAAC,CAAC,CAAC;IACtE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,SAAS,KAAK,CAAC,KAAqB;QACnC,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;YAClC,MAAM,EAAE,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACpC,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBACnB,OAAO,KAAK,CAAC;YACd,CAAC;YACD,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACd,OAAO,IAAI,CAAC;QACb,CAAC,CAAC,CAAC;QACH,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACnF,CAAC;IACF,CAAC;IACD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;QAC1C,MAAM,EAAE,YAAY,CAAC,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;QACrE,UAAU,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3D,MAAM,EAAE,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CACzC,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,CAC5B,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,WAAW,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAC3I,CACD;KACD,CAAC,CAAC;IACH,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;AAChC,CAAC"}
package/dist/bind.d.ts CHANGED
@@ -7,6 +7,7 @@ import type * as AtomRpc from "effect/reactivity/AtomRpc";
7
7
  import * as Reactivity from "effect/reactivity/Reactivity";
8
8
  import type { Rpc } from "effect/rpc";
9
9
  import type { RpcClientError } from "effect/rpc/RpcClientError";
10
+ import { type LiveBound } from "./automaticLive.ts";
10
11
  import type { Contract, Tag } from "./contract.ts";
11
12
  import type { CommandShape, QueryShape, StreamShape } from "./operation.ts";
12
13
  export type Failure<R extends Rpc.Any> = R extends Rpc.Rpc<infer _Tag, infer _Payload, infer _Success, infer _Error, infer TMiddleware, infer _Requires> ? Rpc.ErrorExit<R> | TMiddleware["error"]["Type"] | RpcClientError : never;
@@ -38,6 +39,6 @@ type MatchingClient<TRpcs extends Rpc.Any, TClientRpcs extends Rpc.Any> = [TRpcs
38
39
  }>] ? [Extract<TClientRpcs, {
39
40
  readonly _tag: TRpcs["_tag"];
40
41
  }>] extends [TRpcs] ? unknown : "Client must preserve the contract RPC types" : "Client must implement the contract RPCs";
41
- export declare function bind<TName extends string, TQueries extends readonly QueryShape[], TCommands extends readonly CommandShape[], TRpcs extends Rpc.Any, TSelf, TId extends string, TClientRpcs extends Rpc.Any, TStreams extends readonly StreamShape[]>(contract: Contract<TName, TQueries, TCommands, TRpcs, TStreams>, service: AtomRpc.AtomRpcClient<TSelf, TId, TClientRpcs> & MatchingClient<NoInfer<TRpcs>, TClientRpcs>): Bound<TName, TQueries, TCommands, TRpcs, TSelf, TStreams>;
42
+ export declare function bind<TName extends string, TQueries extends readonly QueryShape[], TCommands extends readonly CommandShape[], TRpcs extends Rpc.Any, TSelf, TId extends string, TClientRpcs extends Rpc.Any, TStreams extends readonly StreamShape[]>(contract: Contract<TName, TQueries, TCommands, TRpcs, TStreams>, service: AtomRpc.AtomRpcClient<TSelf, TId, TClientRpcs> & MatchingClient<NoInfer<TRpcs>, TClientRpcs>): Bound<TName, TQueries, TCommands, TRpcs, TSelf, TStreams> & LiveBound;
42
43
  export {};
43
44
  //# sourceMappingURL=bind.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bind.d.ts","sourceRoot":"","sources":["../src/bind.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bind.d.ts","sourceRoot":"","sources":["../src/bind.ts"],"names":[],"mappings":"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"}
package/dist/bind.js CHANGED
@@ -1,15 +1,19 @@
1
1
  import { Effect, Stream } from "effect";
2
2
  import * as Reactivity from "effect/reactivity/Reactivity";
3
+ import { automaticLive, LiveConnection } from "./automaticLive.js";
3
4
  import { invalidationKeys, readKeys } from "./keys.js";
4
5
  const invalidateAfter = Effect.fn("EffectContract.invalidateAfter")(function* (keys) {
5
6
  yield* Reactivity.invalidate(invalidationKeys(keys));
6
7
  });
7
- function boundQuery(service, tag, query) {
8
+ function boundQuery(service, tag, query, track) {
8
9
  function reads(payload) {
9
10
  return readKeys(query.reads(payload));
10
11
  }
11
12
  return {
12
- query: (payload, options = {}) => service.query(tag, payload, { ...options, reactivityKeys: reads(payload) }),
13
+ query: (payload, options = {}) => {
14
+ track(query.reads(payload));
15
+ return service.query(tag, payload, { ...options, reactivityKeys: reads(payload) });
16
+ },
13
17
  run: (payload) => service.use((client) => client(tag, payload)),
14
18
  };
15
19
  }
@@ -25,10 +29,11 @@ function boundStream(service, tag) {
25
29
  }
26
30
  export function bind(contract, service) {
27
31
  const { name, queries, commands, streams } = contract.declaration;
28
- return Object.fromEntries([
29
- ...queries.map((query) => [query.name, boundQuery(service, `${name}.${query.name}`, query)]),
32
+ const automatic = automaticLive(service, name);
33
+ return Object.assign(Object.fromEntries([
34
+ ...queries.map((query) => [query.name, boundQuery(service, `${name}.${query.name}`, query, automatic.track)]),
30
35
  ...commands.map((command) => [command.name, boundCommand(service, `${name}.${command.name}`, command)]),
31
36
  ...streams.map((stream) => [stream.name, boundStream(service, `${name}.${stream.name}`)]),
32
- ]);
37
+ ]), { [LiveConnection]: automatic.subscription });
33
38
  }
34
39
  //# sourceMappingURL=bind.js.map
package/dist/bind.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bind.js","sourceRoot":"","sources":["../src/bind.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAKxC,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAI3D,OAAO,EAAE,gBAAgB,EAAY,QAAQ,EAAE,MAAM,WAAW,CAAC;AA6DjE,MAAM,eAAe,GAAG,MAAM,CAAC,EAAE,CAAC,gCAAgC,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAoB;IAClG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AAEH,SAAS,UAAU,CAAC,OAAqB,EAAE,GAAW,EAAE,KAAiB;IACxE,SAAS,KAAK,CAAC,OAAgB;QAC9B,OAAO,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IACvC,CAAC;IACD,OAAO;QACN,KAAK,EAAE,CAAC,OAAgB,EAAE,OAAO,GAAiB,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,EAAE,GAAG,OAAO,EAAE,cAAc,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACpI,GAAG,EAAE,CAAC,OAAgB,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;KACxE,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,OAAqB,EAAE,GAAW,EAAE,OAAqB;IAC9E,OAAO;QACN,GAAG,EAAE,CAAC,OAAgB,EAAE,EAAE,CACzB,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;KAClI,CAAC;AACH,CAAC;AAED,SAAS,WAAW,CAAC,OAA8B,EAAE,GAAW;IAC/D,OAAO;QACN,GAAG,EAAE,CAAC,OAAgB,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;KACvG,CAAC;AACH,CAAC;AAeD,MAAM,UAAU,IAAI,CACnB,QAA4H,EAC5H,OAAwD;IAExD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,QAAQ,CAAC,WAAW,CAAC;IAClE,OAAO,MAAM,CAAC,WAAW,CAAC;QACzB,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,KAAK,CAAC,CAAU,CAAC;QACrG,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,YAAY,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,OAAO,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,CAAU,CAAC;QAChH,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC,CAAU,CAAC;KAClG,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"bind.js","sourceRoot":"","sources":["../src/bind.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { Schema } from "effect";
1
+ import { type Schema } from "effect";
2
2
  import { Rpc, RpcGroup, type RpcMiddleware, type RpcSchema } from "effect/rpc";
3
3
  import type { CommandShape, OperationShape, QueryShape, StreamShape } from "./operation.ts";
4
4
  export type Tag<TContract extends string, TName extends string> = `${TContract}.${TName}`;
@@ -1 +1 @@
1
- {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
package/dist/contract.js CHANGED
@@ -1,4 +1,6 @@
1
+ import { Context } from "effect";
1
2
  import { Rpc, RpcGroup } from "effect/rpc";
3
+ import { CommandInvalidation, changesRpc } from "./serverMetadata.js";
2
4
  function assertUnique(operations) {
3
5
  const duplicated = operations.map(({ name }) => name).filter((name, index, names) => names.indexOf(name) !== index);
4
6
  if (duplicated.length > 0) {
@@ -20,7 +22,7 @@ export function contract(name, operations) {
20
22
  payload: operation.payload,
21
23
  stream: operation.kind === "stream",
22
24
  success: operation.success,
23
- }));
24
- return withDeclaration(RpcGroup.make(...rpcs), { commands, name, queries, streams });
25
+ }).annotateMerge(operation.kind === "command" ? Context.make(CommandInvalidation, operation.invalidates) : Context.empty()));
26
+ return withDeclaration(RpcGroup.make(...rpcs, changesRpc(name)), { commands, name, queries, streams });
25
27
  }
26
28
  //# sourceMappingURL=contract.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAsC,MAAM,YAAY,CAAC;AA6C/E,SAAS,YAAY,CAAC,UAAqC;IAC1D,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC;IACpH,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvG,CAAC;AACF,CAAC;AAYD,SAAS,eAAe,CAAC,KAAY,EAAE,WAAwB;IAC9D,MAAM,gBAAgB,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtD,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE;QAC3B,WAAW;QACX,UAAU,EAAE,CAAC,UAAoC,EAAE,EAAE,CAAC,eAAe,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC;KAChH,CAAC,CAAC;AACJ,CAAC;AAeD,MAAM,UAAU,QAAQ,CACvB,IAAY,EACZ,UAAgJ;IAEhJ,MAAM,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,EAAE,EAAE,OAAO,GAAG,EAAE,EAAE,GAAG,UAAU,CAAC;IACjE,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,CAAC,GAAG,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CACpE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC,IAAI,EAAE,EAAE;QACrC,KAAK,EAAE,SAAS,CAAC,KAAK;QACtB,OAAO,EAAE,SAAS,CAAC,OAAO;QAC1B,MAAM,EAAE,SAAS,CAAC,IAAI,KAAK,QAAQ;QACnC,OAAO,EAAE,SAAS,CAAC,OAAO;KAC1B,CAAC,CACF,CAAC;IACF,OAAO,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AACtF,CAAC"}
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAe,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAsC,MAAM,YAAY,CAAC;AAE/E,OAAO,EAAE,mBAAmB,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AA4CtE,SAAS,YAAY,CAAC,UAAqC;IAC1D,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC;IACpH,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CAAC,+CAA+C,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvG,CAAC;AACF,CAAC;AAYD,SAAS,eAAe,CAAC,KAAY,EAAE,WAAwB;IAC9D,MAAM,gBAAgB,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtD,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE;QAC3B,WAAW;QACX,UAAU,EAAE,CAAC,UAAoC,EAAE,EAAE,CAAC,eAAe,CAAC,gBAAgB,CAAC,UAAU,CAAC,EAAE,WAAW,CAAC;KAChH,CAAC,CAAC;AACJ,CAAC;AAeD,MAAM,UAAU,QAAQ,CACvB,IAAY,EACZ,UAAgJ;IAEhJ,MAAM,EAAE,OAAO,GAAG,EAAE,EAAE,QAAQ,GAAG,EAAE,EAAE,OAAO,GAAG,EAAE,EAAE,GAAG,UAAU,CAAC;IACjE,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC;IACpD,MAAM,IAAI,GAAG,CAAC,GAAG,OAAO,EAAE,GAAG,QAAQ,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CACpE,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,IAAI,SAAS,CAAC,IAAI,EAAE,EAAE;QACrC,KAAK,EAAE,SAAS,CAAC,KAAK;QACtB,OAAO,EAAE,SAAS,CAAC,OAAO;QAC1B,MAAM,EAAE,SAAS,CAAC,IAAI,KAAK,QAAQ;QACnC,OAAO,EAAE,SAAS,CAAC,OAAO;KAC1B,CAAC,CAAC,aAAa,CAAC,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,mBAAmB,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAC3H,CAAC;IACF,OAAO,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;AACxG,CAAC"}
package/dist/keys.d.ts CHANGED
@@ -23,9 +23,9 @@ export interface Collection<TName extends string, TId extends Identity> {
23
23
  readonly list: ListKey<TName>;
24
24
  readonly name: TName;
25
25
  }
26
- export declare const collection: <const TName extends string, TId extends Schema.Top & {
26
+ export declare function collection<const TName extends string, TId extends Schema.Top & {
27
27
  readonly Type: Identity;
28
- }>(name: TName, _id: TId) => Collection<TName, TId["Type"]>;
29
- export declare const readKeys: (keys: readonly Key[]) => readonly string[];
30
- export declare const invalidationKeys: (keys: readonly Key[]) => readonly string[];
28
+ }>(name: TName, _id: TId): Collection<TName, TId["Type"]>;
29
+ export declare function readKeys(keys: readonly Key[]): readonly string[];
30
+ export declare function invalidationKeys(keys: readonly Key[]): readonly string[];
31
31
  //# sourceMappingURL=keys.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvC,MAAM,WAAW,OAAO,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;CAC3B;AAED,MAAM,WAAW,OAAO,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;IAC3B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;CACtB;AAED,MAAM,MAAM,GAAG,GAAG,OAAO,GAAG,OAAO,CAAC;AAEpC,eAAO,MAAM,GAAG;;;;;;;IAGd,CAAC;AAEH,MAAM,WAAW,UAAU,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,SAAS,QAAQ;IACrE,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3C,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;CACrB;AAED,eAAO,MAAM,UAAU,GAAI,KAAK,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,CAAC,GAAG,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,QACpG,KAAK,OACN,GAAG,KACN,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAI9B,CAAC;AAMH,eAAO,MAAM,QAAQ,SAAU,SAAS,GAAG,EAAE,KAAG,SAAS,MAAM,EAAiC,CAAC;AAEjG,eAAO,MAAM,gBAAgB,SAAU,SAAS,GAAG,EAAE,KAAG,SAAS,MAAM,EAEtE,CAAC"}
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAEvC,MAAM,WAAW,OAAO,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;CAC3B;AAED,MAAM,WAAW,OAAO,CAAC,KAAK,SAAS,MAAM,GAAG,MAAM;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC;IAC3B,QAAQ,CAAC,EAAE,EAAE,QAAQ,CAAC;CACtB;AAED,MAAM,MAAM,GAAG,GAAG,OAAO,GAAG,OAAO,CAAC;AAEpC,eAAO,MAAM,GAAG;;;;;;;IAGd,CAAC;AAEH,MAAM,WAAW,UAAU,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,SAAS,QAAQ;IACrE,QAAQ,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,GAAG,KAAK,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3C,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;CACrB;AAED,wBAAgB,UAAU,CAAC,KAAK,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,CAAC,GAAG,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,EAC1G,IAAI,EAAE,KAAK,EACX,GAAG,EAAE,GAAG,GACN,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAMhC;AAMD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,EAAE,GAAG,SAAS,MAAM,EAAE,CAEhE;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,GAAG,EAAE,GAAG,SAAS,MAAM,EAAE,CAExE"}
package/dist/keys.js CHANGED
@@ -3,16 +3,20 @@ export const Key = Schema.Union([
3
3
  Schema.Struct({ _tag: Schema.Literal("List"), collection: Schema.String }),
4
4
  Schema.Struct({ _tag: Schema.Literal("Item"), collection: Schema.String, id: Schema.Union([Schema.String, Schema.Number]) }),
5
5
  ]);
6
- export const collection = (name, _id) => ({
7
- item: (id) => ({ _tag: "Item", collection: name, id }),
8
- list: { _tag: "List", collection: name },
9
- name,
10
- });
6
+ export function collection(name, _id) {
7
+ return {
8
+ item: (id) => ({ _tag: "Item", collection: name, id }),
9
+ list: { _tag: "List", collection: name },
10
+ name,
11
+ };
12
+ }
11
13
  function own(key) {
12
14
  return key._tag === "List" ? key.collection : `${key.collection}:${key.id}`;
13
15
  }
14
- export const readKeys = (keys) => [...new Set(keys.map(own))];
15
- export const invalidationKeys = (keys) => [
16
- ...new Set(keys.flatMap((key) => (key._tag === "List" ? [key.collection] : [own(key), key.collection]))),
17
- ];
16
+ export function readKeys(keys) {
17
+ return [...new Set(keys.map(own))];
18
+ }
19
+ export function invalidationKeys(keys) {
20
+ return [...new Set(keys.flatMap((key) => (key._tag === "List" ? [key.collection] : [own(key), key.collection])))];
21
+ }
18
22
  //# sourceMappingURL=keys.js.map
package/dist/keys.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAiBhC,MAAM,CAAC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC;IAC/B,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IAC1E,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;CAC5H,CAAC,CAAC;AAQH,MAAM,CAAC,MAAM,UAAU,GAAG,CACzB,IAAW,EACX,GAAQ,EACyB,EAAE,CAAC,CAAC;IACrC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IACtD,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE;IACxC,IAAI;CACJ,CAAC,CAAC;AAEH,SAAS,GAAG,CAAC,GAAQ;IACpB,OAAO,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;AAC7E,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAoB,EAAqB,EAAE,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAEjG,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAoB,EAAqB,EAAE,CAAC;IAC5E,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;CACxG,CAAC"}
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAiBhC,MAAM,CAAC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC;IAC/B,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;IAC1E,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;CAC5H,CAAC,CAAC;AAQH,MAAM,UAAU,UAAU,CACzB,IAAW,EACX,GAAQ;IAER,OAAO;QACN,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACtD,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE;QACxC,IAAI;KACJ,CAAC;AACH,CAAC;AAED,SAAS,GAAG,CAAC,GAAQ;IACpB,OAAO,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,UAAU,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,IAAoB;IAC5C,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAoB;IACpD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnH,CAAC"}
package/dist/live.d.ts CHANGED
@@ -23,7 +23,7 @@ export interface LiveStatus<E> {
23
23
  }
24
24
  export interface LiveOptions<E, R> {
25
25
  readonly resume?: Atom.Atom<unknown>;
26
- readonly resyncKeys: readonly Key[];
26
+ readonly resyncKeys: readonly Key[] | (() => readonly Key[]);
27
27
  readonly retryDelay?: Duration.Input;
28
28
  readonly stream: Stream.Stream<LiveHint, E, R>;
29
29
  }
@@ -1 +1 @@
1
- {"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../src/live.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,QAAQ,EAAU,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACnF,OAAO,KAAK,IAAI,MAAM,wBAAwB,CAAC;AAE/C,OAAO,EAAoB,GAAG,EAAY,MAAM,WAAW,CAAC;AAE5D,eAAO,MAAM,QAAQ;;;;;;;;;;;;IAGnB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;AAC5C,MAAM,WAAW,UAAU,CAAC,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,YAAY,GAAG,MAAM,GAAG,cAAc,CAAC;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AACD,MAAM,WAAW,WAAW,CAAC,CAAC,EAAE,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,SAAS,GAAG,EAAE,CAAC;IACpC,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;CAC/C;AAID,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,aAAa,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC;;;;EAuDhH"}
1
+ {"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../src/live.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,KAAK,QAAQ,EAAU,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACnF,OAAO,KAAK,IAAI,MAAM,wBAAwB,CAAC;AAE/C,OAAO,EAAoB,GAAG,EAAY,MAAM,WAAW,CAAC;AAE5D,eAAO,MAAM,QAAQ;;;;;;;;;;;;IAGnB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;AAC5C,MAAM,WAAW,UAAU,CAAC,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,YAAY,GAAG,MAAM,GAAG,cAAc,CAAC;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AACD,MAAM,WAAW,WAAW,CAAC,CAAC,EAAE,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,SAAS,GAAG,EAAE,GAAG,CAAC,MAAM,SAAS,GAAG,EAAE,CAAC,CAAC;IAC7D,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;CAC/C;AAID,wBAAgB,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,aAAa,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC;;;;EAuDhH"}
package/dist/live.js CHANGED
@@ -22,7 +22,7 @@ export function live(runtime, options) {
22
22
  update({ needsResync: true });
23
23
  return;
24
24
  }
25
- reactivity.invalidateUnsafe(readKeys(options.resyncKeys));
25
+ reactivity.invalidateUnsafe(readKeys(typeof options.resyncKeys === "function" ? options.resyncKeys() : options.resyncKeys));
26
26
  update({ needsResync: false, pending: 0 });
27
27
  }
28
28
  get.subscribe(paused, (value) => {
package/dist/live.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"live.js","sourceRoot":"","sources":["../src/live.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"live.js","sourceRoot":"","sources":["../src/live.ts"],"names":[],"mappings":"AAAA,OAAO,EAA6B,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACnF,OAAO,KAAK,IAAI,MAAM,wBAAwB,CAAC;AAC/C,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE5D,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC;IACpC,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;IAC3E,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;CACjD,CAAC,CAAC;AAeH,MAAM,WAAW,GAAG,GAAG,CAAC;AAExB,MAAM,UAAU,IAAI,CAAsB,OAA2C,EAAE,OAA0B;IAChH,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAgB,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7H,MAAM,UAAU,GAAG,OAAO;SACxB,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CACb,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAClB,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;QAChD,SAAS,MAAM,CAAC,KAA6B;YAC5C,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;QACpD,CAAC;QACD,SAAS,MAAM;YACd,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBACtB,MAAM,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;gBAC9B,OAAO;YACR,CAAC;YACD,UAAU,CAAC,gBAAgB,CAAC,QAAQ,CAAC,OAAO,OAAO,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC;YAC5H,MAAM,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC;QAC5C,CAAC;QACD,GAAG,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;YAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;gBACZ,MAAM,EAAE,CAAC;YACV,CAAC;QACF,CAAC,CAAC,CAAC;QACH,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAClC,4EAA4E;YAC5E,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACzB,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACvC,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;YACnC,IAAI,OAAO,GAAG,IAAI,CAAC;YACnB,SAAS,OAAO,CAAC,IAAc;gBAC9B,MAAM,CAAC,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACvD,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;oBACtB,MAAM,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC7F,CAAC;qBAAM,IAAI,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBAC9C,MAAM,EAAE,CAAC;gBACV,CAAC;qBAAM,CAAC;oBACP,UAAU,CAAC,gBAAgB,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC1D,CAAC;gBACD,OAAO,GAAG,KAAK,CAAC;YACjB,CAAC;YACD,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,CACzB,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAC7D,MAAM,CAAC,MAAM,EACb,MAAM,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAC5F,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,UAAU,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAC5F,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,IAAI,UAAU,CAAC,CAAC,CAC9D,CAAC;QACH,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC,CAAC,CACF;SACA,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AACvC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"makeLiveServer.d.ts","sourceRoot":"","sources":["../src/makeLiveServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,KAAK,GAAG,EAAY,MAAM,WAAW,CAAC;AAG/C,eAAO,MAAM,cAAc;;mBAIJ,SAAS,GAAG,EAAE;;;;;;;;;;;;;gCAsBnC,CAAC"}
1
+ {"version":3,"file":"makeLiveServer.d.ts","sourceRoot":"","sources":["../src/makeLiveServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAS,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC/C,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,KAAK,GAAG,EAAY,MAAM,WAAW,CAAC;AAG/C,eAAO,MAAM,cAAc;;mBAGJ,SAAS,GAAG,EAAE;;;;;;;;;;;;;gCAoDnC,CAAC"}
@@ -1,24 +1,52 @@
1
- import { Effect, Stream } from "effect";
1
+ import { Effect, Queue, Stream } from "effect";
2
2
  import * as Reactivity from "effect/reactivity/Reactivity";
3
3
  import { readKeys } from "./keys.js";
4
4
  export const makeLiveServer = Effect.gen(function* () {
5
5
  const reactivity = yield* Reactivity.Reactivity;
6
6
  const resyncKey = `@shivaedev/effect-contract/resync/${crypto.randomUUID()}`;
7
- let generation = 0;
8
7
  function stream(keys) {
9
- return Stream.unwrap(Effect.sync(() => {
10
- let seen = -1;
11
- return reactivity.stream([...readKeys(keys), resyncKey], Effect.sync(() => {
12
- if (seen !== generation) {
13
- seen = generation;
14
- return { _tag: "Resync" };
8
+ return Stream.unwrap(Effect.gen(function* () {
9
+ const queue = yield* Queue.unbounded();
10
+ const pending = new Map();
11
+ let scheduled = false;
12
+ let resync = false;
13
+ let active = true;
14
+ function flush() {
15
+ if (scheduled) {
16
+ return;
15
17
  }
16
- return { _tag: "Changed", keys };
18
+ scheduled = true;
19
+ queueMicrotask(() => {
20
+ scheduled = false;
21
+ if (active) {
22
+ Queue.offerUnsafe(queue, resync ? { _tag: "Resync" } : { _tag: "Changed", keys: [...pending.values()] });
23
+ }
24
+ pending.clear();
25
+ resync = false;
26
+ });
27
+ }
28
+ const cancel = keys.map((key) => {
29
+ const own = readKeys([key]);
30
+ return reactivity.registerUnsafe(own, () => {
31
+ pending.set(own[0] ?? "", key);
32
+ flush();
33
+ });
34
+ });
35
+ cancel.push(reactivity.registerUnsafe([resyncKey], () => {
36
+ resync = true;
37
+ flush();
17
38
  }));
39
+ yield* Effect.addFinalizer(() => Effect.sync(() => {
40
+ active = false;
41
+ for (const stop of cancel) {
42
+ stop();
43
+ }
44
+ }));
45
+ yield* Queue.offer(queue, { _tag: "Resync" });
46
+ return Stream.fromQueue(queue);
18
47
  }));
19
48
  }
20
49
  const resync = Effect.sync(() => {
21
- generation += 1;
22
50
  reactivity.invalidateUnsafe([resyncKey]);
23
51
  });
24
52
  return { resync, stream };
@@ -1 +1 @@
1
- {"version":3,"file":"makeLiveServer.js","sourceRoot":"","sources":["../src/makeLiveServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAY,QAAQ,EAAE,MAAM,WAAW,CAAC;AAG/C,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IACjD,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC;IAChD,MAAM,SAAS,GAAG,qCAAqC,MAAM,CAAC,UAAU,EAAE,EAAE,CAAC;IAC7E,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,SAAS,MAAM,CAAC,IAAoB;QACnC,OAAO,MAAM,CAAC,MAAM,CACnB,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YAChB,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC;YACd,OAAO,UAAU,CAAC,MAAM,CACvB,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC,EAC9B,MAAM,CAAC,IAAI,CAAC,GAAa,EAAE;gBAC1B,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;oBACzB,IAAI,GAAG,UAAU,CAAC;oBAClB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;gBAC3B,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;YAClC,CAAC,CAAC,CACF,CAAC;QACH,CAAC,CAAC,CACF,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QAC/B,UAAU,IAAI,CAAC,CAAC;QAChB,UAAU,CAAC,gBAAgB,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC3B,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"makeLiveServer.js","sourceRoot":"","sources":["../src/makeLiveServer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { Context, Schema } from "effect";
2
+ import { Rpc } from "effect/rpc";
3
+ import { Key } from "./keys.ts";
4
+ declare const CommandInvalidation_base: Context.ServiceClass<CommandInvalidation, "@shivaedev/effect-contract/CommandInvalidation", import("@shivaedev/types/bivariant.ts").Bivariant<(payload: unknown, result: unknown) => readonly Key[]>>;
5
+ export declare class CommandInvalidation extends CommandInvalidation_base {
6
+ }
7
+ export declare function changesRpc(name: string): Rpc.Rpc<`${string}.$changes`, Schema.Struct<{
8
+ keys: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
9
+ readonly _tag: Schema.Literal<"List">;
10
+ readonly collection: Schema.String;
11
+ }>, Schema.Struct<{
12
+ readonly _tag: Schema.Literal<"Item">;
13
+ readonly collection: Schema.String;
14
+ readonly id: Schema.Union<readonly [Schema.String, Schema.Number]>;
15
+ }>]>>;
16
+ }>, import("effect/rpc/RpcSchema").Stream<Schema.Union<readonly [Schema.Struct<{
17
+ readonly _tag: Schema.Literal<"Changed">;
18
+ readonly keys: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
19
+ readonly _tag: Schema.Literal<"List">;
20
+ readonly collection: Schema.String;
21
+ }>, Schema.Struct<{
22
+ readonly _tag: Schema.Literal<"Item">;
23
+ readonly collection: Schema.String;
24
+ readonly id: Schema.Union<readonly [Schema.String, Schema.Number]>;
25
+ }>]>>;
26
+ }>, Schema.Struct<{
27
+ readonly _tag: Schema.Literal<"Resync">;
28
+ }>]>, Schema.Never>, Schema.Never, never, never>;
29
+ export {};
30
+ //# sourceMappingURL=serverMetadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverMetadata.d.ts","sourceRoot":"","sources":["../src/serverMetadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;;AAIhC,qBAAa,mBAAoB,SAAQ,wBAExC;CAAG;AAEJ,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM;;;;;;;;;;;;;;;;;;;;;iDAEtC"}
@@ -0,0 +1,10 @@
1
+ import { Context, Schema } from "effect";
2
+ import { Rpc } from "effect/rpc";
3
+ import { Key } from "./keys.js";
4
+ import { LiveHint } from "./live.js";
5
+ export class CommandInvalidation extends Context.Service()("@shivaedev/effect-contract/CommandInvalidation") {
6
+ }
7
+ export function changesRpc(name) {
8
+ return Rpc.make(`${name}.$changes`, { payload: { keys: Schema.Array(Key) }, stream: true, success: LiveHint });
9
+ }
10
+ //# sourceMappingURL=serverMetadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serverMetadata.js","sourceRoot":"","sources":["../src/serverMetadata.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAGrC,MAAM,OAAO,mBAAoB,SAAQ,OAAO,CAAC,OAAO,EAAoD,CAC3G,gDAAgD,CAChD;CAAG;AAEJ,MAAM,UAAU,UAAU,CAAC,IAAY;IACtC,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,WAAW,EAAE,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;AAChH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/effect-contract",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Typed query and command contracts over native Effect RPC and reactivity",
5
5
  "homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/effect-contract#readme",
6
6
  "bugs": {
@@ -23,55 +23,12 @@
23
23
  }
24
24
  },
25
25
  "exports": {
26
- "./package.json": "./package.json",
27
- "./bind.ts": {
28
- "types": "./dist/bind.d.ts",
29
- "source": "./src/bind.ts",
30
- "import": "./dist/bind.js",
31
- "default": "./dist/bind.js"
32
- },
33
- "./contract.ts": {
34
- "types": "./dist/contract.d.ts",
35
- "source": "./src/contract.ts",
36
- "import": "./dist/contract.js",
37
- "default": "./dist/contract.js"
38
- },
39
- "./keys.ts": {
40
- "types": "./dist/keys.d.ts",
41
- "source": "./src/keys.ts",
42
- "import": "./dist/keys.js",
43
- "default": "./dist/keys.js"
44
- },
45
- "./live.ts": {
46
- "types": "./dist/live.d.ts",
47
- "source": "./src/live.ts",
48
- "import": "./dist/live.js",
49
- "default": "./dist/live.js"
50
- },
51
- "./makeLiveServer.ts": {
52
- "types": "./dist/makeLiveServer.d.ts",
53
- "source": "./src/makeLiveServer.ts",
54
- "import": "./dist/makeLiveServer.js",
55
- "default": "./dist/makeLiveServer.js"
56
- },
57
- "./operation.ts": {
58
- "types": "./dist/operation.d.ts",
59
- "source": "./src/operation.ts",
60
- "import": "./dist/operation.js",
61
- "default": "./dist/operation.js"
62
- },
63
- "./rejection.ts": {
64
- "types": "./dist/rejection.d.ts",
65
- "source": "./src/rejection.ts",
66
- "import": "./dist/rejection.js",
67
- "default": "./dist/rejection.js"
26
+ "./*.ts": {
27
+ "source": "./src/*.ts",
28
+ "types": "./dist/*.d.ts",
29
+ "default": "./dist/*.js"
68
30
  },
69
- "./resume.ts": {
70
- "types": "./dist/resume.d.ts",
71
- "source": "./src/resume.ts",
72
- "import": "./dist/resume.js",
73
- "default": "./dist/resume.js"
74
- }
31
+ "./package.json": "./package.json"
75
32
  },
76
33
  "files": [
77
34
  "dist",
@@ -79,7 +36,6 @@
79
36
  "!src/**/*.test.ts",
80
37
  "!src/**/*.spec.ts",
81
38
  "!src/**/test-support",
82
- "docs",
83
39
  "CHANGELOG.md",
84
40
  "README.md"
85
41
  ],
@@ -89,14 +45,14 @@
89
45
  "devDependencies": {
90
46
  "@effect/platform-node": "4.0.4",
91
47
  "@effect/vitest": "4.0.4",
92
- "@shivaedev/effect-test": "0.5.0",
93
- "@shivaedev/quality": "0.22.0",
94
- "@types/node": "24.19.1",
95
- "@typescript/native": "npm:typescript@7.0.2",
48
+ "@shivaedev/effect-test": "0.7.1",
49
+ "@shivaedev/quality": "0.28.0",
50
+ "@shivaedev/test-story": "0.12.0",
51
+ "@types/node": "24.19.2",
96
52
  "effect": "4.0.4",
97
53
  "esbuild": "0.28.2",
98
54
  "happy-dom": "20.14.6",
99
- "typescript": "npm:@typescript/typescript6@6.0.2",
55
+ "typescript": "7.0.2",
100
56
  "vitest": "5.0.3"
101
57
  },
102
58
  "peerDependencies": {
@@ -0,0 +1,44 @@
1
+ import { Effect, Stream, SubscriptionRef } from "effect";
2
+ import type * as Atom from "effect/reactivity/Atom";
3
+ import type * as AtomRpc from "effect/reactivity/AtomRpc";
4
+ import { type Key, readKeys } from "./keys.ts";
5
+ import { type LiveStatus, live } from "./live.ts";
6
+ import { type ResumeWindow, resumeSignal } from "./resume.ts";
7
+ import type { changesRpc } from "./serverMetadata.ts";
8
+
9
+ declare const window: ResumeWindow | undefined;
10
+
11
+ export const LiveConnection = Symbol.for("@shivaedev/effect-contract/LiveConnection");
12
+ export interface LiveBound {
13
+ readonly [LiveConnection]: { readonly connection: Atom.Atom<unknown>; readonly status: Atom.Atom<LiveStatus<unknown>> };
14
+ }
15
+
16
+ export type ChangesClient = AtomRpc.AtomRpcClient<unknown, string, ReturnType<typeof changesRpc>>;
17
+
18
+ export function automaticLive(service: ChangesClient, name: string) {
19
+ const keys = Effect.runSync(SubscriptionRef.make<readonly Key[]>([]));
20
+ const known = new Set<string>();
21
+ function track(reads: readonly Key[]) {
22
+ const fresh = reads.filter((key) => {
23
+ const id = readKeys([key])[0] ?? "";
24
+ if (known.has(id)) {
25
+ return false;
26
+ }
27
+ known.add(id);
28
+ return true;
29
+ });
30
+ if (fresh.length > 0) {
31
+ Effect.runSync(SubscriptionRef.update(keys, (current) => [...current, ...fresh]));
32
+ }
33
+ }
34
+ const subscription = live(service.runtime, {
35
+ resume: resumeSignal(typeof window === "undefined" ? {} : { window }),
36
+ resyncKeys: () => Effect.runSync(SubscriptionRef.get(keys)),
37
+ stream: SubscriptionRef.changes(keys).pipe(
38
+ Stream.switchMap((current) =>
39
+ current.length === 0 ? Stream.never : Stream.unwrap(service.use((client) => Effect.succeed(client(`${name}.$changes`, { keys: current })))),
40
+ ),
41
+ ),
42
+ });
43
+ return { subscription, track };
44
+ }
package/src/bind.ts CHANGED
@@ -7,6 +7,7 @@ import type * as AtomRpc from "effect/reactivity/AtomRpc";
7
7
  import * as Reactivity from "effect/reactivity/Reactivity";
8
8
  import type { Rpc, RpcSchema } from "effect/rpc";
9
9
  import type { RpcClientError } from "effect/rpc/RpcClientError";
10
+ import { automaticLive, type LiveBound, LiveConnection } from "./automaticLive.ts";
10
11
  import type { Contract, Declared, Tag } from "./contract.ts";
11
12
  import { invalidationKeys, type Key, readKeys } from "./keys.ts";
12
13
  import type { CommandShape, QueryShape, StreamShape } from "./operation.ts";
@@ -73,12 +74,15 @@ const invalidateAfter = Effect.fn("EffectContract.invalidateAfter")(function* (k
73
74
  yield* Reactivity.invalidate(invalidationKeys(keys));
74
75
  });
75
76
 
76
- function boundQuery(service: ErasedClient, tag: string, query: QueryShape) {
77
+ function boundQuery(service: ErasedClient, tag: string, query: QueryShape, track: (keys: readonly Key[]) => void) {
77
78
  function reads(payload: unknown) {
78
79
  return readKeys(query.reads(payload));
79
80
  }
80
81
  return {
81
- query: (payload: unknown, options: QueryOptions = {}) => service.query(tag, payload, { ...options, reactivityKeys: reads(payload) }),
82
+ query: (payload: unknown, options: QueryOptions = {}) => {
83
+ track(query.reads(payload));
84
+ return service.query(tag, payload, { ...options, reactivityKeys: reads(payload) });
85
+ },
82
86
  run: (payload: unknown) => service.use((client) => client(tag, payload)),
83
87
  };
84
88
  }
@@ -108,15 +112,19 @@ export function bind<
108
112
  >(
109
113
  contract: Contract<TName, TQueries, TCommands, TRpcs, TStreams>,
110
114
  service: AtomRpc.AtomRpcClient<TSelf, TId, TClientRpcs> & MatchingClient<NoInfer<TRpcs>, TClientRpcs>,
111
- ): Bound<TName, TQueries, TCommands, TRpcs, TSelf, TStreams>;
115
+ ): Bound<TName, TQueries, TCommands, TRpcs, TSelf, TStreams> & LiveBound;
112
116
  export function bind(
113
117
  contract: { readonly declaration: Declared<string, readonly QueryShape[], readonly CommandShape[], readonly StreamShape[]> },
114
118
  service: AtomRpc.AtomRpcClient<unknown, string, Rpc.Any>,
115
119
  ): unknown {
116
120
  const { name, queries, commands, streams } = contract.declaration;
117
- return Object.fromEntries([
118
- ...queries.map((query) => [query.name, boundQuery(service, `${name}.${query.name}`, query)] as const),
119
- ...commands.map((command) => [command.name, boundCommand(service, `${name}.${command.name}`, command)] as const),
120
- ...streams.map((stream) => [stream.name, boundStream(service, `${name}.${stream.name}`)] as const),
121
- ]);
121
+ const automatic = automaticLive(service, name);
122
+ return Object.assign(
123
+ Object.fromEntries([
124
+ ...queries.map((query) => [query.name, boundQuery(service, `${name}.${query.name}`, query, automatic.track)] as const),
125
+ ...commands.map((command) => [command.name, boundCommand(service, `${name}.${command.name}`, command)] as const),
126
+ ...streams.map((stream) => [stream.name, boundStream(service, `${name}.${stream.name}`)] as const),
127
+ ]),
128
+ { [LiveConnection]: automatic.subscription },
129
+ );
122
130
  }
package/src/contract.ts CHANGED
@@ -1,6 +1,7 @@
1
- import type { Schema } from "effect";
1
+ import { Context, type Schema } from "effect";
2
2
  import { Rpc, RpcGroup, type RpcMiddleware, type RpcSchema } from "effect/rpc";
3
3
  import type { CommandShape, OperationShape, QueryShape, StreamShape } from "./operation.ts";
4
+ import { CommandInvalidation, changesRpc } from "./serverMetadata.ts";
4
5
 
5
6
  export type Tag<TContract extends string, TName extends string> = `${TContract}.${TName}`;
6
7
 
@@ -94,7 +95,7 @@ export function contract(
94
95
  payload: operation.payload,
95
96
  stream: operation.kind === "stream",
96
97
  success: operation.success,
97
- }),
98
+ }).annotateMerge(operation.kind === "command" ? Context.make(CommandInvalidation, operation.invalidates) : Context.empty()),
98
99
  );
99
- return withDeclaration(RpcGroup.make(...rpcs), { commands, name, queries, streams });
100
+ return withDeclaration(RpcGroup.make(...rpcs, changesRpc(name)), { commands, name, queries, streams });
100
101
  }
package/src/keys.ts CHANGED
@@ -26,21 +26,25 @@ export interface Collection<TName extends string, TId extends Identity> {
26
26
  readonly name: TName;
27
27
  }
28
28
 
29
- export const collection = <const TName extends string, TId extends Schema.Top & { readonly Type: Identity }>(
29
+ export function collection<const TName extends string, TId extends Schema.Top & { readonly Type: Identity }>(
30
30
  name: TName,
31
31
  _id: TId,
32
- ): Collection<TName, TId["Type"]> => ({
33
- item: (id) => ({ _tag: "Item", collection: name, id }),
34
- list: { _tag: "List", collection: name },
35
- name,
36
- });
32
+ ): Collection<TName, TId["Type"]> {
33
+ return {
34
+ item: (id) => ({ _tag: "Item", collection: name, id }),
35
+ list: { _tag: "List", collection: name },
36
+ name,
37
+ };
38
+ }
37
39
 
38
40
  function own(key: Key): string {
39
41
  return key._tag === "List" ? key.collection : `${key.collection}:${key.id}`;
40
42
  }
41
43
 
42
- export const readKeys = (keys: readonly Key[]): readonly string[] => [...new Set(keys.map(own))];
44
+ export function readKeys(keys: readonly Key[]): readonly string[] {
45
+ return [...new Set(keys.map(own))];
46
+ }
43
47
 
44
- export const invalidationKeys = (keys: readonly Key[]): readonly string[] => [
45
- ...new Set(keys.flatMap((key) => (key._tag === "List" ? [key.collection] : [own(key), key.collection]))),
46
- ];
48
+ export function invalidationKeys(keys: readonly Key[]): readonly string[] {
49
+ return [...new Set(keys.flatMap((key) => (key._tag === "List" ? [key.collection] : [own(key), key.collection])))];
50
+ }
package/src/live.ts CHANGED
@@ -16,7 +16,7 @@ export interface LiveStatus<E> {
16
16
  }
17
17
  export interface LiveOptions<E, R> {
18
18
  readonly resume?: Atom.Atom<unknown>;
19
- readonly resyncKeys: readonly Key[];
19
+ readonly resyncKeys: readonly Key[] | (() => readonly Key[]);
20
20
  readonly retryDelay?: Duration.Input;
21
21
  readonly stream: Stream.Stream<LiveHint, E, R>;
22
22
  }
@@ -39,7 +39,7 @@ export function live<R, E, TRuntimeError>(runtime: Atom.AtomRuntime<R, TRuntimeE
39
39
  update({ needsResync: true });
40
40
  return;
41
41
  }
42
- reactivity.invalidateUnsafe(readKeys(options.resyncKeys));
42
+ reactivity.invalidateUnsafe(readKeys(typeof options.resyncKeys === "function" ? options.resyncKeys() : options.resyncKeys));
43
43
  update({ needsResync: false, pending: 0 });
44
44
  }
45
45
  get.subscribe(paused, (value) => {
@@ -1,4 +1,4 @@
1
- import { Effect, Stream } from "effect";
1
+ import { Effect, Queue, Stream } from "effect";
2
2
  import * as Reactivity from "effect/reactivity/Reactivity";
3
3
  import { type Key, readKeys } from "./keys.ts";
4
4
  import type { LiveHint } from "./live.ts";
@@ -6,26 +6,55 @@ import type { LiveHint } from "./live.ts";
6
6
  export const makeLiveServer = Effect.gen(function* () {
7
7
  const reactivity = yield* Reactivity.Reactivity;
8
8
  const resyncKey = `@shivaedev/effect-contract/resync/${crypto.randomUUID()}`;
9
- let generation = 0;
10
9
  function stream(keys: readonly Key[]) {
11
10
  return Stream.unwrap(
12
- Effect.sync(() => {
13
- let seen = -1;
14
- return reactivity.stream(
15
- [...readKeys(keys), resyncKey],
16
- Effect.sync((): LiveHint => {
17
- if (seen !== generation) {
18
- seen = generation;
19
- return { _tag: "Resync" };
11
+ Effect.gen(function* () {
12
+ const queue = yield* Queue.unbounded<LiveHint>();
13
+ const pending = new Map<string, Key>();
14
+ let scheduled = false;
15
+ let resync = false;
16
+ let active = true;
17
+ function flush() {
18
+ if (scheduled) {
19
+ return;
20
+ }
21
+ scheduled = true;
22
+ queueMicrotask(() => {
23
+ scheduled = false;
24
+ if (active) {
25
+ Queue.offerUnsafe(queue, resync ? { _tag: "Resync" } : { _tag: "Changed", keys: [...pending.values()] });
20
26
  }
21
- return { _tag: "Changed", keys };
27
+ pending.clear();
28
+ resync = false;
29
+ });
30
+ }
31
+ const cancel = keys.map((key) => {
32
+ const own = readKeys([key]);
33
+ return reactivity.registerUnsafe(own, () => {
34
+ pending.set(own[0] ?? "", key);
35
+ flush();
36
+ });
37
+ });
38
+ cancel.push(
39
+ reactivity.registerUnsafe([resyncKey], () => {
40
+ resync = true;
41
+ flush();
22
42
  }),
23
43
  );
44
+ yield* Effect.addFinalizer(() =>
45
+ Effect.sync(() => {
46
+ active = false;
47
+ for (const stop of cancel) {
48
+ stop();
49
+ }
50
+ }),
51
+ );
52
+ yield* Queue.offer(queue, { _tag: "Resync" });
53
+ return Stream.fromQueue(queue);
24
54
  }),
25
55
  );
26
56
  }
27
57
  const resync = Effect.sync(() => {
28
- generation += 1;
29
58
  reactivity.invalidateUnsafe([resyncKey]);
30
59
  });
31
60
  return { resync, stream };
@@ -0,0 +1,13 @@
1
+ import { Context, Schema } from "effect";
2
+ import { Rpc } from "effect/rpc";
3
+ import { Key } from "./keys.ts";
4
+ import { LiveHint } from "./live.ts";
5
+ import type { CommandShape } from "./operation.ts";
6
+
7
+ export class CommandInvalidation extends Context.Service<CommandInvalidation, CommandShape["invalidates"]>()(
8
+ "@shivaedev/effect-contract/CommandInvalidation",
9
+ ) {}
10
+
11
+ export function changesRpc(name: string) {
12
+ return Rpc.make(`${name}.$changes`, { payload: { keys: Schema.Array(Key) }, stream: true, success: LiveHint });
13
+ }
@@ -1,45 +0,0 @@
1
- # North star
2
-
3
- ## The problem
4
-
5
- A feature crosses several boundaries. A service knows its input, result and expected failures. An RPC operation repeats them. A client needs the same types, then separately needs to know which queries a successful write should refresh. When those descriptions live apart, they drift: a failure becomes an untyped message, a newly created item does not refresh, or a broad invalidation replaces unrelated views.
6
-
7
- Effect already provides the underlying pieces: Schema, typed Effects, Rpc, RpcGroup, middleware, AtomRpc, Reactivity and Streams. The missing piece is a small declaration that ties the feature's boundary and read/write dependencies together. Replacing those native pieces would introduce another model to learn and another owner for errors, interruption and resource lifetime.
8
-
9
- ## The ideal
10
-
11
- A feature declares its boundary once. Its payload and success come from Schema. Its expected failures are tagged rejection classes that handlers and clients share. Queries name the dependencies they read. Commands name the dependencies their successful results change, including IDs returned by a create operation.
12
-
13
- That declaration is an ordinary native RPC group. Implementing, securing, hosting and calling it uses the APIs an Effect engineer already knows. The client binding adds only the mechanical work: register declared read keys on native query atoms and invalidate declared change keys after a successful call.
14
-
15
- Freshness has one vocabulary. An item change invalidates that item and its collection list. A reconciliation request uses an explicit read scope, rather than guessing that a list key covers all item queries. Optional live hints and resume signals request those same reads again. They do not hold replacement data; the successful query remains authoritative.
16
-
17
- ## What good looks like
18
-
19
- - A reader finds the input, result, rejection and dependency rule together, and an agent can infer the types without repeating them at call sites.
20
- - A domain service can raise a rejection class that several operations reuse. Generated field rejections name the form fields they describe.
21
- - An ordinary query/command feature needs no subscription. A feature needing live freshness composes a native Stream with the same registry and keys.
22
- - A successful command refreshes affected views. A rejected command leaves those reads alone. A reconnect reconciles the caller's explicit scope, including changes missed while disconnected.
23
- - Native atoms retain query state and own stale-request interruption. The helper never becomes another cache or a parallel resource owner.
24
- - Browser code can import declarations, bindings and resume signals without bringing in React or server implementation packages.
25
- - Type tests, native client tests and real HTTP tests make different claims. Each claim stops at the boundary its evidence actually exercises.
26
-
27
- ## Trade-offs
28
-
29
- - **Native composition over a shorter host setup.** Applications choose their native middleware, server host, serialization and AtomRpc service. The package removes repeated declarations without concealing those decisions.
30
- - **Precise dependency keys over convenient broad refresh.** A feature must name its reads and changes. Reconciliation must name a common scope when it needs one. Incorrect dependency discovery is an application error the library cannot infer away.
31
- - **Ordinary calls over mandatory streaming.** Live invalidation is optional. No query waits for a journal sequence, subscription cursor or change acknowledgment to return its domain value.
32
- - **Bounded observations over replay.** Paused live coordination records a bounded observation count and asks for current values on resume. It does not retain an unbounded list of events or promise recovery of each source change.
33
- - **Typed expected failures over transport-owned business wording.** Rejection schemas describe domain failures. Authentication policy, user-facing text and error presentation remain with the application and neighbouring packages.
34
- - **Consumer evidence over speculative helpers.** Add an abstraction after a consuming feature demonstrates repeated work. Preserve the native route for cases the helper does not cover.
35
-
36
- ## What it deliberately leaves out
37
-
38
- - Service implementations, repositories, SQL transactions and commit-bound publication. These belong to `effect-service`, `effect-sql` and `effect-changes`.
39
- - Authentication policy, session resolution and request identity implementation. The shared server boundary belongs to `platform`; the application's host supplies its policy.
40
- - React hooks, rendering, editable drafts and form submission state. These belong to `effect-react` and `effect-form`.
41
- - A second client cache, a custom RPC transport or a required mutation atom.
42
- - An event journal, replay cursor, authoritative data stream, offline writes or cross-process delivery policy.
43
- - Claims of SSR, authenticated live delivery, physical browser lifecycle or native-device behavior based only on a local library fixture.
44
-
45
- Package status belongs in [roadmap.md](roadmap.md). Framework composition and deployment decisions belong in the [framework roadmap](https://github.com/ShivaeDev/platform/blob/main/docs/framework/roadmap.md).
package/docs/roadmap.md DELETED
@@ -1,26 +0,0 @@
1
- # Roadmap
2
-
3
- This file owns effect-contract's implementation status and package questions. The [framework roadmap](https://github.com/ShivaeDev/platform/blob/main/docs/framework/roadmap.md) owns application composition, adoption and deployment validation; it links here instead of repeating these package milestones.
4
-
5
- ## Built
6
-
7
- - [x] `query`, `command` and `contract`: Schema payloads/results, namespaced native RPC groups, inline declarations, typed defaults, native handler layers and middleware. Compiler fixtures and native client round-trips cover the declared types and failures.
8
- - [x] `stream(name, { payload?, success, rejections?, reads })` declarations in `contract(..., { streams })` map to native RPC with `stream: true`. Bound `run` returns a native Stream. Compiler checks cover payloads, elements, errors, duplicate names and unary-client mismatch; real HTTP tests cover delivery, declared rejection, completion and client interruption releasing the server subscription. Work Board uses this path for its live subscription.
9
- - [x] Shared tagged rejection classes, generated operation-specific rejection classes and typed `reject` helpers. Field rejections derive and optionally narrow a struct's field names.
10
- - [x] Unique operation names checked in literal declarations and at construction when arrays conceal them.
11
- - [x] Collection item/list keys with native read hashing, deduplication and item-to-list mutation invalidation. Native Reactivity tests distinguish affected and unrelated items.
12
- - [x] Native AtomRpc binding with query atoms and per-call Effects. Successful commands invalidate their declared keys, including keys derived from results; rejected commands do not invalidate. Client type checks cover matching, larger, incomplete and incompatible RPC groups.
13
- - [x] Optional `LiveHint`/`Key` schemas and native live coordination: precise refresh, explicit first/reconnected scope reconciliation, a paused count capped at 256, typed stream failures and registry teardown. The framework guide owns the composed HTTP fixture evidence.
14
- - [x] `makeLiveServer` turns committed Reactivity keys into scoped hint streams and resyncs every active read scope. [Server evidence](https://github.com/ShivaeDev/platform/blob/main/packages/effect-contract/src/makeLiveServer.test.ts).
15
- - [x] Shared browser/native `resumeSignal`. Synthetic event tests cover visible versus hidden browser events, injected native callbacks, native SWR composition and listener disposal. The installed consumer checks an otherwise unobserved source's initialization and cleanup through `live`.
16
- - [x] Installed-package runtime and type fixtures for contracts, keys, rejection fields and live pause/resume composition.
17
-
18
- ## Next
19
-
20
- No additional declaration or live helper is selected beyond the server helper used by the reference order handlers. A concrete consuming feature should establish the next repeated task before this package adds an abstraction. Host/proxy behavior, authenticated subscriptions, SSR and physical browser/mobile lifecycle remain framework or application validation work.
21
-
22
- ## Open questions
23
-
24
- - Should query freshness, including live coordination and resume, remain this package's long-term scope? The APIs live here and public package consumers use them; the original declaration boundary does not by itself decide every future lifecycle helper.
25
- - Is the exported lower-level `rejectionSet` intended for application authors as well as operation construction? It is public and the operation tests exercise its implementation; direct standalone use has no dedicated regression fixture.
26
- - Which concrete consumer requirement should select the next package helper? There is no package-specific committed API backlog beyond the existing declarations, binding and freshness composition.