@panneau/data 5.0.0-alpha.3 → 5.0.0-alpha.5
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 +30 -1
- package/dist/adapter.d.ts +33 -15
- package/dist/adapter.d.ts.map +1 -1
- package/dist/errors.d.ts +14 -2
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +13 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/problem.d.ts +5 -0
- package/dist/problem.d.ts.map +1 -1
- package/dist/problem.js +12 -1
- package/dist/problem.js.map +1 -1
- package/dist/query/context.d.ts +5 -0
- package/dist/query/context.d.ts.map +1 -1
- package/dist/query/context.js +8 -1
- package/dist/query/context.js.map +1 -1
- package/dist/query/hooks.d.ts +14 -2
- package/dist/query/hooks.d.ts.map +1 -1
- package/dist/query/hooks.js +70 -2
- package/dist/query/hooks.js.map +1 -1
- package/dist/query/index.d.ts +4 -4
- package/dist/query/index.d.ts.map +1 -1
- package/dist/query/index.js +4 -4
- package/dist/query/keys.d.ts +9 -0
- package/dist/query/keys.d.ts.map +1 -1
- package/dist/query/keys.js +17 -0
- package/dist/query/keys.js.map +1 -1
- package/dist/query/options.d.ts +15 -3
- package/dist/query/options.d.ts.map +1 -1
- package/dist/query/options.js +37 -1
- package/dist/query/options.js.map +1 -1
- package/dist/rest.d.ts.map +1 -1
- package/dist/rest.js +34 -42
- package/dist/rest.js.map +1 -1
- package/dist/uploads/index.d.ts +58 -0
- package/dist/uploads/index.d.ts.map +1 -0
- package/dist/uploads/index.js +61 -0
- package/dist/uploads/index.js.map +1 -0
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -22,11 +22,12 @@ try {
|
|
|
22
22
|
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
23
23
|
| `@panneau/data` | The `DataAdapter` interface, `createRestAdapter()`, the errors (`ApiError`, `ValidationError`, `ConflictError`, `UnauthorizedError`, `ForbiddenError`, `NotFoundError`), `createApiError()` and `readProblem()` |
|
|
24
24
|
| `@panneau/data/laravel` | `createLaravelAdapter()`: the REST adapter with credentials, the `X-XSRF-TOKEN` header from Laravel's cookie, and `419` as unauthorized; `getLaravelHeaders()` and `createLaravelError()` for an app's own endpoints |
|
|
25
|
+
| `@panneau/data/uploads` | `createPresignedUploads()`: direct uploads to S3 with URLs that the backend signs, for Uppy 6's `@uppy/aws-s3` |
|
|
25
26
|
| `@panneau/data/query` | `DataAdapterProvider` and the TanStack Query hooks: `useDefinition`, `useResourceList`, `useResourceItem`, `useCreateItem`, `useUpdateItem`, `useDeleteItem`, `useRunAction`, `useActionRun` |
|
|
26
27
|
|
|
27
28
|
## Adapters
|
|
28
29
|
|
|
29
|
-
A `DataAdapter` is everything the engine and the app need from a backend: the definition, resources (list, get, create, update, delete), actions and their runs,
|
|
30
|
+
A `DataAdapter` is everything the engine and the app need from a backend: the definition, resources (list, get, create, update, delete), actions and their runs, the auth session, search and widget data. Media are items of resources of kind `media` ([0026](../../docs/decisions/0026-media-libraries.md)), through the resource methods. Methods reject with an `ApiError` when the backend answers with an error.
|
|
30
31
|
|
|
31
32
|
- **`createRestAdapter({ baseUrl, fetch, headers, credentials })`** implements the HTTP API of the protocol:
|
|
32
33
|
- `application/problem+json` errors become typed errors, by status: `401` `UnauthorizedError`, `403` `ForbiddenError`, `404` `NotFoundError`, `412` `ConflictError` (with `current`), `422` `ValidationError` (with `errors` keyed by dot path), others `ApiError`. Responses that are not problems get a problem built from their status;
|
|
@@ -59,6 +60,33 @@ if (!response.ok) {
|
|
|
59
60
|
- `createApiError(problem)` maps a problem to its typed error by status; `createLaravelError(problem)` also treats `419` (an expired CSRF token or session) as `UnauthorizedError`.
|
|
60
61
|
- `getLaravelHeaders({ headers, xsrfCookie, xsrfHeader, cookies })` adds `X-Requested-With` and the CSRF token from Laravel's cookie.
|
|
61
62
|
|
|
63
|
+
### Direct uploads
|
|
64
|
+
|
|
65
|
+
Large files (videos of several gigabytes) go from the browser to S3-compatible storage, in parts, with URLs that the backend signs: the `presigned` mode of the protocol ([0025](../../docs/decisions/0025-presigned-uploads.md)). `createPresignedUploads()` gives Uppy 6 its `signRequest`, and completes each upload. An upload is not a media: values reference it as `{ upload: 'upl_…' }`, such as the `file` of a media.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import AwsS3 from '@uppy/aws-s3';
|
|
69
|
+
import Uppy from '@uppy/core';
|
|
70
|
+
|
|
71
|
+
import { createLaravelError, getLaravelHeaders } from '@panneau/data/laravel';
|
|
72
|
+
import { createPresignedUploads } from '@panneau/data/uploads';
|
|
73
|
+
|
|
74
|
+
const uploads = createPresignedUploads({
|
|
75
|
+
baseUrl: definition.api.baseUrl, // the routes are under {baseUrl}/uploads, or at uploads.endpoint
|
|
76
|
+
headers: () => getLaravelHeaders(),
|
|
77
|
+
createError: createLaravelError,
|
|
78
|
+
});
|
|
79
|
+
const uppy = new Uppy().use(AwsS3, { signRequest: uploads.signRequest });
|
|
80
|
+
uppy.on('upload-success', async (file, response) => {
|
|
81
|
+
const upload = await uploads.complete(response.body.key, { name: file.name });
|
|
82
|
+
const media = await adapter.create('media', { file: { upload: upload.id } }); // media.media.status: 'processing'
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
It is plain `fetch` and doesn't import Uppy. Without direct uploads, `create()` sends a file in `multipart/form-data`.
|
|
87
|
+
|
|
88
|
+
The REST adapter gives the same object, with its own base URL, headers, credentials and errors: `adapter.presignedUploads({ endpoint: definition.uploads.endpoint })` (optional in `DataAdapter`). The fields of `@panneau/fields` and `@panneau/media` upload through it with `createUppyUploadSource()` of `@panneau/fields/uppy`.
|
|
89
|
+
|
|
62
90
|
## Query hooks
|
|
63
91
|
|
|
64
92
|
The hooks need TanStack Query v5 and React (optional peer dependencies, only for this entry point), with a `QueryClientProvider` and a `DataAdapterProvider` above them.
|
|
@@ -83,6 +111,7 @@ function Pages() {
|
|
|
83
111
|
|
|
84
112
|
- Mutations update the cache: create and update cache the item and refetch the lists of the resource; delete removes the item and refetches the lists.
|
|
85
113
|
- `useRunAction()` refreshes what the action result asks (`refresh`: `none`, `items` or `list`, the default). A queued action returns a run instead: `useActionRun(run.id)` follows it until it finishes, then refreshes.
|
|
114
|
+
- `useDataAdapter()` gives the adapter of `DataAdapterProvider`, and throws without one. `useOptionalDataAdapter()` gives `null` instead, for components that also work without a backend, such as the media field.
|
|
86
115
|
- `queryKeys` and the `*QueryOptions()` functions give the keys and options of the hooks, to prefetch or invalidate their data.
|
|
87
116
|
|
|
88
117
|
The package layout, scripts and API reports are explained in [`docs/packages.md`](../../docs/packages.md).
|
package/dist/adapter.d.ts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import type { ActionRequest, ActionResult, ActionRun, Definition, Item, ListQuery, ListResponse,
|
|
1
|
+
import type { ActionRequest, ActionResult, ActionRun, Definition, FieldCheckRequest, FieldCheckResponse, Item, ListQuery, ListResponse, PreviewResponse, SearchResponse, Session } from '@panneau/protocol';
|
|
2
|
+
import type { PresignedUploads } from './uploads';
|
|
2
3
|
/** Values of an item's fields, keyed by field name. */
|
|
3
4
|
export type Values = Record<string, unknown>;
|
|
4
5
|
export interface RequestOptions {
|
|
5
6
|
/** Aborts the request. TanStack Query passes one to cancel queries. */
|
|
6
7
|
signal?: AbortSignal;
|
|
7
8
|
}
|
|
9
|
+
/** Options of the global search (`GET /search`). */
|
|
10
|
+
export interface SearchOptions extends RequestOptions {
|
|
11
|
+
/** Items per resource, from 1 to 20. The backend defaults to 5. */
|
|
12
|
+
limit?: number;
|
|
13
|
+
/** The searchable resources to search. Defaults to every one. */
|
|
14
|
+
resources?: readonly string[];
|
|
15
|
+
}
|
|
8
16
|
export interface UpdateOptions extends RequestOptions {
|
|
9
17
|
/**
|
|
10
18
|
* The version of the item the values were edited from. The update fails with a
|
|
@@ -20,8 +28,6 @@ export type ActionResponse = {
|
|
|
20
28
|
run: ActionRun;
|
|
21
29
|
result?: undefined;
|
|
22
30
|
};
|
|
23
|
-
/** Query of the media list: a list query whose filters include `type` and `tags`. */
|
|
24
|
-
export type MediaQuery = ListQuery;
|
|
25
31
|
/** Credentials of a password login. */
|
|
26
32
|
export interface LoginCredentials {
|
|
27
33
|
email: string;
|
|
@@ -60,24 +66,36 @@ export interface DataAdapter {
|
|
|
60
66
|
listActionRuns(options?: RequestOptions): Promise<ActionRun[]>;
|
|
61
67
|
/** Cancels a run that hasn't finished. */
|
|
62
68
|
cancelActionRun(id: string, options?: RequestOptions): Promise<void>;
|
|
63
|
-
/**
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
/**
|
|
70
|
+
* Direct uploads to storage through this adapter, with its headers, cookies and errors, when
|
|
71
|
+
* the definition announces `"uploads": { "mode": "presigned" }`. `endpoint` is the definition's
|
|
72
|
+
* `uploads.endpoint`, when the routes are not under the API. Optional: an adapter without it
|
|
73
|
+
* sends files in multipart requests only. Media are items of resources of kind `media`
|
|
74
|
+
* (decision 0026): `create()` makes one from `{ file: { upload } }`.
|
|
75
|
+
*/
|
|
76
|
+
presignedUploads?(options?: {
|
|
77
|
+
endpoint?: string;
|
|
78
|
+
}): PresignedUploads;
|
|
73
79
|
/** The current session. Rejects with an `UnauthorizedError` without one. */
|
|
74
80
|
getSession(options?: RequestOptions): Promise<Session>;
|
|
75
81
|
/** Logs in with a password. */
|
|
76
82
|
login(credentials: LoginCredentials, options?: RequestOptions): Promise<Session>;
|
|
77
83
|
/** Logs out. */
|
|
78
84
|
logout(options?: RequestOptions): Promise<void>;
|
|
79
|
-
/**
|
|
80
|
-
|
|
85
|
+
/**
|
|
86
|
+
* A short-lived URL of the item's page on the site, rendered with these values, for the iframe
|
|
87
|
+
* previews of resources whose `features.preview` is `{ "mode": "iframe" }` (decision 0009).
|
|
88
|
+
* Optional: without it, iframe previews are not shown.
|
|
89
|
+
*/
|
|
90
|
+
previewItem?(resource: string, id: string, values: Values, options?: RequestOptions): Promise<PreviewResponse>;
|
|
91
|
+
/** Searches the searchable resources: every one, or those of `resources`. */
|
|
92
|
+
search(q: string, options?: SearchOptions): Promise<SearchResponse>;
|
|
93
|
+
/**
|
|
94
|
+
* Checks a value of a field with `check` as it is typed: whether a slug is available, and its
|
|
95
|
+
* URL (`POST /resources/{resource}/fields/{field}/check`). Optional: without it, fields don't
|
|
96
|
+
* check.
|
|
97
|
+
*/
|
|
98
|
+
checkField?(resource: string, field: string, request: FieldCheckRequest, options?: RequestOptions): Promise<FieldCheckResponse>;
|
|
81
99
|
/** Data of a dashboard widget, in the shape its type defines. */
|
|
82
100
|
getWidgetData(widget: string, options?: RequestOptions): Promise<unknown>;
|
|
83
101
|
}
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,aAAa,EACb,YAAY,EACZ,SAAS,EACT,UAAU,EACV,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Item, Problem, ValidationIssue } from '@panneau/protocol';
|
|
2
2
|
/**
|
|
3
3
|
* An error response of the API, parsed from `application/problem+json`. Subclasses cover the
|
|
4
|
-
* statuses a client handles differently. Other statuses (`409`, `
|
|
4
|
+
* statuses a client handles differently. Other statuses (`409`, `5xx`…) are an `ApiError`.
|
|
5
5
|
*/
|
|
6
6
|
export declare class ApiError extends Error {
|
|
7
7
|
readonly name: string;
|
|
@@ -37,6 +37,18 @@ export declare class ForbiddenError extends ApiError {
|
|
|
37
37
|
export declare class NotFoundError extends ApiError {
|
|
38
38
|
readonly name: string;
|
|
39
39
|
}
|
|
40
|
+
/** Options of `createApiError()`: what the response says besides its problem. */
|
|
41
|
+
export interface ApiErrorOptions extends ErrorOptions {
|
|
42
|
+
/** Seconds to wait before the next request, from `Retry-After`. */
|
|
43
|
+
retryAfter?: number | null;
|
|
44
|
+
}
|
|
45
|
+
/** `429`: too many requests. The next one can be sent after `retryAfter` seconds. */
|
|
46
|
+
export declare class RateLimitedError extends ApiError {
|
|
47
|
+
readonly name: string;
|
|
48
|
+
/** Seconds to wait, from `Retry-After`, or `null` when the backend didn't say. */
|
|
49
|
+
readonly retryAfter: number | null;
|
|
50
|
+
constructor(problem: Problem, options?: ApiErrorOptions);
|
|
51
|
+
}
|
|
40
52
|
/** A successful response whose body doesn't match the protocol. */
|
|
41
53
|
export declare class InvalidResponseError extends Error {
|
|
42
54
|
readonly name: string;
|
|
@@ -45,7 +57,7 @@ export declare class InvalidResponseError extends Error {
|
|
|
45
57
|
constructor(message: string, issues: ValidationIssue[]);
|
|
46
58
|
}
|
|
47
59
|
/** Creates the error that matches the status of a problem. */
|
|
48
|
-
export declare function createApiError(problem: Problem, options?:
|
|
60
|
+
export declare function createApiError(problem: Problem, options?: ApiErrorOptions): ApiError;
|
|
49
61
|
/** Whether a value is an error of the API. */
|
|
50
62
|
export declare function isApiError(error: unknown): error is ApiError;
|
|
51
63
|
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAExE;;;GAGG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAC/B,SAAkB,IAAI,EAAE,MAAM,CAAc;IAC5C,uBAAuB;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2FAA2F;IAC3F,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;gBAEd,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY;CAKvD;AAED,+CAA+C;AAC/C,qBAAa,eAAgB,SAAQ,QAAQ;IACzC,SAAkB,IAAI,EAAE,MAAM,CAAqB;IACnD,0FAA0F;IAC1F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;gBAE9B,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY;CAIvD;AAED,oEAAoE;AACpE,qBAAa,aAAc,SAAQ,QAAQ;IACvC,SAAkB,IAAI,EAAE,MAAM,CAAmB;IACjD,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;gBAElB,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY;CAIvD;AAED,+EAA+E;AAC/E,qBAAa,iBAAkB,SAAQ,QAAQ;IAC3C,SAAkB,IAAI,EAAE,MAAM,CAAuB;CACxD;AAED,iDAAiD;AACjD,qBAAa,cAAe,SAAQ,QAAQ;IACxC,SAAkB,IAAI,EAAE,MAAM,CAAoB;CACrD;AAED,wDAAwD;AACxD,qBAAa,aAAc,SAAQ,QAAQ;IACvC,SAAkB,IAAI,EAAE,MAAM,CAAmB;CACpD;AAED,mEAAmE;AACnE,qBAAa,oBAAqB,SAAQ,KAAK;IAC3C,SAAkB,IAAI,EAAE,MAAM,CAA0B;IACxD,wDAAwD;IACxD,QAAQ,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC;gBAEvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE;CAIzD;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAExE;;;GAGG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAC/B,SAAkB,IAAI,EAAE,MAAM,CAAc;IAC5C,uBAAuB;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2FAA2F;IAC3F,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;gBAEd,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY;CAKvD;AAED,+CAA+C;AAC/C,qBAAa,eAAgB,SAAQ,QAAQ;IACzC,SAAkB,IAAI,EAAE,MAAM,CAAqB;IACnD,0FAA0F;IAC1F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;gBAE9B,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY;CAIvD;AAED,oEAAoE;AACpE,qBAAa,aAAc,SAAQ,QAAQ;IACvC,SAAkB,IAAI,EAAE,MAAM,CAAmB;IACjD,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,IAAI,GAAG,IAAI,CAAC;gBAElB,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,YAAY;CAIvD;AAED,+EAA+E;AAC/E,qBAAa,iBAAkB,SAAQ,QAAQ;IAC3C,SAAkB,IAAI,EAAE,MAAM,CAAuB;CACxD;AAED,iDAAiD;AACjD,qBAAa,cAAe,SAAQ,QAAQ;IACxC,SAAkB,IAAI,EAAE,MAAM,CAAoB;CACrD;AAED,wDAAwD;AACxD,qBAAa,aAAc,SAAQ,QAAQ;IACvC,SAAkB,IAAI,EAAE,MAAM,CAAmB;CACpD;AAED,iFAAiF;AACjF,MAAM,WAAW,eAAgB,SAAQ,YAAY;IACjD,mEAAmE;IACnE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,qFAAqF;AACrF,qBAAa,gBAAiB,SAAQ,QAAQ;IAC1C,SAAkB,IAAI,EAAE,MAAM,CAAsB;IACpD,kFAAkF;IAClF,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;gBAEvB,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,eAAe;CAI1D;AAED,mEAAmE;AACnE,qBAAa,oBAAqB,SAAQ,KAAK;IAC3C,SAAkB,IAAI,EAAE,MAAM,CAA0B;IACxD,wDAAwD;IACxD,QAAQ,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC;gBAEvB,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE;CAIzD;AAED,8DAA8D;AAC9D,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,eAAe,GAAG,QAAQ,CAiBpF;AAED,8CAA8C;AAC9C,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D"}
|
package/dist/errors.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//#region src/errors.ts
|
|
2
2
|
/**
|
|
3
3
|
* An error response of the API, parsed from `application/problem+json`. Subclasses cover the
|
|
4
|
-
* statuses a client handles differently. Other statuses (`409`, `
|
|
4
|
+
* statuses a client handles differently. Other statuses (`409`, `5xx`…) are an `ApiError`.
|
|
5
5
|
*/
|
|
6
6
|
var ApiError = class extends Error {
|
|
7
7
|
name = "ApiError";
|
|
@@ -47,6 +47,16 @@ var ForbiddenError = class extends ApiError {
|
|
|
47
47
|
var NotFoundError = class extends ApiError {
|
|
48
48
|
name = "NotFoundError";
|
|
49
49
|
};
|
|
50
|
+
/** `429`: too many requests. The next one can be sent after `retryAfter` seconds. */
|
|
51
|
+
var RateLimitedError = class extends ApiError {
|
|
52
|
+
name = "RateLimitedError";
|
|
53
|
+
/** Seconds to wait, from `Retry-After`, or `null` when the backend didn't say. */
|
|
54
|
+
retryAfter;
|
|
55
|
+
constructor(problem, options) {
|
|
56
|
+
super(problem, options);
|
|
57
|
+
this.retryAfter = options?.retryAfter ?? null;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
50
60
|
/** A successful response whose body doesn't match the protocol. */
|
|
51
61
|
var InvalidResponseError = class extends Error {
|
|
52
62
|
name = "InvalidResponseError";
|
|
@@ -65,6 +75,7 @@ function createApiError(problem, options) {
|
|
|
65
75
|
case 404: return new NotFoundError(problem, options);
|
|
66
76
|
case 412: return new ConflictError(problem, options);
|
|
67
77
|
case 422: return new ValidationError(problem, options);
|
|
78
|
+
case 429: return new RateLimitedError(problem, options);
|
|
68
79
|
default: return new ApiError(problem, options);
|
|
69
80
|
}
|
|
70
81
|
}
|
|
@@ -73,6 +84,6 @@ function isApiError(error) {
|
|
|
73
84
|
return error instanceof ApiError;
|
|
74
85
|
}
|
|
75
86
|
//#endregion
|
|
76
|
-
export { ApiError, ConflictError, ForbiddenError, InvalidResponseError, NotFoundError, UnauthorizedError, ValidationError, createApiError, isApiError };
|
|
87
|
+
export { ApiError, ConflictError, ForbiddenError, InvalidResponseError, NotFoundError, RateLimitedError, UnauthorizedError, ValidationError, createApiError, isApiError };
|
|
77
88
|
|
|
78
89
|
//# sourceMappingURL=errors.js.map
|
package/dist/errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import type { Item, Problem, ValidationIssue } from '@panneau/protocol';\n\n/**\n * An error response of the API, parsed from `application/problem+json`. Subclasses cover the\n * statuses a client handles differently. Other statuses (`409`, `
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["import type { Item, Problem, ValidationIssue } from '@panneau/protocol';\n\n/**\n * An error response of the API, parsed from `application/problem+json`. Subclasses cover the\n * statuses a client handles differently. Other statuses (`409`, `5xx`…) are an `ApiError`.\n */\nexport class ApiError extends Error {\n override readonly name: string = 'ApiError';\n /** The HTTP status. */\n readonly status: number;\n /** The problem details sent by the backend, or built from the status when it sent none. */\n readonly problem: Problem;\n\n constructor(problem: Problem, options?: ErrorOptions) {\n super(problem.detail ?? problem.title, options);\n this.status = problem.status;\n this.problem = problem;\n }\n}\n\n/** `422`: the submitted values are invalid. */\nexport class ValidationError extends ApiError {\n override readonly name: string = 'ValidationError';\n /** Messages keyed by the dot path of the invalid value (`title.fr`, `blocks.2.image`). */\n readonly errors: Record<string, string[]>;\n\n constructor(problem: Problem, options?: ErrorOptions) {\n super(problem, options);\n this.errors = problem.errors ?? {};\n }\n}\n\n/** `412`: the item changed since the version sent in `If-Match`. */\nexport class ConflictError extends ApiError {\n override readonly name: string = 'ConflictError';\n /** The current version of the item, when the backend sent it. */\n readonly current: Item | null;\n\n constructor(problem: Problem, options?: ErrorOptions) {\n super(problem, options);\n this.current = problem.current ?? null;\n }\n}\n\n/** `401` (or `419` with the Laravel preset): the user is not authenticated. */\nexport class UnauthorizedError extends ApiError {\n override readonly name: string = 'UnauthorizedError';\n}\n\n/** `403`: the user is not allowed to do this. */\nexport class ForbiddenError extends ApiError {\n override readonly name: string = 'ForbiddenError';\n}\n\n/** `404`: the resource, item or route doesn't exist. */\nexport class NotFoundError extends ApiError {\n override readonly name: string = 'NotFoundError';\n}\n\n/** Options of `createApiError()`: what the response says besides its problem. */\nexport interface ApiErrorOptions extends ErrorOptions {\n /** Seconds to wait before the next request, from `Retry-After`. */\n retryAfter?: number | null;\n}\n\n/** `429`: too many requests. The next one can be sent after `retryAfter` seconds. */\nexport class RateLimitedError extends ApiError {\n override readonly name: string = 'RateLimitedError';\n /** Seconds to wait, from `Retry-After`, or `null` when the backend didn't say. */\n readonly retryAfter: number | null;\n\n constructor(problem: Problem, options?: ApiErrorOptions) {\n super(problem, options);\n this.retryAfter = options?.retryAfter ?? null;\n }\n}\n\n/** A successful response whose body doesn't match the protocol. */\nexport class InvalidResponseError extends Error {\n override readonly name: string = 'InvalidResponseError';\n /** What doesn't match, with dot paths into the body. */\n readonly issues: ValidationIssue[];\n\n constructor(message: string, issues: ValidationIssue[]) {\n super(message);\n this.issues = issues;\n }\n}\n\n/** Creates the error that matches the status of a problem. */\nexport function createApiError(problem: Problem, options?: ApiErrorOptions): ApiError {\n switch (problem.status) {\n case 401:\n return new UnauthorizedError(problem, options);\n case 403:\n return new ForbiddenError(problem, options);\n case 404:\n return new NotFoundError(problem, options);\n case 412:\n return new ConflictError(problem, options);\n case 422:\n return new ValidationError(problem, options);\n case 429:\n return new RateLimitedError(problem, options);\n default:\n return new ApiError(problem, options);\n }\n}\n\n/** Whether a value is an error of the API. */\nexport function isApiError(error: unknown): error is ApiError {\n return error instanceof ApiError;\n}\n"],"mappings":";;;;;AAMA,IAAa,WAAb,cAA8B,MAAM;CAChC,OAAiC;;CAEjC;;CAEA;CAEA,YAAY,SAAkB,SAAwB;EAClD,MAAM,QAAQ,UAAU,QAAQ,OAAO,OAAO;EAC9C,KAAK,SAAS,QAAQ;EACtB,KAAK,UAAU;CACnB;AACJ;;AAGA,IAAa,kBAAb,cAAqC,SAAS;CAC1C,OAAiC;;CAEjC;CAEA,YAAY,SAAkB,SAAwB;EAClD,MAAM,SAAS,OAAO;EACtB,KAAK,SAAS,QAAQ,UAAU,CAAC;CACrC;AACJ;;AAGA,IAAa,gBAAb,cAAmC,SAAS;CACxC,OAAiC;;CAEjC;CAEA,YAAY,SAAkB,SAAwB;EAClD,MAAM,SAAS,OAAO;EACtB,KAAK,UAAU,QAAQ,WAAW;CACtC;AACJ;;AAGA,IAAa,oBAAb,cAAuC,SAAS;CAC5C,OAAiC;AACrC;;AAGA,IAAa,iBAAb,cAAoC,SAAS;CACzC,OAAiC;AACrC;;AAGA,IAAa,gBAAb,cAAmC,SAAS;CACxC,OAAiC;AACrC;;AASA,IAAa,mBAAb,cAAsC,SAAS;CAC3C,OAAiC;;CAEjC;CAEA,YAAY,SAAkB,SAA2B;EACrD,MAAM,SAAS,OAAO;EACtB,KAAK,aAAa,SAAS,cAAc;CAC7C;AACJ;;AAGA,IAAa,uBAAb,cAA0C,MAAM;CAC5C,OAAiC;;CAEjC;CAEA,YAAY,SAAiB,QAA2B;EACpD,MAAM,OAAO;EACb,KAAK,SAAS;CAClB;AACJ;;AAGA,SAAgB,eAAe,SAAkB,SAAqC;CAClF,QAAQ,QAAQ,QAAhB;EACI,KAAK,KACD,OAAO,IAAI,kBAAkB,SAAS,OAAO;EACjD,KAAK,KACD,OAAO,IAAI,eAAe,SAAS,OAAO;EAC9C,KAAK,KACD,OAAO,IAAI,cAAc,SAAS,OAAO;EAC7C,KAAK,KACD,OAAO,IAAI,cAAc,SAAS,OAAO;EAC7C,KAAK,KACD,OAAO,IAAI,gBAAgB,SAAS,OAAO;EAC/C,KAAK,KACD,OAAO,IAAI,iBAAiB,SAAS,OAAO;EAChD,SACI,OAAO,IAAI,SAAS,SAAS,OAAO;CAC5C;AACJ;;AAGA,SAAgB,WAAW,OAAmC;CAC1D,OAAO,iBAAiB;AAC5B"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export type { ActionResponse, DataAdapter, LoginCredentials,
|
|
2
|
-
export { ApiError, ConflictError, ForbiddenError, InvalidResponseError, NotFoundError, UnauthorizedError, ValidationError, createApiError, isApiError, } from './errors';
|
|
3
|
-
export { readProblem } from './problem';
|
|
1
|
+
export type { ActionResponse, DataAdapter, LoginCredentials, RequestOptions, SearchOptions, UpdateOptions, Values, } from './adapter';
|
|
2
|
+
export { ApiError, type ApiErrorOptions, ConflictError, ForbiddenError, InvalidResponseError, NotFoundError, RateLimitedError, UnauthorizedError, ValidationError, createApiError, isApiError, } from './errors';
|
|
3
|
+
export { readProblem, readRetryAfter } from './problem';
|
|
4
4
|
export { type RestAdapterOptions, createRestAdapter } from './rest';
|
|
5
|
+
export type { PresignableRequest, PresignedResponse, PresignedUploads } from './uploads';
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACR,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACR,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,aAAa,EACb,MAAM,GACT,MAAM,WAAW,CAAC;AACnB,OAAO,EACH,QAAQ,EACR,KAAK,eAAe,EACpB,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,UAAU,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,KAAK,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AACpE,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ApiError, ConflictError, ForbiddenError, InvalidResponseError, NotFoundError, UnauthorizedError, ValidationError, createApiError, isApiError } from "./errors.js";
|
|
2
|
-
import { readProblem } from "./problem.js";
|
|
1
|
+
import { ApiError, ConflictError, ForbiddenError, InvalidResponseError, NotFoundError, RateLimitedError, UnauthorizedError, ValidationError, createApiError, isApiError } from "./errors.js";
|
|
2
|
+
import { readProblem, readRetryAfter } from "./problem.js";
|
|
3
3
|
import { createRestAdapter } from "./rest.js";
|
|
4
|
-
export { ApiError, ConflictError, ForbiddenError, InvalidResponseError, NotFoundError, UnauthorizedError, ValidationError, createApiError, createRestAdapter, isApiError, readProblem };
|
|
4
|
+
export { ApiError, ConflictError, ForbiddenError, InvalidResponseError, NotFoundError, RateLimitedError, UnauthorizedError, ValidationError, createApiError, createRestAdapter, isApiError, readProblem, readRetryAfter };
|
package/dist/problem.d.ts
CHANGED
|
@@ -13,4 +13,9 @@ import type { Problem } from '@panneau/protocol';
|
|
|
13
13
|
* ```
|
|
14
14
|
*/
|
|
15
15
|
export declare function readProblem(response: Response): Promise<Problem>;
|
|
16
|
+
/**
|
|
17
|
+
* The seconds to wait that a response gives in `Retry-After`, as seconds or as a date, or `null`.
|
|
18
|
+
* Pass it to `createApiError()` with the problem: a `429` becomes a `RateLimitedError`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function readRetryAfter(response: Response): number | null;
|
|
16
21
|
//# sourceMappingURL=problem.d.ts.map
|
package/dist/problem.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"problem.d.ts","sourceRoot":"","sources":["../src/problem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAGjD;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CA8BtE"}
|
|
1
|
+
{"version":3,"file":"problem.d.ts","sourceRoot":"","sources":["../src/problem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAGjD;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CA8BtE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAUhE"}
|
package/dist/problem.js
CHANGED
|
@@ -26,7 +26,18 @@ async function readProblem(response) {
|
|
|
26
26
|
if (errors !== null && typeof errors === "object" && !Array.isArray(errors)) problem.errors = Object.fromEntries(Object.entries(errors).map(([path, messages]) => [path, (Array.isArray(messages) ? messages : [messages]).map(String)]));
|
|
27
27
|
return problem;
|
|
28
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* The seconds to wait that a response gives in `Retry-After`, as seconds or as a date, or `null`.
|
|
31
|
+
* Pass it to `createApiError()` with the problem: a `429` becomes a `RateLimitedError`.
|
|
32
|
+
*/
|
|
33
|
+
function readRetryAfter(response) {
|
|
34
|
+
const value = response.headers.get("Retry-After")?.trim();
|
|
35
|
+
if (value === void 0 || value === "") return null;
|
|
36
|
+
if (/^\d+$/.test(value)) return Number(value);
|
|
37
|
+
const date = Date.parse(value);
|
|
38
|
+
return Number.isNaN(date) ? null : Math.max(0, Math.ceil((date - Date.now()) / 1e3));
|
|
39
|
+
}
|
|
29
40
|
//#endregion
|
|
30
|
-
export { readProblem };
|
|
41
|
+
export { readProblem, readRetryAfter };
|
|
31
42
|
|
|
32
43
|
//# sourceMappingURL=problem.js.map
|
package/dist/problem.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"problem.js","names":[],"sources":["../src/problem.ts"],"sourcesContent":["import type { Problem } from '@panneau/protocol';\nimport { isProblem } from '@panneau/protocol/runtime';\n\n/**\n * The problem of a failed response: its body when it is one (`application/problem+json`), or a\n * problem built from Laravel's `{ message, errors }` body, or from the status alone. Pass it to\n * `createApiError()` to get the typed error (`ValidationError`, `UnauthorizedError`…) that Panneau\n * forms and the app handle, for an app's own endpoints too.\n *\n * ```ts\n * const response = await fetch('/api/subscriptions', { method: 'POST', body });\n * if (!response.ok) {\n * throw createApiError(await readProblem(response));\n * }\n * ```\n */\nexport async function readProblem(response: Response): Promise<Problem> {\n const body: unknown = await response\n .clone()\n .json()\n .catch(() => null);\n if (isProblem(body)) {\n return body;\n }\n const record =\n body !== null && typeof body === 'object' ? (body as Record<string, unknown>) : {};\n const problem: Problem = {\n type: 'about:blank',\n title:\n typeof record.title === 'string'\n ? record.title\n : typeof record.message === 'string'\n ? record.message\n : response.statusText || `HTTP ${response.status}`,\n status: response.status,\n };\n const errors = record.errors;\n if (errors !== null && typeof errors === 'object' && !Array.isArray(errors)) {\n problem.errors = Object.fromEntries(\n Object.entries(errors).map(([path, messages]) => [\n path,\n (Array.isArray(messages) ? messages : [messages]).map(String),\n ]),\n );\n }\n return problem;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,eAAsB,YAAY,UAAsC;CACpE,MAAM,OAAgB,MAAM,SACvB,MAAM,CAAC,CACP,KAAK,CAAC,CACN,YAAY,IAAI;CACrB,IAAI,UAAU,IAAI,GACd,OAAO;CAEX,MAAM,SACF,SAAS,QAAQ,OAAO,SAAS,WAAY,OAAmC,CAAC;CACrF,MAAM,UAAmB;EACrB,MAAM;EACN,OACI,OAAO,OAAO,UAAU,WAClB,OAAO,QACP,OAAO,OAAO,YAAY,WACxB,OAAO,UACP,SAAS,cAAc,QAAQ,SAAS;EACpD,QAAQ,SAAS;CACrB;CACA,MAAM,SAAS,OAAO;CACtB,IAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GACtE,QAAQ,SAAS,OAAO,YACpB,OAAO,QAAQ,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,cAAc,CAC7C,OACC,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ,EAAA,CAAG,IAAI,MAAM,CAAC,CAChE,CACL;CAEJ,OAAO;AACX"}
|
|
1
|
+
{"version":3,"file":"problem.js","names":[],"sources":["../src/problem.ts"],"sourcesContent":["import type { Problem } from '@panneau/protocol';\nimport { isProblem } from '@panneau/protocol/runtime';\n\n/**\n * The problem of a failed response: its body when it is one (`application/problem+json`), or a\n * problem built from Laravel's `{ message, errors }` body, or from the status alone. Pass it to\n * `createApiError()` to get the typed error (`ValidationError`, `UnauthorizedError`…) that Panneau\n * forms and the app handle, for an app's own endpoints too.\n *\n * ```ts\n * const response = await fetch('/api/subscriptions', { method: 'POST', body });\n * if (!response.ok) {\n * throw createApiError(await readProblem(response));\n * }\n * ```\n */\nexport async function readProblem(response: Response): Promise<Problem> {\n const body: unknown = await response\n .clone()\n .json()\n .catch(() => null);\n if (isProblem(body)) {\n return body;\n }\n const record =\n body !== null && typeof body === 'object' ? (body as Record<string, unknown>) : {};\n const problem: Problem = {\n type: 'about:blank',\n title:\n typeof record.title === 'string'\n ? record.title\n : typeof record.message === 'string'\n ? record.message\n : response.statusText || `HTTP ${response.status}`,\n status: response.status,\n };\n const errors = record.errors;\n if (errors !== null && typeof errors === 'object' && !Array.isArray(errors)) {\n problem.errors = Object.fromEntries(\n Object.entries(errors).map(([path, messages]) => [\n path,\n (Array.isArray(messages) ? messages : [messages]).map(String),\n ]),\n );\n }\n return problem;\n}\n\n/**\n * The seconds to wait that a response gives in `Retry-After`, as seconds or as a date, or `null`.\n * Pass it to `createApiError()` with the problem: a `429` becomes a `RateLimitedError`.\n */\nexport function readRetryAfter(response: Response): number | null {\n const value = response.headers.get('Retry-After')?.trim();\n if (value === undefined || value === '') {\n return null;\n }\n if (/^\\d+$/.test(value)) {\n return Number(value);\n }\n const date = Date.parse(value);\n return Number.isNaN(date) ? null : Math.max(0, Math.ceil((date - Date.now()) / 1000));\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,eAAsB,YAAY,UAAsC;CACpE,MAAM,OAAgB,MAAM,SACvB,MAAM,CAAC,CACP,KAAK,CAAC,CACN,YAAY,IAAI;CACrB,IAAI,UAAU,IAAI,GACd,OAAO;CAEX,MAAM,SACF,SAAS,QAAQ,OAAO,SAAS,WAAY,OAAmC,CAAC;CACrF,MAAM,UAAmB;EACrB,MAAM;EACN,OACI,OAAO,OAAO,UAAU,WAClB,OAAO,QACP,OAAO,OAAO,YAAY,WACxB,OAAO,UACP,SAAS,cAAc,QAAQ,SAAS;EACpD,QAAQ,SAAS;CACrB;CACA,MAAM,SAAS,OAAO;CACtB,IAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GACtE,QAAQ,SAAS,OAAO,YACpB,OAAO,QAAQ,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,cAAc,CAC7C,OACC,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ,EAAA,CAAG,IAAI,MAAM,CAAC,CAChE,CACL;CAEJ,OAAO;AACX;;;;;AAMA,SAAgB,eAAe,UAAmC;CAC9D,MAAM,QAAQ,SAAS,QAAQ,IAAI,aAAa,CAAC,EAAE,KAAK;CACxD,IAAI,UAAU,KAAA,KAAa,UAAU,IACjC,OAAO;CAEX,IAAI,QAAQ,KAAK,KAAK,GAClB,OAAO,OAAO,KAAK;CAEvB,MAAM,OAAO,KAAK,MAAM,KAAK;CAC7B,OAAO,OAAO,MAAM,IAAI,IAAI,OAAO,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,KAAK,IAAI,KAAK,GAAI,CAAC;AACxF"}
|
package/dist/query/context.d.ts
CHANGED
|
@@ -12,4 +12,9 @@ export interface DataAdapterProviderProps {
|
|
|
12
12
|
export declare function DataAdapterProvider({ adapter, children }: DataAdapterProviderProps): import("react").JSX.Element;
|
|
13
13
|
/** The adapter of the closest `DataAdapterProvider`. */
|
|
14
14
|
export declare function useDataAdapter(): DataAdapter;
|
|
15
|
+
/**
|
|
16
|
+
* The adapter of the closest `DataAdapterProvider`, or `null` without one: for components that
|
|
17
|
+
* also work without a backend, such as fields that can upload.
|
|
18
|
+
*/
|
|
19
|
+
export declare function useOptionalDataAdapter(): DataAdapter | null;
|
|
15
20
|
//# sourceMappingURL=context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/query/context.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAsB,MAAM,OAAO,CAAC;AAE3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C,MAAM,WAAW,wBAAwB;IACrC,2FAA2F;IAC3F,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,wBAAwB,+BAElF;AAED,wDAAwD;AACxD,wBAAgB,cAAc,IAAI,WAAW,CAM5C"}
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/query/context.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAsB,MAAM,OAAO,CAAC;AAE3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAI9C,MAAM,WAAW,wBAAwB;IACrC,2FAA2F;IAC3F,OAAO,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,wBAAwB,+BAElF;AAED,wDAAwD;AACxD,wBAAgB,cAAc,IAAI,WAAW,CAM5C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,WAAW,GAAG,IAAI,CAE3D"}
|
package/dist/query/context.js
CHANGED
|
@@ -28,7 +28,14 @@ function useDataAdapter() {
|
|
|
28
28
|
if (adapter === null) throw new Error("useDataAdapter() needs a <DataAdapterProvider> above it.");
|
|
29
29
|
return adapter;
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* The adapter of the closest `DataAdapterProvider`, or `null` without one: for components that
|
|
33
|
+
* also work without a backend, such as fields that can upload.
|
|
34
|
+
*/
|
|
35
|
+
function useOptionalDataAdapter() {
|
|
36
|
+
return use(DataAdapterContext);
|
|
37
|
+
}
|
|
31
38
|
//#endregion
|
|
32
|
-
export { DataAdapterProvider, useDataAdapter };
|
|
39
|
+
export { DataAdapterProvider, useDataAdapter, useOptionalDataAdapter };
|
|
33
40
|
|
|
34
41
|
//# sourceMappingURL=context.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","names":["adapter","children","_c"],"sources":["../../src/query/context.tsx"],"sourcesContent":["import { type ReactNode, createContext, use } from 'react';\n\nimport type { DataAdapter } from '../adapter';\n\nconst DataAdapterContext = createContext<DataAdapter | null>(null);\n\nexport interface DataAdapterProviderProps {\n /** The adapter of the hooks below: `createRestAdapter()`, a preset or a custom adapter. */\n adapter: DataAdapter;\n children?: ReactNode;\n}\n\n/**\n * Provides the adapter used by the query hooks. The hooks also need TanStack Query's\n * `QueryClientProvider` above them.\n */\nexport function DataAdapterProvider({ adapter, children }: DataAdapterProviderProps) {\n return <DataAdapterContext value={adapter}>{children}</DataAdapterContext>;\n}\n\n/** The adapter of the closest `DataAdapterProvider`. */\nexport function useDataAdapter(): DataAdapter {\n const adapter = use(DataAdapterContext);\n if (adapter === null) {\n throw new Error('useDataAdapter() needs a <DataAdapterProvider> above it.');\n }\n return adapter;\n}\n"],"mappings":";;;;AAIA,IAAM,qBAAqB,cAAkC,IAAI;;;;;AAYjE,SAAO,oBAAA,IAAA;CAAA,MAAA,IAAAE,EAAA,CAAA;CAA6B,MAAA,EAAA,SAAA,aAAA;CAA+C,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,EAAA,OAAA,UAAA;EACxE,KAAA,oBAAC,oBAAD;GAA2BF,OAAA;GAAU;EAAlB,CAAA;EAAgD,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OAAnE;AAAmE;;AAI9E,SAAgB,iBAA8B;CAC1C,MAAM,UAAU,IAAI,kBAAkB;CACtC,IAAI,YAAY,MACZ,MAAM,IAAI,MAAM,0DAA0D;CAE9E,OAAO;AACX"}
|
|
1
|
+
{"version":3,"file":"context.js","names":["adapter","children","_c"],"sources":["../../src/query/context.tsx"],"sourcesContent":["import { type ReactNode, createContext, use } from 'react';\n\nimport type { DataAdapter } from '../adapter';\n\nconst DataAdapterContext = createContext<DataAdapter | null>(null);\n\nexport interface DataAdapterProviderProps {\n /** The adapter of the hooks below: `createRestAdapter()`, a preset or a custom adapter. */\n adapter: DataAdapter;\n children?: ReactNode;\n}\n\n/**\n * Provides the adapter used by the query hooks. The hooks also need TanStack Query's\n * `QueryClientProvider` above them.\n */\nexport function DataAdapterProvider({ adapter, children }: DataAdapterProviderProps) {\n return <DataAdapterContext value={adapter}>{children}</DataAdapterContext>;\n}\n\n/** The adapter of the closest `DataAdapterProvider`. */\nexport function useDataAdapter(): DataAdapter {\n const adapter = use(DataAdapterContext);\n if (adapter === null) {\n throw new Error('useDataAdapter() needs a <DataAdapterProvider> above it.');\n }\n return adapter;\n}\n\n/**\n * The adapter of the closest `DataAdapterProvider`, or `null` without one: for components that\n * also work without a backend, such as fields that can upload.\n */\nexport function useOptionalDataAdapter(): DataAdapter | null {\n return use(DataAdapterContext);\n}\n"],"mappings":";;;;AAIA,IAAM,qBAAqB,cAAkC,IAAI;;;;;AAYjE,SAAO,oBAAA,IAAA;CAAA,MAAA,IAAAE,EAAA,CAAA;CAA6B,MAAA,EAAA,SAAA,aAAA;CAA+C,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,EAAA,OAAA,UAAA;EACxE,KAAA,oBAAC,oBAAD;GAA2BF,OAAA;GAAU;EAAlB,CAAA;EAAgD,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OAAnE;AAAmE;;AAI9E,SAAgB,iBAA8B;CAC1C,MAAM,UAAU,IAAI,kBAAkB;CACtC,IAAI,YAAY,MACZ,MAAM,IAAI,MAAM,0DAA0D;CAE9E,OAAO;AACX;;;;;AAMA,SAAgB,yBAA6C;CACzD,OAAO,IAAI,kBAAkB;AACjC"}
|
package/dist/query/hooks.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type UseMutationResult, type UseQueryOptions, type UseQueryResult } from '@tanstack/react-query';
|
|
2
|
-
import type { ActionRequest, ActionRun, Definition, Item, ListQuery, ListResponse } from '@panneau/protocol';
|
|
2
|
+
import type { ActionRequest, ActionRun, Definition, Item, ListQuery, ListResponse, PreviewResponse, SearchResponse } from '@panneau/protocol';
|
|
3
3
|
import type { ActionResponse, Values } from '../adapter';
|
|
4
|
-
import { queryKeys } from './keys';
|
|
4
|
+
import { type SearchKeyOptions, queryKeys } from './keys';
|
|
5
5
|
/** Options of a query hook: TanStack Query's options, without the key and the function. */
|
|
6
6
|
export type QueryHookOptions<TData, TKey extends readonly unknown[]> = Omit<UseQueryOptions<TData, Error, TData, TKey>, 'queryKey' | 'queryFn'>;
|
|
7
7
|
/**
|
|
@@ -12,6 +12,18 @@ export declare function useDefinition(options?: QueryHookOptions<Definition, Ret
|
|
|
12
12
|
export declare function useResourceList(resource: string, query?: ListQuery, options?: QueryHookOptions<ListResponse, ReturnType<typeof queryKeys.list>>): UseQueryResult<ListResponse>;
|
|
13
13
|
/** One item of a resource. Nothing is fetched while `id` is `null` or `undefined`. */
|
|
14
14
|
export declare function useResourceItem(resource: string, id: string | null | undefined, options?: QueryHookOptions<Item, ReturnType<typeof queryKeys.item>>): UseQueryResult<Item>;
|
|
15
|
+
/**
|
|
16
|
+
* The iframe preview of an item: a short-lived URL of its page on the site, rendered with
|
|
17
|
+
* `values` (`{}` for the saved item). Pass `enabled: false` until the preview is shown.
|
|
18
|
+
*/
|
|
19
|
+
export declare function useItemPreview(resource: string, id: string, values?: Values, options?: QueryHookOptions<PreviewResponse, ReturnType<typeof queryKeys.itemPreview>>): UseQueryResult<PreviewResponse>;
|
|
20
|
+
/**
|
|
21
|
+
* The global search: items of every searchable resource, or of those of `resources`, grouped by
|
|
22
|
+
* resource (decision 0029). Pass `enabled: false` until there is a query worth sending (2
|
|
23
|
+
* characters), and debounce `q`. A `429` fails with a
|
|
24
|
+
* `RateLimitedError`: don't retry it before its `retryAfter`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function useSearch(q: string, { limit, resources, ...options }?: QueryHookOptions<SearchResponse, ReturnType<typeof queryKeys.search>> & SearchKeyOptions): UseQueryResult<SearchResponse>;
|
|
15
27
|
/** Creates an item, then refetches the lists of the resource. */
|
|
16
28
|
export declare function useCreateItem(resource: string): UseMutationResult<Item, Error, Values>;
|
|
17
29
|
export interface UpdateItemVariables {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/query/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,cAAc,EAKtB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EACR,aAAa,EACb,SAAS,EACT,UAAU,EACV,IAAI,EACJ,SAAS,EACT,YAAY,EACf,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/query/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACpB,KAAK,cAAc,EAKtB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EACR,aAAa,EACb,SAAS,EACT,UAAU,EACV,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,eAAe,EACf,cAAc,EACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAU1D,2FAA2F;AAC3F,MAAM,MAAM,gBAAgB,CAAC,KAAK,EAAE,IAAI,SAAS,SAAS,OAAO,EAAE,IAAI,IAAI,CACvE,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,EAC1C,UAAU,GAAG,SAAS,CACzB,CAAC;AAEF;;GAEG;AACH,wBAAgB,aAAa,CACzB,OAAO,GAAE,gBAAgB,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,UAAU,CAAC,CAAM,GACpF,cAAc,CAAC,UAAU,CAAC,CAG5B;AAED,qCAAqC;AACrC,wBAAgB,eAAe,CAC3B,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,SAAc,EACrB,OAAO,GAAE,gBAAgB,CAAC,YAAY,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAM,GAChF,cAAc,CAAC,YAAY,CAAC,CAG9B;AAED,sFAAsF;AACtF,wBAAgB,eAAe,CAC3B,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC7B,OAAO,GAAE,gBAAgB,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAM,GACxE,cAAc,CAAC,IAAI,CAAC,CAItB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC1B,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,EACV,MAAM,GAAE,MAAW,EACnB,OAAO,GAAE,gBAAgB,CAAC,eAAe,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,WAAW,CAAC,CAAM,GAC1F,cAAc,CAAC,eAAe,CAAC,CAGjC;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CACrB,CAAC,EAAE,MAAM,EACT,EACI,KAAK,EACL,SAAS,EACT,GAAG,OAAO,EACb,GAAE,gBAAgB,CAAC,cAAc,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,GACpE,gBAAqB,GAC1B,cAAc,CAAC,cAAc,CAAC,CAGhC;AAED,iEAAiE;AACjE,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAUtF;AAED,MAAM,WAAW,mBAAmB;IAChC,sBAAsB;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;GAGG;AACH,wBAAgB,aAAa,CACzB,QAAQ,EAAE,MAAM,GACjB,iBAAiB,CAAC,IAAI,EAAE,KAAK,EAAE,mBAAmB,CAAC,CAWrD;AAED,+FAA+F;AAC/F,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAAC,IAAI,EAAE,KAAK,EAAE,MAAM,CAAC,CAUtF;AAED,MAAM,WAAW,kBAAmB,SAAQ,aAAa;IACrD,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CACxB,QAAQ,EAAE,MAAM,GACjB,iBAAiB,CAAC,cAAc,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAc9D;AASD,uDAAuD;AACvD,wBAAgB,aAAa,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAErD;AAED,MAAM,WAAW,mBAAmB;IAChC,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CACxB,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC7B,EAAE,QAAe,EAAE,GAAE,mBAAwB,GAC9C,cAAc,CAAC,SAAS,CAAC,CAsB3B"}
|
package/dist/query/hooks.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useDataAdapter } from "./context.js";
|
|
2
2
|
import { queryKeys } from "./keys.js";
|
|
3
|
-
import { definitionQueryOptions, resourceItemQueryOptions, resourceListQueryOptions } from "./options.js";
|
|
3
|
+
import { definitionQueryOptions, itemPreviewQueryOptions, resourceItemQueryOptions, resourceListQueryOptions, searchQueryOptions } from "./options.js";
|
|
4
4
|
import { invalidateActionResult } from "./refresh.js";
|
|
5
5
|
import { c } from "react/compiler-runtime";
|
|
6
6
|
import { skipToken, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
@@ -110,6 +110,74 @@ function useResourceItem(resource, id, t0) {
|
|
|
110
110
|
} else t5 = $[9];
|
|
111
111
|
return useQuery(t5);
|
|
112
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* The iframe preview of an item: a short-lived URL of its page on the site, rendered with
|
|
115
|
+
* `values` (`{}` for the saved item). Pass `enabled: false` until the preview is shown.
|
|
116
|
+
*/
|
|
117
|
+
function useItemPreview(resource, id, t0, t1) {
|
|
118
|
+
const $ = c(12);
|
|
119
|
+
let t2;
|
|
120
|
+
if ($[0] !== t0) {
|
|
121
|
+
t2 = t0 === void 0 ? {} : t0;
|
|
122
|
+
$[0] = t0;
|
|
123
|
+
$[1] = t2;
|
|
124
|
+
} else t2 = $[1];
|
|
125
|
+
const values = t2;
|
|
126
|
+
let t3;
|
|
127
|
+
if ($[2] !== t1) {
|
|
128
|
+
t3 = t1 === void 0 ? {} : t1;
|
|
129
|
+
$[2] = t1;
|
|
130
|
+
$[3] = t3;
|
|
131
|
+
} else t3 = $[3];
|
|
132
|
+
const options = t3;
|
|
133
|
+
const adapter = useDataAdapter();
|
|
134
|
+
let t4;
|
|
135
|
+
if ($[4] !== adapter || $[5] !== id || $[6] !== resource || $[7] !== values) {
|
|
136
|
+
t4 = itemPreviewQueryOptions(adapter, resource, id, values);
|
|
137
|
+
$[4] = adapter;
|
|
138
|
+
$[5] = id;
|
|
139
|
+
$[6] = resource;
|
|
140
|
+
$[7] = values;
|
|
141
|
+
$[8] = t4;
|
|
142
|
+
} else t4 = $[8];
|
|
143
|
+
let t5;
|
|
144
|
+
if ($[9] !== options || $[10] !== t4) {
|
|
145
|
+
t5 = {
|
|
146
|
+
...t4,
|
|
147
|
+
...options
|
|
148
|
+
};
|
|
149
|
+
$[9] = options;
|
|
150
|
+
$[10] = t4;
|
|
151
|
+
$[11] = t5;
|
|
152
|
+
} else t5 = $[11];
|
|
153
|
+
return useQuery(t5);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* The global search: items of every searchable resource, or of those of `resources`, grouped by
|
|
157
|
+
* resource (decision 0029). Pass `enabled: false` until there is a query worth sending (2
|
|
158
|
+
* characters), and debounce `q`. A `429` fails with a
|
|
159
|
+
* `RateLimitedError`: don't retry it before its `retryAfter`.
|
|
160
|
+
*/
|
|
161
|
+
function useSearch(q, t0) {
|
|
162
|
+
const $ = c(3);
|
|
163
|
+
const { limit, resources, ...options } = t0 === void 0 ? {} : t0;
|
|
164
|
+
const adapter = useDataAdapter();
|
|
165
|
+
const t1 = searchQueryOptions(adapter, q, {
|
|
166
|
+
limit,
|
|
167
|
+
resources
|
|
168
|
+
});
|
|
169
|
+
let t2;
|
|
170
|
+
if ($[0] !== options || $[1] !== t1) {
|
|
171
|
+
t2 = {
|
|
172
|
+
...t1,
|
|
173
|
+
...options
|
|
174
|
+
};
|
|
175
|
+
$[0] = options;
|
|
176
|
+
$[1] = t1;
|
|
177
|
+
$[2] = t2;
|
|
178
|
+
} else t2 = $[2];
|
|
179
|
+
return useQuery(t2);
|
|
180
|
+
}
|
|
113
181
|
/** Creates an item, then refetches the lists of the resource. */
|
|
114
182
|
function useCreateItem(resource) {
|
|
115
183
|
const $ = c(9);
|
|
@@ -294,6 +362,6 @@ function useActionRun(id, { interval = 2e3 } = {}) {
|
|
|
294
362
|
});
|
|
295
363
|
}
|
|
296
364
|
//#endregion
|
|
297
|
-
export { isRunFinished, useActionRun, useCreateItem, useDefinition, useDeleteItem, useResourceItem, useResourceList, useRunAction, useUpdateItem };
|
|
365
|
+
export { isRunFinished, useActionRun, useCreateItem, useDefinition, useDeleteItem, useItemPreview, useResourceItem, useResourceList, useRunAction, useSearch, useUpdateItem };
|
|
298
366
|
|
|
299
367
|
//# sourceMappingURL=hooks.js.map
|
package/dist/query/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","names":["id","_c"],"sources":["../../src/query/hooks.ts"],"sourcesContent":["import {\n type UseMutationResult,\n type UseQueryOptions,\n type UseQueryResult,\n skipToken,\n useMutation,\n useQuery,\n useQueryClient,\n} from '@tanstack/react-query';\n\nimport type {\n ActionRequest,\n ActionRun,\n Definition,\n Item,\n ListQuery,\n ListResponse,\n} from '@panneau/protocol';\n\nimport type { ActionResponse, Values } from '../adapter';\nimport { useDataAdapter } from './context';\nimport { queryKeys } from './keys';\nimport {\n definitionQueryOptions,\n resourceItemQueryOptions,\n resourceListQueryOptions,\n} from './options';\nimport { invalidateActionResult } from './refresh';\n\n/** Options of a query hook: TanStack Query's options, without the key and the function. */\nexport type QueryHookOptions<TData, TKey extends readonly unknown[]> = Omit<\n UseQueryOptions<TData, Error, TData, TKey>,\n 'queryKey' | 'queryFn'\n>;\n\n/**\n * The definition for the current user. Pass `initialData` when the server inlines it in the page.\n */\nexport function useDefinition(\n options: QueryHookOptions<Definition, ReturnType<typeof queryKeys.definition>> = {},\n): UseQueryResult<Definition> {\n const adapter = useDataAdapter();\n return useQuery({ ...definitionQueryOptions(adapter), ...options });\n}\n\n/** A page of items of a resource. */\nexport function useResourceList(\n resource: string,\n query: ListQuery = {},\n options: QueryHookOptions<ListResponse, ReturnType<typeof queryKeys.list>> = {},\n): UseQueryResult<ListResponse> {\n const adapter = useDataAdapter();\n return useQuery({ ...resourceListQueryOptions(adapter, resource, query), ...options });\n}\n\n/** One item of a resource. Nothing is fetched while `id` is `null` or `undefined`. */\nexport function useResourceItem(\n resource: string,\n id: string | null | undefined,\n options: QueryHookOptions<Item, ReturnType<typeof queryKeys.item>> = {},\n): UseQueryResult<Item> {\n const adapter = useDataAdapter();\n const query = resourceItemQueryOptions(adapter, resource, id ?? '');\n return useQuery({ ...query, queryFn: id == null ? skipToken : query.queryFn, ...options });\n}\n\n/** Creates an item, then refetches the lists of the resource. */\nexport function useCreateItem(resource: string): UseMutationResult<Item, Error, Values> {\n const adapter = useDataAdapter();\n const client = useQueryClient();\n return useMutation({\n mutationFn: (values: Values) => adapter.create(resource, values),\n onSuccess: (item) => {\n client.setQueryData(queryKeys.item(resource, item.id), item);\n return client.invalidateQueries({ queryKey: queryKeys.lists(resource) });\n },\n });\n}\n\nexport interface UpdateItemVariables {\n /** Id of the item. */\n id: string;\n /** The values to change, keyed by field name. */\n values: Values;\n /** The version the values were edited from, sent as `If-Match`. */\n version?: string | null;\n}\n\n/**\n * Updates an item, then caches the updated item and refetches the lists of the resource. Fails\n * with a `ConflictError` when the item changed since `version`.\n */\nexport function useUpdateItem(\n resource: string,\n): UseMutationResult<Item, Error, UpdateItemVariables> {\n const adapter = useDataAdapter();\n const client = useQueryClient();\n return useMutation({\n mutationFn: ({ id, values, version }: UpdateItemVariables) =>\n adapter.update(resource, id, values, { version }),\n onSuccess: (item) => {\n client.setQueryData(queryKeys.item(resource, item.id), item);\n return client.invalidateQueries({ queryKey: queryKeys.lists(resource) });\n },\n });\n}\n\n/** Deletes an item, then removes it from the cache and refetches the lists of the resource. */\nexport function useDeleteItem(resource: string): UseMutationResult<void, Error, string> {\n const adapter = useDataAdapter();\n const client = useQueryClient();\n return useMutation({\n mutationFn: (id: string) => adapter.delete(resource, id),\n onSuccess: (_result, id) => {\n client.removeQueries({ queryKey: queryKeys.item(resource, id) });\n return client.invalidateQueries({ queryKey: queryKeys.lists(resource) });\n },\n });\n}\n\nexport interface RunActionVariables extends ActionRequest {\n /** Id of the action. */\n action: string;\n}\n\n/**\n * Runs an action of a resource. When it finishes right away, the cache is refreshed as its\n * result asks (`refresh`). When the backend queues it, follow the run with `useActionRun()`.\n */\nexport function useRunAction(\n resource: string,\n): UseMutationResult<ActionResponse, Error, RunActionVariables> {\n const adapter = useDataAdapter();\n const client = useQueryClient();\n return useMutation({\n mutationFn: ({ action, ...request }: RunActionVariables) =>\n adapter.runAction(resource, action, request),\n onSuccess: (response, { selection }) => {\n if (response.run !== undefined) {\n client.setQueryData(queryKeys.actionRun(response.run.id), response.run);\n return client.invalidateQueries({ queryKey: queryKeys.actionRuns(), exact: true });\n }\n return invalidateActionResult(client, resource, response.result, selection);\n },\n });\n}\n\nconst FINISHED_RUN_STATUSES: ReadonlySet<ActionRun['status']> = new Set([\n 'succeeded',\n 'partial',\n 'failed',\n 'cancelled',\n]);\n\n/** Whether a run has finished, successfully or not. */\nexport function isRunFinished(run: ActionRun): boolean {\n return FINISHED_RUN_STATUSES.has(run.status);\n}\n\nexport interface UseActionRunOptions {\n /** Milliseconds between two checks while the run is not finished. Defaults to 2 seconds. */\n interval?: number;\n}\n\n/**\n * Follows a queued action run until it finishes. When it does, the cache is refreshed as its\n * result asks. Nothing is fetched while `id` is `null` or `undefined`.\n */\nexport function useActionRun(\n id: string | null | undefined,\n { interval = 2000 }: UseActionRunOptions = {},\n): UseQueryResult<ActionRun> {\n const adapter = useDataAdapter();\n const client = useQueryClient();\n return useQuery({\n queryKey: queryKeys.actionRun(id ?? ''),\n queryFn:\n id == null\n ? skipToken\n : async ({ signal }) => {\n const previous = client.getQueryData<ActionRun>(queryKeys.actionRun(id));\n const run = await adapter.getActionRun(id, { signal });\n const justFinished =\n isRunFinished(run) &&\n (previous === undefined || !isRunFinished(previous));\n if (justFinished && run.result !== undefined) {\n await invalidateActionResult(client, run.resource, run.result);\n }\n return run;\n },\n refetchInterval: (query) =>\n query.state.data !== undefined && isRunFinished(query.state.data) ? false : interval,\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hooks.js","names":["id","_c"],"sources":["../../src/query/hooks.ts"],"sourcesContent":["import {\n type UseMutationResult,\n type UseQueryOptions,\n type UseQueryResult,\n skipToken,\n useMutation,\n useQuery,\n useQueryClient,\n} from '@tanstack/react-query';\n\nimport type {\n ActionRequest,\n ActionRun,\n Definition,\n Item,\n ListQuery,\n ListResponse,\n PreviewResponse,\n SearchResponse,\n} from '@panneau/protocol';\n\nimport type { ActionResponse, Values } from '../adapter';\nimport { useDataAdapter } from './context';\nimport { type SearchKeyOptions, queryKeys } from './keys';\nimport {\n definitionQueryOptions,\n itemPreviewQueryOptions,\n resourceItemQueryOptions,\n resourceListQueryOptions,\n searchQueryOptions,\n} from './options';\nimport { invalidateActionResult } from './refresh';\n\n/** Options of a query hook: TanStack Query's options, without the key and the function. */\nexport type QueryHookOptions<TData, TKey extends readonly unknown[]> = Omit<\n UseQueryOptions<TData, Error, TData, TKey>,\n 'queryKey' | 'queryFn'\n>;\n\n/**\n * The definition for the current user. Pass `initialData` when the server inlines it in the page.\n */\nexport function useDefinition(\n options: QueryHookOptions<Definition, ReturnType<typeof queryKeys.definition>> = {},\n): UseQueryResult<Definition> {\n const adapter = useDataAdapter();\n return useQuery({ ...definitionQueryOptions(adapter), ...options });\n}\n\n/** A page of items of a resource. */\nexport function useResourceList(\n resource: string,\n query: ListQuery = {},\n options: QueryHookOptions<ListResponse, ReturnType<typeof queryKeys.list>> = {},\n): UseQueryResult<ListResponse> {\n const adapter = useDataAdapter();\n return useQuery({ ...resourceListQueryOptions(adapter, resource, query), ...options });\n}\n\n/** One item of a resource. Nothing is fetched while `id` is `null` or `undefined`. */\nexport function useResourceItem(\n resource: string,\n id: string | null | undefined,\n options: QueryHookOptions<Item, ReturnType<typeof queryKeys.item>> = {},\n): UseQueryResult<Item> {\n const adapter = useDataAdapter();\n const query = resourceItemQueryOptions(adapter, resource, id ?? '');\n return useQuery({ ...query, queryFn: id == null ? skipToken : query.queryFn, ...options });\n}\n\n/**\n * The iframe preview of an item: a short-lived URL of its page on the site, rendered with\n * `values` (`{}` for the saved item). Pass `enabled: false` until the preview is shown.\n */\nexport function useItemPreview(\n resource: string,\n id: string,\n values: Values = {},\n options: QueryHookOptions<PreviewResponse, ReturnType<typeof queryKeys.itemPreview>> = {},\n): UseQueryResult<PreviewResponse> {\n const adapter = useDataAdapter();\n return useQuery({ ...itemPreviewQueryOptions(adapter, resource, id, values), ...options });\n}\n\n/**\n * The global search: items of every searchable resource, or of those of `resources`, grouped by\n * resource (decision 0029). Pass `enabled: false` until there is a query worth sending (2\n * characters), and debounce `q`. A `429` fails with a\n * `RateLimitedError`: don't retry it before its `retryAfter`.\n */\nexport function useSearch(\n q: string,\n {\n limit,\n resources,\n ...options\n }: QueryHookOptions<SearchResponse, ReturnType<typeof queryKeys.search>> &\n SearchKeyOptions = {},\n): UseQueryResult<SearchResponse> {\n const adapter = useDataAdapter();\n return useQuery({ ...searchQueryOptions(adapter, q, { limit, resources }), ...options });\n}\n\n/** Creates an item, then refetches the lists of the resource. */\nexport function useCreateItem(resource: string): UseMutationResult<Item, Error, Values> {\n const adapter = useDataAdapter();\n const client = useQueryClient();\n return useMutation({\n mutationFn: (values: Values) => adapter.create(resource, values),\n onSuccess: (item) => {\n client.setQueryData(queryKeys.item(resource, item.id), item);\n return client.invalidateQueries({ queryKey: queryKeys.lists(resource) });\n },\n });\n}\n\nexport interface UpdateItemVariables {\n /** Id of the item. */\n id: string;\n /** The values to change, keyed by field name. */\n values: Values;\n /** The version the values were edited from, sent as `If-Match`. */\n version?: string | null;\n}\n\n/**\n * Updates an item, then caches the updated item and refetches the lists of the resource. Fails\n * with a `ConflictError` when the item changed since `version`.\n */\nexport function useUpdateItem(\n resource: string,\n): UseMutationResult<Item, Error, UpdateItemVariables> {\n const adapter = useDataAdapter();\n const client = useQueryClient();\n return useMutation({\n mutationFn: ({ id, values, version }: UpdateItemVariables) =>\n adapter.update(resource, id, values, { version }),\n onSuccess: (item) => {\n client.setQueryData(queryKeys.item(resource, item.id), item);\n return client.invalidateQueries({ queryKey: queryKeys.lists(resource) });\n },\n });\n}\n\n/** Deletes an item, then removes it from the cache and refetches the lists of the resource. */\nexport function useDeleteItem(resource: string): UseMutationResult<void, Error, string> {\n const adapter = useDataAdapter();\n const client = useQueryClient();\n return useMutation({\n mutationFn: (id: string) => adapter.delete(resource, id),\n onSuccess: (_result, id) => {\n client.removeQueries({ queryKey: queryKeys.item(resource, id) });\n return client.invalidateQueries({ queryKey: queryKeys.lists(resource) });\n },\n });\n}\n\nexport interface RunActionVariables extends ActionRequest {\n /** Id of the action. */\n action: string;\n}\n\n/**\n * Runs an action of a resource. When it finishes right away, the cache is refreshed as its\n * result asks (`refresh`). When the backend queues it, follow the run with `useActionRun()`.\n */\nexport function useRunAction(\n resource: string,\n): UseMutationResult<ActionResponse, Error, RunActionVariables> {\n const adapter = useDataAdapter();\n const client = useQueryClient();\n return useMutation({\n mutationFn: ({ action, ...request }: RunActionVariables) =>\n adapter.runAction(resource, action, request),\n onSuccess: (response, { selection }) => {\n if (response.run !== undefined) {\n client.setQueryData(queryKeys.actionRun(response.run.id), response.run);\n return client.invalidateQueries({ queryKey: queryKeys.actionRuns(), exact: true });\n }\n return invalidateActionResult(client, resource, response.result, selection);\n },\n });\n}\n\nconst FINISHED_RUN_STATUSES: ReadonlySet<ActionRun['status']> = new Set([\n 'succeeded',\n 'partial',\n 'failed',\n 'cancelled',\n]);\n\n/** Whether a run has finished, successfully or not. */\nexport function isRunFinished(run: ActionRun): boolean {\n return FINISHED_RUN_STATUSES.has(run.status);\n}\n\nexport interface UseActionRunOptions {\n /** Milliseconds between two checks while the run is not finished. Defaults to 2 seconds. */\n interval?: number;\n}\n\n/**\n * Follows a queued action run until it finishes. When it does, the cache is refreshed as its\n * result asks. Nothing is fetched while `id` is `null` or `undefined`.\n */\nexport function useActionRun(\n id: string | null | undefined,\n { interval = 2000 }: UseActionRunOptions = {},\n): UseQueryResult<ActionRun> {\n const adapter = useDataAdapter();\n const client = useQueryClient();\n return useQuery({\n queryKey: queryKeys.actionRun(id ?? ''),\n queryFn:\n id == null\n ? skipToken\n : async ({ signal }) => {\n const previous = client.getQueryData<ActionRun>(queryKeys.actionRun(id));\n const run = await adapter.getActionRun(id, { signal });\n const justFinished =\n isRunFinished(run) &&\n (previous === undefined || !isRunFinished(previous));\n if (justFinished && run.result !== undefined) {\n await invalidateActionResult(client, run.resource, run.result);\n }\n return run;\n },\n refetchInterval: (query) =>\n query.state.data !== undefined && isRunFinished(query.state.data) ? false : interval,\n });\n}\n"],"mappings":"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"}
|