@samline/forms 2.4.0 → 2.7.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 (58) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +11 -8
  3. package/dist/browser/global.d.ts +28 -18
  4. package/dist/browser/global.global.js +477 -66
  5. package/dist/browser/global.global.js.map +1 -1
  6. package/dist/browser/index.cjs +1450 -0
  7. package/dist/browser/index.cjs.map +1 -0
  8. package/dist/browser/index.d.cts +14 -0
  9. package/dist/browser/index.d.ts +14 -0
  10. package/dist/browser/index.js +23 -0
  11. package/dist/browser/index.js.map +1 -0
  12. package/dist/chunk-WGPAVJTK.js +1415 -0
  13. package/dist/chunk-WGPAVJTK.js.map +1 -0
  14. package/dist/index.cjs +296 -40
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +4 -158
  17. package/dist/index.d.ts +4 -158
  18. package/dist/index.js +7 -1148
  19. package/dist/index.js.map +1 -1
  20. package/dist/registry-GzopYPCR.d.cts +164 -0
  21. package/dist/registry-GzopYPCR.d.ts +164 -0
  22. package/docs/api/add-cleanup.md +52 -0
  23. package/docs/api/append.md +96 -0
  24. package/docs/api/auto-submit.md +91 -0
  25. package/docs/api/clear-errors.md +69 -0
  26. package/docs/api/destroy.md +80 -0
  27. package/docs/api/disable-auto-submit.md +65 -0
  28. package/docs/api/element.md +78 -0
  29. package/docs/api/form.md +156 -0
  30. package/docs/api/format.md +270 -0
  31. package/docs/api/get-data.md +90 -0
  32. package/docs/api/get-field.md +87 -0
  33. package/docs/api/get-state.md +111 -0
  34. package/docs/api/get-value.md +114 -0
  35. package/docs/api/index.md +55 -0
  36. package/docs/api/observe.md +126 -0
  37. package/docs/api/on-submit.md +94 -0
  38. package/docs/api/parse-form-data.md +94 -0
  39. package/docs/api/prefill.md +89 -0
  40. package/docs/api/regex.md +155 -0
  41. package/docs/api/reset.md +61 -0
  42. package/docs/api/revalidate.md +52 -0
  43. package/docs/api/set-errors.md +97 -0
  44. package/docs/api/set-value.md +120 -0
  45. package/docs/api/subscribe.md +107 -0
  46. package/docs/api/unwatch.md +74 -0
  47. package/docs/api/validate-field-value.md +144 -0
  48. package/docs/api/validate-values.md +103 -0
  49. package/docs/api/validate.md +102 -0
  50. package/docs/api/watch.md +117 -0
  51. package/docs/browser.md +156 -0
  52. package/docs/css-styling.md +191 -0
  53. package/docs/getting-started.md +222 -0
  54. package/docs/options.md +326 -0
  55. package/docs/recipes.md +530 -0
  56. package/docs/typescript.md +615 -0
  57. package/docs/vanilla.md +12 -0
  58. package/package.json +58 -14
@@ -0,0 +1,615 @@
1
+ # TypeScript Reference
2
+
3
+ This page lists every type exported from `@samline/forms`, what it represents, and where it shows up. Each type links to the method(s) that consume or produce it.
4
+
5
+ All types are exported from the package root:
6
+
7
+ ```ts
8
+ import type {
9
+ AppendContentOptions,
10
+ AutoSubmitOptions,
11
+ FieldFormatConfig,
12
+ FieldFormatConfigMap,
13
+ FieldValidationContext,
14
+ FieldValidationRules,
15
+ FieldValidator,
16
+ FormatType,
17
+ FormController,
18
+ FormControllerOptions,
19
+ FormCleanup,
20
+ FormDataPrimitive,
21
+ FormErrors,
22
+ FormFieldElement,
23
+ FormFieldValue,
24
+ FormFieldWatcher,
25
+ FormsApi,
26
+ FormsAvailable,
27
+ FormStateListener,
28
+ FormStateSnapshot,
29
+ FormSubmitHandler,
30
+ FormTarget,
31
+ FormValues,
32
+ NewFormInput,
33
+ RuleConfig,
34
+ SerializedFormResult,
35
+ SerializedFormValue,
36
+ ValidationResult,
37
+ ValidationSchema,
38
+ ValueValidationRules,
39
+ VisualAttributes
40
+ } from '@samline/forms'
41
+ ```
42
+
43
+ ---
44
+
45
+ ## `FormController`
46
+
47
+ The full controller surface returned by [`form()`](api/form.md). All public methods are defined here.
48
+
49
+ ```ts
50
+ interface FormController {
51
+ readonly element: HTMLFormElement | null
52
+ readonly f: HTMLFormElement | null
53
+ readonly options: FormControllerOptions
54
+ onSubmit: (callback: FormSubmitHandler, preventDefault?: boolean) => FormController
55
+ addCleanup: (cleanup: FormCleanup) => () => void
56
+ watch: (field: string, callback: FormFieldWatcher) => FormController
57
+ observe: (field: string, callback: FormFieldWatcher) => () => void
58
+ unwatch: (field?: string, callback?: FormFieldWatcher) => FormController
59
+ subscribe: (listener: FormStateListener) => () => void
60
+ prefill: (fieldName?: string) => FormController
61
+ append: (options: AppendContentOptions) => HTMLElement | null
62
+ setErrors: (fields: string[] | FormErrors) => FormController
63
+ clearErrors: (fields?: string[]) => FormController
64
+ setValue: (name: string, value: unknown) => FormController
65
+ validate: (fields?: string[]) => ValidationResult
66
+ revalidate: (fields?: string[]) => ValidationResult
67
+ /**
68
+ * Apply the same `@samline/formatter` pipeline to a single field or
69
+ * to a list of fields sharing one configuration. Chainable.
70
+ *
71
+ * Requires the optional peer dependency `@samline/formatter`. When
72
+ * the peer is not installed the call logs a single `console.error`
73
+ * and returns the controller unchanged.
74
+ */
75
+ format: (config: FieldFormatConfig) => FormController
76
+ formatAll: (config: FieldFormatConfig) => FormController
77
+ reset: () => FormController
78
+ autoSubmit: (options?: boolean | AutoSubmitOptions) => FormController
79
+ disableAutoSubmit: () => FormController
80
+ getValue: (name: string) => FormFieldValue
81
+ getField: (name: string) => FormFieldElement | FormFieldElement[] | null
82
+ getData: () => SerializedFormResult
83
+ getState: () => FormStateSnapshot
84
+ destroy: () => void
85
+ }
86
+ ```
87
+
88
+ ---
89
+
90
+ ## `FormControllerOptions`
91
+
92
+ The second argument to [`form()`](api/form.md). See [docs/options.md](options.md) for the full reference.
93
+
94
+ ```ts
95
+ interface FormControllerOptions {
96
+ attributes?: Partial<VisualAttributes>
97
+ autoValidate?: boolean
98
+ autoSubmit?: boolean | AutoSubmitOptions
99
+ clearManualErrorsOnChange?: boolean
100
+ clearErrorsOnSubmit?: boolean
101
+ validators?: ValidationSchema
102
+ /**
103
+ * Declarative format configuration. Each entry is applied during
104
+ * `form()` initialization using the same logic as `controller.format(...)`.
105
+ * The key is just an identifier — the canonical field name lives in
106
+ * `FieldFormatConfig.field`.
107
+ */
108
+ formats?: FieldFormatConfigMap
109
+ }
110
+ ```
111
+
112
+ ---
113
+
114
+ ## `FormTarget`
115
+
116
+ What [`form()`](api/form.md) accepts as its first argument.
117
+
118
+ ```ts
119
+ type FormTarget =
120
+ | string // element id
121
+ | HTMLFormElement // direct element
122
+ | { current: HTMLFormElement | null } // ref-like
123
+ | null
124
+ | undefined
125
+ ```
126
+
127
+ A string is treated as a `document.getElementById(id)` lookup; only `HTMLFormElement` matches count. A ref-like value lets you pass a Vue/React-style ref object.
128
+
129
+ ---
130
+
131
+ ## `FormFieldElement`
132
+
133
+ The set of DOM field types the controller manages.
134
+
135
+ ```ts
136
+ type FormFieldElement =
137
+ | HTMLInputElement
138
+ | HTMLSelectElement
139
+ | HTMLTextAreaElement
140
+ ```
141
+
142
+ Returned by [`getField`](api/get-field.md).
143
+
144
+ ---
145
+
146
+ ## `FormFieldValue`
147
+
148
+ The normalized value of a field.
149
+
150
+ ```ts
151
+ type FormFieldValue = string | string[] | File[] | undefined
152
+ ```
153
+
154
+ - `string` — for text inputs, textareas, single selects, and single checked checkboxes / radios.
155
+ - `string[]` — for checkbox groups, multiple selects, and repeated `name="field[]"` controls. Radio groups remain scalar.
156
+ - `File[]` — for `<input type="file">` (may be empty).
157
+ - `undefined` — when no field with that name exists.
158
+
159
+ Returned by [`getValue`](api/get-value.md).
160
+
161
+ ---
162
+
163
+ ## `FormValues`
164
+
165
+ The aggregated values map produced by the controller and the helpers.
166
+
167
+ ```ts
168
+ type FormValues = Record<string, FormFieldValue>
169
+ ```
170
+
171
+ Useful when you build custom validators that need cross-field logic beyond exact equality (`sameAs` handles equality directly):
172
+
173
+ ```ts
174
+ form('booking-form', {
175
+ validators: {
176
+ end_date: {
177
+ validate: ({ value, values }) =>
178
+ typeof value === 'string' &&
179
+ typeof values.start_date === 'string' &&
180
+ value >= values.start_date
181
+ ? null
182
+ : 'End date must not precede start date.'
183
+ }
184
+ }
185
+ })
186
+ ```
187
+
188
+ The controller infers reactive dependencies from `sameAs`, but not from arbitrary reads inside `validate`. Add `dependsOn: 'start_date'` to the `end_date` rules when changes to the source should automatically revalidate the custom rule.
189
+
190
+ ---
191
+
192
+ ## `FormErrors`
193
+
194
+ A map from field name to an array of error messages. Each field can have multiple errors (one per failed rule or per failing custom validator).
195
+
196
+ ```ts
197
+ type FormErrors = Record<string, string[]>
198
+ ```
199
+
200
+ Drives the `css-error` attribute on fields and is returned inside [`FormStateSnapshot`](#formstatesnapshot) and [`ValidationResult`](#validationresult).
201
+
202
+ ---
203
+
204
+ ## `FormStateSnapshot`
205
+
206
+ The shape returned by [`getState()`](api/get-state.md). Built fresh on every call — it does not retain references to controller internals.
207
+
208
+ ```ts
209
+ interface FormStateSnapshot {
210
+ values: FormValues
211
+ errors: FormErrors
212
+ filledFields: string[]
213
+ isValid: boolean
214
+ isValidated: boolean
215
+ autoSubmit: boolean
216
+ isSubmitting: boolean
217
+ submitCount: number
218
+ }
219
+ ```
220
+
221
+ | Field | Meaning |
222
+ | --- | --- |
223
+ | `values` | Current values for every tracked field. |
224
+ | `errors` | Merged validation and manual errors. |
225
+ | `filledFields` | Names of fields that have a non-empty value. |
226
+ | `isValid` | `true` when `errors` has no entries. |
227
+ | `isValidated` | `true` after the initial auto-validation pass or once [`validate`](api/validate.md) has run. |
228
+ | `autoSubmit` | `true` while auto-submit is enabled. |
229
+ | `isSubmitting` | `true` while one or more valid submissions have asynchronous handlers that have not settled. |
230
+ | `submitCount` | Number of submit attempts (valid or invalid). |
231
+
232
+ ---
233
+
234
+ ## `FormStateListener`
235
+
236
+ The callback passed to [`subscribe`](api/subscribe.md).
237
+
238
+ ```ts
239
+ type FormStateListener = (state: FormStateSnapshot) => void
240
+ ```
241
+
242
+ Receives the current snapshot immediately, then at controller notification points such as handled input, manual-error changes, reset, submit attempts, async-submit state transitions, auto-submit toggles, and observed DOM mutations. A direct `validate()` call does not independently notify it.
243
+
244
+ ---
245
+
246
+ ## `FormSubmitHandler`
247
+
248
+ The callback passed to [`onSubmit`](api/on-submit.md).
249
+
250
+ ```ts
251
+ type FormSubmitHandler = (
252
+ form: HTMLFormElement,
253
+ data: Record<string, SerializedFormValue>,
254
+ formData: FormData,
255
+ state: FormStateSnapshot
256
+ ) => void | Promise<void>
257
+ ```
258
+
259
+ Only invoked when the form is valid. `data` and `formData` are produced fresh on each invocation. Returned promises are tracked by `FormStateSnapshot.isSubmitting`; fulfillment or rejection settles the tracked work.
260
+
261
+ ---
262
+
263
+ ## `FormFieldWatcher`
264
+
265
+ The callback passed to [`watch`](api/watch.md) and [`observe`](api/observe.md).
266
+
267
+ ```ts
268
+ type FormFieldWatcher = (
269
+ value: FormFieldValue,
270
+ field: FormFieldElement | FormFieldElement[] | null,
271
+ form: HTMLFormElement,
272
+ state: FormStateSnapshot
273
+ ) => void
274
+ ```
275
+
276
+ - `value` — the current value of the field that just changed.
277
+ - `field` — the DOM element(s) backing the field (same shape as [`getField`](api/get-field.md) returns). For a single matching field it is `HTMLInputElement` / `HTMLSelectElement` / `HTMLTextAreaElement`; for repeated fields (e.g. checkbox / radio groups) it is an array of them; it is `null` when no element matches the name.
278
+ - `form` — the bound `HTMLFormElement`.
279
+ - `state` — a snapshot of the whole controller state (see [`FormStateSnapshot`](#formstatesnapshot)).
280
+
281
+ `observe` and `watch` fire once immediately with the current value, then on every matching `input` event. `observe` returns an unsubscribe function; `watch` returns the controller.
282
+
283
+ ---
284
+
285
+ ## `SerializedFormResult`
286
+
287
+ The shape returned by [`getData()`](api/get-data.md) and [`parseFormData()`](api/parse-form-data.md).
288
+
289
+ ```ts
290
+ interface SerializedFormResult {
291
+ data: Record<string, SerializedFormValue>
292
+ formData: FormData
293
+ }
294
+ ```
295
+
296
+ `data` is a plain object mirror of the `FormData`. Repeated names become arrays (e.g. `interests: ['design', 'code']`). Empty file entries are filtered out.
297
+
298
+ ---
299
+
300
+ ## `SerializedFormValue`
301
+
302
+ The value shape inside `SerializedFormResult.data`.
303
+
304
+ ```ts
305
+ type SerializedFormValue = FormDataPrimitive | FormDataPrimitive[]
306
+
307
+ type FormDataPrimitive = FormDataEntryValue
308
+ ```
309
+
310
+ `FormDataEntryValue` is `string | File` in the DOM lib types.
311
+
312
+ ---
313
+
314
+ ## `ValidationResult`
315
+
316
+ The shape returned by [`validate`](api/validate.md), [`revalidate`](api/revalidate.md), and [`validateValues`](api/validate-values.md).
317
+
318
+ ```ts
319
+ interface ValidationResult {
320
+ isValid: boolean
321
+ errors: FormErrors
322
+ }
323
+ ```
324
+
325
+ `errors` is always a fresh object — safe to mutate.
326
+
327
+ ---
328
+
329
+ ## `ValidationSchema`
330
+
331
+ The shape of `options.validators`.
332
+
333
+ ```ts
334
+ type ValidationSchema = Record<string, FieldValidationRules>
335
+ ```
336
+
337
+ ```ts
338
+ form('signup-form', {
339
+ validators: {
340
+ email: { required: true, pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ },
341
+ password: { required: true, minLength: 8 }
342
+ }
343
+ })
344
+ ```
345
+
346
+ ---
347
+
348
+ ## `FieldValidationRules`
349
+
350
+ The rule set for a single field.
351
+
352
+ ```ts
353
+ interface FieldValidationRules extends ValueValidationRules {
354
+ sameAs?: RuleConfig<string>
355
+ dependsOn?: string | string[]
356
+ each?: ValueValidationRules
357
+ }
358
+
359
+ interface ValueValidationRules {
360
+ required?: RuleConfig<boolean>
361
+ minLength?: RuleConfig<number>
362
+ maxLength?: RuleConfig<number>
363
+ pattern?: RuleConfig<RegExp>
364
+ numeric?: RuleConfig<boolean>
365
+ min?: RuleConfig<number>
366
+ max?: RuleConfig<number>
367
+ validate?: FieldValidator | FieldValidator[]
368
+ }
369
+ ```
370
+
371
+ Value rules run in the order: `required` → `minLength` → `maxLength` → `pattern` → numeric bounds → `validate`. Field-level `sameAs` and member-level `each` are then evaluated. All are optional; an empty rules object contributes nothing.
372
+
373
+ `sameAs` names another exact field key. It compares non-empty strings exactly and arrays by ordered contents; file entries compare by `File` object identity. In a controller, changing the referenced field automatically revalidates the field that declares `sameAs`. Reciprocal declarations are cycle-safe, but usually duplicate the same error on both controls; prefer declaring the rule only on the confirmation field.
374
+
375
+ `dependsOn` declares one or more exact source field names for controller revalidation. It is intended for custom validators that read `values`; it does not add an error or affect the pure helpers by itself. Dependency chains and cycles are traversed once per input event.
376
+
377
+ `each` applies a `ValueValidationRules` object to every collection member independently. Controller validation can therefore mark only the failing DOM controls, while [`FormErrors`](#formerrors) remains a flat `Record<string, string[]>` with item messages collected under the field name.
378
+
379
+ ## `ValueValidationRules`
380
+
381
+ The exported subset of rules that can validate one value, including each member of a collection. It excludes field-level `sameAs`, `dependsOn`, and nested `each`.
382
+
383
+ `numeric`, `min`, and `max` skip empty values. Non-empty input must be a finite ordinary signed decimal: decimal integers and fractions are accepted, while exponent notation, hexadecimal, `Infinity`, `NaN`, and formatted separators are rejected. `min` and `max` are inclusive and imply numeric parsing even when `numeric` is omitted.
384
+
385
+ ---
386
+
387
+ ## `RuleConfig<T>`
388
+
389
+ Lets a rule be configured with a plain value or a `{ value, message }` object.
390
+
391
+ ```ts
392
+ type RuleConfig<T> = T | { value: T; message?: string }
393
+ ```
394
+
395
+ ```ts
396
+ form('signup-form', {
397
+ validators: {
398
+ password: {
399
+ minLength: { value: 8, message: 'Use at least 8 characters.' }
400
+ },
401
+ terms: {
402
+ required: { value: true, message: 'Please accept the terms.' }
403
+ }
404
+ }
405
+ })
406
+ ```
407
+
408
+ When the plain form is used, default messages are produced automatically.
409
+
410
+ ---
411
+
412
+ ## `FieldValidationContext`
413
+
414
+ The argument passed to a custom validator.
415
+
416
+ ```ts
417
+ interface FieldValidationContext {
418
+ field: string
419
+ value: FormFieldValue
420
+ values: FormValues
421
+ element?: FormFieldElement
422
+ index?: number
423
+ }
424
+ ```
425
+
426
+ `element` and `index` are populated while a bound controller evaluates an `each` member. `index` is zero-based. Pure helper validation has no DOM element, so `element` remains undefined; `index` is still provided for array members.
427
+
428
+ Use it to write cross-field validators:
429
+
430
+ ```ts
431
+ form('checkout-form', {
432
+ validators: {
433
+ card: {
434
+ validate: ({ value, field }) => {
435
+ if (typeof value !== 'string') return `${field} is required.`
436
+ return /^\d{16}$/.test(value.replace(/\s+/g, ''))
437
+ ? null
438
+ : 'Card number must be 16 digits.'
439
+ }
440
+ }
441
+ }
442
+ })
443
+ ```
444
+
445
+ ---
446
+
447
+ ## `FieldValidator`
448
+
449
+ The custom validator signature.
450
+
451
+ ```ts
452
+ type FieldValidator = (
453
+ context: FieldValidationContext
454
+ ) => string | undefined | null | false | true
455
+ ```
456
+
457
+ | Return | Meaning |
458
+ | --- | --- |
459
+ | `string` (non-empty) | Push as the error message. |
460
+ | `undefined`, `null`, `true` | Pass. |
461
+ | `false` | Push the generic error message `"Validation failed."`. |
462
+
463
+ Multiple validators can be chained by passing an array as `validate`:
464
+
465
+ ```ts
466
+ form('order-form', {
467
+ validators: {
468
+ quantity: {
469
+ validate: [
470
+ ({ value }) => (typeof value === 'string' && Number(value) > 0 ? null : 'Must be greater than zero.'),
471
+ ({ value }) => (typeof value === 'string' && Number.isInteger(Number(value)) ? null : 'Must be an integer.')
472
+ ]
473
+ }
474
+ }
475
+ })
476
+ ```
477
+
478
+ ---
479
+
480
+ ## `FormCleanup`
481
+
482
+ The callback accepted by [`addCleanup()`](api/add-cleanup.md).
483
+
484
+ ```ts
485
+ type FormCleanup = () => void
486
+ ```
487
+
488
+ Cleanups are synchronous and run once in reverse registration order during `destroy()`, unless their returned unregister function removes them first.
489
+
490
+ ---
491
+
492
+ ## `AutoSubmitOptions`
493
+
494
+ The argument to [`autoSubmit()`](api/auto-submit.md) and the `autoSubmit` option.
495
+
496
+ ```ts
497
+ interface AutoSubmitOptions {
498
+ debounce?: number
499
+ }
500
+ ```
501
+
502
+ `debounce` is in milliseconds.
503
+
504
+ ---
505
+
506
+ ## `AppendContentOptions`
507
+
508
+ The argument to [`append()`](api/append.md).
509
+
510
+ ```ts
511
+ interface AppendContentOptions {
512
+ tag: keyof HTMLElementTagNameMap
513
+ content: string
514
+ class?: string
515
+ atStart?: boolean
516
+ }
517
+ ```
518
+
519
+ | Field | Meaning |
520
+ | --- | --- |
521
+ | `tag` | The HTML tag to create. |
522
+ | `content` | `innerHTML` content for the new node. |
523
+ | `class` | Optional class name. If a node with the same first class already exists inside the form, it is removed before the new one is inserted. |
524
+ | `atStart` | When `true`, inserts at the start of the form. Defaults to `false` (append at the end). |
525
+
526
+ ---
527
+
528
+ ## `VisualAttributes`
529
+
530
+ The shape of `options.attributes`.
531
+
532
+ ```ts
533
+ interface VisualAttributes {
534
+ filled: string
535
+ error: string
536
+ }
537
+ ```
538
+
539
+ `Partial<VisualAttributes>` is accepted, so you can override only one of them. See [docs/css-styling.md](css-styling.md).
540
+
541
+ ---
542
+
543
+ ## `FieldFormatConfig` and `FieldFormatConfigMap`
544
+
545
+ The argument to [`format()`](api/format.md) and [`formatAll()`](api/format.md). The map is the shape of `options.formats`.
546
+
547
+ ```ts
548
+ type FormatType =
549
+ | 'general'
550
+ | 'phone'
551
+ | 'numeral'
552
+ | 'date'
553
+ | 'time'
554
+ | 'creditCard'
555
+ | 'creditCardType'
556
+
557
+ interface FieldFormatConfig {
558
+ type: FormatType
559
+ field: string | string[]
560
+ displayField?: string
561
+ options?: Record<string, unknown>
562
+ }
563
+
564
+ type FieldFormatConfigMap = Record<string, FieldFormatConfig>
565
+ ```
566
+
567
+ The `field` name is the **canonical** name of the formatted pair. A custom `displayField` is valid only for one string field; passing it with a field array logs an error and leaves the form unchanged. Array configurations derive `${fieldName}_displayed` independently for every field. Every map value includes its own `field` because the map key is only an identifier. See [The mirror convention](api/format.md#the-mirror-convention).
568
+
569
+ ---
570
+
571
+ ## `FormsApi`
572
+
573
+ The shape of the [`browser`](../getting-started.md#browser-registry-helpers) singleton exported from the vanilla entrypoint. The IIFE bundle exposes the same shape as `window.Forms`.
574
+
575
+ ```ts
576
+ interface FormsApi {
577
+ form: (target: FormTarget, options?: FormControllerOptions) => FormController
578
+ newForm: (input: NewFormInput) => FormController | undefined
579
+ destroyForm: (id: string) => void
580
+ available: FormsAvailable
581
+ }
582
+ ```
583
+
584
+ `form` is the same reference exported from `@samline/forms`. `newForm` and `destroyForm` manage the module-level `available` registry keyed by the form id. Spread `browser` into a new object to derive an independent registry (with its own `available` map); see the getting-started guide for the pattern.
585
+
586
+ ---
587
+
588
+ ## `NewFormInput`
589
+
590
+ The argument to `FormsApi.newForm`.
591
+
592
+ ```ts
593
+ interface NewFormInput {
594
+ id: string
595
+ options?: FormControllerOptions
596
+ }
597
+ ```
598
+
599
+ A missing `id` logs `Form ID is required` to `console.error` and `newForm` returns `undefined` without touching the registry.
600
+
601
+ ---
602
+
603
+ ## `FormsAvailable`
604
+
605
+ The shape of `FormsApi.available`.
606
+
607
+ ```ts
608
+ interface FormsAvailable {
609
+ [id: string]: FormController
610
+ }
611
+ ```
612
+
613
+ Use it to inspect or iterate over every live controller in a registry. Each entry is the `FormController` returned by `newForm` for that id.
614
+
615
+ `Partial<VisualAttributes>` is accepted, so you can override only one of them. See [docs/css-styling.md](css-styling.md).
@@ -0,0 +1,12 @@
1
+ # Vanilla
2
+
3
+ The vanilla documentation has been reorganised into a modular tree. Start here:
4
+
5
+ - [Getting started](getting-started.md) — concepts, observable contract, lifecycle, and the side-effect overview.
6
+ - [Options reference](options.md) — every `FormControllerOptions` field with defaults.
7
+ - [CSS styling](css-styling.md) — `css-filled` / `css-error` recipes and accessibility notes.
8
+ - [TypeScript reference](typescript.md) — every exported type, with examples.
9
+ - [API reference](api/index.md) — one page per public method.
10
+ - [Recipes](recipes.md) — end-to-end patterns.
11
+
12
+ If you arrived here from an older link, the equivalent of the previous one-page vanilla guide is now [docs/getting-started.md](getting-started.md).