@usgb/forms 0.1.0 → 1.0.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 (61) hide show
  1. package/README.md +256 -71
  2. package/dist/forms-manifest.json +249 -0
  3. package/dist/index.js +1032 -31
  4. package/dist/src/contract/collectContext.d.ts +9 -0
  5. package/dist/src/contract/collectContext.d.ts.map +1 -0
  6. package/dist/src/contract/createStubAdapter.d.ts +9 -0
  7. package/dist/src/contract/createStubAdapter.d.ts.map +1 -0
  8. package/dist/src/contract/types.d.ts +90 -0
  9. package/dist/src/contract/types.d.ts.map +1 -0
  10. package/dist/src/definitions/consents.d.ts +37 -0
  11. package/dist/src/definitions/consents.d.ts.map +1 -0
  12. package/dist/src/definitions/fields.d.ts +19 -0
  13. package/dist/src/definitions/fields.d.ts.map +1 -0
  14. package/dist/src/definitions/index.d.ts +7 -0
  15. package/dist/src/definitions/index.d.ts.map +1 -0
  16. package/dist/src/definitions/manifest.d.ts +8 -0
  17. package/dist/src/definitions/manifest.d.ts.map +1 -0
  18. package/dist/src/definitions/types.d.ts +77 -0
  19. package/dist/src/definitions/types.d.ts.map +1 -0
  20. package/dist/src/form/FormShell.d.ts +10 -0
  21. package/dist/src/form/FormShell.d.ts.map +1 -0
  22. package/dist/src/form/RegisteredTextField.d.ts +14 -0
  23. package/dist/src/form/RegisteredTextField.d.ts.map +1 -0
  24. package/dist/src/form/UsgbForm.d.ts +25 -0
  25. package/dist/src/form/UsgbForm.d.ts.map +1 -0
  26. package/dist/src/form/chunkFields.d.ts +17 -0
  27. package/dist/src/form/chunkFields.d.ts.map +1 -0
  28. package/dist/src/form/kinds/LeadKindForm.d.ts +4 -0
  29. package/dist/src/form/kinds/LeadKindForm.d.ts.map +1 -0
  30. package/dist/src/form/kinds/NewsletterKindForm.d.ts +4 -0
  31. package/dist/src/form/kinds/NewsletterKindForm.d.ts.map +1 -0
  32. package/dist/src/form/kinds/index.d.ts +6 -0
  33. package/dist/src/form/kinds/index.d.ts.map +1 -0
  34. package/dist/src/form/kinds/types.d.ts +11 -0
  35. package/dist/src/form/kinds/types.d.ts.map +1 -0
  36. package/dist/src/form/useUsgbFormSession.d.ts +41 -0
  37. package/dist/src/form/useUsgbFormSession.d.ts.map +1 -0
  38. package/dist/src/index.d.ts +14 -0
  39. package/dist/src/index.d.ts.map +1 -0
  40. package/dist/src/ui/Button.d.ts +7 -0
  41. package/dist/src/ui/Button.d.ts.map +1 -0
  42. package/dist/src/ui/ConsentField.d.ts +9 -0
  43. package/dist/src/ui/ConsentField.d.ts.map +1 -0
  44. package/dist/src/ui/FieldError.d.ts +8 -0
  45. package/dist/src/ui/FieldError.d.ts.map +1 -0
  46. package/dist/src/ui/FieldLabel.d.ts +9 -0
  47. package/dist/src/ui/FieldLabel.d.ts.map +1 -0
  48. package/dist/src/ui/TextField.d.ts +19 -0
  49. package/dist/src/ui/TextField.d.ts.map +1 -0
  50. package/dist/src/ui/icons.d.ts +4 -0
  51. package/dist/src/ui/icons.d.ts.map +1 -0
  52. package/dist/usgb-forms.1.0.0.js +44 -0
  53. package/dist/usgb-forms.css +574 -0
  54. package/dist/usgb-forms.js +44 -0
  55. package/forms-manifest.json +249 -0
  56. package/package.json +51 -46
  57. package/dist/index.cjs +0 -64
  58. package/dist/index.cjs.map +0 -1
  59. package/dist/index.d.cts +0 -15
  60. package/dist/index.d.ts +0 -15
  61. package/dist/index.js.map +0 -1
package/README.md CHANGED
@@ -1,110 +1,295 @@
1
1
  # @usgb/forms
2
2
 
3
- Sample React package that builds with [tsup](https://tsup.egoist.dev/) and publishes to npm using [OIDC trusted publishing](https://docs.npmjs.com/trusted-publishers/). Releases do not use a long-lived `NPM_TOKEN`. Replace this code and this readme with real versions.
3
+ Shared lead and newsletter forms for the **PWA** (React) and **WordPress** (Web Component) hosts.
4
4
 
5
- ## What's included
5
+ You pick a predefined `formId`. The package owns fields, validation, consent copy, tracking keys, and layout. You own the submit endpoint, legal-document URLs, heading/CTA copy, and thank-you behavior.
6
6
 
7
- - React 18+ form primitives (`Form`, `TextField`) with TypeScript types
8
- - Dual ESM/CJS build output in `dist/`
9
- - CI on pull requests
10
- - Manual publish from GitHub Actions via OIDC (no git tags)
7
+ ## What you get
11
8
 
12
- ## Local development
9
+ | Artifact | Path / export | Who uses it |
10
+ | ------------- | --------------------------------------------------------------- | ----------------------------------- |
11
+ | React package | `@usgb/forms` → `dist/index.js` | PWA / any React host |
12
+ | Stylesheet | `@usgb/forms/usgb-forms.css` | **Required** for React hosts |
13
+ | Web Component | `dist/usgb-forms.js` (also versioned `usgb-forms.<version>.js`) | WordPress and other non-React hosts |
14
+ | Catalog | `@usgb/forms/manifest` → `forms-manifest.json` | CMS pickers, docs, tooling |
15
+
16
+ The Web Component script injects the same CSS once on load. React hosts must import the stylesheet themselves.
17
+
18
+ ## Mental model
19
+
20
+ | You pass | Package already decided |
21
+ | -------------------------------- | ------------------------------------------------------------------------------------- |
22
+ | `formId` | Fields, required/optional rules, consent bundle, tracking set, layout family (`kind`) |
23
+ | `adapter.submit` | Where the payload goes (never an attribute or hardcoded URL) |
24
+ | `legalUrls` / `*-url` | Hrefs for package-owned consent copy |
25
+ | `heading`, `body`, `ctaLabel` | Marketing copy around the form |
26
+ | `successMessage` or `successUrl` | Thank-you UX after accept |
27
+
28
+ Hosts **must not** invent field lists, consent wording, or tracking keys. New forms are new catalog entries in this package, not host-built schemas.
29
+
30
+ Use `listFormDefinitions()` (or the manifest) when a CMS needs a picker of available `formId`s. Deeper catalog rules: [`src/definitions/context.md`](src/definitions/context.md).
31
+
32
+ ## Available forms
33
+
34
+ | `formId` | Kind | Fields | Consent | Variants |
35
+ | -------------------- | ------------ | --------------------------------------------------------- | ------------------------------ | ------------------------------------------------------- |
36
+ | `main-investors-kit` | `lead` | First name, last name, email (required); phone (optional) | TCPA checkbox (`lead-tcpa` v1) | `dense`, `large`, `sidebar`, plus optional `two-column` |
37
+ | `newsletter` | `newsletter` | Email only | None | None — omit `variants` / `variant` |
38
+
39
+ Appearances for both: `light` | `navy`.
40
+
41
+ `kind` is the layout family (lead grid vs inline newsletter row). The root exposes `data-kind` for CSS. Several catalog forms can share one kind (e.g. another lead kit that reuses the lead layout).
42
+
43
+ ## Install
44
+
45
+ Registry package name, scope, and CDN base URL are **TBD** — examples below use the working package name `@usgb/forms` and a placeholder CDN host. Replace them when publishing is finalized.
46
+
47
+ ### npm (React / PWA)
13
48
 
14
49
  ```bash
15
- npm install
16
- npm test
17
- npm run build
50
+ yarn add @usgb/forms
51
+ # or
52
+ npm install @usgb/forms
18
53
  ```
19
54
 
20
- Peer dependencies: `react` and `react-dom` >= 18.
55
+ Then import the component and stylesheet:
56
+
57
+ ```ts
58
+ import { UsgbForm } from '@usgb/forms'
59
+ import '@usgb/forms/usgb-forms.css'
60
+ ```
21
61
 
22
- ## Publish with OIDC
62
+ Peer dependency: `react` and `react-dom` `^18` or `^19`.
23
63
 
24
- Trusted publishing creates a trust relationship between npm and this repository. GitHub Actions mints a short-lived OIDC token; npm CLI 11.5.1+ exchanges it for a publish credential. No npm write token is stored in GitHub.
64
+ ### CDN (Web Component / WordPress)
25
65
 
26
- Requirements:
66
+ Load the built IIFE (styles inject themselves):
67
+
68
+ ```html
69
+ <script src="https://cdn.example.com/usgb-forms/usgb-forms.1.0.0.js"></script>
70
+ ```
71
+
72
+ Exact CDN hostname, path, and versioning scheme are **TBD**. Prefer a versioned filename (`usgb-forms.<version>.js`) so hosts can pin releases.
73
+
74
+ Until the package is published, you can still develop against a local checkout (`yarn add file:../usgb-forms` or `yarn link`).
75
+
76
+ ## React (PWA)
77
+
78
+ ```tsx
79
+ import { UsgbForm, createStubAdapter, PWA_LEGAL_URLS } from '@usgb/forms'
80
+ import '@usgb/forms/usgb-forms.css'
81
+ ;<UsgbForm
82
+ formId="main-investors-kit"
83
+ appearance="navy"
84
+ variants={['large', 'two-column']}
85
+ heading="Get My Free Guide"
86
+ body="Enter your details to receive the Main Investors Kit."
87
+ ctaLabel="GET MY FREE GUIDE"
88
+ source="cms-home"
89
+ campaign="spring-kit"
90
+ legalUrls={PWA_LEGAL_URLS}
91
+ successMessage="Thanks — we will send your kit shortly."
92
+ adapter={yourHostAdapter}
93
+ onAccepted={(payload) => {
94
+ /* analytics bridge */
95
+ }}
96
+ onFailed={(payload, message) => {
97
+ /* error bridge */
98
+ }}
99
+ />
100
+ ```
27
101
 
28
- - Node 22.14.0+ (this workflow uses Node 24)
29
- - npm 11.5.1+
30
- - GitHub-hosted runners (self-hosted runners are not supported)
31
- - A public GitHub repository if you want automatic provenance attestations
102
+ Newsletter (no consent, no variants):
32
103
 
33
- ### 1. Point `package.json` at the real package and repo
104
+ ```tsx
105
+ <UsgbForm
106
+ formId="newsletter"
107
+ appearance="light"
108
+ heading="Subscribe"
109
+ ctaLabel="SUBSCRIBE"
110
+ adapter={yourHostAdapter}
111
+ />
112
+ ```
34
113
 
35
- Update these fields before the first publish. npm checks that `repository.url` matches the GitHub repository that is publishing:
114
+ ### React props
115
+
116
+ | Prop | Required | Role |
117
+ | ------------------------- | -------------------------- | --------------------------------------------------------------------------------------------------- |
118
+ | `formId` | yes | Catalog key (`main-investors-kit`, `newsletter`, …) |
119
+ | `adapter` | yes | `HostAdapter` with `host`, `getTrackingHints()`, `submit()` |
120
+ | `appearance` | no | `light` (default) or `navy` |
121
+ | `variants` | no | Lead only. Default `['dense']`. One of `dense` \| `large` \| `sidebar`, optionally add `two-column` |
122
+ | `heading` / `body` | no | Title and supporting copy |
123
+ | `ctaLabel` | no | Submit button label |
124
+ | `source` / `campaign` | no | Placement / campaign context in the payload |
125
+ | `legalUrls` | when consent requires them | Host CMS paths for legal links in consent copy |
126
+ | `successMessage` | no | Inline thank-you when `successUrl` is omitted |
127
+ | `successUrl` | no | Redirect after accept (analytics / `onAccepted` are invoked first; see Success behavior) |
128
+ | `analytics` | no | `(event) => void` for view / validation / submit / success / failure |
129
+ | `className` | no | Extra class on `.usgb-form` |
130
+ | `onAccepted` / `onFailed` | no | Callbacks with the submission payload |
131
+
132
+ ## Web Component (WordPress)
133
+
134
+ Load one script. Styles are injected automatically.
135
+
136
+ ````html
137
+ <script src="https://cdn.example.com/usgb-forms/usgb-forms.1.0.0.js"></script>
138
+ <usgb-form
139
+ ```
140
+ form-id="main-investors-kit"
141
+ appearance="navy"
142
+ variant="large two-column"
143
+ heading="Get My Free Guide"
144
+ cta="GET MY FREE GUIDE"
145
+ source="cms-home"
146
+ client-agreement-url="/client-agreement/"
147
+ privacy-policy-url="/privacy-policy/"
148
+ success-message="Thanks — we will send your kit shortly."
149
+ ></usgb-form>
150
+ ````
151
+
152
+ Submit is **not** an attribute. Markup alone uses a stub adapter. Assign a real `HostAdapter` in script:
153
+
154
+ ```html
155
+ <script>
156
+ const el = document.querySelector('usgb-form')
157
+ el.adapter = {
158
+ host: 'wordpress',
159
+ getTrackingHints() {
160
+ // Cookies, UTMs, click ids — keys must match the package tracking registry
161
+ return {}
162
+ },
163
+ async submit(payload) {
164
+ const res = await fetch('/your-form-submit', {
165
+ method: 'POST',
166
+ headers: { 'Content-Type': 'application/json' },
167
+ body: JSON.stringify(payload)
168
+ })
169
+ if (!res.ok) return { status: 'failed', message: res.statusText }
170
+ return { status: 'accepted' }
171
+ }
172
+ }
173
+
174
+ el.addEventListener('usgb-form:success', (e) => {
175
+ /* e.detail is SubmissionPayload */
176
+ })
177
+ el.addEventListener('usgb-form:failure', (e) => {
178
+ /* e.detail = { payload, message } */
179
+ })
180
+ </script>
181
+ ```
36
182
 
37
- ```json
38
- {
39
- "name": "@your-scope/forms",
40
- "repository": {
41
- "type": "git",
42
- "url": "git+https://github.com/OWNER/REPO.git"
43
- }
183
+ ### Attribute map
184
+
185
+ | Attribute | React prop |
186
+ | ----------------------------------------------- | ------------------------------------------------------------- |
187
+ | `form-id` | `formId` |
188
+ | `appearance` | `appearance` |
189
+ | `variant` | `variants` (space-separated) |
190
+ | `heading` / `body` | `heading` / `body` |
191
+ | `cta` | `ctaLabel` |
192
+ | `source` / `campaign` | `source` / `campaign` |
193
+ | `success-message` / `success-url` | `successMessage` / `successUrl` |
194
+ | `client-agreement-url`, `privacy-policy-url`, … | `legalUrls.*` |
195
+ | `host` | Used only for the stub adapter before you assign `el.adapter` |
196
+
197
+ ## Host adapter
198
+
199
+ ```ts
200
+ interface HostAdapter {
201
+ host: 'pwa' | 'wordpress'
202
+ getTrackingHints(): Partial<Record<TrackingKey, string | undefined>>
203
+ submit(payload: SubmissionPayload): Promise<SubmitResult>
44
204
  }
45
205
  ```
46
206
 
47
- ### 2. Create the npm package (first publish only)
207
+ - `submit` must return `{ status: 'accepted' }` or `{ status: 'failed', message?: string }`.
208
+ - The package never embeds Workato, Magento, or WordPress endpoints.
209
+ - `createStubAdapter('pwa' | 'wordpress')` accepts every payload (playground / markup-only WC).
210
+ - Tracking keys and destination names live in `forms-manifest.json`. Hosts may supply values for registered keys; they must not invent new ones.
48
211
 
49
- Trusted publishers are configured on an existing package. Create the package on [npmjs.com](https://www.npmjs.com/) if it does not exist yet, or publish once with a granular token and then switch to OIDC.
212
+ ### Payload shape (summary)
50
213
 
51
- ### 3. Add a trusted publisher on npmjs.com
214
+ Successful submits send a nested envelope: `formId`, `definitionVersion`, `submissionContractVersion`, `idempotencyKey`, `data` (field values), optional `consent`, and `context` (`host`, `source`, `campaign`, `page`, `tracking`). Exact field and tracking sets come from the form’s catalog entry.
52
215
 
53
- Open the package **Settings → Trusted Publisher**, choose **GitHub Actions**, and enter values that match this repository exactly (all fields are case-sensitive):
216
+ ## Consent and legal URLs
54
217
 
55
- | Field | Value |
56
- | --- | --- |
57
- | Organization or user | `OWNER` |
58
- | Repository | `REPO` |
59
- | Workflow filename | `publish.yml` |
60
- | Environment name | `npm-publish` |
61
- | Allowed actions | `npm publish` |
218
+ Consent bundles are versioned and package-owned (checkboxes and/or clickwrap notices). Hosts only supply document **hrefs**.
62
219
 
63
- Use only the workflow filename (`publish.yml`), not `.github/workflows/publish.yml`. npm validates the top-level workflow that started the run.
220
+ - `main-investors-kit` uses `lead-tcpa` v1 and requires `clientAgreement` and `privacyPolicy`.
221
+ - PWA Magento defaults: `PWA_LEGAL_URLS` → `/content/client-agreement`, `/content/privacy-policy`.
222
+ - WordPress must pass every URL required by the selected consent. A consent form with a missing required URL **does not render**.
223
+ - Checkbox values land in `consent.values` (and mirrored in `data`). Notices have no boolean field; the payload still records consent definition id/version.
224
+ - Distinct legal wording requires a new consent id/version in this package — not freeform copy from the host.
64
225
 
65
- ### 4. Protect the GitHub environment (recommended)
226
+ `LEAD_CLICKWRAP_V1` (“By clicking the button…”) is in the catalog and needs five legal URLs (`privacyPolicy`, `userAgreement`, `marketLossPolicy`, `electronicDisclaimer`, `termsOfSale`). It is not attached to a `formId` until that recipe is chosen.
66
227
 
67
- Create a GitHub environment named `npm-publish` and add required reviewers if you want a human approval step before each release. The environment name in the workflow and on npmjs.com must match.
228
+ ## Success behavior
68
229
 
69
- ### 5. Release
230
+ 1. Adapter returns `accepted`.
231
+ 2. `analytics({ type: 'success' })` and `onAccepted` / `usgb-form:success` run synchronously. The package does not `await` them.
232
+ 3. If `successUrl` / `success-url` is set, the page calls `window.location.assign` immediately after those callbacks return.
233
+ 4. Otherwise the form shows `successMessage` / `success-message`, or a package default thank-you line.
70
234
 
71
- Bump `version` in `package.json` without creating a git tag, then run the publish workflow from the Actions tab.
235
+ ### Redirect vs analytics
72
236
 
73
- ```bash
74
- npm version patch --no-git-tag-version # or minor / major
75
- git add package.json package-lock.json
76
- git commit -m "Release 0.1.1"
77
- git push origin main
78
- ```
237
+ Invocation order is fixed (hooks first, then navigate). Delivery is not. `location.assign` starts unload in the same turn, so a typical GTM / `gtag` / `dataLayer.push` / image pixel started in `analytics` or `onAccepted` can be cancelled before the beacon leaves — especially on Safari and slow networks. Same for async work inside a `usgb-form:success` listener: the CustomEvent is dispatched synchronously, but anything it schedules after return can die with the document.
238
+
239
+ This package does **not** wait on a returned Promise, add a flush timeout, or call `sendBeacon` on the host’s behalf. A hung analytics callback must not trap the user on the submit screen.
240
+
241
+ What is reliable today:
242
+
243
+ - Inline thank-you (`successMessage` only) — no navigation, so tags usually complete.
244
+ - Same-document hash redirects (`#thank-you`) — no unload.
245
+ - Host `analytics` that uses `navigator.sendBeacon` or `fetch(..., { keepalive: true })`.
246
+ - Conversion measured on the thank-you page itself, not on this `success` event.
247
+
248
+ `submit_attempt` is safer than `success` because it fires before `adapter.submit` and has the round-trip to flush. Prefer the thank-you page (or a beacon) when `successUrl` is a full navigation.
249
+
250
+ ## Styling
79
251
 
80
- In GitHub, open **Actions → Publish Package → Run workflow** and run it on `main`. [`.github/workflows/publish.yml`](.github/workflows/publish.yml) then:
252
+ Light DOM with stable `usgb-` class names (not CSS-in-JS). Isolation comes from the prefix and from styling `.usgb-field-control` / `.usgb-btn` instead of bare `input` / `button`. Host theme rules can still win; load host CSS **after** this package’s stylesheet and override under `.usgb-form`.
81
253
 
82
- 1. Requests `id-token: write` so Actions can mint an OIDC token
83
- 2. Installs Node 24 (ships npm 11.x)
84
- 3. Runs tests and the library build
85
- 4. Publishes the version already in `package.json` with **no token secret**
254
+ | Host | How styles load |
255
+ | ------------- | ---------------------------------------------- |
256
+ | React | `import '@usgb/forms/usgb-forms.css'` |
257
+ | Web Component | Injected as `<style id="usgb-forms-css">` once |
86
258
 
87
- npm automatically attaches [provenance](https://docs.npmjs.com/generating-provenance-statements/) for public packages published from a public repository. Do not pass `--provenance`; the CLI enables it when OIDC publishing is in use.
259
+ Prefer **custom properties** on `.usgb-form` for color, type, and spacing:
88
260
 
89
- ### 6. Lock down token publishing (recommended)
261
+ ```css
262
+ .usgb-form {
263
+ --usgb-button-background: #123456;
264
+ --usgb-field-border: #cccccc;
265
+ }
266
+
267
+ .usgb-form[data-appearance='navy'] {
268
+ --usgb-button-background: #f4e08e;
269
+ }
270
+ ```
271
+
272
+ Use class selectors when a token does not exist (`.usgb-form .usgb-btn { border-radius: 0; }`).
90
273
 
91
- After the first OIDC publish succeeds, on the package **Settings → Publishing access** select **Require two-factor authentication and disallow tokens**. Trusted publishing keeps working because it does not use classic npm tokens.
274
+ Stable hooks: `.usgb-form` (`data-appearance`, `data-variant`, `data-kind`), `.usgb-form-heading`, `.usgb-form-body`, `.usgb-form-grid`, `.usgb-form-inline`, `.usgb-field-control`, `.usgb-field-error`, `.usgb-consent-*`, `.usgb-btn`, `.usgb-form-status-*`.
92
275
 
93
- ## Workflow notes
276
+ Semantic tokens (`--usgb-form-surface`, `--usgb-field-*`, `--usgb-button-*`, `--usgb-newsletter-underline`, …) are the first place to retheme. Palette vars (`--usgb-blue-1`, …) are a PWA snapshot. Error and checkbox glyphs are inline SVGs that use `currentColor` — override `--usgb-alert-danger` or `--usgb-checkbox-mark` to recolor them.
94
277
 
95
- - `permissions.id-token: write` is required. Without it the CLI cannot obtain an OIDC token and publish fails with `ENEEDAUTH`.
96
- - Do not set `NODE_AUTH_TOKEN` or `NPM_TOKEN` on the publish step. A leftover token can mask a broken trusted-publisher config.
97
- - `actions/setup-node` must set `registry-url: https://registry.npmjs.org`.
98
- - If this workflow is later called from another workflow, npm still checks the **calling** workflow filename, and both jobs need `id-token: write`.
99
- - Private dependencies still need a **read-only** npm token for `npm ci`. Trusted publishing only authenticates `npm publish`.
278
+ Visual styles are a ported snapshot of the PWA form kit. When PWA form styles change, update this package and bump the version.
279
+
280
+ ## Develop (package maintainers)
281
+
282
+ ```bash
283
+ yarn install
284
+ yarn dev # Vite playground (tries :5174)
285
+ yarn build
286
+ yarn typecheck
287
+ yarn lint
288
+ yarn format
289
+ ```
100
290
 
101
- ## Troubleshooting
291
+ Prettier and ESLint match magento-frontend (4-space indent, single quotes, no semicolons, print width 80).
102
292
 
103
- | Symptom | Likely cause |
104
- | --- | --- |
105
- | `ENEEDAUTH` / unable to authenticate | Workflow filename, repo, or environment does not match the trusted publisher |
106
- | 404 during OIDC exchange | Trusted publisher is missing, or npm CLI is older than 11.5.1 |
107
- | Provenance missing | Repository or package is private |
108
- | `npm ci` fails on private deps | Need a read-only install token; OIDC does not apply to install |
293
+ ## Git
109
294
 
110
- See the [npm trusted publishing docs](https://docs.npmjs.com/trusted-publishers/) for the full provider matrix and security recommendations.
295
+ Local repo only. Connect a GitHub remote later when ready to publish.
@@ -0,0 +1,249 @@
1
+ {
2
+ "schemaVersion": "1.0.0",
3
+ "package": {
4
+ "name": "@usgb/forms",
5
+ "compatiblePackageVersions": "^1.0.0"
6
+ },
7
+ "submissionContract": {
8
+ "version": "1.0.0",
9
+ "transport": "server-proxy",
10
+ "payloadShape": "data-and-context"
11
+ },
12
+ "trackingRegistry": {
13
+ "page_url": {
14
+ "destination": "form_submission_url",
15
+ "category": "page",
16
+ "collection": "server",
17
+ "required": true,
18
+ "states": ["collected", "unavailable"],
19
+ "description": "Canonical landing-page URL at submission time"
20
+ },
21
+ "page_title": {
22
+ "destination": "pageName",
23
+ "category": "page",
24
+ "collection": "server",
25
+ "required": false,
26
+ "states": ["collected", "unavailable"]
27
+ },
28
+ "referrer": {
29
+ "destination": "referrer",
30
+ "category": "page",
31
+ "collection": "browser",
32
+ "required": false,
33
+ "states": ["collected", "unavailable"]
34
+ },
35
+ "first_touch_url": {
36
+ "destination": "crossDomain_landing_url",
37
+ "category": "attribution",
38
+ "collection": "host-adapter",
39
+ "required": false,
40
+ "states": ["collected", "unavailable"]
41
+ },
42
+ "utm_source": {
43
+ "destination": "utm_source",
44
+ "category": "attribution",
45
+ "collection": "host-adapter",
46
+ "required": false,
47
+ "states": ["collected", "unavailable"]
48
+ },
49
+ "utm_medium": {
50
+ "destination": "utm_medium",
51
+ "category": "attribution",
52
+ "collection": "host-adapter",
53
+ "required": false,
54
+ "states": ["collected", "unavailable"]
55
+ },
56
+ "utm_campaign": {
57
+ "destination": "utm_campaign",
58
+ "category": "attribution",
59
+ "collection": "host-adapter",
60
+ "required": false,
61
+ "states": ["collected", "unavailable"]
62
+ },
63
+ "utm_term": {
64
+ "destination": "utm_term",
65
+ "category": "attribution",
66
+ "collection": "host-adapter",
67
+ "required": false,
68
+ "states": ["collected", "unavailable"]
69
+ },
70
+ "utm_content": {
71
+ "destination": "utm_content",
72
+ "category": "attribution",
73
+ "collection": "host-adapter",
74
+ "required": false,
75
+ "states": ["collected", "unavailable"]
76
+ },
77
+ "gclid": {
78
+ "destination": "gclid",
79
+ "category": "click-id",
80
+ "collection": "host-adapter",
81
+ "required": false,
82
+ "states": ["collected", "unavailable"]
83
+ },
84
+ "msclkid": {
85
+ "destination": "msclkid",
86
+ "category": "click-id",
87
+ "collection": "host-adapter",
88
+ "required": false,
89
+ "states": ["collected", "unavailable"]
90
+ },
91
+ "gbraid": {
92
+ "destination": "gbraid",
93
+ "category": "click-id",
94
+ "collection": "host-adapter",
95
+ "required": false,
96
+ "states": ["collected", "unavailable"]
97
+ },
98
+ "wbraid": {
99
+ "destination": "wbraid",
100
+ "category": "click-id",
101
+ "collection": "host-adapter",
102
+ "required": false,
103
+ "states": ["collected", "unavailable"]
104
+ },
105
+ "hubspot_utk": {
106
+ "destination": "hubspot_utk",
107
+ "category": "identity",
108
+ "collection": "host-adapter",
109
+ "required": false,
110
+ "states": ["collected", "unavailable", "consent-withheld"]
111
+ },
112
+ "ga_client_id": {
113
+ "destination": "ga_client_id",
114
+ "category": "analytics",
115
+ "collection": "host-adapter",
116
+ "required": false,
117
+ "states": ["collected", "unavailable", "consent-withheld"]
118
+ },
119
+ "ga_session_id": {
120
+ "destination": "session_id_start",
121
+ "category": "analytics",
122
+ "collection": "host-adapter",
123
+ "required": false,
124
+ "states": ["collected", "unavailable", "consent-withheld"]
125
+ },
126
+ "snowplow_id": {
127
+ "destination": "snowplow_id",
128
+ "category": "analytics",
129
+ "collection": "host-adapter",
130
+ "required": false,
131
+ "states": ["collected", "unavailable", "consent-withheld"]
132
+ },
133
+ "vwo_uuid": {
134
+ "destination": "vwo_uuid",
135
+ "category": "experiment",
136
+ "collection": "host-adapter",
137
+ "required": false,
138
+ "states": ["collected", "unavailable", "consent-withheld"]
139
+ },
140
+ "user_agent": {
141
+ "destination": "header_http_client_details",
142
+ "category": "technical",
143
+ "collection": "server",
144
+ "required": false,
145
+ "states": ["collected", "unavailable"]
146
+ },
147
+ "ip_address": {
148
+ "destination": "ipAddress",
149
+ "category": "technical",
150
+ "collection": "server",
151
+ "required": false,
152
+ "states": ["collected", "unavailable"]
153
+ }
154
+ },
155
+ "forms": {
156
+ "main-investors-kit": {
157
+ "definitionVersion": "1",
158
+ "kind": "lead",
159
+ "flow": { "type": "single" },
160
+ "supportedHosts": ["pwa", "wordpress"],
161
+ "fields": [
162
+ { "name": "firstname", "required": true, "group": "name" },
163
+ { "name": "lastname", "required": true, "group": "name" },
164
+ { "name": "email", "required": true },
165
+ { "name": "phone", "required": false }
166
+ ],
167
+ "trackingFields": [
168
+ "page_url",
169
+ "page_title",
170
+ "referrer",
171
+ "first_touch_url",
172
+ "utm_source",
173
+ "utm_medium",
174
+ "utm_campaign",
175
+ "utm_term",
176
+ "utm_content",
177
+ "gclid",
178
+ "msclkid",
179
+ "gbraid",
180
+ "wbraid",
181
+ "hubspot_utk",
182
+ "ga_client_id",
183
+ "ga_session_id",
184
+ "snowplow_id",
185
+ "vwo_uuid",
186
+ "user_agent",
187
+ "ip_address"
188
+ ],
189
+ "consent": {
190
+ "definitionId": "lead-tcpa",
191
+ "definitionVersion": "1",
192
+ "required": true,
193
+ "submitFields": [
194
+ "tcpa_form_consent_granted",
195
+ "tcpa_form_opt_in_date"
196
+ ]
197
+ },
198
+ "editor": {
199
+ "label": "Main Investors Kit",
200
+ "description": "Standard lead form: name, email, phone, TCPA consent. Use on full-width landing pages and PWA kit/sidebar placements.",
201
+ "whenToUse": "Default kit request. Do not use for affiliate landers or IRA-specific leads.",
202
+ "whenNotToUse": "Affiliate pages (separate definition). IRA kit (separate definition when added).",
203
+ "supportedAppearances": ["light", "navy"],
204
+ "supportedVariants": ["dense", "large", "sidebar", "two-column"]
205
+ }
206
+ },
207
+ "newsletter": {
208
+ "definitionVersion": "1",
209
+ "kind": "newsletter",
210
+ "flow": { "type": "single" },
211
+ "supportedHosts": ["pwa"],
212
+ "fields": [{ "name": "email", "required": true }],
213
+ "trackingFields": [
214
+ "page_url",
215
+ "page_title",
216
+ "referrer",
217
+ "utm_source",
218
+ "utm_medium",
219
+ "utm_campaign",
220
+ "utm_term",
221
+ "utm_content"
222
+ ],
223
+ "editor": {
224
+ "label": "Newsletter",
225
+ "description": "Email-only newsletter signup. No TCPA consent.",
226
+ "whenToUse": "Footer or landing newsletter capture where only an email is required.",
227
+ "whenNotToUse": "Lead kits that need name, phone, or TCPA consent.",
228
+ "supportedAppearances": ["light", "navy"],
229
+ "supportedVariants": []
230
+ }
231
+ }
232
+ },
233
+ "hostAdapterContract": {
234
+ "hosts": ["pwa", "wordpress"],
235
+ "mayProvide": [
236
+ "browser cookies",
237
+ "URL parameters",
238
+ "page and placement context",
239
+ "host/store identity"
240
+ ],
241
+ "mustNotDefine": [
242
+ "new tracking keys",
243
+ "destination property names",
244
+ "consent definitions",
245
+ "form fields",
246
+ "submission endpoints"
247
+ ]
248
+ }
249
+ }