@querykitjs/web 4.0.0 → 4.1.0
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/README.en.md +101 -15
- package/README.md +102 -16
- package/dist/angular/index.cjs +2 -0
- package/dist/angular/index.cjs.map +1 -0
- package/dist/angular/index.d.cts +70 -0
- package/dist/angular/index.d.ts +70 -0
- package/dist/angular/index.js +2 -0
- package/dist/angular/index.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -4
- package/dist/index.d.ts +48 -4
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +1 -1
- package/dist/react/index.js.map +1 -1
- package/dist/{registry-DOVi_zCU.d.cts → registry-CEq4xFui.d.cts} +46 -44
- package/dist/{registry-DOVi_zCU.d.ts → registry-CEq4xFui.d.ts} +46 -44
- package/dist/vue/index.cjs +2 -0
- package/dist/vue/index.cjs.map +1 -0
- package/dist/vue/index.d.cts +64 -0
- package/dist/vue/index.d.ts +64 -0
- package/dist/vue/index.js +2 -0
- package/dist/vue/index.js.map +1 -0
- package/package.json +67 -6
- package/skills/querykit-web/SKILL.md +63 -3
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/url.ts","../../src/react/use-list-params-base.ts","../../src/react/use-list-params.ts"],"names":["URL_DEFAULTS","encodeSort","sort","i","clone","searchParams","setParam","name","value","url","cfg","next","setPage","page","setSize","size","setSort","encoded","resetParams","keep","key","useListParamsBase","resource","options","schema","setSearchParams","withRel","params","useMemo","useCallback","useListParams","useSearchParams"],"mappings":"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","file":"index.js","sourcesContent":["import { DEFAULT_MAX_OFFSET, DEFAULT_MAX_PER_PAGE, DEFAULT_MAX_SORT_FIELDS, DEFAULT_PER_PAGE } from \"@querykitjs/core\";\n\nimport { buildListParams, clampInt } from \"./query\";\nimport type { FieldDescriptor, ListSchema } from \"./schema\";\nimport type { FieldCondition, Filter, FilterNode, FilterValue, ListParams, ListPayload, Sort, SortDirection, SortInput } from \"./types\";\n\n/** URL param nomlari (sahifa/o'lcham/sort) — sozlanadigan. */\nexport interface UrlConfig {\n pageParam?: string;\n sizeParam?: string;\n sortParam?: string;\n}\n\nconst URL_DEFAULTS: Required<UrlConfig> = {\n pageParam: \"page\",\n sizeParam: \"size\",\n sortParam: \"sortType\",\n};\n\n/**\n * `\"-createdAt,id,-appNumber\"` → `[{ key, direction }, …]` — comma-separated,\n * order preserved, `-` prefix = `desc` (else `asc`). Empty → `undefined`.\n */\nexport function decodeSort(value: string | null | undefined): Sort | undefined {\n if (!value) return undefined;\n const items = value\n .split(\",\")\n .map(s => s.trim())\n .filter(Boolean)\n .map(tok => {\n const desc = tok.startsWith(\"-\");\n return { key: desc ? tok.slice(1) : tok, direction: (desc ? \"desc\" : \"asc\") as SortDirection };\n })\n .filter(i => i.key); // drop empty keys (e.g. a lone \"-\")\n return items.length ? items : undefined;\n}\n\n/** `[{ key, direction }, …]` (or `\"-field\"` shorthand strings) → `\"-createdAt,id,-appNumber\"` (empty → `\"\"`). */\nexport function encodeSort(sort: SortInput): string {\n return sort\n .map(i => (typeof i === \"string\" ? i.trim() : i && i.key ? (i.direction === \"desc\" ? \"-\" : \"\") + i.key : \"\"))\n .filter(Boolean)\n .join(\",\");\n}\n\n/**\n * Schema + searchParams → filter. Applies `range`/`between`/`search`/`split`/\n * `default`/`trim`; empty/absent params are dropped. Returns a flat\n * `FieldCondition[]` (implicit AND), or a tree when a `search` OR-group is\n * present (`{ and: [orGroup, …conditions] }`).\n */\nexport function schemaToFilter(schema: ListSchema, searchParams: URLSearchParams): Filter {\n const conditions: FieldCondition[] = [];\n const groups: FilterNode[] = [];\n\n for (const [param, d] of Object.entries(schema) as [string, FieldDescriptor][]) {\n const key = d.key ?? param;\n\n // range → two inclusive conditions (>= , <=)\n if (d.range) {\n const from = searchParams.get(d.range[0]);\n const to = searchParams.get(d.range[1]);\n if (from) conditions.push(cond(key, \">=\", from));\n if (to) conditions.push(cond(key, \"<=\", to));\n continue;\n }\n\n // between → one condition with a 2-tuple value\n if (d.between) {\n const from = searchParams.get(d.between[0]);\n const to = searchParams.get(d.between[1]);\n if (from && to) conditions.push(cond(key, \"between\", [from, to]));\n continue;\n }\n\n // read value, falling back to `default` only when the param is ABSENT\n // (an empty `?x=` means the user cleared the filter → prune, don't default).\n const raw = searchParams.get(param);\n let value: FilterValue | undefined = raw !== null ? raw : d.default;\n if (typeof value === \"string\" && d.trim) value = value.trim();\n if (value === undefined || value === null || value === \"\") continue;\n if (Array.isArray(value) && value.length === 0) continue;\n\n // search → OR of `contains` across the listed fields (needs a string)\n if (d.search && typeof value === \"string\") {\n groups.push({ or: d.search.map(field => cond(field, \"%_%\", value as string)) });\n continue;\n }\n\n // split → comma-string → array (for `in`/`notIn`); guarded against empties\n if (d.split && typeof value === \"string\") {\n const delimiter = d.split === true ? \",\" : d.split;\n const parts = value\n .split(delimiter)\n .map(s => s.trim())\n .filter(Boolean);\n if (parts.length === 0) continue;\n value = parts;\n }\n\n conditions.push(cond(key, d.operation ?? \"=\", value));\n }\n\n return groups.length === 0 ? conditions : { and: [...groups, ...conditions] };\n}\n\nfunction cond(key: string, operation: FieldCondition[\"operation\"], value: FilterValue): FieldCondition {\n return { key, operation, value };\n}\n\n/**\n * The bounds the URL readers clamp to — the builder's own (`createRegistry`\n * `defaults`); an absent one is the core default.\n */\nexport interface UrlLimits {\n perPage?: number;\n maxPerPage?: number;\n maxOffset?: number;\n maxSortFields?: number;\n}\n\n/** {@link ListParams} read from a URL. `clamped` — the URL's page or sort was cut to the limits. */\nexport type UrlListParams = ListParams & { clamped: boolean };\n\n/**\n * A list payload built from a URL. `clamped` is non-enumerable: readable in code,\n * never serialized, so the wire payload is unchanged.\n */\nexport type UrlListPayload = ListPayload & { readonly clamped?: boolean };\n\n/** Marks a payload with a non-enumerable `clamped` flag. */\nexport function withClamped(payload: ListPayload, clamped: boolean): UrlListPayload {\n return Object.defineProperty(payload, \"clamped\", { value: clamped, enumerable: false });\n}\n\n/** The last page the builder accepts: `(page - 1) × perPage ≤ maxOffset`. */\nfunction lastPage(perPage: number | undefined, limits: UrlLimits): number {\n const resolved = clampInt(perPage, limits.perPage ?? DEFAULT_PER_PAGE, limits.maxPerPage ?? DEFAULT_MAX_PER_PAGE);\n return Math.floor((limits.maxOffset ?? DEFAULT_MAX_OFFSET) / resolved) + 1;\n}\n\n/**\n * searchParams → normalizatsiyalanmagan {@link ListParams} (filter/sort/page/perPage).\n * The URL is user input: a page past `maxOffset` becomes the last allowed page and a\n * sort past `maxSortFields` keeps its first fields, with `clamped: true` — so a\n * hand-edited `?page=9999` never throws during render. The builders still throw\n * for a direct call (task 008).\n */\nexport function readListParams(schema: ListSchema, searchParams: URLSearchParams, url: UrlConfig = {}, limits: UrlLimits = {}): UrlListParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const size = searchParams.get(cfg.sizeParam);\n const perPage = size ? Number(size) : undefined;\n const page = Math.trunc(Number(searchParams.get(cfg.pageParam))) || 1;\n const sort = decodeSort(searchParams.get(cfg.sortParam));\n const maxPage = lastPage(perPage, limits);\n const maxSortFields = limits.maxSortFields ?? DEFAULT_MAX_SORT_FIELDS;\n const isPageClamped = page > maxPage;\n const isSortClamped = sort !== undefined && sort.length > maxSortFields;\n return {\n filter: schemaToFilter(schema, searchParams),\n sort: isSortClamped ? sort.slice(0, maxSortFields) : sort,\n page: isPageClamped ? maxPage : page,\n perPage,\n clamped: isPageClamped || isSortClamped,\n };\n}\n\n/** searchParams → to'liq, yuborishga tayyor {@link ListPayload} (core default limits; clamped like {@link readListParams}). */\nexport function searchParamsToPayload(schema: ListSchema, searchParams: URLSearchParams, url?: UrlConfig): UrlListPayload {\n const params = readListParams(schema, searchParams, url);\n return withClamped(buildListParams(params), params.clamped);\n}\n\n/* ------------------------- URL yozish (immutable) ------------------------- */\n/* Har biri YANGI URLSearchParams qaytaradi; filter/sort o'zgarsa `page` reset. */\n\nfunction clone(searchParams: URLSearchParams): URLSearchParams {\n return new URLSearchParams(searchParams);\n}\n\n/** Filter param'ini o'rnatadi/o'chiradi va `page`ni reset qiladi. */\nexport function setParam(searchParams: URLSearchParams, name: string, value: string | null | undefined, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.delete(cfg.pageParam);\n if (value === null || value === undefined || value === \"\") next.delete(name);\n else next.set(name, value);\n return next;\n}\n\n/** Sahifani o'rnatadi (`page` reset qilinmaydi). */\nexport function setPage(searchParams: URLSearchParams, page: number, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.set(cfg.pageParam, String(page));\n return next;\n}\n\n/** Sahifa o'lchamini o'rnatadi va `page`ni reset qiladi. */\nexport function setSize(searchParams: URLSearchParams, size: number, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.delete(cfg.pageParam);\n next.set(cfg.sizeParam, String(size));\n return next;\n}\n\n/** Sortni o'rnatadi (`[{ key, direction }, …]`) va `page`ni reset qiladi. */\nexport function setSort(searchParams: URLSearchParams, sort: SortInput, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.delete(cfg.pageParam);\n const encoded = encodeSort(sort);\n if (encoded) next.set(cfg.sortParam, encoded);\n else next.delete(cfg.sortParam);\n return next;\n}\n\n/** Barcha list param'larini tozalaydi (ixtiyoriy saqlanadigan kalitlar bilan). */\nexport function resetParams(searchParams: URLSearchParams, keep: string[] = []): URLSearchParams {\n const next = new URLSearchParams();\n for (const key of keep) {\n const value = searchParams.get(key);\n if (value !== null) next.set(key, value);\n }\n return next;\n}\n","import { useCallback, useMemo } from \"react\";\n\nimport type { AdapterName, WithFor } from \"../adapter\";\nimport type { Resource } from \"../registry\";\nimport type { ListSchema } from \"../schema\";\nimport type { ListPayload, SortInput } from \"../types\";\nimport type { UrlConfig } from \"../url\";\nimport { resetParams, setPage, setParam, setSize, setSort } from \"../url\";\n\nexport interface UseListParamsBaseOptions<A extends AdapterName> {\n /** URL↔filter schema ({@link defineListSchema} / `resource.schema`). Memoize it. */\n schema: ListSchema;\n /** Current URL search params (from your router). */\n searchParams: URLSearchParams;\n /** Apply new search params (`(next) => void`). */\n setSearchParams: (next: URLSearchParams) => void;\n /** URL param names (page/size/sortType) — optional. Memoize it. */\n url?: UrlConfig;\n /** Fixed relations to load on every request (not URL-driven). */\n with?: WithFor<A>;\n}\n\nexport interface UseListParamsResult {\n /** Ready-to-send list payload (built from the URL via the resource's defaults). */\n params: ListPayload;\n /**\n * The URL's page or sort was past the limits and was clamped (`?page=9999` → the last\n * allowed page). The app may rewrite the URL (`setPage(params.page)`).\n */\n clamped: boolean;\n searchParams: URLSearchParams;\n /** Set/clear a filter param (resets `page`). */\n setParam: (name: string, value: string | null | undefined) => void;\n setPage: (page: number) => void;\n setSize: (size: number) => void;\n setSort: (sort: SortInput) => void;\n reset: (keep?: string[]) => void;\n}\n\n/**\n * Router-agnostic list URL-state hook. Reads the URL via the schema (a page or sort\n * past the limits is clamped — `clamped`, never a render error), builds a\n * ready-to-send payload through the `resource` (registry defaults + adapter), and\n * returns URL setters (filter/sort/size changes reset `page`). Bring your own\n * `searchParams`/`setSearchParams` (Next.js, TanStack Router, …).\n */\nexport function useListParamsBase<T, A extends AdapterName>(resource: Resource<T, A>, options: UseListParamsBaseOptions<A>): UseListParamsResult {\n const { schema, searchParams, setSearchParams, url, with: withRel } = options;\n\n const params = useMemo(() => resource.fromSearchParams(searchParams, { schema, url, with: withRel }), [resource, schema, searchParams, url, withRel]);\n\n return {\n params,\n clamped: params.clamped === true,\n searchParams,\n setParam: useCallback((name, value) => setSearchParams(setParam(searchParams, name, value, url)), [searchParams, setSearchParams, url]),\n setPage: useCallback((page: number) => setSearchParams(setPage(searchParams, page, url)), [searchParams, setSearchParams, url]),\n setSize: useCallback((size: number) => setSearchParams(setSize(searchParams, size, url)), [searchParams, setSearchParams, url]),\n setSort: useCallback((sort: SortInput) => setSearchParams(setSort(searchParams, sort, url)), [searchParams, setSearchParams, url]),\n reset: useCallback((keep?: string[]) => setSearchParams(resetParams(searchParams, keep)), [searchParams, setSearchParams]),\n };\n}\n","import { useSearchParams } from \"react-router\";\n\nimport type { AdapterName, WithFor } from \"../adapter\";\nimport type { Resource } from \"../registry\";\nimport type { ListSchema } from \"../schema\";\nimport type { UrlConfig } from \"../url\";\nimport { useListParamsBase, type UseListParamsResult } from \"./use-list-params-base\";\n\nexport interface UseListParamsOptions<A extends AdapterName> {\n /** URL↔filter schema ({@link defineListSchema} / `resource.schema`). Memoize it. */\n schema: ListSchema;\n /** URL param names (page/size/sortType) — optional. Memoize it. */\n url?: UrlConfig;\n /** Fixed relations to load on every request (not URL-driven). */\n with?: WithFor<A>;\n}\n\n/**\n * List URL-state hook — turnkey with **react-router** v7 / v8 (uses `useSearchParams`\n * internally). Reads filters/sort/page from the URL via the schema, builds a\n * ready-to-send payload through the `resource`, and returns URL setters (which\n * reset `page` on filter/sort/size change). Does **not** fetch — pass `params`\n * to your own axios / TanStack Query.\n *\n * For other routers (Next.js, TanStack Router), use {@link useListParamsBase} and\n * supply `searchParams`/`setSearchParams` yourself.\n *\n * @example\n * ```tsx\n * const users = qk.resource<IUser>(\"users\");\n * const { params, setPage, setSort } = useListParams(users, { schema: usersSchema });\n * const { data } = useQuery({ queryKey: users.keys.list(params), queryFn: () => post(\"/users/list\", params) });\n * ```\n */\nexport function useListParams<T, A extends AdapterName>(resource: Resource<T, A>, options: UseListParamsOptions<A>): UseListParamsResult {\n const [searchParams, setSearchParams] = useSearchParams();\n return useListParamsBase(resource, {\n ...options,\n searchParams,\n setSearchParams: next => setSearchParams(next),\n });\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/url.ts","../../src/list-state.ts","../../src/react/use-list-params-base.ts","../../src/react/use-list-params.ts"],"names":["URL_DEFAULTS","encodeSort","sort","i","clone","searchParams","setParam","name","value","url","cfg","next","setPage","page","setSize","size","setSort","encoded","resetParams","keep","key","createListState","resource","options","schema","withRel","params","useListParamsBase","setSearchParams","state","useMemo","clamped","actions","useCallback","useListParams","useSearchParams"],"mappings":"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","file":"index.js","sourcesContent":["import { DEFAULT_MAX_OFFSET, DEFAULT_MAX_PER_PAGE, DEFAULT_MAX_SORT_FIELDS, DEFAULT_PER_PAGE } from \"@querykitjs/core\";\n\nimport { buildListParams, clampInt } from \"./query\";\nimport type { FieldDescriptor, ListSchema } from \"./schema\";\nimport type { FieldCondition, Filter, FilterNode, FilterValue, ListParams, ListPayload, Sort, SortDirection, SortInput } from \"./types\";\n\n/** URL param nomlari (sahifa/o'lcham/sort) — sozlanadigan. */\nexport interface UrlConfig {\n pageParam?: string;\n sizeParam?: string;\n sortParam?: string;\n}\n\nconst URL_DEFAULTS: Required<UrlConfig> = {\n pageParam: \"page\",\n sizeParam: \"size\",\n sortParam: \"sortType\",\n};\n\n/**\n * `\"-createdAt,id,-appNumber\"` → `[{ key, direction }, …]` — comma-separated,\n * order preserved, `-` prefix = `desc` (else `asc`). Empty → `undefined`.\n */\nexport function decodeSort(value: string | null | undefined): Sort | undefined {\n if (!value) return undefined;\n const items = value\n .split(\",\")\n .map(s => s.trim())\n .filter(Boolean)\n .map(tok => {\n const desc = tok.startsWith(\"-\");\n return { key: desc ? tok.slice(1) : tok, direction: (desc ? \"desc\" : \"asc\") as SortDirection };\n })\n .filter(i => i.key); // drop empty keys (e.g. a lone \"-\")\n return items.length ? items : undefined;\n}\n\n/** `[{ key, direction }, …]` (or `\"-field\"` shorthand strings) → `\"-createdAt,id,-appNumber\"` (empty → `\"\"`). */\nexport function encodeSort(sort: SortInput): string {\n return sort\n .map(i => (typeof i === \"string\" ? i.trim() : i && i.key ? (i.direction === \"desc\" ? \"-\" : \"\") + i.key : \"\"))\n .filter(Boolean)\n .join(\",\");\n}\n\n/**\n * Schema + searchParams → filter. Applies `range`/`between`/`search`/`split`/\n * `default`/`trim`; empty/absent params are dropped. Returns a flat\n * `FieldCondition[]` (implicit AND), or a tree when a `search` OR-group is\n * present (`{ and: [orGroup, …conditions] }`).\n */\nexport function schemaToFilter(schema: ListSchema, searchParams: URLSearchParams): Filter {\n const conditions: FieldCondition[] = [];\n const groups: FilterNode[] = [];\n\n for (const [param, d] of Object.entries(schema) as [string, FieldDescriptor][]) {\n const key = d.key ?? param;\n\n // range → two inclusive conditions (>= , <=)\n if (d.range) {\n const from = searchParams.get(d.range[0]);\n const to = searchParams.get(d.range[1]);\n if (from) conditions.push(cond(key, \">=\", from));\n if (to) conditions.push(cond(key, \"<=\", to));\n continue;\n }\n\n // between → one condition with a 2-tuple value\n if (d.between) {\n const from = searchParams.get(d.between[0]);\n const to = searchParams.get(d.between[1]);\n if (from && to) conditions.push(cond(key, \"between\", [from, to]));\n continue;\n }\n\n // read value, falling back to `default` only when the param is ABSENT\n // (an empty `?x=` means the user cleared the filter → prune, don't default).\n const raw = searchParams.get(param);\n let value: FilterValue | undefined = raw !== null ? raw : d.default;\n if (typeof value === \"string\" && d.trim) value = value.trim();\n if (value === undefined || value === null || value === \"\") continue;\n if (Array.isArray(value) && value.length === 0) continue;\n\n // search → OR of `contains` across the listed fields (needs a string)\n if (d.search && typeof value === \"string\") {\n groups.push({ or: d.search.map(field => cond(field, \"%_%\", value as string)) });\n continue;\n }\n\n // split → comma-string → array (for `in`/`notIn`); guarded against empties\n if (d.split && typeof value === \"string\") {\n const delimiter = d.split === true ? \",\" : d.split;\n const parts = value\n .split(delimiter)\n .map(s => s.trim())\n .filter(Boolean);\n if (parts.length === 0) continue;\n value = parts;\n }\n\n conditions.push(cond(key, d.operation ?? \"=\", value));\n }\n\n return groups.length === 0 ? conditions : { and: [...groups, ...conditions] };\n}\n\nfunction cond(key: string, operation: FieldCondition[\"operation\"], value: FilterValue): FieldCondition {\n return { key, operation, value };\n}\n\n/**\n * The bounds the URL readers clamp to — the builder's own (`createRegistry`\n * `defaults`); an absent one is the core default.\n */\nexport interface UrlLimits {\n perPage?: number;\n maxPerPage?: number;\n maxOffset?: number;\n maxSortFields?: number;\n}\n\n/** {@link ListParams} read from a URL. `clamped` — the URL's page or sort was cut to the limits. */\nexport type UrlListParams = ListParams & { clamped: boolean };\n\n/**\n * A list payload built from a URL. `clamped` is non-enumerable: readable in code,\n * never serialized, so the wire payload is unchanged.\n */\nexport type UrlListPayload = ListPayload & { readonly clamped?: boolean };\n\n/** Marks a payload with a non-enumerable `clamped` flag. */\nexport function withClamped(payload: ListPayload, clamped: boolean): UrlListPayload {\n return Object.defineProperty(payload, \"clamped\", { value: clamped, enumerable: false });\n}\n\n/** The last page the builder accepts: `(page - 1) × perPage ≤ maxOffset`. */\nfunction lastPage(perPage: number | undefined, limits: UrlLimits): number {\n const resolved = clampInt(perPage, limits.perPage ?? DEFAULT_PER_PAGE, limits.maxPerPage ?? DEFAULT_MAX_PER_PAGE);\n return Math.floor((limits.maxOffset ?? DEFAULT_MAX_OFFSET) / resolved) + 1;\n}\n\n/**\n * searchParams → normalizatsiyalanmagan {@link ListParams} (filter/sort/page/perPage).\n * The URL is user input: a page past `maxOffset` becomes the last allowed page and a\n * sort past `maxSortFields` keeps its first fields, with `clamped: true` — so a\n * hand-edited `?page=9999` never throws during render. The builders still throw\n * for a direct call.\n */\nexport function readListParams(schema: ListSchema, searchParams: URLSearchParams, url: UrlConfig = {}, limits: UrlLimits = {}): UrlListParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const size = searchParams.get(cfg.sizeParam);\n const perPage = size ? Number(size) : undefined;\n const page = Math.trunc(Number(searchParams.get(cfg.pageParam))) || 1;\n const sort = decodeSort(searchParams.get(cfg.sortParam));\n const maxPage = lastPage(perPage, limits);\n const maxSortFields = limits.maxSortFields ?? DEFAULT_MAX_SORT_FIELDS;\n const isPageClamped = page > maxPage;\n const isSortClamped = sort !== undefined && sort.length > maxSortFields;\n return {\n filter: schemaToFilter(schema, searchParams),\n sort: isSortClamped ? sort.slice(0, maxSortFields) : sort,\n page: isPageClamped ? maxPage : page,\n perPage,\n clamped: isPageClamped || isSortClamped,\n };\n}\n\n/** searchParams → to'liq, yuborishga tayyor {@link ListPayload} (core default limits; clamped like {@link readListParams}). */\nexport function searchParamsToPayload(schema: ListSchema, searchParams: URLSearchParams, url?: UrlConfig): UrlListPayload {\n const params = readListParams(schema, searchParams, url);\n return withClamped(buildListParams(params), params.clamped);\n}\n\n/* ------------------------- URL yozish (immutable) ------------------------- */\n/* Har biri YANGI URLSearchParams qaytaradi; filter/sort o'zgarsa `page` reset. */\n\nfunction clone(searchParams: URLSearchParams): URLSearchParams {\n return new URLSearchParams(searchParams);\n}\n\n/** Filter param'ini o'rnatadi/o'chiradi va `page`ni reset qiladi. */\nexport function setParam(searchParams: URLSearchParams, name: string, value: string | null | undefined, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.delete(cfg.pageParam);\n if (value === null || value === undefined || value === \"\") next.delete(name);\n else next.set(name, value);\n return next;\n}\n\n/** Sahifani o'rnatadi (`page` reset qilinmaydi). */\nexport function setPage(searchParams: URLSearchParams, page: number, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.set(cfg.pageParam, String(page));\n return next;\n}\n\n/** Sahifa o'lchamini o'rnatadi va `page`ni reset qiladi. */\nexport function setSize(searchParams: URLSearchParams, size: number, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.delete(cfg.pageParam);\n next.set(cfg.sizeParam, String(size));\n return next;\n}\n\n/** Sortni o'rnatadi (`[{ key, direction }, …]`) va `page`ni reset qiladi. */\nexport function setSort(searchParams: URLSearchParams, sort: SortInput, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.delete(cfg.pageParam);\n const encoded = encodeSort(sort);\n if (encoded) next.set(cfg.sortParam, encoded);\n else next.delete(cfg.sortParam);\n return next;\n}\n\n/** Barcha list param'larini tozalaydi (ixtiyoriy saqlanadigan kalitlar bilan). */\nexport function resetParams(searchParams: URLSearchParams, keep: string[] = []): URLSearchParams {\n const next = new URLSearchParams();\n for (const key of keep) {\n const value = searchParams.get(key);\n if (value !== null) next.set(key, value);\n }\n return next;\n}\n","import type { AdapterName, WithFor } from \"./adapter\";\nimport type { Resource } from \"./registry\";\nimport type { ListSchema } from \"./schema\";\nimport type { ListPayload, SortInput } from \"./types\";\nimport type { UrlConfig } from \"./url\";\nimport { resetParams, setPage, setParam, setSize, setSort } from \"./url\";\n\nexport interface ListStateOptions<A extends AdapterName> {\n /** URL↔filter schema ({@link defineListSchema} / `resource.schema`). */\n schema: ListSchema;\n /** URL param names (page/size/sortType) — optional. */\n url?: UrlConfig;\n /** Fixed relations to load on every request (not URL-driven). */\n with?: WithFor<A>;\n}\n\nexport interface ListStateRead {\n /** Ready-to-send list payload (built from the URL via the resource's defaults). */\n params: ListPayload;\n /** The URL's page or sort was past the limits and was clamped (`?page=9999` → the last allowed page). */\n clamped: boolean;\n}\n\n/** URL setters: each returns the NEXT search params and never mutates the given ones. */\nexport interface ListStateActions {\n /** Set/clear a filter param (resets `page`). */\n setParam: (name: string, value: string | null | undefined) => URLSearchParams;\n setPage: (page: number) => URLSearchParams;\n /** Resets `page`. */\n setSize: (size: number) => URLSearchParams;\n /** Resets `page`; an empty sort clears the param. */\n setSort: (sort: SortInput) => URLSearchParams;\n /** Clears every param except `keep`. */\n reset: (keep?: string[]) => URLSearchParams;\n}\n\nexport interface ListState {\n read: (searchParams: URLSearchParams) => ListStateRead;\n actions: (searchParams: URLSearchParams) => ListStateActions;\n}\n\n/**\n * Framework-free list URL state — the logic behind `useListParams` in `@querykitjs/web/react`\n * and `@querykitjs/web/vue`, for any other framework (Svelte, Solid, vanilla). Pure: it reads\n * search params and returns the next ones; navigating is the caller's job.\n *\n * @example\n * ```ts\n * const list = createListState(users, { schema: usersSchema });\n * const { params, clamped } = list.read(new URLSearchParams(location.search));\n * history.pushState(null, \"\", `?${list.actions(new URLSearchParams(location.search)).setPage(2)}`);\n * ```\n */\nexport function createListState<T, A extends AdapterName>(resource: Resource<T, A>, options: ListStateOptions<A>): ListState {\n const { schema, url, with: withRel } = options;\n return {\n read(searchParams) {\n const params = resource.fromSearchParams(searchParams, { schema, url, with: withRel });\n return { params, clamped: params.clamped === true };\n },\n actions(searchParams) {\n return {\n setParam: (name, value) => setParam(searchParams, name, value, url),\n setPage: page => setPage(searchParams, page, url),\n setSize: size => setSize(searchParams, size, url),\n setSort: sort => setSort(searchParams, sort, url),\n reset: keep => resetParams(searchParams, keep),\n };\n },\n };\n}\n","import { useCallback, useMemo } from \"react\";\n\nimport type { AdapterName, WithFor } from \"../adapter\";\nimport { createListState } from \"../list-state\";\nimport type { Resource } from \"../registry\";\nimport type { ListSchema } from \"../schema\";\nimport type { ListPayload, SortInput } from \"../types\";\nimport type { UrlConfig } from \"../url\";\n\nexport interface UseListParamsBaseOptions<A extends AdapterName> {\n /** URL↔filter schema ({@link defineListSchema} / `resource.schema`). Memoize it. */\n schema: ListSchema;\n /** Current URL search params (from your router). */\n searchParams: URLSearchParams;\n /** Apply new search params (`(next) => void`). */\n setSearchParams: (next: URLSearchParams) => void;\n /** URL param names (page/size/sortType) — optional. Memoize it. */\n url?: UrlConfig;\n /** Fixed relations to load on every request (not URL-driven). */\n with?: WithFor<A>;\n}\n\nexport interface UseListParamsResult {\n /** Ready-to-send list payload (built from the URL via the resource's defaults). */\n params: ListPayload;\n /**\n * The URL's page or sort was past the limits and was clamped (`?page=9999` → the last\n * allowed page). The app may rewrite the URL (`setPage(params.page)`).\n */\n clamped: boolean;\n searchParams: URLSearchParams;\n /** Set/clear a filter param (resets `page`). */\n setParam: (name: string, value: string | null | undefined) => void;\n setPage: (page: number) => void;\n setSize: (size: number) => void;\n setSort: (sort: SortInput) => void;\n reset: (keep?: string[]) => void;\n}\n\n/**\n * Router-agnostic list URL-state hook. Reads the URL via the schema (a page or sort\n * past the limits is clamped — `clamped`, never a render error), builds a\n * ready-to-send payload through the `resource` (registry defaults + adapter), and\n * returns URL setters (filter/sort/size changes reset `page`). Bring your own\n * `searchParams`/`setSearchParams` (Next.js, TanStack Router, …).\n */\nexport function useListParamsBase<T, A extends AdapterName>(resource: Resource<T, A>, options: UseListParamsBaseOptions<A>): UseListParamsResult {\n const { schema, searchParams, setSearchParams, url, with: withRel } = options;\n\n const state = useMemo(() => createListState(resource, { schema, url, with: withRel }), [resource, schema, url, withRel]);\n const { params, clamped } = useMemo(() => state.read(searchParams), [state, searchParams]);\n const actions = useMemo(() => state.actions(searchParams), [state, searchParams]);\n\n return {\n params,\n clamped,\n searchParams,\n setParam: useCallback((name, value) => setSearchParams(actions.setParam(name, value)), [actions, setSearchParams]),\n setPage: useCallback((page: number) => setSearchParams(actions.setPage(page)), [actions, setSearchParams]),\n setSize: useCallback((size: number) => setSearchParams(actions.setSize(size)), [actions, setSearchParams]),\n setSort: useCallback((sort: SortInput) => setSearchParams(actions.setSort(sort)), [actions, setSearchParams]),\n reset: useCallback((keep?: string[]) => setSearchParams(actions.reset(keep)), [actions, setSearchParams]),\n };\n}\n","import { useSearchParams } from \"react-router\";\n\nimport type { AdapterName, WithFor } from \"../adapter\";\nimport type { Resource } from \"../registry\";\nimport type { ListSchema } from \"../schema\";\nimport type { UrlConfig } from \"../url\";\nimport { useListParamsBase, type UseListParamsResult } from \"./use-list-params-base\";\n\nexport interface UseListParamsOptions<A extends AdapterName> {\n /** URL↔filter schema ({@link defineListSchema} / `resource.schema`). Memoize it. */\n schema: ListSchema;\n /** URL param names (page/size/sortType) — optional. Memoize it. */\n url?: UrlConfig;\n /** Fixed relations to load on every request (not URL-driven). */\n with?: WithFor<A>;\n}\n\n/**\n * List URL-state hook — turnkey with **react-router** v7 / v8 (uses `useSearchParams`\n * internally). Reads filters/sort/page from the URL via the schema, builds a\n * ready-to-send payload through the `resource`, and returns URL setters (which\n * reset `page` on filter/sort/size change). Does **not** fetch — pass `params`\n * to your own axios / TanStack Query.\n *\n * For other routers (Next.js, TanStack Router), use {@link useListParamsBase} and\n * supply `searchParams`/`setSearchParams` yourself.\n *\n * @example\n * ```tsx\n * const users = qk.resource<IUser>(\"users\");\n * const { params, setPage, setSort } = useListParams(users, { schema: usersSchema });\n * const { data } = useQuery({ queryKey: users.keys.list(params), queryFn: () => post(\"/users/list\", params) });\n * ```\n */\nexport function useListParams<T, A extends AdapterName>(resource: Resource<T, A>, options: UseListParamsOptions<A>): UseListParamsResult {\n const [searchParams, setSearchParams] = useSearchParams();\n return useListParamsBase(resource, {\n ...options,\n searchParams,\n setSearchParams: next => setSearchParams(next),\n });\n}\n"]}
|
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
import * as Core from '@querykitjs/core';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Per-backend `with` (relation-loading) config shapes. querykit unifies
|
|
5
|
+
* filter/sort/pagination, but relation loading differs per ORM — so the registry
|
|
6
|
+
* types `with` from the chosen `adapter`. Only JSON-serializable, wire-safe
|
|
7
|
+
* options are typed; function callbacks (e.g. Drizzle `where: (f,op)=>…`) and raw
|
|
8
|
+
* SQL are deliberately **never** included — you can't send them over JSON.
|
|
9
|
+
*/
|
|
10
|
+
/** Mongoose `populate` config (serializable subset). */
|
|
11
|
+
interface MongoosePopulate {
|
|
12
|
+
select?: string;
|
|
13
|
+
match?: Record<string, unknown>;
|
|
14
|
+
options?: Record<string, unknown>;
|
|
15
|
+
populate?: string | MongoosePopulate | Record<string, boolean | MongoosePopulate>;
|
|
16
|
+
}
|
|
17
|
+
/** Drizzle relational `with` config (serializable subset — no `where`/`orderBy` fns, no SQL). */
|
|
18
|
+
interface DrizzleWith {
|
|
19
|
+
columns?: Record<string, boolean>;
|
|
20
|
+
with?: Record<string, boolean | DrizzleWith>;
|
|
21
|
+
limit?: number;
|
|
22
|
+
offset?: number;
|
|
23
|
+
}
|
|
24
|
+
/** Prisma relation config (serializable subset). */
|
|
25
|
+
interface PrismaInclude {
|
|
26
|
+
select?: Record<string, boolean>;
|
|
27
|
+
include?: Record<string, boolean | PrismaInclude>;
|
|
28
|
+
where?: Record<string, unknown>;
|
|
29
|
+
orderBy?: Record<string, unknown>;
|
|
30
|
+
take?: number;
|
|
31
|
+
skip?: number;
|
|
32
|
+
}
|
|
33
|
+
/** Supported registry backend adapters. */
|
|
34
|
+
type AdapterName = "mongoose" | "mongodb" | "drizzle-pg" | "drizzle-sqlite" | "prisma-pg";
|
|
35
|
+
/** The per-relation `with` value shape for each adapter. */
|
|
36
|
+
interface AdapterWithConfig {
|
|
37
|
+
mongoose: boolean | MongoosePopulate;
|
|
38
|
+
/** `never`: `@querykitjs/mongodb` loads no relations and refuses any `with` (`forbidden-key`) — load them with a second request. */
|
|
39
|
+
mongodb: never;
|
|
40
|
+
"drizzle-pg": boolean | DrizzleWith;
|
|
41
|
+
"drizzle-sqlite": boolean | DrizzleWith;
|
|
42
|
+
"prisma-pg": boolean | PrismaInclude;
|
|
43
|
+
}
|
|
44
|
+
/** `with` map typed for an adapter: relation name → its config (boolean or object). */
|
|
45
|
+
type WithFor<A extends AdapterName> = Record<string, AdapterWithConfig[A]>;
|
|
46
|
+
|
|
3
47
|
/**
|
|
4
48
|
* Filter/query DSL tiplari — `@querykitjs/core`dan, entity maydoni (`FieldKey`)
|
|
5
49
|
* bilan ixtisoslashtirilgan. querykit backend (`@querykitjs/drizzle-pg`) qabul
|
|
@@ -294,7 +338,7 @@ type UrlListPayload = ListPayload & {
|
|
|
294
338
|
* The URL is user input: a page past `maxOffset` becomes the last allowed page and a
|
|
295
339
|
* sort past `maxSortFields` keeps its first fields, with `clamped: true` — so a
|
|
296
340
|
* hand-edited `?page=9999` never throws during render. The builders still throw
|
|
297
|
-
* for a direct call
|
|
341
|
+
* for a direct call.
|
|
298
342
|
*/
|
|
299
343
|
declare function readListParams(schema: ListSchema, searchParams: URLSearchParams, url?: UrlConfig, limits?: UrlLimits): UrlListParams;
|
|
300
344
|
/** searchParams → to'liq, yuborishga tayyor {@link ListPayload} (core default limits; clamped like {@link readListParams}). */
|
|
@@ -310,48 +354,6 @@ declare function setSort(searchParams: URLSearchParams, sort: SortInput, url?: U
|
|
|
310
354
|
/** Barcha list param'larini tozalaydi (ixtiyoriy saqlanadigan kalitlar bilan). */
|
|
311
355
|
declare function resetParams(searchParams: URLSearchParams, keep?: string[]): URLSearchParams;
|
|
312
356
|
|
|
313
|
-
/**
|
|
314
|
-
* Per-backend `with` (relation-loading) config shapes. querykit unifies
|
|
315
|
-
* filter/sort/pagination, but relation loading differs per ORM — so the registry
|
|
316
|
-
* types `with` from the chosen `adapter`. Only JSON-serializable, wire-safe
|
|
317
|
-
* options are typed; function callbacks (e.g. Drizzle `where: (f,op)=>…`) and raw
|
|
318
|
-
* SQL are deliberately **never** included — you can't send them over JSON.
|
|
319
|
-
*/
|
|
320
|
-
/** Mongoose `populate` config (serializable subset). */
|
|
321
|
-
interface MongoosePopulate {
|
|
322
|
-
select?: string;
|
|
323
|
-
match?: Record<string, unknown>;
|
|
324
|
-
options?: Record<string, unknown>;
|
|
325
|
-
populate?: string | MongoosePopulate | Record<string, boolean | MongoosePopulate>;
|
|
326
|
-
}
|
|
327
|
-
/** Drizzle relational `with` config (serializable subset — no `where`/`orderBy` fns, no SQL). */
|
|
328
|
-
interface DrizzleWith {
|
|
329
|
-
columns?: Record<string, boolean>;
|
|
330
|
-
with?: Record<string, boolean | DrizzleWith>;
|
|
331
|
-
limit?: number;
|
|
332
|
-
offset?: number;
|
|
333
|
-
}
|
|
334
|
-
/** Prisma relation config (serializable subset). */
|
|
335
|
-
interface PrismaInclude {
|
|
336
|
-
select?: Record<string, boolean>;
|
|
337
|
-
include?: Record<string, boolean | PrismaInclude>;
|
|
338
|
-
where?: Record<string, unknown>;
|
|
339
|
-
orderBy?: Record<string, unknown>;
|
|
340
|
-
take?: number;
|
|
341
|
-
skip?: number;
|
|
342
|
-
}
|
|
343
|
-
/** Supported registry backend adapters. */
|
|
344
|
-
type AdapterName = "mongoose" | "drizzle-pg" | "drizzle-sqlite" | "prisma-pg";
|
|
345
|
-
/** The per-relation `with` value shape for each adapter. */
|
|
346
|
-
interface AdapterWithConfig {
|
|
347
|
-
mongoose: boolean | MongoosePopulate;
|
|
348
|
-
"drizzle-pg": boolean | DrizzleWith;
|
|
349
|
-
"drizzle-sqlite": boolean | DrizzleWith;
|
|
350
|
-
"prisma-pg": boolean | PrismaInclude;
|
|
351
|
-
}
|
|
352
|
-
/** `with` map typed for an adapter: relation name → its config (boolean or object). */
|
|
353
|
-
type WithFor<A extends AdapterName> = Record<string, AdapterWithConfig[A]>;
|
|
354
|
-
|
|
355
357
|
type ResourceListParams<T, A extends AdapterName> = Omit<ListParams<T>, "with"> & {
|
|
356
358
|
with?: WithFor<A>;
|
|
357
359
|
};
|
|
@@ -487,4 +489,4 @@ interface Registry<A extends AdapterName> {
|
|
|
487
489
|
declare function createRegistry<A extends AdapterName>(config: RegistryConfig<A>): Registry<A>;
|
|
488
490
|
|
|
489
491
|
export { createFilters as $, createRegistry as a0, decodeSort as a1, defineListSchema as a2, encodeSort as a3, readListParams as a4, resetParams as a5, schemaToFilter as a6, searchParamsToPayload as a7, setPage as a8, setParam as a9, setSize as aa, setSort as ab };
|
|
490
|
-
export type { AdapterName as A,
|
|
492
|
+
export type { AdapterName as A, RegistryTimestamps as B, CursorParams as C, DrizzleWith as D, ResourceCursorParams as E, Filter as F, ResourceInfiniteParams as G, ResourceKeys as H, InfiniteParams as I, ResourceListParams as J, ResourceParams as K, ListSchema as L, Meta as M, NotGroup as N, OrGroup as O, Params as P, QueryPayload as Q, Resource as R, SortInput as S, SearchOptions as T, UrlConfig as U, SortDirection as V, WithFor as W, SortItem as X, UrlLimits as Y, UrlListParams as Z, UrlListPayload as _, ListPayload as a, Sort as b, CursorPayload as c, InfinitePayload as d, ListParams as e, FieldCondition as f, FilterNode as g, AndGroup as h, CursorMeta as i, CursorResult as j, FieldDescriptor as k, FieldKey as l, FilterOperator as m, FilterScalar as n, FilterValue as o, InfiniteMeta as p, InfiniteResult as q, ListResult as r, MongoosePopulate as s, PrismaInclude as t, RawCursorMeta as u, RawInfiniteMeta as v, RawMeta as w, Registry as x, RegistryConfig as y, RegistryDefaults as z };
|
|
@@ -1,5 +1,49 @@
|
|
|
1
1
|
import * as Core from '@querykitjs/core';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Per-backend `with` (relation-loading) config shapes. querykit unifies
|
|
5
|
+
* filter/sort/pagination, but relation loading differs per ORM — so the registry
|
|
6
|
+
* types `with` from the chosen `adapter`. Only JSON-serializable, wire-safe
|
|
7
|
+
* options are typed; function callbacks (e.g. Drizzle `where: (f,op)=>…`) and raw
|
|
8
|
+
* SQL are deliberately **never** included — you can't send them over JSON.
|
|
9
|
+
*/
|
|
10
|
+
/** Mongoose `populate` config (serializable subset). */
|
|
11
|
+
interface MongoosePopulate {
|
|
12
|
+
select?: string;
|
|
13
|
+
match?: Record<string, unknown>;
|
|
14
|
+
options?: Record<string, unknown>;
|
|
15
|
+
populate?: string | MongoosePopulate | Record<string, boolean | MongoosePopulate>;
|
|
16
|
+
}
|
|
17
|
+
/** Drizzle relational `with` config (serializable subset — no `where`/`orderBy` fns, no SQL). */
|
|
18
|
+
interface DrizzleWith {
|
|
19
|
+
columns?: Record<string, boolean>;
|
|
20
|
+
with?: Record<string, boolean | DrizzleWith>;
|
|
21
|
+
limit?: number;
|
|
22
|
+
offset?: number;
|
|
23
|
+
}
|
|
24
|
+
/** Prisma relation config (serializable subset). */
|
|
25
|
+
interface PrismaInclude {
|
|
26
|
+
select?: Record<string, boolean>;
|
|
27
|
+
include?: Record<string, boolean | PrismaInclude>;
|
|
28
|
+
where?: Record<string, unknown>;
|
|
29
|
+
orderBy?: Record<string, unknown>;
|
|
30
|
+
take?: number;
|
|
31
|
+
skip?: number;
|
|
32
|
+
}
|
|
33
|
+
/** Supported registry backend adapters. */
|
|
34
|
+
type AdapterName = "mongoose" | "mongodb" | "drizzle-pg" | "drizzle-sqlite" | "prisma-pg";
|
|
35
|
+
/** The per-relation `with` value shape for each adapter. */
|
|
36
|
+
interface AdapterWithConfig {
|
|
37
|
+
mongoose: boolean | MongoosePopulate;
|
|
38
|
+
/** `never`: `@querykitjs/mongodb` loads no relations and refuses any `with` (`forbidden-key`) — load them with a second request. */
|
|
39
|
+
mongodb: never;
|
|
40
|
+
"drizzle-pg": boolean | DrizzleWith;
|
|
41
|
+
"drizzle-sqlite": boolean | DrizzleWith;
|
|
42
|
+
"prisma-pg": boolean | PrismaInclude;
|
|
43
|
+
}
|
|
44
|
+
/** `with` map typed for an adapter: relation name → its config (boolean or object). */
|
|
45
|
+
type WithFor<A extends AdapterName> = Record<string, AdapterWithConfig[A]>;
|
|
46
|
+
|
|
3
47
|
/**
|
|
4
48
|
* Filter/query DSL tiplari — `@querykitjs/core`dan, entity maydoni (`FieldKey`)
|
|
5
49
|
* bilan ixtisoslashtirilgan. querykit backend (`@querykitjs/drizzle-pg`) qabul
|
|
@@ -294,7 +338,7 @@ type UrlListPayload = ListPayload & {
|
|
|
294
338
|
* The URL is user input: a page past `maxOffset` becomes the last allowed page and a
|
|
295
339
|
* sort past `maxSortFields` keeps its first fields, with `clamped: true` — so a
|
|
296
340
|
* hand-edited `?page=9999` never throws during render. The builders still throw
|
|
297
|
-
* for a direct call
|
|
341
|
+
* for a direct call.
|
|
298
342
|
*/
|
|
299
343
|
declare function readListParams(schema: ListSchema, searchParams: URLSearchParams, url?: UrlConfig, limits?: UrlLimits): UrlListParams;
|
|
300
344
|
/** searchParams → to'liq, yuborishga tayyor {@link ListPayload} (core default limits; clamped like {@link readListParams}). */
|
|
@@ -310,48 +354,6 @@ declare function setSort(searchParams: URLSearchParams, sort: SortInput, url?: U
|
|
|
310
354
|
/** Barcha list param'larini tozalaydi (ixtiyoriy saqlanadigan kalitlar bilan). */
|
|
311
355
|
declare function resetParams(searchParams: URLSearchParams, keep?: string[]): URLSearchParams;
|
|
312
356
|
|
|
313
|
-
/**
|
|
314
|
-
* Per-backend `with` (relation-loading) config shapes. querykit unifies
|
|
315
|
-
* filter/sort/pagination, but relation loading differs per ORM — so the registry
|
|
316
|
-
* types `with` from the chosen `adapter`. Only JSON-serializable, wire-safe
|
|
317
|
-
* options are typed; function callbacks (e.g. Drizzle `where: (f,op)=>…`) and raw
|
|
318
|
-
* SQL are deliberately **never** included — you can't send them over JSON.
|
|
319
|
-
*/
|
|
320
|
-
/** Mongoose `populate` config (serializable subset). */
|
|
321
|
-
interface MongoosePopulate {
|
|
322
|
-
select?: string;
|
|
323
|
-
match?: Record<string, unknown>;
|
|
324
|
-
options?: Record<string, unknown>;
|
|
325
|
-
populate?: string | MongoosePopulate | Record<string, boolean | MongoosePopulate>;
|
|
326
|
-
}
|
|
327
|
-
/** Drizzle relational `with` config (serializable subset — no `where`/`orderBy` fns, no SQL). */
|
|
328
|
-
interface DrizzleWith {
|
|
329
|
-
columns?: Record<string, boolean>;
|
|
330
|
-
with?: Record<string, boolean | DrizzleWith>;
|
|
331
|
-
limit?: number;
|
|
332
|
-
offset?: number;
|
|
333
|
-
}
|
|
334
|
-
/** Prisma relation config (serializable subset). */
|
|
335
|
-
interface PrismaInclude {
|
|
336
|
-
select?: Record<string, boolean>;
|
|
337
|
-
include?: Record<string, boolean | PrismaInclude>;
|
|
338
|
-
where?: Record<string, unknown>;
|
|
339
|
-
orderBy?: Record<string, unknown>;
|
|
340
|
-
take?: number;
|
|
341
|
-
skip?: number;
|
|
342
|
-
}
|
|
343
|
-
/** Supported registry backend adapters. */
|
|
344
|
-
type AdapterName = "mongoose" | "drizzle-pg" | "drizzle-sqlite" | "prisma-pg";
|
|
345
|
-
/** The per-relation `with` value shape for each adapter. */
|
|
346
|
-
interface AdapterWithConfig {
|
|
347
|
-
mongoose: boolean | MongoosePopulate;
|
|
348
|
-
"drizzle-pg": boolean | DrizzleWith;
|
|
349
|
-
"drizzle-sqlite": boolean | DrizzleWith;
|
|
350
|
-
"prisma-pg": boolean | PrismaInclude;
|
|
351
|
-
}
|
|
352
|
-
/** `with` map typed for an adapter: relation name → its config (boolean or object). */
|
|
353
|
-
type WithFor<A extends AdapterName> = Record<string, AdapterWithConfig[A]>;
|
|
354
|
-
|
|
355
357
|
type ResourceListParams<T, A extends AdapterName> = Omit<ListParams<T>, "with"> & {
|
|
356
358
|
with?: WithFor<A>;
|
|
357
359
|
};
|
|
@@ -487,4 +489,4 @@ interface Registry<A extends AdapterName> {
|
|
|
487
489
|
declare function createRegistry<A extends AdapterName>(config: RegistryConfig<A>): Registry<A>;
|
|
488
490
|
|
|
489
491
|
export { createFilters as $, createRegistry as a0, decodeSort as a1, defineListSchema as a2, encodeSort as a3, readListParams as a4, resetParams as a5, schemaToFilter as a6, searchParamsToPayload as a7, setPage as a8, setParam as a9, setSize as aa, setSort as ab };
|
|
490
|
-
export type { AdapterName as A,
|
|
492
|
+
export type { AdapterName as A, RegistryTimestamps as B, CursorParams as C, DrizzleWith as D, ResourceCursorParams as E, Filter as F, ResourceInfiniteParams as G, ResourceKeys as H, InfiniteParams as I, ResourceListParams as J, ResourceParams as K, ListSchema as L, Meta as M, NotGroup as N, OrGroup as O, Params as P, QueryPayload as Q, Resource as R, SortInput as S, SearchOptions as T, UrlConfig as U, SortDirection as V, WithFor as W, SortItem as X, UrlLimits as Y, UrlListParams as Z, UrlListPayload as _, ListPayload as a, Sort as b, CursorPayload as c, InfinitePayload as d, ListParams as e, FieldCondition as f, FilterNode as g, AndGroup as h, CursorMeta as i, CursorResult as j, FieldDescriptor as k, FieldKey as l, FilterOperator as m, FilterScalar as n, FilterValue as o, InfiniteMeta as p, InfiniteResult as q, ListResult as r, MongoosePopulate as s, PrismaInclude as t, RawCursorMeta as u, RawInfiniteMeta as v, RawMeta as w, Registry as x, RegistryConfig as y, RegistryDefaults as z };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
'use strict';var vueRouter=require('vue-router'),vue=require('vue');require('@querykitjs/core');function P(a){let t=new URLSearchParams;for(let[s,e]of Object.entries(a))for(let r of Array.isArray(e)?e:[e])r!==void 0&&t.append(s,r??"");return t}function g(a){let t={};for(let s of new Set(a.keys())){let e=a.getAll(s);t[s]=e.length>1?e:e[0]??"";}return t}var p={pageParam:"page",sizeParam:"size",sortParam:"sortType"};function A(a){return a.map(t=>typeof t=="string"?t.trim():t&&t.key?(t.direction==="desc"?"-":"")+t.key:"").filter(Boolean).join(",")}function u(a){return new URLSearchParams(a)}function S(a,t,s,e={}){let r={...p,...e},o=u(a);return o.delete(r.pageParam),s==null||s===""?o.delete(t):o.set(t,s),o}function L(a,t,s={}){let e={...p,...s},r=u(a);return r.set(e.pageParam,String(t)),r}function h(a,t,s={}){let e={...p,...s},r=u(a);return r.delete(e.pageParam),r.set(e.sizeParam,String(t)),r}function y(a,t,s={}){let e={...p,...s},r=u(a);r.delete(e.pageParam);let o=A(t);return o?r.set(e.sortParam,o):r.delete(e.sortParam),r}function U(a,t=[]){let s=new URLSearchParams;for(let e of t){let r=a.get(e);r!==null&&s.set(e,r);}return s}function R(a,t){let{schema:s,url:e,with:r}=t;return {read(o){let n=a.fromSearchParams(o,{schema:s,url:e,with:r});return {params:n,clamped:n.clamped===true}},actions(o){return {setParam:(n,m)=>S(o,n,m,e),setPage:n=>L(o,n,e),setSize:n=>h(o,n,e),setSort:n=>y(o,n,e),reset:n=>U(o,n)}}}}function f(a,t){let{schema:s,setSearchParams:e,url:r,with:o}=t,n=R(a,{schema:s,url:r,with:o}),m=vue.computed(()=>vue.toValue(t.searchParams)),d=vue.computed(()=>n.read(m.value)),c=()=>n.actions(m.value);return {params:vue.computed(()=>d.value.params),clamped:vue.computed(()=>d.value.clamped),searchParams:m,setParam:(i,x)=>e(c().setParam(i,x)),setPage:i=>e(c().setPage(i)),setSize:i=>e(c().setSize(i)),setSort:i=>e(c().setSort(i)),reset:i=>e(c().reset(i))}}function T(a,t){let s=vueRouter.useRoute(),e=vueRouter.useRouter();return f(a,{...t,searchParams:()=>P(s.query),setSearchParams:r=>{e.push({query:g(r)});}})}exports.useListParams=T;exports.useListParamsBase=f;//# sourceMappingURL=index.cjs.map
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/route-query.ts","../../src/url.ts","../../src/list-state.ts","../../src/vue/use-list-params-base.ts","../../src/vue/use-list-params.ts"],"names":["queryToSearchParams","query","searchParams","key","value","item","searchParamsToQuery","values","URL_DEFAULTS","encodeSort","sort","i","clone","setParam","name","url","cfg","next","setPage","page","setSize","size","setSort","encoded","resetParams","keep","createListState","resource","options","schema","withRel","params","useListParamsBase","setSearchParams","state","computed","toValue","read","actions","useListParams","route","useRoute","router","useRouter"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n^ * A route query as vue-router gives it: v4 has `string | null | (string | null)[]` per key,\n * v5 always an array. `null` is a key without `=` (`?q`).\n */\nexport type RouteQuery = Record<string, string | null | (string | null)[] | undefined>;\n\n/** Route query → search params: a repeated key keeps every value; `null` becomes an empty value. */\nexport function queryToSearchParams(query: RouteQuery): URLSearchParams {\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n for (const item of Array.isArray(value) ? value : [value]) {\n if (item !== undefined) searchParams.append(key, item ?? \"\");\n }\n }\n return searchParams;\n}\n\n/** Search params → route query for `router.push({ query })`: one value → string, a repeated key → array. */\nexport function searchParamsToQuery(searchParams: URLSearchParams): Record<string, string | string[]> {\n const query: Record<string, string | string[]> = {};\n for (const key of new Set(searchParams.keys())) {\n const values = searchParams.getAll(key);\n query[key] = values.length > 1 ? values : (values[0] ?? \"\");\n }\n return query;\n}\n","import { DEFAULT_MAX_OFFSET, DEFAULT_MAX_PER_PAGE, DEFAULT_MAX_SORT_FIELDS, DEFAULT_PER_PAGE } from \"@querykitjs/core\";\n\nimport { buildListParams, clampInt } from \"./query\";\nimport type { FieldDescriptor, ListSchema } from \"./schema\";\nimport type { FieldCondition, Filter, FilterNode, FilterValue, ListParams, ListPayload, Sort, SortDirection, SortInput } from \"./types\";\n\n/** URL param nomlari (sahifa/o'lcham/sort) — sozlanadigan. */\nexport interface UrlConfig {\n pageParam?: string;\n sizeParam?: string;\n sortParam?: string;\n}\n\nconst URL_DEFAULTS: Required<UrlConfig> = {\n pageParam: \"page\",\n sizeParam: \"size\",\n sortParam: \"sortType\",\n};\n\n/**\n * `\"-createdAt,id,-appNumber\"` → `[{ key, direction }, …]` — comma-separated,\n * order preserved, `-` prefix = `desc` (else `asc`). Empty → `undefined`.\n */\nexport function decodeSort(value: string | null | undefined): Sort | undefined {\n if (!value) return undefined;\n const items = value\n .split(\",\")\n .map(s => s.trim())\n .filter(Boolean)\n .map(tok => {\n const desc = tok.startsWith(\"-\");\n return { key: desc ? tok.slice(1) : tok, direction: (desc ? \"desc\" : \"asc\") as SortDirection };\n })\n .filter(i => i.key); // drop empty keys (e.g. a lone \"-\")\n return items.length ? items : undefined;\n}\n\n/** `[{ key, direction }, …]` (or `\"-field\"` shorthand strings) → `\"-createdAt,id,-appNumber\"` (empty → `\"\"`). */\nexport function encodeSort(sort: SortInput): string {\n return sort\n .map(i => (typeof i === \"string\" ? i.trim() : i && i.key ? (i.direction === \"desc\" ? \"-\" : \"\") + i.key : \"\"))\n .filter(Boolean)\n .join(\",\");\n}\n\n/**\n * Schema + searchParams → filter. Applies `range`/`between`/`search`/`split`/\n * `default`/`trim`; empty/absent params are dropped. Returns a flat\n * `FieldCondition[]` (implicit AND), or a tree when a `search` OR-group is\n * present (`{ and: [orGroup, …conditions] }`).\n */\nexport function schemaToFilter(schema: ListSchema, searchParams: URLSearchParams): Filter {\n const conditions: FieldCondition[] = [];\n const groups: FilterNode[] = [];\n\n for (const [param, d] of Object.entries(schema) as [string, FieldDescriptor][]) {\n const key = d.key ?? param;\n\n // range → two inclusive conditions (>= , <=)\n if (d.range) {\n const from = searchParams.get(d.range[0]);\n const to = searchParams.get(d.range[1]);\n if (from) conditions.push(cond(key, \">=\", from));\n if (to) conditions.push(cond(key, \"<=\", to));\n continue;\n }\n\n // between → one condition with a 2-tuple value\n if (d.between) {\n const from = searchParams.get(d.between[0]);\n const to = searchParams.get(d.between[1]);\n if (from && to) conditions.push(cond(key, \"between\", [from, to]));\n continue;\n }\n\n // read value, falling back to `default` only when the param is ABSENT\n // (an empty `?x=` means the user cleared the filter → prune, don't default).\n const raw = searchParams.get(param);\n let value: FilterValue | undefined = raw !== null ? raw : d.default;\n if (typeof value === \"string\" && d.trim) value = value.trim();\n if (value === undefined || value === null || value === \"\") continue;\n if (Array.isArray(value) && value.length === 0) continue;\n\n // search → OR of `contains` across the listed fields (needs a string)\n if (d.search && typeof value === \"string\") {\n groups.push({ or: d.search.map(field => cond(field, \"%_%\", value as string)) });\n continue;\n }\n\n // split → comma-string → array (for `in`/`notIn`); guarded against empties\n if (d.split && typeof value === \"string\") {\n const delimiter = d.split === true ? \",\" : d.split;\n const parts = value\n .split(delimiter)\n .map(s => s.trim())\n .filter(Boolean);\n if (parts.length === 0) continue;\n value = parts;\n }\n\n conditions.push(cond(key, d.operation ?? \"=\", value));\n }\n\n return groups.length === 0 ? conditions : { and: [...groups, ...conditions] };\n}\n\nfunction cond(key: string, operation: FieldCondition[\"operation\"], value: FilterValue): FieldCondition {\n return { key, operation, value };\n}\n\n/**\n * The bounds the URL readers clamp to — the builder's own (`createRegistry`\n * `defaults`); an absent one is the core default.\n */\nexport interface UrlLimits {\n perPage?: number;\n maxPerPage?: number;\n maxOffset?: number;\n maxSortFields?: number;\n}\n\n/** {@link ListParams} read from a URL. `clamped` — the URL's page or sort was cut to the limits. */\nexport type UrlListParams = ListParams & { clamped: boolean };\n\n/**\n * A list payload built from a URL. `clamped` is non-enumerable: readable in code,\n * never serialized, so the wire payload is unchanged.\n */\nexport type UrlListPayload = ListPayload & { readonly clamped?: boolean };\n\n/** Marks a payload with a non-enumerable `clamped` flag. */\nexport function withClamped(payload: ListPayload, clamped: boolean): UrlListPayload {\n return Object.defineProperty(payload, \"clamped\", { value: clamped, enumerable: false });\n}\n\n/** The last page the builder accepts: `(page - 1) × perPage ≤ maxOffset`. */\nfunction lastPage(perPage: number | undefined, limits: UrlLimits): number {\n const resolved = clampInt(perPage, limits.perPage ?? DEFAULT_PER_PAGE, limits.maxPerPage ?? DEFAULT_MAX_PER_PAGE);\n return Math.floor((limits.maxOffset ?? DEFAULT_MAX_OFFSET) / resolved) + 1;\n}\n\n/**\n * searchParams → normalizatsiyalanmagan {@link ListParams} (filter/sort/page/perPage).\n * The URL is user input: a page past `maxOffset` becomes the last allowed page and a\n * sort past `maxSortFields` keeps its first fields, with `clamped: true` — so a\n * hand-edited `?page=9999` never throws during render. The builders still throw\n * for a direct call.\n */\nexport function readListParams(schema: ListSchema, searchParams: URLSearchParams, url: UrlConfig = {}, limits: UrlLimits = {}): UrlListParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const size = searchParams.get(cfg.sizeParam);\n const perPage = size ? Number(size) : undefined;\n const page = Math.trunc(Number(searchParams.get(cfg.pageParam))) || 1;\n const sort = decodeSort(searchParams.get(cfg.sortParam));\n const maxPage = lastPage(perPage, limits);\n const maxSortFields = limits.maxSortFields ?? DEFAULT_MAX_SORT_FIELDS;\n const isPageClamped = page > maxPage;\n const isSortClamped = sort !== undefined && sort.length > maxSortFields;\n return {\n filter: schemaToFilter(schema, searchParams),\n sort: isSortClamped ? sort.slice(0, maxSortFields) : sort,\n page: isPageClamped ? maxPage : page,\n perPage,\n clamped: isPageClamped || isSortClamped,\n };\n}\n\n/** searchParams → to'liq, yuborishga tayyor {@link ListPayload} (core default limits; clamped like {@link readListParams}). */\nexport function searchParamsToPayload(schema: ListSchema, searchParams: URLSearchParams, url?: UrlConfig): UrlListPayload {\n const params = readListParams(schema, searchParams, url);\n return withClamped(buildListParams(params), params.clamped);\n}\n\n/* ------------------------- URL yozish (immutable) ------------------------- */\n/* Har biri YANGI URLSearchParams qaytaradi; filter/sort o'zgarsa `page` reset. */\n\nfunction clone(searchParams: URLSearchParams): URLSearchParams {\n return new URLSearchParams(searchParams);\n}\n\n/** Filter param'ini o'rnatadi/o'chiradi va `page`ni reset qiladi. */\nexport function setParam(searchParams: URLSearchParams, name: string, value: string | null | undefined, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.delete(cfg.pageParam);\n if (value === null || value === undefined || value === \"\") next.delete(name);\n else next.set(name, value);\n return next;\n}\n\n/** Sahifani o'rnatadi (`page` reset qilinmaydi). */\nexport function setPage(searchParams: URLSearchParams, page: number, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.set(cfg.pageParam, String(page));\n return next;\n}\n\n/** Sahifa o'lchamini o'rnatadi va `page`ni reset qiladi. */\nexport function setSize(searchParams: URLSearchParams, size: number, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.delete(cfg.pageParam);\n next.set(cfg.sizeParam, String(size));\n return next;\n}\n\n/** Sortni o'rnatadi (`[{ key, direction }, …]`) va `page`ni reset qiladi. */\nexport function setSort(searchParams: URLSearchParams, sort: SortInput, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.delete(cfg.pageParam);\n const encoded = encodeSort(sort);\n if (encoded) next.set(cfg.sortParam, encoded);\n else next.delete(cfg.sortParam);\n return next;\n}\n\n/** Barcha list param'larini tozalaydi (ixtiyoriy saqlanadigan kalitlar bilan). */\nexport function resetParams(searchParams: URLSearchParams, keep: string[] = []): URLSearchParams {\n const next = new URLSearchParams();\n for (const key of keep) {\n const value = searchParams.get(key);\n if (value !== null) next.set(key, value);\n }\n return next;\n}\n","import type { AdapterName, WithFor } from \"./adapter\";\nimport type { Resource } from \"./registry\";\nimport type { ListSchema } from \"./schema\";\nimport type { ListPayload, SortInput } from \"./types\";\nimport type { UrlConfig } from \"./url\";\nimport { resetParams, setPage, setParam, setSize, setSort } from \"./url\";\n\nexport interface ListStateOptions<A extends AdapterName> {\n /** URL↔filter schema ({@link defineListSchema} / `resource.schema`). */\n schema: ListSchema;\n /** URL param names (page/size/sortType) — optional. */\n url?: UrlConfig;\n /** Fixed relations to load on every request (not URL-driven). */\n with?: WithFor<A>;\n}\n\nexport interface ListStateRead {\n /** Ready-to-send list payload (built from the URL via the resource's defaults). */\n params: ListPayload;\n /** The URL's page or sort was past the limits and was clamped (`?page=9999` → the last allowed page). */\n clamped: boolean;\n}\n\n/** URL setters: each returns the NEXT search params and never mutates the given ones. */\nexport interface ListStateActions {\n /** Set/clear a filter param (resets `page`). */\n setParam: (name: string, value: string | null | undefined) => URLSearchParams;\n setPage: (page: number) => URLSearchParams;\n /** Resets `page`. */\n setSize: (size: number) => URLSearchParams;\n /** Resets `page`; an empty sort clears the param. */\n setSort: (sort: SortInput) => URLSearchParams;\n /** Clears every param except `keep`. */\n reset: (keep?: string[]) => URLSearchParams;\n}\n\nexport interface ListState {\n read: (searchParams: URLSearchParams) => ListStateRead;\n actions: (searchParams: URLSearchParams) => ListStateActions;\n}\n\n/**\n * Framework-free list URL state — the logic behind `useListParams` in `@querykitjs/web/react`\n * and `@querykitjs/web/vue`, for any other framework (Svelte, Solid, vanilla). Pure: it reads\n * search params and returns the next ones; navigating is the caller's job.\n *\n * @example\n * ```ts\n * const list = createListState(users, { schema: usersSchema });\n * const { params, clamped } = list.read(new URLSearchParams(location.search));\n * history.pushState(null, \"\", `?${list.actions(new URLSearchParams(location.search)).setPage(2)}`);\n * ```\n */\nexport function createListState<T, A extends AdapterName>(resource: Resource<T, A>, options: ListStateOptions<A>): ListState {\n const { schema, url, with: withRel } = options;\n return {\n read(searchParams) {\n const params = resource.fromSearchParams(searchParams, { schema, url, with: withRel });\n return { params, clamped: params.clamped === true };\n },\n actions(searchParams) {\n return {\n setParam: (name, value) => setParam(searchParams, name, value, url),\n setPage: page => setPage(searchParams, page, url),\n setSize: size => setSize(searchParams, size, url),\n setSort: sort => setSort(searchParams, sort, url),\n reset: keep => resetParams(searchParams, keep),\n };\n },\n };\n}\n","import { computed, type ComputedRef, type MaybeRefOrGetter, toValue } from \"vue\";\n\nimport type { AdapterName, WithFor } from \"../adapter\";\nimport { createListState } from \"../list-state\";\nimport type { Resource } from \"../registry\";\nimport type { ListSchema } from \"../schema\";\nimport type { ListPayload, SortInput } from \"../types\";\nimport type { UrlConfig } from \"../url\";\n\nexport interface UseListParamsBaseOptions<A extends AdapterName> {\n /** URL↔filter schema ({@link defineListSchema} / `resource.schema`). */\n schema: ListSchema;\n /** Current URL search params — a ref, a getter or a plain value (from your router). */\n searchParams: MaybeRefOrGetter<URLSearchParams>;\n /** Apply new search params (`(next) => void`). */\n setSearchParams: (next: URLSearchParams) => void;\n /** URL param names (page/size/sortType) — optional. */\n url?: UrlConfig;\n /** Fixed relations to load on every request (not URL-driven). */\n with?: WithFor<A>;\n}\n\nexport interface UseListParamsResult {\n /** Ready-to-send list payload (built from the URL via the resource's defaults). */\n params: ComputedRef<ListPayload>;\n /**\n * The URL's page or sort was past the limits and was clamped (`?page=9999` → the last\n * allowed page). The app may rewrite the URL (`setPage(params.value.page)`).\n */\n clamped: ComputedRef<boolean>;\n searchParams: ComputedRef<URLSearchParams>;\n /** Set/clear a filter param (resets `page`). */\n setParam: (name: string, value: string | null | undefined) => void;\n setPage: (page: number) => void;\n setSize: (size: number) => void;\n setSort: (sort: SortInput) => void;\n reset: (keep?: string[]) => void;\n}\n\n/**\n * Router-agnostic list URL-state composable — the Vue twin of the React\n * `useListParamsBase`: the same reads, clamping and setters, as computed refs. Bring your\n * own `searchParams`/`setSearchParams` (Nuxt `useRoute`, a custom router, …).\n */\nexport function useListParamsBase<T, A extends AdapterName>(resource: Resource<T, A>, options: UseListParamsBaseOptions<A>): UseListParamsResult {\n const { schema, setSearchParams, url, with: withRel } = options;\n const state = createListState(resource, { schema, url, with: withRel });\n const searchParams = computed(() => toValue(options.searchParams));\n const read = computed(() => state.read(searchParams.value));\n const actions = () => state.actions(searchParams.value);\n\n return {\n params: computed(() => read.value.params),\n clamped: computed(() => read.value.clamped),\n searchParams,\n setParam: (name, value) => setSearchParams(actions().setParam(name, value)),\n setPage: page => setSearchParams(actions().setPage(page)),\n setSize: size => setSearchParams(actions().setSize(size)),\n setSort: sort => setSearchParams(actions().setSort(sort)),\n reset: keep => setSearchParams(actions().reset(keep)),\n };\n}\n","import { useRoute, useRouter } from \"vue-router\";\n\nimport type { AdapterName, WithFor } from \"../adapter\";\nimport type { Resource } from \"../registry\";\nimport type { ListSchema } from \"../schema\";\nimport type { UrlConfig } from \"../url\";\nimport { queryToSearchParams, searchParamsToQuery } from \"../route-query\";\nimport { useListParamsBase, type UseListParamsResult } from \"./use-list-params-base\";\n\nexport interface UseListParamsOptions<A extends AdapterName> {\n /** URL↔filter schema ({@link defineListSchema} / `resource.schema`). */\n schema: ListSchema;\n /** URL param names (page/size/sortType) — optional. */\n url?: UrlConfig;\n /** Fixed relations to load on every request (not URL-driven). */\n with?: WithFor<A>;\n}\n\n/**\n * List URL-state composable — turnkey with **vue-router** 4 / 5 (`useRoute` + `useRouter`).\n * Reads filters/sort/page from the route query via the schema, builds a ready-to-send\n * payload through the `resource`, and returns URL setters that `router.push({ query })` (a\n * new history entry; filter/sort/size changes reset `page`). Does **not** fetch.\n *\n * @example\n * ```ts\n * const users = qk.resource<IUser>(\"users\");\n * const { params, setPage, setSort } = useListParams(users, { schema: usersSchema });\n * const { data } = useQuery({ queryKey: computed(() => users.keys.list(params.value)), queryFn: () => post(\"/users/list\", params.value) });\n * ```\n */\nexport function useListParams<T, A extends AdapterName>(resource: Resource<T, A>, options: UseListParamsOptions<A>): UseListParamsResult {\n const route = useRoute();\n const router = useRouter();\n return useListParamsBase(resource, {\n ...options,\n searchParams: () => queryToSearchParams(route.query),\n setSearchParams: next => void router.push({ query: searchParamsToQuery(next) }),\n });\n}\n"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { AdapterName, ListSchema, UrlConfig, WithFor, ListPayload, SortInput, Resource } from '../index.cjs';
|
|
2
|
+
import { MaybeRefOrGetter, ComputedRef } from 'vue';
|
|
3
|
+
import '@querykitjs/core';
|
|
4
|
+
|
|
5
|
+
interface UseListParamsBaseOptions<A extends AdapterName> {
|
|
6
|
+
/** URL↔filter schema ({@link defineListSchema} / `resource.schema`). */
|
|
7
|
+
schema: ListSchema;
|
|
8
|
+
/** Current URL search params — a ref, a getter or a plain value (from your router). */
|
|
9
|
+
searchParams: MaybeRefOrGetter<URLSearchParams>;
|
|
10
|
+
/** Apply new search params (`(next) => void`). */
|
|
11
|
+
setSearchParams: (next: URLSearchParams) => void;
|
|
12
|
+
/** URL param names (page/size/sortType) — optional. */
|
|
13
|
+
url?: UrlConfig;
|
|
14
|
+
/** Fixed relations to load on every request (not URL-driven). */
|
|
15
|
+
with?: WithFor<A>;
|
|
16
|
+
}
|
|
17
|
+
interface UseListParamsResult {
|
|
18
|
+
/** Ready-to-send list payload (built from the URL via the resource's defaults). */
|
|
19
|
+
params: ComputedRef<ListPayload>;
|
|
20
|
+
/**
|
|
21
|
+
* The URL's page or sort was past the limits and was clamped (`?page=9999` → the last
|
|
22
|
+
* allowed page). The app may rewrite the URL (`setPage(params.value.page)`).
|
|
23
|
+
*/
|
|
24
|
+
clamped: ComputedRef<boolean>;
|
|
25
|
+
searchParams: ComputedRef<URLSearchParams>;
|
|
26
|
+
/** Set/clear a filter param (resets `page`). */
|
|
27
|
+
setParam: (name: string, value: string | null | undefined) => void;
|
|
28
|
+
setPage: (page: number) => void;
|
|
29
|
+
setSize: (size: number) => void;
|
|
30
|
+
setSort: (sort: SortInput) => void;
|
|
31
|
+
reset: (keep?: string[]) => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Router-agnostic list URL-state composable — the Vue twin of the React
|
|
35
|
+
* `useListParamsBase`: the same reads, clamping and setters, as computed refs. Bring your
|
|
36
|
+
* own `searchParams`/`setSearchParams` (Nuxt `useRoute`, a custom router, …).
|
|
37
|
+
*/
|
|
38
|
+
declare function useListParamsBase<T, A extends AdapterName>(resource: Resource<T, A>, options: UseListParamsBaseOptions<A>): UseListParamsResult;
|
|
39
|
+
|
|
40
|
+
interface UseListParamsOptions<A extends AdapterName> {
|
|
41
|
+
/** URL↔filter schema ({@link defineListSchema} / `resource.schema`). */
|
|
42
|
+
schema: ListSchema;
|
|
43
|
+
/** URL param names (page/size/sortType) — optional. */
|
|
44
|
+
url?: UrlConfig;
|
|
45
|
+
/** Fixed relations to load on every request (not URL-driven). */
|
|
46
|
+
with?: WithFor<A>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* List URL-state composable — turnkey with **vue-router** 4 / 5 (`useRoute` + `useRouter`).
|
|
50
|
+
* Reads filters/sort/page from the route query via the schema, builds a ready-to-send
|
|
51
|
+
* payload through the `resource`, and returns URL setters that `router.push({ query })` (a
|
|
52
|
+
* new history entry; filter/sort/size changes reset `page`). Does **not** fetch.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* const users = qk.resource<IUser>("users");
|
|
57
|
+
* const { params, setPage, setSort } = useListParams(users, { schema: usersSchema });
|
|
58
|
+
* const { data } = useQuery({ queryKey: computed(() => users.keys.list(params.value)), queryFn: () => post("/users/list", params.value) });
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
declare function useListParams<T, A extends AdapterName>(resource: Resource<T, A>, options: UseListParamsOptions<A>): UseListParamsResult;
|
|
62
|
+
|
|
63
|
+
export { useListParams, useListParamsBase };
|
|
64
|
+
export type { UseListParamsBaseOptions, UseListParamsOptions, UseListParamsResult };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { AdapterName, ListSchema, UrlConfig, WithFor, ListPayload, SortInput, Resource } from '../index.js';
|
|
2
|
+
import { MaybeRefOrGetter, ComputedRef } from 'vue';
|
|
3
|
+
import '@querykitjs/core';
|
|
4
|
+
|
|
5
|
+
interface UseListParamsBaseOptions<A extends AdapterName> {
|
|
6
|
+
/** URL↔filter schema ({@link defineListSchema} / `resource.schema`). */
|
|
7
|
+
schema: ListSchema;
|
|
8
|
+
/** Current URL search params — a ref, a getter or a plain value (from your router). */
|
|
9
|
+
searchParams: MaybeRefOrGetter<URLSearchParams>;
|
|
10
|
+
/** Apply new search params (`(next) => void`). */
|
|
11
|
+
setSearchParams: (next: URLSearchParams) => void;
|
|
12
|
+
/** URL param names (page/size/sortType) — optional. */
|
|
13
|
+
url?: UrlConfig;
|
|
14
|
+
/** Fixed relations to load on every request (not URL-driven). */
|
|
15
|
+
with?: WithFor<A>;
|
|
16
|
+
}
|
|
17
|
+
interface UseListParamsResult {
|
|
18
|
+
/** Ready-to-send list payload (built from the URL via the resource's defaults). */
|
|
19
|
+
params: ComputedRef<ListPayload>;
|
|
20
|
+
/**
|
|
21
|
+
* The URL's page or sort was past the limits and was clamped (`?page=9999` → the last
|
|
22
|
+
* allowed page). The app may rewrite the URL (`setPage(params.value.page)`).
|
|
23
|
+
*/
|
|
24
|
+
clamped: ComputedRef<boolean>;
|
|
25
|
+
searchParams: ComputedRef<URLSearchParams>;
|
|
26
|
+
/** Set/clear a filter param (resets `page`). */
|
|
27
|
+
setParam: (name: string, value: string | null | undefined) => void;
|
|
28
|
+
setPage: (page: number) => void;
|
|
29
|
+
setSize: (size: number) => void;
|
|
30
|
+
setSort: (sort: SortInput) => void;
|
|
31
|
+
reset: (keep?: string[]) => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Router-agnostic list URL-state composable — the Vue twin of the React
|
|
35
|
+
* `useListParamsBase`: the same reads, clamping and setters, as computed refs. Bring your
|
|
36
|
+
* own `searchParams`/`setSearchParams` (Nuxt `useRoute`, a custom router, …).
|
|
37
|
+
*/
|
|
38
|
+
declare function useListParamsBase<T, A extends AdapterName>(resource: Resource<T, A>, options: UseListParamsBaseOptions<A>): UseListParamsResult;
|
|
39
|
+
|
|
40
|
+
interface UseListParamsOptions<A extends AdapterName> {
|
|
41
|
+
/** URL↔filter schema ({@link defineListSchema} / `resource.schema`). */
|
|
42
|
+
schema: ListSchema;
|
|
43
|
+
/** URL param names (page/size/sortType) — optional. */
|
|
44
|
+
url?: UrlConfig;
|
|
45
|
+
/** Fixed relations to load on every request (not URL-driven). */
|
|
46
|
+
with?: WithFor<A>;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* List URL-state composable — turnkey with **vue-router** 4 / 5 (`useRoute` + `useRouter`).
|
|
50
|
+
* Reads filters/sort/page from the route query via the schema, builds a ready-to-send
|
|
51
|
+
* payload through the `resource`, and returns URL setters that `router.push({ query })` (a
|
|
52
|
+
* new history entry; filter/sort/size changes reset `page`). Does **not** fetch.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```ts
|
|
56
|
+
* const users = qk.resource<IUser>("users");
|
|
57
|
+
* const { params, setPage, setSort } = useListParams(users, { schema: usersSchema });
|
|
58
|
+
* const { data } = useQuery({ queryKey: computed(() => users.keys.list(params.value)), queryFn: () => post("/users/list", params.value) });
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
declare function useListParams<T, A extends AdapterName>(resource: Resource<T, A>, options: UseListParamsOptions<A>): UseListParamsResult;
|
|
62
|
+
|
|
63
|
+
export { useListParams, useListParamsBase };
|
|
64
|
+
export type { UseListParamsBaseOptions, UseListParamsOptions, UseListParamsResult };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {useRoute,useRouter}from'vue-router';import {computed,toValue}from'vue';import'@querykitjs/core';function P(a){let t=new URLSearchParams;for(let[s,e]of Object.entries(a))for(let r of Array.isArray(e)?e:[e])r!==void 0&&t.append(s,r??"");return t}function g(a){let t={};for(let s of new Set(a.keys())){let e=a.getAll(s);t[s]=e.length>1?e:e[0]??"";}return t}var p={pageParam:"page",sizeParam:"size",sortParam:"sortType"};function A(a){return a.map(t=>typeof t=="string"?t.trim():t&&t.key?(t.direction==="desc"?"-":"")+t.key:"").filter(Boolean).join(",")}function u(a){return new URLSearchParams(a)}function S(a,t,s,e={}){let r={...p,...e},o=u(a);return o.delete(r.pageParam),s==null||s===""?o.delete(t):o.set(t,s),o}function L(a,t,s={}){let e={...p,...s},r=u(a);return r.set(e.pageParam,String(t)),r}function h(a,t,s={}){let e={...p,...s},r=u(a);return r.delete(e.pageParam),r.set(e.sizeParam,String(t)),r}function y(a,t,s={}){let e={...p,...s},r=u(a);r.delete(e.pageParam);let o=A(t);return o?r.set(e.sortParam,o):r.delete(e.sortParam),r}function U(a,t=[]){let s=new URLSearchParams;for(let e of t){let r=a.get(e);r!==null&&s.set(e,r);}return s}function R(a,t){let{schema:s,url:e,with:r}=t;return {read(o){let n=a.fromSearchParams(o,{schema:s,url:e,with:r});return {params:n,clamped:n.clamped===true}},actions(o){return {setParam:(n,m)=>S(o,n,m,e),setPage:n=>L(o,n,e),setSize:n=>h(o,n,e),setSort:n=>y(o,n,e),reset:n=>U(o,n)}}}}function f(a,t){let{schema:s,setSearchParams:e,url:r,with:o}=t,n=R(a,{schema:s,url:r,with:o}),m=computed(()=>toValue(t.searchParams)),d=computed(()=>n.read(m.value)),c=()=>n.actions(m.value);return {params:computed(()=>d.value.params),clamped:computed(()=>d.value.clamped),searchParams:m,setParam:(i,x)=>e(c().setParam(i,x)),setPage:i=>e(c().setPage(i)),setSize:i=>e(c().setSize(i)),setSort:i=>e(c().setSort(i)),reset:i=>e(c().reset(i))}}function T(a,t){let s=useRoute(),e=useRouter();return f(a,{...t,searchParams:()=>P(s.query),setSearchParams:r=>{e.push({query:g(r)});}})}export{T as useListParams,f as useListParamsBase};//# sourceMappingURL=index.js.map
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/route-query.ts","../../src/url.ts","../../src/list-state.ts","../../src/vue/use-list-params-base.ts","../../src/vue/use-list-params.ts"],"names":["queryToSearchParams","query","searchParams","key","value","item","searchParamsToQuery","values","URL_DEFAULTS","encodeSort","sort","i","clone","setParam","name","url","cfg","next","setPage","page","setSize","size","setSort","encoded","resetParams","keep","createListState","resource","options","schema","withRel","params","useListParamsBase","setSearchParams","state","computed","toValue","read","actions","useListParams","route","useRoute","router","useRouter"],"mappings":"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","file":"index.js","sourcesContent":["/**\n^ * A route query as vue-router gives it: v4 has `string | null | (string | null)[]` per key,\n * v5 always an array. `null` is a key without `=` (`?q`).\n */\nexport type RouteQuery = Record<string, string | null | (string | null)[] | undefined>;\n\n/** Route query → search params: a repeated key keeps every value; `null` becomes an empty value. */\nexport function queryToSearchParams(query: RouteQuery): URLSearchParams {\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(query)) {\n for (const item of Array.isArray(value) ? value : [value]) {\n if (item !== undefined) searchParams.append(key, item ?? \"\");\n }\n }\n return searchParams;\n}\n\n/** Search params → route query for `router.push({ query })`: one value → string, a repeated key → array. */\nexport function searchParamsToQuery(searchParams: URLSearchParams): Record<string, string | string[]> {\n const query: Record<string, string | string[]> = {};\n for (const key of new Set(searchParams.keys())) {\n const values = searchParams.getAll(key);\n query[key] = values.length > 1 ? values : (values[0] ?? \"\");\n }\n return query;\n}\n","import { DEFAULT_MAX_OFFSET, DEFAULT_MAX_PER_PAGE, DEFAULT_MAX_SORT_FIELDS, DEFAULT_PER_PAGE } from \"@querykitjs/core\";\n\nimport { buildListParams, clampInt } from \"./query\";\nimport type { FieldDescriptor, ListSchema } from \"./schema\";\nimport type { FieldCondition, Filter, FilterNode, FilterValue, ListParams, ListPayload, Sort, SortDirection, SortInput } from \"./types\";\n\n/** URL param nomlari (sahifa/o'lcham/sort) — sozlanadigan. */\nexport interface UrlConfig {\n pageParam?: string;\n sizeParam?: string;\n sortParam?: string;\n}\n\nconst URL_DEFAULTS: Required<UrlConfig> = {\n pageParam: \"page\",\n sizeParam: \"size\",\n sortParam: \"sortType\",\n};\n\n/**\n * `\"-createdAt,id,-appNumber\"` → `[{ key, direction }, …]` — comma-separated,\n * order preserved, `-` prefix = `desc` (else `asc`). Empty → `undefined`.\n */\nexport function decodeSort(value: string | null | undefined): Sort | undefined {\n if (!value) return undefined;\n const items = value\n .split(\",\")\n .map(s => s.trim())\n .filter(Boolean)\n .map(tok => {\n const desc = tok.startsWith(\"-\");\n return { key: desc ? tok.slice(1) : tok, direction: (desc ? \"desc\" : \"asc\") as SortDirection };\n })\n .filter(i => i.key); // drop empty keys (e.g. a lone \"-\")\n return items.length ? items : undefined;\n}\n\n/** `[{ key, direction }, …]` (or `\"-field\"` shorthand strings) → `\"-createdAt,id,-appNumber\"` (empty → `\"\"`). */\nexport function encodeSort(sort: SortInput): string {\n return sort\n .map(i => (typeof i === \"string\" ? i.trim() : i && i.key ? (i.direction === \"desc\" ? \"-\" : \"\") + i.key : \"\"))\n .filter(Boolean)\n .join(\",\");\n}\n\n/**\n * Schema + searchParams → filter. Applies `range`/`between`/`search`/`split`/\n * `default`/`trim`; empty/absent params are dropped. Returns a flat\n * `FieldCondition[]` (implicit AND), or a tree when a `search` OR-group is\n * present (`{ and: [orGroup, …conditions] }`).\n */\nexport function schemaToFilter(schema: ListSchema, searchParams: URLSearchParams): Filter {\n const conditions: FieldCondition[] = [];\n const groups: FilterNode[] = [];\n\n for (const [param, d] of Object.entries(schema) as [string, FieldDescriptor][]) {\n const key = d.key ?? param;\n\n // range → two inclusive conditions (>= , <=)\n if (d.range) {\n const from = searchParams.get(d.range[0]);\n const to = searchParams.get(d.range[1]);\n if (from) conditions.push(cond(key, \">=\", from));\n if (to) conditions.push(cond(key, \"<=\", to));\n continue;\n }\n\n // between → one condition with a 2-tuple value\n if (d.between) {\n const from = searchParams.get(d.between[0]);\n const to = searchParams.get(d.between[1]);\n if (from && to) conditions.push(cond(key, \"between\", [from, to]));\n continue;\n }\n\n // read value, falling back to `default` only when the param is ABSENT\n // (an empty `?x=` means the user cleared the filter → prune, don't default).\n const raw = searchParams.get(param);\n let value: FilterValue | undefined = raw !== null ? raw : d.default;\n if (typeof value === \"string\" && d.trim) value = value.trim();\n if (value === undefined || value === null || value === \"\") continue;\n if (Array.isArray(value) && value.length === 0) continue;\n\n // search → OR of `contains` across the listed fields (needs a string)\n if (d.search && typeof value === \"string\") {\n groups.push({ or: d.search.map(field => cond(field, \"%_%\", value as string)) });\n continue;\n }\n\n // split → comma-string → array (for `in`/`notIn`); guarded against empties\n if (d.split && typeof value === \"string\") {\n const delimiter = d.split === true ? \",\" : d.split;\n const parts = value\n .split(delimiter)\n .map(s => s.trim())\n .filter(Boolean);\n if (parts.length === 0) continue;\n value = parts;\n }\n\n conditions.push(cond(key, d.operation ?? \"=\", value));\n }\n\n return groups.length === 0 ? conditions : { and: [...groups, ...conditions] };\n}\n\nfunction cond(key: string, operation: FieldCondition[\"operation\"], value: FilterValue): FieldCondition {\n return { key, operation, value };\n}\n\n/**\n * The bounds the URL readers clamp to — the builder's own (`createRegistry`\n * `defaults`); an absent one is the core default.\n */\nexport interface UrlLimits {\n perPage?: number;\n maxPerPage?: number;\n maxOffset?: number;\n maxSortFields?: number;\n}\n\n/** {@link ListParams} read from a URL. `clamped` — the URL's page or sort was cut to the limits. */\nexport type UrlListParams = ListParams & { clamped: boolean };\n\n/**\n * A list payload built from a URL. `clamped` is non-enumerable: readable in code,\n * never serialized, so the wire payload is unchanged.\n */\nexport type UrlListPayload = ListPayload & { readonly clamped?: boolean };\n\n/** Marks a payload with a non-enumerable `clamped` flag. */\nexport function withClamped(payload: ListPayload, clamped: boolean): UrlListPayload {\n return Object.defineProperty(payload, \"clamped\", { value: clamped, enumerable: false });\n}\n\n/** The last page the builder accepts: `(page - 1) × perPage ≤ maxOffset`. */\nfunction lastPage(perPage: number | undefined, limits: UrlLimits): number {\n const resolved = clampInt(perPage, limits.perPage ?? DEFAULT_PER_PAGE, limits.maxPerPage ?? DEFAULT_MAX_PER_PAGE);\n return Math.floor((limits.maxOffset ?? DEFAULT_MAX_OFFSET) / resolved) + 1;\n}\n\n/**\n * searchParams → normalizatsiyalanmagan {@link ListParams} (filter/sort/page/perPage).\n * The URL is user input: a page past `maxOffset` becomes the last allowed page and a\n * sort past `maxSortFields` keeps its first fields, with `clamped: true` — so a\n * hand-edited `?page=9999` never throws during render. The builders still throw\n * for a direct call.\n */\nexport function readListParams(schema: ListSchema, searchParams: URLSearchParams, url: UrlConfig = {}, limits: UrlLimits = {}): UrlListParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const size = searchParams.get(cfg.sizeParam);\n const perPage = size ? Number(size) : undefined;\n const page = Math.trunc(Number(searchParams.get(cfg.pageParam))) || 1;\n const sort = decodeSort(searchParams.get(cfg.sortParam));\n const maxPage = lastPage(perPage, limits);\n const maxSortFields = limits.maxSortFields ?? DEFAULT_MAX_SORT_FIELDS;\n const isPageClamped = page > maxPage;\n const isSortClamped = sort !== undefined && sort.length > maxSortFields;\n return {\n filter: schemaToFilter(schema, searchParams),\n sort: isSortClamped ? sort.slice(0, maxSortFields) : sort,\n page: isPageClamped ? maxPage : page,\n perPage,\n clamped: isPageClamped || isSortClamped,\n };\n}\n\n/** searchParams → to'liq, yuborishga tayyor {@link ListPayload} (core default limits; clamped like {@link readListParams}). */\nexport function searchParamsToPayload(schema: ListSchema, searchParams: URLSearchParams, url?: UrlConfig): UrlListPayload {\n const params = readListParams(schema, searchParams, url);\n return withClamped(buildListParams(params), params.clamped);\n}\n\n/* ------------------------- URL yozish (immutable) ------------------------- */\n/* Har biri YANGI URLSearchParams qaytaradi; filter/sort o'zgarsa `page` reset. */\n\nfunction clone(searchParams: URLSearchParams): URLSearchParams {\n return new URLSearchParams(searchParams);\n}\n\n/** Filter param'ini o'rnatadi/o'chiradi va `page`ni reset qiladi. */\nexport function setParam(searchParams: URLSearchParams, name: string, value: string | null | undefined, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.delete(cfg.pageParam);\n if (value === null || value === undefined || value === \"\") next.delete(name);\n else next.set(name, value);\n return next;\n}\n\n/** Sahifani o'rnatadi (`page` reset qilinmaydi). */\nexport function setPage(searchParams: URLSearchParams, page: number, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.set(cfg.pageParam, String(page));\n return next;\n}\n\n/** Sahifa o'lchamini o'rnatadi va `page`ni reset qiladi. */\nexport function setSize(searchParams: URLSearchParams, size: number, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.delete(cfg.pageParam);\n next.set(cfg.sizeParam, String(size));\n return next;\n}\n\n/** Sortni o'rnatadi (`[{ key, direction }, …]`) va `page`ni reset qiladi. */\nexport function setSort(searchParams: URLSearchParams, sort: SortInput, url: UrlConfig = {}): URLSearchParams {\n const cfg = { ...URL_DEFAULTS, ...url };\n const next = clone(searchParams);\n next.delete(cfg.pageParam);\n const encoded = encodeSort(sort);\n if (encoded) next.set(cfg.sortParam, encoded);\n else next.delete(cfg.sortParam);\n return next;\n}\n\n/** Barcha list param'larini tozalaydi (ixtiyoriy saqlanadigan kalitlar bilan). */\nexport function resetParams(searchParams: URLSearchParams, keep: string[] = []): URLSearchParams {\n const next = new URLSearchParams();\n for (const key of keep) {\n const value = searchParams.get(key);\n if (value !== null) next.set(key, value);\n }\n return next;\n}\n","import type { AdapterName, WithFor } from \"./adapter\";\nimport type { Resource } from \"./registry\";\nimport type { ListSchema } from \"./schema\";\nimport type { ListPayload, SortInput } from \"./types\";\nimport type { UrlConfig } from \"./url\";\nimport { resetParams, setPage, setParam, setSize, setSort } from \"./url\";\n\nexport interface ListStateOptions<A extends AdapterName> {\n /** URL↔filter schema ({@link defineListSchema} / `resource.schema`). */\n schema: ListSchema;\n /** URL param names (page/size/sortType) — optional. */\n url?: UrlConfig;\n /** Fixed relations to load on every request (not URL-driven). */\n with?: WithFor<A>;\n}\n\nexport interface ListStateRead {\n /** Ready-to-send list payload (built from the URL via the resource's defaults). */\n params: ListPayload;\n /** The URL's page or sort was past the limits and was clamped (`?page=9999` → the last allowed page). */\n clamped: boolean;\n}\n\n/** URL setters: each returns the NEXT search params and never mutates the given ones. */\nexport interface ListStateActions {\n /** Set/clear a filter param (resets `page`). */\n setParam: (name: string, value: string | null | undefined) => URLSearchParams;\n setPage: (page: number) => URLSearchParams;\n /** Resets `page`. */\n setSize: (size: number) => URLSearchParams;\n /** Resets `page`; an empty sort clears the param. */\n setSort: (sort: SortInput) => URLSearchParams;\n /** Clears every param except `keep`. */\n reset: (keep?: string[]) => URLSearchParams;\n}\n\nexport interface ListState {\n read: (searchParams: URLSearchParams) => ListStateRead;\n actions: (searchParams: URLSearchParams) => ListStateActions;\n}\n\n/**\n * Framework-free list URL state — the logic behind `useListParams` in `@querykitjs/web/react`\n * and `@querykitjs/web/vue`, for any other framework (Svelte, Solid, vanilla). Pure: it reads\n * search params and returns the next ones; navigating is the caller's job.\n *\n * @example\n * ```ts\n * const list = createListState(users, { schema: usersSchema });\n * const { params, clamped } = list.read(new URLSearchParams(location.search));\n * history.pushState(null, \"\", `?${list.actions(new URLSearchParams(location.search)).setPage(2)}`);\n * ```\n */\nexport function createListState<T, A extends AdapterName>(resource: Resource<T, A>, options: ListStateOptions<A>): ListState {\n const { schema, url, with: withRel } = options;\n return {\n read(searchParams) {\n const params = resource.fromSearchParams(searchParams, { schema, url, with: withRel });\n return { params, clamped: params.clamped === true };\n },\n actions(searchParams) {\n return {\n setParam: (name, value) => setParam(searchParams, name, value, url),\n setPage: page => setPage(searchParams, page, url),\n setSize: size => setSize(searchParams, size, url),\n setSort: sort => setSort(searchParams, sort, url),\n reset: keep => resetParams(searchParams, keep),\n };\n },\n };\n}\n","import { computed, type ComputedRef, type MaybeRefOrGetter, toValue } from \"vue\";\n\nimport type { AdapterName, WithFor } from \"../adapter\";\nimport { createListState } from \"../list-state\";\nimport type { Resource } from \"../registry\";\nimport type { ListSchema } from \"../schema\";\nimport type { ListPayload, SortInput } from \"../types\";\nimport type { UrlConfig } from \"../url\";\n\nexport interface UseListParamsBaseOptions<A extends AdapterName> {\n /** URL↔filter schema ({@link defineListSchema} / `resource.schema`). */\n schema: ListSchema;\n /** Current URL search params — a ref, a getter or a plain value (from your router). */\n searchParams: MaybeRefOrGetter<URLSearchParams>;\n /** Apply new search params (`(next) => void`). */\n setSearchParams: (next: URLSearchParams) => void;\n /** URL param names (page/size/sortType) — optional. */\n url?: UrlConfig;\n /** Fixed relations to load on every request (not URL-driven). */\n with?: WithFor<A>;\n}\n\nexport interface UseListParamsResult {\n /** Ready-to-send list payload (built from the URL via the resource's defaults). */\n params: ComputedRef<ListPayload>;\n /**\n * The URL's page or sort was past the limits and was clamped (`?page=9999` → the last\n * allowed page). The app may rewrite the URL (`setPage(params.value.page)`).\n */\n clamped: ComputedRef<boolean>;\n searchParams: ComputedRef<URLSearchParams>;\n /** Set/clear a filter param (resets `page`). */\n setParam: (name: string, value: string | null | undefined) => void;\n setPage: (page: number) => void;\n setSize: (size: number) => void;\n setSort: (sort: SortInput) => void;\n reset: (keep?: string[]) => void;\n}\n\n/**\n * Router-agnostic list URL-state composable — the Vue twin of the React\n * `useListParamsBase`: the same reads, clamping and setters, as computed refs. Bring your\n * own `searchParams`/`setSearchParams` (Nuxt `useRoute`, a custom router, …).\n */\nexport function useListParamsBase<T, A extends AdapterName>(resource: Resource<T, A>, options: UseListParamsBaseOptions<A>): UseListParamsResult {\n const { schema, setSearchParams, url, with: withRel } = options;\n const state = createListState(resource, { schema, url, with: withRel });\n const searchParams = computed(() => toValue(options.searchParams));\n const read = computed(() => state.read(searchParams.value));\n const actions = () => state.actions(searchParams.value);\n\n return {\n params: computed(() => read.value.params),\n clamped: computed(() => read.value.clamped),\n searchParams,\n setParam: (name, value) => setSearchParams(actions().setParam(name, value)),\n setPage: page => setSearchParams(actions().setPage(page)),\n setSize: size => setSearchParams(actions().setSize(size)),\n setSort: sort => setSearchParams(actions().setSort(sort)),\n reset: keep => setSearchParams(actions().reset(keep)),\n };\n}\n","import { useRoute, useRouter } from \"vue-router\";\n\nimport type { AdapterName, WithFor } from \"../adapter\";\nimport type { Resource } from \"../registry\";\nimport type { ListSchema } from \"../schema\";\nimport type { UrlConfig } from \"../url\";\nimport { queryToSearchParams, searchParamsToQuery } from \"../route-query\";\nimport { useListParamsBase, type UseListParamsResult } from \"./use-list-params-base\";\n\nexport interface UseListParamsOptions<A extends AdapterName> {\n /** URL↔filter schema ({@link defineListSchema} / `resource.schema`). */\n schema: ListSchema;\n /** URL param names (page/size/sortType) — optional. */\n url?: UrlConfig;\n /** Fixed relations to load on every request (not URL-driven). */\n with?: WithFor<A>;\n}\n\n/**\n * List URL-state composable — turnkey with **vue-router** 4 / 5 (`useRoute` + `useRouter`).\n * Reads filters/sort/page from the route query via the schema, builds a ready-to-send\n * payload through the `resource`, and returns URL setters that `router.push({ query })` (a\n * new history entry; filter/sort/size changes reset `page`). Does **not** fetch.\n *\n * @example\n * ```ts\n * const users = qk.resource<IUser>(\"users\");\n * const { params, setPage, setSort } = useListParams(users, { schema: usersSchema });\n * const { data } = useQuery({ queryKey: computed(() => users.keys.list(params.value)), queryFn: () => post(\"/users/list\", params.value) });\n * ```\n */\nexport function useListParams<T, A extends AdapterName>(resource: Resource<T, A>, options: UseListParamsOptions<A>): UseListParamsResult {\n const route = useRoute();\n const router = useRouter();\n return useListParamsBase(resource, {\n ...options,\n searchParams: () => queryToSearchParams(route.query),\n setSearchParams: next => void router.push({ query: searchParamsToQuery(next) }),\n });\n}\n"]}
|