@kb-labs/devkit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursorrules +32 -0
- package/.github/CODEOWNERS +2 -0
- package/.github/actions/setup-node-pnpm/action.yml +47 -0
- package/.github/workflow-templates/ci.yml +13 -0
- package/.github/workflow-templates/drift-check.yml +10 -0
- package/.github/workflow-templates/profiles-validate.yml +16 -0
- package/.github/workflow-templates/release.yml +8 -0
- package/.github/workflows/ci-reusable.yml +131 -0
- package/.github/workflows/drift-check-reusable.yml +23 -0
- package/.github/workflows/fixtures.yml +74 -0
- package/.github/workflows/profiles-validate-reusable.yml +67 -0
- package/.github/workflows/release-reusable.yml +50 -0
- package/.vscode/settings.json +23 -0
- package/AGENTS.md +130 -0
- package/LICENSE +21 -0
- package/README.md +1542 -0
- package/agents/devkit-maintainer/context.globs +15 -0
- package/agents/devkit-maintainer/permissions.yml +17 -0
- package/agents/devkit-maintainer/prompt.md +28 -0
- package/agents/devkit-maintainer/runbook.md +31 -0
- package/agents/docs-crafter/prompt.md +24 -0
- package/agents/docs-crafter/runbook.md +18 -0
- package/agents/release-manager/context.globs +7 -0
- package/agents/release-manager/prompt.md +27 -0
- package/agents/release-manager/runbook.md +17 -0
- package/agents/test-generator/context.globs +7 -0
- package/agents/test-generator/prompt.md +27 -0
- package/agents/test-generator/runbook.md +18 -0
- package/bin/devkit-architecture.mjs +1225 -0
- package/bin/devkit-build-order.mjs +500 -0
- package/bin/devkit-check-build-readiness.mjs +222 -0
- package/bin/devkit-check-commands.mjs +394 -0
- package/bin/devkit-check-configs.mjs +461 -0
- package/bin/devkit-check-deprecated.mjs +532 -0
- package/bin/devkit-check-duplicates.mjs +431 -0
- package/bin/devkit-check-exports.mjs +561 -0
- package/bin/devkit-check-imports.mjs +712 -0
- package/bin/devkit-check-paths.mjs +670 -0
- package/bin/devkit-check-scripts.mjs +335 -0
- package/bin/devkit-check-structure.mjs +495 -0
- package/bin/devkit-check-types.mjs +450 -0
- package/bin/devkit-ci.mjs +261 -0
- package/bin/devkit-core-gate.mjs +225 -0
- package/bin/devkit-fix-deps.mjs +1169 -0
- package/bin/devkit-freshness.mjs +199 -0
- package/bin/devkit-health.mjs +489 -0
- package/bin/devkit-migrate-configs.mjs +370 -0
- package/bin/devkit-paths.mjs +192 -0
- package/bin/devkit-stats.mjs +453 -0
- package/bin/devkit-sync.mjs +12 -0
- package/bin/devkit-tsup-external.mjs +148 -0
- package/bin/devkit-types-audit.mjs +615 -0
- package/bin/devkit-types-order.mjs +637 -0
- package/bin/devkit-validate-naming.mjs +203 -0
- package/bin/devkit-visualize.mjs +452 -0
- package/bin/kb-devkit-qa-history.mjs +453 -0
- package/bin/kb-devkit-qa.mjs +915 -0
- package/eslint/node.js +134 -0
- package/eslint/react.js +260 -0
- package/package.json +186 -0
- package/prettier/index.json +10 -0
- package/sync/index.mjs +693 -0
- package/templates/configs/README.md +306 -0
- package/templates/configs/eslint.config.js +27 -0
- package/templates/configs/package.json.bin +25 -0
- package/templates/configs/package.json.lib +30 -0
- package/templates/configs/tsconfig.build.json +15 -0
- package/templates/configs/tsconfig.json +9 -0
- package/templates/configs/tsup.config.bin.ts +34 -0
- package/templates/configs/tsup.config.cli.ts +41 -0
- package/templates/configs/tsup.config.dual.ts +46 -0
- package/templates/configs/tsup.config.ts +36 -0
- package/templates/package-json/README.md +290 -0
- package/templates/package-json/package.json.bin.template +40 -0
- package/templates/package-json/package.json.template +45 -0
- package/tsconfig/base.json +21 -0
- package/tsconfig/cli.json +12 -0
- package/tsconfig/dist/__tests__/cache.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/cache.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/cache.spec.js +85 -0
- package/tsconfig/dist/__tests__/cache.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/fs-atomic.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/fs-atomic.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/fs-atomic.spec.js +153 -0
- package/tsconfig/dist/__tests__/fs-atomic.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/init-workspace.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/init-workspace.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/init-workspace.spec.js +99 -0
- package/tsconfig/dist/__tests__/init-workspace.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/kb-error.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/kb-error.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/kb-error.spec.js +190 -0
- package/tsconfig/dist/__tests__/kb-error.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/preset-lockfile.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/preset-lockfile.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/preset-lockfile.spec.js +142 -0
- package/tsconfig/dist/__tests__/preset-lockfile.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/product-config-profiles.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/product-config-profiles.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/product-config-profiles.spec.js +100 -0
- package/tsconfig/dist/__tests__/product-config-profiles.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/product-config.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/product-config.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/product-config.spec.js +298 -0
- package/tsconfig/dist/__tests__/product-config.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/runtime.spec.d.ts +2 -0
- package/tsconfig/dist/__tests__/runtime.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/runtime.spec.js +127 -0
- package/tsconfig/dist/__tests__/runtime.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/upsert-lockfile.spec.d.ts +6 -0
- package/tsconfig/dist/__tests__/upsert-lockfile.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/upsert-lockfile.spec.js +251 -0
- package/tsconfig/dist/__tests__/upsert-lockfile.spec.js.map +1 -0
- package/tsconfig/dist/__tests__/validate-config.spec.d.ts +2 -0
- package/tsconfig/dist/__tests__/validate-config.spec.d.ts.map +1 -0
- package/tsconfig/dist/__tests__/validate-config.spec.js +14 -0
- package/tsconfig/dist/__tests__/validate-config.spec.js.map +1 -0
- package/tsconfig/dist/api/init-workspace.d.ts +10 -0
- package/tsconfig/dist/api/init-workspace.d.ts.map +1 -0
- package/tsconfig/dist/api/init-workspace.js +191 -0
- package/tsconfig/dist/api/init-workspace.js.map +1 -0
- package/tsconfig/dist/api/product-config.d.ts +21 -0
- package/tsconfig/dist/api/product-config.d.ts.map +1 -0
- package/tsconfig/dist/api/product-config.js +192 -0
- package/tsconfig/dist/api/product-config.js.map +1 -0
- package/tsconfig/dist/api/read-config.d.ts +22 -0
- package/tsconfig/dist/api/read-config.d.ts.map +1 -0
- package/tsconfig/dist/api/read-config.js +105 -0
- package/tsconfig/dist/api/read-config.js.map +1 -0
- package/tsconfig/dist/api/upsert-lockfile.d.ts +10 -0
- package/tsconfig/dist/api/upsert-lockfile.d.ts.map +1 -0
- package/tsconfig/dist/api/upsert-lockfile.js +63 -0
- package/tsconfig/dist/api/upsert-lockfile.js.map +1 -0
- package/tsconfig/dist/cache/fs-cache.d.ts +38 -0
- package/tsconfig/dist/cache/fs-cache.d.ts.map +1 -0
- package/tsconfig/dist/cache/fs-cache.js +142 -0
- package/tsconfig/dist/cache/fs-cache.js.map +1 -0
- package/tsconfig/dist/errors/kb-error.d.ts +32 -0
- package/tsconfig/dist/errors/kb-error.d.ts.map +1 -0
- package/tsconfig/dist/errors/kb-error.js +54 -0
- package/tsconfig/dist/errors/kb-error.js.map +1 -0
- package/tsconfig/dist/fs/__tests__/fs.spec.d.ts +2 -0
- package/tsconfig/dist/fs/__tests__/fs.spec.d.ts.map +1 -0
- package/tsconfig/dist/fs/__tests__/fs.spec.js +22 -0
- package/tsconfig/dist/fs/__tests__/fs.spec.js.map +1 -0
- package/tsconfig/dist/fs/fs.d.ts +6 -0
- package/tsconfig/dist/fs/fs.d.ts.map +1 -0
- package/tsconfig/dist/fs/fs.js +12 -0
- package/tsconfig/dist/fs/fs.js.map +1 -0
- package/tsconfig/dist/fs/index.d.ts +2 -0
- package/tsconfig/dist/fs/index.d.ts.map +1 -0
- package/tsconfig/dist/fs/index.js +2 -0
- package/tsconfig/dist/fs/index.js.map +1 -0
- package/tsconfig/dist/hash/config-hash.d.ts +17 -0
- package/tsconfig/dist/hash/config-hash.d.ts.map +1 -0
- package/tsconfig/dist/hash/config-hash.js +55 -0
- package/tsconfig/dist/hash/config-hash.js.map +1 -0
- package/tsconfig/dist/index.d.ts +5 -0
- package/tsconfig/dist/index.d.ts.map +1 -0
- package/tsconfig/dist/index.js +5 -0
- package/tsconfig/dist/index.js.map +1 -0
- package/tsconfig/dist/lockfile/lockfile.d.ts +54 -0
- package/tsconfig/dist/lockfile/lockfile.d.ts.map +1 -0
- package/tsconfig/dist/lockfile/lockfile.js +141 -0
- package/tsconfig/dist/lockfile/lockfile.js.map +1 -0
- package/tsconfig/dist/logging/__tests__/logger.spec.d.ts +2 -0
- package/tsconfig/dist/logging/__tests__/logger.spec.d.ts.map +1 -0
- package/tsconfig/dist/logging/__tests__/logger.spec.js +65 -0
- package/tsconfig/dist/logging/__tests__/logger.spec.js.map +1 -0
- package/tsconfig/dist/logging/__tests__/redaction.spec.d.ts +2 -0
- package/tsconfig/dist/logging/__tests__/redaction.spec.d.ts.map +1 -0
- package/tsconfig/dist/logging/__tests__/redaction.spec.js +34 -0
- package/tsconfig/dist/logging/__tests__/redaction.spec.js.map +1 -0
- package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.d.ts +2 -0
- package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.d.ts.map +1 -0
- package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.js +90 -0
- package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.js.map +1 -0
- package/tsconfig/dist/logging/index.d.ts +6 -0
- package/tsconfig/dist/logging/index.d.ts.map +1 -0
- package/tsconfig/dist/logging/index.js +6 -0
- package/tsconfig/dist/logging/index.js.map +1 -0
- package/tsconfig/dist/logging/logger.d.ts +9 -0
- package/tsconfig/dist/logging/logger.d.ts.map +1 -0
- package/tsconfig/dist/logging/logger.js +101 -0
- package/tsconfig/dist/logging/logger.js.map +1 -0
- package/tsconfig/dist/logging/redaction.d.ts +7 -0
- package/tsconfig/dist/logging/redaction.d.ts.map +1 -0
- package/tsconfig/dist/logging/redaction.js +23 -0
- package/tsconfig/dist/logging/redaction.js.map +1 -0
- package/tsconfig/dist/logging/sinks/json.d.ts +4 -0
- package/tsconfig/dist/logging/sinks/json.d.ts.map +1 -0
- package/tsconfig/dist/logging/sinks/json.js +23 -0
- package/tsconfig/dist/logging/sinks/json.js.map +1 -0
- package/tsconfig/dist/logging/sinks/stdout.d.ts +3 -0
- package/tsconfig/dist/logging/sinks/stdout.d.ts.map +1 -0
- package/tsconfig/dist/logging/sinks/stdout.js +24 -0
- package/tsconfig/dist/logging/sinks/stdout.js.map +1 -0
- package/tsconfig/dist/logging/types/index.d.ts +2 -0
- package/tsconfig/dist/logging/types/index.d.ts.map +1 -0
- package/tsconfig/dist/logging/types/index.js +2 -0
- package/tsconfig/dist/logging/types/index.js.map +1 -0
- package/tsconfig/dist/logging/types/types.d.ts +37 -0
- package/tsconfig/dist/logging/types/types.d.ts.map +1 -0
- package/tsconfig/dist/logging/types/types.js +2 -0
- package/tsconfig/dist/logging/types/types.js.map +1 -0
- package/tsconfig/dist/merge/layered-merge.d.ts +16 -0
- package/tsconfig/dist/merge/layered-merge.d.ts.map +1 -0
- package/tsconfig/dist/merge/layered-merge.js +97 -0
- package/tsconfig/dist/merge/layered-merge.js.map +1 -0
- package/tsconfig/dist/preset/resolve-preset.d.ts +29 -0
- package/tsconfig/dist/preset/resolve-preset.d.ts.map +1 -0
- package/tsconfig/dist/preset/resolve-preset.js +104 -0
- package/tsconfig/dist/preset/resolve-preset.js.map +1 -0
- package/tsconfig/dist/repo/__tests__/repo.spec.d.ts +2 -0
- package/tsconfig/dist/repo/__tests__/repo.spec.d.ts.map +1 -0
- package/tsconfig/dist/repo/__tests__/repo.spec.js +25 -0
- package/tsconfig/dist/repo/__tests__/repo.spec.js.map +1 -0
- package/tsconfig/dist/repo/index.d.ts +2 -0
- package/tsconfig/dist/repo/index.d.ts.map +1 -0
- package/tsconfig/dist/repo/index.js +2 -0
- package/tsconfig/dist/repo/index.js.map +1 -0
- package/tsconfig/dist/repo/repo.d.ts +6 -0
- package/tsconfig/dist/repo/repo.d.ts.map +1 -0
- package/tsconfig/dist/repo/repo.js +25 -0
- package/tsconfig/dist/repo/repo.js.map +1 -0
- package/tsconfig/dist/runtime/index.d.ts +2 -0
- package/tsconfig/dist/runtime/index.d.ts.map +1 -0
- package/tsconfig/dist/runtime/index.js +2 -0
- package/tsconfig/dist/runtime/index.js.map +1 -0
- package/tsconfig/dist/runtime/runtime.d.ts +46 -0
- package/tsconfig/dist/runtime/runtime.d.ts.map +1 -0
- package/tsconfig/dist/runtime/runtime.js +126 -0
- package/tsconfig/dist/runtime/runtime.js.map +1 -0
- package/tsconfig/dist/tsconfig.tools.tsbuildinfo +1 -0
- package/tsconfig/dist/tsconfig.tsbuildinfo +1 -0
- package/tsconfig/dist/types/index.d.ts +2 -0
- package/tsconfig/dist/types/index.d.ts.map +1 -0
- package/tsconfig/dist/types/index.js +2 -0
- package/tsconfig/dist/types/index.js.map +1 -0
- package/tsconfig/dist/types/init.d.ts +34 -0
- package/tsconfig/dist/types/init.d.ts.map +1 -0
- package/tsconfig/dist/types/init.js +6 -0
- package/tsconfig/dist/types/init.js.map +1 -0
- package/tsconfig/dist/types/preset.d.ts +27 -0
- package/tsconfig/dist/types/preset.d.ts.map +1 -0
- package/tsconfig/dist/types/preset.js +6 -0
- package/tsconfig/dist/types/preset.js.map +1 -0
- package/tsconfig/dist/types/types.d.ts +6 -0
- package/tsconfig/dist/types/types.d.ts.map +1 -0
- package/tsconfig/dist/types/types.js +2 -0
- package/tsconfig/dist/types/types.js.map +1 -0
- package/tsconfig/dist/utils/__tests__/env.spec.d.ts +2 -0
- package/tsconfig/dist/utils/__tests__/env.spec.d.ts.map +1 -0
- package/tsconfig/dist/utils/__tests__/env.spec.js +33 -0
- package/tsconfig/dist/utils/__tests__/env.spec.js.map +1 -0
- package/tsconfig/dist/utils/env.d.ts +7 -0
- package/tsconfig/dist/utils/env.d.ts.map +1 -0
- package/tsconfig/dist/utils/env.js +25 -0
- package/tsconfig/dist/utils/env.js.map +1 -0
- package/tsconfig/dist/utils/fs-atomic.d.ts +15 -0
- package/tsconfig/dist/utils/fs-atomic.d.ts.map +1 -0
- package/tsconfig/dist/utils/fs-atomic.js +45 -0
- package/tsconfig/dist/utils/fs-atomic.js.map +1 -0
- package/tsconfig/dist/utils/index.d.ts +3 -0
- package/tsconfig/dist/utils/index.d.ts.map +1 -0
- package/tsconfig/dist/utils/index.js +3 -0
- package/tsconfig/dist/utils/index.js.map +1 -0
- package/tsconfig/dist/utils/paths.d.ts +21 -0
- package/tsconfig/dist/utils/paths.d.ts.map +1 -0
- package/tsconfig/dist/utils/paths.js +32 -0
- package/tsconfig/dist/utils/paths.js.map +1 -0
- package/tsconfig/dist/utils/product-normalize.d.ts +27 -0
- package/tsconfig/dist/utils/product-normalize.d.ts.map +1 -0
- package/tsconfig/dist/utils/product-normalize.js +45 -0
- package/tsconfig/dist/utils/product-normalize.js.map +1 -0
- package/tsconfig/dist/validation/validate-config.d.ts +7 -0
- package/tsconfig/dist/validation/validate-config.d.ts.map +1 -0
- package/tsconfig/dist/validation/validate-config.js +22 -0
- package/tsconfig/dist/validation/validate-config.js.map +1 -0
- package/tsconfig/lib.json +13 -0
- package/tsconfig/node.json +12 -0
- package/tsconfig/react-app.json +8 -0
- package/tsconfig/react-lib.json +8 -0
- package/tsconfig/test.json +15 -0
- package/tsup/bin.js +172 -0
- package/tsup/dual.js +155 -0
- package/tsup/external-sync.mjs +41 -0
- package/tsup/external.mjs +109 -0
- package/tsup/node.js +65 -0
- package/tsup/react-lib.js +18 -0
- package/tsup/sdk.js +118 -0
- package/vite/react-app.js +50 -0
- package/vitest/node.js +37 -0
- package/vitest/react.js +39 -0
- package/vitest/vitest-setup.ts +9 -0
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
# Package.json Templates
|
|
2
|
+
|
|
3
|
+
Standard package.json templates for KB Labs monorepo packages.
|
|
4
|
+
|
|
5
|
+
## Philosophy
|
|
6
|
+
|
|
7
|
+
**Minimum Required Standard:**
|
|
8
|
+
- All packages MUST have the sections and scripts defined in these templates
|
|
9
|
+
- Packages CAN add MORE sections and scripts as needed
|
|
10
|
+
- Packages CANNOT have LESS than the standard (enforced by `kb-devkit-check-structure`)
|
|
11
|
+
|
|
12
|
+
## Templates
|
|
13
|
+
|
|
14
|
+
### Library Package (Standard)
|
|
15
|
+
**File:** `package.json.template`
|
|
16
|
+
|
|
17
|
+
**Use for:** Most packages (90%)
|
|
18
|
+
- Core libraries
|
|
19
|
+
- Contracts packages
|
|
20
|
+
- Utilities
|
|
21
|
+
- API clients
|
|
22
|
+
|
|
23
|
+
**Required sections:**
|
|
24
|
+
- `name`, `version`, `type`, `private`, `description`
|
|
25
|
+
- `main`, `types`, `exports`
|
|
26
|
+
- `files`, `sideEffects`
|
|
27
|
+
- `scripts` (8 required scripts)
|
|
28
|
+
- `dependencies`, `devDependencies`
|
|
29
|
+
- `engines`
|
|
30
|
+
|
|
31
|
+
**Required scripts:**
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"clean": "rimraf dist",
|
|
35
|
+
"build": "tsup --config tsup.config.ts",
|
|
36
|
+
"dev": "tsup --config tsup.config.ts --watch",
|
|
37
|
+
"lint": "eslint src --ext .ts",
|
|
38
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
39
|
+
"type-check": "tsc --noEmit",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:watch": "vitest"
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Binary Package
|
|
46
|
+
**File:** `package.json.bin.template`
|
|
47
|
+
|
|
48
|
+
**Use for:** CLI binaries
|
|
49
|
+
- `cli-bin`
|
|
50
|
+
- Daemon executables
|
|
51
|
+
- Standalone tools
|
|
52
|
+
|
|
53
|
+
**Differences from library:**
|
|
54
|
+
- Has `bin` field instead of `main`/`types`
|
|
55
|
+
- No `exports` (binaries don't export modules)
|
|
56
|
+
|
|
57
|
+
## Required DevDependencies
|
|
58
|
+
|
|
59
|
+
**All packages MUST have:**
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"@kb-labs/devkit": "workspace:*",
|
|
63
|
+
"@types/node": "^24.3.3",
|
|
64
|
+
"rimraf": "^6.0.1",
|
|
65
|
+
"tsup": "^8.5.0",
|
|
66
|
+
"typescript": "^5.6.3",
|
|
67
|
+
"vitest": "^3.2.4"
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Why these are required:**
|
|
72
|
+
- `@kb-labs/devkit` - Build system presets (tsup, eslint, tsconfig)
|
|
73
|
+
- `@types/node` - TypeScript types for Node.js
|
|
74
|
+
- `rimraf` - Cross-platform file deletion (clean script)
|
|
75
|
+
- `tsup` - Build tool (used in all packages)
|
|
76
|
+
- `typescript` - TypeScript compiler (type-check script)
|
|
77
|
+
- `vitest` - Test runner (test scripts)
|
|
78
|
+
|
|
79
|
+
## Optional Additions
|
|
80
|
+
|
|
81
|
+
**Packages CAN add:**
|
|
82
|
+
- Additional scripts (e.g., `test:benchmarks`, `test:all`)
|
|
83
|
+
- Additional dependencies
|
|
84
|
+
- Additional `exports` entries (subpath exports)
|
|
85
|
+
- Custom fields (e.g., `repository`, `keywords`)
|
|
86
|
+
|
|
87
|
+
**Examples:**
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"scripts": {
|
|
91
|
+
// ✅ Required scripts (must have all 8)
|
|
92
|
+
"clean": "rimraf dist",
|
|
93
|
+
"build": "tsup --config tsup.config.ts",
|
|
94
|
+
"dev": "tsup --config tsup.config.ts --watch",
|
|
95
|
+
"lint": "eslint src --ext .ts",
|
|
96
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
97
|
+
"type-check": "tsc --noEmit",
|
|
98
|
+
"test": "vitest run",
|
|
99
|
+
"test:watch": "vitest",
|
|
100
|
+
|
|
101
|
+
// ✅ Additional scripts (OK to add)
|
|
102
|
+
"test:benchmarks": "vitest run tests/benchmarks",
|
|
103
|
+
"test:all": "vitest run && vitest run tests/benchmarks",
|
|
104
|
+
"pretype-check": "pnpm --filter @kb-labs/package build"
|
|
105
|
+
},
|
|
106
|
+
"exports": {
|
|
107
|
+
// ✅ Required default export
|
|
108
|
+
".": {
|
|
109
|
+
"import": "./dist/index.js",
|
|
110
|
+
"types": "./dist/index.d.ts"
|
|
111
|
+
},
|
|
112
|
+
// ✅ Additional exports (OK to add)
|
|
113
|
+
"./analyzer": {
|
|
114
|
+
"import": "./dist/analyzer/index.js",
|
|
115
|
+
"types": "./dist/analyzer/index.d.ts"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Validation
|
|
122
|
+
|
|
123
|
+
**Check package.json structure:**
|
|
124
|
+
```bash
|
|
125
|
+
npx kb-devkit-check-structure
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**What it checks:**
|
|
129
|
+
- ✅ All required sections present
|
|
130
|
+
- ✅ All required scripts present
|
|
131
|
+
- ✅ All required devDependencies present
|
|
132
|
+
- ✅ Correct `type: "module"`
|
|
133
|
+
- ✅ Correct `engines` versions
|
|
134
|
+
- ⚠️ Warns about missing optional sections (repository, keywords)
|
|
135
|
+
|
|
136
|
+
**Auto-fix missing scripts:**
|
|
137
|
+
```bash
|
|
138
|
+
npx kb-devkit-check-structure --fix
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Common Issues
|
|
142
|
+
|
|
143
|
+
### Missing Scripts
|
|
144
|
+
|
|
145
|
+
**Problem:**
|
|
146
|
+
```bash
|
|
147
|
+
pnpm -r run test
|
|
148
|
+
# ❌ Error: "test" script not found in @kb-labs/some-package
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**Solution:**
|
|
152
|
+
```bash
|
|
153
|
+
# Check which packages are missing scripts
|
|
154
|
+
npx kb-devkit-check-structure
|
|
155
|
+
|
|
156
|
+
# Auto-add missing scripts
|
|
157
|
+
npx kb-devkit-check-structure --fix
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Wrong Script Names
|
|
161
|
+
|
|
162
|
+
**Problem:**
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"scripts": {
|
|
166
|
+
"test:unit": "vitest run" // ❌ Wrong, should be "test"
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Solution:**
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"scripts": {
|
|
175
|
+
"test": "vitest run", // ✅ Required
|
|
176
|
+
"test:unit": "vitest run" // ✅ Additional is OK
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Missing DevDependencies
|
|
182
|
+
|
|
183
|
+
**Problem:**
|
|
184
|
+
```bash
|
|
185
|
+
pnpm run build
|
|
186
|
+
# ❌ Error: Cannot find module 'tsup'
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Solution:**
|
|
190
|
+
```bash
|
|
191
|
+
# Check missing devDependencies
|
|
192
|
+
npx kb-devkit-check-structure --verbose
|
|
193
|
+
|
|
194
|
+
# Install missing deps
|
|
195
|
+
pnpm install
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Migration Guide
|
|
199
|
+
|
|
200
|
+
**For existing packages:**
|
|
201
|
+
|
|
202
|
+
1. **Check current status:**
|
|
203
|
+
```bash
|
|
204
|
+
npx kb-devkit-check-structure --package=@kb-labs/your-package
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
2. **Add missing scripts:**
|
|
208
|
+
```bash
|
|
209
|
+
npx kb-devkit-check-structure --package=@kb-labs/your-package --fix
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
3. **Verify:**
|
|
213
|
+
```bash
|
|
214
|
+
cd packages/your-package
|
|
215
|
+
pnpm run build
|
|
216
|
+
pnpm run test
|
|
217
|
+
pnpm run lint
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
**For new packages:**
|
|
221
|
+
|
|
222
|
+
1. **Copy template:**
|
|
223
|
+
```bash
|
|
224
|
+
cp kb-labs-devkit/templates/package-json/package.json.template package.json
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
2. **Update metadata:**
|
|
228
|
+
- Change `name` to your package name
|
|
229
|
+
- Update `description`
|
|
230
|
+
- Add dependencies
|
|
231
|
+
|
|
232
|
+
3. **Verify:**
|
|
233
|
+
```bash
|
|
234
|
+
npx kb-devkit-check-structure --package=@kb-labs/your-package
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Design Decisions
|
|
238
|
+
|
|
239
|
+
### Why "More is OK, Less is Not"?
|
|
240
|
+
|
|
241
|
+
**Consistency vs Flexibility:**
|
|
242
|
+
- **Minimum standard** ensures all packages work with monorepo scripts (`pnpm -r run build`)
|
|
243
|
+
- **Additional scripts** allow package-specific needs (benchmarks, integration tests)
|
|
244
|
+
- **Enforced baseline** prevents "missing script" errors in CI/CD
|
|
245
|
+
|
|
246
|
+
### Why These Exact Scripts?
|
|
247
|
+
|
|
248
|
+
**Based on actual usage:**
|
|
249
|
+
- `clean` - Required before rebuild (prevents stale artifacts)
|
|
250
|
+
- `build` - Used by CI, pre-commit hooks, dependencies
|
|
251
|
+
- `dev` - Developer workflow (watch mode)
|
|
252
|
+
- `lint` - Code quality checks
|
|
253
|
+
- `lint:fix` - Auto-fix linting issues
|
|
254
|
+
- `type-check` - TypeScript validation (separate from build)
|
|
255
|
+
- `test` - CI test runs
|
|
256
|
+
- `test:watch` - Developer workflow (TDD)
|
|
257
|
+
|
|
258
|
+
### Why Vitest (not Jest)?
|
|
259
|
+
|
|
260
|
+
**Modern, fast, ESM-native:**
|
|
261
|
+
- ✅ Native ESM support (no config needed)
|
|
262
|
+
- ✅ 10x faster than Jest for small tests
|
|
263
|
+
- ✅ Compatible with Vite ecosystem
|
|
264
|
+
- ✅ Built-in TypeScript support
|
|
265
|
+
- ✅ Watch mode with UI
|
|
266
|
+
|
|
267
|
+
**Migration from Jest:**
|
|
268
|
+
```bash
|
|
269
|
+
# Replace jest with vitest
|
|
270
|
+
pnpm remove jest @types/jest
|
|
271
|
+
pnpm add -D vitest
|
|
272
|
+
|
|
273
|
+
# Update scripts
|
|
274
|
+
# "test": "jest" → "test": "vitest run"
|
|
275
|
+
# "test:watch": "jest --watch" → "test:watch": "vitest"
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Future Improvements
|
|
279
|
+
|
|
280
|
+
**Planned features:**
|
|
281
|
+
- Auto-migration tool (`kb-devkit-migrate-package-json`)
|
|
282
|
+
- Version alignment checker (ensure all packages use same vitest/tsup versions)
|
|
283
|
+
- Script validation (ensure scripts actually work)
|
|
284
|
+
- Dependency audit (find unused deps)
|
|
285
|
+
|
|
286
|
+
## See Also
|
|
287
|
+
|
|
288
|
+
- [Config Templates](../configs/README.md) - tsup, eslint, tsconfig templates
|
|
289
|
+
- [DevKit README](../../README.md) - Complete DevKit documentation
|
|
290
|
+
- [Check Structure Tool](../../bin/devkit-check-structure.mjs) - Validation script
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Binary package template for @kb-labs CLI binaries",
|
|
3
|
+
"name": "@kb-labs/package-bin",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": true,
|
|
7
|
+
"description": "CLI binary package",
|
|
8
|
+
"bin": {
|
|
9
|
+
"kb": "./dist/bin.cjs"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md",
|
|
14
|
+
"LICENSE"
|
|
15
|
+
],
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"scripts": {
|
|
18
|
+
"clean": "rimraf dist",
|
|
19
|
+
"build": "tsup --config tsup.config.ts",
|
|
20
|
+
"dev": "tsup --config tsup.config.ts --watch",
|
|
21
|
+
"lint": "eslint src --ext .ts",
|
|
22
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
23
|
+
"type-check": "tsc --noEmit",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"test:watch": "vitest"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@kb-labs/devkit": "workspace:*",
|
|
30
|
+
"@types/node": "^24.3.3",
|
|
31
|
+
"rimraf": "^6.0.1",
|
|
32
|
+
"tsup": "^8.5.0",
|
|
33
|
+
"typescript": "^5.6.3",
|
|
34
|
+
"vitest": "^3.2.4"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=20.0.0",
|
|
38
|
+
"pnpm": ">=9.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Standard package.json template for @kb-labs packages",
|
|
3
|
+
"name": "@kb-labs/package-name",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": true,
|
|
7
|
+
"description": "Package description",
|
|
8
|
+
"main": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"scripts": {
|
|
23
|
+
"clean": "rimraf dist",
|
|
24
|
+
"build": "tsup --config tsup.config.ts",
|
|
25
|
+
"dev": "tsup --config tsup.config.ts --watch",
|
|
26
|
+
"lint": "eslint src --ext .ts",
|
|
27
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
28
|
+
"type-check": "tsc --noEmit",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@kb-labs/devkit": "workspace:*",
|
|
35
|
+
"@types/node": "^24.3.3",
|
|
36
|
+
"rimraf": "^6.0.1",
|
|
37
|
+
"tsup": "^8.5.0",
|
|
38
|
+
"typescript": "^5.6.3",
|
|
39
|
+
"vitest": "^3.2.4"
|
|
40
|
+
},
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=20.0.0",
|
|
43
|
+
"pnpm": ">=9.0.0"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"module": "ESNext",
|
|
6
|
+
"moduleResolution": "bundler",
|
|
7
|
+
"lib": [
|
|
8
|
+
"ES2022"
|
|
9
|
+
],
|
|
10
|
+
"strict": true,
|
|
11
|
+
"noUncheckedIndexedAccess": true,
|
|
12
|
+
"noImplicitOverride": true,
|
|
13
|
+
"noFallthroughCasesInSwitch": true,
|
|
14
|
+
"resolveJsonModule": true,
|
|
15
|
+
"esModuleInterop": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"forceConsistentCasingInFileNames": true,
|
|
18
|
+
"isolatedModules": true,
|
|
19
|
+
"sourceMap": true
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.spec.d.ts","sourceRoot":"","sources":["../../../../kb-labs-core/packages/config/src/__tests__/cache.spec.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/core/config/__tests__/cache.spec.ts
|
|
3
|
+
* Tests for filesystem cache functionality
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import { promises as fsp } from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
import { fsCache, clearCaches } from '../cache/fs-cache';
|
|
10
|
+
import { readConfigFile } from '../api/read-config';
|
|
11
|
+
describe('Filesystem Cache', () => {
|
|
12
|
+
let testDir;
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
testDir = path.join(tmpdir(), `kb-labs-cache-test-${Date.now()}`);
|
|
15
|
+
await fsp.mkdir(testDir, { recursive: true });
|
|
16
|
+
clearCaches();
|
|
17
|
+
});
|
|
18
|
+
afterEach(async () => {
|
|
19
|
+
await fsp.rm(testDir, { recursive: true, force: true });
|
|
20
|
+
clearCaches();
|
|
21
|
+
});
|
|
22
|
+
describe('Cache Operations', () => {
|
|
23
|
+
it('should cache file reads', async () => {
|
|
24
|
+
const configPath = path.join(testDir, 'test-config.json');
|
|
25
|
+
const configData = { test: 'value' };
|
|
26
|
+
await fsp.writeFile(configPath, JSON.stringify(configData, null, 2));
|
|
27
|
+
// First read - should not be cached
|
|
28
|
+
const result1 = await readConfigFile(configPath);
|
|
29
|
+
expect(result1.data).toEqual(configData);
|
|
30
|
+
// Second read - should be cached
|
|
31
|
+
const result2 = await readConfigFile(configPath);
|
|
32
|
+
expect(result2.data).toEqual(configData);
|
|
33
|
+
});
|
|
34
|
+
it('should invalidate cache on file change', async () => {
|
|
35
|
+
const configPath = path.join(testDir, 'test-config.json');
|
|
36
|
+
const configData1 = { test: 'value1' };
|
|
37
|
+
const configData2 = { test: 'value2' };
|
|
38
|
+
await fsp.writeFile(configPath, JSON.stringify(configData1, null, 2));
|
|
39
|
+
// First read
|
|
40
|
+
const result1 = await readConfigFile(configPath);
|
|
41
|
+
expect(result1.data).toEqual(configData1);
|
|
42
|
+
// Small delay to ensure mtime changes
|
|
43
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
44
|
+
// Modify file
|
|
45
|
+
await fsp.writeFile(configPath, JSON.stringify(configData2, null, 2));
|
|
46
|
+
// Second read - should get new data
|
|
47
|
+
const result2 = await readConfigFile(configPath);
|
|
48
|
+
expect(result2.data).toEqual(configData2);
|
|
49
|
+
});
|
|
50
|
+
it('should clear caches', async () => {
|
|
51
|
+
const configPath = path.join(testDir, 'test-config.json');
|
|
52
|
+
const configData = { test: 'value' };
|
|
53
|
+
await fsp.writeFile(configPath, JSON.stringify(configData, null, 2));
|
|
54
|
+
// Read to populate cache
|
|
55
|
+
await readConfigFile(configPath);
|
|
56
|
+
// Clear cache
|
|
57
|
+
clearCaches();
|
|
58
|
+
// Verify cache is cleared by checking stats
|
|
59
|
+
const stats = fsCache.getStats();
|
|
60
|
+
expect(stats.size).toBe(0);
|
|
61
|
+
});
|
|
62
|
+
it('should handle YAML files', async () => {
|
|
63
|
+
const configPath = path.join(testDir, 'test-config.yaml');
|
|
64
|
+
const configData = { test: 'value', nested: { key: 'value' } };
|
|
65
|
+
await fsp.writeFile(configPath, `test: value\nnested:\n key: value`);
|
|
66
|
+
const result = await readConfigFile(configPath);
|
|
67
|
+
expect(result.format).toBe('yaml');
|
|
68
|
+
expect(result.data).toEqual(configData);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
describe('Cache Statistics', () => {
|
|
72
|
+
it('should track cache size', async () => {
|
|
73
|
+
const configPath1 = path.join(testDir, 'config1.json');
|
|
74
|
+
const configPath2 = path.join(testDir, 'config2.json');
|
|
75
|
+
await fsp.writeFile(configPath1, JSON.stringify({ test1: 'value1' }));
|
|
76
|
+
await fsp.writeFile(configPath2, JSON.stringify({ test2: 'value2' }));
|
|
77
|
+
// Read both files
|
|
78
|
+
await readConfigFile(configPath1);
|
|
79
|
+
await readConfigFile(configPath2);
|
|
80
|
+
const stats = fsCache.getStats();
|
|
81
|
+
expect(stats.size).toBeGreaterThan(0);
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
//# sourceMappingURL=cache.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.spec.js","sourceRoot":"","sources":["../../../../kb-labs-core/packages/config/src/__tests__/cache.spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs-atomic.spec.d.ts","sourceRoot":"","sources":["../../../../kb-labs-core/packages/config/src/__tests__/fs-atomic.spec.ts"],"names":[],"mappings":"AAAA;;;GAGG"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module @kb-labs/core/config/__tests__/fs-atomic.spec.ts
|
|
3
|
+
* Tests for atomic file operations and workspace validation
|
|
4
|
+
*/
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
6
|
+
import { promises as fsp } from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { tmpdir } from 'node:os';
|
|
9
|
+
import { writeFileAtomic, ensureWithinWorkspace } from '../utils/fs-atomic';
|
|
10
|
+
import { KbError } from '../errors/kb-error';
|
|
11
|
+
describe('fs-atomic', () => {
|
|
12
|
+
let testDir;
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
testDir = path.join(tmpdir(), `kb-labs-fs-atomic-test-${Date.now()}`);
|
|
15
|
+
await fsp.mkdir(testDir, { recursive: true });
|
|
16
|
+
});
|
|
17
|
+
afterEach(async () => {
|
|
18
|
+
await fsp.rm(testDir, { recursive: true, force: true });
|
|
19
|
+
});
|
|
20
|
+
describe('writeFileAtomic', () => {
|
|
21
|
+
it('should write file atomically', async () => {
|
|
22
|
+
const filePath = path.join(testDir, 'test.txt');
|
|
23
|
+
const content = 'Hello, World!';
|
|
24
|
+
await writeFileAtomic(filePath, content);
|
|
25
|
+
const result = await fsp.readFile(filePath, 'utf-8');
|
|
26
|
+
expect(result).toBe(content);
|
|
27
|
+
});
|
|
28
|
+
it('should create parent directories', async () => {
|
|
29
|
+
const filePath = path.join(testDir, 'nested', 'deep', 'test.txt');
|
|
30
|
+
const content = 'Nested content';
|
|
31
|
+
await writeFileAtomic(filePath, content);
|
|
32
|
+
const result = await fsp.readFile(filePath, 'utf-8');
|
|
33
|
+
expect(result).toBe(content);
|
|
34
|
+
});
|
|
35
|
+
it('should handle Uint8Array data', async () => {
|
|
36
|
+
const filePath = path.join(testDir, 'binary.txt');
|
|
37
|
+
const content = new Uint8Array([72, 101, 108, 108, 111]); // "Hello"
|
|
38
|
+
await writeFileAtomic(filePath, content);
|
|
39
|
+
const result = await fsp.readFile(filePath);
|
|
40
|
+
expect(Array.from(result)).toEqual(Array.from(content));
|
|
41
|
+
});
|
|
42
|
+
it('should clean up temp file on error', async () => {
|
|
43
|
+
const filePath = path.join(testDir, 'test.txt');
|
|
44
|
+
// Mock fs.writeFile to throw error
|
|
45
|
+
const originalWriteFile = fsp.writeFile;
|
|
46
|
+
vi.spyOn(fsp, 'writeFile').mockRejectedValueOnce(new Error('Write failed'));
|
|
47
|
+
await expect(writeFileAtomic(filePath, 'content')).rejects.toThrow('Write failed');
|
|
48
|
+
// Check that temp file was cleaned up
|
|
49
|
+
const files = await fsp.readdir(testDir);
|
|
50
|
+
const tempFiles = files.filter(f => f.startsWith('test.txt.tmp-'));
|
|
51
|
+
expect(tempFiles).toHaveLength(0);
|
|
52
|
+
// Restore original function
|
|
53
|
+
vi.restoreAllMocks();
|
|
54
|
+
});
|
|
55
|
+
it('should handle cleanup errors gracefully', async () => {
|
|
56
|
+
const filePath = path.join(testDir, 'test.txt');
|
|
57
|
+
// Mock fs.writeFile to throw error
|
|
58
|
+
const originalWriteFile = fsp.writeFile;
|
|
59
|
+
const originalUnlink = fsp.unlink;
|
|
60
|
+
vi.spyOn(fsp, 'writeFile').mockRejectedValueOnce(new Error('Write failed'));
|
|
61
|
+
vi.spyOn(fsp, 'unlink').mockRejectedValueOnce(new Error('Cleanup failed'));
|
|
62
|
+
// Should not throw cleanup error
|
|
63
|
+
await expect(writeFileAtomic(filePath, 'content')).rejects.toThrow('Write failed');
|
|
64
|
+
// Restore original functions
|
|
65
|
+
vi.restoreAllMocks();
|
|
66
|
+
});
|
|
67
|
+
it('should overwrite existing file', async () => {
|
|
68
|
+
const filePath = path.join(testDir, 'existing.txt');
|
|
69
|
+
const originalContent = 'Original content';
|
|
70
|
+
const newContent = 'New content';
|
|
71
|
+
// Create original file
|
|
72
|
+
await fsp.writeFile(filePath, originalContent);
|
|
73
|
+
// Overwrite atomically
|
|
74
|
+
await writeFileAtomic(filePath, newContent);
|
|
75
|
+
const result = await fsp.readFile(filePath, 'utf-8');
|
|
76
|
+
expect(result).toBe(newContent);
|
|
77
|
+
});
|
|
78
|
+
it('should generate unique temp file names', async () => {
|
|
79
|
+
const filePath = path.join(testDir, 'test.txt');
|
|
80
|
+
const content = 'content';
|
|
81
|
+
// Mock Date.now and Math.random for predictable temp names
|
|
82
|
+
const mockDateNow = vi.spyOn(Date, 'now').mockReturnValue(1234567890);
|
|
83
|
+
const mockMathRandom = vi.spyOn(Math, 'random').mockReturnValue(0.5);
|
|
84
|
+
await writeFileAtomic(filePath, content);
|
|
85
|
+
// Check that temp file name was generated correctly
|
|
86
|
+
const files = await fsp.readdir(testDir);
|
|
87
|
+
expect(files).toContain('test.txt');
|
|
88
|
+
mockDateNow.mockRestore();
|
|
89
|
+
mockMathRandom.mockRestore();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
describe('ensureWithinWorkspace', () => {
|
|
93
|
+
it('should allow paths within workspace', () => {
|
|
94
|
+
const workspaceRoot = '/workspace';
|
|
95
|
+
const targetPath = '/workspace/subdir/file.txt';
|
|
96
|
+
expect(() => ensureWithinWorkspace(targetPath, workspaceRoot)).not.toThrow();
|
|
97
|
+
});
|
|
98
|
+
it('should allow relative paths within workspace', () => {
|
|
99
|
+
const workspaceRoot = '/workspace';
|
|
100
|
+
const targetPath = path.join(workspaceRoot, 'subdir', 'file.txt');
|
|
101
|
+
expect(() => ensureWithinWorkspace(targetPath, workspaceRoot)).not.toThrow();
|
|
102
|
+
});
|
|
103
|
+
it('should throw error for paths outside workspace', () => {
|
|
104
|
+
const workspaceRoot = '/workspace';
|
|
105
|
+
const targetPath = '/outside/file.txt';
|
|
106
|
+
expect(() => ensureWithinWorkspace(targetPath, workspaceRoot)).toThrow(KbError);
|
|
107
|
+
});
|
|
108
|
+
it('should throw error for paths that escape via ..', () => {
|
|
109
|
+
const workspaceRoot = '/workspace';
|
|
110
|
+
const targetPath = '/workspace/../outside/file.txt';
|
|
111
|
+
expect(() => ensureWithinWorkspace(targetPath, workspaceRoot)).toThrow(KbError);
|
|
112
|
+
});
|
|
113
|
+
it('should handle edge case with trailing slashes', () => {
|
|
114
|
+
const workspaceRoot = '/workspace/';
|
|
115
|
+
const targetPath = '/workspace/file.txt';
|
|
116
|
+
expect(() => ensureWithinWorkspace(targetPath, workspaceRoot)).not.toThrow();
|
|
117
|
+
});
|
|
118
|
+
it('should handle same directory as workspace', () => {
|
|
119
|
+
const workspaceRoot = '/workspace';
|
|
120
|
+
const targetPath = '/workspace';
|
|
121
|
+
expect(() => ensureWithinWorkspace(targetPath, workspaceRoot)).not.toThrow();
|
|
122
|
+
});
|
|
123
|
+
it('should throw error with correct error details', () => {
|
|
124
|
+
const workspaceRoot = '/workspace';
|
|
125
|
+
const targetPath = '/outside/file.txt';
|
|
126
|
+
expect(() => ensureWithinWorkspace(targetPath, workspaceRoot)).toThrow(expect.objectContaining({
|
|
127
|
+
code: 'ERR_PATH_OUTSIDE_WORKSPACE',
|
|
128
|
+
message: 'Refusing to write outside workspace: /outside/file.txt',
|
|
129
|
+
hint: 'Check cwd or use a relative path within the workspace',
|
|
130
|
+
meta: {
|
|
131
|
+
targetPath: '/outside/file.txt',
|
|
132
|
+
workspaceRoot: '/workspace',
|
|
133
|
+
},
|
|
134
|
+
}));
|
|
135
|
+
});
|
|
136
|
+
it('should handle empty workspace root', () => {
|
|
137
|
+
const workspaceRoot = '';
|
|
138
|
+
const targetPath = '/some/path';
|
|
139
|
+
expect(() => ensureWithinWorkspace(targetPath, workspaceRoot)).toThrow(KbError);
|
|
140
|
+
});
|
|
141
|
+
it('should handle empty target path', () => {
|
|
142
|
+
const workspaceRoot = '/workspace';
|
|
143
|
+
const targetPath = '';
|
|
144
|
+
expect(() => ensureWithinWorkspace(targetPath, workspaceRoot)).toThrow(KbError);
|
|
145
|
+
});
|
|
146
|
+
it('should resolve paths correctly', () => {
|
|
147
|
+
const workspaceRoot = '/workspace';
|
|
148
|
+
const targetPath = path.resolve(workspaceRoot, 'subdir', '..', 'subdir', 'file.txt');
|
|
149
|
+
expect(() => ensureWithinWorkspace(targetPath, workspaceRoot)).not.toThrow();
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
//# sourceMappingURL=fs-atomic.spec.js.map
|