@confect/react 10.0.0-next.21 → 10.0.0-next.23

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.
@@ -53,12 +53,11 @@ const isFailure = (queryResult) => queryResult._tag === "Failure";
53
53
  * not required.
54
54
  *
55
55
  * @example
56
- * ```tsx
57
- * const result = QueryResult.match(queryResult, {
58
- * onLoading: (skipped) => skipped ? null : <p>Loading…</p>,
59
- * onSuccess: (value) => <p>{value.text}</p>,
60
- * onFailure: (error) => <p>Error: {error.message}</p>,
61
- * });
56
+ * const result = QueryResult.match(queryResult, {
57
+ * onLoading: (skipped) => (skipped ? null : <p>Loading…</p>),
58
+ * onSuccess: (value) => <p>{value.text}</p>,
59
+ * onFailure: (error) => <p>Error: {error.message}</p>,
60
+ * });
62
61
  */
63
62
  const match = Function.dual(2, (self, options) => Match.value(self).pipe(Match.tag("Loading", (loading) => options.onLoading(loading.skipped)), Match.tag("Success", (success) => options.onSuccess(success.value)), Match.tag("Failure", (failure) => {
64
63
  if (Predicate.hasProperty(options, "onFailure")) return options.onFailure(failure.error);
@@ -1 +1 @@
1
- {"version":3,"file":"QueryResult.js","names":[],"sources":["../src/QueryResult.ts"],"sourcesContent":["import * as Equal from \"effect/Equal\";\nimport * as Function from \"effect/Function\";\nimport * as Hash from \"effect/Hash\";\nimport * as Match from \"effect/Match\";\nimport * as Pipeable from \"effect/Pipeable\";\nimport * as Predicate from \"effect/Predicate\";\n\nconst TypeId = \"~@confect/react/QueryResult\";\ntype TypeId = typeof TypeId;\n\n/**\n * A `QueryResult` represents the result of a Confect query.\n *\n * @typeParam A - The type of the decoded `returns` value in the `Success` variant.\n * @typeParam E - The type of the decoded typed error in the `Failure` variant.\n */\nexport type QueryResult<A, E = never> =\n | Loading<A, E>\n | Success<A, E>\n | Failure<A, E>;\n\nexport declare namespace QueryResult {\n export interface Proto<out A, out E> extends Pipeable.Pipeable {\n readonly [TypeId]: TypeId;\n readonly \"~A\": A;\n readonly \"~E\": E;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-shadow\n export type Success<R> = R extends { readonly \"~A\": infer A } ? A : never;\n\n // eslint-disable-next-line @typescript-eslint/no-shadow\n export type Failure<R> = R extends { readonly \"~E\": infer E } ? E : never;\n}\n\nexport interface Loading<A, E = never> extends QueryResult.Proto<A, E> {\n readonly _tag: \"Loading\";\n readonly skipped: boolean;\n}\n\nexport interface Success<A, E = never> extends QueryResult.Proto<A, E> {\n readonly _tag: \"Success\";\n readonly value: A;\n}\n\nexport interface Failure<A, E = never> extends QueryResult.Proto<A, E> {\n readonly _tag: \"Failure\";\n readonly error: E;\n}\n\nexport const isQueryResult = (u: unknown): u is QueryResult<unknown, unknown> =>\n Predicate.hasProperty(u, TypeId);\n\nconst QueryResultProto = {\n [TypeId]: TypeId,\n pipe(this: QueryResult<any, any>, ...args: ReadonlyArray<unknown>) {\n return Pipeable.pipeArguments(\n this,\n args as unknown as Parameters<typeof Pipeable.pipeArguments>[1],\n );\n },\n [Equal.symbol](\n this: QueryResult<any, any>,\n that: QueryResult<any, any>,\n ): boolean {\n if (this._tag !== that._tag) {\n return false;\n }\n return Match.value(this).pipe(\n Match.tag(\n \"Loading\",\n (self) => self.skipped === (that as Loading<any, any>).skipped,\n ),\n Match.tag(\"Success\", (self) =>\n Equal.equals(self.value, (that as Success<any, any>).value),\n ),\n Match.tag(\"Failure\", (self) =>\n Equal.equals(self.error, (that as Failure<any, any>).error),\n ),\n Match.exhaustive,\n );\n },\n [Hash.symbol](this: QueryResult<any, any>): number {\n const tagHash = Hash.string(this._tag);\n return Match.value(this).pipe(\n Match.tag(\"Loading\", (self) =>\n Hash.combine(tagHash)(Hash.hash(self.skipped)),\n ),\n Match.tag(\"Success\", (self) =>\n Hash.combine(tagHash)(Hash.hash(self.value)),\n ),\n Match.tag(\"Failure\", (self) =>\n Hash.combine(tagHash)(Hash.hash(self.error)),\n ),\n Match.exhaustive,\n );\n },\n};\n\nexport const load = <A = never, E = never>(skipped: boolean): Loading<A, E> =>\n Object.assign(Object.create(QueryResultProto), {\n _tag: \"Loading\" as const,\n skipped,\n });\n\nexport const succeed = <A, E = never>(value: A): Success<A, E> =>\n Object.assign(Object.create(QueryResultProto), {\n _tag: \"Success\" as const,\n value,\n });\n\nexport const fail = <E, A = never>(error: E): Failure<A, E> =>\n Object.assign(Object.create(QueryResultProto), {\n _tag: \"Failure\" as const,\n error,\n });\n\nexport const isLoading = <A, E>(\n queryResult: QueryResult<A, E>,\n): queryResult is Loading<A, E> => queryResult._tag === \"Loading\";\n\nexport const isSuccess = <A, E>(\n queryResult: QueryResult<A, E>,\n): queryResult is Success<A, E> => queryResult._tag === \"Success\";\n\nexport const isFailure = <A, E>(\n queryResult: QueryResult<A, E>,\n): queryResult is Failure<A, E> => queryResult._tag === \"Failure\";\n\ntype MatchOptions<A, E, X, Y, Z> = {\n readonly onLoading: (skipped: boolean) => X;\n readonly onSuccess: (value: A) => Y;\n} & ([E] extends [never] ? {} : { readonly onFailure: (error: E) => Z });\n\ntype MatchReturns<E, X, Y, Z> = [E] extends [never] ? X | Y : X | Y | Z;\n\n/**\n * Matches a {@link QueryResult} to the appropriate handler based on its tag. If\n * the provided `QueryResult` cannot fail (i.e. `E` is `never`), `onFailure` is\n * not required.\n *\n * @example\n * ```tsx\n * const result = QueryResult.match(queryResult, {\n * onLoading: (skipped) => skipped ? null : <p>Loading…</p>,\n * onSuccess: (value) => <p>{value.text}</p>,\n * onFailure: (error) => <p>Error: {error.message}</p>,\n * });\n */\nexport const match: {\n <A, E, X, Y, Z = never>(\n options: MatchOptions<A, E, X, Y, Z>,\n ): (self: QueryResult<A, E>) => MatchReturns<E, X, Y, Z>;\n <A, E, X, Y, Z = never>(\n self: QueryResult<A, E>,\n options: MatchOptions<A, E, X, Y, Z>,\n ): MatchReturns<E, X, Y, Z>;\n} = Function.dual(\n 2,\n <A, E, X, Y, Z = never>(\n self: QueryResult<A, E>,\n options: MatchOptions<A, E, X, Y, Z>,\n ): MatchReturns<E, X, Y, Z> =>\n Match.value(self).pipe(\n Match.tag(\"Loading\", (loading) => options.onLoading(loading.skipped)),\n Match.tag(\"Success\", (success) => options.onSuccess(success.value)),\n Match.tag(\"Failure\", (failure) => {\n if (Predicate.hasProperty(options, \"onFailure\")) {\n return options.onFailure(failure.error);\n }\n throw new Error(\n \"`onFailure` is required when error schema is provided\",\n );\n }),\n Match.exhaustive,\n ) as MatchReturns<E, X, Y, Z>,\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;AAOA,MAAM,SAAS;AA2Cf,MAAa,iBAAiB,MAC5B,UAAU,YAAY,GAAG,MAAM;AAEjC,MAAM,mBAAmB;EACtB,SAAS;CACV,KAAkC,GAAG,MAA8B;EACjE,OAAO,SAAS,cACd,MACA,IACF;CACF;CACA,CAAC,MAAM,QAEL,MACS;EACT,IAAI,KAAK,SAAS,KAAK,MACrB,OAAO;EAET,OAAO,MAAM,MAAM,IAAI,CAAC,CAAC,KACvB,MAAM,IACJ,YACC,SAAS,KAAK,YAAa,KAA2B,OACzD,GACA,MAAM,IAAI,YAAY,SACpB,MAAM,OAAO,KAAK,OAAQ,KAA2B,KAAK,CAC5D,GACA,MAAM,IAAI,YAAY,SACpB,MAAM,OAAO,KAAK,OAAQ,KAA2B,KAAK,CAC5D,GACA,MAAM,UACR;CACF;CACA,CAAC,KAAK,UAA6C;EACjD,MAAM,UAAU,KAAK,OAAO,KAAK,IAAI;EACrC,OAAO,MAAM,MAAM,IAAI,CAAC,CAAC,KACvB,MAAM,IAAI,YAAY,SACpB,KAAK,QAAQ,OAAO,CAAC,CAAC,KAAK,KAAK,KAAK,OAAO,CAAC,CAC/C,GACA,MAAM,IAAI,YAAY,SACpB,KAAK,QAAQ,OAAO,CAAC,CAAC,KAAK,KAAK,KAAK,KAAK,CAAC,CAC7C,GACA,MAAM,IAAI,YAAY,SACpB,KAAK,QAAQ,OAAO,CAAC,CAAC,KAAK,KAAK,KAAK,KAAK,CAAC,CAC7C,GACA,MAAM,UACR;CACF;AACF;AAEA,MAAa,QAA8B,YACzC,OAAO,OAAO,OAAO,OAAO,gBAAgB,GAAG;CAC7C,MAAM;CACN;AACF,CAAC;AAEH,MAAa,WAAyB,UACpC,OAAO,OAAO,OAAO,OAAO,gBAAgB,GAAG;CAC7C,MAAM;CACN;AACF,CAAC;AAEH,MAAa,QAAsB,UACjC,OAAO,OAAO,OAAO,OAAO,gBAAgB,GAAG;CAC7C,MAAM;CACN;AACF,CAAC;AAEH,MAAa,aACX,gBACiC,YAAY,SAAS;AAExD,MAAa,aACX,gBACiC,YAAY,SAAS;AAExD,MAAa,aACX,gBACiC,YAAY,SAAS;;;;;;;;;;;;;;AAsBxD,MAAa,QAQT,SAAS,KACX,IAEE,MACA,YAEA,MAAM,MAAM,IAAI,CAAC,CAAC,KAChB,MAAM,IAAI,YAAY,YAAY,QAAQ,UAAU,QAAQ,OAAO,CAAC,GACpE,MAAM,IAAI,YAAY,YAAY,QAAQ,UAAU,QAAQ,KAAK,CAAC,GAClE,MAAM,IAAI,YAAY,YAAY;CAChC,IAAI,UAAU,YAAY,SAAS,WAAW,GAC5C,OAAO,QAAQ,UAAU,QAAQ,KAAK;CAExC,MAAM,IAAI,MACR,uDACF;AACF,CAAC,GACD,MAAM,UACR,CACJ"}
1
+ {"version":3,"file":"QueryResult.js","names":[],"sources":["../src/QueryResult.ts"],"sourcesContent":["import * as Equal from \"effect/Equal\";\nimport * as Function from \"effect/Function\";\nimport * as Hash from \"effect/Hash\";\nimport * as Match from \"effect/Match\";\nimport * as Pipeable from \"effect/Pipeable\";\nimport * as Predicate from \"effect/Predicate\";\n\nconst TypeId = \"~@confect/react/QueryResult\";\ntype TypeId = typeof TypeId;\n\n/**\n * A `QueryResult` represents the result of a Confect query.\n *\n * @typeParam A - The type of the decoded `returns` value in the `Success`\n * variant.\n * @typeParam E - The type of the decoded typed error in the `Failure` variant.\n */\nexport type QueryResult<A, E = never> =\n | Loading<A, E>\n | Success<A, E>\n | Failure<A, E>;\n\nexport declare namespace QueryResult {\n export interface Proto<out A, out E> extends Pipeable.Pipeable {\n readonly [TypeId]: TypeId;\n readonly \"~A\": A;\n readonly \"~E\": E;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-shadow\n export type Success<R> = R extends { readonly \"~A\": infer A } ? A : never;\n\n // eslint-disable-next-line @typescript-eslint/no-shadow\n export type Failure<R> = R extends { readonly \"~E\": infer E } ? E : never;\n}\n\nexport interface Loading<A, E = never> extends QueryResult.Proto<A, E> {\n readonly _tag: \"Loading\";\n readonly skipped: boolean;\n}\n\nexport interface Success<A, E = never> extends QueryResult.Proto<A, E> {\n readonly _tag: \"Success\";\n readonly value: A;\n}\n\nexport interface Failure<A, E = never> extends QueryResult.Proto<A, E> {\n readonly _tag: \"Failure\";\n readonly error: E;\n}\n\nexport const isQueryResult = (u: unknown): u is QueryResult<unknown, unknown> =>\n Predicate.hasProperty(u, TypeId);\n\nconst QueryResultProto = {\n [TypeId]: TypeId,\n pipe(this: QueryResult<any, any>, ...args: ReadonlyArray<unknown>) {\n return Pipeable.pipeArguments(\n this,\n args as unknown as Parameters<typeof Pipeable.pipeArguments>[1],\n );\n },\n [Equal.symbol](\n this: QueryResult<any, any>,\n that: QueryResult<any, any>,\n ): boolean {\n if (this._tag !== that._tag) {\n return false;\n }\n return Match.value(this).pipe(\n Match.tag(\n \"Loading\",\n (self) => self.skipped === (that as Loading<any, any>).skipped,\n ),\n Match.tag(\"Success\", (self) =>\n Equal.equals(self.value, (that as Success<any, any>).value),\n ),\n Match.tag(\"Failure\", (self) =>\n Equal.equals(self.error, (that as Failure<any, any>).error),\n ),\n Match.exhaustive,\n );\n },\n [Hash.symbol](this: QueryResult<any, any>): number {\n const tagHash = Hash.string(this._tag);\n return Match.value(this).pipe(\n Match.tag(\"Loading\", (self) =>\n Hash.combine(tagHash)(Hash.hash(self.skipped)),\n ),\n Match.tag(\"Success\", (self) =>\n Hash.combine(tagHash)(Hash.hash(self.value)),\n ),\n Match.tag(\"Failure\", (self) =>\n Hash.combine(tagHash)(Hash.hash(self.error)),\n ),\n Match.exhaustive,\n );\n },\n};\n\nexport const load = <A = never, E = never>(skipped: boolean): Loading<A, E> =>\n Object.assign(Object.create(QueryResultProto), {\n _tag: \"Loading\" as const,\n skipped,\n });\n\nexport const succeed = <A, E = never>(value: A): Success<A, E> =>\n Object.assign(Object.create(QueryResultProto), {\n _tag: \"Success\" as const,\n value,\n });\n\nexport const fail = <E, A = never>(error: E): Failure<A, E> =>\n Object.assign(Object.create(QueryResultProto), {\n _tag: \"Failure\" as const,\n error,\n });\n\nexport const isLoading = <A, E>(\n queryResult: QueryResult<A, E>,\n): queryResult is Loading<A, E> => queryResult._tag === \"Loading\";\n\nexport const isSuccess = <A, E>(\n queryResult: QueryResult<A, E>,\n): queryResult is Success<A, E> => queryResult._tag === \"Success\";\n\nexport const isFailure = <A, E>(\n queryResult: QueryResult<A, E>,\n): queryResult is Failure<A, E> => queryResult._tag === \"Failure\";\n\ntype MatchOptions<A, E, X, Y, Z> = {\n readonly onLoading: (skipped: boolean) => X;\n readonly onSuccess: (value: A) => Y;\n} & ([E] extends [never] ? {} : { readonly onFailure: (error: E) => Z });\n\ntype MatchReturns<E, X, Y, Z> = [E] extends [never] ? X | Y : X | Y | Z;\n\n/**\n * Matches a {@link QueryResult} to the appropriate handler based on its tag. If\n * the provided `QueryResult` cannot fail (i.e. `E` is `never`), `onFailure` is\n * not required.\n *\n * @example\n * const result = QueryResult.match(queryResult, {\n * onLoading: (skipped) => (skipped ? null : <p>Loading…</p>),\n * onSuccess: (value) => <p>{value.text}</p>,\n * onFailure: (error) => <p>Error: {error.message}</p>,\n * });\n */\nexport const match: {\n <A, E, X, Y, Z = never>(\n options: MatchOptions<A, E, X, Y, Z>,\n ): (self: QueryResult<A, E>) => MatchReturns<E, X, Y, Z>;\n <A, E, X, Y, Z = never>(\n self: QueryResult<A, E>,\n options: MatchOptions<A, E, X, Y, Z>,\n ): MatchReturns<E, X, Y, Z>;\n} = Function.dual(\n 2,\n <A, E, X, Y, Z = never>(\n self: QueryResult<A, E>,\n options: MatchOptions<A, E, X, Y, Z>,\n ): MatchReturns<E, X, Y, Z> =>\n Match.value(self).pipe(\n Match.tag(\"Loading\", (loading) => options.onLoading(loading.skipped)),\n Match.tag(\"Success\", (success) => options.onSuccess(success.value)),\n Match.tag(\"Failure\", (failure) => {\n if (Predicate.hasProperty(options, \"onFailure\")) {\n return options.onFailure(failure.error);\n }\n throw new Error(\n \"`onFailure` is required when error schema is provided\",\n );\n }),\n Match.exhaustive,\n ) as MatchReturns<E, X, Y, Z>,\n);\n"],"mappings":";;;;;;;;;;;;;;;;;;AAOA,MAAM,SAAS;AA4Cf,MAAa,iBAAiB,MAC5B,UAAU,YAAY,GAAG,MAAM;AAEjC,MAAM,mBAAmB;EACtB,SAAS;CACV,KAAkC,GAAG,MAA8B;EACjE,OAAO,SAAS,cACd,MACA,IACF;CACF;CACA,CAAC,MAAM,QAEL,MACS;EACT,IAAI,KAAK,SAAS,KAAK,MACrB,OAAO;EAET,OAAO,MAAM,MAAM,IAAI,CAAC,CAAC,KACvB,MAAM,IACJ,YACC,SAAS,KAAK,YAAa,KAA2B,OACzD,GACA,MAAM,IAAI,YAAY,SACpB,MAAM,OAAO,KAAK,OAAQ,KAA2B,KAAK,CAC5D,GACA,MAAM,IAAI,YAAY,SACpB,MAAM,OAAO,KAAK,OAAQ,KAA2B,KAAK,CAC5D,GACA,MAAM,UACR;CACF;CACA,CAAC,KAAK,UAA6C;EACjD,MAAM,UAAU,KAAK,OAAO,KAAK,IAAI;EACrC,OAAO,MAAM,MAAM,IAAI,CAAC,CAAC,KACvB,MAAM,IAAI,YAAY,SACpB,KAAK,QAAQ,OAAO,CAAC,CAAC,KAAK,KAAK,KAAK,OAAO,CAAC,CAC/C,GACA,MAAM,IAAI,YAAY,SACpB,KAAK,QAAQ,OAAO,CAAC,CAAC,KAAK,KAAK,KAAK,KAAK,CAAC,CAC7C,GACA,MAAM,IAAI,YAAY,SACpB,KAAK,QAAQ,OAAO,CAAC,CAAC,KAAK,KAAK,KAAK,KAAK,CAAC,CAC7C,GACA,MAAM,UACR;CACF;AACF;AAEA,MAAa,QAA8B,YACzC,OAAO,OAAO,OAAO,OAAO,gBAAgB,GAAG;CAC7C,MAAM;CACN;AACF,CAAC;AAEH,MAAa,WAAyB,UACpC,OAAO,OAAO,OAAO,OAAO,gBAAgB,GAAG;CAC7C,MAAM;CACN;AACF,CAAC;AAEH,MAAa,QAAsB,UACjC,OAAO,OAAO,OAAO,OAAO,gBAAgB,GAAG;CAC7C,MAAM;CACN;AACF,CAAC;AAEH,MAAa,aACX,gBACiC,YAAY,SAAS;AAExD,MAAa,aACX,gBACiC,YAAY,SAAS;AAExD,MAAa,aACX,gBACiC,YAAY,SAAS;;;;;;;;;;;;;AAqBxD,MAAa,QAQT,SAAS,KACX,IAEE,MACA,YAEA,MAAM,MAAM,IAAI,CAAC,CAAC,KAChB,MAAM,IAAI,YAAY,YAAY,QAAQ,UAAU,QAAQ,OAAO,CAAC,GACpE,MAAM,IAAI,YAAY,YAAY,QAAQ,UAAU,QAAQ,KAAK,CAAC,GAClE,MAAM,IAAI,YAAY,YAAY;CAChC,IAAI,UAAU,YAAY,SAAS,WAAW,GAC5C,OAAO,QAAQ,UAAU,QAAQ,KAAK;CAExC,MAAM,IAAI,MACR,uDACF;AACF,CAAC,GACD,MAAM,UACR,CACJ"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * EXPERIMENTAL—the pure state machine behind {@link useStreamPaginatedQuery}
3
+ * (see `notes/stream-based-querying.md` in the repo root).
4
+ *
5
+ * Reactive pagination without the query journal: every loaded page—including
6
+ * the first—is pinned to a fixed index range by echoing its `continueCursor`
7
+ * back as the next subscription's `endCursor` (the last page pins to the
8
+ * end-of-stream sentinel once exhausted), so adjacent pages stay exactly
9
+ * contiguous and pages never shed items as data changes—the "range-defined
10
+ * pages" the Fully Reactive Pagination article calls for, and the mechanism of
11
+ * `convex-helpers/react`'s `usePaginatedQuery`.
12
+ *
13
+ * Pinning a freshly loaded page, loading more, and splitting an overgrown page
14
+ * are the same transition shape: the affected page keeps rendering while its
15
+ * replacement subscriptions ("ongoing split") load, and is swapped out once all
16
+ * of them have results.
17
+ *
18
+ * This module is framework-free (it imports only `effect/*` and a type from
19
+ * `convex/server`); it lives here for now because the React hook is its only
20
+ * consumer—a second client (e.g. `@confect/foldkit`) should prompt a move down
21
+ * to `@confect/js`.
22
+ */
23
+ import type { PaginationResult } from "convex/server";
24
+ import * as Option from "effect/Option";
25
+ import * as Record from "effect/Record";
26
+ /**
27
+ * The `paginationOpts` one subscribed page queries with.
28
+ */
29
+ export interface PageRequest {
30
+ readonly numItems: number;
31
+ readonly cursor: string | null;
32
+ /**
33
+ * Present once the page is pinned to a fixed range.
34
+ */
35
+ readonly endCursor?: string;
36
+ /**
37
+ * Per-page read budgets, forwarded to the server so a scan-heavy page fails
38
+ * over to `SplitRequired` instead of exceeding query limits.
39
+ */
40
+ readonly maximumRowsRead?: number;
41
+ readonly maximumBytesRead?: number;
42
+ }
43
+ /**
44
+ * The per-page read budgets every growing page is requested with.
45
+ */
46
+ export interface ReadBudget {
47
+ readonly maximumRowsRead?: number | undefined;
48
+ readonly maximumBytesRead?: number | undefined;
49
+ }
50
+ export interface State {
51
+ readonly nextPageKey: number;
52
+ /**
53
+ * Page keys in display order.
54
+ */
55
+ readonly pageKeys: ReadonlyArray<string>;
56
+ /**
57
+ * The request each subscribed page queries with, by page key.
58
+ */
59
+ readonly pages: Record.ReadonlyRecord<string, PageRequest>;
60
+ /**
61
+ * Pages being replaced by one (a pin) or two (a split) narrower
62
+ * subscriptions: the original stays subscribed (and rendered) until every
63
+ * replacement has a result.
64
+ */
65
+ readonly ongoingSplits: Record.ReadonlyRecord<string, ReadonlyArray<string>>;
66
+ }
67
+ /**
68
+ * The skipped state: nothing subscribed.
69
+ */
70
+ export declare const empty: State;
71
+ /**
72
+ * One growing (unpinned) page from the start of the stream.
73
+ */
74
+ export declare const initial: (initialNumItems: number, budget?: ReadBudget) => State;
75
+ /**
76
+ * Pin a freshly loaded growing page at its `continueCursor` (or, once
77
+ * exhausted, at the end-of-stream sentinel), so it stops being a sliding
78
+ * window: a pinned page grows and shrinks with the data in its range but never
79
+ * sheds items past its edges. Modeled as a one-replacement split so the
80
+ * original keeps rendering until the pinned twin has a result.
81
+ */
82
+ export declare const pin: (key: string, endCursor: string) => ((state: State) => State);
83
+ /**
84
+ * Pin the growing last page at `continueCursor` and start a new growing page
85
+ * from there. Modeled as a split of the last page into its pinned replacement
86
+ * and the new page, so the swap waits for both.
87
+ */
88
+ export declare const loadMore: (continueCursor: string, numItems: number, budget?: ReadBudget) => ((state: State) => State);
89
+ /**
90
+ * Split the page at `key` in two at `splitCursor`. The first replacement pins
91
+ * at the split point; the second keeps the page's own `endCursor`—for a pinned
92
+ * page its original end (so a truncated `SplitRequired` page loses none of its
93
+ * range), and for a growing page no end at all (it keeps growing).
94
+ */
95
+ export declare const split: (key: string, splitCursor: string) => ((state: State) => State);
96
+ /**
97
+ * Swap a completed split's replacements in for the original page.
98
+ */
99
+ export declare const completeSplit: (key: string) => ((state: State) => State);
100
+ /**
101
+ * Whether the last (rendered) page is currently being split or pinned.
102
+ */
103
+ export declare const isLastPageSplitting: (state: State) => boolean;
104
+ /**
105
+ * Build one subscription request per subscribed page.
106
+ */
107
+ export declare const pageRequests: <A>(state: State, f: (page: PageRequest) => A) => Record.ReadonlyRecord<string, A>;
108
+ /**
109
+ * The per-page results a render observes, keyed like {@link pageRequests}.
110
+ */
111
+ export type Results = Record.ReadonlyRecord<string, PageResult | Error | undefined>;
112
+ /**
113
+ * The wire shape of one loaded page—`PaginationResult` from `convex/server`, so
114
+ * the protocol has a single source of truth.
115
+ */
116
+ export type PageResult = PaginationResult<unknown>;
117
+ /**
118
+ * What one render pass derives from the subscribed pages' results: the items to
119
+ * show, the trailing complete result (absent while the tail is loading or
120
+ * force-splitting), state transitions to apply, and whether a failure or an
121
+ * invalid cursor was hit.
122
+ */
123
+ export type Interpretation = {
124
+ readonly _tag: "ResetRequired";
125
+ } | {
126
+ readonly _tag: "Failed";
127
+ readonly error: unknown;
128
+ /**
129
+ * Encoded items loaded before the failing page.
130
+ */
131
+ readonly items: ReadonlyArray<unknown>;
132
+ } | {
133
+ readonly _tag: "Interpreted";
134
+ /**
135
+ * Encoded items across the loaded pages, in order.
136
+ */
137
+ readonly items: ReadonlyArray<unknown>;
138
+ /**
139
+ * The last page's result, when every page has loaded completely.
140
+ */
141
+ readonly lastResult: Option.Option<PageResult>;
142
+ /**
143
+ * Split/swap transitions this render discovered.
144
+ */
145
+ readonly transitions: ReadonlyArray<(state: State) => State>;
146
+ };
147
+ /**
148
+ * Walk the pages in display order, concatenating their items and collecting the
149
+ * transitions to apply (completed split swaps, recommended splits, eager splits
150
+ * of pages that outgrew `initialNumItems`).
151
+ *
152
+ * `isInvalidCursorError` marks errors that call for a full pagination reset
153
+ * rather than a failure (the cursor no longer matches the query).
154
+ */
155
+ export declare const interpret: (state: State, results: Record.ReadonlyRecord<string, PageResult | Error | undefined>, options: {
156
+ readonly initialNumItems: number;
157
+ readonly isInvalidCursorError: (error: Error) => boolean;
158
+ }) => Interpretation;
159
+ //# sourceMappingURL=StreamPagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StreamPagination.d.ts","sourceRoot":"","sources":["../src/StreamPagination.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,182 @@
1
+ import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
+ import * as Option from "effect/Option";
3
+ import { pipe } from "effect/Function";
4
+ import * as Array from "effect/Array";
5
+ import * as Record from "effect/Record";
6
+ //#region src/StreamPagination.ts
7
+ var StreamPagination_exports = /* @__PURE__ */ __exportAll({
8
+ completeSplit: () => completeSplit,
9
+ empty: () => empty,
10
+ initial: () => initial,
11
+ interpret: () => interpret,
12
+ isLastPageSplitting: () => isLastPageSplitting,
13
+ loadMore: () => loadMore,
14
+ pageRequests: () => pageRequests,
15
+ pin: () => pin,
16
+ split: () => split
17
+ });
18
+ /**
19
+ * A fresh growing-page request (no `endCursor`).
20
+ */
21
+ const growingRequest = (numItems, cursor, budget) => ({
22
+ numItems,
23
+ cursor,
24
+ ...budget.maximumRowsRead === void 0 ? {} : { maximumRowsRead: budget.maximumRowsRead },
25
+ ...budget.maximumBytesRead === void 0 ? {} : { maximumBytesRead: budget.maximumBytesRead }
26
+ });
27
+ /**
28
+ * The skipped state: nothing subscribed.
29
+ */
30
+ const empty = {
31
+ nextPageKey: 0,
32
+ pageKeys: [],
33
+ pages: Record.empty(),
34
+ ongoingSplits: Record.empty()
35
+ };
36
+ /**
37
+ * One growing (unpinned) page from the start of the stream.
38
+ */
39
+ const initial = (initialNumItems, budget = {}) => ({
40
+ nextPageKey: 1,
41
+ pageKeys: ["0"],
42
+ pages: Record.singleton("0", growingRequest(initialNumItems, null, budget)),
43
+ ongoingSplits: Record.empty()
44
+ });
45
+ /**
46
+ * Pin a freshly loaded growing page at its `continueCursor` (or, once
47
+ * exhausted, at the end-of-stream sentinel), so it stops being a sliding
48
+ * window: a pinned page grows and shrinks with the data in its range but never
49
+ * sheds items past its edges. Modeled as a one-replacement split so the
50
+ * original keeps rendering until the pinned twin has a result.
51
+ */
52
+ const pin = (key, endCursor) => (state) => Option.match(Record.get(state.pages, key), {
53
+ onNone: () => state,
54
+ onSome: (page) => {
55
+ if (page.endCursor !== void 0 || Record.has(state.ongoingSplits, key)) return state;
56
+ const pinnedKey = String(state.nextPageKey);
57
+ return {
58
+ nextPageKey: state.nextPageKey + 1,
59
+ pageKeys: state.pageKeys,
60
+ pages: Record.set(state.pages, pinnedKey, {
61
+ ...page,
62
+ endCursor
63
+ }),
64
+ ongoingSplits: Record.set(state.ongoingSplits, key, [pinnedKey])
65
+ };
66
+ }
67
+ });
68
+ /**
69
+ * Pin the growing last page at `continueCursor` and start a new growing page
70
+ * from there. Modeled as a split of the last page into its pinned replacement
71
+ * and the new page, so the swap waits for both.
72
+ */
73
+ const loadMore = (continueCursor, numItems, budget = {}) => (state) => pipe(Option.Do, Option.bind("lastKey", () => Array.last(state.pageKeys)), Option.bind("lastPage", ({ lastKey }) => Record.get(state.pages, lastKey)), Option.match({
74
+ onNone: () => state,
75
+ onSome: ({ lastKey, lastPage }) => {
76
+ if (lastPage.endCursor !== void 0) {
77
+ const nextKey = String(state.nextPageKey);
78
+ return {
79
+ nextPageKey: state.nextPageKey + 1,
80
+ pageKeys: Array.append(state.pageKeys, nextKey),
81
+ pages: Record.set(state.pages, nextKey, growingRequest(numItems, continueCursor, budget)),
82
+ ongoingSplits: state.ongoingSplits
83
+ };
84
+ }
85
+ const pinnedKey = String(state.nextPageKey);
86
+ const nextKey = String(state.nextPageKey + 1);
87
+ return {
88
+ nextPageKey: state.nextPageKey + 2,
89
+ pageKeys: state.pageKeys,
90
+ pages: pipe(state.pages, Record.set(pinnedKey, {
91
+ ...lastPage,
92
+ endCursor: continueCursor
93
+ }), Record.set(nextKey, growingRequest(numItems, continueCursor, budget))),
94
+ ongoingSplits: Record.set(state.ongoingSplits, lastKey, [pinnedKey, nextKey])
95
+ };
96
+ }
97
+ }));
98
+ /**
99
+ * Split the page at `key` in two at `splitCursor`. The first replacement pins
100
+ * at the split point; the second keeps the page's own `endCursor`—for a pinned
101
+ * page its original end (so a truncated `SplitRequired` page loses none of its
102
+ * range), and for a growing page no end at all (it keeps growing).
103
+ */
104
+ const split = (key, splitCursor) => (state) => Option.match(Record.get(state.pages, key), {
105
+ onNone: () => state,
106
+ onSome: (page) => {
107
+ const firstKey = String(state.nextPageKey);
108
+ const secondKey = String(state.nextPageKey + 1);
109
+ return {
110
+ nextPageKey: state.nextPageKey + 2,
111
+ pageKeys: state.pageKeys,
112
+ pages: pipe(state.pages, Record.set(firstKey, {
113
+ ...page,
114
+ endCursor: splitCursor
115
+ }), Record.set(secondKey, {
116
+ ...page,
117
+ cursor: splitCursor
118
+ })),
119
+ ongoingSplits: Record.set(state.ongoingSplits, key, [firstKey, secondKey])
120
+ };
121
+ }
122
+ });
123
+ /**
124
+ * Swap a completed split's replacements in for the original page.
125
+ */
126
+ const completeSplit = (key) => (state) => Option.match(Record.get(state.ongoingSplits, key), {
127
+ onNone: () => state,
128
+ onSome: (replacements) => ({
129
+ nextPageKey: state.nextPageKey,
130
+ pageKeys: Array.flatMap(state.pageKeys, (pageKey) => pageKey === key ? replacements : [pageKey]),
131
+ pages: Record.remove(state.pages, key),
132
+ ongoingSplits: Record.remove(state.ongoingSplits, key)
133
+ })
134
+ });
135
+ /**
136
+ * Whether the last (rendered) page is currently being split or pinned.
137
+ */
138
+ const isLastPageSplitting = (state) => Option.exists(Array.last(state.pageKeys), (lastKey) => Record.has(state.ongoingSplits, lastKey));
139
+ /**
140
+ * Build one subscription request per subscribed page.
141
+ */
142
+ const pageRequests = (state, f) => Record.map(state.pages, f);
143
+ const interpreted = (items, lastResult, transitions) => ({
144
+ _tag: "Interpreted",
145
+ items,
146
+ lastResult,
147
+ transitions
148
+ });
149
+ /**
150
+ * Walk the pages in display order, concatenating their items and collecting the
151
+ * transitions to apply (completed split swaps, recommended splits, eager splits
152
+ * of pages that outgrew `initialNumItems`).
153
+ *
154
+ * `isInvalidCursorError` marks errors that call for a full pagination reset
155
+ * rather than a failure (the cursor no longer matches the query).
156
+ */
157
+ const interpret = (state, results, options) => {
158
+ const go = (remaining, items, transitions, lastResult) => Option.match(Array.head(remaining), {
159
+ onNone: () => interpreted(items, lastResult, transitions),
160
+ onSome: (pageKey) => {
161
+ const result = Record.get(results, pageKey).pipe(Option.flatMap(Option.fromNullishOr), Option.getOrUndefined);
162
+ if (result === void 0) return interpreted(items, Option.none(), transitions);
163
+ if (result instanceof Error) return options.isInvalidCursorError(result) ? { _tag: "ResetRequired" } : {
164
+ _tag: "Failed",
165
+ error: result,
166
+ items
167
+ };
168
+ const hasResult = (key) => Record.get(results, key).pipe(Option.flatMap(Option.fromNullishOr), Option.isSome);
169
+ const ongoingSplit = Record.get(state.ongoingSplits, pageKey);
170
+ const nextTransitions = Option.match(ongoingSplit, {
171
+ onSome: (replacements) => Array.every(replacements, hasResult) ? Array.append(transitions, completeSplit(pageKey)) : transitions,
172
+ onNone: () => typeof result.splitCursor === "string" && (result.pageStatus === "SplitRecommended" || result.pageStatus === "SplitRequired" || result.page.length > options.initialNumItems) ? Array.append(transitions, split(pageKey, result.splitCursor)) : Option.exists(Record.get(state.pages, pageKey), (request) => request.endCursor === void 0) && result.pageStatus !== "SplitRequired" ? Array.append(transitions, pin(pageKey, result.continueCursor)) : transitions
173
+ });
174
+ return result.pageStatus === "SplitRequired" ? interpreted(items, Option.none(), nextTransitions) : go(Array.drop(remaining, 1), Array.appendAll(items, result.page), nextTransitions, Option.some(result));
175
+ }
176
+ });
177
+ return go(state.pageKeys, [], [], Option.none());
178
+ };
179
+ //#endregion
180
+ export { StreamPagination_exports, completeSplit, empty, initial, interpret, isLastPageSplitting, loadMore, pageRequests, pin, split };
181
+
182
+ //# sourceMappingURL=StreamPagination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StreamPagination.js","names":[],"sources":["../src/StreamPagination.ts"],"sourcesContent":["/**\n * EXPERIMENTAL—the pure state machine behind {@link useStreamPaginatedQuery}\n * (see `notes/stream-based-querying.md` in the repo root).\n *\n * Reactive pagination without the query journal: every loaded page—including\n * the first—is pinned to a fixed index range by echoing its `continueCursor`\n * back as the next subscription's `endCursor` (the last page pins to the\n * end-of-stream sentinel once exhausted), so adjacent pages stay exactly\n * contiguous and pages never shed items as data changes—the \"range-defined\n * pages\" the Fully Reactive Pagination article calls for, and the mechanism of\n * `convex-helpers/react`'s `usePaginatedQuery`.\n *\n * Pinning a freshly loaded page, loading more, and splitting an overgrown page\n * are the same transition shape: the affected page keeps rendering while its\n * replacement subscriptions (\"ongoing split\") load, and is swapped out once all\n * of them have results.\n *\n * This module is framework-free (it imports only `effect/*` and a type from\n * `convex/server`); it lives here for now because the React hook is its only\n * consumer—a second client (e.g. `@confect/foldkit`) should prompt a move down\n * to `@confect/js`.\n */\nimport type { PaginationResult } from \"convex/server\";\nimport * as Array from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport { pipe } from \"effect/Function\";\nimport * as Record from \"effect/Record\";\n\n/**\n * The `paginationOpts` one subscribed page queries with.\n */\nexport interface PageRequest {\n readonly numItems: number;\n readonly cursor: string | null;\n /**\n * Present once the page is pinned to a fixed range.\n */\n readonly endCursor?: string;\n /**\n * Per-page read budgets, forwarded to the server so a scan-heavy page fails\n * over to `SplitRequired` instead of exceeding query limits.\n */\n readonly maximumRowsRead?: number;\n readonly maximumBytesRead?: number;\n}\n\n/**\n * The per-page read budgets every growing page is requested with.\n */\nexport interface ReadBudget {\n readonly maximumRowsRead?: number | undefined;\n readonly maximumBytesRead?: number | undefined;\n}\n\n/**\n * A fresh growing-page request (no `endCursor`).\n */\nconst growingRequest = (\n numItems: number,\n cursor: string | null,\n budget: ReadBudget,\n): PageRequest => ({\n numItems,\n cursor,\n ...(budget.maximumRowsRead === undefined\n ? {}\n : { maximumRowsRead: budget.maximumRowsRead }),\n ...(budget.maximumBytesRead === undefined\n ? {}\n : { maximumBytesRead: budget.maximumBytesRead }),\n});\n\nexport interface State {\n readonly nextPageKey: number;\n /**\n * Page keys in display order.\n */\n readonly pageKeys: ReadonlyArray<string>;\n /**\n * The request each subscribed page queries with, by page key.\n */\n readonly pages: Record.ReadonlyRecord<string, PageRequest>;\n /**\n * Pages being replaced by one (a pin) or two (a split) narrower\n * subscriptions: the original stays subscribed (and rendered) until every\n * replacement has a result.\n */\n readonly ongoingSplits: Record.ReadonlyRecord<string, ReadonlyArray<string>>;\n}\n\n/**\n * The skipped state: nothing subscribed.\n */\nexport const empty: State = {\n nextPageKey: 0,\n pageKeys: [],\n pages: Record.empty(),\n ongoingSplits: Record.empty(),\n};\n\n/**\n * One growing (unpinned) page from the start of the stream.\n */\nexport const initial = (\n initialNumItems: number,\n budget: ReadBudget = {},\n): State => ({\n nextPageKey: 1,\n pageKeys: [\"0\"],\n pages: Record.singleton(\"0\", growingRequest(initialNumItems, null, budget)),\n ongoingSplits: Record.empty(),\n});\n\n/**\n * Pin a freshly loaded growing page at its `continueCursor` (or, once\n * exhausted, at the end-of-stream sentinel), so it stops being a sliding\n * window: a pinned page grows and shrinks with the data in its range but never\n * sheds items past its edges. Modeled as a one-replacement split so the\n * original keeps rendering until the pinned twin has a result.\n */\nexport const pin =\n (key: string, endCursor: string): ((state: State) => State) =>\n (state) =>\n Option.match(Record.get(state.pages, key), {\n onNone: () => state,\n onSome: (page) => {\n if (\n page.endCursor !== undefined ||\n Record.has(state.ongoingSplits, key)\n ) {\n return state;\n }\n const pinnedKey = String(state.nextPageKey);\n return {\n nextPageKey: state.nextPageKey + 1,\n pageKeys: state.pageKeys,\n pages: Record.set(state.pages, pinnedKey, { ...page, endCursor }),\n ongoingSplits: Record.set(state.ongoingSplits, key, [pinnedKey]),\n };\n },\n });\n\n/**\n * Pin the growing last page at `continueCursor` and start a new growing page\n * from there. Modeled as a split of the last page into its pinned replacement\n * and the new page, so the swap waits for both.\n */\nexport const loadMore =\n (\n continueCursor: string,\n numItems: number,\n budget: ReadBudget = {},\n ): ((state: State) => State) =>\n (state) =>\n pipe(\n Option.Do,\n Option.bind(\"lastKey\", () => Array.last(state.pageKeys)),\n Option.bind(\"lastPage\", ({ lastKey }) =>\n Record.get(state.pages, lastKey),\n ),\n Option.match({\n onNone: () => state,\n onSome: ({ lastKey, lastPage }) => {\n if (lastPage.endCursor !== undefined) {\n // The last page is already pinned (the usual case, since pages\n // pin themselves on first load): just append a new growing page.\n const nextKey = String(state.nextPageKey);\n return {\n nextPageKey: state.nextPageKey + 1,\n pageKeys: Array.append(state.pageKeys, nextKey),\n pages: Record.set(\n state.pages,\n nextKey,\n growingRequest(numItems, continueCursor, budget),\n ),\n ongoingSplits: state.ongoingSplits,\n };\n }\n const pinnedKey = String(state.nextPageKey);\n const nextKey = String(state.nextPageKey + 1);\n return {\n nextPageKey: state.nextPageKey + 2,\n pageKeys: state.pageKeys,\n pages: pipe(\n state.pages,\n Record.set(pinnedKey, { ...lastPage, endCursor: continueCursor }),\n Record.set(\n nextKey,\n growingRequest(numItems, continueCursor, budget),\n ),\n ),\n ongoingSplits: Record.set(state.ongoingSplits, lastKey, [\n pinnedKey,\n nextKey,\n ]),\n };\n },\n }),\n );\n\n/**\n * Split the page at `key` in two at `splitCursor`. The first replacement pins\n * at the split point; the second keeps the page's own `endCursor`—for a pinned\n * page its original end (so a truncated `SplitRequired` page loses none of its\n * range), and for a growing page no end at all (it keeps growing).\n */\nexport const split =\n (key: string, splitCursor: string): ((state: State) => State) =>\n (state) =>\n Option.match(Record.get(state.pages, key), {\n onNone: () => state,\n onSome: (page) => {\n const firstKey = String(state.nextPageKey);\n const secondKey = String(state.nextPageKey + 1);\n return {\n nextPageKey: state.nextPageKey + 2,\n pageKeys: state.pageKeys,\n pages: pipe(\n state.pages,\n Record.set(firstKey, { ...page, endCursor: splitCursor }),\n Record.set(secondKey, { ...page, cursor: splitCursor }),\n ),\n ongoingSplits: Record.set(state.ongoingSplits, key, [\n firstKey,\n secondKey,\n ]),\n };\n },\n });\n\n/**\n * Swap a completed split's replacements in for the original page.\n */\nexport const completeSplit =\n (key: string): ((state: State) => State) =>\n (state) =>\n Option.match(Record.get(state.ongoingSplits, key), {\n onNone: () => state,\n onSome: (replacements) => ({\n nextPageKey: state.nextPageKey,\n pageKeys: Array.flatMap(state.pageKeys, (pageKey) =>\n pageKey === key ? replacements : [pageKey],\n ),\n pages: Record.remove(state.pages, key),\n ongoingSplits: Record.remove(state.ongoingSplits, key),\n }),\n });\n\n/**\n * Whether the last (rendered) page is currently being split or pinned.\n */\nexport const isLastPageSplitting = (state: State): boolean =>\n Option.exists(Array.last(state.pageKeys), (lastKey) =>\n Record.has(state.ongoingSplits, lastKey),\n );\n\n/**\n * Build one subscription request per subscribed page.\n */\nexport const pageRequests = <A>(\n state: State,\n f: (page: PageRequest) => A,\n): Record.ReadonlyRecord<string, A> => Record.map(state.pages, f);\n\n/**\n * The per-page results a render observes, keyed like {@link pageRequests}.\n */\nexport type Results = Record.ReadonlyRecord<\n string,\n PageResult | Error | undefined\n>;\n\n// -----------------------------------------------------------------------------\n// Interpretation\n// -----------------------------------------------------------------------------\n\n/**\n * The wire shape of one loaded page—`PaginationResult` from `convex/server`, so\n * the protocol has a single source of truth.\n */\nexport type PageResult = PaginationResult<unknown>;\n\n/**\n * What one render pass derives from the subscribed pages' results: the items to\n * show, the trailing complete result (absent while the tail is loading or\n * force-splitting), state transitions to apply, and whether a failure or an\n * invalid cursor was hit.\n */\nexport type Interpretation =\n | { readonly _tag: \"ResetRequired\" }\n | {\n readonly _tag: \"Failed\";\n readonly error: unknown;\n /**\n * Encoded items loaded before the failing page.\n */\n readonly items: ReadonlyArray<unknown>;\n }\n | {\n readonly _tag: \"Interpreted\";\n /**\n * Encoded items across the loaded pages, in order.\n */\n readonly items: ReadonlyArray<unknown>;\n /**\n * The last page's result, when every page has loaded completely.\n */\n readonly lastResult: Option.Option<PageResult>;\n /**\n * Split/swap transitions this render discovered.\n */\n readonly transitions: ReadonlyArray<(state: State) => State>;\n };\n\nconst interpreted = (\n items: ReadonlyArray<unknown>,\n lastResult: Option.Option<PageResult>,\n transitions: ReadonlyArray<(state: State) => State>,\n): Interpretation => ({ _tag: \"Interpreted\", items, lastResult, transitions });\n\n/**\n * Walk the pages in display order, concatenating their items and collecting the\n * transitions to apply (completed split swaps, recommended splits, eager splits\n * of pages that outgrew `initialNumItems`).\n *\n * `isInvalidCursorError` marks errors that call for a full pagination reset\n * rather than a failure (the cursor no longer matches the query).\n */\nexport const interpret = (\n state: State,\n results: Record.ReadonlyRecord<string, PageResult | Error | undefined>,\n options: {\n readonly initialNumItems: number;\n readonly isInvalidCursorError: (error: Error) => boolean;\n },\n): Interpretation => {\n const go = (\n remaining: ReadonlyArray<string>,\n items: ReadonlyArray<unknown>,\n transitions: ReadonlyArray<(s: State) => State>,\n lastResult: Option.Option<PageResult>,\n ): Interpretation =>\n Option.match(Array.head(remaining), {\n onNone: () => interpreted(items, lastResult, transitions),\n onSome: (pageKey) => {\n const result = Record.get(results, pageKey).pipe(\n Option.flatMap(Option.fromNullishOr),\n Option.getOrUndefined,\n );\n if (result === undefined) {\n // The trailing pages are still loading.\n return interpreted(items, Option.none(), transitions);\n }\n if (result instanceof Error) {\n return options.isInvalidCursorError(result)\n ? { _tag: \"ResetRequired\" }\n : { _tag: \"Failed\", error: result, items };\n }\n\n const hasResult = (key: string) =>\n Record.get(results, key).pipe(\n Option.flatMap(Option.fromNullishOr),\n Option.isSome,\n );\n const ongoingSplit = Record.get(state.ongoingSplits, pageKey);\n const nextTransitions = Option.match(ongoingSplit, {\n onSome: (replacements) =>\n // Swap the replacements in once all of them have results.\n Array.every(replacements, hasResult)\n ? Array.append(transitions, completeSplit(pageKey))\n : transitions,\n onNone: () =>\n typeof result.splitCursor === \"string\" &&\n (result.pageStatus === \"SplitRecommended\" ||\n result.pageStatus === \"SplitRequired\" ||\n result.page.length > options.initialNumItems)\n ? Array.append(transitions, split(pageKey, result.splitCursor))\n : // A loaded growing page pins itself to the range it just\n // served (`END_CURSOR` once exhausted), so it stops being a\n // sliding window that sheds items as new documents arrive.\n Option.exists(\n Record.get(state.pages, pageKey),\n (request) => request.endCursor === undefined,\n ) && result.pageStatus !== \"SplitRequired\"\n ? Array.append(transitions, pin(pageKey, result.continueCursor))\n : transitions,\n });\n\n // A force-split page couldn't be fetched in full: show the items\n // before it and report the tail as still loading.\n return result.pageStatus === \"SplitRequired\"\n ? interpreted(items, Option.none(), nextTransitions)\n : go(\n Array.drop(remaining, 1),\n Array.appendAll(items, result.page),\n nextTransitions,\n Option.some(result),\n );\n },\n });\n\n return go(state.pageKeys, [], [], Option.none());\n};\n"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -4,7 +4,8 @@ import * as Result from "effect/Result";
4
4
  import * as OptimisticLocalStore from "./OptimisticLocalStore";
5
5
  import * as PaginatedQueryResult from "./PaginatedQueryResult";
6
6
  import * as QueryResult from "./QueryResult";
7
- export { OptimisticLocalStore, PaginatedQueryResult, QueryResult };
7
+ import * as StreamPagination from "./StreamPagination";
8
+ export { OptimisticLocalStore, PaginatedQueryResult, QueryResult, StreamPagination, };
8
9
  export type InvokeReturn<Ref_ extends Ref.Any> = [Ref.Error<Ref_>] extends [
9
10
  never
10
11
  ] ? Promise<Ref.Returns<Ref_>> : Promise<Result.Result<Ref.Returns<Ref_>, Ref.Error<Ref_>>>;
@@ -26,8 +27,8 @@ type OmitKey<T, K extends PropertyKey> = {
26
27
  */
27
28
  export type PaginatedQueryArgs<Query extends Ref.AnyPublicPaginatedQuery> = OmitKey<Ref.Args<Query>, "paginationOpts"> extends infer Args_ ? keyof Args_ extends never ? Record<string, never> : Args_ : never;
28
29
  /**
29
- * The item type for a paginated query: the element type of the `page` field
30
- * of the ref's returns.
30
+ * The item type for a paginated query: the element type of the `page` field of
31
+ * the ref's returns.
31
32
  */
32
33
  export type PaginatedQueryItem<Query extends Ref.AnyPublicPaginatedQuery> = Ref.Returns<Query>["page"][number];
33
34
  /**
@@ -53,6 +54,35 @@ export type PaginatedQueryOptions = Parameters<typeof useConvexPaginatedQuery>[2
53
54
  * errors are thrown, to be caught by an error boundary.
54
55
  */
55
56
  export declare const usePaginatedQuery: <Query extends Ref.AnyPublicPaginatedQuery>(ref: Query, args: UsePaginatedQueryArgs<Query>, options: PaginatedQueryOptions) => PaginatedQueryResult.PaginatedQueryResult<PaginatedQueryItem<Query>, Ref.Error<Query>>;
57
+ /**
58
+ * EXPERIMENTAL—endCursor-pinned reactive pagination (see
59
+ * `notes/stream-based-querying.md`). Use it with paginated queries whose
60
+ * handlers paginate via `QueryStream.paginate`: those don't write the query
61
+ * journal that {@link usePaginatedQuery}'s built-in reactivity relies on, so
62
+ * gap-free pages must be maintained by the client instead. (It works with any
63
+ * paginated query honoring the `endCursor` protocol field, including the
64
+ * built-in `paginate`.)
65
+ *
66
+ * Each loaded page is pinned to a fixed index range by re-subscribing it with
67
+ * its `continueCursor` echoed back as `endCursor`—pages then grow and shrink
68
+ * reactively but always meet exactly, and a page that outgrows
69
+ * `initialNumItems` is split in two. This is the mechanism of
70
+ * `convex-helpers/react`'s `usePaginatedQuery`, re-expressed over the pure
71
+ * {@link StreamPagination} state machine, with args/items/errors codec'd through
72
+ * the ref's schemas exactly like {@link usePaginatedQuery}.
73
+ */
74
+ export declare const useStreamPaginatedQuery: <Query extends Ref.AnyPublicPaginatedQuery>(ref: Query, args: UsePaginatedQueryArgs<Query>, options: {
75
+ readonly initialNumItems: number;
76
+ /**
77
+ * Per-page read budgets forwarded as `paginationOpts.maximumRowsRead` and
78
+ * `paginationOpts.maximumBytesRead`: a page that would scan more rows, or
79
+ * read more bytes, than these returns truncated with `SplitRequired` (and
80
+ * the hook splits it) instead of exceeding Convex's query limits on a
81
+ * filter-heavy stream.
82
+ */
83
+ readonly maximumRowsRead?: number;
84
+ readonly maximumBytesRead?: number;
85
+ }) => PaginatedQueryResult.PaginatedQueryResult<PaginatedQueryItem<Query>, Ref.Error<Query>>;
56
86
  /**
57
87
  * An optimistic update for a Confect mutation. Mirrors Convex's
58
88
  * `OptimisticUpdate`, but receives a Confect {@link OptimisticLocalStore} and
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}