@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
package/README.md CHANGED
@@ -28,7 +28,7 @@ list of records — that your app implements;
28
28
  The page format is also a JSON Schema, for editors and other languages:
29
29
  `@fieldia/core/page.schema.json`.
30
30
 
31
- A page says which format it is written in (`"fieldia": "0.3"`, `FORMAT_VERSION`; a page in 0.1 or 0.2 is read as it is).
31
+ A page says which format it is written in (`"fieldia": "0.4"`, `FORMAT_VERSION`; a page in 0.1 or 0.2 is read as it is).
32
32
  One in an older format is brought up to date as it is read — by `checkPage`,
33
33
  `validatePage` and `createForm`, so by the viewer and the designer too — and
34
34
  `upgradePage` does it for the pages your app keeps. One newer than this Fieldia
@@ -63,6 +63,21 @@ settings each kind has are `PART_LOOKS`:
63
63
  }
64
64
  ```
65
65
 
66
+ ## Values that wait for a line
67
+
68
+ A value worked out from others waits while one of them holds text that does
69
+ not read — "abc" in a line's Quantity — as the table's own total does, so it is
70
+ never shown counting the value that text replaced. Who works it out does not
71
+ matter: the page (a `compute`, such as `sum(lines, 'subtotal')`, and what is
72
+ worked out from that in turn) or your app (an amount or totals kept whole, read
73
+ only, which a source with an `onchange` works out as the lines change — a sales
74
+ order's Untaxed, Taxes and Total, Flectra's `tax_totals`). `form.waitsFor(field)`
75
+ says what it waits for — `{ field: 'line_ids', line: 1, column: 'qty' }`, the line
76
+ as people count lines, or `{ field: 'rate' }` for a field of the record's own —
77
+ and null when it shows its value; the viewer draws it as a dash, said in words
78
+ ("Fix line 1 of Order lines to see Untaxed amount"). An answer your onchange
79
+ gives meanwhile is kept, and shown once the line reads.
80
+
66
81
  ## A public form's bot check
67
82
 
68
83
  A page of responses anyone may send — a sign-up, a contact form — can check that
@@ -202,6 +217,43 @@ const dataSource = createMemoryDataSource({
202
217
  });
203
218
  ```
204
219
 
220
+ ## Possible duplicates and Merge
221
+
222
+ A record's page may name the fields records like the one typed are found by —
223
+ the same phone, email or tax id, a name close enough:
224
+
225
+ ```json
226
+ { "duplicates": { "fields": ["name", "phone", "email", "vat"], "on": "type" } }
227
+ ```
228
+
229
+ The data source's `duplicates({ model, recordId, values, fields })` is asked
230
+ with the fields named that hold a value (none: nothing is asked), every value of
231
+ the form and the record typed (null while new), and answers with the records
232
+ like it — `{ matches: [{ id, name, why: ['phone'], score? }] }`, `why` naming
233
+ the fields each matched on. `on: "type"` (the default) asks as those fields are
234
+ typed in, as a saved record opens and as it is saved; `"save"` only as it is
235
+ saved. Nothing is in the way of a save. `merge({ model, keepId, mergeIds,
236
+ values, fields })` keeps `keepId` with the value picked for each field and takes
237
+ `mergeIds` away; moving what was theirs (messages, activities, links) is the
238
+ source's work, and `saveRefused({ kind: 'rule', message })` refuses it.
239
+ `form.findDuplicates()`, `form.readRecord(id)` and `form.merge(…)` ask the
240
+ source for a screen of your own, `form.canFindDuplicates()` and
241
+ `form.canMerge()` say whether it can; the viewer says each match under the field
242
+ it matched on, with Open it, "It's someone new" and Merge (`@fieldia/viewer`).
243
+ `checkPage` and `validatePage` refuse a field the page has not, one named twice,
244
+ and one of a kind records are not compared by (`DUPLICATE_KINDS`). Without
245
+ `duplicates` in the source, nothing is asked. For demos and tests,
246
+ `alikeRecords()` answers from the memory source's records — the same value as
247
+ people write them apart (a phone's spaces, an email's case, a link's record) or,
248
+ in the name field, a name close enough (`nameLikeness`) — and its `merge`
249
+ merges as a backend would:
250
+
251
+ ```ts
252
+ import { alikeRecords, createMemoryDataSource } from '@fieldia/core';
253
+
254
+ const dataSource = createMemoryDataSource({ records, duplicates: alikeRecords() });
255
+ ```
256
+
205
257
  ## Two people, one record
206
258
 
207
259
  A source that keeps versions returns each record with its version, and what this
@@ -289,6 +341,30 @@ says which fields Edit offers. `createMemoryDataSource` gives `allIds` and
289
341
  `writeMany` (each record refused as its save would be), and no `exportRecords`.
290
342
  See [many records at once](https://fieldia.dev/data/#many).
291
343
 
344
+ ## Records that point at this one
345
+
346
+ A page's `related` part lists another model's records whose link is this
347
+ record — a customer's invoices — inside it (the viewer draws it). Its
348
+ `model`, `field` (their many2one to this page's model), `fields` and `columns`
349
+ say what it lists; `filter`, `sort`, `limit`, `cells`, `searchFields`,
350
+ `viewAll` and `create` the rest. `form.relatedFilter(id)` gives what it asks
351
+ the data source's `list` for, as the source gets it: the link to the record
352
+ shown, then its own filter with each `valueFrom` read from that record — or null
353
+ before the record's first save. No new data source member: `list` answers it.
354
+ The page check holds its columns, order, search and cells to its own `fields`,
355
+ its link to a many2one of theirs to this model, and it to a record's page.
356
+
357
+ A list's Import makes records from a file's rows. `createMany({ model, records,
358
+ fields })` makes many at once and answers each in order: `{ id }`, or `{ message,
359
+ problem? }` for one it refused; without it the list saves each as a new record,
360
+ `save(newRecordSave(model, fields, values))`. `importRecords({ model, file,
361
+ columns, rows, fields, locale, dateOrder })` takes the file on the server once
362
+ its columns are matched: answer `{ message }`, or `false` to let the browser
363
+ import it. A page offers Import with its list's `import` (`true`, or the
364
+ fields); `importFields(page, list)` says which fields a file's columns may fill.
365
+ `createMemoryDataSource` gives `createMany`. See [many records made at
366
+ once](https://fieldia.dev/data/#import).
367
+
292
368
  ## A saved form placed in another
293
369
 
294
370
  A block made once — an address, a contact person, a consent — is a page of its
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fieldia/core",
3
- "version": "0.24.0",
3
+ "version": "0.25.0",
4
4
  "description": "Headless form model for Fieldia: the page format, modifiers, validation and record state. No DOM.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://fieldia.dev",