@fieldia/core 0.23.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.
- package/README.md +211 -3
- package/package.json +1 -1
- package/schema/page.schema.json +1051 -325
- package/src/index.d.mts +9 -2
- package/src/index.d.ts +9 -2
- package/src/index.js +31 -1
- package/src/index.js.map +1 -1
- package/src/index.mjs +12 -2
- package/src/index.mjs.map +1 -1
- package/src/lib/format/actions.d.mts +31 -1
- package/src/lib/format/actions.d.ts +31 -1
- package/src/lib/format/actions.js +1 -0
- package/src/lib/format/actions.js.map +1 -1
- package/src/lib/format/actions.mjs +1 -0
- package/src/lib/format/actions.mjs.map +1 -1
- package/src/lib/format/autofill-group.d.mts +12 -0
- package/src/lib/format/autofill-group.d.ts +12 -0
- package/src/lib/format/autofill-group.js +23 -0
- package/src/lib/format/autofill-group.js.map +1 -0
- package/src/lib/format/autofill-group.mjs +19 -0
- package/src/lib/format/autofill-group.mjs.map +1 -0
- package/src/lib/format/autofill.d.mts +14 -0
- package/src/lib/format/autofill.d.ts +14 -0
- package/src/lib/format/autofill.js +54 -0
- package/src/lib/format/autofill.js.map +1 -0
- package/src/lib/format/autofill.mjs +48 -0
- package/src/lib/format/autofill.mjs.map +1 -0
- package/src/lib/format/bulk-edit.d.mts +18 -0
- package/src/lib/format/bulk-edit.d.ts +18 -0
- package/src/lib/format/bulk-edit.js +26 -0
- package/src/lib/format/bulk-edit.js.map +1 -0
- package/src/lib/format/bulk-edit.mjs +21 -0
- package/src/lib/format/bulk-edit.mjs.map +1 -0
- package/src/lib/format/duplicate-kinds.d.mts +10 -0
- package/src/lib/format/duplicate-kinds.d.ts +10 -0
- package/src/lib/format/duplicate-kinds.js +14 -0
- package/src/lib/format/duplicate-kinds.js.map +1 -0
- package/src/lib/format/duplicate-kinds.mjs +10 -0
- package/src/lib/format/duplicate-kinds.mjs.map +1 -0
- package/src/lib/format/duplicates.d.mts +22 -0
- package/src/lib/format/duplicates.d.ts +22 -0
- package/src/lib/format/duplicates.js +24 -0
- package/src/lib/format/duplicates.js.map +1 -0
- package/src/lib/format/duplicates.mjs +21 -0
- package/src/lib/format/duplicates.mjs.map +1 -0
- package/src/lib/format/field.d.mts +291 -34
- package/src/lib/format/field.d.ts +291 -34
- package/src/lib/format/field.js +19 -4
- package/src/lib/format/field.js.map +1 -1
- package/src/lib/format/field.mjs +17 -2
- package/src/lib/format/field.mjs.map +1 -1
- package/src/lib/format/import-fields.d.mts +18 -0
- package/src/lib/format/import-fields.d.ts +18 -0
- package/src/lib/format/import-fields.js +26 -0
- package/src/lib/format/import-fields.js.map +1 -0
- package/src/lib/format/import-fields.mjs +21 -0
- package/src/lib/format/import-fields.mjs.map +1 -0
- package/src/lib/format/layout.d.mts +7357 -3127
- package/src/lib/format/layout.d.ts +7357 -3127
- package/src/lib/format/layout.js +108 -32
- package/src/lib/format/layout.js.map +1 -1
- package/src/lib/format/layout.mjs +107 -32
- package/src/lib/format/layout.mjs.map +1 -1
- package/src/lib/format/layout.types.d.mts +5 -2
- package/src/lib/format/layout.types.d.ts +5 -2
- package/src/lib/format/page.d.mts +1080 -106
- package/src/lib/format/page.d.ts +1080 -106
- package/src/lib/format/page.js +25 -0
- package/src/lib/format/page.js.map +1 -1
- package/src/lib/format/page.mjs +26 -1
- package/src/lib/format/page.mjs.map +1 -1
- package/src/lib/format/phone.d.mts +27 -0
- package/src/lib/format/phone.d.ts +27 -0
- package/src/lib/format/phone.js +43 -0
- package/src/lib/format/phone.js.map +1 -0
- package/src/lib/format/phone.mjs +39 -0
- package/src/lib/format/phone.mjs.map +1 -0
- package/src/lib/format/references.d.mts +39 -0
- package/src/lib/format/references.d.ts +39 -0
- package/src/lib/format/references.js +263 -20
- package/src/lib/format/references.js.map +1 -1
- package/src/lib/format/references.mjs +263 -20
- package/src/lib/format/references.mjs.map +1 -1
- package/src/lib/format/slots-options.d.mts +26 -0
- package/src/lib/format/slots-options.d.ts +26 -0
- package/src/lib/format/slots-options.js +35 -0
- package/src/lib/format/slots-options.js.map +1 -0
- package/src/lib/format/slots-options.mjs +31 -0
- package/src/lib/format/slots-options.mjs.map +1 -0
- package/src/lib/format/translate.js +8 -4
- package/src/lib/format/translate.js.map +1 -1
- package/src/lib/format/translate.mjs +8 -4
- package/src/lib/format/translate.mjs.map +1 -1
- package/src/lib/format/upgrade.d.mts +8 -3
- package/src/lib/format/upgrade.d.ts +8 -3
- package/src/lib/format/upgrade.js +8 -3
- package/src/lib/format/upgrade.js.map +1 -1
- package/src/lib/format/upgrade.mjs +8 -3
- package/src/lib/format/upgrade.mjs.map +1 -1
- package/src/lib/format/version.d.mts +2 -2
- package/src/lib/format/version.d.ts +2 -2
- package/src/lib/format/version.js +2 -2
- package/src/lib/format/version.js.map +1 -1
- package/src/lib/format/version.mjs +2 -2
- package/src/lib/format/version.mjs.map +1 -1
- package/src/lib/format/zoned.d.mts +8 -0
- package/src/lib/format/zoned.d.ts +8 -0
- package/src/lib/format/zoned.js +36 -0
- package/src/lib/format/zoned.js.map +1 -0
- package/src/lib/format/zoned.mjs +33 -0
- package/src/lib/format/zoned.mjs.map +1 -0
- package/src/lib/record/check.d.mts +6 -1
- package/src/lib/record/check.d.ts +6 -1
- package/src/lib/record/check.js +12 -4
- package/src/lib/record/check.js.map +1 -1
- package/src/lib/record/check.mjs +12 -4
- package/src/lib/record/check.mjs.map +1 -1
- package/src/lib/record/data-source.d.mts +309 -3
- package/src/lib/record/data-source.d.ts +309 -3
- package/src/lib/record/data-source.js +16 -0
- package/src/lib/record/data-source.js.map +1 -1
- package/src/lib/record/data-source.mjs +15 -0
- package/src/lib/record/data-source.mjs.map +1 -1
- package/src/lib/record/form.d.mts +116 -25
- package/src/lib/record/form.d.ts +116 -25
- package/src/lib/record/form.js +457 -78
- package/src/lib/record/form.js.map +1 -1
- package/src/lib/record/form.mjs +457 -78
- package/src/lib/record/form.mjs.map +1 -1
- package/src/lib/record/input-rules.d.mts +51 -9
- package/src/lib/record/input-rules.d.ts +51 -9
- package/src/lib/record/input-rules.js +119 -20
- package/src/lib/record/input-rules.js.map +1 -1
- package/src/lib/record/input-rules.mjs +118 -19
- package/src/lib/record/input-rules.mjs.map +1 -1
- package/src/lib/record/inputs.js +4 -2
- package/src/lib/record/inputs.js.map +1 -1
- package/src/lib/record/inputs.mjs +4 -2
- package/src/lib/record/inputs.mjs.map +1 -1
- package/src/lib/record/limits.d.mts +1 -1
- package/src/lib/record/limits.d.ts +1 -1
- package/src/lib/record/limits.js +4 -2
- package/src/lib/record/limits.js.map +1 -1
- package/src/lib/record/limits.mjs +4 -2
- package/src/lib/record/limits.mjs.map +1 -1
- package/src/lib/record/locales/ar.js +3 -2
- package/src/lib/record/locales/ar.js.map +1 -1
- package/src/lib/record/locales/ar.mjs +3 -2
- package/src/lib/record/locales/ar.mjs.map +1 -1
- package/src/lib/record/locales/de.js +3 -2
- package/src/lib/record/locales/de.js.map +1 -1
- package/src/lib/record/locales/de.mjs +3 -2
- package/src/lib/record/locales/de.mjs.map +1 -1
- package/src/lib/record/locales/fr.js +3 -2
- package/src/lib/record/locales/fr.js.map +1 -1
- package/src/lib/record/locales/fr.mjs +3 -2
- package/src/lib/record/locales/fr.mjs.map +1 -1
- package/src/lib/record/memory-data-source.d.mts +20 -2
- package/src/lib/record/memory-data-source.d.ts +20 -2
- package/src/lib/record/memory-data-source.js +153 -35
- package/src/lib/record/memory-data-source.js.map +1 -1
- package/src/lib/record/memory-data-source.mjs +154 -36
- package/src/lib/record/memory-data-source.mjs.map +1 -1
- package/src/lib/record/memory-duplicates.d.mts +19 -0
- package/src/lib/record/memory-duplicates.d.ts +19 -0
- package/src/lib/record/memory-duplicates.js +78 -0
- package/src/lib/record/memory-duplicates.js.map +1 -0
- package/src/lib/record/memory-duplicates.mjs +74 -0
- package/src/lib/record/memory-duplicates.mjs.map +1 -0
- package/src/lib/record/memory-schedules.d.mts +45 -0
- package/src/lib/record/memory-schedules.d.ts +45 -0
- package/src/lib/record/memory-schedules.js +81 -0
- package/src/lib/record/memory-schedules.js.map +1 -0
- package/src/lib/record/memory-schedules.mjs +78 -0
- package/src/lib/record/memory-schedules.mjs.map +1 -0
- package/src/lib/record/messages.d.mts +3 -1
- package/src/lib/record/messages.d.ts +3 -1
- package/src/lib/record/messages.js +3 -2
- package/src/lib/record/messages.js.map +1 -1
- package/src/lib/record/messages.mjs +3 -2
- package/src/lib/record/messages.mjs.map +1 -1
- package/src/lib/record/periods.d.mts +47 -0
- package/src/lib/record/periods.d.ts +47 -0
- package/src/lib/record/periods.js +86 -0
- package/src/lib/record/periods.js.map +1 -0
- package/src/lib/record/periods.mjs +79 -0
- package/src/lib/record/periods.mjs.map +1 -0
- package/src/lib/record/rules.js +5 -6
- package/src/lib/record/rules.js.map +1 -1
- package/src/lib/record/rules.mjs +5 -6
- package/src/lib/record/rules.mjs.map +1 -1
- package/src/lib/record/run.d.mts +75 -9
- package/src/lib/record/run.d.ts +75 -9
- package/src/lib/record/run.js +169 -26
- package/src/lib/record/run.js.map +1 -1
- package/src/lib/record/run.mjs +170 -27
- package/src/lib/record/run.mjs.map +1 -1
- package/src/lib/record/search.d.mts +13 -3
- package/src/lib/record/search.d.ts +13 -3
- package/src/lib/record/search.js +11 -4
- package/src/lib/record/search.js.map +1 -1
- package/src/lib/record/search.mjs +11 -4
- package/src/lib/record/search.mjs.map +1 -1
- package/src/lib/record/waits.d.mts +32 -0
- package/src/lib/record/waits.d.ts +32 -0
- package/src/lib/record/waits.js +89 -0
- package/src/lib/record/waits.js.map +1 -0
- package/src/lib/record/waits.mjs +85 -0
- package/src/lib/record/waits.mjs.map +1 -0
- package/src/lib/record/zones.d.mts +57 -0
- package/src/lib/record/zones.d.ts +57 -0
- package/src/lib/record/zones.js +147 -0
- package/src/lib/record/zones.js.map +1 -0
- package/src/lib/record/zones.mjs +132 -0
- package/src/lib/record/zones.mjs.map +1 -0
- package/src/runtime.d.mts +19 -9
- package/src/runtime.d.ts +19 -9
- package/src/runtime.js +37 -2
- package/src/runtime.js.map +1 -1
- package/src/runtime.mjs +9 -2
- package/src/runtime.mjs.map +1 -1
|
@@ -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 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';
|
|
@@ -61,11 +62,12 @@ export interface FormState {
|
|
|
61
62
|
/**
|
|
62
63
|
* Fields holding a change of the person's that the last finished save of
|
|
63
64
|
* this record did not send: made while it was on its way, while the run that
|
|
64
|
-
* saved went on (its afterSave run, a copy being made), or since
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
65
|
+
* saved went on (its afterSave run, a copy being made), or since — and text
|
|
66
|
+
* they left since in a box of it that does not read (`unread`), which no save
|
|
67
|
+
* can send. Worked out from the record as that save left it, so a change back
|
|
68
|
+
* to it is none; and before the record's first save, only a file the store
|
|
69
|
+
* holds and no save sent. Never a value a step, a rule, a compute or the app
|
|
70
|
+
* set: each change is told by its origin. The next record starts with none.
|
|
69
71
|
*/
|
|
70
72
|
readonly unsent: readonly string[];
|
|
71
73
|
/**
|
|
@@ -80,6 +82,15 @@ export interface FormState {
|
|
|
80
82
|
*/
|
|
81
83
|
readonly unread: Readonly<Record<string, UnreadText>>;
|
|
82
84
|
}
|
|
85
|
+
/** How a search for a link's records is asked (`form.search`, `form.searchLine`). */
|
|
86
|
+
export interface SearchOptions {
|
|
87
|
+
/** The model searched: a reference's, or the model structured data names records of. */
|
|
88
|
+
model?: string;
|
|
89
|
+
/** Those records alone, for their names. */
|
|
90
|
+
ids?: RecordId[];
|
|
91
|
+
/** Only the records named exactly the query, in any case (`SearchRequest.exact`): a typed name read as a record. */
|
|
92
|
+
exact?: boolean;
|
|
93
|
+
}
|
|
83
94
|
/** Text left in a box that does not read, and why, as `FormState.unread` keeps it. */
|
|
84
95
|
export interface UnreadText {
|
|
85
96
|
readonly text: string;
|
|
@@ -110,12 +121,15 @@ export interface FormConflict {
|
|
|
110
121
|
export declare function unsavedWork(state: FormState): boolean;
|
|
111
122
|
/**
|
|
112
123
|
* Whether a way out would lose the person's work without their knowing: a
|
|
113
|
-
* change of theirs the last save did not send
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
124
|
+
* change of theirs the last save did not send, or text they left since that
|
|
125
|
+
* does not read ("12a" counts as "12" does: `unsent`), files still on
|
|
126
|
+
* their way, changes Reload set aside, the changes of a save refused for a
|
|
127
|
+
* conflict, waiting for Reload or Keep mine (only a source that keeps
|
|
128
|
+
* versions has one) — and, `how.changes`, any change not saved. What
|
|
129
|
+
* `form.leave()` — the one check every way out goes through — asks about,
|
|
130
|
+
* once the save on its way and the files on theirs settled. Before a
|
|
131
|
+
* record's first save, a change not saved is not at risk: as in 0.20, each
|
|
132
|
+
* way out decides.
|
|
119
133
|
*/
|
|
120
134
|
export declare function workAtRisk(state: FormState, how?: LeaveHow): boolean;
|
|
121
135
|
/**
|
|
@@ -169,14 +183,15 @@ export interface FormEvents {
|
|
|
169
183
|
values: Readonly<Values>;
|
|
170
184
|
by: ChangeBy;
|
|
171
185
|
};
|
|
172
|
-
/** A record was saved. */
|
|
186
|
+
/** A record was saved: its values as the save left it — what it sent and what came back; a change made meanwhile is not among them. */
|
|
173
187
|
save: {
|
|
174
188
|
recordId: RecordId | null;
|
|
175
189
|
values: Readonly<Values>;
|
|
176
190
|
};
|
|
177
|
-
/** A page of responses sent its answers: those sent. */
|
|
191
|
+
/** A page of responses sent its answers: those sent, and on a page with a bot check (`botCheck`) what it saw. */
|
|
178
192
|
send: {
|
|
179
193
|
values: Values;
|
|
194
|
+
bot?: BotReport;
|
|
180
195
|
};
|
|
181
196
|
/** The app answered a call: with nothing (undefined), or an `ActionResult`. */
|
|
182
197
|
action: {
|
|
@@ -313,6 +328,14 @@ export interface Form {
|
|
|
313
328
|
fieldReadonly(name: string): boolean;
|
|
314
329
|
/** What a field would be told if the form were checked now, or null when it passes. Nothing is shown. */
|
|
315
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;
|
|
316
339
|
/**
|
|
317
340
|
* Keep text a person left in a box that does not read (`FormState.unread`), by its path — `field`, or a line's
|
|
318
341
|
* `field.lineKey.subfield` — with why in words; null once it reads, or is let go. Nothing is shown until the box
|
|
@@ -341,6 +364,15 @@ export interface Form {
|
|
|
341
364
|
* Resolves once every reading that waits (a name looked up) has ended.
|
|
342
365
|
*/
|
|
343
366
|
settle(): Promise<void>;
|
|
367
|
+
/**
|
|
368
|
+
* Tell a public page's bot check (`page.botCheck`) what was seen: its hidden box filled (`honeypot`, the viewer's), or
|
|
369
|
+
* the captcha's token as the app's widget gives it (`captcha`; null once it expires). Each send carries them, with
|
|
370
|
+
* the time the answers took, as `bot` (`SubmitRequest`); a token goes once it is sent, as a captcha's is spent.
|
|
371
|
+
*/
|
|
372
|
+
botCheck(seen: {
|
|
373
|
+
honeypot?: boolean;
|
|
374
|
+
captcha?: string | null;
|
|
375
|
+
}): void;
|
|
344
376
|
/**
|
|
345
377
|
* Whether a box's reading of what was typed still waits (a name being looked up): a check or a step forward made
|
|
346
378
|
* now refuses its text with "still looking", so a caller that can wait for it (`settle`) checks again after.
|
|
@@ -390,6 +422,23 @@ export interface Form {
|
|
|
390
422
|
* first, so a way out with nothing to wait for is not put off.
|
|
391
423
|
*/
|
|
392
424
|
leaveNow(how?: LeaveHow): boolean;
|
|
425
|
+
/**
|
|
426
|
+
* The record shown now, for an answer asked for it that comes later: the
|
|
427
|
+
* function returned is true while that record is still the one shown, as it
|
|
428
|
+
* was — no other opened, it not loaded again, no new one started, the
|
|
429
|
+
* person's Discard not pressed, the form not closed. What writes on the
|
|
430
|
+
* form, or acts on its record, once a wait ends — a name looked up, a
|
|
431
|
+
* record made from a link, a line's dialog, a step's call to the source —
|
|
432
|
+
* asks it first, so an answer is laid only on the record it was asked for
|
|
433
|
+
* (docs/plans/wave1-contract.md, section 10, part 2).
|
|
434
|
+
*/
|
|
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;
|
|
393
442
|
/**
|
|
394
443
|
* Post words in the record's conversation, as a `post` step does: through
|
|
395
444
|
* the form's host when it has a way to, else told to the form's own
|
|
@@ -414,17 +463,12 @@ export interface Form {
|
|
|
414
463
|
* Records a many2one, many2many or reference may point to. A reference needs
|
|
415
464
|
* `options.model`, and so does structured data naming records of a model (a
|
|
416
465
|
* json field, such as an analytic distribution's accounts); `options.ids`
|
|
417
|
-
* finds those records alone, for their names.
|
|
466
|
+
* finds those records alone, for their names; `options.exact`, those named
|
|
467
|
+
* exactly the query (`SearchRequest.exact`).
|
|
418
468
|
*/
|
|
419
|
-
search(field: string, query: string, limit?: number, options?:
|
|
420
|
-
model?: string;
|
|
421
|
-
ids?: RecordId[];
|
|
422
|
-
}): Promise<RelatedRecord[]>;
|
|
469
|
+
search(field: string, query: string, limit?: number, options?: SearchOptions): Promise<RelatedRecord[]>;
|
|
423
470
|
/** The same, for a relation inside a one2many line, filtered by that line's values — or structured data of a line naming records of a model (`options.model`). */
|
|
424
|
-
searchLine(field: string, key: string, subfield: string, query: string, limit?: number, options?:
|
|
425
|
-
model?: string;
|
|
426
|
-
ids?: RecordId[];
|
|
427
|
-
}): Promise<RelatedRecord[]>;
|
|
471
|
+
searchLine(field: string, key: string, subfield: string, query: string, limit?: number, options?: SearchOptions): Promise<RelatedRecord[]>;
|
|
428
472
|
/**
|
|
429
473
|
* The values of records a many2many links to, for a table of them: `fields`
|
|
430
474
|
* of the records with these ids, in the order asked, read with the data
|
|
@@ -460,7 +504,9 @@ export interface Form {
|
|
|
460
504
|
/**
|
|
461
505
|
* Press a button: ask its `confirm` first, run its `steps`, then call its
|
|
462
506
|
* `action`, when it names one, as a final `call`. A list's button passes
|
|
463
|
-
* the records chosen in it, and every call of the run carries them.
|
|
507
|
+
* the records chosen in it, and every call of the run carries them. What is
|
|
508
|
+
* typed is read before its first step that reads the form or hands its
|
|
509
|
+
* values on, as a save reads it: text that does not read stops the run.
|
|
464
510
|
*/
|
|
465
511
|
runAction(id: string, chosen?: {
|
|
466
512
|
recordIds: RecordId[];
|
|
@@ -485,7 +531,7 @@ export interface Form {
|
|
|
485
531
|
* carrying the lines. Hidden, or with no line chosen, it does not run.
|
|
486
532
|
*/
|
|
487
533
|
runLinesAction(nodeId: string, buttonId: string, keys: readonly string[]): Promise<RunResult>;
|
|
488
|
-
/** Run steps on this form, as a button would: `context.id` is what their calls carry (`run` when left out). */
|
|
534
|
+
/** Run steps on this form, as a button would — what is typed read first: `context.id` is what their calls carry (`run` when left out). */
|
|
489
535
|
run(steps: readonly ActionStep[], context?: RunContext): Promise<RunResult>;
|
|
490
536
|
/** A tab was shown: the viewer says so, and the page's `show` steps for it run. */
|
|
491
537
|
shown(tab: string): void;
|
|
@@ -499,6 +545,51 @@ export interface Form {
|
|
|
499
545
|
* travels inside the record as base64, as it always has.
|
|
500
546
|
*/
|
|
501
547
|
canUpload(): boolean;
|
|
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`). */
|
|
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>;
|
|
557
|
+
/** Whether a date and time drawn as booking slots can ask for its free times: the data source has `slots`. */
|
|
558
|
+
canFindSlots(): boolean;
|
|
559
|
+
/**
|
|
560
|
+
* The free times of a date and time drawn as booking slots, asked of the data source's `slots` for the days `from`
|
|
561
|
+
* to `to` — in the field's time zone: its own, its page's, or the browser's — with the record, its values and the
|
|
562
|
+
* zone filled in. Rejects where the source has no `slots`.
|
|
563
|
+
*/
|
|
564
|
+
findSlots(field: string, ask: {
|
|
565
|
+
from: string;
|
|
566
|
+
to: string;
|
|
567
|
+
schedule?: string;
|
|
568
|
+
length?: number;
|
|
569
|
+
between?: number;
|
|
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>;
|
|
502
593
|
/**
|
|
503
594
|
* Store files picked for a file or picture field — or a field on a table's
|
|
504
595
|
* line (`how.line`) — through the data source's `upload`, each as soon as
|
package/src/lib/record/form.d.ts
CHANGED
|
@@ -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 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';
|
|
@@ -61,11 +62,12 @@ export interface FormState {
|
|
|
61
62
|
/**
|
|
62
63
|
* Fields holding a change of the person's that the last finished save of
|
|
63
64
|
* this record did not send: made while it was on its way, while the run that
|
|
64
|
-
* saved went on (its afterSave run, a copy being made), or since
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
65
|
+
* saved went on (its afterSave run, a copy being made), or since — and text
|
|
66
|
+
* they left since in a box of it that does not read (`unread`), which no save
|
|
67
|
+
* can send. Worked out from the record as that save left it, so a change back
|
|
68
|
+
* to it is none; and before the record's first save, only a file the store
|
|
69
|
+
* holds and no save sent. Never a value a step, a rule, a compute or the app
|
|
70
|
+
* set: each change is told by its origin. The next record starts with none.
|
|
69
71
|
*/
|
|
70
72
|
readonly unsent: readonly string[];
|
|
71
73
|
/**
|
|
@@ -80,6 +82,15 @@ export interface FormState {
|
|
|
80
82
|
*/
|
|
81
83
|
readonly unread: Readonly<Record<string, UnreadText>>;
|
|
82
84
|
}
|
|
85
|
+
/** How a search for a link's records is asked (`form.search`, `form.searchLine`). */
|
|
86
|
+
export interface SearchOptions {
|
|
87
|
+
/** The model searched: a reference's, or the model structured data names records of. */
|
|
88
|
+
model?: string;
|
|
89
|
+
/** Those records alone, for their names. */
|
|
90
|
+
ids?: RecordId[];
|
|
91
|
+
/** Only the records named exactly the query, in any case (`SearchRequest.exact`): a typed name read as a record. */
|
|
92
|
+
exact?: boolean;
|
|
93
|
+
}
|
|
83
94
|
/** Text left in a box that does not read, and why, as `FormState.unread` keeps it. */
|
|
84
95
|
export interface UnreadText {
|
|
85
96
|
readonly text: string;
|
|
@@ -110,12 +121,15 @@ export interface FormConflict {
|
|
|
110
121
|
export declare function unsavedWork(state: FormState): boolean;
|
|
111
122
|
/**
|
|
112
123
|
* Whether a way out would lose the person's work without their knowing: a
|
|
113
|
-
* change of theirs the last save did not send
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
124
|
+
* change of theirs the last save did not send, or text they left since that
|
|
125
|
+
* does not read ("12a" counts as "12" does: `unsent`), files still on
|
|
126
|
+
* their way, changes Reload set aside, the changes of a save refused for a
|
|
127
|
+
* conflict, waiting for Reload or Keep mine (only a source that keeps
|
|
128
|
+
* versions has one) — and, `how.changes`, any change not saved. What
|
|
129
|
+
* `form.leave()` — the one check every way out goes through — asks about,
|
|
130
|
+
* once the save on its way and the files on theirs settled. Before a
|
|
131
|
+
* record's first save, a change not saved is not at risk: as in 0.20, each
|
|
132
|
+
* way out decides.
|
|
119
133
|
*/
|
|
120
134
|
export declare function workAtRisk(state: FormState, how?: LeaveHow): boolean;
|
|
121
135
|
/**
|
|
@@ -169,14 +183,15 @@ export interface FormEvents {
|
|
|
169
183
|
values: Readonly<Values>;
|
|
170
184
|
by: ChangeBy;
|
|
171
185
|
};
|
|
172
|
-
/** A record was saved. */
|
|
186
|
+
/** A record was saved: its values as the save left it — what it sent and what came back; a change made meanwhile is not among them. */
|
|
173
187
|
save: {
|
|
174
188
|
recordId: RecordId | null;
|
|
175
189
|
values: Readonly<Values>;
|
|
176
190
|
};
|
|
177
|
-
/** A page of responses sent its answers: those sent. */
|
|
191
|
+
/** A page of responses sent its answers: those sent, and on a page with a bot check (`botCheck`) what it saw. */
|
|
178
192
|
send: {
|
|
179
193
|
values: Values;
|
|
194
|
+
bot?: BotReport;
|
|
180
195
|
};
|
|
181
196
|
/** The app answered a call: with nothing (undefined), or an `ActionResult`. */
|
|
182
197
|
action: {
|
|
@@ -313,6 +328,14 @@ export interface Form {
|
|
|
313
328
|
fieldReadonly(name: string): boolean;
|
|
314
329
|
/** What a field would be told if the form were checked now, or null when it passes. Nothing is shown. */
|
|
315
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;
|
|
316
339
|
/**
|
|
317
340
|
* Keep text a person left in a box that does not read (`FormState.unread`), by its path — `field`, or a line's
|
|
318
341
|
* `field.lineKey.subfield` — with why in words; null once it reads, or is let go. Nothing is shown until the box
|
|
@@ -341,6 +364,15 @@ export interface Form {
|
|
|
341
364
|
* Resolves once every reading that waits (a name looked up) has ended.
|
|
342
365
|
*/
|
|
343
366
|
settle(): Promise<void>;
|
|
367
|
+
/**
|
|
368
|
+
* Tell a public page's bot check (`page.botCheck`) what was seen: its hidden box filled (`honeypot`, the viewer's), or
|
|
369
|
+
* the captcha's token as the app's widget gives it (`captcha`; null once it expires). Each send carries them, with
|
|
370
|
+
* the time the answers took, as `bot` (`SubmitRequest`); a token goes once it is sent, as a captcha's is spent.
|
|
371
|
+
*/
|
|
372
|
+
botCheck(seen: {
|
|
373
|
+
honeypot?: boolean;
|
|
374
|
+
captcha?: string | null;
|
|
375
|
+
}): void;
|
|
344
376
|
/**
|
|
345
377
|
* Whether a box's reading of what was typed still waits (a name being looked up): a check or a step forward made
|
|
346
378
|
* now refuses its text with "still looking", so a caller that can wait for it (`settle`) checks again after.
|
|
@@ -390,6 +422,23 @@ export interface Form {
|
|
|
390
422
|
* first, so a way out with nothing to wait for is not put off.
|
|
391
423
|
*/
|
|
392
424
|
leaveNow(how?: LeaveHow): boolean;
|
|
425
|
+
/**
|
|
426
|
+
* The record shown now, for an answer asked for it that comes later: the
|
|
427
|
+
* function returned is true while that record is still the one shown, as it
|
|
428
|
+
* was — no other opened, it not loaded again, no new one started, the
|
|
429
|
+
* person's Discard not pressed, the form not closed. What writes on the
|
|
430
|
+
* form, or acts on its record, once a wait ends — a name looked up, a
|
|
431
|
+
* record made from a link, a line's dialog, a step's call to the source —
|
|
432
|
+
* asks it first, so an answer is laid only on the record it was asked for
|
|
433
|
+
* (docs/plans/wave1-contract.md, section 10, part 2).
|
|
434
|
+
*/
|
|
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;
|
|
393
442
|
/**
|
|
394
443
|
* Post words in the record's conversation, as a `post` step does: through
|
|
395
444
|
* the form's host when it has a way to, else told to the form's own
|
|
@@ -414,17 +463,12 @@ export interface Form {
|
|
|
414
463
|
* Records a many2one, many2many or reference may point to. A reference needs
|
|
415
464
|
* `options.model`, and so does structured data naming records of a model (a
|
|
416
465
|
* json field, such as an analytic distribution's accounts); `options.ids`
|
|
417
|
-
* finds those records alone, for their names.
|
|
466
|
+
* finds those records alone, for their names; `options.exact`, those named
|
|
467
|
+
* exactly the query (`SearchRequest.exact`).
|
|
418
468
|
*/
|
|
419
|
-
search(field: string, query: string, limit?: number, options?:
|
|
420
|
-
model?: string;
|
|
421
|
-
ids?: RecordId[];
|
|
422
|
-
}): Promise<RelatedRecord[]>;
|
|
469
|
+
search(field: string, query: string, limit?: number, options?: SearchOptions): Promise<RelatedRecord[]>;
|
|
423
470
|
/** The same, for a relation inside a one2many line, filtered by that line's values — or structured data of a line naming records of a model (`options.model`). */
|
|
424
|
-
searchLine(field: string, key: string, subfield: string, query: string, limit?: number, options?:
|
|
425
|
-
model?: string;
|
|
426
|
-
ids?: RecordId[];
|
|
427
|
-
}): Promise<RelatedRecord[]>;
|
|
471
|
+
searchLine(field: string, key: string, subfield: string, query: string, limit?: number, options?: SearchOptions): Promise<RelatedRecord[]>;
|
|
428
472
|
/**
|
|
429
473
|
* The values of records a many2many links to, for a table of them: `fields`
|
|
430
474
|
* of the records with these ids, in the order asked, read with the data
|
|
@@ -460,7 +504,9 @@ export interface Form {
|
|
|
460
504
|
/**
|
|
461
505
|
* Press a button: ask its `confirm` first, run its `steps`, then call its
|
|
462
506
|
* `action`, when it names one, as a final `call`. A list's button passes
|
|
463
|
-
* the records chosen in it, and every call of the run carries them.
|
|
507
|
+
* the records chosen in it, and every call of the run carries them. What is
|
|
508
|
+
* typed is read before its first step that reads the form or hands its
|
|
509
|
+
* values on, as a save reads it: text that does not read stops the run.
|
|
464
510
|
*/
|
|
465
511
|
runAction(id: string, chosen?: {
|
|
466
512
|
recordIds: RecordId[];
|
|
@@ -485,7 +531,7 @@ export interface Form {
|
|
|
485
531
|
* carrying the lines. Hidden, or with no line chosen, it does not run.
|
|
486
532
|
*/
|
|
487
533
|
runLinesAction(nodeId: string, buttonId: string, keys: readonly string[]): Promise<RunResult>;
|
|
488
|
-
/** Run steps on this form, as a button would: `context.id` is what their calls carry (`run` when left out). */
|
|
534
|
+
/** Run steps on this form, as a button would — what is typed read first: `context.id` is what their calls carry (`run` when left out). */
|
|
489
535
|
run(steps: readonly ActionStep[], context?: RunContext): Promise<RunResult>;
|
|
490
536
|
/** A tab was shown: the viewer says so, and the page's `show` steps for it run. */
|
|
491
537
|
shown(tab: string): void;
|
|
@@ -499,6 +545,51 @@ export interface Form {
|
|
|
499
545
|
* travels inside the record as base64, as it always has.
|
|
500
546
|
*/
|
|
501
547
|
canUpload(): boolean;
|
|
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`). */
|
|
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>;
|
|
557
|
+
/** Whether a date and time drawn as booking slots can ask for its free times: the data source has `slots`. */
|
|
558
|
+
canFindSlots(): boolean;
|
|
559
|
+
/**
|
|
560
|
+
* The free times of a date and time drawn as booking slots, asked of the data source's `slots` for the days `from`
|
|
561
|
+
* to `to` — in the field's time zone: its own, its page's, or the browser's — with the record, its values and the
|
|
562
|
+
* zone filled in. Rejects where the source has no `slots`.
|
|
563
|
+
*/
|
|
564
|
+
findSlots(field: string, ask: {
|
|
565
|
+
from: string;
|
|
566
|
+
to: string;
|
|
567
|
+
schedule?: string;
|
|
568
|
+
length?: number;
|
|
569
|
+
between?: number;
|
|
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>;
|
|
502
593
|
/**
|
|
503
594
|
* Store files picked for a file or picture field — or a field on a table's
|
|
504
595
|
* line (`how.line`) — through the data source's `upload`, each as soon as
|