@panneau/protocol 0.0.0-stage → 5.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +35 -2
  2. package/dist/conditions.d.ts +7 -0
  3. package/dist/conditions.d.ts.map +1 -0
  4. package/dist/conditions.js +21 -0
  5. package/dist/conditions.js.map +1 -0
  6. package/dist/conformance/index.d.ts +42 -0
  7. package/dist/conformance/index.d.ts.map +1 -0
  8. package/dist/conformance/index.js +270 -0
  9. package/dist/conformance/index.js.map +1 -0
  10. package/dist/constants.d.ts +21 -0
  11. package/dist/constants.d.ts.map +1 -0
  12. package/dist/constants.js +73 -0
  13. package/dist/constants.js.map +1 -0
  14. package/dist/index.d.ts +5 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +21 -0
  17. package/dist/lint.d.ts +8 -0
  18. package/dist/lint.d.ts.map +1 -0
  19. package/dist/lint.js +73 -0
  20. package/dist/lint.js.map +1 -0
  21. package/dist/mock/example.d.ts +8 -0
  22. package/dist/mock/example.d.ts.map +1 -0
  23. package/dist/mock/example.js +190 -0
  24. package/dist/mock/example.js.map +1 -0
  25. package/dist/mock/index.d.ts +24 -0
  26. package/dist/mock/index.d.ts.map +1 -0
  27. package/dist/mock/index.js +254 -0
  28. package/dist/mock/index.js.map +1 -0
  29. package/dist/openapi.d.ts +11 -0
  30. package/dist/openapi.d.ts.map +1 -0
  31. package/dist/openapi.js +540 -0
  32. package/dist/openapi.js.map +1 -0
  33. package/dist/problem.d.ts +7 -0
  34. package/dist/problem.d.ts.map +1 -0
  35. package/dist/problem.js +22 -0
  36. package/dist/problem.js.map +1 -0
  37. package/dist/query.d.ts +41 -0
  38. package/dist/query.d.ts.map +1 -0
  39. package/dist/query.js +80 -0
  40. package/dist/query.js.map +1 -0
  41. package/dist/rules.d.ts +29 -0
  42. package/dist/rules.d.ts.map +1 -0
  43. package/dist/rules.js +143 -0
  44. package/dist/rules.js.map +1 -0
  45. package/dist/runtime/index.d.ts +10 -0
  46. package/dist/runtime/index.d.ts.map +1 -0
  47. package/dist/runtime/index.js +9 -0
  48. package/dist/schemas/action.d.ts +163 -0
  49. package/dist/schemas/action.d.ts.map +1 -0
  50. package/dist/schemas/action.js +56 -0
  51. package/dist/schemas/action.js.map +1 -0
  52. package/dist/schemas/api.d.ts +371 -0
  53. package/dist/schemas/api.d.ts.map +1 -0
  54. package/dist/schemas/api.js +140 -0
  55. package/dist/schemas/api.js.map +1 -0
  56. package/dist/schemas/boot.d.ts +14 -0
  57. package/dist/schemas/boot.d.ts.map +1 -0
  58. package/dist/schemas/boot.js +16 -0
  59. package/dist/schemas/boot.js.map +1 -0
  60. package/dist/schemas/common.d.ts +28 -0
  61. package/dist/schemas/common.d.ts.map +1 -0
  62. package/dist/schemas/common.js +35 -0
  63. package/dist/schemas/common.js.map +1 -0
  64. package/dist/schemas/definition.d.ts +462 -0
  65. package/dist/schemas/definition.d.ts.map +1 -0
  66. package/dist/schemas/definition.js +101 -0
  67. package/dist/schemas/definition.js.map +1 -0
  68. package/dist/schemas/field.d.ts +139 -0
  69. package/dist/schemas/field.d.ts.map +1 -0
  70. package/dist/schemas/field.js +94 -0
  71. package/dist/schemas/field.js.map +1 -0
  72. package/dist/schemas/index.d.ts +1536 -0
  73. package/dist/schemas/index.d.ts.map +1 -0
  74. package/dist/schemas/index.js +68 -0
  75. package/dist/schemas/index.js.map +1 -0
  76. package/dist/schemas/layout.d.ts +26 -0
  77. package/dist/schemas/layout.d.ts.map +1 -0
  78. package/dist/schemas/layout.js +23 -0
  79. package/dist/schemas/layout.js.map +1 -0
  80. package/dist/schemas/list.d.ts +69 -0
  81. package/dist/schemas/list.d.ts.map +1 -0
  82. package/dist/schemas/list.js +41 -0
  83. package/dist/schemas/list.js.map +1 -0
  84. package/dist/schemas/resource.d.ts +328 -0
  85. package/dist/schemas/resource.d.ts.map +1 -0
  86. package/dist/schemas/resource.js +38 -0
  87. package/dist/schemas/resource.js.map +1 -0
  88. package/dist/validate.d.ts +28 -0
  89. package/dist/validate.d.ts.map +1 -0
  90. package/dist/validate.js +52 -0
  91. package/dist/validate.js.map +1 -0
  92. package/dist/values.d.ts +7 -0
  93. package/dist/values.d.ts.map +1 -0
  94. package/dist/values.js +29 -0
  95. package/dist/values.js.map +1 -0
  96. package/dist/version.d.ts +5 -0
  97. package/dist/version.d.ts.map +1 -0
  98. package/dist/version.js +9 -0
  99. package/dist/version.js.map +1 -0
  100. package/package.json +53 -5
  101. package/schema/boot.schema.json +1229 -0
  102. package/schema/definition.schema.json +1195 -0
  103. package/schema/openapi.json +3839 -0
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The bounds of a range filter (`date-range`), both inclusive. An open end is left out. Sent as
3
+ * `filter[published_at][from]=2026-01-01&filter[published_at][to]=2026-01-31`.
4
+ */
5
+ export interface FilterRange {
6
+ from?: string | number;
7
+ to?: string | number;
8
+ }
9
+ /**
10
+ * The value of a filter: one value (`filter[status]=draft`), a list that matches any of its values
11
+ * (`filter[tags][]=a&filter[tags][]=b`), or the bounds of a range.
12
+ */
13
+ export type FilterValue = string | number | boolean | Array<string | number | boolean> | FilterRange;
14
+ /** Whether a filter value is the bounds of a range, and not one value or a list of values. */
15
+ export declare function isFilterRange(value: unknown): value is FilterRange;
16
+ /**
17
+ * Whether a value is within the bounds of a range filter, both inclusive: the reference of how a
18
+ * backend reads them. Numbers compare as numbers. Other values compare as strings, on the length of
19
+ * the bound, so that a day (`2026-01-31`) includes the times of that day (`2026-01-31T18:00:00Z`).
20
+ * An empty value is never in a range.
21
+ */
22
+ export declare function matchesFilterRange(value: unknown, range: FilterRange): boolean;
23
+ /** Query of a list request (`GET /resources/{resource}`). */
24
+ export interface ListQuery {
25
+ page?: number;
26
+ perPage?: number;
27
+ /** Fields to sort by, in order. A `-` prefix sorts descending. */
28
+ sort?: string[];
29
+ filter?: Record<string, FilterValue>;
30
+ q?: string;
31
+ /** Fields to include in each item. */
32
+ fields?: string[];
33
+ }
34
+ /** Serializes a list query to the protocol's query string format. */
35
+ export declare function toSearchParams(query: ListQuery): URLSearchParams;
36
+ /**
37
+ * Parses a list query from the protocol's query string format. Filter values stay strings, and the
38
+ * bounds of a range (`filter[id][from]`, `filter[id][to]`) become a `FilterRange`.
39
+ */
40
+ export declare function fromSearchParams(params: URLSearchParams): ListQuery;
41
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,WAAW;IACxB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GACnB,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,WAAW,CAAC;AAE/E,8FAA8F;AAC9F,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,WAAW,CAElE;AAID;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAe9E;AAED,6DAA6D;AAC7D,MAAM,WAAW,SAAS;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,eAAe,CAgChE;AAID;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CAkDnE"}
package/dist/query.js ADDED
@@ -0,0 +1,80 @@
1
+ //#region src/query.ts
2
+ /** Whether a filter value is the bounds of a range, and not one value or a list of values. */
3
+ function isFilterRange(value) {
4
+ return value !== null && typeof value === "object" && !Array.isArray(value);
5
+ }
6
+ var RANGE_BOUNDS = ["from", "to"];
7
+ /**
8
+ * Whether a value is within the bounds of a range filter, both inclusive: the reference of how a
9
+ * backend reads them. Numbers compare as numbers. Other values compare as strings, on the length of
10
+ * the bound, so that a day (`2026-01-31`) includes the times of that day (`2026-01-31T18:00:00Z`).
11
+ * An empty value is never in a range.
12
+ */
13
+ function matchesFilterRange(value, range) {
14
+ if (value === null || value === void 0 || value === "") return false;
15
+ const compare = (bound) => {
16
+ if (typeof value === "number" || typeof bound === "number") return Number(value) - Number(bound);
17
+ const prefix = String(value).slice(0, bound.length);
18
+ return prefix < bound ? -1 : prefix > bound ? 1 : 0;
19
+ };
20
+ return (range.from === void 0 || range.from === "" || compare(range.from) >= 0) && (range.to === void 0 || range.to === "" || compare(range.to) <= 0);
21
+ }
22
+ /** Serializes a list query to the protocol's query string format. */
23
+ function toSearchParams(query) {
24
+ const params = new URLSearchParams();
25
+ if (query.page !== void 0) params.set("page", String(query.page));
26
+ if (query.perPage !== void 0) params.set("perPage", String(query.perPage));
27
+ if (query.sort !== void 0 && query.sort.length > 0) params.set("sort", query.sort.join(","));
28
+ Object.entries(query.filter ?? {}).forEach(([key, value]) => {
29
+ if (Array.isArray(value)) value.forEach((item) => params.append(`filter[${key}][]`, String(item)));
30
+ else if (isFilterRange(value)) RANGE_BOUNDS.forEach((bound) => {
31
+ const limit = value[bound];
32
+ if (limit !== void 0 && limit !== "") params.set(`filter[${key}][${bound}]`, String(limit));
33
+ });
34
+ else params.set(`filter[${key}]`, String(value));
35
+ });
36
+ if (query.q !== void 0 && query.q !== "") params.set("q", query.q);
37
+ if (query.fields !== void 0 && query.fields.length > 0) params.set("fields", query.fields.join(","));
38
+ return params;
39
+ }
40
+ var FILTER_KEY = /^filter\[([^\]]+)\](?:\[(|from|to)\])?$/;
41
+ /**
42
+ * Parses a list query from the protocol's query string format. Filter values stay strings, and the
43
+ * bounds of a range (`filter[id][from]`, `filter[id][to]`) become a `FilterRange`.
44
+ */
45
+ function fromSearchParams(params) {
46
+ const query = {};
47
+ const integer = (value) => {
48
+ if (value === null || !/^\d+$/.test(value)) return;
49
+ return Number.parseInt(value, 10);
50
+ };
51
+ const page = integer(params.get("page"));
52
+ if (page !== void 0) query.page = page;
53
+ const perPage = integer(params.get("perPage"));
54
+ if (perPage !== void 0) query.perPage = perPage;
55
+ const sort = params.get("sort");
56
+ if (sort !== null && sort !== "") query.sort = sort.split(",").filter((part) => part !== "");
57
+ const q = params.get("q");
58
+ if (q !== null && q !== "") query.q = q;
59
+ const fields = params.get("fields");
60
+ if (fields !== null && fields !== "") query.fields = fields.split(",").filter((part) => part !== "");
61
+ params.forEach((value, key) => {
62
+ const match = FILTER_KEY.exec(key);
63
+ if (match?.[1] === void 0) return;
64
+ query.filter ??= {};
65
+ const name = match[1];
66
+ const current = query.filter[name];
67
+ if (match[2] === "") query.filter[name] = [...Array.isArray(current) ? current : [], value];
68
+ else if (match[2] !== void 0) {
69
+ if (value !== "") query.filter[name] = {
70
+ ...isFilterRange(current) ? current : {},
71
+ [match[2]]: value
72
+ };
73
+ } else query.filter[name] = value;
74
+ });
75
+ return query;
76
+ }
77
+ //#endregion
78
+ export { fromSearchParams, isFilterRange, matchesFilterRange, toSearchParams };
79
+
80
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","names":[],"sources":["../src/query.ts"],"sourcesContent":["/**\n * The bounds of a range filter (`date-range`), both inclusive. An open end is left out. Sent as\n * `filter[published_at][from]=2026-01-01&filter[published_at][to]=2026-01-31`.\n */\nexport interface FilterRange {\n from?: string | number;\n to?: string | number;\n}\n\n/**\n * The value of a filter: one value (`filter[status]=draft`), a list that matches any of its values\n * (`filter[tags][]=a&filter[tags][]=b`), or the bounds of a range.\n */\nexport type FilterValue =\n string | number | boolean | Array<string | number | boolean> | FilterRange;\n\n/** Whether a filter value is the bounds of a range, and not one value or a list of values. */\nexport function isFilterRange(value: unknown): value is FilterRange {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\nconst RANGE_BOUNDS = ['from', 'to'] as const;\n\n/**\n * Whether a value is within the bounds of a range filter, both inclusive: the reference of how a\n * backend reads them. Numbers compare as numbers. Other values compare as strings, on the length of\n * the bound, so that a day (`2026-01-31`) includes the times of that day (`2026-01-31T18:00:00Z`).\n * An empty value is never in a range.\n */\nexport function matchesFilterRange(value: unknown, range: FilterRange): boolean {\n if (value === null || value === undefined || value === '') {\n return false;\n }\n const compare = (bound: string | number): number => {\n if (typeof value === 'number' || typeof bound === 'number') {\n return Number(value) - Number(bound);\n }\n const prefix = String(value).slice(0, bound.length);\n return prefix < bound ? -1 : prefix > bound ? 1 : 0;\n };\n return (\n (range.from === undefined || range.from === '' || compare(range.from) >= 0) &&\n (range.to === undefined || range.to === '' || compare(range.to) <= 0)\n );\n}\n\n/** Query of a list request (`GET /resources/{resource}`). */\nexport interface ListQuery {\n page?: number;\n perPage?: number;\n /** Fields to sort by, in order. A `-` prefix sorts descending. */\n sort?: string[];\n filter?: Record<string, FilterValue>;\n q?: string;\n /** Fields to include in each item. */\n fields?: string[];\n}\n\n/** Serializes a list query to the protocol's query string format. */\nexport function toSearchParams(query: ListQuery): URLSearchParams {\n const params = new URLSearchParams();\n if (query.page !== undefined) {\n params.set('page', String(query.page));\n }\n if (query.perPage !== undefined) {\n params.set('perPage', String(query.perPage));\n }\n if (query.sort !== undefined && query.sort.length > 0) {\n params.set('sort', query.sort.join(','));\n }\n Object.entries(query.filter ?? {}).forEach(([key, value]) => {\n if (Array.isArray(value)) {\n value.forEach((item) => params.append(`filter[${key}][]`, String(item)));\n } else if (isFilterRange(value)) {\n RANGE_BOUNDS.forEach((bound) => {\n const limit = value[bound];\n if (limit !== undefined && limit !== '') {\n params.set(`filter[${key}][${bound}]`, String(limit));\n }\n });\n } else {\n params.set(`filter[${key}]`, String(value));\n }\n });\n if (query.q !== undefined && query.q !== '') {\n params.set('q', query.q);\n }\n if (query.fields !== undefined && query.fields.length > 0) {\n params.set('fields', query.fields.join(','));\n }\n return params;\n}\n\nconst FILTER_KEY = /^filter\\[([^\\]]+)\\](?:\\[(|from|to)\\])?$/;\n\n/**\n * Parses a list query from the protocol's query string format. Filter values stay strings, and the\n * bounds of a range (`filter[id][from]`, `filter[id][to]`) become a `FilterRange`.\n */\nexport function fromSearchParams(params: URLSearchParams): ListQuery {\n const query: ListQuery = {};\n const integer = (value: string | null): number | undefined => {\n if (value === null || !/^\\d+$/.test(value)) {\n return undefined;\n }\n return Number.parseInt(value, 10);\n };\n const page = integer(params.get('page'));\n if (page !== undefined) {\n query.page = page;\n }\n const perPage = integer(params.get('perPage'));\n if (perPage !== undefined) {\n query.perPage = perPage;\n }\n const sort = params.get('sort');\n if (sort !== null && sort !== '') {\n query.sort = sort.split(',').filter((part) => part !== '');\n }\n const q = params.get('q');\n if (q !== null && q !== '') {\n query.q = q;\n }\n const fields = params.get('fields');\n if (fields !== null && fields !== '') {\n query.fields = fields.split(',').filter((part) => part !== '');\n }\n params.forEach((value, key) => {\n const match = FILTER_KEY.exec(key);\n if (match?.[1] === undefined) {\n return;\n }\n query.filter ??= {};\n const name = match[1];\n const current = query.filter[name];\n if (match[2] === '') {\n query.filter[name] = [...(Array.isArray(current) ? current : []), value];\n } else if (match[2] !== undefined) {\n if (value !== '') {\n query.filter[name] = {\n ...(isFilterRange(current) ? current : {}),\n [match[2]]: value,\n };\n }\n } else {\n query.filter[name] = value;\n }\n });\n return query;\n}\n"],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { Field, Rule } from './schemas/field';
2
+ /** A rule that a value doesn't satisfy. */
3
+ export interface RuleFailure {
4
+ rule: string;
5
+ /** The rule's parameter (`max: 120` gives `120`). */
6
+ value?: unknown;
7
+ /** The message set on the rule, if any. */
8
+ message?: string;
9
+ }
10
+ export interface CheckOptions {
11
+ /**
12
+ * Locales a `required` localized field must be filled in. Without it, one filled locale is
13
+ * enough.
14
+ */
15
+ locales?: string[];
16
+ /** `create` skips `updateOnly` fields; `update` skips `createOnly` fields. */
17
+ mode?: 'create' | 'update';
18
+ /** Only check the fields present in the values (partial updates). */
19
+ partial?: boolean;
20
+ }
21
+ /** Checks one value against a field's rules. Returns the rules that fail. */
22
+ export declare function checkRules(rules: Rule[], value: unknown, root?: Record<string, unknown>): RuleFailure[];
23
+ /**
24
+ * Checks form values against the portable rules of their fields. Returns the failures keyed by
25
+ * the dot path of the value (`title.fr`, `blocks.2.image`), the same keys as validation errors.
26
+ * Hidden and read-only fields are skipped. Rules this version doesn't know are ignored.
27
+ */
28
+ export declare function checkValues(fields: Field[], values: Record<string, unknown>, options?: CheckOptions): Record<string, RuleFailure[]>;
29
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAGnD,2CAA2C;AAC3C,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2CAA2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IACzB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,8EAA8E;IAC9E,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC3B,qEAAqE;IACrE,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAyDD,6EAA6E;AAC7E,wBAAgB,UAAU,CACtB,KAAK,EAAE,IAAI,EAAE,EACb,KAAK,EAAE,OAAO,EACd,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACnC,WAAW,EAAE,CAwEf;AAuFD;;;;GAIG;AACH,wBAAgB,WAAW,CACvB,MAAM,EAAE,KAAK,EAAE,EACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,GAAE,YAAiB,GAC3B,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAI/B"}
package/dist/rules.js ADDED
@@ -0,0 +1,143 @@
1
+ import { PORTABLE_RULES } from "./constants.js";
2
+ import { getValue, isFilled, isRecord } from "./values.js";
3
+ import { evaluateCondition } from "./conditions.js";
4
+ //#region src/rules.ts
5
+ var EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
6
+ function isKnownRule(rule) {
7
+ return PORTABLE_RULES.includes(rule.rule);
8
+ }
9
+ function failure(rule) {
10
+ const result = { rule: rule.rule };
11
+ if ("value" in rule && rule.value !== void 0) result.value = rule.value;
12
+ if (rule.message !== void 0) result.message = rule.message;
13
+ return result;
14
+ }
15
+ function sizeOf(value) {
16
+ if (typeof value === "number") return value;
17
+ if (typeof value === "string" || Array.isArray(value)) return value.length;
18
+ return null;
19
+ }
20
+ function matchesMime(mime, patterns) {
21
+ return patterns.some((pattern) => {
22
+ if (pattern.endsWith("/*")) return mime.startsWith(pattern.slice(0, -1));
23
+ return mime === pattern;
24
+ });
25
+ }
26
+ function mediaList(value) {
27
+ return (Array.isArray(value) ? value : [value]).filter(isRecord);
28
+ }
29
+ function toTime(value) {
30
+ if (typeof value !== "string" && typeof value !== "number") return null;
31
+ const time = new Date(value).getTime();
32
+ return Number.isNaN(time) ? null : time;
33
+ }
34
+ /** Checks one value against a field's rules. Returns the rules that fail. */
35
+ function checkRules(rules, value, root = {}) {
36
+ const failures = [];
37
+ const filled = isFilled(value);
38
+ rules.filter(isKnownRule).forEach((rule) => {
39
+ if (rule.rule === "required") {
40
+ if (!filled) failures.push(failure(rule));
41
+ return;
42
+ }
43
+ if (!filled) return;
44
+ let valid = true;
45
+ switch (rule.rule) {
46
+ case "min":
47
+ case "max": {
48
+ const size = sizeOf(value);
49
+ const limit = rule.value;
50
+ valid = size === null || (rule.rule === "min" ? size >= limit : size <= limit);
51
+ break;
52
+ }
53
+ case "pattern":
54
+ valid = typeof value !== "string" || new RegExp(rule.value, rule.flags).test(value);
55
+ break;
56
+ case "email":
57
+ valid = typeof value !== "string" || EMAIL.test(value);
58
+ break;
59
+ case "url":
60
+ valid = typeof value !== "string" || URL.canParse(value);
61
+ break;
62
+ case "in": {
63
+ const allowed = rule.value;
64
+ valid = (Array.isArray(value) ? value : [value]).every((item) => allowed.includes(item));
65
+ break;
66
+ }
67
+ case "accept":
68
+ valid = mediaList(value).every((media) => typeof media.mime !== "string" || matchesMime(media.mime, rule.value));
69
+ break;
70
+ case "maxSize":
71
+ valid = mediaList(value).every((media) => typeof media.size !== "number" || media.size <= rule.value);
72
+ break;
73
+ case "before":
74
+ case "after": {
75
+ const bound = rule.value;
76
+ const boundTime = toTime(typeof bound === "string" ? bound : getValue(root, bound.field));
77
+ const time = toTime(value);
78
+ if (boundTime !== null && time !== null) valid = rule.rule === "before" ? time < boundTime : time > boundTime;
79
+ break;
80
+ }
81
+ }
82
+ if (!valid) failures.push(failure(rule));
83
+ });
84
+ return failures;
85
+ }
86
+ function addFailures(context, path, failures) {
87
+ if (failures.length > 0) context.failures[path] = [...context.failures[path] ?? [], ...failures];
88
+ }
89
+ function checkFieldValue(field, value, path, context) {
90
+ const rules = field.rules ?? [];
91
+ if (field.localized === true) {
92
+ const localized = isRecord(value) ? value : {};
93
+ const required = rules.filter((rule) => rule.rule === "required");
94
+ const others = rules.filter((rule) => rule.rule !== "required");
95
+ if (required.length > 0) {
96
+ const locales = context.locales;
97
+ if (locales === void 0 || locales.length === 0) {
98
+ if (!Object.values(localized).some(isFilled)) addFailures(context, path, required.map(failure));
99
+ } else locales.forEach((locale) => {
100
+ addFailures(context, `${path}.${locale}`, checkRules(required, localized[locale], context.root));
101
+ });
102
+ }
103
+ Object.entries(localized).forEach(([locale, localeValue]) => {
104
+ addFailures(context, `${path}.${locale}`, checkRules(others, localeValue, context.root));
105
+ });
106
+ return;
107
+ }
108
+ addFailures(context, path, checkRules(rules, value, context.root));
109
+ if (field.type === "group" && field.fields !== void 0 && isRecord(value)) checkFields(field.fields, value, `${path}.`, context);
110
+ if (field.type === "repeater" && Array.isArray(value)) value.forEach((item, index) => {
111
+ if (!isRecord(item)) return;
112
+ const blockFields = field.types !== void 0 ? field.types.find((type) => type.id === item.type)?.fields : field.fields;
113
+ if (blockFields !== void 0) checkFields(blockFields, item, `${path}.${index}.`, context);
114
+ });
115
+ }
116
+ function checkFields(fields, values, prefix, context) {
117
+ fields.forEach((field) => {
118
+ if (field.readOnly === true) return;
119
+ if (context.mode === "create" && field.updateOnly === true) return;
120
+ if (context.mode === "update" && field.createOnly === true) return;
121
+ if (field.visibleWhen !== void 0 && !evaluateCondition(field.visibleWhen, values)) return;
122
+ if (context.partial === true && prefix === "" && !(field.name in values)) return;
123
+ checkFieldValue(field, values[field.name], `${prefix}${field.name}`, context);
124
+ });
125
+ }
126
+ /**
127
+ * Checks form values against the portable rules of their fields. Returns the failures keyed by
128
+ * the dot path of the value (`title.fr`, `blocks.2.image`), the same keys as validation errors.
129
+ * Hidden and read-only fields are skipped. Rules this version doesn't know are ignored.
130
+ */
131
+ function checkValues(fields, values, options = {}) {
132
+ const context = {
133
+ ...options,
134
+ root: values,
135
+ failures: {}
136
+ };
137
+ checkFields(fields, values, "", context);
138
+ return context.failures;
139
+ }
140
+ //#endregion
141
+ export { checkRules, checkValues };
142
+
143
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","names":[],"sources":["../src/rules.ts"],"sourcesContent":["import { evaluateCondition } from './conditions';\nimport { PORTABLE_RULES } from './constants';\nimport type { Field, Rule } from './schemas/field';\nimport { getValue, isFilled, isRecord } from './values';\n\n/** A rule that a value doesn't satisfy. */\nexport interface RuleFailure {\n rule: string;\n /** The rule's parameter (`max: 120` gives `120`). */\n value?: unknown;\n /** The message set on the rule, if any. */\n message?: string;\n}\n\nexport interface CheckOptions {\n /**\n * Locales a `required` localized field must be filled in. Without it, one filled locale is\n * enough.\n */\n locales?: string[];\n /** `create` skips `updateOnly` fields; `update` skips `createOnly` fields. */\n mode?: 'create' | 'update';\n /** Only check the fields present in the values (partial updates). */\n partial?: boolean;\n}\n\ninterface Context extends CheckOptions {\n /** The whole form, for `visibleWhen` and date rules that reference other fields. */\n root: Record<string, unknown>;\n failures: Record<string, RuleFailure[]>;\n}\n\nconst EMAIL = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nfunction isKnownRule(rule: Rule): boolean {\n return (PORTABLE_RULES as readonly string[]).includes(rule.rule);\n}\n\nfunction failure(rule: Rule): RuleFailure {\n const result: RuleFailure = { rule: rule.rule };\n if ('value' in rule && rule.value !== undefined) {\n result.value = rule.value;\n }\n if (rule.message !== undefined) {\n result.message = rule.message;\n }\n return result;\n}\n\nfunction sizeOf(value: unknown): number | null {\n if (typeof value === 'number') {\n return value;\n }\n if (typeof value === 'string' || Array.isArray(value)) {\n return value.length;\n }\n return null;\n}\n\nfunction matchesMime(mime: string, patterns: string[]): boolean {\n return patterns.some((pattern) => {\n if (pattern.endsWith('/*')) {\n return mime.startsWith(pattern.slice(0, -1));\n }\n return mime === pattern;\n });\n}\n\nfunction mediaList(value: unknown): Record<string, unknown>[] {\n const list = Array.isArray(value) ? value : [value];\n return list.filter(isRecord);\n}\n\nfunction toTime(value: unknown): number | null {\n if (typeof value !== 'string' && typeof value !== 'number') {\n return null;\n }\n const time = new Date(value).getTime();\n return Number.isNaN(time) ? null : time;\n}\n\n/** Checks one value against a field's rules. Returns the rules that fail. */\nexport function checkRules(\n rules: Rule[],\n value: unknown,\n root: Record<string, unknown> = {},\n): RuleFailure[] {\n const failures: RuleFailure[] = [];\n const filled = isFilled(value);\n rules.filter(isKnownRule).forEach((rule) => {\n if (rule.rule === 'required') {\n if (!filled) {\n failures.push(failure(rule));\n }\n return;\n }\n if (!filled) {\n return;\n }\n let valid = true;\n switch (rule.rule) {\n case 'min':\n case 'max': {\n const size = sizeOf(value);\n const limit = rule.value as number;\n valid = size === null || (rule.rule === 'min' ? size >= limit : size <= limit);\n break;\n }\n case 'pattern':\n valid =\n typeof value !== 'string' ||\n new RegExp(rule.value as string, (rule as { flags?: string }).flags).test(\n value,\n );\n break;\n case 'email':\n valid = typeof value !== 'string' || EMAIL.test(value);\n break;\n case 'url':\n valid = typeof value !== 'string' || URL.canParse(value);\n break;\n case 'in': {\n const allowed = rule.value as unknown[];\n const values = Array.isArray(value) ? value : [value];\n valid = values.every((item) => allowed.includes(item));\n break;\n }\n case 'accept':\n valid = mediaList(value).every(\n (media) =>\n typeof media.mime !== 'string' ||\n matchesMime(media.mime, rule.value as string[]),\n );\n break;\n case 'maxSize':\n valid = mediaList(value).every(\n (media) =>\n typeof media.size !== 'number' || media.size <= (rule.value as number),\n );\n break;\n case 'before':\n case 'after': {\n const bound = rule.value as string | { field: string };\n const boundTime = toTime(\n typeof bound === 'string' ? bound : getValue(root, bound.field),\n );\n const time = toTime(value);\n if (boundTime !== null && time !== null) {\n valid = rule.rule === 'before' ? time < boundTime : time > boundTime;\n }\n break;\n }\n }\n if (!valid) {\n failures.push(failure(rule));\n }\n });\n return failures;\n}\n\nfunction addFailures(context: Context, path: string, failures: RuleFailure[]) {\n if (failures.length > 0) {\n context.failures[path] = [...(context.failures[path] ?? []), ...failures];\n }\n}\n\nfunction checkFieldValue(field: Field, value: unknown, path: string, context: Context) {\n const rules = field.rules ?? [];\n if (field.localized === true) {\n const localized = isRecord(value) ? value : {};\n const required = rules.filter((rule) => rule.rule === 'required');\n const others = rules.filter((rule) => rule.rule !== 'required');\n if (required.length > 0) {\n const locales = context.locales;\n if (locales === undefined || locales.length === 0) {\n if (!Object.values(localized).some(isFilled)) {\n addFailures(context, path, required.map(failure));\n }\n } else {\n locales.forEach((locale) => {\n addFailures(\n context,\n `${path}.${locale}`,\n checkRules(required, localized[locale], context.root),\n );\n });\n }\n }\n Object.entries(localized).forEach(([locale, localeValue]) => {\n addFailures(\n context,\n `${path}.${locale}`,\n checkRules(others, localeValue, context.root),\n );\n });\n return;\n }\n\n addFailures(context, path, checkRules(rules, value, context.root));\n\n if (field.type === 'group' && field.fields !== undefined && isRecord(value)) {\n checkFields(field.fields, value, `${path}.`, context);\n }\n if (field.type === 'repeater' && Array.isArray(value)) {\n value.forEach((item, index) => {\n if (!isRecord(item)) {\n return;\n }\n const blockFields =\n field.types !== undefined\n ? field.types.find((type) => type.id === item.type)?.fields\n : field.fields;\n if (blockFields !== undefined) {\n checkFields(blockFields, item, `${path}.${index}.`, context);\n }\n });\n }\n}\n\nfunction checkFields(\n fields: Field[],\n values: Record<string, unknown>,\n prefix: string,\n context: Context,\n) {\n fields.forEach((field) => {\n if (field.readOnly === true) {\n return;\n }\n if (context.mode === 'create' && field.updateOnly === true) {\n return;\n }\n if (context.mode === 'update' && field.createOnly === true) {\n return;\n }\n if (field.visibleWhen !== undefined && !evaluateCondition(field.visibleWhen, values)) {\n return;\n }\n if (context.partial === true && prefix === '' && !(field.name in values)) {\n return;\n }\n checkFieldValue(field, values[field.name], `${prefix}${field.name}`, context);\n });\n}\n\n/**\n * Checks form values against the portable rules of their fields. Returns the failures keyed by\n * the dot path of the value (`title.fr`, `blocks.2.image`), the same keys as validation errors.\n * Hidden and read-only fields are skipped. Rules this version doesn't know are ignored.\n */\nexport function checkValues(\n fields: Field[],\n values: Record<string, unknown>,\n options: CheckOptions = {},\n): Record<string, RuleFailure[]> {\n const context: Context = { ...options, root: values, failures: {} };\n checkFields(fields, values, '', context);\n return context.failures;\n}\n"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export { PROBLEM_CONTENT_TYPE, PROTOCOL_VERSION } from '../version';
2
+ export * from '../constants';
3
+ export { type CheckOptions, type RuleFailure, checkRules, checkValues } from '../rules';
4
+ export { evaluateCondition } from '../conditions';
5
+ export { lintDefinition } from '../lint';
6
+ export type { ValidationIssue } from '../validate';
7
+ export { type FilterRange, type FilterValue, type ListQuery, fromSearchParams, isFilterRange, matchesFilterRange, toSearchParams, } from '../query';
8
+ export { getValue, isFilled } from '../values';
9
+ export { isProblem } from '../problem';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACpE,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AACxF,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACzC,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EACH,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,cAAc,GACjB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { PROBLEM_CONTENT_TYPE, PROTOCOL_VERSION } from "../version.js";
2
+ import { BOOT_CONFIG_ELEMENT_ID, BUILT_IN_ACTIONS, BUILT_IN_FIELD_TYPES, BUILT_IN_LAYOUT_NODE_TYPES, BUILT_IN_LIST_LAYOUTS, PORTABLE_RULES } from "../constants.js";
3
+ import { getValue, isFilled } from "../values.js";
4
+ import { evaluateCondition } from "../conditions.js";
5
+ import { checkRules, checkValues } from "../rules.js";
6
+ import { lintDefinition } from "../lint.js";
7
+ import { fromSearchParams, isFilterRange, matchesFilterRange, toSearchParams } from "../query.js";
8
+ import { isProblem } from "../problem.js";
9
+ export { BOOT_CONFIG_ELEMENT_ID, BUILT_IN_ACTIONS, BUILT_IN_FIELD_TYPES, BUILT_IN_LAYOUT_NODE_TYPES, BUILT_IN_LIST_LAYOUTS, PORTABLE_RULES, PROBLEM_CONTENT_TYPE, PROTOCOL_VERSION, checkRules, checkValues, evaluateCondition, fromSearchParams, getValue, isFilled, isFilterRange, isProblem, lintDefinition, matchesFilterRange, toSearchParams };
@@ -0,0 +1,163 @@
1
+ import { z } from 'zod';
2
+ import { Field } from './field';
3
+ import { LayoutNode } from './layout';
4
+ export declare const ActionScope: z.ZodEnum<{
5
+ resource: "resource";
6
+ item: "item";
7
+ selection: "selection";
8
+ }>;
9
+ export declare const ActionPlacement: z.ZodEnum<{
10
+ row: "row";
11
+ toolbar: "toolbar";
12
+ form: "form";
13
+ menu: "menu";
14
+ }>;
15
+ export declare const ActionConfirm: z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
16
+ title: z.ZodOptional<z.ZodString>;
17
+ message: z.ZodOptional<z.ZodString>;
18
+ confirmLabel: z.ZodOptional<z.ZodString>;
19
+ cancelLabel: z.ZodOptional<z.ZodString>;
20
+ }, z.core.$loose>]>;
21
+ export declare const Action: z.ZodObject<{
22
+ id: z.ZodString;
23
+ label: z.ZodString;
24
+ icon: z.ZodOptional<z.ZodString>;
25
+ kind: z.ZodDefault<z.ZodEnum<{
26
+ link: "link";
27
+ server: "server";
28
+ client: "client";
29
+ }>>;
30
+ scope: z.ZodArray<z.ZodEnum<{
31
+ resource: "resource";
32
+ item: "item";
33
+ selection: "selection";
34
+ }>>;
35
+ placement: z.ZodOptional<z.ZodArray<z.ZodEnum<{
36
+ row: "row";
37
+ toolbar: "toolbar";
38
+ form: "form";
39
+ menu: "menu";
40
+ }>>>;
41
+ destructive: z.ZodOptional<z.ZodBoolean>;
42
+ confirm: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
43
+ title: z.ZodOptional<z.ZodString>;
44
+ message: z.ZodOptional<z.ZodString>;
45
+ confirmLabel: z.ZodOptional<z.ZodString>;
46
+ cancelLabel: z.ZodOptional<z.ZodString>;
47
+ }, z.core.$loose>]>>;
48
+ fields: z.ZodOptional<z.ZodArray<z.ZodObject<{
49
+ name: z.ZodString;
50
+ type: z.ZodString;
51
+ label: z.ZodOptional<z.ZodString>;
52
+ help: z.ZodOptional<z.ZodString>;
53
+ placeholder: z.ZodOptional<z.ZodString>;
54
+ default: z.ZodOptional<z.ZodUnknown>;
55
+ localized: z.ZodOptional<z.ZodBoolean>;
56
+ readOnly: z.ZodOptional<z.ZodBoolean>;
57
+ createOnly: z.ZodOptional<z.ZodBoolean>;
58
+ updateOnly: z.ZodOptional<z.ZodBoolean>;
59
+ rules: z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
60
+ rule: z.ZodLiteral<"required">;
61
+ message: z.ZodOptional<z.ZodString>;
62
+ }, z.core.$loose>, z.ZodObject<{
63
+ rule: z.ZodEnum<{
64
+ min: "min";
65
+ max: "max";
66
+ }>;
67
+ value: z.ZodNumber;
68
+ message: z.ZodOptional<z.ZodString>;
69
+ }, z.core.$loose>, z.ZodObject<{
70
+ rule: z.ZodLiteral<"pattern">;
71
+ value: z.ZodString;
72
+ flags: z.ZodOptional<z.ZodString>;
73
+ message: z.ZodOptional<z.ZodString>;
74
+ }, z.core.$loose>, z.ZodObject<{
75
+ rule: z.ZodEnum<{
76
+ email: "email";
77
+ url: "url";
78
+ }>;
79
+ message: z.ZodOptional<z.ZodString>;
80
+ }, z.core.$loose>, z.ZodObject<{
81
+ rule: z.ZodLiteral<"in">;
82
+ value: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>;
83
+ message: z.ZodOptional<z.ZodString>;
84
+ }, z.core.$loose>, z.ZodObject<{
85
+ rule: z.ZodLiteral<"accept">;
86
+ value: z.ZodArray<z.ZodString>;
87
+ message: z.ZodOptional<z.ZodString>;
88
+ }, z.core.$loose>, z.ZodObject<{
89
+ rule: z.ZodLiteral<"maxSize">;
90
+ value: z.ZodNumber;
91
+ message: z.ZodOptional<z.ZodString>;
92
+ }, z.core.$loose>, z.ZodObject<{
93
+ rule: z.ZodEnum<{
94
+ before: "before";
95
+ after: "after";
96
+ }>;
97
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
98
+ field: z.ZodString;
99
+ }, z.core.$loose>]>;
100
+ message: z.ZodOptional<z.ZodString>;
101
+ }, z.core.$loose>, z.ZodObject<{
102
+ rule: z.ZodString;
103
+ message: z.ZodOptional<z.ZodString>;
104
+ }, z.core.$loose>]>>>;
105
+ visibleWhen: z.ZodOptional<z.ZodObject<{
106
+ field: z.ZodOptional<z.ZodString>;
107
+ equals: z.ZodOptional<z.ZodUnknown>;
108
+ in: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
109
+ notIn: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
110
+ filled: z.ZodOptional<z.ZodBoolean>;
111
+ all: z.ZodOptional<z.ZodArray<typeof import("./common").Condition>>;
112
+ any: z.ZodOptional<z.ZodArray<typeof import("./common").Condition>>;
113
+ }, z.core.$loose>>;
114
+ options: z.ZodOptional<z.ZodArray<z.ZodObject<{
115
+ value: z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>;
116
+ label: z.ZodString;
117
+ description: z.ZodOptional<z.ZodString>;
118
+ disabled: z.ZodOptional<z.ZodBoolean>;
119
+ }, z.core.$loose>>>;
120
+ multiple: z.ZodOptional<z.ZodBoolean>;
121
+ resource: z.ZodOptional<z.ZodString>;
122
+ accept: z.ZodOptional<z.ZodArray<z.ZodString>>;
123
+ from: z.ZodOptional<z.ZodString>;
124
+ display: z.ZodOptional<z.ZodString>;
125
+ fields: z.ZodOptional<z.ZodArray<typeof Field>>;
126
+ types: z.ZodOptional<z.ZodArray<typeof import("./field").BlockType>>;
127
+ settings: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
128
+ }, z.core.$loose>>>;
129
+ layout: z.ZodOptional<z.ZodArray<z.ZodObject<{
130
+ type: z.ZodString;
131
+ name: z.ZodOptional<z.ZodString>;
132
+ label: z.ZodOptional<z.ZodString>;
133
+ description: z.ZodOptional<z.ZodString>;
134
+ text: z.ZodOptional<z.ZodString>;
135
+ component: z.ZodOptional<z.ZodString>;
136
+ props: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
137
+ width: z.ZodOptional<z.ZodNumber>;
138
+ collapsible: z.ZodOptional<z.ZodBoolean>;
139
+ collapsed: z.ZodOptional<z.ZodBoolean>;
140
+ visibleWhen: z.ZodOptional<z.ZodObject<{
141
+ field: z.ZodOptional<z.ZodString>;
142
+ equals: z.ZodOptional<z.ZodUnknown>;
143
+ in: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
144
+ notIn: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
145
+ filled: z.ZodOptional<z.ZodBoolean>;
146
+ all: z.ZodOptional<z.ZodArray<typeof import("./common").Condition>>;
147
+ any: z.ZodOptional<z.ZodArray<typeof import("./common").Condition>>;
148
+ }, z.core.$loose>>;
149
+ children: z.ZodOptional<z.ZodArray<typeof LayoutNode>>;
150
+ }, z.core.$loose>>>;
151
+ queued: z.ZodOptional<z.ZodBoolean>;
152
+ url: z.ZodOptional<z.ZodString>;
153
+ route: z.ZodOptional<z.ZodString>;
154
+ component: z.ZodOptional<z.ZodString>;
155
+ abilities: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
156
+ }, z.core.$loose>;
157
+ export type ActionScope = z.infer<typeof ActionScope>;
158
+ export type ActionPlacement = z.infer<typeof ActionPlacement>;
159
+ export type ActionConfirm = z.infer<typeof ActionConfirm>;
160
+ export type Action = z.infer<typeof Action>;
161
+ /** An action as a backend writes it: `kind` defaults to `server`. */
162
+ export type ActionInput = z.input<typeof Action>;
163
+ //# sourceMappingURL=action.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.d.ts","sourceRoot":"","sources":["../../src/schemas/action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAEtC,eAAO,MAAM,WAAW;;;;EAA4C,CAAC;AAErE,eAAO,MAAM,eAAe;;;;;EAA6C,CAAC;AAE1E,eAAO,MAAM,aAAa;;;;;mBAWxB,CAAC;AAEH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2CwE,CAAC;AAE5F,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AACtD,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAC9D,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAC1D,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC;AAC5C,qEAAqE;AACrE,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { Abilities } from "./common.js";
2
+ import { Field } from "./field.js";
3
+ import { LayoutNode } from "./layout.js";
4
+ import { z } from "zod";
5
+ //#region src/schemas/action.ts
6
+ var ActionScope = z.enum([
7
+ "item",
8
+ "selection",
9
+ "resource"
10
+ ]);
11
+ var ActionPlacement = z.enum([
12
+ "row",
13
+ "toolbar",
14
+ "form",
15
+ "menu"
16
+ ]);
17
+ var ActionConfirm = z.union([z.boolean(), z.looseObject({
18
+ title: z.string().optional(),
19
+ message: z.string().optional().meta({ description: "ICU message. `{count}` is the number of items." }),
20
+ confirmLabel: z.string().optional(),
21
+ cancelLabel: z.string().optional()
22
+ })]);
23
+ var Action = z.looseObject({
24
+ id: z.string().min(1),
25
+ label: z.string(),
26
+ icon: z.string().optional(),
27
+ kind: z.enum([
28
+ "server",
29
+ "link",
30
+ "client"
31
+ ]).default("server").meta({ description: "`server` calls the action endpoint; `link` navigates; `client` runs a plugin handler." }),
32
+ scope: z.array(ActionScope).min(1),
33
+ placement: z.array(ActionPlacement).optional(),
34
+ destructive: z.boolean().optional(),
35
+ confirm: ActionConfirm.optional(),
36
+ fields: z.array(Field).optional().meta({ description: "Parameters, shown in a modal." }),
37
+ layout: z.array(LayoutNode).optional(),
38
+ queued: z.boolean().optional().meta({ description: "The action always answers `202` with a run." }),
39
+ url: z.string().optional().meta({ description: "`link`: URL template, with `{id}` for the item." }),
40
+ route: z.string().optional().meta({ description: "`link`: name of a route in the definition." }),
41
+ component: z.string().optional().meta({ description: "`client`: the plugin handler." }),
42
+ abilities: Abilities.optional().meta({ description: "`run`: whether the current user can run the action on the resource." })
43
+ }).superRefine((action, context) => {
44
+ if (action.kind === "link" && action.url === void 0 && action.route === void 0) context.addIssue({
45
+ code: "custom",
46
+ message: "A `link` action needs a `url` or a `route`."
47
+ });
48
+ if (action.kind === "client" && action.component === void 0) context.addIssue({
49
+ code: "custom",
50
+ message: "A `client` action needs a `component`."
51
+ });
52
+ }).meta({ description: "An operation on one item, a selection, or the whole resource." });
53
+ //#endregion
54
+ export { Action, ActionConfirm, ActionPlacement, ActionScope };
55
+
56
+ //# sourceMappingURL=action.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"action.js","names":[],"sources":["../../src/schemas/action.ts"],"sourcesContent":["import { z } from 'zod';\n\nimport { Abilities } from './common';\nimport { Field } from './field';\nimport { LayoutNode } from './layout';\n\nexport const ActionScope = z.enum(['item', 'selection', 'resource']);\n\nexport const ActionPlacement = z.enum(['row', 'toolbar', 'form', 'menu']);\n\nexport const ActionConfirm = z.union([\n z.boolean(),\n z.looseObject({\n title: z.string().optional(),\n message: z\n .string()\n .optional()\n .meta({ description: 'ICU message. `{count}` is the number of items.' }),\n confirmLabel: z.string().optional(),\n cancelLabel: z.string().optional(),\n }),\n]);\n\nexport const Action = z\n .looseObject({\n id: z.string().min(1),\n label: z.string(),\n icon: z.string().optional(),\n kind: z.enum(['server', 'link', 'client']).default('server').meta({\n description:\n '`server` calls the action endpoint; `link` navigates; `client` runs a plugin handler.',\n }),\n scope: z.array(ActionScope).min(1),\n placement: z.array(ActionPlacement).optional(),\n destructive: z.boolean().optional(),\n confirm: ActionConfirm.optional(),\n fields: z.array(Field).optional().meta({ description: 'Parameters, shown in a modal.' }),\n layout: z.array(LayoutNode).optional(),\n queued: z\n .boolean()\n .optional()\n .meta({ description: 'The action always answers `202` with a run.' }),\n url: z\n .string()\n .optional()\n .meta({ description: '`link`: URL template, with `{id}` for the item.' }),\n route: z\n .string()\n .optional()\n .meta({ description: '`link`: name of a route in the definition.' }),\n component: z.string().optional().meta({ description: '`client`: the plugin handler.' }),\n abilities: Abilities.optional().meta({\n description: '`run`: whether the current user can run the action on the resource.',\n }),\n })\n .superRefine((action, context) => {\n if (action.kind === 'link' && action.url === undefined && action.route === undefined) {\n context.addIssue({\n code: 'custom',\n message: 'A `link` action needs a `url` or a `route`.',\n });\n }\n if (action.kind === 'client' && action.component === undefined) {\n context.addIssue({ code: 'custom', message: 'A `client` action needs a `component`.' });\n }\n })\n .meta({ description: 'An operation on one item, a selection, or the whole resource.' });\n\nexport type ActionScope = z.infer<typeof ActionScope>;\nexport type ActionPlacement = z.infer<typeof ActionPlacement>;\nexport type ActionConfirm = z.infer<typeof ActionConfirm>;\nexport type Action = z.infer<typeof Action>;\n/** An action as a backend writes it: `kind` defaults to `server`. */\nexport type ActionInput = z.input<typeof Action>;\n"],"mappings":";;;;;AAMA,IAAa,cAAc,EAAE,KAAK;CAAC;CAAQ;CAAa;AAAU,CAAC;AAEnE,IAAa,kBAAkB,EAAE,KAAK;CAAC;CAAO;CAAW;CAAQ;AAAM,CAAC;AAExE,IAAa,gBAAgB,EAAE,MAAM,CACjC,EAAE,QAAQ,GACV,EAAE,YAAY;CACV,OAAO,EAAE,OAAO,CAAC,CAAC,SAAS;CAC3B,SAAS,EACJ,OAAO,CAAC,CACR,SAAS,CAAC,CACV,KAAK,EAAE,aAAa,iDAAiD,CAAC;CAC3E,cAAc,EAAE,OAAO,CAAC,CAAC,SAAS;CAClC,aAAa,EAAE,OAAO,CAAC,CAAC,SAAS;AACrC,CAAC,CACL,CAAC;AAED,IAAa,SAAS,EACjB,YAAY;CACT,IAAI,EAAE,OAAO,CAAC,CAAC,IAAI,CAAC;CACpB,OAAO,EAAE,OAAO;CAChB,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS;CAC1B,MAAM,EAAE,KAAK;EAAC;EAAU;EAAQ;CAAQ,CAAC,CAAC,CAAC,QAAQ,QAAQ,CAAC,CAAC,KAAK,EAC9D,aACI,wFACR,CAAC;CACD,OAAO,EAAE,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC;CACjC,WAAW,EAAE,MAAM,eAAe,CAAC,CAAC,SAAS;CAC7C,aAAa,EAAE,QAAQ,CAAC,CAAC,SAAS;CAClC,SAAS,cAAc,SAAS;CAChC,QAAQ,EAAE,MAAM,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,aAAa,gCAAgC,CAAC;CACvF,QAAQ,EAAE,MAAM,UAAU,CAAC,CAAC,SAAS;CACrC,QAAQ,EACH,QAAQ,CAAC,CACT,SAAS,CAAC,CACV,KAAK,EAAE,aAAa,8CAA8C,CAAC;CACxE,KAAK,EACA,OAAO,CAAC,CACR,SAAS,CAAC,CACV,KAAK,EAAE,aAAa,kDAAkD,CAAC;CAC5E,OAAO,EACF,OAAO,CAAC,CACR,SAAS,CAAC,CACV,KAAK,EAAE,aAAa,6CAA6C,CAAC;CACvE,WAAW,EAAE,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,aAAa,gCAAgC,CAAC;CACtF,WAAW,UAAU,SAAS,CAAC,CAAC,KAAK,EACjC,aAAa,sEACjB,CAAC;AACL,CAAC,CAAC,CACD,aAAa,QAAQ,YAAY;CAC9B,IAAI,OAAO,SAAS,UAAU,OAAO,QAAQ,KAAA,KAAa,OAAO,UAAU,KAAA,GACvE,QAAQ,SAAS;EACb,MAAM;EACN,SAAS;CACb,CAAC;CAEL,IAAI,OAAO,SAAS,YAAY,OAAO,cAAc,KAAA,GACjD,QAAQ,SAAS;EAAE,MAAM;EAAU,SAAS;CAAyC,CAAC;AAE9F,CAAC,CAAC,CACD,KAAK,EAAE,aAAa,gEAAgE,CAAC"}