@duffcloudservices/site-forms 0.5.0 → 0.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.
- package/README.md +42 -3
- package/dist/composables/readExpectedJson.d.ts +158 -0
- package/dist/composables/useSitePhiPosture.d.ts +71 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +753 -588
- package/dist/index.js.map +1 -1
- package/dist/site-forms.css +1 -1
- package/dist/types.d.ts +16 -0
- package/package.json +1 -1
- package/src/DcsForm.vue +61 -10
- package/src/__tests__/missing-form-error-surfacing.test.ts +7 -2
- package/src/__tests__/non-json-response.test.ts +282 -0
- package/src/__tests__/phi-guidance.test.ts +239 -0
- package/src/__tests__/receipt-validation.test.ts +425 -0
- package/src/__tests__/submission.test.ts +23 -6
- package/src/__tests__/visible-if.test.ts +17 -4
- package/src/composables/readExpectedJson.ts +360 -0
- package/src/composables/useFormSubmission.ts +55 -9
- package/src/composables/useSitePhiPosture.ts +123 -0
- package/src/index.ts +29 -0
- package/src/style.css +25 -0
- package/src/types.ts +16 -0
package/README.md
CHANGED
|
@@ -165,10 +165,34 @@ import { dcsFormsModules } from '@/dcs-forms'
|
|
|
165
165
|
|
|
166
166
|
| Event | Payload | When |
|
|
167
167
|
| ------------------ | ---------------------- | ------------------------------------------- |
|
|
168
|
-
| `submit-success` | `DcsFormSubmitSuccess` | API responded `2xx
|
|
169
|
-
| `submit-error` | `DcsFormSubmitError` | Network
|
|
168
|
+
| `submit-success` | `DcsFormSubmitSuccess` | API responded `2xx` **with a JSON (or empty) body**. |
|
|
169
|
+
| `submit-error` | `DcsFormSubmitError` | Network error, non-2xx after retries, **or a 2xx whose body is not JSON** (`nonJsonResponse: true`). |
|
|
170
170
|
| `validation-error` | `FormErrors` | Submit attempted with invalid required/regex/etc. fields. |
|
|
171
171
|
|
|
172
|
+
### A 2xx with a non-JSON body is a FAILURE (0.6.0, C-301)
|
|
173
|
+
|
|
174
|
+
Before 0.6.0 the submission helper swallowed every body-parse failure and
|
|
175
|
+
returned `null`, so a POST answered by **HTTP 200 `text/html`** — a static
|
|
176
|
+
host's SPA shell, a Front Door catch-all, a captive-portal or corporate-proxy
|
|
177
|
+
interstitial — was indistinguishable from a quiet success. The visitor saw the
|
|
178
|
+
success state and the success chime while **nothing was stored**: silent lead
|
|
179
|
+
loss.
|
|
180
|
+
|
|
181
|
+
Now:
|
|
182
|
+
|
|
183
|
+
| Response | Behaviour |
|
|
184
|
+
| ------------------------------ | ------------------------------------------------------ |
|
|
185
|
+
| `2xx` + JSON body | success, `response` = parsed JSON (unchanged) |
|
|
186
|
+
| `2xx` + empty body (incl. 204) | success, `response` = `null` (unchanged) |
|
|
187
|
+
| `2xx` + any other body | **`submit-error`** with `nonJsonResponse: true`, **never retried** (retrying could double-store a lead), plus a `console.error` naming the resolved base |
|
|
188
|
+
| non-2xx | unchanged error contract |
|
|
189
|
+
|
|
190
|
+
The assertion is inlined here on purpose rather than delegated to
|
|
191
|
+
`@duffcloudservices/cms-core`'s `platformFetch`: `site-forms` is a leaf package
|
|
192
|
+
that sites pin independently (coron8 sits on `0.1.4`), and a new cms-core
|
|
193
|
+
dependency would make the fix unpublishable to exactly the sites that most need
|
|
194
|
+
it.
|
|
195
|
+
|
|
172
196
|
## Slots
|
|
173
197
|
|
|
174
198
|
Every slot exposes scoped data so consumers (KT Braun, Kept) can swap
|
|
@@ -207,6 +231,11 @@ import {
|
|
|
207
231
|
- `submitFormValues({ apiBase, siteSlug, payload })` — one-shot POST
|
|
208
232
|
with a single retry on 5xx and `multipart/form-data` when files are
|
|
209
233
|
present.
|
|
234
|
+
- `fetchSiteHandlesPhi({ apiBase, siteSlug })` — the site's PHI posture
|
|
235
|
+
(C-415), memoized per site. `<DcsForm/>` already calls it; a custom
|
|
236
|
+
layout should use it rather than inventing a second HIPAA signal.
|
|
237
|
+
`PHI_GUIDANCE_COPY` is the standard wording, `resetSitePhiPostureCache()`
|
|
238
|
+
clears the memo (tests / the portal preview iframe).
|
|
210
239
|
|
|
211
240
|
File fields emit a single `File` by default. When `attachmentPolicy.maxFiles`
|
|
212
241
|
is greater than `1`, the file field emits `File[]`, enables multiple
|
|
@@ -285,7 +314,17 @@ Use the **Legal Intake — Standard** form template (created via the portal Form
|
|
|
285
314
|
- Automatically sets `isSensitive: true`
|
|
286
315
|
- Includes matter-type and adverse-parties fields for conflict screening
|
|
287
316
|
|
|
288
|
-
For healthcare intake, mark
|
|
317
|
+
For healthcare intake, mark each PHI-bearing field `phi: true` in the YAML. There is no `hipaa` form kind (the kinds are `freeform`, `contact`, `revenue-contractor`, `resume-submission`); PHI handling is driven by the per-field `phi` marker plus the **site-level** predicate below.
|
|
318
|
+
|
|
319
|
+
### PHI guidance on HIPAA-mode tenants (C-415)
|
|
320
|
+
|
|
321
|
+
On a HIPAA-mode tenant, `<DcsForm/>` renders a standard guidance line immediately above the first free-text field:
|
|
322
|
+
|
|
323
|
+
> **Please don't include personal health details.** A general description of what you need is enough — we'll go over anything specific with you directly.
|
|
324
|
+
|
|
325
|
+
It is on by default and there is deliberately **no prop and no YAML flag** to force it on or off. The verdict comes from the platform: on mount the component GETs `/api/v1/sites/{slug}/forms/compliance` (memoized per site; the slug-free `/api/v1/forms/compliance` when the build has no baked slug), which is a boolean projection of the server's one site-level PHI predicate, `portal.Service.SiteHandlesPHI` — authoritative input `SiteAgentConfigs/{slug}/default.HipaaMode`, the same row the C-352 redaction gate reads. A site-local switch could disagree with the tenant's real posture, which is exactly the class C-352 fixed.
|
|
326
|
+
|
|
327
|
+
If the read fails the component renders nothing; the server-side redactions remain the actual control. Style it via `.dcs-form__phi-guidance`; probe for it with `[data-form-phi-guidance]` (the copy contains no "HIPAA"/"protected health information" legalese by design — it is guidance for a visitor, not a legal notice).
|
|
289
328
|
|
|
290
329
|
---
|
|
291
330
|
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CANONICAL receipt primitive for DCS form-submission clients (C-310).
|
|
3
|
+
*
|
|
4
|
+
* This module is the single definition of "did the submission actually get
|
|
5
|
+
* stored?" for the whole fleet. Site-local copies (lamphere's
|
|
6
|
+
* `theme/lib/readOkBody.ts`, iron-oak's `src/lib/submissionResponse.ts`, and the
|
|
7
|
+
* first-party `web/`+`portal/` helpers) are BACKPORTS of this file and say so in
|
|
8
|
+
* their headers. Change the contract here first.
|
|
9
|
+
*
|
|
10
|
+
* ── Two layers, deliberately separate ──────────────────────────────────────
|
|
11
|
+
*
|
|
12
|
+
* `readOkBody(response)` is TRANSPORT validation: did a DCS API answer at all?
|
|
13
|
+
* `readExpectedJson(response, validator)` adds RECEIPT validation: does the
|
|
14
|
+
* body it answered with actually prove a row exists?
|
|
15
|
+
*
|
|
16
|
+
* Transport validation alone is not enough (Codex cross-model review §c,
|
|
17
|
+
* 2026-07-26). Before C-310 any syntactically valid JSON passed — `{}`, `[]`,
|
|
18
|
+
* `"ok"`, `{"hello":"world"}` — so a 2xx from anything that speaks JSON was
|
|
19
|
+
* reported to the visitor as a stored lead. The endpoints all return a real
|
|
20
|
+
* receipt; requiring one is cheap and materially stronger.
|
|
21
|
+
*
|
|
22
|
+
* ── Why 204 is NOT exempt any more (C-310, was the last hole) ──────────────
|
|
23
|
+
*
|
|
24
|
+
* C-301/C-302 trusted an empty body when the STATUS asserted the emptiness
|
|
25
|
+
* (`204 No Content`). That was defensible in the abstract and wrong in fact:
|
|
26
|
+
* `204` proves only that *the responder* deliberately sent no content. It does
|
|
27
|
+
* not prove the DCS API ran, and every guarded endpoint returns a JSON receipt:
|
|
28
|
+
*
|
|
29
|
+
* - managed forms `202` + `PublicSiteFormSubmissionResponse`
|
|
30
|
+
* (server/internal/handlers/public_site_forms.go)
|
|
31
|
+
* - site contact `201` + `ContactFormResponse`
|
|
32
|
+
* (server/internal/handlers/contact_form.go)
|
|
33
|
+
* - estimate creation `201` + estimate JSON
|
|
34
|
+
* (server/internal/handlers/revenue_estimate_visitor.go)
|
|
35
|
+
* - passwordless verify `200` + `{success, redirectTo}`
|
|
36
|
+
* (server/internal/handlers/site_auth_multi.go:401)
|
|
37
|
+
* - booking cancellation `200` + `CancelBookingResponse`
|
|
38
|
+
* (server/internal/handlers/revenue_public.go:2134)
|
|
39
|
+
*
|
|
40
|
+
* No guarded operation needs `204`. Keeping the exemption meant a bodyless 204
|
|
41
|
+
* from a proxy, a CDN, or a CORS-mangled hop still read as "sent". Removed. If a
|
|
42
|
+
* future endpoint genuinely contracts `204`, add it back for that endpoint only,
|
|
43
|
+
* with its own predicate — not as a blanket transport exemption.
|
|
44
|
+
*
|
|
45
|
+
* ── Failures are TERMINAL ──────────────────────────────────────────────────
|
|
46
|
+
*
|
|
47
|
+
* Every throw here means "we cannot confirm this was stored", which is NOT the
|
|
48
|
+
* same as "this was not stored". Callers must surface it as a visible failure
|
|
49
|
+
* and must never retry: if the request *did* reach the API, a retry double-stores
|
|
50
|
+
* the lead; if it did not, a retry cannot help. Wording shown to the visitor says
|
|
51
|
+
* "could not confirm", never "not received".
|
|
52
|
+
*/
|
|
53
|
+
/** A caller-supplied type predicate describing one endpoint's receipt. */
|
|
54
|
+
export type ReceiptValidator<T> = (value: unknown) => value is T;
|
|
55
|
+
/**
|
|
56
|
+
* Error thrown when a submission cannot be confirmed. `receiptRejected`
|
|
57
|
+
* distinguishes the two causes, because they have different remedies:
|
|
58
|
+
*
|
|
59
|
+
* - `false` — transport failure. The body was not JSON at all (HTML shell,
|
|
60
|
+
* proxy interstitial, empty body, unreadable stream). Check routing.
|
|
61
|
+
* - `true` — the body WAS valid JSON but did not carry a receipt. Check that
|
|
62
|
+
* the endpoint contract and the predicate still agree.
|
|
63
|
+
*/
|
|
64
|
+
export interface UnconfirmedSubmissionError extends Error {
|
|
65
|
+
receiptRejected?: boolean;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Reads the body of an **ok** (2xx) submission response and refuses to treat a
|
|
69
|
+
* non-JSON body as a successful submission.
|
|
70
|
+
*
|
|
71
|
+
* Contract:
|
|
72
|
+
* - JSON body -> parsed value
|
|
73
|
+
* - anything else -> throws (empty, non-JSON content-type, unparseable,
|
|
74
|
+
* unreadable stream — including on `204`)
|
|
75
|
+
*
|
|
76
|
+
* @param label prefix for the console diagnostic, so a site copy can name itself.
|
|
77
|
+
*/
|
|
78
|
+
export declare function readOkBody(res: Response, label?: string): Promise<unknown>;
|
|
79
|
+
/**
|
|
80
|
+
* Reads an **ok** (2xx) response and requires it to carry the receipt the
|
|
81
|
+
* endpoint contracts. Throws an {@link UnconfirmedSubmissionError} otherwise.
|
|
82
|
+
*
|
|
83
|
+
* Zero dependencies and no schema library on purpose: `site-forms` is a leaf
|
|
84
|
+
* package that sites pin independently, so anything that adds a dependency is
|
|
85
|
+
* unshippable to exactly the sites that need the fix most.
|
|
86
|
+
*/
|
|
87
|
+
export declare function readExpectedJson<T>(res: Response, validator: ReceiptValidator<T>, options?: {
|
|
88
|
+
label?: string;
|
|
89
|
+
receiptName?: string;
|
|
90
|
+
}): Promise<T>;
|
|
91
|
+
/**
|
|
92
|
+
* NO LEGACY SPAM-QUARANTINE EXEMPTION — removed by C-319, deliberately.
|
|
93
|
+
*
|
|
94
|
+
* C-310 widened both predicates below to accept a bare `{"status":"ok"}`,
|
|
95
|
+
* because two of the five spam-quarantine paths answered that instead of their
|
|
96
|
+
* endpoint's real receipt (C-307 held every quarantine response byte-identical
|
|
97
|
+
* to its pre-change self, and two of those shapes were already wrong). Without
|
|
98
|
+
* the widening, every quarantined submitter — including an AI false-positive
|
|
99
|
+
* victim whose message really was stored — would have seen a visible failure.
|
|
100
|
+
*
|
|
101
|
+
* C-319 removed the cause: all five quarantine paths now answer their route's
|
|
102
|
+
* GENUINE success receipt, built by the same writer the success path uses and
|
|
103
|
+
* populated from the row C-307 stores. So the widening is gone with it. A
|
|
104
|
+
* `{"status":"ok"}` body from any of these endpoints is now what it always
|
|
105
|
+
* looked like — a misroute — and must fail.
|
|
106
|
+
*
|
|
107
|
+
* ORDERING NOTE, because this package ships independently of the server: this
|
|
108
|
+
* removal is only safe while no DEPLOYED client carries these predicates.
|
|
109
|
+
* Verified at removal time — the predicates were introduced in 0.6.0 (C-310)
|
|
110
|
+
* and 0.6.0 has never been published (npm latest: 0.5.0), so every deployed
|
|
111
|
+
* site is on a version with no receipt validation at all. site-forms must not
|
|
112
|
+
* be published AHEAD of the server roll that carries C-319; both ride the same
|
|
113
|
+
* attended C-239 train.
|
|
114
|
+
*/
|
|
115
|
+
/** `PublicSiteFormSubmissionResponse` — types.gen.ts:14845. */
|
|
116
|
+
export interface ManagedFormReceipt {
|
|
117
|
+
id?: string;
|
|
118
|
+
status: string;
|
|
119
|
+
submittedAt?: string;
|
|
120
|
+
message?: string;
|
|
121
|
+
contactMessageId?: string | null;
|
|
122
|
+
notificationQueued?: boolean;
|
|
123
|
+
}
|
|
124
|
+
export declare function isManagedFormReceipt(value: unknown): value is ManagedFormReceipt;
|
|
125
|
+
/** `ContactFormResponse` — types.gen.ts:19831. */
|
|
126
|
+
export interface SiteContactReceipt {
|
|
127
|
+
id?: string;
|
|
128
|
+
status: string;
|
|
129
|
+
submittedAt?: string;
|
|
130
|
+
message?: string;
|
|
131
|
+
}
|
|
132
|
+
export declare function isSiteContactReceipt(value: unknown): value is SiteContactReceipt;
|
|
133
|
+
/**
|
|
134
|
+
* Visitor estimate creation — `201` + the mapped estimate row
|
|
135
|
+
* (revenue_estimate_visitor.go:1419 `mapSiteVisitorEstimateResponse`). The
|
|
136
|
+
* estimate honeypot's decoy is built by the same row constructor and answered
|
|
137
|
+
* by the same writer a genuine estimate uses (C-319), so it carries a real ULID
|
|
138
|
+
* `id` and passes — which is the point: the honeypot must stay
|
|
139
|
+
* indistinguishable from a genuine submission.
|
|
140
|
+
*/
|
|
141
|
+
export interface EstimateReceipt {
|
|
142
|
+
id: string;
|
|
143
|
+
status?: string;
|
|
144
|
+
}
|
|
145
|
+
export declare function isEstimateReceipt(value: unknown): value is EstimateReceipt;
|
|
146
|
+
/** `PasswordlessVerifyCodeResponse` — types.gen.ts:20780. */
|
|
147
|
+
export interface PasswordlessVerifyReceipt {
|
|
148
|
+
success: true;
|
|
149
|
+
redirectTo?: string;
|
|
150
|
+
}
|
|
151
|
+
export declare function isPasswordlessVerifyReceipt(value: unknown): value is PasswordlessVerifyReceipt;
|
|
152
|
+
/** `CancelBookingResponse` — types.gen.ts:21486. */
|
|
153
|
+
export interface BookingCancellationReceipt {
|
|
154
|
+
success: true;
|
|
155
|
+
refundAmountCents?: number | null;
|
|
156
|
+
message?: string;
|
|
157
|
+
}
|
|
158
|
+
export declare function isBookingCancellationReceipt(value: unknown): value is BookingCancellationReceipt;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Site PHI posture — the ONE platform signal that decides whether a managed
|
|
3
|
+
* form shows PHI guidance to the visitor.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS EXISTS (C-415 / KEPT re-review F1). On a HIPAA-mode tenant the AI
|
|
6
|
+
* chat widget already opens with "please don't include personal health
|
|
7
|
+
* details", because `/agent/config` projects `hipaaMode` to it. The managed
|
|
8
|
+
* form on the same tenant — the higher-volume intake path, with a required
|
|
9
|
+
* free-text message box — projected nothing, so the platform was doing the
|
|
10
|
+
* hard half of the problem (redacting PHI it had already received) and
|
|
11
|
+
* skipping the cheap half (asking the visitor not to send it).
|
|
12
|
+
*
|
|
13
|
+
* THE SIGNAL, AND ONLY THIS SIGNAL. `GET {apiBase}/sites/{slug}/forms/compliance`
|
|
14
|
+
* returns `{ siteHandlesPhi }`, the server's projection of
|
|
15
|
+
* `portal.Service.SiteHandlesPHI` — whose authoritative input is
|
|
16
|
+
* `SiteAgentConfigs/{slug}/default.HipaaMode`, the same admin-pinned row the
|
|
17
|
+
* C-352 redaction gate reads. There is no second signal, no YAML flag, and no
|
|
18
|
+
* per-site opt-in: a site-local switch could drift from the tenant's real HIPAA
|
|
19
|
+
* posture, which is the exact class of bug C-352 fixed.
|
|
20
|
+
*
|
|
21
|
+
* NOT `/agent/config`, which already carries `hipaaMode`: that endpoint 404s
|
|
22
|
+
* unless the `aiAgent` feature, the per-site toggle, and the global kill switch
|
|
23
|
+
* are all on. Reading the flag from there would silently give no guidance to a
|
|
24
|
+
* HIPAA-mode tenant that does not run the chat widget — a fail-OPEN gate hidden
|
|
25
|
+
* behind an unrelated feature flag.
|
|
26
|
+
*
|
|
27
|
+
* FAIL DIRECTION. The server side fails CLOSED (an unresolvable HipaaMode
|
|
28
|
+
* reports `true`, so the guidance shows). The client cannot: a transport
|
|
29
|
+
* failure or an offline build has no verdict at all, and defaulting to "show"
|
|
30
|
+
* would put a health-privacy line on every non-medical customer's contact form.
|
|
31
|
+
* So the client defaults to `false` on any failure, and the server-side
|
|
32
|
+
* redactions (C-352) remain the actual control — this module is guidance, not
|
|
33
|
+
* enforcement.
|
|
34
|
+
*/
|
|
35
|
+
export interface SitePhiPostureOptions {
|
|
36
|
+
/** API base URL, e.g. `https://api.duffcloudservices.com/api/v1`. */
|
|
37
|
+
apiBase: string;
|
|
38
|
+
/**
|
|
39
|
+
* Site slug. When empty the slug-free `/forms/compliance` route is used and
|
|
40
|
+
* the server infers the site from the request origin/host — the same
|
|
41
|
+
* fallback `submitFormValues` uses, so a build with no baked slug still gets
|
|
42
|
+
* a verdict instead of requesting the broken `/sites//forms/...` shape.
|
|
43
|
+
*/
|
|
44
|
+
siteSlug: string;
|
|
45
|
+
/** Optional fetch implementation override (tests). */
|
|
46
|
+
fetchImpl?: typeof fetch;
|
|
47
|
+
}
|
|
48
|
+
/** Clears the memoized verdicts. Exported for tests and the portal preview iframe. */
|
|
49
|
+
export declare function resetSitePhiPostureCache(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Resolves whether this site's managed-form submissions are PHI-bearing.
|
|
52
|
+
*
|
|
53
|
+
* Returns `false` on any failure (network, non-2xx, non-JSON, or a body whose
|
|
54
|
+
* `siteHandlesPhi` is not a boolean). A missing/garbled body is treated as "no
|
|
55
|
+
* verdict", never as an implicit yes — see the fail-direction note above.
|
|
56
|
+
*/
|
|
57
|
+
export declare function fetchSiteHandlesPhi(opts: SitePhiPostureOptions): Promise<boolean>;
|
|
58
|
+
/**
|
|
59
|
+
* The standard PHI guidance copy. It lives in the package, not in the API
|
|
60
|
+
* response and not in any site's YAML, so one platform decision renders the
|
|
61
|
+
* same sentence on every HIPAA-mode tenant.
|
|
62
|
+
*
|
|
63
|
+
* It is guidance, not a legal notice: it tells the visitor what to do instead
|
|
64
|
+
* of what the law requires, and it deliberately mirrors the sentence the AI
|
|
65
|
+
* chat already ships on these tenants ("please don't include personal health
|
|
66
|
+
* details") so the two surfaces read as one product.
|
|
67
|
+
*/
|
|
68
|
+
export declare const PHI_GUIDANCE_COPY: {
|
|
69
|
+
readonly lead: "Please don't include personal health details.";
|
|
70
|
+
readonly detail: "A general description of what you need is enough — we'll go over anything specific with you directly.";
|
|
71
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,10 @@ export type { UseDcsFormOptions, UseDcsFormReturn } from './composables/useDcsFo
|
|
|
16
16
|
export { validateField, validateForm, isFieldVisible, hasErrors, } from './composables/useFormValidation';
|
|
17
17
|
export { submitFormValues } from './composables/useFormSubmission';
|
|
18
18
|
export type { SubmitOptions } from './composables/useFormSubmission';
|
|
19
|
+
export { fetchSiteHandlesPhi, resetSitePhiPostureCache, PHI_GUIDANCE_COPY, } from './composables/useSitePhiPosture';
|
|
20
|
+
export type { SitePhiPostureOptions } from './composables/useSitePhiPosture';
|
|
21
|
+
export { readOkBody, readExpectedJson, isManagedFormReceipt, isSiteContactReceipt, isEstimateReceipt, isPasswordlessVerifyReceipt, isBookingCancellationReceipt, } from './composables/readExpectedJson';
|
|
22
|
+
export type { ReceiptValidator, UnconfirmedSubmissionError, ManagedFormReceipt, SiteContactReceipt, EstimateReceipt, PasswordlessVerifyReceipt, BookingCancellationReceipt, } from './composables/readExpectedJson';
|
|
19
23
|
export { loadFormDefinitions, parseFormYaml } from './loaders/yaml';
|
|
20
24
|
export { buildStandardFormDefinition, STANDARD_FORM_PRESET_META, } from './presets';
|
|
21
25
|
export type { StandardFormPreset, BuildStandardFormOptions, } from './presets';
|