{"_id":"@bridgenodelabs/firestore-models","_rev":"2-e51babab8717932fff6bbed8c922dc95","name":"@bridgenodelabs/firestore-models","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.1":{"name":"@bridgenodelabs/firestore-models","version":"0.1.1","keywords":["firestore","typescript","firebase","types"],"author":{"name":"Todd Warren","email":"todd@warrenfamily.org"},"license":"MIT","_id":"@bridgenodelabs/firestore-models@0.1.1","maintainers":[{"name":"toddwseattle","email":"todd@warrenfamily.org"}],"homepage":"https://github.com/bridgenodelabs/firestore-models","bugs":{"url":"https://github.com/bridgenodelabs/firestore-models/issues"},"dist":{"shasum":"031edbb8459cfb024993c7a7fc963d0c78eb6b02","tarball":"https://registry.npmjs.org/@bridgenodelabs/firestore-models/-/firestore-models-0.1.1.tgz","fileCount":28,"integrity":"sha512-Z9DctjcvJJbCqM6ZWVzK5+Hg1cNJmSptiJqXHOZmd6AV+tG/ADhCrbzVlhdUHVveWSlYlCVdZgFIAj+NcvJ+nw==","signatures":[{"sig":"MEUCIQCmVDPjLGA7MWDv2jeCcUwHoEIYSHq8+FHo9Vu3TFOtdwIgFiIVQ3DHbiLHBLETL2rX7envk89yO/HBmgAN5EmUDm4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bridgenodelabs%2ffirestore-models@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":111818},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=22.0.0","pnpm":">=9"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./core":{"types":"./dist/core/index.d.ts","import":"./dist/core/index.js"},"./time":{"types":"./dist/time/index.d.ts","import":"./dist/time/index.js"},"./react":{"types":"./dist/react/index.d.ts","import":"./dist/react/index.js"},"./adapters/firebase-admin":{"types":"./dist/adapters/firebase-admin/index.d.ts","import":"./dist/adapters/firebase-admin/index.js"},"./adapters/firebase-client":{"types":"./dist/adapters/firebase-client/index.d.ts","import":"./dist/adapters/firebase-client/index.js"}},"gitHead":"b658110d53e7253c99e0f6f5c7d79c960f83e111","scripts":{"dev":"tsup --watch","lint":"tsc --noEmit","test":"vitest run","build":"tsup","prepare":"husky","prebuild":"node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"","typecheck":"tsc --noEmit","pack:check":"node scripts/verify-package-contents.mjs","test:watch":"vitest","prepublishOnly":"pnpm run lint && pnpm run test && pnpm run build && pnpm run pack:check"},"_npmUser":{"name":"toddwseattle","email":"todd@warrenfamily.org"},"repository":{"url":"git+https://github.com/bridgenodelabs/firestore-models.git","type":"git"},"_npmVersion":"10.9.7","description":"TypeScript types and utilities for Firestore, usable in both browser and Node.js environments","directories":{},"_nodeVersion":"22.22.2","_hasShrinkwrap":false,"packageManager":"pnpm@9.0.0","devDependencies":{"tsup":"^8.0.0","husky":"^9.1.7","jsdom":"^26.1.0","react":"^19.2.5","vitest":"^4.1.3","react-dom":"^19.2.5","typescript":"^6.0.2","@types/react":"^19.1.12","@types/react-dom":"^19.1.9","@testing-library/react":"^16.3.0"},"peerDependencies":{"react":">=18.0.0","firebase":">=10.0.0","firebase-admin":">=12.0.0"},"peerDependenciesMeta":{"react":{"optional":true},"firebase":{"optional":true},"firebase-admin":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/firestore-models_0.1.1_1776347098660_0.3441371583187791","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@bridgenodelabs/firestore-models","version":"0.2.0","description":"TypeScript types and utilities for Firestore, usable in both browser and Node.js environments","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"},"./core":{"import":"./dist/core/index.js","types":"./dist/core/index.d.ts"},"./time":{"import":"./dist/time/index.js","types":"./dist/time/index.d.ts"},"./adapters/firebase-client":{"import":"./dist/adapters/firebase-client/index.js","types":"./dist/adapters/firebase-client/index.d.ts"},"./adapters/firebase-admin":{"import":"./dist/adapters/firebase-admin/index.js","types":"./dist/adapters/firebase-admin/index.d.ts"},"./react":{"import":"./dist/react/index.js","types":"./dist/react/index.d.ts"}},"scripts":{"prebuild":"node -e \"require('fs').rmSync('dist', { recursive: true, force: true })\"","build":"tsup","dev":"tsup --watch","test":"vitest run","test:watch":"vitest","lint":"tsc --noEmit","typecheck":"tsc --noEmit","pack:check":"node scripts/verify-package-contents.mjs","prepublishOnly":"pnpm run lint && pnpm run test && pnpm run build && pnpm run pack:check","prepare":"husky"},"keywords":["firestore","typescript","firebase","types"],"author":{"name":"Todd Warren","email":"todd@warrenfamily.org"},"license":"MIT","repository":{"type":"git","url":"git+https://github.com/bridgenodelabs/firestore-models.git"},"homepage":"https://github.com/bridgenodelabs/firestore-models","bugs":{"url":"https://github.com/bridgenodelabs/firestore-models/issues"},"engines":{"node":">=22.0.0","pnpm":">=9"},"packageManager":"pnpm@9.0.0","devDependencies":{"@testing-library/react":"^16.3.0","@types/react":"^19.1.12","@types/react-dom":"^19.1.9","husky":"^9.1.7","jsdom":"^26.1.0","react":"^19.2.5","react-dom":"^19.2.5","tsup":"^8.0.0","typescript":"^6.0.2","vitest":"^4.1.3"},"peerDependencies":{"firebase":">=10.0.0","firebase-admin":">=12.0.0","react":">=18.0.0"},"peerDependenciesMeta":{"firebase":{"optional":true},"firebase-admin":{"optional":true},"react":{"optional":true}},"_id":"@bridgenodelabs/firestore-models@0.2.0","gitHead":"f094e0be11ff9be7d0b61cb066f4ea87083b0ecf","_nodeVersion":"22.22.2","_npmVersion":"10.9.7","dist":{"integrity":"sha512-auoYNqujaxDAP4dRyWeUbnXF/OjE+Nk2gPxbq/kPUSxZlxslBAB+2iSyejcELNjjy74BEaE6GW9MIrZKMMoXKw==","shasum":"45573ab7b8292761936edd2676bf9449d68b6b55","tarball":"https://registry.npmjs.org/@bridgenodelabs/firestore-models/-/firestore-models-0.2.0.tgz","fileCount":28,"unpackedSize":132843,"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@bridgenodelabs%2ffirestore-models@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEoP/MxApQz6O3UsH1BFr0XMkbhHdrMxZDMfEAcMHeWlAiBBpQP2dfTue2ziDqCTl4pAT0rfMlDlp0/S0Mz5diVoug=="}]},"_npmUser":{"name":"toddwseattle","email":"todd@warrenfamily.org"},"directories":{},"maintainers":[{"name":"toddwseattle","email":"todd@warrenfamily.org"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/firestore-models_0.2.0_1776637812905_0.3284660130423447"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-16T13:44:58.559Z","modified":"2026-04-19T22:30:13.294Z","0.1.1":"2026-04-16T13:44:58.808Z","0.2.0":"2026-04-19T22:30:13.028Z"},"bugs":{"url":"https://github.com/bridgenodelabs/firestore-models/issues"},"author":{"name":"Todd Warren","email":"todd@warrenfamily.org"},"license":"MIT","homepage":"https://github.com/bridgenodelabs/firestore-models","keywords":["firestore","typescript","firebase","types"],"repository":{"type":"git","url":"git+https://github.com/bridgenodelabs/firestore-models.git"},"description":"TypeScript types and utilities for Firestore, usable in both browser and Node.js environments","maintainers":[{"name":"toddwseattle","email":"todd@warrenfamily.org"}],"readme":"# firestore-models\n\nTypeScript utilities for defining Firestore-backed models with explicit persisted shapes, schema-versioned migrations, runtime validation hooks, and timestamp normalization.\n\nThe library keeps your application-facing domain types separate from the objects you actually store in Firestore.\n\nRepository: https://github.com/bridgenodelabs/firestore-models\n\nPackage: `@bridgenodelabs/firestore-models`\n\n## What it does\n\n- Separates domain objects from persisted Firestore documents\n- Requires `schemaVersion` on persisted documents\n- Migrates older document versions on read\n- Supports optional runtime validation before migration/hydration\n- Normalizes timestamp-like values without importing Firebase types into core code\n- Provides optional adapters for Firebase Web SDK and Firebase Admin SDK\n- Provides an optional React hooks subpath for Firebase Web SDK subscriptions and mutations\n\n## Install\n\nInstall the library:\n\n```bash\npnpm add @bridgenodelabs/firestore-models\n```\n\nIf you want to use one of the Firebase adapters, also install the relevant peer dependency:\n\n```bash\npnpm add firebase\n```\n\n```bash\npnpm add firebase-admin\n```\n\nIf you want to use the optional React hooks subpath, install React as well:\n\n```bash\npnpm add react\n```\n\n## Using the library\n\nUse the core package to define pure model logic, then choose the adapter that matches the Firestore runtime you use:\n\n- `@bridgenodelabs/firestore-models/core` for model definitions, migrations, read hydration, and validators\n- `@bridgenodelabs/firestore-models/time` for SDK-independent timestamp conversion\n- `@bridgenodelabs/firestore-models/adapters/firebase-client` for Firebase Web SDK snapshots\n- `@bridgenodelabs/firestore-models/adapters/firebase-admin` for Firebase Admin SDK snapshots\n- `@bridgenodelabs/firestore-models/react` for optional Firebase Web SDK React subscriptions and mutations\n\nThe library does not own your Firestore collection paths, Firebase app initialization, security rules, or write orchestration. It gives you a typed model boundary so application code can work with domain objects while Firestore stores explicit, versioned persisted shapes.\n\n### Core idea\n\nModel definitions work with two shapes:\n\n- `Domain`: the object your application uses in memory\n- `Persisted`: the object stored in Firestore, including `schemaVersion`\n\nYou define how to convert between them, and the library handles validation and migration-on-read.\n\n### Quick start\n\nThis example stores a `Task` with a versioned persisted shape.\n\n```ts\nimport {\n  defineModel,\n  readDomain,\n  type PersistedBase,\n} from \"@bridgenodelabs/firestore-models/core\";\n\ninterface Task {\n  title: string;\n  done: boolean;\n}\n\ninterface TaskDocument extends PersistedBase {\n  schemaVersion: 1;\n  title: string;\n  done: boolean;\n}\n\nconst taskModel = defineModel<Task, TaskDocument>({\n  currentVersion: 1,\n  toPersisted: (task) => ({\n    schemaVersion: 1,\n    title: task.title,\n    done: task.done,\n  }),\n  fromPersisted: (doc) => ({\n    title: doc.title,\n    done: doc.done,\n  }),\n});\n\nconst raw = {\n  schemaVersion: 1,\n  title: \"Ship README\",\n  done: false,\n};\n\nconst task = readDomain(raw, taskModel);\n// { title: 'Ship README', done: false }\n```\n\n### Migration on read\n\nIf older documents are still in Firestore, define migrations keyed by the source version.\n\n```ts\nimport {\n  defineModel,\n  readDomain,\n  type PersistedBase,\n} from \"@bridgenodelabs/firestore-models/core\";\n\ninterface Task {\n  title: string;\n  done: boolean;\n}\n\ninterface TaskDocumentV0 extends PersistedBase {\n  schemaVersion: 0;\n  title: string;\n}\n\ninterface TaskDocumentV1 extends PersistedBase {\n  schemaVersion: 1;\n  title: string;\n  done: boolean;\n}\n\nconst taskModel = defineModel<Task, TaskDocumentV1>({\n  currentVersion: 1,\n  toPersisted: (task) => ({\n    schemaVersion: 1,\n    title: task.title,\n    done: task.done,\n  }),\n  fromPersisted: (doc) => ({\n    title: doc.title,\n    done: doc.done,\n  }),\n  migrations: {\n    0: (doc: TaskDocumentV0): TaskDocumentV1 => ({\n      schemaVersion: 1,\n      title: doc.title,\n      done: false,\n    }),\n  },\n});\n\nconst oldDoc = {\n  schemaVersion: 0,\n  title: \"Imported task\",\n};\n\nconst task = readDomain(oldDoc, taskModel);\n// { title: 'Imported task', done: false }\n```\n\nThe read flow is:\n\n1. Validate the raw value if `validatePersisted` is provided\n2. Assert that `schemaVersion` exists and is valid\n3. Apply migrations in order until `currentVersion`\n4. Convert the latest persisted shape into the domain object\n\n### Validation hook\n\nYou can run runtime validation before migration and hydration.\n\n```ts\nimport {\n  assertNumber,\n  assertObject,\n  createValidator,\n  defineModel,\n  readDomain,\n  type PersistedBase,\n} from \"@bridgenodelabs/firestore-models/core\";\n\ninterface Counter {\n  value: number;\n}\n\ninterface CounterDocument extends PersistedBase {\n  schemaVersion: 1;\n  value: number;\n}\n\nconst validateCounter = createValidator<CounterDocument>((value) => {\n  assertObject(value, \"Counter document must be an object\");\n  assertNumber(value.schemaVersion, \"schemaVersion must be a number\");\n  assertNumber(value.value, \"value must be a number\");\n});\n\nconst counterModel = defineModel<Counter, CounterDocument>({\n  currentVersion: 1,\n  validatePersisted: validateCounter,\n  toPersisted: (counter) => ({\n    schemaVersion: 1,\n    value: counter.value,\n  }),\n  fromPersisted: (doc) => ({\n    value: doc.value,\n  }),\n});\n\nconst counter = readDomain({ schemaVersion: 1, value: 42 }, counterModel);\n```\n\n### Timestamps without Firebase imports in core models\n\nThe `time` module accepts a duck-typed timestamp shape, so your model definitions do not need to import Firebase SDK types.\n\n```ts\nimport {\n  defineModel,\n  type PersistedBase,\n  type ToTimestamp,\n} from \"@bridgenodelabs/firestore-models/core\";\nimport {\n  dateFromTimestamp,\n  type TimestampLike,\n} from \"@bridgenodelabs/firestore-models/time\";\n\ninterface Task {\n  title: string;\n  createdAt: Date;\n}\n\ninterface TaskDocument extends PersistedBase {\n  schemaVersion: 1;\n  title: string;\n  createdAt: TimestampLike;\n}\n\nconst taskModel = defineModel<Task, TaskDocument>({\n  currentVersion: 1,\n  toPersisted: (task, toTimestamp?: ToTimestamp) => {\n    if (!toTimestamp) {\n      throw new Error(\"toTimestamp is required when persisting Task.createdAt\");\n    }\n\n    return {\n      schemaVersion: 1,\n      title: task.title,\n      createdAt: toTimestamp(task.createdAt),\n    };\n  },\n  fromPersisted: (doc) => ({\n    title: doc.title,\n    createdAt: dateFromTimestamp(doc.createdAt),\n  }),\n});\n```\n\nAt the Firestore boundary, pass the SDK's timestamp factory:\n\n```ts\nimport { Timestamp } from \"firebase/firestore\";\n\nconst persisted = taskModel.toPersisted(\n  { title: \"Ship release\", createdAt: new Date() },\n  Timestamp.fromDate,\n);\n```\n\nYou can also use the helper directly:\n\n```ts\nimport { timestampFromDate } from \"@bridgenodelabs/firestore-models/time\";\nimport { Timestamp } from \"firebase/firestore\";\n\nconst timestamp = timestampFromDate(new Date(), Timestamp.fromDate);\n```\n\n### Firebase Web SDK example\n\nUse the Web adapter to read a `DocumentSnapshot` and run the full migration-on-read flow.\n\n```ts\nimport { doc, getDoc, Timestamp } from \"firebase/firestore\";\nimport { readDocumentDomain } from \"@bridgenodelabs/firestore-models/adapters/firebase-client\";\n\nconst snapshot = await getDoc(doc(db, \"tasks/task-1\"));\n\nconst task = readDocumentDomain(snapshot, taskModel);\n\nconst persisted = taskModel.toPersisted(\n  { title: \"Client write\", createdAt: new Date() },\n  Timestamp.fromDate,\n);\n```\n\nIf you only want typed snapshot wrappers, the adapter also exports `toTypedSnapshot` and `toTypedQuerySnapshot`.\n\n### Firebase Admin SDK example\n\nUse the Admin adapter the same way on the server.\n\n```ts\nimport { getFirestore, Timestamp } from \"firebase-admin/firestore\";\nimport { readDocumentDomain } from \"@bridgenodelabs/firestore-models/adapters/firebase-admin\";\n\nconst snapshot = await getFirestore().doc(\"tasks/task-1\").get();\n\nconst task = readDocumentDomain(snapshot, taskModel);\n\nconst persisted = taskModel.toPersisted(\n  { title: \"Server write\", createdAt: new Date() },\n  Timestamp.fromDate,\n);\n```\n\n### Optional React hooks example\n\nUse the optional `@bridgenodelabs/firestore-models/react` subpath to compose migration-on-read and model-aware writes in client apps.\n\n```ts\nimport { query } from \"firebase/firestore\";\nimport {\n  useFirestoreCollectionDomain,\n  useFirestoreMutations,\n} from \"@bridgenodelabs/firestore-models/react\";\n\nconst {\n  documents: tasks,\n  loading,\n  error,\n} = useFirestoreCollectionDomain({\n  source: query(tasksCollection),\n  model: taskModel,\n});\n\nconst { create, updateById, deleteById } = useFirestoreMutations({\n  collection: tasksCollection,\n  model: taskModel,\n});\n\nawait create({\n  title: \"Ship docs\",\n  done: false,\n  dueAt: new Date(),\n  priority: \"high\",\n});\n\nawait updateById(\"task-1\", { done: true });\nawait deleteById(\"task-1\");\n```\n\nPreferred write lanes:\n\n- full domain writes: `create(...)`, `setDocumentDomain(...)`, or `model.toPersisted(...)`\n- partial domain updates: `updateById(...)` or `updateDocumentDomain(...)` with `toPartialPersisted(...)`\n- raw persisted escape hatches: `setPersistedById(...)`, `updatePersistedById(...)`, or persisted adapter helpers when you intentionally want Firestore-shaped data\n\n## Main exports\n\nTop-level package:\n\n```ts\nimport { core, time, getDocumentData } from \"@bridgenodelabs/firestore-models\";\n```\n\nFocused subpath imports:\n\n```ts\nimport { defineModel, readDomain } from \"@bridgenodelabs/firestore-models/core\";\nimport {\n  dateFromTimestamp,\n  timestampFromDate,\n} from \"@bridgenodelabs/firestore-models/time\";\nimport { readDocumentDomain } from \"@bridgenodelabs/firestore-models/adapters/firebase-client\";\nimport { readDocumentDomain as readAdminDocumentDomain } from \"@bridgenodelabs/firestore-models/adapters/firebase-admin\";\nimport {\n  useFirestoreCollectionDomain,\n  useFirestoreMutations,\n} from \"@bridgenodelabs/firestore-models/react\";\n```\n\n## Claude Code agents\n\nThe package ships two [Claude Code](https://claude.ai/code) subagent definitions in the `agents/` directory. Once installed they automate the most repetitive parts of adopting the library.\n\n| File                                   | What it does                                                                                                                                                                                                                        |\n| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `agents/persistInterfaceToFirebase.md` | Give it a TypeScript interface and it scaffolds the full `@bridgenodelabs/firestore-models` model: persisted type, validator, `defineModel` with `toPersisted`/`fromPersisted`, and a Firebase Web SDK usage snippet                |\n| `agents/createFirestoreHooks.md`       | Give it an existing model and collection reference and it generates typed React hooks (`use<Name>List`, `use<Name>Document`) that compose `useFirestoreCollectionDomain`, `useFirestoreDocumentDomain`, and `useFirestoreMutations` |\n\n### Using the agents\n\nCopy the agent files you want into your project's `.claude/agents/` directory:\n\n```bash\nmkdir -p .claude/agents\ncp node_modules/@bridgenodelabs/firestore-models/agents/persistInterfaceToFirebase.md .claude/agents/\ncp node_modules/@bridgenodelabs/firestore-models/agents/createFirestoreHooks.md .claude/agents/\n```\n\nClaude Code picks up any `.md` files in `.claude/agents/` automatically. The agents will then appear when Claude decides the task matches their description, or you can invoke them explicitly:\n\n```\nUse the persistInterfaceToFirebase agent to scaffold a model for this interface:\n\ninterface Product {\n  name: string;\n  price: number;\n  createdAt: Date;\n}\n```\n\n```\nUse the createFirestoreHooks agent to generate React hooks for productModel\nusing the productsCollection reference in src/lib/firestore.ts.\n```\n\n## Quality gates\n\nThis repository includes local and remote quality gates so publishable changes are checked before they land.\n\n### Pre-commit hooks\n\nHusky is configured through the `prepare` script and installs two hooks:\n\n- `pre-commit`: runs `pnpm run lint && pnpm run typecheck`\n- `pre-push`: runs `pnpm run test && pnpm run build`\n\nThat means type errors are blocked before commit, and tests plus package build are blocked before push.\n\n### CI and publish automation\n\nGitHub Actions adds the same checks in automation:\n\n- CI runs on pull requests and pushes to `main`\n- CI runs `lint`, `typecheck`, `test`, `build`, and `npm pack --dry-run`\n- Publish runs on version tags like `v0.1.0`\n- Publish re-runs verification before `npm publish --access public --provenance`\n\nThe publish workflow is intended for the repository at `bridgenodelabs/firestore-models`.\n\n## Creating a pull request\n\nUse Node.js 22 or later and pnpm 9 or later. From a fresh checkout:\n\n```bash\npnpm install\npnpm run lint\npnpm run typecheck\npnpm run test\npnpm run build\npnpm run pack:check\n```\n\nBefore opening a PR:\n\n1. Keep changes scoped to the model, adapter, sample, or documentation behavior being changed.\n2. Add or update tests when changing runtime behavior.\n3. Update README, `docs/user-guide.md`, or sample READMEs when public usage changes.\n4. Run the checks above locally. CI runs the same lint, typecheck, test, build, and package-content checks on pull requests.\n5. For sample changes, run the affected sample command as well, such as `pnpm --dir samples/shared run check`, `pnpm --dir samples/web-app typecheck`, or `pnpm --dir samples/project-task-sample typecheck`.\n\nPRs should describe the user-visible behavior change, the verification performed, and any follow-up work intentionally left out.\n\n## Development\n\n```bash\npnpm install\npnpm test\npnpm build\n```\n\nSee `docs/user-guide.md` for the longer usage guide and `docs/firestore-object-toolkit-design.md` for the design overview.\n\nSample projects:\n\n- `samples/shared`: shared Task model with migration, full-write conversion, and partial-update conversion\n- `samples/web-app`: runnable React + Vite Firebase Emulator sample using `create(...)` for full domain writes and `updateById(...)` for partial domain updates\n- `samples/project-task-sample`: CLI runner demonstrating model-owned conversion plus transactional writes to a nested `projects/{projectId}/tasks` subcollection\n\nDocumentation note: several files under `docs/` are historical planning or design-capture artifacts rather than current user guides. Keep `README.md`, `docs/user-guide.md`, and the sample READMEs as the primary documentation surface.\n","readmeFilename":"README.md"}