@panneau/protocol 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.
Files changed (66) hide show
  1. package/dist/conformance/index.d.ts.map +1 -1
  2. package/dist/conformance/index.js +121 -1
  3. package/dist/conformance/index.js.map +1 -1
  4. package/dist/constants.d.ts +2 -2
  5. package/dist/constants.d.ts.map +1 -1
  6. package/dist/constants.js +12 -2
  7. package/dist/constants.js.map +1 -1
  8. package/dist/index.js +5 -2
  9. package/dist/libraries.d.ts +16 -0
  10. package/dist/libraries.d.ts.map +1 -0
  11. package/dist/libraries.js +15 -0
  12. package/dist/libraries.js.map +1 -0
  13. package/dist/lint.d.ts +2 -2
  14. package/dist/lint.d.ts.map +1 -1
  15. package/dist/lint.js +15 -9
  16. package/dist/lint.js.map +1 -1
  17. package/dist/mock/example.d.ts.map +1 -1
  18. package/dist/mock/example.js +8 -0
  19. package/dist/mock/example.js.map +1 -1
  20. package/dist/mock/index.d.ts +15 -2
  21. package/dist/mock/index.d.ts.map +1 -1
  22. package/dist/mock/index.js +164 -13
  23. package/dist/mock/index.js.map +1 -1
  24. package/dist/openapi.d.ts.map +1 -1
  25. package/dist/openapi.js +45 -4
  26. package/dist/openapi.js.map +1 -1
  27. package/dist/rules.d.ts.map +1 -1
  28. package/dist/rules.js +26 -1
  29. package/dist/rules.js.map +1 -1
  30. package/dist/runtime/index.d.ts +3 -0
  31. package/dist/runtime/index.d.ts.map +1 -1
  32. package/dist/runtime/index.js +4 -1
  33. package/dist/schemas/action.d.ts +18 -1
  34. package/dist/schemas/action.d.ts.map +1 -1
  35. package/dist/schemas/api.d.ts +141 -0
  36. package/dist/schemas/api.d.ts.map +1 -1
  37. package/dist/schemas/api.js +54 -9
  38. package/dist/schemas/api.js.map +1 -1
  39. package/dist/schemas/definition.d.ts +40 -2
  40. package/dist/schemas/definition.d.ts.map +1 -1
  41. package/dist/schemas/definition.js +2 -1
  42. package/dist/schemas/definition.js.map +1 -1
  43. package/dist/schemas/field.d.ts +26 -1
  44. package/dist/schemas/field.d.ts.map +1 -1
  45. package/dist/schemas/field.js +27 -5
  46. package/dist/schemas/field.js.map +1 -1
  47. package/dist/schemas/index.d.ts +259 -6
  48. package/dist/schemas/index.d.ts.map +1 -1
  49. package/dist/schemas/index.js +8 -3
  50. package/dist/schemas/index.js.map +1 -1
  51. package/dist/schemas/resource.d.ts +38 -2
  52. package/dist/schemas/resource.d.ts.map +1 -1
  53. package/dist/schemas/resource.js +3 -1
  54. package/dist/schemas/resource.js.map +1 -1
  55. package/dist/search.d.ts +51 -0
  56. package/dist/search.d.ts.map +1 -0
  57. package/dist/search.js +53 -0
  58. package/dist/search.js.map +1 -0
  59. package/dist/transforms.d.ts +17 -0
  60. package/dist/transforms.d.ts.map +1 -0
  61. package/dist/transforms.js +34 -0
  62. package/dist/transforms.js.map +1 -0
  63. package/package.json +1 -1
  64. package/schema/boot.schema.json +82 -2
  65. package/schema/definition.schema.json +82 -2
  66. package/schema/openapi.json +507 -14
@@ -95,6 +95,14 @@ function createExampleDefinition() {
95
95
  rule: "accept",
96
96
  value: ["application/pdf"]
97
97
  }]
98
+ },
99
+ {
100
+ name: "slug",
101
+ type: "slug",
102
+ from: "title",
103
+ localized: true,
104
+ check: true,
105
+ settings: { prefix: "https://example.com/" }
98
106
  }
99
107
  ],
100
108
  form: { layout: [{
@@ -1 +1 @@
1
- {"version":3,"file":"example.js","names":[],"sources":["../../src/mock/example.ts"],"sourcesContent":["import type { DefinitionInput } from '../schemas';\n\n/**\n * A small but complete definition: pages with localized titles, a status, a publication date shown\n * for published pages, an author, an attachment and typed blocks, in tabs and a sidebar, plus\n * authors with a photo, and a media library. For tests\n * and stories, with `createMockBackend()`.\n */\nexport function createExampleDefinition(): DefinitionInput {\n return {\n protocol: 1,\n name: 'Test site',\n locale: 'fr',\n locales: ['fr', 'en'],\n api: { baseUrl: '/panneau/api' },\n resources: [\n {\n id: 'pages',\n label: { singular: 'page', plural: 'pages' },\n itemTitle: 'title',\n fields: [\n {\n name: 'title',\n type: 'text',\n label: 'Titre',\n localized: true,\n rules: [{ rule: 'required' }, { rule: 'max', value: 120 }],\n },\n {\n name: 'status',\n type: 'select',\n options: [\n { value: 'draft', label: 'Brouillon' },\n { value: 'published', label: 'Publiée' },\n ],\n rules: [\n { rule: 'required' },\n { rule: 'in', value: ['draft', 'published'] },\n ],\n },\n {\n name: 'published_at',\n type: 'datetime',\n visibleWhen: { field: 'status', equals: 'published' },\n rules: [{ rule: 'required' }],\n },\n { name: 'author', type: 'relation', resource: 'authors' },\n {\n name: 'blocks',\n type: 'repeater',\n types: [\n {\n id: 'text',\n label: 'Texte',\n fields: [\n {\n name: 'body',\n type: 'richtext',\n rules: [{ rule: 'required' }],\n },\n ],\n },\n {\n id: 'image',\n label: 'Image',\n fields: [\n {\n name: 'image',\n type: 'media',\n rules: [\n { rule: 'accept', value: ['image/*'] },\n { rule: 'maxSize', value: 1000 },\n ],\n },\n ],\n },\n ],\n },\n {\n name: 'attachment',\n type: 'file',\n label: 'Pièce jointe',\n rules: [{ rule: 'accept', value: ['application/pdf'] }],\n },\n ],\n form: {\n layout: [\n {\n type: 'tabs',\n children: [\n {\n type: 'tab',\n label: 'Contenu',\n children: [\n {\n type: 'row',\n children: [\n { type: 'field', name: 'title', width: 8 },\n { type: 'field', name: 'status', width: 4 },\n ],\n },\n { type: 'field', name: 'blocks' },\n ],\n },\n ],\n },\n {\n type: 'sidebar',\n children: [\n { type: 'field', name: 'published_at' },\n { type: 'field', name: 'attachment' },\n ],\n },\n ],\n },\n list: {\n columns: [\n { field: 'title', sortable: true },\n { field: 'status' },\n { id: 'updated' },\n ],\n filters: [\n { id: 'status', type: 'select' },\n { id: 'published_at', type: 'date-range', label: 'Publication' },\n ],\n sorts: ['title', 'published_at'],\n defaultSort: 'title',\n perPage: 20,\n search: true,\n },\n actions: [\n {\n id: 'publish',\n label: 'Publier',\n scope: ['item', 'selection'],\n placement: ['row', 'toolbar'],\n confirm: { message: 'Publier {count} pages ?' },\n fields: [{ name: 'notify', type: 'boolean' }],\n dryRun: true,\n },\n {\n id: 'edit',\n label: 'Modifier',\n kind: 'link',\n url: '/pages/{id}/edit',\n scope: ['item'],\n },\n ],\n abilities: { viewAny: true, create: true },\n },\n {\n id: 'authors',\n label: { singular: 'auteur', plural: 'auteurs' },\n fields: [\n { name: 'name', type: 'text', rules: [{ rule: 'required' }] },\n { name: 'photo', type: 'media', accept: ['image/*'] },\n ],\n },\n {\n id: 'media',\n kind: 'media',\n label: { singular: 'média', plural: 'médias' },\n itemTitle: 'name',\n fields: [\n {\n name: 'file',\n type: 'file',\n accept: ['image/*', 'video/*', 'application/pdf'],\n rules: [{ rule: 'required' }],\n },\n { name: 'name', type: 'text' },\n { name: 'alt', type: 'text', localized: true },\n ],\n list: {\n filters: [\n {\n id: 'type',\n type: 'select',\n options: [\n { value: 'image', label: 'Images' },\n { value: 'video', label: 'Vidéos' },\n { value: 'document', label: 'Documents' },\n ],\n },\n ],\n search: true,\n },\n abilities: { viewAny: true, create: true },\n },\n ],\n menus: { main: [{ type: 'resources' }, { type: 'link', label: 'Site', url: '/' }] },\n auth: { mode: 'panneau', password: true, providers: [{ id: 'google', label: 'Google' }] },\n uploads: { mode: 'presigned' },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"example.js","names":[],"sources":["../../src/mock/example.ts"],"sourcesContent":["import type { DefinitionInput } from '../schemas';\n\n/**\n * A small but complete definition: pages with localized titles, a status, a publication date shown\n * for published pages, an author, an attachment and typed blocks, in tabs and a sidebar, plus\n * authors with a photo, and a media library. For tests\n * and stories, with `createMockBackend()`.\n */\nexport function createExampleDefinition(): DefinitionInput {\n return {\n protocol: 1,\n name: 'Test site',\n locale: 'fr',\n locales: ['fr', 'en'],\n api: { baseUrl: '/panneau/api' },\n resources: [\n {\n id: 'pages',\n label: { singular: 'page', plural: 'pages' },\n itemTitle: 'title',\n fields: [\n {\n name: 'title',\n type: 'text',\n label: 'Titre',\n localized: true,\n rules: [{ rule: 'required' }, { rule: 'max', value: 120 }],\n },\n {\n name: 'status',\n type: 'select',\n options: [\n { value: 'draft', label: 'Brouillon' },\n { value: 'published', label: 'Publiée' },\n ],\n rules: [\n { rule: 'required' },\n { rule: 'in', value: ['draft', 'published'] },\n ],\n },\n {\n name: 'published_at',\n type: 'datetime',\n visibleWhen: { field: 'status', equals: 'published' },\n rules: [{ rule: 'required' }],\n },\n { name: 'author', type: 'relation', resource: 'authors' },\n {\n name: 'blocks',\n type: 'repeater',\n types: [\n {\n id: 'text',\n label: 'Texte',\n fields: [\n {\n name: 'body',\n type: 'richtext',\n rules: [{ rule: 'required' }],\n },\n ],\n },\n {\n id: 'image',\n label: 'Image',\n fields: [\n {\n name: 'image',\n type: 'media',\n rules: [\n { rule: 'accept', value: ['image/*'] },\n { rule: 'maxSize', value: 1000 },\n ],\n },\n ],\n },\n ],\n },\n {\n name: 'attachment',\n type: 'file',\n label: 'Pièce jointe',\n rules: [{ rule: 'accept', value: ['application/pdf'] }],\n },\n {\n name: 'slug',\n type: 'slug',\n from: 'title',\n localized: true,\n check: true,\n settings: { prefix: 'https://example.com/' },\n },\n ],\n form: {\n layout: [\n {\n type: 'tabs',\n children: [\n {\n type: 'tab',\n label: 'Contenu',\n children: [\n {\n type: 'row',\n children: [\n { type: 'field', name: 'title', width: 8 },\n { type: 'field', name: 'status', width: 4 },\n ],\n },\n { type: 'field', name: 'blocks' },\n ],\n },\n ],\n },\n {\n type: 'sidebar',\n children: [\n { type: 'field', name: 'published_at' },\n { type: 'field', name: 'attachment' },\n ],\n },\n ],\n },\n list: {\n columns: [\n { field: 'title', sortable: true },\n { field: 'status' },\n { id: 'updated' },\n ],\n filters: [\n { id: 'status', type: 'select' },\n { id: 'published_at', type: 'date-range', label: 'Publication' },\n ],\n sorts: ['title', 'published_at'],\n defaultSort: 'title',\n perPage: 20,\n search: true,\n },\n actions: [\n {\n id: 'publish',\n label: 'Publier',\n scope: ['item', 'selection'],\n placement: ['row', 'toolbar'],\n confirm: { message: 'Publier {count} pages ?' },\n fields: [{ name: 'notify', type: 'boolean' }],\n dryRun: true,\n },\n {\n id: 'edit',\n label: 'Modifier',\n kind: 'link',\n url: '/pages/{id}/edit',\n scope: ['item'],\n },\n ],\n abilities: { viewAny: true, create: true },\n },\n {\n id: 'authors',\n label: { singular: 'auteur', plural: 'auteurs' },\n fields: [\n { name: 'name', type: 'text', rules: [{ rule: 'required' }] },\n { name: 'photo', type: 'media', accept: ['image/*'] },\n ],\n },\n {\n id: 'media',\n kind: 'media',\n label: { singular: 'média', plural: 'médias' },\n itemTitle: 'name',\n fields: [\n {\n name: 'file',\n type: 'file',\n accept: ['image/*', 'video/*', 'application/pdf'],\n rules: [{ rule: 'required' }],\n },\n { name: 'name', type: 'text' },\n { name: 'alt', type: 'text', localized: true },\n ],\n list: {\n filters: [\n {\n id: 'type',\n type: 'select',\n options: [\n { value: 'image', label: 'Images' },\n { value: 'video', label: 'Vidéos' },\n { value: 'document', label: 'Documents' },\n ],\n },\n ],\n search: true,\n },\n abilities: { viewAny: true, create: true },\n },\n ],\n menus: { main: [{ type: 'resources' }, { type: 'link', label: 'Site', url: '/' }] },\n auth: { mode: 'panneau', password: true, providers: [{ id: 'google', label: 'Google' }] },\n uploads: { mode: 'presigned' },\n };\n}\n"],"mappings":";;;;;;;AAQA,SAAgB,0BAA2C;CACvD,OAAO;EACH,UAAU;EACV,MAAM;EACN,QAAQ;EACR,SAAS,CAAC,MAAM,IAAI;EACpB,KAAK,EAAE,SAAS,eAAe;EAC/B,WAAW;GACP;IACI,IAAI;IACJ,OAAO;KAAE,UAAU;KAAQ,QAAQ;IAAQ;IAC3C,WAAW;IACX,QAAQ;KACJ;MACI,MAAM;MACN,MAAM;MACN,OAAO;MACP,WAAW;MACX,OAAO,CAAC,EAAE,MAAM,WAAW,GAAG;OAAE,MAAM;OAAO,OAAO;MAAI,CAAC;KAC7D;KACA;MACI,MAAM;MACN,MAAM;MACN,SAAS,CACL;OAAE,OAAO;OAAS,OAAO;MAAY,GACrC;OAAE,OAAO;OAAa,OAAO;MAAU,CAC3C;MACA,OAAO,CACH,EAAE,MAAM,WAAW,GACnB;OAAE,MAAM;OAAM,OAAO,CAAC,SAAS,WAAW;MAAE,CAChD;KACJ;KACA;MACI,MAAM;MACN,MAAM;MACN,aAAa;OAAE,OAAO;OAAU,QAAQ;MAAY;MACpD,OAAO,CAAC,EAAE,MAAM,WAAW,CAAC;KAChC;KACA;MAAE,MAAM;MAAU,MAAM;MAAY,UAAU;KAAU;KACxD;MACI,MAAM;MACN,MAAM;MACN,OAAO,CACH;OACI,IAAI;OACJ,OAAO;OACP,QAAQ,CACJ;QACI,MAAM;QACN,MAAM;QACN,OAAO,CAAC,EAAE,MAAM,WAAW,CAAC;OAChC,CACJ;MACJ,GACA;OACI,IAAI;OACJ,OAAO;OACP,QAAQ,CACJ;QACI,MAAM;QACN,MAAM;QACN,OAAO,CACH;SAAE,MAAM;SAAU,OAAO,CAAC,SAAS;QAAE,GACrC;SAAE,MAAM;SAAW,OAAO;QAAK,CACnC;OACJ,CACJ;MACJ,CACJ;KACJ;KACA;MACI,MAAM;MACN,MAAM;MACN,OAAO;MACP,OAAO,CAAC;OAAE,MAAM;OAAU,OAAO,CAAC,iBAAiB;MAAE,CAAC;KAC1D;KACA;MACI,MAAM;MACN,MAAM;MACN,MAAM;MACN,WAAW;MACX,OAAO;MACP,UAAU,EAAE,QAAQ,uBAAuB;KAC/C;IACJ;IACA,MAAM,EACF,QAAQ,CACJ;KACI,MAAM;KACN,UAAU,CACN;MACI,MAAM;MACN,OAAO;MACP,UAAU,CACN;OACI,MAAM;OACN,UAAU,CACN;QAAE,MAAM;QAAS,MAAM;QAAS,OAAO;OAAE,GACzC;QAAE,MAAM;QAAS,MAAM;QAAU,OAAO;OAAE,CAC9C;MACJ,GACA;OAAE,MAAM;OAAS,MAAM;MAAS,CACpC;KACJ,CACJ;IACJ,GACA;KACI,MAAM;KACN,UAAU,CACN;MAAE,MAAM;MAAS,MAAM;KAAe,GACtC;MAAE,MAAM;MAAS,MAAM;KAAa,CACxC;IACJ,CACJ,EACJ;IACA,MAAM;KACF,SAAS;MACL;OAAE,OAAO;OAAS,UAAU;MAAK;MACjC,EAAE,OAAO,SAAS;MAClB,EAAE,IAAI,UAAU;KACpB;KACA,SAAS,CACL;MAAE,IAAI;MAAU,MAAM;KAAS,GAC/B;MAAE,IAAI;MAAgB,MAAM;MAAc,OAAO;KAAc,CACnE;KACA,OAAO,CAAC,SAAS,cAAc;KAC/B,aAAa;KACb,SAAS;KACT,QAAQ;IACZ;IACA,SAAS,CACL;KACI,IAAI;KACJ,OAAO;KACP,OAAO,CAAC,QAAQ,WAAW;KAC3B,WAAW,CAAC,OAAO,SAAS;KAC5B,SAAS,EAAE,SAAS,0BAA0B;KAC9C,QAAQ,CAAC;MAAE,MAAM;MAAU,MAAM;KAAU,CAAC;KAC5C,QAAQ;IACZ,GACA;KACI,IAAI;KACJ,OAAO;KACP,MAAM;KACN,KAAK;KACL,OAAO,CAAC,MAAM;IAClB,CACJ;IACA,WAAW;KAAE,SAAS;KAAM,QAAQ;IAAK;GAC7C;GACA;IACI,IAAI;IACJ,OAAO;KAAE,UAAU;KAAU,QAAQ;IAAU;IAC/C,QAAQ,CACJ;KAAE,MAAM;KAAQ,MAAM;KAAQ,OAAO,CAAC,EAAE,MAAM,WAAW,CAAC;IAAE,GAC5D;KAAE,MAAM;KAAS,MAAM;KAAS,QAAQ,CAAC,SAAS;IAAE,CACxD;GACJ;GACA;IACI,IAAI;IACJ,MAAM;IACN,OAAO;KAAE,UAAU;KAAS,QAAQ;IAAS;IAC7C,WAAW;IACX,QAAQ;KACJ;MACI,MAAM;MACN,MAAM;MACN,QAAQ;OAAC;OAAW;OAAW;MAAiB;MAChD,OAAO,CAAC,EAAE,MAAM,WAAW,CAAC;KAChC;KACA;MAAE,MAAM;MAAQ,MAAM;KAAO;KAC7B;MAAE,MAAM;MAAO,MAAM;MAAQ,WAAW;KAAK;IACjD;IACA,MAAM;KACF,SAAS,CACL;MACI,IAAI;MACJ,MAAM;MACN,SAAS;OACL;QAAE,OAAO;QAAS,OAAO;OAAS;OAClC;QAAE,OAAO;QAAS,OAAO;OAAS;OAClC;QAAE,OAAO;QAAY,OAAO;OAAY;MAC5C;KACJ,CACJ;KACA,QAAQ;IACZ;IACA,WAAW;KAAE,SAAS;KAAM,QAAQ;IAAK;GAC7C;EACJ;EACA,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,YAAY,GAAG;GAAE,MAAM;GAAQ,OAAO;GAAQ,KAAK;EAAI,CAAC,EAAE;EAClF,MAAM;GAAE,MAAM;GAAW,UAAU;GAAM,WAAW,CAAC;IAAE,IAAI;IAAU,OAAO;GAAS,CAAC;EAAE;EACxF,SAAS,EAAE,MAAM,YAAY;CACjC;AACJ"}
@@ -1,11 +1,24 @@
1
1
  import type { DefinitionInput } from '../schemas';
2
- import type { Item } from '../schemas/api';
2
+ import type { EmbedValue, Item } from '../schemas/api';
3
3
  type Values = Record<string, unknown>;
4
4
  export interface MockBackendOptions {
5
5
  /** The definition served by `GET /definition`. Its `api.baseUrl` is the prefix of every route. */
6
6
  definition: DefinitionInput;
7
7
  /** Initial values of the items, per resource id. Items without an `id` get one. */
8
8
  items?: Record<string, Values[]>;
9
+ /**
10
+ * The URL that `POST /resources/{resource}/{id}/preview` returns, for the item with the posted
11
+ * values. Defaults to a `data:` URL of a plain page with the item's values.
12
+ */
13
+ previewUrl?: (context: {
14
+ resource: string;
15
+ item: Item;
16
+ }) => string;
17
+ /**
18
+ * The value of `GET /embeds?url=` for a URL, or `null` to refuse it with a `422`. Defaults to
19
+ * a video for YouTube and Vimeo URLs, and a link for other web pages.
20
+ */
21
+ embed?: (url: string) => EmbedValue | null;
9
22
  }
10
23
  export interface MockBackend {
11
24
  /** A `fetch`-compatible function that answers protocol requests. */
@@ -19,6 +32,6 @@ export interface MockBackend {
19
32
  * with the portable rules. Used to test the conformance kit, and to develop frontends without a
20
33
  * real backend.
21
34
  */
22
- export declare function createMockBackend({ definition, items }: MockBackendOptions): MockBackend;
35
+ export declare function createMockBackend(options: MockBackendOptions): MockBackend;
23
36
  export { createExampleDefinition } from './example';
24
37
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mock/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,YAAY,CAAC;AACjE,OAAO,KAAK,EAER,IAAI,EAQP,MAAM,gBAAgB,CAAC;AAIxB,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtC,MAAM,WAAW,kBAAkB;IAC/B,kGAAkG;IAClG,UAAU,EAAE,eAAe,CAAC;IAC5B,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,WAAW;IACxB,oEAAoE;IACpE,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChF,uCAAuC;IACvC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EAAE,CAAC;CACvC;AAqID;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,UAAU,EAAE,KAAU,EAAE,EAAE,kBAAkB,GAAG,WAAW,CAwa7F;AAED,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mock/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,YAAY,CAAC;AACjE,OAAO,KAAK,EAER,UAAU,EAEV,IAAI,EAUP,MAAM,gBAAgB,CAAC;AAKxB,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtC,MAAM,WAAW,kBAAkB;IAC/B,kGAAkG;IAClG,UAAU,EAAE,eAAe,CAAC;IAC5B,mFAAmF;IACnF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACjC;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,IAAI,CAAA;KAAE,KAAK,MAAM,CAAC;IACnE;;;OAGG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,UAAU,GAAG,IAAI,CAAC;CAC9C;AAED,MAAM,WAAW,WAAW;IACxB,oEAAoE;IACpE,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;IAChF,uCAAuC;IACvC,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,EAAE,CAAC;CACvC;AAuND;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAgiB1E;AAED,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC"}
@@ -1,7 +1,9 @@
1
1
  import { PROBLEM_CONTENT_TYPE } from "../version.js";
2
2
  import { isRecord } from "../values.js";
3
3
  import { checkValues } from "../rules.js";
4
+ import { getMediaLibraries } from "../libraries.js";
4
5
  import { fromSearchParams, isFilterRange, matchesFilterRange } from "../query.js";
6
+ import { readSearchQuery } from "../search.js";
5
7
  import { createExampleDefinition } from "./example.js";
6
8
  //#region src/mock/index.ts
7
9
  var RESERVED_KEYS = /* @__PURE__ */ new Set([
@@ -9,6 +11,51 @@ var RESERVED_KEYS = /* @__PURE__ */ new Set([
9
11
  "version",
10
12
  "abilities"
11
13
  ]);
14
+ function escapeHtml(text) {
15
+ return text.replace(/[&<>"']/g, (character) => `&#${character.charCodeAt(0)};`);
16
+ }
17
+ /** A plain page with the values of an item, as a `data:` URL, for iframe previews. */
18
+ function defaultPreviewUrl(item) {
19
+ const rows = Object.entries(item).filter(([key]) => !RESERVED_KEYS.has(key)).map(([key, value]) => `<dt>${escapeHtml(key)}</dt><dd>${escapeHtml(typeof value === "string" ? value : JSON.stringify(value))}</dd>`).join("");
20
+ const html = `<!doctype html><meta charset="utf-8"><title>Preview</title><body style="font-family:system-ui;margin:2rem"><h1>Item ${escapeHtml(item.id)}</h1><dl>${rows}</dl></body>`;
21
+ return `data:text/html;charset=utf-8,${encodeURIComponent(html)}`;
22
+ }
23
+ /** What the mock knows of a URL to embed: a video of YouTube or Vimeo, or a web page. */
24
+ function defaultEmbed(url) {
25
+ if (!URL.canParse(url)) return null;
26
+ const parsed = new URL(url);
27
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") return null;
28
+ const host = parsed.hostname.replace(/^www\./, "");
29
+ const youtube = host === "youtu.be" ? parsed.pathname.slice(1) : host.endsWith("youtube.com") ? parsed.searchParams.get("v") : null;
30
+ if (youtube !== null && youtube !== "") return {
31
+ url,
32
+ type: "video",
33
+ provider: "YouTube",
34
+ title: `YouTube video ${youtube}`,
35
+ thumbnailUrl: `https://i.ytimg.com/vi/${youtube}/hqdefault.jpg`,
36
+ html: `<iframe src="https://www.youtube.com/embed/${youtube}" width="560" height="315" allowfullscreen></iframe>`,
37
+ width: 560,
38
+ height: 315
39
+ };
40
+ if (host === "vimeo.com") {
41
+ const id = parsed.pathname.split("/").filter(Boolean)[0] ?? "";
42
+ return {
43
+ url,
44
+ type: "video",
45
+ provider: "Vimeo",
46
+ title: `Vimeo video ${id}`,
47
+ html: `<iframe src="https://player.vimeo.com/video/${id}" width="640" height="360" allowfullscreen></iframe>`,
48
+ width: 640,
49
+ height: 360
50
+ };
51
+ }
52
+ return {
53
+ url,
54
+ type: "link",
55
+ provider: host,
56
+ title: host
57
+ };
58
+ }
12
59
  var DEFAULT_MESSAGES = {
13
60
  required: () => "This field is required.",
14
61
  min: (failure) => `This value must be at least ${String(failure.value)}.`,
@@ -16,11 +63,16 @@ var DEFAULT_MESSAGES = {
16
63
  pattern: () => "This value has an invalid format.",
17
64
  email: () => "This value must be an email address.",
18
65
  url: () => "This value must be a URL.",
66
+ json: () => "This value must be valid JSON: an object or an array.",
19
67
  in: () => "This value is not one of the choices.",
20
68
  accept: () => "This file type is not accepted.",
21
69
  maxSize: () => "This file is too large.",
22
70
  before: () => "This date is too late.",
23
- after: () => "This date is too early."
71
+ after: () => "This date is too early.",
72
+ crop: (failure) => {
73
+ const ratios = isRecord(failure.value) ? failure.value.ratios : void 0;
74
+ return Array.isArray(ratios) && ratios.length > 0 ? `Crop this image to ${ratios.join(" or ")}.` : "Crop this image.";
75
+ }
24
76
  };
25
77
  function message(failure) {
26
78
  return failure.message ?? DEFAULT_MESSAGES[failure.rule]?.(failure) ?? "This value is invalid.";
@@ -113,12 +165,28 @@ function mediaOf(file) {
113
165
  };
114
166
  }
115
167
  /**
168
+ * A media item with its own transform (its `transform` field) in its `media`, where references
169
+ * give it to clients (decision 0031).
170
+ */
171
+ function withOwnTransform(resource, item) {
172
+ if (resource.kind !== "media" || !isRecord(item.media)) return item;
173
+ const { transform: _previous, ...media } = item.media;
174
+ return {
175
+ ...item,
176
+ media: isRecord(item.transform) ? {
177
+ ...media,
178
+ transform: item.transform
179
+ } : media
180
+ };
181
+ }
182
+ /**
116
183
  * An in-memory backend that implements the protocol's resource routes: definition, list, show,
117
184
  * create, update (with `If-Match`), delete and the built-in `delete` action. Values are checked
118
185
  * with the portable rules. Used to test the conformance kit, and to develop frontends without a
119
186
  * real backend.
120
187
  */
121
- function createMockBackend({ definition, items = {} }) {
188
+ function createMockBackend(options) {
189
+ const { definition, items = {} } = options;
122
190
  const store = /* @__PURE__ */ new Map();
123
191
  let nextId = 1;
124
192
  const abilitiesFor = (resource) => Object.fromEntries([
@@ -226,29 +294,36 @@ function createMockBackend({ definition, items = {} }) {
226
294
  };
227
295
  const missingMedia = (paths) => problem(422, "The given data was invalid.", {
228
296
  type: "urn:panneau:problem:validation",
229
- errors: Object.fromEntries(paths.map((path) => [path, ["This media does not exist."]]))
297
+ errors: Object.fromEntries(Object.entries(paths).map(([path, message]) => [path, [message]]))
230
298
  });
231
299
  const missingUploads = (paths) => problem(422, "The given data was invalid.", {
232
300
  type: "urn:panneau:problem:validation",
233
301
  errors: Object.fromEntries(paths.map((path) => [path, ["This upload does not exist."]]))
234
302
  });
235
303
  const resolveMedia = (resource, values) => {
236
- const missing = [];
304
+ const missing = {};
237
305
  const resolved = { ...values };
238
- const libraries = definition.resources.filter((candidate) => candidate.kind === "media");
239
- resource.fields.filter((field) => field.type === "media" && field.name in values).forEach((field) => {
240
- const library = libraries.find((candidate) => field.resource !== void 0 ? candidate.id === field.resource : true);
306
+ resource.fields.filter((field) => (field.type === "media" || field.type === "image") && field.name in values).forEach((field) => {
307
+ const allowed = getMediaLibraries(definition, field);
241
308
  const toReference = (value, path) => {
242
309
  const id = isRecord(value) ? value.id : value;
243
310
  if (typeof id !== "string" && typeof id !== "number") return value;
311
+ const named = isRecord(value) && typeof value.resource === "string" ? value.resource : void 0;
312
+ if (named === void 0 && allowed.length > 1) {
313
+ missing[path] = "This field allows several libraries: name the library.";
314
+ return value;
315
+ }
316
+ const libraryId = named ?? allowed[0];
317
+ const library = definition.resources.find((candidate) => candidate.id === libraryId && allowed.includes(candidate.id));
244
318
  const media = (store.get(library?.id ?? "") ?? []).find((candidate) => candidate.id === String(id));
245
319
  if (media === void 0 || library === void 0) {
246
- missing.push(path);
320
+ missing[path] = "This media does not exist.";
247
321
  return value;
248
322
  }
249
323
  const title = media[library.itemTitle ?? "name"];
250
324
  return {
251
325
  id: media.id,
326
+ ...allowed.length > 1 ? { resource: library.id } : {},
252
327
  ...typeof title === "string" ? { label: title } : {},
253
328
  ...isRecord(media.media) ? { media: media.media } : {},
254
329
  ...isRecord(value) && isRecord(value.transform) ? { transform: value.transform } : {}
@@ -271,7 +346,7 @@ function createMockBackend({ definition, items = {} }) {
271
346
  problem: missingUploads(files.missing)
272
347
  };
273
348
  const media = resolveMedia(resource, files.values);
274
- if (media.missing.length > 0) return {
349
+ if (Object.keys(media.missing).length > 0) return {
275
350
  values: body,
276
351
  problem: missingMedia(media.missing)
277
352
  };
@@ -282,6 +357,26 @@ function createMockBackend({ definition, items = {} }) {
282
357
  problem: null
283
358
  };
284
359
  };
360
+ const search = (params) => {
361
+ const read = readSearchQuery(params, definition);
362
+ if (read.errors !== void 0) return problem(422, "The given data was invalid.", {
363
+ type: "urn:panneau:problem:validation",
364
+ errors: read.errors
365
+ });
366
+ const { q, limit, resources } = read.query;
367
+ let left = 20;
368
+ return json(200, { data: resources.flatMap((id) => {
369
+ if (left === 0) return [];
370
+ const found = applyQuery(store.get(id) ?? [], { q });
371
+ const items = found.slice(0, Math.min(limit, left));
372
+ left -= items.length;
373
+ return items.length > 0 ? [{
374
+ resource: id,
375
+ items,
376
+ total: found.length
377
+ }] : [];
378
+ }) });
379
+ };
285
380
  const handle = async (request) => {
286
381
  const url = new URL(request.url, "http://panneau.test");
287
382
  if (!url.pathname.startsWith(basePath)) return problem(404, "Not found.");
@@ -292,6 +387,15 @@ function createMockBackend({ definition, items = {} }) {
292
387
  return isRecord(body) ? body : {};
293
388
  };
294
389
  if (segments.length === 1 && segments[0] === "definition" && method === "GET") return json(200, definition);
390
+ if (segments.length === 1 && segments[0] === "embeds" && method === "GET") {
391
+ const url = new URL(request.url, "http://panneau.test").searchParams.get("url") ?? "";
392
+ const embed = url === "" ? null : (options.embed ?? defaultEmbed)(url);
393
+ return embed !== null ? json(200, { data: embed }) : problem(422, "The given data was invalid.", {
394
+ type: "urn:panneau:problem:validation",
395
+ errors: { url: ["This URL can’t be embedded."] }
396
+ });
397
+ }
398
+ if (segments.length === 1 && segments[0] === "search" && method === "GET") return search(url.searchParams);
295
399
  if (segments[0] === "uploads") return handleUploads(segments, method, readBody);
296
400
  if (segments[0] !== "resources" || segments[1] === void 0) return problem(404, "Not found.");
297
401
  const resource = definition.resources.find((candidate) => candidate.id === segments[1]);
@@ -323,15 +427,45 @@ function createMockBackend({ definition, items = {} }) {
323
427
  if (invalid !== null) return invalid;
324
428
  const { values, problem: unresolved } = resolveValues(resource, body);
325
429
  if (unresolved !== null) return unresolved;
326
- const item = makeItem(resource, {
430
+ const item = withOwnTransform(resource, makeItem(resource, {
327
431
  ...values,
328
432
  id: void 0
329
- });
433
+ }));
330
434
  list.push(item);
331
435
  return json(201, { data: item }, etag(item));
332
436
  }
333
437
  return problem(405, "Method not allowed.");
334
438
  }
439
+ if (segments[2] === "fields" && segments[4] === "check" && segments.length === 5) {
440
+ if (method !== "POST") return problem(405, "Method not allowed.");
441
+ const field = resource.fields.find((candidate) => candidate.name === segments[3] && candidate.check === true);
442
+ if (field === void 0) return problem(404, "Field not found.");
443
+ const body = await readBody();
444
+ if (typeof body.value !== "string") return problem(422, "The given data was invalid.", {
445
+ type: "urn:panneau:problem:validation",
446
+ errors: { value: ["The value is required."] }
447
+ });
448
+ const locale = typeof body.locale === "string" ? body.locale : null;
449
+ const id = typeof body.id === "string" || typeof body.id === "number" ? String(body.id) : null;
450
+ const valueOf = (item) => {
451
+ const value = item[field.name];
452
+ return locale !== null && isRecord(value) ? value[locale] : value;
453
+ };
454
+ const taken = (candidate) => list.some((item) => item.id !== id && valueOf(item) === candidate);
455
+ const available = !taken(body.value);
456
+ let suggestion = null;
457
+ for (let index = 2; !available && suggestion === null; index += 1) {
458
+ const candidate = `${body.value}-${String(index)}`;
459
+ suggestion = taken(candidate) ? null : candidate;
460
+ }
461
+ const prefix = typeof field.settings?.prefix === "string" ? field.settings.prefix : null;
462
+ return json(200, {
463
+ available,
464
+ suggestion,
465
+ prefix,
466
+ url: prefix !== null ? `${prefix}${body.value}` : null
467
+ });
468
+ }
335
469
  if (segments[2] === "actions" && segments[3] !== void 0 && segments.length === 4) {
336
470
  if (method !== "POST") return problem(405, "Method not allowed.");
337
471
  const actionId = segments[3];
@@ -367,6 +501,23 @@ function createMockBackend({ definition, items = {} }) {
367
501
  refresh: "list"
368
502
  });
369
503
  }
504
+ if (segments.length === 4 && segments[3] === "preview" && segments[2] !== void 0) {
505
+ const current = list.find((item) => item.id === segments[2]);
506
+ if (current === void 0) return problem(404, "Item not found.");
507
+ if (method !== "POST") return problem(405, "Method not allowed.");
508
+ const body = await readBody();
509
+ const item = {
510
+ ...current,
511
+ ...isRecord(body) ? body : {}
512
+ };
513
+ return json(200, {
514
+ url: options.previewUrl?.({
515
+ resource: resource.id,
516
+ item
517
+ }) ?? defaultPreviewUrl(item),
518
+ expiresAt: new Date(Date.now() + 3e5).toISOString()
519
+ });
520
+ }
370
521
  if (segments.length !== 3 || segments[2] === void 0) return problem(404, "Not found.");
371
522
  const id = segments[2];
372
523
  const index = list.findIndex((item) => item.id === id);
@@ -385,11 +536,11 @@ function createMockBackend({ definition, items = {} }) {
385
536
  const { values, problem: unresolved } = resolveValues(resource, body);
386
537
  if (unresolved !== null) return unresolved;
387
538
  const updates = Object.fromEntries(Object.entries(values).filter(([key]) => !RESERVED_KEYS.has(key)));
388
- const updated = {
539
+ const updated = withOwnTransform(resource, {
389
540
  ...current,
390
541
  ...updates,
391
542
  version: current.version === null ? null : String(Number(current.version) + 1)
392
- };
543
+ });
393
544
  list[index] = updated;
394
545
  return json(200, { data: updated }, etag(updated));
395
546
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../src/mock/index.ts"],"sourcesContent":["import { type ListQuery, fromSearchParams, isFilterRange, matchesFilterRange } from '../query';\nimport { type RuleFailure, checkValues } from '../rules';\nimport type { DefinitionInput, ResourceInput } from '../schemas';\nimport type {\n ActionResult,\n Item,\n MediaInfo,\n MediaReference,\n Problem,\n Selection,\n StoredFile,\n Upload,\n UploadSignResponse,\n} from '../schemas/api';\nimport { isRecord } from '../values';\nimport { PROBLEM_CONTENT_TYPE } from '../version';\n\ntype Values = Record<string, unknown>;\n\nexport interface MockBackendOptions {\n /** The definition served by `GET /definition`. Its `api.baseUrl` is the prefix of every route. */\n definition: DefinitionInput;\n /** Initial values of the items, per resource id. Items without an `id` get one. */\n items?: Record<string, Values[]>;\n}\n\nexport interface MockBackend {\n /** A `fetch`-compatible function that answers protocol requests. */\n fetch: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;\n /** The current items of a resource. */\n items: (resource: string) => Item[];\n}\n\nconst RESERVED_KEYS = new Set(['id', 'version', 'abilities']);\n\nconst DEFAULT_MESSAGES: Record<string, (failure: RuleFailure) => string> = {\n required: () => 'This field is required.',\n min: (failure) => `This value must be at least ${String(failure.value)}.`,\n max: (failure) => `This value must be at most ${String(failure.value)}.`,\n pattern: () => 'This value has an invalid format.',\n email: () => 'This value must be an email address.',\n url: () => 'This value must be a URL.',\n in: () => 'This value is not one of the choices.',\n accept: () => 'This file type is not accepted.',\n maxSize: () => 'This file is too large.',\n before: () => 'This date is too late.',\n after: () => 'This date is too early.',\n};\n\nfunction message(failure: RuleFailure): string {\n return failure.message ?? DEFAULT_MESSAGES[failure.rule]?.(failure) ?? 'This value is invalid.';\n}\n\nfunction json(status: number, body: unknown, headers: Record<string, string> = {}): Response {\n return new Response(status === 204 ? null : JSON.stringify(body), {\n status,\n headers: status === 204 ? headers : { 'Content-Type': 'application/json', ...headers },\n });\n}\n\nfunction problem(status: number, title: string, extra: Partial<Problem> = {}): Response {\n const body: Problem = { type: 'about:blank', title, status, ...extra };\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': PROBLEM_CONTENT_TYPE },\n });\n}\n\nfunction etag(item: Item): Record<string, string> {\n return item.version === null ? {} : { ETag: `\"${item.version}\"` };\n}\n\nfunction parseEtag(value: string): string {\n return value.replace(/^W\\//, '').replace(/^\"(.*)\"$/, '$1');\n}\n\nfunction searchableText(value: unknown): string {\n if (typeof value === 'string') {\n return value;\n }\n if (isRecord(value)) {\n return Object.values(value).map(searchableText).join(' ');\n }\n return '';\n}\n\nfunction compare(a: unknown, b: unknown): number {\n const left = isRecord(a) ? searchableText(a) : a;\n const right = isRecord(b) ? searchableText(b) : b;\n if (left === right) {\n return 0;\n }\n if (left === undefined || left === null) {\n return 1;\n }\n if (right === undefined || right === null) {\n return -1;\n }\n return String(left).localeCompare(String(right), undefined, { numeric: true });\n}\n\nfunction applyQuery(items: Item[], query: Pick<ListQuery, 'filter' | 'q' | 'sort'>): Item[] {\n let result = items;\n Object.entries(query.filter ?? {}).forEach(([key, expected]) => {\n if (isFilterRange(expected)) {\n result = result.filter((item) => matchesFilterRange(item[key], expected));\n return;\n }\n const accepted = (Array.isArray(expected) ? expected : [expected]).map(String);\n result = result.filter((item) => {\n // A filter that isn't a value of the item reads its `media`: `filter[type]=image`.\n const value =\n key in item ? item[key] : isRecord(item.media) ? item.media[key] : undefined;\n const values = Array.isArray(value) ? value : [value];\n return values.some((candidate) => accepted.includes(String(candidate)));\n });\n });\n if (query.q !== undefined && query.q !== '') {\n const terms = query.q.toLowerCase();\n result = result.filter((item) =>\n Object.entries(item)\n .filter(([key]) => !RESERVED_KEYS.has(key))\n .some(([, value]) => searchableText(value).toLowerCase().includes(terms)),\n );\n }\n const sort = query.sort ?? [];\n if (sort.length > 0) {\n result = [...result].sort((a, b) => {\n for (const part of sort) {\n const descending = part.startsWith('-');\n const key = descending ? part.slice(1) : part;\n const order = compare(a[key], b[key]);\n if (order !== 0) {\n return descending ? -order : order;\n }\n }\n return 0;\n });\n }\n return result;\n}\n\n/** The type of a media, from its MIME type. */\nfunction mediaType(mime: string | null | undefined): string {\n const family = (mime ?? '').split('/')[0] ?? '';\n if (family === 'image' || family === 'video' || family === 'audio') {\n return family;\n }\n return mime === 'application/pdf' || family === 'text' ? 'document' : 'other';\n}\n\n/** What the mock computes about a stored file: no renditions, ready at once. */\nfunction mediaOf(file: StoredFile): MediaInfo {\n const type = mediaType(file.mime);\n return {\n type,\n mime: file.mime ?? null,\n size: file.size ?? null,\n url: file.url ?? null,\n thumbnailUrl: type === 'image' ? (file.url ?? null) : null,\n status: 'ready',\n };\n}\n\n/**\n * An in-memory backend that implements the protocol's resource routes: definition, list, show,\n * create, update (with `If-Match`), delete and the built-in `delete` action. Values are checked\n * with the portable rules. Used to test the conformance kit, and to develop frontends without a\n * real backend.\n */\nexport function createMockBackend({ definition, items = {} }: MockBackendOptions): MockBackend {\n const store = new Map<string, Item[]>();\n let nextId = 1;\n\n const abilitiesFor = (resource: ResourceInput) =>\n Object.fromEntries([\n ['update', true],\n ['delete', true],\n ...(resource.actions ?? []).map((action) => [action.id, true]),\n ]);\n\n const makeItem = (resource: ResourceInput, values: Values): Item => {\n const id =\n typeof values.id === 'string' || typeof values.id === 'number'\n ? String(values.id)\n : String(nextId++);\n const rest = Object.fromEntries(\n Object.entries(values).filter(([key]) => !RESERVED_KEYS.has(key)),\n );\n return { ...rest, id, version: '1', abilities: abilitiesFor(resource) };\n };\n\n definition.resources.forEach((resource) => {\n store.set(\n resource.id,\n (items[resource.id] ?? []).map((values) => makeItem(resource, values)),\n );\n });\n\n const basePath = new URL(definition.api.baseUrl, 'http://panneau.test').pathname.replace(\n /\\/$/,\n '',\n );\n\n const validationProblem = (\n resource: ResourceInput,\n values: Values,\n mode: 'create' | 'update',\n ) => {\n const failures = checkValues(resource.fields, values, {\n mode,\n partial: mode === 'update',\n locales: definition.locales,\n });\n const paths = Object.keys(failures);\n if (paths.length === 0) {\n return null;\n }\n return problem(422, 'The given data was invalid.', {\n type: 'urn:panneau:problem:validation',\n errors: Object.fromEntries(\n paths.map((path) => [path, (failures[path] ?? []).map(message)]),\n ),\n });\n };\n\n const selectItems = (list: Item[], selection: Selection | undefined): Item[] => {\n if (selection === undefined) {\n return [];\n }\n if ('ids' in selection) {\n const ids = selection.ids as string[];\n return list.filter((item) => ids.includes(item.id));\n }\n const except = selection.except ?? [];\n const { filter, q } = selection.query;\n const query: Pick<ListQuery, 'filter' | 'q'> = {};\n if (filter !== undefined) {\n query.filter = filter as NonNullable<ListQuery['filter']>;\n }\n if (q !== undefined) {\n query.q = q;\n }\n return applyQuery(list, query).filter((item) => !except.includes(item.id));\n };\n\n // Direct uploads: the open uploads by id, with their key. The mock signs fake URLs, and\n // trusts the client when it completes an upload.\n const uploads = new Map<string, Upload>();\n let nextUpload = 1;\n\n const handleUploads = async (\n segments: string[],\n method: string,\n readBody: () => Promise<Values>,\n ): Promise<Response> => {\n if (definition.uploads?.mode !== 'presigned') {\n return problem(404, 'Not found.');\n }\n if (method !== 'POST') {\n return problem(405, 'Method not allowed.');\n }\n if (segments.length === 2 && segments[1] === 'sign') {\n const body = await readBody();\n const opens =\n body.uploadId === undefined && (body.method === 'POST' || body.method === 'PUT');\n if (opens) {\n const id = `upl_${nextUpload++}`;\n const upload: Upload = { id, key: `uploads/${id}` };\n uploads.set(id, upload);\n const response: UploadSignResponse = {\n url: `https://storage.test/${upload.key}?signature=mock`,\n key: upload.key,\n upload: id,\n };\n return json(200, response);\n }\n const upload = [...uploads.values()].find((candidate) => candidate.key === body.key);\n if (upload === undefined) {\n return problem(404, 'Upload not found.');\n }\n return json(200, { url: `https://storage.test/${upload.key}?signature=mock` });\n }\n if (segments.length === 3 && segments[2] === 'complete' && segments[1] !== undefined) {\n const upload = uploads.get(segments[1]);\n if (upload === undefined) {\n return problem(404, 'Upload not found.');\n }\n const { name } = await readBody();\n if (typeof name === 'string') {\n upload.name = name;\n }\n return json(200, { data: upload });\n }\n return problem(404, 'Not found.');\n };\n\n // The values of `file` fields at the top of a resource: an upload becomes a stored file, and\n // a stored file or null is kept. Returns the paths of uploads that don't exist.\n const resolveFiles = (\n resource: ResourceInput,\n values: Values,\n ): { values: Values; missing: string[] } => {\n const missing: string[] = [];\n const resolved = { ...values };\n const toFile = (value: unknown, path: string): unknown => {\n if (!isRecord(value) || typeof value.upload !== 'string') {\n return value;\n }\n const upload = uploads.get(value.upload);\n if (upload === undefined) {\n missing.push(path);\n return value;\n }\n const file: StoredFile = {\n name: upload.name ?? (typeof value.name === 'string' ? value.name : upload.id),\n url: `https://storage.test/${upload.key ?? upload.id}`,\n mime: upload.mime ?? (typeof value.mime === 'string' ? value.mime : null),\n size: upload.size ?? (typeof value.size === 'number' ? value.size : null),\n };\n return file;\n };\n resource.fields\n .filter((field) => field.type === 'file' && field.name in values)\n .forEach((field) => {\n const value = values[field.name];\n resolved[field.name] = Array.isArray(value)\n ? value.map((entry, index) => toFile(entry, `${field.name}.${String(index)}`))\n : toFile(value, field.name);\n });\n return { values: resolved, missing };\n };\n\n const missingMedia = (paths: string[]) =>\n problem(422, 'The given data was invalid.', {\n type: 'urn:panneau:problem:validation',\n errors: Object.fromEntries(paths.map((path) => [path, ['This media does not exist.']])),\n });\n\n const missingUploads = (paths: string[]) =>\n problem(422, 'The given data was invalid.', {\n type: 'urn:panneau:problem:validation',\n errors: Object.fromEntries(\n paths.map((path) => [path, ['This upload does not exist.']]),\n ),\n });\n\n // The values of `media` fields at the top of a resource: an id, or `{ id, transform }`,\n // becomes a reference to a media of its library. Returns the paths of media that don't exist.\n const resolveMedia = (\n resource: ResourceInput,\n values: Values,\n ): { values: Values; missing: string[] } => {\n const missing: string[] = [];\n const resolved = { ...values };\n const libraries = definition.resources.filter((candidate) => candidate.kind === 'media');\n resource.fields\n .filter((field) => field.type === 'media' && field.name in values)\n .forEach((field) => {\n const library = libraries.find((candidate) =>\n field.resource !== undefined ? candidate.id === field.resource : true,\n );\n const toReference = (value: unknown, path: string): unknown => {\n const id = isRecord(value) ? value.id : value;\n if (typeof id !== 'string' && typeof id !== 'number') {\n return value;\n }\n const media = (store.get(library?.id ?? '') ?? []).find(\n (candidate) => candidate.id === String(id),\n );\n if (media === undefined || library === undefined) {\n missing.push(path);\n return value;\n }\n const title = media[library.itemTitle ?? 'name'];\n const reference: MediaReference = {\n id: media.id,\n ...(typeof title === 'string' ? { label: title } : {}),\n ...(isRecord(media.media) ? { media: media.media as MediaInfo } : {}),\n ...(isRecord(value) && isRecord(value.transform)\n ? { transform: value.transform }\n : {}),\n };\n return reference;\n };\n const value = values[field.name];\n resolved[field.name] = Array.isArray(value)\n ? value.map((entry, index) =>\n toReference(entry, `${field.name}.${String(index)}`),\n )\n : toReference(value, field.name);\n });\n return { values: resolved, missing };\n };\n\n // Resolves the files and media of the values, and computes the `media` of a media item from\n // its file. The client never sets `media`.\n const resolveValues = (\n resource: ResourceInput,\n body: Values,\n ): { values: Values; problem: Response | null } => {\n const sent = { ...body };\n if (resource.kind === 'media') {\n delete sent.media;\n }\n const files = resolveFiles(resource, sent);\n if (files.missing.length > 0) {\n return { values: body, problem: missingUploads(files.missing) };\n }\n const media = resolveMedia(resource, files.values);\n if (media.missing.length > 0) {\n return { values: body, problem: missingMedia(media.missing) };\n }\n const values = media.values;\n if (resource.kind === 'media' && isRecord(values.file)) {\n values.media = mediaOf(values.file as StoredFile);\n }\n return { values, problem: null };\n };\n\n const handle = async (request: Request): Promise<Response> => {\n const url = new URL(request.url, 'http://panneau.test');\n if (!url.pathname.startsWith(basePath)) {\n return problem(404, 'Not found.');\n }\n const segments = url.pathname\n .slice(basePath.length)\n .split('/')\n .filter((segment) => segment !== '')\n .map(decodeURIComponent);\n const method = request.method.toUpperCase();\n const readBody = async (): Promise<Values> => {\n const body: unknown = await request.json().catch(() => null);\n return isRecord(body) ? body : {};\n };\n\n if (segments.length === 1 && segments[0] === 'definition' && method === 'GET') {\n return json(200, definition);\n }\n if (segments[0] === 'uploads') {\n return handleUploads(segments, method, readBody);\n }\n if (segments[0] !== 'resources' || segments[1] === undefined) {\n return problem(404, 'Not found.');\n }\n\n const resource = definition.resources.find((candidate) => candidate.id === segments[1]);\n const list = resource !== undefined ? store.get(resource.id) : undefined;\n if (resource === undefined || list === undefined) {\n return problem(404, 'Resource not found.');\n }\n\n if (segments.length === 2) {\n if (method === 'GET') {\n const query = fromSearchParams(url.searchParams);\n const perPage = query.perPage ?? resource.list?.perPage ?? 20;\n const page = query.page ?? 1;\n const sort = query.sort ?? resource.list?.defaultSort?.split(',');\n const matching = applyQuery(list, {\n ...query,\n ...(sort !== undefined ? { sort } : {}),\n });\n return json(200, {\n data: matching.slice((page - 1) * perPage, page * perPage),\n meta: {\n page,\n perPage,\n total: matching.length,\n lastPage: Math.max(1, Math.ceil(matching.length / perPage)),\n },\n });\n }\n if (method === 'POST') {\n const body = await readBody();\n const invalid = validationProblem(resource, body, 'create');\n if (invalid !== null) {\n return invalid;\n }\n const { values, problem: unresolved } = resolveValues(resource, body);\n if (unresolved !== null) {\n return unresolved;\n }\n const item = makeItem(resource, { ...values, id: undefined });\n list.push(item);\n return json(201, { data: item }, etag(item));\n }\n return problem(405, 'Method not allowed.');\n }\n\n if (segments[2] === 'actions' && segments[3] !== undefined && segments.length === 4) {\n if (method !== 'POST') {\n return problem(405, 'Method not allowed.');\n }\n const actionId = segments[3];\n const action = (resource.actions ?? []).find((candidate) => candidate.id === actionId);\n const body = await readBody();\n if (body.dryRun === true) {\n // A dry run counts what the action would do, and changes nothing.\n if (action?.dryRun !== true) {\n return problem(400, 'This action does not support dry runs.');\n }\n const selected = selectItems(list, body.selection as Selection | undefined);\n const allowed = selected.filter((item) => item.abilities?.[actionId] !== false);\n const result: ActionResult = {\n summary: {\n succeeded: allowed.length,\n failed: 0,\n skipped: selected.length - allowed.length,\n },\n results: selected\n .filter((item) => !allowed.includes(item))\n .slice(0, 20)\n .map((item) => ({ id: item.id, status: 'skipped', reason: 'forbidden' })),\n };\n return json(200, result);\n }\n if (actionId !== 'delete') {\n return problem(404, 'Action not found.');\n }\n const selected = selectItems(list, body.selection as Selection | undefined);\n store.set(\n resource.id,\n list.filter((item) => !selected.includes(item)),\n );\n const result: ActionResult = {\n message: `${selected.length} deleted.`,\n summary: { succeeded: selected.length, failed: 0, skipped: 0 },\n refresh: 'list',\n };\n return json(200, result);\n }\n\n if (segments.length !== 3 || segments[2] === undefined) {\n return problem(404, 'Not found.');\n }\n const id = segments[2];\n const index = list.findIndex((item) => item.id === id);\n const current = list[index];\n if (current === undefined) {\n return problem(404, 'Item not found.');\n }\n\n if (method === 'GET') {\n return json(200, { data: current }, etag(current));\n }\n if (method === 'PATCH') {\n const ifMatch = request.headers.get('If-Match');\n if (\n ifMatch !== null &&\n current.version !== null &&\n parseEtag(ifMatch) !== current.version\n ) {\n return problem(412, 'The item was changed by someone else.', {\n type: 'urn:panneau:problem:version-conflict',\n current,\n });\n }\n const body = await readBody();\n const invalid = validationProblem(resource, body, 'update');\n if (invalid !== null) {\n return invalid;\n }\n const { values, problem: unresolved } = resolveValues(resource, body);\n if (unresolved !== null) {\n return unresolved;\n }\n const updates = Object.fromEntries(\n Object.entries(values).filter(([key]) => !RESERVED_KEYS.has(key)),\n );\n const updated: Item = {\n ...current,\n ...updates,\n version: current.version === null ? null : String(Number(current.version) + 1),\n };\n list[index] = updated;\n return json(200, { data: updated }, etag(updated));\n }\n if (method === 'DELETE') {\n list.splice(index, 1);\n return json(204, null);\n }\n return problem(405, 'Method not allowed.');\n };\n\n return {\n fetch: (input, init) => {\n if (input instanceof Request) {\n return handle(input);\n }\n // Relative URLs are accepted, as a browser would resolve them.\n return handle(new Request(new URL(input, 'http://panneau.test'), init));\n },\n items: (resource) => [...(store.get(resource) ?? [])],\n };\n}\n\nexport { createExampleDefinition } from './example';\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../src/mock/index.ts"],"sourcesContent":["import { getMediaLibraries } from '../libraries';\nimport { type ListQuery, fromSearchParams, isFilterRange, matchesFilterRange } from '../query';\nimport { type RuleFailure, checkValues } from '../rules';\nimport type { DefinitionInput, ResourceInput } from '../schemas';\nimport type {\n ActionResult,\n EmbedValue,\n FieldCheckResponse,\n Item,\n MediaInfo,\n MediaReference,\n PreviewResponse,\n Problem,\n SearchResponse,\n Selection,\n StoredFile,\n Upload,\n UploadSignResponse,\n} from '../schemas/api';\nimport { SEARCH_MAX_LIMIT, readSearchQuery } from '../search';\nimport { isRecord } from '../values';\nimport { PROBLEM_CONTENT_TYPE } from '../version';\n\ntype Values = Record<string, unknown>;\n\nexport interface MockBackendOptions {\n /** The definition served by `GET /definition`. Its `api.baseUrl` is the prefix of every route. */\n definition: DefinitionInput;\n /** Initial values of the items, per resource id. Items without an `id` get one. */\n items?: Record<string, Values[]>;\n /**\n * The URL that `POST /resources/{resource}/{id}/preview` returns, for the item with the posted\n * values. Defaults to a `data:` URL of a plain page with the item's values.\n */\n previewUrl?: (context: { resource: string; item: Item }) => string;\n /**\n * The value of `GET /embeds?url=` for a URL, or `null` to refuse it with a `422`. Defaults to\n * a video for YouTube and Vimeo URLs, and a link for other web pages.\n */\n embed?: (url: string) => EmbedValue | null;\n}\n\nexport interface MockBackend {\n /** A `fetch`-compatible function that answers protocol requests. */\n fetch: (input: string | URL | Request, init?: RequestInit) => Promise<Response>;\n /** The current items of a resource. */\n items: (resource: string) => Item[];\n}\n\nconst RESERVED_KEYS = new Set(['id', 'version', 'abilities']);\n\nfunction escapeHtml(text: string): string {\n return text.replace(/[&<>\"']/g, (character) => `&#${character.charCodeAt(0)};`);\n}\n\n/** A plain page with the values of an item, as a `data:` URL, for iframe previews. */\nfunction defaultPreviewUrl(item: Item): string {\n const rows = Object.entries(item)\n .filter(([key]) => !RESERVED_KEYS.has(key))\n .map(\n ([key, value]) =>\n `<dt>${escapeHtml(key)}</dt><dd>${escapeHtml(typeof value === 'string' ? value : JSON.stringify(value))}</dd>`,\n )\n .join('');\n const html = `<!doctype html><meta charset=\"utf-8\"><title>Preview</title><body style=\"font-family:system-ui;margin:2rem\"><h1>Item ${escapeHtml(item.id)}</h1><dl>${rows}</dl></body>`;\n return `data:text/html;charset=utf-8,${encodeURIComponent(html)}`;\n}\n\n/** What the mock knows of a URL to embed: a video of YouTube or Vimeo, or a web page. */\nfunction defaultEmbed(url: string): EmbedValue | null {\n if (!URL.canParse(url)) {\n return null;\n }\n const parsed = new URL(url);\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {\n return null;\n }\n const host = parsed.hostname.replace(/^www\\./, '');\n const youtube =\n host === 'youtu.be'\n ? parsed.pathname.slice(1)\n : host.endsWith('youtube.com')\n ? parsed.searchParams.get('v')\n : null;\n if (youtube !== null && youtube !== '') {\n return {\n url,\n type: 'video',\n provider: 'YouTube',\n title: `YouTube video ${youtube}`,\n thumbnailUrl: `https://i.ytimg.com/vi/${youtube}/hqdefault.jpg`,\n html: `<iframe src=\"https://www.youtube.com/embed/${youtube}\" width=\"560\" height=\"315\" allowfullscreen></iframe>`,\n width: 560,\n height: 315,\n };\n }\n if (host === 'vimeo.com') {\n const id = parsed.pathname.split('/').filter(Boolean)[0] ?? '';\n return {\n url,\n type: 'video',\n provider: 'Vimeo',\n title: `Vimeo video ${id}`,\n html: `<iframe src=\"https://player.vimeo.com/video/${id}\" width=\"640\" height=\"360\" allowfullscreen></iframe>`,\n width: 640,\n height: 360,\n };\n }\n return { url, type: 'link', provider: host, title: host };\n}\n\nconst DEFAULT_MESSAGES: Record<string, (failure: RuleFailure) => string> = {\n required: () => 'This field is required.',\n min: (failure) => `This value must be at least ${String(failure.value)}.`,\n max: (failure) => `This value must be at most ${String(failure.value)}.`,\n pattern: () => 'This value has an invalid format.',\n email: () => 'This value must be an email address.',\n url: () => 'This value must be a URL.',\n json: () => 'This value must be valid JSON: an object or an array.',\n in: () => 'This value is not one of the choices.',\n accept: () => 'This file type is not accepted.',\n maxSize: () => 'This file is too large.',\n before: () => 'This date is too late.',\n after: () => 'This date is too early.',\n crop: (failure) => {\n const ratios = isRecord(failure.value) ? failure.value.ratios : undefined;\n return Array.isArray(ratios) && ratios.length > 0\n ? `Crop this image to ${ratios.join(' or ')}.`\n : 'Crop this image.';\n },\n};\n\nfunction message(failure: RuleFailure): string {\n return failure.message ?? DEFAULT_MESSAGES[failure.rule]?.(failure) ?? 'This value is invalid.';\n}\n\nfunction json(status: number, body: unknown, headers: Record<string, string> = {}): Response {\n return new Response(status === 204 ? null : JSON.stringify(body), {\n status,\n headers: status === 204 ? headers : { 'Content-Type': 'application/json', ...headers },\n });\n}\n\nfunction problem(status: number, title: string, extra: Partial<Problem> = {}): Response {\n const body: Problem = { type: 'about:blank', title, status, ...extra };\n return new Response(JSON.stringify(body), {\n status,\n headers: { 'Content-Type': PROBLEM_CONTENT_TYPE },\n });\n}\n\nfunction etag(item: Item): Record<string, string> {\n return item.version === null ? {} : { ETag: `\"${item.version}\"` };\n}\n\nfunction parseEtag(value: string): string {\n return value.replace(/^W\\//, '').replace(/^\"(.*)\"$/, '$1');\n}\n\nfunction searchableText(value: unknown): string {\n if (typeof value === 'string') {\n return value;\n }\n if (isRecord(value)) {\n return Object.values(value).map(searchableText).join(' ');\n }\n return '';\n}\n\nfunction compare(a: unknown, b: unknown): number {\n const left = isRecord(a) ? searchableText(a) : a;\n const right = isRecord(b) ? searchableText(b) : b;\n if (left === right) {\n return 0;\n }\n if (left === undefined || left === null) {\n return 1;\n }\n if (right === undefined || right === null) {\n return -1;\n }\n return String(left).localeCompare(String(right), undefined, { numeric: true });\n}\n\nfunction applyQuery(items: Item[], query: Pick<ListQuery, 'filter' | 'q' | 'sort'>): Item[] {\n let result = items;\n Object.entries(query.filter ?? {}).forEach(([key, expected]) => {\n if (isFilterRange(expected)) {\n result = result.filter((item) => matchesFilterRange(item[key], expected));\n return;\n }\n const accepted = (Array.isArray(expected) ? expected : [expected]).map(String);\n result = result.filter((item) => {\n // A filter that isn't a value of the item reads its `media`: `filter[type]=image`.\n const value =\n key in item ? item[key] : isRecord(item.media) ? item.media[key] : undefined;\n const values = Array.isArray(value) ? value : [value];\n return values.some((candidate) => accepted.includes(String(candidate)));\n });\n });\n if (query.q !== undefined && query.q !== '') {\n const terms = query.q.toLowerCase();\n result = result.filter((item) =>\n Object.entries(item)\n .filter(([key]) => !RESERVED_KEYS.has(key))\n .some(([, value]) => searchableText(value).toLowerCase().includes(terms)),\n );\n }\n const sort = query.sort ?? [];\n if (sort.length > 0) {\n result = [...result].sort((a, b) => {\n for (const part of sort) {\n const descending = part.startsWith('-');\n const key = descending ? part.slice(1) : part;\n const order = compare(a[key], b[key]);\n if (order !== 0) {\n return descending ? -order : order;\n }\n }\n return 0;\n });\n }\n return result;\n}\n\n/** The type of a media, from its MIME type. */\nfunction mediaType(mime: string | null | undefined): string {\n const family = (mime ?? '').split('/')[0] ?? '';\n if (family === 'image' || family === 'video' || family === 'audio') {\n return family;\n }\n return mime === 'application/pdf' || family === 'text' ? 'document' : 'other';\n}\n\n/** What the mock computes about a stored file: no renditions, ready at once. */\nfunction mediaOf(file: StoredFile): MediaInfo {\n const type = mediaType(file.mime);\n return {\n type,\n mime: file.mime ?? null,\n size: file.size ?? null,\n url: file.url ?? null,\n thumbnailUrl: type === 'image' ? (file.url ?? null) : null,\n status: 'ready',\n };\n}\n\n/**\n * A media item with its own transform (its `transform` field) in its `media`, where references\n * give it to clients (decision 0031).\n */\nfunction withOwnTransform(resource: ResourceInput, item: Item): Item {\n if (resource.kind !== 'media' || !isRecord(item.media)) {\n return item;\n }\n const { transform: _previous, ...media } = item.media;\n return {\n ...item,\n media: isRecord(item.transform) ? { ...media, transform: item.transform } : media,\n };\n}\n\n/**\n * An in-memory backend that implements the protocol's resource routes: definition, list, show,\n * create, update (with `If-Match`), delete and the built-in `delete` action. Values are checked\n * with the portable rules. Used to test the conformance kit, and to develop frontends without a\n * real backend.\n */\nexport function createMockBackend(options: MockBackendOptions): MockBackend {\n const { definition, items = {} } = options;\n const store = new Map<string, Item[]>();\n let nextId = 1;\n\n const abilitiesFor = (resource: ResourceInput) =>\n Object.fromEntries([\n ['update', true],\n ['delete', true],\n ...(resource.actions ?? []).map((action) => [action.id, true]),\n ]);\n\n const makeItem = (resource: ResourceInput, values: Values): Item => {\n const id =\n typeof values.id === 'string' || typeof values.id === 'number'\n ? String(values.id)\n : String(nextId++);\n const rest = Object.fromEntries(\n Object.entries(values).filter(([key]) => !RESERVED_KEYS.has(key)),\n );\n return { ...rest, id, version: '1', abilities: abilitiesFor(resource) };\n };\n\n definition.resources.forEach((resource) => {\n store.set(\n resource.id,\n (items[resource.id] ?? []).map((values) => makeItem(resource, values)),\n );\n });\n\n const basePath = new URL(definition.api.baseUrl, 'http://panneau.test').pathname.replace(\n /\\/$/,\n '',\n );\n\n const validationProblem = (\n resource: ResourceInput,\n values: Values,\n mode: 'create' | 'update',\n ) => {\n const failures = checkValues(resource.fields, values, {\n mode,\n partial: mode === 'update',\n locales: definition.locales,\n });\n const paths = Object.keys(failures);\n if (paths.length === 0) {\n return null;\n }\n return problem(422, 'The given data was invalid.', {\n type: 'urn:panneau:problem:validation',\n errors: Object.fromEntries(\n paths.map((path) => [path, (failures[path] ?? []).map(message)]),\n ),\n });\n };\n\n const selectItems = (list: Item[], selection: Selection | undefined): Item[] => {\n if (selection === undefined) {\n return [];\n }\n if ('ids' in selection) {\n const ids = selection.ids as string[];\n return list.filter((item) => ids.includes(item.id));\n }\n const except = selection.except ?? [];\n const { filter, q } = selection.query;\n const query: Pick<ListQuery, 'filter' | 'q'> = {};\n if (filter !== undefined) {\n query.filter = filter as NonNullable<ListQuery['filter']>;\n }\n if (q !== undefined) {\n query.q = q;\n }\n return applyQuery(list, query).filter((item) => !except.includes(item.id));\n };\n\n // Direct uploads: the open uploads by id, with their key. The mock signs fake URLs, and\n // trusts the client when it completes an upload.\n const uploads = new Map<string, Upload>();\n let nextUpload = 1;\n\n const handleUploads = async (\n segments: string[],\n method: string,\n readBody: () => Promise<Values>,\n ): Promise<Response> => {\n if (definition.uploads?.mode !== 'presigned') {\n return problem(404, 'Not found.');\n }\n if (method !== 'POST') {\n return problem(405, 'Method not allowed.');\n }\n if (segments.length === 2 && segments[1] === 'sign') {\n const body = await readBody();\n const opens =\n body.uploadId === undefined && (body.method === 'POST' || body.method === 'PUT');\n if (opens) {\n const id = `upl_${nextUpload++}`;\n const upload: Upload = { id, key: `uploads/${id}` };\n uploads.set(id, upload);\n const response: UploadSignResponse = {\n url: `https://storage.test/${upload.key}?signature=mock`,\n key: upload.key,\n upload: id,\n };\n return json(200, response);\n }\n const upload = [...uploads.values()].find((candidate) => candidate.key === body.key);\n if (upload === undefined) {\n return problem(404, 'Upload not found.');\n }\n return json(200, { url: `https://storage.test/${upload.key}?signature=mock` });\n }\n if (segments.length === 3 && segments[2] === 'complete' && segments[1] !== undefined) {\n const upload = uploads.get(segments[1]);\n if (upload === undefined) {\n return problem(404, 'Upload not found.');\n }\n const { name } = await readBody();\n if (typeof name === 'string') {\n upload.name = name;\n }\n return json(200, { data: upload });\n }\n return problem(404, 'Not found.');\n };\n\n // The values of `file` fields at the top of a resource: an upload becomes a stored file, and\n // a stored file or null is kept. Returns the paths of uploads that don't exist.\n const resolveFiles = (\n resource: ResourceInput,\n values: Values,\n ): { values: Values; missing: string[] } => {\n const missing: string[] = [];\n const resolved = { ...values };\n const toFile = (value: unknown, path: string): unknown => {\n if (!isRecord(value) || typeof value.upload !== 'string') {\n return value;\n }\n const upload = uploads.get(value.upload);\n if (upload === undefined) {\n missing.push(path);\n return value;\n }\n const file: StoredFile = {\n name: upload.name ?? (typeof value.name === 'string' ? value.name : upload.id),\n url: `https://storage.test/${upload.key ?? upload.id}`,\n mime: upload.mime ?? (typeof value.mime === 'string' ? value.mime : null),\n size: upload.size ?? (typeof value.size === 'number' ? value.size : null),\n };\n return file;\n };\n resource.fields\n .filter((field) => field.type === 'file' && field.name in values)\n .forEach((field) => {\n const value = values[field.name];\n resolved[field.name] = Array.isArray(value)\n ? value.map((entry, index) => toFile(entry, `${field.name}.${String(index)}`))\n : toFile(value, field.name);\n });\n return { values: resolved, missing };\n };\n\n const missingMedia = (paths: Record<string, string>) =>\n problem(422, 'The given data was invalid.', {\n type: 'urn:panneau:problem:validation',\n errors: Object.fromEntries(\n Object.entries(paths).map(([path, message]) => [path, [message]]),\n ),\n });\n\n const missingUploads = (paths: string[]) =>\n problem(422, 'The given data was invalid.', {\n type: 'urn:panneau:problem:validation',\n errors: Object.fromEntries(\n paths.map((path) => [path, ['This upload does not exist.']]),\n ),\n });\n\n // The values of `media` and `image` fields at the top of a resource: an id, or\n // `{ id, resource, transform }`, becomes a reference to a media of one of its libraries. A\n // field that allows several libraries needs the library of each media (decision 0027).\n // Returns the problems by path.\n const resolveMedia = (\n resource: ResourceInput,\n values: Values,\n ): { values: Values; missing: Record<string, string> } => {\n const missing: Record<string, string> = {};\n const resolved = { ...values };\n resource.fields\n .filter(\n (field) =>\n (field.type === 'media' || field.type === 'image') && field.name in values,\n )\n .forEach((field) => {\n const allowed = getMediaLibraries(definition, field);\n const toReference = (value: unknown, path: string): unknown => {\n const id = isRecord(value) ? value.id : value;\n if (typeof id !== 'string' && typeof id !== 'number') {\n return value;\n }\n const named =\n isRecord(value) && typeof value.resource === 'string'\n ? value.resource\n : undefined;\n if (named === undefined && allowed.length > 1) {\n missing[path] = 'This field allows several libraries: name the library.';\n return value;\n }\n const libraryId = named ?? allowed[0];\n const library = definition.resources.find(\n (candidate) => candidate.id === libraryId && allowed.includes(candidate.id),\n );\n const media = (store.get(library?.id ?? '') ?? []).find(\n (candidate) => candidate.id === String(id),\n );\n if (media === undefined || library === undefined) {\n missing[path] = 'This media does not exist.';\n return value;\n }\n const title = media[library.itemTitle ?? 'name'];\n const reference: MediaReference = {\n id: media.id,\n ...(allowed.length > 1 ? { resource: library.id } : {}),\n ...(typeof title === 'string' ? { label: title } : {}),\n ...(isRecord(media.media) ? { media: media.media as MediaInfo } : {}),\n ...(isRecord(value) && isRecord(value.transform)\n ? { transform: value.transform }\n : {}),\n };\n return reference;\n };\n const value = values[field.name];\n resolved[field.name] = Array.isArray(value)\n ? value.map((entry, index) =>\n toReference(entry, `${field.name}.${String(index)}`),\n )\n : toReference(value, field.name);\n });\n return { values: resolved, missing };\n };\n\n // Resolves the files and media of the values, and computes the `media` of a media item from\n // its file. The client never sets `media`.\n const resolveValues = (\n resource: ResourceInput,\n body: Values,\n ): { values: Values; problem: Response | null } => {\n const sent = { ...body };\n if (resource.kind === 'media') {\n delete sent.media;\n }\n const files = resolveFiles(resource, sent);\n if (files.missing.length > 0) {\n return { values: body, problem: missingUploads(files.missing) };\n }\n const media = resolveMedia(resource, files.values);\n if (Object.keys(media.missing).length > 0) {\n return { values: body, problem: missingMedia(media.missing) };\n }\n const values = media.values;\n if (resource.kind === 'media' && isRecord(values.file)) {\n values.media = mediaOf(values.file as StoredFile);\n }\n return { values, problem: null };\n };\n\n // The global search (decision 0029): the searchable resources asked for, `limit` items each\n // (5 by default), 20 in all, from 2 characters.\n const search = (params: URLSearchParams): Response => {\n const read = readSearchQuery(params, definition);\n if (read.errors !== undefined) {\n return problem(422, 'The given data was invalid.', {\n type: 'urn:panneau:problem:validation',\n errors: read.errors,\n });\n }\n const { q, limit, resources } = read.query;\n let left = SEARCH_MAX_LIMIT;\n const data: SearchResponse['data'] = resources.flatMap((id) => {\n if (left === 0) {\n return [];\n }\n const found = applyQuery(store.get(id) ?? [], { q });\n const items = found.slice(0, Math.min(limit, left));\n left -= items.length;\n return items.length > 0 ? [{ resource: id, items, total: found.length }] : [];\n });\n return json(200, { data } satisfies SearchResponse);\n };\n\n const handle = async (request: Request): Promise<Response> => {\n const url = new URL(request.url, 'http://panneau.test');\n if (!url.pathname.startsWith(basePath)) {\n return problem(404, 'Not found.');\n }\n const segments = url.pathname\n .slice(basePath.length)\n .split('/')\n .filter((segment) => segment !== '')\n .map(decodeURIComponent);\n const method = request.method.toUpperCase();\n const readBody = async (): Promise<Values> => {\n const body: unknown = await request.json().catch(() => null);\n return isRecord(body) ? body : {};\n };\n\n if (segments.length === 1 && segments[0] === 'definition' && method === 'GET') {\n return json(200, definition);\n }\n if (segments.length === 1 && segments[0] === 'embeds' && method === 'GET') {\n const url = new URL(request.url, 'http://panneau.test').searchParams.get('url') ?? '';\n const embed = url === '' ? null : (options.embed ?? defaultEmbed)(url);\n return embed !== null\n ? json(200, { data: embed })\n : problem(422, 'The given data was invalid.', {\n type: 'urn:panneau:problem:validation',\n errors: { url: ['This URL can’t be embedded.'] },\n });\n }\n if (segments.length === 1 && segments[0] === 'search' && method === 'GET') {\n return search(url.searchParams);\n }\n if (segments[0] === 'uploads') {\n return handleUploads(segments, method, readBody);\n }\n if (segments[0] !== 'resources' || segments[1] === undefined) {\n return problem(404, 'Not found.');\n }\n\n const resource = definition.resources.find((candidate) => candidate.id === segments[1]);\n const list = resource !== undefined ? store.get(resource.id) : undefined;\n if (resource === undefined || list === undefined) {\n return problem(404, 'Resource not found.');\n }\n\n if (segments.length === 2) {\n if (method === 'GET') {\n const query = fromSearchParams(url.searchParams);\n const perPage = query.perPage ?? resource.list?.perPage ?? 20;\n const page = query.page ?? 1;\n const sort = query.sort ?? resource.list?.defaultSort?.split(',');\n const matching = applyQuery(list, {\n ...query,\n ...(sort !== undefined ? { sort } : {}),\n });\n return json(200, {\n data: matching.slice((page - 1) * perPage, page * perPage),\n meta: {\n page,\n perPage,\n total: matching.length,\n lastPage: Math.max(1, Math.ceil(matching.length / perPage)),\n },\n });\n }\n if (method === 'POST') {\n const body = await readBody();\n const invalid = validationProblem(resource, body, 'create');\n if (invalid !== null) {\n return invalid;\n }\n const { values, problem: unresolved } = resolveValues(resource, body);\n if (unresolved !== null) {\n return unresolved;\n }\n const item = withOwnTransform(\n resource,\n makeItem(resource, { ...values, id: undefined }),\n );\n list.push(item);\n return json(201, { data: item }, etag(item));\n }\n return problem(405, 'Method not allowed.');\n }\n\n if (segments[2] === 'fields' && segments[4] === 'check' && segments.length === 5) {\n if (method !== 'POST') {\n return problem(405, 'Method not allowed.');\n }\n const field = resource.fields.find(\n (candidate) => candidate.name === segments[3] && candidate.check === true,\n );\n if (field === undefined) {\n return problem(404, 'Field not found.');\n }\n const body = await readBody();\n if (typeof body.value !== 'string') {\n return problem(422, 'The given data was invalid.', {\n type: 'urn:panneau:problem:validation',\n errors: { value: ['The value is required.'] },\n });\n }\n const locale = typeof body.locale === 'string' ? body.locale : null;\n const id =\n typeof body.id === 'string' || typeof body.id === 'number' ? String(body.id) : null;\n const valueOf = (item: Item): unknown => {\n const value = item[field.name];\n return locale !== null && isRecord(value) ? value[locale] : value;\n };\n const taken = (candidate: string) =>\n list.some((item) => item.id !== id && valueOf(item) === candidate);\n const available = !taken(body.value);\n let suggestion: string | null = null;\n for (let index = 2; !available && suggestion === null; index += 1) {\n const candidate = `${body.value}-${String(index)}`;\n suggestion = taken(candidate) ? null : candidate;\n }\n // The mock gives a URL when the field has a fixed prefix\n const prefix =\n typeof field.settings?.prefix === 'string' ? field.settings.prefix : null;\n const response: FieldCheckResponse = {\n available,\n suggestion,\n prefix,\n url: prefix !== null ? `${prefix}${body.value}` : null,\n };\n return json(200, response);\n }\n\n if (segments[2] === 'actions' && segments[3] !== undefined && segments.length === 4) {\n if (method !== 'POST') {\n return problem(405, 'Method not allowed.');\n }\n const actionId = segments[3];\n const action = (resource.actions ?? []).find((candidate) => candidate.id === actionId);\n const body = await readBody();\n if (body.dryRun === true) {\n // A dry run counts what the action would do, and changes nothing.\n if (action?.dryRun !== true) {\n return problem(400, 'This action does not support dry runs.');\n }\n const selected = selectItems(list, body.selection as Selection | undefined);\n const allowed = selected.filter((item) => item.abilities?.[actionId] !== false);\n const result: ActionResult = {\n summary: {\n succeeded: allowed.length,\n failed: 0,\n skipped: selected.length - allowed.length,\n },\n results: selected\n .filter((item) => !allowed.includes(item))\n .slice(0, 20)\n .map((item) => ({ id: item.id, status: 'skipped', reason: 'forbidden' })),\n };\n return json(200, result);\n }\n if (actionId !== 'delete') {\n return problem(404, 'Action not found.');\n }\n const selected = selectItems(list, body.selection as Selection | undefined);\n store.set(\n resource.id,\n list.filter((item) => !selected.includes(item)),\n );\n const result: ActionResult = {\n message: `${selected.length} deleted.`,\n summary: { succeeded: selected.length, failed: 0, skipped: 0 },\n refresh: 'list',\n };\n return json(200, result);\n }\n\n if (segments.length === 4 && segments[3] === 'preview' && segments[2] !== undefined) {\n const current = list.find((item) => item.id === segments[2]);\n if (current === undefined) {\n return problem(404, 'Item not found.');\n }\n if (method !== 'POST') {\n return problem(405, 'Method not allowed.');\n }\n const body = await readBody();\n const item: Item = { ...current, ...(isRecord(body) ? body : {}) };\n const url =\n options.previewUrl?.({ resource: resource.id, item }) ?? defaultPreviewUrl(item);\n return json(200, {\n url,\n expiresAt: new Date(Date.now() + 5 * 60_000).toISOString(),\n } satisfies PreviewResponse);\n }\n\n if (segments.length !== 3 || segments[2] === undefined) {\n return problem(404, 'Not found.');\n }\n const id = segments[2];\n const index = list.findIndex((item) => item.id === id);\n const current = list[index];\n if (current === undefined) {\n return problem(404, 'Item not found.');\n }\n\n if (method === 'GET') {\n return json(200, { data: current }, etag(current));\n }\n if (method === 'PATCH') {\n const ifMatch = request.headers.get('If-Match');\n if (\n ifMatch !== null &&\n current.version !== null &&\n parseEtag(ifMatch) !== current.version\n ) {\n return problem(412, 'The item was changed by someone else.', {\n type: 'urn:panneau:problem:version-conflict',\n current,\n });\n }\n const body = await readBody();\n const invalid = validationProblem(resource, body, 'update');\n if (invalid !== null) {\n return invalid;\n }\n const { values, problem: unresolved } = resolveValues(resource, body);\n if (unresolved !== null) {\n return unresolved;\n }\n const updates = Object.fromEntries(\n Object.entries(values).filter(([key]) => !RESERVED_KEYS.has(key)),\n );\n const updated = withOwnTransform(resource, {\n ...current,\n ...updates,\n version: current.version === null ? null : String(Number(current.version) + 1),\n });\n list[index] = updated;\n return json(200, { data: updated }, etag(updated));\n }\n if (method === 'DELETE') {\n list.splice(index, 1);\n return json(204, null);\n }\n return problem(405, 'Method not allowed.');\n };\n\n return {\n fetch: (input, init) => {\n if (input instanceof Request) {\n return handle(input);\n }\n // Relative URLs are accepted, as a browser would resolve them.\n return handle(new Request(new URL(input, 'http://panneau.test'), init));\n },\n items: (resource) => [...(store.get(resource) ?? [])],\n };\n}\n\nexport { createExampleDefinition } from './example';\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"AAQA,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA4E1C,oEAAoE;AACpE,wBAAgB,sBAAsB,IAAI,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAWnE;AAED,6DAA6D;AAC7D,wBAAgB,0BAA0B,IAAI,UAAU,CAQvD;AAED,oEAAoE;AACpE,wBAAgB,qBAAqB,IAAI,UAAU,CAQlD;AAED,wFAAwF;AACxF,wBAAgB,0BAA0B,IAAI,UAAU,CAQvD;AAED,wDAAwD;AACxD,wBAAgB,qBAAqB,IAAI,UAAU,CAyVlD"}
1
+ {"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"AAcA,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AA6E1C,oEAAoE;AACpE,wBAAgB,sBAAsB,IAAI,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAWnE;AAED,6DAA6D;AAC7D,wBAAgB,0BAA0B,IAAI,UAAU,CAQvD;AAED,oEAAoE;AACpE,wBAAgB,qBAAqB,IAAI,UAAU,CAQlD;AAED,wFAAwF;AACxF,wBAAgB,0BAA0B,IAAI,UAAU,CAQvD;AAED,wDAAwD;AACxD,wBAAgB,qBAAqB,IAAI,UAAU,CA6YlD"}