kitcn 0.17.1 → 0.17.2
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/dist/solid/index.js +107 -56
- package/package.json +1 -1
package/dist/solid/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { getFunctionName } from "convex/server";
|
|
2
|
-
import { Show, createContext, createEffect, createMemo, createSignal, on, onCleanup, onMount, useContext } from "solid-js";
|
|
2
|
+
import { Show, createComputed, createContext, createEffect, createMemo, createRenderEffect, createSignal, on, onCleanup, onMount, useContext } from "solid-js";
|
|
3
3
|
import { createComponent, memo } from "solid-js/web";
|
|
4
4
|
import { createStore } from "solid-js/store";
|
|
5
|
-
import { notifyManager, skipToken,
|
|
5
|
+
import { notifyManager, skipToken, useQueryClient } from "@tanstack/solid-query";
|
|
6
6
|
import { ConvexClient, ConvexHttpClient } from "convex/browser";
|
|
7
|
-
import { hashKey } from "@tanstack/query-core";
|
|
7
|
+
import { QueriesObserver, hashKey } from "@tanstack/query-core";
|
|
8
8
|
import { convexToJson } from "convex/values";
|
|
9
9
|
|
|
10
10
|
//#region src/crpc/error.ts
|
|
@@ -2504,6 +2504,42 @@ const getConvexQueryClientSingleton = ({ authStore, convex, queryClient, symbolK
|
|
|
2504
2504
|
//#region src/internal/pagination.ts
|
|
2505
2505
|
const shouldSplitPaginationPage = (page, initialNumItems) => Boolean(page.splitCursor) && (page.pageStatus === "SplitRecommended" || page.pageStatus === "SplitRequired" || initialNumItems !== void 0 && page.page.length > initialNumItems * 2);
|
|
2506
2506
|
|
|
2507
|
+
//#endregion
|
|
2508
|
+
//#region src/solid/create-queries-results.ts
|
|
2509
|
+
/**
|
|
2510
|
+
* Subscribe a reactive list of queries and expose the raw observer results.
|
|
2511
|
+
*
|
|
2512
|
+
* Solid Query's `useQueries` cannot back an aggregate result: it feeds the
|
|
2513
|
+
* `combine` output straight into `createStore` and then calls `.map()` on it,
|
|
2514
|
+
* so any non-array aggregate throws `state.map is not a function` while the
|
|
2515
|
+
* component is still setting up. It also routes `.data` through
|
|
2516
|
+
* `createResource`, which suspends the nearest boundary for as long as a query
|
|
2517
|
+
* is in flight.
|
|
2518
|
+
*
|
|
2519
|
+
* Driving `QueriesObserver` directly keeps every entry a plain
|
|
2520
|
+
* `QueryObserverResult` - the same value the React port aggregates - and leaves
|
|
2521
|
+
* aggregation to the caller.
|
|
2522
|
+
*
|
|
2523
|
+
* @param queries Reactive list of query options.
|
|
2524
|
+
* @returns Accessor for the raw observer results, in query order.
|
|
2525
|
+
*/
|
|
2526
|
+
function createQueriesResults(queries) {
|
|
2527
|
+
const queryClient = useQueryClient();
|
|
2528
|
+
const defaulted = () => queries().map((options) => ({
|
|
2529
|
+
...queryClient.defaultQueryOptions(options),
|
|
2530
|
+
_optimisticResults: "optimistic"
|
|
2531
|
+
}));
|
|
2532
|
+
const observer = new QueriesObserver(queryClient, defaulted());
|
|
2533
|
+
const [results, setResults] = createSignal(observer.getCurrentResult(), { equals: false });
|
|
2534
|
+
createComputed(() => {
|
|
2535
|
+
const next = defaulted();
|
|
2536
|
+
observer.setQueries(next);
|
|
2537
|
+
setResults(observer.getOptimisticResult(next, void 0)[0]);
|
|
2538
|
+
});
|
|
2539
|
+
onCleanup(observer.subscribe((next) => setResults(next)));
|
|
2540
|
+
return results;
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2507
2543
|
//#endregion
|
|
2508
2544
|
//#region src/solid/use-infinite-query.ts
|
|
2509
2545
|
const PAGINATION_KEY_PREFIX = "__pagination__";
|
|
@@ -2516,6 +2552,59 @@ const getOrCreatePaginationId = (storeKey) => {
|
|
|
2516
2552
|
paginationIdStore.set(storeKey, newId);
|
|
2517
2553
|
return newId;
|
|
2518
2554
|
};
|
|
2555
|
+
/** Read the identity of a Convex document, tolerating `id` and `_id` shapes */
|
|
2556
|
+
const getItemId = (item) => {
|
|
2557
|
+
const doc = item;
|
|
2558
|
+
return doc?._id || doc?.id;
|
|
2559
|
+
};
|
|
2560
|
+
/**
|
|
2561
|
+
* Fold the per-page observer results into one pagination-shaped aggregate.
|
|
2562
|
+
* Pure: same inputs always produce the same output, so it is safe to re-run
|
|
2563
|
+
* inside a reactive derivation.
|
|
2564
|
+
*/
|
|
2565
|
+
const aggregatePages = (results, hasPlaceholderData) => {
|
|
2566
|
+
const allItems = [];
|
|
2567
|
+
const pages = [];
|
|
2568
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
2569
|
+
let lastPage;
|
|
2570
|
+
let status = "LoadingFirstPage";
|
|
2571
|
+
for (let i = 0; i < results.length; i++) {
|
|
2572
|
+
const pageQuery = results[i];
|
|
2573
|
+
if (pageQuery.isLoading || pageQuery.data === void 0) {
|
|
2574
|
+
status = i === 0 ? "LoadingFirstPage" : "LoadingMore";
|
|
2575
|
+
break;
|
|
2576
|
+
}
|
|
2577
|
+
const page = pageQuery.data;
|
|
2578
|
+
lastPage = page;
|
|
2579
|
+
pages.push(page.page);
|
|
2580
|
+
for (const item of page.page) {
|
|
2581
|
+
const id = getItemId(item);
|
|
2582
|
+
if (id && seenIds.has(id)) continue;
|
|
2583
|
+
if (id) seenIds.add(id);
|
|
2584
|
+
allItems.push(item);
|
|
2585
|
+
}
|
|
2586
|
+
status = page.isDone ? "Exhausted" : "CanLoadMore";
|
|
2587
|
+
}
|
|
2588
|
+
const firstPage = results.length > 0 ? results[0] : void 0;
|
|
2589
|
+
const isPlaceholderData = firstPage ? firstPage.isPlaceholderData : hasPlaceholderData;
|
|
2590
|
+
const isFetching = results.some((r) => r.isFetching);
|
|
2591
|
+
const error = results.find((r) => r.isError)?.error ?? null;
|
|
2592
|
+
return {
|
|
2593
|
+
data: allItems,
|
|
2594
|
+
dataUpdatedAt: Math.max(...results.map((r) => r.dataUpdatedAt)),
|
|
2595
|
+
error,
|
|
2596
|
+
failureReason: error,
|
|
2597
|
+
isError: results.some((r) => r.isError),
|
|
2598
|
+
isFetchNextPageError: results.length > 1 && (results.at(-1)?.isError ?? false),
|
|
2599
|
+
isFetching,
|
|
2600
|
+
isLoading: status === "LoadingFirstPage",
|
|
2601
|
+
isPlaceholderData,
|
|
2602
|
+
isRefetching: isFetching && allItems.length > 0 && !isPlaceholderData,
|
|
2603
|
+
lastPage,
|
|
2604
|
+
pages,
|
|
2605
|
+
status
|
|
2606
|
+
};
|
|
2607
|
+
};
|
|
2519
2608
|
/** Build a unique key for recovery attempt detection */
|
|
2520
2609
|
const buildRecoveryKey = (pageKeys, page0Cursor, page0UpdatedAt) => JSON.stringify({
|
|
2521
2610
|
pageKeys,
|
|
@@ -2531,26 +2620,27 @@ const buildRecoveryKey = (pageKeys, page0Cursor, page0UpdatedAt) => JSON.stringi
|
|
|
2531
2620
|
* This hook detects this pattern and creates a recovery page that fetches
|
|
2532
2621
|
* enough items to cover the lost pages, preserving the user's scroll position.
|
|
2533
2622
|
*/
|
|
2534
|
-
const useStaleCursorRecovery = ({ argsObject, combined, limit, setState, state }) => {
|
|
2623
|
+
const useStaleCursorRecovery = ({ argsObject, combined, limit, pageResults, setState, state }) => {
|
|
2535
2624
|
createEffect(on([
|
|
2536
2625
|
() => combined.isFetchNextPageError,
|
|
2537
|
-
|
|
2626
|
+
pageResults,
|
|
2538
2627
|
() => state().pageKeys,
|
|
2539
2628
|
() => state().queries,
|
|
2540
2629
|
() => state().autoRecoveryAttempted,
|
|
2541
2630
|
argsObject
|
|
2542
2631
|
], () => {
|
|
2543
2632
|
if (!combined.isFetchNextPageError) return;
|
|
2544
|
-
const
|
|
2633
|
+
const results = pageResults();
|
|
2634
|
+
const page0Result = results[0];
|
|
2545
2635
|
const page0Data = page0Result?.data;
|
|
2546
2636
|
const page0UpdatedAt = page0Result?.dataUpdatedAt ?? 0;
|
|
2547
2637
|
const hasPage0Data = page0Data !== void 0 && !page0Result?.isError;
|
|
2548
|
-
const hasSubsequentErrors =
|
|
2638
|
+
const hasSubsequentErrors = results.slice(1).some((q) => q?.isError && !q?.isFetching);
|
|
2549
2639
|
if (!hasPage0Data || !hasSubsequentErrors || !page0Data?.continueCursor) return;
|
|
2550
2640
|
const currentState = state();
|
|
2551
2641
|
const recoveryKey = buildRecoveryKey(currentState.pageKeys, page0Data.continueCursor, page0UpdatedAt);
|
|
2552
2642
|
if (currentState.autoRecoveryAttempted === recoveryKey) return;
|
|
2553
|
-
const erroredPageKeys = currentState.pageKeys.filter((_, i) => i > 0 &&
|
|
2643
|
+
const erroredPageKeys = currentState.pageKeys.filter((_, i) => i > 0 && results[i]?.isError);
|
|
2554
2644
|
const itemsToRecover = erroredPageKeys.reduce((sum, key) => {
|
|
2555
2645
|
return sum + (currentState.queries[key]?.args?.limit ?? limit ?? 20);
|
|
2556
2646
|
}, 0);
|
|
@@ -2711,66 +2801,27 @@ const useInfiniteQueryInternal = (query, args, options) => {
|
|
|
2711
2801
|
} } : {}
|
|
2712
2802
|
};
|
|
2713
2803
|
}));
|
|
2714
|
-
const
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
const pages = [];
|
|
2719
|
-
const seenIds = /* @__PURE__ */ new Set();
|
|
2720
|
-
let lastPage;
|
|
2721
|
-
let paginationStatus = "LoadingFirstPage";
|
|
2722
|
-
for (let i = 0; i < results.length; i++) {
|
|
2723
|
-
const pageQuery = results[i];
|
|
2724
|
-
if (pageQuery.isLoading || pageQuery.data === void 0) {
|
|
2725
|
-
paginationStatus = i === 0 ? "LoadingFirstPage" : "LoadingMore";
|
|
2726
|
-
break;
|
|
2727
|
-
}
|
|
2728
|
-
const page = pageQuery.data;
|
|
2729
|
-
lastPage = page;
|
|
2730
|
-
pages.push(page.page);
|
|
2731
|
-
for (const item of page.page) {
|
|
2732
|
-
const id = item._id || item.id;
|
|
2733
|
-
if (id && seenIds.has(id)) continue;
|
|
2734
|
-
if (id) seenIds.add(id);
|
|
2735
|
-
allItems.push(item);
|
|
2736
|
-
}
|
|
2737
|
-
paginationStatus = page.isDone ? "Exhausted" : "CanLoadMore";
|
|
2738
|
-
}
|
|
2739
|
-
const isPlaceholderData = results[0]?.isPlaceholderData ?? !!placeholderData;
|
|
2740
|
-
const isFetching = results.some((r) => r.isFetching);
|
|
2741
|
-
return {
|
|
2742
|
-
data: allItems,
|
|
2743
|
-
dataUpdatedAt: Math.max(...results.map((r) => r.dataUpdatedAt ?? 0)),
|
|
2744
|
-
lastPage,
|
|
2745
|
-
pages,
|
|
2746
|
-
status: paginationStatus,
|
|
2747
|
-
error: results.find((r) => r.isError)?.error ?? null,
|
|
2748
|
-
isError: results.some((r) => r.isError),
|
|
2749
|
-
isFetching,
|
|
2750
|
-
isFetchNextPageError: results.length > 1 && (results.at(-1)?.isError ?? false),
|
|
2751
|
-
isPlaceholderData,
|
|
2752
|
-
isRefetching: isFetching && allItems.length > 0 && !isPlaceholderData,
|
|
2753
|
-
isLoading: paginationStatus === "LoadingFirstPage",
|
|
2754
|
-
failureReason: results.find((r) => r.isError)?.error ?? null,
|
|
2755
|
-
_rawResults: results
|
|
2756
|
-
};
|
|
2757
|
-
}
|
|
2758
|
-
}));
|
|
2804
|
+
const pageResults = createQueriesResults(() => tanstackQueries());
|
|
2805
|
+
const derive = () => aggregatePages(pageResults(), !!placeholderData);
|
|
2806
|
+
const [combined, setCombined] = createStore(derive());
|
|
2807
|
+
createRenderEffect(() => setCombined(derive()));
|
|
2759
2808
|
useStaleCursorRecovery({
|
|
2760
2809
|
argsObject,
|
|
2761
2810
|
combined,
|
|
2762
2811
|
limit,
|
|
2812
|
+
pageResults,
|
|
2763
2813
|
setState,
|
|
2764
2814
|
state
|
|
2765
2815
|
});
|
|
2766
2816
|
createEffect(on([
|
|
2767
|
-
|
|
2817
|
+
pageResults,
|
|
2768
2818
|
() => state().pageKeys,
|
|
2769
2819
|
() => state().queries,
|
|
2770
2820
|
argsObject
|
|
2771
2821
|
], () => {
|
|
2772
|
-
|
|
2773
|
-
|
|
2822
|
+
const results = pageResults();
|
|
2823
|
+
for (let i = 0; i < results.length; i++) {
|
|
2824
|
+
const pageQuery = results[i];
|
|
2774
2825
|
if (pageQuery.data) {
|
|
2775
2826
|
const page = pageQuery.data;
|
|
2776
2827
|
const pageKey = state().pageKeys[i];
|