@shivaedev/effect-contract 0.6.0 → 0.8.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.
Files changed (44) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +44 -22
  3. package/dist/automaticLive.d.ts +3 -0
  4. package/dist/automaticLive.d.ts.map +1 -1
  5. package/dist/automaticLive.js.map +1 -1
  6. package/dist/bind.d.ts.map +1 -1
  7. package/dist/bind.js +3 -1
  8. package/dist/bind.js.map +1 -1
  9. package/dist/contract.d.ts.map +1 -1
  10. package/dist/contract.js +8 -2
  11. package/dist/contract.js.map +1 -1
  12. package/dist/internal/makeHintPolicy.d.ts +4 -0
  13. package/dist/internal/makeHintPolicy.d.ts.map +1 -0
  14. package/dist/internal/makeHintPolicy.js +25 -0
  15. package/dist/internal/makeHintPolicy.js.map +1 -0
  16. package/dist/internal/queryReactivity.d.ts +6 -0
  17. package/dist/internal/queryReactivity.d.ts.map +1 -0
  18. package/dist/internal/queryReactivity.js +72 -0
  19. package/dist/internal/queryReactivity.js.map +1 -0
  20. package/dist/live.d.ts +3 -1
  21. package/dist/live.d.ts.map +1 -1
  22. package/dist/live.js +37 -3
  23. package/dist/live.js.map +1 -1
  24. package/dist/makeLiveServer.d.ts.map +1 -1
  25. package/dist/makeLiveServer.js +5 -1
  26. package/dist/makeLiveServer.js.map +1 -1
  27. package/dist/readPolicy.d.ts +10 -0
  28. package/dist/readPolicy.d.ts.map +1 -0
  29. package/dist/readPolicy.js +24 -0
  30. package/dist/readPolicy.js.map +1 -0
  31. package/dist/serverMetadata.d.ts +4 -1
  32. package/dist/serverMetadata.d.ts.map +1 -1
  33. package/dist/serverMetadata.js +2 -0
  34. package/dist/serverMetadata.js.map +1 -1
  35. package/package.json +5 -3
  36. package/src/automaticLive.ts +6 -2
  37. package/src/bind.ts +3 -1
  38. package/src/contract.ts +9 -2
  39. package/src/internal/makeHintPolicy.ts +40 -0
  40. package/src/internal/queryReactivity.ts +84 -0
  41. package/src/live.ts +38 -3
  42. package/src/makeLiveServer.ts +12 -1
  43. package/src/readPolicy.ts +33 -0
  44. package/src/serverMetadata.ts +4 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.0 - 2026-10-19
4
+
5
+ ### Added
6
+
7
+ - `authorizeReads` accepts typed policy failures and preserves them in the query's error channel. Declare those errors in the native RPC middleware's error schema alongside its denial so clients can distinguish an outage from denied access.
8
+ - `ReadPolicyMemoWindow`, a `Context.Reference<Duration.Input>`, configures per-subscription, per-key memoisation of successful policy results. The default is one second; provide a shorter duration or `0` to disable it. Queries always check afresh, and failures are never memoised.
9
+ - Coalesce live changed hints over a default 20 ms window, with one follow-up read when hints arrive during an in-flight refetch. Configure `live()` with `coalesceWindow`, or set its returned `coalesceWindow` atom; zero retains immediate invalidation. Reconciliation stays immediate.
10
+ - Configure automatic live connections per React `SessionBoundary` with `liveCoalesceWindow`.
11
+
12
+ ### Fixed
13
+
14
+ - Failed live hint checks omit the affected keys and keep the stream open. Scoped rechecks after one second deliver an allowed key after recovery without another change or reconnect; a `false` does not schedule a recheck.
15
+
16
+ ### Breaking
17
+
18
+ - Direct readers of `ReadPolicy` now receive `Effect<boolean, unknown>` instead of an infallible Effect. Handle its error channel when invoking the reference directly. Existing infallible policies passed to `authorizeReads` still work; fallible policies should declare their errors in their RPC middleware schema.
19
+
20
+ ## 0.7.0 - 2026-10-19
21
+
22
+ ### Added
23
+
24
+ - `authorizeReads(effect, request, policy, denied)` and `ReadPolicy` in `readPolicy.ts` share a key authorization policy between query reads and live hints. `QueryReads` in `serverMetadata.ts` exposes the declared query read keys to server middleware.
25
+ - `changesRpc(name)` retains the literal contract name for typed native clients.
26
+
27
+ ### Fixed
28
+
29
+ - effect-contract's `live` coordinator no longer reconciles its read scope when `paused` is read for the first time; a component that showed the pause state while connecting used to make every loaded query read again.
30
+ - Opening a Board page sends each read once. The page starts its reads after the live connection's first update, which used to read navigation and watcher state a second time, and a failed connection does not hold the page.
31
+ - A file event that changes neither the bytes nor the modification time no longer refreshes open Board pages. A file saved with the same bytes and a new modification time still refreshes them.
32
+ - Live streams filter changed keys with the viewer's read policy and suppress empty hints, while preserving independently allowed list hints.
33
+
3
34
  ## 0.6.0 - 2026-10-18
4
35
 
5
36
  ### Added
package/README.md CHANGED
@@ -236,32 +236,19 @@ For a custom transport or explicit pause/status controls, the native live APIs r
236
236
 
237
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.
238
238
 
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:
239
+ Each item query reads its own item key, and each list query reads its list key. Commands invalidate the keys they change; an item invalidation also refreshes its collection list. Reuse the feature's `Get`, `List`, `Rename` and `Create` declarations:
240
240
 
241
241
  ```ts
242
- import { Schema } from "effect";
243
242
  import { contract } from "@shivaedev/effect-contract/contract.ts";
244
- import { collection } from "@shivaedev/effect-contract/keys.ts";
245
243
  import { LiveHint } from "@shivaedev/effect-contract/live.ts";
246
- import { query, stream } from "@shivaedev/effect-contract/operation.ts";
244
+ import { stream } from "@shivaedev/effect-contract/operation.ts";
247
245
  import { Create, Get, List, notes, Rename } from "./notes.ts";
248
246
 
249
- export const workspace = collection("workspace", Schema.String);
250
- const LiveGet = query("get", {
251
- payload: Get.payload,
252
- success: Get.success,
253
- rejections: Get.rejections,
254
- reads: ({ id }) => [notes.item(id), workspace.list],
255
- });
256
- const LiveList = query("list", {
257
- success: List.success,
258
- reads: () => [notes.list, workspace.list],
259
- });
260
- export const Subscribe = stream("subscribe", { reads: () => [workspace.list], success: LiveHint });
261
- export const LiveNotes = contract("notes", { queries: [LiveGet, LiveList], commands: [Rename, Create], streams: [Subscribe] });
247
+ export const Subscribe = stream("subscribe", { reads: () => [notes.list], success: LiveHint });
248
+ export const LiveNotes = contract("notes", { queries: [Get, List], commands: [Rename, Create], streams: [Subscribe] });
262
249
  ```
263
250
 
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.
251
+ Save the shared module as `live-notes.ts`. Keep full reconciliation in the session's explicit `resyncKeys`: enumerate the item keys the session reads and its list keys. A list key alone does not cover separately registered item queries. Having every query read a broad shared key makes every invalidation of that key refetch them all.
265
252
 
266
253
  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:
267
254
 
@@ -274,7 +261,8 @@ import { RpcClient, RpcSerialization } from "effect/rpc";
274
261
  import { bind } from "@shivaedev/effect-contract/bind.ts";
275
262
  import { live } from "@shivaedev/effect-contract/live.ts";
276
263
  import { resumeSignal } from "@shivaedev/effect-contract/resume.ts";
277
- import { LiveNotes, Subscribe } from "./live-notes.ts";
264
+ import { LiveNotes } from "./live-notes.ts";
265
+ import { notes } from "./notes.ts";
278
266
 
279
267
  class Client extends AtomRpc.Service<Client>()("app/LiveNotes", {
280
268
  group: LiveNotes,
@@ -286,7 +274,7 @@ const api = bind(LiveNotes, Client);
286
274
  const registry = AtomRegistry.make();
287
275
  const updates = live(Client.runtime, {
288
276
  stream: api.subscribe.run(undefined),
289
- resyncKeys: Subscribe.reads(undefined),
277
+ resyncKeys: [notes.item(1), notes.item(2), notes.list],
290
278
  resume: resumeSignal({ window }),
291
279
  retryDelay: "100 millis",
292
280
  });
@@ -294,7 +282,7 @@ const releaseQuery = registry.mount(api.get.query({ id: 1 }));
294
282
  const releaseConnection = registry.mount(updates.connection);
295
283
  ```
296
284
 
297
- `LiveNotes` supplies both the declaration for binding and the native RPC group for transport. Stream read keys describe its dependencies; `run` does not mount query atoms or trigger automatic invalidation. Here the caller uses `Subscribe.reads(undefined)` as the explicit live reconciliation scope. `bind` also accepts a larger native client group that preserves the contract RPC types. The [live updates guide](https://github.com/ShivaeDev/platform/blob/main/docs/framework/live-updates.md) shows server and client composition, and the [HTTP fixture](https://github.com/ShivaeDev/platform/tree/main/packages/effect-contract/src/test-support/live) supplies a complete working server.
285
+ `LiveNotes` supplies both the declaration for binding and the native RPC group for transport. Stream read keys describe its dependencies; `run` does not mount query atoms or trigger automatic invalidation. Here the caller explicitly reconciles two items and their list. `bind` also accepts a larger native client group that preserves the contract RPC types. The [live updates guide](https://github.com/ShivaeDev/platform/blob/main/docs/framework/live-updates.md) shows server and client composition, and the [HTTP fixture](https://github.com/ShivaeDev/platform/tree/main/packages/effect-contract/src/test-support/live) supplies a complete working server.
298
286
 
299
287
  | Observation | Invalidation |
300
288
  | --- | --- |
@@ -302,6 +290,8 @@ const releaseConnection = registry.mount(updates.connection);
302
290
  | `Resync` | The exact `resyncKeys` read scope. |
303
291
  | First hint on a new or reconnected stream | The full explicit scope, even if the hint is targeted. |
304
292
 
293
+ Changed hints share a fixed 20 ms window by default. Their keys merge, and each affected bound query refetches once per window. A bound query already fetching keeps its request and refetches once more after it settles if further hints arrive. Set `coalesceWindow` in `live()` to a native Duration, or set `SessionBoundary`'s `liveCoalesceWindow` for automatic session connections. Zero restores immediate per-hint invalidation, including interruption of in-flight reads. Full reconciliation on first hint, reconnect, resume and unpause stays immediate. Registry teardown cancels a pending window.
294
+
305
295
  `LiveHint` and `Key` are schemas. Decoding rejects unknown hint tags and item keys with an invalid ID type.
306
296
 
307
297
  ```ts
@@ -328,6 +318,36 @@ Call the release functions and dispose the registry when the application's root
328
318
 
329
319
  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
320
 
321
+ For viewer-scoped hints, call `authorizeReads` once in the application's native authorization middleware, after resolving the session:
322
+
323
+ ```ts
324
+ import { authorizeReads } from "@shivaedev/effect-contract/readPolicy.ts";
325
+
326
+ const guard = Layer.succeed(Guard, (effect, request) =>
327
+ Effect.flatMap(resolveSession(request.headers), (session) =>
328
+ authorizeReads(effect, request, (key) => canRead(session, key), Effect.fail(new Denied())).pipe(
329
+ Effect.provideService(Session, session),
330
+ ),
331
+ ),
332
+ );
333
+ ```
334
+
335
+ The application supplies `resolveSession`, `canRead` and its middleware's `Denied` failure. Apply this middleware to the contract, including its reserved change stream. The policy returns `Effect<boolean, PolicyError, PolicyServices>` for a key. `authorizeReads` checks every declared query read key before running its handler and supplies the same policy to `makeLiveServer` streams, with the request's Effect context. Queries need no additional authorization declaration. A `false` returns the supplied denial; a failed policy returns its original typed error. Declare policy errors alongside the denial in the native middleware's error schema so they reach the client, for example `error: Schema.Union([Denied, GitHubUnavailable])`.
336
+
337
+ Each changed hint checks its keys against that viewer's policy before delivery and omits denied keys. An empty hint is suppressed. Item and list keys are checked independently: allow a list key when the viewer may read that list, even when a changed item is private to another viewer. The list query still selects its visible rows. Without a supplied `ReadPolicy`, streams keep the default of allowing their subscribed keys; applications own the policy.
338
+
339
+ A failed hint check omits that key without ending the stream. The subscription rechecks failed keys after one second and repeats while checks fail. Once allowed, the key reaches the existing consumer even without another change or a reconnect. A `false` does not schedule a recheck. Interruption of the subscription stops its pending rechecks.
340
+
341
+ Successful boolean results are memoised per subscription stream and key for one second by default, including `false`; failures are never memoised. Queries always run their policy afresh. The memo holds at most the subscription's key count. It can delay filtering a revoked key from hints by at most that window; hints contain keys, and the subsequent query still checks current access before returning data. Set `ReadPolicyMemoWindow` around the request effect to shorten the window or use `0` to turn memoisation off:
342
+
343
+ ```ts
344
+ import { ReadPolicyMemoWindow } from "@shivaedev/effect-contract/readPolicy.ts";
345
+
346
+ authorizeReads(effect, request, canRead, Effect.fail(new Denied())).pipe(
347
+ Effect.provideService(ReadPolicyMemoWindow, "100 millis"),
348
+ );
349
+ ```
350
+
331
351
  ### API by module
332
352
 
333
353
  Import the module that defines the name; the package has no root entry.
@@ -339,8 +359,10 @@ Import the module that defines the name; the package has no root entry.
339
359
  | `rejection.ts` | `fieldRejection(struct, fields?)`; lower-level `rejectionSet(specs)`, which constructs the rejection classes, union schema and failure helpers used by operations. |
340
360
  | `keys.ts` | `collection(name, idSchema)`; `Key` schema; `readKeys(keys)` and `invalidationKeys(keys)`. |
341
361
  | `bind.ts` | `bind(contract, atomRpcService)`, exposing `{ query, run }` for queries, `{ run }` Effects for commands and `{ run }` native Streams for streams. |
342
- | `live.ts` | `LiveHint` schema; `live(runtime, { stream, resyncKeys, resume?, retryDelay? })`, exposing `connection`, `paused` and `status`. |
362
+ | `live.ts` | `LiveHint` schema; `live(runtime, { stream, resyncKeys, coalesceWindow?, resume?, retryDelay? })`, exposing `connection`, `coalesceWindow`, `paused` and `status`. |
343
363
  | `makeLiveServer.ts` | `makeLiveServer`, an Effect requiring `Reactivity` that creates scoped server hint streams and a `resync` Effect. |
364
+ | `readPolicy.ts` | `authorizeReads(effect, request, policy, denied)`; `ReadPolicy`, the request-local fallible key policy used by server hint streams; `ReadPolicyMemoWindow`, a `Context.Reference<Duration.Input>` controlling each stream's memo duration. |
365
+ | `serverMetadata.ts` | `QueryReads`, `CommandInvalidation` and `changesRpc(name)` for server adapters. |
344
366
  | `resume.ts` | `resumeSignal({ window?, native? })`, returning a native numeric signal atom. |
345
367
 
346
368
  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.
@@ -1,3 +1,4 @@
1
+ import { type Duration } from "effect";
1
2
  import type * as Atom from "effect/reactivity/Atom";
2
3
  import type * as AtomRpc from "effect/reactivity/AtomRpc";
3
4
  import { type Key } from "./keys.ts";
@@ -6,6 +7,7 @@ import type { changesRpc } from "./serverMetadata.ts";
6
7
  export declare const LiveConnection: unique symbol;
7
8
  export interface LiveBound {
8
9
  readonly [LiveConnection]: {
10
+ readonly coalesceWindow: Atom.Writable<Duration.Input>;
9
11
  readonly connection: Atom.Atom<unknown>;
10
12
  readonly status: Atom.Atom<LiveStatus<unknown>>;
11
13
  };
@@ -13,6 +15,7 @@ export interface LiveBound {
13
15
  export type ChangesClient = AtomRpc.AtomRpcClient<unknown, string, ReturnType<typeof changesRpc>>;
14
16
  export declare function automaticLive(service: ChangesClient, name: string): {
15
17
  subscription: {
18
+ coalesceWindow: Atom.Writable<Duration.Input, Duration.Input>;
16
19
  connection: Atom.Atom<import("effect/reactivity/AsyncResult").AsyncResult<void, never>>;
17
20
  paused: Atom.Writable<boolean, boolean>;
18
21
  status: Atom.Writable<LiveStatus<import("effect/rpc/RpcClientError").RpcClientError>, LiveStatus<import("effect/rpc/RpcClientError").RpcClientError>>;
@@ -1 +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"}
1
+ {"version":3,"file":"automaticLive.d.ts","sourceRoot":"","sources":["../src/automaticLive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAmC,MAAM,QAAQ,CAAC;AACxE,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;QAC1B,QAAQ,CAAC,cAAc,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACvD,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;KAChD,CAAC;CACF;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"}
@@ -1 +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"}
1
+ {"version":3,"file":"automaticLive.js","sourceRoot":"","sources":["../src/automaticLive.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAGxE,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;AAWtF,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"}
@@ -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,18 +1,20 @@
1
1
  import { Effect, Stream } from "effect";
2
2
  import * as Reactivity from "effect/reactivity/Reactivity";
3
+ import { queryReactivity } from "#internal/queryReactivity.ts";
3
4
  import { automaticLive, LiveConnection } from "./automaticLive.js";
4
5
  import { invalidationKeys, readKeys } from "./keys.js";
5
6
  const invalidateAfter = Effect.fn("EffectContract.invalidateAfter")(function* (keys) {
6
7
  yield* Reactivity.invalidate(invalidationKeys(keys));
7
8
  });
8
9
  function boundQuery(service, tag, query, track) {
10
+ const reactive = queryReactivity(service.runtime);
9
11
  function reads(payload) {
10
12
  return readKeys(query.reads(payload));
11
13
  }
12
14
  return {
13
15
  query: (payload, options = {}) => {
14
16
  track(query.reads(payload));
15
- return service.query(tag, payload, { ...options, reactivityKeys: reads(payload) });
17
+ return reactive(service.query(tag, payload, options), reads(payload));
16
18
  },
17
19
  run: (payload) => service.use((client) => client(tag, payload)),
18
20
  };
package/dist/bind.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bind.js","sourceRoot":"","sources":["../src/bind.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bind.js","sourceRoot":"","sources":["../src/bind.ts"],"names":[],"mappings":"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"}
@@ -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,12 +1,18 @@
1
1
  import { Context } from "effect";
2
2
  import { Rpc, RpcGroup } from "effect/rpc";
3
- import { CommandInvalidation, changesRpc } from "./serverMetadata.js";
3
+ import { CommandInvalidation, changesRpc, QueryReads } from "./serverMetadata.js";
4
4
  function assertUnique(operations) {
5
5
  const duplicated = operations.map(({ name }) => name).filter((name, index, names) => names.indexOf(name) !== index);
6
6
  if (duplicated.length > 0) {
7
7
  throw new Error(`Operation names must be unique; duplicated: ${[...new Set(duplicated)].join(", ")}`);
8
8
  }
9
9
  }
10
+ function annotations(operation) {
11
+ if (operation.kind === "command") {
12
+ return Context.make(CommandInvalidation, operation.invalidates);
13
+ }
14
+ return operation.kind === "query" ? Context.make(QueryReads, operation.reads) : Context.empty();
15
+ }
10
16
  function withDeclaration(group, declaration) {
11
17
  const nativeMiddleware = group.middleware.bind(group);
12
18
  return Object.assign(group, {
@@ -22,7 +28,7 @@ export function contract(name, operations) {
22
28
  payload: operation.payload,
23
29
  stream: operation.kind === "stream",
24
30
  success: operation.success,
25
- }).annotateMerge(operation.kind === "command" ? Context.make(CommandInvalidation, operation.invalidates) : Context.empty()));
31
+ }).annotateMerge(annotations(operation)));
26
32
  return withDeclaration(RpcGroup.make(...rpcs, changesRpc(name)), { commands, name, queries, streams });
27
33
  }
28
34
  //# sourceMappingURL=contract.js.map
@@ -1 +1 @@
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"}
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,UAAU,EAAE,MAAM,qBAAqB,CAAC;AA4ClF,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;AAED,SAAS,WAAW,CAAC,SAAkD;IACtE,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAClC,OAAO,OAAO,CAAC,IAAI,CAAC,mBAAmB,EAAE,SAAS,CAAC,WAAW,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,SAAS,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;AACjG,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,WAAW,CAAC,SAAS,CAAC,CAAC,CACxC,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"}
@@ -0,0 +1,4 @@
1
+ import { Effect } from "effect";
2
+ import type { Key } from "#keys.ts";
3
+ export declare const makeHintPolicy: (capacity: number, retry: (keys: readonly Key[]) => Effect.Effect<void>) => Effect.Effect<(keys: readonly Key[]) => Effect.Effect<Key[], never, never>, never, import("effect/Scope").Scope>;
4
+ //# sourceMappingURL=makeHintPolicy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"makeHintPolicy.d.ts","sourceRoot":"","sources":["../../src/internal/makeHintPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,MAAM,EAAe,MAAM,QAAQ,CAAC;AAC1D,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAGpC,eAAO,MAAM,cAAc,mCAAgE,SAAS,GAAG,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,0BAqBlH,SAAS,GAAG,EAAE,6EAc3B,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { Cache, Data, Effect, Exit, Queue } from "effect";
2
+ import { ReadPolicy, ReadPolicyMemoWindow } from "#readPolicy.ts";
3
+ export const makeHintPolicy = Effect.fnUntraced(function* (capacity, retry) {
4
+ const policy = yield* ReadPolicy;
5
+ const window = yield* ReadPolicyMemoWindow;
6
+ const cache = yield* Cache.makeWith(policy, {
7
+ capacity: Math.max(1, capacity),
8
+ timeToLive: (exit) => (Exit.isSuccess(exit) ? window : 0),
9
+ });
10
+ const failed = new Map();
11
+ const wake = yield* Queue.unbounded();
12
+ yield* Queue.take(wake).pipe(Effect.andThen(Effect.sleep("1 second")), Effect.andThen(Effect.suspend(() => {
13
+ const keys = [...failed.values()];
14
+ failed.clear();
15
+ return retry(keys);
16
+ })), Effect.forever, Effect.forkScoped);
17
+ return (keys) => Effect.filter(keys, (key) => Cache.get(cache, new Data.Class(key)).pipe(Effect.catch(() => Effect.gen(function* () {
18
+ if (failed.size === 0) {
19
+ yield* Queue.offer(wake, undefined);
20
+ }
21
+ failed.set(JSON.stringify(key), key);
22
+ return false;
23
+ }))));
24
+ });
25
+ //# sourceMappingURL=makeHintPolicy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"makeHintPolicy.js","sourceRoot":"","sources":["../../src/internal/makeHintPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE1D,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAElE,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,QAAgB,EAAE,KAAoD;IAChI,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC;IACjC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,oBAAoB,CAAC;IAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE;QAC3C,QAAQ,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC;QAC/B,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;KACzD,CAAC,CAAC;IACH,MAAM,MAAM,GAAG,IAAI,GAAG,EAAe,CAAC;IACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,SAAS,EAAQ,CAAC;IAC5C,KAAK,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAC3B,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,EACxC,MAAM,CAAC,OAAO,CACb,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;QACnB,MAAM,IAAI,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAClC,MAAM,CAAC,KAAK,EAAE,CAAC;QACf,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC,CAAC,CACF,EACD,MAAM,CAAC,OAAO,EACd,MAAM,CAAC,UAAU,CACjB,CAAC;IACF,OAAO,CAAC,IAAoB,EAAE,EAAE,CAC/B,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAC3B,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,IAAI,CAAC,KAAK,CAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAC9C,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,CACjB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACrC,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;QACrC,OAAO,KAAK,CAAC;IACd,CAAC,CAAC,CACF,CACD,CACD,CAAC;AACJ,CAAC,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import * as AsyncResult from "effect/reactivity/AsyncResult";
2
+ import * as Atom from "effect/reactivity/Atom";
3
+ import * as Reactivity from "effect/reactivity/Reactivity";
4
+ export declare function invalidateLive(reactivity: Reactivity.Reactivity, keys: readonly string[], active: () => boolean): void;
5
+ export declare function queryReactivity<R, E>(runtime: Atom.AtomRuntime<R, E>): (atom: Atom.Atom<AsyncResult.AsyncResult<unknown, unknown>>, keys: readonly string[]) => Atom.Atom<AsyncResult.AsyncResult<unknown, unknown>>;
6
+ //# sourceMappingURL=queryReactivity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queryReactivity.d.ts","sourceRoot":"","sources":["../../src/internal/queryReactivity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,WAAW,MAAM,+BAA+B,CAAC;AAC7D,OAAO,KAAK,IAAI,MAAM,wBAAwB,CAAC;AAC/C,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAQ3D,wBAAgB,cAAc,CAAC,UAAU,EAAE,UAAU,CAAC,UAAU,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,OAAO,QAO/G;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,sEAIzC,SAAS,MAAM,EAAE,0DA2D5C"}
@@ -0,0 +1,72 @@
1
+ import { Effect } from "effect";
2
+ import * as AsyncResult from "effect/reactivity/AsyncResult";
3
+ import * as Atom from "effect/reactivity/Atom";
4
+ import * as Reactivity from "effect/reactivity/Reactivity";
5
+ const batches = new WeakMap();
6
+ export function invalidateLive(reactivity, keys, active) {
7
+ batches.set(reactivity, { active, seen: new Set() });
8
+ try {
9
+ reactivity.invalidateUnsafe(keys);
10
+ }
11
+ finally {
12
+ batches.delete(reactivity);
13
+ }
14
+ }
15
+ export function queryReactivity(runtime) {
16
+ const service = runtime.atom(Effect.service(Reactivity.Reactivity));
17
+ const cached = new WeakMap();
18
+ return (atom, keys) => {
19
+ const existing = cached.get(atom);
20
+ if (existing !== undefined) {
21
+ return existing;
22
+ }
23
+ const source = Atom.setIdleTTL(Atom.readable((get) => atom.read(get), atom.refresh), 0);
24
+ let wrapped = Atom.transform(source, (get) => {
25
+ const context = get(service);
26
+ if (!AsyncResult.isSuccess(context)) {
27
+ return get(source);
28
+ }
29
+ const reactivity = context.value;
30
+ let pending;
31
+ function invalidate() {
32
+ const batch = batches.get(reactivity);
33
+ if (batch !== undefined) {
34
+ if (batch.seen.has(invalidate)) {
35
+ return;
36
+ }
37
+ batch.seen.add(invalidate);
38
+ if (get.once(source).waiting) {
39
+ pending = batch.active;
40
+ return;
41
+ }
42
+ }
43
+ pending = undefined;
44
+ get.refresh(source);
45
+ }
46
+ get.addFinalizer(reactivity.registerUnsafe(keys, invalidate));
47
+ get.subscribe(source, (value) => {
48
+ get.setSelf(value);
49
+ if (pending !== undefined && !value.waiting) {
50
+ const active = pending;
51
+ pending = undefined;
52
+ if (active()) {
53
+ get.refresh(source);
54
+ }
55
+ }
56
+ });
57
+ return get.once(source);
58
+ }, { initialValueTarget: source });
59
+ if (atom.keepAlive) {
60
+ wrapped = Atom.keepAlive(wrapped);
61
+ }
62
+ else if (atom.idleTTL !== undefined) {
63
+ wrapped = Atom.setIdleTTL(wrapped, atom.idleTTL);
64
+ }
65
+ if (Atom.isSerializable(atom)) {
66
+ Object.assign(wrapped, { [Atom.SerializableTypeId]: atom[Atom.SerializableTypeId] });
67
+ }
68
+ cached.set(atom, wrapped);
69
+ return wrapped;
70
+ };
71
+ }
72
+ //# sourceMappingURL=queryReactivity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queryReactivity.js","sourceRoot":"","sources":["../../src/internal/queryReactivity.ts"],"names":[],"mappings":"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"}
package/dist/live.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type Cause, type Duration, Option, Schema, Stream } from "effect";
1
+ import { type Cause, Duration, Option, Schema, Stream } from "effect";
2
2
  import * as Atom from "effect/reactivity/Atom";
3
3
  import { Key } from "./keys.ts";
4
4
  export declare const LiveHint: Schema.Union<readonly [Schema.Struct<{
@@ -22,12 +22,14 @@ export interface LiveStatus<E> {
22
22
  readonly pending: number;
23
23
  }
24
24
  export interface LiveOptions<E, R> {
25
+ readonly coalesceWindow?: Duration.Input;
25
26
  readonly resume?: Atom.Atom<unknown>;
26
27
  readonly resyncKeys: readonly Key[] | (() => readonly Key[]);
27
28
  readonly retryDelay?: Duration.Input;
28
29
  readonly stream: Stream.Stream<LiveHint, E, R>;
29
30
  }
30
31
  export declare function live<R, E, TRuntimeError>(runtime: Atom.AtomRuntime<R, TRuntimeError>, options: LiveOptions<E, R>): {
32
+ coalesceWindow: Atom.Writable<Duration.Input, Duration.Input>;
31
33
  connection: Atom.Atom<import("effect/reactivity/AsyncResult").AsyncResult<void, TRuntimeError>>;
32
34
  paused: Atom.Writable<boolean, boolean>;
33
35
  status: Atom.Writable<LiveStatus<E>, LiveStatus<E>>;
@@ -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,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"}
1
+ {"version":3,"file":"live.d.ts","sourceRoot":"","sources":["../src/live.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,QAAQ,EAAU,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9E,OAAO,KAAK,IAAI,MAAM,wBAAwB,CAAC;AAG/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,cAAc,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC;IACzC,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;;;;;EAwFhH"}
package/dist/live.js CHANGED
@@ -1,6 +1,7 @@
1
- import { Effect, Option, Schema, Stream } from "effect";
1
+ import { Duration, Effect, Option, Schema, Stream } from "effect";
2
2
  import * as Atom from "effect/reactivity/Atom";
3
3
  import * as Reactivity from "effect/reactivity/Reactivity";
4
+ import { invalidateLive } from "#internal/queryReactivity.ts";
4
5
  import { invalidationKeys, Key, readKeys } from "./keys.js";
5
6
  export const LiveHint = Schema.Union([
6
7
  Schema.Struct({ _tag: Schema.Literal("Changed"), keys: Schema.Array(Key) }),
@@ -8,6 +9,7 @@ export const LiveHint = Schema.Union([
8
9
  ]);
9
10
  const MAX_PENDING = 256;
10
11
  export function live(runtime, options) {
12
+ const coalesceWindow = Atom.keepAlive(Atom.make(options.coalesceWindow ?? "20 millis"));
11
13
  const paused = Atom.make(false);
12
14
  const status = Atom.make({ connection: "connecting", failure: Option.none(), needsResync: true, pending: 0 });
13
15
  const connection = runtime
@@ -17,7 +19,37 @@ export function live(runtime, options) {
17
19
  function update(value) {
18
20
  get.set(status, { ...get.once(status), ...value });
19
21
  }
22
+ const pending = new Set();
23
+ let timer;
24
+ function clear() {
25
+ clearTimeout(timer);
26
+ timer = undefined;
27
+ pending.clear();
28
+ }
29
+ let active = true;
30
+ get.addFinalizer(() => {
31
+ active = false;
32
+ clear();
33
+ });
34
+ function changed(changedKeys) {
35
+ const delay = Duration.toMillis(Duration.fromInputUnsafe(get.once(coalesceWindow)));
36
+ if (delay === 0) {
37
+ reactivity.invalidateUnsafe(invalidationKeys(changedKeys));
38
+ return;
39
+ }
40
+ for (const key of invalidationKeys(changedKeys)) {
41
+ pending.add(key);
42
+ }
43
+ timer ??= setTimeout(() => {
44
+ const keys = [...pending];
45
+ clear();
46
+ if (!get.once(paused)) {
47
+ invalidateLive(reactivity, keys, () => active && !get.once(paused));
48
+ }
49
+ }, delay);
50
+ }
20
51
  function resync() {
52
+ clear();
21
53
  if (get.once(paused)) {
22
54
  update({ needsResync: true });
23
55
  return;
@@ -25,6 +57,8 @@ export function live(runtime, options) {
25
57
  reactivity.invalidateUnsafe(readKeys(typeof options.resyncKeys === "function" ? options.resyncKeys() : options.resyncKeys));
26
58
  update({ needsResync: false, pending: 0 });
27
59
  }
60
+ // A subscription alone does not build the flag, so its first read would otherwise announce a change.
61
+ get.once(paused);
28
62
  get.subscribe(paused, (value) => {
29
63
  if (!value) {
30
64
  resync();
@@ -46,7 +80,7 @@ export function live(runtime, options) {
46
80
  resync();
47
81
  }
48
82
  else {
49
- reactivity.invalidateUnsafe(invalidationKeys(hint.keys));
83
+ changed(hint.keys);
50
84
  }
51
85
  isFirst = false;
52
86
  }
@@ -55,6 +89,6 @@ export function live(runtime, options) {
55
89
  yield* Effect.forever(attempt);
56
90
  }))
57
91
  .pipe(Atom.setIdleTTL(0));
58
- return { connection, paused, status };
92
+ return { coalesceWindow, connection, paused, status };
59
93
  }
60
94
  //# sourceMappingURL=live.js.map
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":"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"}
@@ -1 +1 @@
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
+ {"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;AAE3D,OAAO,EAAE,KAAK,GAAG,EAAY,MAAM,WAAW,CAAC;AAG/C,eAAO,MAAM,cAAc;;mBAGJ,SAAS,GAAG,EAAE;;;;;;;;;;;;;gCA8DnC,CAAC"}
@@ -1,5 +1,6 @@
1
1
  import { Effect, Queue, Stream } from "effect";
2
2
  import * as Reactivity from "effect/reactivity/Reactivity";
3
+ import { makeHintPolicy } from "#internal/makeHintPolicy.ts";
3
4
  import { readKeys } from "./keys.js";
4
5
  export const makeLiveServer = Effect.gen(function* () {
5
6
  const reactivity = yield* Reactivity.Reactivity;
@@ -7,6 +8,7 @@ export const makeLiveServer = Effect.gen(function* () {
7
8
  function stream(keys) {
8
9
  return Stream.unwrap(Effect.gen(function* () {
9
10
  const queue = yield* Queue.unbounded();
11
+ const allowedKeys = yield* makeHintPolicy(keys.length, (retryKeys) => Queue.offer(queue, { _tag: "Changed", keys: retryKeys }).pipe(Effect.asVoid));
10
12
  const pending = new Map();
11
13
  let scheduled = false;
12
14
  let resync = false;
@@ -43,7 +45,9 @@ export const makeLiveServer = Effect.gen(function* () {
43
45
  }
44
46
  }));
45
47
  yield* Queue.offer(queue, { _tag: "Resync" });
46
- return Stream.fromQueue(queue);
48
+ return Stream.fromQueue(queue).pipe(Stream.mapEffect((hint) => hint._tag === "Resync"
49
+ ? Effect.succeed(hint)
50
+ : Effect.map(allowedKeys(hint.keys), (allowed) => ({ _tag: "Changed", keys: allowed }))), Stream.filter((hint) => hint._tag === "Resync" || hint.keys.length > 0));
47
51
  }));
48
52
  }
49
53
  const resync = Effect.sync(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"makeLiveServer.js","sourceRoot":"","sources":["../src/makeLiveServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC/C,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,SAAS,MAAM,CAAC,IAAoB;QACnC,OAAO,MAAM,CAAC,MAAM,CACnB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,KAAK,CAAC,SAAS,EAAY,CAAC;YACjD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAe,CAAC;YACvC,IAAI,SAAS,GAAG,KAAK,CAAC;YACtB,IAAI,MAAM,GAAG,KAAK,CAAC;YACnB,IAAI,MAAM,GAAG,IAAI,CAAC;YAClB,SAAS,KAAK;gBACb,IAAI,SAAS,EAAE,CAAC;oBACf,OAAO;gBACR,CAAC;gBACD,SAAS,GAAG,IAAI,CAAC;gBACjB,cAAc,CAAC,GAAG,EAAE;oBACnB,SAAS,GAAG,KAAK,CAAC;oBAClB,IAAI,MAAM,EAAE,CAAC;wBACZ,KAAK,CAAC,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;oBAC1G,CAAC;oBACD,OAAO,CAAC,KAAK,EAAE,CAAC;oBAChB,MAAM,GAAG,KAAK,CAAC;gBAChB,CAAC,CAAC,CAAC;YACJ,CAAC;YACD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE;gBAC/B,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC5B,OAAO,UAAU,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE;oBAC1C,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;oBAC/B,KAAK,EAAE,CAAC;gBACT,CAAC,CAAC,CAAC;YACJ,CAAC,CAAC,CAAC;YACH,MAAM,CAAC,IAAI,CACV,UAAU,CAAC,cAAc,CAAC,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE;gBAC3C,MAAM,GAAG,IAAI,CAAC;gBACd,KAAK,EAAE,CAAC;YACT,CAAC,CAAC,CACF,CAAC;YACF,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAC/B,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;gBAChB,MAAM,GAAG,KAAK,CAAC;gBACf,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;oBAC3B,IAAI,EAAE,CAAC;gBACR,CAAC;YACF,CAAC,CAAC,CACF,CAAC;YACF,KAAK,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC9C,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC,CAAC,CACF,CAAC;IACH,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QAC/B,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,10 @@
1
+ import { Context, type Duration, Effect } from "effect";
2
+ import type { Rpc } from "effect/rpc";
3
+ import type { Key } from "./keys.ts";
4
+ export declare const ReadPolicy: Context.Reference<(key: Key) => Effect.Effect<boolean, unknown>>;
5
+ export declare const ReadPolicyMemoWindow: Context.Reference<Duration.Input>;
6
+ export declare const authorizeReads: <A, E, R, TPolicyServices, TDenied, TPolicyError>(effect: Effect.Effect<A, E, R>, request: {
7
+ readonly rpc: Rpc.AnyWithProps;
8
+ readonly payload: unknown;
9
+ }, policy: (key: Key) => Effect.Effect<boolean, TPolicyError, TPolicyServices>, denied: Effect.Effect<never, TDenied, never>) => Effect.Effect<A, E | TDenied | TPolicyError, TPolicyServices | Exclude<R, never>>;
10
+ //# sourceMappingURL=readPolicy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readPolicy.d.ts","sourceRoot":"","sources":["../src/readPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AAChE,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAGrC,eAAO,MAAM,UAAU,0BAA2B,GAAG,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,CAEvF,CAAC;AAEH,eAAO,MAAM,oBAAoB,mCAE/B,CAAC;AAEH,eAAO,MAAM,cAAc,GAAgC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,eAAe,EAAE,OAAO,EAAE,YAAY;kBAEhF,GAAG,CAAC,YAAY;sBAAoB,OAAO;iBACtD,GAAG,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,EAAE,eAAe,CAAC,oIAgB1E,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { Context, Effect, Option } from "effect";
2
+ import { QueryReads } from "./serverMetadata.js";
3
+ export const ReadPolicy = Context.Reference("@shivaedev/effect-contract/ReadPolicy", {
4
+ defaultValue: () => () => Effect.succeed(true),
5
+ });
6
+ export const ReadPolicyMemoWindow = Context.Reference("@shivaedev/effect-contract/ReadPolicyMemoWindow", {
7
+ defaultValue: () => "1 second",
8
+ });
9
+ export const authorizeReads = Effect.fnUntraced(function* (effect, request, policy, denied) {
10
+ const context = yield* Effect.context();
11
+ function canRead(key) {
12
+ return Effect.provideContext(policy(key), context);
13
+ }
14
+ const reads = Context.getOption(request.rpc.annotations, QueryReads);
15
+ if (Option.isSome(reads)) {
16
+ for (const key of reads.value(request.payload)) {
17
+ if (!(yield* canRead(key))) {
18
+ return yield* denied;
19
+ }
20
+ }
21
+ }
22
+ return yield* Effect.provideService(effect, ReadPolicy, canRead);
23
+ });
24
+ //# sourceMappingURL=readPolicy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readPolicy.js","sourceRoot":"","sources":["../src/readPolicy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAiB,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhE,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,MAAM,CAAC,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,CAAgD,uCAAuC,EAAE;IACnI,YAAY,EAAE,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;CAC9C,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,oBAAoB,GAAG,OAAO,CAAC,SAAS,CAAiB,iDAAiD,EAAE;IACxH,YAAY,EAAE,GAAG,EAAE,CAAC,UAAU;CAC9B,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EACxD,MAA8B,EAC9B,OAAsE,EACtE,MAA2E,EAC3E,MAAqC;IAErC,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,EAAmB,CAAC;IACzD,SAAS,OAAO,CAAC,GAAQ;QACxB,OAAO,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IACrE,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAChD,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC5B,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC;YACtB,CAAC;QACF,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC"}
@@ -1,10 +1,13 @@
1
1
  import { Context, Schema } from "effect";
2
2
  import { Rpc } from "effect/rpc";
3
3
  import { Key } from "./keys.ts";
4
+ declare const QueryReads_base: Context.ServiceClass<QueryReads, "@shivaedev/effect-contract/QueryReads", import("@shivaedev/types/bivariant.ts").Bivariant<(payload: unknown) => readonly Key[]>>;
5
+ export declare class QueryReads extends QueryReads_base {
6
+ }
4
7
  declare const CommandInvalidation_base: Context.ServiceClass<CommandInvalidation, "@shivaedev/effect-contract/CommandInvalidation", import("@shivaedev/types/bivariant.ts").Bivariant<(payload: unknown, result: unknown) => readonly Key[]>>;
5
8
  export declare class CommandInvalidation extends CommandInvalidation_base {
6
9
  }
7
- export declare function changesRpc(name: string): Rpc.Rpc<`${string}.$changes`, Schema.Struct<{
10
+ export declare function changesRpc<const TName extends string>(name: TName): Rpc.Rpc<`${TName}.$changes`, Schema.Struct<{
8
11
  keys: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
9
12
  readonly _tag: Schema.Literal<"List">;
10
13
  readonly collection: Schema.String;
@@ -1 +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"}
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,UAAW,SAAQ,eAA2F;CAAG;;AAE9H,qBAAa,mBAAoB,SAAQ,wBAExC;CAAG;AAEJ,wBAAgB,UAAU,CAAC,KAAK,CAAC,KAAK,SAAS,MAAM,EAAE,IAAI,EAAE,KAAK;;;;;;;;;;;;;;;;;;;;;iDAEjE"}
@@ -2,6 +2,8 @@ import { Context, Schema } from "effect";
2
2
  import { Rpc } from "effect/rpc";
3
3
  import { Key } from "./keys.js";
4
4
  import { LiveHint } from "./live.js";
5
+ export class QueryReads extends Context.Service()("@shivaedev/effect-contract/QueryReads") {
6
+ }
5
7
  export class CommandInvalidation extends Context.Service()("@shivaedev/effect-contract/CommandInvalidation") {
6
8
  }
7
9
  export function changesRpc(name) {
@@ -1 +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"}
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,UAAW,SAAQ,OAAO,CAAC,OAAO,EAAmC,CAAC,uCAAuC,CAAC;CAAG;AAE9H,MAAM,OAAO,mBAAoB,SAAQ,OAAO,CAAC,OAAO,EAAoD,CAC3G,gDAAgD,CAChD;CAAG;AAEJ,MAAM,UAAU,UAAU,CAA6B,IAAW;IACjE,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.6.0",
3
+ "version": "0.8.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": {
@@ -28,6 +28,7 @@
28
28
  "types": "./dist/*.d.ts",
29
29
  "default": "./dist/*.js"
30
30
  },
31
+ "./internal/*": null,
31
32
  "./package.json": "./package.json"
32
33
  },
33
34
  "files": [
@@ -46,8 +47,9 @@
46
47
  "@effect/platform-node": "4.0.4",
47
48
  "@effect/vitest": "4.0.4",
48
49
  "@shivaedev/effect-test": "0.7.1",
49
- "@shivaedev/quality": "0.28.0",
50
- "@shivaedev/test-story": "0.12.0",
50
+ "@shivaedev/effect-test-sql": "0.7.4",
51
+ "@shivaedev/quality": "0.29.1",
52
+ "@shivaedev/test-story": "0.16.0",
51
53
  "@types/node": "24.19.2",
52
54
  "effect": "4.0.4",
53
55
  "esbuild": "0.28.2",
@@ -1,4 +1,4 @@
1
- import { Effect, Stream, SubscriptionRef } from "effect";
1
+ import { type Duration, Effect, Stream, SubscriptionRef } from "effect";
2
2
  import type * as Atom from "effect/reactivity/Atom";
3
3
  import type * as AtomRpc from "effect/reactivity/AtomRpc";
4
4
  import { type Key, readKeys } from "./keys.ts";
@@ -10,7 +10,11 @@ declare const window: ResumeWindow | undefined;
10
10
 
11
11
  export const LiveConnection = Symbol.for("@shivaedev/effect-contract/LiveConnection");
12
12
  export interface LiveBound {
13
- readonly [LiveConnection]: { readonly connection: Atom.Atom<unknown>; readonly status: Atom.Atom<LiveStatus<unknown>> };
13
+ readonly [LiveConnection]: {
14
+ readonly coalesceWindow: Atom.Writable<Duration.Input>;
15
+ readonly connection: Atom.Atom<unknown>;
16
+ readonly status: Atom.Atom<LiveStatus<unknown>>;
17
+ };
14
18
  }
15
19
 
16
20
  export type ChangesClient = AtomRpc.AtomRpcClient<unknown, string, ReturnType<typeof changesRpc>>;
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 { queryReactivity } from "#internal/queryReactivity.ts";
10
11
  import { automaticLive, type LiveBound, LiveConnection } from "./automaticLive.ts";
11
12
  import type { Contract, Declared, Tag } from "./contract.ts";
12
13
  import { invalidationKeys, type Key, readKeys } from "./keys.ts";
@@ -75,13 +76,14 @@ const invalidateAfter = Effect.fn("EffectContract.invalidateAfter")(function* (k
75
76
  });
76
77
 
77
78
  function boundQuery(service: ErasedClient, tag: string, query: QueryShape, track: (keys: readonly Key[]) => void) {
79
+ const reactive = queryReactivity(service.runtime);
78
80
  function reads(payload: unknown) {
79
81
  return readKeys(query.reads(payload));
80
82
  }
81
83
  return {
82
84
  query: (payload: unknown, options: QueryOptions = {}) => {
83
85
  track(query.reads(payload));
84
- return service.query(tag, payload, { ...options, reactivityKeys: reads(payload) });
86
+ return reactive(service.query(tag, payload, options), reads(payload));
85
87
  },
86
88
  run: (payload: unknown) => service.use((client) => client(tag, payload)),
87
89
  };
package/src/contract.ts CHANGED
@@ -1,7 +1,7 @@
1
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
+ import { CommandInvalidation, changesRpc, QueryReads } from "./serverMetadata.ts";
5
5
 
6
6
  export type Tag<TContract extends string, TName extends string> = `${TContract}.${TName}`;
7
7
 
@@ -52,6 +52,13 @@ function assertUnique(operations: readonly OperationShape[]) {
52
52
  }
53
53
  }
54
54
 
55
+ function annotations(operation: QueryShape | CommandShape | StreamShape) {
56
+ if (operation.kind === "command") {
57
+ return Context.make(CommandInvalidation, operation.invalidates);
58
+ }
59
+ return operation.kind === "query" ? Context.make(QueryReads, operation.reads) : Context.empty();
60
+ }
61
+
55
62
  type AnyDeclared = Declared<string, readonly QueryShape[], readonly CommandShape[], readonly StreamShape[]>;
56
63
 
57
64
  interface Group {
@@ -95,7 +102,7 @@ export function contract(
95
102
  payload: operation.payload,
96
103
  stream: operation.kind === "stream",
97
104
  success: operation.success,
98
- }).annotateMerge(operation.kind === "command" ? Context.make(CommandInvalidation, operation.invalidates) : Context.empty()),
105
+ }).annotateMerge(annotations(operation)),
99
106
  );
100
107
  return withDeclaration(RpcGroup.make(...rpcs, changesRpc(name)), { commands, name, queries, streams });
101
108
  }
@@ -0,0 +1,40 @@
1
+ import { Cache, Data, Effect, Exit, Queue } from "effect";
2
+ import type { Key } from "#keys.ts";
3
+ import { ReadPolicy, ReadPolicyMemoWindow } from "#readPolicy.ts";
4
+
5
+ export const makeHintPolicy = Effect.fnUntraced(function* (capacity: number, retry: (keys: readonly Key[]) => Effect.Effect<void>) {
6
+ const policy = yield* ReadPolicy;
7
+ const window = yield* ReadPolicyMemoWindow;
8
+ const cache = yield* Cache.makeWith(policy, {
9
+ capacity: Math.max(1, capacity),
10
+ timeToLive: (exit) => (Exit.isSuccess(exit) ? window : 0),
11
+ });
12
+ const failed = new Map<string, Key>();
13
+ const wake = yield* Queue.unbounded<void>();
14
+ yield* Queue.take(wake).pipe(
15
+ Effect.andThen(Effect.sleep("1 second")),
16
+ Effect.andThen(
17
+ Effect.suspend(() => {
18
+ const keys = [...failed.values()];
19
+ failed.clear();
20
+ return retry(keys);
21
+ }),
22
+ ),
23
+ Effect.forever,
24
+ Effect.forkScoped,
25
+ );
26
+ return (keys: readonly Key[]) =>
27
+ Effect.filter(keys, (key) =>
28
+ Cache.get(cache, new Data.Class<Key>(key)).pipe(
29
+ Effect.catch(() =>
30
+ Effect.gen(function* () {
31
+ if (failed.size === 0) {
32
+ yield* Queue.offer(wake, undefined);
33
+ }
34
+ failed.set(JSON.stringify(key), key);
35
+ return false;
36
+ }),
37
+ ),
38
+ ),
39
+ );
40
+ });
@@ -0,0 +1,84 @@
1
+ import { Effect } from "effect";
2
+ import * as AsyncResult from "effect/reactivity/AsyncResult";
3
+ import * as Atom from "effect/reactivity/Atom";
4
+ import * as Reactivity from "effect/reactivity/Reactivity";
5
+
6
+ interface Batch {
7
+ readonly active: () => boolean;
8
+ readonly seen: Set<() => void>;
9
+ }
10
+ const batches = new WeakMap<Reactivity.Reactivity, Batch>();
11
+
12
+ export function invalidateLive(reactivity: Reactivity.Reactivity, keys: readonly string[], active: () => boolean) {
13
+ batches.set(reactivity, { active, seen: new Set() });
14
+ try {
15
+ reactivity.invalidateUnsafe(keys);
16
+ } finally {
17
+ batches.delete(reactivity);
18
+ }
19
+ }
20
+
21
+ export function queryReactivity<R, E>(runtime: Atom.AtomRuntime<R, E>) {
22
+ const service = runtime.atom(Effect.service(Reactivity.Reactivity));
23
+ type Query = Atom.Atom<AsyncResult.AsyncResult<unknown, unknown>>;
24
+ const cached = new WeakMap<Query, Query>();
25
+ return (atom: Query, keys: readonly string[]): Query => {
26
+ const existing = cached.get(atom);
27
+ if (existing !== undefined) {
28
+ return existing;
29
+ }
30
+ const source = Atom.setIdleTTL(
31
+ Atom.readable((get) => atom.read(get), atom.refresh),
32
+ 0,
33
+ );
34
+ let wrapped = Atom.transform(
35
+ source,
36
+ (get) => {
37
+ const context = get(service);
38
+ if (!AsyncResult.isSuccess(context)) {
39
+ return get(source);
40
+ }
41
+ const reactivity = context.value;
42
+ let pending: (() => boolean) | undefined;
43
+ function invalidate() {
44
+ const batch = batches.get(reactivity);
45
+ if (batch !== undefined) {
46
+ if (batch.seen.has(invalidate)) {
47
+ return;
48
+ }
49
+ batch.seen.add(invalidate);
50
+ if (get.once(source).waiting) {
51
+ pending = batch.active;
52
+ return;
53
+ }
54
+ }
55
+ pending = undefined;
56
+ get.refresh(source);
57
+ }
58
+ get.addFinalizer(reactivity.registerUnsafe(keys, invalidate));
59
+ get.subscribe(source, (value) => {
60
+ get.setSelf(value);
61
+ if (pending !== undefined && !value.waiting) {
62
+ const active = pending;
63
+ pending = undefined;
64
+ if (active()) {
65
+ get.refresh(source);
66
+ }
67
+ }
68
+ });
69
+ return get.once(source);
70
+ },
71
+ { initialValueTarget: source },
72
+ );
73
+ if (atom.keepAlive) {
74
+ wrapped = Atom.keepAlive(wrapped);
75
+ } else if (atom.idleTTL !== undefined) {
76
+ wrapped = Atom.setIdleTTL(wrapped, atom.idleTTL);
77
+ }
78
+ if (Atom.isSerializable(atom)) {
79
+ Object.assign(wrapped, { [Atom.SerializableTypeId]: atom[Atom.SerializableTypeId] });
80
+ }
81
+ cached.set(atom, wrapped);
82
+ return wrapped;
83
+ };
84
+ }
package/src/live.ts CHANGED
@@ -1,6 +1,7 @@
1
- import { type Cause, type Duration, Effect, Option, Schema, Stream } from "effect";
1
+ import { type Cause, Duration, Effect, Option, Schema, Stream } from "effect";
2
2
  import * as Atom from "effect/reactivity/Atom";
3
3
  import * as Reactivity from "effect/reactivity/Reactivity";
4
+ import { invalidateLive } from "#internal/queryReactivity.ts";
4
5
  import { invalidationKeys, Key, readKeys } from "./keys.ts";
5
6
 
6
7
  export const LiveHint = Schema.Union([
@@ -15,6 +16,7 @@ export interface LiveStatus<E> {
15
16
  readonly pending: number;
16
17
  }
17
18
  export interface LiveOptions<E, R> {
19
+ readonly coalesceWindow?: Duration.Input;
18
20
  readonly resume?: Atom.Atom<unknown>;
19
21
  readonly resyncKeys: readonly Key[] | (() => readonly Key[]);
20
22
  readonly retryDelay?: Duration.Input;
@@ -24,6 +26,7 @@ export interface LiveOptions<E, R> {
24
26
  const MAX_PENDING = 256;
25
27
 
26
28
  export function live<R, E, TRuntimeError>(runtime: Atom.AtomRuntime<R, TRuntimeError>, options: LiveOptions<E, R>) {
29
+ const coalesceWindow = Atom.keepAlive(Atom.make<Duration.Input>(options.coalesceWindow ?? "20 millis"));
27
30
  const paused = Atom.make(false);
28
31
  const status = Atom.make<LiveStatus<E>>({ connection: "connecting", failure: Option.none(), needsResync: true, pending: 0 });
29
32
  const connection = runtime
@@ -34,7 +37,37 @@ export function live<R, E, TRuntimeError>(runtime: Atom.AtomRuntime<R, TRuntimeE
34
37
  function update(value: Partial<LiveStatus<E>>) {
35
38
  get.set(status, { ...get.once(status), ...value });
36
39
  }
40
+ const pending = new Set<string>();
41
+ let timer: ReturnType<typeof setTimeout> | undefined;
42
+ function clear() {
43
+ clearTimeout(timer);
44
+ timer = undefined;
45
+ pending.clear();
46
+ }
47
+ let active = true;
48
+ get.addFinalizer(() => {
49
+ active = false;
50
+ clear();
51
+ });
52
+ function changed(changedKeys: readonly Key[]) {
53
+ const delay = Duration.toMillis(Duration.fromInputUnsafe(get.once(coalesceWindow)));
54
+ if (delay === 0) {
55
+ reactivity.invalidateUnsafe(invalidationKeys(changedKeys));
56
+ return;
57
+ }
58
+ for (const key of invalidationKeys(changedKeys)) {
59
+ pending.add(key);
60
+ }
61
+ timer ??= setTimeout(() => {
62
+ const keys = [...pending];
63
+ clear();
64
+ if (!get.once(paused)) {
65
+ invalidateLive(reactivity, keys, () => active && !get.once(paused));
66
+ }
67
+ }, delay);
68
+ }
37
69
  function resync() {
70
+ clear();
38
71
  if (get.once(paused)) {
39
72
  update({ needsResync: true });
40
73
  return;
@@ -42,6 +75,8 @@ export function live<R, E, TRuntimeError>(runtime: Atom.AtomRuntime<R, TRuntimeE
42
75
  reactivity.invalidateUnsafe(readKeys(typeof options.resyncKeys === "function" ? options.resyncKeys() : options.resyncKeys));
43
76
  update({ needsResync: false, pending: 0 });
44
77
  }
78
+ // A subscription alone does not build the flag, so its first read would otherwise announce a change.
79
+ get.once(paused);
45
80
  get.subscribe(paused, (value) => {
46
81
  if (!value) {
47
82
  resync();
@@ -61,7 +96,7 @@ export function live<R, E, TRuntimeError>(runtime: Atom.AtomRuntime<R, TRuntimeE
61
96
  } else if (isFirst || hint._tag === "Resync") {
62
97
  resync();
63
98
  } else {
64
- reactivity.invalidateUnsafe(invalidationKeys(hint.keys));
99
+ changed(hint.keys);
65
100
  }
66
101
  isFirst = false;
67
102
  }
@@ -77,5 +112,5 @@ export function live<R, E, TRuntimeError>(runtime: Atom.AtomRuntime<R, TRuntimeE
77
112
  }),
78
113
  )
79
114
  .pipe(Atom.setIdleTTL(0));
80
- return { connection, paused, status };
115
+ return { coalesceWindow, connection, paused, status };
81
116
  }
@@ -1,5 +1,6 @@
1
1
  import { Effect, Queue, Stream } from "effect";
2
2
  import * as Reactivity from "effect/reactivity/Reactivity";
3
+ import { makeHintPolicy } from "#internal/makeHintPolicy.ts";
3
4
  import { type Key, readKeys } from "./keys.ts";
4
5
  import type { LiveHint } from "./live.ts";
5
6
 
@@ -10,6 +11,9 @@ export const makeLiveServer = Effect.gen(function* () {
10
11
  return Stream.unwrap(
11
12
  Effect.gen(function* () {
12
13
  const queue = yield* Queue.unbounded<LiveHint>();
14
+ const allowedKeys = yield* makeHintPolicy(keys.length, (retryKeys) =>
15
+ Queue.offer(queue, { _tag: "Changed", keys: retryKeys }).pipe(Effect.asVoid),
16
+ );
13
17
  const pending = new Map<string, Key>();
14
18
  let scheduled = false;
15
19
  let resync = false;
@@ -50,7 +54,14 @@ export const makeLiveServer = Effect.gen(function* () {
50
54
  }),
51
55
  );
52
56
  yield* Queue.offer(queue, { _tag: "Resync" });
53
- return Stream.fromQueue(queue);
57
+ return Stream.fromQueue(queue).pipe(
58
+ Stream.mapEffect((hint) =>
59
+ hint._tag === "Resync"
60
+ ? Effect.succeed(hint)
61
+ : Effect.map(allowedKeys(hint.keys), (allowed): LiveHint => ({ _tag: "Changed", keys: allowed })),
62
+ ),
63
+ Stream.filter((hint) => hint._tag === "Resync" || hint.keys.length > 0),
64
+ );
54
65
  }),
55
66
  );
56
67
  }
@@ -0,0 +1,33 @@
1
+ import { Context, type Duration, Effect, Option } from "effect";
2
+ import type { Rpc } from "effect/rpc";
3
+ import type { Key } from "./keys.ts";
4
+ import { QueryReads } from "./serverMetadata.ts";
5
+
6
+ export const ReadPolicy = Context.Reference<(key: Key) => Effect.Effect<boolean, unknown>>("@shivaedev/effect-contract/ReadPolicy", {
7
+ defaultValue: () => () => Effect.succeed(true),
8
+ });
9
+
10
+ export const ReadPolicyMemoWindow = Context.Reference<Duration.Input>("@shivaedev/effect-contract/ReadPolicyMemoWindow", {
11
+ defaultValue: () => "1 second",
12
+ });
13
+
14
+ export const authorizeReads = Effect.fnUntraced(function* <A, E, R, TPolicyServices, TDenied, TPolicyError>(
15
+ effect: Effect.Effect<A, E, R>,
16
+ request: { readonly rpc: Rpc.AnyWithProps; readonly payload: unknown },
17
+ policy: (key: Key) => Effect.Effect<boolean, TPolicyError, TPolicyServices>,
18
+ denied: Effect.Effect<never, TDenied>,
19
+ ) {
20
+ const context = yield* Effect.context<TPolicyServices>();
21
+ function canRead(key: Key) {
22
+ return Effect.provideContext(policy(key), context);
23
+ }
24
+ const reads = Context.getOption(request.rpc.annotations, QueryReads);
25
+ if (Option.isSome(reads)) {
26
+ for (const key of reads.value(request.payload)) {
27
+ if (!(yield* canRead(key))) {
28
+ return yield* denied;
29
+ }
30
+ }
31
+ }
32
+ return yield* Effect.provideService(effect, ReadPolicy, canRead);
33
+ });
@@ -2,12 +2,14 @@ import { Context, Schema } from "effect";
2
2
  import { Rpc } from "effect/rpc";
3
3
  import { Key } from "./keys.ts";
4
4
  import { LiveHint } from "./live.ts";
5
- import type { CommandShape } from "./operation.ts";
5
+ import type { CommandShape, QueryShape } from "./operation.ts";
6
+
7
+ export class QueryReads extends Context.Service<QueryReads, QueryShape["reads"]>()("@shivaedev/effect-contract/QueryReads") {}
6
8
 
7
9
  export class CommandInvalidation extends Context.Service<CommandInvalidation, CommandShape["invalidates"]>()(
8
10
  "@shivaedev/effect-contract/CommandInvalidation",
9
11
  ) {}
10
12
 
11
- export function changesRpc(name: string) {
13
+ export function changesRpc<const TName extends string>(name: TName) {
12
14
  return Rpc.make(`${name}.$changes`, { payload: { keys: Schema.Array(Key) }, stream: true, success: LiveHint });
13
15
  }