@emeryld/rrroutes-client 1.1.2 → 1.1.3

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/index.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  "use strict";
2
+ "use client";
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/routesV3.client.fetch.ts","../src/routesV3.client.index.ts"],"sourcesContent":["export * from './routesV3.client.types';\nexport * from './routesV3.client.fetch';\nexport * from './routesV3.client.index';\n","// routesV3.client.fetch.ts\n\nimport { Fetcher, FetchInput } from './routesV3.client.types';\n\nconst logger = {\n debug: (...args: any[]) => {\n // Uncomment to enable debug logging\n console.log('[routesV3.client.fetch]', ...args);\n },\n};\n\n/**\n * Default fetch implementation used by the route client helper.\n * @param req Normalized request information (URL, method, body, headers).\n * @returns Parsed JSON (or text fallback) from the server response.\n */\nexport const defaultFetcher: Fetcher = async <T>(req: FetchInput): Promise<T> => {\n const headers: Record<string, string> = { ...(req.headers ?? {}) };\n const isFormData = typeof FormData !== 'undefined' && req.body instanceof FormData;\n if (!isFormData) {\n headers['Content-Type'] ||= 'application/json';\n headers['Accept'] ||= 'application/json';\n }\n\n logger.debug('fetch', req.method, req.url, isFormData ? 'FormData' : req.body);\n const res = await fetch(req.url, {\n method: req.method,\n headers,\n body: isFormData ? (req.body as any) : req.body == null ? undefined : JSON.stringify(req.body),\n });\n\n const text = await res.text();\n if (!res.ok) {\n const snippet = text.slice(0, 400);\n throw new Error(`[${res.status}] ${res.statusText} — ${snippet}`);\n }\n\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n};\n","// routesV3.client.ts\nimport {\n keepPreviousData,\n useInfiniteQuery,\n useMutation,\n useQuery,\n type InfiniteData,\n type QueryKey,\n} from '@tanstack/react-query';\nimport type { ZodType } from 'zod';\nimport {\n HttpMethod,\n buildCacheKey,\n compilePath,\n} from '@emeryld/rrroutes-contract';\nimport type {\n AnyLeaf,\n InferBody,\n InferOutput,\n InferParams,\n InferQuery,\n} from '@emeryld/rrroutes-contract';\nimport { defaultFetcher } from './routesV3.client.fetch';\nimport type {\n ArgsFor,\n ArgsTuple,\n BuiltForLeaf,\n BuiltInfinite,\n BuiltMutation,\n BuiltQuery,\n Cursor,\n DataShape,\n InfiniteBuildOptionsFor,\n MutationBuildOptionsFor,\n QueryBuildOptionsFor,\n RouteClient,\n RouteClientOptions,\n Updater,\n} from './routesV3.client.types';\n\n// -------------------------------------------------------------------------------------\n// Tiny helpers\n// -------------------------------------------------------------------------------------\n/**\n * Convert an HTTP method to uppercase (as expected by fetch).\n * @param m Lowercase HTTP method.\n * @returns Uppercase method string.\n */\nconst toUpper = (m: HttpMethod): Uppercase<HttpMethod> => m.toUpperCase() as Uppercase<HttpMethod>;\n\n/**\n * Parse the given value with the supplied schema (if present).\n * @param value Raw value to validate.\n * @param schema Optional Zod schema used for validation/coercion.\n * @returns The validated or original value.\n */\nfunction zParse<T>(value: unknown, schema?: ZodType): T {\n return schema ? (schema.parse(value) as T) : (value as T);\n}\n\n/**\n * Serialize a query object into a search string.\n * @param query Query params object (possibly undefined).\n * @returns Query string prefixed with `?`, or empty string.\n */\nfunction toSearchString(query: Record<string, unknown> | undefined) {\n if (!query) return '';\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v == null) continue;\n if (Array.isArray(v)) {\n v.forEach((x) => {\n if (x == null) return;\n if (typeof x === 'object') {\n params.append(k, JSON.stringify(x));\n } else {\n params.append(k, String(x));\n }\n });\n continue;\n }\n if (typeof v === 'object') {\n params.set(k, JSON.stringify(v));\n continue;\n }\n params.set(k, String(v));\n }\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\n/**\n * Remove the given key from an object (used to drop cursors from cache keys).\n * @param obj Source object.\n * @param key Property name to omit.\n * @returns Copy of the object without the specified key.\n */\nfunction stripKey<Q extends Record<string, unknown> | undefined>(obj: Q, key: string): Q {\n if (!obj) return obj;\n const { [key]: _omit, ...rest } = obj as any;\n return rest as Q;\n}\n\n/**\n * Default cursor extractor used by infinite queries.\n * @param p Page result returned from the server.\n * @returns Next cursor string, if present.\n */\nconst defaultGetNextCursor = (p: unknown): Cursor =>\n p && typeof p === 'object' && 'nextCursor' in p ? (p as any).nextCursor : undefined;\n\n// Split the variadic tuple at runtime\n/**\n * Extract the optional argument object from a variadic tuple.\n * @param args Tuple passed to a built endpoint helper.\n * @returns The argument object if present.\n */\nfunction extractArgs<L extends AnyLeaf>(args: ArgsTuple<L>): ArgsFor<L> | undefined {\n // At runtime ArgsTuple<L> is either [] or [obj]; we just pick the first if present.\n return (args as unknown as any[])[0] as any;\n}\n\n/**\n * Normalize params and query values, then construct a request URL for the given leaf.\n * @param leaf Leaf describing the endpoint.\n * @param baseUrl Optional base URL prepended to the path.\n * @param params Route parameters supplied by the caller.\n * @param query Query parameters supplied by the caller.\n * @returns Object containing the composed URL and normalized query payload.\n */\nfunction buildUrl<L extends AnyLeaf>(\n leaf: L,\n baseUrl: string,\n params: InferParams<L> | undefined,\n query: InferQuery<L> | undefined,\n) {\n const normalizedParams = zParse<InferParams<L>>(params, leaf.cfg.paramsSchema);\n const normalizedQuery = zParse<InferQuery<L>>(query, leaf.cfg.querySchema);\n const path = compilePath<L['path']>(leaf.path, (normalizedParams ?? {}) as any);\n const url = `${baseUrl ?? ''}${path}${toSearchString(normalizedQuery as any)}`;\n return { url, normalizedQuery };\n}\n\n// -------------------------------------------------------------------------------------\n// Client factory\n// -------------------------------------------------------------------------------------\n/**\n * Construct typed React Query helpers backed by a routes-v3 registry leaf.\n * @param opts Route client configuration (query client, fetcher overrides, etc).\n * @returns Object that can build endpoint hooks/mutations from leaves.\n */\nexport function createRouteClient(opts: RouteClientOptions): RouteClient {\n const queryClient = opts.queryClient;\n const fetcher = opts.fetcher ?? defaultFetcher;\n const baseUrl = opts.baseUrl;\n const cursorParam = opts.cursorParam ?? 'cursor';\n const getNextCursor = opts.getNextCursor ?? defaultGetNextCursor;\n\n /**\n * Invalidate a set of queries sharing the given prefix.\n * @param prefix Key parts shared by matching endpoints.\n * @param exact When true, invalidate only exact key matches.\n */\n async function invalidate(prefix: string[], exact = false) {\n await queryClient.invalidateQueries({ queryKey: prefix as unknown as QueryKey, exact });\n }\n\n /**\n * Build the client surface for a single leaf (query/mutation/infinite query).\n * @param leaf Leaf describing the endpoint.\n * @param rqOpts Optional React Query configuration.\n * @returns Helper object exposing key/invalidate/setData/useEndpoint.\n */\n function buildInternal<L extends AnyLeaf>(\n leaf: L,\n rqOpts?: QueryBuildOptionsFor<L> | InfiniteBuildOptionsFor<L> | MutationBuildOptionsFor<L>,\n ): BuiltForLeaf<L> {\n const isGet = leaf.method === 'get';\n const isFeed = !!leaf.cfg.feed;\n\n // --- key/invalidate/setData shared helpers ---\n const key = (...tuple: ArgsTuple<L>): QueryKey => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n const qForKey = isGet && isFeed ? stripKey(query as any, cursorParam) : (query as any);\n return buildCacheKey({ leaf, params: params as any, query: qForKey }) as unknown as QueryKey;\n };\n\n /**\n * Invalidate the React Query cache for this exact leaf invocation.\n * @param tuple Optional params/query tuple.\n */\n const invalidateExact = (...tuple: ArgsTuple<L>) =>\n queryClient.invalidateQueries({ queryKey: key(...tuple), exact: true });\n\n /**\n * Update the cache entries for this leaf.\n * @param args Tuple whose first entry is the updater and optional params/query follow.\n */\n const setData = (...args: [Updater<DataShape<L>>, ...rest: ArgsTuple<L>]) => {\n const [updater, ...rest] = args;\n const k = key(...(rest as ArgsTuple<L>));\n if (isGet && isFeed) {\n queryClient.setQueryData<InfiniteData<InferOutput<L>> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n } else {\n queryClient.setQueryData<InferOutput<L> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n }\n };\n\n // --- Infinite GET ---\n if (isGet && isFeed) {\n const useEndpoint: BuiltInfinite<L>['useEndpoint'] = (...tuple) => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n // Normalize once; we’ll inject the cursor per page below.\n const { normalizedQuery } = buildUrl(leaf, baseUrl, params, query);\n return useInfiniteQuery<\n InferOutput<L>, // TQueryFnData (per page)\n unknown, // TError\n InfiniteData<InferOutput<L>>, // TData (returned by the hook)\n QueryKey,\n Cursor\n >({\n ...(rqOpts as InfiniteBuildOptionsFor<L>),\n queryKey: key(...tuple),\n initialPageParam: undefined,\n getNextPageParam: (lastPage) => getNextCursor(lastPage),\n placeholderData: keepPreviousData,\n queryFn: async ({ pageParam }) => {\n const pageQuery = {\n ...(normalizedQuery as any),\n ...(pageParam ? { [cursorParam]: pageParam } : {}),\n };\n const { url } = buildUrl(leaf, baseUrl, params, pageQuery);\n const out = await fetcher<unknown>({ url, method: toUpper(leaf.method) });\n return zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n },\n // NOTE: TData is InfiniteData<T>, so we don't need a select here.\n });\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n // --- Plain GET ---\n if (isGet) {\n const useEndpoint: BuiltQuery<L>['useEndpoint'] = (...tuple) => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n const { url } = buildUrl(leaf, baseUrl, params, query);\n return useQuery<InferOutput<L>, unknown, InferOutput<L>, QueryKey>({\n ...(rqOpts as QueryBuildOptionsFor<L>),\n queryKey: key(...tuple),\n placeholderData: keepPreviousData,\n queryFn: async () => {\n const out = await fetcher<unknown>({ url, method: toUpper(leaf.method) });\n return zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n },\n });\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n // --- Mutation (POST/PUT/PATCH/DELETE) ---\n const useEndpoint: BuiltMutation<L>['useEndpoint'] = (...tuple) => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n const { url } = buildUrl(leaf, baseUrl, params, query);\n return useMutation<InferOutput<L>, unknown, InferBody<L>, unknown>({\n ...(rqOpts as MutationBuildOptionsFor<L>),\n mutationKey: key(...tuple),\n mutationFn: async (body: InferBody<L>) => {\n const normalizedBody = zParse<InferBody<L>>(body, leaf.cfg.bodySchema);\n\n // Optional: switch to FormData if your method declares bodyFiles\n const isMultipart = Array.isArray(leaf.cfg.bodyFiles) && leaf.cfg.bodyFiles.length > 0;\n const payload = isMultipart ? toFormData(normalizedBody as any) : normalizedBody;\n\n const out = await fetcher<unknown>({ url, method: toUpper(leaf.method), body: payload });\n return zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n },\n });\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n return {\n queryClient,\n invalidate,\n build: buildInternal as RouteClient['build'],\n };\n}\n\n// -------------------------------------------------------------------------------------\n// Multipart helper\n// -------------------------------------------------------------------------------------\nfunction toFormData(body: Record<string, any>): FormData {\n const fd = new FormData();\n for (const [k, v] of Object.entries(body ?? {})) {\n if (v == null) continue;\n if (Array.isArray(v)) v.forEach((item, i) => fd.append(`${k}[${i}]`, item as any));\n else fd.append(k, v as any);\n }\n return fd;\n}\n"],"mappings":"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","names":["useEndpoint"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/routesV3.client.fetch.ts","../src/routesV3.client.index.ts"],"sourcesContent":["/**\n * This package exports React Query hooks, so we mark it as client-only to keep Next.js happy.\n */\n'use client';\n\nexport * from './routesV3.client.types';\nexport * from './routesV3.client.fetch';\nexport * from './routesV3.client.index';\n","// routesV3.client.fetch.ts\n\nimport { Fetcher, FetchInput } from './routesV3.client.types';\n\nconst logger = {\n debug: (...args: any[]) => {\n // Uncomment to enable debug logging\n console.log('[routesV3.client.fetch]', ...args);\n },\n};\n\n/**\n * Default fetch implementation used by the route client helper.\n * @param req Normalized request information (URL, method, body, headers).\n * @returns Parsed JSON (or text fallback) from the server response.\n */\nexport const defaultFetcher: Fetcher = async <T>(req: FetchInput): Promise<T> => {\n const headers: Record<string, string> = { ...(req.headers ?? {}) };\n const isFormData = typeof FormData !== 'undefined' && req.body instanceof FormData;\n if (!isFormData) {\n headers['Content-Type'] ||= 'application/json';\n headers['Accept'] ||= 'application/json';\n }\n\n logger.debug('fetch', req.method, req.url, isFormData ? 'FormData' : req.body);\n const res = await fetch(req.url, {\n method: req.method,\n headers,\n body: isFormData ? (req.body as any) : req.body == null ? undefined : JSON.stringify(req.body),\n });\n\n const text = await res.text();\n if (!res.ok) {\n const snippet = text.slice(0, 400);\n throw new Error(`[${res.status}] ${res.statusText} — ${snippet}`);\n }\n\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n};\n","// routesV3.client.ts\nimport {\n keepPreviousData,\n useInfiniteQuery,\n useMutation,\n useQuery,\n type InfiniteData,\n type QueryKey,\n} from '@tanstack/react-query';\nimport type { ZodType } from 'zod';\nimport {\n HttpMethod,\n buildCacheKey,\n compilePath,\n} from '@emeryld/rrroutes-contract';\nimport type {\n AnyLeaf,\n InferBody,\n InferOutput,\n InferParams,\n InferQuery,\n} from '@emeryld/rrroutes-contract';\nimport { defaultFetcher } from './routesV3.client.fetch';\nimport type {\n ArgsFor,\n ArgsTuple,\n BuiltForLeaf,\n BuiltInfinite,\n BuiltMutation,\n BuiltQuery,\n Cursor,\n DataShape,\n InfiniteBuildOptionsFor,\n MutationBuildOptionsFor,\n QueryBuildOptionsFor,\n RouteClient,\n RouteClientOptions,\n Updater,\n} from './routesV3.client.types';\n\n// -------------------------------------------------------------------------------------\n// Tiny helpers\n// -------------------------------------------------------------------------------------\n/**\n * Convert an HTTP method to uppercase (as expected by fetch).\n * @param m Lowercase HTTP method.\n * @returns Uppercase method string.\n */\nconst toUpper = (m: HttpMethod): Uppercase<HttpMethod> => m.toUpperCase() as Uppercase<HttpMethod>;\n\n/**\n * Parse the given value with the supplied schema (if present).\n * @param value Raw value to validate.\n * @param schema Optional Zod schema used for validation/coercion.\n * @returns The validated or original value.\n */\nfunction zParse<T>(value: unknown, schema?: ZodType): T {\n return schema ? (schema.parse(value) as T) : (value as T);\n}\n\n/**\n * Serialize a query object into a search string.\n * @param query Query params object (possibly undefined).\n * @returns Query string prefixed with `?`, or empty string.\n */\nfunction toSearchString(query: Record<string, unknown> | undefined) {\n if (!query) return '';\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v == null) continue;\n if (Array.isArray(v)) {\n v.forEach((x) => {\n if (x == null) return;\n if (typeof x === 'object') {\n params.append(k, JSON.stringify(x));\n } else {\n params.append(k, String(x));\n }\n });\n continue;\n }\n if (typeof v === 'object') {\n params.set(k, JSON.stringify(v));\n continue;\n }\n params.set(k, String(v));\n }\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\n/**\n * Remove the given key from an object (used to drop cursors from cache keys).\n * @param obj Source object.\n * @param key Property name to omit.\n * @returns Copy of the object without the specified key.\n */\nfunction stripKey<Q extends Record<string, unknown> | undefined>(obj: Q, key: string): Q {\n if (!obj) return obj;\n const { [key]: _omit, ...rest } = obj as any;\n return rest as Q;\n}\n\n/**\n * Default cursor extractor used by infinite queries.\n * @param p Page result returned from the server.\n * @returns Next cursor string, if present.\n */\nconst defaultGetNextCursor = (p: unknown): Cursor =>\n p && typeof p === 'object' && 'nextCursor' in p ? (p as any).nextCursor : undefined;\n\n// Split the variadic tuple at runtime\n/**\n * Extract the optional argument object from a variadic tuple.\n * @param args Tuple passed to a built endpoint helper.\n * @returns The argument object if present.\n */\nfunction extractArgs<L extends AnyLeaf>(args: ArgsTuple<L>): ArgsFor<L> | undefined {\n // At runtime ArgsTuple<L> is either [] or [obj]; we just pick the first if present.\n return (args as unknown as any[])[0] as any;\n}\n\n/**\n * Normalize params and query values, then construct a request URL for the given leaf.\n * @param leaf Leaf describing the endpoint.\n * @param baseUrl Optional base URL prepended to the path.\n * @param params Route parameters supplied by the caller.\n * @param query Query parameters supplied by the caller.\n * @returns Object containing the composed URL and normalized query payload.\n */\nfunction buildUrl<L extends AnyLeaf>(\n leaf: L,\n baseUrl: string,\n params: InferParams<L> | undefined,\n query: InferQuery<L> | undefined,\n) {\n const normalizedParams = zParse<InferParams<L>>(params, leaf.cfg.paramsSchema);\n const normalizedQuery = zParse<InferQuery<L>>(query, leaf.cfg.querySchema);\n const path = compilePath<L['path']>(leaf.path, (normalizedParams ?? {}) as any);\n const url = `${baseUrl ?? ''}${path}${toSearchString(normalizedQuery as any)}`;\n return { url, normalizedQuery };\n}\n\n// -------------------------------------------------------------------------------------\n// Client factory\n// -------------------------------------------------------------------------------------\n/**\n * Construct typed React Query helpers backed by a routes-v3 registry leaf.\n * @param opts Route client configuration (query client, fetcher overrides, etc).\n * @returns Object that can build endpoint hooks/mutations from leaves.\n */\nexport function createRouteClient(opts: RouteClientOptions): RouteClient {\n const queryClient = opts.queryClient;\n const fetcher = opts.fetcher ?? defaultFetcher;\n const baseUrl = opts.baseUrl;\n const cursorParam = opts.cursorParam ?? 'cursor';\n const getNextCursor = opts.getNextCursor ?? defaultGetNextCursor;\n\n /**\n * Invalidate a set of queries sharing the given prefix.\n * @param prefix Key parts shared by matching endpoints.\n * @param exact When true, invalidate only exact key matches.\n */\n async function invalidate(prefix: string[], exact = false) {\n await queryClient.invalidateQueries({ queryKey: prefix as unknown as QueryKey, exact });\n }\n\n /**\n * Build the client surface for a single leaf (query/mutation/infinite query).\n * @param leaf Leaf describing the endpoint.\n * @param rqOpts Optional React Query configuration.\n * @returns Helper object exposing key/invalidate/setData/useEndpoint.\n */\n function buildInternal<L extends AnyLeaf>(\n leaf: L,\n rqOpts?: QueryBuildOptionsFor<L> | InfiniteBuildOptionsFor<L> | MutationBuildOptionsFor<L>,\n ): BuiltForLeaf<L> {\n const isGet = leaf.method === 'get';\n const isFeed = !!leaf.cfg.feed;\n\n // --- key/invalidate/setData shared helpers ---\n const key = (...tuple: ArgsTuple<L>): QueryKey => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n const qForKey = isGet && isFeed ? stripKey(query as any, cursorParam) : (query as any);\n return buildCacheKey({ leaf, params: params as any, query: qForKey }) as unknown as QueryKey;\n };\n\n /**\n * Invalidate the React Query cache for this exact leaf invocation.\n * @param tuple Optional params/query tuple.\n */\n const invalidateExact = (...tuple: ArgsTuple<L>) =>\n queryClient.invalidateQueries({ queryKey: key(...tuple), exact: true });\n\n /**\n * Update the cache entries for this leaf.\n * @param args Tuple whose first entry is the updater and optional params/query follow.\n */\n const setData = (...args: [Updater<DataShape<L>>, ...rest: ArgsTuple<L>]) => {\n const [updater, ...rest] = args;\n const k = key(...(rest as ArgsTuple<L>));\n if (isGet && isFeed) {\n queryClient.setQueryData<InfiniteData<InferOutput<L>> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n } else {\n queryClient.setQueryData<InferOutput<L> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n }\n };\n\n // --- Infinite GET ---\n if (isGet && isFeed) {\n const useEndpoint: BuiltInfinite<L>['useEndpoint'] = (...tuple) => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n // Normalize once; we’ll inject the cursor per page below.\n const { normalizedQuery } = buildUrl(leaf, baseUrl, params, query);\n return useInfiniteQuery<\n InferOutput<L>, // TQueryFnData (per page)\n unknown, // TError\n InfiniteData<InferOutput<L>>, // TData (returned by the hook)\n QueryKey,\n Cursor\n >({\n ...(rqOpts as InfiniteBuildOptionsFor<L>),\n queryKey: key(...tuple),\n initialPageParam: undefined,\n getNextPageParam: (lastPage) => getNextCursor(lastPage),\n placeholderData: keepPreviousData,\n queryFn: async ({ pageParam }) => {\n const pageQuery = {\n ...(normalizedQuery as any),\n ...(pageParam ? { [cursorParam]: pageParam } : {}),\n };\n const { url } = buildUrl(leaf, baseUrl, params, pageQuery);\n const out = await fetcher<unknown>({ url, method: toUpper(leaf.method) });\n return zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n },\n // NOTE: TData is InfiniteData<T>, so we don't need a select here.\n });\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n // --- Plain GET ---\n if (isGet) {\n const useEndpoint: BuiltQuery<L>['useEndpoint'] = (...tuple) => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n const { url } = buildUrl(leaf, baseUrl, params, query);\n return useQuery<InferOutput<L>, unknown, InferOutput<L>, QueryKey>({\n ...(rqOpts as QueryBuildOptionsFor<L>),\n queryKey: key(...tuple),\n placeholderData: keepPreviousData,\n queryFn: async () => {\n const out = await fetcher<unknown>({ url, method: toUpper(leaf.method) });\n return zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n },\n });\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n // --- Mutation (POST/PUT/PATCH/DELETE) ---\n const useEndpoint: BuiltMutation<L>['useEndpoint'] = (...tuple) => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n const { url } = buildUrl(leaf, baseUrl, params, query);\n return useMutation<InferOutput<L>, unknown, InferBody<L>, unknown>({\n ...(rqOpts as MutationBuildOptionsFor<L>),\n mutationKey: key(...tuple),\n mutationFn: async (body: InferBody<L>) => {\n const normalizedBody = zParse<InferBody<L>>(body, leaf.cfg.bodySchema);\n\n // Optional: switch to FormData if your method declares bodyFiles\n const isMultipart = Array.isArray(leaf.cfg.bodyFiles) && leaf.cfg.bodyFiles.length > 0;\n const payload = isMultipart ? toFormData(normalizedBody as any) : normalizedBody;\n\n const out = await fetcher<unknown>({ url, method: toUpper(leaf.method), body: payload });\n return zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n },\n });\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n return {\n queryClient,\n invalidate,\n build: buildInternal as RouteClient['build'],\n };\n}\n\n// -------------------------------------------------------------------------------------\n// Multipart helper\n// -------------------------------------------------------------------------------------\nfunction toFormData(body: Record<string, any>): FormData {\n const fd = new FormData();\n for (const [k, v] of Object.entries(body ?? {})) {\n if (v == null) continue;\n if (Array.isArray(v)) v.forEach((item, i) => fd.append(`${k}[${i}]`, item as any));\n else fd.append(k, v as any);\n }\n return fd;\n}\n"],"mappings":"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","names":["useEndpoint"]}
package/dist/index.js CHANGED
@@ -1,3 +1,5 @@
1
+ "use client";
2
+
1
3
  // src/routesV3.client.fetch.ts
2
4
  var logger = {
3
5
  debug: (...args) => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/routesV3.client.fetch.ts","../src/routesV3.client.index.ts"],"sourcesContent":["// routesV3.client.fetch.ts\n\nimport { Fetcher, FetchInput } from './routesV3.client.types';\n\nconst logger = {\n debug: (...args: any[]) => {\n // Uncomment to enable debug logging\n console.log('[routesV3.client.fetch]', ...args);\n },\n};\n\n/**\n * Default fetch implementation used by the route client helper.\n * @param req Normalized request information (URL, method, body, headers).\n * @returns Parsed JSON (or text fallback) from the server response.\n */\nexport const defaultFetcher: Fetcher = async <T>(req: FetchInput): Promise<T> => {\n const headers: Record<string, string> = { ...(req.headers ?? {}) };\n const isFormData = typeof FormData !== 'undefined' && req.body instanceof FormData;\n if (!isFormData) {\n headers['Content-Type'] ||= 'application/json';\n headers['Accept'] ||= 'application/json';\n }\n\n logger.debug('fetch', req.method, req.url, isFormData ? 'FormData' : req.body);\n const res = await fetch(req.url, {\n method: req.method,\n headers,\n body: isFormData ? (req.body as any) : req.body == null ? undefined : JSON.stringify(req.body),\n });\n\n const text = await res.text();\n if (!res.ok) {\n const snippet = text.slice(0, 400);\n throw new Error(`[${res.status}] ${res.statusText} — ${snippet}`);\n }\n\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n};\n","// routesV3.client.ts\nimport {\n keepPreviousData,\n useInfiniteQuery,\n useMutation,\n useQuery,\n type InfiniteData,\n type QueryKey,\n} from '@tanstack/react-query';\nimport type { ZodType } from 'zod';\nimport {\n HttpMethod,\n buildCacheKey,\n compilePath,\n} from '@emeryld/rrroutes-contract';\nimport type {\n AnyLeaf,\n InferBody,\n InferOutput,\n InferParams,\n InferQuery,\n} from '@emeryld/rrroutes-contract';\nimport { defaultFetcher } from './routesV3.client.fetch';\nimport type {\n ArgsFor,\n ArgsTuple,\n BuiltForLeaf,\n BuiltInfinite,\n BuiltMutation,\n BuiltQuery,\n Cursor,\n DataShape,\n InfiniteBuildOptionsFor,\n MutationBuildOptionsFor,\n QueryBuildOptionsFor,\n RouteClient,\n RouteClientOptions,\n Updater,\n} from './routesV3.client.types';\n\n// -------------------------------------------------------------------------------------\n// Tiny helpers\n// -------------------------------------------------------------------------------------\n/**\n * Convert an HTTP method to uppercase (as expected by fetch).\n * @param m Lowercase HTTP method.\n * @returns Uppercase method string.\n */\nconst toUpper = (m: HttpMethod): Uppercase<HttpMethod> => m.toUpperCase() as Uppercase<HttpMethod>;\n\n/**\n * Parse the given value with the supplied schema (if present).\n * @param value Raw value to validate.\n * @param schema Optional Zod schema used for validation/coercion.\n * @returns The validated or original value.\n */\nfunction zParse<T>(value: unknown, schema?: ZodType): T {\n return schema ? (schema.parse(value) as T) : (value as T);\n}\n\n/**\n * Serialize a query object into a search string.\n * @param query Query params object (possibly undefined).\n * @returns Query string prefixed with `?`, or empty string.\n */\nfunction toSearchString(query: Record<string, unknown> | undefined) {\n if (!query) return '';\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v == null) continue;\n if (Array.isArray(v)) {\n v.forEach((x) => {\n if (x == null) return;\n if (typeof x === 'object') {\n params.append(k, JSON.stringify(x));\n } else {\n params.append(k, String(x));\n }\n });\n continue;\n }\n if (typeof v === 'object') {\n params.set(k, JSON.stringify(v));\n continue;\n }\n params.set(k, String(v));\n }\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\n/**\n * Remove the given key from an object (used to drop cursors from cache keys).\n * @param obj Source object.\n * @param key Property name to omit.\n * @returns Copy of the object without the specified key.\n */\nfunction stripKey<Q extends Record<string, unknown> | undefined>(obj: Q, key: string): Q {\n if (!obj) return obj;\n const { [key]: _omit, ...rest } = obj as any;\n return rest as Q;\n}\n\n/**\n * Default cursor extractor used by infinite queries.\n * @param p Page result returned from the server.\n * @returns Next cursor string, if present.\n */\nconst defaultGetNextCursor = (p: unknown): Cursor =>\n p && typeof p === 'object' && 'nextCursor' in p ? (p as any).nextCursor : undefined;\n\n// Split the variadic tuple at runtime\n/**\n * Extract the optional argument object from a variadic tuple.\n * @param args Tuple passed to a built endpoint helper.\n * @returns The argument object if present.\n */\nfunction extractArgs<L extends AnyLeaf>(args: ArgsTuple<L>): ArgsFor<L> | undefined {\n // At runtime ArgsTuple<L> is either [] or [obj]; we just pick the first if present.\n return (args as unknown as any[])[0] as any;\n}\n\n/**\n * Normalize params and query values, then construct a request URL for the given leaf.\n * @param leaf Leaf describing the endpoint.\n * @param baseUrl Optional base URL prepended to the path.\n * @param params Route parameters supplied by the caller.\n * @param query Query parameters supplied by the caller.\n * @returns Object containing the composed URL and normalized query payload.\n */\nfunction buildUrl<L extends AnyLeaf>(\n leaf: L,\n baseUrl: string,\n params: InferParams<L> | undefined,\n query: InferQuery<L> | undefined,\n) {\n const normalizedParams = zParse<InferParams<L>>(params, leaf.cfg.paramsSchema);\n const normalizedQuery = zParse<InferQuery<L>>(query, leaf.cfg.querySchema);\n const path = compilePath<L['path']>(leaf.path, (normalizedParams ?? {}) as any);\n const url = `${baseUrl ?? ''}${path}${toSearchString(normalizedQuery as any)}`;\n return { url, normalizedQuery };\n}\n\n// -------------------------------------------------------------------------------------\n// Client factory\n// -------------------------------------------------------------------------------------\n/**\n * Construct typed React Query helpers backed by a routes-v3 registry leaf.\n * @param opts Route client configuration (query client, fetcher overrides, etc).\n * @returns Object that can build endpoint hooks/mutations from leaves.\n */\nexport function createRouteClient(opts: RouteClientOptions): RouteClient {\n const queryClient = opts.queryClient;\n const fetcher = opts.fetcher ?? defaultFetcher;\n const baseUrl = opts.baseUrl;\n const cursorParam = opts.cursorParam ?? 'cursor';\n const getNextCursor = opts.getNextCursor ?? defaultGetNextCursor;\n\n /**\n * Invalidate a set of queries sharing the given prefix.\n * @param prefix Key parts shared by matching endpoints.\n * @param exact When true, invalidate only exact key matches.\n */\n async function invalidate(prefix: string[], exact = false) {\n await queryClient.invalidateQueries({ queryKey: prefix as unknown as QueryKey, exact });\n }\n\n /**\n * Build the client surface for a single leaf (query/mutation/infinite query).\n * @param leaf Leaf describing the endpoint.\n * @param rqOpts Optional React Query configuration.\n * @returns Helper object exposing key/invalidate/setData/useEndpoint.\n */\n function buildInternal<L extends AnyLeaf>(\n leaf: L,\n rqOpts?: QueryBuildOptionsFor<L> | InfiniteBuildOptionsFor<L> | MutationBuildOptionsFor<L>,\n ): BuiltForLeaf<L> {\n const isGet = leaf.method === 'get';\n const isFeed = !!leaf.cfg.feed;\n\n // --- key/invalidate/setData shared helpers ---\n const key = (...tuple: ArgsTuple<L>): QueryKey => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n const qForKey = isGet && isFeed ? stripKey(query as any, cursorParam) : (query as any);\n return buildCacheKey({ leaf, params: params as any, query: qForKey }) as unknown as QueryKey;\n };\n\n /**\n * Invalidate the React Query cache for this exact leaf invocation.\n * @param tuple Optional params/query tuple.\n */\n const invalidateExact = (...tuple: ArgsTuple<L>) =>\n queryClient.invalidateQueries({ queryKey: key(...tuple), exact: true });\n\n /**\n * Update the cache entries for this leaf.\n * @param args Tuple whose first entry is the updater and optional params/query follow.\n */\n const setData = (...args: [Updater<DataShape<L>>, ...rest: ArgsTuple<L>]) => {\n const [updater, ...rest] = args;\n const k = key(...(rest as ArgsTuple<L>));\n if (isGet && isFeed) {\n queryClient.setQueryData<InfiniteData<InferOutput<L>> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n } else {\n queryClient.setQueryData<InferOutput<L> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n }\n };\n\n // --- Infinite GET ---\n if (isGet && isFeed) {\n const useEndpoint: BuiltInfinite<L>['useEndpoint'] = (...tuple) => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n // Normalize once; we’ll inject the cursor per page below.\n const { normalizedQuery } = buildUrl(leaf, baseUrl, params, query);\n return useInfiniteQuery<\n InferOutput<L>, // TQueryFnData (per page)\n unknown, // TError\n InfiniteData<InferOutput<L>>, // TData (returned by the hook)\n QueryKey,\n Cursor\n >({\n ...(rqOpts as InfiniteBuildOptionsFor<L>),\n queryKey: key(...tuple),\n initialPageParam: undefined,\n getNextPageParam: (lastPage) => getNextCursor(lastPage),\n placeholderData: keepPreviousData,\n queryFn: async ({ pageParam }) => {\n const pageQuery = {\n ...(normalizedQuery as any),\n ...(pageParam ? { [cursorParam]: pageParam } : {}),\n };\n const { url } = buildUrl(leaf, baseUrl, params, pageQuery);\n const out = await fetcher<unknown>({ url, method: toUpper(leaf.method) });\n return zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n },\n // NOTE: TData is InfiniteData<T>, so we don't need a select here.\n });\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n // --- Plain GET ---\n if (isGet) {\n const useEndpoint: BuiltQuery<L>['useEndpoint'] = (...tuple) => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n const { url } = buildUrl(leaf, baseUrl, params, query);\n return useQuery<InferOutput<L>, unknown, InferOutput<L>, QueryKey>({\n ...(rqOpts as QueryBuildOptionsFor<L>),\n queryKey: key(...tuple),\n placeholderData: keepPreviousData,\n queryFn: async () => {\n const out = await fetcher<unknown>({ url, method: toUpper(leaf.method) });\n return zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n },\n });\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n // --- Mutation (POST/PUT/PATCH/DELETE) ---\n const useEndpoint: BuiltMutation<L>['useEndpoint'] = (...tuple) => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n const { url } = buildUrl(leaf, baseUrl, params, query);\n return useMutation<InferOutput<L>, unknown, InferBody<L>, unknown>({\n ...(rqOpts as MutationBuildOptionsFor<L>),\n mutationKey: key(...tuple),\n mutationFn: async (body: InferBody<L>) => {\n const normalizedBody = zParse<InferBody<L>>(body, leaf.cfg.bodySchema);\n\n // Optional: switch to FormData if your method declares bodyFiles\n const isMultipart = Array.isArray(leaf.cfg.bodyFiles) && leaf.cfg.bodyFiles.length > 0;\n const payload = isMultipart ? toFormData(normalizedBody as any) : normalizedBody;\n\n const out = await fetcher<unknown>({ url, method: toUpper(leaf.method), body: payload });\n return zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n },\n });\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n return {\n queryClient,\n invalidate,\n build: buildInternal as RouteClient['build'],\n };\n}\n\n// -------------------------------------------------------------------------------------\n// Multipart helper\n// -------------------------------------------------------------------------------------\nfunction toFormData(body: Record<string, any>): FormData {\n const fd = new FormData();\n for (const [k, v] of Object.entries(body ?? {})) {\n if (v == null) continue;\n if (Array.isArray(v)) v.forEach((item, i) => fd.append(`${k}[${i}]`, item as any));\n else fd.append(k, v as any);\n }\n return fd;\n}\n"],"mappings":";AAIA,IAAM,SAAS;AAAA,EACb,OAAO,IAAI,SAAgB;AAEzB,YAAQ,IAAI,2BAA2B,GAAG,IAAI;AAAA,EAChD;AACF;AAOO,IAAM,iBAA0B,OAAU,QAAgC;AAC/E,QAAM,UAAkC,EAAE,GAAI,IAAI,WAAW,CAAC,EAAG;AACjE,QAAM,aAAa,OAAO,aAAa,eAAe,IAAI,gBAAgB;AAC1E,MAAI,CAAC,YAAY;AACf,0DAA4B;AAC5B,8CAAsB;AAAA,EACxB;AAEA,SAAO,MAAM,SAAS,IAAI,QAAQ,IAAI,KAAK,aAAa,aAAa,IAAI,IAAI;AAC7E,QAAM,MAAM,MAAM,MAAM,IAAI,KAAK;AAAA,IAC/B,QAAQ,IAAI;AAAA,IACZ;AAAA,IACA,MAAM,aAAc,IAAI,OAAe,IAAI,QAAQ,OAAO,SAAY,KAAK,UAAU,IAAI,IAAI;AAAA,EAC/F,CAAC;AAED,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,CAAC,IAAI,IAAI;AACX,UAAM,UAAU,KAAK,MAAM,GAAG,GAAG;AACjC,UAAM,IAAI,MAAM,IAAI,IAAI,MAAM,KAAK,IAAI,UAAU,WAAM,OAAO,EAAE;AAAA,EAClE;AAEA,MAAI;AACF,WAAO,KAAK,MAAM,IAAI;AAAA,EACxB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;ACzCA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;AAEP;AAAA,EAEE;AAAA,EACA;AAAA,OACK;AAkCP,IAAM,UAAU,CAAC,MAAyC,EAAE,YAAY;AAQxE,SAAS,OAAU,OAAgB,QAAqB;AACtD,SAAO,SAAU,OAAO,MAAM,KAAK,IAAW;AAChD;AAOA,SAAS,eAAe,OAA4C;AAClE,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,SAAS,IAAI,gBAAgB;AACnC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,KAAK,GAAG;AAC1C,QAAI,KAAK,KAAM;AACf,QAAI,MAAM,QAAQ,CAAC,GAAG;AACpB,QAAE,QAAQ,CAAC,MAAM;AACf,YAAI,KAAK,KAAM;AACf,YAAI,OAAO,MAAM,UAAU;AACzB,iBAAO,OAAO,GAAG,KAAK,UAAU,CAAC,CAAC;AAAA,QACpC,OAAO;AACL,iBAAO,OAAO,GAAG,OAAO,CAAC,CAAC;AAAA,QAC5B;AAAA,MACF,CAAC;AACD;AAAA,IACF;AACA,QAAI,OAAO,MAAM,UAAU;AACzB,aAAO,IAAI,GAAG,KAAK,UAAU,CAAC,CAAC;AAC/B;AAAA,IACF;AACA,WAAO,IAAI,GAAG,OAAO,CAAC,CAAC;AAAA,EACzB;AACA,QAAM,IAAI,OAAO,SAAS;AAC1B,SAAO,IAAI,IAAI,CAAC,KAAK;AACvB;AAQA,SAAS,SAAwD,KAAQ,KAAgB;AACvF,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,EAAE,CAAC,GAAG,GAAG,OAAO,GAAG,KAAK,IAAI;AAClC,SAAO;AACT;AAOA,IAAM,uBAAuB,CAAC,MAC5B,KAAK,OAAO,MAAM,YAAY,gBAAgB,IAAK,EAAU,aAAa;AAQ5E,SAAS,YAA+B,MAA4C;AAElF,SAAQ,KAA0B,CAAC;AACrC;AAUA,SAAS,SACP,MACA,SACA,QACA,OACA;AACA,QAAM,mBAAmB,OAAuB,QAAQ,KAAK,IAAI,YAAY;AAC7E,QAAM,kBAAkB,OAAsB,OAAO,KAAK,IAAI,WAAW;AACzE,QAAM,OAAO,YAAuB,KAAK,MAAO,oBAAoB,CAAC,CAAS;AAC9E,QAAM,MAAM,GAAG,WAAW,EAAE,GAAG,IAAI,GAAG,eAAe,eAAsB,CAAC;AAC5E,SAAO,EAAE,KAAK,gBAAgB;AAChC;AAUO,SAAS,kBAAkB,MAAuC;AACvE,QAAM,cAAc,KAAK;AACzB,QAAM,UAAU,KAAK,WAAW;AAChC,QAAM,UAAU,KAAK;AACrB,QAAM,cAAc,KAAK,eAAe;AACxC,QAAM,gBAAgB,KAAK,iBAAiB;AAO5C,iBAAe,WAAW,QAAkB,QAAQ,OAAO;AACzD,UAAM,YAAY,kBAAkB,EAAE,UAAU,QAA+B,MAAM,CAAC;AAAA,EACxF;AAQA,WAAS,cACP,MACA,QACiB;AACjB,UAAM,QAAQ,KAAK,WAAW;AAC9B,UAAM,SAAS,CAAC,CAAC,KAAK,IAAI;AAG1B,UAAM,MAAM,IAAI,UAAkC;AAChD,YAAM,IAAI,YAAe,KAAK;AAC9B,YAAM,SAAU,GAAW;AAC3B,YAAM,QAAS,GAAW;AAC1B,YAAM,UAAU,SAAS,SAAS,SAAS,OAAc,WAAW,IAAK;AACzE,aAAO,cAAc,EAAE,MAAM,QAAuB,OAAO,QAAQ,CAAC;AAAA,IACtE;AAMA,UAAM,kBAAkB,IAAI,UAC1B,YAAY,kBAAkB,EAAE,UAAU,IAAI,GAAG,KAAK,GAAG,OAAO,KAAK,CAAC;AAMxE,UAAM,UAAU,IAAI,SAAyD;AAC3E,YAAM,CAAC,SAAS,GAAG,IAAI,IAAI;AAC3B,YAAM,IAAI,IAAI,GAAI,IAAqB;AACvC,UAAI,SAAS,QAAQ;AACnB,oBAAY;AAAA,UAAuD;AAAA,UAAG,CAAC,SACrE,OAAO,YAAY,aAAc,QAAgB,IAAI,IAAK;AAAA,QAC5D;AAAA,MACF,OAAO;AACL,oBAAY;AAAA,UAAyC;AAAA,UAAG,CAAC,SACvD,OAAO,YAAY,aAAc,QAAgB,IAAI,IAAK;AAAA,QAC5D;AAAA,MACF;AAAA,IACF;AAGA,QAAI,SAAS,QAAQ;AACnB,YAAMA,eAA+C,IAAI,UAAU;AACjE,cAAM,IAAI,YAAe,KAAK;AAC9B,cAAM,SAAU,GAAW;AAC3B,cAAM,QAAS,GAAW;AAG1B,cAAM,EAAE,gBAAgB,IAAI,SAAS,MAAM,SAAS,QAAQ,KAAK;AACjE,eAAO,iBAML;AAAA,UACA,GAAI;AAAA,UACJ,UAAU,IAAI,GAAG,KAAK;AAAA,UACtB,kBAAkB;AAAA,UAClB,kBAAkB,CAAC,aAAa,cAAc,QAAQ;AAAA,UACtD,iBAAiB;AAAA,UACjB,SAAS,OAAO,EAAE,UAAU,MAAM;AAChC,kBAAM,YAAY;AAAA,cAChB,GAAI;AAAA,cACJ,GAAI,YAAY,EAAE,CAAC,WAAW,GAAG,UAAU,IAAI,CAAC;AAAA,YAClD;AACA,kBAAM,EAAE,IAAI,IAAI,SAAS,MAAM,SAAS,QAAQ,SAAS;AACzD,kBAAM,MAAM,MAAM,QAAiB,EAAE,KAAK,QAAQ,QAAQ,KAAK,MAAM,EAAE,CAAC;AACxE,mBAAO,OAAuB,KAAK,KAAK,IAAI,YAAY;AAAA,UAC1D;AAAA;AAAA,QAEF,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,QACL;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,QACA,aAAAA;AAAA,MACF;AAAA,IACF;AAGA,QAAI,OAAO;AACT,YAAMA,eAA4C,IAAI,UAAU;AAC9D,cAAM,IAAI,YAAe,KAAK;AAC9B,cAAM,SAAU,GAAW;AAC3B,cAAM,QAAS,GAAW;AAE1B,cAAM,EAAE,IAAI,IAAI,SAAS,MAAM,SAAS,QAAQ,KAAK;AACrD,eAAO,SAA4D;AAAA,UACjE,GAAI;AAAA,UACJ,UAAU,IAAI,GAAG,KAAK;AAAA,UACtB,iBAAiB;AAAA,UACjB,SAAS,YAAY;AACnB,kBAAM,MAAM,MAAM,QAAiB,EAAE,KAAK,QAAQ,QAAQ,KAAK,MAAM,EAAE,CAAC;AACxE,mBAAO,OAAuB,KAAK,KAAK,IAAI,YAAY;AAAA,UAC1D;AAAA,QACF,CAAC;AAAA,MACH;AAEA,aAAO;AAAA,QACL;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,QACA,aAAAA;AAAA,MACF;AAAA,IACF;AAGA,UAAM,cAA+C,IAAI,UAAU;AACjE,YAAM,IAAI,YAAe,KAAK;AAC9B,YAAM,SAAU,GAAW;AAC3B,YAAM,QAAS,GAAW;AAE1B,YAAM,EAAE,IAAI,IAAI,SAAS,MAAM,SAAS,QAAQ,KAAK;AACrD,aAAO,YAA4D;AAAA,QACjE,GAAI;AAAA,QACJ,aAAa,IAAI,GAAG,KAAK;AAAA,QACzB,YAAY,OAAO,SAAuB;AACxC,gBAAM,iBAAiB,OAAqB,MAAM,KAAK,IAAI,UAAU;AAGrE,gBAAM,cAAc,MAAM,QAAQ,KAAK,IAAI,SAAS,KAAK,KAAK,IAAI,UAAU,SAAS;AACrF,gBAAM,UAAU,cAAc,WAAW,cAAqB,IAAI;AAElE,gBAAM,MAAM,MAAM,QAAiB,EAAE,KAAK,QAAQ,QAAQ,KAAK,MAAM,GAAG,MAAM,QAAQ,CAAC;AACvF,iBAAO,OAAuB,KAAK,KAAK,IAAI,YAAY;AAAA,QAC1D;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,MACL;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,OAAO;AAAA,EACT;AACF;AAKA,SAAS,WAAW,MAAqC;AACvD,QAAM,KAAK,IAAI,SAAS;AACxB,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,QAAQ,CAAC,CAAC,GAAG;AAC/C,QAAI,KAAK,KAAM;AACf,QAAI,MAAM,QAAQ,CAAC,EAAG,GAAE,QAAQ,CAAC,MAAM,MAAM,GAAG,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,IAAW,CAAC;AAAA,QAC5E,IAAG,OAAO,GAAG,CAAQ;AAAA,EAC5B;AACA,SAAO;AACT;","names":["useEndpoint"]}
1
+ {"version":3,"sources":["../src/routesV3.client.fetch.ts","../src/routesV3.client.index.ts"],"sourcesContent":["// routesV3.client.fetch.ts\n\nimport { Fetcher, FetchInput } from './routesV3.client.types';\n\nconst logger = {\n debug: (...args: any[]) => {\n // Uncomment to enable debug logging\n console.log('[routesV3.client.fetch]', ...args);\n },\n};\n\n/**\n * Default fetch implementation used by the route client helper.\n * @param req Normalized request information (URL, method, body, headers).\n * @returns Parsed JSON (or text fallback) from the server response.\n */\nexport const defaultFetcher: Fetcher = async <T>(req: FetchInput): Promise<T> => {\n const headers: Record<string, string> = { ...(req.headers ?? {}) };\n const isFormData = typeof FormData !== 'undefined' && req.body instanceof FormData;\n if (!isFormData) {\n headers['Content-Type'] ||= 'application/json';\n headers['Accept'] ||= 'application/json';\n }\n\n logger.debug('fetch', req.method, req.url, isFormData ? 'FormData' : req.body);\n const res = await fetch(req.url, {\n method: req.method,\n headers,\n body: isFormData ? (req.body as any) : req.body == null ? undefined : JSON.stringify(req.body),\n });\n\n const text = await res.text();\n if (!res.ok) {\n const snippet = text.slice(0, 400);\n throw new Error(`[${res.status}] ${res.statusText} — ${snippet}`);\n }\n\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n};\n","// routesV3.client.ts\nimport {\n keepPreviousData,\n useInfiniteQuery,\n useMutation,\n useQuery,\n type InfiniteData,\n type QueryKey,\n} from '@tanstack/react-query';\nimport type { ZodType } from 'zod';\nimport {\n HttpMethod,\n buildCacheKey,\n compilePath,\n} from '@emeryld/rrroutes-contract';\nimport type {\n AnyLeaf,\n InferBody,\n InferOutput,\n InferParams,\n InferQuery,\n} from '@emeryld/rrroutes-contract';\nimport { defaultFetcher } from './routesV3.client.fetch';\nimport type {\n ArgsFor,\n ArgsTuple,\n BuiltForLeaf,\n BuiltInfinite,\n BuiltMutation,\n BuiltQuery,\n Cursor,\n DataShape,\n InfiniteBuildOptionsFor,\n MutationBuildOptionsFor,\n QueryBuildOptionsFor,\n RouteClient,\n RouteClientOptions,\n Updater,\n} from './routesV3.client.types';\n\n// -------------------------------------------------------------------------------------\n// Tiny helpers\n// -------------------------------------------------------------------------------------\n/**\n * Convert an HTTP method to uppercase (as expected by fetch).\n * @param m Lowercase HTTP method.\n * @returns Uppercase method string.\n */\nconst toUpper = (m: HttpMethod): Uppercase<HttpMethod> => m.toUpperCase() as Uppercase<HttpMethod>;\n\n/**\n * Parse the given value with the supplied schema (if present).\n * @param value Raw value to validate.\n * @param schema Optional Zod schema used for validation/coercion.\n * @returns The validated or original value.\n */\nfunction zParse<T>(value: unknown, schema?: ZodType): T {\n return schema ? (schema.parse(value) as T) : (value as T);\n}\n\n/**\n * Serialize a query object into a search string.\n * @param query Query params object (possibly undefined).\n * @returns Query string prefixed with `?`, or empty string.\n */\nfunction toSearchString(query: Record<string, unknown> | undefined) {\n if (!query) return '';\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v == null) continue;\n if (Array.isArray(v)) {\n v.forEach((x) => {\n if (x == null) return;\n if (typeof x === 'object') {\n params.append(k, JSON.stringify(x));\n } else {\n params.append(k, String(x));\n }\n });\n continue;\n }\n if (typeof v === 'object') {\n params.set(k, JSON.stringify(v));\n continue;\n }\n params.set(k, String(v));\n }\n const s = params.toString();\n return s ? `?${s}` : '';\n}\n\n/**\n * Remove the given key from an object (used to drop cursors from cache keys).\n * @param obj Source object.\n * @param key Property name to omit.\n * @returns Copy of the object without the specified key.\n */\nfunction stripKey<Q extends Record<string, unknown> | undefined>(obj: Q, key: string): Q {\n if (!obj) return obj;\n const { [key]: _omit, ...rest } = obj as any;\n return rest as Q;\n}\n\n/**\n * Default cursor extractor used by infinite queries.\n * @param p Page result returned from the server.\n * @returns Next cursor string, if present.\n */\nconst defaultGetNextCursor = (p: unknown): Cursor =>\n p && typeof p === 'object' && 'nextCursor' in p ? (p as any).nextCursor : undefined;\n\n// Split the variadic tuple at runtime\n/**\n * Extract the optional argument object from a variadic tuple.\n * @param args Tuple passed to a built endpoint helper.\n * @returns The argument object if present.\n */\nfunction extractArgs<L extends AnyLeaf>(args: ArgsTuple<L>): ArgsFor<L> | undefined {\n // At runtime ArgsTuple<L> is either [] or [obj]; we just pick the first if present.\n return (args as unknown as any[])[0] as any;\n}\n\n/**\n * Normalize params and query values, then construct a request URL for the given leaf.\n * @param leaf Leaf describing the endpoint.\n * @param baseUrl Optional base URL prepended to the path.\n * @param params Route parameters supplied by the caller.\n * @param query Query parameters supplied by the caller.\n * @returns Object containing the composed URL and normalized query payload.\n */\nfunction buildUrl<L extends AnyLeaf>(\n leaf: L,\n baseUrl: string,\n params: InferParams<L> | undefined,\n query: InferQuery<L> | undefined,\n) {\n const normalizedParams = zParse<InferParams<L>>(params, leaf.cfg.paramsSchema);\n const normalizedQuery = zParse<InferQuery<L>>(query, leaf.cfg.querySchema);\n const path = compilePath<L['path']>(leaf.path, (normalizedParams ?? {}) as any);\n const url = `${baseUrl ?? ''}${path}${toSearchString(normalizedQuery as any)}`;\n return { url, normalizedQuery };\n}\n\n// -------------------------------------------------------------------------------------\n// Client factory\n// -------------------------------------------------------------------------------------\n/**\n * Construct typed React Query helpers backed by a routes-v3 registry leaf.\n * @param opts Route client configuration (query client, fetcher overrides, etc).\n * @returns Object that can build endpoint hooks/mutations from leaves.\n */\nexport function createRouteClient(opts: RouteClientOptions): RouteClient {\n const queryClient = opts.queryClient;\n const fetcher = opts.fetcher ?? defaultFetcher;\n const baseUrl = opts.baseUrl;\n const cursorParam = opts.cursorParam ?? 'cursor';\n const getNextCursor = opts.getNextCursor ?? defaultGetNextCursor;\n\n /**\n * Invalidate a set of queries sharing the given prefix.\n * @param prefix Key parts shared by matching endpoints.\n * @param exact When true, invalidate only exact key matches.\n */\n async function invalidate(prefix: string[], exact = false) {\n await queryClient.invalidateQueries({ queryKey: prefix as unknown as QueryKey, exact });\n }\n\n /**\n * Build the client surface for a single leaf (query/mutation/infinite query).\n * @param leaf Leaf describing the endpoint.\n * @param rqOpts Optional React Query configuration.\n * @returns Helper object exposing key/invalidate/setData/useEndpoint.\n */\n function buildInternal<L extends AnyLeaf>(\n leaf: L,\n rqOpts?: QueryBuildOptionsFor<L> | InfiniteBuildOptionsFor<L> | MutationBuildOptionsFor<L>,\n ): BuiltForLeaf<L> {\n const isGet = leaf.method === 'get';\n const isFeed = !!leaf.cfg.feed;\n\n // --- key/invalidate/setData shared helpers ---\n const key = (...tuple: ArgsTuple<L>): QueryKey => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n const qForKey = isGet && isFeed ? stripKey(query as any, cursorParam) : (query as any);\n return buildCacheKey({ leaf, params: params as any, query: qForKey }) as unknown as QueryKey;\n };\n\n /**\n * Invalidate the React Query cache for this exact leaf invocation.\n * @param tuple Optional params/query tuple.\n */\n const invalidateExact = (...tuple: ArgsTuple<L>) =>\n queryClient.invalidateQueries({ queryKey: key(...tuple), exact: true });\n\n /**\n * Update the cache entries for this leaf.\n * @param args Tuple whose first entry is the updater and optional params/query follow.\n */\n const setData = (...args: [Updater<DataShape<L>>, ...rest: ArgsTuple<L>]) => {\n const [updater, ...rest] = args;\n const k = key(...(rest as ArgsTuple<L>));\n if (isGet && isFeed) {\n queryClient.setQueryData<InfiniteData<InferOutput<L>> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n } else {\n queryClient.setQueryData<InferOutput<L> | undefined>(k, (prev) =>\n typeof updater === 'function' ? (updater as any)(prev) : (updater as any),\n );\n }\n };\n\n // --- Infinite GET ---\n if (isGet && isFeed) {\n const useEndpoint: BuiltInfinite<L>['useEndpoint'] = (...tuple) => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n // Normalize once; we’ll inject the cursor per page below.\n const { normalizedQuery } = buildUrl(leaf, baseUrl, params, query);\n return useInfiniteQuery<\n InferOutput<L>, // TQueryFnData (per page)\n unknown, // TError\n InfiniteData<InferOutput<L>>, // TData (returned by the hook)\n QueryKey,\n Cursor\n >({\n ...(rqOpts as InfiniteBuildOptionsFor<L>),\n queryKey: key(...tuple),\n initialPageParam: undefined,\n getNextPageParam: (lastPage) => getNextCursor(lastPage),\n placeholderData: keepPreviousData,\n queryFn: async ({ pageParam }) => {\n const pageQuery = {\n ...(normalizedQuery as any),\n ...(pageParam ? { [cursorParam]: pageParam } : {}),\n };\n const { url } = buildUrl(leaf, baseUrl, params, pageQuery);\n const out = await fetcher<unknown>({ url, method: toUpper(leaf.method) });\n return zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n },\n // NOTE: TData is InfiniteData<T>, so we don't need a select here.\n });\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n // --- Plain GET ---\n if (isGet) {\n const useEndpoint: BuiltQuery<L>['useEndpoint'] = (...tuple) => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n const { url } = buildUrl(leaf, baseUrl, params, query);\n return useQuery<InferOutput<L>, unknown, InferOutput<L>, QueryKey>({\n ...(rqOpts as QueryBuildOptionsFor<L>),\n queryKey: key(...tuple),\n placeholderData: keepPreviousData,\n queryFn: async () => {\n const out = await fetcher<unknown>({ url, method: toUpper(leaf.method) });\n return zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n },\n });\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n // --- Mutation (POST/PUT/PATCH/DELETE) ---\n const useEndpoint: BuiltMutation<L>['useEndpoint'] = (...tuple) => {\n const a = extractArgs<L>(tuple);\n const params = (a as any)?.params as InferParams<L> | undefined;\n const query = (a as any)?.query as InferQuery<L> | undefined;\n\n const { url } = buildUrl(leaf, baseUrl, params, query);\n return useMutation<InferOutput<L>, unknown, InferBody<L>, unknown>({\n ...(rqOpts as MutationBuildOptionsFor<L>),\n mutationKey: key(...tuple),\n mutationFn: async (body: InferBody<L>) => {\n const normalizedBody = zParse<InferBody<L>>(body, leaf.cfg.bodySchema);\n\n // Optional: switch to FormData if your method declares bodyFiles\n const isMultipart = Array.isArray(leaf.cfg.bodyFiles) && leaf.cfg.bodyFiles.length > 0;\n const payload = isMultipart ? toFormData(normalizedBody as any) : normalizedBody;\n\n const out = await fetcher<unknown>({ url, method: toUpper(leaf.method), body: payload });\n return zParse<InferOutput<L>>(out, leaf.cfg.outputSchema);\n },\n });\n };\n\n return {\n key,\n invalidate: invalidateExact,\n setData: setData as any,\n useEndpoint,\n } as BuiltForLeaf<L>;\n }\n\n return {\n queryClient,\n invalidate,\n build: buildInternal as RouteClient['build'],\n };\n}\n\n// -------------------------------------------------------------------------------------\n// Multipart helper\n// -------------------------------------------------------------------------------------\nfunction toFormData(body: Record<string, any>): FormData {\n const fd = new FormData();\n for (const [k, v] of Object.entries(body ?? {})) {\n if (v == null) continue;\n if (Array.isArray(v)) v.forEach((item, i) => fd.append(`${k}[${i}]`, item as any));\n else fd.append(k, v as any);\n }\n return fd;\n}\n"],"mappings":"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","names":["useEndpoint"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emeryld/rrroutes-client",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",