@bb-labs/convex-cache 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +342 -0
  2. package/dist/cli/fns/dev.d.ts +11 -0
  3. package/dist/cli/fns/dev.js +59 -0
  4. package/dist/cli/fns/fns/convex-runner.d.ts +26 -0
  5. package/dist/cli/fns/fns/convex-runner.js +71 -0
  6. package/dist/cli/fns/fns/generate-z-schema/fns/build-schema-map.d.ts +8 -0
  7. package/dist/cli/fns/fns/generate-z-schema/fns/build-schema-map.js +39 -0
  8. package/dist/cli/fns/fns/generate-z-schema/fns/load-convex.d.ts +7 -0
  9. package/dist/cli/fns/fns/generate-z-schema/fns/load-convex.js +49 -0
  10. package/dist/cli/fns/fns/generate-z-schema/generate.d.ts +7 -0
  11. package/dist/cli/fns/fns/generate-z-schema/generate.js +16 -0
  12. package/dist/cli/fns/fns/generate-z-schema/utils/build-schema-file.d.ts +13 -0
  13. package/dist/cli/fns/fns/generate-z-schema/utils/build-schema-file.js +42 -0
  14. package/dist/cli/fns/fns/generate-z-schema/utils/extract-schema.d.ts +3 -0
  15. package/dist/cli/fns/fns/generate-z-schema/utils/extract-schema.js +84 -0
  16. package/dist/cli/fns/fns/generate-z-schema/utils/find-fn-ref.d.ts +5 -0
  17. package/dist/cli/fns/fns/generate-z-schema/utils/find-fn-ref.js +27 -0
  18. package/dist/cli/index.d.ts +2 -0
  19. package/dist/cli/index.js +25 -0
  20. package/dist/cli/lib/convex-config.d.ts +21 -0
  21. package/dist/cli/lib/convex-config.js +37 -0
  22. package/dist/cli/lib/dir-watcher.d.ts +49 -0
  23. package/dist/cli/lib/dir-watcher.js +97 -0
  24. package/dist/cli/lib/package-cmds.d.ts +10 -0
  25. package/dist/cli/lib/package-cmds.js +17 -0
  26. package/dist/cli/lib/resolve-bun.d.ts +4 -0
  27. package/dist/cli/lib/resolve-bun.js +24 -0
  28. package/dist/cli/lib/shell-runner.d.ts +61 -0
  29. package/dist/cli/lib/shell-runner.js +133 -0
  30. package/dist/convex-cache/adapters/next/fns/build-preloader.d.ts +5 -0
  31. package/dist/convex-cache/adapters/next/fns/build-preloader.js +6 -0
  32. package/dist/convex-cache/adapters/next/fns/preload-query.d.ts +12 -0
  33. package/dist/convex-cache/adapters/next/fns/preload-query.js +27 -0
  34. package/dist/convex-cache/adapters/next/hooks/hooks.d.ts +8 -0
  35. package/dist/convex-cache/adapters/next/hooks/hooks.js +9 -0
  36. package/dist/convex-cache/adapters/next/index.d.ts +1 -0
  37. package/dist/convex-cache/adapters/next/index.js +1 -0
  38. package/dist/convex-cache/adapters/next/lib/revalidate-query-cache.d.ts +13 -0
  39. package/dist/convex-cache/adapters/next/lib/revalidate-query-cache.js +21 -0
  40. package/dist/convex-cache/adapters/next/server-fns/preload-query.d.ts +16 -0
  41. package/dist/convex-cache/adapters/next/server-fns/preload-query.js +32 -0
  42. package/dist/convex-cache/adapters/next/server-fns/revalidate-cache.d.ts +3 -0
  43. package/dist/convex-cache/adapters/next/server-fns/revalidate-cache.js +6 -0
  44. package/dist/convex-cache/adapters/next/server.d.ts +2 -0
  45. package/dist/convex-cache/adapters/next/server.js +2 -0
  46. package/dist/convex-cache/adapters/next/types/preloaded.d.ts +8 -0
  47. package/dist/convex-cache/adapters/next/types/preloaded.js +1 -0
  48. package/dist/convex-cache/adapters/next/utils/cache-profile.d.ts +1 -0
  49. package/dist/convex-cache/adapters/next/utils/cache-profile.js +1 -0
  50. package/dist/convex-cache/adapters/react/hooks/hooks.d.ts +8 -0
  51. package/dist/convex-cache/adapters/react/hooks/hooks.js +12 -0
  52. package/dist/convex-cache/adapters/react/index.d.ts +2 -0
  53. package/dist/convex-cache/adapters/react/index.js +2 -0
  54. package/dist/convex-cache/adapters/react/provider/provider.d.ts +13 -0
  55. package/dist/convex-cache/adapters/react/provider/provider.js +16 -0
  56. package/dist/convex-cache/core/client-cache/helpers/hooks/use-client-cache.d.ts +10 -0
  57. package/dist/convex-cache/core/client-cache/helpers/hooks/use-client-cache.js +16 -0
  58. package/dist/convex-cache/core/client-cache/helpers/hooks/use-query-key.d.ts +7 -0
  59. package/dist/convex-cache/core/client-cache/helpers/hooks/use-query-key.js +5 -0
  60. package/dist/convex-cache/core/client-cache/queries/paginated-query.d.ts +13 -0
  61. package/dist/convex-cache/core/client-cache/queries/paginated-query.js +37 -0
  62. package/dist/convex-cache/core/client-cache/queries/query.d.ts +10 -0
  63. package/dist/convex-cache/core/client-cache/queries/query.js +23 -0
  64. package/dist/convex-cache/core/helpers/utils/convert-paginated-schema.d.ts +13 -0
  65. package/dist/convex-cache/core/helpers/utils/convert-paginated-schema.js +45 -0
  66. package/dist/convex-cache/core/helpers/utils/fetch-schema-from-map.d.ts +21 -0
  67. package/dist/convex-cache/core/helpers/utils/fetch-schema-from-map.js +16 -0
  68. package/dist/convex-cache/core/helpers/utils/query-key.d.ts +10 -0
  69. package/dist/convex-cache/core/helpers/utils/query-key.js +16 -0
  70. package/dist/convex-cache/core/server-cache/queries/paginated-query.d.ts +15 -0
  71. package/dist/convex-cache/core/server-cache/queries/paginated-query.js +16 -0
  72. package/dist/convex-cache/core/server-cache/queries/query.d.ts +12 -0
  73. package/dist/convex-cache/core/server-cache/queries/query.js +16 -0
  74. package/dist/convex-cache/core/types/index.d.ts +2 -0
  75. package/dist/convex-cache/core/types/index.js +2 -0
  76. package/dist/convex-cache/core/types/types/paginated-query.d.ts +8 -0
  77. package/dist/convex-cache/core/types/types/paginated-query.js +2 -0
  78. package/dist/convex-cache/core/types/types/query.d.ts +5 -0
  79. package/dist/convex-cache/core/types/types/query.js +1 -0
  80. package/dist/convex-cache/index.d.ts +1 -0
  81. package/dist/convex-cache/index.js +1 -0
  82. package/dist/convex-cache/types/schema-map.d.ts +4 -0
  83. package/dist/convex-cache/types/schema-map.js +1 -0
  84. package/dist/validation/index.d.ts +10 -0
  85. package/dist/validation/index.js +83 -0
  86. package/package.json +58 -0
@@ -0,0 +1,12 @@
1
+ import { PQ_CachedResult, PQ_Query } from "../../../core/types/types/paginated-query";
2
+ import { Q_Query, Q_Result } from "../../../core/types/types/query";
3
+ import { PQ_ArgsPreloaded, PQ_OptionsPreloaded, Q_ArgsPreloaded, Q_OptionsPreloaded } from "../types/preloaded";
4
+ import { T_SchemaMap } from "../../../types/schema-map";
5
+ export type PreloadQueryReturn<Q extends Q_Query | PQ_Query> = Q extends PQ_Query ? PQ_CachedResult<Q> | undefined : Q_Result<Q> | undefined;
6
+ export type T_PreloadQueryParams<Q extends Q_Query | PQ_Query> = {
7
+ query: Q;
8
+ args: Q_ArgsPreloaded<Q> | PQ_ArgsPreloaded<Q>;
9
+ options?: Q_OptionsPreloaded<Q> | PQ_OptionsPreloaded<Q>;
10
+ schemaMap: T_SchemaMap;
11
+ };
12
+ export declare function preloadQuery<Q extends Q_Query | PQ_Query>(params: T_PreloadQueryParams<Q>): Promise<PreloadQueryReturn<Q>>;
@@ -0,0 +1,27 @@
1
+ import { _preloadPaginatedQuery, _preloadQuery } from "../server-fns/preload-query";
2
+ import { getFunctionName } from "convex/server";
3
+ import { fetchSchemaFromMap } from "../../../core/helpers/utils/fetch-schema-from-map";
4
+ export async function preloadQuery(params) {
5
+ const { query, args, options, schemaMap } = params;
6
+ const queryName = getFunctionName(query);
7
+ if (args && "paginationOpts" in args) {
8
+ // Paginated branch
9
+ const result = (await _preloadPaginatedQuery({
10
+ queryName,
11
+ args: args,
12
+ options,
13
+ }));
14
+ const schema = fetchSchemaFromMap({ queryName, schemaMap, type: "paginated" });
15
+ const validated = schema.safeParse(result);
16
+ return (validated.success ? result : undefined);
17
+ }
18
+ // Non-paginated branch
19
+ const result = (await _preloadQuery({
20
+ queryName,
21
+ args,
22
+ options,
23
+ }));
24
+ const schema = fetchSchemaFromMap({ queryName, schemaMap, type: "query" });
25
+ const validated = schema.safeParse(result);
26
+ return (validated.success ? result : undefined);
27
+ }
@@ -0,0 +1,8 @@
1
+ import { Q_Query, PQ_Query } from "../../../core/types";
2
+ import { T_UseCachedPaginatedQueryServer } from "../../../core/server-cache/queries/paginated-query";
3
+ import { T_UseCachedQueryServer } from "../../../core/server-cache/queries/query";
4
+ type T_UseCachedQueryServerParams<Q extends Q_Query> = Omit<T_UseCachedQueryServer<Q>, "revalidateCache">;
5
+ export declare const useCachedQueryServer: <Q extends Q_Query>({ query, args, preloadedData }: T_UseCachedQueryServerParams<Q>) => import("../../../core/types").Q_Result<Q> | undefined;
6
+ type T_UseCachedPaginatedQueryServerParams<Q extends PQ_Query> = Omit<T_UseCachedPaginatedQueryServer<Q>, "revalidateCache">;
7
+ export declare const useCachedPaginatedQueryServer: <Q extends PQ_Query>({ query, args, options, preloadedData }: T_UseCachedPaginatedQueryServerParams<Q>) => import("../../../core/types").PQ_Result<Q>;
8
+ export {};
@@ -0,0 +1,9 @@
1
+ import { _useCachedPaginatedQueryServer } from "../../../core/server-cache/queries/paginated-query";
2
+ import { _useCachedQueryServer } from "../../../core/server-cache/queries/query";
3
+ import { revalidatePaginatedQueryCache, revalidateQueryCache } from "../lib/revalidate-query-cache";
4
+ export const useCachedQueryServer = ({ query, args, preloadedData }) => {
5
+ return _useCachedQueryServer({ query, args, preloadedData, revalidateCache: revalidateQueryCache });
6
+ };
7
+ export const useCachedPaginatedQueryServer = ({ query, args, options, preloadedData }) => {
8
+ return _useCachedPaginatedQueryServer({ query, args, options, preloadedData, revalidateCache: revalidatePaginatedQueryCache });
9
+ };
@@ -0,0 +1 @@
1
+ export * from "./hooks/hooks";
@@ -0,0 +1 @@
1
+ export * from "./hooks/hooks";
@@ -0,0 +1,13 @@
1
+ import { Q_Query } from "../../../core/types/types/query";
2
+ import { PQ_Query } from "../../../core/types/types/paginated-query";
3
+ import { PQ_ArgsPreloaded, Q_ArgsPreloaded } from "../types/preloaded";
4
+ export declare const revalidateQueryCache: <Q extends Q_Query>({ query, args }: {
5
+ query: Q;
6
+ args: Q_ArgsPreloaded<Q>;
7
+ }) => Promise<void>;
8
+ type T_RevalidatePaginatedQueryCacheParams<Q extends PQ_Query> = {
9
+ query: Q;
10
+ args: PQ_ArgsPreloaded<Q>;
11
+ };
12
+ export declare const revalidatePaginatedQueryCache: <Q extends PQ_Query>({ query, args }: T_RevalidatePaginatedQueryCacheParams<Q>) => Promise<void>;
13
+ export {};
@@ -0,0 +1,21 @@
1
+ import { makeQueryKey } from "../../../core/helpers/utils/query-key";
2
+ import { getFunctionName } from "convex/server";
3
+ import { revalidateCache } from "../server-fns/revalidate-cache";
4
+ export const revalidateQueryCache = async ({ query, args }) => {
5
+ const queryName = getFunctionName(query);
6
+ const { tag } = makeQueryKey({
7
+ queryName,
8
+ args,
9
+ kind: "query",
10
+ });
11
+ await revalidateCache({ tag });
12
+ };
13
+ export const revalidatePaginatedQueryCache = async ({ query, args }) => {
14
+ const queryName = getFunctionName(query);
15
+ const { tag } = makeQueryKey({
16
+ queryName,
17
+ args,
18
+ kind: "paginated",
19
+ });
20
+ await revalidateCache({ tag });
21
+ };
@@ -0,0 +1,16 @@
1
+ import { Q_Query, Q_Result } from "../../../core/types/types/query";
2
+ import { PQ_CachedResult, PQ_Query } from "../../../core/types/types/paginated-query";
3
+ import type { PQ_ArgsPreloaded, PQ_OptionsPreloaded, Q_ArgsPreloaded, Q_OptionsPreloaded } from "../types/preloaded";
4
+ type T_PreloadQueryParams<Q extends Q_Query> = {
5
+ queryName: string;
6
+ args: Q_ArgsPreloaded<Q>;
7
+ options?: Q_OptionsPreloaded<Q>;
8
+ };
9
+ export declare const _preloadQuery: ({ queryName, args, options }: T_PreloadQueryParams<Q_Query>) => Promise<Q_Result<Q_Query>>;
10
+ type T_PreloadPaginatedQueryParams<Q extends PQ_Query> = {
11
+ queryName: string;
12
+ args: PQ_ArgsPreloaded<Q>;
13
+ options?: PQ_OptionsPreloaded<Q>;
14
+ };
15
+ export declare const _preloadPaginatedQuery: ({ queryName, args, options }: T_PreloadPaginatedQueryParams<PQ_Query>) => Promise<PQ_CachedResult<PQ_Query>>;
16
+ export {};
@@ -0,0 +1,32 @@
1
+ import { makeFunctionReference } from "convex/server";
2
+ import { fetchQuery } from "convex/nextjs";
3
+ import { cacheLife, cacheTag } from "next/cache";
4
+ import { makeQueryKey } from "../../../core/helpers/utils/query-key";
5
+ import { defaultCacheProfile } from "../utils/cache-profile";
6
+ export const _preloadQuery = async ({ queryName, args, options }) => {
7
+ "use cache";
8
+ const { tag } = makeQueryKey({ queryName, args, kind: "query" });
9
+ cacheTag(tag);
10
+ cacheLife(defaultCacheProfile);
11
+ const fnRef = makeFunctionReference(queryName);
12
+ const argsTuple = [args, options];
13
+ const result = await fetchQuery(fnRef, ...argsTuple);
14
+ return result;
15
+ };
16
+ export const _preloadPaginatedQuery = async ({ queryName, args, options }) => {
17
+ "use cache";
18
+ const { tag } = makeQueryKey({ queryName, args, kind: "paginated" });
19
+ cacheTag(tag);
20
+ cacheLife(defaultCacheProfile);
21
+ const fnRef = makeFunctionReference(queryName);
22
+ const argsTuple = [args, options];
23
+ const result = await fetchQuery(fnRef, ...argsTuple);
24
+ return convertPaginatedResultForClient({ result });
25
+ };
26
+ const convertPaginatedResultForClient = ({ result }) => {
27
+ return {
28
+ results: result.page,
29
+ status: "Exhausted",
30
+ isLoading: false,
31
+ };
32
+ };
@@ -0,0 +1,3 @@
1
+ export declare const revalidateCache: ({ tag }: {
2
+ tag: string;
3
+ }) => Promise<void>;
@@ -0,0 +1,6 @@
1
+ "use server";
2
+ import { revalidateTag } from "next/cache";
3
+ import { defaultCacheProfile } from "../utils/cache-profile";
4
+ export const revalidateCache = async ({ tag }) => {
5
+ revalidateTag(tag, defaultCacheProfile);
6
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./fns/preload-query";
2
+ export * from "./fns/build-preloader";
@@ -0,0 +1,2 @@
1
+ export * from "./fns/preload-query";
2
+ export * from "./fns/build-preloader";
@@ -0,0 +1,8 @@
1
+ import { ArgsAndOptions } from "convex/server";
2
+ import { PQ_Query } from "../../../core/types/types/paginated-query";
3
+ import { Q_Query } from "../../../core/types/types/query";
4
+ import { NextjsOptions } from "convex/nextjs";
5
+ export type Q_ArgsPreloaded<Q extends Q_Query> = ArgsAndOptions<Q, NextjsOptions>[0];
6
+ export type PQ_ArgsPreloaded<Q extends PQ_Query> = ArgsAndOptions<Q, NextjsOptions>[0];
7
+ export type Q_OptionsPreloaded<Q extends Q_Query> = ArgsAndOptions<Q, NextjsOptions>[1];
8
+ export type PQ_OptionsPreloaded<Q extends PQ_Query> = ArgsAndOptions<Q, NextjsOptions>[1];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare const defaultCacheProfile = "default";
@@ -0,0 +1 @@
1
+ export const defaultCacheProfile = "default";
@@ -0,0 +1,8 @@
1
+ import { T_UseCachedPaginatedQueryClient } from "../../../core/client-cache/queries/paginated-query";
2
+ import { Q_Query, PQ_Query } from "../../../core/types";
3
+ import { T_UseCachedQueryClient } from "../../../core/client-cache/queries/query";
4
+ type T_UseQuery<Q extends Q_Query> = Omit<T_UseCachedQueryClient<Q>, "schemaMap" | "useLocalDb">;
5
+ type T_UsePaginatedQuery<Q extends PQ_Query> = Omit<T_UseCachedPaginatedQueryClient<Q>, "schemaMap" | "useLocalDb">;
6
+ export declare const useCachedQueryClient: <Q extends Q_Query>({ query, args }: T_UseQuery<Q>) => import("../../../core/types").Q_Result<Q> | undefined;
7
+ export declare const useCachedPaginatedQueryClient: <Q extends PQ_Query>({ query, args, options }: T_UsePaginatedQuery<Q>) => import("../../../core/types").PQ_Result<Q>;
8
+ export {};
@@ -0,0 +1,12 @@
1
+ import { _useCachedPaginatedQueryClient } from "../../../core/client-cache/queries/paginated-query";
2
+ import { _useCachedQueryClient } from "../../../core/client-cache/queries/query";
3
+ import { useConvexProvider } from "../provider/provider";
4
+ import { useLocalDb as useLocalDbDefault } from "@bb-labs/local-db";
5
+ export const useCachedQueryClient = ({ query, args }) => {
6
+ const { schemaMap, useLocalDb } = useConvexProvider();
7
+ return _useCachedQueryClient({ query, args, schemaMap, useLocalDb: useLocalDb ?? useLocalDbDefault });
8
+ };
9
+ export const useCachedPaginatedQueryClient = ({ query, args, options }) => {
10
+ const { schemaMap, useLocalDb } = useConvexProvider();
11
+ return _useCachedPaginatedQueryClient({ query, args, options, schemaMap, useLocalDb: (useLocalDb ?? useLocalDbDefault) });
12
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./hooks/hooks";
2
+ export * from "./provider/provider";
@@ -0,0 +1,2 @@
1
+ export * from "./hooks/hooks";
2
+ export * from "./provider/provider";
@@ -0,0 +1,13 @@
1
+ import type { T_SchemaMap } from "../../../types/schema-map";
2
+ import { useLocalDb as useLocalDbDefault } from "@bb-labs/local-db";
3
+ type T_ConvexCacheProvider = {
4
+ children: React.ReactNode;
5
+ schemaMap: T_SchemaMap;
6
+ useLocalDb?: typeof useLocalDbDefault<unknown>;
7
+ };
8
+ export declare const ConvexCacheProvider: ({ children, schemaMap, useLocalDb }: T_ConvexCacheProvider) => import("react/jsx-runtime").JSX.Element;
9
+ export declare const useConvexProvider: () => {
10
+ schemaMap: T_SchemaMap;
11
+ useLocalDb: typeof useLocalDbDefault<unknown> | undefined;
12
+ };
13
+ export {};
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext } from "react";
3
+ const convexCacheContext = createContext({
4
+ schemaMap: undefined,
5
+ useLocalDb: undefined,
6
+ });
7
+ export const ConvexCacheProvider = ({ children, schemaMap, useLocalDb }) => {
8
+ return _jsx(convexCacheContext.Provider, { value: { schemaMap, useLocalDb }, children: children });
9
+ };
10
+ export const useConvexProvider = () => {
11
+ const context = useContext(convexCacheContext);
12
+ if (!context || !context.schemaMap) {
13
+ throw new Error("Application must be wrapped in a <ConvexCacheProvider />");
14
+ }
15
+ return { schemaMap: context.schemaMap, useLocalDb: context.useLocalDb };
16
+ };
@@ -0,0 +1,10 @@
1
+ import { useLocalDb as useLocalDbDefault } from "@bb-labs/local-db";
2
+ import { z } from "zod";
3
+ type T_UseClientCache<T> = {
4
+ storageKey: string;
5
+ raw: T | undefined;
6
+ schema: z.ZodSchema<T>;
7
+ useLocalDb: typeof useLocalDbDefault<T>;
8
+ };
9
+ export declare const useClientCache: <T>({ storageKey, raw, schema, useLocalDb }: T_UseClientCache<T>) => T | null | undefined;
10
+ export {};
@@ -0,0 +1,16 @@
1
+ import { useEffect } from "react";
2
+ import deepEqual from "fast-deep-equal";
3
+ export const useClientCache = ({ storageKey, raw, schema, useLocalDb }) => {
4
+ const { value: stored, setValue: setStored } = useLocalDb({
5
+ key: storageKey,
6
+ schema,
7
+ initialValue: undefined,
8
+ dbName: "convex-cache",
9
+ storeName: "local-store",
10
+ });
11
+ useEffect(() => {
12
+ if (raw !== undefined && !deepEqual(raw, stored))
13
+ setStored(raw);
14
+ }, [raw, stored, setStored]);
15
+ return stored;
16
+ };
@@ -0,0 +1,7 @@
1
+ type T_UseQueryKey = {
2
+ queryName: string;
3
+ args: unknown;
4
+ kind: "query" | "paginated";
5
+ };
6
+ export declare const useQueryKey: ({ queryName, args, kind }: T_UseQueryKey) => string;
7
+ export {};
@@ -0,0 +1,5 @@
1
+ import { useMemo } from "react";
2
+ import { makeQueryKey } from "../../../helpers/utils/query-key";
3
+ export const useQueryKey = ({ queryName, args, kind }) => {
4
+ return useMemo(() => makeQueryKey({ queryName, args, kind }).key, [queryName, args, kind]);
5
+ };
@@ -0,0 +1,13 @@
1
+ import { PQ_Query, PQ_Args, PQ_Result, PQ_CachedResult } from "../../types/types/paginated-query";
2
+ import type { T_SchemaMap } from "../../../types/schema-map";
3
+ import { useLocalDb as useLocalDbDefault } from "@bb-labs/local-db";
4
+ export type T_UseCachedPaginatedQueryClient<Q extends PQ_Query> = {
5
+ query: Q;
6
+ args: PQ_Args<Q>;
7
+ options: {
8
+ initialNumItems: number;
9
+ };
10
+ schemaMap: T_SchemaMap;
11
+ useLocalDb: typeof useLocalDbDefault<PQ_CachedResult<Q>>;
12
+ };
13
+ export declare const _useCachedPaginatedQueryClient: <Q extends PQ_Query>({ query, args, options, schemaMap, useLocalDb }: T_UseCachedPaginatedQueryClient<Q>) => PQ_Result<Q>;
@@ -0,0 +1,37 @@
1
+ import { usePaginatedQuery } from "convex/react";
2
+ import { useQueryKey } from "../helpers/hooks/use-query-key";
3
+ import { useClientCache } from "../helpers/hooks/use-client-cache";
4
+ import { getFunctionName } from "convex/server";
5
+ import { useMemo } from "react";
6
+ import { fetchSchemaFromMap } from "../../helpers/utils/fetch-schema-from-map";
7
+ export const _useCachedPaginatedQueryClient = ({ query, args, options, schemaMap, useLocalDb }) => {
8
+ const queryName = useMemo(() => getFunctionName(query), [query]);
9
+ const schema = useMemo(() => fetchSchemaFromMap({
10
+ queryName,
11
+ schemaMap,
12
+ type: "paginated",
13
+ }), [queryName, schemaMap]);
14
+ const raw = usePaginatedQuery(query, args, options);
15
+ const storageKey = useQueryKey({ queryName, args, kind: "paginated" });
16
+ const cacheInput = raw.status === "LoadingFirstPage"
17
+ ? undefined
18
+ : {
19
+ results: raw.results,
20
+ status: raw.status,
21
+ isLoading: raw.isLoading,
22
+ };
23
+ const cached = useClientCache({
24
+ storageKey,
25
+ raw: cacheInput,
26
+ schema,
27
+ useLocalDb,
28
+ });
29
+ if (!cached)
30
+ return raw;
31
+ return {
32
+ ...raw,
33
+ results: cached.results,
34
+ status: cached.status,
35
+ isLoading: cached.isLoading,
36
+ };
37
+ };
@@ -0,0 +1,10 @@
1
+ import { Q_Query, Q_Args, Q_Result } from "../../types/types/query";
2
+ import type { T_SchemaMap } from "../../../types/schema-map";
3
+ import { useLocalDb as useLocalDbDefault } from "@bb-labs/local-db";
4
+ export type T_UseCachedQueryClient<Q extends Q_Query> = {
5
+ query: Q;
6
+ args: Q_Args<Q>;
7
+ schemaMap: T_SchemaMap;
8
+ useLocalDb: typeof useLocalDbDefault<Q_Result<Q>>;
9
+ };
10
+ export declare const _useCachedQueryClient: <Q extends Q_Query>({ query, args, schemaMap, useLocalDb }: T_UseCachedQueryClient<Q>) => Q_Result<Q> | undefined;
@@ -0,0 +1,23 @@
1
+ import { useQuery } from "convex/react";
2
+ import { getFunctionName } from "convex/server";
3
+ import { useQueryKey } from "../helpers/hooks/use-query-key";
4
+ import { useClientCache } from "../helpers/hooks/use-client-cache";
5
+ import { useMemo } from "react";
6
+ import { fetchSchemaFromMap } from "../../helpers/utils/fetch-schema-from-map";
7
+ export const _useCachedQueryClient = ({ query, args, schemaMap, useLocalDb }) => {
8
+ const queryName = useMemo(() => getFunctionName(query), [query]);
9
+ const schema = useMemo(() => fetchSchemaFromMap({
10
+ queryName,
11
+ schemaMap,
12
+ type: "query",
13
+ }), [queryName, schemaMap]);
14
+ const raw = useQuery(query, args);
15
+ const storageKey = useQueryKey({ queryName, args, kind: "query" });
16
+ const cached = useClientCache({
17
+ storageKey,
18
+ raw,
19
+ schema,
20
+ useLocalDb,
21
+ });
22
+ return (cached ?? raw);
23
+ };
@@ -0,0 +1,13 @@
1
+ import { z, type ZodType } from "zod";
2
+ import type { PaginationResult } from "convex/server";
3
+ import type { UsePaginatedQueryResult } from "convex/react";
4
+ type T_ServerSchema<T> = PaginationResult<T>;
5
+ type CacheableUsePaginated<T> = Pick<UsePaginatedQueryResult<T>, "results" | "status" | "isLoading">;
6
+ /**
7
+ * Convert a Zod schema for `PaginationResult<T>` (server) into a Zod schema
8
+ * for the cacheable subset of `UsePaginatedQueryResult<T>` (client).
9
+ *
10
+ * `Item` is inferred as: z.output<ServerSchema>["page"][number]
11
+ */
12
+ export declare const convertPaginatedSchemaForClient: <ServerSchema extends ZodType<T_ServerSchema<unknown>>, Item = z.output<ServerSchema>["page"][number]>(schema: ServerSchema) => ZodType<CacheableUsePaginated<Item>>;
13
+ export {};
@@ -0,0 +1,45 @@
1
+ import { z, ZodArray, ZodObject } from "zod";
2
+ /**
3
+ * Convert a Zod schema for `PaginationResult<T>` (server) into a Zod schema
4
+ * for the cacheable subset of `UsePaginatedQueryResult<T>` (client).
5
+ *
6
+ * `Item` is inferred as: z.output<ServerSchema>["page"][number]
7
+ */
8
+ export const convertPaginatedSchemaForClient = (schema) => {
9
+ let itemSchema;
10
+ // Try to extract the item schema from the server schema's `page` field.
11
+ if (schema instanceof ZodObject) {
12
+ const objShape = schema.shape;
13
+ if (objShape && objShape.page instanceof ZodArray) {
14
+ itemSchema = objShape.page.element;
15
+ }
16
+ else {
17
+ throw new Error("[convertPaginatedSchemaForClient] Schema object has no `page` array;");
18
+ }
19
+ }
20
+ else {
21
+ throw new Error("[convertPaginatedSchemaForClient] Schema is not a ZodObject;");
22
+ }
23
+ // Base part shared by all cacheable paginated variants (no loadMore)
24
+ const base = z.object({
25
+ results: z.array(itemSchema),
26
+ });
27
+ const loadingFirstPage = base.extend({
28
+ status: z.literal("LoadingFirstPage"),
29
+ isLoading: z.literal(true),
30
+ });
31
+ const canLoadMore = base.extend({
32
+ status: z.literal("CanLoadMore"),
33
+ isLoading: z.literal(false),
34
+ });
35
+ const loadingMore = base.extend({
36
+ status: z.literal("LoadingMore"),
37
+ isLoading: z.literal(true),
38
+ });
39
+ const exhausted = base.extend({
40
+ status: z.literal("Exhausted"),
41
+ isLoading: z.literal(false),
42
+ });
43
+ const clientSchema = z.union([loadingFirstPage, canLoadMore, loadingMore, exhausted]);
44
+ return clientSchema;
45
+ };
@@ -0,0 +1,21 @@
1
+ import type { ZodType } from "zod";
2
+ import type { PaginationResult } from "convex/server";
3
+ import type { UsePaginatedQueryResult } from "convex/react";
4
+ import type { T_SchemaMap } from "../../../types/schema-map";
5
+ import type { ValidatorJSON } from "convex/values";
6
+ type OutputOf<Map, Key extends keyof Map> = Map[Key] extends {
7
+ returns: ValidatorJSON;
8
+ } ? ValidatorJSON : unknown;
9
+ type PaginatedItem<Map, Key extends keyof Map> = OutputOf<Map, Key> extends PaginationResult<infer Item> ? Item : never;
10
+ type CacheableUsePaginated<T> = Pick<UsePaginatedQueryResult<T>, "results" | "status" | "isLoading">;
11
+ export declare function fetchSchemaFromMap<Map extends T_SchemaMap, Key extends keyof Map>(params: {
12
+ queryName: Key;
13
+ schemaMap: Map;
14
+ type: "query";
15
+ }): ZodType<OutputOf<Map, Key>>;
16
+ export declare function fetchSchemaFromMap<Map extends T_SchemaMap, Key extends keyof Map>(params: {
17
+ queryName: Key;
18
+ schemaMap: Map;
19
+ type: "paginated";
20
+ }): ZodType<CacheableUsePaginated<PaginatedItem<Map, Key>>>;
21
+ export {};
@@ -0,0 +1,16 @@
1
+ import { convertPaginatedSchemaForClient } from "./convert-paginated-schema";
2
+ import { validatorFromJSON } from "../../../../validation";
3
+ import { convexToZod } from "convex-helpers/server/zod4";
4
+ export function fetchSchemaFromMap({ queryName, schemaMap, type }) {
5
+ const entry = schemaMap[queryName];
6
+ if (!entry || !entry.returns) {
7
+ throw new Error(`Schema not found for function ${String(queryName)}`);
8
+ }
9
+ const validator = validatorFromJSON(entry.returns);
10
+ const zodSchema = convexToZod(validator);
11
+ if (type === "query") {
12
+ return zodSchema;
13
+ }
14
+ const paginatedSchema = convertPaginatedSchemaForClient(zodSchema);
15
+ return paginatedSchema;
16
+ }
@@ -0,0 +1,10 @@
1
+ type T_MakeQueryKey = {
2
+ queryName: string;
3
+ args: unknown;
4
+ kind: "query" | "paginated";
5
+ };
6
+ export declare const makeQueryKey: ({ queryName, args, kind }: T_MakeQueryKey) => {
7
+ key: string;
8
+ tag: string;
9
+ };
10
+ export {};
@@ -0,0 +1,16 @@
1
+ import superjson from "superjson";
2
+ import { deepSort } from "@bb-labs/deep-sort";
3
+ import crypto from "crypto";
4
+ export const makeQueryKey = ({ queryName, args, kind }) => {
5
+ const ns = kind === "paginated" ? "pq" : "q";
6
+ const sortedArgs = deepSort(args);
7
+ const argKey = superjson.stringify(sortedArgs);
8
+ const queryKey = `${ns}:${queryName}:${argKey}`;
9
+ return {
10
+ key: queryKey,
11
+ tag: hashKey(queryKey),
12
+ };
13
+ };
14
+ const hashKey = (key) => {
15
+ return crypto.createHash("sha256").update(key).digest("hex").slice(0, 16);
16
+ };
@@ -0,0 +1,15 @@
1
+ import { PQ_Query, PQ_Args, PQ_Result, PQ_CachedResult } from "../../types/types/paginated-query";
2
+ import { PQ_ArgsPreloaded } from "../../../adapters/next/types/preloaded";
3
+ export type T_UseCachedPaginatedQueryServer<Q extends PQ_Query> = {
4
+ query: Q;
5
+ args: PQ_Args<Q>;
6
+ options: {
7
+ initialNumItems: number;
8
+ };
9
+ preloadedData: PQ_CachedResult<Q> | undefined;
10
+ revalidateCache: ({ query, args }: {
11
+ query: Q;
12
+ args: PQ_ArgsPreloaded<Q>;
13
+ }) => void;
14
+ };
15
+ export declare const _useCachedPaginatedQueryServer: <Q extends PQ_Query>({ query, args, options, preloadedData, revalidateCache }: T_UseCachedPaginatedQueryServer<Q>) => PQ_Result<Q>;
@@ -0,0 +1,16 @@
1
+ import { usePaginatedQuery } from "convex/react";
2
+ import { useEffect } from "react";
3
+ import { isDeepEqual } from "@bb-labs/deep-equal";
4
+ export const _useCachedPaginatedQueryServer = ({ query, args, options, preloadedData, revalidateCache }) => {
5
+ const raw = usePaginatedQuery(query, args, options);
6
+ useEffect(() => {
7
+ if (args === "skip")
8
+ return;
9
+ if (raw.isLoading)
10
+ return;
11
+ if (isDeepEqual(preloadedData, raw))
12
+ return;
13
+ revalidateCache({ query, args: { ...args, paginationOpts: { numItems: options.initialNumItems, cursor: null } } });
14
+ }, [raw]);
15
+ return raw.status == "LoadingFirstPage" ? { ...(preloadedData ?? { results: [], status: "LoadingFirstPage", isLoading: false }), loadMore: () => { } } : raw;
16
+ };
@@ -0,0 +1,12 @@
1
+ import { Q_Query, Q_Args, Q_Result } from "../../types/types/query";
2
+ import { Q_ArgsPreloaded } from "../../../adapters/next/types/preloaded";
3
+ export type T_UseCachedQueryServer<Q extends Q_Query> = {
4
+ query: Q;
5
+ args: Q_Args<Q>;
6
+ preloadedData: Q_Result<Q> | undefined;
7
+ revalidateCache: ({ query, args }: {
8
+ query: Q;
9
+ args: Q_ArgsPreloaded<Q>;
10
+ }) => void;
11
+ };
12
+ export declare const _useCachedQueryServer: <Q extends Q_Query>({ query, args, preloadedData, revalidateCache }: T_UseCachedQueryServer<Q>) => Q_Result<Q> | undefined;
@@ -0,0 +1,16 @@
1
+ import { useQuery } from "convex/react";
2
+ import { useEffect } from "react";
3
+ import { isDeepEqual } from "@bb-labs/deep-equal";
4
+ export const _useCachedQueryServer = ({ query, args, preloadedData, revalidateCache }) => {
5
+ const raw = useQuery(query, args);
6
+ useEffect(() => {
7
+ if (args === "skip")
8
+ return;
9
+ if (!raw)
10
+ return;
11
+ if (isDeepEqual(preloadedData, raw))
12
+ return;
13
+ revalidateCache({ query, args });
14
+ }, [raw]);
15
+ return raw ?? preloadedData;
16
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./types/query";
2
+ export * from "./types/paginated-query";
@@ -0,0 +1,2 @@
1
+ export * from "./types/query";
2
+ export * from "./types/paginated-query";
@@ -0,0 +1,8 @@
1
+ import { ConvexHttpClient } from "convex/browser";
2
+ import { PaginatedQueryReference, PaginatedQueryArgs, UsePaginatedQueryResult, PaginatedQueryItem } from "convex/react";
3
+ export type PQ_Query = PaginatedQueryReference;
4
+ export type PQ_Args<Q extends PQ_Query> = PaginatedQueryArgs<Q> | "skip";
5
+ export type PQ_Result<Q extends PQ_Query> = UsePaginatedQueryResult<PaginatedQueryItem<Q>>;
6
+ export type PQ_Item<Q extends PQ_Query> = PaginatedQueryItem<Q>;
7
+ export type PQ_CachedResult<Q extends PQ_Query> = Pick<UsePaginatedQueryResult<PQ_Item<Q>>, "results" | "status" | "isLoading">;
8
+ export declare const client: ConvexHttpClient;
@@ -0,0 +1,2 @@
1
+ import { ConvexHttpClient } from "convex/browser";
2
+ export const client = new ConvexHttpClient(process.env["NEXT_PUBLIC_CONVEX_URL"] ?? "");
@@ -0,0 +1,5 @@
1
+ import { OptionalRestArgsOrSkip } from "convex/react";
2
+ import { FunctionReference, FunctionReturnType } from "convex/server";
3
+ export type Q_Query = FunctionReference<"query">;
4
+ export type Q_Args<Q extends Q_Query> = OptionalRestArgsOrSkip<Q>[0];
5
+ export type Q_Result<Q extends Q_Query> = FunctionReturnType<Q>;
@@ -0,0 +1 @@
1
+ export {};