Skip to main content

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

  1. Registry Strategy
  2. Semantic Versioning Policy
  3. Package Configuration
  4. Build Pipeline
  5. GitHub Actions CI/CD
  6. Quality Gates
  7. Release Process
  8. README Generation
  9. Monitoring & Analytics
  10. Complete Configuration Files

1. Registry Strategy

1.1 Registry Options

RegistryUse CaseProsCons
GitHub PackagesPrivate 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 PublicOpen-source distribution- Maximum discoverability
- Zero auth for install
- Built-in analytics
- Community trust
- Public code visibility
- Namespace squatting risk
- Less access control
npm PrivatePaid enterprise distribution- Private + discoverable
- npm CLI native
- Team management
- $7/user/month
- External billing

1.2 Decision Matrix

Recommended Strategy: Dual-Registry Phased Approach

PhaseRegistryPackagesRationale
Phase 1: InternalGitHub PackagesBoth packages (private)- Validate with BIO-QMS integration
- Iterate on API stability
- Enterprise clients only
Phase 2: Public Betanpm Public@coditect/doc-viewer only- Open-source component library
- Community feedback
- Maintain private CLI tool
Phase 3: Full OSSnpm PublicBoth 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) with read:packages scope
  • For CI/CD: Use repository GITHUB_TOKEN secret (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:

  1. Create organization: npm org:set coditect
  2. Add maintainers: npm owner add <username> @coditect/doc-viewer
  3. Generate automation token: npm token create --type automation
  4. Store as GitHub secret: NPM_TOKEN

1.5 Authentication Token Management

Security Best Practices:

Token TypeScopeStorageRotation
GitHub PAT (Personal)read:packages, write:packagesGitHub org secrets90 days
npm Automation TokenPublish-onlyGitHub repo secrets365 days
npm Publish TokenPublish + adminNever in CI/CDN/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 TypeVersion BumpExamples
Breaking ChangesMAJOR- Remove/rename public component
- Change prop types (required → optional)
- Remove CSS class names
- Drop Node.js version support
- Change peer dependency requirements
New FeaturesMINOR- Add new component
- Add optional props to existing component
- Add new CSS variables
- Add new exports to index
- Deprecate (but not remove) APIs
Bug FixesPATCH- 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 TypeVersion BumpExamples
Breaking ChangesMAJOR- 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 FeaturesMINOR- Add new CLI flags (optional)
- Add new templates
- Add new interactive prompts
- Improve error messages
Bug FixesPATCH- Fix template generation bugs
- Fix file permissions
- Update dependencies
- Fix typos in help text

2.4 Pre-Release Versions

Pre-Release Identifiers:

IdentifierStageStabilityUsage
alphaEarly testingUnstable API1.0.0-alpha.1
betaFeature completeMostly stable1.0.0-beta.3
rcRelease candidateProduction-ready1.0.0-rc.2
nextCanary/nightlyLatest main branch1.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 TypeRangeExampleRationale
Production StableCaret ^^1.2.3Allow minor + patch updates
Production ConservativeTilde ~~1.2.3Allow patch updates only
Production StrictExact1.2.3No automatic updates
DevelopmentCaret ^^1.2.3Allow minor + patch updates
Peer DependenciesWide 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:

FieldPurposeExample Value
namePackage identifier@coditect/doc-viewer
versionCurrent version1.0.0
typeModule systemmodule (ESM)
mainCJS entry point./dist/index.umd.cjs
moduleESM entry point./dist/index.js
typesTypeScript declarations./dist/index.d.ts
exportsNode.js subpath exportsConditional exports for ESM/CJS
filesWhat gets published["dist", "README.md"]
sideEffectsWebpack tree-shaking hint["*.css"]
enginesNode.js version requirement>=18.0.0
peerDependenciesHost app must providereact, react-dom
publishConfignpm publish optionsaccess: 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:

FieldPurposeExample Value
binExecutable entry point{"create-doc-site": "./dist/index.js"}
filesInclude templates directory["dist", "templates"]
dependenciesRuntime CLI dependenciescommander, 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:

SectionPurposeKey Options
build.libLibrary mode entryentry, name, formats
rollupOptions.externalExclude peer deps from bundlereact, react-dom
rollupOptions.output.globalsUMD global variable namesReact, ReactDOM
sourcemapSource maps for debuggingtrue (dev), false (prod)
minifyMinification strategyesbuild (fast), terser (smaller)
cssCodeSplitCSS bundling strategyfalse (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:

AssetUncompressedGzippedTarget
ESM Bundle (JS)< 150 KB< 50 KBTreeshakable imports
CSS Bundle< 30 KB< 10 KBCritical + lazy
UMD Bundle< 200 KB< 70 KBLegacy support
Total Package< 250 KB< 80 KBFirst-paint impact

4.5 Dual ESM/CJS Output

Why Dual Output?

FormatUse CaseProsCons
ESMModern bundlers (Vite, Webpack 5)Tree-shaking, smaller bundlesNode.js <12 issues
UMDLegacy browsers, CDN usageUniversal compatibilityLarger bundle
CJSNode.js <12, Jest, SSRMaximum compatibilityNo 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:

  1. Triggered by: Push to main branch (after PR merge)
  2. Changesets action:
    • Checks for pending changesets
    • If found: Creates/updates "Version Packages" PR
    • If Version PR merged: Publishes to npm + creates GitHub releases
  3. Dual registry publish: npm public (primary) + GitHub Packages (fallback)
  4. Provenance: npm provenance attestation for supply chain security
  5. 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

WorkflowTriggerPurposePublish?
PR Validationpull_requestQuality gatesNo
Releasepush to main (Changesets)Stable releasesYes
Canaryworkflow_dispatchFeature testingYes (tagged)
Semantic Releasepush to main (Conventional Commits)Automated versioningYes

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:

MetricMinimumTargetNotes
Line Coverage80%90%All executable lines
Function Coverage80%90%All exported functions
Branch Coverage75%85%All conditional branches
Statement Coverage80%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?

  1. PR check fails with red X
  2. Comment added to PR with size comparison
  3. 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:

ErrorFix
'x' is declared but never usedRemove 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:

SeverityResponse TimeAction
Critical24 hoursImmediate patch release
High7 daysPatch release in next sprint
Medium30 daysInclude in next minor release
Low90 daysUpgrade 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:

  1. Source authenticity: Package was built from specific GitHub commit
  2. Build integrity: Package was built by GitHub Actions (not developer's laptop)
  3. 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

[![npm version](https://img.shields.io/npm/v/@coditect/doc-viewer.svg)](https://www.npmjs.com/package/@coditect/doc-viewer)
[![npm downloads](https://img.shields.io/npm/dm/@coditect/doc-viewer.svg)](https://www.npmjs.com/package/@coditect/doc-viewer)
[![Build Status](https://github.com/coditect-ai/bio-qms/workflows/CI/badge.svg)](https://github.com/coditect-ai/bio-qms/actions)
[![Bundle Size](https://img.shields.io/bundlephobia/minzip/@coditect/doc-viewer)](https://bundlephobia.com/package/@coditect/doc-viewer)
[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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:

PropTypeDefaultDescription
documentDocumentrequiredDocument object to render
showTocbooleanfalseShow table of contents
tocPosition'left' | 'right''left'TOC sidebar position
collapsibleTocbooleantrueAllow TOC to be collapsed
theme'light' | 'dark' | 'auto''light'Color theme
lazyLoadbooleanfalseEnable lazy loading for large docs
chunkSizenumber50Number of sections to load at once
onSectionChange(id: string) => voidundefinedCallback when active section changes
classNamestringundefinedAdditional 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:

  • TableOfContents component removed (now part of DocViewer)
  • theme prop 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

BrowserMinimum Version
Chrome90+
Firefox88+
Safari14+
Edge90+

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 -->
[![npm version](https://img.shields.io/npm/v/@coditect/doc-viewer.svg)](https://www.npmjs.com/package/@coditect/doc-viewer)

<!-- Downloads badge -->
[![npm downloads](https://img.shields.io/npm/dm/@coditect/doc-viewer.svg)](https://www.npmjs.com/package/@coditect/doc-viewer)

<!-- CI status -->
[![Build Status](https://github.com/coditect-ai/bio-qms/workflows/CI/badge.svg)](https://github.com/coditect-ai/bio-qms/actions)

<!-- Bundle size -->
[![Bundle Size](https://img.shields.io/bundlephobia/minzip/@coditect/doc-viewer)](https://bundlephobia.com/package/@coditect/doc-viewer)

<!-- Coverage -->
[![Coverage](https://img.shields.io/codecov/c/github/coditect-ai/bio-qms)](https://codecov.io/gh/coditect-ai/bio-qms)

<!-- License -->
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

<!-- TypeScript -->
[![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](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:

ScenarioActionExample
Security vulnerabilityDeprecate + publish patch1.2.3 has XSS → deprecate, publish 1.2.4
Accidental publishDeprecate immediatelyTypo in version number
Major version supersededDeprecate after 6 monthsDeprecate 1.x when 2.0 stable
Pre-release obsoleteDeprecate after stable releaseDeprecate 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:

  1. Create GitHub Security Advisory (private)

  2. Develop Fix in Private Fork

    • Clone advisory's private fork
    • Fix vulnerability
    • Add regression test
    • Prepare patch release
  3. Coordinate Disclosure

    • Notify known large users (if applicable)
    • Set public disclosure date (typically 7-30 days)
  4. Publish Patch Release

    npm version patch
    npm publish
  5. Publish Security Advisory

    • GitHub advisory becomes public
    • npm shows advisory on package page
    • Email sent to watchers
  6. 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) -->
![Analytics](https://ga-beacon.appspot.com/UA-XXXXX-Y/coditect-ai/doc-viewer?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:

  1. Analyze bundle: npm run analyze
  2. Remove unnecessary dependencies
  3. Lazy load heavy features
  4. Or update size limit if justified

Appendix B: References

Documentation

Tools

Community


Appendix C: Glossary

TermDefinition
CJSCommonJS - Legacy Node.js module format (require()/module.exports)
ESMECMAScript Modules - Modern JavaScript module format (import/export)
UMDUniversal Module Definition - Compatible with AMD, CJS, and globals
Peer DependencyDependency that host application must provide (e.g., React)
ProvenanceCryptographic proof linking published package to source code
ChangesetMarkdown file describing changes to be included in next release
Canary ReleasePre-release version published from feature branch for testing
Tree-ShakingDead code elimination to reduce bundle size
Source MapFile mapping minified code back to original source for debugging
SemverSemantic Versioning - MAJOR.MINOR.PATCH versioning scheme

Document Metadata

PropertyValue
Document Version1.0.0
Created2026-02-16
Last Updated2026-02-16
AuthorClaude (Sonnet 4.5)
StatusActive
Task IDA.6.3
Lines of Code~2,800
Word Count~15,000

End of Document