@fieldia/core 0.24.0 → 0.25.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 (117) hide show
  1. package/README.md +77 -1
  2. package/package.json +1 -1
  3. package/schema/page.schema.json +951 -380
  4. package/src/index.d.mts +5 -2
  5. package/src/index.d.ts +5 -2
  6. package/src/index.js +14 -1
  7. package/src/index.js.map +1 -1
  8. package/src/index.mjs +6 -2
  9. package/src/index.mjs.map +1 -1
  10. package/src/lib/format/actions.d.mts +31 -1
  11. package/src/lib/format/actions.d.ts +31 -1
  12. package/src/lib/format/actions.js +1 -0
  13. package/src/lib/format/actions.js.map +1 -1
  14. package/src/lib/format/actions.mjs +1 -0
  15. package/src/lib/format/actions.mjs.map +1 -1
  16. package/src/lib/format/duplicate-kinds.d.mts +10 -0
  17. package/src/lib/format/duplicate-kinds.d.ts +10 -0
  18. package/src/lib/format/duplicate-kinds.js +14 -0
  19. package/src/lib/format/duplicate-kinds.js.map +1 -0
  20. package/src/lib/format/duplicate-kinds.mjs +10 -0
  21. package/src/lib/format/duplicate-kinds.mjs.map +1 -0
  22. package/src/lib/format/duplicates.d.mts +22 -0
  23. package/src/lib/format/duplicates.d.ts +22 -0
  24. package/src/lib/format/duplicates.js +24 -0
  25. package/src/lib/format/duplicates.js.map +1 -0
  26. package/src/lib/format/duplicates.mjs +21 -0
  27. package/src/lib/format/duplicates.mjs.map +1 -0
  28. package/src/lib/format/field.d.mts +236 -0
  29. package/src/lib/format/field.d.ts +236 -0
  30. package/src/lib/format/field.js +4 -3
  31. package/src/lib/format/field.js.map +1 -1
  32. package/src/lib/format/field.mjs +2 -1
  33. package/src/lib/format/field.mjs.map +1 -1
  34. package/src/lib/format/import-fields.d.mts +18 -0
  35. package/src/lib/format/import-fields.d.ts +18 -0
  36. package/src/lib/format/import-fields.js +26 -0
  37. package/src/lib/format/import-fields.js.map +1 -0
  38. package/src/lib/format/import-fields.mjs +21 -0
  39. package/src/lib/format/import-fields.mjs.map +1 -0
  40. package/src/lib/format/layout.d.mts +5453 -1522
  41. package/src/lib/format/layout.d.ts +5453 -1522
  42. package/src/lib/format/layout.js +90 -32
  43. package/src/lib/format/layout.js.map +1 -1
  44. package/src/lib/format/layout.mjs +89 -31
  45. package/src/lib/format/layout.mjs.map +1 -1
  46. package/src/lib/format/layout.types.d.mts +5 -2
  47. package/src/lib/format/layout.types.d.ts +5 -2
  48. package/src/lib/format/page.d.mts +961 -127
  49. package/src/lib/format/page.d.ts +961 -127
  50. package/src/lib/format/page.js +14 -0
  51. package/src/lib/format/page.js.map +1 -1
  52. package/src/lib/format/page.mjs +14 -0
  53. package/src/lib/format/page.mjs.map +1 -1
  54. package/src/lib/format/references.d.mts +29 -0
  55. package/src/lib/format/references.d.ts +29 -0
  56. package/src/lib/format/references.js +172 -24
  57. package/src/lib/format/references.js.map +1 -1
  58. package/src/lib/format/references.mjs +172 -24
  59. package/src/lib/format/references.mjs.map +1 -1
  60. package/src/lib/format/translate.js +8 -4
  61. package/src/lib/format/translate.js.map +1 -1
  62. package/src/lib/format/translate.mjs +8 -4
  63. package/src/lib/format/translate.mjs.map +1 -1
  64. package/src/lib/format/upgrade.d.mts +5 -3
  65. package/src/lib/format/upgrade.d.ts +5 -3
  66. package/src/lib/format/upgrade.js +5 -3
  67. package/src/lib/format/upgrade.js.map +1 -1
  68. package/src/lib/format/upgrade.mjs +5 -3
  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/record/data-source.d.mts +121 -0
  77. package/src/lib/record/data-source.d.ts +121 -0
  78. package/src/lib/record/data-source.js +16 -0
  79. package/src/lib/record/data-source.js.map +1 -1
  80. package/src/lib/record/data-source.mjs +15 -0
  81. package/src/lib/record/data-source.mjs.map +1 -1
  82. package/src/lib/record/form.d.mts +45 -1
  83. package/src/lib/record/form.d.ts +45 -1
  84. package/src/lib/record/form.js +53 -0
  85. package/src/lib/record/form.js.map +1 -1
  86. package/src/lib/record/form.mjs +53 -0
  87. package/src/lib/record/form.mjs.map +1 -1
  88. package/src/lib/record/memory-data-source.d.mts +12 -5
  89. package/src/lib/record/memory-data-source.d.ts +12 -5
  90. package/src/lib/record/memory-data-source.js +61 -0
  91. package/src/lib/record/memory-data-source.js.map +1 -1
  92. package/src/lib/record/memory-data-source.mjs +62 -1
  93. package/src/lib/record/memory-data-source.mjs.map +1 -1
  94. package/src/lib/record/memory-duplicates.d.mts +19 -0
  95. package/src/lib/record/memory-duplicates.d.ts +19 -0
  96. package/src/lib/record/memory-duplicates.js +78 -0
  97. package/src/lib/record/memory-duplicates.js.map +1 -0
  98. package/src/lib/record/memory-duplicates.mjs +74 -0
  99. package/src/lib/record/memory-duplicates.mjs.map +1 -0
  100. package/src/lib/record/run.d.mts +31 -1
  101. package/src/lib/record/run.d.ts +31 -1
  102. package/src/lib/record/run.js +15 -1
  103. package/src/lib/record/run.js.map +1 -1
  104. package/src/lib/record/run.mjs +15 -1
  105. package/src/lib/record/run.mjs.map +1 -1
  106. package/src/lib/record/waits.d.mts +32 -0
  107. package/src/lib/record/waits.d.ts +32 -0
  108. package/src/lib/record/waits.js +89 -0
  109. package/src/lib/record/waits.js.map +1 -0
  110. package/src/lib/record/waits.mjs +85 -0
  111. package/src/lib/record/waits.mjs.map +1 -0
  112. package/src/runtime.d.mts +10 -7
  113. package/src/runtime.d.ts +10 -7
  114. package/src/runtime.js +8 -3
  115. package/src/runtime.js.map +1 -1
  116. package/src/runtime.mjs +2 -1
  117. package/src/runtime.mjs.map +1 -1
@@ -1,11 +1,11 @@
1
1
  /** The page format version this build reads and writes. */
2
- export declare const FORMAT_VERSION: "0.3";
2
+ export declare const FORMAT_VERSION: "0.4";
3
3
  /**
4
4
  * Every format Fieldia has had, oldest first; the last is FORMAT_VERSION. A page in an older one is brought up to date
5
5
  * by `upgradePage`, step by step (`PAGE_UPGRADES`), wherever it is read: `checkPage`, `validatePage`, the viewer, the
6
6
  * designer. A release that changes what a page may say adds its version here (see the format's docs, "Versions").
7
7
  */
8
- export declare const FORMAT_VERSIONS: readonly ["0.1", "0.2", "0.3"];
8
+ export declare const FORMAT_VERSIONS: readonly ["0.1", "0.2", "0.3", "0.4"];
9
9
  /** A format Fieldia has had: a page may say any of them, and is read in the current one. */
10
10
  export type FormatVersion = (typeof FORMAT_VERSIONS)[number];
11
11
  /** Which of two format versions is newer: below 0 when `a` is older, 0 when they are the same, null when either is not a version. */
@@ -4,13 +4,13 @@ exports.FORMAT_VERSIONS = exports.FORMAT_VERSION = void 0;
4
4
  exports.compareFormats = compareFormats;
5
5
  exports.formatProblem = formatProblem;
6
6
  /** The page format version this build reads and writes. */
7
- exports.FORMAT_VERSION = '0.3';
7
+ exports.FORMAT_VERSION = '0.4';
8
8
  /**
9
9
  * Every format Fieldia has had, oldest first; the last is FORMAT_VERSION. A page in an older one is brought up to date
10
10
  * by `upgradePage`, step by step (`PAGE_UPGRADES`), wherever it is read: `checkPage`, `validatePage`, the viewer, the
11
11
  * designer. A release that changes what a page may say adds its version here (see the format's docs, "Versions").
12
12
  */
13
- exports.FORMAT_VERSIONS = ['0.1', '0.2', '0.3'];
13
+ exports.FORMAT_VERSIONS = ['0.1', '0.2', '0.3', '0.4'];
14
14
  /** `major.minor` as two numbers, or null when it is not a version at all. */
15
15
  function parts(version) {
16
16
  const match = typeof version === 'string' ? /^(\d+)\.(\d+)$/.exec(version) : null;
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/format/version.ts"],"names":[],"mappings":";;;AAoBA,wCAIC;AAMD,sCAQC;AAtCD,2DAA2D;AAC9C,QAAA,cAAc,GAAG,KAAc,CAAC;AAE7C;;;;GAIG;AACU,QAAA,eAAe,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAsC,CAAC;AAK1F,6EAA6E;AAC7E,SAAS,KAAK,CAAC,OAAgB;IAC7B,MAAM,KAAK,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAClF,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC;AAED,qIAAqI;AACrI,SAAgB,cAAc,CAAC,CAAU,EAAE,CAAU;IACnD,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1B,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,SAAgB,aAAa,CAAC,OAAgB,EAAE,WAA8B,uBAAe;IAC3F,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC9C,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAClD,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,wDAAwD,OAAO,oBAAoB,CAAC;IAClH,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,0BAA0B,OAAiB,oCAAoC,OAAO,8BAA8B,CAAC;IAC9I,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAiB,CAAC;QAAE,OAAO,0BAA0B,OAAiB,oCAAoC,OAAO,EAAE,CAAC;IAC1I,OAAO,UAAU,OAAiB,mDAAmD,OAAO,EAAE,CAAC;AACjG,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/format/version.ts"],"names":[],"mappings":";;;AAoBA,wCAIC;AAMD,sCAQC;AAtCD,2DAA2D;AAC9C,QAAA,cAAc,GAAG,KAAc,CAAC;AAE7C;;;;GAIG;AACU,QAAA,eAAe,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAsC,CAAC;AAKjG,6EAA6E;AAC7E,SAAS,KAAK,CAAC,OAAgB;IAC7B,MAAM,KAAK,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAClF,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC;AAED,qIAAqI;AACrI,SAAgB,cAAc,CAAC,CAAU,EAAE,CAAU;IACnD,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1B,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,SAAgB,aAAa,CAAC,OAAgB,EAAE,WAA8B,uBAAe;IAC3F,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC9C,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAClD,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,wDAAwD,OAAO,oBAAoB,CAAC;IAClH,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,0BAA0B,OAAiB,oCAAoC,OAAO,8BAA8B,CAAC;IAC9I,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAiB,CAAC;QAAE,OAAO,0BAA0B,OAAiB,oCAAoC,OAAO,EAAE,CAAC;IAC1I,OAAO,UAAU,OAAiB,mDAAmD,OAAO,EAAE,CAAC;AACjG,CAAC"}
@@ -1,11 +1,11 @@
1
1
  /** The page format version this build reads and writes. */
2
- export const FORMAT_VERSION = '0.3';
2
+ export const FORMAT_VERSION = '0.4';
3
3
  /**
4
4
  * Every format Fieldia has had, oldest first; the last is FORMAT_VERSION. A page in an older one is brought up to date
5
5
  * by `upgradePage`, step by step (`PAGE_UPGRADES`), wherever it is read: `checkPage`, `validatePage`, the viewer, the
6
6
  * designer. A release that changes what a page may say adds its version here (see the format's docs, "Versions").
7
7
  */
8
- export const FORMAT_VERSIONS = ['0.1', '0.2', '0.3'];
8
+ export const FORMAT_VERSIONS = ['0.1', '0.2', '0.3', '0.4'];
9
9
  /** `major.minor` as two numbers, or null when it is not a version at all. */
10
10
  function parts(version) {
11
11
  const match = typeof version === 'string' ? /^(\d+)\.(\d+)$/.exec(version) : null;
@@ -1 +1 @@
1
- {"version":3,"file":"version.mjs","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/format/version.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,MAAM,CAAC,MAAM,cAAc,GAAG,KAAc,CAAC;AAE7C;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAsC,CAAC;AAK1F,6EAA6E;AAC7E,SAAS,KAAK,CAAC,OAAgB;IAC7B,MAAM,KAAK,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAClF,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC;AAED,qIAAqI;AACrI,MAAM,UAAU,cAAc,CAAC,CAAU,EAAE,CAAU;IACnD,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1B,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,OAAgB,EAAE,WAA8B,eAAe;IAC3F,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC9C,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAClD,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,wDAAwD,OAAO,oBAAoB,CAAC;IAClH,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,0BAA0B,OAAiB,oCAAoC,OAAO,8BAA8B,CAAC;IAC9I,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAiB,CAAC;QAAE,OAAO,0BAA0B,OAAiB,oCAAoC,OAAO,EAAE,CAAC;IAC1I,OAAO,UAAU,OAAiB,mDAAmD,OAAO,EAAE,CAAC;AACjG,CAAC"}
1
+ {"version":3,"file":"version.mjs","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/format/version.ts"],"names":[],"mappings":"AAAA,2DAA2D;AAC3D,MAAM,CAAC,MAAM,cAAc,GAAG,KAAc,CAAC;AAE7C;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAsC,CAAC;AAKjG,6EAA6E;AAC7E,SAAS,KAAK,CAAC,OAAgB;IAC7B,MAAM,KAAK,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAClF,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7D,CAAC;AAED,qIAAqI;AACrI,MAAM,UAAU,cAAc,CAAC,CAAU,EAAE,CAAU;IACnD,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1B,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,OAAgB,EAAE,WAA8B,eAAe;IAC3F,MAAM,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC9C,IAAI,OAAO,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAClD,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,wDAAwD,OAAO,oBAAoB,CAAC;IAClH,IAAI,QAAQ,GAAG,CAAC;QAAE,OAAO,0BAA0B,OAAiB,oCAAoC,OAAO,8BAA8B,CAAC;IAC9I,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAiB,CAAC;QAAE,OAAO,0BAA0B,OAAiB,oCAAoC,OAAO,EAAE,CAAC;IAC1I,OAAO,UAAU,OAAiB,mDAAmD,OAAO,EAAE,CAAC;AACjG,CAAC"}
@@ -99,6 +99,121 @@ export interface DataSource {
99
99
  exportRecords?(request: ExportRequest): Promise<ExportedFile | {
100
100
  message: string;
101
101
  } | false>;
102
+ /**
103
+ * Make many records at once, each with its own values: a list's Import of
104
+ * a file's rows, a thousand at a time. Answer for each record asked for,
105
+ * in its order: the id it was made with, or why it was not made, in the
106
+ * person's words. Without it, the list saves each in turn (`save` with no
107
+ * id).
108
+ */
109
+ createMany?(request: CreateManyRequest): Promise<CreateManyResult>;
110
+ /**
111
+ * Take a list's Import on the server, in place of the browser: a file too
112
+ * big for one, or a server that imports its own way. Asked once the person
113
+ * has matched the file's columns to the page's fields, before a row is
114
+ * checked: answer `{ message }` when the server takes the file (the list
115
+ * says it: "Your file is being imported: you will get an email"), or
116
+ * `false` to let the browser import it after all, as it does without this.
117
+ */
118
+ importRecords?(request: ImportRequest): Promise<{
119
+ message: string;
120
+ } | false>;
121
+ /**
122
+ * Records like the one typed, for a page's possible duplicates (`duplicates`): asked a moment after a person stops
123
+ * typing in a field it names, as a saved record is opened, and as a record is saved — with the fields it names that
124
+ * hold a value. Answer with the records that have the same phone, email or id, or a name close enough, each with
125
+ * the fields it matched on; never the record itself. Without it, nothing is asked and nothing shows.
126
+ */
127
+ duplicates?(request: DuplicatesRequest): Promise<DuplicatesResult>;
128
+ /**
129
+ * Merge records into one, as a person chose on a possible duplicate's Merge: `keepId` kept, holding the values
130
+ * picked for each field, and `mergeIds` gone. Moving what was theirs — messages, activities, the records that link
131
+ * to them — to the one kept is the source's work; the page says it will happen. Refuse with `saveRefused` (a `rule`,
132
+ * with why) and nothing changes. Without it, possible duplicates offer no Merge.
133
+ */
134
+ merge?(request: MergeRequest): Promise<MergeResult | void>;
135
+ }
136
+ /** Many records made at once, each with its own values: a list's Import. */
137
+ export interface CreateManyRequest {
138
+ model: string;
139
+ /** Each record's values, in the file's order: what its row fills in, and the page's defaults for the rest. Links as `{ id, label }`. */
140
+ records: Values[];
141
+ /** The fields' definitions, as a save gets them. */
142
+ fields: Fields;
143
+ }
144
+ export interface CreateManyResult {
145
+ /**
146
+ * One answer for each record asked for, in the same order: the id it was made with, or why it was not made — and,
147
+ * where the source has it, the problem as a save would throw it (a field's own words). A record left unanswered is
148
+ * taken as not made.
149
+ */
150
+ records: ({
151
+ id: RecordId;
152
+ } | {
153
+ message: string;
154
+ problem?: SaveProblem;
155
+ })[];
156
+ }
157
+ /** A file a list's Import hands to the server, its columns matched to the page's fields. */
158
+ export interface ImportRequest {
159
+ model: string;
160
+ /** The file as the person picked it: a browser's File fits. */
161
+ file: UploadFile;
162
+ /** Each column of the file, in its order — its header's words, and the field it fills, null where it is left out. */
163
+ columns: {
164
+ header: string;
165
+ field: string | null;
166
+ }[];
167
+ /** How many rows it holds under its header. */
168
+ rows: number;
169
+ /** The fields' definitions, as a save gets them. */
170
+ fields: Fields;
171
+ /** The page's language and the order it writes dates in, so a server reads the cells as the page would. */
172
+ locale: string;
173
+ dateOrder: 'dmy' | 'mdy' | 'ymd';
174
+ }
175
+ /** Records like the one typed: what `duplicates` is asked. */
176
+ export interface DuplicatesRequest {
177
+ model: string;
178
+ /** The record typed, never its own duplicate; null while it is new. */
179
+ recordId: RecordId | null;
180
+ /** Every value of the form, as typed: a phone kept as E.164 where the page reads phones the world over. */
181
+ values: Values;
182
+ /** The fields to compare it by, by name: those the page names (`duplicates.fields`) that hold a value now. */
183
+ fields: string[];
184
+ }
185
+ /** A record like the one typed. */
186
+ export interface DuplicateMatch {
187
+ id: RecordId;
188
+ /** Its name, as people know it: "Nile Traders". */
189
+ name: string;
190
+ /**
191
+ * What it matched on: the names of the fields asked that it holds the same (`phone`, `email`), or a name close to
192
+ * the one typed (the name's field). Each is said under that field's box; words of the source's own ("the same
193
+ * address") are said as they are.
194
+ */
195
+ why: string[];
196
+ /** How alike, from 0 to 1, where the source tells: the likest first. */
197
+ score?: number;
198
+ }
199
+ export interface DuplicatesResult {
200
+ matches: DuplicateMatch[];
201
+ }
202
+ /** Records merged into one: what `merge` is asked. */
203
+ export interface MergeRequest {
204
+ model: string;
205
+ /** The record kept. */
206
+ keepId: RecordId;
207
+ /** The records merged into it, then gone. */
208
+ mergeIds: RecordId[];
209
+ /** The kept record's values, as picked on the merge's screen for each field the records held apart. */
210
+ values: Values;
211
+ /** The page's fields, so the source knows each link's model, as a save does. */
212
+ fields: Fields;
213
+ }
214
+ /** What a merge may tell: words of the source's own about it, said once it is done ("4 messages moved"). */
215
+ export interface MergeResult {
216
+ message?: string;
102
217
  }
103
218
  /** The same value written in a field of each record: a list's bulk edit. */
104
219
  export interface WriteManyRequest {
@@ -629,3 +744,9 @@ export interface SlotsResult {
629
744
  /** The minutes a booking lasts, when the source knows better than the page: the service's own length. */
630
745
  length?: number;
631
746
  }
747
+ /**
748
+ * A new record's save, made from its values alone — as a list's Import saves a row where the source has no
749
+ * `createMany`, and a memory source makes each of `createMany`'s: every value in `values`, the links of a many2many
750
+ * as one `set`, the rest as changes.
751
+ */
752
+ export declare function newRecordSave(model: string, fields: Fields, values: Values): SaveRequest;
@@ -99,6 +99,121 @@ export interface DataSource {
99
99
  exportRecords?(request: ExportRequest): Promise<ExportedFile | {
100
100
  message: string;
101
101
  } | false>;
102
+ /**
103
+ * Make many records at once, each with its own values: a list's Import of
104
+ * a file's rows, a thousand at a time. Answer for each record asked for,
105
+ * in its order: the id it was made with, or why it was not made, in the
106
+ * person's words. Without it, the list saves each in turn (`save` with no
107
+ * id).
108
+ */
109
+ createMany?(request: CreateManyRequest): Promise<CreateManyResult>;
110
+ /**
111
+ * Take a list's Import on the server, in place of the browser: a file too
112
+ * big for one, or a server that imports its own way. Asked once the person
113
+ * has matched the file's columns to the page's fields, before a row is
114
+ * checked: answer `{ message }` when the server takes the file (the list
115
+ * says it: "Your file is being imported: you will get an email"), or
116
+ * `false` to let the browser import it after all, as it does without this.
117
+ */
118
+ importRecords?(request: ImportRequest): Promise<{
119
+ message: string;
120
+ } | false>;
121
+ /**
122
+ * Records like the one typed, for a page's possible duplicates (`duplicates`): asked a moment after a person stops
123
+ * typing in a field it names, as a saved record is opened, and as a record is saved — with the fields it names that
124
+ * hold a value. Answer with the records that have the same phone, email or id, or a name close enough, each with
125
+ * the fields it matched on; never the record itself. Without it, nothing is asked and nothing shows.
126
+ */
127
+ duplicates?(request: DuplicatesRequest): Promise<DuplicatesResult>;
128
+ /**
129
+ * Merge records into one, as a person chose on a possible duplicate's Merge: `keepId` kept, holding the values
130
+ * picked for each field, and `mergeIds` gone. Moving what was theirs — messages, activities, the records that link
131
+ * to them — to the one kept is the source's work; the page says it will happen. Refuse with `saveRefused` (a `rule`,
132
+ * with why) and nothing changes. Without it, possible duplicates offer no Merge.
133
+ */
134
+ merge?(request: MergeRequest): Promise<MergeResult | void>;
135
+ }
136
+ /** Many records made at once, each with its own values: a list's Import. */
137
+ export interface CreateManyRequest {
138
+ model: string;
139
+ /** Each record's values, in the file's order: what its row fills in, and the page's defaults for the rest. Links as `{ id, label }`. */
140
+ records: Values[];
141
+ /** The fields' definitions, as a save gets them. */
142
+ fields: Fields;
143
+ }
144
+ export interface CreateManyResult {
145
+ /**
146
+ * One answer for each record asked for, in the same order: the id it was made with, or why it was not made — and,
147
+ * where the source has it, the problem as a save would throw it (a field's own words). A record left unanswered is
148
+ * taken as not made.
149
+ */
150
+ records: ({
151
+ id: RecordId;
152
+ } | {
153
+ message: string;
154
+ problem?: SaveProblem;
155
+ })[];
156
+ }
157
+ /** A file a list's Import hands to the server, its columns matched to the page's fields. */
158
+ export interface ImportRequest {
159
+ model: string;
160
+ /** The file as the person picked it: a browser's File fits. */
161
+ file: UploadFile;
162
+ /** Each column of the file, in its order — its header's words, and the field it fills, null where it is left out. */
163
+ columns: {
164
+ header: string;
165
+ field: string | null;
166
+ }[];
167
+ /** How many rows it holds under its header. */
168
+ rows: number;
169
+ /** The fields' definitions, as a save gets them. */
170
+ fields: Fields;
171
+ /** The page's language and the order it writes dates in, so a server reads the cells as the page would. */
172
+ locale: string;
173
+ dateOrder: 'dmy' | 'mdy' | 'ymd';
174
+ }
175
+ /** Records like the one typed: what `duplicates` is asked. */
176
+ export interface DuplicatesRequest {
177
+ model: string;
178
+ /** The record typed, never its own duplicate; null while it is new. */
179
+ recordId: RecordId | null;
180
+ /** Every value of the form, as typed: a phone kept as E.164 where the page reads phones the world over. */
181
+ values: Values;
182
+ /** The fields to compare it by, by name: those the page names (`duplicates.fields`) that hold a value now. */
183
+ fields: string[];
184
+ }
185
+ /** A record like the one typed. */
186
+ export interface DuplicateMatch {
187
+ id: RecordId;
188
+ /** Its name, as people know it: "Nile Traders". */
189
+ name: string;
190
+ /**
191
+ * What it matched on: the names of the fields asked that it holds the same (`phone`, `email`), or a name close to
192
+ * the one typed (the name's field). Each is said under that field's box; words of the source's own ("the same
193
+ * address") are said as they are.
194
+ */
195
+ why: string[];
196
+ /** How alike, from 0 to 1, where the source tells: the likest first. */
197
+ score?: number;
198
+ }
199
+ export interface DuplicatesResult {
200
+ matches: DuplicateMatch[];
201
+ }
202
+ /** Records merged into one: what `merge` is asked. */
203
+ export interface MergeRequest {
204
+ model: string;
205
+ /** The record kept. */
206
+ keepId: RecordId;
207
+ /** The records merged into it, then gone. */
208
+ mergeIds: RecordId[];
209
+ /** The kept record's values, as picked on the merge's screen for each field the records held apart. */
210
+ values: Values;
211
+ /** The page's fields, so the source knows each link's model, as a save does. */
212
+ fields: Fields;
213
+ }
214
+ /** What a merge may tell: words of the source's own about it, said once it is done ("4 messages moved"). */
215
+ export interface MergeResult {
216
+ message?: string;
102
217
  }
103
218
  /** The same value written in a field of each record: a list's bulk edit. */
104
219
  export interface WriteManyRequest {
@@ -629,3 +744,9 @@ export interface SlotsResult {
629
744
  /** The minutes a booking lasts, when the source knows better than the page: the service's own length. */
630
745
  length?: number;
631
746
  }
747
+ /**
748
+ * A new record's save, made from its values alone — as a list's Import saves a row where the source has no
749
+ * `createMany`, and a memory source makes each of `createMany`'s: every value in `values`, the links of a many2many
750
+ * as one `set`, the rest as changes.
751
+ */
752
+ export declare function newRecordSave(model: string, fields: Fields, values: Values): SaveRequest;
@@ -5,6 +5,7 @@ exports.loadedRecord = loadedRecord;
5
5
  exports.loadedRecordOf = loadedRecordOf;
6
6
  exports.saveRefused = saveRefused;
7
7
  exports.saveProblemOf = saveProblemOf;
8
+ exports.newRecordSave = newRecordSave;
8
9
  /** What `load` returns to tell a record's version, or what this person may do with it, beside its values. */
9
10
  function loadedRecord(values, more = {}) {
10
11
  return { $loaded: true, values, ...(more.version ? { version: more.version } : {}), ...(more.can ? { can: more.can } : {}) };
@@ -45,4 +46,19 @@ function saveProblemOf(error) {
45
46
  return { kind: 'network', message };
46
47
  return { kind: 'other', message };
47
48
  }
49
+ /**
50
+ * A new record's save, made from its values alone — as a list's Import saves a row where the source has no
51
+ * `createMany`, and a memory source makes each of `createMany`'s: every value in `values`, the links of a many2many
52
+ * as one `set`, the rest as changes.
53
+ */
54
+ function newRecordSave(model, fields, values) {
55
+ const changes = { values: {}, lines: {}, links: {} };
56
+ for (const [name, value] of Object.entries(values)) {
57
+ if (fields[name]?.type === 'many2many')
58
+ changes.links[name] = [{ op: 'set', ids: Array.isArray(value) ? value.map((record) => record.id) : [] }];
59
+ else
60
+ changes.values[name] = value;
61
+ }
62
+ return { model, id: null, fields, changes, values };
63
+ }
48
64
  //# sourceMappingURL=data-source.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"data-source.js","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/record/data-source.ts"],"names":[],"mappings":";;;AA6MA,oCAEC;AAGD,wCAGC;AAwYD,kCAEC;AAGD,sCAMC;AA5ZD,6GAA6G;AAC7G,SAAgB,YAAY,CAAC,MAAc,EAAE,OAA6D,EAAE;IAC1G,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC/H,CAAC;AAED,6HAA6H;AAC7H,SAAgB,cAAc,CAAC,MAA6B;IAC1D,MAAM,MAAM,GAAG,MAA+B,CAAC;IAC/C,OAAO,MAAM,CAAC,OAAO,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAE,MAAuB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAgB,EAAE,CAAC;AACzK,CAAC;AAsXD;;;;;GAKG;AACH,MAAa,aAAc,SAAQ,KAAK;IAC7B,QAAQ,CAAe;IACvB,OAAO,CAAc;IAC9B,YAAY,QAAsB,EAAE,OAAO,GAAG,sDAAsD;QAClG,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IACzD,CAAC;CACF;AATD,sCASC;AAED,8EAA8E;AAC9E,SAAgB,WAAW,CAAC,OAAoB;IAC9C,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,wGAAwG;AACxG,SAAgB,aAAa,CAAC,KAAc;IAC1C,MAAM,OAAO,GAAI,KAA0C,EAAE,OAAO,CAAC;IACrE,IAAI,OAAO,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChE,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,IAAI,KAAK,YAAY,SAAS,IAAI,4BAA4B,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;IAClH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACpC,CAAC"}
1
+ {"version":3,"file":"data-source.js","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/record/data-source.ts"],"names":[],"mappings":"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"}
@@ -37,4 +37,19 @@ export function saveProblemOf(error) {
37
37
  return { kind: 'network', message };
38
38
  return { kind: 'other', message };
39
39
  }
40
+ /**
41
+ * A new record's save, made from its values alone — as a list's Import saves a row where the source has no
42
+ * `createMany`, and a memory source makes each of `createMany`'s: every value in `values`, the links of a many2many
43
+ * as one `set`, the rest as changes.
44
+ */
45
+ export function newRecordSave(model, fields, values) {
46
+ const changes = { values: {}, lines: {}, links: {} };
47
+ for (const [name, value] of Object.entries(values)) {
48
+ if (fields[name]?.type === 'many2many')
49
+ changes.links[name] = [{ op: 'set', ids: Array.isArray(value) ? value.map((record) => record.id) : [] }];
50
+ else
51
+ changes.values[name] = value;
52
+ }
53
+ return { model, id: null, fields, changes, values };
54
+ }
40
55
  //# sourceMappingURL=data-source.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"data-source.mjs","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/record/data-source.ts"],"names":[],"mappings":"AA4MA,6GAA6G;AAC7G,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,OAA6D,EAAE;IAC1G,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;AAC/H,CAAC;AAED,6HAA6H;AAC7H,MAAM,UAAU,cAAc,CAAC,MAA6B;IAC1D,MAAM,MAAM,GAAG,MAA+B,CAAC;IAC/C,OAAO,MAAM,CAAC,OAAO,KAAK,IAAI,IAAI,MAAM,CAAC,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAE,MAAuB,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAgB,EAAE,CAAC;AACzK,CAAC;AAsXD;;;;;GAKG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,QAAQ,CAAe;IACvB,OAAO,CAAc;IAC9B,YAAY,QAAsB,EAAE,OAAO,GAAG,sDAAsD;QAClG,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,OAAO,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;IACzD,CAAC;CACF;AAED,8EAA8E;AAC9E,MAAM,UAAU,WAAW,CAAC,OAAoB;IAC9C,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;AAChE,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,MAAM,OAAO,GAAI,KAA0C,EAAE,OAAO,CAAC;IACrE,IAAI,OAAO,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAChE,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,IAAI,KAAK,YAAY,SAAS,IAAI,4BAA4B,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;IAClH,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;AACpC,CAAC"}
1
+ {"version":3,"file":"data-source.mjs","sourceRoot":"","sources":["../../../../../../libs/core/src/lib/record/data-source.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,8 @@ import type { Page } from '../format/page.mjs';
4
4
  import type { JsonValue } from '../format/json.mjs';
5
5
  import { type FieldTone, type LineState } from './cells.mjs';
6
6
  import { type Messages } from './messages.mjs';
7
- import { type DataSource, type RecordChanges, type RecordPermissions, type RecordVersion, type SaveProblem, type BotReport, type SlotsResult, type UploadFile } from './data-source.mjs';
7
+ import { type DataSource, type RecordChanges, type RecordPermissions, type RecordVersion, type ResolvedFilter, type SaveProblem, type BotReport, type SlotsResult, type UploadFile, type DuplicateMatch, type MergeResult } from './data-source.mjs';
8
+ import { type WaitsFor } from './waits.mjs';
8
9
  import { type FileUpload, type UploadHow } from './uploads.mjs';
9
10
  import { type Scheduler } from './scheduler.mjs';
10
11
  import { type ActionHost, type ActionRequest, type ActionResult, type OnAction, type PostedMessage, type RunContext, type RunResult } from './run.mjs';
@@ -327,6 +328,14 @@ export interface Form {
327
328
  fieldReadonly(name: string): boolean;
328
329
  /** What a field would be told if the form were checked now, or null when it passes. Nothing is shown. */
329
330
  problem(field: string): string | null;
331
+ /**
332
+ * What a value worked out from others waits for while one of them holds text that does not read (`unread`): a
333
+ * total of the page's own (`compute`) over a table whose line says "abc" in its Quantity, or an amount the app
334
+ * works out as the lines change (read-only, from a source with an onchange) — the line, as people count them, and
335
+ * its cell. Shown, it is a dash said in words, as the table's own total waits: never an amount worked out from the
336
+ * value that text replaced. Null when it shows its value.
337
+ */
338
+ waitsFor(field: string): WaitsFor | null;
330
339
  /**
331
340
  * Keep text a person left in a box that does not read (`FormState.unread`), by its path — `field`, or a line's
332
341
  * `field.lineKey.subfield` — with why in words; null once it reads, or is let go. Nothing is shown until the box
@@ -424,6 +433,12 @@ export interface Form {
424
433
  * (docs/plans/wave1-contract.md, section 10, part 2).
425
434
  */
426
435
  recordShown(): () => boolean;
436
+ /**
437
+ * What a related list (a `related` part, by its id) asks the data source's `list` for, on the record shown: the
438
+ * records whose link is this record, and that its own `filter` lets through, each `valueFrom` read from this record.
439
+ * Null before the record's first save — nothing points at it yet — and for no related list of that id.
440
+ */
441
+ relatedFilter(id: string): ResolvedFilter[] | null;
427
442
  /**
428
443
  * Post words in the record's conversation, as a `post` step does: through
429
444
  * the form's host when it has a way to, else told to the form's own
@@ -532,6 +547,13 @@ export interface Form {
532
547
  canUpload(): boolean;
533
548
  /** Today, as 2026-10-15, on the clock of the page's time zone — the browser's own where it names none — by the form's clock (`now`). */
534
549
  today(): string;
550
+ /**
551
+ * A record of the page's model as conditions read it, the form's own way: its values (a link as its id), its `id`,
552
+ * the person using the form (`user`), the app's `context`, and `editing` false — for what reads conditions on records
553
+ * the form does not hold, such as a list's rows (their `rowTones`, `rowBold` and `rowButtons`). Of another model — a
554
+ * related list's rows — with that model's `fields` (the related list's own).
555
+ */
556
+ recordReads(id: RecordId | null, values: Values, fields?: Record<string, Field | LineField>): Record<string, unknown>;
535
557
  /** Whether a date and time drawn as booking slots can ask for its free times: the data source has `slots`. */
536
558
  canFindSlots(): boolean;
537
559
  /**
@@ -546,6 +568,28 @@ export interface Form {
546
568
  length?: number;
547
569
  between?: number;
548
570
  }): Promise<SlotsResult>;
571
+ /** Whether the page asks for possible duplicates: a record's page naming fields in `duplicates`, its source with `duplicates`. */
572
+ canFindDuplicates(): boolean;
573
+ /**
574
+ * Records like the one shown, asked of the data source's `duplicates` with the fields the page names that hold a
575
+ * value now — none, and nothing is asked — and every value of the form: the matches, the likest first where the
576
+ * source scores them, never the record itself. Rejects where the source has no `duplicates`. Its answer is for the
577
+ * record shown as it was asked (`recordShown`).
578
+ */
579
+ findDuplicates(): Promise<DuplicateMatch[]>;
580
+ /** Whether records of the page's model can be merged: its source has `merge`, and `load` to read the others. */
581
+ canMerge(): boolean;
582
+ /** Another record of the page's model, as its source loads it, its values alone: the form is left as it is. For a merge's screen. */
583
+ readRecord(id: RecordId): Promise<Values>;
584
+ /**
585
+ * Merge records of the page's model into one through the source's `merge`: `keepId` kept with `values`, `mergeIds`
586
+ * gone. The form is left as it is: show the record kept (`openRecord`, `load`) once it is done.
587
+ */
588
+ merge(request: {
589
+ keepId: RecordId;
590
+ mergeIds: RecordId[];
591
+ values: Values;
592
+ }): Promise<MergeResult | void>;
549
593
  /**
550
594
  * Store files picked for a file or picture field — or a field on a table's
551
595
  * line (`how.line`) — through the data source's `upload`, each as soon as
@@ -4,7 +4,8 @@ import type { Page } from '../format/page';
4
4
  import type { JsonValue } from '../format/json';
5
5
  import { type FieldTone, type LineState } from './cells';
6
6
  import { type Messages } from './messages';
7
- import { type DataSource, type RecordChanges, type RecordPermissions, type RecordVersion, type SaveProblem, type BotReport, type SlotsResult, type UploadFile } from './data-source';
7
+ import { type DataSource, type RecordChanges, type RecordPermissions, type RecordVersion, type ResolvedFilter, type SaveProblem, type BotReport, type SlotsResult, type UploadFile, type DuplicateMatch, type MergeResult } from './data-source';
8
+ import { type WaitsFor } from './waits';
8
9
  import { type FileUpload, type UploadHow } from './uploads';
9
10
  import { type Scheduler } from './scheduler';
10
11
  import { type ActionHost, type ActionRequest, type ActionResult, type OnAction, type PostedMessage, type RunContext, type RunResult } from './run';
@@ -327,6 +328,14 @@ export interface Form {
327
328
  fieldReadonly(name: string): boolean;
328
329
  /** What a field would be told if the form were checked now, or null when it passes. Nothing is shown. */
329
330
  problem(field: string): string | null;
331
+ /**
332
+ * What a value worked out from others waits for while one of them holds text that does not read (`unread`): a
333
+ * total of the page's own (`compute`) over a table whose line says "abc" in its Quantity, or an amount the app
334
+ * works out as the lines change (read-only, from a source with an onchange) — the line, as people count them, and
335
+ * its cell. Shown, it is a dash said in words, as the table's own total waits: never an amount worked out from the
336
+ * value that text replaced. Null when it shows its value.
337
+ */
338
+ waitsFor(field: string): WaitsFor | null;
330
339
  /**
331
340
  * Keep text a person left in a box that does not read (`FormState.unread`), by its path — `field`, or a line's
332
341
  * `field.lineKey.subfield` — with why in words; null once it reads, or is let go. Nothing is shown until the box
@@ -424,6 +433,12 @@ export interface Form {
424
433
  * (docs/plans/wave1-contract.md, section 10, part 2).
425
434
  */
426
435
  recordShown(): () => boolean;
436
+ /**
437
+ * What a related list (a `related` part, by its id) asks the data source's `list` for, on the record shown: the
438
+ * records whose link is this record, and that its own `filter` lets through, each `valueFrom` read from this record.
439
+ * Null before the record's first save — nothing points at it yet — and for no related list of that id.
440
+ */
441
+ relatedFilter(id: string): ResolvedFilter[] | null;
427
442
  /**
428
443
  * Post words in the record's conversation, as a `post` step does: through
429
444
  * the form's host when it has a way to, else told to the form's own
@@ -532,6 +547,13 @@ export interface Form {
532
547
  canUpload(): boolean;
533
548
  /** Today, as 2026-10-15, on the clock of the page's time zone — the browser's own where it names none — by the form's clock (`now`). */
534
549
  today(): string;
550
+ /**
551
+ * A record of the page's model as conditions read it, the form's own way: its values (a link as its id), its `id`,
552
+ * the person using the form (`user`), the app's `context`, and `editing` false — for what reads conditions on records
553
+ * the form does not hold, such as a list's rows (their `rowTones`, `rowBold` and `rowButtons`). Of another model — a
554
+ * related list's rows — with that model's `fields` (the related list's own).
555
+ */
556
+ recordReads(id: RecordId | null, values: Values, fields?: Record<string, Field | LineField>): Record<string, unknown>;
535
557
  /** Whether a date and time drawn as booking slots can ask for its free times: the data source has `slots`. */
536
558
  canFindSlots(): boolean;
537
559
  /**
@@ -546,6 +568,28 @@ export interface Form {
546
568
  length?: number;
547
569
  between?: number;
548
570
  }): Promise<SlotsResult>;
571
+ /** Whether the page asks for possible duplicates: a record's page naming fields in `duplicates`, its source with `duplicates`. */
572
+ canFindDuplicates(): boolean;
573
+ /**
574
+ * Records like the one shown, asked of the data source's `duplicates` with the fields the page names that hold a
575
+ * value now — none, and nothing is asked — and every value of the form: the matches, the likest first where the
576
+ * source scores them, never the record itself. Rejects where the source has no `duplicates`. Its answer is for the
577
+ * record shown as it was asked (`recordShown`).
578
+ */
579
+ findDuplicates(): Promise<DuplicateMatch[]>;
580
+ /** Whether records of the page's model can be merged: its source has `merge`, and `load` to read the others. */
581
+ canMerge(): boolean;
582
+ /** Another record of the page's model, as its source loads it, its values alone: the form is left as it is. For a merge's screen. */
583
+ readRecord(id: RecordId): Promise<Values>;
584
+ /**
585
+ * Merge records of the page's model into one through the source's `merge`: `keepId` kept with `values`, `mergeIds`
586
+ * gone. The form is left as it is: show the record kept (`openRecord`, `load`) once it is done.
587
+ */
588
+ merge(request: {
589
+ keepId: RecordId;
590
+ mergeIds: RecordId[];
591
+ values: Values;
592
+ }): Promise<MergeResult | void>;
549
593
  /**
550
594
  * Store files picked for a file or picture field — or a field on a table's
551
595
  * line (`how.line`) — through the data source's `upload`, each as soon as