@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
@@ -20,6 +20,7 @@ const roles_1 = require("./roles");
20
20
  const cells_1 = require("./cells");
21
21
  const messages_1 = require("./messages");
22
22
  const data_source_1 = require("./data-source");
23
+ const waits_1 = require("./waits");
23
24
  const uploads_1 = require("./uploads");
24
25
  const scheduler_1 = require("./scheduler");
25
26
  const run_1 = require("./run");
@@ -164,6 +165,8 @@ function innerForm(options, within, place, outer) {
164
165
  const editingNow = () => editing && !(started && state.can?.edit === false);
165
166
  const about = () => ({ recordId: started ? state.recordId : (options.recordId ?? null), user: options.user, editing: editingNow(), context: options.context, parent: parentContext });
166
167
  const computed = (0, compute_1.compileComputed)(page, today, about);
168
+ /** Each value worked out from others — by the page, or by the app as the lines change — and what it waits for. */
169
+ const workedOut = (0, waits_1.workedOutFrom)(page, !!options.dataSource?.onchange && page.data.kind === 'record', !!options.parent);
167
170
  const setWhen = (0, set_when_1.compileSetWhen)(page, today, about);
168
171
  /** The saved forms placed on the page, by the part's id: each one's answers sit under its name, as JSON. */
169
172
  const parts = new Map();
@@ -323,6 +326,13 @@ function innerForm(options, within, place, outer) {
323
326
  * for is still the one shown, as it was. A save's answer, a call's, an onchange's, a name looked up, a line's
324
327
  * dialog, a record step's — none is laid on another record, nor over the values the person's Discard let go.
325
328
  */
329
+ /** The data source with a member a record's page asks of it, and the page's model: refused where either is missing. */
330
+ function recordSource(member) {
331
+ const source = options.dataSource;
332
+ if (page.data.kind !== 'record' || !source?.[member])
333
+ throw new Error(`The data source has no ${member}()`);
334
+ return [source, page.data.model];
335
+ }
326
336
  function shownNow() {
327
337
  const at = generation;
328
338
  const reset = resets;
@@ -2026,6 +2036,13 @@ function innerForm(options, within, place, outer) {
2026
2036
  leave: (how) => Promise.resolve(letGo(how)),
2027
2037
  leaveNow: (how = {}) => (outer ? outer.leaveNow(how) : atOnce(how)),
2028
2038
  recordShown: shownNow,
2039
+ relatedFilter(id) {
2040
+ const node = index.get(id)?.source;
2041
+ if (node?.type !== 'related' || state.recordId == null)
2042
+ return null;
2043
+ const related = node;
2044
+ return [{ field: related.field, op: '=', value: state.recordId }, ...(related.filter ? (0, filter_1.resolveFilter)(related.filter, context()) : [])];
2045
+ },
2029
2046
  post,
2030
2047
  setEditing(next) {
2031
2048
  if (next === editing)
@@ -2038,6 +2055,10 @@ function innerForm(options, within, place, outer) {
2038
2055
  loadChoices,
2039
2056
  loadDefinitions,
2040
2057
  addDefinition,
2058
+ waitsFor(name) {
2059
+ const from = workedOut.get(name);
2060
+ return from ? (0, waits_1.waitingOn)(page, from, state.values, state.unread) : null;
2061
+ },
2041
2062
  problem(name) {
2042
2063
  fieldDef(name);
2043
2064
  // Only a field someone can see is asked anything, and required wherever it is shown so.
@@ -2116,6 +2137,7 @@ function innerForm(options, within, place, outer) {
2116
2137
  save: () => track(save()),
2117
2138
  canUpload: () => typeof options.dataSource?.upload === 'function',
2118
2139
  today,
2140
+ recordReads: (id, values, fields) => (0, env_1.recordContext)(values, fields ?? page.fields, { recordId: id, user: options.user, editing: false, context: options.context }),
2119
2141
  canFindSlots: () => typeof options.dataSource?.slots === 'function',
2120
2142
  findSlots(field, ask) {
2121
2143
  const source = options.dataSource;
@@ -2136,6 +2158,28 @@ function innerForm(options, within, place, outer) {
2136
2158
  values: (0, values_1.structuredCopy)(state.values),
2137
2159
  });
2138
2160
  },
2161
+ canFindDuplicates: () => !!(page.data.kind === 'record' && page.duplicates?.fields?.length && options.dataSource?.duplicates),
2162
+ async findDuplicates() {
2163
+ const [source, model] = recordSource('duplicates');
2164
+ const fields = (page.duplicates?.fields ?? []).filter((name) => page.fields[name] && !(0, values_1.isEmpty)(page.fields[name], state.values[name]));
2165
+ const recordId = state.recordId;
2166
+ // Nothing typed in any of them: nothing asked.
2167
+ if (!fields.length)
2168
+ return [];
2169
+ const { matches } = await source.duplicates({ model, recordId, values: (0, values_1.structuredCopy)(state.values), fields });
2170
+ // Never the record itself, whatever the source says; the likest first where it scores them.
2171
+ const others = (matches ?? []).filter((match) => match && String(match.id) !== String(recordId));
2172
+ return others.every((match) => typeof match.score === 'number') ? others.sort((a, b) => b.score - a.score) : others;
2173
+ },
2174
+ canMerge: () => !!(page.data.kind === 'record' && options.dataSource?.merge && options.dataSource.load),
2175
+ async readRecord(id) {
2176
+ const [source, model] = recordSource('load');
2177
+ return (0, data_source_1.loadedRecordOf)(await source.load({ model, id, fields: page.fields })).values;
2178
+ },
2179
+ async merge(request) {
2180
+ const [source, model] = recordSource('merge');
2181
+ return source.merge({ model, keepId: request.keepId, mergeIds: [...request.mergeIds], values: (0, values_1.structuredCopy)(request.values), fields: page.fields });
2182
+ },
2139
2183
  upload: (field, files, how = {}) => uploads.start(field, files, how),
2140
2184
  uploading: (field) => state.uploads.filter((upload) => upload.field === field && !upload.line),
2141
2185
  cancelUpload(id) {
@@ -2812,6 +2856,12 @@ function indexLayout(root) {
2812
2856
  else if (node.type === 'form') {
2813
2857
  add(node, parent, 'form', { invisible: node.invisible, readonly: node.readonly });
2814
2858
  }
2859
+ else if (node.type === 'related') {
2860
+ add(node, parent, 'other', { invisible: node.invisible });
2861
+ // A row's buttons: their roles here, hidden with the list, their condition read by it on each row's record.
2862
+ for (const button of node.rowButtons ?? [])
2863
+ add(button, node.id, 'button', {});
2864
+ }
2815
2865
  else {
2816
2866
  add(node, parent, 'other', { invisible: node.invisible });
2817
2867
  }
@@ -2838,6 +2888,9 @@ function indexLayout(root) {
2838
2888
  // A list holds no fields of its own record: only the buttons for what is selected.
2839
2889
  for (const button of root.actions ?? [])
2840
2890
  add(button, root.id, 'button', { invisible: button.invisible });
2891
+ // A row's buttons: their roles here, their condition read by the list on each row's record, which the form does not hold.
2892
+ for (const button of root.rowButtons ?? [])
2893
+ add(button, root.id, 'button', {});
2841
2894
  }
2842
2895
  else {
2843
2896
  // The title and statusbar show fields too, so they are validated like