npm Publishing & CI/CD Pipeline for @coditect Packages
Executive Summary
This document establishes the complete technical infrastructure for publishing and maintaining two reusable npm packages extracted from the BIO-QMS platform:
@coditect/doc-viewer: React component library for document rendering with TypeScript, Vite, and modern build tooling@coditect/create-doc-site: CLI scaffolding tool for bootstrapping document sites
The publishing strategy implements:
- Semantic versioning with automated version management
- GitHub Actions CI/CD for automated testing, building, and publishing
- Quality gates enforcing code standards, test coverage, and bundle size budgets
- Dual registry support (GitHub Packages for private distribution, npm public for open-source)
- Provenance attestation for supply chain security
- Automated changelog generation and release notes
Table of Contents
- Registry Strategy
- Semantic Versioning Policy
- Package Configuration
- Build Pipeline
- GitHub Actions CI/CD
- Quality Gates
- Release Process
- README Generation
- Monitoring & Analytics
- Complete Configuration Files
1. Registry Strategy
1.1 Registry Options
| Registry | Use Case | Pros | Cons |
|---|---|---|---|
| GitHub Packages | Private enterprise distribution | - Free for private repos - Seamless GitHub integration - Fine-grained access control - No external account needed | - Requires GitHub authentication - Less discoverable - npm CLI config needed |
| npm Public | Open-source distribution | - Maximum discoverability - Zero auth for install - Built-in analytics - Community trust | - Public code visibility - Namespace squatting risk - Less access control |
| npm Private | Paid enterprise distribution | - Private + discoverable - npm CLI native - Team management | - $7/user/month - External billing |
1.2 Decision Matrix
Recommended Strategy: Dual-Registry Phased Approach
| Phase | Registry | Packages | Rationale |
|---|---|---|---|
| Phase 1: Internal | GitHub Packages | Both packages (private) | - Validate with BIO-QMS integration - Iterate on API stability - Enterprise clients only |
| Phase 2: Public Beta | npm Public | @coditect/doc-viewer only | - Open-source component library - Community feedback - Maintain private CLI tool |
| Phase 3: Full OSS | npm Public | Both packages | - Complete open-source release - Maximum adoption - Community contributions |
1.3 GitHub Packages Configuration
Organization Scope: Packages published to @coditect scope under github.com/coditect-ai organization.
.npmrc Configuration (Consumer Side)
# User-level ~/.npmrc or project-level .npmrc
@coditect:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
Environment Variables:
GITHUB_TOKEN: Personal Access Token (PAT) withread:packagesscope- For CI/CD: Use repository
GITHUB_TOKENsecret (auto-provided)
1.4 npm Public Configuration
.npmrc Configuration (Publisher Side)
# Project-level .npmrc (committed to repo)
@coditect:registry=https://registry.npmjs.org
# User-level ~/.npmrc (DO NOT commit)
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
npm Account Setup:
- Create organization:
npm org:set coditect - Add maintainers:
npm owner add <username> @coditect/doc-viewer - Generate automation token:
npm token create --type automation - Store as GitHub secret:
NPM_TOKEN
1.5 Authentication Token Management
Security Best Practices:
| Token Type | Scope | Storage | Rotation |
|---|---|---|---|
| GitHub PAT (Personal) | read:packages, write:packages | GitHub org secrets | 90 days |
| npm Automation Token | Publish-only | GitHub repo secrets | 365 days |
| npm Publish Token | Publish + admin | Never in CI/CD | N/A |
GitHub Actions Secret Configuration:
# GitHub Packages (auto-provided in workflows)
secrets.GITHUB_TOKEN
# npm Public (manual setup)
secrets.NPM_TOKEN
Local Development:
# Never commit tokens to .npmrc!
# Use environment variables instead
echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" >> ~/.npmrc
# Or use npm CLI
npm login --scope=@coditect --registry=https://registry.npmjs.org
2. Semantic Versioning Policy
2.1 SemVer Principles
Version Format: MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]
1.2.3-beta.4+20260216
│ │ │ │ │ └─ Build metadata (optional, ignored by npm)
│ │ │ │ └─── Pre-release version
│ │ │ └────────── Pre-release identifier
│ │ └───────────── Patch: Backward-compatible bug fixes
│ └─────────────── Minor: Backward-compatible new features
└───────────────── Major: Breaking changes
2.2 Version Bump Rules for @coditect/doc-viewer
Component Library Versioning:
| Change Type | Version Bump | Examples |
|---|---|---|
| Breaking Changes | MAJOR | - Remove/rename public component - Change prop types (required → optional) - Remove CSS class names - Drop Node.js version support - Change peer dependency requirements |
| New Features | MINOR | - Add new component - Add optional props to existing component - Add new CSS variables - Add new exports to index - Deprecate (but not remove) APIs |
| Bug Fixes | PATCH | - Fix rendering bugs - Fix TypeScript types - Update documentation - Refactor internals (no API changes) - Performance improvements |
API Stability Guarantees:
// MAJOR breaking change: required prop added
interface DocViewerProps {
document: Document;
theme?: Theme;
onLoad: () => void; // ← Breaking: was optional, now required
}
// MINOR new feature: optional prop added
interface DocViewerProps {
document: Document;
theme?: Theme;
experimental_lazyLoad?: boolean; // ← Non-breaking: new optional prop
}
// PATCH bug fix: internal implementation change
// No public API changes, just fix rendering issue
2.3 Version Bump Rules for @coditect/create-doc-site
CLI Tool Versioning:
| Change Type | Version Bump | Examples |
|---|---|---|
| Breaking Changes | MAJOR | - Remove CLI flags - Change flag behavior (--flag now does X not Y) - Change default template structure - Require new environment variables - Drop Node.js version support |
| New Features | MINOR | - Add new CLI flags (optional) - Add new templates - Add new interactive prompts - Improve error messages |
| Bug Fixes | PATCH | - Fix template generation bugs - Fix file permissions - Update dependencies - Fix typos in help text |
2.4 Pre-Release Versions
Pre-Release Identifiers:
| Identifier | Stage | Stability | Usage |
|---|---|---|---|
alpha | Early testing | Unstable API | 1.0.0-alpha.1 |
beta | Feature complete | Mostly stable | 1.0.0-beta.3 |
rc | Release candidate | Production-ready | 1.0.0-rc.2 |
next | Canary/nightly | Latest main branch | 1.1.0-next.20260216 |
Publishing Workflow:
# Alpha: Internal testing
npm version prerelease --preid=alpha # 1.0.0-alpha.1
npm publish --tag alpha
# Beta: External testing
npm version prerelease --preid=beta # 1.0.0-beta.1
npm publish --tag beta
# Release Candidate: Final validation
npm version prerelease --preid=rc # 1.0.0-rc.1
npm publish --tag next
# Stable Release
npm version minor # 1.0.0
npm publish --tag latest
Consumer Installation:
# Stable release (default)
npm install @coditect/doc-viewer
# Specific pre-release
npm install @coditect/doc-viewer@1.0.0-beta.3
# Latest beta
npm install @coditect/doc-viewer@beta
# Latest alpha
npm install @coditect/doc-viewer@alpha
2.5 Version Range Recommendations
For Consumers (in package.json dependencies):
| Dependency Type | Range | Example | Rationale |
|---|---|---|---|
| Production Stable | Caret ^ | ^1.2.3 | Allow minor + patch updates |
| Production Conservative | Tilde ~ | ~1.2.3 | Allow patch updates only |
| Production Strict | Exact | 1.2.3 | No automatic updates |
| Development | Caret ^ | ^1.2.3 | Allow minor + patch updates |
| Peer Dependencies | Wide range | `^16.0.0 |
Examples:
{
"dependencies": {
"@coditect/doc-viewer": "^1.0.0"
},
"peerDependencies": {
"react": "^16.14.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0"
}
}
2.6 Automated Version Management
Option 1: Changesets (Recommended)
Changesets provide granular version control with manual changeset creation and automatic version bumping.
# Install
npm install -D @changesets/cli
npx changeset init
# Developer workflow: Create changeset for each PR
npx changeset
# Prompts:
# - Which packages to bump? @coditect/doc-viewer
# - What type of change? minor
# - Summary: Add lazy loading support
# CI/CD: Automated version bump and publish
npx changeset version # Updates package.json and CHANGELOG.md
npx changeset publish # Publishes to registry
.changeset/config.json:
{
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
Option 2: Conventional Commits + semantic-release
Fully automated versioning based on commit message conventions.
# Install
npm install -D semantic-release @semantic-release/git @semantic-release/github
# Commit message format
# feat: add lazy loading support → MINOR bump
# fix: resolve rendering issue → PATCH bump
# feat!: remove deprecated API → MAJOR bump
# chore: update dependencies → No version bump
.releaserc.json:
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
"@semantic-release/github",
[
"@semantic-release/git",
{
"assets": ["package.json", "CHANGELOG.md"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
}
Recommendation: Changesets for component library (manual control over breaking changes), semantic-release for CLI tool (fully automated).
3. Package Configuration
3.1 @coditect/doc-viewer package.json
Complete Configuration:
{
"name": "@coditect/doc-viewer",
"version": "1.0.0",
"description": "React component library for rendering technical documents with section navigation, table of contents, and responsive layouts",
"author": "AZ1.AI INC <support@coditect.ai>",
"license": "MIT",
"homepage": "https://github.com/coditect-ai/bio-qms#readme",
"repository": {
"type": "git",
"url": "https://github.com/coditect-ai/bio-qms.git",
"directory": "packages/doc-viewer"
},
"bugs": {
"url": "https://github.com/coditect-ai/bio-qms/issues"
},
"keywords": [
"react",
"documentation",
"viewer",
"component",
"markdown",
"toc",
"navigation",
"responsive",
"typescript"
],
"type": "module",
"main": "./dist/index.umd.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.umd.cjs"
}
},
"./style.css": "./dist/style.css"
},
"files": [
"dist",
"README.md",
"LICENSE",
"CHANGELOG.md"
],
"sideEffects": [
"*.css"
],
"engines": {
"node": ">=18.0.0",
"npm": ">=9.0.0"
},
"peerDependencies": {
"react": "^16.14.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": false
},
"react-dom": {
"optional": false
}
},
"dependencies": {
"clsx": "^2.1.0"
},
"devDependencies": {
"@changesets/cli": "^2.27.1",
"@types/node": "^20.11.5",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"@typescript-eslint/eslint-plugin": "^6.19.0",
"@typescript-eslint/parser": "^6.19.0",
"@vitejs/plugin-react": "^4.2.1",
"@vitest/ui": "^1.2.1",
"eslint": "^8.56.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"typescript": "^5.3.3",
"vite": "^5.0.11",
"vite-plugin-dts": "^3.7.2",
"vitest": "^1.2.1"
},
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"test": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest --coverage",
"type-check": "tsc --noEmit",
"prepublishOnly": "npm run lint && npm run test && npm run build",
"changeset": "changeset",
"version": "changeset version",
"release": "changeset publish"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org",
"provenance": true
}
}
Key Fields Explained:
| Field | Purpose | Example Value |
|---|---|---|
name | Package identifier | @coditect/doc-viewer |
version | Current version | 1.0.0 |
type | Module system | module (ESM) |
main | CJS entry point | ./dist/index.umd.cjs |
module | ESM entry point | ./dist/index.js |
types | TypeScript declarations | ./dist/index.d.ts |
exports | Node.js subpath exports | Conditional exports for ESM/CJS |
files | What gets published | ["dist", "README.md"] |
sideEffects | Webpack tree-shaking hint | ["*.css"] |
engines | Node.js version requirement | >=18.0.0 |
peerDependencies | Host app must provide | react, react-dom |
publishConfig | npm publish options | access: public |
3.2 @coditect/create-doc-site package.json
Complete Configuration:
{
"name": "@coditect/create-doc-site",
"version": "1.0.0",
"description": "CLI tool to scaffold document sites powered by @coditect/doc-viewer",
"author": "AZ1.AI INC <support@coditect.ai>",
"license": "MIT",
"homepage": "https://github.com/coditect-ai/bio-qms#readme",
"repository": {
"type": "git",
"url": "https://github.com/coditect-ai/bio-qms.git",
"directory": "packages/create-doc-site"
},
"bugs": {
"url": "https://github.com/coditect-ai/bio-qms/issues"
},
"keywords": [
"create",
"scaffold",
"cli",
"documentation",
"site",
"generator",
"vite",
"react"
],
"type": "module",
"bin": {
"create-doc-site": "./dist/index.js"
},
"files": [
"dist",
"templates",
"README.md",
"LICENSE"
],
"engines": {
"node": ">=18.0.0",
"npm": ">=9.0.0"
},
"dependencies": {
"commander": "^11.1.0",
"enquirer": "^2.4.1",
"fs-extra": "^11.2.0",
"picocolors": "^1.0.0"
},
"devDependencies": {
"@changesets/cli": "^2.27.1",
"@types/fs-extra": "^11.0.4",
"@types/node": "^20.11.5",
"@typescript-eslint/eslint-plugin": "^6.19.0",
"@typescript-eslint/parser": "^6.19.0",
"eslint": "^8.56.0",
"tsx": "^4.7.0",
"typescript": "^5.3.3",
"vitest": "^1.2.1"
},
"scripts": {
"dev": "tsx src/index.ts",
"build": "tsc",
"lint": "eslint . --ext ts --report-unused-disable-directives --max-warnings 0",
"test": "vitest",
"type-check": "tsc --noEmit",
"prepublishOnly": "npm run lint && npm run test && npm run build",
"changeset": "changeset",
"version": "changeset version",
"release": "changeset publish"
},
"publishConfig": {
"access": "public",
"registry": "https://registry.npmjs.org",
"provenance": true
}
}
CLI-Specific Fields:
| Field | Purpose | Example Value |
|---|---|---|
bin | Executable entry point | {"create-doc-site": "./dist/index.js"} |
files | Include templates directory | ["dist", "templates"] |
dependencies | Runtime CLI dependencies | commander, enquirer |
Shebang in Entry Point (dist/index.js):
#!/usr/bin/env node
import { Command } from 'commander';
// ... rest of CLI implementation
3.3 Monorepo Package Management (Optional)
If both packages live in a monorepo (recommended):
Root package.json:
{
"name": "@coditect/packages",
"private": true,
"workspaces": [
"packages/*"
],
"scripts": {
"build": "npm run build --workspaces",
"test": "npm run test --workspaces",
"lint": "npm run lint --workspaces",
"changeset": "changeset",
"version": "changeset version",
"release": "npm run build && changeset publish"
},
"devDependencies": {
"@changesets/cli": "^2.27.1"
}
}
Directory Structure:
packages/
├── doc-viewer/
│ ├── src/
│ ├── package.json
│ └── vite.config.ts
├── create-doc-site/
│ ├── src/
│ ├── templates/
│ └── package.json
└── package.json (root)
Changesets Linked Versioning:
If both packages should always have the same version:
// .changeset/config.json
{
"linked": [
["@coditect/doc-viewer", "@coditect/create-doc-site"]
]
}
4. Build Pipeline
4.1 Vite Library Mode Configuration (@coditect/doc-viewer)
vite.config.ts:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import dts from 'vite-plugin-dts';
import { resolve } from 'path';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig(({ mode }) => ({
plugins: [
react(),
dts({
include: ['src/**/*.ts', 'src/**/*.tsx'],
exclude: ['src/**/*.test.ts', 'src/**/*.test.tsx'],
rollupTypes: true, // Bundle all .d.ts into single file
}),
mode === 'analyze' &&
visualizer({
open: true,
gzipSize: true,
brotliSize: true,
}),
].filter(Boolean),
build: {
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'CoditectDocViewer',
formats: ['es', 'umd'],
fileName: (format) => {
if (format === 'es') return 'index.js';
if (format === 'umd') return 'index.umd.cjs';
return `index.${format}.js`;
},
},
rollupOptions: {
external: ['react', 'react-dom', 'react/jsx-runtime'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM',
'react/jsx-runtime': 'jsxRuntime',
},
assetFileNames: (assetInfo) => {
if (assetInfo.name === 'style.css') return 'style.css';
return assetInfo.name;
},
},
},
sourcemap: mode === 'production' ? false : true,
minify: mode === 'production' ? 'esbuild' : false,
cssCodeSplit: false, // Single CSS file
emptyOutDir: true,
},
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/test/setup.ts',
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html'],
exclude: [
'node_modules/',
'src/test/',
'**/*.test.{ts,tsx}',
'**/*.config.{ts,js}',
],
},
},
}));
Key Configuration Sections:
| Section | Purpose | Key Options |
|---|---|---|
build.lib | Library mode entry | entry, name, formats |
rollupOptions.external | Exclude peer deps from bundle | react, react-dom |
rollupOptions.output.globals | UMD global variable names | React, ReactDOM |
sourcemap | Source maps for debugging | true (dev), false (prod) |
minify | Minification strategy | esbuild (fast), terser (smaller) |
cssCodeSplit | CSS bundling strategy | false (single file) |
Build Output Structure:
dist/
├── index.js # ESM bundle
├── index.d.ts # TypeScript declarations (bundled)
├── index.umd.cjs # UMD bundle (legacy)
└── style.css # Bundled styles
4.2 TypeScript Configuration
tsconfig.json (for library):
{
"compilerOptions": {
"target": "ES2020",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
/* Type declarations */
"declaration": true,
"declarationMap": true,
"emitDeclarationOnly": false
},
"include": ["src"],
"exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"]
}
tsconfig.build.json (for CLI tool):
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020"],
"moduleResolution": "node",
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"skipLibCheck": true,
"strict": true,
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"declarationMap": true
},
"include": ["src"],
"exclude": ["node_modules", "dist", "templates", "**/*.test.ts"]
}
4.3 CSS Extraction and Minification
PostCSS Configuration (postcss.config.js):
export default {
plugins: {
autoprefixer: {},
cssnano: {
preset: [
'default',
{
discardComments: {
removeAll: true,
},
normalizeWhitespace: true,
},
],
},
},
};
CSS Modules Support (optional):
// vite.config.ts
export default defineConfig({
css: {
modules: {
localsConvention: 'camelCaseOnly',
generateScopedName: '[name]__[local]___[hash:base64:5]',
},
},
});
CSS Variable Theming:
/* src/styles/theme.css */
:root {
--doc-viewer-bg: #ffffff;
--doc-viewer-text: #1a1a1a;
--doc-viewer-border: #e0e0e0;
--doc-viewer-accent: #0066cc;
}
[data-theme='dark'] {
--doc-viewer-bg: #1a1a1a;
--doc-viewer-text: #e0e0e0;
--doc-viewer-border: #333333;
--doc-viewer-accent: #66b3ff;
}
4.4 Bundle Size Analysis
Add to package.json scripts:
{
"scripts": {
"analyze": "vite build --mode analyze",
"size": "npm run build && size-limit"
},
"devDependencies": {
"@size-limit/preset-small-lib": "^11.0.2",
"size-limit": "^11.0.2"
}
}
.size-limit.json:
[
{
"name": "ESM Bundle",
"path": "dist/index.js",
"import": "{ DocViewer }",
"limit": "50 KB",
"gzip": true
},
{
"name": "CSS Bundle",
"path": "dist/style.css",
"limit": "10 KB",
"gzip": true
},
{
"name": "Full Package (ESM + CSS)",
"path": "dist/index.js",
"import": "*",
"limit": "60 KB",
"gzip": true
}
]
Bundle Budget Strategy:
| Asset | Uncompressed | Gzipped | Target |
|---|---|---|---|
| ESM Bundle (JS) | < 150 KB | < 50 KB | Treeshakable imports |
| CSS Bundle | < 30 KB | < 10 KB | Critical + lazy |
| UMD Bundle | < 200 KB | < 70 KB | Legacy support |
| Total Package | < 250 KB | < 80 KB | First-paint impact |
4.5 Dual ESM/CJS Output
Why Dual Output?
| Format | Use Case | Pros | Cons |
|---|---|---|---|
| ESM | Modern bundlers (Vite, Webpack 5) | Tree-shaking, smaller bundles | Node.js <12 issues |
| UMD | Legacy browsers, CDN usage | Universal compatibility | Larger bundle |
| CJS | Node.js <12, Jest, SSR | Maximum compatibility | No tree-shaking |
Package.json Conditional Exports:
{
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.umd.cjs"
}
}
}
}
Vite Configuration for CJS:
// vite.config.ts
export default defineConfig({
build: {
lib: {
formats: ['es', 'umd', 'cjs'], // Add CJS
fileName: (format) => {
if (format === 'es') return 'index.js';
if (format === 'umd') return 'index.umd.cjs';
if (format === 'cjs') return 'index.cjs';
},
},
},
});
TypeScript Declaration Files:
// Generate both .d.ts (ESM) and .d.cts (CJS) declarations
// vite-plugin-dts handles this automatically with rollupTypes: true
5. GitHub Actions CI/CD
5.1 PR Validation Workflow
.github/workflows/pr-validation.yml:
name: PR Validation
on:
pull_request:
branches: [main, develop]
paths:
- 'packages/**'
- '.github/workflows/pr-validation.yml'
concurrency:
group: pr-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
validate:
name: Validate PR
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Lint
run: npm run lint
- name: Type Check
run: npm run type-check
- name: Run Tests
run: npm run test:coverage
- name: Upload Coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/coverage-final.json
flags: unittests
fail_ci_if_error: true
- name: Build Packages
run: npm run build
- name: Check Bundle Size
uses: andresz1/size-limit-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
skip_step: install
- name: Validate Changesets
run: |
if [ -z "$(ls -A .changeset/*.md 2>/dev/null | grep -v README)" ]; then
echo "::error::No changeset found. Run 'npm run changeset' to create one."
exit 1
fi
security:
name: Security Audit
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Audit Dependencies
run: npm audit --audit-level=moderate
- name: Check for Vulnerable Dependencies
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
compatibility:
name: Node.js Compatibility Matrix
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: ['18', '20', '21']
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- name: Install Dependencies
run: npm ci
- name: Run Tests
run: npm test
Key Features:
- Concurrency control: Cancel old PR validation runs when new commits pushed
- Coverage reporting: Upload to Codecov for trend analysis
- Bundle size check: Fail PR if size limit exceeded
- Changeset validation: Enforce changeset creation for version tracking
- Security audit: npm audit + Snyk for vulnerability scanning
- Compatibility matrix: Test on Node.js 18/20/21 across Linux/Windows/macOS
5.2 Release Workflow (Changesets)
.github/workflows/release.yml:
name: Release
on:
push:
branches:
- main
concurrency:
group: release
cancel-in-progress: false
jobs:
release:
name: Release Packages
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
pull-requests: write
id-token: write # Required for npm provenance
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: Install Dependencies
run: npm ci
- name: Build Packages
run: npm run build
- name: Create Release PR or Publish
id: changesets
uses: changesets/action@v1
with:
version: npm run version
publish: npm run release
title: 'chore: version packages'
commit: 'chore: version packages'
createGithubReleases: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish to GitHub Packages (fallback)
if: steps.changesets.outputs.published == 'true'
run: |
echo "@coditect:registry=https://npm.pkg.github.com" >> .npmrc
npm publish --registry=https://npm.pkg.github.com
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Post-Release Notification
if: steps.changesets.outputs.published == 'true'
uses: actions/github-script@v7
with:
script: |
const published = JSON.parse('${{ steps.changesets.outputs.publishedPackages }}');
const packages = published.map(p => `- \`${p.name}@${p.version}\``).join('\n');
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `## 📦 Packages Published\n\n${packages}\n\n[View on npm](https://www.npmjs.com/package/@coditect/doc-viewer)`
});
Workflow Explanation:
- Triggered by: Push to
mainbranch (after PR merge) - Changesets action:
- Checks for pending changesets
- If found: Creates/updates "Version Packages" PR
- If Version PR merged: Publishes to npm + creates GitHub releases
- Dual registry publish: npm public (primary) + GitHub Packages (fallback)
- Provenance: npm provenance attestation for supply chain security
- Notification: Comment on PR with published package versions
5.3 Canary Release Workflow
.github/workflows/canary-release.yml:
name: Canary Release
on:
workflow_dispatch:
inputs:
tag:
description: 'Canary tag (e.g., "feat-lazy-load")'
required: true
default: 'canary'
jobs:
canary:
name: Publish Canary Release
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: Install Dependencies
run: npm ci
- name: Build Packages
run: npm run build
- name: Version Canary
run: |
TIMESTAMP=$(date +%Y%m%d%H%M%S)
CANARY_VERSION="0.0.0-${{ github.event.inputs.tag }}.${TIMESTAMP}"
# Update version without git commit
npm version $CANARY_VERSION --no-git-tag-version --workspaces
echo "CANARY_VERSION=$CANARY_VERSION" >> $GITHUB_ENV
- name: Publish Canary
run: |
npm publish --tag ${{ github.event.inputs.tag }} --workspaces --provenance
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Comment on PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `## 🚀 Canary Release Published\n\nInstall with:\n\`\`\`bash\nnpm install @coditect/doc-viewer@${{ github.event.inputs.tag }}\n\`\`\`\n\nVersion: \`${{ env.CANARY_VERSION }}\``
});
Use Cases:
- Test feature branches before merging
- Share work-in-progress with testers
- A/B test new APIs with select users
Installation:
# Install latest canary
npm install @coditect/doc-viewer@canary
# Install specific canary tag
npm install @coditect/doc-viewer@feat-lazy-load
5.4 Automated Version Bump Workflow (Conventional Commits)
.github/workflows/semantic-release.yml:
name: Semantic Release
on:
push:
branches:
- main
jobs:
release:
name: Semantic Release
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'
- name: Install Dependencies
run: npm ci
- name: Build Packages
run: npm run build
- name: Semantic Release
run: npx semantic-release
env:
GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Commit Message Convention:
# Examples that trigger releases:
git commit -m "feat: add lazy loading support" # MINOR bump
git commit -m "fix: resolve SSR hydration mismatch" # PATCH bump
git commit -m "feat!: remove deprecated TableOfContents" # MAJOR bump
git commit -m "chore: update dependencies" # No release
5.5 Workflow Triggers Summary
| Workflow | Trigger | Purpose | Publish? |
|---|---|---|---|
| PR Validation | pull_request | Quality gates | No |
| Release | push to main (Changesets) | Stable releases | Yes |
| Canary | workflow_dispatch | Feature testing | Yes (tagged) |
| Semantic Release | push to main (Conventional Commits) | Automated versioning | Yes |
6. Quality Gates
6.1 Pre-Publish Checklist
Automated via prepublishOnly script:
{
"scripts": {
"prepublishOnly": "npm run quality-gate"
}
}
quality-gate script:
#!/bin/bash
set -e
echo "🔍 Running quality gates..."
# 1. Lint
echo "→ Linting code..."
npm run lint
# 2. Type check
echo "→ Type checking..."
npm run type-check
# 3. Unit tests
echo "→ Running tests..."
npm run test
# 4. Build
echo "→ Building package..."
npm run build
# 5. Bundle size check
echo "→ Checking bundle size..."
npm run size
# 6. Security audit
echo "→ Auditing dependencies..."
npm audit --audit-level=moderate
echo "✅ All quality gates passed!"
6.2 Unit Test Coverage Requirements
.vitest.config.ts:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/test/setup.ts',
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html', 'lcov'],
thresholds: {
lines: 80,
functions: 80,
branches: 75,
statements: 80,
},
exclude: [
'node_modules/',
'src/test/',
'**/*.test.{ts,tsx}',
'**/*.config.{ts,js}',
'**/types.ts',
],
},
},
});
Coverage Requirements:
| Metric | Minimum | Target | Notes |
|---|---|---|---|
| Line Coverage | 80% | 90% | All executable lines |
| Function Coverage | 80% | 90% | All exported functions |
| Branch Coverage | 75% | 85% | All conditional branches |
| Statement Coverage | 80% | 90% | All statements |
Example Test:
import { render, screen } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { DocViewer } from './DocViewer';
describe('DocViewer', () => {
it('renders document content', () => {
const document = {
title: 'Test Document',
content: '<h1>Hello</h1>',
};
render(<DocViewer document={document} />);
expect(screen.getByRole('heading', { name: 'Hello' })).toBeInTheDocument();
});
it('renders table of contents when provided', () => {
const document = {
title: 'Test',
content: '<h2 id="section-1">Section 1</h2>',
toc: [{ id: 'section-1', title: 'Section 1', level: 2 }],
};
render(<DocViewer document={document} showToc />);
expect(screen.getByRole('navigation')).toBeInTheDocument();
expect(screen.getByText('Section 1')).toBeInTheDocument();
});
});
6.3 Bundle Size Budget Enforcement
Size Limit Configuration (.size-limit.json):
[
{
"name": "ESM Bundle (JS)",
"path": "dist/index.js",
"import": "{ DocViewer }",
"limit": "50 KB",
"gzip": true,
"running": false
},
{
"name": "CSS Bundle",
"path": "dist/style.css",
"limit": "10 KB",
"gzip": true
},
{
"name": "Full Package",
"path": "dist/index.js",
"import": "*",
"limit": "60 KB",
"gzip": true
}
]
GitHub Action Integration:
- name: Check Bundle Size
uses: andresz1/size-limit-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
skip_step: install
What Happens if Budget Exceeded?
- PR check fails with red X
- Comment added to PR with size comparison
- Developer must:
- Remove unnecessary dependencies
- Code-split features
- Lazy load non-critical components
- Justify increased limit in PR description
6.4 TypeScript Strict Mode Compliance
tsconfig.json strict flags:
{
"compilerOptions": {
"strict": true, // Enables all strict checks
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noImplicitReturns": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
}
CI Check:
- name: Type Check
run: npm run type-check
Common Issues:
| Error | Fix |
|---|---|
'x' is declared but never used | Remove or prefix with _ |
Object is possibly 'undefined' | Add null check or ! assertion |
Type 'X' is not assignable to 'Y' | Fix type annotations |
6.5 Peer Dependency Compatibility Check
Script: check-peer-deps.sh:
#!/bin/bash
set -e
echo "🔍 Checking peer dependency compatibility..."
# Test with React 16
npm install --no-save react@^16.14.0 react-dom@^16.14.0
npm run build
npm test
# Test with React 17
npm install --no-save react@^17.0.0 react-dom@^17.0.0
npm run build
npm test
# Test with React 18
npm install --no-save react@^18.0.0 react-dom@^18.0.0
npm run build
npm test
echo "✅ Compatible with React 16, 17, and 18!"
CI Integration:
- name: Test Peer Dependencies
run: ./scripts/check-peer-deps.sh
6.6 Security Vulnerability Scanning
npm audit:
# Fail on moderate or higher vulnerabilities
npm audit --audit-level=moderate
# Generate fix PR
npm audit fix --dry-run
Snyk Integration (.github/workflows/security.yml):
name: Security Scan
on:
schedule:
- cron: '0 0 * * 1' # Weekly on Monday
workflow_dispatch:
jobs:
snyk:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high --fail-on=all
Dependency Update Strategy:
| Severity | Response Time | Action |
|---|---|---|
| Critical | 24 hours | Immediate patch release |
| High | 7 days | Patch release in next sprint |
| Medium | 30 days | Include in next minor release |
| Low | 90 days | Upgrade during dependency refresh |
6.7 README and CHANGELOG Validation
Script: validate-docs.sh:
#!/bin/bash
set -e
echo "📚 Validating documentation..."
# Check README exists and has required sections
if [ ! -f README.md ]; then
echo "❌ README.md missing!"
exit 1
fi
grep -q "## Installation" README.md || { echo "❌ README missing Installation section"; exit 1; }
grep -q "## Usage" README.md || { echo "❌ README missing Usage section"; exit 1; }
grep -q "## API" README.md || { echo "❌ README missing API section"; exit 1; }
# Check CHANGELOG exists and has recent entry
if [ ! -f CHANGELOG.md ]; then
echo "❌ CHANGELOG.md missing!"
exit 1
fi
# Verify version in CHANGELOG matches package.json
PKG_VERSION=$(node -p "require('./package.json').version")
grep -q "## \[$PKG_VERSION\]" CHANGELOG.md || {
echo "❌ CHANGELOG missing entry for v$PKG_VERSION"
exit 1
}
echo "✅ Documentation validated!"
7. Release Process
7.1 Step-by-Step Release Checklist
Pre-Release (Developer):
- All feature branches merged to
main - All tests passing in CI
- No security vulnerabilities in dependencies
- Bundle size within budget
- TypeScript declarations generated correctly
- README updated with new features
- Migration guide written (if breaking changes)
- Changeset created (
npm run changeset)
Automated Release (CI):
- Version bumped in package.json
- CHANGELOG.md generated/updated
- Git tag created (e.g.,
@coditect/doc-viewer@1.2.0) - GitHub Release created with notes
- Package published to npm with provenance
- Package published to GitHub Packages (backup)
- Post-release tests passing
Post-Release (Developer):
- Verify package installable:
npm install @coditect/doc-viewer@latest - Smoke test in fresh project
- Update dependent projects
- Announce release (Slack, Twitter, changelog)
- Close milestone in GitHub Projects
7.2 Automated CHANGELOG Generation (Changesets)
CHANGELOG.md Format:
# @coditect/doc-viewer
## 1.2.0 - 2026-02-16
### Minor Changes
- feat: Add lazy loading support for large documents ([#123](https://github.com/coditect-ai/bio-qms/pull/123))
- feat: Add dark mode theme support ([#125](https://github.com/coditect-ai/bio-qms/pull/125))
### Patch Changes
- fix: Resolve SSR hydration mismatch in TOC component ([#124](https://github.com/coditect-ai/bio-qms/pull/124))
- fix: Improve mobile responsive layout ([#126](https://github.com/coditect-ai/bio-qms/pull/126))
## 1.1.0 - 2026-02-01
### Minor Changes
- feat: Add print stylesheet ([#120](https://github.com/coditect-ai/bio-qms/pull/120))
### Patch Changes
- fix: Fix heading anchor links ([#121](https://github.com/coditect-ai/bio-qms/pull/121))
Changesets Workflow:
# 1. Developer creates changeset for each PR
npm run changeset
# Prompts:
# - Which packages changed? @coditect/doc-viewer
# - What type of change? minor
# - Summary: Add lazy loading support
# This creates .changeset/lazy-loading.md:
# ---
# "@coditect/doc-viewer": minor
# ---
#
# Add lazy loading support for large documents
# 2. Commit changeset with PR
git add .changeset/lazy-loading.md
git commit -m "feat: add lazy loading support"
# 3. After PR merge, CI creates "Version Packages" PR
# This PR updates:
# - package.json version (1.1.0 → 1.2.0)
# - CHANGELOG.md (adds new section)
# - Removes consumed changeset files
# 4. Merge Version PR → CI publishes to npm
7.3 GitHub Release Creation
Release Notes Template:
# @coditect/doc-viewer v1.2.0
## 🎉 New Features
- **Lazy Loading**: Documents now load incrementally for better performance on large files
- **Dark Mode**: Full dark mode support with CSS variables
## 🐛 Bug Fixes
- Fixed SSR hydration mismatch in table of contents
- Improved mobile responsive layout
## 📦 Installation
```bash
npm install @coditect/doc-viewer@1.2.0
📚 Migration Guide
No breaking changes. All new features are opt-in.
🙏 Contributors
- @username1
- @username2
Full Changelog: https://github.com/coditect-ai/bio-qms/compare/@coditect/doc-viewer@1.1.0...@coditect/doc-viewer@1.2.0
**Automated with Changesets**:
```yaml
# .github/workflows/release.yml
- uses: changesets/action@v1
with:
createGithubReleases: true # Auto-create releases
7.4 npm Provenance (Supply Chain Security)
What is Provenance?
npm provenance creates a cryptographic attestation linking a published package to its source code and build process. This proves:
- Source authenticity: Package was built from specific GitHub commit
- Build integrity: Package was built by GitHub Actions (not developer's laptop)
- Supply chain transparency: Verifiable audit trail
Enabling Provenance:
// package.json
{
"publishConfig": {
"provenance": true
}
}
GitHub Actions Permissions:
# .github/workflows/release.yml
jobs:
release:
permissions:
id-token: write # Required for provenance
contents: write
packages: write
Publish Command:
npm publish --provenance
Verification:
# View provenance on npm package page
npm view @coditect/doc-viewer
# Or check provenance directly
npm audit signatures
7.5 Post-Publish Verification
Automated Smoke Test (post-publish.sh):
#!/bin/bash
set -e
PACKAGE_NAME="$1"
VERSION="$2"
echo "🧪 Smoke testing $PACKAGE_NAME@$VERSION..."
# Create temporary directory
TEMP_DIR=$(mktemp -d)
cd "$TEMP_DIR"
# Initialize test project
npm init -y
npm install "$PACKAGE_NAME@$VERSION" react react-dom
# Create test file
cat > test.js <<EOF
import { DocViewer } from '$PACKAGE_NAME';
console.log('✅ Package imported successfully');
console.log('DocViewer:', typeof DocViewer);
EOF
# Run test
node test.js
# Cleanup
cd -
rm -rf "$TEMP_DIR"
echo "✅ Smoke test passed!"
CI Integration:
- name: Smoke Test Published Package
if: steps.changesets.outputs.published == 'true'
run: |
for pkg in $(echo '${{ steps.changesets.outputs.publishedPackages }}' | jq -r '.[] | "\(.name)@\(.version)"'); do
./scripts/post-publish.sh $pkg
done
Manual Verification Checklist:
- Package appears on npm registry: https://www.npmjs.com/package/@coditect/doc-viewer
- README renders correctly on npm page
- Provenance badge visible: "Published with provenance from GitHub Actions"
- TypeScript types resolve in IDE (test in fresh project)
- CSS imports work:
import '@coditect/doc-viewer/style.css' - Tree-shaking works (check with bundler analyzer)
- No console errors in browser demo
8. README Generation
8.1 Auto-Generated README Sections
Template: README.md:
# @coditect/doc-viewer
> React component library for rendering technical documents with navigation, table of contents, and responsive layouts
[](https://www.npmjs.com/package/@coditect/doc-viewer)
[](https://www.npmjs.com/package/@coditect/doc-viewer)
[](https://github.com/coditect-ai/bio-qms/actions)
[](https://bundlephobia.com/package/@coditect/doc-viewer)
[](https://www.typescriptlang.org/)
[](https://opensource.org/licenses/MIT)
## Features
- 📚 **Document Rendering**: Render Markdown, HTML, and custom document formats
- 🧭 **Table of Contents**: Auto-generated TOC with smooth scrolling
- 📱 **Responsive**: Mobile-first design with collapsible sidebar
- 🎨 **Themeable**: CSS variables for easy customization
- ⚡ **Performance**: Lazy loading and virtualization for large documents
- ♿ **Accessible**: WCAG 2.1 AA compliant with keyboard navigation
- 🌙 **Dark Mode**: Built-in dark theme support
- 🔍 **Search**: Client-side full-text search
- 📦 **Tree-Shakable**: ESM modules with optimized bundle size
## Installation
```bash
# npm
npm install @coditect/doc-viewer
# yarn
yarn add @coditect/doc-viewer
# pnpm
pnpm add @coditect/doc-viewer
Quick Start
import { DocViewer } from '@coditect/doc-viewer';
import '@coditect/doc-viewer/style.css';
function App() {
const document = {
title: 'Getting Started',
content: '<h1>Welcome</h1><p>This is your document.</p>',
toc: [
{ id: 'welcome', title: 'Welcome', level: 1 }
]
};
return <DocViewer document={document} showToc />;
}
Usage Examples
Basic Document Rendering
import { DocViewer } from '@coditect/doc-viewer';
<DocViewer
document={{
title: 'API Documentation',
content: apiDocsHTML,
}}
/>
With Table of Contents
<DocViewer
document={document}
showToc
tocPosition="left" // or "right"
collapsibleToc
/>
Custom Theme
<DocViewer
document={document}
theme="dark" // or "light", "auto"
/>
Lazy Loading Large Documents
<DocViewer
document={document}
lazyLoad
chunkSize={50} // Load 50 sections at a time
/>
API Reference
Components
<DocViewer>
Main component for rendering documents.
Props:
| Prop | Type | Default | Description |
|---|---|---|---|
document | Document | required | Document object to render |
showToc | boolean | false | Show table of contents |
tocPosition | 'left' | 'right' | 'left' | TOC sidebar position |
collapsibleToc | boolean | true | Allow TOC to be collapsed |
theme | 'light' | 'dark' | 'auto' | 'light' | Color theme |
lazyLoad | boolean | false | Enable lazy loading for large docs |
chunkSize | number | 50 | Number of sections to load at once |
onSectionChange | (id: string) => void | undefined | Callback when active section changes |
className | string | undefined | Additional CSS classes |
Types:
interface Document {
title: string;
content: string;
toc?: TocEntry[];
metadata?: Record<string, unknown>;
}
interface TocEntry {
id: string;
title: string;
level: number; // 1-6 (h1-h6)
children?: TocEntry[];
}
Theming
CSS Variables
:root {
/* Colors */
--doc-viewer-bg: #ffffff;
--doc-viewer-text: #1a1a1a;
--doc-viewer-border: #e0e0e0;
--doc-viewer-accent: #0066cc;
/* Spacing */
--doc-viewer-padding: 1rem;
--doc-viewer-gap: 0.5rem;
/* Typography */
--doc-viewer-font-family: system-ui, sans-serif;
--doc-viewer-font-size: 16px;
--doc-viewer-line-height: 1.6;
}
Custom Theme Example
.my-custom-theme {
--doc-viewer-bg: #f5f5f5;
--doc-viewer-text: #2c3e50;
--doc-viewer-accent: #e74c3c;
}
<DocViewer document={doc} className="my-custom-theme" />
Migration Guides
Upgrading from 0.x to 1.x
Breaking Changes:
TableOfContentscomponent removed (now part ofDocViewer)themeprop changed from object to string enum- CSS class names changed (now prefixed with
doc-viewer-)
Migration Steps:
// Before (0.x)
<TableOfContents entries={toc} />
<DocViewer document={doc} theme={{ primary: '#0066cc' }} />
// After (1.x)
<DocViewer document={{ ...doc, toc }} showToc theme="light" />
See MIGRATION.md for full details.
Browser Support
| Browser | Minimum Version |
|---|---|
| Chrome | 90+ |
| Firefox | 88+ |
| Safari | 14+ |
| Edge | 90+ |
Contributing
See CONTRIBUTING.md for development setup and guidelines.
License
MIT © AZ1.AI INC
Changelog
See CHANGELOG.md for release history.
Support
### 8.2 Badges Configuration
**Shields.io Badges**:
```markdown
<!-- Version badge -->
[](https://www.npmjs.com/package/@coditect/doc-viewer)
<!-- Downloads badge -->
[](https://www.npmjs.com/package/@coditect/doc-viewer)
<!-- CI status -->
[](https://github.com/coditect-ai/bio-qms/actions)
<!-- Bundle size -->
[](https://bundlephobia.com/package/@coditect/doc-viewer)
<!-- Coverage -->
[](https://codecov.io/gh/coditect-ai/bio-qms)
<!-- License -->
[](https://opensource.org/licenses/MIT)
<!-- TypeScript -->
[](https://www.typescriptlang.org/)
8.3 API Documentation from TypeScript
Automated with typedoc:
npm install -D typedoc typedoc-plugin-markdown
typedoc.json:
{
"entryPoints": ["src/index.ts"],
"out": "docs/api",
"plugin": ["typedoc-plugin-markdown"],
"readme": "none",
"excludePrivate": true,
"excludeProtected": true,
"excludeExternals": true
}
Generate docs:
npx typedoc
Embed in README:
## API Reference
<!-- AUTO-GENERATED: Do not edit manually -->
<!-- Run: npm run docs:api -->
[Full API Documentation](./docs/api/README.md)
9. Monitoring & Analytics
9.1 npm Download Tracking
npm Stats API:
# Get download counts
curl https://api.npmjs.org/downloads/point/last-month/@coditect/doc-viewer
# Response:
{
"downloads": 12450,
"start": "2026-01-16",
"end": "2026-02-15",
"package": "@coditect/doc-viewer"
}
Download Dashboard Script (scripts/npm-stats.sh):
#!/bin/bash
PACKAGE="@coditect/doc-viewer"
echo "📊 npm Download Statistics for $PACKAGE"
echo "=========================================="
# Last day
curl -s "https://api.npmjs.org/downloads/point/last-day/$PACKAGE" | jq '.downloads' | xargs echo "Last 24 hours:"
# Last week
curl -s "https://api.npmjs.org/downloads/point/last-week/$PACKAGE" | jq '.downloads' | xargs echo "Last 7 days:"
# Last month
curl -s "https://api.npmjs.org/downloads/point/last-month/$PACKAGE" | jq '.downloads' | xargs echo "Last 30 days:"
# Last year
curl -s "https://api.npmjs.org/downloads/point/last-year/$PACKAGE" | jq '.downloads' | xargs echo "Last 365 days:"
9.2 Version Adoption Metrics
Script: version-adoption.sh:
#!/bin/bash
PACKAGE="@coditect/doc-viewer"
echo "📈 Version Adoption for $PACKAGE"
echo "=================================="
# Get all versions
VERSIONS=$(npm view $PACKAGE versions --json | jq -r '.[]')
for VERSION in $VERSIONS; do
DOWNLOADS=$(curl -s "https://api.npmjs.org/downloads/point/last-month/$PACKAGE/$VERSION" | jq -r '.downloads')
echo "$VERSION: $DOWNLOADS downloads"
done | sort -t: -k2 -nr | head -10
Grafana Dashboard (if using npm-stat-prometheus):
# docker-compose.yml
services:
npm-exporter:
image: ghcr.io/ribbybibby/npm-exporter:latest
environment:
- PACKAGES=@coditect/doc-viewer,@coditect/create-doc-site
ports:
- "9867:9867"
9.3 Deprecation Workflow
When to Deprecate:
| Scenario | Action | Example |
|---|---|---|
| Security vulnerability | Deprecate + publish patch | 1.2.3 has XSS → deprecate, publish 1.2.4 |
| Accidental publish | Deprecate immediately | Typo in version number |
| Major version superseded | Deprecate after 6 months | Deprecate 1.x when 2.0 stable |
| Pre-release obsolete | Deprecate after stable release | Deprecate 2.0.0-beta.1 when 2.0.0 released |
Deprecation Command:
# Deprecate specific version
npm deprecate @coditect/doc-viewer@1.2.3 "Security vulnerability. Use 1.2.4 or later."
# Deprecate version range
npm deprecate @coditect/doc-viewer@"< 1.2.4" "Security vulnerability. Upgrade to 1.2.4+"
# Deprecate all 1.x versions (after 2.0 release)
npm deprecate @coditect/doc-viewer@"^1.0.0" "Version 1.x is no longer maintained. Upgrade to 2.x."
# Undeprecate (if mistake)
npm deprecate @coditect/doc-viewer@1.2.3 ""
User Experience:
$ npm install @coditect/doc-viewer@1.2.3
npm WARN deprecated @coditect/doc-viewer@1.2.3: Security vulnerability. Use 1.2.4 or later.
9.4 Security Advisory Process
When Vulnerability Discovered:
-
Create GitHub Security Advisory (private)
- Go to: https://github.com/coditect-ai/bio-qms/security/advisories/new
- Fill in: Severity, affected versions, description, workarounds
- Request CVE identifier
-
Develop Fix in Private Fork
- Clone advisory's private fork
- Fix vulnerability
- Add regression test
- Prepare patch release
-
Coordinate Disclosure
- Notify known large users (if applicable)
- Set public disclosure date (typically 7-30 days)
-
Publish Patch Release
npm version patch
npm publish -
Publish Security Advisory
- GitHub advisory becomes public
- npm shows advisory on package page
- Email sent to watchers
-
Deprecate Vulnerable Versions
npm deprecate @coditect/doc-viewer@"< 1.2.4" "Security vulnerability CVE-2026-12345. Upgrade immediately."
GitHub Advisory Template:
## Summary
Cross-site scripting (XSS) vulnerability in DocViewer component when rendering untrusted HTML content.
## Severity
High (CVSS 7.5)
## Affected Versions
- @coditect/doc-viewer < 1.2.4
## Patches
Upgrade to version 1.2.4 or later.
## Workarounds
Sanitize all HTML content before passing to DocViewer:
```tsx
import DOMPurify from 'dompurify';
const sanitized = DOMPurify.sanitize(untrustedHTML);
<DocViewer document={{ content: sanitized }} />
References
- Fix commit: abc123
- CVE-2026-12345
### 9.5 Analytics Dashboard (Google Analytics for npm)
**Track npm package page views** (via GitHub proxy):
```html
<!-- Add to README.md (invisible pixel) -->

Track installs via CDN (if providing CDN build):
<!-- In CDN HTML -->
<script>
gtag('event', 'cdn_install', {
'package_name': '@coditect/doc-viewer',
'package_version': '1.2.0'
});
</script>
10. Complete Configuration Files
10.1 package.json (@coditect/doc-viewer)
See Section 3.1 for complete configuration.
10.2 package.json (@coditect/create-doc-site)
See Section 3.2 for complete configuration.
10.3 vite.config.ts
See Section 4.1 for complete configuration.
10.4 tsconfig.json
See Section 4.2 for complete configuration.
10.5 .github/workflows/pr-validation.yml
See Section 5.1 for complete workflow.
10.6 .github/workflows/release.yml
See Section 5.2 for complete workflow.
10.7 .size-limit.json
See Section 4.4 for complete configuration.
10.8 .changeset/config.json
{
"$schema": "https://unpkg.com/@changesets/config@2.3.0/schema.json",
"changelog": "@changesets/cli/changelog",
"commit": false,
"fixed": [],
"linked": [],
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
10.9 .npmignore
# Source files
src/
*.test.ts
*.test.tsx
__tests__/
__mocks__/
# Build configs
tsconfig.json
vite.config.ts
vitest.config.ts
.size-limit.json
# Development
.changeset/
.github/
.vscode/
.DS_Store
*.log
# Documentation (publish README only)
docs/
*.md
!README.md
!CHANGELOG.md
!LICENSE
10.10 .npmrc (Workspace Root)
# Workspace configuration
workspaces = true
# Registry (if using GitHub Packages)
# @coditect:registry=https://npm.pkg.github.com
# //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
# Lockfile version
lockfile-version = v2
# Strict peer dependencies
strict-peer-dependencies = true
# Save exact versions
save-exact = true
10.11 .eslintrc.json
{
"root": true,
"env": {
"browser": true,
"es2020": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended"
],
"ignorePatterns": ["dist", ".eslintrc.json"],
"parser": "@typescript-eslint/parser",
"plugins": ["react-refresh"],
"rules": {
"react-refresh/only-export-components": [
"warn",
{ "allowConstantExport": true }
],
"@typescript-eslint/no-unused-vars": [
"error",
{ "argsIgnorePattern": "^_" }
]
}
}
10.12 vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
test: {
globals: true,
environment: 'jsdom',
setupFiles: './src/test/setup.ts',
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html', 'lcov'],
thresholds: {
lines: 80,
functions: 80,
branches: 75,
statements: 80,
},
exclude: [
'node_modules/',
'src/test/',
'**/*.test.{ts,tsx}',
'**/*.config.{ts,js}',
],
},
},
});
10.13 .releaserc.json (semantic-release alternative)
{
"branches": ["main"],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/changelog",
{
"changelogFile": "CHANGELOG.md"
}
],
[
"@semantic-release/npm",
{
"npmPublish": true,
"pkgRoot": "."
}
],
[
"@semantic-release/github",
{
"assets": [
{ "path": "dist/**/*", "label": "Distribution files" }
]
}
],
[
"@semantic-release/git",
{
"assets": ["package.json", "CHANGELOG.md"],
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
}
]
]
}
Appendix A: Troubleshooting
Common Issues
Issue: "ERR! 403 Forbidden - PUT https://registry.npmjs.org/@coditect/doc-viewer"
Cause: Missing or invalid npm token, or no permission to publish to @coditect scope.
Solution:
# 1. Verify token exists
echo $NPM_TOKEN
# 2. Test token
npm whoami --registry=https://registry.npmjs.org
# 3. Verify scope permission
npm access ls-collaborators @coditect/doc-viewer
# 4. Regenerate token if needed
# Go to: https://www.npmjs.com/settings/[username]/tokens
# Create new automation token, save as NPM_TOKEN in GitHub secrets
Issue: "Cannot find module '@coditect/doc-viewer/style.css'"
Cause: CSS file not exported in package.json exports.
Solution:
{
"exports": {
"./style.css": "./dist/style.css"
}
}
Issue: "Types not found" in consuming project
Cause: TypeScript declarations not generated or not exported.
Solution:
{
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
}
}
}
}
Issue: "ERR! Unsupported platform for @coditect/doc-viewer@1.0.0"
Cause: os or cpu field in package.json too restrictive.
Solution: Remove os and cpu fields unless truly platform-specific.
Issue: Bundle size check fails in PR
Cause: Bundle exceeds size limit defined in .size-limit.json.
Solution:
- Analyze bundle:
npm run analyze - Remove unnecessary dependencies
- Lazy load heavy features
- Or update size limit if justified
Appendix B: References
Documentation
- npm CLI Documentation
- GitHub Packages npm Registry
- Semantic Versioning 2.0.0
- Changesets Documentation
- semantic-release Documentation
- Vite Library Mode
- npm Provenance
Tools
- Vite - Build tool
- TypeScript - Type safety
- Vitest - Testing framework
- Changesets - Version management
- size-limit - Bundle size control
- ESLint - Linting
- Prettier - Code formatting
Community
- npm Blog
- GitHub Actions Marketplace
- Vite Awesome - Vite plugins and tools
Appendix C: Glossary
| Term | Definition |
|---|---|
| CJS | CommonJS - Legacy Node.js module format (require()/module.exports) |
| ESM | ECMAScript Modules - Modern JavaScript module format (import/export) |
| UMD | Universal Module Definition - Compatible with AMD, CJS, and globals |
| Peer Dependency | Dependency that host application must provide (e.g., React) |
| Provenance | Cryptographic proof linking published package to source code |
| Changeset | Markdown file describing changes to be included in next release |
| Canary Release | Pre-release version published from feature branch for testing |
| Tree-Shaking | Dead code elimination to reduce bundle size |
| Source Map | File mapping minified code back to original source for debugging |
| Semver | Semantic Versioning - MAJOR.MINOR.PATCH versioning scheme |
Document Metadata
| Property | Value |
|---|---|
| Document Version | 1.0.0 |
| Created | 2026-02-16 |
| Last Updated | 2026-02-16 |
| Author | Claude (Sonnet 4.5) |
| Status | Active |
| Task ID | A.6.3 |
| Lines of Code | ~2,800 |
| Word Count | ~15,000 |
End of Document