@classytic/arc-next 0.9.0 → 0.10.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/dist/prefetch.js CHANGED
@@ -1,11 +1,25 @@
1
- import { createQueryKeys } from "./cache.js";
1
+ import { createEntityQueries } from "./query-options.js";
2
2
  import { HydrationBoundary, dehydrate } from "@tanstack/react-query";
3
3
 
4
4
  //#region src/prefetch.ts
5
+ /** Extract the queryFn context (auth + fetch caching) from prefetch options. */
6
+ function toCtx(options) {
7
+ const { staleTime: _staleTime, ...ctx } = options;
8
+ return ctx;
9
+ }
5
10
  /**
6
11
  * Create server-safe prefetch helpers for CRUD queries.
7
12
  * Use in Next.js server components to pre-populate the query cache before rendering.
8
13
  *
14
+ * Since 0.10 this is a thin layer over `createEntityQueries`
15
+ * (@classytic/arc-next/query-options) — the queryOptions factories are the
16
+ * single source of key + queryFn, shared with the client CRUD hooks, so
17
+ * prefetch keys can never drift from hook keys. Prefer the factories directly
18
+ * for new code that also needs `ensureQueryData` / router-loader integration:
19
+ *
20
+ * const products = createEntityQueries(productApi, 'products');
21
+ * await queryClient.prefetchQuery({ ...products.list({ limit: 20 }, { token }), staleTime: 60_000 });
22
+ *
9
23
  * @example
10
24
  * // products-prefetch.ts
11
25
  * import { productsApi } from '@/api/products-api';
@@ -28,146 +42,54 @@ import { HydrationBoundary, dehydrate } from "@tanstack/react-query";
28
42
  * }
29
43
  */
30
44
  function createCrudPrefetcher(api, entityKey) {
31
- const KEYS = createQueryKeys(entityKey);
32
- const apiOptions = (o) => {
33
- const opt = {};
34
- if (o.headers) opt.headerOptions = o.headers;
35
- if (o.cache !== void 0) opt.cache = o.cache;
36
- if (o.revalidate !== void 0) opt.revalidate = o.revalidate;
37
- if (o.tags !== void 0) opt.tags = o.tags;
38
- return Object.keys(opt).length ? { options: opt } : {};
39
- };
45
+ const queries = createEntityQueries(api, entityKey);
40
46
  return {
41
47
  async prefetchList(queryClient, params = {}, options = {}) {
42
- const { organizationId: paramOrgId, ...restParams } = params;
43
- const orgId = paramOrgId ?? options.organizationId ?? null;
44
- const scope = orgId ? "tenant" : "super-admin";
45
- const queryKey = KEYS.scopedList(scope, {
46
- ...orgId ? { organizationId: orgId } : {},
47
- ...restParams
48
- });
49
48
  await queryClient.prefetchQuery({
50
- queryKey,
51
- queryFn: () => api.getAll({
52
- params: restParams,
53
- token: options.token ?? null,
54
- organizationId: orgId,
55
- ...apiOptions(options)
56
- }),
49
+ ...queries.list(params, toCtx(options)),
57
50
  staleTime: options.staleTime
58
51
  });
59
52
  },
60
53
  async prefetchDetail(queryClient, id, options = {}) {
61
- const { params, staleTime, token, organizationId } = options;
62
- const baseKey = KEYS.detail(id);
63
- const queryKey = params ? [...baseKey, params] : baseKey;
54
+ const { staleTime, ...detailOpts } = options;
64
55
  await queryClient.prefetchQuery({
65
- queryKey,
66
- queryFn: () => api.getById({
67
- id,
68
- token: token ?? null,
69
- organizationId: organizationId ?? null,
70
- ...params ? { params } : {},
71
- ...apiOptions(options)
72
- }),
56
+ ...queries.detail(id, detailOpts),
73
57
  staleTime
74
58
  });
75
59
  },
76
60
  async prefetchBySlug(queryClient, slug, options = {}) {
77
61
  if (!api.getBySlug) throw new Error(`[arc-next] prefetchBySlug requires an api with getBySlug (slugLookup preset)`);
78
- const { params, staleTime, token, organizationId } = options;
79
- const queryKey = params ? KEYS.custom("slug", slug, params) : KEYS.custom("slug", slug);
62
+ const { staleTime, ...detailOpts } = options;
80
63
  await queryClient.prefetchQuery({
81
- queryKey,
82
- queryFn: () => api.getBySlug({
83
- slug,
84
- token: token ?? null,
85
- organizationId: organizationId ?? null,
86
- ...params ? { params } : {},
87
- ...apiOptions(options)
88
- }),
64
+ ...queries.bySlug(slug, detailOpts),
89
65
  staleTime
90
66
  });
91
67
  },
92
68
  async prefetchDeleted(queryClient, params = {}, options = {}) {
93
69
  if (!api.getDeleted) throw new Error(`[arc-next] prefetchDeleted requires an api with getDeleted (softDelete preset)`);
94
- const { organizationId: paramOrgId, ...restParams } = params;
95
- const orgId = paramOrgId ?? options.organizationId ?? null;
96
- const queryKey = KEYS.custom("deleted", {
97
- ...orgId ? { organizationId: orgId } : {},
98
- ...restParams
99
- });
100
70
  await queryClient.prefetchQuery({
101
- queryKey,
102
- queryFn: () => api.getDeleted({
103
- params: restParams,
104
- token: options.token ?? null,
105
- organizationId: orgId,
106
- ...apiOptions(options)
107
- }),
71
+ ...queries.deleted(params, toCtx(options)),
108
72
  staleTime: options.staleTime
109
73
  });
110
74
  },
111
75
  async prefetchAggregation(queryClient, name, filter, options = {}) {
112
76
  if (!api.aggregate) throw new Error(`[arc-next] prefetchAggregation requires an api with aggregate (arc 2.13+)`);
113
77
  if (!name) throw new Error("[arc-next] prefetchAggregation: aggregation name is required");
114
- const orgId = options.organizationId ?? null;
115
- const filterKey = orgId ? {
116
- _org: orgId,
117
- ...filter ?? {}
118
- } : filter ?? {};
119
78
  await queryClient.prefetchQuery({
120
- queryKey: KEYS.aggregation(name, filterKey),
121
- queryFn: () => api.aggregate({
122
- name,
123
- filter,
124
- token: options.token ?? null,
125
- organizationId: orgId,
126
- ...apiOptions(options)
127
- }),
79
+ ...queries.aggregation(name, filter, toCtx(options)),
128
80
  staleTime: options.staleTime
129
81
  });
130
82
  },
131
83
  async prefetchTree(queryClient, params = {}, options = {}) {
132
84
  if (!api.getTree) throw new Error(`[arc-next] prefetchTree requires an api with getTree (tree preset)`);
133
- const { organizationId: paramOrgId, ...restParams } = params;
134
- const orgId = paramOrgId ?? options.organizationId ?? null;
135
- const queryKey = KEYS.custom("tree", {
136
- ...orgId ? { organizationId: orgId } : {},
137
- ...restParams
138
- });
139
85
  await queryClient.prefetchQuery({
140
- queryKey,
141
- queryFn: () => api.getTree({
142
- params: restParams,
143
- token: options.token ?? null,
144
- organizationId: orgId,
145
- ...apiOptions(options)
146
- }),
86
+ ...queries.tree(params, toCtx(options)),
147
87
  staleTime: options.staleTime
148
88
  });
149
89
  },
150
90
  async prefetchInfiniteList(queryClient, params = {}, options = {}) {
151
- const { organizationId: paramOrgId, ...restParams } = params;
152
- const orgId = paramOrgId ?? options.organizationId ?? null;
153
- const scope = orgId ? "tenant" : "super-admin";
154
- const queryKey = [...KEYS.scopedList(scope, {
155
- ...orgId ? { organizationId: orgId } : {},
156
- ...restParams
157
- }), "infinite"];
158
91
  await queryClient.prefetchInfiniteQuery({
159
- queryKey,
160
- queryFn: ({ pageParam }) => api.getAll({
161
- params: {
162
- ...restParams,
163
- ...pageParam ? { page: pageParam } : {}
164
- },
165
- token: options.token ?? null,
166
- organizationId: orgId,
167
- ...apiOptions(options)
168
- }),
169
- initialPageParam: 1,
170
- getNextPageParam: () => void 0,
92
+ ...queries.infiniteList(params, toCtx(options)),
171
93
  staleTime: options.staleTime
172
94
  });
173
95
  }
@@ -16,14 +16,10 @@
16
16
  function withTree(api) {
17
17
  return Object.assign(api, {
18
18
  async getTree({ token = null, organizationId = null, params = {}, options = {} } = {}) {
19
- const merged = {
20
- ...api.config.defaultParams,
21
- ...params
22
- };
23
19
  return api.request("GET", `${api.baseUrl}/tree`, {
24
20
  token,
25
21
  organizationId,
26
- params: merged,
22
+ params,
27
23
  options
28
24
  });
29
25
  },
@@ -0,0 +1,165 @@
1
+ import { QueryKeys } from "./cache.js";
2
+ import * as _$_tanstack_react_query0 from "@tanstack/react-query";
3
+
4
+ //#region src/query-options.d.ts
5
+ /** Per-call request context: auth + Next.js fetch caching passthrough. */
6
+ interface QueryFnContext {
7
+ /** Bearer token for protected endpoints (required server-side for non-public reads). */
8
+ token?: string | null;
9
+ /** Organization ID for multi-tenant reads. Also becomes part of the cache key. */
10
+ organizationId?: string | null;
11
+ /** Extra headers (e.g. x-api-key). */
12
+ headers?: Record<string, string>;
13
+ /** Next.js fetch caching forwarded to the API call (ISR-friendly prefetch). */
14
+ cache?: RequestCache;
15
+ revalidate?: number | false;
16
+ tags?: string[];
17
+ }
18
+ type ForwardedApiOptions = {
19
+ headerOptions?: Record<string, string>;
20
+ cache?: RequestCache;
21
+ revalidate?: number | false;
22
+ tags?: string[];
23
+ signal?: AbortSignal;
24
+ };
25
+ /** Structural read-API contract (BaseApi satisfies this; presets add the optionals). */
26
+ interface EntityReadApi {
27
+ getAll: (opts: {
28
+ params?: Record<string, unknown>;
29
+ token?: string | null;
30
+ organizationId?: string | null;
31
+ options?: ForwardedApiOptions;
32
+ }) => Promise<unknown>;
33
+ getById: (opts: {
34
+ id: string;
35
+ token?: string | null;
36
+ organizationId?: string | null;
37
+ params?: Record<string, unknown>;
38
+ options?: ForwardedApiOptions;
39
+ }) => Promise<unknown>;
40
+ getBySlug?: (opts: {
41
+ slug: string;
42
+ token?: string | null;
43
+ organizationId?: string | null;
44
+ params?: Record<string, unknown>;
45
+ options?: ForwardedApiOptions;
46
+ }) => Promise<unknown>;
47
+ getDeleted?: (opts: {
48
+ params?: Record<string, unknown>;
49
+ token?: string | null;
50
+ organizationId?: string | null;
51
+ options?: ForwardedApiOptions;
52
+ }) => Promise<unknown>;
53
+ getTree?: (opts: {
54
+ params?: Record<string, unknown>;
55
+ token?: string | null;
56
+ organizationId?: string | null;
57
+ options?: ForwardedApiOptions;
58
+ }) => Promise<unknown>;
59
+ getChildren?: (opts: {
60
+ parentId: string;
61
+ params?: Record<string, unknown>;
62
+ token?: string | null;
63
+ organizationId?: string | null;
64
+ options?: ForwardedApiOptions;
65
+ }) => Promise<unknown>;
66
+ aggregate?: (opts: {
67
+ name: string;
68
+ filter?: Record<string, unknown>;
69
+ token?: string | null;
70
+ organizationId?: string | null;
71
+ options?: ForwardedApiOptions;
72
+ }) => Promise<unknown>;
73
+ }
74
+ interface DetailQueryOpts extends QueryFnContext {
75
+ /** Query params (select, populate) — becomes part of the key, matching useDetail. */
76
+ params?: {
77
+ select?: string;
78
+ populate?: string | string[];
79
+ };
80
+ }
81
+ type EntityQueries = ReturnType<typeof createEntityQueries>;
82
+ /**
83
+ * Build queryOptions factories for one entity. Server-safe; keys are
84
+ * hash-identical to the corresponding `createCrudHooks` hooks.
85
+ *
86
+ * @example
87
+ * // queries/products.ts — colocate next to the api definition
88
+ * export const productQueries = createEntityQueries(productApi, 'products');
89
+ *
90
+ * // RSC / route loader
91
+ * await queryClient.ensureQueryData(productQueries.detail(id, { token }));
92
+ */
93
+ declare function createEntityQueries(api: EntityReadApi, entityKey: string): {
94
+ /** The entity's key factory — for invalidation / setQueryData at call sites. */keys: QueryKeys; /** GET /:resource — mirrors `useList`'s key (scoped, org-normalized). */
95
+ list(params?: Record<string, unknown>, ctx?: QueryFnContext): _$_tanstack_react_query0.OmitKeyof<_$_tanstack_react_query0.UseQueryOptions<unknown, Error, unknown, readonly unknown[]>, "queryFn"> & {
96
+ queryFn?: _$_tanstack_react_query0.QueryFunction<unknown, readonly unknown[], never> | undefined;
97
+ } & {
98
+ queryKey: readonly unknown[] & {
99
+ [dataTagSymbol]: unknown;
100
+ [dataTagErrorSymbol]: Error;
101
+ };
102
+ }; /** GET /:resource/:id — mirrors `useDetail`'s scoped key (+ params variant). */
103
+ detail(id: string, opts?: DetailQueryOpts): _$_tanstack_react_query0.OmitKeyof<_$_tanstack_react_query0.UseQueryOptions<unknown, Error, unknown, readonly unknown[]>, "queryFn"> & {
104
+ queryFn?: _$_tanstack_react_query0.QueryFunction<unknown, readonly unknown[], never> | undefined;
105
+ } & {
106
+ queryKey: readonly unknown[] & {
107
+ [dataTagSymbol]: unknown;
108
+ [dataTagErrorSymbol]: Error;
109
+ };
110
+ }; /** GET /:resource/slug/:slug — mirrors `useDetailBySlug`'s key. */
111
+ bySlug(slug: string, opts?: DetailQueryOpts): _$_tanstack_react_query0.OmitKeyof<_$_tanstack_react_query0.UseQueryOptions<unknown, Error, unknown, readonly unknown[]>, "queryFn"> & {
112
+ queryFn?: _$_tanstack_react_query0.QueryFunction<unknown, readonly unknown[], never> | undefined;
113
+ } & {
114
+ queryKey: readonly unknown[] & {
115
+ [dataTagSymbol]: unknown;
116
+ [dataTagErrorSymbol]: Error;
117
+ };
118
+ }; /** GET /:resource/deleted — mirrors `useDeleted`'s key. */
119
+ deleted(params?: Record<string, unknown>, ctx?: QueryFnContext): _$_tanstack_react_query0.OmitKeyof<_$_tanstack_react_query0.UseQueryOptions<unknown, Error, unknown, readonly unknown[]>, "queryFn"> & {
120
+ queryFn?: _$_tanstack_react_query0.QueryFunction<unknown, readonly unknown[], never> | undefined;
121
+ } & {
122
+ queryKey: readonly unknown[] & {
123
+ [dataTagSymbol]: unknown;
124
+ [dataTagErrorSymbol]: Error;
125
+ };
126
+ }; /** GET /:resource/tree — mirrors `useTree`'s key. */
127
+ tree(params?: Record<string, unknown>, ctx?: QueryFnContext): _$_tanstack_react_query0.OmitKeyof<_$_tanstack_react_query0.UseQueryOptions<unknown, Error, unknown, readonly unknown[]>, "queryFn"> & {
128
+ queryFn?: _$_tanstack_react_query0.QueryFunction<unknown, readonly unknown[], never> | undefined;
129
+ } & {
130
+ queryKey: readonly unknown[] & {
131
+ [dataTagSymbol]: unknown;
132
+ [dataTagErrorSymbol]: Error;
133
+ };
134
+ }; /** GET /:resource/:parentId/children — mirrors `useChildren`'s key. */
135
+ children(parentId: string, params?: Record<string, unknown>, ctx?: QueryFnContext): _$_tanstack_react_query0.OmitKeyof<_$_tanstack_react_query0.UseQueryOptions<unknown, Error, unknown, readonly unknown[]>, "queryFn"> & {
136
+ queryFn?: _$_tanstack_react_query0.QueryFunction<unknown, readonly unknown[], never> | undefined;
137
+ } & {
138
+ queryKey: readonly unknown[] & {
139
+ [dataTagSymbol]: unknown;
140
+ [dataTagErrorSymbol]: Error;
141
+ };
142
+ }; /** GET /:resource/aggregations/:name — mirrors `useAggregation`'s tenant-scoped key. */
143
+ aggregation(name: string, filter?: Record<string, unknown>, ctx?: QueryFnContext): _$_tanstack_react_query0.OmitKeyof<_$_tanstack_react_query0.UseQueryOptions<unknown, Error, unknown, readonly unknown[]>, "queryFn"> & {
144
+ queryFn?: _$_tanstack_react_query0.QueryFunction<unknown, readonly unknown[], never> | undefined;
145
+ } & {
146
+ queryKey: readonly unknown[] & {
147
+ [dataTagSymbol]: unknown;
148
+ [dataTagErrorSymbol]: Error;
149
+ };
150
+ };
151
+ /**
152
+ * Infinite list — mirrors `useInfiniteList`'s key (`scopedList + 'infinite'`)
153
+ * and its page-param semantics (keyset cursor or offset page + 1).
154
+ */
155
+ infiniteList(params?: Record<string, unknown>, ctx?: QueryFnContext): _$_tanstack_react_query0.OmitKeyof<_$_tanstack_react_query0.UseInfiniteQueryOptions<unknown, Error, _$_tanstack_react_query0.InfiniteData<unknown, unknown>, unknown[], unknown>, "queryFn"> & {
156
+ queryFn?: _$_tanstack_react_query0.QueryFunction<unknown, unknown[], unknown> | undefined;
157
+ } & {
158
+ queryKey: unknown[] & {
159
+ [dataTagSymbol]: _$_tanstack_react_query0.InfiniteData<unknown, unknown>;
160
+ [dataTagErrorSymbol]: Error;
161
+ };
162
+ };
163
+ };
164
+ //#endregion
165
+ export { DetailQueryOpts, EntityQueries, EntityReadApi, QueryFnContext, createEntityQueries };
@@ -0,0 +1,188 @@
1
+ import { isKeysetPagination, isOffsetPagination } from "./api.js";
2
+ import { createQueryKeys, withOrgParams } from "./cache.js";
3
+ import { infiniteQueryOptions, queryOptions } from "@tanstack/react-query";
4
+
5
+ //#region src/query-options.ts
6
+ /**
7
+ * Build queryOptions factories for one entity. Server-safe; keys are
8
+ * hash-identical to the corresponding `createCrudHooks` hooks.
9
+ *
10
+ * @example
11
+ * // queries/products.ts — colocate next to the api definition
12
+ * export const productQueries = createEntityQueries(productApi, 'products');
13
+ *
14
+ * // RSC / route loader
15
+ * await queryClient.ensureQueryData(productQueries.detail(id, { token }));
16
+ */
17
+ function createEntityQueries(api, entityKey) {
18
+ const KEYS = createQueryKeys(entityKey);
19
+ const fwd = (ctx, signal) => {
20
+ const opt = {};
21
+ if (ctx.headers) opt.headerOptions = ctx.headers;
22
+ if (ctx.cache !== void 0) opt.cache = ctx.cache;
23
+ if (ctx.revalidate !== void 0) opt.revalidate = ctx.revalidate;
24
+ if (ctx.tags !== void 0) opt.tags = ctx.tags;
25
+ if (signal) opt.signal = signal;
26
+ return Object.keys(opt).length ? { options: opt } : {};
27
+ };
28
+ const resolveOrg = (params, ctx) => {
29
+ const { organizationId: paramOrg, ...rest } = params;
30
+ return {
31
+ org: paramOrg ?? ctx.organizationId ?? null,
32
+ rest
33
+ };
34
+ };
35
+ return {
36
+ /** The entity's key factory — for invalidation / setQueryData at call sites. */
37
+ keys: KEYS,
38
+ /** GET /:resource — mirrors `useList`'s key (scoped, org-normalized). */
39
+ list(params = {}, ctx = {}) {
40
+ const { org, rest } = resolveOrg(params, ctx);
41
+ const scope = org ? "tenant" : "super-admin";
42
+ return queryOptions({
43
+ queryKey: KEYS.scopedList(scope, withOrgParams(org, rest)),
44
+ queryFn: ({ signal }) => api.getAll({
45
+ params: rest,
46
+ token: ctx.token ?? null,
47
+ organizationId: org,
48
+ ...fwd(ctx, signal)
49
+ })
50
+ });
51
+ },
52
+ /** GET /:resource/:id — mirrors `useDetail`'s scoped key (+ params variant). */
53
+ detail(id, opts = {}) {
54
+ const { params, ...ctx } = opts;
55
+ const baseKey = KEYS.scopedDetail(id, ctx.organizationId ?? null);
56
+ return queryOptions({
57
+ queryKey: params ? [...baseKey, params] : baseKey,
58
+ queryFn: ({ signal }) => api.getById({
59
+ id,
60
+ token: ctx.token ?? null,
61
+ organizationId: ctx.organizationId ?? null,
62
+ ...params ? { params } : {},
63
+ ...fwd(ctx, signal)
64
+ })
65
+ });
66
+ },
67
+ /** GET /:resource/slug/:slug — mirrors `useDetailBySlug`'s key. */
68
+ bySlug(slug, opts = {}) {
69
+ const { params, ...ctx } = opts;
70
+ return queryOptions({
71
+ queryKey: params ? KEYS.custom("slug", slug, params) : KEYS.custom("slug", slug),
72
+ queryFn: ({ signal }) => {
73
+ if (!api.getBySlug) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define getBySlug (slugLookup preset)`));
74
+ return api.getBySlug({
75
+ slug,
76
+ token: ctx.token ?? null,
77
+ organizationId: ctx.organizationId ?? null,
78
+ ...params ? { params } : {},
79
+ ...fwd(ctx, signal)
80
+ });
81
+ }
82
+ });
83
+ },
84
+ /** GET /:resource/deleted — mirrors `useDeleted`'s key. */
85
+ deleted(params = {}, ctx = {}) {
86
+ const { org, rest } = resolveOrg(params, ctx);
87
+ return queryOptions({
88
+ queryKey: KEYS.custom("deleted", withOrgParams(org, rest)),
89
+ queryFn: ({ signal }) => {
90
+ if (!api.getDeleted) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define getDeleted (softDelete preset)`));
91
+ return api.getDeleted({
92
+ params: rest,
93
+ token: ctx.token ?? null,
94
+ organizationId: org,
95
+ ...fwd(ctx, signal)
96
+ });
97
+ }
98
+ });
99
+ },
100
+ /** GET /:resource/tree — mirrors `useTree`'s key. */
101
+ tree(params = {}, ctx = {}) {
102
+ const { org, rest } = resolveOrg(params, ctx);
103
+ return queryOptions({
104
+ queryKey: KEYS.custom("tree", withOrgParams(org, rest)),
105
+ queryFn: ({ signal }) => {
106
+ if (!api.getTree) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define getTree (tree preset)`));
107
+ return api.getTree({
108
+ params: rest,
109
+ token: ctx.token ?? null,
110
+ organizationId: org,
111
+ ...fwd(ctx, signal)
112
+ });
113
+ }
114
+ });
115
+ },
116
+ /** GET /:resource/:parentId/children — mirrors `useChildren`'s key. */
117
+ children(parentId, params = {}, ctx = {}) {
118
+ const { org, rest } = resolveOrg(params, ctx);
119
+ return queryOptions({
120
+ queryKey: KEYS.custom("children", parentId, withOrgParams(org, rest)),
121
+ queryFn: ({ signal }) => {
122
+ if (!api.getChildren) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define getChildren (tree preset)`));
123
+ return api.getChildren({
124
+ parentId,
125
+ params: rest,
126
+ token: ctx.token ?? null,
127
+ organizationId: org,
128
+ ...fwd(ctx, signal)
129
+ });
130
+ }
131
+ });
132
+ },
133
+ /** GET /:resource/aggregations/:name — mirrors `useAggregation`'s tenant-scoped key. */
134
+ aggregation(name, filter, ctx = {}) {
135
+ const org = ctx.organizationId ?? null;
136
+ const filterKey = org ? {
137
+ _org: org,
138
+ ...filter ?? {}
139
+ } : filter ?? {};
140
+ return queryOptions({
141
+ queryKey: KEYS.aggregation(name, filterKey),
142
+ queryFn: ({ signal }) => {
143
+ if (!api.aggregate) return Promise.reject(/* @__PURE__ */ new Error(`[arc-next] "${entityKey}" api does not define aggregate (arc 2.13+)`));
144
+ return api.aggregate({
145
+ name,
146
+ filter,
147
+ token: ctx.token ?? null,
148
+ organizationId: org,
149
+ ...fwd(ctx, signal)
150
+ });
151
+ }
152
+ });
153
+ },
154
+ /**
155
+ * Infinite list — mirrors `useInfiniteList`'s key (`scopedList + 'infinite'`)
156
+ * and its page-param semantics (keyset cursor or offset page + 1).
157
+ */
158
+ infiniteList(params = {}, ctx = {}) {
159
+ const { org, rest } = resolveOrg(params, ctx);
160
+ const scope = org ? "tenant" : "super-admin";
161
+ return infiniteQueryOptions({
162
+ queryKey: [...KEYS.scopedList(scope, withOrgParams(org, rest)), "infinite"],
163
+ queryFn: ({ pageParam, signal }) => api.getAll({
164
+ params: {
165
+ ...rest,
166
+ ...pageParam ? { page: pageParam } : {}
167
+ },
168
+ token: ctx.token ?? null,
169
+ organizationId: org,
170
+ ...fwd(ctx, signal)
171
+ }),
172
+ initialPageParam: 1,
173
+ getNextPageParam: (lastPage) => {
174
+ if (isKeysetPagination(lastPage)) return lastPage.hasMore ? lastPage.next : void 0;
175
+ if (isOffsetPagination(lastPage)) {
176
+ const p = lastPage;
177
+ return p.hasNext ? p.page + 1 : void 0;
178
+ }
179
+ const p = lastPage;
180
+ if (p && typeof p.hasNext === "boolean" && typeof p.page === "number") return p.hasNext ? p.page + 1 : void 0;
181
+ }
182
+ });
183
+ }
184
+ };
185
+ }
186
+
187
+ //#endregion
188
+ export { createEntityQueries };