@panneau/data 5.0.0-alpha.4 → 5.0.0-alpha.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/adapter.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ActionRequest, ActionResult, ActionRun, Definition, Item, ListQuery, ListResponse, SearchResponse, Session } from '@panneau/protocol';
1
+ import type { ActionRequest, ActionResult, ActionRun, Definition, EmbedValue, FieldCheckRequest, FieldCheckResponse, Item, ListQuery, ListResponse, PreviewResponse, SearchResponse, Session } from '@panneau/protocol';
2
2
  import type { PresignedUploads } from './uploads';
3
3
  /** Values of an item's fields, keyed by field name. */
4
4
  export type Values = Record<string, unknown>;
@@ -6,6 +6,13 @@ export interface RequestOptions {
6
6
  /** Aborts the request. TanStack Query passes one to cancel queries. */
7
7
  signal?: AbortSignal;
8
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
+ }
9
16
  export interface UpdateOptions extends RequestOptions {
10
17
  /**
11
18
  * The version of the item the values were edited from. The update fails with a
@@ -75,8 +82,25 @@ export interface DataAdapter {
75
82
  login(credentials: LoginCredentials, options?: RequestOptions): Promise<Session>;
76
83
  /** Logs out. */
77
84
  logout(options?: RequestOptions): Promise<void>;
78
- /** Searches every searchable resource. */
79
- search(q: string, options?: RequestOptions): Promise<SearchResponse>;
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>;
99
+ /**
100
+ * The value of an `embed` field for a URL, from its provider's oEmbed (`GET /embeds`).
101
+ * Optional: without it, `embed` fields keep the URL alone.
102
+ */
103
+ resolveEmbed?(url: string, options?: RequestOptions): Promise<EmbedValue>;
80
104
  /** Data of a dashboard widget, in the shape its type defines. */
81
105
  getWidgetData(widget: string, options?: RequestOptions): Promise<unknown>;
82
106
  }
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
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`, `429`, `5xx`…) are an `ApiError`.
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?: ErrorOptions): ApiError;
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
@@ -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,YAAY,GAAG,QAAQ,CAejF;AAED,8CAA8C;AAC9C,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D"}
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`, `429`, `5xx`…) are an `ApiError`.
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
@@ -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`, `429`, `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/** 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?: ErrorOptions): 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 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;;AAGA,IAAa,uBAAb,cAA0C,MAAM;CAC5C,OAAiC;;CAEjC;CAEA,YAAY,SAAiB,QAA2B;EACpD,MAAM,OAAO;EACb,KAAK,SAAS;CAClB;AACJ;;AAGA,SAAgB,eAAe,SAAkB,SAAkC;CAC/E,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,SACI,OAAO,IAAI,SAAS,SAAS,OAAO;CAC5C;AACJ;;AAGA,SAAgB,WAAW,OAAmC;CAC1D,OAAO,iBAAiB;AAC5B"}
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,6 +1,6 @@
1
- export type { ActionResponse, DataAdapter, LoginCredentials, RequestOptions, UpdateOptions, Values, } from './adapter';
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
5
  export type { PresignableRequest, PresignedResponse, PresignedUploads } from './uploads';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -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,cAAc,EACd,aAAa,EACb,MAAM,GACT,MAAM,WAAW,CAAC;AACnB,OAAO,EACH,QAAQ,EACR,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,UAAU,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,KAAK,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AACpE,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC"}
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
@@ -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
@@ -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"}
@@ -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;AAQnC,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,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"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../src/query/hooks.ts"],"names":[],"mappings":"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"}
@@ -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
@@ -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":";;;;;;;;;;AA0CA,SAAO,cAAA,IAAA;CAAA,MAAA,IAAAC,EAAA,CAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,IAAA;EACH,KAAA,OAAA,KAAA,IAAA,CAAkF,IAAlF;EAAmF,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAnF,MAAA,UAAA;CAEA,MAAA,UAAgB,eAAe;CAAE,IAAA;CAAA,IAAA,EAAA,OAAA,SAAA;EACZ,KAAA,uBAAuB,OAAO;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,EAAA,OAAA,IAAA;EAApC,KAAA;GAAA,GAAK;GAA+B,GAAK;EAAQ;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OAA3D,SAAS,EAAkD;AAAC;;AAIvE,SAAO,gBAAA,UAAA,IAAA,IAAA;CAAA,MAAA,IAAAA,EAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,IAAA;EAEH,KAAA,OAAA,KAAA,IAAA,CAAoB,IAApB;EAAqB,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAArB,MAAA,QAAA;CAAqB,IAAA;CAAA,IAAA,EAAA,OAAA,IAAA;EACrB,KAAA,OAAA,KAAA,IAAA,CAA8E,IAA9E;EAA+E,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAA/E,MAAA,UAAA;CAEA,MAAA,UAAgB,eAAe;CAAE,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,EAAA,OAAA,SAAA,EAAA,OAAA,UAAA;EACZ,KAAA,yBAAyB,SAAS,UAAU,KAAK;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,EAAA,OAAA,IAAA;EAAvD,KAAA;GAAA,GAAK;GAAkD,GAAK;EAAQ;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,MAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OAA9E,SAAS,EAAqE;AAAC;;AAI1F,SAAO,gBAAA,UAAA,IAAA,IAAA;CAAA,MAAA,IAAAA,EAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,IAAA;EAGH,KAAA,OAAA,KAAA,IAAA,CAAsE,IAAtE;EAAuE,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAvE,MAAA,UAAA;CAEA,MAAA,UAAgB,eAAe;CAC2B,MAAA,KAAA,MAAA;CAAQ,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,EAAA,OAAA,YAAA,EAAA,OAAA,IAAA;EAApD,KAAA,yBAAyB,SAAS,UAAU,EAAQ;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAnE,MAAA,QAAc;CACuB,MAAA,KAAA,MAAM,OAAN,YAAyB,MAAK;CAAQ,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,EAAA,OAAA,SAAA,EAAA,OAAA,IAAA;EAA3D,KAAA;GAAA,GAAK;GAAK,SAAW;GAAsC,GAAK;EAAQ;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OAAlF,SAAS,EAAyE;AAAC;;;;;AAO9F,SAAO,eAAA,UAAA,IAAA,IAAA,IAAA;CAAA,MAAA,IAAAA,EAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,IAAA;EAGH,KAAA,OAAA,KAAA,IAAA,CAAkB,IAAlB;EAAmB,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAnB,MAAA,SAAA;CAAmB,IAAA;CAAA,IAAA,EAAA,OAAA,IAAA;EACnB,KAAA,OAAA,KAAA,IAAA,CAAwF,IAAxF;EAAyF,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAzF,MAAA,UAAA;CAEA,MAAA,UAAgB,eAAe;CAAE,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,EAAA,OAAA,MAAA,EAAA,OAAA,YAAA,EAAA,OAAA,QAAA;EACZ,KAAA,wBAAwB,SAAS,UAAU,IAAI,MAAM;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,EAAA,QAAA,IAAA;EAA3D,KAAA;GAAA,GAAK;GAAsD,GAAK;EAAQ;EAAC,EAAA,KAAA;EAAA,EAAA,MAAA;EAAA,EAAA,MAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OAAlF,SAAS,EAAyE;AAAC;;;;;;;AAS9F,SAAO,UAAA,GAAA,IAAA;CAAA,MAAA,IAAAA,EAAA,CAAA;CAEH,MAAA,EAAA,OAAA,WAAA,GAAA,YAAA,OAAA,KAAA,IAAA,CAKwB,IALxB;CAOA,MAAA,UAAgB,eAAe;CACV,MAAA,KAAA,mBAAmB,SAAS,GAAG;EAAA;EAAA;CAAmB,CAAC;CAAC,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,EAAA,OAAA,IAAA;EAAzD,KAAA;GAAA,GAAK;GAAoD,GAAK;EAAQ;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OAAhF,SAAS,EAAuE;AAAC;;AAI5F,SAAO,cAAA,UAAA;CAAA,MAAA,IAAAA,EAAA,CAAA;CACH,MAAA,UAAgB,eAAe;CAC/B,MAAA,SAAe,eAAe;CAAE,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,EAAA,OAAA,UAAA;EAEhB,MAAA,WAAoB,QAAO,OAAQ,UAAU,MAAM;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,UAAA,EAAA,OAAA,UAAA;EACrD,MAAA,SAAA;GACP,OAAM,aAAc,UAAS,KAAM,UAAU,KAAI,EAAG,GAAG,IAAI;GAAC,OACrD,OAAM,kBAAmB,EAAA,UAAY,UAAS,MAAO,QAAQ,EAAE,CAAC;EAAC;EAC3E,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,MAAA,EAAA,OAAA,IAAA;EALc,KAAA;GAAA,YACH;GAAoD,WACrD;EAIf;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OANM,YAAY,EAMlB;AAAC;;;;;AAgBN,SAAO,cAAA,UAAA;CAAA,MAAA,IAAAA,EAAA,CAAA;CAGH,MAAA,UAAgB,eAAe;CAC/B,MAAA,SAAe,eAAe;CAAE,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,EAAA,OAAA,UAAA;EAEhB,MAAA,OAAA;GAAC,MAAA,EAAA,IAAA,QAAA,YAAA;GAA4C,OACrD,QAAO,OAAQ,UAAU,IAAI,QAAQ,EAAA,QAAU,CAAC;EAAC;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,UAAA,EAAA,OAAA,UAAA;EAC1C,MAAA,SAAA;GACP,OAAM,aAAc,UAAS,KAAM,UAAU,KAAI,EAAG,GAAG,IAAI;GAAC,OACrD,OAAM,kBAAmB,EAAA,UAAY,UAAS,MAAO,QAAQ,EAAE,CAAC;EAAC;EAC3E,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,MAAA,EAAA,OAAA,IAAA;EANc,KAAA;GAAA,YACH;GACyC,WAC1C;EAIf;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OAPM,YAAY,EAOlB;AAAC;;AAIN,SAAO,cAAA,UAAA;CAAA,MAAA,IAAAA,EAAA,CAAA;CACH,MAAA,UAAgB,eAAe;CAC/B,MAAA,SAAe,eAAe;CAAE,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,EAAA,OAAA,UAAA;EAEhB,MAAA,OAAgB,QAAO,OAAQ,UAAU,EAAE;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,UAAA,EAAA,OAAA,UAAA;EAC7C,MAAA,SAAA,SAAA;GACP,OAAM,cAAe,EAAA,UAAY,UAAS,KAAM,UAAUD,IAAE,EAAE,CAAC;GAAC,OACzD,OAAM,kBAAmB,EAAA,UAAY,UAAS,MAAO,QAAQ,EAAE,CAAC;EAAC;EAC3E,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,MAAA,EAAA,OAAA,IAAA;EALc,KAAA;GAAA,YACH;GAA4C,WAC7C;EAIf;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OANM,YAAY,EAMlB;AAAC;;;;;AAYN,SAAO,aAAA,UAAA;CAAA,MAAA,IAAAC,EAAA,CAAA;CAGH,MAAA,UAAgB,eAAe;CAC/B,MAAA,SAAe,eAAe;CAAE,IAAA;CAAA,IAAA,EAAA,OAAA,WAAA,EAAA,OAAA,UAAA;EAEhB,MAAA,OAAA;GAAC,MAAA,EAAA,QAAA,GAAA,YAAA;GAA0C,OACnD,QAAO,UAAW,UAAU,QAAQ,OAAO;EAAC;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,UAAA,EAAA,OAAA,UAAA;EACrC,MAAA,UAAA,OAAA;GAAW,MAAA,EAAA,cAAA;GAClB,IAAI,SAAQ,QAAS,KAAA,GAAS;IAC1B,OAAM,aAAc,UAAS,UAAW,SAAQ,IAAI,EAAG,GAAG,SAAQ,GAAI;IAAC,OAChE,OAAM,kBAAmB;KAAA,UAAY,UAAS,WAAY;KAAC,OAAS;IAAK,CAAC;GAAC;GACrF,OACM,uBAAuB,QAAQ,UAAU,SAAQ,QAAS,SAAS;EAAC;EAC9E,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,IAAA;CAAA,IAAA,EAAA,OAAA,MAAA,EAAA,OAAA,IAAA;EATc,KAAA;GAAA,YACH;GACoC,WACrC;EAOf;EAAC,EAAA,KAAA;EAAA,EAAA,KAAA;EAAA,EAAA,KAAA;CAAA,OAAA,KAAA,EAAA;CAAA,OAVM,YAAY,EAUlB;AAAC;AAGN,IAAM,wCAA0D,IAAI,IAAI;CACpE;CACA;CACA;CACA;AAAW,CACd;;AAGD,SAAgB,cAAc,KAAyB;CACnD,OAAO,sBAAsB,IAAI,IAAI,MAAM;AAC/C;;;;;AAWA,SAAgB,aACZ,IACA,EAAE,WAAW,QAA8B,CAAC,GACnB;CACzB,MAAM,UAAU,eAAe;CAC/B,MAAM,SAAS,eAAe;CAC9B,OAAO,SAAS;EACZ,UAAU,UAAU,UAAU,MAAM,EAAE;EACtC,SACI,MAAM,OACA,YACA,OAAO,EAAE,aAAa;GAClB,MAAM,WAAW,OAAO,aAAwB,UAAU,UAAU,EAAE,CAAC;GACvE,MAAM,MAAM,MAAM,QAAQ,aAAa,IAAI,EAAE,OAAO,CAAC;GAIrD,IAFI,cAAc,GAAG,MAChB,aAAa,KAAA,KAAa,CAAC,cAAc,QAAQ,MAClC,IAAI,WAAW,KAAA,GAC/B,MAAM,uBAAuB,QAAQ,IAAI,UAAU,IAAI,MAAM;GAEjE,OAAO;EACX;EACV,kBAAkB,UACd,MAAM,MAAM,SAAS,KAAA,KAAa,cAAc,MAAM,MAAM,IAAI,IAAI,QAAQ;CACpF,CAAC;AACL"}
@@ -1,6 +1,6 @@
1
1
  export { DataAdapterProvider, type DataAdapterProviderProps, useDataAdapter, useOptionalDataAdapter, } from './context';
2
- export { type QueryHookOptions, type RunActionVariables, type UpdateItemVariables, type UseActionRunOptions, isRunFinished, useActionRun, useCreateItem, useDefinition, useDeleteItem, useResourceItem, useResourceList, useRunAction, useUpdateItem, } from './hooks';
3
- export { queryKeys } from './keys';
4
- export { type DataQueryOptions, definitionQueryOptions, resourceItemQueryOptions, resourceListQueryOptions, } from './options';
2
+ export { type QueryHookOptions, type RunActionVariables, type UpdateItemVariables, type UseActionRunOptions, isRunFinished, useActionRun, useCreateItem, useDefinition, useDeleteItem, useItemPreview, useResourceItem, useResourceList, useRunAction, useSearch, useUpdateItem, } from './hooks';
3
+ export { type SearchKeyOptions, queryKeys } from './keys';
4
+ export { type DataQueryOptions, definitionQueryOptions, itemPreviewQueryOptions, resourceItemQueryOptions, resourceListQueryOptions, searchQueryOptions, } from './options';
5
5
  export { invalidateActionResult } from './refresh';
6
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,cAAc,EACd,sBAAsB,GACzB,MAAM,WAAW,CAAC;AACnB,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,aAAa,EACb,YAAY,EACZ,aAAa,EACb,aAAa,EACb,aAAa,EACb,eAAe,EACf,eAAe,EACf,YAAY,EACZ,aAAa,GAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EACH,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,wBAAwB,EACxB,wBAAwB,GAC3B,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,cAAc,EACd,sBAAsB,GACzB,MAAM,WAAW,CAAC;AACnB,OAAO,EACH,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACxB,aAAa,EACb,YAAY,EACZ,aAAa,EACb,aAAa,EACb,aAAa,EACb,cAAc,EACd,eAAe,EACf,eAAe,EACf,YAAY,EACZ,SAAS,EACT,aAAa,GAChB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EACH,KAAK,gBAAgB,EACrB,sBAAsB,EACtB,uBAAuB,EACvB,wBAAwB,EACxB,wBAAwB,EACxB,kBAAkB,GACrB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { DataAdapterProvider, useDataAdapter, useOptionalDataAdapter } 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
- import { isRunFinished, useActionRun, useCreateItem, useDefinition, useDeleteItem, useResourceItem, useResourceList, useRunAction, useUpdateItem } from "./hooks.js";
6
- export { DataAdapterProvider, definitionQueryOptions, invalidateActionResult, isRunFinished, queryKeys, resourceItemQueryOptions, resourceListQueryOptions, useActionRun, useCreateItem, useDataAdapter, useDefinition, useDeleteItem, useOptionalDataAdapter, useResourceItem, useResourceList, useRunAction, useUpdateItem };
5
+ import { isRunFinished, useActionRun, useCreateItem, useDefinition, useDeleteItem, useItemPreview, useResourceItem, useResourceList, useRunAction, useSearch, useUpdateItem } from "./hooks.js";
6
+ export { DataAdapterProvider, definitionQueryOptions, invalidateActionResult, isRunFinished, itemPreviewQueryOptions, queryKeys, resourceItemQueryOptions, resourceListQueryOptions, searchQueryOptions, useActionRun, useCreateItem, useDataAdapter, useDefinition, useDeleteItem, useItemPreview, useOptionalDataAdapter, useResourceItem, useResourceList, useRunAction, useSearch, useUpdateItem };
@@ -1,4 +1,9 @@
1
1
  import type { ListQuery } from '@panneau/protocol';
2
+ /** What a search is keyed by besides its query: its `limit` and `resources`. */
3
+ export interface SearchKeyOptions {
4
+ limit?: number;
5
+ resources?: readonly string[];
6
+ }
2
7
  /**
3
8
  * Query keys of the hooks, to read, prefetch or invalidate their data with the query client.
4
9
  * Keys are nested: invalidating `queryKeys.resource('pages')` covers its lists and items.
@@ -11,6 +16,10 @@ export declare const queryKeys: {
11
16
  list: (resource: string, query?: ListQuery) => readonly ["panneau", "resources", string, "list", ListQuery];
12
17
  items: (resource: string) => readonly ["panneau", "resources", string, "item"];
13
18
  item: (resource: string, id: string) => readonly ["panneau", "resources", string, "item", string];
19
+ /** Under the item's key: refetching or invalidating the item refreshes its preview. */
20
+ itemPreview: (resource: string, id: string, values?: Record<string, unknown>) => readonly ["panneau", "resources", string, "item", string, "preview", Record<string, unknown>];
21
+ /** Under `['panneau', 'search']`: invalidating it refreshes every search. */
22
+ search: (q: string, options?: SearchKeyOptions) => readonly ["panneau", "search", string, SearchKeyOptions];
14
23
  actionRuns: () => readonly ["panneau", "actions", "runs"];
15
24
  actionRun: (id: string) => readonly ["panneau", "actions", "runs", string];
16
25
  };
@@ -1 +1 @@
1
- {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/query/keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,SAAS;;;yBAGG,MAAM;sBACT,MAAM;qBACP,MAAM,UAAS,SAAS;sBAEvB,MAAM;qBACP,MAAM,MAAM,MAAM;;oBAEnB,MAAM;CACzB,CAAC"}
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../../src/query/keys.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,gFAAgF;AAChF,MAAM,WAAW,gBAAgB;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC;AAED;;;GAGG;AACH,eAAO,MAAM,SAAS;;;yBAGG,MAAM;sBACT,MAAM;qBACP,MAAM,UAAS,SAAS;sBAEvB,MAAM;qBACP,MAAM,MAAM,MAAM;IACnC,uFAAuF;4BAC/D,MAAM,MAAM,MAAM,WAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAE3E,6EAA6E;gBACjE,MAAM,YAAW,gBAAgB;;oBAG7B,MAAM;CACzB,CAAC"}
@@ -37,6 +37,23 @@ var queryKeys = {
37
37
  "item",
38
38
  id
39
39
  ],
40
+ /** Under the item's key: refetching or invalidating the item refreshes its preview. */
41
+ itemPreview: (resource, id, values = {}) => [
42
+ "panneau",
43
+ "resources",
44
+ resource,
45
+ "item",
46
+ id,
47
+ "preview",
48
+ values
49
+ ],
50
+ /** Under `['panneau', 'search']`: invalidating it refreshes every search. */
51
+ search: (q, options = {}) => [
52
+ "panneau",
53
+ "search",
54
+ q,
55
+ options
56
+ ],
40
57
  actionRuns: () => [
41
58
  "panneau",
42
59
  "actions",
@@ -1 +1 @@
1
- {"version":3,"file":"keys.js","names":[],"sources":["../../src/query/keys.ts"],"sourcesContent":["import type { ListQuery } from '@panneau/protocol';\n\n/**\n * Query keys of the hooks, to read, prefetch or invalidate their data with the query client.\n * Keys are nested: invalidating `queryKeys.resource('pages')` covers its lists and items.\n */\nexport const queryKeys = {\n all: ['panneau'] as const,\n definition: () => ['panneau', 'definition'] as const,\n resource: (resource: string) => ['panneau', 'resources', resource] as const,\n lists: (resource: string) => ['panneau', 'resources', resource, 'list'] as const,\n list: (resource: string, query: ListQuery = {}) =>\n ['panneau', 'resources', resource, 'list', query] as const,\n items: (resource: string) => ['panneau', 'resources', resource, 'item'] as const,\n item: (resource: string, id: string) => ['panneau', 'resources', resource, 'item', id] as const,\n actionRuns: () => ['panneau', 'actions', 'runs'] as const,\n actionRun: (id: string) => ['panneau', 'actions', 'runs', id] as const,\n};\n"],"mappings":";;;;;AAMA,IAAa,YAAY;CACrB,KAAK,CAAC,SAAS;CACf,kBAAkB,CAAC,WAAW,YAAY;CAC1C,WAAW,aAAqB;EAAC;EAAW;EAAa;CAAQ;CACjE,QAAQ,aAAqB;EAAC;EAAW;EAAa;EAAU;CAAM;CACtE,OAAO,UAAkB,QAAmB,CAAC,MACzC;EAAC;EAAW;EAAa;EAAU;EAAQ;CAAK;CACpD,QAAQ,aAAqB;EAAC;EAAW;EAAa;EAAU;CAAM;CACtE,OAAO,UAAkB,OAAe;EAAC;EAAW;EAAa;EAAU;EAAQ;CAAE;CACrF,kBAAkB;EAAC;EAAW;EAAW;CAAM;CAC/C,YAAY,OAAe;EAAC;EAAW;EAAW;EAAQ;CAAE;AAChE"}
1
+ {"version":3,"file":"keys.js","names":[],"sources":["../../src/query/keys.ts"],"sourcesContent":["import type { ListQuery } from '@panneau/protocol';\n\n/** What a search is keyed by besides its query: its `limit` and `resources`. */\nexport interface SearchKeyOptions {\n limit?: number;\n resources?: readonly string[];\n}\n\n/**\n * Query keys of the hooks, to read, prefetch or invalidate their data with the query client.\n * Keys are nested: invalidating `queryKeys.resource('pages')` covers its lists and items.\n */\nexport const queryKeys = {\n all: ['panneau'] as const,\n definition: () => ['panneau', 'definition'] as const,\n resource: (resource: string) => ['panneau', 'resources', resource] as const,\n lists: (resource: string) => ['panneau', 'resources', resource, 'list'] as const,\n list: (resource: string, query: ListQuery = {}) =>\n ['panneau', 'resources', resource, 'list', query] as const,\n items: (resource: string) => ['panneau', 'resources', resource, 'item'] as const,\n item: (resource: string, id: string) => ['panneau', 'resources', resource, 'item', id] as const,\n /** Under the item's key: refetching or invalidating the item refreshes its preview. */\n itemPreview: (resource: string, id: string, values: Record<string, unknown> = {}) =>\n ['panneau', 'resources', resource, 'item', id, 'preview', values] as const,\n /** Under `['panneau', 'search']`: invalidating it refreshes every search. */\n search: (q: string, options: SearchKeyOptions = {}) =>\n ['panneau', 'search', q, options] as const,\n actionRuns: () => ['panneau', 'actions', 'runs'] as const,\n actionRun: (id: string) => ['panneau', 'actions', 'runs', id] as const,\n};\n"],"mappings":";;;;;AAYA,IAAa,YAAY;CACrB,KAAK,CAAC,SAAS;CACf,kBAAkB,CAAC,WAAW,YAAY;CAC1C,WAAW,aAAqB;EAAC;EAAW;EAAa;CAAQ;CACjE,QAAQ,aAAqB;EAAC;EAAW;EAAa;EAAU;CAAM;CACtE,OAAO,UAAkB,QAAmB,CAAC,MACzC;EAAC;EAAW;EAAa;EAAU;EAAQ;CAAK;CACpD,QAAQ,aAAqB;EAAC;EAAW;EAAa;EAAU;CAAM;CACtE,OAAO,UAAkB,OAAe;EAAC;EAAW;EAAa;EAAU;EAAQ;CAAE;;CAErF,cAAc,UAAkB,IAAY,SAAkC,CAAC,MAC3E;EAAC;EAAW;EAAa;EAAU;EAAQ;EAAI;EAAW;CAAM;;CAEpE,SAAS,GAAW,UAA4B,CAAC,MAC7C;EAAC;EAAW;EAAU;EAAG;CAAO;CACpC,kBAAkB;EAAC;EAAW;EAAW;CAAM;CAC/C,YAAY,OAAe;EAAC;EAAW;EAAW;EAAQ;CAAE;AAChE"}
@@ -1,7 +1,7 @@
1
1
  import { type DataTag, type QueryKey, type UseQueryOptions } from '@tanstack/react-query';
2
- import type { Definition, Item, ListQuery, ListResponse } from '@panneau/protocol';
3
- import type { DataAdapter } from '../adapter';
4
- import { queryKeys } from './keys';
2
+ import type { Definition, Item, ListQuery, ListResponse, PreviewResponse, SearchResponse } from '@panneau/protocol';
3
+ import type { DataAdapter, Values } from '../adapter';
4
+ import { type SearchKeyOptions, queryKeys } from './keys';
5
5
  /**
6
6
  * Query options made by the `*QueryOptions()` functions. Their key is tagged with the type of the
7
7
  * data, so `queryClient.getQueryData(options.queryKey)` is typed.
@@ -15,4 +15,16 @@ export declare function definitionQueryOptions(adapter: DataAdapter): DataQueryO
15
15
  export declare function resourceListQueryOptions(adapter: DataAdapter, resource: string, query?: ListQuery): DataQueryOptions<ListResponse, ReturnType<typeof queryKeys.list>>;
16
16
  /** Query options of one item. */
17
17
  export declare function resourceItemQueryOptions(adapter: DataAdapter, resource: string, id: string): DataQueryOptions<Item, ReturnType<typeof queryKeys.item>>;
18
+ /**
19
+ * Query options of the iframe preview of an item: a short-lived URL of its page on the site,
20
+ * rendered with `values` (`{}` for the saved item). The previous URL stays while the next one
21
+ * loads, so the iframe doesn't blink. Fails when the adapter has no `previewItem()`.
22
+ */
23
+ export declare function itemPreviewQueryOptions(adapter: DataAdapter, resource: string, id: string, values?: Values): DataQueryOptions<PreviewResponse, ReturnType<typeof queryKeys.itemPreview>>;
24
+ /**
25
+ * Query options of the global search (decision 0029): every searchable resource, or those of
26
+ * `resources`, `limit` items each. The previous results stay while the next ones load. Debounce
27
+ * `q`, and only enable the query from 2 characters.
28
+ */
29
+ export declare function searchQueryOptions(adapter: DataAdapter, q: string, { limit, resources }?: SearchKeyOptions): DataQueryOptions<SearchResponse, ReturnType<typeof queryKeys.search>>;
18
30
  //# sourceMappingURL=options.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/query/options.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,eAAe,EAGvB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEnF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAEnC;;;GAGG;AACH,MAAM,MAAM,gBAAgB,CAAC,KAAK,EAAE,IAAI,SAAS,QAAQ,IAAI,eAAe,CACxE,KAAK,EACL,KAAK,EACL,KAAK,EACL,IAAI,CACP,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;CAAE,CAAC;AAE9C,uFAAuF;AACvF,wBAAgB,sBAAsB,CAClC,OAAO,EAAE,WAAW,GACrB,gBAAgB,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,UAAU,CAAC,CAAC,CAOvE;AAED,gGAAgG;AAChG,wBAAgB,wBAAwB,CACpC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,SAAc,GACtB,gBAAgB,CAAC,YAAY,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAMnE;AAED,iCAAiC;AACjC,wBAAgB,wBAAwB,CACpC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,GACX,gBAAgB,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAK3D"}
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/query/options.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,OAAO,EACZ,KAAK,QAAQ,EACb,KAAK,eAAe,EAGvB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EACR,UAAU,EACV,IAAI,EACJ,SAAS,EACT,YAAY,EACZ,eAAe,EACf,cAAc,EACjB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,EAAE,KAAK,gBAAgB,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAE1D;;;GAGG;AACH,MAAM,MAAM,gBAAgB,CAAC,KAAK,EAAE,IAAI,SAAS,QAAQ,IAAI,eAAe,CACxE,KAAK,EACL,KAAK,EACL,KAAK,EACL,IAAI,CACP,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;CAAE,CAAC;AAE9C,uFAAuF;AACvF,wBAAgB,sBAAsB,CAClC,OAAO,EAAE,WAAW,GACrB,gBAAgB,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,UAAU,CAAC,CAAC,CAOvE;AAED,gGAAgG;AAChG,wBAAgB,wBAAwB,CACpC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,SAAc,GACtB,gBAAgB,CAAC,YAAY,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAMnE;AAED,iCAAiC;AACjC,wBAAgB,wBAAwB,CACpC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,GACX,gBAAgB,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAK3D;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACnC,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,EACV,MAAM,GAAE,MAAW,GACpB,gBAAgB,CAAC,eAAe,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,WAAW,CAAC,CAAC,CAa7E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAC9B,OAAO,EAAE,WAAW,EACpB,CAAC,EAAE,MAAM,EACT,EAAE,KAAK,EAAE,SAAS,EAAE,GAAE,gBAAqB,GAC5C,gBAAgB,CAAC,cAAc,EAAE,UAAU,CAAC,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,CAWvE"}
@@ -24,7 +24,43 @@ function resourceItemQueryOptions(adapter, resource, id) {
24
24
  queryFn: ({ signal }) => adapter.get(resource, id, { signal })
25
25
  });
26
26
  }
27
+ /**
28
+ * Query options of the iframe preview of an item: a short-lived URL of its page on the site,
29
+ * rendered with `values` (`{}` for the saved item). The previous URL stays while the next one
30
+ * loads, so the iframe doesn't blink. Fails when the adapter has no `previewItem()`.
31
+ */
32
+ function itemPreviewQueryOptions(adapter, resource, id, values = {}) {
33
+ return queryOptions({
34
+ queryKey: queryKeys.itemPreview(resource, id, values),
35
+ queryFn: ({ signal }) => {
36
+ if (adapter.previewItem === void 0) throw new Error("The data adapter has no previewItem().");
37
+ return adapter.previewItem(resource, id, values, { signal });
38
+ },
39
+ placeholderData: keepPreviousData,
40
+ staleTime: 6e4
41
+ });
42
+ }
43
+ /**
44
+ * Query options of the global search (decision 0029): every searchable resource, or those of
45
+ * `resources`, `limit` items each. The previous results stay while the next ones load. Debounce
46
+ * `q`, and only enable the query from 2 characters.
47
+ */
48
+ function searchQueryOptions(adapter, q, { limit, resources } = {}) {
49
+ const options = {
50
+ ...limit !== void 0 && { limit },
51
+ ...resources !== void 0 && { resources }
52
+ };
53
+ return queryOptions({
54
+ queryKey: queryKeys.search(q, options),
55
+ queryFn: ({ signal }) => adapter.search(q, {
56
+ ...options,
57
+ signal
58
+ }),
59
+ placeholderData: keepPreviousData,
60
+ staleTime: 3e4
61
+ });
62
+ }
27
63
  //#endregion
28
- export { definitionQueryOptions, resourceItemQueryOptions, resourceListQueryOptions };
64
+ export { definitionQueryOptions, itemPreviewQueryOptions, resourceItemQueryOptions, resourceListQueryOptions, searchQueryOptions };
29
65
 
30
66
  //# sourceMappingURL=options.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"options.js","names":[],"sources":["../../src/query/options.ts"],"sourcesContent":["import {\n type DataTag,\n type QueryKey,\n type UseQueryOptions,\n keepPreviousData,\n queryOptions,\n} from '@tanstack/react-query';\n\nimport type { Definition, Item, ListQuery, ListResponse } from '@panneau/protocol';\n\nimport type { DataAdapter } from '../adapter';\nimport { queryKeys } from './keys';\n\n/**\n * Query options made by the `*QueryOptions()` functions. Their key is tagged with the type of the\n * data, so `queryClient.getQueryData(options.queryKey)` is typed.\n */\nexport type DataQueryOptions<TData, TKey extends QueryKey> = UseQueryOptions<\n TData,\n Error,\n TData,\n TKey\n> & { queryKey: DataTag<TKey, TData, Error> };\n\n/** Query options of the definition, for `useQuery()`, prefetching or route loaders. */\nexport function definitionQueryOptions(\n adapter: DataAdapter,\n): DataQueryOptions<Definition, ReturnType<typeof queryKeys.definition>> {\n return queryOptions({\n queryKey: queryKeys.definition(),\n queryFn: ({ signal }) => adapter.getDefinition({ signal }),\n // The definition only changes with the user or the deployment.\n staleTime: Infinity,\n });\n}\n\n/** Query options of a page of items. The previous page stays shown while the next one loads. */\nexport function resourceListQueryOptions(\n adapter: DataAdapter,\n resource: string,\n query: ListQuery = {},\n): DataQueryOptions<ListResponse, ReturnType<typeof queryKeys.list>> {\n return queryOptions({\n queryKey: queryKeys.list(resource, query),\n queryFn: ({ signal }) => adapter.list(resource, query, { signal }),\n placeholderData: keepPreviousData,\n });\n}\n\n/** Query options of one item. */\nexport function resourceItemQueryOptions(\n adapter: DataAdapter,\n resource: string,\n id: string,\n): DataQueryOptions<Item, ReturnType<typeof queryKeys.item>> {\n return queryOptions({\n queryKey: queryKeys.item(resource, id),\n queryFn: ({ signal }) => adapter.get(resource, id, { signal }),\n });\n}\n"],"mappings":";;;;AAyBA,SAAgB,uBACZ,SACqE;CACrE,OAAO,aAAa;EAChB,UAAU,UAAU,WAAW;EAC/B,UAAU,EAAE,aAAa,QAAQ,cAAc,EAAE,OAAO,CAAC;EAEzD,WAAW;CACf,CAAC;AACL;;AAGA,SAAgB,yBACZ,SACA,UACA,QAAmB,CAAC,GAC6C;CACjE,OAAO,aAAa;EAChB,UAAU,UAAU,KAAK,UAAU,KAAK;EACxC,UAAU,EAAE,aAAa,QAAQ,KAAK,UAAU,OAAO,EAAE,OAAO,CAAC;EACjE,iBAAiB;CACrB,CAAC;AACL;;AAGA,SAAgB,yBACZ,SACA,UACA,IACyD;CACzD,OAAO,aAAa;EAChB,UAAU,UAAU,KAAK,UAAU,EAAE;EACrC,UAAU,EAAE,aAAa,QAAQ,IAAI,UAAU,IAAI,EAAE,OAAO,CAAC;CACjE,CAAC;AACL"}
1
+ {"version":3,"file":"options.js","names":[],"sources":["../../src/query/options.ts"],"sourcesContent":["import {\n type DataTag,\n type QueryKey,\n type UseQueryOptions,\n keepPreviousData,\n queryOptions,\n} from '@tanstack/react-query';\n\nimport type {\n Definition,\n Item,\n ListQuery,\n ListResponse,\n PreviewResponse,\n SearchResponse,\n} from '@panneau/protocol';\n\nimport type { DataAdapter, Values } from '../adapter';\nimport { type SearchKeyOptions, queryKeys } from './keys';\n\n/**\n * Query options made by the `*QueryOptions()` functions. Their key is tagged with the type of the\n * data, so `queryClient.getQueryData(options.queryKey)` is typed.\n */\nexport type DataQueryOptions<TData, TKey extends QueryKey> = UseQueryOptions<\n TData,\n Error,\n TData,\n TKey\n> & { queryKey: DataTag<TKey, TData, Error> };\n\n/** Query options of the definition, for `useQuery()`, prefetching or route loaders. */\nexport function definitionQueryOptions(\n adapter: DataAdapter,\n): DataQueryOptions<Definition, ReturnType<typeof queryKeys.definition>> {\n return queryOptions({\n queryKey: queryKeys.definition(),\n queryFn: ({ signal }) => adapter.getDefinition({ signal }),\n // The definition only changes with the user or the deployment.\n staleTime: Infinity,\n });\n}\n\n/** Query options of a page of items. The previous page stays shown while the next one loads. */\nexport function resourceListQueryOptions(\n adapter: DataAdapter,\n resource: string,\n query: ListQuery = {},\n): DataQueryOptions<ListResponse, ReturnType<typeof queryKeys.list>> {\n return queryOptions({\n queryKey: queryKeys.list(resource, query),\n queryFn: ({ signal }) => adapter.list(resource, query, { signal }),\n placeholderData: keepPreviousData,\n });\n}\n\n/** Query options of one item. */\nexport function resourceItemQueryOptions(\n adapter: DataAdapter,\n resource: string,\n id: string,\n): DataQueryOptions<Item, ReturnType<typeof queryKeys.item>> {\n return queryOptions({\n queryKey: queryKeys.item(resource, id),\n queryFn: ({ signal }) => adapter.get(resource, id, { signal }),\n });\n}\n\n/**\n * Query options of the iframe preview of an item: a short-lived URL of its page on the site,\n * rendered with `values` (`{}` for the saved item). The previous URL stays while the next one\n * loads, so the iframe doesn't blink. Fails when the adapter has no `previewItem()`.\n */\nexport function itemPreviewQueryOptions(\n adapter: DataAdapter,\n resource: string,\n id: string,\n values: Values = {},\n): DataQueryOptions<PreviewResponse, ReturnType<typeof queryKeys.itemPreview>> {\n return queryOptions({\n queryKey: queryKeys.itemPreview(resource, id, values),\n queryFn: ({ signal }) => {\n if (adapter.previewItem === undefined) {\n throw new Error('The data adapter has no previewItem().');\n }\n return adapter.previewItem(resource, id, values, { signal });\n },\n placeholderData: keepPreviousData,\n // Preview URLs are signed for a short time: a new one when the panel opens again later.\n staleTime: 60_000,\n });\n}\n\n/**\n * Query options of the global search (decision 0029): every searchable resource, or those of\n * `resources`, `limit` items each. The previous results stay while the next ones load. Debounce\n * `q`, and only enable the query from 2 characters.\n */\nexport function searchQueryOptions(\n adapter: DataAdapter,\n q: string,\n { limit, resources }: SearchKeyOptions = {},\n): DataQueryOptions<SearchResponse, ReturnType<typeof queryKeys.search>> {\n const options: SearchKeyOptions = {\n ...(limit !== undefined && { limit }),\n ...(resources !== undefined && { resources }),\n };\n return queryOptions({\n queryKey: queryKeys.search(q, options),\n queryFn: ({ signal }) => adapter.search(q, { ...options, signal }),\n placeholderData: keepPreviousData,\n staleTime: 30_000,\n });\n}\n"],"mappings":";;;;AAgCA,SAAgB,uBACZ,SACqE;CACrE,OAAO,aAAa;EAChB,UAAU,UAAU,WAAW;EAC/B,UAAU,EAAE,aAAa,QAAQ,cAAc,EAAE,OAAO,CAAC;EAEzD,WAAW;CACf,CAAC;AACL;;AAGA,SAAgB,yBACZ,SACA,UACA,QAAmB,CAAC,GAC6C;CACjE,OAAO,aAAa;EAChB,UAAU,UAAU,KAAK,UAAU,KAAK;EACxC,UAAU,EAAE,aAAa,QAAQ,KAAK,UAAU,OAAO,EAAE,OAAO,CAAC;EACjE,iBAAiB;CACrB,CAAC;AACL;;AAGA,SAAgB,yBACZ,SACA,UACA,IACyD;CACzD,OAAO,aAAa;EAChB,UAAU,UAAU,KAAK,UAAU,EAAE;EACrC,UAAU,EAAE,aAAa,QAAQ,IAAI,UAAU,IAAI,EAAE,OAAO,CAAC;CACjE,CAAC;AACL;;;;;;AAOA,SAAgB,wBACZ,SACA,UACA,IACA,SAAiB,CAAC,GACyD;CAC3E,OAAO,aAAa;EAChB,UAAU,UAAU,YAAY,UAAU,IAAI,MAAM;EACpD,UAAU,EAAE,aAAa;GACrB,IAAI,QAAQ,gBAAgB,KAAA,GACxB,MAAM,IAAI,MAAM,wCAAwC;GAE5D,OAAO,QAAQ,YAAY,UAAU,IAAI,QAAQ,EAAE,OAAO,CAAC;EAC/D;EACA,iBAAiB;EAEjB,WAAW;CACf,CAAC;AACL;;;;;;AAOA,SAAgB,mBACZ,SACA,GACA,EAAE,OAAO,cAAgC,CAAC,GAC2B;CACrE,MAAM,UAA4B;EAC9B,GAAI,UAAU,KAAA,KAAa,EAAE,MAAM;EACnC,GAAI,cAAc,KAAA,KAAa,EAAE,UAAU;CAC/C;CACA,OAAO,aAAa;EAChB,UAAU,UAAU,OAAO,GAAG,OAAO;EACrC,UAAU,EAAE,aAAa,QAAQ,OAAO,GAAG;GAAE,GAAG;GAAS;EAAO,CAAC;EACjE,iBAAiB;EACjB,WAAW;CACf,CAAC;AACL"}
@@ -1 +1 @@
1
- {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../src/rest.ts"],"names":[],"mappings":"AAAA,OAAO,EAMH,KAAK,OAAO,EAGf,MAAM,mBAAmB,CAAC;AAG3B,OAAO,KAAK,EAAkB,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,KAAK,QAAQ,EAAwC,MAAM,UAAU,CAAC;AAI/E,MAAM,WAAW,kBAAkB;IAC/B,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChE,wFAAwF;IACxF,OAAO,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;IACnE,+DAA+D;IAC/D,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK,QAAQ,GAAG,SAAS,CAAC;CAChF;AAsDD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CA+K1E"}
1
+ {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../src/rest.ts"],"names":[],"mappings":"AAAA,OAAO,EAMH,KAAK,OAAO,EAIf,MAAM,mBAAmB,CAAC;AAW3B,OAAO,KAAK,EAAkB,WAAW,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,KAAK,QAAQ,EAAwC,MAAM,UAAU,CAAC;AAI/E,MAAM,WAAW,kBAAkB;IAC/B,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChE,wFAAwF;IACxF,OAAO,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;IACnE,+DAA+D;IAC/D,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK,QAAQ,GAAG,SAAS,CAAC;CAChF;AAsDD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CA+M1E"}
package/dist/rest.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { InvalidResponseError, createApiError } from "./errors.js";
2
- import { readProblem } from "./problem.js";
2
+ import { readProblem, readRetryAfter } from "./problem.js";
3
3
  import { createPresignedUploads } from "./uploads/index.js";
4
- import { ActionResult, ActionRun, Definition, PROBLEM_CONTENT_TYPE, toSearchParams, validate } from "@panneau/protocol";
4
+ import { ActionResult, ActionRun, Definition, PROBLEM_CONTENT_TYPE, toSearchParams, toSearchQueryParams, validate } from "@panneau/protocol";
5
5
  //#region src/rest.ts
6
6
  var ACCEPT = `application/json, ${PROBLEM_CONTENT_TYPE}`;
7
7
  function isBlob(value) {
@@ -59,7 +59,7 @@ function createRestAdapter(options) {
59
59
  const response = await fetcher(`${baseUrl}${path}${search !== "" ? `?${search}` : ""}`, init);
60
60
  if (!response.ok) {
61
61
  const problem = await readProblem(response);
62
- throw options.createError?.(problem, response) ?? createApiError(problem);
62
+ throw options.createError?.(problem, response) ?? createApiError(problem, { retryAfter: readRetryAfter(response) });
63
63
  }
64
64
  return response;
65
65
  };
@@ -157,7 +157,7 @@ function createRestAdapter(options) {
157
157
  fetch: fetcher,
158
158
  ...options.headers !== void 0 && { headers: options.headers },
159
159
  credentials: options.credentials ?? "same-origin",
160
- createError: (problem, response) => options.createError?.(problem, response) ?? createApiError(problem)
160
+ createError: (problem, response) => options.createError?.(problem, response) ?? createApiError(problem, { retryAfter: readRetryAfter(response) })
161
161
  });
162
162
  },
163
163
  getSession({ signal } = {}) {
@@ -181,13 +181,40 @@ function createRestAdapter(options) {
181
181
  signal
182
182
  });
183
183
  },
184
- search(q, { signal } = {}) {
184
+ previewItem(resource, id, values, { signal } = {}) {
185
+ return json({
186
+ method: "POST",
187
+ path: `/resources/${encode(resource)}/${encode(id)}/preview`,
188
+ body: values,
189
+ signal
190
+ });
191
+ },
192
+ search(q, { signal, limit, resources } = {}) {
185
193
  return json({
186
194
  path: "/search",
187
- query: new URLSearchParams({ q }),
195
+ query: toSearchQueryParams({
196
+ q,
197
+ ...limit !== void 0 && { limit },
198
+ ...resources !== void 0 && { resources }
199
+ }),
188
200
  signal
189
201
  });
190
202
  },
203
+ checkField(resource, field, request, { signal } = {}) {
204
+ return json({
205
+ method: "POST",
206
+ path: `/resources/${encode(resource)}/fields/${encode(field)}/check`,
207
+ body: request,
208
+ signal
209
+ });
210
+ },
211
+ async resolveEmbed(url, { signal } = {}) {
212
+ return (await json({
213
+ path: "/embeds",
214
+ query: new URLSearchParams({ url }),
215
+ signal
216
+ })).data;
217
+ },
191
218
  getWidgetData(widget, { signal } = {}) {
192
219
  return json({
193
220
  path: `/dashboard/widgets/${encode(widget)}`,
package/dist/rest.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"rest.js","names":[],"sources":["../src/rest.ts"],"sourcesContent":["import {\n ActionResult,\n ActionRun,\n Definition,\n type ListQuery,\n PROBLEM_CONTENT_TYPE,\n type Problem,\n toSearchParams,\n validate,\n} from '@panneau/protocol';\nimport type { Item, ListResponse, SearchResponse, Session } from '@panneau/protocol';\n\nimport type { ActionResponse, DataAdapter } from './adapter';\nimport { type ApiError, InvalidResponseError, createApiError } from './errors';\nimport { readProblem } from './problem';\nimport { createPresignedUploads } from './uploads';\n\nexport interface RestAdapterOptions {\n /** URL of the API, as `api.baseUrl` in the definition (`/panneau/api`). */\n baseUrl: string;\n /** The `fetch` function to use. Defaults to the global one. */\n fetch?: (input: string, init: RequestInit) => Promise<Response>;\n /** Headers added to every request, or a function that returns them for each request. */\n headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);\n /** Whether requests send cookies (`fetch`'s `credentials`). */\n credentials?: RequestCredentials;\n /**\n * Creates the error of a failed response. Return `undefined` to use the default mapping from\n * the status (`createApiError()`). The Laravel preset uses it for `419`.\n */\n createError?: (problem: Problem, response: Response) => ApiError | undefined;\n}\n\ninterface Request {\n method?: string;\n path: string;\n query?: URLSearchParams;\n body?: unknown;\n headers?: HeadersInit;\n signal?: AbortSignal | undefined;\n}\n\nconst ACCEPT = `application/json, ${PROBLEM_CONTENT_TYPE}`;\n\nfunction isBlob(value: unknown): value is Blob {\n return typeof Blob !== 'undefined' && value instanceof Blob;\n}\n\nfunction hasBlob(value: unknown): boolean {\n if (isBlob(value)) {\n return true;\n }\n if (Array.isArray(value)) {\n return value.some(hasBlob);\n }\n if (value !== null && typeof value === 'object') {\n return Object.values(value).some(hasBlob);\n }\n return false;\n}\n\n/** Appends a value to form data with bracket keys: `values[tags][]`, `selection[ids][]`. */\nfunction appendFormData(data: FormData, key: string, value: unknown) {\n if (value === undefined) {\n return;\n }\n if (isBlob(value)) {\n data.append(key, value);\n } else if (Array.isArray(value)) {\n value.forEach((item) => appendFormData(data, `${key}[]`, item));\n } else if (value !== null && typeof value === 'object') {\n Object.entries(value).forEach(([name, item]) =>\n appendFormData(data, key === '' ? name : `${key}[${name}]`, item),\n );\n } else if (typeof value === 'boolean') {\n data.append(key, value ? '1' : '0');\n } else {\n data.append(key, value === null ? '' : String(value));\n }\n}\n\nfunction encode(path: string): string {\n return encodeURIComponent(path);\n}\n\n/**\n * A `DataAdapter` over the HTTP API of the protocol. Errors (`application/problem+json`) reject\n * with typed errors, updates send `If-Match` from the item version, and list queries use the\n * protocol's query string format.\n */\nexport function createRestAdapter(options: RestAdapterOptions): DataAdapter {\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n const fetcher = options.fetch ?? ((input, init) => globalThis.fetch(input, init));\n\n const send = async ({\n method = 'GET',\n path,\n query,\n body,\n headers: requestHeaders,\n signal,\n }: Request): Promise<Response> => {\n const headers = new Headers(\n typeof options.headers === 'function' ? await options.headers() : options.headers,\n );\n headers.set('Accept', ACCEPT);\n new Headers(requestHeaders).forEach((value, name) => headers.set(name, value));\n\n let payload: BodyInit | undefined;\n if (body !== undefined) {\n if (hasBlob(body)) {\n const data = new FormData();\n appendFormData(data, '', body);\n payload = data;\n } else {\n headers.set('Content-Type', 'application/json');\n payload = JSON.stringify(body);\n }\n }\n\n const search = query?.toString() ?? '';\n const init: RequestInit = { method, headers };\n if (payload !== undefined) {\n init.body = payload;\n }\n if (options.credentials !== undefined) {\n init.credentials = options.credentials;\n }\n if (signal !== undefined) {\n init.signal = signal;\n }\n const response = await fetcher(\n `${baseUrl}${path}${search !== '' ? `?${search}` : ''}`,\n init,\n );\n if (!response.ok) {\n const problem = await readProblem(response);\n throw options.createError?.(problem, response) ?? createApiError(problem);\n }\n return response;\n };\n\n const json = async <T>(request: Request): Promise<T> => {\n const response = await send(request);\n return (await response.json()) as T;\n };\n\n const data = async <T>(request: Request): Promise<T> => (await json<{ data: T }>(request)).data;\n\n return {\n async getDefinition({ signal } = {}) {\n const body = await json<unknown>({ path: '/definition', signal });\n const result = validate(Definition, body);\n if (!result.success) {\n throw new InvalidResponseError('The definition is invalid.', result.issues);\n }\n return result.data;\n },\n\n list(resource, query: ListQuery = {}, { signal } = {}) {\n return json<ListResponse>({\n path: `/resources/${encode(resource)}`,\n query: toSearchParams(query),\n signal,\n });\n },\n get(resource, id, { signal } = {}) {\n return data<Item>({ path: `/resources/${encode(resource)}/${encode(id)}`, signal });\n },\n create(resource, values, { signal } = {}) {\n return data<Item>({\n method: 'POST',\n path: `/resources/${encode(resource)}`,\n body: values,\n signal,\n });\n },\n update(resource, id, values, { version, signal } = {}) {\n return data<Item>({\n method: 'PATCH',\n path: `/resources/${encode(resource)}/${encode(id)}`,\n body: values,\n headers:\n version !== undefined && version !== null ? { 'If-Match': `\"${version}\"` } : {},\n signal,\n });\n },\n async delete(resource, id, { signal } = {}) {\n await send({\n method: 'DELETE',\n path: `/resources/${encode(resource)}/${encode(id)}`,\n signal,\n });\n },\n\n async runAction(resource, action, request, { signal } = {}) {\n const response = await send({\n method: 'POST',\n path: `/resources/${encode(resource)}/actions/${encode(action)}`,\n body: request,\n signal,\n });\n const body: unknown = response.status === 204 ? {} : await response.json();\n if (response.status === 202) {\n const run = validate(ActionRun, (body as { run?: unknown }).run);\n if (!run.success) {\n throw new InvalidResponseError('The action run is invalid.', run.issues);\n }\n return { run: run.data } satisfies ActionResponse;\n }\n const result = validate(ActionResult, body);\n if (!result.success) {\n throw new InvalidResponseError('The action result is invalid.', result.issues);\n }\n return { result: result.data } satisfies ActionResponse;\n },\n async getActionRun(id, { signal } = {}) {\n return (await json<{ run: ActionRun }>({ path: `/actions/runs/${encode(id)}`, signal }))\n .run;\n },\n listActionRuns({ signal } = {}) {\n return data<ActionRun[]>({ path: '/actions/runs', signal });\n },\n async cancelActionRun(id, { signal } = {}) {\n await send({ method: 'DELETE', path: `/actions/runs/${encode(id)}`, signal });\n },\n\n presignedUploads({ endpoint } = {}) {\n return createPresignedUploads({\n baseUrl,\n ...(endpoint !== undefined && { endpoint }),\n fetch: fetcher,\n ...(options.headers !== undefined && { headers: options.headers }),\n // The default of `fetch`, as the adapter's other requests\n credentials: options.credentials ?? 'same-origin',\n createError: (problem, response) =>\n options.createError?.(problem, response) ?? createApiError(problem),\n });\n },\n getSession({ signal } = {}) {\n return json<Session>({ path: '/auth/session', signal });\n },\n login(credentials, { signal } = {}) {\n return json<Session>({\n method: 'POST',\n path: '/auth/login',\n body: credentials,\n signal,\n });\n },\n async logout({ signal } = {}) {\n await send({ method: 'POST', path: '/auth/logout', signal });\n },\n\n search(q, { signal } = {}) {\n return json<SearchResponse>({\n path: '/search',\n query: new URLSearchParams({ q }),\n signal,\n });\n },\n getWidgetData(widget, { signal } = {}) {\n return json<unknown>({ path: `/dashboard/widgets/${encode(widget)}`, signal });\n },\n };\n}\n"],"mappings":";;;;;AA0CA,IAAM,SAAS,qBAAqB;AAEpC,SAAS,OAAO,OAA+B;CAC3C,OAAO,OAAO,SAAS,eAAe,iBAAiB;AAC3D;AAEA,SAAS,QAAQ,OAAyB;CACtC,IAAI,OAAO,KAAK,GACZ,OAAO;CAEX,IAAI,MAAM,QAAQ,KAAK,GACnB,OAAO,MAAM,KAAK,OAAO;CAE7B,IAAI,UAAU,QAAQ,OAAO,UAAU,UACnC,OAAO,OAAO,OAAO,KAAK,CAAC,CAAC,KAAK,OAAO;CAE5C,OAAO;AACX;;AAGA,SAAS,eAAe,MAAgB,KAAa,OAAgB;CACjE,IAAI,UAAU,KAAA,GACV;CAEJ,IAAI,OAAO,KAAK,GACZ,KAAK,OAAO,KAAK,KAAK;MACnB,IAAI,MAAM,QAAQ,KAAK,GAC1B,MAAM,SAAS,SAAS,eAAe,MAAM,GAAG,IAAG,KAAM,IAAI,CAAC;MAC3D,IAAI,UAAU,QAAQ,OAAO,UAAU,UAC1C,OAAO,QAAQ,KAAK,CAAC,CAAC,SAAS,CAAC,MAAM,UAClC,eAAe,MAAM,QAAQ,KAAK,OAAO,GAAG,IAAG,GAAI,KAAI,IAAK,IAAI,CACpE;MACG,IAAI,OAAO,UAAU,WACxB,KAAK,OAAO,KAAK,QAAQ,MAAM,GAAG;MAElC,KAAK,OAAO,KAAK,UAAU,OAAO,KAAK,OAAO,KAAK,CAAC;AAE5D;AAEA,SAAS,OAAO,MAAsB;CAClC,OAAO,mBAAmB,IAAI;AAClC;;;;;;AAOA,SAAgB,kBAAkB,SAA0C;CACxE,MAAM,UAAU,QAAQ,QAAQ,QAAQ,QAAQ,EAAE;CAClD,MAAM,UAAU,QAAQ,WAAW,OAAO,SAAS,WAAW,MAAM,OAAO,IAAI;CAE/E,MAAM,OAAO,OAAO,EAChB,SAAS,OACT,MACA,OACA,MACA,SAAS,gBACT,aAC8B;EAC9B,MAAM,UAAU,IAAI,QAChB,OAAO,QAAQ,YAAY,aAAa,MAAM,QAAQ,QAAQ,IAAI,QAAQ,OAC9E;EACA,QAAQ,IAAI,UAAU,MAAM;EAC5B,IAAI,QAAQ,cAAc,CAAC,CAAC,SAAS,OAAO,SAAS,QAAQ,IAAI,MAAM,KAAK,CAAC;EAE7E,IAAI;EACJ,IAAI,SAAS,KAAA,GAAW;GACpB,IAAI,QAAQ,IAAI,GAAG;IACf,MAAM,OAAO,IAAI,SAAS;IAC1B,eAAe,MAAM,IAAI,IAAI;IAC7B,UAAU;GACd,OAAO;IACH,QAAQ,IAAI,gBAAgB,kBAAkB;IAC9C,UAAU,KAAK,UAAU,IAAI;GACjC;EACJ;EAEA,MAAM,SAAS,OAAO,SAAS,KAAK;EACpC,MAAM,OAAoB;GAAE;GAAQ;EAAQ;EAC5C,IAAI,YAAY,KAAA,GACZ,KAAK,OAAO;EAEhB,IAAI,QAAQ,gBAAgB,KAAA,GACxB,KAAK,cAAc,QAAQ;EAE/B,IAAI,WAAW,KAAA,GACX,KAAK,SAAS;EAElB,MAAM,WAAW,MAAM,QACnB,GAAG,UAAU,OAAO,WAAW,KAAK,IAAI,WAAW,MACnD,IACJ;EACA,IAAI,CAAC,SAAS,IAAI;GACd,MAAM,UAAU,MAAM,YAAY,QAAQ;GAC1C,MAAM,QAAQ,cAAc,SAAS,QAAQ,KAAK,eAAe,OAAO;EAC5E;EACA,OAAO;CACX;CAEA,MAAM,OAAO,OAAU,YAAiC;EAEpD,OAAQ,OAAM,MADS,KAAK,OAAO,EAAA,CACZ,KAAK;CAChC;CAEA,MAAM,OAAO,OAAU,aAAkC,MAAM,KAAkB,OAAO,EAAA,CAAG;CAE3F,OAAO;EACH,MAAM,cAAc,EAAE,WAAW,CAAC,GAAG;GACjC,MAAM,OAAO,MAAM,KAAc;IAAE,MAAM;IAAe;GAAO,CAAC;GAChE,MAAM,SAAS,SAAS,YAAY,IAAI;GACxC,IAAI,CAAC,OAAO,SACR,MAAM,IAAI,qBAAqB,8BAA8B,OAAO,MAAM;GAE9E,OAAO,OAAO;EAClB;EAEA,KAAK,UAAU,QAAmB,CAAC,GAAG,EAAE,WAAW,CAAC,GAAG;GACnD,OAAO,KAAmB;IACtB,MAAM,cAAc,OAAO,QAAQ;IACnC,OAAO,eAAe,KAAK;IAC3B;GACJ,CAAC;EACL;EACA,IAAI,UAAU,IAAI,EAAE,WAAW,CAAC,GAAG;GAC/B,OAAO,KAAW;IAAE,MAAM,cAAc,OAAO,QAAQ,EAAC,GAAI,OAAO,EAAE;IAAK;GAAO,CAAC;EACtF;EACA,OAAO,UAAU,QAAQ,EAAE,WAAW,CAAC,GAAG;GACtC,OAAO,KAAW;IACd,QAAQ;IACR,MAAM,cAAc,OAAO,QAAQ;IACnC,MAAM;IACN;GACJ,CAAC;EACL;EACA,OAAO,UAAU,IAAI,QAAQ,EAAE,SAAS,WAAW,CAAC,GAAG;GACnD,OAAO,KAAW;IACd,QAAQ;IACR,MAAM,cAAc,OAAO,QAAQ,EAAC,GAAI,OAAO,EAAE;IACjD,MAAM;IACN,SACI,YAAY,KAAA,KAAa,YAAY,OAAO,EAAE,YAAY,IAAI,QAAO,GAAI,IAAI,CAAC;IAClF;GACJ,CAAC;EACL;EACA,MAAM,OAAO,UAAU,IAAI,EAAE,WAAW,CAAC,GAAG;GACxC,MAAM,KAAK;IACP,QAAQ;IACR,MAAM,cAAc,OAAO,QAAQ,EAAC,GAAI,OAAO,EAAE;IACjD;GACJ,CAAC;EACL;EAEA,MAAM,UAAU,UAAU,QAAQ,SAAS,EAAE,WAAW,CAAC,GAAG;GACxD,MAAM,WAAW,MAAM,KAAK;IACxB,QAAQ;IACR,MAAM,cAAc,OAAO,QAAQ,EAAC,WAAY,OAAO,MAAM;IAC7D,MAAM;IACN;GACJ,CAAC;GACD,MAAM,OAAgB,SAAS,WAAW,MAAM,CAAC,IAAI,MAAM,SAAS,KAAK;GACzE,IAAI,SAAS,WAAW,KAAK;IACzB,MAAM,MAAM,SAAS,WAAY,KAA2B,GAAG;IAC/D,IAAI,CAAC,IAAI,SACL,MAAM,IAAI,qBAAqB,8BAA8B,IAAI,MAAM;IAE3E,OAAO,EAAE,KAAK,IAAI,KAAK;GAC3B;GACA,MAAM,SAAS,SAAS,cAAc,IAAI;GAC1C,IAAI,CAAC,OAAO,SACR,MAAM,IAAI,qBAAqB,iCAAiC,OAAO,MAAM;GAEjF,OAAO,EAAE,QAAQ,OAAO,KAAK;EACjC;EACA,MAAM,aAAa,IAAI,EAAE,WAAW,CAAC,GAAG;GACpC,QAAQ,MAAM,KAAyB;IAAE,MAAM,iBAAiB,OAAO,EAAE;IAAK;GAAO,CAAC,EAAA,CACjF;EACT;EACA,eAAe,EAAE,WAAW,CAAC,GAAG;GAC5B,OAAO,KAAkB;IAAE,MAAM;IAAiB;GAAO,CAAC;EAC9D;EACA,MAAM,gBAAgB,IAAI,EAAE,WAAW,CAAC,GAAG;GACvC,MAAM,KAAK;IAAE,QAAQ;IAAU,MAAM,iBAAiB,OAAO,EAAE;IAAK;GAAO,CAAC;EAChF;EAEA,iBAAiB,EAAE,aAAa,CAAC,GAAG;GAChC,OAAO,uBAAuB;IAC1B;IACA,GAAI,aAAa,KAAA,KAAa,EAAE,SAAS;IACzC,OAAO;IACP,GAAI,QAAQ,YAAY,KAAA,KAAa,EAAE,SAAS,QAAQ,QAAQ;IAEhE,aAAa,QAAQ,eAAe;IACpC,cAAc,SAAS,aACnB,QAAQ,cAAc,SAAS,QAAQ,KAAK,eAAe,OAAO;GAC1E,CAAC;EACL;EACA,WAAW,EAAE,WAAW,CAAC,GAAG;GACxB,OAAO,KAAc;IAAE,MAAM;IAAiB;GAAO,CAAC;EAC1D;EACA,MAAM,aAAa,EAAE,WAAW,CAAC,GAAG;GAChC,OAAO,KAAc;IACjB,QAAQ;IACR,MAAM;IACN,MAAM;IACN;GACJ,CAAC;EACL;EACA,MAAM,OAAO,EAAE,WAAW,CAAC,GAAG;GAC1B,MAAM,KAAK;IAAE,QAAQ;IAAQ,MAAM;IAAgB;GAAO,CAAC;EAC/D;EAEA,OAAO,GAAG,EAAE,WAAW,CAAC,GAAG;GACvB,OAAO,KAAqB;IACxB,MAAM;IACN,OAAO,IAAI,gBAAgB,EAAE,EAAE,CAAC;IAChC;GACJ,CAAC;EACL;EACA,cAAc,QAAQ,EAAE,WAAW,CAAC,GAAG;GACnC,OAAO,KAAc;IAAE,MAAM,sBAAsB,OAAO,MAAM;IAAK;GAAO,CAAC;EACjF;CACJ;AACJ"}
1
+ {"version":3,"file":"rest.js","names":[],"sources":["../src/rest.ts"],"sourcesContent":["import {\n ActionResult,\n ActionRun,\n Definition,\n type ListQuery,\n PROBLEM_CONTENT_TYPE,\n type Problem,\n toSearchParams,\n toSearchQueryParams,\n validate,\n} from '@panneau/protocol';\nimport type {\n EmbedResponse,\n FieldCheckResponse,\n Item,\n ListResponse,\n PreviewResponse,\n SearchResponse,\n Session,\n} from '@panneau/protocol';\n\nimport type { ActionResponse, DataAdapter } from './adapter';\nimport { type ApiError, InvalidResponseError, createApiError } from './errors';\nimport { readProblem, readRetryAfter } from './problem';\nimport { createPresignedUploads } from './uploads';\n\nexport interface RestAdapterOptions {\n /** URL of the API, as `api.baseUrl` in the definition (`/panneau/api`). */\n baseUrl: string;\n /** The `fetch` function to use. Defaults to the global one. */\n fetch?: (input: string, init: RequestInit) => Promise<Response>;\n /** Headers added to every request, or a function that returns them for each request. */\n headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);\n /** Whether requests send cookies (`fetch`'s `credentials`). */\n credentials?: RequestCredentials;\n /**\n * Creates the error of a failed response. Return `undefined` to use the default mapping from\n * the status (`createApiError()`). The Laravel preset uses it for `419`.\n */\n createError?: (problem: Problem, response: Response) => ApiError | undefined;\n}\n\ninterface Request {\n method?: string;\n path: string;\n query?: URLSearchParams;\n body?: unknown;\n headers?: HeadersInit;\n signal?: AbortSignal | undefined;\n}\n\nconst ACCEPT = `application/json, ${PROBLEM_CONTENT_TYPE}`;\n\nfunction isBlob(value: unknown): value is Blob {\n return typeof Blob !== 'undefined' && value instanceof Blob;\n}\n\nfunction hasBlob(value: unknown): boolean {\n if (isBlob(value)) {\n return true;\n }\n if (Array.isArray(value)) {\n return value.some(hasBlob);\n }\n if (value !== null && typeof value === 'object') {\n return Object.values(value).some(hasBlob);\n }\n return false;\n}\n\n/** Appends a value to form data with bracket keys: `values[tags][]`, `selection[ids][]`. */\nfunction appendFormData(data: FormData, key: string, value: unknown) {\n if (value === undefined) {\n return;\n }\n if (isBlob(value)) {\n data.append(key, value);\n } else if (Array.isArray(value)) {\n value.forEach((item) => appendFormData(data, `${key}[]`, item));\n } else if (value !== null && typeof value === 'object') {\n Object.entries(value).forEach(([name, item]) =>\n appendFormData(data, key === '' ? name : `${key}[${name}]`, item),\n );\n } else if (typeof value === 'boolean') {\n data.append(key, value ? '1' : '0');\n } else {\n data.append(key, value === null ? '' : String(value));\n }\n}\n\nfunction encode(path: string): string {\n return encodeURIComponent(path);\n}\n\n/**\n * A `DataAdapter` over the HTTP API of the protocol. Errors (`application/problem+json`) reject\n * with typed errors, updates send `If-Match` from the item version, and list queries use the\n * protocol's query string format.\n */\nexport function createRestAdapter(options: RestAdapterOptions): DataAdapter {\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n const fetcher = options.fetch ?? ((input, init) => globalThis.fetch(input, init));\n\n const send = async ({\n method = 'GET',\n path,\n query,\n body,\n headers: requestHeaders,\n signal,\n }: Request): Promise<Response> => {\n const headers = new Headers(\n typeof options.headers === 'function' ? await options.headers() : options.headers,\n );\n headers.set('Accept', ACCEPT);\n new Headers(requestHeaders).forEach((value, name) => headers.set(name, value));\n\n let payload: BodyInit | undefined;\n if (body !== undefined) {\n if (hasBlob(body)) {\n const data = new FormData();\n appendFormData(data, '', body);\n payload = data;\n } else {\n headers.set('Content-Type', 'application/json');\n payload = JSON.stringify(body);\n }\n }\n\n const search = query?.toString() ?? '';\n const init: RequestInit = { method, headers };\n if (payload !== undefined) {\n init.body = payload;\n }\n if (options.credentials !== undefined) {\n init.credentials = options.credentials;\n }\n if (signal !== undefined) {\n init.signal = signal;\n }\n const response = await fetcher(\n `${baseUrl}${path}${search !== '' ? `?${search}` : ''}`,\n init,\n );\n if (!response.ok) {\n const problem = await readProblem(response);\n throw (\n options.createError?.(problem, response) ??\n createApiError(problem, { retryAfter: readRetryAfter(response) })\n );\n }\n return response;\n };\n\n const json = async <T>(request: Request): Promise<T> => {\n const response = await send(request);\n return (await response.json()) as T;\n };\n\n const data = async <T>(request: Request): Promise<T> => (await json<{ data: T }>(request)).data;\n\n return {\n async getDefinition({ signal } = {}) {\n const body = await json<unknown>({ path: '/definition', signal });\n const result = validate(Definition, body);\n if (!result.success) {\n throw new InvalidResponseError('The definition is invalid.', result.issues);\n }\n return result.data;\n },\n\n list(resource, query: ListQuery = {}, { signal } = {}) {\n return json<ListResponse>({\n path: `/resources/${encode(resource)}`,\n query: toSearchParams(query),\n signal,\n });\n },\n get(resource, id, { signal } = {}) {\n return data<Item>({ path: `/resources/${encode(resource)}/${encode(id)}`, signal });\n },\n create(resource, values, { signal } = {}) {\n return data<Item>({\n method: 'POST',\n path: `/resources/${encode(resource)}`,\n body: values,\n signal,\n });\n },\n update(resource, id, values, { version, signal } = {}) {\n return data<Item>({\n method: 'PATCH',\n path: `/resources/${encode(resource)}/${encode(id)}`,\n body: values,\n headers:\n version !== undefined && version !== null ? { 'If-Match': `\"${version}\"` } : {},\n signal,\n });\n },\n async delete(resource, id, { signal } = {}) {\n await send({\n method: 'DELETE',\n path: `/resources/${encode(resource)}/${encode(id)}`,\n signal,\n });\n },\n\n async runAction(resource, action, request, { signal } = {}) {\n const response = await send({\n method: 'POST',\n path: `/resources/${encode(resource)}/actions/${encode(action)}`,\n body: request,\n signal,\n });\n const body: unknown = response.status === 204 ? {} : await response.json();\n if (response.status === 202) {\n const run = validate(ActionRun, (body as { run?: unknown }).run);\n if (!run.success) {\n throw new InvalidResponseError('The action run is invalid.', run.issues);\n }\n return { run: run.data } satisfies ActionResponse;\n }\n const result = validate(ActionResult, body);\n if (!result.success) {\n throw new InvalidResponseError('The action result is invalid.', result.issues);\n }\n return { result: result.data } satisfies ActionResponse;\n },\n async getActionRun(id, { signal } = {}) {\n return (await json<{ run: ActionRun }>({ path: `/actions/runs/${encode(id)}`, signal }))\n .run;\n },\n listActionRuns({ signal } = {}) {\n return data<ActionRun[]>({ path: '/actions/runs', signal });\n },\n async cancelActionRun(id, { signal } = {}) {\n await send({ method: 'DELETE', path: `/actions/runs/${encode(id)}`, signal });\n },\n\n presignedUploads({ endpoint } = {}) {\n return createPresignedUploads({\n baseUrl,\n ...(endpoint !== undefined && { endpoint }),\n fetch: fetcher,\n ...(options.headers !== undefined && { headers: options.headers }),\n // The default of `fetch`, as the adapter's other requests\n credentials: options.credentials ?? 'same-origin',\n createError: (problem, response) =>\n options.createError?.(problem, response) ??\n createApiError(problem, { retryAfter: readRetryAfter(response) }),\n });\n },\n getSession({ signal } = {}) {\n return json<Session>({ path: '/auth/session', signal });\n },\n login(credentials, { signal } = {}) {\n return json<Session>({\n method: 'POST',\n path: '/auth/login',\n body: credentials,\n signal,\n });\n },\n async logout({ signal } = {}) {\n await send({ method: 'POST', path: '/auth/logout', signal });\n },\n\n previewItem(resource, id, values, { signal } = {}) {\n return json<PreviewResponse>({\n method: 'POST',\n path: `/resources/${encode(resource)}/${encode(id)}/preview`,\n body: values,\n signal,\n });\n },\n search(q, { signal, limit, resources } = {}) {\n return json<SearchResponse>({\n path: '/search',\n query: toSearchQueryParams({\n q,\n ...(limit !== undefined && { limit }),\n ...(resources !== undefined && { resources }),\n }),\n signal,\n });\n },\n checkField(resource, field, request, { signal } = {}) {\n return json<FieldCheckResponse>({\n method: 'POST',\n path: `/resources/${encode(resource)}/fields/${encode(field)}/check`,\n body: request,\n signal,\n });\n },\n async resolveEmbed(url, { signal } = {}) {\n const response = await json<EmbedResponse>({\n path: '/embeds',\n query: new URLSearchParams({ url }),\n signal,\n });\n return response.data;\n },\n getWidgetData(widget, { signal } = {}) {\n return json<unknown>({ path: `/dashboard/widgets/${encode(widget)}`, signal });\n },\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@panneau/data",
3
- "version": "5.0.0-alpha.4",
3
+ "version": "5.0.0-alpha.6",
4
4
  "description": "The data layer of Panneau: the DataAdapter interface, the REST adapter of the protocol, its Laravel preset and TanStack Query hooks.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -49,7 +49,7 @@
49
49
  "react": "^19.2.0"
50
50
  },
51
51
  "dependencies": {
52
- "@panneau/protocol": "5.0.0-alpha.4"
52
+ "@panneau/protocol": "5.0.0-alpha.6"
53
53
  },
54
54
  "peerDependenciesMeta": {
55
55
  "@tanstack/react-query": {
@@ -60,6 +60,6 @@
60
60
  }
61
61
  },
62
62
  "devDependencies": {
63
- "@panneau/core": "5.0.0-alpha.4"
63
+ "@panneau/core": "5.0.0-alpha.6"
64
64
  }
65
65
  }