@classytic/arc-next 0.3.1 → 0.4.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 +139 -32
- package/dist/api.d.ts +120 -3
- package/dist/api.js +109 -5
- package/dist/client.d.ts +63 -16
- package/dist/client.js +87 -18
- package/dist/hooks.d.ts +56 -2
- package/dist/hooks.js +360 -46
- package/dist/mutation.d.ts +5 -22
- package/dist/mutation.js +14 -15
- package/dist/prefetch.d.ts +56 -2
- package/dist/prefetch.js +70 -24
- package/dist/query.d.ts +41 -8
- package/dist/query.js +33 -11
- package/dist/sse.d.ts +69 -0
- package/dist/sse.js +145 -0
- package/package.json +22 -13
package/README.md
CHANGED
|
@@ -30,8 +30,9 @@ 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' | 'bearer' (default) | 'header'
|
|
34
|
+
// apiVersion: '2', // sends Accept-Version header
|
|
35
|
+
// autoIdempotency: true, // auto Idempotency-Key on mutations (retry-safe)
|
|
35
36
|
});
|
|
36
37
|
|
|
37
38
|
// Optional — auto-inject tenant context into queries/mutations
|
|
@@ -59,6 +60,7 @@ configureNavigation(useRouter);
|
|
|
59
60
|
| `@classytic/arc-next/hooks` | `createCrudHooks`, `configureNavigation` | Yes |
|
|
60
61
|
| `@classytic/arc-next/query-client` | `getQueryClient` (SSR-safe singleton) | No |
|
|
61
62
|
| `@classytic/arc-next/prefetch` | `createCrudPrefetcher`, `dehydrate` (SSR prefetch) | No |
|
|
63
|
+
| `@classytic/arc-next/sse` | `useEventStream` (Server-Sent Events for real-time cache invalidation) | Yes |
|
|
62
64
|
|
|
63
65
|
No barrel index — every file is its own entry point. Tree-shakeable (`sideEffects: false`).
|
|
64
66
|
|
|
@@ -148,25 +150,28 @@ export function ProductsPage() {
|
|
|
148
150
|
```ts
|
|
149
151
|
configureClient({
|
|
150
152
|
baseUrl: string; // Required — API base URL
|
|
151
|
-
authMode?: 'cookie' | '
|
|
153
|
+
authMode?: 'bearer' | 'cookie' | 'header'; // Default: 'bearer'
|
|
152
154
|
credentials?: RequestCredentials; // Default: derived from authMode
|
|
153
|
-
internalApiKey?: string; //
|
|
154
|
-
defaultHeaders?: Record<string, string>; //
|
|
155
|
+
internalApiKey?: string; // Sent as x-internal-api-key header
|
|
156
|
+
defaultHeaders?: Record<string, string>; // Merged into every request
|
|
157
|
+
apiVersion?: string; // Sent as Accept-Version header
|
|
158
|
+
autoIdempotency?: boolean; // Auto Idempotency-Key on mutations (retry-safe)
|
|
155
159
|
});
|
|
156
160
|
```
|
|
157
161
|
|
|
158
|
-
- `authMode: 'bearer'` (default) — requires
|
|
159
|
-
- `authMode: 'cookie'` — HTTP-only cookies (e.g. Better Auth); queries always enabled
|
|
160
|
-
- `
|
|
162
|
+
- `authMode: 'bearer'` (default) — requires token; queries disabled until provided
|
|
163
|
+
- `authMode: 'cookie'` — HTTP-only cookies (e.g. Better Auth); queries always enabled
|
|
164
|
+
- `authMode: 'header'` — custom header auth (e.g. `x-api-key`); uses `headerName` from `configureAuth`
|
|
161
165
|
|
|
162
|
-
Must be called before any API requests.
|
|
166
|
+
Must be called before any API requests. Warns if called on the server (SSR safety).
|
|
163
167
|
|
|
164
168
|
### `configureAuth(config)`
|
|
165
169
|
|
|
166
170
|
```ts
|
|
167
171
|
configureAuth({
|
|
168
|
-
getToken?: () => string | null; // For bearer auth — return access token
|
|
172
|
+
getToken?: () => string | null; // For bearer/header auth — return access token or API key
|
|
169
173
|
getOrgId?: () => string | null; // Return active organization ID
|
|
174
|
+
headerName?: string; // Custom header name for authMode: 'header' (default: 'x-api-key')
|
|
170
175
|
});
|
|
171
176
|
```
|
|
172
177
|
|
|
@@ -235,12 +240,15 @@ Factory that returns everything you need. The `api` parameter accepts any `creat
|
|
|
235
240
|
const {
|
|
236
241
|
KEYS, cache,
|
|
237
242
|
useList, useDetail, useInfiniteList,
|
|
238
|
-
useActions,
|
|
243
|
+
useActions, useBulkActions,
|
|
244
|
+
useDeleted, useDetailBySlug, useTree, useChildren, useFindBy,
|
|
245
|
+
useUpload, useSearch, useCustomMutation,
|
|
239
246
|
useNavigation,
|
|
240
247
|
} = createCrudHooks<Product, CreateProduct>({
|
|
241
248
|
api: productsApi, // from createCrudApi() — types inferred, no cast
|
|
242
249
|
entityKey: "products", // TanStack Query key prefix
|
|
243
250
|
singular: "Product", // for toast messages
|
|
251
|
+
idField: "sku", // optional — custom ID field for cache keys (default: _id → id)
|
|
244
252
|
defaults: { // optional
|
|
245
253
|
staleTime: 60_000,
|
|
246
254
|
messages: { createSuccess: "Product added!" },
|
|
@@ -393,27 +401,94 @@ const { mutateAsync: publish, isPending } = useCustomMutation({
|
|
|
393
401
|
});
|
|
394
402
|
```
|
|
395
403
|
|
|
404
|
+
#### `useDeleted(params?, options?)`
|
|
405
|
+
|
|
406
|
+
List soft-deleted items. Requires `softDelete` preset on the Arc resource.
|
|
407
|
+
|
|
408
|
+
#### `useDetailBySlug(slug, options?)`
|
|
409
|
+
|
|
410
|
+
Fetch a single item by slug (`GET /slug/:slug`). Requires `slugLookup` preset.
|
|
411
|
+
|
|
412
|
+
#### `useTree(params?, options?)`
|
|
413
|
+
|
|
414
|
+
Fetch hierarchical tree data (`GET /tree`). Requires `tree` preset.
|
|
415
|
+
|
|
416
|
+
#### `useChildren(parentId, params?, options?)`
|
|
417
|
+
|
|
418
|
+
Fetch children of a parent node (`GET /:parentId/children`). Requires `tree` preset.
|
|
419
|
+
|
|
420
|
+
#### `useFindBy(field, value, options?)`
|
|
421
|
+
|
|
422
|
+
Query by a single field with optional operator:
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
const { items } = useFindBy("status", "active");
|
|
426
|
+
const { items } = useFindBy("price", 50, { operator: "gte" });
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
#### `useBulkActions()`
|
|
430
|
+
|
|
431
|
+
```ts
|
|
432
|
+
const { bulkCreate, bulkUpdate, bulkRemove } = useBulkActions();
|
|
433
|
+
await bulkCreate({ data: [{ name: "A" }, { name: "B" }] });
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
#### `useEventStream(options)` (from `./sse`)
|
|
437
|
+
|
|
438
|
+
Subscribe to Arc SSE events with auto-reconnect and query invalidation:
|
|
439
|
+
|
|
440
|
+
```ts
|
|
441
|
+
import { useEventStream } from "@classytic/arc-next/sse";
|
|
442
|
+
|
|
443
|
+
// Global stream — all events (matches Arc's /events/stream)
|
|
444
|
+
const { isConnected } = useEventStream({
|
|
445
|
+
invalidateQueries: [agentKeys.lists()],
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// Filtered by resource — auto-generates patterns: ['agents.*']
|
|
449
|
+
const { lastEvent } = useEventStream({
|
|
450
|
+
resource: "agents",
|
|
451
|
+
invalidateQueries: [agentKeys.lists()],
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
// Custom SSE path or explicit patterns
|
|
455
|
+
const { isConnected } = useEventStream({
|
|
456
|
+
path: "/api/v2/events",
|
|
457
|
+
patterns: ["orders.created", "orders.updated"],
|
|
458
|
+
});
|
|
459
|
+
```
|
|
460
|
+
|
|
396
461
|
### Query Keys (`KEYS`)
|
|
397
462
|
|
|
398
463
|
```ts
|
|
399
|
-
KEYS.all
|
|
400
|
-
KEYS.lists()
|
|
401
|
-
KEYS.list(params)
|
|
402
|
-
KEYS.details()
|
|
403
|
-
KEYS.detail(id)
|
|
404
|
-
KEYS.
|
|
405
|
-
KEYS.
|
|
464
|
+
KEYS.all // ["products"]
|
|
465
|
+
KEYS.lists() // ["products", "list"]
|
|
466
|
+
KEYS.list(params) // ["products", "list", params]
|
|
467
|
+
KEYS.details() // ["products", "detail"]
|
|
468
|
+
KEYS.detail(id) // ["products", "detail", id]
|
|
469
|
+
KEYS.scopedDetail(id, orgId) // ["products", "detail", id, { _org: orgId }] (or bare when null)
|
|
470
|
+
KEYS.custom("stats", orgId) // ["products", "stats", orgId]
|
|
471
|
+
KEYS.scopedList("tenant", params) // ["products", "list", { _scope: "tenant", ...params }]
|
|
406
472
|
```
|
|
407
473
|
|
|
408
474
|
### Cache Utilities (`cache`)
|
|
409
475
|
|
|
410
476
|
```ts
|
|
411
|
-
|
|
412
|
-
await cache.invalidateLists(queryClient);
|
|
413
|
-
await cache.invalidateDetail(queryClient, id);
|
|
477
|
+
// Bare (single-tenant or public)
|
|
414
478
|
cache.setDetail(queryClient, id, data);
|
|
415
|
-
cache.getDetail(queryClient, id);
|
|
479
|
+
cache.getDetail(queryClient, id);
|
|
416
480
|
cache.removeDetail(queryClient, id);
|
|
481
|
+
await cache.invalidateDetail(queryClient, id); // prefix-matches ALL scoped variants
|
|
482
|
+
|
|
483
|
+
// Tenant-scoped (multi-tenant — isolated per org)
|
|
484
|
+
cache.setScopedDetail(queryClient, id, orgId, data);
|
|
485
|
+
cache.getScopedDetail(queryClient, id, orgId);
|
|
486
|
+
cache.removeScopedDetail(queryClient, id, orgId);
|
|
487
|
+
await cache.invalidateScopedDetail(queryClient, id, orgId);
|
|
488
|
+
|
|
489
|
+
// Global
|
|
490
|
+
await cache.invalidateAll(queryClient);
|
|
491
|
+
await cache.invalidateLists(queryClient);
|
|
417
492
|
```
|
|
418
493
|
|
|
419
494
|
### `getQueryClient(overrides?)`
|
|
@@ -467,7 +542,17 @@ export default async function ProductsPage() {
|
|
|
467
542
|
}
|
|
468
543
|
```
|
|
469
544
|
|
|
470
|
-
**Methods:** `prefetchList
|
|
545
|
+
**Methods:** `prefetchList`, `prefetchDetail`, `prefetchBySlug`, `prefetchDeleted`, `prefetchTree`
|
|
546
|
+
|
|
547
|
+
All accept auth options for protected routes:
|
|
548
|
+
|
|
549
|
+
```ts
|
|
550
|
+
await prefetcher.prefetchList(queryClient, { limit: 20 }, {
|
|
551
|
+
token: serverToken, // for bearer/header auth
|
|
552
|
+
organizationId: "org-1", // for multi-tenant
|
|
553
|
+
headers: { "x-api-key": apiKey }, // for custom header auth
|
|
554
|
+
});
|
|
555
|
+
```
|
|
471
556
|
|
|
472
557
|
## Custom Mutations
|
|
473
558
|
|
|
@@ -566,16 +651,30 @@ By default, `configureClient()` sets a single global `baseUrl`. Use `createClien
|
|
|
566
651
|
|
|
567
652
|
### Create isolated clients
|
|
568
653
|
|
|
654
|
+
Each client gets its own `baseUrl`, auth, and headers — fully independent from the global config:
|
|
655
|
+
|
|
569
656
|
```ts
|
|
570
657
|
import { createClient } from "@classytic/arc-next/client";
|
|
571
|
-
import { toast } from "sonner";
|
|
572
|
-
import { useRouter } from "next/navigation";
|
|
573
658
|
|
|
659
|
+
// Bearer auth for main API
|
|
660
|
+
const mainClient = createClient({
|
|
661
|
+
baseUrl: "https://api.example.com",
|
|
662
|
+
getToken: () => session.jwt,
|
|
663
|
+
getOrgId: () => currentOrg.id,
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
// API key auth for analytics (no token needed — hooks auto-enable)
|
|
574
667
|
const analyticsClient = createClient({
|
|
575
|
-
baseUrl: "https://analytics.
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
668
|
+
baseUrl: "https://analytics.internal",
|
|
669
|
+
authMode: "header",
|
|
670
|
+
getToken: () => env.ANALYTICS_KEY,
|
|
671
|
+
headerName: "x-analytics-key",
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
// Cookie auth for auth service
|
|
675
|
+
const authClient = createClient({
|
|
676
|
+
baseUrl: "https://auth.example.com",
|
|
677
|
+
authMode: "cookie",
|
|
579
678
|
});
|
|
580
679
|
```
|
|
581
680
|
|
|
@@ -709,12 +808,20 @@ const adminApi = createCrudApi("users", {
|
|
|
709
808
|
|
|
710
809
|
- **CRUD Factory** — `createCrudApi` + `createCrudHooks` generates typed API clients and React Query hooks
|
|
711
810
|
- **Optimistic Updates** — Create, update, delete with instant UI feedback and automatic rollback
|
|
712
|
-
- **Multi-Tenant Scoping** —
|
|
811
|
+
- **Multi-Tenant Scoping** — `scopedDetail(id, orgId)` + `scopedList` isolate cache per tenant. Scoped cache utils for reads/writes. Navigation prefill is tenant-aware.
|
|
713
812
|
- **Pagination Normalization** — Handles `docs`/`data`/`items`/`results` + any custom key, offset/keyset/aggregate pagination
|
|
714
813
|
- **Detail Cache Prefilling** — List results auto-populate detail query cache
|
|
715
814
|
- **React 19 Transitions** — `useMutationWithTransition` wraps invalidation in `startTransition`
|
|
716
|
-
- **Cookie &
|
|
717
|
-
- **
|
|
815
|
+
- **Cookie, Bearer & Header Auth** — `authMode: 'cookie'` / `'bearer'` / `'header'` (custom header like `x-api-key`)
|
|
816
|
+
- **Custom ID Fields** — `idField` on `createCrudHooks` for resources keyed by `sku`, `slug`, `code`, etc.
|
|
817
|
+
- **Preset Hooks** — `useDeleted`, `useBulkActions`, `useDetailBySlug`, `useTree`, `useChildren`, `useFindBy`
|
|
818
|
+
- **SSE Real-Time** — `useEventStream` with auto-reconnect, auto-pattern derivation from `resource`, query invalidation. Works with zero config or custom `path`.
|
|
819
|
+
- **Infinite Scroll** — `maxPages` for memory management with automatic scroll-back support
|
|
820
|
+
- **Idempotency** — `autoIdempotency` generates retry-safe keys at mutation level
|
|
821
|
+
- **API Versioning** — `apiVersion` sends `Accept-Version` header
|
|
822
|
+
- **SSR Prefetch** — `createCrudPrefetcher` + `prefetchBySlug` / `prefetchDeleted` / `prefetchTree`
|
|
823
|
+
- **SSR Safety** — warns when `configureClient`/`configureAuth` called on the server
|
|
824
|
+
- **Per-Client Auth** — `createClient({ getToken, getOrgId, headerName })` — each backend gets its own auth, queries auto-enable
|
|
718
825
|
- **Multi-Client** — `createClient()` for multiple API backends side by side
|
|
719
826
|
- **Pluggable Toast** — `configureToast()` — use sonner, react-hot-toast, or anything
|
|
720
827
|
- **Pluggable Navigation** — `configureNavigation()` — use Next.js, React Router, or any router
|
package/dist/api.d.ts
CHANGED
|
@@ -52,9 +52,22 @@ interface DeleteResponse {
|
|
|
52
52
|
soft?: boolean;
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
+
interface BulkCreateResponse<T = unknown> {
|
|
56
|
+
success: boolean;
|
|
57
|
+
data?: T[];
|
|
58
|
+
count?: number;
|
|
59
|
+
}
|
|
60
|
+
interface BulkUpdateResponse {
|
|
61
|
+
success: boolean;
|
|
62
|
+
modifiedCount?: number;
|
|
63
|
+
}
|
|
64
|
+
interface BulkDeleteResponse {
|
|
65
|
+
success: boolean;
|
|
66
|
+
deletedCount?: number;
|
|
67
|
+
}
|
|
55
68
|
type SortDirection = 1 | -1 | 'asc' | 'desc';
|
|
56
69
|
type SortSpec = Record<string, SortDirection> | string;
|
|
57
|
-
type FilterOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'startsWith' | 'endsWith' | 'regex';
|
|
70
|
+
type FilterOperator = 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'nin' | 'contains' | 'startsWith' | 'endsWith' | 'regex' | 'like' | 'exists' | 'size' | 'type';
|
|
58
71
|
interface QueryParams {
|
|
59
72
|
page?: number;
|
|
60
73
|
limit?: number;
|
|
@@ -65,6 +78,13 @@ interface QueryParams {
|
|
|
65
78
|
populate?: string | string[];
|
|
66
79
|
populateOptions?: PopulateOption[];
|
|
67
80
|
lean?: boolean | 'true' | 'false';
|
|
81
|
+
/** Database-agnostic joins. Maps alias → collection or full lookup config. */
|
|
82
|
+
lookup?: Record<string, string | {
|
|
83
|
+
from: string;
|
|
84
|
+
localField: string;
|
|
85
|
+
foreignField: string;
|
|
86
|
+
select?: string;
|
|
87
|
+
}>;
|
|
68
88
|
[key: string]: unknown;
|
|
69
89
|
}
|
|
70
90
|
interface RequestOptions {
|
|
@@ -212,12 +232,109 @@ declare class BaseApi<TDoc = Record<string, unknown>, TCreate = Partial<TDoc>, T
|
|
|
212
232
|
params,
|
|
213
233
|
options
|
|
214
234
|
}?: {
|
|
215
|
-
token?: string;
|
|
235
|
+
token?: string | null;
|
|
216
236
|
organizationId?: string | null;
|
|
217
237
|
data?: unknown;
|
|
218
238
|
params?: QueryParams;
|
|
219
239
|
options?: Omit<RequestOptions, 'token' | 'organizationId'>;
|
|
220
240
|
}): Promise<TResponse>;
|
|
241
|
+
getDeleted({
|
|
242
|
+
token,
|
|
243
|
+
organizationId,
|
|
244
|
+
params,
|
|
245
|
+
options
|
|
246
|
+
}?: {
|
|
247
|
+
token?: string | null;
|
|
248
|
+
organizationId?: string | null;
|
|
249
|
+
params?: QueryParams;
|
|
250
|
+
options?: Omit<RequestOptions, 'token' | 'organizationId'>;
|
|
251
|
+
}): Promise<PaginatedResponse<TDoc>>;
|
|
252
|
+
restore({
|
|
253
|
+
token,
|
|
254
|
+
organizationId,
|
|
255
|
+
id,
|
|
256
|
+
options
|
|
257
|
+
}: {
|
|
258
|
+
token?: string | null;
|
|
259
|
+
organizationId?: string | null;
|
|
260
|
+
id: string;
|
|
261
|
+
options?: Omit<RequestOptions, 'token' | 'organizationId'>;
|
|
262
|
+
}): Promise<ApiResponse<TDoc>>;
|
|
263
|
+
bulkCreate({
|
|
264
|
+
token,
|
|
265
|
+
organizationId,
|
|
266
|
+
data,
|
|
267
|
+
options
|
|
268
|
+
}: {
|
|
269
|
+
token?: string | null;
|
|
270
|
+
organizationId?: string | null;
|
|
271
|
+
data: TCreate[];
|
|
272
|
+
options?: Omit<RequestOptions, 'token' | 'organizationId'>;
|
|
273
|
+
}): Promise<BulkCreateResponse<TDoc>>;
|
|
274
|
+
bulkUpdate({
|
|
275
|
+
token,
|
|
276
|
+
organizationId,
|
|
277
|
+
filter,
|
|
278
|
+
data,
|
|
279
|
+
options
|
|
280
|
+
}: {
|
|
281
|
+
token?: string | null;
|
|
282
|
+
organizationId?: string | null;
|
|
283
|
+
filter: Record<string, unknown>;
|
|
284
|
+
data: TUpdate;
|
|
285
|
+
options?: Omit<RequestOptions, 'token' | 'organizationId'>;
|
|
286
|
+
}): Promise<BulkUpdateResponse>;
|
|
287
|
+
bulkDelete({
|
|
288
|
+
token,
|
|
289
|
+
organizationId,
|
|
290
|
+
filter,
|
|
291
|
+
options
|
|
292
|
+
}: {
|
|
293
|
+
token?: string | null;
|
|
294
|
+
organizationId?: string | null;
|
|
295
|
+
filter: Record<string, unknown>;
|
|
296
|
+
options?: Omit<RequestOptions, 'token' | 'organizationId'>;
|
|
297
|
+
}): Promise<BulkDeleteResponse>;
|
|
298
|
+
getBySlug({
|
|
299
|
+
token,
|
|
300
|
+
organizationId,
|
|
301
|
+
slug,
|
|
302
|
+
params,
|
|
303
|
+
options
|
|
304
|
+
}: {
|
|
305
|
+
token?: string | null;
|
|
306
|
+
organizationId?: string | null;
|
|
307
|
+
slug: string;
|
|
308
|
+
params?: {
|
|
309
|
+
select?: string;
|
|
310
|
+
populate?: string | string[];
|
|
311
|
+
};
|
|
312
|
+
options?: Omit<RequestOptions, 'token' | 'organizationId'>;
|
|
313
|
+
}): Promise<ApiResponse<TDoc>>;
|
|
314
|
+
getTree({
|
|
315
|
+
token,
|
|
316
|
+
organizationId,
|
|
317
|
+
params,
|
|
318
|
+
options
|
|
319
|
+
}?: {
|
|
320
|
+
token?: string | null;
|
|
321
|
+
organizationId?: string | null;
|
|
322
|
+
params?: QueryParams;
|
|
323
|
+
options?: Omit<RequestOptions, 'token' | 'organizationId'>;
|
|
324
|
+
}): Promise<ApiResponse<TDoc[]>>;
|
|
325
|
+
getChildren({
|
|
326
|
+
token,
|
|
327
|
+
organizationId,
|
|
328
|
+
parentId,
|
|
329
|
+
params,
|
|
330
|
+
options
|
|
331
|
+
}: {
|
|
332
|
+
token?: string | null;
|
|
333
|
+
organizationId?: string | null;
|
|
334
|
+
parentId: string;
|
|
335
|
+
params?: QueryParams;
|
|
336
|
+
options?: Omit<RequestOptions, 'token' | 'organizationId'>;
|
|
337
|
+
}): Promise<PaginatedResponse<TDoc>>;
|
|
221
338
|
}
|
|
222
339
|
declare function createCrudApi<TDoc = Record<string, unknown>, TCreate = Partial<TDoc>, TUpdate = Partial<TDoc>>(entity: string, config?: BaseApiConfig): BaseApi<TDoc, TCreate, TUpdate>;
|
|
223
340
|
type ExtractDoc<T> = T extends PaginatedResponse<infer D> ? D : never;
|
|
@@ -225,4 +342,4 @@ declare function isOffsetPagination<T>(response: PaginatedResponse<T>): response
|
|
|
225
342
|
declare function isKeysetPagination<T>(response: PaginatedResponse<T>): response is KeysetPaginationResponse<T>;
|
|
226
343
|
declare function isAggregatePagination<T>(response: PaginatedResponse<T>): response is AggregatePaginationResponse<T>;
|
|
227
344
|
//#endregion
|
|
228
|
-
export { AggregatePaginationResponse, ApiResponse, BaseApi, BaseApiConfig, DeleteResponse, ExtractDoc, FilterOperator, KeysetPaginationResponse, OffsetPaginationResponse, PaginatedResponse, PopulateOption, QueryParams, RequestOptions, SortDirection, SortSpec, createCrudApi, isAggregatePagination, isKeysetPagination, isOffsetPagination };
|
|
345
|
+
export { AggregatePaginationResponse, ApiResponse, BaseApi, BaseApiConfig, BulkCreateResponse, BulkDeleteResponse, BulkUpdateResponse, DeleteResponse, ExtractDoc, FilterOperator, KeysetPaginationResponse, OffsetPaginationResponse, PaginatedResponse, PopulateOption, QueryParams, RequestOptions, SortDirection, SortSpec, createCrudApi, isAggregatePagination, isKeysetPagination, isOffsetPagination };
|
package/dist/api.js
CHANGED
|
@@ -48,6 +48,19 @@ var BaseApi = class {
|
|
|
48
48
|
if (Array.isArray(value) && value.length > 0) result[key] = value;
|
|
49
49
|
return;
|
|
50
50
|
}
|
|
51
|
+
if (key === "lookup") {
|
|
52
|
+
if (typeof value === "object" && value !== null) Object.entries(value).forEach(([alias, lv]) => {
|
|
53
|
+
if (typeof lv === "string") result[`lookup[${alias}]`] = lv;
|
|
54
|
+
else if (typeof lv === "object" && lv !== null) {
|
|
55
|
+
const cfg = lv;
|
|
56
|
+
result[`lookup[${alias}][from]`] = cfg.from;
|
|
57
|
+
result[`lookup[${alias}][localField]`] = cfg.localField;
|
|
58
|
+
result[`lookup[${alias}][foreignField]`] = cfg.foreignField;
|
|
59
|
+
if (cfg.select) result[`lookup[${alias}][select]`] = cfg.select;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
51
64
|
if (value !== void 0 && value !== "") if (["page", "limit"].includes(key)) result[key] = parseInt(String(value)) || (key === "page" ? 1 : 10);
|
|
52
65
|
else if (Array.isArray(value)) {
|
|
53
66
|
if (value.length > 1) result[`${key}[in]`] = value.join(",");
|
|
@@ -83,7 +96,7 @@ var BaseApi = class {
|
|
|
83
96
|
if (organizationId) requestOptions.organizationId = organizationId;
|
|
84
97
|
return this.requestFn("GET", url, this.withHeaders(requestOptions));
|
|
85
98
|
}
|
|
86
|
-
async create({ token, organizationId = null, data, options = {} }) {
|
|
99
|
+
async create({ token = null, organizationId = null, data, options = {} }) {
|
|
87
100
|
const requestOptions = {
|
|
88
101
|
body: data,
|
|
89
102
|
...options
|
|
@@ -92,7 +105,7 @@ var BaseApi = class {
|
|
|
92
105
|
if (organizationId) requestOptions.organizationId = organizationId;
|
|
93
106
|
return this.requestFn("POST", this.baseUrl, this.withHeaders(requestOptions));
|
|
94
107
|
}
|
|
95
|
-
async update({ token, organizationId = null, id, data, options = {} }) {
|
|
108
|
+
async update({ token = null, organizationId = null, id, data, options = {} }) {
|
|
96
109
|
if (!id) throw new Error("ID is required");
|
|
97
110
|
const requestOptions = {
|
|
98
111
|
body: data,
|
|
@@ -102,14 +115,14 @@ var BaseApi = class {
|
|
|
102
115
|
if (organizationId) requestOptions.organizationId = organizationId;
|
|
103
116
|
return this.requestFn("PATCH", `${this.baseUrl}/${id}`, this.withHeaders(requestOptions));
|
|
104
117
|
}
|
|
105
|
-
async delete({ token, organizationId = null, id, options = {} }) {
|
|
118
|
+
async delete({ token = null, organizationId = null, id, options = {} }) {
|
|
106
119
|
if (!id) throw new Error("ID is required");
|
|
107
120
|
const requestOptions = { ...options };
|
|
108
121
|
if (token) requestOptions.token = token;
|
|
109
122
|
if (organizationId) requestOptions.organizationId = organizationId;
|
|
110
123
|
return this.requestFn("DELETE", `${this.baseUrl}/${id}`, this.withHeaders(requestOptions));
|
|
111
124
|
}
|
|
112
|
-
async upload({ token, organizationId = null, data, id, path, options = {} }) {
|
|
125
|
+
async upload({ token = null, organizationId = null, data, id, path, options = {} }) {
|
|
113
126
|
const suffix = path ?? (id ? `${id}/upload` : void 0);
|
|
114
127
|
const url = suffix ? `${this.baseUrl}/${suffix}` : this.baseUrl;
|
|
115
128
|
const requestOptions = {
|
|
@@ -154,7 +167,7 @@ var BaseApi = class {
|
|
|
154
167
|
if (organizationId) requestOptions.organizationId = organizationId;
|
|
155
168
|
return this.requestFn("GET", `${this.baseUrl}?${queryString}`, this.withHeaders(requestOptions));
|
|
156
169
|
}
|
|
157
|
-
async request(method, endpoint, { token, organizationId = null, data, params, options = {} } = {}) {
|
|
170
|
+
async request(method, endpoint, { token = null, organizationId = null, data, params, options = {} } = {}) {
|
|
158
171
|
let url = endpoint;
|
|
159
172
|
if (params) {
|
|
160
173
|
const processedParams = this.prepareParams(params);
|
|
@@ -169,6 +182,97 @@ var BaseApi = class {
|
|
|
169
182
|
if (organizationId) requestOptions.organizationId = organizationId;
|
|
170
183
|
return this.requestFn(method, url, this.withHeaders(requestOptions));
|
|
171
184
|
}
|
|
185
|
+
async getDeleted({ token = null, organizationId = null, params = {}, options = {} } = {}) {
|
|
186
|
+
const mergedParams = {
|
|
187
|
+
...this.config.defaultParams,
|
|
188
|
+
...params
|
|
189
|
+
};
|
|
190
|
+
const processedParams = this.prepareParams(mergedParams);
|
|
191
|
+
const queryString = this.createQueryString(processedParams);
|
|
192
|
+
const requestOptions = {
|
|
193
|
+
cache: this.config.cache,
|
|
194
|
+
...options
|
|
195
|
+
};
|
|
196
|
+
if (token) requestOptions.token = token;
|
|
197
|
+
if (organizationId) requestOptions.organizationId = organizationId;
|
|
198
|
+
return this.requestFn("GET", `${this.baseUrl}/deleted?${queryString}`, this.withHeaders(requestOptions));
|
|
199
|
+
}
|
|
200
|
+
async restore({ token = null, organizationId = null, id, options = {} }) {
|
|
201
|
+
if (!id) throw new Error("ID is required");
|
|
202
|
+
const requestOptions = { ...options };
|
|
203
|
+
if (token) requestOptions.token = token;
|
|
204
|
+
if (organizationId) requestOptions.organizationId = organizationId;
|
|
205
|
+
return this.requestFn("POST", `${this.baseUrl}/${id}/restore`, this.withHeaders(requestOptions));
|
|
206
|
+
}
|
|
207
|
+
async bulkCreate({ token = null, organizationId = null, data, options = {} }) {
|
|
208
|
+
const requestOptions = {
|
|
209
|
+
body: data,
|
|
210
|
+
...options
|
|
211
|
+
};
|
|
212
|
+
if (token) requestOptions.token = token;
|
|
213
|
+
if (organizationId) requestOptions.organizationId = organizationId;
|
|
214
|
+
return this.requestFn("POST", `${this.baseUrl}/bulk`, this.withHeaders(requestOptions));
|
|
215
|
+
}
|
|
216
|
+
async bulkUpdate({ token = null, organizationId = null, filter, data, options = {} }) {
|
|
217
|
+
const requestOptions = {
|
|
218
|
+
body: {
|
|
219
|
+
filter,
|
|
220
|
+
data
|
|
221
|
+
},
|
|
222
|
+
...options
|
|
223
|
+
};
|
|
224
|
+
if (token) requestOptions.token = token;
|
|
225
|
+
if (organizationId) requestOptions.organizationId = organizationId;
|
|
226
|
+
return this.requestFn("PATCH", `${this.baseUrl}/bulk`, this.withHeaders(requestOptions));
|
|
227
|
+
}
|
|
228
|
+
async bulkDelete({ token = null, organizationId = null, filter, options = {} }) {
|
|
229
|
+
const requestOptions = {
|
|
230
|
+
body: { filter },
|
|
231
|
+
...options
|
|
232
|
+
};
|
|
233
|
+
if (token) requestOptions.token = token;
|
|
234
|
+
if (organizationId) requestOptions.organizationId = organizationId;
|
|
235
|
+
return this.requestFn("DELETE", `${this.baseUrl}/bulk`, this.withHeaders(requestOptions));
|
|
236
|
+
}
|
|
237
|
+
async getBySlug({ token = null, organizationId = null, slug, params = {}, options = {} }) {
|
|
238
|
+
if (!slug) throw new Error("Slug is required");
|
|
239
|
+
const queryString = this.createQueryString(params);
|
|
240
|
+
const url = queryString ? `${this.baseUrl}/slug/${slug}?${queryString}` : `${this.baseUrl}/slug/${slug}`;
|
|
241
|
+
const requestOptions = {
|
|
242
|
+
cache: this.config.cache,
|
|
243
|
+
...options
|
|
244
|
+
};
|
|
245
|
+
if (token) requestOptions.token = token;
|
|
246
|
+
if (organizationId) requestOptions.organizationId = organizationId;
|
|
247
|
+
return this.requestFn("GET", url, this.withHeaders(requestOptions));
|
|
248
|
+
}
|
|
249
|
+
async getTree({ token = null, organizationId = null, params = {}, options = {} } = {}) {
|
|
250
|
+
const processedParams = this.prepareParams(params);
|
|
251
|
+
const queryString = this.createQueryString(processedParams);
|
|
252
|
+
const requestOptions = {
|
|
253
|
+
cache: this.config.cache,
|
|
254
|
+
...options
|
|
255
|
+
};
|
|
256
|
+
if (token) requestOptions.token = token;
|
|
257
|
+
if (organizationId) requestOptions.organizationId = organizationId;
|
|
258
|
+
return this.requestFn("GET", `${this.baseUrl}/tree?${queryString}`, this.withHeaders(requestOptions));
|
|
259
|
+
}
|
|
260
|
+
async getChildren({ token = null, organizationId = null, parentId, params = {}, options = {} }) {
|
|
261
|
+
if (!parentId) throw new Error("Parent ID is required");
|
|
262
|
+
const mergedParams = {
|
|
263
|
+
...this.config.defaultParams,
|
|
264
|
+
...params
|
|
265
|
+
};
|
|
266
|
+
const processedParams = this.prepareParams(mergedParams);
|
|
267
|
+
const queryString = this.createQueryString(processedParams);
|
|
268
|
+
const requestOptions = {
|
|
269
|
+
cache: this.config.cache,
|
|
270
|
+
...options
|
|
271
|
+
};
|
|
272
|
+
if (token) requestOptions.token = token;
|
|
273
|
+
if (organizationId) requestOptions.organizationId = organizationId;
|
|
274
|
+
return this.requestFn("GET", `${this.baseUrl}/${parentId}/children?${queryString}`, this.withHeaders(requestOptions));
|
|
275
|
+
}
|
|
172
276
|
};
|
|
173
277
|
function createCrudApi(entity, config = {}) {
|
|
174
278
|
return new BaseApi(entity, config);
|