@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
@@ -0,0 +1,500 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @kb-labs/devkit - Build Order Calculator
5
+ *
6
+ * Calculates the correct build order for all packages based on dependencies.
7
+ * Uses topological sort to determine which packages need to be built first.
8
+ *
9
+ * Usage:
10
+ * kb-devkit-build-order # Show build order
11
+ * kb-devkit-build-order --layers # Show parallel build layers
12
+ * kb-devkit-build-order --package=cli # Build order for specific package
13
+ * kb-devkit-build-order --json # Output JSON
14
+ * kb-devkit-build-order --script # Generate build script
15
+ */
16
+
17
+ import fs from 'node:fs';
18
+ import path from 'node:path';
19
+ import { fileURLToPath } from 'node:url';
20
+
21
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
22
+
23
+ // ANSI colors
24
+ const colors = {
25
+ reset: '\x1b[0m',
26
+ red: '\x1b[31m',
27
+ green: '\x1b[32m',
28
+ yellow: '\x1b[33m',
29
+ blue: '\x1b[34m',
30
+ magenta: '\x1b[35m',
31
+ cyan: '\x1b[36m',
32
+ gray: '\x1b[90m',
33
+ bold: '\x1b[1m',
34
+ };
35
+
36
+ function log(message, color = 'reset') {
37
+ if (!options.json) {
38
+ console.log(`${colors[color]}${message}${colors.reset}`);
39
+ }
40
+ }
41
+
42
+ // Parse CLI arguments
43
+ const args = process.argv.slice(2);
44
+ const options = {
45
+ layers: args.includes('--layers'),
46
+ json: args.includes('--json'),
47
+ script: args.includes('--script'),
48
+ verbose: args.includes('--verbose') || args.includes('-v'),
49
+ package: args.find((arg) => arg.startsWith('--package='))?.split('=')[1],
50
+ };
51
+
52
+ /**
53
+ * Find all packages
54
+ */
55
+ function findPackages(rootDir) {
56
+ const packages = [];
57
+ const entries = fs.readdirSync(rootDir, { withFileTypes: true });
58
+
59
+ for (const entry of entries) {
60
+ if (!entry.isDirectory() || !entry.name.startsWith('kb-labs-')) {continue;}
61
+
62
+ const repoPath = path.join(rootDir, entry.name);
63
+ const packagesDir = path.join(repoPath, 'packages');
64
+
65
+ if (!fs.existsSync(packagesDir)) {continue;}
66
+
67
+ const packageDirs = fs.readdirSync(packagesDir, { withFileTypes: true });
68
+
69
+ for (const pkgDir of packageDirs) {
70
+ if (!pkgDir.isDirectory()) {continue;}
71
+
72
+ const packageJsonPath = path.join(packagesDir, pkgDir.name, 'package.json');
73
+
74
+ if (fs.existsSync(packageJsonPath)) {
75
+ packages.push(packageJsonPath);
76
+ }
77
+ }
78
+ }
79
+
80
+ return packages;
81
+ }
82
+
83
+ /**
84
+ * Build dependency graph
85
+ */
86
+ function buildDependencyGraph(packages) {
87
+ const graph = new Map(); // packageName -> { deps: Set, path: string }
88
+ const nameToPath = new Map();
89
+
90
+ // First pass: collect all package names
91
+ for (const packagePath of packages) {
92
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));
93
+ const packageName = packageJson.name;
94
+
95
+ if (!packageName || !packageName.startsWith('@kb-labs/')) {continue;}
96
+
97
+ graph.set(packageName, {
98
+ deps: new Set(),
99
+ path: packagePath,
100
+ dir: path.dirname(packagePath),
101
+ });
102
+
103
+ nameToPath.set(packageName, packagePath);
104
+ }
105
+
106
+ // Second pass: build dependency edges
107
+ for (const packagePath of packages) {
108
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));
109
+ const packageName = packageJson.name;
110
+
111
+ if (!packageName || !packageName.startsWith('@kb-labs/')) {continue;}
112
+
113
+ const allDeps = {
114
+ ...packageJson.dependencies,
115
+ ...packageJson.devDependencies,
116
+ };
117
+
118
+ for (const [dep, version] of Object.entries(allDeps)) {
119
+ // Skip external dependencies
120
+ if (!dep.startsWith('@kb-labs/')) {continue;}
121
+
122
+ // Track ALL workspace dependencies regardless of protocol
123
+ // Supports: workspace:*, workspace:^1.0.0, link:../path, *
124
+ const isWorkspaceDep =
125
+ version.startsWith('workspace:') ||
126
+ version.startsWith('link:') ||
127
+ version === '*';
128
+
129
+ if (isWorkspaceDep && graph.has(dep)) {
130
+ graph.get(packageName).deps.add(dep);
131
+ }
132
+ }
133
+ }
134
+
135
+ return graph;
136
+ }
137
+
138
+ /**
139
+ * Topological sort using Kahn's algorithm
140
+ * Returns layers of packages that can be built in parallel
141
+ */
142
+ function topologicalSort(graph) {
143
+ // Calculate in-degree (number of dependencies) for each package
144
+ const inDegree = new Map();
145
+ const reverseDeps = new Map(); // who depends on me
146
+
147
+ for (const [pkg, { deps }] of graph.entries()) {
148
+ if (!inDegree.has(pkg)) {
149
+ inDegree.set(pkg, 0);
150
+ }
151
+ if (!reverseDeps.has(pkg)) {
152
+ reverseDeps.set(pkg, new Set());
153
+ }
154
+
155
+ for (const dep of deps) {
156
+ inDegree.set(pkg, (inDegree.get(pkg) || 0) + 1);
157
+
158
+ if (!reverseDeps.has(dep)) {
159
+ reverseDeps.set(dep, new Set());
160
+ }
161
+ reverseDeps.get(dep).add(pkg);
162
+ }
163
+ }
164
+
165
+ // Find packages with no dependencies (can be built first)
166
+ const queue = [];
167
+ for (const [pkg, degree] of inDegree.entries()) {
168
+ if (degree === 0) {
169
+ queue.push(pkg);
170
+ }
171
+ }
172
+
173
+ const layers = [];
174
+ const sorted = [];
175
+
176
+ while (queue.length > 0) {
177
+ // All packages in queue can be built in parallel (same layer)
178
+ const layer = [...queue];
179
+ layers.push(layer);
180
+ sorted.push(...layer);
181
+
182
+ queue.length = 0;
183
+
184
+ // Process all packages in current layer
185
+ for (const pkg of layer) {
186
+ // For each package that depends on this one
187
+ for (const dependent of reverseDeps.get(pkg) || []) {
188
+ inDegree.set(dependent, inDegree.get(dependent) - 1);
189
+
190
+ // If all dependencies are satisfied, add to queue
191
+ if (inDegree.get(dependent) === 0) {
192
+ queue.push(dependent);
193
+ }
194
+ }
195
+ }
196
+ }
197
+
198
+ // Check for circular dependencies
199
+ if (sorted.length !== graph.size) {
200
+ const missing = Array.from(graph.keys()).filter((pkg) => !sorted.includes(pkg));
201
+ return { layers, sorted, circular: missing };
202
+ }
203
+
204
+ return { layers, sorted, circular: [] };
205
+ }
206
+
207
+ /**
208
+ * Find circular dependency cycles using DFS
209
+ */
210
+ function findCircularDependencies(graph, circularPackages) {
211
+ const cycles = [];
212
+ const visited = new Set();
213
+ const recursionStack = new Set();
214
+ const path = [];
215
+
216
+ function dfs(pkg) {
217
+ if (recursionStack.has(pkg)) {
218
+ // Found a cycle - extract it from path
219
+ const cycleStart = path.indexOf(pkg);
220
+ const cycle = [...path.slice(cycleStart), pkg];
221
+
222
+ // Check if this cycle is new (not a rotation of existing cycle)
223
+ const cycleKey = cycle.slice(0, -1).sort().join('→');
224
+ if (!cycles.some(c => c.key === cycleKey)) {
225
+ cycles.push({ cycle, key: cycleKey });
226
+ }
227
+ return;
228
+ }
229
+
230
+ if (visited.has(pkg)) {
231
+ return;
232
+ }
233
+
234
+ visited.add(pkg);
235
+ recursionStack.add(pkg);
236
+ path.push(pkg);
237
+
238
+ const node = graph.get(pkg);
239
+ if (node) {
240
+ for (const dep of node.deps) {
241
+ // Only follow dependencies that are in circular packages
242
+ if (circularPackages.includes(dep)) {
243
+ dfs(dep);
244
+ }
245
+ }
246
+ }
247
+
248
+ path.pop();
249
+ recursionStack.delete(pkg);
250
+ }
251
+
252
+ // Start DFS from each circular package
253
+ for (const pkg of circularPackages) {
254
+ if (!visited.has(pkg)) {
255
+ dfs(pkg);
256
+ }
257
+ }
258
+
259
+ return cycles;
260
+ }
261
+
262
+ /**
263
+ * Get build order for specific package and its dependencies
264
+ */
265
+ function getBuildOrderForPackage(graph, packageName) {
266
+ const visited = new Set();
267
+ const order = [];
268
+
269
+ function visit(pkg) {
270
+ if (visited.has(pkg)) {return;}
271
+ visited.add(pkg);
272
+
273
+ const node = graph.get(pkg);
274
+ if (!node) {return;}
275
+
276
+ // Visit dependencies first
277
+ for (const dep of node.deps) {
278
+ visit(dep);
279
+ }
280
+
281
+ order.push(pkg);
282
+ }
283
+
284
+ visit(packageName);
285
+ return order;
286
+ }
287
+
288
+ /**
289
+ * Print build order
290
+ */
291
+ function printBuildOrder(result, graph) {
292
+ log('\nšŸ”Ø KB Labs Build Order\n', 'bold');
293
+
294
+ if (result.circular.length > 0) {
295
+ log('āŒ Circular dependencies detected!\n', 'red');
296
+
297
+ // Find actual cycles
298
+ const cycles = findCircularDependencies(graph, result.circular);
299
+
300
+ if (cycles.length > 0) {
301
+ log(`Found ${cycles.length} circular dependency cycle(s):\n`, 'yellow');
302
+
303
+ for (let i = 0; i < cycles.length; i++) {
304
+ const { cycle } = cycles[i];
305
+ log(`${i + 1}. ${cycle.join(' → ')}`, 'red');
306
+ log('', 'reset');
307
+ }
308
+ } else {
309
+ log('The following packages are involved in circular dependencies:', 'yellow');
310
+ for (const pkg of result.circular) {
311
+ log(` ${pkg}`, 'red');
312
+ }
313
+ }
314
+
315
+ log('\nCannot determine build order. Fix circular dependencies first.', 'gray');
316
+ log('šŸ’” To fix: Break the cycle by extracting shared code into a separate package\n', 'cyan');
317
+ return;
318
+ }
319
+
320
+ log(`Found ${result.sorted.length} packages\n`, 'gray');
321
+
322
+ if (options.layers) {
323
+ // Show parallel build layers
324
+ log('šŸ“¦ Build Layers (packages in same layer can build in parallel):\n', 'blue');
325
+
326
+ for (let i = 0; i < result.layers.length; i++) {
327
+ const layer = result.layers[i];
328
+ log(`Layer ${i + 1} (${layer.length} packages):`, 'cyan');
329
+
330
+ for (const pkg of layer) {
331
+ const deps = graph.get(pkg).deps;
332
+ log(` ${pkg}`, 'gray');
333
+ if (options.verbose && deps.size > 0) {
334
+ log(` depends on: ${Array.from(deps).join(', ')}`, 'gray');
335
+ }
336
+ }
337
+ log('', 'reset');
338
+ }
339
+
340
+ log(`Total layers: ${result.layers.length}`, 'blue');
341
+ log(`Max parallelism: ${Math.max(...result.layers.map((l) => l.length))} packages\n`, 'blue');
342
+ } else {
343
+ // Show sequential build order
344
+ log('šŸ“¦ Build Order (sequential):\n', 'blue');
345
+
346
+ for (let i = 0; i < result.sorted.length; i++) {
347
+ const pkg = result.sorted[i];
348
+ const deps = graph.get(pkg).deps;
349
+
350
+ log(`${(i + 1).toString().padStart(3)}. ${pkg}`, 'cyan');
351
+
352
+ if (options.verbose && deps.size > 0) {
353
+ log(` depends on: ${Array.from(deps).join(', ')}`, 'gray');
354
+ }
355
+ }
356
+ log('', 'reset');
357
+ }
358
+
359
+ // Tips
360
+ log('šŸ’” Tips:', 'blue');
361
+ log(' • Use --layers to see parallel build opportunities', 'gray');
362
+ log(' • Use --package=name to see build order for specific package', 'gray');
363
+ log(' • Use --script to generate a build script', 'gray');
364
+ log(' • Use pnpm --filter to build specific packages', 'gray');
365
+ log('', 'reset');
366
+ }
367
+
368
+ /**
369
+ * Generate build script
370
+ */
371
+ function generateBuildScript(result, graph) {
372
+ const lines = [
373
+ '#!/bin/bash',
374
+ '',
375
+ '# Auto-generated build script',
376
+ '# Generated by kb-devkit-build-order',
377
+ '',
378
+ 'set -e # Exit on error',
379
+ '',
380
+ ];
381
+
382
+ if (options.layers) {
383
+ // Parallel build script
384
+ lines.push('# Build in layers (parallel within each layer)');
385
+ lines.push('');
386
+
387
+ for (let i = 0; i < result.layers.length; i++) {
388
+ const layer = result.layers[i];
389
+ lines.push(`echo "Building layer ${i + 1}/${result.layers.length} (${layer.length} packages)..."`);
390
+
391
+ if (layer.length === 1) {
392
+ // Single package, build sequentially
393
+ const pkg = layer[0];
394
+ lines.push(`pnpm --filter ${pkg} run build`);
395
+ } else {
396
+ // Multiple packages, build in parallel
397
+ const filters = layer.map((pkg) => `--filter ${pkg}`).join(' ');
398
+ lines.push(`pnpm ${filters} run build --parallel`);
399
+ }
400
+
401
+ lines.push('');
402
+ }
403
+
404
+ lines.push('echo "All packages built successfully!"');
405
+ } else {
406
+ // Sequential build script
407
+ lines.push('# Build in order (sequential)');
408
+ lines.push('');
409
+
410
+ for (let i = 0; i < result.sorted.length; i++) {
411
+ const pkg = result.sorted[i];
412
+ lines.push(`echo "Building ${i + 1}/${result.sorted.length}: ${pkg}..."`);
413
+ lines.push(`pnpm --filter ${pkg} run build`);
414
+ }
415
+
416
+ lines.push('');
417
+ lines.push('echo "All packages built successfully!"');
418
+ }
419
+
420
+ return lines.join('\n');
421
+ }
422
+
423
+ /**
424
+ * Main function
425
+ */
426
+ function main() {
427
+ const rootDir = process.cwd();
428
+
429
+ if (!options.json) {
430
+ log('\nšŸš€ KB Labs Build Order Calculator\n', 'bold');
431
+ }
432
+
433
+ const packages = findPackages(rootDir);
434
+
435
+ if (packages.length === 0) {
436
+ log('āš ļø No KB Labs packages found', 'yellow');
437
+ process.exit(0);
438
+ }
439
+
440
+ const graph = buildDependencyGraph(packages);
441
+
442
+ if (options.package) {
443
+ // Build order for specific package
444
+ const fullPackageName = options.package.startsWith('@kb-labs/')
445
+ ? options.package
446
+ : `@kb-labs/${options.package}`;
447
+
448
+ if (!graph.has(fullPackageName)) {
449
+ log(`āš ļø Package not found: ${fullPackageName}`, 'yellow');
450
+ log('\nAvailable packages:', 'gray');
451
+ for (const pkg of Array.from(graph.keys()).sort()) {
452
+ log(` ${pkg}`, 'gray');
453
+ }
454
+ process.exit(1);
455
+ }
456
+
457
+ const order = getBuildOrderForPackage(graph, fullPackageName);
458
+
459
+ if (options.json) {
460
+ console.log(JSON.stringify({ package: fullPackageName, order }, null, 2));
461
+ } else {
462
+ log(`\nšŸ“¦ Build order for ${fullPackageName}:\n`, 'blue');
463
+ for (let i = 0; i < order.length; i++) {
464
+ const pkg = order[i];
465
+ const isCurrent = pkg === fullPackageName;
466
+ log(`${(i + 1).toString().padStart(3)}. ${pkg}${isCurrent ? ' ⬅ target' : ''}`, isCurrent ? 'yellow' : 'gray');
467
+ }
468
+ log('', 'reset');
469
+ }
470
+ } else {
471
+ // Full build order
472
+ const result = topologicalSort(graph);
473
+
474
+ if (options.json) {
475
+ const output = {
476
+ total: graph.size,
477
+ layers: result.layers,
478
+ sorted: result.sorted,
479
+ circular: result.circular,
480
+ };
481
+
482
+ // Add cycle details if circular dependencies exist
483
+ if (result.circular.length > 0) {
484
+ const cycles = findCircularDependencies(graph, result.circular);
485
+ output.cycles = cycles.map(c => c.cycle);
486
+ }
487
+
488
+ console.log(JSON.stringify(output, null, 2));
489
+ } else if (options.script) {
490
+ const script = generateBuildScript(result, graph);
491
+ console.log(script);
492
+ } else {
493
+ printBuildOrder(result, graph);
494
+ }
495
+ }
496
+
497
+ process.exit(0);
498
+ }
499
+
500
+ main();
@@ -0,0 +1,222 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * KB DevKit - Build Readiness Checker
4
+ *
5
+ * Analyzes workspace to determine if packages can be successfully bundled.
6
+ * Specifically useful before creating standalone executables with esbuild/rollup.
7
+ *
8
+ * Checks:
9
+ * 1. Missing packages referenced in imports
10
+ * 2. Broken import paths (e.g., missing .js files after compilation)
11
+ * 3. Packages that would fail bundling due to dependencies
12
+ * 4. Dependency chains that are broken
13
+ *
14
+ * Usage:
15
+ * npx kb-devkit-check-build-readiness
16
+ * npx kb-devkit-check-build-readiness --package @kb-labs/cli-bin
17
+ * npx kb-devkit-check-build-readiness --fix (auto-fix what's possible)
18
+ */
19
+
20
+ import { execSync } from 'child_process';
21
+ import { readFileSync, existsSync, readdirSync } from 'fs';
22
+ import { join, dirname } from 'path';
23
+ import { fileURLToPath } from 'url';
24
+
25
+ const __dirname = dirname(fileURLToPath(import.meta.url));
26
+ const rootDir = join(__dirname, '../..');
27
+
28
+ // Parse args
29
+ const args = process.argv.slice(2);
30
+ const targetPackage = args.find(a => a.startsWith('--package='))?.split('=')[1];
31
+ const shouldFix = args.includes('--fix');
32
+
33
+ console.log('šŸ” KB Labs Build Readiness Checker\n');
34
+
35
+ // Step 1: Find all workspace packages
36
+ function findWorkspacePackages() {
37
+ const packages = [];
38
+ const monorepos = readdirSync(rootDir).filter(d => d.startsWith('kb-labs-'));
39
+
40
+ for (const monorepo of monorepos) {
41
+ const packagesDir = join(rootDir, monorepo, 'packages');
42
+ if (!existsSync(packagesDir)) {continue;}
43
+
44
+ for (const pkg of readdirSync(packagesDir)) {
45
+ const pkgJsonPath = join(packagesDir, pkg, 'package.json');
46
+ if (!existsSync(pkgJsonPath)) {continue;}
47
+
48
+ try {
49
+ const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf8'));
50
+ packages.push({
51
+ name: pkgJson.name,
52
+ path: join(packagesDir, pkg),
53
+ pkgJson,
54
+ });
55
+ } catch (e) {
56
+ // Skip invalid package.json
57
+ }
58
+ }
59
+ }
60
+
61
+ return packages;
62
+ }
63
+
64
+ // Step 2: Get import checker results
65
+ function getImportIssues() {
66
+ try {
67
+ return execSync('npx kb-devkit-check-imports 2>&1', {
68
+ cwd: rootDir,
69
+ encoding: 'utf8',
70
+ maxBuffer: 10 * 1024 * 1024, // 10MB buffer
71
+ });
72
+ } catch (e) {
73
+ return e.stdout || e.message;
74
+ }
75
+ }
76
+
77
+ // Step 3: Parse issues from output
78
+ function parseIssues(output) {
79
+ const issues = {
80
+ missingPackages: new Set(),
81
+ brokenImports: new Map(), // pkg -> [files]
82
+ packagesWithIssues: new Set(),
83
+ };
84
+
85
+ const lines = output.split('\n');
86
+ let currentPkg = null;
87
+
88
+ for (const line of lines) {
89
+ // Detect package name
90
+ const pkgMatch = line.match(/@kb-labs\/[\w-]+/);
91
+ if (pkgMatch && line.includes('āŒ')) {
92
+ currentPkg = pkgMatch[0];
93
+ issues.packagesWithIssues.add(currentPkg);
94
+ }
95
+
96
+ // Detect missing packages
97
+ if (line.includes('Missing workspace dependencies') && currentPkg) {
98
+ issues.brokenImports.set(currentPkg, []);
99
+ }
100
+
101
+ // Detect broken imports
102
+ if (line.includes('Cannot resolve:') && currentPkg) {
103
+ const pathMatch = line.match(/Cannot resolve: (.+)/);
104
+ if (pathMatch) {
105
+ const arr = issues.brokenImports.get(currentPkg) || [];
106
+ arr.push(pathMatch[1].trim());
107
+ issues.brokenImports.set(currentPkg, arr);
108
+ }
109
+ }
110
+
111
+ // Collect missing package names
112
+ if (line.includes('@kb-labs/plugin-manifest')) {
113
+ issues.missingPackages.add('@kb-labs/plugin-manifest');
114
+ }
115
+ if (line.includes('@kb-labs/plugin-adapter-rest')) {
116
+ issues.missingPackages.add('@kb-labs/plugin-adapter-rest');
117
+ }
118
+ if (line.includes('@kb-labs/plugin-adapter-cli')) {
119
+ issues.missingPackages.add('@kb-labs/plugin-adapter-cli');
120
+ }
121
+ if (line.includes('@kb-labs/plugin-adapter-studio')) {
122
+ issues.missingPackages.add('@kb-labs/plugin-adapter-studio');
123
+ }
124
+ }
125
+
126
+ return issues;
127
+ }
128
+
129
+ // Step 4: Check if package can be bundled
130
+ function checkBundleability(pkgName, issues, allPackages) {
131
+ const problems = [];
132
+
133
+ // Check if package itself has issues
134
+ if (issues.packagesWithIssues.has(pkgName)) {
135
+ const brokenImports = issues.brokenImports.get(pkgName) || [];
136
+ if (brokenImports.length > 0) {
137
+ problems.push(`šŸ”“ Has ${brokenImports.length} broken import(s)`);
138
+ }
139
+ }
140
+
141
+ // Find package deps
142
+ const pkg = allPackages.find(p => p.name === pkgName);
143
+ if (!pkg) {
144
+ problems.push('šŸ”“ Package not found in workspace');
145
+ return { canBundle: false, problems };
146
+ }
147
+
148
+ const deps = [
149
+ ...Object.keys(pkg.pkgJson.dependencies || {}),
150
+ ...Object.keys(pkg.pkgJson.peerDependencies || {})
151
+ ];
152
+
153
+ // Check if any dependency is missing
154
+ for (const dep of deps) {
155
+ if (dep.startsWith('@kb-labs/') && issues.missingPackages.has(dep)) {
156
+ problems.push(`šŸ”“ Missing dependency: ${dep}`);
157
+ }
158
+ }
159
+
160
+ // Recursively check dependencies
161
+ for (const dep of deps) {
162
+ if (dep.startsWith('@kb-labs/') && issues.packagesWithIssues.has(dep)) {
163
+ problems.push(`🟔 Dependency has issues: ${dep}`);
164
+ }
165
+ }
166
+
167
+ const canBundle = problems.filter(p => p.startsWith('šŸ”“')).length === 0;
168
+ return { canBundle, problems };
169
+ }
170
+
171
+ // Main execution
172
+ const packages = findWorkspacePackages();
173
+ console.log(`šŸ“¦ Found ${packages.length} workspace packages\n`);
174
+
175
+ console.log('šŸ” Running import checker...\n');
176
+ const importOutput = getImportIssues();
177
+ const issues = parseIssues(importOutput);
178
+
179
+ console.log('šŸ“Š Analysis Results:\n');
180
+ console.log(`šŸ”“ Missing packages: ${issues.missingPackages.size}`);
181
+ if (issues.missingPackages.size > 0) {
182
+ for (const pkg of issues.missingPackages) {
183
+ console.log(` - ${pkg}`);
184
+ }
185
+ }
186
+ console.log(`\nšŸ”“ Packages with issues: ${issues.packagesWithIssues.size}`);
187
+ console.log(`šŸ”“ Packages with broken imports: ${issues.brokenImports.size}\n`);
188
+
189
+ // If target package specified, check bundleability
190
+ if (targetPackage) {
191
+ console.log(`\nšŸŽÆ Checking bundleability for: ${targetPackage}\n`);
192
+ const result = checkBundleability(targetPackage, issues, packages);
193
+
194
+ if (result.canBundle) {
195
+ console.log('āœ… Package CAN be bundled!\n');
196
+ } else {
197
+ console.log('āŒ Package CANNOT be bundled due to:\n');
198
+ for (const problem of result.problems) {
199
+ console.log(` ${problem}`);
200
+ }
201
+ console.log('\nšŸ’” Recommendations:');
202
+ console.log(' 1. Fix missing packages (create or remove references)');
203
+ console.log(' 2. Fix broken imports (update paths after build)');
204
+ console.log(' 3. Run: npx kb-devkit-fix-deps --remove-unused');
205
+ process.exit(1);
206
+ }
207
+ } else {
208
+ console.log('\nšŸ’” To check specific package:');
209
+ console.log(' npx kb-devkit-check-build-readiness --package @kb-labs/cli-bin\n');
210
+ }
211
+
212
+ // Show top problematic packages
213
+ console.log('\nšŸ” Most problematic packages:\n');
214
+ const sorted = Array.from(issues.brokenImports.entries())
215
+ .sort((a, b) => b[1].length - a[1].length)
216
+ .slice(0, 10);
217
+
218
+ for (const [pkg, imports] of sorted) {
219
+ console.log(` ${pkg}: ${imports.length} broken import(s)`);
220
+ }
221
+
222
+ console.log('\n');