@fieldia/core 0.10.0 → 0.12.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 (205) hide show
  1. package/README.md +204 -0
  2. package/package.json +1 -1
  3. package/schema/page.schema.json +4700 -904
  4. package/src/index.d.mts +2 -1
  5. package/src/index.d.ts +2 -1
  6. package/src/index.js +7 -1
  7. package/src/index.js.map +1 -1
  8. package/src/index.mjs +2 -1
  9. package/src/index.mjs.map +1 -1
  10. package/src/lib/expression/built-ins.d.mts +4 -0
  11. package/src/lib/expression/built-ins.d.ts +4 -0
  12. package/src/lib/expression/built-ins.js +8 -0
  13. package/src/lib/expression/built-ins.js.map +1 -0
  14. package/src/lib/expression/built-ins.mjs +5 -0
  15. package/src/lib/expression/built-ins.mjs.map +1 -0
  16. package/src/lib/expression/evaluator.js +17 -7
  17. package/src/lib/expression/evaluator.js.map +1 -1
  18. package/src/lib/expression/evaluator.mjs +17 -7
  19. package/src/lib/expression/evaluator.mjs.map +1 -1
  20. package/src/lib/expression/expression.d.mts +7 -0
  21. package/src/lib/expression/expression.d.ts +7 -0
  22. package/src/lib/expression/expression.js +2 -0
  23. package/src/lib/expression/expression.js.map +1 -1
  24. package/src/lib/expression/expression.mjs +3 -1
  25. package/src/lib/expression/expression.mjs.map +1 -1
  26. package/src/lib/expression/functions.d.mts +2 -0
  27. package/src/lib/expression/functions.d.ts +2 -0
  28. package/src/lib/expression/functions.js +38 -5
  29. package/src/lib/expression/functions.js.map +1 -1
  30. package/src/lib/expression/functions.mjs +38 -5
  31. package/src/lib/expression/functions.mjs.map +1 -1
  32. package/src/lib/expression/modifier.d.mts +7 -0
  33. package/src/lib/expression/modifier.d.ts +7 -0
  34. package/src/lib/expression/modifier.js +3 -1
  35. package/src/lib/expression/modifier.js.map +1 -1
  36. package/src/lib/expression/modifier.mjs +4 -2
  37. package/src/lib/expression/modifier.mjs.map +1 -1
  38. package/src/lib/expression/parser.d.mts +8 -1
  39. package/src/lib/expression/parser.d.ts +8 -1
  40. package/src/lib/expression/parser.js +30 -2
  41. package/src/lib/expression/parser.js.map +1 -1
  42. package/src/lib/expression/parser.mjs +30 -2
  43. package/src/lib/expression/parser.mjs.map +1 -1
  44. package/src/lib/expression/reads.d.mts +7 -0
  45. package/src/lib/expression/reads.d.ts +7 -0
  46. package/src/lib/expression/reads.js +23 -0
  47. package/src/lib/expression/reads.js.map +1 -1
  48. package/src/lib/expression/reads.mjs +21 -0
  49. package/src/lib/expression/reads.mjs.map +1 -1
  50. package/src/lib/format/actions.d.mts +1176 -0
  51. package/src/lib/format/actions.d.ts +1176 -0
  52. package/src/lib/format/actions.js +62 -0
  53. package/src/lib/format/actions.js.map +1 -0
  54. package/src/lib/format/actions.mjs +59 -0
  55. package/src/lib/format/actions.mjs.map +1 -0
  56. package/src/lib/format/check-page.d.mts +7 -2
  57. package/src/lib/format/check-page.d.ts +7 -2
  58. package/src/lib/format/check-page.js +6 -3
  59. package/src/lib/format/check-page.js.map +1 -1
  60. package/src/lib/format/check-page.mjs +6 -3
  61. package/src/lib/format/check-page.mjs.map +1 -1
  62. package/src/lib/format/field.d.mts +3285 -214
  63. package/src/lib/format/field.d.ts +3285 -214
  64. package/src/lib/format/field.js +105 -16
  65. package/src/lib/format/field.js.map +1 -1
  66. package/src/lib/format/field.mjs +104 -15
  67. package/src/lib/format/field.mjs.map +1 -1
  68. package/src/lib/format/hotkeys.d.mts +6 -0
  69. package/src/lib/format/hotkeys.d.ts +6 -0
  70. package/src/lib/format/hotkeys.js +11 -0
  71. package/src/lib/format/hotkeys.js.map +1 -0
  72. package/src/lib/format/hotkeys.mjs +7 -0
  73. package/src/lib/format/hotkeys.mjs.map +1 -0
  74. package/src/lib/format/layout.d.mts +4773 -211
  75. package/src/lib/format/layout.d.ts +4773 -211
  76. package/src/lib/format/layout.js +147 -21
  77. package/src/lib/format/layout.js.map +1 -1
  78. package/src/lib/format/layout.mjs +146 -20
  79. package/src/lib/format/layout.mjs.map +1 -1
  80. package/src/lib/format/layout.types.d.mts +2 -1
  81. package/src/lib/format/layout.types.d.ts +2 -1
  82. package/src/lib/format/page.d.mts +3563 -222
  83. package/src/lib/format/page.d.ts +3563 -222
  84. package/src/lib/format/page.js +19 -2
  85. package/src/lib/format/page.js.map +1 -1
  86. package/src/lib/format/page.mjs +20 -3
  87. package/src/lib/format/page.mjs.map +1 -1
  88. package/src/lib/format/part-look.d.mts +36 -0
  89. package/src/lib/format/part-look.d.ts +36 -0
  90. package/src/lib/format/part-look.js +23 -0
  91. package/src/lib/format/part-look.js.map +1 -0
  92. package/src/lib/format/part-look.mjs +20 -0
  93. package/src/lib/format/part-look.mjs.map +1 -0
  94. package/src/lib/format/references.d.mts +54 -2
  95. package/src/lib/format/references.d.ts +54 -2
  96. package/src/lib/format/references.js +450 -43
  97. package/src/lib/format/references.js.map +1 -1
  98. package/src/lib/format/references.mjs +450 -43
  99. package/src/lib/format/references.mjs.map +1 -1
  100. package/src/lib/format/translate.js +5 -3
  101. package/src/lib/format/translate.js.map +1 -1
  102. package/src/lib/format/translate.mjs +5 -3
  103. package/src/lib/format/translate.mjs.map +1 -1
  104. package/src/lib/format/words.d.mts +9 -0
  105. package/src/lib/format/words.d.ts +9 -0
  106. package/src/lib/format/words.js +35 -0
  107. package/src/lib/format/words.js.map +1 -0
  108. package/src/lib/format/words.mjs +30 -0
  109. package/src/lib/format/words.mjs.map +1 -0
  110. package/src/lib/record/cells.d.mts +37 -0
  111. package/src/lib/record/cells.d.ts +37 -0
  112. package/src/lib/record/cells.js +59 -0
  113. package/src/lib/record/cells.js.map +1 -0
  114. package/src/lib/record/cells.mjs +54 -0
  115. package/src/lib/record/cells.mjs.map +1 -0
  116. package/src/lib/record/compute.d.mts +5 -4
  117. package/src/lib/record/compute.d.ts +5 -4
  118. package/src/lib/record/compute.js +11 -10
  119. package/src/lib/record/compute.js.map +1 -1
  120. package/src/lib/record/compute.mjs +12 -11
  121. package/src/lib/record/compute.mjs.map +1 -1
  122. package/src/lib/record/data-source.d.mts +37 -3
  123. package/src/lib/record/data-source.d.ts +37 -3
  124. package/src/lib/record/data-source.js.map +1 -1
  125. package/src/lib/record/data-source.mjs.map +1 -1
  126. package/src/lib/record/env.d.mts +38 -1
  127. package/src/lib/record/env.d.ts +38 -1
  128. package/src/lib/record/env.js +41 -3
  129. package/src/lib/record/env.js.map +1 -1
  130. package/src/lib/record/env.mjs +38 -3
  131. package/src/lib/record/env.mjs.map +1 -1
  132. package/src/lib/record/filter.d.mts +10 -0
  133. package/src/lib/record/filter.d.ts +10 -0
  134. package/src/lib/record/filter.js +49 -0
  135. package/src/lib/record/filter.js.map +1 -1
  136. package/src/lib/record/filter.mjs +48 -0
  137. package/src/lib/record/filter.mjs.map +1 -1
  138. package/src/lib/record/form.d.mts +226 -25
  139. package/src/lib/record/form.d.ts +226 -25
  140. package/src/lib/record/form.js +785 -83
  141. package/src/lib/record/form.js.map +1 -1
  142. package/src/lib/record/form.mjs +787 -85
  143. package/src/lib/record/form.mjs.map +1 -1
  144. package/src/lib/record/locales/ar.js +3 -0
  145. package/src/lib/record/locales/ar.js.map +1 -1
  146. package/src/lib/record/locales/ar.mjs +3 -0
  147. package/src/lib/record/locales/ar.mjs.map +1 -1
  148. package/src/lib/record/locales/de.js +3 -0
  149. package/src/lib/record/locales/de.js.map +1 -1
  150. package/src/lib/record/locales/de.mjs +3 -0
  151. package/src/lib/record/locales/de.mjs.map +1 -1
  152. package/src/lib/record/locales/fr.js +3 -0
  153. package/src/lib/record/locales/fr.js.map +1 -1
  154. package/src/lib/record/locales/fr.mjs +3 -0
  155. package/src/lib/record/locales/fr.mjs.map +1 -1
  156. package/src/lib/record/memory-data-source.d.mts +26 -2
  157. package/src/lib/record/memory-data-source.d.ts +26 -2
  158. package/src/lib/record/memory-data-source.js +98 -7
  159. package/src/lib/record/memory-data-source.js.map +1 -1
  160. package/src/lib/record/memory-data-source.mjs +98 -7
  161. package/src/lib/record/memory-data-source.mjs.map +1 -1
  162. package/src/lib/record/messages.d.mts +5 -0
  163. package/src/lib/record/messages.d.ts +5 -0
  164. package/src/lib/record/messages.js +3 -0
  165. package/src/lib/record/messages.js.map +1 -1
  166. package/src/lib/record/messages.mjs +3 -0
  167. package/src/lib/record/messages.mjs.map +1 -1
  168. package/src/lib/record/roles.d.mts +10 -0
  169. package/src/lib/record/roles.d.ts +10 -0
  170. package/src/lib/record/roles.js +21 -0
  171. package/src/lib/record/roles.js.map +1 -0
  172. package/src/lib/record/roles.mjs +18 -0
  173. package/src/lib/record/roles.mjs.map +1 -0
  174. package/src/lib/record/rules.js +30 -0
  175. package/src/lib/record/rules.js.map +1 -1
  176. package/src/lib/record/rules.mjs +31 -1
  177. package/src/lib/record/rules.mjs.map +1 -1
  178. package/src/lib/record/run.d.mts +248 -0
  179. package/src/lib/record/run.d.ts +248 -0
  180. package/src/lib/record/run.js +455 -0
  181. package/src/lib/record/run.js.map +1 -0
  182. package/src/lib/record/run.mjs +450 -0
  183. package/src/lib/record/run.mjs.map +1 -0
  184. package/src/lib/record/set-when.d.mts +5 -2
  185. package/src/lib/record/set-when.d.ts +5 -2
  186. package/src/lib/record/set-when.js +28 -12
  187. package/src/lib/record/set-when.js.map +1 -1
  188. package/src/lib/record/set-when.mjs +30 -14
  189. package/src/lib/record/set-when.mjs.map +1 -1
  190. package/src/lib/record/value-map.d.mts +41 -0
  191. package/src/lib/record/value-map.d.ts +41 -0
  192. package/src/lib/record/value-map.js +80 -0
  193. package/src/lib/record/value-map.js.map +1 -0
  194. package/src/lib/record/value-map.mjs +73 -0
  195. package/src/lib/record/value-map.mjs.map +1 -0
  196. package/src/lib/record/values.d.mts +10 -0
  197. package/src/lib/record/values.d.ts +10 -0
  198. package/src/lib/record/values.js.map +1 -1
  199. package/src/lib/record/values.mjs.map +1 -1
  200. package/src/runtime.d.mts +11 -4
  201. package/src/runtime.d.ts +11 -4
  202. package/src/runtime.js +13 -1
  203. package/src/runtime.js.map +1 -1
  204. package/src/runtime.mjs +4 -0
  205. package/src/runtime.mjs.map +1 -1
@@ -1,16 +1,19 @@
1
- import type { Option } from '../format/field';
2
- import type { JsonValue } from '../format/json';
1
+ import type { ActionStep } from '../format/actions';
2
+ import type { Field, LineField, Option, PropertyDefinition } from '../format/field';
3
3
  import type { Page } from '../format/page';
4
+ import type { JsonValue } from '../format/json';
5
+ import { type FieldTone, type LineState } from './cells';
4
6
  import { type Messages } from './messages';
5
7
  import { type DataSource, type RecordChanges, type SaveProblem } from './data-source';
6
8
  import { type Scheduler } from './scheduler';
9
+ import { type ActionHost, type ActionRequest, type ActionResult, type OnAction, type PostedMessage, type RunContext, type RunResult } from './run';
7
10
  import { type RecordId, type RelatedRecord, type Value, type Values } from './values';
8
11
  export type FormStatus = 'idle' | 'loading' | 'ready' | 'saving' | 'saved' | 'error';
9
12
  export interface FormState {
10
13
  readonly status: FormStatus;
11
14
  readonly recordId: RecordId | null;
12
15
  readonly values: Readonly<Values>;
13
- /** Messages by field name; a line's field is `field.lineKey.subfield`. */
16
+ /** Messages by field name; a line's field is `field.lineKey.subfield`, a saved form's field `name.field`. */
14
17
  readonly errors: Readonly<Record<string, string>>;
15
18
  /** Fields that differ from the record as loaded or last saved. */
16
19
  readonly dirty: readonly string[];
@@ -37,6 +40,16 @@ export interface FormState {
37
40
  readonly saveProblem: SaveProblem | null;
38
41
  /** The choices loaded from the app's lists, by field: those of a selection with `optionsFrom`, once asked for. */
39
42
  readonly choices: Readonly<Record<string, Choices>>;
43
+ /** The definitions loaded for a properties field with `definitionsFrom`, by field, once asked for. */
44
+ readonly definitions: Readonly<Record<string, Definitions>>;
45
+ }
46
+ /** A properties field's definitions from the app, as they load. */
47
+ export interface Definitions {
48
+ /** The definitions last loaded: null until the first load ends well. */
49
+ readonly definitions: PropertyDefinition[] | null;
50
+ readonly loading?: boolean;
51
+ /** Why the last load failed. */
52
+ readonly error?: string;
40
53
  }
41
54
  /** A field's choices from one of the app's lists, as they load. */
42
55
  export interface Choices {
@@ -58,17 +71,69 @@ export interface DraftStore {
58
71
  setItem(key: string, value: string): void;
59
72
  removeItem(key: string): void;
60
73
  }
61
- export interface ActionRequest {
62
- /** The button or stat button pressed. */
63
- id: string;
64
- action: string;
65
- params?: {
66
- [key: string]: JsonValue;
74
+ export type { ActionRequest } from './run';
75
+ /** Whose change it was: a person's (in the UI), a step's, or the app's (`setValues`, or `setValue` told so). Only a person's runs the page's `change` steps. */
76
+ export type ChangeBy = 'person' | 'step' | 'app';
77
+ /** What a form tells the app, by event: `form.on(event, listener)`. */
78
+ export interface FormEvents {
79
+ /** A field was written: by a person, a step or the app. Values worked out from it are not told. */
80
+ change: {
81
+ field: string;
82
+ value: Value;
83
+ values: Readonly<Values>;
84
+ by: ChangeBy;
85
+ };
86
+ /** A record was saved. */
87
+ save: {
88
+ recordId: RecordId | null;
89
+ values: Readonly<Values>;
90
+ };
91
+ /** A page of responses sent its answers: those sent. */
92
+ send: {
93
+ values: Values;
94
+ };
95
+ /** The app answered a call: with nothing (undefined), or an `ActionResult`. */
96
+ action: {
97
+ request: ActionRequest;
98
+ result: ActionResult | undefined;
99
+ };
100
+ /** A run of steps ended — a button's, a moment's, `form.run`'s — and how. */
101
+ run: {
102
+ id: string;
103
+ steps: readonly ActionStep[];
104
+ result: RunResult;
105
+ };
106
+ /** A wizard's step was entered. */
107
+ step: {
108
+ step: string;
109
+ };
110
+ /** The form has its values: loaded, or new. */
111
+ open: {
112
+ recordId: RecordId | null;
113
+ values: Readonly<Values>;
114
+ };
115
+ /** The record was archived (`archived` true) or brought back, by a step. */
116
+ archive: {
117
+ recordId: RecordId;
118
+ archived: boolean;
119
+ };
120
+ /** The record was copied, by a step: the form shows the copy now. */
121
+ duplicate: {
122
+ from: RecordId;
123
+ recordId: RecordId;
124
+ };
125
+ /**
126
+ * The record was deleted, by a step. A listener may show another record
127
+ * (`openRecord`), as a pager moves on; else the form starts a new one.
128
+ */
129
+ delete: {
130
+ recordId: RecordId;
131
+ };
132
+ /** Words to post in the record's conversation, from a `post` step or the app's answer: the chatter beside it listens. */
133
+ post: {
134
+ recordId: RecordId | null;
135
+ message: PostedMessage;
67
136
  };
68
- recordId: RecordId | null;
69
- values: Values;
70
- /** The records chosen in a list, when the button belongs to one. */
71
- recordIds?: RecordId[];
72
137
  }
73
138
  export interface FormOptions {
74
139
  page: Page;
@@ -77,8 +142,17 @@ export interface FormOptions {
77
142
  recordId?: RecordId | null;
78
143
  /** Values to start with, on top of each field's default. */
79
144
  values?: Values;
80
- /** What a button does. Fieldia hands the press over; the app decides. */
81
- onAction?: (request: ActionRequest) => void | Promise<void>;
145
+ /**
146
+ * The app's own actions: a button's `action`, and each `call` step. Fieldia
147
+ * hands the request over and waits; the app may answer (`ActionResult`)
148
+ * with values to set, words to say, a page to open, or to stop. Nothing
149
+ * answered means go on.
150
+ */
151
+ onAction?: OnAction;
152
+ /** The UI the core cannot draw — pages opened, words said, questions asked — given by the viewer. */
153
+ host?: ActionHost;
154
+ /** How a question is asked when there is no host. Without either, a question goes on as though answered Yes. */
155
+ confirm?: (message: string) => boolean | Promise<boolean>;
82
156
  drafts?: {
83
157
  store: DraftStore;
84
158
  restore?: 'auto' | 'ask';
@@ -92,14 +166,55 @@ export interface FormOptions {
92
166
  messages?: Messages;
93
167
  /** The clock, for `today()` and for dates that must be past or future. The computer's own when left out. */
94
168
  now?: () => Date;
169
+ /**
170
+ * The person using the form: conditions read it as `user` (`user.id`,
171
+ * `user.name`, `user.roles`), and a part with `roles` shows only to people
172
+ * in them. Left out, the person has no id and no roles.
173
+ */
174
+ user?: FormUser;
175
+ /** Whether the record starts being edited, as conditions read `editing`; true unless said. A view locking it says so with `setEditing`. */
176
+ editing?: boolean;
177
+ /**
178
+ * Values the app passes in, as Flectra's context: read by conditions and
179
+ * expressions as `context.name`, and by a filter's `valueFrom:
180
+ * "context.name"` — a transfer opened from Receipts restricting its
181
+ * operation types. They are the app's, never the page's: nothing saves
182
+ * them, and a page's field of that name comes first.
183
+ */
184
+ context?: Readonly<Record<string, JsonValue>>;
185
+ /**
186
+ * The record this form's values are a line of, when the form edits one
187
+ * table's line in a form of its own (a node's `lineForm`): its values and
188
+ * fields, read by conditions and filters as `parent`, as a line's are —
189
+ * `parent.state`, a filter's `valueFrom: "parent.company_id"`.
190
+ */
191
+ parent?: {
192
+ values: Readonly<Values>;
193
+ fields: Readonly<Record<string, Field | LineField>>;
194
+ id?: RecordId | null;
195
+ };
196
+ }
197
+ /** The person using a form, as the app knows them. */
198
+ export interface FormUser {
199
+ id: RecordId | null;
200
+ name?: string;
201
+ /** The roles they hold, by the names the page's `roles` use: Flectra's groups, such as `sales_team.group_sale_manager`. */
202
+ roles?: readonly string[];
95
203
  }
96
204
  export interface Form {
97
205
  readonly page: Page;
98
206
  getState(): FormState;
99
207
  subscribe(listener: (state: FormState) => void): () => void;
100
208
  load(): Promise<void>;
101
- setValue(name: string, value: Value): void;
209
+ /** Write a field: a person's change unless told otherwise, and only a person's runs the page's `change` steps. */
210
+ setValue(name: string, value: Value, how?: {
211
+ by?: ChangeBy;
212
+ }): void;
213
+ /** Write several fields at once, worked out once: the app's change, never a person's. Throws for a field the page lacks. */
214
+ setValues(values: Values): void;
102
215
  node(id: string): NodeState;
216
+ /** Whether the record is being edited, as conditions read `editing`: a view that locks it to be read says false, and parts follow. */
217
+ setEditing(editing: boolean): void;
103
218
  fieldReadonly(name: string): boolean;
104
219
  /** What a field would be told if the form were checked now, or null when it passes. Nothing is shown. */
105
220
  problem(field: string): string | null;
@@ -118,25 +233,68 @@ export interface Form {
118
233
  * written: the record, its changes and its autosave stay as they are.
119
234
  */
120
235
  previewLine(field: string, key: string, values: Values): Promise<Values>;
236
+ /**
237
+ * Show another record of the page's model in this form, loaded from the
238
+ * data source as a pager moves to it, or a new one for null. Changes not
239
+ * saved are dropped: save first. The page's `open` steps run for it.
240
+ */
241
+ openRecord(id: RecordId | null): Promise<void>;
242
+ /**
243
+ * Post words in the record's conversation, as a `post` step does: through
244
+ * the form's host when it has a way to, else told to the form's own
245
+ * listeners (`post`). False when nothing heard them.
246
+ */
247
+ post(message: PostedMessage): Promise<boolean>;
121
248
  /**
122
249
  * Load, or load again, the choices of a selection whose options come from
123
250
  * the app's list (`optionsFrom`). Once loaded, they load again by themselves
124
251
  * when a field they change with changes; the latest answer wins.
125
252
  */
126
253
  loadChoices(field: string): void;
127
- /** Records a many2one, many2many or reference may point to. A reference needs `options.model`. */
254
+ /**
255
+ * Load, or load again, the definitions of a properties field whose
256
+ * definitions come from a linked record (`definitionsFrom`). Once loaded,
257
+ * they load again by themselves when a field they change with changes.
258
+ */
259
+ loadDefinitions(field: string): void;
260
+ /** A property added in place to such a field: kept in its definitions, and handed to the data source's `saveDefinitions`. */
261
+ addDefinition(field: string, definition: PropertyDefinition): Promise<void>;
262
+ /**
263
+ * Records a many2one, many2many or reference may point to. A reference needs
264
+ * `options.model`, and so does structured data naming records of a model (a
265
+ * json field, such as an analytic distribution's accounts); `options.ids`
266
+ * finds those records alone, for their names.
267
+ */
128
268
  search(field: string, query: string, limit?: number, options?: {
129
269
  model?: string;
270
+ ids?: RecordId[];
130
271
  }): Promise<RelatedRecord[]>;
131
- /** The same, for a relation inside a one2many line, filtered by that line's values. */
132
- searchLine(field: string, key: string, subfield: string, query: string, limit?: number): Promise<RelatedRecord[]>;
272
+ /** 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`). */
273
+ searchLine(field: string, key: string, subfield: string, query: string, limit?: number, options?: {
274
+ model?: string;
275
+ ids?: RecordId[];
276
+ }): Promise<RelatedRecord[]>;
277
+ /**
278
+ * The values of records a many2many links to, for a table of them: `fields`
279
+ * of the records with these ids, in the order asked, read with the data
280
+ * source's `list`. None without one.
281
+ */
282
+ linkedValues(field: string, ids: readonly RecordId[], fields: readonly string[]): Promise<{
283
+ id: RecordId;
284
+ values: Values;
285
+ }[]>;
133
286
  /** Whether a link field can make a record from a typed name: the data source must be able to. */
134
287
  canCreate(field: string): boolean;
135
- /** Make a record from a typed name, for the link field to point to. */
288
+ /** Make a record from a typed name, for the link field to point to, with what its `createValues` hand on. */
136
289
  quickCreate(field: string, name: string): Promise<RelatedRecord>;
137
- /** The same, for a link inside a one2many's lines. */
290
+ /** The same, for a link inside a one2many's lines; with the line's key, its `createValues` are read on that line. */
138
291
  canCreateLine(field: string, subfield: string): boolean;
139
- quickCreateLine(field: string, subfield: string, name: string): Promise<RelatedRecord>;
292
+ quickCreateLine(field: string, subfield: string, name: string, key?: string): Promise<RelatedRecord>;
293
+ /** What a record made from a link starts with besides its name: its `createValues`, read now — on a line, on that line. */
294
+ createValues(field: string, line?: {
295
+ lines: string;
296
+ key: string;
297
+ }): Values;
140
298
  steps(): string[];
141
299
  next(): boolean;
142
300
  back(): boolean;
@@ -148,14 +306,57 @@ export interface Form {
148
306
  * an optional one instead.
149
307
  */
150
308
  goTo(step: string): boolean;
151
- /** Run a button; a list's button passes the records chosen in it. */
309
+ /**
310
+ * Press a button: ask its `confirm` first, run its `steps`, then call its
311
+ * `action`, when it names one, as a final `call`. A list's button passes
312
+ * the records chosen in it, and every call of the run carries them.
313
+ */
152
314
  runAction(id: string, chosen?: {
153
315
  recordIds: RecordId[];
154
- }): Promise<void>;
316
+ }): Promise<RunResult>;
317
+ /** A field's own value as its tones have it now: its tone and whether it is bold, read on the record. */
318
+ fieldTone(nodeId: string): FieldTone;
319
+ /** A table's line as its rules have it now — its tone, each ruled column's cells, which of its buttons show — by the table's field node. */
320
+ lineState(nodeId: string, key: string): LineState;
321
+ /** Whether a table's column is hidden now by its `hidden` rule, read on the record. */
322
+ columnHidden(nodeId: string, column: string): boolean;
323
+ /**
324
+ * The words a field's empty box shows now, by its node (`#title` for a
325
+ * sheet's title): its first `placeholderWhen` that holds, else its
326
+ * `placeholder`; undefined when it has neither.
327
+ */
328
+ placeholder(nodeId: string): string | undefined;
329
+ /** Press a button on a table's line: its confirmation, its steps, then its action, each call carrying the line. Hidden on that line, it does not run. */
330
+ runRowAction(nodeId: string, buttonId: string, key: string): Promise<RunResult>;
331
+ /**
332
+ * Press a button for the lines chosen in a table (its `selectedButtons`),
333
+ * by their keys: its confirmation, its steps, then its action, each call
334
+ * carrying the lines. Hidden, or with no line chosen, it does not run.
335
+ */
336
+ runLinesAction(nodeId: string, buttonId: string, keys: readonly string[]): Promise<RunResult>;
337
+ /** Run steps on this form, as a button would: `context.id` is what their calls carry (`run` when left out). */
338
+ run(steps: readonly ActionStep[], context?: RunContext): Promise<RunResult>;
339
+ /** A tab was shown: the viewer says so, and the page's `show` steps for it run. */
340
+ shown(tab: string): void;
341
+ /** Listen to one of the form's events; returns what stops listening. */
342
+ on<E extends keyof FormEvents>(event: E, listener: (event: FormEvents[E]) => void): () => void;
155
343
  restoreDraft(): void;
156
344
  discardDraft(): void;
157
- /** Resolves once every onchange, save and autosave started so far has finished. */
345
+ /** Resolves once every onchange, save, autosave and run of steps started so far has finished — a question still waiting for its answer included. */
158
346
  settled(): Promise<void>;
347
+ /**
348
+ * Give a saved form placed on this page (a `form` part, by its id) its page,
349
+ * once the app has found it. From then its answers sit under the part's
350
+ * name as an object, checked by that page's own rules: this form refuses
351
+ * to save or send while one of them fails, with the error under
352
+ * `name.field`, and shows it on the inner form beside its field. Returns the
353
+ * inner form, which a view draws the part's fields from; giving the page
354
+ * again returns the same one. A page placed inside itself, directly or
355
+ * through others, is refused.
356
+ */
357
+ embed(id: string, page: Page): Form;
358
+ /** The inner form of a saved form placed on this page, once it has its page; null until then. */
359
+ embedded(id: string): Form | null;
159
360
  dispose(): void;
160
361
  }
161
362
  /**