@lunora/client 1.0.0-alpha.4 → 1.0.0-alpha.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +6 -0
- package/README.md +2 -0
- package/dist/auth/index.d.mts +11 -10
- package/dist/auth/index.d.ts +11 -10
- package/dist/auth/index.mjs +1 -60
- package/dist/index.d.mts +736 -172
- package/dist/index.d.ts +736 -172
- package/dist/index.mjs +1 -14
- package/dist/packem_shared/CONFLICT_ERROR_CODE-B2SU0CaJ.mjs +1 -0
- package/dist/packem_shared/ClientServiceWorker-CRQ5yl7-.mjs +1 -0
- package/dist/packem_shared/DEFAULT_MAX_BUFFER-TBn2s7Rz.mjs +1 -0
- package/dist/packem_shared/LunoraClient-Cz-RavLT.mjs +1 -0
- package/dist/packem_shared/OfflineQueue-y4mymc5n.mjs +1 -0
- package/dist/packem_shared/RETIRE_AFTER_DURABLE_SEQ_ADVANCE-D96iRJDY.mjs +1 -0
- package/dist/packem_shared/SKIP-CArzT3Vl.mjs +1 -0
- package/dist/packem_shared/SubscriptionRegistry-C72rCp44.mjs +1 -0
- package/dist/packem_shared/TabCoordinator-CMFYQCL-.mjs +1 -0
- package/dist/packem_shared/anyApi-BD4dgvDY.mjs +1 -0
- package/dist/packem_shared/applyDelta-DXqQORbM.mjs +1 -0
- package/dist/packem_shared/createAsyncStoragePersistence-BRLqeXsC.mjs +1 -0
- package/dist/packem_shared/createClientQuery-CrdRoicO.mjs +1 -0
- package/dist/packem_shared/createInMemoryBookmarkStorage-DCd5Ej-t.mjs +1 -0
- package/dist/packem_shared/createInMemoryPersistence-CQYXy8UP.mjs +1 -0
- package/dist/packem_shared/createInMemoryQueryCache-DjCKrLjk.mjs +1 -0
- package/dist/packem_shared/createLocalStore-CwuKPK9x.mjs +1 -0
- package/dist/packem_shared/createMutationRunner-lQXH8Amk.mjs +1 -0
- package/dist/packem_shared/createMutatorRunner-BVzo0FBZ.mjs +1 -0
- package/dist/packem_shared/createReconnect-CFT7YRSR.mjs +1 -0
- package/dist/packem_shared/createReply-DF0jRsv-.mjs +1 -0
- package/dist/packem_shared/createServerClient-CQf6XOk3.mjs +1 -0
- package/dist/packem_shared/createSnapshotPrecondition-CKvCNKyD.mjs +1 -0
- package/dist/packem_shared/deserializePreloaded-CHfOFpka.mjs +1 -0
- package/dist/packem_shared/getServerSession-CGjgRBAE.mjs +1 -0
- package/dist/packem_shared/httpStream-Cz4_QyjV.mjs +11 -0
- package/dist/packem_shared/idb-utility-C-DR8nG1.mjs +1 -0
- package/dist/packem_shared/local-store-DFMSEM2j.mjs +1 -0
- package/dist/packem_shared/lunora-client.d-B23myJYW.d.ts +2168 -0
- package/dist/packem_shared/lunora-client.d-BRXKXo0T.d.mts +2168 -0
- package/dist/packem_shared/offline-queue-BeWay5u5.mjs +1 -0
- package/dist/packem_shared/preload.d-BgoF8JQZ.d.mts +21 -0
- package/dist/packem_shared/preload.d-cq7evweX.d.ts +21 -0
- package/dist/packem_shared/preloadQuery-DjwSjFwt.mjs +1 -0
- package/dist/packem_shared/types.d-CA7ZeJm9.d.mts +942 -0
- package/dist/packem_shared/types.d-CA7ZeJm9.d.ts +942 -0
- package/dist/packem_shared/wire-codec-D_fiax9d.mjs +1 -0
- package/dist/packem_shared/wire-key-j8BZ65HQ.mjs +1 -0
- package/dist/pagination/index.d.mts +42 -42
- package/dist/pagination/index.d.ts +42 -42
- package/dist/pagination/index.mjs +1 -61
- package/dist/query/index.d.mts +43 -42
- package/dist/query/index.d.ts +43 -42
- package/dist/query/index.mjs +1 -1
- package/dist/service.d.mts +49 -0
- package/dist/service.d.ts +49 -0
- package/dist/service.mjs +1 -0
- package/dist/ssr/index.d.mts +80 -79
- package/dist/ssr/index.d.ts +80 -79
- package/dist/ssr/index.mjs +1 -4
- package/dist/upload.d.mts +35 -0
- package/dist/upload.d.ts +35 -0
- package/dist/upload.mjs +1 -0
- package/package.json +13 -1
- package/dist/packem_shared/CONFLICT_ERROR_CODE-aUdVbEDw.mjs +0 -4
- package/dist/packem_shared/DEFAULT_MAX_BUFFER-BDkqO5PW.mjs +0 -107
- package/dist/packem_shared/LunoraClient-DHMV-94j.mjs +0 -2198
- package/dist/packem_shared/OfflineQueue-D5p_QgF_.mjs +0 -127
- package/dist/packem_shared/SKIP-vItZChkw.mjs +0 -50
- package/dist/packem_shared/SubscriptionRegistry-Dn-7k7eo.mjs +0 -1
- package/dist/packem_shared/applyDelta-4jFGTPA3.mjs +0 -61
- package/dist/packem_shared/createAsyncStoragePersistence-1Z5BZ8RC.mjs +0 -45
- package/dist/packem_shared/createInMemoryBookmarkStorage-BoN7a7TH.mjs +0 -11
- package/dist/packem_shared/createInMemoryPersistence-CW82inU5.mjs +0 -105
- package/dist/packem_shared/createInMemoryQueryCache-B1PQ9Twl.mjs +0 -138
- package/dist/packem_shared/createLocalStore-DSUfoLqY.mjs +0 -36
- package/dist/packem_shared/createMutationRunner-BqsavzvG.mjs +0 -21
- package/dist/packem_shared/createReconnect-Di_-oHH7.mjs +0 -22
- package/dist/packem_shared/createServerClient-DMh-2UDv.mjs +0 -11
- package/dist/packem_shared/deserializePreloaded-C0eJTY_W.mjs +0 -4
- package/dist/packem_shared/getServerSession-8jXewqxd.mjs +0 -13
- package/dist/packem_shared/lunora-client.d-CbnxmuqG.d.mts +0 -1623
- package/dist/packem_shared/lunora-client.d-CbnxmuqG.d.ts +0 -1623
- package/dist/packem_shared/preload.d-Bhq6DBSe.d.mts +0 -20
- package/dist/packem_shared/preload.d-C73DT6bL.d.ts +0 -20
- package/dist/packem_shared/preloadQuery-lobFkD2Z.mjs +0 -13
- package/dist/packem_shared/subscription-C1Jy7HiF.mjs +0 -55
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const b=r=>{let n="";for(let u=0;u<r.length;u+=32768)n+=String.fromCharCode(...r.subarray(u,u+32768));return btoa(n)},g=r=>{const n=atob(r),u=new Uint8Array(n.length);for(let c=0;c<n.length;c+=1)u[c]=n.codePointAt(c)??0;return u},o="$lunora.wire$";const p="__proto__",w={BigInt64Array,BigUint64Array,Float32Array,Float64Array,Int8Array,Int16Array,Int32Array,Uint8Array,Uint8ClampedArray,Uint16Array,Uint32Array},d={Error,EvalError,RangeError,ReferenceError,SyntaxError,TypeError,URIError},m=r=>{if(r===null||typeof r!="object")return!1;const n=Object.getPrototypeOf(r);return n===null||n===Object.prototype},s=(r,n=0)=>{if(n>64)throw new RangeError("wire-codec: value nesting exceeds the 64-level limit");if(r===void 0)return[o,"undefined"];if(r===null)return null;const u=typeof r;if(u==="bigint")return[o,"bigint",r.toString()];if(u==="number"){const e=r;return Number.isNaN(e)?[o,"nan"]:e===1/0?[o,"inf"]:e===-1/0?[o,"-inf"]:e}if(u!=="object")return r;if(r instanceof Date)return[o,"date",s(r.getTime(),n+1)];if(r instanceof Error){const e=r,a={};for(const l of Object.keys(e))e[l]!==void 0&&(a[l]=s(e[l],n+1));const i=[o,"error",e.name,e.message,a];return e.cause!==void 0&&i.push(s(e.cause,n+1)),i}if(r instanceof URL)return[o,"url",r.href];if(r instanceof Map)return[o,"map",[...r.entries()].map(([e,a])=>[s(e,n+1),s(a,n+1)])];if(r instanceof Set)return[o,"set",[...r].map(e=>s(e,n+1))];if(r instanceof ArrayBuffer)return[o,"bytes",b(new Uint8Array(r)),"ArrayBuffer"];if(ArrayBuffer.isView(r)){const e=r,a=e.constructor.name,i=new Uint8Array(e.buffer,e.byteOffset,e.byteLength);return a==="Uint8Array"?[o,"bytes",b(i)]:[o,"bytes",b(i),a]}if(Array.isArray(r)){const e=r.map(a=>s(a,n+1));return e.length>0&&e[0]===o?[o,"arr",e]:e}if(!m(r)){const e=r.constructor?.name??"value";throw new TypeError(`wire-codec: cannot encode a ${e} 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 c=r,t={};for(const e of Object.keys(c)){const a=c[e];if(a===void 0)continue;const i=s(a,n+1);e===p?Object.defineProperty(t,e,{configurable:!0,enumerable:!0,value:i,writable:!0}):t[e]=i}return t},f=(r,n=0)=>{if(n>64)throw new RangeError("wire-codec: value nesting exceeds the 64-level limit");if(r===null||typeof r!="object")return r;if(Array.isArray(r)){if(r[0]===o)switch(r[1]){case"-inf":return-1/0;case"arr":return r[2].map(t=>f(t,n+1));case"bigint":{const t=r[2];if(typeof t!="string"||t.length>1024||!/^-?\d+$/.test(t))throw new RangeError("wire-codec: invalid or over-long bigint (max 1024 digits)");return BigInt(t)}case"date":return new Date(f(r[2],n+1));case"map":return new Map(r[2].map(([t,e])=>[f(t,n+1),f(e,n+1)]));case"set":return new Set(r[2].map(t=>f(t,n+1)));case"url":return new URL(r[2]);case"error":{const t=r[2],e=r[3],a=(Object.hasOwn(d,t)?d[t]:void 0)??Error,i=new a(e);i.name!==t&&Object.defineProperty(i,"name",{configurable:!0,value:t,writable:!0});const l=f(r[4],n+1);for(const y of Object.keys(l))y===p?Object.defineProperty(i,y,{configurable:!0,enumerable:!0,value:l[y],writable:!0}):i[y]=l[y];return r.length>5&&Object.defineProperty(i,"cause",{configurable:!0,value:f(r[5],n+1),writable:!0}),i}case"bytes":{const t=g(r[2]),e=r[3]??"Uint8Array";if(e==="ArrayBuffer")return t.buffer.byteLength===t.byteLength?t.buffer:t.slice().buffer;const a=Object.hasOwn(w,e)?w[e]:void 0;return a?new a(t.slice().buffer):t}case"inf":return 1/0;case"nan":return Number.NaN;case"undefined":return;default:return r.map(t=>f(t,n+1))}return r.map(t=>f(t,n+1))}const u=r,c={};for(const t of Object.keys(u)){const e=f(u[t],n+1);t===p?Object.defineProperty(c,t,{configurable:!0,enumerable:!0,value:e,writable:!0}):c[t]=e}return c};export{s as a,f as s};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as c}from"./wire-codec-D_fiax9d.mjs";const y=(e,t)=>e<t?-1:e>t?1:0,n=e=>{if(e===void 0)return"null";if(typeof e=="bigint")throw new TypeError("stableStringify: cannot use a bigint in a stable JSON cache key — pass it as a string, or use stableWireKey");if(e===null||typeof e!="object")return JSON.stringify(e);if(Array.isArray(e))return`[${e.map(r=>n(r)).join(",")}]`;const t=Object.getPrototypeOf(e);if(t!==null&&t!==Object.prototype){const r=e.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 o=e,s=Object.keys(o).toSorted(y),i=[];for(const r of s){const a=o[r];a!==void 0&&i.push(`${JSON.stringify(r)}:${n(a)}`)}return`{${i.join(",")}}`},l=e=>n(c(e));export{l as t};
|
|
@@ -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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
|
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,a=.5,p=r=>[{lower:null,numItems:r,upper:null}],m=(r,s)=>{for(const[n,e]of r.entries()){if(e.upper===null)continue;const t=s[n];if(!t)continue;const i=t.page.length;if(i>2*e.numItems&&t.splitCursor){const u=t.splitCursor,o=[...r];return o.splice(n,1,{lower:e.lower,numItems:e.numItems,upper:u},{lower:u,numItems:e.numItems,upper:e.upper}),o}if(i<.5*e.numItems&&n+1<r.length){const u=r[n+1];if(!u)continue;const o=[...r];return o.splice(n,2,{lower:e.lower,numItems:e.numItems,upper:u.upper}),o}}},c=(r,s)=>{if(r||!s[0])return{nextCursor:void 0,status:"LoadingFirstPage"};const n=s.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=(r,s,n)=>{if(s==null)return;const e=[...r],t=e.at(-1);return t&&(e[e.length-1]={lower:t.lower,numItems:t.numItems,upper:s}),e.push({lower:s,numItems:n,upper:null}),e};export{a as JOIN_FACTOR,l as SPLIT_FACTOR,I as applyLoadMore,c as derivePaginationStatus,p as initialPages,m as rebalance};
|
package/dist/query/index.d.mts
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { F as FunctionReference, A as ArgsOf, R as ReturnOf, a as Unsubscribe } from "../packem_shared/types.d-CA7ZeJm9.mjs";
|
|
2
|
+
import { S as SubscriptionError, L as LunoraClient } from "../packem_shared/lunora-client.d-BRXKXo0T.mjs";
|
|
3
|
+
export type { a as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-BRXKXo0T.mjs";
|
|
3
4
|
import '@lunora/runtime';
|
|
4
5
|
/**
|
|
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
|
-
*/
|
|
6
|
+
* The sentinel a framework adapter resolves its reactive args to when it wants
|
|
7
|
+
* to short-circuit a query — no network call, no socket.
|
|
8
|
+
*/
|
|
8
9
|
declare const SKIP: "skip";
|
|
9
10
|
/** Args after a framework has resolved its reactivity primitive, or the skip sentinel. */
|
|
10
11
|
type ResolvedArgs<F extends FunctionReference> = ArgsOf<F> | typeof SKIP;
|
|
11
12
|
/**
|
|
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
|
-
*/
|
|
13
|
+
* The framework-neutral sinks a {@link createQuerySubscription} call drives. A
|
|
14
|
+
* framework adapter supplies these, wiring each into its own reactivity
|
|
15
|
+
* primitive (a TanStack cache write, a Vue `ref`, a Svelte store `set`, a Solid
|
|
16
|
+
* signal setter).
|
|
17
|
+
*
|
|
18
|
+
* `onData` fires when a fresh server value lands (the initial frame or a delta).
|
|
19
|
+
* `onError` fires when the subscription attach threw, or the server pushed a
|
|
20
|
+
* subscription-scoped error — it is optional, and when omitted an attach throw
|
|
21
|
+
* propagates to the caller (preserving the "no error channel" behaviour of
|
|
22
|
+
* adapters like Solid/Vue that never had one) rather than being swallowed.
|
|
23
|
+
* `onReset` fires when the resolved args are `"skip"`: clear any prior value.
|
|
24
|
+
*/
|
|
24
25
|
interface QuerySubscriptionSinks<T> {
|
|
25
26
|
onData: (value: T) => void;
|
|
26
27
|
onError?: (error: SubscriptionError) => void;
|
|
@@ -33,30 +34,30 @@ interface QuerySubscriptionOptions {
|
|
|
33
34
|
/** Normalise an unknown thrown value into the client's {@link SubscriptionError} shape. */
|
|
34
35
|
declare const toSubscriptionError: (error: unknown) => SubscriptionError;
|
|
35
36
|
/**
|
|
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
|
-
*/
|
|
37
|
+
* The subscribe → snapshot → error/reset → cleanup state machine shared by
|
|
38
|
+
* every Lunora framework adapter's live-query hook.
|
|
39
|
+
*
|
|
40
|
+
* Given a `client`, a function reference, the already-resolved `args` (a
|
|
41
|
+
* framework reads its own reactive source first, then hands the plain value in),
|
|
42
|
+
* and a set of {@link QuerySubscriptionSinks}, this opens one `client.subscribe`
|
|
43
|
+
* registration and returns the {@link Unsubscribe} to tear it down. Each
|
|
44
|
+
* framework owns *when* to call this (a React effect, a Vue `watch`, a Svelte
|
|
45
|
+
* store start callback, a Solid `createEffect(on(...))`) and *where* to stash
|
|
46
|
+
* the value — this owns the lifecycle in between, so the skip-handling, the
|
|
47
|
+
* value/error fan-out, the attach-throw normalisation, and the
|
|
48
|
+
* cancellation-guarded teardown live in exactly one place.
|
|
49
|
+
*
|
|
50
|
+
* Behaviour. When `args === "skip"` it calls `sinks.onReset?.()` and returns a
|
|
51
|
+
* no-op teardown; no `client.subscribe` is issued. Otherwise it opens
|
|
52
|
+
* `client.subscribe(fn, args, …, { shardKey, onError })`: server pushes route to
|
|
53
|
+
* `sinks.onData`, and the client's own `onError` channel (a server-rejected
|
|
54
|
+
* subscription) routes to `sinks.onError` when present. The attach is wrapped —
|
|
55
|
+
* if `client.subscribe` itself throws, the error is normalised to a
|
|
56
|
+
* {@link SubscriptionError} and delivered to `sinks.onError` when present; with
|
|
57
|
+
* no `onError` sink the throw is rethrown so adapters without an error channel
|
|
58
|
+
* behave exactly as before. The returned teardown is idempotent and
|
|
59
|
+
* cancellation-guarded: once it runs, no further `onData`/`onError` from an
|
|
60
|
+
* in-flight push reaches the sinks.
|
|
61
|
+
*/
|
|
61
62
|
declare const createQuerySubscription: <F extends FunctionReference, T = ReturnOf<F>>(client: LunoraClient, function_: F, args: ResolvedArgs<F>, sinks: QuerySubscriptionSinks<T>, options?: QuerySubscriptionOptions) => Unsubscribe;
|
|
62
63
|
export { type ArgsOf, type FunctionReference, type QuerySubscriptionOptions, type QuerySubscriptionSinks, type ResolvedArgs, type ReturnOf, SKIP, type SubscriptionError, type Unsubscribe, createQuerySubscription, toSubscriptionError };
|
package/dist/query/index.d.ts
CHANGED
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { F as FunctionReference, A as ArgsOf, R as ReturnOf, a as Unsubscribe } from "../packem_shared/types.d-CA7ZeJm9.js";
|
|
2
|
+
import { S as SubscriptionError, L as LunoraClient } from "../packem_shared/lunora-client.d-B23myJYW.js";
|
|
3
|
+
export type { a as SubscriptionErrorCallback } from "../packem_shared/lunora-client.d-B23myJYW.js";
|
|
3
4
|
import '@lunora/runtime';
|
|
4
5
|
/**
|
|
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
|
-
*/
|
|
6
|
+
* The sentinel a framework adapter resolves its reactive args to when it wants
|
|
7
|
+
* to short-circuit a query — no network call, no socket.
|
|
8
|
+
*/
|
|
8
9
|
declare const SKIP: "skip";
|
|
9
10
|
/** Args after a framework has resolved its reactivity primitive, or the skip sentinel. */
|
|
10
11
|
type ResolvedArgs<F extends FunctionReference> = ArgsOf<F> | typeof SKIP;
|
|
11
12
|
/**
|
|
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
|
-
*/
|
|
13
|
+
* The framework-neutral sinks a {@link createQuerySubscription} call drives. A
|
|
14
|
+
* framework adapter supplies these, wiring each into its own reactivity
|
|
15
|
+
* primitive (a TanStack cache write, a Vue `ref`, a Svelte store `set`, a Solid
|
|
16
|
+
* signal setter).
|
|
17
|
+
*
|
|
18
|
+
* `onData` fires when a fresh server value lands (the initial frame or a delta).
|
|
19
|
+
* `onError` fires when the subscription attach threw, or the server pushed a
|
|
20
|
+
* subscription-scoped error — it is optional, and when omitted an attach throw
|
|
21
|
+
* propagates to the caller (preserving the "no error channel" behaviour of
|
|
22
|
+
* adapters like Solid/Vue that never had one) rather than being swallowed.
|
|
23
|
+
* `onReset` fires when the resolved args are `"skip"`: clear any prior value.
|
|
24
|
+
*/
|
|
24
25
|
interface QuerySubscriptionSinks<T> {
|
|
25
26
|
onData: (value: T) => void;
|
|
26
27
|
onError?: (error: SubscriptionError) => void;
|
|
@@ -33,30 +34,30 @@ interface QuerySubscriptionOptions {
|
|
|
33
34
|
/** Normalise an unknown thrown value into the client's {@link SubscriptionError} shape. */
|
|
34
35
|
declare const toSubscriptionError: (error: unknown) => SubscriptionError;
|
|
35
36
|
/**
|
|
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
|
-
*/
|
|
37
|
+
* The subscribe → snapshot → error/reset → cleanup state machine shared by
|
|
38
|
+
* every Lunora framework adapter's live-query hook.
|
|
39
|
+
*
|
|
40
|
+
* Given a `client`, a function reference, the already-resolved `args` (a
|
|
41
|
+
* framework reads its own reactive source first, then hands the plain value in),
|
|
42
|
+
* and a set of {@link QuerySubscriptionSinks}, this opens one `client.subscribe`
|
|
43
|
+
* registration and returns the {@link Unsubscribe} to tear it down. Each
|
|
44
|
+
* framework owns *when* to call this (a React effect, a Vue `watch`, a Svelte
|
|
45
|
+
* store start callback, a Solid `createEffect(on(...))`) and *where* to stash
|
|
46
|
+
* the value — this owns the lifecycle in between, so the skip-handling, the
|
|
47
|
+
* value/error fan-out, the attach-throw normalisation, and the
|
|
48
|
+
* cancellation-guarded teardown live in exactly one place.
|
|
49
|
+
*
|
|
50
|
+
* Behaviour. When `args === "skip"` it calls `sinks.onReset?.()` and returns a
|
|
51
|
+
* no-op teardown; no `client.subscribe` is issued. Otherwise it opens
|
|
52
|
+
* `client.subscribe(fn, args, …, { shardKey, onError })`: server pushes route to
|
|
53
|
+
* `sinks.onData`, and the client's own `onError` channel (a server-rejected
|
|
54
|
+
* subscription) routes to `sinks.onError` when present. The attach is wrapped —
|
|
55
|
+
* if `client.subscribe` itself throws, the error is normalised to a
|
|
56
|
+
* {@link SubscriptionError} and delivered to `sinks.onError` when present; with
|
|
57
|
+
* no `onError` sink the throw is rethrown so adapters without an error channel
|
|
58
|
+
* behave exactly as before. The returned teardown is idempotent and
|
|
59
|
+
* cancellation-guarded: once it runs, no further `onData`/`onError` from an
|
|
60
|
+
* in-flight push reaches the sinks.
|
|
61
|
+
*/
|
|
61
62
|
declare const createQuerySubscription: <F extends FunctionReference, T = ReturnOf<F>>(client: LunoraClient, function_: F, args: ResolvedArgs<F>, sinks: QuerySubscriptionSinks<T>, options?: QuerySubscriptionOptions) => Unsubscribe;
|
|
62
63
|
export { type ArgsOf, type FunctionReference, type QuerySubscriptionOptions, type QuerySubscriptionSinks, type ResolvedArgs, type ReturnOf, SKIP, type SubscriptionError, type Unsubscribe, createQuerySubscription, toSubscriptionError };
|
package/dist/query/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import{SKIP as t,createQuerySubscription as e,toSubscriptionError as i}from"../packem_shared/SKIP-CArzT3Vl.mjs";export{t as SKIP,e as createQuerySubscription,i as toSubscriptionError};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { F as FunctionReference, A as ArgsOf, R as ReturnOf } from "./packem_shared/types.d-CA7ZeJm9.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* The wire endpoint every Lunora Worker serves. Matches `createWorker`'s RPC
|
|
4
|
+
* route; the envelope shape below is that route's documented contract.
|
|
5
|
+
*/
|
|
6
|
+
declare const RPC_PATH = "/_lunora/rpc";
|
|
7
|
+
/**
|
|
8
|
+
* The slice of a Cloudflare service binding this module uses.
|
|
9
|
+
*
|
|
10
|
+
* Structural on purpose: `env.BACKEND` satisfies it, and so does a one-method
|
|
11
|
+
* fake in a test. Declaring the full `Fetcher` would drag
|
|
12
|
+
* `@cloudflare/workers-types` into every consumer for a single method.
|
|
13
|
+
*/
|
|
14
|
+
interface ServiceBindingLike {
|
|
15
|
+
fetch: (request: Request) => Promise<Response>;
|
|
16
|
+
}
|
|
17
|
+
interface ServiceCallOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Route to a specific shard. Omitted, the callee routes to its default shard
|
|
20
|
+
* — the same rule the HTTP path follows, so a sharded app must pass the key
|
|
21
|
+
* it would have passed there.
|
|
22
|
+
*/
|
|
23
|
+
shardKey?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A typed caller bound to one Lunora Worker.
|
|
27
|
+
*
|
|
28
|
+
* The three methods are separate rather than one `call` so a reader can tell a
|
|
29
|
+
* read from a write at the call site, and so the reference's `Kind` is checked:
|
|
30
|
+
* passing a mutation reference to `query` is a compile error, matching how
|
|
31
|
+
* `ctx.runQuery`/`ctx.runMutation` behave inside an app.
|
|
32
|
+
*/
|
|
33
|
+
interface LunoraServiceClient {
|
|
34
|
+
action: <F extends FunctionReference<"action">>(reference: F, args?: ArgsOf<F>, options?: ServiceCallOptions) => Promise<ReturnOf<F>>;
|
|
35
|
+
mutation: <F extends FunctionReference<"mutation">>(reference: F, args?: ArgsOf<F>, options?: ServiceCallOptions) => Promise<ReturnOf<F>>;
|
|
36
|
+
query: <F extends FunctionReference<"query">>(reference: F, args?: ArgsOf<F>, options?: ServiceCallOptions) => Promise<ReturnOf<F>>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Build a typed caller for the Lunora Worker behind `binding`.
|
|
40
|
+
*
|
|
41
|
+
* Only PUBLIC functions are reachable. An `internal*` procedure is deliberately
|
|
42
|
+
* absent from the generated `api` object and rejected by the callee, because a
|
|
43
|
+
* service binding is a trust boundary between deployments, not the intra-app
|
|
44
|
+
* `ctx.run*` seam.
|
|
45
|
+
* @param binding The service binding, e.g. `env.BACKEND`.
|
|
46
|
+
* @returns a client whose methods type-check against the generated `api`.
|
|
47
|
+
*/
|
|
48
|
+
declare const createServiceClient: (binding: ServiceBindingLike) => LunoraServiceClient;
|
|
49
|
+
export { type LunoraServiceClient, RPC_PATH, type ServiceBindingLike, type ServiceCallOptions, createServiceClient };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { F as FunctionReference, A as ArgsOf, R as ReturnOf } from "./packem_shared/types.d-CA7ZeJm9.js";
|
|
2
|
+
/**
|
|
3
|
+
* The wire endpoint every Lunora Worker serves. Matches `createWorker`'s RPC
|
|
4
|
+
* route; the envelope shape below is that route's documented contract.
|
|
5
|
+
*/
|
|
6
|
+
declare const RPC_PATH = "/_lunora/rpc";
|
|
7
|
+
/**
|
|
8
|
+
* The slice of a Cloudflare service binding this module uses.
|
|
9
|
+
*
|
|
10
|
+
* Structural on purpose: `env.BACKEND` satisfies it, and so does a one-method
|
|
11
|
+
* fake in a test. Declaring the full `Fetcher` would drag
|
|
12
|
+
* `@cloudflare/workers-types` into every consumer for a single method.
|
|
13
|
+
*/
|
|
14
|
+
interface ServiceBindingLike {
|
|
15
|
+
fetch: (request: Request) => Promise<Response>;
|
|
16
|
+
}
|
|
17
|
+
interface ServiceCallOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Route to a specific shard. Omitted, the callee routes to its default shard
|
|
20
|
+
* — the same rule the HTTP path follows, so a sharded app must pass the key
|
|
21
|
+
* it would have passed there.
|
|
22
|
+
*/
|
|
23
|
+
shardKey?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A typed caller bound to one Lunora Worker.
|
|
27
|
+
*
|
|
28
|
+
* The three methods are separate rather than one `call` so a reader can tell a
|
|
29
|
+
* read from a write at the call site, and so the reference's `Kind` is checked:
|
|
30
|
+
* passing a mutation reference to `query` is a compile error, matching how
|
|
31
|
+
* `ctx.runQuery`/`ctx.runMutation` behave inside an app.
|
|
32
|
+
*/
|
|
33
|
+
interface LunoraServiceClient {
|
|
34
|
+
action: <F extends FunctionReference<"action">>(reference: F, args?: ArgsOf<F>, options?: ServiceCallOptions) => Promise<ReturnOf<F>>;
|
|
35
|
+
mutation: <F extends FunctionReference<"mutation">>(reference: F, args?: ArgsOf<F>, options?: ServiceCallOptions) => Promise<ReturnOf<F>>;
|
|
36
|
+
query: <F extends FunctionReference<"query">>(reference: F, args?: ArgsOf<F>, options?: ServiceCallOptions) => Promise<ReturnOf<F>>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Build a typed caller for the Lunora Worker behind `binding`.
|
|
40
|
+
*
|
|
41
|
+
* Only PUBLIC functions are reachable. An `internal*` procedure is deliberately
|
|
42
|
+
* absent from the generated `api` object and rejected by the callee, because a
|
|
43
|
+
* service binding is a trust boundary between deployments, not the intra-app
|
|
44
|
+
* `ctx.run*` seam.
|
|
45
|
+
* @param binding The service binding, e.g. `env.BACKEND`.
|
|
46
|
+
* @returns a client whose methods type-check against the generated `api`.
|
|
47
|
+
*/
|
|
48
|
+
declare const createServiceClient: (binding: ServiceBindingLike) => LunoraServiceClient;
|
|
49
|
+
export { type LunoraServiceClient, RPC_PATH, type ServiceBindingLike, type ServiceCallOptions, createServiceClient };
|
package/dist/service.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as u}from"@lunora/errors";import{a as f,s as w}from"./packem_shared/wire-codec-D_fiax9d.mjs";const p="/_lunora/rpc",$="https://lunora.internal",g=e=>{const t=new Error(e.message??"request failed");return t.code=e.code,e.data!==void 0&&(t.data=w(e.data)),e.hint!==void 0&&(t.hint=e.hint),e.docsUrl!==void 0&&(t.docsUrl=e.docsUrl),t},h=async(e,t,a,o,d)=>{const s=a.__lunoraRef;let l;try{l=JSON.stringify({args:f(o??{}),functionPath:s,...d?.shardKey===void 0?{}:{shardKey:d.shardKey}})}catch(n){const y=n instanceof Error?n.message:String(n);throw new TypeError(`@lunora/client/service: cannot encode args for '${s}' — ${y}`,n instanceof Error?{cause:n}:void 0)}const r=await e.fetch(new Request(`${$}${p}`,{body:l,headers:{"content-type":"application/json"},method:"POST"}));let i;try{i=await r.json()}catch{throw new u("INTERNAL",`@lunora/client/service: ${t} "${s}" got a non-JSON response (status ${r.status.toString()}). Check the service binding points at the Lunora Worker that declares this function.`,{status:r.status})}if(i===null||typeof i!="object")throw new u("INTERNAL",`@lunora/client/service: ${t} "${s}" returned a JSON body that is not an object (status ${r.status.toString()}). Check the service binding points at the Lunora Worker that declares this function.`,{status:r.status});const c=i;if("error"in c)throw g(c.error);if(!r.ok)throw new u("INTERNAL",`@lunora/client/service: ${t} "${s}" failed with status ${r.status.toString()} and no error envelope.`,{status:r.status});return w(c.result)},m=e=>({action:async(t,a,o)=>await h(e,"action",t,a,o),mutation:async(t,a,o)=>await h(e,"mutation",t,a,o),query:async(t,a,o)=>await h(e,"query",t,a,o)});export{p as RPC_PATH,m as createServiceClient};
|