@lunora/client 1.0.0-alpha.18 → 1.0.0-alpha.181

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 (89) hide show
  1. package/README.md +16 -3
  2. package/dist/auth/index.d.mts +43 -12
  3. package/dist/auth/index.d.ts +43 -12
  4. package/dist/auth/index.mjs +1 -60
  5. package/dist/index.d.mts +808 -261
  6. package/dist/index.d.ts +808 -261
  7. package/dist/index.mjs +1 -15
  8. package/dist/packem_shared/CONFLICT_ERROR_CODE-B7qNZMMk.mjs +1 -0
  9. package/dist/packem_shared/DEFAULT_MAX_BUFFER-D3QH2iaq.mjs +1 -0
  10. package/dist/packem_shared/LunoraClient-DnqTxmHd.mjs +1 -0
  11. package/dist/packem_shared/OfflineQueue-BWamzR6p.mjs +1 -0
  12. package/dist/packem_shared/RETIRE_AFTER_DURABLE_SEQ_ADVANCE-DoEHtuR8.mjs +1 -0
  13. package/dist/packem_shared/SKIP-d7LeP-sY.mjs +1 -0
  14. package/dist/packem_shared/SubscriptionRegistry-C3kLqped.mjs +1 -0
  15. package/dist/packem_shared/TabCoordinator-CXD7vulJ.mjs +1 -0
  16. package/dist/packem_shared/anyApi-CBOws2ZA.mjs +1 -0
  17. package/dist/packem_shared/applyDelta-CIjpZ5h9.mjs +1 -0
  18. package/dist/packem_shared/createAsyncStoragePersistence-CeUW9NTj.mjs +1 -0
  19. package/dist/packem_shared/createAsyncStorageQueryCache-Dpszf7UD.mjs +1 -0
  20. package/dist/packem_shared/createCallRunner-Bwcrfh76.mjs +1 -0
  21. package/dist/packem_shared/createClientQuery-B8Nfj-7o.mjs +1 -0
  22. package/dist/packem_shared/createInMemoryBookmarkStorage-BooZhW0n.mjs +1 -0
  23. package/dist/packem_shared/createInMemoryPersistence-Cz2W6XVj.mjs +1 -0
  24. package/dist/packem_shared/createInMemoryQueryCache-D2uNHLfM.mjs +1 -0
  25. package/dist/packem_shared/createLocalStore-0ARUZ892.mjs +1 -0
  26. package/dist/packem_shared/createMutatorRunner-CuMRJpUx.mjs +1 -0
  27. package/dist/packem_shared/createReconnect-CjTmjJDH.mjs +1 -0
  28. package/dist/packem_shared/createServerClient-DApE8zg1.mjs +1 -0
  29. package/dist/packem_shared/createSnapshotPrecondition-5GQ_5fFu.mjs +1 -0
  30. package/dist/packem_shared/delta-merge-D_5ChS-C.mjs +1 -0
  31. package/dist/packem_shared/deserializePreloaded-Bffy70Wm.mjs +1 -0
  32. package/dist/packem_shared/function-reference.d-Br_hsKje.d.mts +45 -0
  33. package/dist/packem_shared/function-reference.d-Br_hsKje.d.ts +45 -0
  34. package/dist/packem_shared/getServerSession-BaOoo1p6.mjs +1 -0
  35. package/dist/packem_shared/httpStream-v4etRV7m.mjs +9 -0
  36. package/dist/packem_shared/idb-utility-C8WS390w.mjs +1 -0
  37. package/dist/packem_shared/local-store-DDppw8Qr.mjs +1 -0
  38. package/dist/packem_shared/lunora-client.d-CUpV_t_w.d.ts +4731 -0
  39. package/dist/packem_shared/lunora-client.d-gPd6tie1.d.mts +4731 -0
  40. package/dist/packem_shared/offline-queue-D_fr-uQ3.mjs +1 -0
  41. package/dist/packem_shared/preload.d-CyjUwQEv.d.ts +21 -0
  42. package/dist/packem_shared/preload.d-DECTCs0m.d.mts +21 -0
  43. package/dist/packem_shared/preloadQuery-uFy24PCR.mjs +1 -0
  44. package/dist/packem_shared/replay-CXXDZ1_h.mjs +1 -0
  45. package/dist/packem_shared/single-blob-store-DrhzObic.mjs +1 -0
  46. package/dist/packem_shared/wire-codec-BeIi1K-T.mjs +1 -0
  47. package/dist/packem_shared/wire-key-DCPYV4t8.mjs +1 -0
  48. package/dist/pagination/index.d.mts +42 -42
  49. package/dist/pagination/index.d.ts +42 -42
  50. package/dist/pagination/index.mjs +1 -61
  51. package/dist/query/index.d.mts +111 -43
  52. package/dist/query/index.d.ts +111 -43
  53. package/dist/query/index.mjs +1 -1
  54. package/dist/service.d.mts +49 -0
  55. package/dist/service.d.ts +49 -0
  56. package/dist/service.mjs +1 -0
  57. package/dist/ssr/index.d.mts +109 -79
  58. package/dist/ssr/index.d.ts +109 -79
  59. package/dist/ssr/index.mjs +1 -4
  60. package/dist/upload.d.mts +35 -0
  61. package/dist/upload.d.ts +35 -0
  62. package/dist/upload.mjs +1 -0
  63. package/package.json +14 -2
  64. package/dist/packem_shared/CONFLICT_ERROR_CODE-B8gQ8tyU.mjs +0 -33
  65. package/dist/packem_shared/DEFAULT_MAX_BUFFER-BDkqO5PW.mjs +0 -107
  66. package/dist/packem_shared/LunoraClient-BECgZjx4.mjs +0 -3413
  67. package/dist/packem_shared/OfflineQueue-GGYJRmhF.mjs +0 -1
  68. package/dist/packem_shared/SKIP-vItZChkw.mjs +0 -50
  69. package/dist/packem_shared/SubscriptionRegistry-Cxr70og-.mjs +0 -1
  70. package/dist/packem_shared/applyDelta-4jFGTPA3.mjs +0 -61
  71. package/dist/packem_shared/createAsyncStoragePersistence-1Z5BZ8RC.mjs +0 -45
  72. package/dist/packem_shared/createInMemoryBookmarkStorage-BoN7a7TH.mjs +0 -11
  73. package/dist/packem_shared/createInMemoryPersistence-Ds7z8n8d.mjs +0 -119
  74. package/dist/packem_shared/createInMemoryQueryCache-iWtKPrid.mjs +0 -152
  75. package/dist/packem_shared/createLocalStore-IOur0jHF.mjs +0 -1
  76. package/dist/packem_shared/createMutationRunner-BqsavzvG.mjs +0 -21
  77. package/dist/packem_shared/createMutatorRunner-BETvCd0p.mjs +0 -31
  78. package/dist/packem_shared/createReconnect-Di_-oHH7.mjs +0 -22
  79. package/dist/packem_shared/createServerClient-Cet4TATX.mjs +0 -11
  80. package/dist/packem_shared/deserializePreloaded-C0eJTY_W.mjs +0 -4
  81. package/dist/packem_shared/getServerSession-8jXewqxd.mjs +0 -13
  82. package/dist/packem_shared/local-store-BNgN3Dw3.mjs +0 -111
  83. package/dist/packem_shared/lunora-client.d-Dy4neKF6.d.mts +0 -2526
  84. package/dist/packem_shared/lunora-client.d-Dy4neKF6.d.ts +0 -2526
  85. package/dist/packem_shared/offline-queue-B9vfdSqp.mjs +0 -170
  86. package/dist/packem_shared/preload.d-BQaCmS5J.d.mts +0 -20
  87. package/dist/packem_shared/preload.d-DI-5eqHP.d.ts +0 -20
  88. package/dist/packem_shared/preloadQuery-lobFkD2Z.mjs +0 -13
  89. package/dist/packem_shared/subscription-DoyO04-2.mjs +0 -65
package/README.md CHANGED
@@ -34,7 +34,7 @@
34
34
 
35
35
  ---
36
36
 
37
- The framework-agnostic browser/edge SDK for Lunora. It runs RPC over HTTP and real-time deltas over a single multiplexed WebSocket, with optimistic updates, an offline mutation queue, and decorrelated-jitter reconnect built in. It is the lowest-level user-facing entry point that every framework adapter (React, Vue, Svelte, Solid) layers on top of.
37
+ The framework-agnostic browser/edge SDK for Lunora. It runs RPC over HTTP and real-time deltas over a single multiplexed WebSocket, with optimistic updates, an offline mutation queue, and exponential-backoff-with-jitter reconnect built in. It is the lowest-level user-facing entry point that every framework adapter (React, Vue, Svelte, Solid) layers on top of.
38
38
 
39
39
  Part of the [Lunora](https://github.com/anolilab/lunora) framework — a type-safe, real-time backend on Cloudflare Workers + Durable Objects with a Vite-first DX.
40
40
 
@@ -63,8 +63,21 @@ const client = new LunoraClient({ url: "https://app.acme.test" });
63
63
  // One-shot query (HTTP, carries the D1 bookmark for read-your-writes).
64
64
  const messages = await client.query(api.messages.list, { room: "general" });
65
65
 
66
- // Mutation with an optimistic update applied to matching subscribers.
67
- await client.mutation(api.messages.send, { body: "hi" }, { optimistic: (current = []) => [...current, { body: "hi", pending: true }] });
66
+ // Mutation with an optimistic update. `optimisticUpdate` names the query it
67
+ // patches — a write and the queries it affects are different functions, and only
68
+ // you know which. (The per-call `optimistic` shortcut patches the subscription
69
+ // registered under the mutation's OWN reference and args, nothing else.)
70
+ await client.mutation(
71
+ api.messages.send,
72
+ { room: "general", body: "hi" },
73
+ {
74
+ optimisticUpdate: (store, args) => {
75
+ const current = store.getQuery(api.messages.list, { room: args.room }) ?? [];
76
+
77
+ store.setQuery(api.messages.list, { room: args.room }, [...current, { body: args.body, pending: true }]);
78
+ },
79
+ },
80
+ );
68
81
 
69
82
  // Live subscription over WS — returns an unsubscribe function.
70
83
  const unsubscribe = client.subscribe(api.messages.list, { room: "general" }, (next) => {
@@ -1,20 +1,51 @@
1
- import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-Dy4neKF6.mjs";
1
+ import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-gPd6tie1.mjs";
2
+ import '@lunora/errors';
3
+ import "../packem_shared/function-reference.d-Br_hsKje.mjs";
2
4
  import '@lunora/runtime';
5
+ /**
6
+ * Every {@link AuthStatus}, as a value — the contract table in this module's
7
+ * header, and the `@lunora/client` docs table that restates it for consumers,
8
+ * are both asserted against this list (`auth-status-docs.test.ts`), so a new
9
+ * state cannot ship undocumented.
10
+ */
11
+ declare const AUTH_STATUSES: readonly ["authenticated", "loading", "unauthenticated", "unreachable"];
12
+ /**
13
+ * Resolved authentication state. See the contract table in this module's header
14
+ * — the states are not interchangeable, and `unreachable` is the one that exists
15
+ * only so "the server said no session" and "we could not ask" stop colliding.
16
+ */
17
+ type AuthStatus = (typeof AUTH_STATUSES)[number];
18
+ /**
19
+ * Whether a gate should render its authenticated branch.
20
+ *
21
+ * Both `authenticated` and `unreachable` qualify: the credential is held in
22
+ * either case, and only the identity round-trip differs.
23
+ */
24
+ declare const isAuthenticatedStatus: (status: AuthStatus) => boolean;
25
+ /** Whether a gate should render its loading branch — the first resolve only. */
26
+ declare const isLoadingStatus: (status: AuthStatus) => boolean;
3
27
  interface IdentityStore {
4
- /** Read the current resolved user, or `null` when signed out / not yet resolved. */
28
+ /** The current {@link AuthStatus} — what a gate branches on. */
29
+ getStatus: () => AuthStatus;
30
+ /**
31
+ * The last resolved user, or `null`. `null` does NOT mean signed out on its
32
+ * own: read `getStatus` for that. A user resolved before the endpoint
33
+ * went unreachable is retained, so the UI keeps rendering the identity it
34
+ * last knew instead of blanking.
35
+ */
5
36
  getUser: () => User | null;
6
37
  /**
7
- * Subscribe to identity changes. `onChange` is called whenever the user or
8
- * token changes. Returns an unsubscribe handle.
9
- */
38
+ * Subscribe to identity changes. `onChange` is called whenever the user or
39
+ * the status changes. Returns an unsubscribe handle.
40
+ */
10
41
  subscribe: (onChange: () => void) => () => void;
11
42
  }
12
43
  /**
13
- * Return the per-client identity store, creating it on first access.
14
- *
15
- * The store is cached via a `WeakMap` so it is GC'd when the client is dropped,
16
- * and creation is idempotent — calling this multiple times with the same client
17
- * returns the same store, keeping the single-fetch / fan-out invariant.
18
- */
44
+ * Return the per-client identity store, creating it on first access.
45
+ *
46
+ * The store is cached via a `WeakMap` so it is GC'd when the client is dropped,
47
+ * and creation is idempotent — calling this multiple times with the same client
48
+ * returns the same store, keeping the single-fetch / fan-out invariant.
49
+ */
19
50
  declare const getIdentityStore: (client: LunoraClient) => IdentityStore;
20
- export { type IdentityStore, getIdentityStore };
51
+ export { AUTH_STATUSES, type AuthStatus, type IdentityStore, getIdentityStore, isAuthenticatedStatus, isLoadingStatus };
@@ -1,20 +1,51 @@
1
- import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-Dy4neKF6.js";
1
+ import { U as User, L as LunoraClient } from "../packem_shared/lunora-client.d-CUpV_t_w.js";
2
+ import '@lunora/errors';
3
+ import "../packem_shared/function-reference.d-Br_hsKje.js";
2
4
  import '@lunora/runtime';
5
+ /**
6
+ * Every {@link AuthStatus}, as a value — the contract table in this module's
7
+ * header, and the `@lunora/client` docs table that restates it for consumers,
8
+ * are both asserted against this list (`auth-status-docs.test.ts`), so a new
9
+ * state cannot ship undocumented.
10
+ */
11
+ declare const AUTH_STATUSES: readonly ["authenticated", "loading", "unauthenticated", "unreachable"];
12
+ /**
13
+ * Resolved authentication state. See the contract table in this module's header
14
+ * — the states are not interchangeable, and `unreachable` is the one that exists
15
+ * only so "the server said no session" and "we could not ask" stop colliding.
16
+ */
17
+ type AuthStatus = (typeof AUTH_STATUSES)[number];
18
+ /**
19
+ * Whether a gate should render its authenticated branch.
20
+ *
21
+ * Both `authenticated` and `unreachable` qualify: the credential is held in
22
+ * either case, and only the identity round-trip differs.
23
+ */
24
+ declare const isAuthenticatedStatus: (status: AuthStatus) => boolean;
25
+ /** Whether a gate should render its loading branch — the first resolve only. */
26
+ declare const isLoadingStatus: (status: AuthStatus) => boolean;
3
27
  interface IdentityStore {
4
- /** Read the current resolved user, or `null` when signed out / not yet resolved. */
28
+ /** The current {@link AuthStatus} — what a gate branches on. */
29
+ getStatus: () => AuthStatus;
30
+ /**
31
+ * The last resolved user, or `null`. `null` does NOT mean signed out on its
32
+ * own: read `getStatus` for that. A user resolved before the endpoint
33
+ * went unreachable is retained, so the UI keeps rendering the identity it
34
+ * last knew instead of blanking.
35
+ */
5
36
  getUser: () => User | null;
6
37
  /**
7
- * Subscribe to identity changes. `onChange` is called whenever the user or
8
- * token changes. Returns an unsubscribe handle.
9
- */
38
+ * Subscribe to identity changes. `onChange` is called whenever the user or
39
+ * the status changes. Returns an unsubscribe handle.
40
+ */
10
41
  subscribe: (onChange: () => void) => () => void;
11
42
  }
12
43
  /**
13
- * Return the per-client identity store, creating it on first access.
14
- *
15
- * The store is cached via a `WeakMap` so it is GC'd when the client is dropped,
16
- * and creation is idempotent — calling this multiple times with the same client
17
- * returns the same store, keeping the single-fetch / fan-out invariant.
18
- */
44
+ * Return the per-client identity store, creating it on first access.
45
+ *
46
+ * The store is cached via a `WeakMap` so it is GC'd when the client is dropped,
47
+ * and creation is idempotent — calling this multiple times with the same client
48
+ * returns the same store, keeping the single-fetch / fan-out invariant.
49
+ */
19
50
  declare const getIdentityStore: (client: LunoraClient) => IdentityStore;
20
- export { type IdentityStore, getIdentityStore };
51
+ export { AUTH_STATUSES, type AuthStatus, type IdentityStore, getIdentityStore, isAuthenticatedStatus, isLoadingStatus };
@@ -1,60 +1 @@
1
- const stores = /* @__PURE__ */ new WeakMap();
2
- const createIdentityStore = (client) => {
3
- const listeners = /* @__PURE__ */ new Set();
4
- let user = null;
5
- let started = false;
6
- const notify = () => {
7
- for (const listener of listeners) {
8
- listener();
9
- }
10
- };
11
- let generation = 0;
12
- const setUser = (next) => {
13
- if (user !== next) {
14
- user = next;
15
- notify();
16
- }
17
- };
18
- const refresh = () => {
19
- generation += 1;
20
- const current = generation;
21
- if (client.getAuthToken() === null) {
22
- setUser(null);
23
- return;
24
- }
25
- client.getCurrentUser().then((next) => {
26
- if (current === generation) {
27
- setUser(next);
28
- }
29
- return void 0;
30
- }).catch(() => {
31
- if (current === generation) {
32
- setUser(null);
33
- }
34
- });
35
- };
36
- client.onAuthTokenChange(refresh);
37
- return {
38
- getUser: () => user,
39
- subscribe: (onChange) => {
40
- listeners.add(onChange);
41
- if (!started) {
42
- started = true;
43
- refresh();
44
- }
45
- return () => {
46
- listeners.delete(onChange);
47
- };
48
- }
49
- };
50
- };
51
- const getIdentityStore = (client) => {
52
- let store = stores.get(client);
53
- if (!store) {
54
- store = createIdentityStore(client);
55
- stores.set(client, store);
56
- }
57
- return store;
58
- };
59
-
60
- export { getIdentityStore };
1
+ const b=["authenticated","loading","unauthenticated","unreachable"],y=t=>t==="authenticated"||t==="unreachable",A=t=>t==="loading",f=new WeakMap,S=t=>{const n=new Set;let u=null,s="loading",c=!1,h=!1,a;const g=()=>{for(const e of n)e()};let i=0;const r=(e,o)=>{(s!==e||u!==o)&&(s=e,u=o,g())},l=(e=!1)=>{i+=1;const o=i;if(!e&&t.getAuthToken()===null&&h){r("unauthenticated",null);return}r(u===null?"loading":"authenticated",u),t.getCurrentUser().then(d=>{o===i&&(h=!0,r(d===null?"unauthenticated":"authenticated",d),d!==null&&a===`subj:${d.id}`&&(a=void 0))}).catch(()=>{a=void 0,o===i&&r("unreachable",u)})};return t.expectIdentityResolution(),t.onAuthTokenChange(()=>{l()}),t.onIdentityChange(()=>{if(!c||t.getAuthToken()!==null)return;const e=t.currentIdentity();e===null?r("unauthenticated",null):e!==a&&(a=e,l(!0))}),t.onConnectionStatus(e=>{e==="connected"&&s==="unreachable"&&l(!0)}),{getStatus:()=>s,getUser:()=>u,subscribe:e=>(n.add(e),c||(c=!0,l()),()=>{n.delete(e)})}},T=t=>{let n=f.get(t);return n||(n=S(t),f.set(t,n)),n};export{b as AUTH_STATUSES,T as getIdentityStore,y as isAuthenticatedStatus,A as isLoadingStatus};