@fieldia/core 0.25.0 → 0.26.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 (115) hide show
  1. package/README.md +58 -2
  2. package/package.json +1 -1
  3. package/schema/page.schema.json +86 -3
  4. package/src/index.d.mts +1 -0
  5. package/src/index.d.ts +1 -0
  6. package/src/index.js +3 -1
  7. package/src/index.js.map +1 -1
  8. package/src/index.mjs +1 -0
  9. package/src/index.mjs.map +1 -1
  10. package/src/lib/format/head-checks.d.mts +3 -1
  11. package/src/lib/format/head-checks.d.ts +3 -1
  12. package/src/lib/format/head-checks.js +25 -4
  13. package/src/lib/format/head-checks.js.map +1 -1
  14. package/src/lib/format/head-checks.mjs +24 -4
  15. package/src/lib/format/head-checks.mjs.map +1 -1
  16. package/src/lib/format/layout.d.mts +208 -12
  17. package/src/lib/format/layout.d.ts +208 -12
  18. package/src/lib/format/layout.js +11 -6
  19. package/src/lib/format/layout.js.map +1 -1
  20. package/src/lib/format/layout.mjs +11 -6
  21. package/src/lib/format/layout.mjs.map +1 -1
  22. package/src/lib/format/page.d.mts +69 -3
  23. package/src/lib/format/page.d.ts +69 -3
  24. package/src/lib/format/page.js +3 -0
  25. package/src/lib/format/page.js.map +1 -1
  26. package/src/lib/format/page.mjs +3 -0
  27. package/src/lib/format/page.mjs.map +1 -1
  28. package/src/lib/format/records.d.mts +23 -0
  29. package/src/lib/format/records.d.ts +23 -0
  30. package/src/lib/format/records.js +28 -0
  31. package/src/lib/format/records.js.map +1 -0
  32. package/src/lib/format/records.mjs +25 -0
  33. package/src/lib/format/records.mjs.map +1 -0
  34. package/src/lib/format/references.d.mts +2 -0
  35. package/src/lib/format/references.d.ts +2 -0
  36. package/src/lib/format/references.js +13 -0
  37. package/src/lib/format/references.js.map +1 -1
  38. package/src/lib/format/references.mjs +13 -0
  39. package/src/lib/format/references.mjs.map +1 -1
  40. package/src/lib/format/translate.js +5 -3
  41. package/src/lib/format/translate.js.map +1 -1
  42. package/src/lib/format/translate.mjs +5 -3
  43. package/src/lib/format/translate.mjs.map +1 -1
  44. package/src/lib/format/upgrade.d.mts +6 -3
  45. package/src/lib/format/upgrade.d.ts +6 -3
  46. package/src/lib/format/upgrade.js +6 -3
  47. package/src/lib/format/upgrade.js.map +1 -1
  48. package/src/lib/format/upgrade.mjs +6 -3
  49. package/src/lib/format/upgrade.mjs.map +1 -1
  50. package/src/lib/format/version.d.mts +2 -2
  51. package/src/lib/format/version.d.ts +2 -2
  52. package/src/lib/format/version.js +2 -2
  53. package/src/lib/format/version.js.map +1 -1
  54. package/src/lib/format/version.mjs +2 -2
  55. package/src/lib/format/version.mjs.map +1 -1
  56. package/src/lib/record/check.js +2 -2
  57. package/src/lib/record/check.js.map +1 -1
  58. package/src/lib/record/check.mjs +3 -3
  59. package/src/lib/record/check.mjs.map +1 -1
  60. package/src/lib/record/form.d.mts +6 -2
  61. package/src/lib/record/form.d.ts +6 -2
  62. package/src/lib/record/form.js +25 -17
  63. package/src/lib/record/form.js.map +1 -1
  64. package/src/lib/record/form.mjs +26 -18
  65. package/src/lib/record/form.mjs.map +1 -1
  66. package/src/lib/record/inputs.js +8 -8
  67. package/src/lib/record/inputs.js.map +1 -1
  68. package/src/lib/record/inputs.mjs +9 -9
  69. package/src/lib/record/inputs.mjs.map +1 -1
  70. package/src/lib/record/limits.js +1 -1
  71. package/src/lib/record/limits.js.map +1 -1
  72. package/src/lib/record/limits.mjs +2 -2
  73. package/src/lib/record/limits.mjs.map +1 -1
  74. package/src/lib/record/locales/de.js +2 -0
  75. package/src/lib/record/locales/de.js.map +1 -1
  76. package/src/lib/record/locales/de.mjs +2 -0
  77. package/src/lib/record/locales/de.mjs.map +1 -1
  78. package/src/lib/record/memory-data-source.js +24 -1
  79. package/src/lib/record/memory-data-source.js.map +1 -1
  80. package/src/lib/record/memory-data-source.mjs +24 -1
  81. package/src/lib/record/memory-data-source.mjs.map +1 -1
  82. package/src/lib/record/messages.d.mts +24 -0
  83. package/src/lib/record/messages.d.ts +24 -0
  84. package/src/lib/record/messages.js +25 -0
  85. package/src/lib/record/messages.js.map +1 -1
  86. package/src/lib/record/messages.mjs +24 -0
  87. package/src/lib/record/messages.mjs.map +1 -1
  88. package/src/lib/record/record-words.d.mts +19 -0
  89. package/src/lib/record/record-words.d.ts +19 -0
  90. package/src/lib/record/record-words.js +60 -0
  91. package/src/lib/record/record-words.js.map +1 -0
  92. package/src/lib/record/record-words.mjs +56 -0
  93. package/src/lib/record/record-words.mjs.map +1 -0
  94. package/src/lib/record/rules.js +1 -1
  95. package/src/lib/record/rules.js.map +1 -1
  96. package/src/lib/record/rules.mjs +2 -2
  97. package/src/lib/record/rules.mjs.map +1 -1
  98. package/src/lib/record/run.d.mts +8 -0
  99. package/src/lib/record/run.d.ts +8 -0
  100. package/src/lib/record/run.js.map +1 -1
  101. package/src/lib/record/run.mjs.map +1 -1
  102. package/src/lib/record/search.js +2 -1
  103. package/src/lib/record/search.js.map +1 -1
  104. package/src/lib/record/search.mjs +2 -1
  105. package/src/lib/record/search.mjs.map +1 -1
  106. package/src/lib/record/structures.js +3 -3
  107. package/src/lib/record/structures.js.map +1 -1
  108. package/src/lib/record/structures.mjs +4 -4
  109. package/src/lib/record/structures.mjs.map +1 -1
  110. package/src/runtime.d.mts +4 -1
  111. package/src/runtime.d.ts +4 -1
  112. package/src/runtime.js +8 -3
  113. package/src/runtime.js.map +1 -1
  114. package/src/runtime.mjs +2 -0
  115. package/src/runtime.mjs.map +1 -1
@@ -12,7 +12,8 @@ import { firstValues, readLooseMap, readValueMap } from './value-map.mjs';
12
12
  import { resolveFilter } from './filter.mjs';
13
13
  import { rolesAllow } from './roles.mjs';
14
14
  import { compileFieldTone, compileTable } from './cells.mjs';
15
- import { fill, MESSAGES } from './messages.mjs';
15
+ import { fillMessage, MESSAGES } from './messages.mjs';
16
+ import { recordSentence } from './record-words.mjs';
16
17
  import { loadedRecordOf, saveProblemOf, } from './data-source.mjs';
17
18
  import { waitingOn, workedOutFrom } from './waits.mjs';
18
19
  import { createUploads, fileBytesIn, uploadGoing, withoutFileBytes } from './uploads.mjs';
@@ -75,7 +76,10 @@ function innerForm(options, within, place, outer) {
75
76
  // Its time zone written into each date and time field naming none, for whatever reads one from its field alone.
76
77
  const page = zonedPage(options.page);
77
78
  const scheduler = options.scheduler ?? hostScheduler;
78
- const messages = options.messages ?? MESSAGES.en;
79
+ // A field's label small inside a message's sentence where the page asks (`lowerLabels`).
80
+ const messages = page.lowerLabels ? { ...(options.messages ?? MESSAGES.en), lowerLabels: true } : (options.messages ?? MESSAGES.en);
81
+ /** One of the form's sentences in the words the page names its records by (`records`), else as the language says it plainly. */
82
+ const named = (noun, plain) => recordSentence(page.records, noun, plain, messages.locale);
79
83
  const index = indexLayout(page.layout);
80
84
  /** Each field node's answer rules, read once. */
81
85
  const nodeRules = new Map();
@@ -858,7 +862,7 @@ function innerForm(options, within, place, outer) {
858
862
  return undefined;
859
863
  const at = (v) => (def.type === 'datetime' ? Date.parse(v) : Date.parse(v.slice(0, 10)));
860
864
  if (at(value) < at(start))
861
- return fill(messages.endBeforeStart, { end: def.label, start: page.fields[node.field].label });
865
+ return fillMessage(messages, messages.endBeforeStart, { end: def.label, start: page.fields[node.field].label });
862
866
  return undefined;
863
867
  }
864
868
  /** Errors for the visible fields under `root`, without publishing them. */
@@ -1247,13 +1251,13 @@ function innerForm(options, within, place, outer) {
1247
1251
  async function savingNow(chain) {
1248
1252
  // A record its source does not let this person change is refused here, in its words or the form's: no source is asked.
1249
1253
  if (page.data.kind === 'record' && state.can?.edit === false) {
1250
- const problem = { kind: 'rule', message: state.can.reason ?? messages.mayNotEdit };
1254
+ const problem = { kind: 'rule', message: state.can.reason ?? named(messages.mayNotEditNoun, messages.mayNotEdit) };
1251
1255
  set({ status: 'error', error: problem.message, saveProblem: problem, conflict: null });
1252
1256
  return { reason: 'save', message: problem.message };
1253
1257
  }
1254
1258
  // Asked for the record shown now: another shown while it waits below, it saves nothing of that one, nor says anything there.
1255
1259
  const asked = generation;
1256
- const elsewhere = () => ({ reason: 'save', message: messages.recordReplaced });
1260
+ const elsewhere = () => ({ reason: 'save', message: named(messages.recordReplacedNoun, messages.recordReplaced) });
1257
1261
  // What is still typed in a box is read first: it is what is saved, or why nothing is. A name still being looked
1258
1262
  // up is waited for, so the record it names is what is saved — or, named by none, why nothing is.
1259
1263
  const waiting = readTyped();
@@ -1453,6 +1457,16 @@ function innerForm(options, within, place, outer) {
1453
1457
  changed(name, by, { keep });
1454
1458
  }
1455
1459
  /** A line added: the person's, theirs; a step's, none of theirs, and the lines they changed stay theirs. */
1460
+ /** What a line added now starts with: each line field's default, then what is worked out from its defaultFrom, then the record's lineDefaults. */
1461
+ function newLineValues(field) {
1462
+ const def = lineField(field);
1463
+ const lineFields = def.fields;
1464
+ const start = initialValues(lineFields, today());
1465
+ Object.assign(start, firstValues(lineFields, lineScope(field, start)));
1466
+ if (def.lineDefaults)
1467
+ Object.assign(start, readValueMap(def.lineDefaults, lineFields, recordScope()));
1468
+ return start;
1469
+ }
1456
1470
  function addLine(field, values, by) {
1457
1471
  const def = lineField(field);
1458
1472
  const current = state.values[field] ?? [];
@@ -1460,13 +1474,7 @@ function innerForm(options, within, place, outer) {
1460
1474
  let key = `new-${++lineKeys}`;
1461
1475
  while (current.some((line) => line.key === key))
1462
1476
  key = `new-${++lineKeys}`;
1463
- // Each line field's default, then what is worked out from its defaultFrom, then the record's lineDefaults, then what the line is given.
1464
- const lineFields = def.fields;
1465
- const start = initialValues(lineFields, today());
1466
- Object.assign(start, firstValues(lineFields, lineScope(field, start)));
1467
- if (def.lineDefaults)
1468
- Object.assign(start, readValueMap(def.lineDefaults, lineFields, recordScope()));
1469
- const line = { key, values: { ...start, ...structuredCopy(values) } };
1477
+ const line = { key, values: { ...newLineValues(field), ...structuredCopy(values) } };
1470
1478
  // A new line comes last, so it is numbered after the last.
1471
1479
  if (def.sequenceField && line.values[def.sequenceField] == null) {
1472
1480
  const numbers = current.map((l) => l.values[def.sequenceField]).filter((n) => typeof n === 'number');
@@ -1737,7 +1745,7 @@ function innerForm(options, within, place, outer) {
1737
1745
  return going;
1738
1746
  }
1739
1747
  async function askToLetGo() {
1740
- const message = messages.discardChanges;
1748
+ const message = named(messages.discardChangesNoun, messages.discardChanges);
1741
1749
  const host = options.host;
1742
1750
  let yes = false;
1743
1751
  try {
@@ -1796,7 +1804,7 @@ function innerForm(options, within, place, outer) {
1796
1804
  const go = letGo({}, chain);
1797
1805
  return go === true ? null : go.then((yes) => (yes ? null : kept()));
1798
1806
  }
1799
- const kept = () => ({ reason: 'no', message: messages.discardChanges });
1807
+ const kept = () => ({ reason: 'no', message: named(messages.discardChangesNoun, messages.discardChanges) });
1800
1808
  /**
1801
1809
  * A step's own move — the record loaded again, or another shown — once the check let what is shown go: kept, the
1802
1810
  * person kept their work; replaced, another record was shown before it ended (the run's steps are not for that one).
@@ -1833,7 +1841,7 @@ function innerForm(options, within, place, outer) {
1833
1841
  }
1834
1842
  /** Why a run stops whose record was replaced while it went on — quietly, when the form was closed. */
1835
1843
  function replaced() {
1836
- return disposed ? { reason: 'closed' } : { reason: 'cannot', message: messages.recordReplaced };
1844
+ return disposed ? { reason: 'closed' } : { reason: 'cannot', message: named(messages.recordReplacedNoun, messages.recordReplaced) };
1837
1845
  }
1838
1846
  /** The record a record step asks the data source about. */
1839
1847
  const record = () => ({ model: page.data.model, id: state.recordId, fields });
@@ -1943,7 +1951,7 @@ function innerForm(options, within, place, outer) {
1943
1951
  },
1944
1952
  post,
1945
1953
  wordsOf: (field) => wordsOfValue(fieldDef(field), state.values[field]),
1946
- barred: (right) => (state.can?.[right] === false ? state.can.reason ?? messages[MAY_NOT[right]] : null),
1954
+ barred: (right) => (state.can?.[right] === false ? state.can.reason ?? named(messages[`${MAY_NOT[right]}Noun`], messages[MAY_NOT[right]]) : null),
1947
1955
  });
1948
1956
  // A moment's run is on the record it was set off on: one set off again while its run goes, and run after it, is let
1949
1957
  // go when another record is shown by then (its steps were for that one).
@@ -1971,7 +1979,7 @@ function innerForm(options, within, place, outer) {
1971
1979
  const builtin = 'builtin' in source && !list.length ? source.builtin : undefined;
1972
1980
  if (builtin)
1973
1981
  list.push({ do: builtin });
1974
- const asked = builtin === 'archive' ? messages.archiveConfirm : builtin === 'delete' ? messages.deleteConfirm : undefined;
1982
+ const asked = builtin === 'archive' ? named(messages.archiveConfirmNoun, messages.archiveConfirm) : builtin === 'delete' ? named(messages.deleteConfirmNoun, messages.deleteConfirm) : undefined;
1975
1983
  const confirm = 'confirm' in source && source.confirm !== undefined ? source.confirm : asked;
1976
1984
  // On the record it was pressed on: another shown while its question is asked, it does nothing to that one.
1977
1985
  scope.generation ??= generation;
@@ -2286,7 +2294,7 @@ function innerForm(options, within, place, outer) {
2286
2294
  const sub = def.fields[subfield];
2287
2295
  if (!sub)
2288
2296
  throw new Error(`The lines of "${field}" have no field "${subfield}"`);
2289
- const line = (state.values[field] ?? []).find((l) => l.key === key);
2297
+ const line = key === null ? { values: newLineValues(field), id: null } : (state.values[field] ?? []).find((l) => l.key === key);
2290
2298
  if (!line)
2291
2299
  throw new Error(`"${field}" has no line "${key}"`);
2292
2300
  return runSearch(sub, subfield, lineContext(line.values, def.fields, context(), line.id ?? null), query, limit, options);