@lunora/client 1.0.0-alpha.7 → 1.0.0-alpha.71

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 (88) hide show
  1. package/LICENSE.md +6 -0
  2. package/dist/auth/index.d.mts +12 -10
  3. package/dist/auth/index.d.ts +12 -10
  4. package/dist/auth/index.mjs +1 -60
  5. package/dist/index.d.mts +878 -230
  6. package/dist/index.d.ts +878 -230
  7. package/dist/index.mjs +1 -15
  8. package/dist/packem_shared/CONFLICT_ERROR_CODE-CDDneB-G.mjs +1 -0
  9. package/dist/packem_shared/ClientServiceWorker-DXnBXeWK.mjs +1 -0
  10. package/dist/packem_shared/DEFAULT_MAX_BUFFER-D3QH2iaq.mjs +1 -0
  11. package/dist/packem_shared/LunoraClient-B9UMg5lY.mjs +1 -0
  12. package/dist/packem_shared/OfflineQueue-B6IWyX5W.mjs +1 -0
  13. package/dist/packem_shared/RETIRE_AFTER_DURABLE_SEQ_ADVANCE-DoEHtuR8.mjs +1 -0
  14. package/dist/packem_shared/SKIP-d7LeP-sY.mjs +1 -0
  15. package/dist/packem_shared/SubscriptionRegistry-BQPU90ST.mjs +1 -0
  16. package/dist/packem_shared/TabCoordinator-CXD7vulJ.mjs +1 -0
  17. package/dist/packem_shared/anyApi-CBOws2ZA.mjs +1 -0
  18. package/dist/packem_shared/applyDelta-BiegnbZR.mjs +1 -0
  19. package/dist/packem_shared/createAsyncStoragePersistence-CNdM5o1u.mjs +1 -0
  20. package/dist/packem_shared/createAsyncStorageQueryCache-MoM77Nwg.mjs +1 -0
  21. package/dist/packem_shared/createCallRunner-Bwcrfh76.mjs +1 -0
  22. package/dist/packem_shared/createClientQuery-B8Nfj-7o.mjs +1 -0
  23. package/dist/packem_shared/createInMemoryBookmarkStorage-BooZhW0n.mjs +1 -0
  24. package/dist/packem_shared/createInMemoryPersistence-C0qm7c47.mjs +1 -0
  25. package/dist/packem_shared/createInMemoryQueryCache-D2uNHLfM.mjs +1 -0
  26. package/dist/packem_shared/createLocalStore-v6oiATqG.mjs +1 -0
  27. package/dist/packem_shared/createMutatorRunner-CuMRJpUx.mjs +1 -0
  28. package/dist/packem_shared/createReconnect-CjTmjJDH.mjs +1 -0
  29. package/dist/packem_shared/createReply-CwFABEJs.mjs +1 -0
  30. package/dist/packem_shared/createServerClient-BevCicNQ.mjs +1 -0
  31. package/dist/packem_shared/createSnapshotPrecondition-C4bSgHkg.mjs +1 -0
  32. package/dist/packem_shared/delta-merge-BPM4sFID.mjs +1 -0
  33. package/dist/packem_shared/deserializePreloaded-CHfOFpka.mjs +1 -0
  34. package/dist/packem_shared/function-reference.d-Br_hsKje.d.mts +45 -0
  35. package/dist/packem_shared/function-reference.d-Br_hsKje.d.ts +45 -0
  36. package/dist/packem_shared/getServerSession-BaOoo1p6.mjs +1 -0
  37. package/dist/packem_shared/httpStream-x6q-x4yK.mjs +11 -0
  38. package/dist/packem_shared/idb-utility-C8WS390w.mjs +1 -0
  39. package/dist/packem_shared/local-store-CNgeGW02.mjs +1 -0
  40. package/dist/packem_shared/lunora-client.d-BSNUC85h.d.mts +3491 -0
  41. package/dist/packem_shared/lunora-client.d-BkDgDzTg.d.ts +3491 -0
  42. package/dist/packem_shared/offline-queue-8mpNPwG8.mjs +1 -0
  43. package/dist/packem_shared/preload.d-Do17SOqk.d.ts +21 -0
  44. package/dist/packem_shared/preload.d-XsWPDlX6.d.mts +21 -0
  45. package/dist/packem_shared/preloadQuery-uFy24PCR.mjs +1 -0
  46. package/dist/packem_shared/single-blob-store-DrhzObic.mjs +1 -0
  47. package/dist/packem_shared/wire-codec-BfIZrTJe.mjs +1 -0
  48. package/dist/packem_shared/wire-key-C9UKT-C3.mjs +1 -0
  49. package/dist/pagination/index.d.mts +42 -42
  50. package/dist/pagination/index.d.ts +42 -42
  51. package/dist/pagination/index.mjs +1 -61
  52. package/dist/query/index.d.mts +111 -43
  53. package/dist/query/index.d.ts +111 -43
  54. package/dist/query/index.mjs +1 -1
  55. package/dist/service.d.mts +49 -0
  56. package/dist/service.d.ts +49 -0
  57. package/dist/service.mjs +1 -0
  58. package/dist/ssr/index.d.mts +100 -79
  59. package/dist/ssr/index.d.ts +100 -79
  60. package/dist/ssr/index.mjs +1 -4
  61. package/dist/upload.d.mts +35 -0
  62. package/dist/upload.d.ts +35 -0
  63. package/dist/upload.mjs +1 -0
  64. package/package.json +16 -1
  65. package/dist/packem_shared/CONFLICT_ERROR_CODE-aUdVbEDw.mjs +0 -4
  66. package/dist/packem_shared/DEFAULT_MAX_BUFFER-BDkqO5PW.mjs +0 -107
  67. package/dist/packem_shared/LunoraClient-Bsv8immC.mjs +0 -2546
  68. package/dist/packem_shared/OfflineQueue-D-ASeqL7.mjs +0 -131
  69. package/dist/packem_shared/SKIP-vItZChkw.mjs +0 -50
  70. package/dist/packem_shared/SubscriptionRegistry-Dn-7k7eo.mjs +0 -1
  71. package/dist/packem_shared/applyDelta-4jFGTPA3.mjs +0 -61
  72. package/dist/packem_shared/createAsyncStoragePersistence-1Z5BZ8RC.mjs +0 -45
  73. package/dist/packem_shared/createInMemoryBookmarkStorage-BoN7a7TH.mjs +0 -11
  74. package/dist/packem_shared/createInMemoryPersistence-CW82inU5.mjs +0 -105
  75. package/dist/packem_shared/createInMemoryQueryCache-B1PQ9Twl.mjs +0 -138
  76. package/dist/packem_shared/createLocalStore-DSUfoLqY.mjs +0 -36
  77. package/dist/packem_shared/createMutationRunner-BqsavzvG.mjs +0 -21
  78. package/dist/packem_shared/createMutatorRunner-BETvCd0p.mjs +0 -31
  79. package/dist/packem_shared/createReconnect-Di_-oHH7.mjs +0 -22
  80. package/dist/packem_shared/createServerClient-DucOyOz3.mjs +0 -11
  81. package/dist/packem_shared/deserializePreloaded-C0eJTY_W.mjs +0 -4
  82. package/dist/packem_shared/getServerSession-8jXewqxd.mjs +0 -13
  83. package/dist/packem_shared/lunora-client.d-DLbxPGH9.d.mts +0 -2006
  84. package/dist/packem_shared/lunora-client.d-DLbxPGH9.d.ts +0 -2006
  85. package/dist/packem_shared/preload.d-BAT6GeSD.d.mts +0 -20
  86. package/dist/packem_shared/preload.d-CSkjbfEq.d.ts +0 -20
  87. package/dist/packem_shared/preloadQuery-lobFkD2Z.mjs +0 -13
  88. package/dist/packem_shared/subscription-C1Jy7HiF.mjs +0 -55
@@ -0,0 +1 @@
1
+ let h=0;const d=()=>{if(typeof crypto<"u"&&typeof crypto.randomUUID=="function")return crypto.randomUUID();h+=1;const r=typeof crypto<"u"&&typeof crypto.getRandomValues=="function"?Array.from(crypto.getRandomValues(new Uint8Array(8)),e=>e.toString(16).padStart(2,"0")).join(""):Math.random().toString(16).slice(2,12);return`id_${Date.now().toString(36)}_${h.toString(36)}_${r}`},f=(r,e)=>r!==void 0&&e!==r,u=d,c=(r,e,t,i)=>{if(r)try{r({error:t,mutationId:i,operation:e});return}catch{}console.warn(`[lunora] offline-queue persistence ${e} failed`,t)};class p{queueBeforeFirstConnect;maxItems;onPersistenceError;persistence;onEvict;onSizeChange;version;items=[];constructor(e={},t={}){this.maxItems=e.maxItems??1e3,this.queueBeforeFirstConnect=e.queueBeforeFirstConnect??!1,this.onPersistenceError=e.onPersistenceError,this.persistence=t.persistence,this.onEvict=t.onEvict,this.onSizeChange=t.onSizeChange,this.version=t.version}get size(){return this.items.length}enqueue(e){const t=e;t.id??=u(),this.items.push(t),this.persistence?.append({args:t.args,clientId:t.clientId,functionPath:t.functionPath,id:t.id,identity:t.identity,shardKey:t.shardKey,...this.version===void 0?{}:{version:this.version}}).catch(i=>{c(this.onPersistenceError,"append",i,t.id)}),this.evictOverflow(),this.notifySize()}async hydrate(){if(!this.persistence)return[];let e;try{e=await this.persistence.load()}catch(s){throw c(this.onPersistenceError,"load",s),s}const t=[],i=new Set(this.items.map(s=>s.id));for(const s of e)if(!i.has(s.id)){if(f(this.version,s.version)){this.persistence.remove(s.id).catch(a=>{c(this.onPersistenceError,"remove",a,s.id)});continue}i.add(s.id),t.push({args:s.args,clientId:s.clientId,functionPath:s.functionPath,id:s.id,identity:s.identity,reject:()=>{},resolve:()=>{},shardKey:s.shardKey})}this.items.unshift(...t),this.evictOverflow(),this.notifySize();const n=new Set(this.items),o=new Set;for(const s of t)n.has(s)&&o.add(s.shardKey);return[...o]}restampIdentity(e,t){for(const i of this.items){if(i.identity!==e)continue;i.identity=t;const{id:n}=i;if(!this.persistence||n===void 0)continue;const o={args:i.args,clientId:i.clientId,functionPath:i.functionPath,id:n,identity:t,shardKey:i.shardKey,...this.version===void 0?{}:{version:this.version}};this.rewriteStamp(n,o,e).catch(s=>{c(this.onPersistenceError,"append",s,n)})}}drain(e){if(!e){const n=[...this.items];return this.items.length=0,this.notifySize(),n}const t=[],i=[];for(const n of this.items)(e(n)?t:i).push(n);return this.items.length=0,this.items.push(...i),this.notifySize(),t}requeue(e){e.length!==0&&(this.items.unshift(...e),this.notifySize())}drainConflict(){const e=[],t=[];for(const i of this.items)if(i.precondition!==void 0&&!i.precondition()){const n=new Error("offline mutation skipped: precondition failed before replay");n.code="OFFLINE_PRECONDITION_FAILED",i.reject(n),e.push(i)}else t.push(i);return e.length>0&&(this.items.length=0,this.items.push(...t),this.notifySize()),e}clear(){for(const e of this.items){const t=new Error("CLIENT_CLOSED");t.code="CLIENT_CLOSED",e.reject(t)}this.items.length=0,this.notifySize()}async rewriteStamp(e,t,i){const n=this.persistence;if(n){try{await n.remove(e)}catch(o){c(this.onPersistenceError,"remove",o,e);return}try{await n.append(t)}catch(o){c(this.onPersistenceError,"append",o,e);try{await n.append({...t,identity:i})}catch(s){c(this.onPersistenceError,"append",s,e)}}}}evictOverflow(){for(;this.items.length>this.maxItems;){const e=this.items.shift();if(e){e.id&&this.persistence?.remove(e.id).catch(i=>{c(this.onPersistenceError,"remove",i,e.id)});const t=new Error("offline queue overflow");t.code="OFFLINE_QUEUE_OVERFLOW",e.reject(t),this.onEvict?.(e,t)}}}notifySize(){this.onSizeChange?.(this.items.length)}}export{p as O,f as i,u as n,c as r};
@@ -0,0 +1,21 @@
1
+ import { L as LunoraClient, P as Preloaded } from "./lunora-client.d-BkDgDzTg.js";
2
+ import { F as FunctionReference, A as ArgsOf, R as ReturnOf } from "./function-reference.d-Br_hsKje.js";
3
+ /**
4
+ * Run a query once on the server (during SSR) and capture its result in a
5
+ * serializable {@link Preloaded} token. Embed the token in the rendered HTML and
6
+ * pass it to `usePreloadedQuery` on the client: the first client render shows
7
+ * the server value with no loading flash, then a live subscription takes over.
8
+ *
9
+ * The query is executed through the supplied {@link LunoraClient} over the same
10
+ * HTTP RPC path the browser uses, so the SSR client only needs a `fetch`
11
+ * implementation that can reach the worker — no in-process Durable Object access.
12
+ */
13
+ declare const preloadQuery: <F extends FunctionReference>(client: LunoraClient, function_: F, args: ArgsOf<F>, options?: {
14
+ shardKey?: string;
15
+ }) => Promise<Preloaded<ReturnOf<F>>>;
16
+ /**
17
+ * Read the captured value out of a {@link Preloaded} token without subscribing.
18
+ * Useful on the server (or in tests) when you only need the data, not a live feed.
19
+ */
20
+ declare const preloadedQueryResult: <T>(preloaded: Preloaded<T>) => T;
21
+ export { preloadedQueryResult as a, preloadQuery as p };
@@ -0,0 +1,21 @@
1
+ import { L as LunoraClient, P as Preloaded } from "./lunora-client.d-BSNUC85h.mjs";
2
+ import { F as FunctionReference, A as ArgsOf, R as ReturnOf } from "./function-reference.d-Br_hsKje.mjs";
3
+ /**
4
+ * Run a query once on the server (during SSR) and capture its result in a
5
+ * serializable {@link Preloaded} token. Embed the token in the rendered HTML and
6
+ * pass it to `usePreloadedQuery` on the client: the first client render shows
7
+ * the server value with no loading flash, then a live subscription takes over.
8
+ *
9
+ * The query is executed through the supplied {@link LunoraClient} over the same
10
+ * HTTP RPC path the browser uses, so the SSR client only needs a `fetch`
11
+ * implementation that can reach the worker — no in-process Durable Object access.
12
+ */
13
+ declare const preloadQuery: <F extends FunctionReference>(client: LunoraClient, function_: F, args: ArgsOf<F>, options?: {
14
+ shardKey?: string;
15
+ }) => Promise<Preloaded<ReturnOf<F>>>;
16
+ /**
17
+ * Read the captured value out of a {@link Preloaded} token without subscribing.
18
+ * Useful on the server (or in tests) when you only need the data, not a live feed.
19
+ */
20
+ declare const preloadedQueryResult: <T>(preloaded: Preloaded<T>) => T;
21
+ export { preloadedQueryResult as a, preloadQuery as p };
@@ -0,0 +1 @@
1
+ const t=async(e,r,a,u={})=>{const l=await e.query(r,a,u);return{__lunoraPreloaded:!0,args:a??{},functionPath:r.__lunoraRef,shardKey:u.shardKey,value:l}},d=e=>e.value;export{t as preloadQuery,d as preloadedQueryResult};
@@ -0,0 +1 @@
1
+ const s=(t,r)=>{let n=Promise.resolve();const i=e=>{const o=n.then(e,e);return n=o.then(()=>{},()=>{}),o};return{clear:()=>i(()=>t.removeItem(r)),read:async()=>{const e=await t.getItem(r);if(e!==null)try{return JSON.parse(e)}catch{return}},serialize:i,write:e=>t.setItem(r,JSON.stringify(e))}};export{s};
@@ -0,0 +1 @@
1
+ const m=e=>{let t="";for(let o=0;o<e.length;o+=32768)t+=String.fromCharCode(...e.subarray(o,o+32768));return btoa(t)},p=e=>{const t=atob(e),a=new Uint8Array(t.length);for(let o=0;o<t.length;o+=1)a[o]=t.codePointAt(o)??0;return a},i="$lunora.wire$";const w="__proto__",g={BigInt64Array,BigUint64Array,Float32Array,Float64Array,Int8Array,Int16Array,Int32Array,Uint8Array,Uint8ClampedArray,Uint16Array,Uint32Array},A={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},l=e=>{if(e===null||typeof e!="object")return!1;const t=Object.getPrototypeOf(e);return t===null||t===Object.prototype},u=(e,t=0)=>{if(t>64)throw new RangeError("wire-codec: value nesting exceeds the 64-level limit");if(e===void 0)return[i,"undefined"];if(e===null)return null;const a=typeof e;if(a==="bigint")return[i,"bigint",e.toString()];if(a==="number"){const r=e;return Number.isNaN(r)?[i,"nan"]:r===1/0?[i,"inf"]:r===-1/0?[i,"-inf"]:r}if(a!=="object")return e;if(e instanceof Date)return[i,"date",u(e.getTime(),t+1)];if(e instanceof Error){const r=e,n={};for(const f of Object.keys(r))r[f]!==void 0&&(n[f]=u(r[f],t+1));const c=[i,"error",r.name,r.message,n];return r.cause!==void 0&&c.push(u(r.cause,t+1)),c}if(e instanceof URL)return[i,"url",e.href];if(e instanceof Map)return[i,"map",[...e.entries()].map(([r,n])=>[u(r,t+1),u(n,t+1)])];if(e instanceof Set)return[i,"set",[...e].map(r=>u(r,t+1))];if(e instanceof ArrayBuffer)return[i,"bytes",m(new Uint8Array(e)),"ArrayBuffer"];if(ArrayBuffer.isView(e)){const r=e,n=r.constructor.name,c=new Uint8Array(r.buffer,r.byteOffset,r.byteLength);return n==="Uint8Array"?[i,"bytes",m(c)]:[i,"bytes",m(c),n]}if(Array.isArray(e)){const r=e.map(n=>u(n,t+1));return r.length>0&&r[0]===i?[i,"arr",r]:r}if(!l(e)){const r=e.constructor?.name??"value";throw new TypeError(`wire-codec: cannot encode a ${r} over the Lunora wire — only plain objects, arrays, and the supported built-ins (Date, Error, URL, Map, Set, ArrayBuffer/typed arrays, bigint) round-trip`)}const o=e,s={};for(const r of Object.keys(o)){const n=o[r];if(n===void 0)continue;const c=u(n,t+1);r===w?Object.defineProperty(s,r,{configurable:!0,enumerable:!0,value:c,writable:!0}):s[r]=c}return s},y=(e,t=0)=>{if(t>64)throw new RangeError("wire-codec: value nesting exceeds the 64-level limit");if(e===null||typeof e!="object")return e;if(Array.isArray(e)){if(e[0]===i)switch(e[1]){case"-inf":return-1/0;case"arr":return e[2].map(r=>y(r,t+1));case"bigint":{const r=e[2];if(typeof r!="string"||r.length>1024||!/^-?\d+$/.test(r))throw new RangeError("wire-codec: invalid or over-long bigint (max 1024 digits)");return BigInt(r)}case"date":{if(e.length<3)throw new TypeError("wire-codec: malformed date — missing payload");return new Date(y(e[2],t+1))}case"map":{const r=e[2];return new Map(r.map(n=>{if(!Array.isArray(n)||n.length!==2)throw new TypeError("wire-codec: malformed map entry — expected a [key, value] pair");return[y(n[0],t+1),y(n[1],t+1)]}))}case"set":return new Set(e[2].map(r=>y(r,t+1)));case"url":return new URL(e[2]);case"error":{const r=e[2],n=e[3],c=(Object.hasOwn(A,r)?A[r]:void 0)??Error,f=new c(n);f.name!==r&&Object.defineProperty(f,"name",{configurable:!0,value:r,writable:!0});const b=y(e[4],t+1);if(b===null||typeof b!="object"||Array.isArray(b))throw new TypeError("wire-codec: malformed error — props must be an object");for(const d of Object.keys(b))d===w?Object.defineProperty(f,d,{configurable:!0,enumerable:!0,value:b[d],writable:!0}):f[d]=b[d];return e.length>5&&Object.defineProperty(f,"cause",{configurable:!0,value:y(e[5],t+1),writable:!0}),f}case"bytes":{const r=p(e[2]),n=e[3]??"Uint8Array";if(n==="ArrayBuffer")return r.buffer.byteLength===r.byteLength?r.buffer:r.slice().buffer;const c=Object.hasOwn(g,n)?g[n]:void 0;return c?new c(r.slice().buffer):r}case"inf":return 1/0;case"nan":return Number.NaN;case"undefined":return;default:return e.map(r=>y(r,t+1))}return e.map(s=>y(s,t+1))}const a=e,o={};for(const s of Object.keys(a)){const r=y(a[s],t+1);s===w?Object.defineProperty(o,s,{configurable:!0,enumerable:!0,value:r,writable:!0}):o[s]=r}return o};export{y as d,u as e,l as i};
@@ -0,0 +1 @@
1
+ import{e as a}from"./wire-codec-BfIZrTJe.mjs";const b=/["\\\u0000-\u001F\uD800-\uDFFF]/,y=t=>b.test(t)?JSON.stringify(t):`"${t}"`,i=t=>{if(t===void 0)return"null";if(typeof t=="bigint")throw new TypeError("stableStringify: cannot use a bigint in a stable JSON cache key — pass it as a string, or use stableWireKey");if(typeof t=="number"){if(Number.isNaN(t))return"nan";if(t===1/0)return"inf";if(t===-1/0)return"-inf";if(Object.is(t,-0))return"-0"}if(typeof t=="string")return y(t);if(t===null||typeof t!="object")return JSON.stringify(t);if(Array.isArray(t)){let r="[";for(let e=0;e<t.length;e++)e>0&&(r+=","),r+=i(t[e]);return r+"]"}const o=Object.getPrototypeOf(t);if(o!==null&&o!==Object.prototype){const r=t.constructor?.name??"value";throw new TypeError(`stableStringify: cannot use a ${r} in a stable JSON cache key — only plain objects, arrays, and JSON primitives are supported (wire-typed values key via stableWireKey)`)}const s=t,c=Object.keys(s).sort();let n="{",f=!0;for(const r of c){const e=s[r];e!==void 0&&(f?f=!1:n+=",",n+=y(r),n+=":",n+=i(e))}return n+"}"},u=t=>i(a(t));export{u as s};
@@ -1,13 +1,13 @@
1
1
  /**
2
- * `@lunora/client/pagination` — the framework-agnostic pagination state machine
3
- * shared by every Lunora UI adapter (React, Vue, Svelte, Solid).
4
- *
5
- * Owns cursor tracking, page-size rebalancing (split/join), `initialPages`,
6
- * `rebalance`, and the shared types (`Page`, `PaginationResult`,
7
- * `PaginationStatus`, `PaginatedCoreResult`). Nothing here imports a UI
8
- * framework — each adapter wires the pure functions into its own reactive
9
- * primitives.
10
- */
2
+ * `@lunora/client/pagination` — the framework-agnostic pagination state machine
3
+ * shared by every Lunora UI adapter (React, Vue, Svelte, Solid).
4
+ *
5
+ * Owns cursor tracking, page-size rebalancing (split/join), `initialPages`,
6
+ * `rebalance`, and the shared types (`Page`, `PaginationResult`,
7
+ * `PaginationStatus`, `PaginatedCoreResult`). Nothing here imports a UI
8
+ * framework — each adapter wires the pure functions into its own reactive
9
+ * primitives.
10
+ */
11
11
  /** Grow factor: a bounded page is split when it exceeds this multiple of its target size. */
12
12
  declare const SPLIT_FACTOR = 2;
13
13
  /** Shrink factor: a bounded page with a neighbour is joined when it falls below this multiple of its target size. */
@@ -24,20 +24,20 @@ interface PaginationResult<T = unknown> {
24
24
  isDone: boolean;
25
25
  page: T[];
26
26
  /**
27
- * Reactive-pagination only: the midpoint cursor of a bounded
28
- * `(cursor, endCursor]` page, used to split an over-grown page into two
29
- * adjacent ranges. Absent on legacy (open-ended) pages.
30
- */
27
+ * Reactive-pagination only: the midpoint cursor of a bounded
28
+ * `(cursor, endCursor]` page, used to split an over-grown page into two
29
+ * adjacent ranges. Absent on legacy (open-ended) pages.
30
+ */
31
31
  splitCursor?: null | string;
32
32
  }
33
33
  /**
34
- * Lifecycle of a `usePaginatedQuery` feed.
35
- *
36
- * - `LoadingFirstPage` — the first page is in flight; `results` is empty.
37
- * - `CanLoadMore` — the loaded tail has a cursor; calling `loadMore` fetches the next page.
38
- * - `LoadingMore` — a `loadMore` page is in flight; earlier results stay visible.
39
- * - `Exhausted` — every page has loaded and the server reported `isDone`.
40
- */
34
+ * Lifecycle of a `usePaginatedQuery` feed.
35
+ *
36
+ * - `LoadingFirstPage` — the first page is in flight; `results` is empty.
37
+ * - `CanLoadMore` — the loaded tail has a cursor; calling `loadMore` fetches the next page.
38
+ * - `LoadingMore` — a `loadMore` page is in flight; earlier results stay visible.
39
+ * - `Exhausted` — every page has loaded and the server reported `isDone`.
40
+ */
41
41
  type PaginationStatus = "CanLoadMore" | "Exhausted" | "LoadingFirstPage" | "LoadingMore";
42
42
  interface PaginatedCoreResult<T> {
43
43
  /** Request another page off the open-ended tail. A no-op unless `status === "CanLoadMore"`. */
@@ -49,34 +49,34 @@ interface PaginatedCoreResult<T> {
49
49
  /** First-page seed: a single open-ended range starting at the feed head. */
50
50
  declare const initialPages: (numberItems: number) => Page[];
51
51
  /**
52
- * Run the SPLIT/JOIN maintenance pass over the current page list given freshly
53
- * resolved results. Returns a new page list when a boundary changed, or
54
- * `undefined` when the layout is already balanced (so the caller can skip a
55
- * setState).
56
- *
57
- * Only ONE structural edit is applied per pass (the first split or join found),
58
- * letting the subsequent re-render's resolved results drive the next pass — this
59
- * keeps each transition observable and avoids reasoning about several
60
- * simultaneous boundary moves.
61
- */
52
+ * Run the SPLIT/JOIN maintenance pass over the current page list given freshly
53
+ * resolved results. Returns a new page list when a boundary changed, or
54
+ * `undefined` when the layout is already balanced (so the caller can skip a
55
+ * setState).
56
+ *
57
+ * Only ONE structural edit is applied per pass (the first split or join found),
58
+ * letting the subsequent re-render's resolved results drive the next pass — this
59
+ * keeps each transition observable and avoids reasoning about several
60
+ * simultaneous boundary moves.
61
+ */
62
62
  declare const rebalance: (pages: Page[], results: (PaginationResult | undefined)[]) => Page[] | undefined;
63
63
  /**
64
- * Derive the feed `status` and the next-page cursor from the current page list
65
- * and resolved page results. Framework adapters call this each render/effect to
66
- * compute what to expose to callers.
67
- */
64
+ * Derive the feed `status` and the next-page cursor from the current page list
65
+ * and resolved page results. Framework adapters call this each render/effect to
66
+ * compute what to expose to callers.
67
+ */
68
68
  declare const derivePaginationStatus: <T>(skipped: boolean, pageResults: (PaginationResult<T> | undefined)[]) => {
69
69
  nextCursor: null | string | undefined;
70
70
  status: PaginationStatus;
71
71
  };
72
72
  /**
73
- * Apply a `loadMore` to the current page list. Pins the open-ended tail at
74
- * `cursor` (making it a fixed bounded range) and appends a fresh open-ended
75
- * page starting at `cursor`. The shared boundary keeps the feed gap- and
76
- * dup-free.
77
- *
78
- * Returns the new page list, or `undefined` when the given `cursor` is not
79
- * valid (null or undefined — caller should no-op).
80
- */
73
+ * Apply a `loadMore` to the current page list. Pins the open-ended tail at
74
+ * `cursor` (making it a fixed bounded range) and appends a fresh open-ended
75
+ * page starting at `cursor`. The shared boundary keeps the feed gap- and
76
+ * dup-free.
77
+ *
78
+ * Returns the new page list, or `undefined` when the given `cursor` is not
79
+ * valid (null or undefined — caller should no-op).
80
+ */
81
81
  declare const applyLoadMore: (pages: Page[], cursor: null | string | undefined, numberItems: number) => Page[] | undefined;
82
82
  export { JOIN_FACTOR, type Page, type PaginatedCoreResult, type PaginationResult, type PaginationStatus, SPLIT_FACTOR, applyLoadMore, derivePaginationStatus, initialPages, rebalance };
@@ -1,13 +1,13 @@
1
1
  /**
2
- * `@lunora/client/pagination` — the framework-agnostic pagination state machine
3
- * shared by every Lunora UI adapter (React, Vue, Svelte, Solid).
4
- *
5
- * Owns cursor tracking, page-size rebalancing (split/join), `initialPages`,
6
- * `rebalance`, and the shared types (`Page`, `PaginationResult`,
7
- * `PaginationStatus`, `PaginatedCoreResult`). Nothing here imports a UI
8
- * framework — each adapter wires the pure functions into its own reactive
9
- * primitives.
10
- */
2
+ * `@lunora/client/pagination` — the framework-agnostic pagination state machine
3
+ * shared by every Lunora UI adapter (React, Vue, Svelte, Solid).
4
+ *
5
+ * Owns cursor tracking, page-size rebalancing (split/join), `initialPages`,
6
+ * `rebalance`, and the shared types (`Page`, `PaginationResult`,
7
+ * `PaginationStatus`, `PaginatedCoreResult`). Nothing here imports a UI
8
+ * framework — each adapter wires the pure functions into its own reactive
9
+ * primitives.
10
+ */
11
11
  /** Grow factor: a bounded page is split when it exceeds this multiple of its target size. */
12
12
  declare const SPLIT_FACTOR = 2;
13
13
  /** Shrink factor: a bounded page with a neighbour is joined when it falls below this multiple of its target size. */
@@ -24,20 +24,20 @@ interface PaginationResult<T = unknown> {
24
24
  isDone: boolean;
25
25
  page: T[];
26
26
  /**
27
- * Reactive-pagination only: the midpoint cursor of a bounded
28
- * `(cursor, endCursor]` page, used to split an over-grown page into two
29
- * adjacent ranges. Absent on legacy (open-ended) pages.
30
- */
27
+ * Reactive-pagination only: the midpoint cursor of a bounded
28
+ * `(cursor, endCursor]` page, used to split an over-grown page into two
29
+ * adjacent ranges. Absent on legacy (open-ended) pages.
30
+ */
31
31
  splitCursor?: null | string;
32
32
  }
33
33
  /**
34
- * Lifecycle of a `usePaginatedQuery` feed.
35
- *
36
- * - `LoadingFirstPage` — the first page is in flight; `results` is empty.
37
- * - `CanLoadMore` — the loaded tail has a cursor; calling `loadMore` fetches the next page.
38
- * - `LoadingMore` — a `loadMore` page is in flight; earlier results stay visible.
39
- * - `Exhausted` — every page has loaded and the server reported `isDone`.
40
- */
34
+ * Lifecycle of a `usePaginatedQuery` feed.
35
+ *
36
+ * - `LoadingFirstPage` — the first page is in flight; `results` is empty.
37
+ * - `CanLoadMore` — the loaded tail has a cursor; calling `loadMore` fetches the next page.
38
+ * - `LoadingMore` — a `loadMore` page is in flight; earlier results stay visible.
39
+ * - `Exhausted` — every page has loaded and the server reported `isDone`.
40
+ */
41
41
  type PaginationStatus = "CanLoadMore" | "Exhausted" | "LoadingFirstPage" | "LoadingMore";
42
42
  interface PaginatedCoreResult<T> {
43
43
  /** Request another page off the open-ended tail. A no-op unless `status === "CanLoadMore"`. */
@@ -49,34 +49,34 @@ interface PaginatedCoreResult<T> {
49
49
  /** First-page seed: a single open-ended range starting at the feed head. */
50
50
  declare const initialPages: (numberItems: number) => Page[];
51
51
  /**
52
- * Run the SPLIT/JOIN maintenance pass over the current page list given freshly
53
- * resolved results. Returns a new page list when a boundary changed, or
54
- * `undefined` when the layout is already balanced (so the caller can skip a
55
- * setState).
56
- *
57
- * Only ONE structural edit is applied per pass (the first split or join found),
58
- * letting the subsequent re-render's resolved results drive the next pass — this
59
- * keeps each transition observable and avoids reasoning about several
60
- * simultaneous boundary moves.
61
- */
52
+ * Run the SPLIT/JOIN maintenance pass over the current page list given freshly
53
+ * resolved results. Returns a new page list when a boundary changed, or
54
+ * `undefined` when the layout is already balanced (so the caller can skip a
55
+ * setState).
56
+ *
57
+ * Only ONE structural edit is applied per pass (the first split or join found),
58
+ * letting the subsequent re-render's resolved results drive the next pass — this
59
+ * keeps each transition observable and avoids reasoning about several
60
+ * simultaneous boundary moves.
61
+ */
62
62
  declare const rebalance: (pages: Page[], results: (PaginationResult | undefined)[]) => Page[] | undefined;
63
63
  /**
64
- * Derive the feed `status` and the next-page cursor from the current page list
65
- * and resolved page results. Framework adapters call this each render/effect to
66
- * compute what to expose to callers.
67
- */
64
+ * Derive the feed `status` and the next-page cursor from the current page list
65
+ * and resolved page results. Framework adapters call this each render/effect to
66
+ * compute what to expose to callers.
67
+ */
68
68
  declare const derivePaginationStatus: <T>(skipped: boolean, pageResults: (PaginationResult<T> | undefined)[]) => {
69
69
  nextCursor: null | string | undefined;
70
70
  status: PaginationStatus;
71
71
  };
72
72
  /**
73
- * Apply a `loadMore` to the current page list. Pins the open-ended tail at
74
- * `cursor` (making it a fixed bounded range) and appends a fresh open-ended
75
- * page starting at `cursor`. The shared boundary keeps the feed gap- and
76
- * dup-free.
77
- *
78
- * Returns the new page list, or `undefined` when the given `cursor` is not
79
- * valid (null or undefined — caller should no-op).
80
- */
73
+ * Apply a `loadMore` to the current page list. Pins the open-ended tail at
74
+ * `cursor` (making it a fixed bounded range) and appends a fresh open-ended
75
+ * page starting at `cursor`. The shared boundary keeps the feed gap- and
76
+ * dup-free.
77
+ *
78
+ * Returns the new page list, or `undefined` when the given `cursor` is not
79
+ * valid (null or undefined — caller should no-op).
80
+ */
81
81
  declare const applyLoadMore: (pages: Page[], cursor: null | string | undefined, numberItems: number) => Page[] | undefined;
82
82
  export { JOIN_FACTOR, type Page, type PaginatedCoreResult, type PaginationResult, type PaginationStatus, SPLIT_FACTOR, applyLoadMore, derivePaginationStatus, initialPages, rebalance };
@@ -1,61 +1 @@
1
- const SPLIT_FACTOR = 2;
2
- const JOIN_FACTOR = 0.5;
3
- const initialPages = (numberItems) => [
4
- // eslint-disable-next-line unicorn/no-null -- `lower: null` is the feed head, `upper: null` the open-ended tail — both are wire-shape cursors.
5
- { lower: null, numItems: numberItems, upper: null }
6
- ];
7
- const rebalance = (pages, results) => {
8
- for (const [index, page] of pages.entries()) {
9
- if (page.upper === null) {
10
- continue;
11
- }
12
- const result = results[index];
13
- if (!result) {
14
- continue;
15
- }
16
- const size = result.page.length;
17
- if (size > SPLIT_FACTOR * page.numItems && result.splitCursor) {
18
- const split = result.splitCursor;
19
- const next = [...pages];
20
- next.splice(index, 1, { lower: page.lower, numItems: page.numItems, upper: split }, { lower: split, numItems: page.numItems, upper: page.upper });
21
- return next;
22
- }
23
- if (size < JOIN_FACTOR * page.numItems && index + 1 < pages.length) {
24
- const neighbour = pages[index + 1];
25
- if (!neighbour) {
26
- continue;
27
- }
28
- const next = [...pages];
29
- next.splice(index, 2, { lower: page.lower, numItems: page.numItems, upper: neighbour.upper });
30
- return next;
31
- }
32
- }
33
- return void 0;
34
- };
35
- const derivePaginationStatus = (skipped, pageResults) => {
36
- if (skipped || !pageResults[0]) {
37
- return { nextCursor: void 0, status: "LoadingFirstPage" };
38
- }
39
- const tail = pageResults.at(-1);
40
- if (!tail) {
41
- return { nextCursor: void 0, status: "LoadingMore" };
42
- }
43
- if (tail.isDone || tail.continueCursor === null) {
44
- return { nextCursor: void 0, status: "Exhausted" };
45
- }
46
- return { nextCursor: tail.continueCursor, status: "CanLoadMore" };
47
- };
48
- const applyLoadMore = (pages, cursor, numberItems) => {
49
- if (cursor === void 0 || cursor === null) {
50
- return void 0;
51
- }
52
- const next = [...pages];
53
- const tail = next.at(-1);
54
- if (tail) {
55
- next[next.length - 1] = { lower: tail.lower, numItems: tail.numItems, upper: cursor };
56
- }
57
- next.push({ lower: cursor, numItems: numberItems, upper: null });
58
- return next;
59
- };
60
-
61
- export { JOIN_FACTOR, SPLIT_FACTOR, applyLoadMore, derivePaginationStatus, initialPages, rebalance };
1
+ const l=2,m=.5,p=e=>[{lower:null,numItems:e,upper:null}],c=(e,r)=>{for(const[n,t]of e.entries()){if(t.upper===null)continue;const o=r[n];if(!o)continue;const i=o.page.length;if(i>2*t.numItems&&o.splitCursor){const u=o.splitCursor,s=[...e];return s.splice(n,1,{lower:t.lower,numItems:t.numItems,upper:u},{lower:u,numItems:t.numItems,upper:t.upper}),s}if(i<.5*t.numItems&&n+1<e.length){const u=e[n+1];if(!u)continue;const s=[...e];return s.splice(n,2,{lower:t.lower,numItems:t.numItems,upper:u.upper}),s}}},a=(e,r)=>{if(e||!r[0])return{nextCursor:void 0,status:"LoadingFirstPage"};const n=r.at(-1);return n?n.isDone||n.continueCursor===null?{nextCursor:void 0,status:"Exhausted"}:{nextCursor:n.continueCursor,status:"CanLoadMore"}:{nextCursor:void 0,status:"LoadingMore"}},I=(e,r,n)=>{if(r==null)return;const t=[...e],o=t.at(-1);return o&&(t[t.length-1]={lower:o.lower,numItems:o.numItems,upper:r}),t.push({lower:r,numItems:n,upper:null}),t};export{m as JOIN_FACTOR,l as SPLIT_FACTOR,I as applyLoadMore,a as derivePaginationStatus,p as initialPages,c as rebalance};
@@ -1,26 +1,39 @@
1
- import { S as SubscriptionError, F as FunctionReference, A as ArgsOf, R as ReturnOf, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-DLbxPGH9.mjs";
2
- export type { b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-DLbxPGH9.mjs";
1
+ import { S as SubscriptionError, L as LunoraClient, a as Unsubscribe } from "../packem_shared/lunora-client.d-BSNUC85h.mjs";
2
+ export type {
3
+ /**
4
+ * `@lunora/client/query` — the framework-neutral live-query state machine shared
5
+ * by every Lunora UI adapter (React, Vue, Svelte, Solid). It owns the seam
6
+ * between a `LunoraClient` subscription and a framework's reactivity primitive:
7
+ * the subscribe → snapshot → error/reset → cleanup lifecycle, with the
8
+ * skip-handling, value/error fan-out, attach-throw normalisation, and
9
+ * cancellation-guarded teardown all in one place. Nothing here imports a UI
10
+ * framework — each adapter supplies thin sinks that write into its own ref /
11
+ * store / signal / cache.
12
+ */
13
+ b as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-BSNUC85h.mjs";
14
+ import { F as FunctionReference, A as ArgsOf, R as ReturnOf } from "../packem_shared/function-reference.d-Br_hsKje.mjs";
15
+ import '@lunora/errors';
3
16
  import '@lunora/runtime';
4
17
  /**
5
- * The sentinel a framework adapter resolves its reactive args to when it wants
6
- * to short-circuit a query — no network call, no socket.
7
- */
18
+ * The sentinel a framework adapter resolves its reactive args to when it wants
19
+ * to short-circuit a query — no network call, no socket.
20
+ */
8
21
  declare const SKIP: "skip";
9
22
  /** Args after a framework has resolved its reactivity primitive, or the skip sentinel. */
10
23
  type ResolvedArgs<F extends FunctionReference> = ArgsOf<F> | typeof SKIP;
11
24
  /**
12
- * The framework-neutral sinks a {@link createQuerySubscription} call drives. A
13
- * framework adapter supplies these, wiring each into its own reactivity
14
- * primitive (a TanStack cache write, a Vue `ref`, a Svelte store `set`, a Solid
15
- * signal setter).
16
- *
17
- * `onData` fires when a fresh server value lands (the initial frame or a delta).
18
- * `onError` fires when the subscription attach threw, or the server pushed a
19
- * subscription-scoped error — it is optional, and when omitted an attach throw
20
- * propagates to the caller (preserving the "no error channel" behaviour of
21
- * adapters like Solid/Vue that never had one) rather than being swallowed.
22
- * `onReset` fires when the resolved args are `"skip"`: clear any prior value.
23
- */
25
+ * The framework-neutral sinks a {@link createQuerySubscription} call drives. A
26
+ * framework adapter supplies these, wiring each into its own reactivity
27
+ * primitive (a TanStack cache write, a Vue `ref`, a Svelte store `set`, a Solid
28
+ * signal setter).
29
+ *
30
+ * `onData` fires when a fresh server value lands (the initial frame or a delta).
31
+ * `onError` fires when the subscription attach threw, or the server pushed a
32
+ * subscription-scoped error — it is optional, and when omitted an attach throw
33
+ * propagates to the caller (preserving the "no error channel" behaviour of
34
+ * adapters like Solid/Vue that never had one) rather than being swallowed.
35
+ * `onReset` fires when the resolved args are `"skip"`: clear any prior value.
36
+ */
24
37
  interface QuerySubscriptionSinks<T> {
25
38
  onData: (value: T) => void;
26
39
  onError?: (error: SubscriptionError) => void;
@@ -33,30 +46,85 @@ interface QuerySubscriptionOptions {
33
46
  /** Normalise an unknown thrown value into the client's {@link SubscriptionError} shape. */
34
47
  declare const toSubscriptionError: (error: unknown) => SubscriptionError;
35
48
  /**
36
- * The subscribe → snapshot → error/reset → cleanup state machine shared by
37
- * every Lunora framework adapter's live-query hook.
38
- *
39
- * Given a `client`, a function reference, the already-resolved `args` (a
40
- * framework reads its own reactive source first, then hands the plain value in),
41
- * and a set of {@link QuerySubscriptionSinks}, this opens one `client.subscribe`
42
- * registration and returns the {@link Unsubscribe} to tear it down. Each
43
- * framework owns *when* to call this (a React effect, a Vue `watch`, a Svelte
44
- * store start callback, a Solid `createEffect(on(...))`) and *where* to stash
45
- * the value — this owns the lifecycle in between, so the skip-handling, the
46
- * value/error fan-out, the attach-throw normalisation, and the
47
- * cancellation-guarded teardown live in exactly one place.
48
- *
49
- * Behaviour. When `args === "skip"` it calls `sinks.onReset?.()` and returns a
50
- * no-op teardown; no `client.subscribe` is issued. Otherwise it opens
51
- * `client.subscribe(fn, args, …, { shardKey, onError })`: server pushes route to
52
- * `sinks.onData`, and the client's own `onError` channel (a server-rejected
53
- * subscription) routes to `sinks.onError` when present. The attach is wrapped —
54
- * if `client.subscribe` itself throws, the error is normalised to a
55
- * {@link SubscriptionError} and delivered to `sinks.onError` when present; with
56
- * no `onError` sink the throw is rethrown so adapters without an error channel
57
- * behave exactly as before. The returned teardown is idempotent and
58
- * cancellation-guarded: once it runs, no further `onData`/`onError` from an
59
- * in-flight push reaches the sinks.
60
- */
49
+ * The subscribe → snapshot → error/reset → cleanup state machine shared by
50
+ * every Lunora framework adapter's live-query hook.
51
+ *
52
+ * Given a `client`, a function reference, the already-resolved `args` (a
53
+ * framework reads its own reactive source first, then hands the plain value in),
54
+ * and a set of {@link QuerySubscriptionSinks}, this opens one `client.subscribe`
55
+ * registration and returns the {@link Unsubscribe} to tear it down. Each
56
+ * framework owns *when* to call this (a React effect, a Vue `watch`, a Svelte
57
+ * store start callback, a Solid `createEffect(on(...))`) and *where* to stash
58
+ * the value — this owns the lifecycle in between, so the skip-handling, the
59
+ * value/error fan-out, the attach-throw normalisation, and the
60
+ * cancellation-guarded teardown live in exactly one place.
61
+ *
62
+ * Behaviour. When `args === "skip"` it calls `sinks.onReset?.()` and returns a
63
+ * no-op teardown; no `client.subscribe` is issued. Otherwise it opens
64
+ * `client.subscribe(fn, args, …, { shardKey, onError })`: server pushes route to
65
+ * `sinks.onData`, and the client's own `onError` channel (a server-rejected
66
+ * subscription) routes to `sinks.onError` when present. The attach is wrapped —
67
+ * if `client.subscribe` itself throws, the error is normalised to a
68
+ * {@link SubscriptionError} and delivered to `sinks.onError` when present; with
69
+ * no `onError` sink the throw is rethrown so adapters without an error channel
70
+ * behave exactly as before. The returned teardown is idempotent and
71
+ * cancellation-guarded: once it runs, no further `onData`/`onError` from an
72
+ * in-flight push reaches the sinks.
73
+ */
61
74
  declare const createQuerySubscription: <F extends FunctionReference, T = ReturnOf<F>>(client: LunoraClient, function_: F, args: ResolvedArgs<F>, sinks: QuerySubscriptionSinks<T>, options?: QuerySubscriptionOptions) => Unsubscribe;
62
- export { type ArgsOf, type FunctionReference, type QuerySubscriptionOptions, type QuerySubscriptionSinks, type ResolvedArgs, type ReturnOf, SKIP, type SubscriptionError, type Unsubscribe, createQuerySubscription, toSubscriptionError };
75
+ export {
76
+ /**
77
+ * `@lunora/client/query` — the framework-neutral live-query state machine shared
78
+ * by every Lunora UI adapter (React, Vue, Svelte, Solid). It owns the seam
79
+ * between a `LunoraClient` subscription and a framework's reactivity primitive:
80
+ * the subscribe → snapshot → error/reset → cleanup lifecycle, with the
81
+ * skip-handling, value/error fan-out, attach-throw normalisation, and
82
+ * cancellation-guarded teardown all in one place. Nothing here imports a UI
83
+ * framework — each adapter supplies thin sinks that write into its own ref /
84
+ * store / signal / cache.
85
+ */
86
+ type ArgsOf,
87
+ /**
88
+ * `@lunora/client/query` — the framework-neutral live-query state machine shared
89
+ * by every Lunora UI adapter (React, Vue, Svelte, Solid). It owns the seam
90
+ * between a `LunoraClient` subscription and a framework's reactivity primitive:
91
+ * the subscribe → snapshot → error/reset → cleanup lifecycle, with the
92
+ * skip-handling, value/error fan-out, attach-throw normalisation, and
93
+ * cancellation-guarded teardown all in one place. Nothing here imports a UI
94
+ * framework — each adapter supplies thin sinks that write into its own ref /
95
+ * store / signal / cache.
96
+ */
97
+ type FunctionReference, type QuerySubscriptionOptions, type QuerySubscriptionSinks, type ResolvedArgs,
98
+ /**
99
+ * `@lunora/client/query` — the framework-neutral live-query state machine shared
100
+ * by every Lunora UI adapter (React, Vue, Svelte, Solid). It owns the seam
101
+ * between a `LunoraClient` subscription and a framework's reactivity primitive:
102
+ * the subscribe → snapshot → error/reset → cleanup lifecycle, with the
103
+ * skip-handling, value/error fan-out, attach-throw normalisation, and
104
+ * cancellation-guarded teardown all in one place. Nothing here imports a UI
105
+ * framework — each adapter supplies thin sinks that write into its own ref /
106
+ * store / signal / cache.
107
+ */
108
+ type ReturnOf, SKIP,
109
+ /**
110
+ * `@lunora/client/query` — the framework-neutral live-query state machine shared
111
+ * by every Lunora UI adapter (React, Vue, Svelte, Solid). It owns the seam
112
+ * between a `LunoraClient` subscription and a framework's reactivity primitive:
113
+ * the subscribe → snapshot → error/reset → cleanup lifecycle, with the
114
+ * skip-handling, value/error fan-out, attach-throw normalisation, and
115
+ * cancellation-guarded teardown all in one place. Nothing here imports a UI
116
+ * framework — each adapter supplies thin sinks that write into its own ref /
117
+ * store / signal / cache.
118
+ */
119
+ type SubscriptionError,
120
+ /**
121
+ * `@lunora/client/query` — the framework-neutral live-query state machine shared
122
+ * by every Lunora UI adapter (React, Vue, Svelte, Solid). It owns the seam
123
+ * between a `LunoraClient` subscription and a framework's reactivity primitive:
124
+ * the subscribe → snapshot → error/reset → cleanup lifecycle, with the
125
+ * skip-handling, value/error fan-out, attach-throw normalisation, and
126
+ * cancellation-guarded teardown all in one place. Nothing here imports a UI
127
+ * framework — each adapter supplies thin sinks that write into its own ref /
128
+ * store / signal / cache.
129
+ */
130
+ type Unsubscribe, createQuerySubscription, toSubscriptionError };