@classytic/arc-next 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -20
- package/dist/api.d.ts +2 -32
- package/dist/api.js +12 -13
- package/dist/client.d.ts +18 -2
- package/dist/client.js +11 -5
- package/dist/hooks.d.ts +15 -73
- package/dist/hooks.js +98 -94
- package/dist/mutation.d.ts +6 -3
- package/dist/mutation.js +6 -3
- package/dist/prefetch.d.ts +9 -3
- package/dist/prefetch.js +9 -5
- package/dist/query-client.d.ts +1 -2
- package/dist/query-client.js +2 -2
- package/dist/query.d.ts +18 -5
- package/dist/query.js +54 -19
- package/package.json +9 -3
- package/dist/api.d.ts.map +0 -1
- package/dist/api.js.map +0 -1
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/hooks.d.ts.map +0 -1
- package/dist/hooks.js.map +0 -1
- package/dist/mutation.d.ts.map +0 -1
- package/dist/mutation.js.map +0 -1
- package/dist/prefetch.d.ts.map +0 -1
- package/dist/prefetch.js.map +0 -1
- package/dist/query-client.d.ts.map +0 -1
- package/dist/query-client.js.map +0 -1
- package/dist/query.d.ts.map +0 -1
- package/dist/query.js.map +0 -1
package/README.md
CHANGED
|
@@ -30,13 +30,13 @@ import { useRouter } from "next/navigation";
|
|
|
30
30
|
// Required — sets the API base URL and auth mode
|
|
31
31
|
configureClient({
|
|
32
32
|
baseUrl: process.env.NEXT_PUBLIC_API_URL!,
|
|
33
|
-
authMode: "cookie",
|
|
34
|
-
|
|
33
|
+
authMode: "cookie", // 'cookie' for Better Auth, 'bearer' for token auth (default)
|
|
34
|
+
// credentials: 'omit', // override if you don't want cookies sent cross-origin
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
-
// Optional — auto-inject
|
|
37
|
+
// Optional — auto-inject tenant context into queries/mutations
|
|
38
38
|
configureAuth({
|
|
39
|
-
getOrgId: () =>
|
|
39
|
+
getOrgId: () => activeTenantId, // return current tenant/org/workspace ID
|
|
40
40
|
getToken: () => null, // null for cookie auth (token only for bearer)
|
|
41
41
|
});
|
|
42
42
|
|
|
@@ -51,10 +51,11 @@ configureNavigation(useRouter);
|
|
|
51
51
|
|
|
52
52
|
| Import | Purpose | `"use client"` |
|
|
53
53
|
| ----------------------------------- | ---------------------------------------------------------------------------- | :-------------: |
|
|
54
|
+
| `@classytic/arc-next` | Root — same as `/hooks` (`createCrudHooks`, `configureNavigation`) | Yes |
|
|
54
55
|
| `@classytic/arc-next/client` | `configureClient`, `configureAuth`, `createClient`, `handleApiRequest`, `createQueryString`, `ArcApiError`, `isArcApiError`, `getAuthMode`, `getAuthContext` | No |
|
|
55
56
|
| `@classytic/arc-next/api` | `BaseApi`, `createCrudApi`, response types, type guards | No |
|
|
56
|
-
| `@classytic/arc-next/query` | `createQueryKeys`, `createCacheUtils`, `
|
|
57
|
-
| `@classytic/arc-next/mutation` | `configureToast`, `useMutationWithTransition`, `
|
|
57
|
+
| `@classytic/arc-next/query` | `createQueryKeys`, `createCacheUtils`, `useListQuery`, `useDetailQuery` | Yes |
|
|
58
|
+
| `@classytic/arc-next/mutation` | `configureToast`, `useMutationWithTransition`, `useOptimisticMutation` | Yes |
|
|
58
59
|
| `@classytic/arc-next/hooks` | `createCrudHooks`, `configureNavigation` | Yes |
|
|
59
60
|
| `@classytic/arc-next/query-client` | `getQueryClient` (SSR-safe singleton) | No |
|
|
60
61
|
| `@classytic/arc-next/prefetch` | `createCrudPrefetcher`, `dehydrate` (SSR prefetch) | No |
|
|
@@ -148,13 +149,15 @@ export function ProductsPage() {
|
|
|
148
149
|
configureClient({
|
|
149
150
|
baseUrl: string; // Required — API base URL
|
|
150
151
|
authMode?: 'cookie' | 'bearer'; // Default: 'bearer'
|
|
152
|
+
credentials?: RequestCredentials; // Default: derived from authMode
|
|
151
153
|
internalApiKey?: string; // Optional — sent as x-internal-api-key header
|
|
152
154
|
defaultHeaders?: Record<string, string>; // Optional — merged into every request
|
|
153
155
|
});
|
|
154
156
|
```
|
|
155
157
|
|
|
156
|
-
- `authMode: 'bearer'` (default) — requires a token
|
|
157
|
-
- `authMode: 'cookie'` —
|
|
158
|
+
- `authMode: 'bearer'` (default) — requires a token; queries disabled until token provided; `credentials: 'same-origin'`
|
|
159
|
+
- `authMode: 'cookie'` — HTTP-only cookies (e.g. Better Auth); queries always enabled; `credentials: 'include'`
|
|
160
|
+
- `credentials` — explicit override: `'include'` (send cookies cross-origin), `'same-origin'` (same-origin only), `'omit'` (never send cookies)
|
|
158
161
|
|
|
159
162
|
Must be called before any API requests. Throws if not configured.
|
|
160
163
|
|
|
@@ -167,7 +170,7 @@ configureAuth({
|
|
|
167
170
|
});
|
|
168
171
|
```
|
|
169
172
|
|
|
170
|
-
Auto-injects `token` and `
|
|
173
|
+
Auto-injects `token` and tenant ID (sent as `x-organization-id` header) into queries/mutations. The header name is a convention — your backend controls how it's read and which field it maps to (`organizationId`, `workspaceId`, `teamId`, etc.). Hooks use the new signature (no explicit token param) — legacy signature still works.
|
|
171
174
|
|
|
172
175
|
### `handleApiRequest<T>(method, endpoint, options?)`
|
|
173
176
|
|
|
@@ -226,7 +229,7 @@ const api = createCrudApi<Product, CreateProduct>("products", {
|
|
|
226
229
|
|
|
227
230
|
### `createCrudHooks<T, TCreate, TUpdate>(config)`
|
|
228
231
|
|
|
229
|
-
Factory that returns everything you need:
|
|
232
|
+
Factory that returns everything you need. The `api` parameter accepts any `createCrudApi()` result directly — no casts needed. Types are derived from `BaseApi` via `Pick`, so generics thread through automatically:
|
|
230
233
|
|
|
231
234
|
```ts
|
|
232
235
|
const {
|
|
@@ -235,7 +238,7 @@ const {
|
|
|
235
238
|
useActions, useUpload, useSearch, useCustomMutation,
|
|
236
239
|
useNavigation,
|
|
237
240
|
} = createCrudHooks<Product, CreateProduct>({
|
|
238
|
-
api: productsApi, // from createCrudApi()
|
|
241
|
+
api: productsApi, // from createCrudApi() — types inferred, no cast
|
|
239
242
|
entityKey: "products", // TanStack Query key prefix
|
|
240
243
|
singular: "Product", // for toast messages
|
|
241
244
|
defaults: { // optional
|
|
@@ -263,7 +266,7 @@ const { items, pagination, isLoading, isFetching, refetch } = useList(
|
|
|
263
266
|
);
|
|
264
267
|
```
|
|
265
268
|
|
|
266
|
-
- Auto-scopes query keys by
|
|
269
|
+
- Auto-scopes list query keys by tenant context (when present → `tenant` scope, otherwise → `super-admin`)
|
|
267
270
|
- Normalizes pagination from `docs`/`data`/`items`/`results` formats
|
|
268
271
|
- Prefills detail cache from list results (skips re-fetch on navigate)
|
|
269
272
|
- `options.public: true` — enables query without token
|
|
@@ -534,7 +537,7 @@ useProducts(token, {}, { ...QUERY_CONFIGS.realtime });
|
|
|
534
537
|
|
|
535
538
|
### `updateListCache(listData, updater)`
|
|
536
539
|
|
|
537
|
-
Transforms list cache regardless of format (`docs[]`, `data[]`, `items[]`, `results[]`, or raw
|
|
540
|
+
Transforms list cache regardless of format — well-known keys (`docs[]`, `data[]`, `items[]`, `results[]`), custom keys (`products[]`, `users[]`, etc.), or raw arrays.
|
|
538
541
|
Automatically adjusts `total`/`totalDocs` counts when items are added or removed (optimistic add/delete).
|
|
539
542
|
|
|
540
543
|
```ts
|
|
@@ -551,11 +554,11 @@ Extracts `_id` or `id` from any item. Returns `string | null`.
|
|
|
551
554
|
|
|
552
555
|
### `normalizePagination(data)`
|
|
553
556
|
|
|
554
|
-
Converts any pagination response format to a normalized `PaginationData` object.
|
|
557
|
+
Converts any pagination response format to a normalized `PaginationData` object. Detects pagination method (`offset`, `keyset`, `aggregate`) and normalizes all fields: `total`/`totalDocs`, `pages`/`totalPages`, `page`/`currentPage`, `hasNext`/`hasNextPage`/`hasMore`, `hasPrev`/`hasPrevPage`, `next` (keyset cursor).
|
|
555
558
|
|
|
556
559
|
### `extractItems<T>(data)`
|
|
557
560
|
|
|
558
|
-
Extracts the items array from any response format
|
|
561
|
+
Extracts the items array from any response format. Checks well-known keys first (`docs`, `data`, `items`, `results`), then falls back to finding the first top-level array — so `{ products: [...] }` or `{ users: [...] }` works without configuration.
|
|
559
562
|
|
|
560
563
|
## Multi-Client (Multiple APIs)
|
|
561
564
|
|
|
@@ -654,8 +657,10 @@ try {
|
|
|
654
657
|
### Multi-tenant data fetching
|
|
655
658
|
|
|
656
659
|
```ts
|
|
657
|
-
//
|
|
658
|
-
|
|
660
|
+
// Tenant ID in params → scoped query key → isolated cache per tenant
|
|
661
|
+
// The param name is up to you — arc-next sends it as x-organization-id header,
|
|
662
|
+
// your backend maps it to whatever tenant field your schema uses.
|
|
663
|
+
const { items } = useProducts(token, { organizationId: currentTenantId });
|
|
659
664
|
```
|
|
660
665
|
|
|
661
666
|
### Public endpoints (no auth)
|
|
@@ -704,11 +709,11 @@ const adminApi = createCrudApi("users", {
|
|
|
704
709
|
|
|
705
710
|
- **CRUD Factory** — `createCrudApi` + `createCrudHooks` generates typed API clients and React Query hooks
|
|
706
711
|
- **Optimistic Updates** — Create, update, delete with instant UI feedback and automatic rollback
|
|
707
|
-
- **Multi-Tenant Scoping** — `
|
|
708
|
-
- **Pagination Normalization** — Handles `docs`/`data`/`items`/`results`
|
|
712
|
+
- **Multi-Tenant Scoping** — Tenant ID sent via `x-organization-id` header + scoped list query keys. Backend controls the tenant field name and access enforcement.
|
|
713
|
+
- **Pagination Normalization** — Handles `docs`/`data`/`items`/`results` + any custom key, offset/keyset/aggregate pagination
|
|
709
714
|
- **Detail Cache Prefilling** — List results auto-populate detail query cache
|
|
710
715
|
- **React 19 Transitions** — `useMutationWithTransition` wraps invalidation in `startTransition`
|
|
711
|
-
- **Cookie & Bearer Auth** — `authMode: 'cookie'` for Better Auth, `'bearer'` for token auth
|
|
716
|
+
- **Cookie & Bearer Auth** — `authMode: 'cookie'` for Better Auth, `'bearer'` for token auth, configurable `credentials` policy
|
|
712
717
|
- **SSR Prefetch** — `createCrudPrefetcher` + `dehydrate` for server component data loading
|
|
713
718
|
- **Multi-Client** — `createClient()` for multiple API backends side by side
|
|
714
719
|
- **Pluggable Toast** — `configureToast()` — use sonner, react-hot-toast, or anything
|
package/dist/api.d.ts
CHANGED
|
@@ -77,16 +77,6 @@ interface RequestOptions {
|
|
|
77
77
|
responseType?: 'json' | 'blob' | 'text';
|
|
78
78
|
signal?: AbortSignal;
|
|
79
79
|
}
|
|
80
|
-
/**
|
|
81
|
-
* Arc scope for multi-tenant APIs.
|
|
82
|
-
*
|
|
83
|
-
* - `'tenant'` (default) — Org-scoped. Hooks auto-inject `organizationId` from auth context.
|
|
84
|
-
* - `'platform'` — Platform admin scope. Skips org injection, sets `x-arc-scope: platform`
|
|
85
|
-
* header so Arc's elevation plugin grants cross-org access for superadmins.
|
|
86
|
-
*/
|
|
87
|
-
type ArcScope = 'tenant' | 'platform';
|
|
88
|
-
/** Header name used by Arc's elevation plugin */
|
|
89
|
-
declare const ARC_SCOPE_HEADER = "x-arc-scope";
|
|
90
80
|
interface BaseApiConfig {
|
|
91
81
|
basePath?: string;
|
|
92
82
|
defaultParams?: {
|
|
@@ -96,32 +86,13 @@ interface BaseApiConfig {
|
|
|
96
86
|
};
|
|
97
87
|
cache?: RequestCache;
|
|
98
88
|
headers?: Record<string, string>;
|
|
99
|
-
/**
|
|
100
|
-
* API scope — controls org context injection and Arc scope headers.
|
|
101
|
-
*
|
|
102
|
-
* @default 'tenant'
|
|
103
|
-
*
|
|
104
|
-
* @example
|
|
105
|
-
* ```ts
|
|
106
|
-
* // Org-scoped API (default) — auto-injects organizationId
|
|
107
|
-
* const postsApi = createCrudApi('posts', { basePath: '/api' });
|
|
108
|
-
*
|
|
109
|
-
* // Platform admin API — skips org injection, adds x-arc-scope header
|
|
110
|
-
* const adminApi = createCrudApi('subscriptions', {
|
|
111
|
-
* basePath: '/api',
|
|
112
|
-
* scope: 'platform',
|
|
113
|
-
* });
|
|
114
|
-
* ```
|
|
115
|
-
*/
|
|
116
|
-
scope?: ArcScope;
|
|
117
89
|
client?: ArcClient;
|
|
118
90
|
}
|
|
119
91
|
declare class BaseApi<TDoc = Record<string, unknown>, TCreate = Partial<TDoc>, TUpdate = Partial<TDoc>> {
|
|
120
92
|
readonly entity: string;
|
|
121
|
-
readonly config: Required<Omit<BaseApiConfig, 'client'
|
|
93
|
+
readonly config: Required<Omit<BaseApiConfig, 'client'>>;
|
|
122
94
|
readonly baseUrl: string;
|
|
123
95
|
private readonly requestFn;
|
|
124
|
-
readonly scope: ArcScope;
|
|
125
96
|
constructor(entity: string, config?: BaseApiConfig);
|
|
126
97
|
/** Merge per-instance headers into request options */
|
|
127
98
|
private withHeaders;
|
|
@@ -254,5 +225,4 @@ declare function isOffsetPagination<T>(response: PaginatedResponse<T>): response
|
|
|
254
225
|
declare function isKeysetPagination<T>(response: PaginatedResponse<T>): response is KeysetPaginationResponse<T>;
|
|
255
226
|
declare function isAggregatePagination<T>(response: PaginatedResponse<T>): response is AggregatePaginationResponse<T>;
|
|
256
227
|
//#endregion
|
|
257
|
-
export {
|
|
258
|
-
//# sourceMappingURL=api.d.ts.map
|
|
228
|
+
export { AggregatePaginationResponse, ApiResponse, BaseApi, BaseApiConfig, DeleteResponse, ExtractDoc, FilterOperator, KeysetPaginationResponse, OffsetPaginationResponse, PaginatedResponse, PopulateOption, QueryParams, RequestOptions, SortDirection, SortSpec, createCrudApi, isAggregatePagination, isKeysetPagination, isOffsetPagination };
|
package/dist/api.js
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import { createQueryString, handleApiRequest } from "./client.js";
|
|
2
2
|
|
|
3
3
|
//#region src/api.ts
|
|
4
|
-
/** Header name used by Arc's elevation plugin */
|
|
5
|
-
const ARC_SCOPE_HEADER = "x-arc-scope";
|
|
6
4
|
var BaseApi = class {
|
|
7
5
|
entity;
|
|
8
6
|
config;
|
|
9
7
|
baseUrl;
|
|
10
8
|
requestFn;
|
|
11
|
-
scope;
|
|
12
9
|
constructor(entity, config = {}) {
|
|
13
10
|
this.entity = entity;
|
|
14
|
-
this.scope = config.scope ?? "tenant";
|
|
15
11
|
this.requestFn = config.client?.request ?? handleApiRequest;
|
|
16
|
-
const scopeHeaders = this.scope === "platform" ? { [ARC_SCOPE_HEADER]: "platform" } : {};
|
|
17
12
|
this.config = {
|
|
18
13
|
basePath: config.basePath ?? "/api/v1",
|
|
19
14
|
defaultParams: {
|
|
@@ -22,10 +17,7 @@ var BaseApi = class {
|
|
|
22
17
|
...config.defaultParams || {}
|
|
23
18
|
},
|
|
24
19
|
cache: config.cache ?? "no-store",
|
|
25
|
-
headers: {
|
|
26
|
-
...scopeHeaders,
|
|
27
|
-
...config.headers || {}
|
|
28
|
-
}
|
|
20
|
+
headers: { ...config.headers || {} }
|
|
29
21
|
};
|
|
30
22
|
this.baseUrl = `${this.config.basePath}/${this.entity}`;
|
|
31
23
|
}
|
|
@@ -65,7 +57,11 @@ var BaseApi = class {
|
|
|
65
57
|
return result;
|
|
66
58
|
}
|
|
67
59
|
async getAll({ token = null, organizationId = null, params = {}, options = {} } = {}) {
|
|
68
|
-
const
|
|
60
|
+
const mergedParams = {
|
|
61
|
+
...this.config.defaultParams,
|
|
62
|
+
...params
|
|
63
|
+
};
|
|
64
|
+
const processedParams = this.prepareParams(mergedParams);
|
|
69
65
|
const queryString = this.createQueryString(processedParams);
|
|
70
66
|
const requestOptions = {
|
|
71
67
|
cache: this.config.cache,
|
|
@@ -126,6 +122,7 @@ var BaseApi = class {
|
|
|
126
122
|
}
|
|
127
123
|
async search({ token = null, organizationId = null, searchParams = {}, params = {}, options = {} } = {}) {
|
|
128
124
|
const queryParams = {
|
|
125
|
+
...this.config.defaultParams,
|
|
129
126
|
...params,
|
|
130
127
|
...searchParams
|
|
131
128
|
};
|
|
@@ -141,7 +138,10 @@ var BaseApi = class {
|
|
|
141
138
|
}
|
|
142
139
|
async findBy({ token = null, organizationId = null, field, value, operator, params = {}, options = {} }) {
|
|
143
140
|
if (!field || value === void 0) throw new Error("Field and value are required");
|
|
144
|
-
const queryParams = {
|
|
141
|
+
const queryParams = {
|
|
142
|
+
...this.config.defaultParams,
|
|
143
|
+
...params
|
|
144
|
+
};
|
|
145
145
|
if (operator) queryParams[`${field}[${operator}]`] = Array.isArray(value) ? value.join(",") : value;
|
|
146
146
|
else queryParams[field] = value;
|
|
147
147
|
const processedParams = this.prepareParams(queryParams);
|
|
@@ -184,5 +184,4 @@ function isAggregatePagination(response) {
|
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
//#endregion
|
|
187
|
-
export {
|
|
188
|
-
//# sourceMappingURL=api.js.map
|
|
187
|
+
export { BaseApi, createCrudApi, isAggregatePagination, isKeysetPagination, isOffsetPagination };
|
package/dist/client.d.ts
CHANGED
|
@@ -56,10 +56,25 @@ interface ClientConfig {
|
|
|
56
56
|
* - 'cookie': Auth is handled via HTTP-only cookies (e.g. Better Auth). Queries are always enabled — no token needed.
|
|
57
57
|
*/
|
|
58
58
|
authMode?: 'bearer' | 'cookie';
|
|
59
|
+
/**
|
|
60
|
+
* Fetch credentials policy.
|
|
61
|
+
* - 'include': Always send cookies cross-origin (required for cookie-based auth).
|
|
62
|
+
* - 'same-origin': Only send cookies to same-origin requests (browser default).
|
|
63
|
+
* - 'omit': Never send cookies.
|
|
64
|
+
*
|
|
65
|
+
* When not set, derived from `authMode`:
|
|
66
|
+
* - `authMode: 'cookie'` → `'include'`
|
|
67
|
+
* - `authMode: 'bearer'` (default) → `'same-origin'`
|
|
68
|
+
*/
|
|
69
|
+
credentials?: RequestCredentials;
|
|
59
70
|
}
|
|
60
71
|
/**
|
|
61
72
|
* Configure the API client. Call once at app init before any API requests.
|
|
62
73
|
*
|
|
74
|
+
* **SSR safety:** This sets module-level state. In Next.js, call this only in
|
|
75
|
+
* client-side code (e.g. a `"use client"` provider or `useEffect`).
|
|
76
|
+
* Calling on the server risks leaking state between requests.
|
|
77
|
+
*
|
|
63
78
|
* @example
|
|
64
79
|
* // Bearer token auth (default)
|
|
65
80
|
* configureClient({ baseUrl: process.env.NEXT_PUBLIC_API_URL! });
|
|
@@ -80,6 +95,8 @@ interface AuthConfig {
|
|
|
80
95
|
* Configure auth context for automatic token/orgId injection.
|
|
81
96
|
* When configured, hooks auto-inject these values so you don't need to pass them manually.
|
|
82
97
|
*
|
|
98
|
+
* **SSR safety:** This sets module-level state. Call only in client-side code.
|
|
99
|
+
*
|
|
83
100
|
* @example
|
|
84
101
|
* // Cookie auth (no token needed)
|
|
85
102
|
* configureAuth({ getOrgId: () => currentOrg.id });
|
|
@@ -168,5 +185,4 @@ declare function handleApiRequest<T = unknown>(method: HttpMethod, endpoint: str
|
|
|
168
185
|
*/
|
|
169
186
|
declare function createQueryString<T extends Record<string, unknown>>(params?: T): string;
|
|
170
187
|
//#endregion
|
|
171
|
-
export { ApiRequestOptions, ArcApiError, ArcApiErrorOptions, ArcClient, ArcClientConfig, AuthConfig, BlobResponse, ClientConfig, HttpMethod, TextResponse, ToastHandler, UseRouterHook, configureAuth, configureClient, createClient, createQueryString, getAuthContext, getAuthMode, handleApiRequest, isArcApiError };
|
|
172
|
-
//# sourceMappingURL=client.d.ts.map
|
|
188
|
+
export { ApiRequestOptions, ArcApiError, ArcApiErrorOptions, ArcClient, ArcClientConfig, AuthConfig, BlobResponse, ClientConfig, HttpMethod, TextResponse, ToastHandler, UseRouterHook, configureAuth, configureClient, createClient, createQueryString, getAuthContext, getAuthMode, handleApiRequest, isArcApiError };
|
package/dist/client.js
CHANGED
|
@@ -43,6 +43,10 @@ let clientConfig = null;
|
|
|
43
43
|
/**
|
|
44
44
|
* Configure the API client. Call once at app init before any API requests.
|
|
45
45
|
*
|
|
46
|
+
* **SSR safety:** This sets module-level state. In Next.js, call this only in
|
|
47
|
+
* client-side code (e.g. a `"use client"` provider or `useEffect`).
|
|
48
|
+
* Calling on the server risks leaking state between requests.
|
|
49
|
+
*
|
|
46
50
|
* @example
|
|
47
51
|
* // Bearer token auth (default)
|
|
48
52
|
* configureClient({ baseUrl: process.env.NEXT_PUBLIC_API_URL! });
|
|
@@ -64,6 +68,8 @@ let authConfig = null;
|
|
|
64
68
|
* Configure auth context for automatic token/orgId injection.
|
|
65
69
|
* When configured, hooks auto-inject these values so you don't need to pass them manually.
|
|
66
70
|
*
|
|
71
|
+
* **SSR safety:** This sets module-level state. Call only in client-side code.
|
|
72
|
+
*
|
|
67
73
|
* @example
|
|
68
74
|
* // Cookie auth (no token needed)
|
|
69
75
|
* configureAuth({ getOrgId: () => currentOrg.id });
|
|
@@ -122,10 +128,11 @@ async function executeRequest(config, method, endpoint, options = {}) {
|
|
|
122
128
|
...headers,
|
|
123
129
|
...headerOptions
|
|
124
130
|
};
|
|
131
|
+
const credentials = config.credentials ?? (config.authMode === "cookie" ? "include" : "same-origin");
|
|
125
132
|
const fetchOptions = {
|
|
126
133
|
method,
|
|
127
134
|
headers,
|
|
128
|
-
credentials
|
|
135
|
+
credentials,
|
|
129
136
|
...signal ? { signal } : {}
|
|
130
137
|
};
|
|
131
138
|
if (body !== void 0 && body !== null) fetchOptions.body = body instanceof FormData ? body : JSON.stringify(body);
|
|
@@ -177,8 +184,8 @@ async function executeRequest(config, method, endpoint, options = {}) {
|
|
|
177
184
|
}
|
|
178
185
|
return data;
|
|
179
186
|
} catch (error) {
|
|
180
|
-
if (error instanceof
|
|
181
|
-
throw new Error(
|
|
187
|
+
if (error instanceof Error) throw error;
|
|
188
|
+
throw new Error("An error occurred while fetching data.");
|
|
182
189
|
}
|
|
183
190
|
}
|
|
184
191
|
/**
|
|
@@ -238,5 +245,4 @@ function createQueryString(params = {}) {
|
|
|
238
245
|
}
|
|
239
246
|
|
|
240
247
|
//#endregion
|
|
241
|
-
export { ArcApiError, configureAuth, configureClient, createClient, createQueryString, getAuthContext, getAuthMode, handleApiRequest, isArcApiError };
|
|
242
|
-
//# sourceMappingURL=client.js.map
|
|
248
|
+
export { ArcApiError, configureAuth, configureClient, createClient, createQueryString, getAuthContext, getAuthMode, handleApiRequest, isArcApiError };
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,84 +1,26 @@
|
|
|
1
1
|
import { ArcClient, UseRouterHook } from "./client.js";
|
|
2
|
+
import { BaseApi } from "./api.js";
|
|
2
3
|
import { MutationCallbacks, MutationMessages, TransitionMutationReturn } from "./mutation.js";
|
|
3
4
|
import { CacheUtils, DetailQueryOptions, DetailQueryResult, InfiniteListQueryOptions, InfiniteListQueryResult, ListQueryOptions, ListQueryResult, QueryKeys } from "./query.js";
|
|
4
5
|
import { QueryKey } from "@tanstack/react-query";
|
|
5
6
|
|
|
6
7
|
//#region src/hooks.d.ts
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}) => Promise<unknown>;
|
|
19
|
-
getById: (options: {
|
|
20
|
-
id: string;
|
|
21
|
-
token?: string | null;
|
|
22
|
-
organizationId?: string | null;
|
|
23
|
-
params?: {
|
|
24
|
-
select?: string;
|
|
25
|
-
populate?: string | string[];
|
|
26
|
-
};
|
|
27
|
-
options?: {
|
|
28
|
-
signal?: AbortSignal;
|
|
29
|
-
[key: string]: unknown;
|
|
30
|
-
};
|
|
31
|
-
}) => Promise<unknown>;
|
|
32
|
-
create: (options: {
|
|
33
|
-
token?: string | null;
|
|
34
|
-
organizationId?: string | null;
|
|
35
|
-
data: TCreate;
|
|
36
|
-
}) => Promise<unknown>;
|
|
37
|
-
update: (options: {
|
|
38
|
-
token?: string | null;
|
|
39
|
-
organizationId?: string | null;
|
|
40
|
-
id: string;
|
|
41
|
-
data: TUpdate;
|
|
42
|
-
}) => Promise<unknown>;
|
|
43
|
-
delete: (options: {
|
|
44
|
-
token?: string | null;
|
|
45
|
-
organizationId?: string | null;
|
|
46
|
-
id: string;
|
|
47
|
-
}) => Promise<unknown>;
|
|
48
|
-
upload?: (options: {
|
|
49
|
-
token?: string | null;
|
|
50
|
-
organizationId?: string | null;
|
|
51
|
-
data: FormData;
|
|
52
|
-
id?: string;
|
|
53
|
-
path?: string;
|
|
54
|
-
}) => Promise<unknown>;
|
|
55
|
-
search?: (options: {
|
|
56
|
-
token?: string | null;
|
|
57
|
-
organizationId?: string | null;
|
|
58
|
-
params?: Record<string, unknown>;
|
|
59
|
-
options?: {
|
|
60
|
-
signal?: AbortSignal;
|
|
61
|
-
[key: string]: unknown;
|
|
62
|
-
};
|
|
63
|
-
}) => Promise<unknown>;
|
|
64
|
-
}
|
|
8
|
+
/**
|
|
9
|
+
* CRUD API interface accepted by createCrudHooks.
|
|
10
|
+
*
|
|
11
|
+
* Derived from BaseApi via Pick so the types are always in sync.
|
|
12
|
+
* BaseApi instances satisfy this exactly (same source of truth).
|
|
13
|
+
* Custom implementations just need to match BaseApi's method signatures.
|
|
14
|
+
*/
|
|
15
|
+
type CrudApi<T = unknown, TCreate = Partial<T>, TUpdate = Partial<T>> = Pick<BaseApi<T, TCreate, TUpdate>, 'getAll' | 'getById' | 'create' | 'update' | 'delete'> & {
|
|
16
|
+
upload?: BaseApi<T, TCreate, TUpdate>['upload'];
|
|
17
|
+
search?: BaseApi<T, TCreate, TUpdate>['search'];
|
|
18
|
+
};
|
|
65
19
|
interface CrudHooksConfig<T, TCreate = Partial<T>, TUpdate = Partial<T>> {
|
|
66
20
|
api: CrudApi<T, TCreate, TUpdate>;
|
|
67
21
|
entityKey: string;
|
|
68
22
|
singular: string;
|
|
69
23
|
plural?: string;
|
|
70
|
-
/**
|
|
71
|
-
* Platform-scoped hooks skip automatic organizationId injection.
|
|
72
|
-
*
|
|
73
|
-
* Use this for admin/superadmin hooks that query across all orgs
|
|
74
|
-
* (e.g., APIs configured with `x-arc-scope: platform`).
|
|
75
|
-
*
|
|
76
|
-
* When true, the hooks will NOT auto-inject the user's active
|
|
77
|
-
* organizationId into requests — the API's own headers control scoping.
|
|
78
|
-
*
|
|
79
|
-
* @default false
|
|
80
|
-
*/
|
|
81
|
-
platformScoped?: boolean;
|
|
82
24
|
defaults?: {
|
|
83
25
|
staleTime?: number;
|
|
84
26
|
gcTime?: number;
|
|
@@ -181,6 +123,8 @@ interface CrudHooksReturn<T, TCreate, TUpdate> {
|
|
|
181
123
|
/**
|
|
182
124
|
* Configure the router hook for useNavigation. Call once at app init.
|
|
183
125
|
*
|
|
126
|
+
* **SSR safety:** This sets module-level state. Call only in client-side code.
|
|
127
|
+
*
|
|
184
128
|
* @example
|
|
185
129
|
* import { useRouter } from "next/navigation";
|
|
186
130
|
* configureNavigation(useRouter);
|
|
@@ -190,11 +134,9 @@ declare function createCrudHooks<T, TCreate = Partial<T>, TUpdate = Partial<T>>(
|
|
|
190
134
|
api,
|
|
191
135
|
entityKey,
|
|
192
136
|
singular,
|
|
193
|
-
platformScoped,
|
|
194
137
|
defaults,
|
|
195
138
|
callbacks,
|
|
196
139
|
client
|
|
197
140
|
}: CrudHooksConfig<T, TCreate, TUpdate>): CrudHooksReturn<T, TCreate, TUpdate>;
|
|
198
141
|
//#endregion
|
|
199
|
-
export { CallOptions, CrudActions, CrudApi, CrudHooksConfig, CrudHooksReturn, DeleteParams, MutationParams, NavigateFn, NavigationOptions, UpdateParams,
|
|
200
|
-
//# sourceMappingURL=hooks.d.ts.map
|
|
142
|
+
export { CallOptions, CrudActions, CrudApi, CrudHooksConfig, CrudHooksReturn, DeleteParams, MutationParams, NavigateFn, NavigationOptions, UpdateParams, configureNavigation, createCrudHooks };
|