@panneau/data 5.0.0-alpha.3 → 5.0.0-alpha.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
- export { DataAdapterProvider, type DataAdapterProviderProps, useDataAdapter } 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';
1
+ export { DataAdapterProvider, type DataAdapterProviderProps, useDataAdapter, useOptionalDataAdapter, } from './context';
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,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC/F,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
- import { DataAdapterProvider, useDataAdapter } from "./context.js";
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, 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;AAU3B,OAAO,KAAK,EAAkB,WAAW,EAAU,MAAM,WAAW,CAAC;AACrE,OAAO,EAAE,KAAK,QAAQ,EAAwC,MAAM,UAAU,CAAC;AAG/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,CAiM1E"}
1
+ {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../src/rest.ts"],"names":[],"mappings":"AAAA,OAAO,EAMH,KAAK,OAAO,EAIf,MAAM,mBAAmB,CAAC;AAU3B,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,CAuM1E"}
package/dist/rest.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { InvalidResponseError, createApiError } from "./errors.js";
2
- import { readProblem } from "./problem.js";
3
- import { ActionResult, ActionRun, Definition, PROBLEM_CONTENT_TYPE, toSearchParams, validate } from "@panneau/protocol";
2
+ import { readProblem, readRetryAfter } from "./problem.js";
3
+ import { createPresignedUploads } from "./uploads/index.js";
4
+ import { ActionResult, ActionRun, Definition, PROBLEM_CONTENT_TYPE, toSearchParams, toSearchQueryParams, validate } from "@panneau/protocol";
4
5
  //#region src/rest.ts
5
6
  var ACCEPT = `application/json, ${PROBLEM_CONTENT_TYPE}`;
6
7
  function isBlob(value) {
@@ -58,7 +59,7 @@ function createRestAdapter(options) {
58
59
  const response = await fetcher(`${baseUrl}${path}${search !== "" ? `?${search}` : ""}`, init);
59
60
  if (!response.ok) {
60
61
  const problem = await readProblem(response);
61
- throw options.createError?.(problem, response) ?? createApiError(problem);
62
+ throw options.createError?.(problem, response) ?? createApiError(problem, { retryAfter: readRetryAfter(response) });
62
63
  }
63
64
  return response;
64
65
  };
@@ -149,43 +150,14 @@ function createRestAdapter(options) {
149
150
  signal
150
151
  });
151
152
  },
152
- listMedia(query = {}, { signal } = {}) {
153
- return json({
154
- path: "/media",
155
- query: toSearchParams(query),
156
- signal
157
- });
158
- },
159
- getMedia(id, { signal } = {}) {
160
- return data({
161
- path: `/media/${encode(id)}`,
162
- signal
163
- });
164
- },
165
- uploadMedia(file, values = {}, { signal } = {}) {
166
- return data({
167
- method: "POST",
168
- path: "/media",
169
- body: {
170
- ...values,
171
- file
172
- },
173
- signal
174
- });
175
- },
176
- updateMedia(id, values, { signal } = {}) {
177
- return data({
178
- method: "PATCH",
179
- path: `/media/${encode(id)}`,
180
- body: values,
181
- signal
182
- });
183
- },
184
- async deleteMedia(id, { signal } = {}) {
185
- await send({
186
- method: "DELETE",
187
- path: `/media/${encode(id)}`,
188
- signal
153
+ presignedUploads({ endpoint } = {}) {
154
+ return createPresignedUploads({
155
+ baseUrl,
156
+ ...endpoint !== void 0 && { endpoint },
157
+ fetch: fetcher,
158
+ ...options.headers !== void 0 && { headers: options.headers },
159
+ credentials: options.credentials ?? "same-origin",
160
+ createError: (problem, response) => options.createError?.(problem, response) ?? createApiError(problem, { retryAfter: readRetryAfter(response) })
189
161
  });
190
162
  },
191
163
  getSession({ signal } = {}) {
@@ -209,10 +181,30 @@ function createRestAdapter(options) {
209
181
  signal
210
182
  });
211
183
  },
212
- 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 } = {}) {
213
193
  return json({
214
194
  path: "/search",
215
- query: new URLSearchParams({ q }),
195
+ query: toSearchQueryParams({
196
+ q,
197
+ ...limit !== void 0 && { limit },
198
+ ...resources !== void 0 && { resources }
199
+ }),
200
+ signal
201
+ });
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,
216
208
  signal
217
209
  });
218
210
  },
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 {\n Item,\n ListResponse,\n Media,\n MediaListResponse,\n SearchResponse,\n Session,\n} from '@panneau/protocol';\n\nimport type { ActionResponse, DataAdapter, Values } from './adapter';\nimport { type ApiError, InvalidResponseError, createApiError } from './errors';\nimport { readProblem } from './problem';\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 listMedia(query: ListQuery = {}, { signal } = {}) {\n return json<MediaListResponse>({\n path: '/media',\n query: toSearchParams(query),\n signal,\n });\n },\n getMedia(id, { signal } = {}) {\n return data<Media>({ path: `/media/${encode(id)}`, signal });\n },\n uploadMedia(file, values: Values = {}, { signal } = {}) {\n return data<Media>({\n method: 'POST',\n path: '/media',\n body: { ...values, file },\n signal,\n });\n },\n updateMedia(id, values, { signal } = {}) {\n return data<Media>({\n method: 'PATCH',\n path: `/media/${encode(id)}`,\n body: values,\n signal,\n });\n },\n async deleteMedia(id, { signal } = {}) {\n await send({ method: 'DELETE', path: `/media/${encode(id)}`, signal });\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":"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"}
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 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 getWidgetData(widget, { signal } = {}) {\n return json<unknown>({ path: `/dashboard/widgets/${encode(widget)}`, signal });\n },\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import type { Problem, Upload, UploadSignRequest } from '@panneau/protocol';
2
+ import { type ApiError } from '../errors';
3
+ /** A request to sign, as `signRequest` of `@uppy/aws-s3` 6 gives it. */
4
+ export type PresignableRequest = UploadSignRequest;
5
+ /** A signed request, as `signRequest` of `@uppy/aws-s3` 6 takes it. */
6
+ export interface PresignedResponse {
7
+ url: string;
8
+ /** The key the backend chose for a new upload. */
9
+ key?: string;
10
+ /** Headers the URL was signed with, which the browser sends. */
11
+ headers?: Record<string, string>;
12
+ }
13
+ export interface PresignedUploadsOptions {
14
+ /** URL of the API, as `api.baseUrl` in the definition: the routes are under `/uploads`. */
15
+ baseUrl?: string;
16
+ /** The `endpoint` of the definition's `uploads`, when the routes are elsewhere. */
17
+ endpoint?: string;
18
+ /** The `fetch` function to use. Defaults to the global one. */
19
+ fetch?: (input: string, init: RequestInit) => Promise<Response>;
20
+ /**
21
+ * Headers of every request, or a function that gives them: `() => getLaravelHeaders()` for
22
+ * a Laravel backend with session authentication.
23
+ */
24
+ headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
25
+ /** Whether requests send cookies. Defaults to `include`. */
26
+ credentials?: RequestCredentials;
27
+ /** Creates the error of a failed response: `createLaravelError` for Laravel. */
28
+ createError?: (problem: Problem, response: Response) => ApiError;
29
+ }
30
+ export interface PresignedUploads {
31
+ /** Signs a request of Uppy's S3 client: pass it as `signRequest` to `@uppy/aws-s3`. */
32
+ signRequest: (request: PresignableRequest) => Promise<PresignedResponse>;
33
+ /**
34
+ * Tells the backend that the upload of a key is complete, once Uppy reports it
35
+ * (`upload-success`, with the key in `response.body.key`), and resolves with the upload.
36
+ * Values reference it as `{ upload: upload.id }`: a file field, an import, or
37
+ * the `file` of a media (`create('media', { file: { upload: upload.id } })`).
38
+ */
39
+ complete: (key: string, options?: {
40
+ name?: string;
41
+ }) => Promise<Upload>;
42
+ }
43
+ /**
44
+ * Direct uploads to S3-compatible storage with URLs that the backend signs, the `presigned` mode
45
+ * of the protocol (decision 0025): the browser sends files itself, in parts for large ones, then
46
+ * completes each upload, which values reference.
47
+ *
48
+ * ```ts
49
+ * const uploads = createPresignedUploads({ baseUrl: definition.api.baseUrl, headers: () => getLaravelHeaders() });
50
+ * uppy.use(AwsS3, { signRequest: uploads.signRequest });
51
+ * uppy.on('upload-success', async (file, response) => {
52
+ * const upload = await uploads.complete(response.body.key, { name: file.name });
53
+ * const media = await adapter.create('media', { file: { upload: upload.id } });
54
+ * });
55
+ * ```
56
+ */
57
+ export declare function createPresignedUploads({ baseUrl, endpoint, fetch: fetcher, headers, credentials, createError, }: PresignedUploadsOptions): PresignedUploads;
58
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/uploads/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAsB,MAAM,mBAAmB,CAAC;AAEhG,OAAO,EAAE,KAAK,QAAQ,EAAwC,MAAM,WAAW,CAAC;AAGhF,wEAAwE;AACxE,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC;AAEnD,uEAAuE;AACvE,MAAM,WAAW,iBAAiB;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,uBAAuB;IACpC,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChE;;;OAGG;IACH,OAAO,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;IACnE,4DAA4D;IAC5D,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,gFAAgF;IAChF,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,KAAK,QAAQ,CAAC;CACpE;AAED,MAAM,WAAW,gBAAgB;IAC7B,uFAAuF;IACvF,WAAW,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACzE;;;;;OAKG;IACH,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,EACnC,OAAO,EACP,QAAQ,EACR,KAAK,EAAE,OAAwD,EAC/D,OAAO,EACP,WAAuB,EACvB,WAAkD,GACrD,EAAE,uBAAuB,GAAG,gBAAgB,CAwD5C"}
@@ -0,0 +1,61 @@
1
+ import { InvalidResponseError, createApiError } from "../errors.js";
2
+ import { readProblem } from "../problem.js";
3
+ //#region src/uploads/index.ts
4
+ /**
5
+ * Direct uploads to S3-compatible storage with URLs that the backend signs, the `presigned` mode
6
+ * of the protocol (decision 0025): the browser sends files itself, in parts for large ones, then
7
+ * completes each upload, which values reference.
8
+ *
9
+ * ```ts
10
+ * const uploads = createPresignedUploads({ baseUrl: definition.api.baseUrl, headers: () => getLaravelHeaders() });
11
+ * uppy.use(AwsS3, { signRequest: uploads.signRequest });
12
+ * uppy.on('upload-success', async (file, response) => {
13
+ * const upload = await uploads.complete(response.body.key, { name: file.name });
14
+ * const media = await adapter.create('media', { file: { upload: upload.id } });
15
+ * });
16
+ * ```
17
+ */
18
+ function createPresignedUploads({ baseUrl, endpoint, fetch: fetcher = (input, init) => globalThis.fetch(input, init), headers, credentials = "include", createError = (problem) => createApiError(problem) }) {
19
+ const base = (endpoint ?? `${(baseUrl ?? "").replace(/\/+$/, "")}/uploads`).replace(/\/+$/, "");
20
+ const uploads = /* @__PURE__ */ new Map();
21
+ const post = async (path, body) => {
22
+ const requestHeaders = new Headers(typeof headers === "function" ? await headers() : headers);
23
+ requestHeaders.set("Accept", "application/json");
24
+ const init = {
25
+ method: "POST",
26
+ headers: requestHeaders,
27
+ credentials
28
+ };
29
+ if (body !== void 0) {
30
+ requestHeaders.set("Content-Type", "application/json");
31
+ init.body = JSON.stringify(body);
32
+ }
33
+ const response = await fetcher(`${base}${path}`, init);
34
+ if (!response.ok) throw createError(await readProblem(response), response);
35
+ return response.json();
36
+ };
37
+ return {
38
+ async signRequest(request) {
39
+ const body = await post("/sign", request);
40
+ if (typeof body?.url !== "string") throw new InvalidResponseError("The signed request has no URL.", []);
41
+ const key = body.key ?? request.key;
42
+ if (typeof body.upload === "string") uploads.set(key, body.upload);
43
+ return {
44
+ url: body.url,
45
+ ...body.key !== void 0 && { key: body.key },
46
+ ...body.headers !== void 0 && { headers: body.headers }
47
+ };
48
+ },
49
+ async complete(key, { name } = {}) {
50
+ const upload = uploads.get(key) ?? key.split("/").pop() ?? key;
51
+ const body = await post(`/${encodeURIComponent(upload)}/complete`, name !== void 0 ? { name } : void 0);
52
+ if (typeof body?.data?.id !== "string") throw new InvalidResponseError("The completed upload has no id.", []);
53
+ uploads.delete(key);
54
+ return body.data;
55
+ }
56
+ };
57
+ }
58
+ //#endregion
59
+ export { createPresignedUploads };
60
+
61
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/uploads/index.ts"],"sourcesContent":["import type { Problem, Upload, UploadSignRequest, UploadSignResponse } from '@panneau/protocol';\n\nimport { type ApiError, InvalidResponseError, createApiError } from '../errors';\nimport { readProblem } from '../problem';\n\n/** A request to sign, as `signRequest` of `@uppy/aws-s3` 6 gives it. */\nexport type PresignableRequest = UploadSignRequest;\n\n/** A signed request, as `signRequest` of `@uppy/aws-s3` 6 takes it. */\nexport interface PresignedResponse {\n url: string;\n /** The key the backend chose for a new upload. */\n key?: string;\n /** Headers the URL was signed with, which the browser sends. */\n headers?: Record<string, string>;\n}\n\nexport interface PresignedUploadsOptions {\n /** URL of the API, as `api.baseUrl` in the definition: the routes are under `/uploads`. */\n baseUrl?: string;\n /** The `endpoint` of the definition's `uploads`, when the routes are elsewhere. */\n endpoint?: string;\n /** The `fetch` function to use. Defaults to the global one. */\n fetch?: (input: string, init: RequestInit) => Promise<Response>;\n /**\n * Headers of every request, or a function that gives them: `() => getLaravelHeaders()` for\n * a Laravel backend with session authentication.\n */\n headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);\n /** Whether requests send cookies. Defaults to `include`. */\n credentials?: RequestCredentials;\n /** Creates the error of a failed response: `createLaravelError` for Laravel. */\n createError?: (problem: Problem, response: Response) => ApiError;\n}\n\nexport interface PresignedUploads {\n /** Signs a request of Uppy's S3 client: pass it as `signRequest` to `@uppy/aws-s3`. */\n signRequest: (request: PresignableRequest) => Promise<PresignedResponse>;\n /**\n * Tells the backend that the upload of a key is complete, once Uppy reports it\n * (`upload-success`, with the key in `response.body.key`), and resolves with the upload.\n * Values reference it as `{ upload: upload.id }`: a file field, an import, or\n * the `file` of a media (`create('media', { file: { upload: upload.id } })`).\n */\n complete: (key: string, options?: { name?: string }) => Promise<Upload>;\n}\n\n/**\n * Direct uploads to S3-compatible storage with URLs that the backend signs, the `presigned` mode\n * of the protocol (decision 0025): the browser sends files itself, in parts for large ones, then\n * completes each upload, which values reference.\n *\n * ```ts\n * const uploads = createPresignedUploads({ baseUrl: definition.api.baseUrl, headers: () => getLaravelHeaders() });\n * uppy.use(AwsS3, { signRequest: uploads.signRequest });\n * uppy.on('upload-success', async (file, response) => {\n * const upload = await uploads.complete(response.body.key, { name: file.name });\n * const media = await adapter.create('media', { file: { upload: upload.id } });\n * });\n * ```\n */\nexport function createPresignedUploads({\n baseUrl,\n endpoint,\n fetch: fetcher = (input, init) => globalThis.fetch(input, init),\n headers,\n credentials = 'include',\n createError = (problem) => createApiError(problem),\n}: PresignedUploadsOptions): PresignedUploads {\n const base = (endpoint ?? `${(baseUrl ?? '').replace(/\\/+$/, '')}/uploads`).replace(/\\/+$/, '');\n // The upload of each key, from the response that created it.\n const uploads = new Map<string, string>();\n\n const post = async (path: string, body?: unknown): Promise<unknown> => {\n const requestHeaders = new Headers(\n typeof headers === 'function' ? await headers() : headers,\n );\n requestHeaders.set('Accept', 'application/json');\n const init: RequestInit = { method: 'POST', headers: requestHeaders, credentials };\n if (body !== undefined) {\n requestHeaders.set('Content-Type', 'application/json');\n init.body = JSON.stringify(body);\n }\n const response = await fetcher(`${base}${path}`, init);\n if (!response.ok) {\n throw createError(await readProblem(response), response);\n }\n return response.json();\n };\n\n return {\n async signRequest(request) {\n const body = (await post('/sign', request)) as UploadSignResponse;\n if (typeof body?.url !== 'string') {\n throw new InvalidResponseError('The signed request has no URL.', []);\n }\n const key = body.key ?? request.key;\n if (typeof body.upload === 'string') {\n uploads.set(key, body.upload);\n }\n return {\n url: body.url,\n ...(body.key !== undefined && { key: body.key }),\n ...(body.headers !== undefined && { headers: body.headers }),\n };\n },\n\n async complete(key, { name } = {}) {\n // The backend names the upload in the sign response; without it, it is the last\n // segment of the key it chose (`uploads/upl_01H…`).\n const upload = uploads.get(key) ?? key.split('/').pop() ?? key;\n const body = (await post(\n `/${encodeURIComponent(upload)}/complete`,\n name !== undefined ? { name } : undefined,\n )) as {\n data?: Upload;\n };\n if (typeof body?.data?.id !== 'string') {\n throw new InvalidResponseError('The completed upload has no id.', []);\n }\n uploads.delete(key);\n return body.data;\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA6DA,SAAgB,uBAAuB,EACnC,SACA,UACA,OAAO,WAAW,OAAO,SAAS,WAAW,MAAM,OAAO,IAAI,GAC9D,SACA,cAAc,WACd,eAAe,YAAY,eAAe,OAAO,KACP;CAC1C,MAAM,QAAQ,YAAY,IAAI,WAAW,GAAA,CAAI,QAAQ,QAAQ,EAAE,EAAC,UAAA,CAAY,QAAQ,QAAQ,EAAE;CAE9F,MAAM,0BAAU,IAAI,IAAoB;CAExC,MAAM,OAAO,OAAO,MAAc,SAAqC;EACnE,MAAM,iBAAiB,IAAI,QACvB,OAAO,YAAY,aAAa,MAAM,QAAQ,IAAI,OACtD;EACA,eAAe,IAAI,UAAU,kBAAkB;EAC/C,MAAM,OAAoB;GAAE,QAAQ;GAAQ,SAAS;GAAgB;EAAY;EACjF,IAAI,SAAS,KAAA,GAAW;GACpB,eAAe,IAAI,gBAAgB,kBAAkB;GACrD,KAAK,OAAO,KAAK,UAAU,IAAI;EACnC;EACA,MAAM,WAAW,MAAM,QAAQ,GAAG,OAAO,QAAQ,IAAI;EACrD,IAAI,CAAC,SAAS,IACV,MAAM,YAAY,MAAM,YAAY,QAAQ,GAAG,QAAQ;EAE3D,OAAO,SAAS,KAAK;CACzB;CAEA,OAAO;EACH,MAAM,YAAY,SAAS;GACvB,MAAM,OAAQ,MAAM,KAAK,SAAS,OAAO;GACzC,IAAI,OAAO,MAAM,QAAQ,UACrB,MAAM,IAAI,qBAAqB,kCAAkC,CAAA,CAAE;GAEvE,MAAM,MAAM,KAAK,OAAO,QAAQ;GAChC,IAAI,OAAO,KAAK,WAAW,UACvB,QAAQ,IAAI,KAAK,KAAK,MAAM;GAEhC,OAAO;IACH,KAAK,KAAK;IACV,GAAI,KAAK,QAAQ,KAAA,KAAa,EAAE,KAAK,KAAK,IAAI;IAC9C,GAAI,KAAK,YAAY,KAAA,KAAa,EAAE,SAAS,KAAK,QAAQ;GAC9D;EACJ;EAEA,MAAM,SAAS,KAAK,EAAE,SAAS,CAAC,GAAG;GAG/B,MAAM,SAAS,QAAQ,IAAI,GAAG,KAAK,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,KAAK;GAC3D,MAAM,OAAQ,MAAM,KAChB,IAAI,mBAAmB,MAAM,EAAC,YAC9B,SAAS,KAAA,IAAY,EAAE,KAAK,IAAI,KAAA,CACpC;GAGA,IAAI,OAAO,MAAM,MAAM,OAAO,UAC1B,MAAM,IAAI,qBAAqB,mCAAmC,CAAA,CAAE;GAExE,QAAQ,OAAO,GAAG;GAClB,OAAO,KAAK;EAChB;CACJ;AACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@panneau/data",
3
- "version": "5.0.0-alpha.3",
3
+ "version": "5.0.0-alpha.5",
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",
@@ -26,6 +26,11 @@
26
26
  "types": "./dist/query/index.d.ts",
27
27
  "default": "./dist/query/index.js"
28
28
  },
29
+ "./uploads": {
30
+ "@panneau/source": "./src/uploads/index.ts",
31
+ "types": "./dist/uploads/index.d.ts",
32
+ "default": "./dist/uploads/index.js"
33
+ },
29
34
  "./package.json": "./package.json"
30
35
  },
31
36
  "files": [
@@ -44,7 +49,7 @@
44
49
  "react": "^19.2.0"
45
50
  },
46
51
  "dependencies": {
47
- "@panneau/protocol": "5.0.0-alpha.3"
52
+ "@panneau/protocol": "5.0.0-alpha.5"
48
53
  },
49
54
  "peerDependenciesMeta": {
50
55
  "@tanstack/react-query": {
@@ -55,6 +60,6 @@
55
60
  }
56
61
  },
57
62
  "devDependencies": {
58
- "@panneau/core": "5.0.0-alpha.3"
63
+ "@panneau/core": "5.0.0-alpha.5"
59
64
  }
60
65
  }