@skillsmith/core 0.4.14 → 0.4.16

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 (287) hide show
  1. package/README.md +2 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/src/analysis/DependencyMerger.d.ts +39 -0
  4. package/dist/src/analysis/DependencyMerger.d.ts.map +1 -0
  5. package/dist/src/analysis/DependencyMerger.js +191 -0
  6. package/dist/src/analysis/DependencyMerger.js.map +1 -0
  7. package/dist/src/analysis/DependencyMerger.test.d.ts +2 -0
  8. package/dist/src/analysis/DependencyMerger.test.d.ts.map +1 -0
  9. package/dist/src/analysis/DependencyMerger.test.js +258 -0
  10. package/dist/src/analysis/DependencyMerger.test.js.map +1 -0
  11. package/dist/src/analysis/McpReferenceExtractor.d.ts +42 -0
  12. package/dist/src/analysis/McpReferenceExtractor.d.ts.map +1 -0
  13. package/dist/src/analysis/McpReferenceExtractor.js +92 -0
  14. package/dist/src/analysis/McpReferenceExtractor.js.map +1 -0
  15. package/dist/src/analysis/McpReferenceExtractor.test.d.ts +2 -0
  16. package/dist/src/analysis/McpReferenceExtractor.test.d.ts.map +1 -0
  17. package/dist/src/analysis/McpReferenceExtractor.test.js +262 -0
  18. package/dist/src/analysis/McpReferenceExtractor.test.js.map +1 -0
  19. package/dist/src/api/client.d.ts +1 -0
  20. package/dist/src/api/client.d.ts.map +1 -1
  21. package/dist/src/api/client.health.d.ts +26 -0
  22. package/dist/src/api/client.health.d.ts.map +1 -0
  23. package/dist/src/api/client.health.js +74 -0
  24. package/dist/src/api/client.health.js.map +1 -0
  25. package/dist/src/api/client.js +3 -51
  26. package/dist/src/api/client.js.map +1 -1
  27. package/dist/src/api/schemas.d.ts +4 -4
  28. package/dist/src/db/createDatabase.d.ts +9 -1
  29. package/dist/src/db/createDatabase.d.ts.map +1 -1
  30. package/dist/src/db/createDatabase.js +9 -1
  31. package/dist/src/db/createDatabase.js.map +1 -1
  32. package/dist/src/db/drivers/sqljsDriver.d.ts.map +1 -1
  33. package/dist/src/db/drivers/sqljsDriver.js +7 -8
  34. package/dist/src/db/drivers/sqljsDriver.js.map +1 -1
  35. package/dist/src/db/migrations/v10-dependencies.d.ts +18 -0
  36. package/dist/src/db/migrations/v10-dependencies.d.ts.map +1 -0
  37. package/dist/src/db/migrations/v10-dependencies.js +50 -0
  38. package/dist/src/db/migrations/v10-dependencies.js.map +1 -0
  39. package/dist/src/db/migrations/v2-phase5-columns.d.ts +7 -0
  40. package/dist/src/db/migrations/v2-phase5-columns.d.ts.map +1 -0
  41. package/dist/src/db/migrations/v2-phase5-columns.js +16 -0
  42. package/dist/src/db/migrations/v2-phase5-columns.js.map +1 -0
  43. package/dist/src/db/migrations/v3-sync-tables.d.ts +6 -0
  44. package/dist/src/db/migrations/v3-sync-tables.d.ts.map +1 -0
  45. package/dist/src/db/migrations/v3-sync-tables.js +41 -0
  46. package/dist/src/db/migrations/v3-sync-tables.js.map +1 -0
  47. package/dist/src/db/migrations/v4-security-columns.d.ts +7 -0
  48. package/dist/src/db/migrations/v4-security-columns.d.ts.map +1 -0
  49. package/dist/src/db/migrations/v4-security-columns.js +17 -0
  50. package/dist/src/db/migrations/v4-security-columns.js.map +1 -0
  51. package/dist/src/db/migrations/v5b-change-type.d.ts +19 -0
  52. package/dist/src/db/migrations/v5b-change-type.d.ts.map +1 -0
  53. package/dist/src/db/migrations/v5b-change-type.js +22 -0
  54. package/dist/src/db/migrations/v5b-change-type.js.map +1 -0
  55. package/dist/src/db/migrations/v6-advisories.d.ts +32 -0
  56. package/dist/src/db/migrations/v6-advisories.d.ts.map +1 -0
  57. package/dist/src/db/migrations/v6-advisories.js +53 -0
  58. package/dist/src/db/migrations/v6-advisories.js.map +1 -0
  59. package/dist/src/db/migrations/v7-compatibility.d.ts +21 -0
  60. package/dist/src/db/migrations/v7-compatibility.d.ts.map +1 -0
  61. package/dist/src/db/migrations/v7-compatibility.js +23 -0
  62. package/dist/src/db/migrations/v7-compatibility.js.map +1 -0
  63. package/dist/src/db/migrations/v8-co-installs.d.ts +21 -0
  64. package/dist/src/db/migrations/v8-co-installs.d.ts.map +1 -0
  65. package/dist/src/db/migrations/v8-co-installs.js +33 -0
  66. package/dist/src/db/migrations/v8-co-installs.js.map +1 -0
  67. package/dist/src/db/schema.d.ts +5 -16
  68. package/dist/src/db/schema.d.ts.map +1 -1
  69. package/dist/src/db/schema.js +50 -79
  70. package/dist/src/db/schema.js.map +1 -1
  71. package/dist/src/exports/repositories.d.ts +6 -0
  72. package/dist/src/exports/repositories.d.ts.map +1 -1
  73. package/dist/src/exports/repositories.js +21 -0
  74. package/dist/src/exports/repositories.js.map +1 -1
  75. package/dist/src/exports/services.d.ts +2 -0
  76. package/dist/src/exports/services.d.ts.map +1 -1
  77. package/dist/src/exports/services.js +5 -0
  78. package/dist/src/exports/services.js.map +1 -1
  79. package/dist/src/exports/types.d.ts +2 -1
  80. package/dist/src/exports/types.d.ts.map +1 -1
  81. package/dist/src/exports/types.js.map +1 -1
  82. package/dist/src/indexer/GitHubIndexer.d.ts +3 -1
  83. package/dist/src/indexer/GitHubIndexer.d.ts.map +1 -1
  84. package/dist/src/indexer/GitHubIndexer.js.map +1 -1
  85. package/dist/src/indexer/SkillParser.d.ts +22 -9
  86. package/dist/src/indexer/SkillParser.d.ts.map +1 -1
  87. package/dist/src/indexer/SkillParser.js +17 -10
  88. package/dist/src/indexer/SkillParser.js.map +1 -1
  89. package/dist/src/learning/PatternStore.d.ts +0 -2
  90. package/dist/src/learning/PatternStore.d.ts.map +1 -1
  91. package/dist/src/learning/PatternStore.helpers.d.ts +19 -1
  92. package/dist/src/learning/PatternStore.helpers.d.ts.map +1 -1
  93. package/dist/src/learning/PatternStore.helpers.js +51 -0
  94. package/dist/src/learning/PatternStore.helpers.js.map +1 -1
  95. package/dist/src/learning/PatternStore.js +5 -37
  96. package/dist/src/learning/PatternStore.js.map +1 -1
  97. package/dist/src/repositories/AdvisoryRepository.d.ts +80 -0
  98. package/dist/src/repositories/AdvisoryRepository.d.ts.map +1 -0
  99. package/dist/src/repositories/AdvisoryRepository.js +128 -0
  100. package/dist/src/repositories/AdvisoryRepository.js.map +1 -0
  101. package/dist/src/repositories/AdvisoryRepository.test.d.ts +6 -0
  102. package/dist/src/repositories/AdvisoryRepository.test.d.ts.map +1 -0
  103. package/dist/src/repositories/AdvisoryRepository.test.js +149 -0
  104. package/dist/src/repositories/AdvisoryRepository.test.js.map +1 -0
  105. package/dist/src/repositories/CoInstallRepository.d.ts +68 -0
  106. package/dist/src/repositories/CoInstallRepository.d.ts.map +1 -0
  107. package/dist/src/repositories/CoInstallRepository.js +122 -0
  108. package/dist/src/repositories/CoInstallRepository.js.map +1 -0
  109. package/dist/src/repositories/IndexerRepository.d.ts.map +1 -1
  110. package/dist/src/repositories/IndexerRepository.js +5 -1
  111. package/dist/src/repositories/IndexerRepository.js.map +1 -1
  112. package/dist/src/repositories/SkillDependencyRepository.d.ts +73 -0
  113. package/dist/src/repositories/SkillDependencyRepository.d.ts.map +1 -0
  114. package/dist/src/repositories/SkillDependencyRepository.js +199 -0
  115. package/dist/src/repositories/SkillDependencyRepository.js.map +1 -0
  116. package/dist/src/repositories/SkillRepository.d.ts.map +1 -1
  117. package/dist/src/repositories/SkillRepository.js +16 -5
  118. package/dist/src/repositories/SkillRepository.js.map +1 -1
  119. package/dist/src/scripts/validation/types.d.ts +2 -2
  120. package/dist/src/services/SearchService.helpers.d.ts.map +1 -1
  121. package/dist/src/services/SearchService.helpers.js +4 -0
  122. package/dist/src/services/SearchService.helpers.js.map +1 -1
  123. package/dist/src/services/SearchService.types.d.ts +1 -0
  124. package/dist/src/services/SearchService.types.d.ts.map +1 -1
  125. package/dist/src/services/TaskRunner.d.ts +5 -81
  126. package/dist/src/services/TaskRunner.d.ts.map +1 -1
  127. package/dist/src/services/TaskRunner.js +7 -68
  128. package/dist/src/services/TaskRunner.js.map +1 -1
  129. package/dist/src/services/TaskRunner.process.d.ts +33 -0
  130. package/dist/src/services/TaskRunner.process.d.ts.map +1 -0
  131. package/dist/src/services/TaskRunner.process.js +70 -0
  132. package/dist/src/services/TaskRunner.process.js.map +1 -0
  133. package/dist/src/services/TaskRunner.types.d.ts +76 -0
  134. package/dist/src/services/TaskRunner.types.d.ts.map +1 -0
  135. package/dist/src/services/TaskRunner.types.js +22 -0
  136. package/dist/src/services/TaskRunner.types.js.map +1 -0
  137. package/dist/src/services/__tests__/TaskRunner.process.test.d.ts +13 -0
  138. package/dist/src/services/__tests__/TaskRunner.process.test.d.ts.map +1 -0
  139. package/dist/src/services/__tests__/TaskRunner.process.test.js +91 -0
  140. package/dist/src/services/__tests__/TaskRunner.process.test.js.map +1 -0
  141. package/dist/src/services/quarantine/QuarantineService.d.ts +3 -24
  142. package/dist/src/services/quarantine/QuarantineService.d.ts.map +1 -1
  143. package/dist/src/services/quarantine/QuarantineService.js +8 -205
  144. package/dist/src/services/quarantine/QuarantineService.js.map +1 -1
  145. package/dist/src/services/quarantine/QuarantineService.multiapproval.d.ts +57 -0
  146. package/dist/src/services/quarantine/QuarantineService.multiapproval.d.ts.map +1 -0
  147. package/dist/src/services/quarantine/QuarantineService.multiapproval.js +211 -0
  148. package/dist/src/services/quarantine/QuarantineService.multiapproval.js.map +1 -0
  149. package/dist/src/session/SessionManager.d.ts +2 -33
  150. package/dist/src/session/SessionManager.d.ts.map +1 -1
  151. package/dist/src/session/SessionManager.js +14 -238
  152. package/dist/src/session/SessionManager.js.map +1 -1
  153. package/dist/src/session/SessionManager.memory.d.ts +67 -0
  154. package/dist/src/session/SessionManager.memory.d.ts.map +1 -0
  155. package/dist/src/session/SessionManager.memory.js +262 -0
  156. package/dist/src/session/SessionManager.memory.js.map +1 -0
  157. package/dist/src/sources/SourceIndexer.d.ts +3 -1
  158. package/dist/src/sources/SourceIndexer.d.ts.map +1 -1
  159. package/dist/src/sources/SourceIndexer.js.map +1 -1
  160. package/dist/src/sync/SyncEngine.d.ts +13 -1
  161. package/dist/src/sync/SyncEngine.d.ts.map +1 -1
  162. package/dist/src/sync/SyncEngine.js +50 -1
  163. package/dist/src/sync/SyncEngine.js.map +1 -1
  164. package/dist/src/testing/MultiLLMProvider.d.ts +5 -6
  165. package/dist/src/testing/MultiLLMProvider.d.ts.map +1 -1
  166. package/dist/src/testing/MultiLLMProvider.js +18 -130
  167. package/dist/src/testing/MultiLLMProvider.js.map +1 -1
  168. package/dist/src/testing/MultiLLMProvider.metrics.d.ts +33 -0
  169. package/dist/src/testing/MultiLLMProvider.metrics.d.ts.map +1 -0
  170. package/dist/src/testing/MultiLLMProvider.metrics.js +87 -0
  171. package/dist/src/testing/MultiLLMProvider.metrics.js.map +1 -0
  172. package/dist/src/testing/MultiLLMProvider.selection.d.ts +77 -0
  173. package/dist/src/testing/MultiLLMProvider.selection.d.ts.map +1 -0
  174. package/dist/src/testing/MultiLLMProvider.selection.js +151 -0
  175. package/dist/src/testing/MultiLLMProvider.selection.js.map +1 -0
  176. package/dist/src/types/dependencies.d.ts +85 -0
  177. package/dist/src/types/dependencies.d.ts.map +1 -0
  178. package/dist/src/types/dependencies.js +11 -0
  179. package/dist/src/types/dependencies.js.map +1 -0
  180. package/dist/src/types/skill.d.ts +13 -0
  181. package/dist/src/types/skill.d.ts.map +1 -1
  182. package/dist/src/types/skill.js.map +1 -1
  183. package/dist/src/types.d.ts +32 -0
  184. package/dist/src/types.d.ts.map +1 -1
  185. package/dist/src/versioning/change-classifier.d.ts +38 -0
  186. package/dist/src/versioning/change-classifier.d.ts.map +1 -0
  187. package/dist/src/versioning/change-classifier.js +187 -0
  188. package/dist/src/versioning/change-classifier.js.map +1 -0
  189. package/dist/src/versioning/change-classifier.test.d.ts +6 -0
  190. package/dist/src/versioning/change-classifier.test.d.ts.map +1 -0
  191. package/dist/src/versioning/change-classifier.test.js +275 -0
  192. package/dist/src/versioning/change-classifier.test.js.map +1 -0
  193. package/dist/src/versioning/update-risk.d.ts +50 -0
  194. package/dist/src/versioning/update-risk.d.ts.map +1 -0
  195. package/dist/src/versioning/update-risk.js +80 -0
  196. package/dist/src/versioning/update-risk.js.map +1 -0
  197. package/dist/src/versioning/update-risk.test.d.ts +6 -0
  198. package/dist/src/versioning/update-risk.test.d.ts.map +1 -0
  199. package/dist/src/versioning/update-risk.test.js +200 -0
  200. package/dist/src/versioning/update-risk.test.js.map +1 -0
  201. package/dist/tests/AuditLogger.edge-cases.test.d.ts +10 -0
  202. package/dist/tests/AuditLogger.edge-cases.test.d.ts.map +1 -0
  203. package/dist/tests/AuditLogger.edge-cases.test.js +183 -0
  204. package/dist/tests/AuditLogger.edge-cases.test.js.map +1 -0
  205. package/dist/tests/CacheManager.test.d.ts +9 -0
  206. package/dist/tests/CacheManager.test.d.ts.map +1 -0
  207. package/dist/tests/CacheManager.test.js +163 -0
  208. package/dist/tests/CacheManager.test.js.map +1 -0
  209. package/dist/tests/DailyIndexPipeline.test.js +3 -3
  210. package/dist/tests/DailyIndexPipeline.test.js.map +1 -1
  211. package/dist/tests/GitHubIndexer.edge-cases.test.d.ts +10 -0
  212. package/dist/tests/GitHubIndexer.edge-cases.test.d.ts.map +1 -0
  213. package/dist/tests/GitHubIndexer.edge-cases.test.js +255 -0
  214. package/dist/tests/GitHubIndexer.edge-cases.test.js.map +1 -0
  215. package/dist/tests/GitHubIndexer.test.js +4 -2
  216. package/dist/tests/GitHubIndexer.test.js.map +1 -1
  217. package/dist/tests/SearchService.test.js +71 -0
  218. package/dist/tests/SearchService.test.js.map +1 -1
  219. package/dist/tests/SkillVersionRepository.test.d.ts +0 -11
  220. package/dist/tests/SkillVersionRepository.test.d.ts.map +1 -1
  221. package/dist/tests/SkillVersionRepository.test.js +131 -194
  222. package/dist/tests/SkillVersionRepository.test.js.map +1 -1
  223. package/dist/tests/api/client.health.test.d.ts +15 -0
  224. package/dist/tests/api/client.health.test.d.ts.map +1 -0
  225. package/dist/tests/api/client.health.test.js +111 -0
  226. package/dist/tests/api/client.health.test.js.map +1 -0
  227. package/dist/tests/db/betterSqlite3Driver.test.d.ts +8 -0
  228. package/dist/tests/db/betterSqlite3Driver.test.d.ts.map +1 -0
  229. package/dist/tests/db/betterSqlite3Driver.test.js +88 -0
  230. package/dist/tests/db/betterSqlite3Driver.test.js.map +1 -0
  231. package/dist/tests/db/database-abstraction.test.js +12 -0
  232. package/dist/tests/db/database-abstraction.test.js.map +1 -1
  233. package/dist/tests/db/schema-migrations.test.d.ts +10 -0
  234. package/dist/tests/db/schema-migrations.test.d.ts.map +1 -0
  235. package/dist/tests/db/schema-migrations.test.js +134 -0
  236. package/dist/tests/db/schema-migrations.test.js.map +1 -0
  237. package/dist/tests/db/sqljsDriver.test.d.ts +9 -0
  238. package/dist/tests/db/sqljsDriver.test.d.ts.map +1 -0
  239. package/dist/tests/db/sqljsDriver.test.js +75 -0
  240. package/dist/tests/db/sqljsDriver.test.js.map +1 -0
  241. package/dist/tests/edge-cases/EdgeCases.test.js +4 -1
  242. package/dist/tests/edge-cases/EdgeCases.test.js.map +1 -1
  243. package/dist/tests/helpers/database.d.ts +32 -0
  244. package/dist/tests/helpers/database.d.ts.map +1 -0
  245. package/dist/tests/helpers/database.js +54 -0
  246. package/dist/tests/helpers/database.js.map +1 -0
  247. package/dist/tests/learning/PatternStore.helpers.test.d.ts +17 -0
  248. package/dist/tests/learning/PatternStore.helpers.test.d.ts.map +1 -0
  249. package/dist/tests/learning/PatternStore.helpers.test.js +301 -0
  250. package/dist/tests/learning/PatternStore.helpers.test.js.map +1 -0
  251. package/dist/tests/repositories/CoInstallRepository.test.d.ts +9 -0
  252. package/dist/tests/repositories/CoInstallRepository.test.d.ts.map +1 -0
  253. package/dist/tests/repositories/CoInstallRepository.test.js +126 -0
  254. package/dist/tests/repositories/CoInstallRepository.test.js.map +1 -0
  255. package/dist/tests/repositories/SkillDependencyRepository.test.d.ts +10 -0
  256. package/dist/tests/repositories/SkillDependencyRepository.test.d.ts.map +1 -0
  257. package/dist/tests/repositories/SkillDependencyRepository.test.js +336 -0
  258. package/dist/tests/repositories/SkillDependencyRepository.test.js.map +1 -0
  259. package/dist/tests/routing/LanguageRouter.test.d.ts +9 -0
  260. package/dist/tests/routing/LanguageRouter.test.d.ts.map +1 -0
  261. package/dist/tests/routing/LanguageRouter.test.js +150 -0
  262. package/dist/tests/routing/LanguageRouter.test.js.map +1 -0
  263. package/dist/tests/session/SessionManager.memory.test.d.ts +11 -0
  264. package/dist/tests/session/SessionManager.memory.test.d.ts.map +1 -0
  265. package/dist/tests/session/SessionManager.memory.test.js +219 -0
  266. package/dist/tests/session/SessionManager.memory.test.js.map +1 -0
  267. package/dist/tests/sources.test.js +1 -1
  268. package/dist/tests/sources.test.js.map +1 -1
  269. package/dist/tests/sync/SyncEngine.test.js +32 -18
  270. package/dist/tests/sync/SyncEngine.test.js.map +1 -1
  271. package/dist/tests/testing/MultiLLMProvider.metrics.test.d.ts +13 -0
  272. package/dist/tests/testing/MultiLLMProvider.metrics.test.d.ts.map +1 -0
  273. package/dist/tests/testing/MultiLLMProvider.metrics.test.js +149 -0
  274. package/dist/tests/testing/MultiLLMProvider.metrics.test.js.map +1 -0
  275. package/dist/tests/testing/MultiLLMProvider.selection.test.d.ts +15 -0
  276. package/dist/tests/testing/MultiLLMProvider.selection.test.d.ts.map +1 -0
  277. package/dist/tests/testing/MultiLLMProvider.selection.test.js +249 -0
  278. package/dist/tests/testing/MultiLLMProvider.selection.test.js.map +1 -0
  279. package/dist/tests/unit/migrations/v10-dependencies.test.d.ts +6 -0
  280. package/dist/tests/unit/migrations/v10-dependencies.test.d.ts.map +1 -0
  281. package/dist/tests/unit/migrations/v10-dependencies.test.js +166 -0
  282. package/dist/tests/unit/migrations/v10-dependencies.test.js.map +1 -0
  283. package/dist/tests/unit/quarantine-query-builder.test.d.ts +10 -0
  284. package/dist/tests/unit/quarantine-query-builder.test.d.ts.map +1 -0
  285. package/dist/tests/unit/quarantine-query-builder.test.js +157 -0
  286. package/dist/tests/unit/quarantine-query-builder.test.js.map +1 -0
  287. package/package.json +1 -1
@@ -0,0 +1,54 @@
1
+ /**
2
+ * @fileoverview Test database helpers — prevents "no such table" for migrated tables
3
+ * @see SMI-2749
4
+ *
5
+ * Use createTestDatabase() instead of createDatabase(':memory:') whenever tests
6
+ * need tables that only exist in migration files (e.g. skill_versions).
7
+ *
8
+ * WHY THIS EXISTS:
9
+ * createDatabase(':memory:') calls initializeSchema(), which records SCHEMA_VERSION=5
10
+ * directly in schema_version WITHOUT running the intermediate migration SQLs.
11
+ * runMigrations() then sees version=5 and skips all migrations — a no-op.
12
+ * Tables defined only in migration SQL (not in SCHEMA_SQL) are never created.
13
+ * Example: skill_versions is in MIGRATION_V5_SQL, not SCHEMA_SQL.
14
+ *
15
+ * createTestDatabase() iterates MIGRATIONS directly (no version gate) so every
16
+ * migration's SQL runs unconditionally. New migrations added to MIGRATIONS are
17
+ * automatically included — no change required here.
18
+ */
19
+ import { createDatabase, MIGRATIONS } from '../../src/db/schema.js';
20
+ // Re-exported for test convenience — tests only need to import from this module
21
+ export { closeDatabase } from '../../src/db/schema.js';
22
+ /**
23
+ * Create an in-memory SQLite database with ALL schema + migrations applied.
24
+ *
25
+ * Safe to use for any test that needs migrated tables (e.g. skill_versions,
26
+ * sync_config, sync_history). Forward-compatible: new migrations added to
27
+ * MIGRATIONS are included automatically.
28
+ *
29
+ * @returns Database with full schema + all migrations applied
30
+ */
31
+ export function createTestDatabase() {
32
+ // createDatabase() calls initializeSchema() — runs SCHEMA_SQL (handling FTS5 triggers
33
+ // and multi-statement SQL correctly) and stamps SCHEMA_VERSION in schema_version.
34
+ const db = createDatabase();
35
+ // Run all migrations unconditionally (no version gate) so tables that only exist in
36
+ // migration SQL (not SCHEMA_SQL) are created. db.exec() handles multi-statement SQL
37
+ // natively — no semicolon split needed.
38
+ //
39
+ // "duplicate column" errors are caught: some migration SQL adds columns that are
40
+ // already included in the canonical SCHEMA_SQL. Those errors are expected and safe.
41
+ for (const migration of MIGRATIONS) {
42
+ try {
43
+ db.exec(migration.sql);
44
+ }
45
+ catch (error) {
46
+ const msg = error instanceof Error ? error.message : String(error);
47
+ if (!msg.includes('duplicate column'))
48
+ throw error;
49
+ }
50
+ db.prepare('INSERT OR REPLACE INTO schema_version (version) VALUES (?)').run(migration.version);
51
+ }
52
+ return db;
53
+ }
54
+ //# sourceMappingURL=database.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"database.js","sourceRoot":"","sources":["../../../tests/helpers/database.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAGnE,gFAAgF;AAChF,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAGtD;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB;IAChC,sFAAsF;IACtF,kFAAkF;IAClF,MAAM,EAAE,GAAG,cAAc,EAAE,CAAA;IAE3B,oFAAoF;IACpF,oFAAoF;IACpF,wCAAwC;IACxC,EAAE;IACF,iFAAiF;IACjF,oFAAoF;IACpF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;YAClE,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,kBAAkB,CAAC;gBAAE,MAAM,KAAK,CAAA;QACpD,CAAC;QACD,EAAE,CAAC,OAAO,CAAC,4DAA4D,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IACjG,CAAC;IAED,OAAO,EAAE,CAAA;AACX,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * SMI-2754: PatternStore Helper Functions Tests
3
+ *
4
+ * Tests covering pure math functions, FisherInformationMatrix,
5
+ * and DB-touching helpers extracted to PatternStore.helpers.ts.
6
+ *
7
+ * Categories:
8
+ * - FisherInformationMatrix: construct, update, accumulate, decay,
9
+ * serialize/deserialize round-trip, reset, invalid buffer size
10
+ * - contextToText: empty, partial, full context
11
+ * - computeGradient, cosineSimilarity, importanceWeightedSimilarity (zero-norm edge)
12
+ * - calculatePatternImportance: positive and negative reward
13
+ * - shouldConsolidate: too recent, no patterns, ratio threshold, near max patterns
14
+ * - computeAverageEmbedding: empty table → zero vector, populated → average
15
+ */
16
+ export {};
17
+ //# sourceMappingURL=PatternStore.helpers.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PatternStore.helpers.test.d.ts","sourceRoot":"","sources":["../../../tests/learning/PatternStore.helpers.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG"}
@@ -0,0 +1,301 @@
1
+ /**
2
+ * SMI-2754: PatternStore Helper Functions Tests
3
+ *
4
+ * Tests covering pure math functions, FisherInformationMatrix,
5
+ * and DB-touching helpers extracted to PatternStore.helpers.ts.
6
+ *
7
+ * Categories:
8
+ * - FisherInformationMatrix: construct, update, accumulate, decay,
9
+ * serialize/deserialize round-trip, reset, invalid buffer size
10
+ * - contextToText: empty, partial, full context
11
+ * - computeGradient, cosineSimilarity, importanceWeightedSimilarity (zero-norm edge)
12
+ * - calculatePatternImportance: positive and negative reward
13
+ * - shouldConsolidate: too recent, no patterns, ratio threshold, near max patterns
14
+ * - computeAverageEmbedding: empty table → zero vector, populated → average
15
+ */
16
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
17
+ import { FisherInformationMatrix, contextToText, computeGradient, cosineSimilarity, importanceWeightedSimilarity, calculatePatternImportance, shouldConsolidate, computeAverageEmbedding, PATTERN_STORE_SCHEMA, } from '../../src/learning/PatternStore.helpers.js';
18
+ import { createDatabase } from '../../src/db/createDatabase.js';
19
+ // ============================================================================
20
+ // FisherInformationMatrix
21
+ // ============================================================================
22
+ describe('FisherInformationMatrix', () => {
23
+ it('constructs with zero importance vector', () => {
24
+ const fim = new FisherInformationMatrix(4);
25
+ expect(fim.getImportanceVector()).toEqual(new Float32Array(4));
26
+ expect(fim.getUpdateCount()).toBe(0);
27
+ expect(fim.getAverageImportance()).toBe(0);
28
+ });
29
+ it('updates importance from a gradient', () => {
30
+ const fim = new FisherInformationMatrix(3);
31
+ const gradient = new Float32Array([1.0, 2.0, 0.0]);
32
+ fim.update(gradient);
33
+ expect(fim.getUpdateCount()).toBe(1);
34
+ // importance = runningSum / updateCount = [1, 4, 0] / 1
35
+ expect(fim.getImportance(0)).toBeCloseTo(1.0);
36
+ expect(fim.getImportance(1)).toBeCloseTo(4.0);
37
+ expect(fim.getImportance(2)).toBeCloseTo(0.0);
38
+ });
39
+ it('accumulates importance over multiple updates', () => {
40
+ const fim = new FisherInformationMatrix(2);
41
+ fim.update(new Float32Array([2.0, 0.0]));
42
+ fim.update(new Float32Array([2.0, 0.0]));
43
+ // runningSum = [4+4, 0] = [8, 0]; importance = [8/2, 0/2] = [4, 0]
44
+ expect(fim.getImportance(0)).toBeCloseTo(4.0);
45
+ expect(fim.getUpdateCount()).toBe(2);
46
+ });
47
+ it('decays importance by decayFactor', () => {
48
+ const fim = new FisherInformationMatrix(2);
49
+ fim.update(new Float32Array([2.0, 4.0]));
50
+ // After update: runningSum=[4, 16], importance=[4, 16]
51
+ fim.decay(0.5);
52
+ // runningSum=[2, 8]; importance = [2, 8] / max(1, 1) = [2, 8]
53
+ expect(fim.getImportance(0)).toBeCloseTo(2.0);
54
+ expect(fim.getImportance(1)).toBeCloseTo(8.0);
55
+ });
56
+ it('serialize and deserialize round-trip preserves values', () => {
57
+ const fim = new FisherInformationMatrix(3);
58
+ fim.update(new Float32Array([1.0, 2.0, 3.0]));
59
+ fim.update(new Float32Array([0.5, 1.0, 1.5]));
60
+ const buffer = fim.serialize();
61
+ const fim2 = new FisherInformationMatrix(3);
62
+ fim2.deserialize(buffer);
63
+ expect(fim2.getUpdateCount()).toBe(2);
64
+ // Values should match within float32 precision
65
+ for (let i = 0; i < 3; i++) {
66
+ expect(fim2.getImportance(i)).toBeCloseTo(fim.getImportance(i), 4);
67
+ }
68
+ });
69
+ it('reset clears all values to zero', () => {
70
+ const fim = new FisherInformationMatrix(3);
71
+ fim.update(new Float32Array([1.0, 2.0, 3.0]));
72
+ fim.reset();
73
+ expect(fim.getUpdateCount()).toBe(0);
74
+ expect(fim.getAverageImportance()).toBe(0);
75
+ expect(fim.getImportance(0)).toBe(0);
76
+ });
77
+ it('throws when deserializing buffer that is too small', () => {
78
+ const fim = new FisherInformationMatrix(4);
79
+ const tooSmall = Buffer.alloc(4); // way smaller than needed
80
+ expect(() => fim.deserialize(tooSmall)).toThrow(/Invalid Fisher matrix buffer/);
81
+ });
82
+ });
83
+ // ============================================================================
84
+ // contextToText
85
+ // ============================================================================
86
+ describe('contextToText', () => {
87
+ it('returns "empty context" for an empty context object', () => {
88
+ const ctx = { installedSkills: [] };
89
+ expect(contextToText(ctx)).toBe('empty context');
90
+ });
91
+ it('returns partial text when only some fields are set', () => {
92
+ const ctx = {
93
+ installedSkills: ['anthropic/commit'],
94
+ timeOfDay: 'morning',
95
+ };
96
+ const text = contextToText(ctx);
97
+ expect(text).toContain('installed: anthropic/commit');
98
+ expect(text).toContain('time: morning');
99
+ expect(text).not.toContain('frameworks');
100
+ });
101
+ it('returns all fields when all are set', () => {
102
+ const ctx = {
103
+ installedSkills: ['anthropic/commit', 'community/jest'],
104
+ frameworks: ['react', 'typescript'],
105
+ keywords: ['testing'],
106
+ timeOfDay: 'afternoon',
107
+ dayType: 'weekday',
108
+ };
109
+ const text = contextToText(ctx);
110
+ expect(text).toContain('installed: anthropic/commit, community/jest');
111
+ expect(text).toContain('frameworks: react, typescript');
112
+ expect(text).toContain('keywords: testing');
113
+ expect(text).toContain('time: afternoon');
114
+ expect(text).toContain('day: weekday');
115
+ });
116
+ });
117
+ // ============================================================================
118
+ // computeGradient
119
+ // ============================================================================
120
+ describe('computeGradient', () => {
121
+ it('computes element-wise difference a - b', () => {
122
+ const a = new Float32Array([3.0, 1.0, 2.0]);
123
+ const b = new Float32Array([1.0, 2.0, 1.0]);
124
+ const gradient = computeGradient(a, b);
125
+ expect(gradient[0]).toBeCloseTo(2.0);
126
+ expect(gradient[1]).toBeCloseTo(-1.0);
127
+ expect(gradient[2]).toBeCloseTo(1.0);
128
+ });
129
+ });
130
+ // ============================================================================
131
+ // cosineSimilarity
132
+ // ============================================================================
133
+ describe('cosineSimilarity', () => {
134
+ it('returns 1.0 for identical non-zero vectors', () => {
135
+ const a = new Float32Array([1.0, 0.0, 0.0]);
136
+ expect(cosineSimilarity(a, a)).toBeCloseTo(1.0);
137
+ });
138
+ it('returns 0 for orthogonal vectors', () => {
139
+ const a = new Float32Array([1.0, 0.0]);
140
+ const b = new Float32Array([0.0, 1.0]);
141
+ expect(cosineSimilarity(a, b)).toBeCloseTo(0.0);
142
+ });
143
+ it('returns 0 when either vector is all zeros (zero-norm edge case)', () => {
144
+ const zero = new Float32Array([0.0, 0.0]);
145
+ const a = new Float32Array([1.0, 2.0]);
146
+ expect(cosineSimilarity(zero, a)).toBe(0);
147
+ expect(cosineSimilarity(a, zero)).toBe(0);
148
+ });
149
+ });
150
+ // ============================================================================
151
+ // importanceWeightedSimilarity
152
+ // ============================================================================
153
+ describe('importanceWeightedSimilarity', () => {
154
+ it('computes weighted similarity between two identical vectors', () => {
155
+ const a = new Float32Array([1.0, 0.0]);
156
+ const importance = new Float32Array([1.0, 0.0]);
157
+ const result = importanceWeightedSimilarity(a, a, importance);
158
+ expect(result).toBeCloseTo(1.0);
159
+ });
160
+ it('returns 0 when the weighted norms are zero (zero-norm edge case)', () => {
161
+ const zero = new Float32Array([0.0, 0.0]);
162
+ const importance = new Float32Array([1.0, 1.0]);
163
+ const result = importanceWeightedSimilarity(zero, zero, importance);
164
+ expect(result).toBe(0);
165
+ });
166
+ });
167
+ // ============================================================================
168
+ // calculatePatternImportance
169
+ // ============================================================================
170
+ function makeStoredPattern(reward) {
171
+ return {
172
+ id: 'test-id',
173
+ context: { installedSkills: [] },
174
+ skill: {
175
+ skillId: 'community/test',
176
+ category: 'testing',
177
+ trustTier: 'community',
178
+ keywords: [],
179
+ qualityScore: 80,
180
+ installCount: 100,
181
+ },
182
+ originalScore: 0.8,
183
+ source: 'recommend',
184
+ contextEmbedding: new Float32Array([0.1, 0.2]),
185
+ outcome: { type: reward >= 0 ? 'accept' : 'dismiss', reward },
186
+ importance: 0.5,
187
+ accessCount: 3,
188
+ createdAt: new Date(),
189
+ lastAccessedAt: new Date(),
190
+ };
191
+ }
192
+ describe('calculatePatternImportance', () => {
193
+ it('gives higher importance for positive reward (1.5x multiplier)', () => {
194
+ const pattern = makeStoredPattern(1.0);
195
+ const outcome = { type: 'accept', reward: 1.0 };
196
+ const importance = calculatePatternImportance(pattern, outcome);
197
+ // baseImportance = 1.0 * 1.5 = 1.5, times other factors
198
+ expect(importance).toBeGreaterThan(0);
199
+ });
200
+ it('gives lower importance for negative reward (no 1.5x multiplier)', () => {
201
+ const positivePattern = makeStoredPattern(1.0);
202
+ const negativePattern = makeStoredPattern(-0.5);
203
+ const positiveOutcome = { type: 'accept', reward: 1.0 };
204
+ const negativeOutcome = { type: 'dismiss', reward: -0.5 };
205
+ const importancePos = calculatePatternImportance(positivePattern, positiveOutcome);
206
+ const importanceNeg = calculatePatternImportance(negativePattern, negativeOutcome);
207
+ // positive reward has 1.5x boost, so its importance should be higher
208
+ expect(importancePos).toBeGreaterThan(importanceNeg);
209
+ });
210
+ });
211
+ // ============================================================================
212
+ // shouldConsolidate
213
+ // ============================================================================
214
+ function makeConsolidationState(overrides = {}) {
215
+ return {
216
+ totalPatterns: 100,
217
+ patternsSinceLastConsolidation: 5,
218
+ lastConsolidation: null,
219
+ ...overrides,
220
+ };
221
+ }
222
+ const ewcConfig = {
223
+ lambda: 5.0,
224
+ fisherDecay: 0.95,
225
+ importanceThreshold: 0.01,
226
+ fisherSampleSize: 100,
227
+ consolidationThreshold: 0.1,
228
+ maxPatterns: 1000,
229
+ };
230
+ describe('shouldConsolidate', () => {
231
+ it('returns false when last consolidation was less than 1 hour ago', () => {
232
+ const state = makeConsolidationState({
233
+ lastConsolidation: new Date(Date.now() - 30 * 60 * 1000), // 30 min ago
234
+ totalPatterns: 200,
235
+ patternsSinceLastConsolidation: 50,
236
+ });
237
+ expect(shouldConsolidate(state, ewcConfig)).toBe(false);
238
+ });
239
+ it('returns false when totalPatterns is 0', () => {
240
+ const state = makeConsolidationState({ totalPatterns: 0, patternsSinceLastConsolidation: 0 });
241
+ expect(shouldConsolidate(state, ewcConfig)).toBe(false);
242
+ });
243
+ it('returns true when newPatternsRatio exceeds consolidationThreshold', () => {
244
+ // 20/100 = 0.2 > 0.1 threshold
245
+ const state = makeConsolidationState({
246
+ totalPatterns: 100,
247
+ patternsSinceLastConsolidation: 20,
248
+ lastConsolidation: new Date(Date.now() - 2 * 60 * 60 * 1000), // 2 hours ago
249
+ });
250
+ expect(shouldConsolidate(state, ewcConfig)).toBe(true);
251
+ });
252
+ it('returns true when totalPatterns > 90% of maxPatterns', () => {
253
+ const state = makeConsolidationState({
254
+ totalPatterns: 950, // > 1000 * 0.9 = 900
255
+ patternsSinceLastConsolidation: 5, // ratio = 5/950 < 0.1
256
+ lastConsolidation: new Date(Date.now() - 2 * 60 * 60 * 1000),
257
+ });
258
+ expect(shouldConsolidate(state, ewcConfig)).toBe(true);
259
+ });
260
+ });
261
+ // ============================================================================
262
+ // computeAverageEmbedding (requires in-memory SQLite)
263
+ // ============================================================================
264
+ describe('computeAverageEmbedding', () => {
265
+ let db;
266
+ beforeEach(() => {
267
+ db = createDatabase(':memory:');
268
+ db.exec(PATTERN_STORE_SCHEMA);
269
+ });
270
+ afterEach(() => {
271
+ db.close();
272
+ });
273
+ it('returns zero vector when the patterns table is empty', async () => {
274
+ const result = await computeAverageEmbedding(db, 100, 4);
275
+ expect(result).toEqual(new Float32Array(4));
276
+ });
277
+ it('returns the average of all stored embeddings', async () => {
278
+ const dims = 4;
279
+ // Store two embeddings: [1,2,3,4] and [3,4,5,6]
280
+ // Expected average: [2,3,4,5]
281
+ const emb1 = new Float32Array([1, 2, 3, 4]);
282
+ const emb2 = new Float32Array([3, 4, 5, 6]);
283
+ const buf1 = Buffer.from(emb1.buffer);
284
+ const buf2 = Buffer.from(emb2.buffer);
285
+ const insertStmt = db.prepare(`
286
+ INSERT INTO patterns (
287
+ pattern_id, context_embedding, skill_id, skill_features, context_data,
288
+ outcome_type, outcome_reward, importance, original_score, source,
289
+ access_count, created_at, last_accessed_at
290
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, unixepoch(), unixepoch())
291
+ `);
292
+ insertStmt.run('p1', buf1, 'skill1', '{}', '{}', 'accept', 1.0, 0.5, 0.8, 'recommend', 0);
293
+ insertStmt.run('p2', buf2, 'skill2', '{}', '{}', 'accept', 1.0, 0.5, 0.8, 'recommend', 0);
294
+ const result = await computeAverageEmbedding(db, 100, dims);
295
+ expect(result[0]).toBeCloseTo(2.0);
296
+ expect(result[1]).toBeCloseTo(3.0);
297
+ expect(result[2]).toBeCloseTo(4.0);
298
+ expect(result[3]).toBeCloseTo(5.0);
299
+ });
300
+ });
301
+ //# sourceMappingURL=PatternStore.helpers.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PatternStore.helpers.test.js","sourceRoot":"","sources":["../../../tests/learning/PatternStore.helpers.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @fileoverview Tests for CoInstallRepository (SMI-2761)
3
+ * @module @skillsmith/core/tests/repositories/CoInstallRepository
4
+ *
5
+ * Tests co-install recording, upsert symmetry, and min-count threshold.
6
+ * Uses createTestDatabase() which runs all migrations including v8.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=CoInstallRepository.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CoInstallRepository.test.d.ts","sourceRoot":"","sources":["../../../tests/repositories/CoInstallRepository.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * @fileoverview Tests for CoInstallRepository (SMI-2761)
3
+ * @module @skillsmith/core/tests/repositories/CoInstallRepository
4
+ *
5
+ * Tests co-install recording, upsert symmetry, and min-count threshold.
6
+ * Uses createTestDatabase() which runs all migrations including v8.
7
+ */
8
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
9
+ import { createTestDatabase, closeDatabase } from '../helpers/database.js';
10
+ import { CoInstallRepository } from '../../src/repositories/CoInstallRepository.js';
11
+ let db;
12
+ let repo;
13
+ beforeEach(() => {
14
+ db = createTestDatabase();
15
+ repo = new CoInstallRepository(db);
16
+ // Insert fixture skills so JOIN works in getTopCoInstalls
17
+ db.exec(`
18
+ INSERT INTO skills (id, name, author, description)
19
+ VALUES
20
+ ('skill-a', 'skill-a', 'test', 'Skill A description'),
21
+ ('skill-b', 'skill-b', 'test', 'Skill B description'),
22
+ ('skill-c', 'skill-c', 'test', 'Skill C description');
23
+ `);
24
+ });
25
+ afterEach(() => {
26
+ closeDatabase(db);
27
+ });
28
+ describe('CoInstallRepository', () => {
29
+ describe('getTopCoInstalls — empty table', () => {
30
+ it('returns empty array when no co-installs recorded', () => {
31
+ const results = repo.getTopCoInstalls('skill-a');
32
+ expect(results).toEqual([]);
33
+ });
34
+ });
35
+ describe('recordCoInstall — upsert symmetry', () => {
36
+ it('creates (A,B) and (B,A) rows on first call', () => {
37
+ repo.recordCoInstall('skill-a', 'skill-b');
38
+ const forA = repo.getTopCoInstalls('skill-a', 10, 1);
39
+ const forB = repo.getTopCoInstalls('skill-b', 10, 1);
40
+ expect(forA.map((r) => r.skillId)).toContain('skill-b');
41
+ expect(forB.map((r) => r.skillId)).toContain('skill-a');
42
+ });
43
+ it('increments install_count on repeated calls', () => {
44
+ repo.recordCoInstall('skill-a', 'skill-b');
45
+ repo.recordCoInstall('skill-a', 'skill-b');
46
+ repo.recordCoInstall('skill-a', 'skill-b');
47
+ const results = repo.getTopCoInstalls('skill-a', 10, 1);
48
+ const entry = results.find((r) => r.skillId === 'skill-b');
49
+ expect(entry?.installCount).toBe(3);
50
+ });
51
+ it('is idempotent for self-pairs (no-op)', () => {
52
+ // Should not throw, simply return
53
+ expect(() => repo.recordCoInstall('skill-a', 'skill-a')).not.toThrow();
54
+ const results = repo.getTopCoInstalls('skill-a', 10, 1);
55
+ expect(results).toEqual([]);
56
+ });
57
+ });
58
+ describe('recordSessionCoInstalls', () => {
59
+ it('records all pairs in a session', () => {
60
+ repo.recordSessionCoInstalls(['skill-a', 'skill-b', 'skill-c']);
61
+ const forA = repo.getTopCoInstalls('skill-a', 10, 1);
62
+ const ids = forA.map((r) => r.skillId);
63
+ expect(ids).toContain('skill-b');
64
+ expect(ids).toContain('skill-c');
65
+ });
66
+ it('is a no-op for fewer than 2 skills', () => {
67
+ expect(() => repo.recordSessionCoInstalls([])).not.toThrow();
68
+ expect(() => repo.recordSessionCoInstalls(['skill-a'])).not.toThrow();
69
+ expect(repo.getTopCoInstalls('skill-a', 10, 1)).toEqual([]);
70
+ });
71
+ });
72
+ describe('getTopCoInstalls — min-count boundary', () => {
73
+ it('excludes entries below minCount (4 → excluded at default threshold 5)', () => {
74
+ // Record 4 co-installs
75
+ for (let i = 0; i < 4; i++) {
76
+ repo.recordCoInstall('skill-a', 'skill-b');
77
+ }
78
+ const results = repo.getTopCoInstalls('skill-a'); // default minCount=5
79
+ expect(results.find((r) => r.skillId === 'skill-b')).toBeUndefined();
80
+ });
81
+ it('includes entries at exactly minCount (5 → included at default threshold 5)', () => {
82
+ // Record exactly 5 co-installs
83
+ for (let i = 0; i < 5; i++) {
84
+ repo.recordCoInstall('skill-a', 'skill-b');
85
+ }
86
+ const results = repo.getTopCoInstalls('skill-a'); // default minCount=5
87
+ expect(results.find((r) => r.skillId === 'skill-b')).toBeDefined();
88
+ });
89
+ });
90
+ describe('getTopCoInstalls — result shape', () => {
91
+ it('returns skills with name/description/author from skills table', () => {
92
+ for (let i = 0; i < 5; i++) {
93
+ repo.recordCoInstall('skill-a', 'skill-b');
94
+ }
95
+ const results = repo.getTopCoInstalls('skill-a');
96
+ expect(results).toHaveLength(1);
97
+ expect(results[0]).toMatchObject({
98
+ skillId: 'skill-b',
99
+ name: 'skill-b',
100
+ description: 'Skill B description',
101
+ author: 'test',
102
+ installCount: 5,
103
+ });
104
+ });
105
+ it('respects the limit parameter', () => {
106
+ for (let i = 0; i < 5; i++) {
107
+ repo.recordCoInstall('skill-a', 'skill-b');
108
+ repo.recordCoInstall('skill-a', 'skill-c');
109
+ }
110
+ const results = repo.getTopCoInstalls('skill-a', 1); // limit=1
111
+ expect(results).toHaveLength(1);
112
+ });
113
+ it('orders results by install_count descending', () => {
114
+ for (let i = 0; i < 10; i++)
115
+ repo.recordCoInstall('skill-a', 'skill-b');
116
+ for (let i = 0; i < 5; i++)
117
+ repo.recordCoInstall('skill-a', 'skill-c');
118
+ const results = repo.getTopCoInstalls('skill-a', 10, 1);
119
+ expect(results[0].skillId).toBe('skill-b');
120
+ expect(results[0].installCount).toBe(10);
121
+ expect(results[1].skillId).toBe('skill-c');
122
+ expect(results[1].installCount).toBe(5);
123
+ });
124
+ });
125
+ });
126
+ //# sourceMappingURL=CoInstallRepository.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CoInstallRepository.test.js","sourceRoot":"","sources":["../../../tests/repositories/CoInstallRepository.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @fileoverview Tests for SkillDependencyRepository (SMI-3143)
3
+ * @module @skillsmith/core/tests/repositories/SkillDependencyRepository
4
+ *
5
+ * Tests CRUD operations, upsert idempotency, source filtering,
6
+ * reverse lookup, and graceful missing-table handling.
7
+ * Uses createTestDatabase() which runs all migrations including v10.
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=SkillDependencyRepository.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SkillDependencyRepository.test.d.ts","sourceRoot":"","sources":["../../../tests/repositories/SkillDependencyRepository.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}