@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.
Files changed (295) hide show
  1. package/.cursorrules +32 -0
  2. package/.github/CODEOWNERS +2 -0
  3. package/.github/actions/setup-node-pnpm/action.yml +47 -0
  4. package/.github/workflow-templates/ci.yml +13 -0
  5. package/.github/workflow-templates/drift-check.yml +10 -0
  6. package/.github/workflow-templates/profiles-validate.yml +16 -0
  7. package/.github/workflow-templates/release.yml +8 -0
  8. package/.github/workflows/ci-reusable.yml +131 -0
  9. package/.github/workflows/drift-check-reusable.yml +23 -0
  10. package/.github/workflows/fixtures.yml +74 -0
  11. package/.github/workflows/profiles-validate-reusable.yml +67 -0
  12. package/.github/workflows/release-reusable.yml +50 -0
  13. package/.vscode/settings.json +23 -0
  14. package/AGENTS.md +130 -0
  15. package/LICENSE +21 -0
  16. package/README.md +1542 -0
  17. package/agents/devkit-maintainer/context.globs +15 -0
  18. package/agents/devkit-maintainer/permissions.yml +17 -0
  19. package/agents/devkit-maintainer/prompt.md +28 -0
  20. package/agents/devkit-maintainer/runbook.md +31 -0
  21. package/agents/docs-crafter/prompt.md +24 -0
  22. package/agents/docs-crafter/runbook.md +18 -0
  23. package/agents/release-manager/context.globs +7 -0
  24. package/agents/release-manager/prompt.md +27 -0
  25. package/agents/release-manager/runbook.md +17 -0
  26. package/agents/test-generator/context.globs +7 -0
  27. package/agents/test-generator/prompt.md +27 -0
  28. package/agents/test-generator/runbook.md +18 -0
  29. package/bin/devkit-architecture.mjs +1225 -0
  30. package/bin/devkit-build-order.mjs +500 -0
  31. package/bin/devkit-check-build-readiness.mjs +222 -0
  32. package/bin/devkit-check-commands.mjs +394 -0
  33. package/bin/devkit-check-configs.mjs +461 -0
  34. package/bin/devkit-check-deprecated.mjs +532 -0
  35. package/bin/devkit-check-duplicates.mjs +431 -0
  36. package/bin/devkit-check-exports.mjs +561 -0
  37. package/bin/devkit-check-imports.mjs +712 -0
  38. package/bin/devkit-check-paths.mjs +670 -0
  39. package/bin/devkit-check-scripts.mjs +335 -0
  40. package/bin/devkit-check-structure.mjs +495 -0
  41. package/bin/devkit-check-types.mjs +450 -0
  42. package/bin/devkit-ci.mjs +261 -0
  43. package/bin/devkit-core-gate.mjs +225 -0
  44. package/bin/devkit-fix-deps.mjs +1169 -0
  45. package/bin/devkit-freshness.mjs +199 -0
  46. package/bin/devkit-health.mjs +489 -0
  47. package/bin/devkit-migrate-configs.mjs +370 -0
  48. package/bin/devkit-paths.mjs +192 -0
  49. package/bin/devkit-stats.mjs +453 -0
  50. package/bin/devkit-sync.mjs +12 -0
  51. package/bin/devkit-tsup-external.mjs +148 -0
  52. package/bin/devkit-types-audit.mjs +615 -0
  53. package/bin/devkit-types-order.mjs +637 -0
  54. package/bin/devkit-validate-naming.mjs +203 -0
  55. package/bin/devkit-visualize.mjs +452 -0
  56. package/bin/kb-devkit-qa-history.mjs +453 -0
  57. package/bin/kb-devkit-qa.mjs +915 -0
  58. package/eslint/node.js +134 -0
  59. package/eslint/react.js +260 -0
  60. package/package.json +186 -0
  61. package/prettier/index.json +10 -0
  62. package/sync/index.mjs +693 -0
  63. package/templates/configs/README.md +306 -0
  64. package/templates/configs/eslint.config.js +27 -0
  65. package/templates/configs/package.json.bin +25 -0
  66. package/templates/configs/package.json.lib +30 -0
  67. package/templates/configs/tsconfig.build.json +15 -0
  68. package/templates/configs/tsconfig.json +9 -0
  69. package/templates/configs/tsup.config.bin.ts +34 -0
  70. package/templates/configs/tsup.config.cli.ts +41 -0
  71. package/templates/configs/tsup.config.dual.ts +46 -0
  72. package/templates/configs/tsup.config.ts +36 -0
  73. package/templates/package-json/README.md +290 -0
  74. package/templates/package-json/package.json.bin.template +40 -0
  75. package/templates/package-json/package.json.template +45 -0
  76. package/tsconfig/base.json +21 -0
  77. package/tsconfig/cli.json +12 -0
  78. package/tsconfig/dist/__tests__/cache.spec.d.ts +6 -0
  79. package/tsconfig/dist/__tests__/cache.spec.d.ts.map +1 -0
  80. package/tsconfig/dist/__tests__/cache.spec.js +85 -0
  81. package/tsconfig/dist/__tests__/cache.spec.js.map +1 -0
  82. package/tsconfig/dist/__tests__/fs-atomic.spec.d.ts +6 -0
  83. package/tsconfig/dist/__tests__/fs-atomic.spec.d.ts.map +1 -0
  84. package/tsconfig/dist/__tests__/fs-atomic.spec.js +153 -0
  85. package/tsconfig/dist/__tests__/fs-atomic.spec.js.map +1 -0
  86. package/tsconfig/dist/__tests__/init-workspace.spec.d.ts +6 -0
  87. package/tsconfig/dist/__tests__/init-workspace.spec.d.ts.map +1 -0
  88. package/tsconfig/dist/__tests__/init-workspace.spec.js +99 -0
  89. package/tsconfig/dist/__tests__/init-workspace.spec.js.map +1 -0
  90. package/tsconfig/dist/__tests__/kb-error.spec.d.ts +6 -0
  91. package/tsconfig/dist/__tests__/kb-error.spec.d.ts.map +1 -0
  92. package/tsconfig/dist/__tests__/kb-error.spec.js +190 -0
  93. package/tsconfig/dist/__tests__/kb-error.spec.js.map +1 -0
  94. package/tsconfig/dist/__tests__/preset-lockfile.spec.d.ts +6 -0
  95. package/tsconfig/dist/__tests__/preset-lockfile.spec.d.ts.map +1 -0
  96. package/tsconfig/dist/__tests__/preset-lockfile.spec.js +142 -0
  97. package/tsconfig/dist/__tests__/preset-lockfile.spec.js.map +1 -0
  98. package/tsconfig/dist/__tests__/product-config-profiles.spec.d.ts +6 -0
  99. package/tsconfig/dist/__tests__/product-config-profiles.spec.d.ts.map +1 -0
  100. package/tsconfig/dist/__tests__/product-config-profiles.spec.js +100 -0
  101. package/tsconfig/dist/__tests__/product-config-profiles.spec.js.map +1 -0
  102. package/tsconfig/dist/__tests__/product-config.spec.d.ts +6 -0
  103. package/tsconfig/dist/__tests__/product-config.spec.d.ts.map +1 -0
  104. package/tsconfig/dist/__tests__/product-config.spec.js +298 -0
  105. package/tsconfig/dist/__tests__/product-config.spec.js.map +1 -0
  106. package/tsconfig/dist/__tests__/runtime.spec.d.ts +2 -0
  107. package/tsconfig/dist/__tests__/runtime.spec.d.ts.map +1 -0
  108. package/tsconfig/dist/__tests__/runtime.spec.js +127 -0
  109. package/tsconfig/dist/__tests__/runtime.spec.js.map +1 -0
  110. package/tsconfig/dist/__tests__/upsert-lockfile.spec.d.ts +6 -0
  111. package/tsconfig/dist/__tests__/upsert-lockfile.spec.d.ts.map +1 -0
  112. package/tsconfig/dist/__tests__/upsert-lockfile.spec.js +251 -0
  113. package/tsconfig/dist/__tests__/upsert-lockfile.spec.js.map +1 -0
  114. package/tsconfig/dist/__tests__/validate-config.spec.d.ts +2 -0
  115. package/tsconfig/dist/__tests__/validate-config.spec.d.ts.map +1 -0
  116. package/tsconfig/dist/__tests__/validate-config.spec.js +14 -0
  117. package/tsconfig/dist/__tests__/validate-config.spec.js.map +1 -0
  118. package/tsconfig/dist/api/init-workspace.d.ts +10 -0
  119. package/tsconfig/dist/api/init-workspace.d.ts.map +1 -0
  120. package/tsconfig/dist/api/init-workspace.js +191 -0
  121. package/tsconfig/dist/api/init-workspace.js.map +1 -0
  122. package/tsconfig/dist/api/product-config.d.ts +21 -0
  123. package/tsconfig/dist/api/product-config.d.ts.map +1 -0
  124. package/tsconfig/dist/api/product-config.js +192 -0
  125. package/tsconfig/dist/api/product-config.js.map +1 -0
  126. package/tsconfig/dist/api/read-config.d.ts +22 -0
  127. package/tsconfig/dist/api/read-config.d.ts.map +1 -0
  128. package/tsconfig/dist/api/read-config.js +105 -0
  129. package/tsconfig/dist/api/read-config.js.map +1 -0
  130. package/tsconfig/dist/api/upsert-lockfile.d.ts +10 -0
  131. package/tsconfig/dist/api/upsert-lockfile.d.ts.map +1 -0
  132. package/tsconfig/dist/api/upsert-lockfile.js +63 -0
  133. package/tsconfig/dist/api/upsert-lockfile.js.map +1 -0
  134. package/tsconfig/dist/cache/fs-cache.d.ts +38 -0
  135. package/tsconfig/dist/cache/fs-cache.d.ts.map +1 -0
  136. package/tsconfig/dist/cache/fs-cache.js +142 -0
  137. package/tsconfig/dist/cache/fs-cache.js.map +1 -0
  138. package/tsconfig/dist/errors/kb-error.d.ts +32 -0
  139. package/tsconfig/dist/errors/kb-error.d.ts.map +1 -0
  140. package/tsconfig/dist/errors/kb-error.js +54 -0
  141. package/tsconfig/dist/errors/kb-error.js.map +1 -0
  142. package/tsconfig/dist/fs/__tests__/fs.spec.d.ts +2 -0
  143. package/tsconfig/dist/fs/__tests__/fs.spec.d.ts.map +1 -0
  144. package/tsconfig/dist/fs/__tests__/fs.spec.js +22 -0
  145. package/tsconfig/dist/fs/__tests__/fs.spec.js.map +1 -0
  146. package/tsconfig/dist/fs/fs.d.ts +6 -0
  147. package/tsconfig/dist/fs/fs.d.ts.map +1 -0
  148. package/tsconfig/dist/fs/fs.js +12 -0
  149. package/tsconfig/dist/fs/fs.js.map +1 -0
  150. package/tsconfig/dist/fs/index.d.ts +2 -0
  151. package/tsconfig/dist/fs/index.d.ts.map +1 -0
  152. package/tsconfig/dist/fs/index.js +2 -0
  153. package/tsconfig/dist/fs/index.js.map +1 -0
  154. package/tsconfig/dist/hash/config-hash.d.ts +17 -0
  155. package/tsconfig/dist/hash/config-hash.d.ts.map +1 -0
  156. package/tsconfig/dist/hash/config-hash.js +55 -0
  157. package/tsconfig/dist/hash/config-hash.js.map +1 -0
  158. package/tsconfig/dist/index.d.ts +5 -0
  159. package/tsconfig/dist/index.d.ts.map +1 -0
  160. package/tsconfig/dist/index.js +5 -0
  161. package/tsconfig/dist/index.js.map +1 -0
  162. package/tsconfig/dist/lockfile/lockfile.d.ts +54 -0
  163. package/tsconfig/dist/lockfile/lockfile.d.ts.map +1 -0
  164. package/tsconfig/dist/lockfile/lockfile.js +141 -0
  165. package/tsconfig/dist/lockfile/lockfile.js.map +1 -0
  166. package/tsconfig/dist/logging/__tests__/logger.spec.d.ts +2 -0
  167. package/tsconfig/dist/logging/__tests__/logger.spec.d.ts.map +1 -0
  168. package/tsconfig/dist/logging/__tests__/logger.spec.js +65 -0
  169. package/tsconfig/dist/logging/__tests__/logger.spec.js.map +1 -0
  170. package/tsconfig/dist/logging/__tests__/redaction.spec.d.ts +2 -0
  171. package/tsconfig/dist/logging/__tests__/redaction.spec.d.ts.map +1 -0
  172. package/tsconfig/dist/logging/__tests__/redaction.spec.js +34 -0
  173. package/tsconfig/dist/logging/__tests__/redaction.spec.js.map +1 -0
  174. package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.d.ts +2 -0
  175. package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.d.ts.map +1 -0
  176. package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.js +90 -0
  177. package/tsconfig/dist/logging/__tests__/sinks-and-env.spec.js.map +1 -0
  178. package/tsconfig/dist/logging/index.d.ts +6 -0
  179. package/tsconfig/dist/logging/index.d.ts.map +1 -0
  180. package/tsconfig/dist/logging/index.js +6 -0
  181. package/tsconfig/dist/logging/index.js.map +1 -0
  182. package/tsconfig/dist/logging/logger.d.ts +9 -0
  183. package/tsconfig/dist/logging/logger.d.ts.map +1 -0
  184. package/tsconfig/dist/logging/logger.js +101 -0
  185. package/tsconfig/dist/logging/logger.js.map +1 -0
  186. package/tsconfig/dist/logging/redaction.d.ts +7 -0
  187. package/tsconfig/dist/logging/redaction.d.ts.map +1 -0
  188. package/tsconfig/dist/logging/redaction.js +23 -0
  189. package/tsconfig/dist/logging/redaction.js.map +1 -0
  190. package/tsconfig/dist/logging/sinks/json.d.ts +4 -0
  191. package/tsconfig/dist/logging/sinks/json.d.ts.map +1 -0
  192. package/tsconfig/dist/logging/sinks/json.js +23 -0
  193. package/tsconfig/dist/logging/sinks/json.js.map +1 -0
  194. package/tsconfig/dist/logging/sinks/stdout.d.ts +3 -0
  195. package/tsconfig/dist/logging/sinks/stdout.d.ts.map +1 -0
  196. package/tsconfig/dist/logging/sinks/stdout.js +24 -0
  197. package/tsconfig/dist/logging/sinks/stdout.js.map +1 -0
  198. package/tsconfig/dist/logging/types/index.d.ts +2 -0
  199. package/tsconfig/dist/logging/types/index.d.ts.map +1 -0
  200. package/tsconfig/dist/logging/types/index.js +2 -0
  201. package/tsconfig/dist/logging/types/index.js.map +1 -0
  202. package/tsconfig/dist/logging/types/types.d.ts +37 -0
  203. package/tsconfig/dist/logging/types/types.d.ts.map +1 -0
  204. package/tsconfig/dist/logging/types/types.js +2 -0
  205. package/tsconfig/dist/logging/types/types.js.map +1 -0
  206. package/tsconfig/dist/merge/layered-merge.d.ts +16 -0
  207. package/tsconfig/dist/merge/layered-merge.d.ts.map +1 -0
  208. package/tsconfig/dist/merge/layered-merge.js +97 -0
  209. package/tsconfig/dist/merge/layered-merge.js.map +1 -0
  210. package/tsconfig/dist/preset/resolve-preset.d.ts +29 -0
  211. package/tsconfig/dist/preset/resolve-preset.d.ts.map +1 -0
  212. package/tsconfig/dist/preset/resolve-preset.js +104 -0
  213. package/tsconfig/dist/preset/resolve-preset.js.map +1 -0
  214. package/tsconfig/dist/repo/__tests__/repo.spec.d.ts +2 -0
  215. package/tsconfig/dist/repo/__tests__/repo.spec.d.ts.map +1 -0
  216. package/tsconfig/dist/repo/__tests__/repo.spec.js +25 -0
  217. package/tsconfig/dist/repo/__tests__/repo.spec.js.map +1 -0
  218. package/tsconfig/dist/repo/index.d.ts +2 -0
  219. package/tsconfig/dist/repo/index.d.ts.map +1 -0
  220. package/tsconfig/dist/repo/index.js +2 -0
  221. package/tsconfig/dist/repo/index.js.map +1 -0
  222. package/tsconfig/dist/repo/repo.d.ts +6 -0
  223. package/tsconfig/dist/repo/repo.d.ts.map +1 -0
  224. package/tsconfig/dist/repo/repo.js +25 -0
  225. package/tsconfig/dist/repo/repo.js.map +1 -0
  226. package/tsconfig/dist/runtime/index.d.ts +2 -0
  227. package/tsconfig/dist/runtime/index.d.ts.map +1 -0
  228. package/tsconfig/dist/runtime/index.js +2 -0
  229. package/tsconfig/dist/runtime/index.js.map +1 -0
  230. package/tsconfig/dist/runtime/runtime.d.ts +46 -0
  231. package/tsconfig/dist/runtime/runtime.d.ts.map +1 -0
  232. package/tsconfig/dist/runtime/runtime.js +126 -0
  233. package/tsconfig/dist/runtime/runtime.js.map +1 -0
  234. package/tsconfig/dist/tsconfig.tools.tsbuildinfo +1 -0
  235. package/tsconfig/dist/tsconfig.tsbuildinfo +1 -0
  236. package/tsconfig/dist/types/index.d.ts +2 -0
  237. package/tsconfig/dist/types/index.d.ts.map +1 -0
  238. package/tsconfig/dist/types/index.js +2 -0
  239. package/tsconfig/dist/types/index.js.map +1 -0
  240. package/tsconfig/dist/types/init.d.ts +34 -0
  241. package/tsconfig/dist/types/init.d.ts.map +1 -0
  242. package/tsconfig/dist/types/init.js +6 -0
  243. package/tsconfig/dist/types/init.js.map +1 -0
  244. package/tsconfig/dist/types/preset.d.ts +27 -0
  245. package/tsconfig/dist/types/preset.d.ts.map +1 -0
  246. package/tsconfig/dist/types/preset.js +6 -0
  247. package/tsconfig/dist/types/preset.js.map +1 -0
  248. package/tsconfig/dist/types/types.d.ts +6 -0
  249. package/tsconfig/dist/types/types.d.ts.map +1 -0
  250. package/tsconfig/dist/types/types.js +2 -0
  251. package/tsconfig/dist/types/types.js.map +1 -0
  252. package/tsconfig/dist/utils/__tests__/env.spec.d.ts +2 -0
  253. package/tsconfig/dist/utils/__tests__/env.spec.d.ts.map +1 -0
  254. package/tsconfig/dist/utils/__tests__/env.spec.js +33 -0
  255. package/tsconfig/dist/utils/__tests__/env.spec.js.map +1 -0
  256. package/tsconfig/dist/utils/env.d.ts +7 -0
  257. package/tsconfig/dist/utils/env.d.ts.map +1 -0
  258. package/tsconfig/dist/utils/env.js +25 -0
  259. package/tsconfig/dist/utils/env.js.map +1 -0
  260. package/tsconfig/dist/utils/fs-atomic.d.ts +15 -0
  261. package/tsconfig/dist/utils/fs-atomic.d.ts.map +1 -0
  262. package/tsconfig/dist/utils/fs-atomic.js +45 -0
  263. package/tsconfig/dist/utils/fs-atomic.js.map +1 -0
  264. package/tsconfig/dist/utils/index.d.ts +3 -0
  265. package/tsconfig/dist/utils/index.d.ts.map +1 -0
  266. package/tsconfig/dist/utils/index.js +3 -0
  267. package/tsconfig/dist/utils/index.js.map +1 -0
  268. package/tsconfig/dist/utils/paths.d.ts +21 -0
  269. package/tsconfig/dist/utils/paths.d.ts.map +1 -0
  270. package/tsconfig/dist/utils/paths.js +32 -0
  271. package/tsconfig/dist/utils/paths.js.map +1 -0
  272. package/tsconfig/dist/utils/product-normalize.d.ts +27 -0
  273. package/tsconfig/dist/utils/product-normalize.d.ts.map +1 -0
  274. package/tsconfig/dist/utils/product-normalize.js +45 -0
  275. package/tsconfig/dist/utils/product-normalize.js.map +1 -0
  276. package/tsconfig/dist/validation/validate-config.d.ts +7 -0
  277. package/tsconfig/dist/validation/validate-config.d.ts.map +1 -0
  278. package/tsconfig/dist/validation/validate-config.js +22 -0
  279. package/tsconfig/dist/validation/validate-config.js.map +1 -0
  280. package/tsconfig/lib.json +13 -0
  281. package/tsconfig/node.json +12 -0
  282. package/tsconfig/react-app.json +8 -0
  283. package/tsconfig/react-lib.json +8 -0
  284. package/tsconfig/test.json +15 -0
  285. package/tsup/bin.js +172 -0
  286. package/tsup/dual.js +155 -0
  287. package/tsup/external-sync.mjs +41 -0
  288. package/tsup/external.mjs +109 -0
  289. package/tsup/node.js +65 -0
  290. package/tsup/react-lib.js +18 -0
  291. package/tsup/sdk.js +118 -0
  292. package/vite/react-app.js +50 -0
  293. package/vitest/node.js +37 -0
  294. package/vitest/react.js +39 -0
  295. package/vitest/vitest-setup.ts +9 -0
package/tsup/dual.js ADDED
@@ -0,0 +1,155 @@
1
+ import { defineConfig } from 'tsup'
2
+ import { readTsupExternalSync } from './external-sync.mjs'
3
+ import { readFileSync } from 'node:fs'
4
+ import { join } from 'node:path'
5
+
6
+ /**
7
+ * Dual Build Preset (ESM + CJS)
8
+ *
9
+ * ## Overview
10
+ *
11
+ * This preset builds BOTH ESM and CJS formats for maximum compatibility.
12
+ * Use this for libraries that need to support both modern ESM imports
13
+ * and legacy CJS requires (e.g., when bundling with tools like esbuild/rollup).
14
+ *
15
+ * ## Features
16
+ *
17
+ * - **Format**: ESM + CJS (dual exports)
18
+ * - **Output**: dist/index.js (ESM) + dist/index.cjs (CJS)
19
+ * - **Types**: dist/index.d.ts (shared for both formats)
20
+ * - **Externals**: All workspace packages + node_modules
21
+ * - **Tree-shaking**: Enabled
22
+ *
23
+ * ## When to use
24
+ *
25
+ * ✅ **Use for:**
26
+ * - Core libraries used by CLI binaries (core-runtime, core-sys, etc.)
27
+ * - Shared utilities that might be bundled by build tools
28
+ * - Packages that need to work in both ESM and CJS environments
29
+ * - Any library that might be consumed by bundlers (esbuild, rollup, webpack)
30
+ *
31
+ * ❌ **DO NOT use for:**
32
+ * - CLI binaries (use tsup/bin.js instead)
33
+ * - Pure ESM-only packages (use tsup/node.js instead)
34
+ * - React libraries (use tsup/react-lib.js instead)
35
+ *
36
+ * ## Package.json Setup
37
+ *
38
+ * Update your package.json with dual exports:
39
+ *
40
+ * ```json
41
+ * {
42
+ * "type": "module",
43
+ * "main": "./dist/index.cjs",
44
+ * "module": "./dist/index.js",
45
+ * "types": "./dist/index.d.ts",
46
+ * "exports": {
47
+ * ".": {
48
+ * "import": "./dist/index.js",
49
+ * "require": "./dist/index.cjs",
50
+ * "types": "./dist/index.d.ts"
51
+ * }
52
+ * }
53
+ * }
54
+ * ```
55
+ *
56
+ * ## Basic Example
57
+ *
58
+ * ```typescript
59
+ * // tsup.config.ts
60
+ * import { defineConfig } from 'tsup';
61
+ * import dualPreset from '@kb-labs/devkit/tsup/dual.js';
62
+ *
63
+ * export default defineConfig({
64
+ * ...dualPreset,
65
+ * entry: { index: 'src/index.ts' },
66
+ * });
67
+ * ```
68
+ *
69
+ * ## Multi-Entry Example
70
+ *
71
+ * ```typescript
72
+ * // tsup.config.ts
73
+ * import { defineConfig } from 'tsup';
74
+ * import dualPreset from '@kb-labs/devkit/tsup/dual.js';
75
+ *
76
+ * export default defineConfig({
77
+ * ...dualPreset,
78
+ * entry: {
79
+ * index: 'src/index.ts',
80
+ * utils: 'src/utils.ts',
81
+ * helpers: 'src/helpers.ts',
82
+ * },
83
+ * });
84
+ * ```
85
+ *
86
+ * ## Bundle Size Impact
87
+ *
88
+ * - Dual format roughly doubles the dist/ size (ESM + CJS)
89
+ * - Types (.d.ts) are shared between both formats
90
+ * - Example: ESM (50KB) + CJS (50KB) + Types (10KB) = 110KB total
91
+ *
92
+ * ## Why Dual Build?
93
+ *
94
+ * - **ESM**: Modern Node.js, better tree-shaking, top-level await
95
+ * - **CJS**: Legacy bundlers, better compatibility, easier to bundle
96
+ * - **Both**: Maximum compatibility across all environments
97
+ *
98
+ * ## Migration from ESM-only
99
+ *
100
+ * 1. Change import from `tsup/node.js` to `tsup/dual.js`
101
+ * 2. Update package.json exports (see example above)
102
+ * 3. Rebuild: `pnpm build`
103
+ * 4. Verify: Check dist/ has both .js (ESM) and .cjs (CJS)
104
+ */
105
+
106
+ function resolveExternalDependencies() {
107
+ try {
108
+ const pkgPath = join(process.cwd(), 'package.json')
109
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
110
+ const deps = Object.keys(pkg.dependencies ?? {})
111
+ const peerDeps = Object.keys(pkg.peerDependencies ?? {})
112
+ return Array.from(new Set([...deps, ...peerDeps]))
113
+ } catch {
114
+ return []
115
+ }
116
+ }
117
+
118
+ function getExternal() {
119
+ // Try to use generated tsup.external.json if available
120
+ try {
121
+ const generated = readTsupExternalSync()
122
+ if (generated.length > 0) {
123
+ return generated
124
+ }
125
+ } catch {
126
+ // If reading fails, fallback to package.json
127
+ }
128
+ // Fallback to reading package.json directly
129
+ return resolveExternalDependencies()
130
+ }
131
+
132
+ // Pre-compute external list once at module load time
133
+ const externalList = getExternal()
134
+
135
+ export default defineConfig({
136
+ entry: ['src/index.ts'],
137
+ format: ['esm', 'cjs'], // ⬅️ Build both ESM and CJS
138
+ target: 'es2022',
139
+ sourcemap: true,
140
+ clean: true,
141
+ dts: true, // Generate .d.ts (shared for both formats)
142
+ treeshake: true,
143
+ minify: false,
144
+ outDir: 'dist',
145
+ splitting: false, // Disable splitting for better CJS compatibility
146
+ skipNodeModulesBundle: true,
147
+ shims: false,
148
+ // Mark all node_modules packages as external (including transitive deps)
149
+ noExternal: [],
150
+ external: [
151
+ /^[^./]|^\.[^./]|^\.\.[^/]/, // All node_modules packages (not relative paths)
152
+ ...externalList, // Explicitly listed packages (workspace + local deps)
153
+ /^@kb-labs\//, // Force all @kb-labs packages to be external
154
+ ],
155
+ })
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFileSync, existsSync } from 'node:fs';
4
+ import { join, dirname, resolve } from 'node:path';
5
+
6
+ /**
7
+ * Synchronously reads tsup.external.json and returns the externals array.
8
+ * Searches up the directory tree to find the file (similar to tsconfig.paths.json).
9
+ * This is a synchronous version for use in tsup.config.ts files.
10
+ *
11
+ * @param {string} [cwd] - Current working directory (defaults to process.cwd())
12
+ * @returns {string[]} Array of external package names
13
+ */
14
+ export function readTsupExternalSync(cwd = process.cwd()) {
15
+ // Search up the directory tree for tsup.external.json
16
+ // Limit search to prevent infinite loops
17
+ let current = resolve(cwd);
18
+ let prev = null;
19
+ let depth = 0;
20
+ const maxDepth = 20; // Safety limit
21
+
22
+ while (current !== prev && depth < maxDepth) {
23
+ const externalPath = join(current, 'tsup.external.json');
24
+ if (existsSync(externalPath)) {
25
+ try {
26
+ const content = readFileSync(externalPath, 'utf8');
27
+ const data = JSON.parse(content);
28
+ return Array.isArray(data.externals) ? data.externals : [];
29
+ } catch {
30
+ // If file is invalid, continue searching up
31
+ }
32
+ }
33
+ prev = current;
34
+ current = dirname(current);
35
+ depth++;
36
+ }
37
+
38
+ // If file not found, return empty array
39
+ return [];
40
+ }
41
+
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { dirname, resolve, join } from 'node:path';
4
+ import { promises as fs } from 'node:fs';
5
+ import process from 'node:process';
6
+ import { glob } from 'glob';
7
+ import { parse as parseYaml } from 'yaml';
8
+
9
+ async function exists(path) {
10
+ try {
11
+ await fs.access(path);
12
+ return true;
13
+ } catch {
14
+ return false;
15
+ }
16
+ }
17
+
18
+ async function readJson(path) {
19
+ return JSON.parse(await fs.readFile(path, 'utf8'));
20
+ }
21
+
22
+ async function findWorkspaceConfigs(startDir) {
23
+ const roots = [];
24
+ let current = resolve(startDir);
25
+ let prev = null;
26
+ while (current !== prev) {
27
+ const wsFile = join(current, 'pnpm-workspace.yaml');
28
+ if (await exists(wsFile)) {
29
+ try {
30
+ const yaml = await fs.readFile(wsFile, 'utf8');
31
+ const data = parseYaml(yaml);
32
+ const patterns = Array.isArray(data?.packages) ? data.packages.filter(Boolean) : [];
33
+ roots.push({ dir: current, patterns });
34
+ } catch (error) {
35
+ // Silent fail in library mode
36
+ }
37
+ }
38
+ prev = current;
39
+ current = dirname(current);
40
+ }
41
+ return roots;
42
+ }
43
+
44
+ async function collectWorkspacePackages(workspaceRoots) {
45
+ const packages = new Set();
46
+
47
+ for (const { dir, patterns } of workspaceRoots) {
48
+ for (const pattern of patterns) {
49
+ if (!pattern || typeof pattern !== 'string') {continue;}
50
+ const searchPattern = pattern.endsWith('/') ? `${pattern}package.json` : join(pattern, 'package.json');
51
+ const matches = await glob(searchPattern, {
52
+ cwd: dir,
53
+ dot: false,
54
+ nodir: true,
55
+ ignore: ['**/node_modules/**', '**/dist/**'],
56
+ });
57
+ for (const match of matches) {
58
+ try {
59
+ const pkgPath = resolve(dir, match);
60
+ const pkg = await readJson(pkgPath);
61
+ if (!pkg?.name || typeof pkg.name !== 'string') {continue;}
62
+ if (!pkg.name.startsWith('@kb-labs/')) {continue;}
63
+ packages.add(pkg.name);
64
+ } catch {
65
+ // Silent fail
66
+ }
67
+ }
68
+ }
69
+ }
70
+
71
+ return Array.from(packages).sort();
72
+ }
73
+
74
+ /**
75
+ * Resolves external dependencies for tsup bundling.
76
+ * Returns an array of package names that should be marked as external:
77
+ * - All @kb-labs/* workspace packages
78
+ * - All dependencies and peerDependencies from the current package.json
79
+ *
80
+ * @param {string} [cwd] - Current working directory (defaults to process.cwd())
81
+ * @returns {Promise<string[]>} Array of external package names
82
+ */
83
+ export async function resolveTsupExternal(cwd = process.cwd()) {
84
+ const workspaceRoots = await findWorkspaceConfigs(cwd);
85
+ if (!workspaceRoots.length) {
86
+ return [];
87
+ }
88
+
89
+ const workspacePackages = await collectWorkspacePackages(workspaceRoots);
90
+
91
+ // Also include dependencies from current package.json
92
+ const pkgPath = join(cwd, 'package.json');
93
+ let localDeps = [];
94
+ if (await exists(pkgPath)) {
95
+ try {
96
+ const pkg = await readJson(pkgPath);
97
+ const deps = Object.keys(pkg.dependencies ?? {});
98
+ const peerDeps = Object.keys(pkg.peerDependencies ?? {});
99
+ localDeps = Array.from(new Set([...deps, ...peerDeps]));
100
+ } catch {
101
+ // Silent fail
102
+ }
103
+ }
104
+
105
+ // Combine workspace packages and local dependencies, remove duplicates
106
+ return Array.from(new Set([...workspacePackages, ...localDeps])).sort();
107
+ }
108
+
109
+
package/tsup/node.js ADDED
@@ -0,0 +1,65 @@
1
+ import { defineConfig } from 'tsup'
2
+ import { readTsupExternalSync } from './external-sync.mjs'
3
+ import { readFileSync } from 'node:fs'
4
+ import { join } from 'node:path'
5
+
6
+ function resolveExternalDependencies() {
7
+ try {
8
+ const pkgPath = join(process.cwd(), 'package.json')
9
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
10
+ const deps = Object.keys(pkg.dependencies ?? {})
11
+ const peerDeps = Object.keys(pkg.peerDependencies ?? {})
12
+ return Array.from(new Set([...deps, ...peerDeps]))
13
+ } catch {
14
+ return []
15
+ }
16
+ }
17
+
18
+ function getExternal() {
19
+ // Try to use generated tsup.external.json if available
20
+ // This file should be generated by kb-devkit-tsup-external and contains:
21
+ // - All @kb-labs/* workspace packages (found via pnpm-workspace.yaml)
22
+ // - All dependencies and peerDependencies from package.json
23
+ try {
24
+ const generated = readTsupExternalSync()
25
+ if (generated.length > 0) {
26
+ return generated
27
+ }
28
+ } catch {
29
+ // If reading fails, fallback to package.json
30
+ }
31
+ // Fallback to reading package.json directly
32
+ return resolveExternalDependencies()
33
+ }
34
+
35
+ // Pre-compute external list once at module load time
36
+ const externalList = getExternal()
37
+
38
+ export default defineConfig({
39
+ entry: ['src/index.ts'],
40
+ format: ['esm'],
41
+ target: 'es2022',
42
+ sourcemap: true,
43
+ clean: true,
44
+ dts: true,
45
+ treeshake: true,
46
+ minify: false,
47
+ outDir: 'dist',
48
+ splitting: false,
49
+ skipNodeModulesBundle: true,
50
+ shims: false,
51
+ // Mark all node_modules packages as external (including transitive deps)
52
+ // Use noExternal: [] to prevent bundling any node_modules packages
53
+ // This is more reliable than regex for ensuring transitive deps stay external
54
+ noExternal: [],
55
+ external: [
56
+ /^[^./]|^\.[^./]|^\.\.[^/]/, // All node_modules packages (not relative paths)
57
+ // All workspace packages from tsup.external.json (generated by kb-devkit-tsup-external)
58
+ // This includes all @kb-labs/* packages found in pnpm-workspace.yaml
59
+ // plus dependencies/peerDependencies from package.json
60
+ ...externalList, // Explicitly listed packages (workspace + local deps)
61
+ // Force all @kb-labs packages to be external
62
+ // This catches packages linked via "link:" protocol that might not be in the workspace scan
63
+ /^@kb-labs\//,
64
+ ],
65
+ })
@@ -0,0 +1,18 @@
1
+ import { defineConfig } from 'tsup'
2
+
3
+ export default defineConfig({
4
+ entry: ['src/index.ts'],
5
+ format: ['esm'],
6
+ target: 'es2022',
7
+ jsx: 'preserve',
8
+ sourcemap: true,
9
+ clean: true,
10
+ dts: true,
11
+ treeshake: true,
12
+ minify: false,
13
+ outDir: 'dist',
14
+ splitting: false,
15
+ skipNodeModulesBundle: true,
16
+ shims: false
17
+ })
18
+
package/tsup/sdk.js ADDED
@@ -0,0 +1,118 @@
1
+ import { defineConfig } from 'tsup';
2
+
3
+ /**
4
+ * Preset for SDK/facade builds.
5
+ *
6
+ * ## Overview
7
+ *
8
+ * This preset bundles ALL @kb-labs/* workspace packages into a single
9
+ * self-contained SDK package. Consumers only need `@kb-labs/sdk` as dependency.
10
+ *
11
+ * ## Features
12
+ *
13
+ * - **Format**: ESM (modern, tree-shakeable)
14
+ * - **Bundling**: ALL @kb-labs/* packages included
15
+ * - **Externals**: Node.js built-ins + problematic CJS packages (execa, cross-spawn)
16
+ * - **Types**: Full .d.ts generation
17
+ * - **Multiple entry points**: Supports cli, helpers, manifest, etc.
18
+ *
19
+ * ## When to use
20
+ *
21
+ * ✅ **Use for:**
22
+ * - SDK packages that re-export from multiple workspace packages
23
+ * - Facade packages that need to be self-contained
24
+ * - Packages where consumers shouldn't need to install transitive deps
25
+ *
26
+ * ❌ **DO NOT use for:**
27
+ * - Regular libraries (use tsup/node.js instead)
28
+ * - Binaries (use tsup/bin.js instead)
29
+ * - Packages that should keep workspace deps as peer deps
30
+ *
31
+ * ## Example
32
+ *
33
+ * ```typescript
34
+ * import { defineConfig } from 'tsup';
35
+ * import sdkPreset from '@kb-labs/devkit/tsup/sdk.js';
36
+ *
37
+ * export default defineConfig({
38
+ * ...sdkPreset,
39
+ * tsconfig: 'tsconfig.build.json',
40
+ * entry: [
41
+ * 'src/index.ts',
42
+ * 'src/cli.ts',
43
+ * 'src/helpers.ts',
44
+ * ],
45
+ * });
46
+ * ```
47
+ *
48
+ * ## Bundle Size
49
+ *
50
+ * Expect 500KB-2MB per entry point depending on how much is imported.
51
+ * This is normal for self-contained SDK packages.
52
+ */
53
+
54
+ // List of Node.js built-in modules (without node: prefix)
55
+ const NODE_BUILTINS = [
56
+ 'assert', 'async_hooks', 'buffer', 'child_process', 'cluster', 'console',
57
+ 'constants', 'crypto', 'dgram', 'diagnostics_channel', 'dns', 'domain',
58
+ 'events', 'fs', 'http', 'http2', 'https', 'inspector', 'module', 'net',
59
+ 'os', 'path', 'perf_hooks', 'process', 'punycode', 'querystring', 'readline',
60
+ 'repl', 'stream', 'string_decoder', 'sys', 'timers', 'tls', 'trace_events',
61
+ 'tty', 'url', 'util', 'v8', 'vm', 'wasi', 'worker_threads', 'zlib',
62
+ ];
63
+
64
+ // CJS packages that use dynamic require and can't be bundled into ESM
65
+ // These must remain external and be installed as dependencies
66
+ const PROBLEMATIC_CJS_PACKAGES = [
67
+ 'execa',
68
+ 'cross-spawn',
69
+ 'glob',
70
+ 'minimatch',
71
+ 'fast-glob',
72
+ 'globby',
73
+ 'picomatch',
74
+ 'micromatch',
75
+ 'better-sqlite3',
76
+ 'sharp',
77
+ 'esbuild',
78
+ 'yaml', // Uses require('process') internally
79
+ ];
80
+
81
+ export default defineConfig({
82
+ format: ['esm'],
83
+ target: 'es2022',
84
+ platform: 'node',
85
+ sourcemap: true,
86
+ clean: true,
87
+ dts: true,
88
+ treeshake: true,
89
+ minify: false,
90
+ outDir: 'dist',
91
+ splitting: false,
92
+
93
+ // Bundle @kb-labs/* workspace packages, but NOT problematic CJS packages
94
+ noExternal: [/^@kb-labs\//],
95
+
96
+ // esbuild plugin to mark Node.js built-ins as external
97
+ esbuildPlugins: [
98
+ {
99
+ name: 'node-builtins-external',
100
+ setup(build) {
101
+ // Match Node.js built-ins with or without node: prefix, including subpaths
102
+ const builtinRegex = new RegExp(`^(node:)?(${NODE_BUILTINS.join('|')})(\\/.*)?$`);
103
+
104
+ build.onResolve({ filter: builtinRegex }, (args) => {
105
+ return { path: args.path, external: true };
106
+ });
107
+ },
108
+ },
109
+ ],
110
+
111
+ // Externalize: Node.js built-ins, native modules, and problematic CJS packages
112
+ external: [
113
+ // Native modules that can't be bundled
114
+ 'fsevents',
115
+ // CJS packages with dynamic require
116
+ ...PROBLEMATIC_CJS_PACKAGES,
117
+ ],
118
+ });
@@ -0,0 +1,50 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+ import path from 'path'
4
+
5
+ /**
6
+ * Vite config factory for React SPA applications
7
+ * @param {object} options - Configuration options
8
+ * @param {string} [options.entry='index.html'] - Entry HTML file
9
+ * @param {string} [options.outDir='dist'] - Output directory
10
+ * @returns {import('vite').UserConfig} Vite configuration
11
+ */
12
+ export default function createReactAppConfig(options = {}) {
13
+ const {
14
+ entry = 'index.html',
15
+ outDir = 'dist',
16
+ root = process.cwd(),
17
+ } = options
18
+
19
+ return defineConfig({
20
+ root,
21
+ publicDir: 'public',
22
+ build: {
23
+ outDir,
24
+ sourcemap: true,
25
+ minify: false,
26
+ target: 'es2022',
27
+ },
28
+ plugins: [
29
+ react({
30
+ jsxRuntime: 'automatic',
31
+ }),
32
+ ],
33
+ resolve: {
34
+ alias: {
35
+ '@': path.resolve(root, 'src'),
36
+ },
37
+ },
38
+ server: {
39
+ port: 3000,
40
+ open: false,
41
+ },
42
+ preview: {
43
+ port: 3000,
44
+ },
45
+ })
46
+ }
47
+
48
+ // Export a default config for convenience
49
+ export { defineConfig }
50
+
package/vitest/node.js ADDED
@@ -0,0 +1,37 @@
1
+ import { defineConfig } from 'vitest/config'
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: 'node',
6
+ globals: false,
7
+ reporters: ['default'],
8
+ include: ['**/*.{test,spec}.?(c|m)[jt]s?(x)'],
9
+ exclude: [
10
+ '**/node_modules/**',
11
+ '**/dist/**',
12
+ '**/cypress/**',
13
+ '**/.{idea,git,cache,output,temp}/**'
14
+ ],
15
+ coverage: {
16
+ enabled: false,
17
+ provider: 'v8',
18
+ all: true,
19
+ reportsDirectory: './coverage',
20
+ reporter: ['text', 'html', 'lcov'],
21
+ exclude: [
22
+ '**/node_modules/**',
23
+ '**/dist/**',
24
+ '**/__tests__/**',
25
+ '**/*.d.ts',
26
+ '**/types.ts',
27
+ '**/types/**'
28
+ ],
29
+ thresholds: {
30
+ statements: 90,
31
+ branches: 85,
32
+ functions: 90,
33
+ lines: 90
34
+ }
35
+ }
36
+ }
37
+ })
@@ -0,0 +1,39 @@
1
+ import { defineConfig } from 'vitest/config'
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: 'jsdom',
6
+ globals: false,
7
+ reporters: ['default'],
8
+ include: ['**/*.{test,spec}.?(c|m)[jt]s?(x)'],
9
+ exclude: [
10
+ '**/node_modules/**',
11
+ '**/dist/**',
12
+ '**/cypress/**',
13
+ '**/.{idea,git,cache,output,temp}/**'
14
+ ],
15
+ setupFiles: ['./vitest-setup.ts'],
16
+ coverage: {
17
+ enabled: false,
18
+ provider: 'v8',
19
+ all: true,
20
+ reportsDirectory: './coverage',
21
+ reporter: ['text', 'html', 'lcov'],
22
+ exclude: [
23
+ '**/node_modules/**',
24
+ '**/dist/**',
25
+ '**/__tests__/**',
26
+ '**/*.d.ts',
27
+ '**/types.ts',
28
+ '**/types/**'
29
+ ],
30
+ thresholds: {
31
+ statements: 90,
32
+ branches: 85,
33
+ functions: 90,
34
+ lines: 90
35
+ }
36
+ }
37
+ }
38
+ })
39
+
@@ -0,0 +1,9 @@
1
+ // Vitest setup file for React testing
2
+ import { afterEach } from 'vitest'
3
+ import { cleanup } from '@testing-library/react'
4
+
5
+ // Cleanup after each test
6
+ afterEach(() => {
7
+ cleanup()
8
+ })
9
+