@usgb/forms 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 USGB
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # @usgb/forms
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.
4
+
5
+ ## What's included
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)
11
+
12
+ ## Local development
13
+
14
+ ```bash
15
+ npm install
16
+ npm test
17
+ npm run build
18
+ ```
19
+
20
+ Peer dependencies: `react` and `react-dom` >= 18.
21
+
22
+ ## Publish with OIDC
23
+
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.
25
+
26
+ Requirements:
27
+
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
32
+
33
+ ### 1. Point `package.json` at the real package and repo
34
+
35
+ Update these fields before the first publish. npm checks that `repository.url` matches the GitHub repository that is publishing:
36
+
37
+ ```json
38
+ {
39
+ "name": "@your-scope/forms",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "git+https://github.com/OWNER/REPO.git"
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### 2. Create the npm package (first publish only)
48
+
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.
50
+
51
+ ### 3. Add a trusted publisher on npmjs.com
52
+
53
+ Open the package **Settings → Trusted Publisher**, choose **GitHub Actions**, and enter values that match this repository exactly (all fields are case-sensitive):
54
+
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` |
62
+
63
+ Use only the workflow filename (`publish.yml`), not `.github/workflows/publish.yml`. npm validates the top-level workflow that started the run.
64
+
65
+ ### 4. Protect the GitHub environment (recommended)
66
+
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.
68
+
69
+ ### 5. Release
70
+
71
+ Bump `version` in `package.json` without creating a git tag, then run the publish workflow from the Actions tab.
72
+
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
+ ```
79
+
80
+ In GitHub, open **Actions → Publish Package → Run workflow** and run it on `main`. [`.github/workflows/publish.yml`](.github/workflows/publish.yml) then:
81
+
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**
86
+
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.
88
+
89
+ ### 6. Lock down token publishing (recommended)
90
+
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.
92
+
93
+ ## Workflow notes
94
+
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`.
100
+
101
+ ## Troubleshooting
102
+
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 |
109
+
110
+ See the [npm trusted publishing docs](https://docs.npmjs.com/trusted-publishers/) for the full provider matrix and security recommendations.
package/dist/index.cjs ADDED
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Form: () => Form,
24
+ TextField: () => TextField
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/Form.tsx
29
+ var import_jsx_runtime = require("react/jsx-runtime");
30
+ function Form({ children, ...props }) {
31
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("form", { ...props, children });
32
+ }
33
+
34
+ // src/TextField.tsx
35
+ var import_jsx_runtime2 = require("react/jsx-runtime");
36
+ function TextField({
37
+ label,
38
+ error,
39
+ id,
40
+ name,
41
+ ...props
42
+ }) {
43
+ const inputId = id ?? name;
44
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
45
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { htmlFor: inputId, children: label }),
46
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
47
+ "input",
48
+ {
49
+ id: inputId,
50
+ name,
51
+ ...props,
52
+ "aria-invalid": error ? "true" : void 0,
53
+ "aria-describedby": error && inputId ? `${inputId}-error` : void 0
54
+ }
55
+ ),
56
+ error ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { id: inputId ? `${inputId}-error` : void 0, role: "alert", children: error }) : null
57
+ ] });
58
+ }
59
+ // Annotate the CommonJS export names for ESM import in node:
60
+ 0 && (module.exports = {
61
+ Form,
62
+ TextField
63
+ });
64
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/Form.tsx","../src/TextField.tsx"],"sourcesContent":["export { Form } from \"./Form\";\nexport type { FormProps } from \"./Form\";\n\nexport { TextField } from \"./TextField\";\nexport type { TextFieldProps } from \"./TextField\";\n","import type { FormHTMLAttributes, ReactNode } from \"react\";\n\nexport type FormProps = FormHTMLAttributes<HTMLFormElement> & {\n children: ReactNode;\n};\n\nexport function Form({ children, ...props }: FormProps) {\n return <form {...props}>{children}</form>;\n}\n","import type { InputHTMLAttributes } from \"react\";\n\nexport type TextFieldProps = InputHTMLAttributes<HTMLInputElement> & {\n label: string;\n error?: string;\n};\n\nexport function TextField({\n label,\n error,\n id,\n name,\n ...props\n}: TextFieldProps) {\n const inputId = id ?? name;\n\n return (\n <div>\n <label htmlFor={inputId}>{label}</label>\n <input\n id={inputId}\n name={name}\n {...props}\n aria-invalid={error ? \"true\" : undefined}\n aria-describedby={error && inputId ? `${inputId}-error` : undefined}\n />\n {error ? (\n <p id={inputId ? `${inputId}-error` : undefined} role=\"alert\">\n {error}\n </p>\n ) : null}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACOS;AADF,SAAS,KAAK,EAAE,UAAU,GAAG,MAAM,GAAc;AACtD,SAAO,4CAAC,UAAM,GAAG,OAAQ,UAAS;AACpC;;;ACSI,IAAAA,sBAAA;AAVG,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAmB;AACjB,QAAM,UAAU,MAAM;AAEtB,SACE,8CAAC,SACC;AAAA,iDAAC,WAAM,SAAS,SAAU,iBAAM;AAAA,IAChC;AAAA,MAAC;AAAA;AAAA,QACC,IAAI;AAAA,QACJ;AAAA,QACC,GAAG;AAAA,QACJ,gBAAc,QAAQ,SAAS;AAAA,QAC/B,oBAAkB,SAAS,UAAU,GAAG,OAAO,WAAW;AAAA;AAAA,IAC5D;AAAA,IACC,QACC,6CAAC,OAAE,IAAI,UAAU,GAAG,OAAO,WAAW,QAAW,MAAK,SACnD,iBACH,IACE;AAAA,KACN;AAEJ;","names":["import_jsx_runtime"]}
@@ -0,0 +1,15 @@
1
+ import * as react from 'react';
2
+ import { FormHTMLAttributes, ReactNode, InputHTMLAttributes } from 'react';
3
+
4
+ type FormProps = FormHTMLAttributes<HTMLFormElement> & {
5
+ children: ReactNode;
6
+ };
7
+ declare function Form({ children, ...props }: FormProps): react.JSX.Element;
8
+
9
+ type TextFieldProps = InputHTMLAttributes<HTMLInputElement> & {
10
+ label: string;
11
+ error?: string;
12
+ };
13
+ declare function TextField({ label, error, id, name, ...props }: TextFieldProps): react.JSX.Element;
14
+
15
+ export { Form, type FormProps, TextField, type TextFieldProps };
@@ -0,0 +1,15 @@
1
+ import * as react from 'react';
2
+ import { FormHTMLAttributes, ReactNode, InputHTMLAttributes } from 'react';
3
+
4
+ type FormProps = FormHTMLAttributes<HTMLFormElement> & {
5
+ children: ReactNode;
6
+ };
7
+ declare function Form({ children, ...props }: FormProps): react.JSX.Element;
8
+
9
+ type TextFieldProps = InputHTMLAttributes<HTMLInputElement> & {
10
+ label: string;
11
+ error?: string;
12
+ };
13
+ declare function TextField({ label, error, id, name, ...props }: TextFieldProps): react.JSX.Element;
14
+
15
+ export { Form, type FormProps, TextField, type TextFieldProps };
package/dist/index.js ADDED
@@ -0,0 +1,36 @@
1
+ // src/Form.tsx
2
+ import { jsx } from "react/jsx-runtime";
3
+ function Form({ children, ...props }) {
4
+ return /* @__PURE__ */ jsx("form", { ...props, children });
5
+ }
6
+
7
+ // src/TextField.tsx
8
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
9
+ function TextField({
10
+ label,
11
+ error,
12
+ id,
13
+ name,
14
+ ...props
15
+ }) {
16
+ const inputId = id ?? name;
17
+ return /* @__PURE__ */ jsxs("div", { children: [
18
+ /* @__PURE__ */ jsx2("label", { htmlFor: inputId, children: label }),
19
+ /* @__PURE__ */ jsx2(
20
+ "input",
21
+ {
22
+ id: inputId,
23
+ name,
24
+ ...props,
25
+ "aria-invalid": error ? "true" : void 0,
26
+ "aria-describedby": error && inputId ? `${inputId}-error` : void 0
27
+ }
28
+ ),
29
+ error ? /* @__PURE__ */ jsx2("p", { id: inputId ? `${inputId}-error` : void 0, role: "alert", children: error }) : null
30
+ ] });
31
+ }
32
+ export {
33
+ Form,
34
+ TextField
35
+ };
36
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/Form.tsx","../src/TextField.tsx"],"sourcesContent":["import type { FormHTMLAttributes, ReactNode } from \"react\";\n\nexport type FormProps = FormHTMLAttributes<HTMLFormElement> & {\n children: ReactNode;\n};\n\nexport function Form({ children, ...props }: FormProps) {\n return <form {...props}>{children}</form>;\n}\n","import type { InputHTMLAttributes } from \"react\";\n\nexport type TextFieldProps = InputHTMLAttributes<HTMLInputElement> & {\n label: string;\n error?: string;\n};\n\nexport function TextField({\n label,\n error,\n id,\n name,\n ...props\n}: TextFieldProps) {\n const inputId = id ?? name;\n\n return (\n <div>\n <label htmlFor={inputId}>{label}</label>\n <input\n id={inputId}\n name={name}\n {...props}\n aria-invalid={error ? \"true\" : undefined}\n aria-describedby={error && inputId ? `${inputId}-error` : undefined}\n />\n {error ? (\n <p id={inputId ? `${inputId}-error` : undefined} role=\"alert\">\n {error}\n </p>\n ) : null}\n </div>\n );\n}\n"],"mappings":";AAOS;AADF,SAAS,KAAK,EAAE,UAAU,GAAG,MAAM,GAAc;AACtD,SAAO,oBAAC,UAAM,GAAG,OAAQ,UAAS;AACpC;;;ACSI,SACE,OAAAA,MADF;AAVG,SAAS,UAAU;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAmB;AACjB,QAAM,UAAU,MAAM;AAEtB,SACE,qBAAC,SACC;AAAA,oBAAAA,KAAC,WAAM,SAAS,SAAU,iBAAM;AAAA,IAChC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,IAAI;AAAA,QACJ;AAAA,QACC,GAAG;AAAA,QACJ,gBAAc,QAAQ,SAAS;AAAA,QAC/B,oBAAkB,SAAS,UAAU,GAAG,OAAO,WAAW;AAAA;AAAA,IAC5D;AAAA,IACC,QACC,gBAAAA,KAAC,OAAE,IAAI,UAAU,GAAG,OAAO,WAAW,QAAW,MAAK,SACnD,iBACH,IACE;AAAA,KACN;AAEJ;","names":["jsx"]}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@usgb/forms",
3
+ "version": "0.1.0",
4
+ "description": "Sample React form components published to npm with OIDC trusted publishing.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.cjs",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "sideEffects": false,
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "typecheck": "tsc --noEmit",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "prepublishOnly": "npm run typecheck && npm run test && npm run build"
27
+ },
28
+ "peerDependencies": {
29
+ "react": ">=18",
30
+ "react-dom": ">=18"
31
+ },
32
+ "devDependencies": {
33
+ "@testing-library/jest-dom": "^6.8.0",
34
+ "@testing-library/react": "^16.3.0",
35
+ "@types/react": "^19.1.12",
36
+ "@types/react-dom": "^19.1.9",
37
+ "jsdom": "^26.1.0",
38
+ "react": "^19.1.1",
39
+ "react-dom": "^19.1.1",
40
+ "tsup": "^8.5.0",
41
+ "typescript": "^5.9.2",
42
+ "vitest": "^3.2.4"
43
+ },
44
+ "engines": {
45
+ "node": ">=22.14.0"
46
+ },
47
+ "publishConfig": {
48
+ "access": "public",
49
+ "registry": "https://registry.npmjs.org"
50
+ },
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "git+https://github.com/usgb/forms.git"
54
+ },
55
+ "keywords": [
56
+ "react",
57
+ "forms",
58
+ "oidc",
59
+ "trusted-publishing"
60
+ ]
61
+ }