@ai-matrx/records 0.87.31 → 0.92.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 (107) hide show
  1. package/CHANGELOG.md +98 -0
  2. package/README.md +115 -0
  3. package/dist/{client-E0VQ0NPO.d.ts → client-DE9Mq6YX.d.cts} +8 -5
  4. package/dist/{client-C3ZZNfGH.d.cts → client-vJAb7g9r.d.ts} +8 -5
  5. package/dist/core/index.cjs +25 -7
  6. package/dist/core/index.cjs.map +1 -1
  7. package/dist/core/index.d.cts +23 -12
  8. package/dist/core/index.d.ts +23 -12
  9. package/dist/core/index.js +25 -7
  10. package/dist/core/index.js.map +1 -1
  11. package/dist/data/index.cjs +19 -7
  12. package/dist/data/index.cjs.map +1 -1
  13. package/dist/data/index.d.cts +12 -142
  14. package/dist/data/index.d.ts +12 -142
  15. package/dist/data/index.js +19 -7
  16. package/dist/data/index.js.map +1 -1
  17. package/dist/{declareTable-BK3zufr7.d.cts → declareTable-Btg5bql1.d.cts} +4 -4
  18. package/dist/{declareTable-B12XybRp.d.ts → declareTable-JKt2q34d.d.ts} +4 -4
  19. package/dist/entity/index.cjs +984 -0
  20. package/dist/entity/index.cjs.map +1 -0
  21. package/dist/entity/index.d.cts +36 -0
  22. package/dist/entity/index.d.ts +36 -0
  23. package/dist/entity/index.js +964 -0
  24. package/dist/entity/index.js.map +1 -0
  25. package/dist/entity/react.cjs +1024 -0
  26. package/dist/entity/react.cjs.map +1 -0
  27. package/dist/entity/react.d.cts +511 -0
  28. package/dist/entity/react.d.ts +511 -0
  29. package/dist/entity/react.js +1015 -0
  30. package/dist/entity/react.js.map +1 -0
  31. package/dist/{field-BanTVGK2.d.cts → field-BJBnTyJ0.d.cts} +1 -1
  32. package/dist/{field-BPABOJ2Y.d.ts → field-Ti5iAxFr.d.ts} +1 -1
  33. package/dist/forms/index.cjs +984 -0
  34. package/dist/forms/index.cjs.map +1 -0
  35. package/dist/forms/index.d.cts +333 -0
  36. package/dist/forms/index.d.ts +333 -0
  37. package/dist/forms/index.js +961 -0
  38. package/dist/forms/index.js.map +1 -0
  39. package/dist/index.cjs +10 -1
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.cts +11 -8
  42. package/dist/index.d.ts +11 -8
  43. package/dist/index.js +10 -1
  44. package/dist/index.js.map +1 -1
  45. package/dist/memory/index.d.cts +7 -6
  46. package/dist/memory/index.d.ts +7 -6
  47. package/dist/port-CPVBfDxS.d.cts +301 -0
  48. package/dist/port-D2i2o7Lg.d.ts +301 -0
  49. package/dist/portal-Db4CYyHe.d.ts +331 -0
  50. package/dist/portal-DfeY2Boy.d.cts +331 -0
  51. package/dist/{ports-TMWX-t9l.d.ts → ports-BWtxAnXh.d.ts} +2 -2
  52. package/dist/{ports-D8-iuY1P.d.cts → ports-DSU2RsR6.d.cts} +2 -2
  53. package/dist/react/index.cjs +55 -523
  54. package/dist/react/index.cjs.map +1 -1
  55. package/dist/react/index.d.cts +14 -1
  56. package/dist/react/index.d.ts +14 -1
  57. package/dist/react/index.js +52 -527
  58. package/dist/react/index.js.map +1 -1
  59. package/dist/realtime/index.d.cts +2 -2
  60. package/dist/realtime/index.d.ts +2 -2
  61. package/dist/{record-DpB2QgAZ.d.cts → record-BQi92d4-.d.cts} +7 -1
  62. package/dist/{record-DpB2QgAZ.d.ts → record-BQi92d4-.d.ts} +7 -1
  63. package/dist/relation-DMwkmzgN.d.cts +230 -0
  64. package/dist/relation-DlcqV_W5.d.ts +230 -0
  65. package/dist/rule-B8kR0l01.d.ts +250 -0
  66. package/dist/rule-BMViCLmW.d.cts +250 -0
  67. package/dist/search/index.cjs +283 -0
  68. package/dist/search/index.cjs.map +1 -0
  69. package/dist/search/index.d.cts +44 -0
  70. package/dist/search/index.d.ts +44 -0
  71. package/dist/search/index.js +260 -0
  72. package/dist/search/index.js.map +1 -0
  73. package/dist/setup/index.cjs +6854 -0
  74. package/dist/setup/index.cjs.map +1 -0
  75. package/dist/setup/index.d.cts +87 -0
  76. package/dist/setup/index.d.ts +87 -0
  77. package/dist/setup/index.js +6829 -0
  78. package/dist/setup/index.js.map +1 -0
  79. package/dist/{subscription-D7KQBHXN.d.ts → subscription-5CLN3Jug.d.ts} +3 -249
  80. package/dist/{subscription-BD9DnJxX.d.cts → subscription-D-VEzvyy.d.cts} +3 -249
  81. package/dist/{table-kind-BZu8mtND.d.cts → table-kind-CVp43g6h.d.ts} +3 -558
  82. package/dist/{table-kind-xKxTHtrV.d.ts → table-kind-Cm6PXkWx.d.cts} +3 -558
  83. package/dist/templates/index.d.cts +10 -9
  84. package/dist/templates/index.d.ts +10 -9
  85. package/dist/typed-table/index.cjs +19 -7
  86. package/dist/typed-table/index.cjs.map +1 -1
  87. package/dist/typed-table/index.d.cts +10 -7
  88. package/dist/typed-table/index.d.ts +10 -7
  89. package/dist/typed-table/index.js +19 -7
  90. package/dist/typed-table/index.js.map +1 -1
  91. package/dist/{types-CLP5H4Wk.d.cts → types-9Q5SHRDJ.d.cts} +2 -2
  92. package/dist/{types-DmPkWQic.d.ts → types-CfYy-vdV.d.ts} +2 -2
  93. package/dist/{types-C2uspRtf.d.cts → types-Cs2MyE3u.d.cts} +2 -2
  94. package/dist/{types-Dv61jStB.d.ts → types-DF-dxB_l.d.ts} +2 -2
  95. package/dist/types-DTEGaxso.d.cts +139 -0
  96. package/dist/types-Dji5SpFS.d.ts +139 -0
  97. package/dist/use-cases/index.d.cts +5 -5
  98. package/dist/use-cases/index.d.ts +5 -5
  99. package/dist/{validate-CWVToe1T.d.ts → validate-BydKd-Le.d.ts} +1 -1
  100. package/dist/{validate-DEB_mn4Y.d.cts → validate-CqJakTKk.d.cts} +1 -1
  101. package/dist/versions/index.cjs +375 -0
  102. package/dist/versions/index.cjs.map +1 -0
  103. package/dist/versions/index.d.cts +160 -0
  104. package/dist/versions/index.d.ts +160 -0
  105. package/dist/versions/index.js +353 -0
  106. package/dist/versions/index.js.map +1 -0
  107. package/package.json +62 -2
@@ -0,0 +1,333 @@
1
+ import { k as PortalAccent } from '../portal-DfeY2Boy.cjs';
2
+ import { F as FormSummary, R as RuleExpression } from '../rule-BMViCLmW.cjs';
3
+ import { U as Uuid } from '../record-BQi92d4-.cjs';
4
+
5
+ /**
6
+ * THE WORDS FOR EACH STATE, SAID ONCE, FOR EVERY SCREEN THAT SHOWS A FORM.
7
+ *
8
+ * `FormsPanel` and `FormBuilder` both put a form in front of a person and both
9
+ * offer the one act that changes this (`custom.anon_publish`). Two copies of
10
+ * these four sentences is two places for them to drift, and a person who reads
11
+ * "Not published" on one screen and something else on the other has been lied
12
+ * to by one of them. The store owns the state; this owns the sentence.
13
+ */
14
+ declare const FORM_STATE_WORDS: Record<FormSummary["state"], string>;
15
+ /** How a form is put in front of a person. */
16
+ declare const FORM_FLOWS: readonly ["one-at-a-time", "single-page"];
17
+ type FormFlow = (typeof FORM_FLOWS)[number];
18
+ /** One question. It ASKS FOR one Field of the subject Table — never its own column. */
19
+ interface FormQuestionSpec {
20
+ /** The Field's key on the subject Table. */
21
+ field: string;
22
+ /** Ask it in the form's own words. Left out, the Field's own label is the question. */
23
+ ask?: string | null;
24
+ /** One line under the question. */
25
+ help?: string | null;
26
+ /** Override the Field's own `required`. Left out, the Field decides. */
27
+ required?: boolean | null;
28
+ /**
29
+ * CONDITIONAL LOGIC, AS A RULE. `{"op":"eq","args":[{"field":"<field id>"},{"const":"Yes"}]}`
30
+ * — the store answers it, this package never does. An UNDECIDED answer (the
31
+ * store's `null`, which is what an unanswered question gives) SHOWS the
32
+ * question: a person is never silently skipped past something.
33
+ */
34
+ showIf?: RuleExpression | null;
35
+ /**
36
+ * LOGIC JUMPS (lane TYPEFORM-DUP). After this question, the FIRST jump whose `when` the store
37
+ * answers true is taken — to a later question or to one of the form's endings. A jump with no
38
+ * `when` is "always". Undecided falls through to the next question. The store judges the route
39
+ * (`custom._form_route`); this package never does.
40
+ */
41
+ jumps?: FormJump[] | null;
42
+ /** SCORING: what each answer is worth (`{"Yes": 5}`); a list answer adds each of its parts. */
43
+ points?: Record<string, number> | null;
44
+ }
45
+ /** One logic jump: when the Rule is true, go to a later question or to an ending. */
46
+ interface FormJump {
47
+ when?: RuleExpression | null;
48
+ to: {
49
+ question: string;
50
+ } | {
51
+ ending: string;
52
+ };
53
+ }
54
+ /**
55
+ * ONE ENDING (lane TYPEFORM-DUP). A form may have several; a jump picks one, or the first whose
56
+ * condition holds (`when`, `scoreMin`, `scoreMax`), or else the first with no condition.
57
+ */
58
+ interface FormEnding {
59
+ /** A short name, lower-case letters, digits, - and _. Jumps point at it. */
60
+ id: string;
61
+ title?: string | null;
62
+ body?: string | null;
63
+ /** One of the organization's public pictures (the builder's side). */
64
+ pictureFileId?: Uuid | null;
65
+ /** The picture's public address (the store resolves it for the public page). */
66
+ pictureUrl?: string | null;
67
+ buttonLabel?: string | null;
68
+ /** A secure page on the organization's own sites — the thank-you's own rule. */
69
+ redirectUrl?: string | null;
70
+ when?: RuleExpression | null;
71
+ scoreMin?: number | null;
72
+ scoreMax?: number | null;
73
+ }
74
+ /** THE WELCOME SCREEN, before question one: a title, a message, a picture and a Start button. */
75
+ interface FormWelcome {
76
+ title?: string | null;
77
+ body?: string | null;
78
+ pictureFileId?: Uuid | null;
79
+ pictureUrl?: string | null;
80
+ buttonLabel?: string | null;
81
+ }
82
+ /** The look. Tokens, so light and dark both come out right without a second theme. */
83
+ interface FormTheme {
84
+ /** A design-system accent class or a CSS color. Left out, the app's own accent. */
85
+ accent?: string | null;
86
+ align?: "left" | "center" | null;
87
+ /** One of design-system `FORM_FONTS`. */
88
+ font?: string | null;
89
+ /** One of design-system `FORM_BUTTON_STYLES`. */
90
+ button?: string | null;
91
+ /** One of design-system `FORM_BACKGROUNDS`. */
92
+ background?: string | null;
93
+ /** The picture for the `picture` background: a public picture of the organization. */
94
+ backgroundFileId?: Uuid | null;
95
+ /** Its public address, as the store resolved it for the public page. */
96
+ backgroundUrl?: string | null;
97
+ }
98
+ /**
99
+ * THE FORM'S OWN LOOK, in the portal's words (`custom._portal_style` resolves both): a colour of
100
+ * the app's own palette and two of the organization's PUBLIC pictures. Each part left out is the
101
+ * organization's look — its newest open portal's, then its own name and logo. `custom.form_declare`
102
+ * refuses a colour the app does not have or a picture a stranger could not see, by name.
103
+ */
104
+ interface FormLook {
105
+ accent?: PortalAccent | null;
106
+ logoFileId?: Uuid | null;
107
+ coverFileId?: Uuid | null;
108
+ }
109
+ /** The screen after sending: the form's words, and optionally where to go next. */
110
+ interface FormThankYou {
111
+ title?: string | null;
112
+ body?: string | null;
113
+ /** A secure page on one of the organization's own sites. The store judges it. */
114
+ redirectUrl?: string | null;
115
+ }
116
+ /** THE COMPLETE DECLARATIVE SPEC. One `record_write` and the form exists. */
117
+ interface FormSpec {
118
+ name: string;
119
+ /** The Table a submission lands in. */
120
+ subject: Uuid;
121
+ intro?: string | null;
122
+ questions: FormQuestionSpec[];
123
+ flow?: FormFlow | null;
124
+ theme?: FormTheme | null;
125
+ /** Colour, logo and cover a stranger sees. Left out, the organization's look. */
126
+ look?: FormLook | null;
127
+ /**
128
+ * WHAT THE PERSON SEES AFTER SENDING. `title` and `body` are the form's own
129
+ * words for the next step ("You are all set" / "Next, book your first visit").
130
+ * `redirectUrl` optionally sends them on to a page instead — and only ever to
131
+ * one of the organization's own sites: `custom.form_declare` refuses anything
132
+ * else by name, and `custom.form_public` stops handing it out the moment its
133
+ * site leaves the organization's list (lane S7-PRIME).
134
+ */
135
+ thankYou?: FormThankYou | null;
136
+ submitLabel?: string | null;
137
+ /** A separate first screen with a Start button (lane TYPEFORM-DUP). */
138
+ welcome?: FormWelcome | null;
139
+ /** Several endings, each picked by the answers (lane TYPEFORM-DUP). */
140
+ endings?: FormEnding[] | null;
141
+ /**
142
+ * HIDDEN FIELDS: names read from the link (`?utm_source=…&ref=…`), kept on the submission and
143
+ * never shown. Not questions — a question with the same key is prefilled instead.
144
+ */
145
+ hiddenFields?: string[] | null;
146
+ /**
147
+ * Is this form meant to be answerable by someone who is not signed in? The
148
+ * runner does NOT make that true by itself — it needs the anonymous write
149
+ * door, and until that door exists it says so BY NAME rather than writing as
150
+ * whoever happens to be signed in and calling it public.
151
+ */
152
+ isPublic?: boolean;
153
+ /**
154
+ * THE OWNER'S LOGO AND NAME ABOVE THE FORM (lane TYPEFORM-2). Left out, the organization's knob
155
+ * `forms/show_owner_header` (default on); false hides the agency's brand on a client's form.
156
+ */
157
+ showOwnerHeader?: boolean | null;
158
+ /** A single choice goes on to the next question when picked. Left out, knob `forms/choice_auto_advance`. */
159
+ autoAdvance?: boolean | null;
160
+ /** NOTIFY ON ANSWER, the builder's words (lane TYPEFORM-2): a webhook and the automation that sends it. */
161
+ notify?: FormNotify | null;
162
+ }
163
+ /** Where an answer is sent besides the owner's own subscription (lane TYPEFORM-2). */
164
+ interface FormNotify {
165
+ webhookUrl?: string | null;
166
+ /** The `custom.automation_declare` row that posts to `webhookUrl` (trigger form_answered). */
167
+ webhookAutomationId?: Uuid | null;
168
+ /**
169
+ * EMAIL THE RESPONDENT (TYPEFORM-2), off unless set: one email to the address the person typed
170
+ * in `respondentField` (an email question), worded from the ending reached — sent by
171
+ * `custom.form_respondent_copy` through the platform's notification sender.
172
+ */
173
+ emailRespondent?: boolean | null;
174
+ respondentField?: string | null;
175
+ }
176
+ /**
177
+ * A form as `custom.forms` reports it — the spec a screen renders, plus the
178
+ * things only the STORE knows about it.
179
+ *
180
+ * The four store-side ids are carried on purpose. `custom.form_declare` is a
181
+ * RE-STATEMENT of the whole form, so a save that left `notifyRuleId` out would
182
+ * set it to null and the owner would quietly stop being told when somebody
183
+ * answers — the exact silent failure the notify Rule exists to prevent. They are
184
+ * read here and handed straight back by `formDeclareArgs`.
185
+ */
186
+ interface SavedForm extends FormSpec {
187
+ id: Uuid;
188
+ /** draft · open · closed · full — the store's own word, never derived on a screen. */
189
+ state: FormSummary["state"];
190
+ /** The form's readable name in the store. `form_declare` keeps it across saves. */
191
+ slug: string;
192
+ /** Stops taking answers at this many. Null is no ceiling. */
193
+ submissionCap: number | null;
194
+ /** The accept Rule: what counts as a complete answer. Null holds every answer for a person. */
195
+ quarantineRuleId: Uuid | null;
196
+ /** The subscription Rule that tells somebody an answer arrived. */
197
+ notifyRuleId: Uuid | null;
198
+ }
199
+ /**
200
+ * The spec as `custom.anon_form.presentation` holds it.
201
+ *
202
+ * `questions` is deliberately NOT here: the door writes it itself from the
203
+ * argument it validated (`presentation || {"questions": p_questions}`), so the
204
+ * words a form is asked in and the keys it is allowed to accept can never be two
205
+ * different lists.
206
+ */
207
+ declare function formPresentation(spec: FormSpec): Record<string, unknown>;
208
+ /** `presentation.notify` back into the screen's words. */
209
+ declare function notifyFromDocument(raw: unknown): FormNotify | null;
210
+ /**
211
+ * WHAT A QUESTION REFUSES BEFORE IT GOES ON (lane TYPEFORM-2) — Typeform's words, one per case.
212
+ * `null` is "may go on". A required question with no answer is refused; an answer of the email,
213
+ * phone or link kind is refused when it cannot be one. The store judges again on send.
214
+ */
215
+ declare function answerRefusal(kind: string, required: boolean, value: unknown): string | null;
216
+ /**
217
+ * ANSWER PIPING — Typeform's recall (lane TYPEFORM-2). `{{key}}` or `{{Question words}}` in a later
218
+ * question, a help line or an ending becomes that question's answer as the person gave it; a
219
+ * choice reads as its label. Unanswered, it becomes nothing.
220
+ */
221
+ declare function pipeAnswers(text: string, answers: Readonly<Record<string, unknown>>, questions: ReadonlyArray<{
222
+ key: string;
223
+ ask?: string | null;
224
+ }>, labelOf?: (key: string, value: unknown) => string | null): string;
225
+ /** `presentation.theme` from the screen's words: camelCase out, the store's snake_case in. */
226
+ declare function themeDocument(theme: FormTheme | null): Record<string, unknown>;
227
+ declare function themeFromDocument(raw: unknown): FormTheme | null;
228
+ declare function welcomeDocument(welcome: FormWelcome | null): Record<string, unknown> | null;
229
+ declare function welcomeFromDocument(raw: unknown): FormWelcome | null;
230
+ declare function endingDocument(ending: FormEnding): Record<string, unknown>;
231
+ declare function endingsFromDocument(raw: unknown): FormEnding[];
232
+ /** The hidden fields' values out of a link's query, narrowed to the names the form declares. */
233
+ declare function hiddenFromLink(hiddenFields: readonly string[] | null | undefined, params: Record<string, string | string[] | undefined>): Record<string, string>;
234
+ /** Does this form route at all — conditions, jumps, endings or points the store must judge? */
235
+ declare function formRoutes(form: Pick<FormSpec, "questions" | "endings">): boolean;
236
+ /** HOW A FORM GOES INTO SOMEBODY ELSE'S PAGE. */
237
+ declare const FORM_EMBEDS: readonly ["inline", "popup", "slider"];
238
+ type FormEmbed = (typeof FORM_EMBEDS)[number];
239
+ /**
240
+ * THE SNIPPET THE OWNER COPIES (lane TYPEFORM-DUP). `inline` is a plain iframe of the public link
241
+ * in embed mode; `popup` and `slider` are one button plus the platform's own embed script
242
+ * (`<origin>/embed/form.js`), which opens the same iframe in a centred window or a side panel.
243
+ * Nothing in either needs a cookie: the form keeps its place in its own storage and posts to its
244
+ * own origin, so it keeps working with third-party cookies blocked.
245
+ */
246
+ declare function formEmbedSnippet(kind: FormEmbed, formUrl: string, options?: {
247
+ label?: string | null;
248
+ height?: number | null;
249
+ }): string;
250
+ /** `presentation.look` both directions. Nothing set is `null`, one spelling on both sides. */
251
+ declare function formLookDocument(look: FormLook | null): Record<string, unknown> | null;
252
+ declare function formLookFromDocument(raw: unknown): FormLook | null;
253
+ /**
254
+ * THE THANK-YOU SCREEN, BOTH DIRECTIONS, IN ONE PLACE. The store holds it as
255
+ * `presentation.thank_you = {title, body, redirect_url}`; the screen reads it as
256
+ * `FormThankYou`. A thank-you with nothing in it is `null`, so "nothing set"
257
+ * has one spelling on both sides.
258
+ */
259
+ declare function thankYouDocument(thankYou: FormThankYou | null): Record<string, unknown> | null;
260
+ /** `presentation.thank_you` as the store (or `custom.form_public`) answers it, for a screen. */
261
+ declare function thankYouFromDocument(raw: unknown): FormThankYou | null;
262
+ /** The exact arguments `custom.form_declare` is called with, for a new form or a re-statement. */
263
+ declare function formDeclareArgs(form: FormSpec | SavedForm): {
264
+ table_id: Uuid;
265
+ title: string;
266
+ questions: FormQuestionSpec[];
267
+ presentation: Record<string, unknown>;
268
+ submission_cap: number | null;
269
+ quarantine_rule_id: Uuid | null;
270
+ notify_rule_id: Uuid | null;
271
+ form_id: Uuid | null;
272
+ slug: string | null;
273
+ };
274
+ /** One row of `custom.forms` back into the spec a screen and an agent both read. */
275
+ declare function formFromSummary(summary: FormSummary): SavedForm;
276
+ /**
277
+ * THE SOURCE STAMP every submission carries. A record that arrived through a
278
+ * form is not indistinguishable from one somebody typed into the grid: it says
279
+ * which form, which version of it, when, and who was acting. `_source` is a
280
+ * stamp on the document beside the answers, so it travels with the record
281
+ * through export, History and the agent's context without a parallel table.
282
+ */
283
+ declare function submissionStamp(args: {
284
+ formId: Uuid | null;
285
+ formName: string;
286
+ actor: string;
287
+ onBehalfOf?: string | null;
288
+ }): Record<string, unknown>;
289
+
290
+ interface PublicBindingShape {
291
+ render_mode: string;
292
+ resource_id: Uuid;
293
+ /** What the door says the address holds. */
294
+ kind: string | null | undefined;
295
+ /** The table a list reads: the table itself, a view's subject, a record's table. */
296
+ table_id: Uuid | null | undefined;
297
+ }
298
+ type PublicRead = {
299
+ read: "table";
300
+ tableId: Uuid;
301
+ } | {
302
+ read: "view";
303
+ tableId: Uuid;
304
+ viewId: Uuid;
305
+ } | {
306
+ read: "record";
307
+ recordId: Uuid;
308
+ } | {
309
+ read: "unknown";
310
+ };
311
+ declare function publicReadOf(binding: PublicBindingShape): PublicRead;
312
+
313
+ interface TypedAnswers {
314
+ /** Ready for the door. */
315
+ values: Record<string, unknown>;
316
+ /**
317
+ * The sentences, in the order the page asks its questions, or null when
318
+ * every answer became a value. Each one names its Field in plain words.
319
+ */
320
+ refusal: string | null;
321
+ /** Field key → its sentence, so a screen can point at the question. */
322
+ byKey: Record<string, string>;
323
+ }
324
+ /**
325
+ * Coerce what a public page collected against the Fields that page published.
326
+ *
327
+ * `fields` is exactly what `custom.form_public` / `custom.booking_public`
328
+ * already hand back — the Field definitions of the questions being asked — so
329
+ * nothing new is read and no door is opened to work this out.
330
+ */
331
+ declare function typedAnswersFor(fields: Array<Record<string, unknown>> | null | undefined, values: Record<string, unknown>): TypedAnswers;
332
+
333
+ export { FORM_EMBEDS, FORM_FLOWS, FORM_STATE_WORDS, type FormEmbed, type FormEnding, type FormFlow, type FormJump, type FormLook, type FormNotify, type FormQuestionSpec, type FormSpec, type FormThankYou, type FormTheme, type FormWelcome, type PublicBindingShape, type PublicRead, type SavedForm, type TypedAnswers, answerRefusal, endingDocument, endingsFromDocument, formDeclareArgs, formEmbedSnippet, formFromSummary, formLookDocument, formLookFromDocument, formPresentation, formRoutes, hiddenFromLink, notifyFromDocument, pipeAnswers, publicReadOf, submissionStamp, thankYouDocument, thankYouFromDocument, themeDocument, themeFromDocument, typedAnswersFor, welcomeDocument, welcomeFromDocument };
@@ -0,0 +1,333 @@
1
+ import { k as PortalAccent } from '../portal-Db4CYyHe.js';
2
+ import { F as FormSummary, R as RuleExpression } from '../rule-B8kR0l01.js';
3
+ import { U as Uuid } from '../record-BQi92d4-.js';
4
+
5
+ /**
6
+ * THE WORDS FOR EACH STATE, SAID ONCE, FOR EVERY SCREEN THAT SHOWS A FORM.
7
+ *
8
+ * `FormsPanel` and `FormBuilder` both put a form in front of a person and both
9
+ * offer the one act that changes this (`custom.anon_publish`). Two copies of
10
+ * these four sentences is two places for them to drift, and a person who reads
11
+ * "Not published" on one screen and something else on the other has been lied
12
+ * to by one of them. The store owns the state; this owns the sentence.
13
+ */
14
+ declare const FORM_STATE_WORDS: Record<FormSummary["state"], string>;
15
+ /** How a form is put in front of a person. */
16
+ declare const FORM_FLOWS: readonly ["one-at-a-time", "single-page"];
17
+ type FormFlow = (typeof FORM_FLOWS)[number];
18
+ /** One question. It ASKS FOR one Field of the subject Table — never its own column. */
19
+ interface FormQuestionSpec {
20
+ /** The Field's key on the subject Table. */
21
+ field: string;
22
+ /** Ask it in the form's own words. Left out, the Field's own label is the question. */
23
+ ask?: string | null;
24
+ /** One line under the question. */
25
+ help?: string | null;
26
+ /** Override the Field's own `required`. Left out, the Field decides. */
27
+ required?: boolean | null;
28
+ /**
29
+ * CONDITIONAL LOGIC, AS A RULE. `{"op":"eq","args":[{"field":"<field id>"},{"const":"Yes"}]}`
30
+ * — the store answers it, this package never does. An UNDECIDED answer (the
31
+ * store's `null`, which is what an unanswered question gives) SHOWS the
32
+ * question: a person is never silently skipped past something.
33
+ */
34
+ showIf?: RuleExpression | null;
35
+ /**
36
+ * LOGIC JUMPS (lane TYPEFORM-DUP). After this question, the FIRST jump whose `when` the store
37
+ * answers true is taken — to a later question or to one of the form's endings. A jump with no
38
+ * `when` is "always". Undecided falls through to the next question. The store judges the route
39
+ * (`custom._form_route`); this package never does.
40
+ */
41
+ jumps?: FormJump[] | null;
42
+ /** SCORING: what each answer is worth (`{"Yes": 5}`); a list answer adds each of its parts. */
43
+ points?: Record<string, number> | null;
44
+ }
45
+ /** One logic jump: when the Rule is true, go to a later question or to an ending. */
46
+ interface FormJump {
47
+ when?: RuleExpression | null;
48
+ to: {
49
+ question: string;
50
+ } | {
51
+ ending: string;
52
+ };
53
+ }
54
+ /**
55
+ * ONE ENDING (lane TYPEFORM-DUP). A form may have several; a jump picks one, or the first whose
56
+ * condition holds (`when`, `scoreMin`, `scoreMax`), or else the first with no condition.
57
+ */
58
+ interface FormEnding {
59
+ /** A short name, lower-case letters, digits, - and _. Jumps point at it. */
60
+ id: string;
61
+ title?: string | null;
62
+ body?: string | null;
63
+ /** One of the organization's public pictures (the builder's side). */
64
+ pictureFileId?: Uuid | null;
65
+ /** The picture's public address (the store resolves it for the public page). */
66
+ pictureUrl?: string | null;
67
+ buttonLabel?: string | null;
68
+ /** A secure page on the organization's own sites — the thank-you's own rule. */
69
+ redirectUrl?: string | null;
70
+ when?: RuleExpression | null;
71
+ scoreMin?: number | null;
72
+ scoreMax?: number | null;
73
+ }
74
+ /** THE WELCOME SCREEN, before question one: a title, a message, a picture and a Start button. */
75
+ interface FormWelcome {
76
+ title?: string | null;
77
+ body?: string | null;
78
+ pictureFileId?: Uuid | null;
79
+ pictureUrl?: string | null;
80
+ buttonLabel?: string | null;
81
+ }
82
+ /** The look. Tokens, so light and dark both come out right without a second theme. */
83
+ interface FormTheme {
84
+ /** A design-system accent class or a CSS color. Left out, the app's own accent. */
85
+ accent?: string | null;
86
+ align?: "left" | "center" | null;
87
+ /** One of design-system `FORM_FONTS`. */
88
+ font?: string | null;
89
+ /** One of design-system `FORM_BUTTON_STYLES`. */
90
+ button?: string | null;
91
+ /** One of design-system `FORM_BACKGROUNDS`. */
92
+ background?: string | null;
93
+ /** The picture for the `picture` background: a public picture of the organization. */
94
+ backgroundFileId?: Uuid | null;
95
+ /** Its public address, as the store resolved it for the public page. */
96
+ backgroundUrl?: string | null;
97
+ }
98
+ /**
99
+ * THE FORM'S OWN LOOK, in the portal's words (`custom._portal_style` resolves both): a colour of
100
+ * the app's own palette and two of the organization's PUBLIC pictures. Each part left out is the
101
+ * organization's look — its newest open portal's, then its own name and logo. `custom.form_declare`
102
+ * refuses a colour the app does not have or a picture a stranger could not see, by name.
103
+ */
104
+ interface FormLook {
105
+ accent?: PortalAccent | null;
106
+ logoFileId?: Uuid | null;
107
+ coverFileId?: Uuid | null;
108
+ }
109
+ /** The screen after sending: the form's words, and optionally where to go next. */
110
+ interface FormThankYou {
111
+ title?: string | null;
112
+ body?: string | null;
113
+ /** A secure page on one of the organization's own sites. The store judges it. */
114
+ redirectUrl?: string | null;
115
+ }
116
+ /** THE COMPLETE DECLARATIVE SPEC. One `record_write` and the form exists. */
117
+ interface FormSpec {
118
+ name: string;
119
+ /** The Table a submission lands in. */
120
+ subject: Uuid;
121
+ intro?: string | null;
122
+ questions: FormQuestionSpec[];
123
+ flow?: FormFlow | null;
124
+ theme?: FormTheme | null;
125
+ /** Colour, logo and cover a stranger sees. Left out, the organization's look. */
126
+ look?: FormLook | null;
127
+ /**
128
+ * WHAT THE PERSON SEES AFTER SENDING. `title` and `body` are the form's own
129
+ * words for the next step ("You are all set" / "Next, book your first visit").
130
+ * `redirectUrl` optionally sends them on to a page instead — and only ever to
131
+ * one of the organization's own sites: `custom.form_declare` refuses anything
132
+ * else by name, and `custom.form_public` stops handing it out the moment its
133
+ * site leaves the organization's list (lane S7-PRIME).
134
+ */
135
+ thankYou?: FormThankYou | null;
136
+ submitLabel?: string | null;
137
+ /** A separate first screen with a Start button (lane TYPEFORM-DUP). */
138
+ welcome?: FormWelcome | null;
139
+ /** Several endings, each picked by the answers (lane TYPEFORM-DUP). */
140
+ endings?: FormEnding[] | null;
141
+ /**
142
+ * HIDDEN FIELDS: names read from the link (`?utm_source=…&ref=…`), kept on the submission and
143
+ * never shown. Not questions — a question with the same key is prefilled instead.
144
+ */
145
+ hiddenFields?: string[] | null;
146
+ /**
147
+ * Is this form meant to be answerable by someone who is not signed in? The
148
+ * runner does NOT make that true by itself — it needs the anonymous write
149
+ * door, and until that door exists it says so BY NAME rather than writing as
150
+ * whoever happens to be signed in and calling it public.
151
+ */
152
+ isPublic?: boolean;
153
+ /**
154
+ * THE OWNER'S LOGO AND NAME ABOVE THE FORM (lane TYPEFORM-2). Left out, the organization's knob
155
+ * `forms/show_owner_header` (default on); false hides the agency's brand on a client's form.
156
+ */
157
+ showOwnerHeader?: boolean | null;
158
+ /** A single choice goes on to the next question when picked. Left out, knob `forms/choice_auto_advance`. */
159
+ autoAdvance?: boolean | null;
160
+ /** NOTIFY ON ANSWER, the builder's words (lane TYPEFORM-2): a webhook and the automation that sends it. */
161
+ notify?: FormNotify | null;
162
+ }
163
+ /** Where an answer is sent besides the owner's own subscription (lane TYPEFORM-2). */
164
+ interface FormNotify {
165
+ webhookUrl?: string | null;
166
+ /** The `custom.automation_declare` row that posts to `webhookUrl` (trigger form_answered). */
167
+ webhookAutomationId?: Uuid | null;
168
+ /**
169
+ * EMAIL THE RESPONDENT (TYPEFORM-2), off unless set: one email to the address the person typed
170
+ * in `respondentField` (an email question), worded from the ending reached — sent by
171
+ * `custom.form_respondent_copy` through the platform's notification sender.
172
+ */
173
+ emailRespondent?: boolean | null;
174
+ respondentField?: string | null;
175
+ }
176
+ /**
177
+ * A form as `custom.forms` reports it — the spec a screen renders, plus the
178
+ * things only the STORE knows about it.
179
+ *
180
+ * The four store-side ids are carried on purpose. `custom.form_declare` is a
181
+ * RE-STATEMENT of the whole form, so a save that left `notifyRuleId` out would
182
+ * set it to null and the owner would quietly stop being told when somebody
183
+ * answers — the exact silent failure the notify Rule exists to prevent. They are
184
+ * read here and handed straight back by `formDeclareArgs`.
185
+ */
186
+ interface SavedForm extends FormSpec {
187
+ id: Uuid;
188
+ /** draft · open · closed · full — the store's own word, never derived on a screen. */
189
+ state: FormSummary["state"];
190
+ /** The form's readable name in the store. `form_declare` keeps it across saves. */
191
+ slug: string;
192
+ /** Stops taking answers at this many. Null is no ceiling. */
193
+ submissionCap: number | null;
194
+ /** The accept Rule: what counts as a complete answer. Null holds every answer for a person. */
195
+ quarantineRuleId: Uuid | null;
196
+ /** The subscription Rule that tells somebody an answer arrived. */
197
+ notifyRuleId: Uuid | null;
198
+ }
199
+ /**
200
+ * The spec as `custom.anon_form.presentation` holds it.
201
+ *
202
+ * `questions` is deliberately NOT here: the door writes it itself from the
203
+ * argument it validated (`presentation || {"questions": p_questions}`), so the
204
+ * words a form is asked in and the keys it is allowed to accept can never be two
205
+ * different lists.
206
+ */
207
+ declare function formPresentation(spec: FormSpec): Record<string, unknown>;
208
+ /** `presentation.notify` back into the screen's words. */
209
+ declare function notifyFromDocument(raw: unknown): FormNotify | null;
210
+ /**
211
+ * WHAT A QUESTION REFUSES BEFORE IT GOES ON (lane TYPEFORM-2) — Typeform's words, one per case.
212
+ * `null` is "may go on". A required question with no answer is refused; an answer of the email,
213
+ * phone or link kind is refused when it cannot be one. The store judges again on send.
214
+ */
215
+ declare function answerRefusal(kind: string, required: boolean, value: unknown): string | null;
216
+ /**
217
+ * ANSWER PIPING — Typeform's recall (lane TYPEFORM-2). `{{key}}` or `{{Question words}}` in a later
218
+ * question, a help line or an ending becomes that question's answer as the person gave it; a
219
+ * choice reads as its label. Unanswered, it becomes nothing.
220
+ */
221
+ declare function pipeAnswers(text: string, answers: Readonly<Record<string, unknown>>, questions: ReadonlyArray<{
222
+ key: string;
223
+ ask?: string | null;
224
+ }>, labelOf?: (key: string, value: unknown) => string | null): string;
225
+ /** `presentation.theme` from the screen's words: camelCase out, the store's snake_case in. */
226
+ declare function themeDocument(theme: FormTheme | null): Record<string, unknown>;
227
+ declare function themeFromDocument(raw: unknown): FormTheme | null;
228
+ declare function welcomeDocument(welcome: FormWelcome | null): Record<string, unknown> | null;
229
+ declare function welcomeFromDocument(raw: unknown): FormWelcome | null;
230
+ declare function endingDocument(ending: FormEnding): Record<string, unknown>;
231
+ declare function endingsFromDocument(raw: unknown): FormEnding[];
232
+ /** The hidden fields' values out of a link's query, narrowed to the names the form declares. */
233
+ declare function hiddenFromLink(hiddenFields: readonly string[] | null | undefined, params: Record<string, string | string[] | undefined>): Record<string, string>;
234
+ /** Does this form route at all — conditions, jumps, endings or points the store must judge? */
235
+ declare function formRoutes(form: Pick<FormSpec, "questions" | "endings">): boolean;
236
+ /** HOW A FORM GOES INTO SOMEBODY ELSE'S PAGE. */
237
+ declare const FORM_EMBEDS: readonly ["inline", "popup", "slider"];
238
+ type FormEmbed = (typeof FORM_EMBEDS)[number];
239
+ /**
240
+ * THE SNIPPET THE OWNER COPIES (lane TYPEFORM-DUP). `inline` is a plain iframe of the public link
241
+ * in embed mode; `popup` and `slider` are one button plus the platform's own embed script
242
+ * (`<origin>/embed/form.js`), which opens the same iframe in a centred window or a side panel.
243
+ * Nothing in either needs a cookie: the form keeps its place in its own storage and posts to its
244
+ * own origin, so it keeps working with third-party cookies blocked.
245
+ */
246
+ declare function formEmbedSnippet(kind: FormEmbed, formUrl: string, options?: {
247
+ label?: string | null;
248
+ height?: number | null;
249
+ }): string;
250
+ /** `presentation.look` both directions. Nothing set is `null`, one spelling on both sides. */
251
+ declare function formLookDocument(look: FormLook | null): Record<string, unknown> | null;
252
+ declare function formLookFromDocument(raw: unknown): FormLook | null;
253
+ /**
254
+ * THE THANK-YOU SCREEN, BOTH DIRECTIONS, IN ONE PLACE. The store holds it as
255
+ * `presentation.thank_you = {title, body, redirect_url}`; the screen reads it as
256
+ * `FormThankYou`. A thank-you with nothing in it is `null`, so "nothing set"
257
+ * has one spelling on both sides.
258
+ */
259
+ declare function thankYouDocument(thankYou: FormThankYou | null): Record<string, unknown> | null;
260
+ /** `presentation.thank_you` as the store (or `custom.form_public`) answers it, for a screen. */
261
+ declare function thankYouFromDocument(raw: unknown): FormThankYou | null;
262
+ /** The exact arguments `custom.form_declare` is called with, for a new form or a re-statement. */
263
+ declare function formDeclareArgs(form: FormSpec | SavedForm): {
264
+ table_id: Uuid;
265
+ title: string;
266
+ questions: FormQuestionSpec[];
267
+ presentation: Record<string, unknown>;
268
+ submission_cap: number | null;
269
+ quarantine_rule_id: Uuid | null;
270
+ notify_rule_id: Uuid | null;
271
+ form_id: Uuid | null;
272
+ slug: string | null;
273
+ };
274
+ /** One row of `custom.forms` back into the spec a screen and an agent both read. */
275
+ declare function formFromSummary(summary: FormSummary): SavedForm;
276
+ /**
277
+ * THE SOURCE STAMP every submission carries. A record that arrived through a
278
+ * form is not indistinguishable from one somebody typed into the grid: it says
279
+ * which form, which version of it, when, and who was acting. `_source` is a
280
+ * stamp on the document beside the answers, so it travels with the record
281
+ * through export, History and the agent's context without a parallel table.
282
+ */
283
+ declare function submissionStamp(args: {
284
+ formId: Uuid | null;
285
+ formName: string;
286
+ actor: string;
287
+ onBehalfOf?: string | null;
288
+ }): Record<string, unknown>;
289
+
290
+ interface PublicBindingShape {
291
+ render_mode: string;
292
+ resource_id: Uuid;
293
+ /** What the door says the address holds. */
294
+ kind: string | null | undefined;
295
+ /** The table a list reads: the table itself, a view's subject, a record's table. */
296
+ table_id: Uuid | null | undefined;
297
+ }
298
+ type PublicRead = {
299
+ read: "table";
300
+ tableId: Uuid;
301
+ } | {
302
+ read: "view";
303
+ tableId: Uuid;
304
+ viewId: Uuid;
305
+ } | {
306
+ read: "record";
307
+ recordId: Uuid;
308
+ } | {
309
+ read: "unknown";
310
+ };
311
+ declare function publicReadOf(binding: PublicBindingShape): PublicRead;
312
+
313
+ interface TypedAnswers {
314
+ /** Ready for the door. */
315
+ values: Record<string, unknown>;
316
+ /**
317
+ * The sentences, in the order the page asks its questions, or null when
318
+ * every answer became a value. Each one names its Field in plain words.
319
+ */
320
+ refusal: string | null;
321
+ /** Field key → its sentence, so a screen can point at the question. */
322
+ byKey: Record<string, string>;
323
+ }
324
+ /**
325
+ * Coerce what a public page collected against the Fields that page published.
326
+ *
327
+ * `fields` is exactly what `custom.form_public` / `custom.booking_public`
328
+ * already hand back — the Field definitions of the questions being asked — so
329
+ * nothing new is read and no door is opened to work this out.
330
+ */
331
+ declare function typedAnswersFor(fields: Array<Record<string, unknown>> | null | undefined, values: Record<string, unknown>): TypedAnswers;
332
+
333
+ export { FORM_EMBEDS, FORM_FLOWS, FORM_STATE_WORDS, type FormEmbed, type FormEnding, type FormFlow, type FormJump, type FormLook, type FormNotify, type FormQuestionSpec, type FormSpec, type FormThankYou, type FormTheme, type FormWelcome, type PublicBindingShape, type PublicRead, type SavedForm, type TypedAnswers, answerRefusal, endingDocument, endingsFromDocument, formDeclareArgs, formEmbedSnippet, formFromSummary, formLookDocument, formLookFromDocument, formPresentation, formRoutes, hiddenFromLink, notifyFromDocument, pipeAnswers, publicReadOf, submissionStamp, thankYouDocument, thankYouFromDocument, themeDocument, themeFromDocument, typedAnswersFor, welcomeDocument, welcomeFromDocument };