@fieldia/core 0.23.0 → 0.24.0

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 (177) hide show
  1. package/README.md +135 -3
  2. package/package.json +1 -1
  3. package/schema/page.schema.json +159 -4
  4. package/src/index.d.mts +6 -2
  5. package/src/index.d.ts +6 -2
  6. package/src/index.js +18 -1
  7. package/src/index.js.map +1 -1
  8. package/src/index.mjs +8 -2
  9. package/src/index.mjs.map +1 -1
  10. package/src/lib/format/autofill-group.d.mts +12 -0
  11. package/src/lib/format/autofill-group.d.ts +12 -0
  12. package/src/lib/format/autofill-group.js +23 -0
  13. package/src/lib/format/autofill-group.js.map +1 -0
  14. package/src/lib/format/autofill-group.mjs +19 -0
  15. package/src/lib/format/autofill-group.mjs.map +1 -0
  16. package/src/lib/format/autofill.d.mts +14 -0
  17. package/src/lib/format/autofill.d.ts +14 -0
  18. package/src/lib/format/autofill.js +54 -0
  19. package/src/lib/format/autofill.js.map +1 -0
  20. package/src/lib/format/autofill.mjs +48 -0
  21. package/src/lib/format/autofill.mjs.map +1 -0
  22. package/src/lib/format/bulk-edit.d.mts +18 -0
  23. package/src/lib/format/bulk-edit.d.ts +18 -0
  24. package/src/lib/format/bulk-edit.js +26 -0
  25. package/src/lib/format/bulk-edit.js.map +1 -0
  26. package/src/lib/format/bulk-edit.mjs +21 -0
  27. package/src/lib/format/bulk-edit.mjs.map +1 -0
  28. package/src/lib/format/field.d.mts +55 -34
  29. package/src/lib/format/field.d.ts +55 -34
  30. package/src/lib/format/field.js +16 -2
  31. package/src/lib/format/field.js.map +1 -1
  32. package/src/lib/format/field.mjs +15 -1
  33. package/src/lib/format/field.mjs.map +1 -1
  34. package/src/lib/format/layout.d.mts +365 -66
  35. package/src/lib/format/layout.d.ts +365 -66
  36. package/src/lib/format/layout.js +22 -4
  37. package/src/lib/format/layout.js.map +1 -1
  38. package/src/lib/format/layout.mjs +20 -3
  39. package/src/lib/format/layout.mjs.map +1 -1
  40. package/src/lib/format/page.d.mts +169 -29
  41. package/src/lib/format/page.d.ts +169 -29
  42. package/src/lib/format/page.js +11 -0
  43. package/src/lib/format/page.js.map +1 -1
  44. package/src/lib/format/page.mjs +12 -1
  45. package/src/lib/format/page.mjs.map +1 -1
  46. package/src/lib/format/phone.d.mts +27 -0
  47. package/src/lib/format/phone.d.ts +27 -0
  48. package/src/lib/format/phone.js +43 -0
  49. package/src/lib/format/phone.js.map +1 -0
  50. package/src/lib/format/phone.mjs +39 -0
  51. package/src/lib/format/phone.mjs.map +1 -0
  52. package/src/lib/format/references.d.mts +10 -0
  53. package/src/lib/format/references.d.ts +10 -0
  54. package/src/lib/format/references.js +96 -1
  55. package/src/lib/format/references.js.map +1 -1
  56. package/src/lib/format/references.mjs +96 -1
  57. package/src/lib/format/references.mjs.map +1 -1
  58. package/src/lib/format/slots-options.d.mts +26 -0
  59. package/src/lib/format/slots-options.d.ts +26 -0
  60. package/src/lib/format/slots-options.js +35 -0
  61. package/src/lib/format/slots-options.js.map +1 -0
  62. package/src/lib/format/slots-options.mjs +31 -0
  63. package/src/lib/format/slots-options.mjs.map +1 -0
  64. package/src/lib/format/upgrade.d.mts +4 -1
  65. package/src/lib/format/upgrade.d.ts +4 -1
  66. package/src/lib/format/upgrade.js +4 -1
  67. package/src/lib/format/upgrade.js.map +1 -1
  68. package/src/lib/format/upgrade.mjs +4 -1
  69. package/src/lib/format/upgrade.mjs.map +1 -1
  70. package/src/lib/format/version.d.mts +2 -2
  71. package/src/lib/format/version.d.ts +2 -2
  72. package/src/lib/format/version.js +2 -2
  73. package/src/lib/format/version.js.map +1 -1
  74. package/src/lib/format/version.mjs +2 -2
  75. package/src/lib/format/version.mjs.map +1 -1
  76. package/src/lib/format/zoned.d.mts +8 -0
  77. package/src/lib/format/zoned.d.ts +8 -0
  78. package/src/lib/format/zoned.js +36 -0
  79. package/src/lib/format/zoned.js.map +1 -0
  80. package/src/lib/format/zoned.mjs +33 -0
  81. package/src/lib/format/zoned.mjs.map +1 -0
  82. package/src/lib/record/check.d.mts +6 -1
  83. package/src/lib/record/check.d.ts +6 -1
  84. package/src/lib/record/check.js +12 -4
  85. package/src/lib/record/check.js.map +1 -1
  86. package/src/lib/record/check.mjs +12 -4
  87. package/src/lib/record/check.mjs.map +1 -1
  88. package/src/lib/record/data-source.d.mts +188 -3
  89. package/src/lib/record/data-source.d.ts +188 -3
  90. package/src/lib/record/data-source.js.map +1 -1
  91. package/src/lib/record/data-source.mjs.map +1 -1
  92. package/src/lib/record/form.d.mts +72 -25
  93. package/src/lib/record/form.d.ts +72 -25
  94. package/src/lib/record/form.js +404 -78
  95. package/src/lib/record/form.js.map +1 -1
  96. package/src/lib/record/form.mjs +404 -78
  97. package/src/lib/record/form.mjs.map +1 -1
  98. package/src/lib/record/input-rules.d.mts +51 -9
  99. package/src/lib/record/input-rules.d.ts +51 -9
  100. package/src/lib/record/input-rules.js +119 -20
  101. package/src/lib/record/input-rules.js.map +1 -1
  102. package/src/lib/record/input-rules.mjs +118 -19
  103. package/src/lib/record/input-rules.mjs.map +1 -1
  104. package/src/lib/record/inputs.js +4 -2
  105. package/src/lib/record/inputs.js.map +1 -1
  106. package/src/lib/record/inputs.mjs +4 -2
  107. package/src/lib/record/inputs.mjs.map +1 -1
  108. package/src/lib/record/limits.d.mts +1 -1
  109. package/src/lib/record/limits.d.ts +1 -1
  110. package/src/lib/record/limits.js +4 -2
  111. package/src/lib/record/limits.js.map +1 -1
  112. package/src/lib/record/limits.mjs +4 -2
  113. package/src/lib/record/limits.mjs.map +1 -1
  114. package/src/lib/record/locales/ar.js +3 -2
  115. package/src/lib/record/locales/ar.js.map +1 -1
  116. package/src/lib/record/locales/ar.mjs +3 -2
  117. package/src/lib/record/locales/ar.mjs.map +1 -1
  118. package/src/lib/record/locales/de.js +3 -2
  119. package/src/lib/record/locales/de.js.map +1 -1
  120. package/src/lib/record/locales/de.mjs +3 -2
  121. package/src/lib/record/locales/de.mjs.map +1 -1
  122. package/src/lib/record/locales/fr.js +3 -2
  123. package/src/lib/record/locales/fr.js.map +1 -1
  124. package/src/lib/record/locales/fr.mjs +3 -2
  125. package/src/lib/record/locales/fr.mjs.map +1 -1
  126. package/src/lib/record/memory-data-source.d.mts +13 -2
  127. package/src/lib/record/memory-data-source.d.ts +13 -2
  128. package/src/lib/record/memory-data-source.js +92 -35
  129. package/src/lib/record/memory-data-source.js.map +1 -1
  130. package/src/lib/record/memory-data-source.mjs +93 -36
  131. package/src/lib/record/memory-data-source.mjs.map +1 -1
  132. package/src/lib/record/memory-schedules.d.mts +45 -0
  133. package/src/lib/record/memory-schedules.d.ts +45 -0
  134. package/src/lib/record/memory-schedules.js +81 -0
  135. package/src/lib/record/memory-schedules.js.map +1 -0
  136. package/src/lib/record/memory-schedules.mjs +78 -0
  137. package/src/lib/record/memory-schedules.mjs.map +1 -0
  138. package/src/lib/record/messages.d.mts +3 -1
  139. package/src/lib/record/messages.d.ts +3 -1
  140. package/src/lib/record/messages.js +3 -2
  141. package/src/lib/record/messages.js.map +1 -1
  142. package/src/lib/record/messages.mjs +3 -2
  143. package/src/lib/record/messages.mjs.map +1 -1
  144. package/src/lib/record/periods.d.mts +47 -0
  145. package/src/lib/record/periods.d.ts +47 -0
  146. package/src/lib/record/periods.js +86 -0
  147. package/src/lib/record/periods.js.map +1 -0
  148. package/src/lib/record/periods.mjs +79 -0
  149. package/src/lib/record/periods.mjs.map +1 -0
  150. package/src/lib/record/rules.js +5 -6
  151. package/src/lib/record/rules.js.map +1 -1
  152. package/src/lib/record/rules.mjs +5 -6
  153. package/src/lib/record/rules.mjs.map +1 -1
  154. package/src/lib/record/run.d.mts +44 -8
  155. package/src/lib/record/run.d.ts +44 -8
  156. package/src/lib/record/run.js +155 -26
  157. package/src/lib/record/run.js.map +1 -1
  158. package/src/lib/record/run.mjs +156 -27
  159. package/src/lib/record/run.mjs.map +1 -1
  160. package/src/lib/record/search.d.mts +13 -3
  161. package/src/lib/record/search.d.ts +13 -3
  162. package/src/lib/record/search.js +11 -4
  163. package/src/lib/record/search.js.map +1 -1
  164. package/src/lib/record/search.mjs +11 -4
  165. package/src/lib/record/search.mjs.map +1 -1
  166. package/src/lib/record/zones.d.mts +57 -0
  167. package/src/lib/record/zones.d.ts +57 -0
  168. package/src/lib/record/zones.js +147 -0
  169. package/src/lib/record/zones.js.map +1 -0
  170. package/src/lib/record/zones.mjs +132 -0
  171. package/src/lib/record/zones.mjs.map +1 -0
  172. package/src/runtime.d.mts +12 -5
  173. package/src/runtime.d.ts +12 -5
  174. package/src/runtime.js +32 -2
  175. package/src/runtime.js.map +1 -1
  176. package/src/runtime.mjs +7 -1
  177. package/src/runtime.mjs.map +1 -1
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PhoneOptionsSchema = exports.PagePhoneSchema = void 0;
4
+ exports.phoneOptionIssues = phoneOptionIssues;
5
+ const z = require("zod");
6
+ /**
7
+ * Phone numbers read and kept as one number the world over (`"widget": "phone"` on a char field): typed at home
8
+ * ("010 1234 5678") or abroad ("+20 10 1234 5678"), checked against its country's lengths and starts, kept as E.164
9
+ * ("+201012345678"), and shown at rest as the page chooses, with a call and a WhatsApp link beside it.
10
+ *
11
+ * A page says so for all its phone fields (`phone`), or a field for itself (its node's `options`); a page that says
12
+ * neither keeps its phone fields as text boxes, as before. `country` is the one a number typed without its + is read
13
+ * in, by its ISO code — never assumed: left out, a number is written with its + and country code, or its country
14
+ * picked beside it. `shown` writes a number at rest abroad's way ("+20 10 1234 5678", the default) or at home's
15
+ * ("010 1234 5678") — a number of another country always abroad's.
16
+ *
17
+ * "phone": { "country": "EG", "shown": "national" }
18
+ * { "field": "mobile", "widget": "phone", "options": { "country": "AE" } }
19
+ */
20
+ const COUNTRY = /^[A-Z]{2}$/;
21
+ const country = z.string().regex(COUNTRY, 'a country is its ISO code in capitals, such as "EG"');
22
+ const shown = z.enum(['international', 'national'], { error: 'shown is "international" (+20 10 1234 5678) or "national" (010 1234 5678)' });
23
+ /** A page's phone numbers: the country a number typed without its + is read in, and how a number is shown at rest. */
24
+ exports.PagePhoneSchema = z
25
+ .strictObject({
26
+ country: country.optional(),
27
+ shown: shown.optional(),
28
+ })
29
+ .meta({ id: 'PagePhone' });
30
+ /** A phone field's own: the page's settings, for this field. Its other options stay as they are. */
31
+ exports.PhoneOptionsSchema = z
32
+ .object({
33
+ country: country.optional(),
34
+ shown: shown.optional(),
35
+ })
36
+ .meta({ id: 'PhoneOptions' });
37
+ /** The issues of a phone field's settings, each at its path under `options`. */
38
+ function phoneOptionIssues(node) {
39
+ if (node.widget !== 'phone')
40
+ return [];
41
+ return (exports.PhoneOptionsSchema.safeParse(node.options ?? {}).error?.issues ?? []).map((issue) => ({ message: issue.message, path: ['options', ...issue.path] }));
42
+ }
43
+ //# sourceMappingURL=phone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phone.js","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/format/phone.ts"],"names":[],"mappings":";;;AA0CA,8CAGC;AA7CD,yBAAyB;AAEzB;;;;;;;;;;;;;GAaG;AAEH,MAAM,OAAO,GAAG,YAAY,CAAC;AAC7B,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,qDAAqD,CAAC,CAAC;AACjG,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,UAAU,CAAC,EAAE,EAAE,KAAK,EAAE,2EAA2E,EAAE,CAAC,CAAC;AAE5I,sHAAsH;AACzG,QAAA,eAAe,GAAG,CAAC;KAC7B,YAAY,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE;IAC3B,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE;CACxB,CAAC;KACD,IAAI,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;AAI7B,oGAAoG;AACvF,QAAA,kBAAkB,GAAG,CAAC;KAChC,MAAM,CAAC;IACN,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE;IAC3B,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE;CACxB,CAAC;KACD,IAAI,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC;AAIhC,gFAAgF;AAChF,SAAgB,iBAAiB,CAAC,IAA4D;IAC5F,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO;QAAE,OAAO,EAAE,CAAC;IACvC,OAAO,CAAC,0BAAkB,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,SAAS,EAAE,GAAG,KAAK,CAAC,IAAI,CAAwB,EAAE,CAAC,CAAC,CAAC;AACtL,CAAC"}
@@ -0,0 +1,39 @@
1
+ import * as z from 'zod';
2
+ /**
3
+ * Phone numbers read and kept as one number the world over (`"widget": "phone"` on a char field): typed at home
4
+ * ("010 1234 5678") or abroad ("+20 10 1234 5678"), checked against its country's lengths and starts, kept as E.164
5
+ * ("+201012345678"), and shown at rest as the page chooses, with a call and a WhatsApp link beside it.
6
+ *
7
+ * A page says so for all its phone fields (`phone`), or a field for itself (its node's `options`); a page that says
8
+ * neither keeps its phone fields as text boxes, as before. `country` is the one a number typed without its + is read
9
+ * in, by its ISO code — never assumed: left out, a number is written with its + and country code, or its country
10
+ * picked beside it. `shown` writes a number at rest abroad's way ("+20 10 1234 5678", the default) or at home's
11
+ * ("010 1234 5678") — a number of another country always abroad's.
12
+ *
13
+ * "phone": { "country": "EG", "shown": "national" }
14
+ * { "field": "mobile", "widget": "phone", "options": { "country": "AE" } }
15
+ */
16
+ const COUNTRY = /^[A-Z]{2}$/;
17
+ const country = z.string().regex(COUNTRY, 'a country is its ISO code in capitals, such as "EG"');
18
+ const shown = z.enum(['international', 'national'], { error: 'shown is "international" (+20 10 1234 5678) or "national" (010 1234 5678)' });
19
+ /** A page's phone numbers: the country a number typed without its + is read in, and how a number is shown at rest. */
20
+ export const PagePhoneSchema = z
21
+ .strictObject({
22
+ country: country.optional(),
23
+ shown: shown.optional(),
24
+ })
25
+ .meta({ id: 'PagePhone' });
26
+ /** A phone field's own: the page's settings, for this field. Its other options stay as they are. */
27
+ export const PhoneOptionsSchema = z
28
+ .object({
29
+ country: country.optional(),
30
+ shown: shown.optional(),
31
+ })
32
+ .meta({ id: 'PhoneOptions' });
33
+ /** The issues of a phone field's settings, each at its path under `options`. */
34
+ export function phoneOptionIssues(node) {
35
+ if (node.widget !== 'phone')
36
+ return [];
37
+ return (PhoneOptionsSchema.safeParse(node.options ?? {}).error?.issues ?? []).map((issue) => ({ message: issue.message, path: ['options', ...issue.path] }));
38
+ }
39
+ //# sourceMappingURL=phone.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"phone.mjs","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/format/phone.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB;;;;;;;;;;;;;GAaG;AAEH,MAAM,OAAO,GAAG,YAAY,CAAC;AAC7B,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,qDAAqD,CAAC,CAAC;AACjG,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,eAAe,EAAE,UAAU,CAAC,EAAE,EAAE,KAAK,EAAE,2EAA2E,EAAE,CAAC,CAAC;AAE5I,sHAAsH;AACtH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC;KAC7B,YAAY,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE;IAC3B,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE;CACxB,CAAC;KACD,IAAI,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;AAI7B,oGAAoG;AACpG,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC;KAChC,MAAM,CAAC;IACN,OAAO,EAAE,OAAO,CAAC,QAAQ,EAAE;IAC3B,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE;CACxB,CAAC;KACD,IAAI,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC;AAIhC,gFAAgF;AAChF,MAAM,UAAU,iBAAiB,CAAC,IAA4D;IAC5F,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO;QAAE,OAAO,EAAE,CAAC;IACvC,OAAO,CAAC,kBAAkB,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,SAAS,EAAE,GAAG,KAAK,CAAC,IAAI,CAAwB,EAAE,CAAC,CAAC,CAAC;AACtL,CAAC"}
@@ -34,8 +34,14 @@ export declare class ReferenceCheck {
34
34
  /** The ids of the tabs and wizard steps a step can go to, and where each goTo and show names one. */
35
35
  private readonly places;
36
36
  private readonly goingTo;
37
+ /** The names of the slots the layout places. */
38
+ private readonly slots;
37
39
  constructor(page: Page, line?: LineOf | undefined);
38
40
  run(): PageIssue[];
41
+ /** A bot check: on a page of responses, its captcha drawn in a slot the page places. */
42
+ private checkBotCheck;
43
+ /** A time zone the browser knows, by its IANA name: a page's dates and times are never drawn on a clock it cannot read. */
44
+ private checkZone;
39
45
  /** Each step's goTo and show names a tab or a wizard step of this page. */
40
46
  private checkGoingTo;
41
47
  /** Where the page's own expressions read: its fields — on a line's form, the line's, with its record as parent. */
@@ -105,6 +111,8 @@ export declare class ReferenceCheck {
105
111
  * the rows chosen need a box to choose one.
106
112
  */
107
113
  private checkListCells;
114
+ /** A list's Edit: each field it names one value can set in many records, and rows to choose to set it on. */
115
+ private checkBulkEdit;
108
116
  /** A list's map: where each record is, and the fields its pin and its card show, each of a type that can. */
109
117
  private walkListMap;
110
118
  /**
@@ -126,6 +134,8 @@ export declare class ReferenceCheck {
126
134
  private walkSheet;
127
135
  /** An address with suggestions: a char field, its place kept as a place field keeps one, its parts in a json field. */
128
136
  private checkAddressSearch;
137
+ /** Booking slots: on a date and time, its end written in another, asked again as the fields it depends on change. */
138
+ private checkSlots;
129
139
  /** A picker's own settings, on the kind of field each is for: a path on a link, Fieldia's own list on a choice, a button adding tags, a colour for each state. */
130
140
  private checkPicker;
131
141
  private checkFieldNode;
@@ -34,8 +34,14 @@ export declare class ReferenceCheck {
34
34
  /** The ids of the tabs and wizard steps a step can go to, and where each goTo and show names one. */
35
35
  private readonly places;
36
36
  private readonly goingTo;
37
+ /** The names of the slots the layout places. */
38
+ private readonly slots;
37
39
  constructor(page: Page, line?: LineOf | undefined);
38
40
  run(): PageIssue[];
41
+ /** A bot check: on a page of responses, its captcha drawn in a slot the page places. */
42
+ private checkBotCheck;
43
+ /** A time zone the browser knows, by its IANA name: a page's dates and times are never drawn on a clock it cannot read. */
44
+ private checkZone;
39
45
  /** Each step's goTo and show names a tab or a wizard step of this page. */
40
46
  private checkGoingTo;
41
47
  /** Where the page's own expressions read: its fields — on a line's form, the line's, with its record as parent. */
@@ -105,6 +111,8 @@ export declare class ReferenceCheck {
105
111
  * the rows chosen need a box to choose one.
106
112
  */
107
113
  private checkListCells;
114
+ /** A list's Edit: each field it names one value can set in many records, and rows to choose to set it on. */
115
+ private checkBulkEdit;
108
116
  /** A list's map: where each record is, and the fields its pin and its card show, each of a type that can. */
109
117
  private walkListMap;
110
118
  /**
@@ -126,6 +134,8 @@ export declare class ReferenceCheck {
126
134
  private walkSheet;
127
135
  /** An address with suggestions: a char field, its place kept as a place field keeps one, its parts in a json field. */
128
136
  private checkAddressSearch;
137
+ /** Booking slots: on a date and time, its end written in another, asked again as the fields it depends on change. */
138
+ private checkSlots;
129
139
  /** A picker's own settings, on the kind of field each is for: a path on a link, Fieldia's own list on a choice, a button adding tags, a colour for each state. */
130
140
  private checkPicker;
131
141
  private checkFieldNode;
@@ -10,7 +10,10 @@ const hotkeys_1 = require("./hotkeys");
10
10
  const head_checks_1 = require("./head-checks");
11
11
  const section_checks_1 = require("./section-checks");
12
12
  const pattern_1 = require("./pattern");
13
+ const bulk_edit_1 = require("./bulk-edit");
14
+ const zones_1 = require("../record/zones");
13
15
  const messages_1 = require("../record/messages");
16
+ const autofill_group_1 = require("./autofill-group");
14
17
  const has = (record, key) => Object.prototype.hasOwnProperty.call(record, key);
15
18
  /** The kinds of field an expression can give a value to: the rest hold records, lines or files. */
16
19
  const EXPRESSIBLE = new Set(['char', 'text', 'html', 'integer', 'float', 'monetary', 'boolean', 'date', 'datetime', 'selection', 'json']);
@@ -41,11 +44,15 @@ class ReferenceCheck {
41
44
  /** The ids of the tabs and wizard steps a step can go to, and where each goTo and show names one. */
42
45
  places = new Set();
43
46
  goingTo = [];
47
+ /** The names of the slots the layout places. */
48
+ slots = new Set();
44
49
  constructor(page, line) {
45
50
  this.page = page;
46
51
  this.line = line;
47
52
  }
48
53
  run() {
54
+ if (this.page.timeZone !== undefined)
55
+ this.checkZone(this.page.timeZone, 'timeZone');
49
56
  this.checkFields(this.page.fields, 'fields', this.line?.name, this.line?.parent);
50
57
  this.walkRoot(this.page.layout, 'layout');
51
58
  (0, head_checks_1.checkHead)(this.page.layout, 'layout', {
@@ -61,9 +68,25 @@ class ReferenceCheck {
61
68
  });
62
69
  if (this.page.on)
63
70
  this.checkEvents(this.page.on, 'on');
71
+ if (this.page.botCheck !== undefined && !this.line)
72
+ this.checkBotCheck(this.page);
64
73
  this.checkGoingTo();
65
74
  return this.issues;
66
75
  }
76
+ /** A bot check: on a page of responses, its captcha drawn in a slot the page places. */
77
+ checkBotCheck(page) {
78
+ const check = page.botCheck;
79
+ if (page.data.kind !== 'responses')
80
+ return this.report('botCheck', 'a bot check is for a page of responses that anyone may send; a record’s page is behind the app’s own sign-in');
81
+ const captcha = check?.captcha;
82
+ if (typeof captcha === 'string' && !this.slots.has(captcha))
83
+ this.report('botCheck.captcha', `no slot "${captcha}" on this page to draw the captcha in: place { "type": "slot", "id": "${captcha}", "name": "${captcha}" } where it goes`);
84
+ }
85
+ /** A time zone the browser knows, by its IANA name: a page's dates and times are never drawn on a clock it cannot read. */
86
+ checkZone(zone, path) {
87
+ if (!(0, zones_1.isTimeZone)(zone))
88
+ this.report(path, `${JSON.stringify(zone)} is not a time zone this browser knows: name one as the IANA does, such as "Africa/Cairo"`);
89
+ }
67
90
  /** Each step's goTo and show names a tab or a wizard step of this page. */
68
91
  checkGoingTo() {
69
92
  for (const { target, path } of this.goingTo) {
@@ -395,6 +418,14 @@ class ReferenceCheck {
395
418
  if (pattern instanceof Error)
396
419
  this.report(`${path}.pattern`, (0, messages_1.fill)(messages_1.MESSAGES.en.patternBroken, { label: def.label, problem: pattern.message }));
397
420
  }
421
+ if (def.type === 'datetime' && def.timeZone !== undefined)
422
+ this.checkZone(def.timeZone, `${path}.timeZone`);
423
+ // A linked record's date and time, in a table of the records a many2many links to.
424
+ if (def.type === 'many2many' && def.fields) {
425
+ for (const [column, linked] of Object.entries(def.fields))
426
+ if (linked.type === 'datetime' && linked.timeZone !== undefined)
427
+ this.checkZone(linked.timeZone, `${path}.fields.${column}.timeZone`);
428
+ }
398
429
  if (def.type === 'selection')
399
430
  def.optionsFrom?.dependsOn?.forEach((other, i) => this.need(other, `${path}.optionsFrom.dependsOn[${i}]`, fields));
400
431
  if (def.type === 'properties')
@@ -511,7 +542,11 @@ class ReferenceCheck {
511
542
  list.filters?.forEach((filter, i) => {
512
543
  this.claim(filter.id, `${path}.filters[${i}]`);
513
544
  named.add(filter.id);
514
- filter.filter.forEach((item, j) => walk(item, `${path}.filters[${i}].filter[${j}]`));
545
+ filter.filter?.forEach((item, j) => walk(item, `${path}.filters[${i}].filter[${j}]`));
546
+ // A period is of a date, or a date and time.
547
+ const dated = filter.period ? this.need(filter.period.field, `${path}.filters[${i}].period.field`) : undefined;
548
+ if (dated && dated.type !== 'date' && dated.type !== 'datetime')
549
+ this.report(`${path}.filters[${i}].period.field`, `"${filter.period?.field}" is a ${dated.type}; a period is of a date, or a date and time`);
515
550
  });
516
551
  list.defaultFilters?.forEach((id, i) => {
517
552
  if (!named.has(id))
@@ -576,6 +611,11 @@ class ReferenceCheck {
576
611
  this.report(at, `"${column}" is no column of this list: its cells are for its columns`);
577
612
  continue;
578
613
  }
614
+ // A sum, an average, the lowest and the highest are a number column's; any column has a count.
615
+ const kind = this.page.fields[column]?.type;
616
+ if (cell.total && cell.total !== 'count' && kind && kind !== 'integer' && kind !== 'float' && kind !== 'monetary') {
617
+ this.report(`${at}.total`, `"${column}" is a ${kind}; its total "${cell.total}" is of numbers — an integer, a float or an amount — and any column's is "count"`);
618
+ }
579
619
  if (!cell.lead)
580
620
  continue;
581
621
  const def = this.need(cell.lead.field, `${at}.lead.field`);
@@ -607,6 +647,32 @@ class ReferenceCheck {
607
647
  }
608
648
  if (list.choose === false && list.actions?.length)
609
649
  this.report(`${path}.actions`, 'a list’s buttons act on the rows chosen: with "choose": false there is no way to choose one');
650
+ this.checkBulkEdit(list, path);
651
+ }
652
+ /** A list's Edit: each field it names one value can set in many records, and rows to choose to set it on. */
653
+ checkBulkEdit(list, path) {
654
+ if (!list.bulkEdit)
655
+ return;
656
+ if (list.choose === false)
657
+ this.report(`${path}.bulkEdit`, 'Edit changes the rows chosen: with "choose": false there is no way to choose one');
658
+ if (list.bulkEdit === true) {
659
+ if (!list.columns.some((name) => (0, bulk_edit_1.bulkSettable)(this.page.fields[name])))
660
+ this.report(`${path}.bulkEdit`, 'Edit offers the columns one value can set, and none of these can: name the fields it offers');
661
+ return;
662
+ }
663
+ const named = list.bulkEdit;
664
+ named.forEach((name, i) => {
665
+ const at = `${path}.bulkEdit[${i}]`;
666
+ if (named.indexOf(name) !== i)
667
+ this.report(at, `"${name}" is offered twice`);
668
+ const def = this.need(name, at);
669
+ if (!def)
670
+ return;
671
+ if (!bulk_edit_1.BULK_KINDS.has(def.type))
672
+ this.report(at, `"${name}" is a ${def.type}; one value set in many records is words, a number, money, a yes or no, a date, a choice or a link`);
673
+ else if (def.readonly === true || def.compute)
674
+ this.report(at, `"${name}" is ${def.compute ? 'worked out from other fields' : 'read-only'}; Edit sets a field people can change`);
675
+ });
610
676
  }
611
677
  /** A list's map: where each record is, and the fields its pin and its card show, each of a type that can. */
612
678
  walkListMap(map, path) {
@@ -802,6 +868,9 @@ class ReferenceCheck {
802
868
  return this.checkValuesIn(node.text, `${path}.text`);
803
869
  case 'map':
804
870
  return this.walkMap(node, path);
871
+ case 'slot':
872
+ this.slots.add(node.name);
873
+ return;
805
874
  default:
806
875
  return;
807
876
  }
@@ -959,6 +1028,23 @@ class ReferenceCheck {
959
1028
  if (parts && parts !== 'json')
960
1029
  this.report(`${path}.options.partsField`, `"${options['partsField']}" is a ${parts}; an address's parts are kept in a json field`);
961
1030
  }
1031
+ /** Booking slots: on a date and time, its end written in another, asked again as the fields it depends on change. */
1032
+ checkSlots(node, def, path) {
1033
+ const options = node.options ?? {};
1034
+ if (def && def.type !== 'datetime')
1035
+ this.report(`${path}.widget`, `slots is for a datetime field; "${node.field}" is a ${def.type}`);
1036
+ const end = options['endField'];
1037
+ if (typeof end === 'string' && has(this.page.fields, end)) {
1038
+ const type = this.page.fields[end].type;
1039
+ if (end === node.field)
1040
+ this.report(`${path}.options.endField`, 'a booking ends in another field than the one it starts in');
1041
+ else if (type !== 'datetime')
1042
+ this.report(`${path}.options.endField`, `"${end}" is a ${type}; a booking's end is kept in a datetime field`);
1043
+ }
1044
+ const on = options['dependsOn'];
1045
+ if (Array.isArray(on))
1046
+ on.forEach((name, i) => typeof name === 'string' && this.need(name, `${path}.options.dependsOn[${i}]`));
1047
+ }
962
1048
  /** A picker's own settings, on the kind of field each is for: a path on a link, Fieldia's own list on a choice, a button adding tags, a colour for each state. */
963
1049
  checkPicker(node, def, path) {
964
1050
  const options = node.options ?? {};
@@ -1004,6 +1090,13 @@ class ReferenceCheck {
1004
1090
  node.tones?.forEach((tone, i) => this.checkModifiers(tone, `${path}.tones[${i}]`, ['when']));
1005
1091
  node.placeholderWhen?.forEach((item, i) => this.checkModifiers(item, `${path}.placeholderWhen[${i}]`, ['when']));
1006
1092
  this.checkModifiers(node, path, ['bold']);
1093
+ // What the browser fills its box in with: a box names what it holds, an address in parts only whose it is. (That
1094
+ // it is a purpose the standard lists is the format's check, `validatePage`, as other shapes are.)
1095
+ if (typeof node.autocomplete === 'string') {
1096
+ const problem = (0, autofill_group_1.autofillGroupProblem)(node.autocomplete, node.widget === 'address');
1097
+ if (problem)
1098
+ this.report(`${path}.autocomplete`, problem);
1099
+ }
1007
1100
  if (def && node.totals) {
1008
1101
  if (def.type !== 'one2many') {
1009
1102
  this.report(`${path}.totals`, `totals only apply to one2many fields; "${node.field}" is a ${def.type}`);
@@ -1026,6 +1119,8 @@ class ReferenceCheck {
1026
1119
  }
1027
1120
  if (node.widget === 'address-search')
1028
1121
  this.checkAddressSearch(node, def, path);
1122
+ if (node.widget === 'slots')
1123
+ this.checkSlots(node, def, path);
1029
1124
  if (def)
1030
1125
  this.checkPicker(node, def, path);
1031
1126
  // The order lines load in: fields of its lines.