@happyvertical/smrt-core 0.30.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 (631) hide show
  1. package/AGENTS.md +124 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +265 -0
  5. package/bin/smrt-prebuild.js +26 -0
  6. package/dist/__tests__/fixtures/advisor-test-classes.d.ts +28 -0
  7. package/dist/__tests__/fixtures/advisor-test-classes.d.ts.map +1 -0
  8. package/dist/__tests__/fixtures/collection-coverage-fixtures.d.ts +12 -0
  9. package/dist/__tests__/fixtures/collection-coverage-fixtures.d.ts.map +1 -0
  10. package/dist/__tests__/fixtures/inheritance-resolver-fixtures.d.ts +28 -0
  11. package/dist/__tests__/fixtures/inheritance-resolver-fixtures.d.ts.map +1 -0
  12. package/dist/__tests__/fixtures/inheritance-test-classes.d.ts +43 -0
  13. package/dist/__tests__/fixtures/inheritance-test-classes.d.ts.map +1 -0
  14. package/dist/__tests__/fixtures/mcp-integration-test-classes.d.ts +18 -0
  15. package/dist/__tests__/fixtures/mcp-integration-test-classes.d.ts.map +1 -0
  16. package/dist/__tests__/fixtures/object-ai-memory-fixtures.d.ts +15 -0
  17. package/dist/__tests__/fixtures/object-ai-memory-fixtures.d.ts.map +1 -0
  18. package/dist/__tests__/fixtures/object-spec-test-classes.d.ts +13 -0
  19. package/dist/__tests__/fixtures/object-spec-test-classes.d.ts.map +1 -0
  20. package/dist/__tests__/fixtures/registry-test-classes.d.ts +23 -0
  21. package/dist/__tests__/fixtures/registry-test-classes.d.ts.map +1 -0
  22. package/dist/adapters/ai-usage.d.ts +23 -0
  23. package/dist/adapters/ai-usage.d.ts.map +1 -0
  24. package/dist/adapters/ai-usage.js +105 -0
  25. package/dist/adapters/ai-usage.js.map +1 -0
  26. package/dist/adapters/cost-rates.d.ts +20 -0
  27. package/dist/adapters/cost-rates.d.ts.map +1 -0
  28. package/dist/adapters/cost-rates.js +40 -0
  29. package/dist/adapters/cost-rates.js.map +1 -0
  30. package/dist/adapters/index.d.ts +19 -0
  31. package/dist/adapters/index.d.ts.map +1 -0
  32. package/dist/adapters/metrics.d.ts +111 -0
  33. package/dist/adapters/metrics.d.ts.map +1 -0
  34. package/dist/adapters/metrics.js +169 -0
  35. package/dist/adapters/metrics.js.map +1 -0
  36. package/dist/adapters/pubsub.d.ts +124 -0
  37. package/dist/adapters/pubsub.d.ts.map +1 -0
  38. package/dist/adapters/pubsub.js +121 -0
  39. package/dist/adapters/pubsub.js.map +1 -0
  40. package/dist/browser.d.ts +32 -0
  41. package/dist/browser.d.ts.map +1 -0
  42. package/dist/browser.js +68 -0
  43. package/dist/browser.js.map +1 -0
  44. package/dist/child-accessors.d.ts +27 -0
  45. package/dist/child-accessors.d.ts.map +1 -0
  46. package/dist/child-accessors.js +35 -0
  47. package/dist/child-accessors.js.map +1 -0
  48. package/dist/class.d.ts +313 -0
  49. package/dist/class.d.ts.map +1 -0
  50. package/dist/class.js +896 -0
  51. package/dist/class.js.map +1 -0
  52. package/dist/collection-cache.d.ts +110 -0
  53. package/dist/collection-cache.d.ts.map +1 -0
  54. package/dist/collection-cache.js +187 -0
  55. package/dist/collection-cache.js.map +1 -0
  56. package/dist/collection.d.ts +894 -0
  57. package/dist/collection.d.ts.map +1 -0
  58. package/dist/collection.js +1987 -0
  59. package/dist/collection.js.map +1 -0
  60. package/dist/config/global-config.d.ts +3 -0
  61. package/dist/config/global-config.d.ts.map +1 -0
  62. package/dist/config/global-config.js +19 -0
  63. package/dist/config/global-config.js.map +1 -0
  64. package/dist/config.d.ts +145 -0
  65. package/dist/config.d.ts.map +1 -0
  66. package/dist/config.js +57 -0
  67. package/dist/config.js.map +1 -0
  68. package/dist/consumer-plugin/index.d.ts +22 -0
  69. package/dist/consumer-plugin/index.d.ts.map +1 -0
  70. package/dist/consumer-plugin/index.js +452 -0
  71. package/dist/consumer-plugin/index.js.map +1 -0
  72. package/dist/consumer-plugin.d.ts +8 -0
  73. package/dist/consumer-plugin.d.ts.map +1 -0
  74. package/dist/consumer-plugin.js +5 -0
  75. package/dist/consumer-plugin.js.map +1 -0
  76. package/dist/database.d.ts +95 -0
  77. package/dist/database.d.ts.map +1 -0
  78. package/dist/database.js +32 -0
  79. package/dist/database.js.map +1 -0
  80. package/dist/decorators/compatibility.d.ts +14 -0
  81. package/dist/decorators/compatibility.d.ts.map +1 -0
  82. package/dist/decorators/compatibility.js +111 -0
  83. package/dist/decorators/compatibility.js.map +1 -0
  84. package/dist/decorators/index.d.ts +381 -0
  85. package/dist/decorators/index.d.ts.map +1 -0
  86. package/dist/decorators/index.js +104 -0
  87. package/dist/decorators/index.js.map +1 -0
  88. package/dist/dispatch/bus.d.ts +306 -0
  89. package/dist/dispatch/bus.d.ts.map +1 -0
  90. package/dist/dispatch/bus.js +583 -0
  91. package/dist/dispatch/bus.js.map +1 -0
  92. package/dist/dispatch/collections/DispatchSubscriptions.d.ts +79 -0
  93. package/dist/dispatch/collections/DispatchSubscriptions.d.ts.map +1 -0
  94. package/dist/dispatch/collections/DispatchSubscriptions.js +243 -0
  95. package/dist/dispatch/collections/DispatchSubscriptions.js.map +1 -0
  96. package/dist/dispatch/collections/Dispatches.d.ts +98 -0
  97. package/dist/dispatch/collections/Dispatches.d.ts.map +1 -0
  98. package/dist/dispatch/collections/Dispatches.js +358 -0
  99. package/dist/dispatch/collections/Dispatches.js.map +1 -0
  100. package/dist/dispatch/index.d.ts +47 -0
  101. package/dist/dispatch/index.d.ts.map +1 -0
  102. package/dist/dispatch/models/Dispatch.d.ts +101 -0
  103. package/dist/dispatch/models/Dispatch.d.ts.map +1 -0
  104. package/dist/dispatch/models/Dispatch.js +162 -0
  105. package/dist/dispatch/models/Dispatch.js.map +1 -0
  106. package/dist/dispatch/models/DispatchSubscription.d.ts +83 -0
  107. package/dist/dispatch/models/DispatchSubscription.d.ts.map +1 -0
  108. package/dist/dispatch/models/DispatchSubscription.js +112 -0
  109. package/dist/dispatch/models/DispatchSubscription.js.map +1 -0
  110. package/dist/dispatch/tenant-resolver.d.ts +98 -0
  111. package/dist/dispatch/tenant-resolver.d.ts.map +1 -0
  112. package/dist/dispatch/tenant-resolver.js +32 -0
  113. package/dist/dispatch/tenant-resolver.js.map +1 -0
  114. package/dist/dispatch/types.d.ts +149 -0
  115. package/dist/dispatch/types.d.ts.map +1 -0
  116. package/dist/embeddings/hash.d.ts +33 -0
  117. package/dist/embeddings/hash.d.ts.map +1 -0
  118. package/dist/embeddings/hash.js +37 -0
  119. package/dist/embeddings/hash.js.map +1 -0
  120. package/dist/embeddings/index.d.ts +36 -0
  121. package/dist/embeddings/index.d.ts.map +1 -0
  122. package/dist/embeddings/provider.d.ts +75 -0
  123. package/dist/embeddings/provider.d.ts.map +1 -0
  124. package/dist/embeddings/provider.js +170 -0
  125. package/dist/embeddings/provider.js.map +1 -0
  126. package/dist/embeddings/similarity.d.ts +47 -0
  127. package/dist/embeddings/similarity.d.ts.map +1 -0
  128. package/dist/embeddings/similarity.js +64 -0
  129. package/dist/embeddings/similarity.js.map +1 -0
  130. package/dist/embeddings/storage.d.ts +125 -0
  131. package/dist/embeddings/storage.d.ts.map +1 -0
  132. package/dist/embeddings/storage.js +283 -0
  133. package/dist/embeddings/storage.js.map +1 -0
  134. package/dist/embeddings/types.d.ts +250 -0
  135. package/dist/embeddings/types.d.ts.map +1 -0
  136. package/dist/errors.d.ts +363 -0
  137. package/dist/errors.d.ts.map +1 -0
  138. package/dist/errors.js +669 -0
  139. package/dist/errors.js.map +1 -0
  140. package/dist/generators/cli.d.ts +162 -0
  141. package/dist/generators/cli.d.ts.map +1 -0
  142. package/dist/generators/cli.js +462 -0
  143. package/dist/generators/cli.js.map +1 -0
  144. package/dist/generators/index.d.ts +13 -0
  145. package/dist/generators/index.d.ts.map +1 -0
  146. package/dist/generators/mcp-runtime-template.d.ts +60 -0
  147. package/dist/generators/mcp-runtime-template.d.ts.map +1 -0
  148. package/dist/generators/mcp-runtime-template.js +509 -0
  149. package/dist/generators/mcp-runtime-template.js.map +1 -0
  150. package/dist/generators/mcp.d.ts +231 -0
  151. package/dist/generators/mcp.d.ts.map +1 -0
  152. package/dist/generators/mcp.js +1220 -0
  153. package/dist/generators/mcp.js.map +1 -0
  154. package/dist/generators/rest.d.ts +171 -0
  155. package/dist/generators/rest.d.ts.map +1 -0
  156. package/dist/generators/rest.js +591 -0
  157. package/dist/generators/rest.js.map +1 -0
  158. package/dist/generators/swagger.d.ts +21 -0
  159. package/dist/generators/swagger.d.ts.map +1 -0
  160. package/dist/generators/swagger.js +307 -0
  161. package/dist/generators/swagger.js.map +1 -0
  162. package/dist/generators/tenant-gate.d.ts +74 -0
  163. package/dist/generators/tenant-gate.d.ts.map +1 -0
  164. package/dist/generators/tenant-gate.js +15 -0
  165. package/dist/generators/tenant-gate.js.map +1 -0
  166. package/dist/generators.d.ts +8 -0
  167. package/dist/generators.d.ts.map +1 -0
  168. package/dist/generators.js +19 -0
  169. package/dist/generators.js.map +1 -0
  170. package/dist/hierarchical.d.ts +103 -0
  171. package/dist/hierarchical.d.ts.map +1 -0
  172. package/dist/hierarchical.js +184 -0
  173. package/dist/hierarchical.js.map +1 -0
  174. package/dist/index.d.ts +57 -0
  175. package/dist/index.d.ts.map +1 -0
  176. package/dist/index.js +202 -0
  177. package/dist/index.js.map +1 -0
  178. package/dist/interceptors.d.ts +251 -0
  179. package/dist/interceptors.d.ts.map +1 -0
  180. package/dist/interceptors.js +259 -0
  181. package/dist/interceptors.js.map +1 -0
  182. package/dist/junction.d.ts +99 -0
  183. package/dist/junction.d.ts.map +1 -0
  184. package/dist/junction.js +136 -0
  185. package/dist/junction.js.map +1 -0
  186. package/dist/knowledge.d.ts +11 -0
  187. package/dist/knowledge.d.ts.map +1 -0
  188. package/dist/knowledge.js +310 -0
  189. package/dist/knowledge.js.map +1 -0
  190. package/dist/lazy-config.d.ts +160 -0
  191. package/dist/lazy-config.d.ts.map +1 -0
  192. package/dist/lazy-config.js +146 -0
  193. package/dist/lazy-config.js.map +1 -0
  194. package/dist/manifest/discover-base-classes.d.ts +78 -0
  195. package/dist/manifest/discover-base-classes.d.ts.map +1 -0
  196. package/dist/manifest/discover-base-classes.js +85 -0
  197. package/dist/manifest/discover-base-classes.js.map +1 -0
  198. package/dist/manifest/discover-smrt-packages.d.ts +48 -0
  199. package/dist/manifest/discover-smrt-packages.d.ts.map +1 -0
  200. package/dist/manifest/discover-smrt-packages.js +361 -0
  201. package/dist/manifest/discover-smrt-packages.js.map +1 -0
  202. package/dist/manifest/generator.d.ts +93 -0
  203. package/dist/manifest/generator.d.ts.map +1 -0
  204. package/dist/manifest/generator.js +380 -0
  205. package/dist/manifest/generator.js.map +1 -0
  206. package/dist/manifest/index.d.ts +16 -0
  207. package/dist/manifest/index.d.ts.map +1 -0
  208. package/dist/manifest/index.js +51 -0
  209. package/dist/manifest/index.js.map +1 -0
  210. package/dist/manifest/manager.d.ts +51 -0
  211. package/dist/manifest/manager.d.ts.map +1 -0
  212. package/dist/manifest/manager.js +89 -0
  213. package/dist/manifest/manager.js.map +1 -0
  214. package/dist/manifest/manifest-loader.d.ts +187 -0
  215. package/dist/manifest/manifest-loader.d.ts.map +1 -0
  216. package/dist/manifest/manifest-loader.js +847 -0
  217. package/dist/manifest/manifest-loader.js.map +1 -0
  218. package/dist/manifest/sources/composite.d.ts +22 -0
  219. package/dist/manifest/sources/composite.d.ts.map +1 -0
  220. package/dist/manifest/sources/composite.js +60 -0
  221. package/dist/manifest/sources/composite.js.map +1 -0
  222. package/dist/manifest/sources/embedded.d.ts +7 -0
  223. package/dist/manifest/sources/embedded.d.ts.map +1 -0
  224. package/dist/manifest/sources/embedded.js +30 -0
  225. package/dist/manifest/sources/embedded.js.map +1 -0
  226. package/dist/manifest/sources/explicit-paths.d.ts +17 -0
  227. package/dist/manifest/sources/explicit-paths.d.ts.map +1 -0
  228. package/dist/manifest/sources/explicit-paths.js +35 -0
  229. package/dist/manifest/sources/explicit-paths.js.map +1 -0
  230. package/dist/manifest/sources/fallback.d.ts +25 -0
  231. package/dist/manifest/sources/fallback.d.ts.map +1 -0
  232. package/dist/manifest/sources/fallback.js +63 -0
  233. package/dist/manifest/sources/fallback.js.map +1 -0
  234. package/dist/manifest/sources/index.d.ts +17 -0
  235. package/dist/manifest/sources/index.d.ts.map +1 -0
  236. package/dist/manifest/sources/local-test.d.ts +7 -0
  237. package/dist/manifest/sources/local-test.d.ts.map +1 -0
  238. package/dist/manifest/sources/local-test.js +21 -0
  239. package/dist/manifest/sources/local-test.js.map +1 -0
  240. package/dist/manifest/sources/static.d.ts +7 -0
  241. package/dist/manifest/sources/static.d.ts.map +1 -0
  242. package/dist/manifest/sources/static.js +19 -0
  243. package/dist/manifest/sources/static.js.map +1 -0
  244. package/dist/manifest/sources/test.d.ts +7 -0
  245. package/dist/manifest/sources/test.d.ts.map +1 -0
  246. package/dist/manifest/sources/test.js +21 -0
  247. package/dist/manifest/sources/test.js.map +1 -0
  248. package/dist/manifest/sources/types.d.ts +79 -0
  249. package/dist/manifest/sources/types.d.ts.map +1 -0
  250. package/dist/manifest/sources/types.js +61 -0
  251. package/dist/manifest/sources/types.js.map +1 -0
  252. package/dist/manifest/static-manifest.d.ts +4 -0
  253. package/dist/manifest/static-manifest.d.ts.map +1 -0
  254. package/dist/manifest/static-manifest.js +1535 -0
  255. package/dist/manifest/static-manifest.js.map +1 -0
  256. package/dist/manifest/store.d.ts +111 -0
  257. package/dist/manifest/store.d.ts.map +1 -0
  258. package/dist/manifest/store.js +431 -0
  259. package/dist/manifest/store.js.map +1 -0
  260. package/dist/manifest/test-manifest-loader.d.ts +3 -0
  261. package/dist/manifest/test-manifest-loader.d.ts.map +1 -0
  262. package/dist/manifest/test-manifest-stub.d.ts +4 -0
  263. package/dist/manifest/test-manifest-stub.d.ts.map +1 -0
  264. package/dist/manifest/test-manifest-stub.js +80013 -0
  265. package/dist/manifest/test-manifest-stub.js.map +1 -0
  266. package/dist/manifest.d.ts +8 -0
  267. package/dist/manifest.d.ts.map +1 -0
  268. package/dist/manifest.js +20 -0
  269. package/dist/manifest.js.map +1 -0
  270. package/dist/manifest.json +1489 -0
  271. package/dist/mcp-advisor/index.d.ts +499 -0
  272. package/dist/mcp-advisor/index.d.ts.map +1 -0
  273. package/dist/mcp-advisor/tools/add-ai-methods.d.ts +6 -0
  274. package/dist/mcp-advisor/tools/add-ai-methods.d.ts.map +1 -0
  275. package/dist/mcp-advisor/tools/configure-decorators.d.ts +6 -0
  276. package/dist/mcp-advisor/tools/configure-decorators.d.ts.map +1 -0
  277. package/dist/mcp-advisor/tools/generate-collection.d.ts +6 -0
  278. package/dist/mcp-advisor/tools/generate-collection.d.ts.map +1 -0
  279. package/dist/mcp-advisor/tools/generate-field-definitions.d.ts +6 -0
  280. package/dist/mcp-advisor/tools/generate-field-definitions.d.ts.map +1 -0
  281. package/dist/mcp-advisor/tools/generate-smrt-class.d.ts +6 -0
  282. package/dist/mcp-advisor/tools/generate-smrt-class.d.ts.map +1 -0
  283. package/dist/mcp-advisor/tools/get-object-config.d.ts +6 -0
  284. package/dist/mcp-advisor/tools/get-object-config.d.ts.map +1 -0
  285. package/dist/mcp-advisor/tools/get-object-schema.d.ts +10 -0
  286. package/dist/mcp-advisor/tools/get-object-schema.d.ts.map +1 -0
  287. package/dist/mcp-advisor/tools/list-registered-objects.d.ts +9 -0
  288. package/dist/mcp-advisor/tools/list-registered-objects.d.ts.map +1 -0
  289. package/dist/mcp-advisor/tools/preview-api-endpoints.d.ts +9 -0
  290. package/dist/mcp-advisor/tools/preview-api-endpoints.d.ts.map +1 -0
  291. package/dist/mcp-advisor/tools/preview-mcp-tools.d.ts +9 -0
  292. package/dist/mcp-advisor/tools/preview-mcp-tools.d.ts.map +1 -0
  293. package/dist/mcp-advisor/tools/validate-smrt-object.d.ts +6 -0
  294. package/dist/mcp-advisor/tools/validate-smrt-object.d.ts.map +1 -0
  295. package/dist/mcp-advisor/types.d.ts +209 -0
  296. package/dist/mcp-advisor/types.d.ts.map +1 -0
  297. package/dist/migrations/backfill-tracker.d.ts +84 -0
  298. package/dist/migrations/backfill-tracker.d.ts.map +1 -0
  299. package/dist/migrations/backfill-tracker.js +118 -0
  300. package/dist/migrations/backfill-tracker.js.map +1 -0
  301. package/dist/migrations/checksum.d.ts +43 -0
  302. package/dist/migrations/checksum.d.ts.map +1 -0
  303. package/dist/migrations/checksum.js +32 -0
  304. package/dist/migrations/checksum.js.map +1 -0
  305. package/dist/migrations/differ.d.ts +186 -0
  306. package/dist/migrations/differ.d.ts.map +1 -0
  307. package/dist/migrations/differ.js +601 -0
  308. package/dist/migrations/differ.js.map +1 -0
  309. package/dist/migrations/generator.d.ts +133 -0
  310. package/dist/migrations/generator.d.ts.map +1 -0
  311. package/dist/migrations/generator.js +328 -0
  312. package/dist/migrations/generator.js.map +1 -0
  313. package/dist/migrations/index.d.ts +20 -0
  314. package/dist/migrations/index.d.ts.map +1 -0
  315. package/dist/migrations/orchestrate.d.ts +148 -0
  316. package/dist/migrations/orchestrate.d.ts.map +1 -0
  317. package/dist/migrations/orchestrate.js +118 -0
  318. package/dist/migrations/orchestrate.js.map +1 -0
  319. package/dist/migrations/tracker.d.ts +134 -0
  320. package/dist/migrations/tracker.d.ts.map +1 -0
  321. package/dist/migrations/tracker.js +624 -0
  322. package/dist/migrations/tracker.js.map +1 -0
  323. package/dist/migrations/types.d.ts +221 -0
  324. package/dist/migrations/types.d.ts.map +1 -0
  325. package/dist/migrations.d.ts +7 -0
  326. package/dist/migrations.d.ts.map +1 -0
  327. package/dist/migrations.js +26 -0
  328. package/dist/migrations.js.map +1 -0
  329. package/dist/node_modules/.pnpm/balanced-match@4.0.4/node_modules/balanced-match/dist/esm/index.js +56 -0
  330. package/dist/node_modules/.pnpm/balanced-match@4.0.4/node_modules/balanced-match/dist/esm/index.js.map +1 -0
  331. package/dist/node_modules/.pnpm/brace-expansion@5.0.5/node_modules/brace-expansion/dist/esm/index.js +163 -0
  332. package/dist/node_modules/.pnpm/brace-expansion@5.0.5/node_modules/brace-expansion/dist/esm/index.js.map +1 -0
  333. package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/assert-valid-pattern.js +13 -0
  334. package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/assert-valid-pattern.js.map +1 -0
  335. package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/ast.js +654 -0
  336. package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/ast.js.map +1 -0
  337. package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/brace-expressions.js +111 -0
  338. package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/brace-expressions.js.map +1 -0
  339. package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/escape.js +10 -0
  340. package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/escape.js.map +1 -0
  341. package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/index.js +824 -0
  342. package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/index.js.map +1 -0
  343. package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/unescape.js +10 -0
  344. package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/unescape.js.map +1 -0
  345. package/dist/object.d.ts +1202 -0
  346. package/dist/object.d.ts.map +1 -0
  347. package/dist/object.js +2731 -0
  348. package/dist/object.js.map +1 -0
  349. package/dist/polymorphic-association.d.ts +69 -0
  350. package/dist/polymorphic-association.d.ts.map +1 -0
  351. package/dist/polymorphic-association.js +96 -0
  352. package/dist/polymorphic-association.js.map +1 -0
  353. package/dist/prebuild/cli.d.ts +7 -0
  354. package/dist/prebuild/cli.d.ts.map +1 -0
  355. package/dist/prebuild/cli.js +29 -0
  356. package/dist/prebuild/cli.js.map +1 -0
  357. package/dist/prebuild/index.d.ts +22 -0
  358. package/dist/prebuild/index.d.ts.map +1 -0
  359. package/dist/prebuild/index.js +239 -0
  360. package/dist/prebuild/index.js.map +1 -0
  361. package/dist/prebuild.d.ts +8 -0
  362. package/dist/prebuild.d.ts.map +1 -0
  363. package/dist/prebuild.js +6 -0
  364. package/dist/prebuild.js.map +1 -0
  365. package/dist/registry/cache-config.d.ts +13 -0
  366. package/dist/registry/cache-config.d.ts.map +1 -0
  367. package/dist/registry/cache-config.js +17 -0
  368. package/dist/registry/cache-config.js.map +1 -0
  369. package/dist/registry/class-registration.d.ts +31 -0
  370. package/dist/registry/class-registration.d.ts.map +1 -0
  371. package/dist/registry/class-registration.js +1074 -0
  372. package/dist/registry/class-registration.js.map +1 -0
  373. package/dist/registry/collection-resolution.d.ts +45 -0
  374. package/dist/registry/collection-resolution.d.ts.map +1 -0
  375. package/dist/registry/collection-resolution.js +121 -0
  376. package/dist/registry/collection-resolution.js.map +1 -0
  377. package/dist/registry/collision-policy.d.ts +179 -0
  378. package/dist/registry/collision-policy.d.ts.map +1 -0
  379. package/dist/registry/collision-policy.js +153 -0
  380. package/dist/registry/collision-policy.js.map +1 -0
  381. package/dist/registry/diagnostics.d.ts +58 -0
  382. package/dist/registry/diagnostics.d.ts.map +1 -0
  383. package/dist/registry/diagnostics.js +54 -0
  384. package/dist/registry/diagnostics.js.map +1 -0
  385. package/dist/registry/embedding-manager.d.ts +23 -0
  386. package/dist/registry/embedding-manager.d.ts.map +1 -0
  387. package/dist/registry/embedding-manager.js +62 -0
  388. package/dist/registry/embedding-manager.js.map +1 -0
  389. package/dist/registry/index.d.ts +13 -0
  390. package/dist/registry/index.d.ts.map +1 -0
  391. package/dist/registry/inheritance-resolver.d.ts +13 -0
  392. package/dist/registry/inheritance-resolver.d.ts.map +1 -0
  393. package/dist/registry/inheritance-resolver.js +244 -0
  394. package/dist/registry/inheritance-resolver.js.map +1 -0
  395. package/dist/registry/manifest-field-merge.d.ts +4 -0
  396. package/dist/registry/manifest-field-merge.d.ts.map +1 -0
  397. package/dist/registry/manifest-field-merge.js +82 -0
  398. package/dist/registry/manifest-field-merge.js.map +1 -0
  399. package/dist/registry/name-resolver.d.ts +102 -0
  400. package/dist/registry/name-resolver.d.ts.map +1 -0
  401. package/dist/registry/name-resolver.js +241 -0
  402. package/dist/registry/name-resolver.js.map +1 -0
  403. package/dist/registry/relationship-graph.d.ts +16 -0
  404. package/dist/registry/relationship-graph.d.ts.map +1 -0
  405. package/dist/registry/relationship-graph.js +79 -0
  406. package/dist/registry/relationship-graph.js.map +1 -0
  407. package/dist/registry/schema-builder.d.ts +113 -0
  408. package/dist/registry/schema-builder.d.ts.map +1 -0
  409. package/dist/registry/schema-builder.js +474 -0
  410. package/dist/registry/schema-builder.js.map +1 -0
  411. package/dist/registry/shared-state.d.ts +62 -0
  412. package/dist/registry/shared-state.d.ts.map +1 -0
  413. package/dist/registry/shared-state.js +135 -0
  414. package/dist/registry/shared-state.js.map +1 -0
  415. package/dist/registry/types.d.ts +667 -0
  416. package/dist/registry/types.d.ts.map +1 -0
  417. package/dist/registry/validator.d.ts +13 -0
  418. package/dist/registry/validator.d.ts.map +1 -0
  419. package/dist/registry/validator.js +138 -0
  420. package/dist/registry/validator.js.map +1 -0
  421. package/dist/registry.d.ts +1358 -0
  422. package/dist/registry.d.ts.map +1 -0
  423. package/dist/registry.js +2301 -0
  424. package/dist/registry.js.map +1 -0
  425. package/dist/runtime/client.d.ts +34 -0
  426. package/dist/runtime/client.d.ts.map +1 -0
  427. package/dist/runtime/client.js +104 -0
  428. package/dist/runtime/client.js.map +1 -0
  429. package/dist/runtime/index.d.ts +10 -0
  430. package/dist/runtime/index.d.ts.map +1 -0
  431. package/dist/runtime/mcp.d.ts +47 -0
  432. package/dist/runtime/mcp.d.ts.map +1 -0
  433. package/dist/runtime/mcp.js +72 -0
  434. package/dist/runtime/mcp.js.map +1 -0
  435. package/dist/runtime/server.d.ts +92 -0
  436. package/dist/runtime/server.d.ts.map +1 -0
  437. package/dist/runtime/server.js +390 -0
  438. package/dist/runtime/server.js.map +1 -0
  439. package/dist/runtime/types.d.ts +58 -0
  440. package/dist/runtime/types.d.ts.map +1 -0
  441. package/dist/runtime.d.ts +8 -0
  442. package/dist/runtime.d.ts.map +1 -0
  443. package/dist/runtime.js +10 -0
  444. package/dist/runtime.js.map +1 -0
  445. package/dist/scanner/import-scanner.d.ts +7 -0
  446. package/dist/scanner/import-scanner.d.ts.map +1 -0
  447. package/dist/scanner/index.d.ts +12 -0
  448. package/dist/scanner/index.d.ts.map +1 -0
  449. package/dist/scanner/manifest-generator.d.ts +304 -0
  450. package/dist/scanner/manifest-generator.d.ts.map +1 -0
  451. package/dist/scanner/manifest-generator.js +1707 -0
  452. package/dist/scanner/manifest-generator.js.map +1 -0
  453. package/dist/scanner/test-file-patterns.d.ts +18 -0
  454. package/dist/scanner/test-file-patterns.d.ts.map +1 -0
  455. package/dist/scanner/test-file-patterns.js +16 -0
  456. package/dist/scanner/test-file-patterns.js.map +1 -0
  457. package/dist/scanner/types.d.ts +313 -0
  458. package/dist/scanner/types.d.ts.map +1 -0
  459. package/dist/scanner/types.js +2 -0
  460. package/dist/scanner/types.js.map +1 -0
  461. package/dist/scanner.d.ts +6 -0
  462. package/dist/scanner.d.ts.map +1 -0
  463. package/dist/scanner.js +6 -0
  464. package/dist/scanner.js.map +1 -0
  465. package/dist/schema/code-generator.d.ts +53 -0
  466. package/dist/schema/code-generator.d.ts.map +1 -0
  467. package/dist/schema/ddl/base-strategy.d.ts +80 -0
  468. package/dist/schema/ddl/base-strategy.d.ts.map +1 -0
  469. package/dist/schema/ddl/base-strategy.js +240 -0
  470. package/dist/schema/ddl/base-strategy.js.map +1 -0
  471. package/dist/schema/ddl/duckdb-strategy.d.ts +33 -0
  472. package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -0
  473. package/dist/schema/ddl/duckdb-strategy.js +74 -0
  474. package/dist/schema/ddl/duckdb-strategy.js.map +1 -0
  475. package/dist/schema/ddl/index.d.ts +53 -0
  476. package/dist/schema/ddl/index.d.ts.map +1 -0
  477. package/dist/schema/ddl/index.js +80 -0
  478. package/dist/schema/ddl/index.js.map +1 -0
  479. package/dist/schema/ddl/json-duckdb-strategy.d.ts +8 -0
  480. package/dist/schema/ddl/json-duckdb-strategy.d.ts.map +1 -0
  481. package/dist/schema/ddl/json-duckdb-strategy.js +14 -0
  482. package/dist/schema/ddl/json-duckdb-strategy.js.map +1 -0
  483. package/dist/schema/ddl/postgres-strategy.d.ts +29 -0
  484. package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -0
  485. package/dist/schema/ddl/postgres-strategy.js +102 -0
  486. package/dist/schema/ddl/postgres-strategy.js.map +1 -0
  487. package/dist/schema/ddl/sqlite-strategy.d.ts +38 -0
  488. package/dist/schema/ddl/sqlite-strategy.d.ts.map +1 -0
  489. package/dist/schema/ddl/sqlite-strategy.js +74 -0
  490. package/dist/schema/ddl/sqlite-strategy.js.map +1 -0
  491. package/dist/schema/ddl/types.d.ts +114 -0
  492. package/dist/schema/ddl/types.d.ts.map +1 -0
  493. package/dist/schema/generator.d.ts +176 -0
  494. package/dist/schema/generator.d.ts.map +1 -0
  495. package/dist/schema/generator.js +1076 -0
  496. package/dist/schema/generator.js.map +1 -0
  497. package/dist/schema/index-utils.d.ts +19 -0
  498. package/dist/schema/index-utils.d.ts.map +1 -0
  499. package/dist/schema/index-utils.js +32 -0
  500. package/dist/schema/index-utils.js.map +1 -0
  501. package/dist/schema/index.d.ts +13 -0
  502. package/dist/schema/index.d.ts.map +1 -0
  503. package/dist/schema/override-system.d.ts +43 -0
  504. package/dist/schema/override-system.d.ts.map +1 -0
  505. package/dist/schema/schema-aggregator.d.ts +112 -0
  506. package/dist/schema/schema-aggregator.d.ts.map +1 -0
  507. package/dist/schema/schema-manager.d.ts +95 -0
  508. package/dist/schema/schema-manager.d.ts.map +1 -0
  509. package/dist/schema/schema-manager.js +283 -0
  510. package/dist/schema/schema-manager.js.map +1 -0
  511. package/dist/schema/sql-identifiers.d.ts +107 -0
  512. package/dist/schema/sql-identifiers.d.ts.map +1 -0
  513. package/dist/schema/sql-identifiers.js +190 -0
  514. package/dist/schema/sql-identifiers.js.map +1 -0
  515. package/dist/schema/table-verifier.d.ts +10 -0
  516. package/dist/schema/table-verifier.d.ts.map +1 -0
  517. package/dist/schema/table-verifier.js +37 -0
  518. package/dist/schema/table-verifier.js.map +1 -0
  519. package/dist/schema/types.d.ts +241 -0
  520. package/dist/schema/types.d.ts.map +1 -0
  521. package/dist/schema/utils.d.ts +32 -0
  522. package/dist/schema/utils.d.ts.map +1 -0
  523. package/dist/schema/utils.js +134 -0
  524. package/dist/schema/utils.js.map +1 -0
  525. package/dist/scripts/create-wrappers.js +89 -0
  526. package/dist/scripts/generate-manifest.js +155 -0
  527. package/dist/scripts/generate-test-manifest.js +77 -0
  528. package/dist/scripts/migrate-datetime-to-timestamp.ts +310 -0
  529. package/dist/scripts/prepack.js +49 -0
  530. package/dist/signals/bus.d.ts +64 -0
  531. package/dist/signals/bus.d.ts.map +1 -0
  532. package/dist/signals/bus.js +102 -0
  533. package/dist/signals/bus.js.map +1 -0
  534. package/dist/signals/index.d.ts +11 -0
  535. package/dist/signals/index.d.ts.map +1 -0
  536. package/dist/signals/sanitizer.d.ts +54 -0
  537. package/dist/signals/sanitizer.d.ts.map +1 -0
  538. package/dist/signals/sanitizer.js +111 -0
  539. package/dist/signals/sanitizer.js.map +1 -0
  540. package/dist/smrt-knowledge.json +335 -0
  541. package/dist/system/compatibility.d.ts +8 -0
  542. package/dist/system/compatibility.d.ts.map +1 -0
  543. package/dist/system/compatibility.js +409 -0
  544. package/dist/system/compatibility.js.map +1 -0
  545. package/dist/system/index.d.ts +9 -0
  546. package/dist/system/index.d.ts.map +1 -0
  547. package/dist/system/schema.d.ts +69 -0
  548. package/dist/system/schema.d.ts.map +1 -0
  549. package/dist/system/schema.js +271 -0
  550. package/dist/system/schema.js.map +1 -0
  551. package/dist/system/types.d.ts +135 -0
  552. package/dist/system/types.d.ts.map +1 -0
  553. package/dist/system-fields.d.ts +44 -0
  554. package/dist/system-fields.d.ts.map +1 -0
  555. package/dist/system-fields.js +55 -0
  556. package/dist/system-fields.js.map +1 -0
  557. package/dist/table-cache.d.ts +28 -0
  558. package/dist/table-cache.d.ts.map +1 -0
  559. package/dist/table-cache.js +21 -0
  560. package/dist/table-cache.js.map +1 -0
  561. package/dist/test-utils.d.ts +140 -0
  562. package/dist/test-utils.d.ts.map +1 -0
  563. package/dist/testing/database.d.ts +73 -0
  564. package/dist/testing/database.d.ts.map +1 -0
  565. package/dist/testing/database.js +204 -0
  566. package/dist/testing/database.js.map +1 -0
  567. package/dist/testing/index.d.ts +21 -0
  568. package/dist/testing/index.d.ts.map +1 -0
  569. package/dist/testing.d.ts +6 -0
  570. package/dist/testing.d.ts.map +1 -0
  571. package/dist/testing.js +5 -0
  572. package/dist/testing.js.map +1 -0
  573. package/dist/tools/index.d.ts +8 -0
  574. package/dist/tools/index.d.ts.map +1 -0
  575. package/dist/tools/tool-executor.d.ts +101 -0
  576. package/dist/tools/tool-executor.d.ts.map +1 -0
  577. package/dist/tools/tool-executor.js +142 -0
  578. package/dist/tools/tool-executor.js.map +1 -0
  579. package/dist/tools/tool-generator.d.ts +54 -0
  580. package/dist/tools/tool-generator.d.ts.map +1 -0
  581. package/dist/tools/tool-generator.js +121 -0
  582. package/dist/tools/tool-generator.js.map +1 -0
  583. package/dist/utils/chunk.d.ts +32 -0
  584. package/dist/utils/chunk.d.ts.map +1 -0
  585. package/dist/utils/chunk.js +14 -0
  586. package/dist/utils/chunk.js.map +1 -0
  587. package/dist/utils/import-workspace-module.d.ts +8 -0
  588. package/dist/utils/import-workspace-module.d.ts.map +1 -0
  589. package/dist/utils/import-workspace-module.js +81 -0
  590. package/dist/utils/import-workspace-module.js.map +1 -0
  591. package/dist/utils/json.d.ts +102 -0
  592. package/dist/utils/json.d.ts.map +1 -0
  593. package/dist/utils/json.js +43 -0
  594. package/dist/utils/json.js.map +1 -0
  595. package/dist/utils/lru-cache.d.ts +69 -0
  596. package/dist/utils/lru-cache.d.ts.map +1 -0
  597. package/dist/utils/lru-cache.js +100 -0
  598. package/dist/utils/lru-cache.js.map +1 -0
  599. package/dist/utils/naming.d.ts +16 -0
  600. package/dist/utils/naming.d.ts.map +1 -0
  601. package/dist/utils/naming.js +23 -0
  602. package/dist/utils/naming.js.map +1 -0
  603. package/dist/utils/qualified-names.d.ts +122 -0
  604. package/dist/utils/qualified-names.d.ts.map +1 -0
  605. package/dist/utils/qualified-names.js +82 -0
  606. package/dist/utils/qualified-names.js.map +1 -0
  607. package/dist/utils/scanner-module.d.ts +37 -0
  608. package/dist/utils/scanner-module.d.ts.map +1 -0
  609. package/dist/utils.d.ts +177 -0
  610. package/dist/utils.d.ts.map +1 -0
  611. package/dist/utils.js +185 -0
  612. package/dist/utils.js.map +1 -0
  613. package/dist/vite-plugin/import-build-aware.d.ts +68 -0
  614. package/dist/vite-plugin/import-build-aware.d.ts.map +1 -0
  615. package/dist/vite-plugin/import-build-aware.js +72 -0
  616. package/dist/vite-plugin/import-build-aware.js.map +1 -0
  617. package/dist/vite-plugin/index.d.ts +59 -0
  618. package/dist/vite-plugin/index.d.ts.map +1 -0
  619. package/dist/vite-plugin/index.js +1400 -0
  620. package/dist/vite-plugin/index.js.map +1 -0
  621. package/dist/vite-plugin/sveltekit-generator.d.ts +66 -0
  622. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -0
  623. package/dist/vite-plugin/sveltekit-generator.js +1375 -0
  624. package/dist/vite-plugin/sveltekit-generator.js.map +1 -0
  625. package/dist/vite-plugin/templates/default-ui.ts +432 -0
  626. package/dist/vite-plugin/templates/default.html +206 -0
  627. package/dist/vite-plugin.d.ts +8 -0
  628. package/dist/vite-plugin.d.ts.map +1 -0
  629. package/dist/vite-plugin.js +11 -0
  630. package/dist/vite-plugin.js.map +1 -0
  631. package/package.json +208 -0
@@ -0,0 +1,283 @@
1
+ import { createLogger } from "@happyvertical/logger";
2
+ import { detectEngine, getDDLStrategy } from "./ddl/index.js";
3
+ class SchemaManager {
4
+ db;
5
+ engine;
6
+ options;
7
+ // Per-instance logger: the `debug` option must actually emit debug traces, so
8
+ // the level follows it (a fixed level: 'info' would filter every debug call).
9
+ logger;
10
+ constructor(db, options = {}) {
11
+ this.db = db;
12
+ this.options = options;
13
+ this.logger = createLogger({ level: options.debug ? "debug" : "info" });
14
+ if (options.engine) {
15
+ this.engine = options.engine;
16
+ } else if (db.exportTable) {
17
+ this.engine = "json";
18
+ } else {
19
+ this.engine = detectEngine(db.url);
20
+ }
21
+ }
22
+ /**
23
+ * Ensure a table exists with the correct schema
24
+ *
25
+ * If table doesn't exist, creates it with DDL from the engine-specific strategy.
26
+ * If table exists but is missing columns, adds them via ALTER TABLE ADD COLUMN.
27
+ *
28
+ * @param schema - The schema definition
29
+ */
30
+ async ensureTable(schema) {
31
+ const { tableName } = schema;
32
+ const exists = await this.db.tableExists(tableName);
33
+ if (exists) {
34
+ await this.addMissingColumns(tableName, schema);
35
+ if (this.options.debug) {
36
+ this.logger.debug(
37
+ `[SchemaManager] Table "${tableName}" already exists, checked for missing columns`
38
+ );
39
+ }
40
+ return;
41
+ }
42
+ const strategy = getDDLStrategy(this.engine);
43
+ const ddl = {
44
+ createTable: strategy.generateCreateTable(schema),
45
+ indexes: strategy.generateIndexes(schema),
46
+ triggers: strategy.generateTriggers(schema)
47
+ };
48
+ if (this.db.syncSchema) {
49
+ const fullSchema = [
50
+ ddl.createTable,
51
+ ...ddl.indexes,
52
+ ...this.options.skipTriggers ? [] : ddl.triggers
53
+ ].join(";\n");
54
+ if (this.options.debug) {
55
+ this.logger.debug(
56
+ `[SchemaManager] Using adapter syncSchema for "${tableName}"`,
57
+ { fullSchema }
58
+ );
59
+ }
60
+ try {
61
+ await this.db.syncSchema(fullSchema);
62
+ const createdSuccessfully = await this.db.tableExists(tableName);
63
+ if (createdSuccessfully) {
64
+ if (this.options.debug) {
65
+ this.logger.debug(
66
+ `[SchemaManager] Table "${tableName}" created via adapter syncSchema`
67
+ );
68
+ }
69
+ return;
70
+ }
71
+ this.logger.warn(
72
+ `[SchemaManager] syncSchema returned but table "${tableName}" doesn't exist, falling back to direct DDL`
73
+ );
74
+ } catch (syncError) {
75
+ this.logger.warn(
76
+ `[SchemaManager] syncSchema failed for "${tableName}", falling back to direct DDL`,
77
+ { error: syncError }
78
+ );
79
+ }
80
+ }
81
+ if (this.options.debug) {
82
+ this.logger.debug(
83
+ `[SchemaManager] Creating table "${tableName}" for ${this.engine} (direct DDL)`,
84
+ { createTable: ddl.createTable }
85
+ );
86
+ }
87
+ await this.db.query(ddl.createTable);
88
+ for (const indexSQL of ddl.indexes) {
89
+ if (this.options.debug) {
90
+ this.logger.debug(`[SchemaManager] Creating index`, { indexSQL });
91
+ }
92
+ await this.db.query(indexSQL);
93
+ }
94
+ if (!this.options.skipTriggers) {
95
+ for (const triggerSQL of ddl.triggers) {
96
+ if (this.options.debug) {
97
+ this.logger.debug(`[SchemaManager] Creating trigger`, { triggerSQL });
98
+ }
99
+ await this.db.query(triggerSQL);
100
+ }
101
+ }
102
+ if (this.options.debug) {
103
+ this.logger.debug(
104
+ `[SchemaManager] Table "${tableName}" created successfully`
105
+ );
106
+ }
107
+ }
108
+ /**
109
+ * Get existing column names from a database table
110
+ *
111
+ * Uses engine-appropriate introspection:
112
+ * - SQLite/DuckDB: PRAGMA table_info
113
+ * - PostgreSQL: information_schema.columns
114
+ *
115
+ * @param tableName - Name of the table to inspect
116
+ * @returns Set of existing column names (lowercase)
117
+ */
118
+ async getExistingColumns(tableName) {
119
+ const columns = /* @__PURE__ */ new Set();
120
+ try {
121
+ if (this.engine === "postgres") {
122
+ const result = await this.db.query(
123
+ "SELECT column_name FROM information_schema.columns WHERE table_name = $1",
124
+ tableName
125
+ );
126
+ const rows = Array.isArray(result) ? result : result?.rows ?? [];
127
+ for (const row of rows) {
128
+ columns.add(row.column_name);
129
+ }
130
+ } else {
131
+ const result = await this.db.query(
132
+ `PRAGMA table_info(${this.quoteIdentifier(tableName)})`
133
+ );
134
+ const rows = Array.isArray(result) ? result : result?.rows ?? [];
135
+ for (const row of rows) {
136
+ columns.add(row.name);
137
+ }
138
+ }
139
+ } catch (error) {
140
+ if (this.options.debug) {
141
+ this.logger.warn(
142
+ `[SchemaManager] Failed to introspect columns for "${tableName}"`,
143
+ { error }
144
+ );
145
+ }
146
+ }
147
+ return columns;
148
+ }
149
+ /**
150
+ * Add missing columns to an existing table via ALTER TABLE ADD COLUMN
151
+ *
152
+ * Compares the schema's expected columns against the table's actual columns
153
+ * and adds any that are missing. This handles the race condition where a table
154
+ * is created with base fields by one code path, then a later code path needs
155
+ * additional columns from a more complete schema.
156
+ *
157
+ * SQLite limitation: ALTER TABLE ADD COLUMN cannot add NOT NULL columns
158
+ * without a DEFAULT value. We relax NOT NULL for such columns.
159
+ *
160
+ * @param tableName - Name of the existing table
161
+ * @param schema - The full schema definition with expected columns
162
+ */
163
+ async addMissingColumns(tableName, schema) {
164
+ if (!schema.columns || Object.keys(schema.columns).length === 0) {
165
+ return;
166
+ }
167
+ const existingColumns = await this.getExistingColumns(tableName);
168
+ if (existingColumns.size === 0) {
169
+ return;
170
+ }
171
+ const strategy = getDDLStrategy(this.engine);
172
+ let addedCount = 0;
173
+ for (const [colName, colDef] of Object.entries(schema.columns)) {
174
+ if (existingColumns.has(colName)) {
175
+ continue;
176
+ }
177
+ const safeDef = { ...colDef };
178
+ if (safeDef.notNull && safeDef.defaultValue === void 0 && !safeDef.primaryKey) {
179
+ safeDef.notNull = false;
180
+ }
181
+ if (safeDef.primaryKey) {
182
+ continue;
183
+ }
184
+ const colSQL = strategy.generateColumnDefinition(colName, safeDef);
185
+ const alterSQL = `ALTER TABLE ${this.quoteIdentifier(tableName)} ADD COLUMN ${colSQL}`;
186
+ try {
187
+ await this.db.query(alterSQL);
188
+ addedCount++;
189
+ if (this.options.debug) {
190
+ this.logger.debug(
191
+ `[SchemaManager] Added column "${colName}" to "${tableName}"`
192
+ );
193
+ }
194
+ } catch (error) {
195
+ if (this.options.debug) {
196
+ this.logger.warn(
197
+ `[SchemaManager] Failed to add column "${colName}" to "${tableName}"`,
198
+ { error }
199
+ );
200
+ }
201
+ }
202
+ }
203
+ if (addedCount > 0 && this.options.debug) {
204
+ this.logger.debug(
205
+ `[SchemaManager] Added ${addedCount} missing column(s) to "${tableName}"`
206
+ );
207
+ }
208
+ }
209
+ quoteIdentifier(name) {
210
+ return `"${name.replace(/"/g, '""')}"`;
211
+ }
212
+ /**
213
+ * Create multiple tables with dependency ordering
214
+ *
215
+ * @param schemas - Array of schema definitions
216
+ */
217
+ async ensureTables(schemas) {
218
+ const sorted = this.sortByDependencies(schemas);
219
+ for (const schema of sorted) {
220
+ await this.ensureTable(schema);
221
+ }
222
+ }
223
+ /**
224
+ * Generate DDL without executing (useful for debugging/preview)
225
+ *
226
+ * @param schema - The schema definition
227
+ * @returns Engine-specific DDL
228
+ */
229
+ generateDDL(schema) {
230
+ const strategy = getDDLStrategy(this.engine);
231
+ return {
232
+ createTable: strategy.generateCreateTable(schema),
233
+ indexes: strategy.generateIndexes(schema),
234
+ triggers: strategy.generateTriggers(schema)
235
+ };
236
+ }
237
+ /**
238
+ * Get the detected/configured database engine
239
+ */
240
+ getEngine() {
241
+ return this.engine;
242
+ }
243
+ /**
244
+ * Sort schemas by dependencies (topological sort)
245
+ *
246
+ * Ensures tables are created in the correct order based on foreign key dependencies.
247
+ */
248
+ sortByDependencies(schemas) {
249
+ const byName = /* @__PURE__ */ new Map();
250
+ const sorted = [];
251
+ const visited = /* @__PURE__ */ new Set();
252
+ const visiting = /* @__PURE__ */ new Set();
253
+ for (const schema of schemas) {
254
+ byName.set(schema.tableName, schema);
255
+ }
256
+ const visit = (tableName) => {
257
+ if (visited.has(tableName)) return;
258
+ if (visiting.has(tableName)) {
259
+ this.logger.warn(
260
+ `[SchemaManager] Circular dependency detected involving ${tableName}`
261
+ );
262
+ return;
263
+ }
264
+ const schema = byName.get(tableName);
265
+ if (!schema) return;
266
+ visiting.add(tableName);
267
+ for (const dep of schema.dependencies || []) {
268
+ visit(dep);
269
+ }
270
+ visiting.delete(tableName);
271
+ visited.add(tableName);
272
+ sorted.push(schema);
273
+ };
274
+ for (const schema of schemas) {
275
+ visit(schema.tableName);
276
+ }
277
+ return sorted;
278
+ }
279
+ }
280
+ export {
281
+ SchemaManager
282
+ };
283
+ //# sourceMappingURL=schema-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-manager.js","sources":["../../src/schema/schema-manager.ts"],"sourcesContent":["/**\n * Schema Manager - Direct DDL Execution for SMRT\n *\n * SMRT handles ALL database maintenance directly.\n * SDK SQL remains a pure query/CRUD layer.\n *\n * This module replaces the use of SDK's syncSchema() with direct DDL execution\n * using engine-specific DDL strategies.\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport type { DatabaseInterface } from '@happyvertical/sql';\nimport {\n type DatabaseEngine,\n detectEngine,\n type EngineSpecificDDL,\n getDDLStrategy,\n} from './ddl/index.js';\nimport type { SchemaDefinition } from './types.js';\n\n/**\n * Schema Manager Options\n */\nexport interface SchemaManagerOptions {\n /** Force engine type (overrides auto-detection) */\n engine?: DatabaseEngine;\n /** Whether to skip triggers (useful for DuckDB-backed JSON) */\n skipTriggers?: boolean;\n /** Log DDL statements before execution */\n debug?: boolean;\n}\n\n/**\n * Schema Manager\n *\n * Handles direct DDL execution for database schema management.\n * Replaces SDK's syncSchema() with SMRT-controlled schema creation.\n */\nexport class SchemaManager {\n private db: DatabaseInterface;\n private engine: DatabaseEngine;\n private options: SchemaManagerOptions;\n // Per-instance logger: the `debug` option must actually emit debug traces, so\n // the level follows it (a fixed level: 'info' would filter every debug call).\n private logger: ReturnType<typeof createLogger>;\n\n constructor(db: DatabaseInterface, options: SchemaManagerOptions = {}) {\n this.db = db;\n this.options = options;\n this.logger = createLogger({ level: options.debug ? 'debug' : 'info' });\n\n // Detect or use provided engine\n if (options.engine) {\n this.engine = options.engine;\n } else if ((db as any).exportTable) {\n // JSON adapter detected (has unique exportTable method)\n // JSON adapter uses DuckDB internally, but native UUID values do not\n // round-trip through the SDK row objects as canonical UUID strings.\n this.engine = 'json';\n } else {\n this.engine = detectEngine(db.url);\n }\n }\n\n /**\n * Ensure a table exists with the correct schema\n *\n * If table doesn't exist, creates it with DDL from the engine-specific strategy.\n * If table exists but is missing columns, adds them via ALTER TABLE ADD COLUMN.\n *\n * @param schema - The schema definition\n */\n async ensureTable(schema: SchemaDefinition): Promise<void> {\n const { tableName } = schema;\n\n // Check if table already exists\n const exists = await this.db.tableExists(tableName);\n if (exists) {\n // Table exists — check for missing columns and add them\n // This handles the case where a table was created with a partial schema\n // (e.g., base SmrtObject fields only) and needs additional columns\n await this.addMissingColumns(tableName, schema);\n\n if (this.options.debug) {\n this.logger.debug(\n `[SchemaManager] Table \"${tableName}\" already exists, checked for missing columns`,\n );\n }\n return;\n }\n\n // Generate DDL using engine-specific strategy\n const strategy = getDDLStrategy(this.engine);\n const ddl: EngineSpecificDDL = {\n createTable: strategy.generateCreateTable(schema),\n indexes: strategy.generateIndexes(schema),\n triggers: strategy.generateTriggers(schema),\n };\n\n // Prefer adapter's syncSchema if available (handles adapter-specific transformations)\n // This is critical for JSON adapter which needs to convert UNIQUE indexes to\n // inline constraints for DuckDB compatibility\n if ((this.db as any).syncSchema) {\n // Combine DDL into single schema string for adapter's syncSchema\n const fullSchema = [\n ddl.createTable,\n ...ddl.indexes,\n ...(this.options.skipTriggers ? [] : ddl.triggers),\n ].join(';\\n');\n\n if (this.options.debug) {\n this.logger.debug(\n `[SchemaManager] Using adapter syncSchema for \"${tableName}\"`,\n { fullSchema },\n );\n }\n\n try {\n await (this.db as any).syncSchema(fullSchema);\n\n // FIX #735: Verify table was actually created\n // Some adapters (notably PostgreSQL) may silently fail to create tables\n // when syncSchema returns without error but doesn't execute the DDL\n const createdSuccessfully = await this.db.tableExists(tableName);\n if (createdSuccessfully) {\n if (this.options.debug) {\n this.logger.debug(\n `[SchemaManager] Table \"${tableName}\" created via adapter syncSchema`,\n );\n }\n return;\n }\n\n // Table wasn't created - fall through to direct DDL execution\n this.logger.warn(\n `[SchemaManager] syncSchema returned but table \"${tableName}\" doesn't exist, falling back to direct DDL`,\n );\n } catch (syncError) {\n // syncSchema failed - fall through to direct DDL execution\n this.logger.warn(\n `[SchemaManager] syncSchema failed for \"${tableName}\", falling back to direct DDL`,\n { error: syncError },\n );\n }\n }\n\n // Fallback: Execute DDL directly (for adapters without syncSchema)\n if (this.options.debug) {\n this.logger.debug(\n `[SchemaManager] Creating table \"${tableName}\" for ${this.engine} (direct DDL)`,\n { createTable: ddl.createTable },\n );\n }\n\n await this.db.query(ddl.createTable);\n\n // Execute indexes\n for (const indexSQL of ddl.indexes) {\n if (this.options.debug) {\n this.logger.debug(`[SchemaManager] Creating index`, { indexSQL });\n }\n await this.db.query(indexSQL);\n }\n\n // Execute triggers (unless skipped)\n if (!this.options.skipTriggers) {\n for (const triggerSQL of ddl.triggers) {\n if (this.options.debug) {\n this.logger.debug(`[SchemaManager] Creating trigger`, { triggerSQL });\n }\n await this.db.query(triggerSQL);\n }\n }\n\n if (this.options.debug) {\n this.logger.debug(\n `[SchemaManager] Table \"${tableName}\" created successfully`,\n );\n }\n }\n\n /**\n * Get existing column names from a database table\n *\n * Uses engine-appropriate introspection:\n * - SQLite/DuckDB: PRAGMA table_info\n * - PostgreSQL: information_schema.columns\n *\n * @param tableName - Name of the table to inspect\n * @returns Set of existing column names (lowercase)\n */\n private async getExistingColumns(tableName: string): Promise<Set<string>> {\n const columns = new Set<string>();\n\n try {\n if (this.engine === 'postgres') {\n const result = await this.db.query(\n 'SELECT column_name FROM information_schema.columns WHERE table_name = $1',\n tableName,\n );\n const rows = Array.isArray(result)\n ? result\n : ((result as any)?.rows ?? []);\n for (const row of rows) {\n columns.add(row.column_name);\n }\n } else {\n // SQLite and DuckDB use PRAGMA\n const result = await this.db.query(\n `PRAGMA table_info(${this.quoteIdentifier(tableName)})`,\n );\n const rows = Array.isArray(result)\n ? result\n : ((result as any)?.rows ?? []);\n for (const row of rows) {\n columns.add(row.name);\n }\n }\n } catch (error) {\n // If introspection fails, return empty set (no columns will be added)\n if (this.options.debug) {\n this.logger.warn(\n `[SchemaManager] Failed to introspect columns for \"${tableName}\"`,\n { error },\n );\n }\n }\n\n return columns;\n }\n\n /**\n * Add missing columns to an existing table via ALTER TABLE ADD COLUMN\n *\n * Compares the schema's expected columns against the table's actual columns\n * and adds any that are missing. This handles the race condition where a table\n * is created with base fields by one code path, then a later code path needs\n * additional columns from a more complete schema.\n *\n * SQLite limitation: ALTER TABLE ADD COLUMN cannot add NOT NULL columns\n * without a DEFAULT value. We relax NOT NULL for such columns.\n *\n * @param tableName - Name of the existing table\n * @param schema - The full schema definition with expected columns\n */\n private async addMissingColumns(\n tableName: string,\n schema: SchemaDefinition,\n ): Promise<void> {\n if (!schema.columns || Object.keys(schema.columns).length === 0) {\n return;\n }\n\n const existingColumns = await this.getExistingColumns(tableName);\n if (existingColumns.size === 0) {\n // Introspection failed or returned no columns — skip to avoid errors\n return;\n }\n\n const strategy = getDDLStrategy(this.engine);\n let addedCount = 0;\n\n for (const [colName, colDef] of Object.entries(schema.columns)) {\n if (existingColumns.has(colName)) {\n continue; // Column already exists\n }\n\n // SQLite limitation: ALTER TABLE ADD COLUMN cannot have NOT NULL\n // without a DEFAULT value. Relax NOT NULL for such columns.\n const safeDef = { ...colDef };\n if (\n safeDef.notNull &&\n safeDef.defaultValue === undefined &&\n !safeDef.primaryKey\n ) {\n safeDef.notNull = false;\n }\n\n // Cannot add PRIMARY KEY columns via ALTER TABLE\n if (safeDef.primaryKey) {\n continue;\n }\n\n const colSQL = strategy.generateColumnDefinition(colName, safeDef);\n const alterSQL = `ALTER TABLE ${this.quoteIdentifier(tableName)} ADD COLUMN ${colSQL}`;\n\n try {\n await this.db.query(alterSQL);\n addedCount++;\n\n if (this.options.debug) {\n this.logger.debug(\n `[SchemaManager] Added column \"${colName}\" to \"${tableName}\"`,\n );\n }\n } catch (error) {\n // Column might already exist (race condition) or other issue\n // Log and continue — don't fail the entire operation\n if (this.options.debug) {\n this.logger.warn(\n `[SchemaManager] Failed to add column \"${colName}\" to \"${tableName}\"`,\n { error },\n );\n }\n }\n }\n\n if (addedCount > 0 && this.options.debug) {\n this.logger.debug(\n `[SchemaManager] Added ${addedCount} missing column(s) to \"${tableName}\"`,\n );\n }\n }\n\n private quoteIdentifier(name: string): string {\n return `\"${name.replace(/\"/g, '\"\"')}\"`;\n }\n\n /**\n * Create multiple tables with dependency ordering\n *\n * @param schemas - Array of schema definitions\n */\n async ensureTables(schemas: SchemaDefinition[]): Promise<void> {\n // Sort by dependencies (topological sort)\n const sorted = this.sortByDependencies(schemas);\n\n for (const schema of sorted) {\n await this.ensureTable(schema);\n }\n }\n\n /**\n * Generate DDL without executing (useful for debugging/preview)\n *\n * @param schema - The schema definition\n * @returns Engine-specific DDL\n */\n generateDDL(schema: SchemaDefinition): EngineSpecificDDL {\n const strategy = getDDLStrategy(this.engine);\n return {\n createTable: strategy.generateCreateTable(schema),\n indexes: strategy.generateIndexes(schema),\n triggers: strategy.generateTriggers(schema),\n };\n }\n\n /**\n * Get the detected/configured database engine\n */\n getEngine(): DatabaseEngine {\n return this.engine;\n }\n\n /**\n * Sort schemas by dependencies (topological sort)\n *\n * Ensures tables are created in the correct order based on foreign key dependencies.\n */\n private sortByDependencies(schemas: SchemaDefinition[]): SchemaDefinition[] {\n const byName = new Map<string, SchemaDefinition>();\n const sorted: SchemaDefinition[] = [];\n const visited = new Set<string>();\n const visiting = new Set<string>();\n\n // Index by table name\n for (const schema of schemas) {\n byName.set(schema.tableName, schema);\n }\n\n // DFS topological sort\n const visit = (tableName: string) => {\n if (visited.has(tableName)) return;\n if (visiting.has(tableName)) {\n // Circular dependency - continue anyway (FK might be nullable)\n this.logger.warn(\n `[SchemaManager] Circular dependency detected involving ${tableName}`,\n );\n return;\n }\n\n const schema = byName.get(tableName);\n if (!schema) return; // External dependency, skip\n\n visiting.add(tableName);\n\n // Visit dependencies first\n for (const dep of schema.dependencies || []) {\n visit(dep);\n }\n\n visiting.delete(tableName);\n visited.add(tableName);\n sorted.push(schema);\n };\n\n // Visit all schemas\n for (const schema of schemas) {\n visit(schema.tableName);\n }\n\n return sorted;\n }\n}\n\n/**\n * Create a SchemaManager instance\n *\n * @param db - Database interface\n * @param options - Optional configuration\n * @returns SchemaManager instance\n */\nexport function createSchemaManager(\n db: DatabaseInterface,\n options?: SchemaManagerOptions,\n): SchemaManager {\n return new SchemaManager(db, options);\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Shared SQL identifier / literal / default-value formatters.
3
+ *
4
+ * SMRT's schema + migration layers previously re-implemented identifier
5
+ * quoting and default-value formatting in several places, and a few of those
6
+ * copies interpolated values raw (`"${name}"`, `'${path}'`) or treated any
7
+ * string containing `(` as raw SQL. Those unsafe copies allowed a developer
8
+ * free-string (a `tableName`, a `@meta` field name used as a JSON path, a
9
+ * column default) that happened to contain a `"` or `'` to break out of the
10
+ * intended token and inject arbitrary DDL.
11
+ *
12
+ * This module is the single safe implementation that every DDL strategy,
13
+ * the schema generator/aggregator, the registry schema-builder, and the
14
+ * migration generator route through. It matches the escaping semantics that
15
+ * `migrations/differ.ts` and `schema/schema-manager.ts` already used
16
+ * correctly, so the codebase converges on one behaviour.
17
+ *
18
+ * IMPORTANT: keep this a pure, dependency-free utility (no runtime imports of
19
+ * registry/collection/object). It is imported by `schema/ddl/*`,
20
+ * `schema/index-utils.ts`, `schema/schema-aggregator.ts`,
21
+ * `schema/generator.ts`, `registry/schema-builder.ts`, and
22
+ * `migrations/generator.ts`.
23
+ */
24
+ /**
25
+ * Quote a SQL identifier (table/column/index/trigger name) by wrapping it in
26
+ * double quotes and escaping any embedded double quote as `""`.
27
+ *
28
+ * This is the ANSI / PostgreSQL / SQLite / DuckDB delimited-identifier rule.
29
+ * A name containing a `"` is rendered as a single valid token rather than
30
+ * closing the identifier early and leaking the remainder into the statement.
31
+ *
32
+ * @example
33
+ * quoteIdentifier('users') // => "users"
34
+ * quoteIdentifier('we"ird') // => "we""ird" (single valid token)
35
+ */
36
+ export declare function quoteIdentifier(name: string): string;
37
+ /**
38
+ * Quote a SQL string literal by wrapping it in single quotes and escaping any
39
+ * embedded single quote as `''`.
40
+ *
41
+ * @example
42
+ * quoteStringLiteral("O'Hara") // => 'O''Hara'
43
+ */
44
+ export declare function quoteStringLiteral(value: string): string;
45
+ /**
46
+ * Validate that a string is a well-formed, injection-safe SQL scalar
47
+ * function-call default, e.g. `datetime('now')` or `coalesce(0, 1)`.
48
+ *
49
+ * The old formatters treated ANY string containing `(` as raw SQL, so a
50
+ * "default" like `f(x') ; DROP TABLE t; --` was passed through untouched and
51
+ * injected. This validator instead requires the value to be *only* a function
52
+ * call whose arguments are themselves safe literals (numbers, properly-quoted
53
+ * string literals, the NULL keyword, or nested function calls). Anything that
54
+ * doesn't parse cleanly — stray quotes, statement terminators, trailing
55
+ * tokens — is rejected and the caller falls back to quoting it as data.
56
+ *
57
+ * @internal exported for testing
58
+ */
59
+ export declare function isSafeSqlFunctionDefault(value: string): boolean;
60
+ /**
61
+ * Format a column default value into a safe SQL fragment for a `DEFAULT`
62
+ * clause.
63
+ *
64
+ * Behaviour (converging the previously-divergent copies):
65
+ * - JS `null`/`undefined` => the SQL `NULL` keyword.
66
+ * - A string that exactly matches a known SQL keyword/niladic-function default
67
+ * (case-insensitive, e.g. `current_timestamp`, `uuid_generate_v4()`) =>
68
+ * passed through verbatim.
69
+ * - A string that is a well-formed, injection-safe SQL function call
70
+ * (e.g. `datetime('now')`) => passed through verbatim.
71
+ * - Everything else is formatted *by column type* and always quoted/escaped
72
+ * as data. A literal string `"null"`/`"NULL"` is therefore stored as the
73
+ * string for a TEXT column, NOT folded to SQL NULL.
74
+ *
75
+ * @param value - The raw default value (any JS value).
76
+ * @param type - The column's SQL type (case-insensitive). Controls literal
77
+ * formatting (TEXT/VARCHAR quote, INTEGER/REAL numeric, BOOLEAN, JSON, ...).
78
+ * @param options - Engine-specific overrides:
79
+ * - `booleanLiterals`: how to render boolean true/false (default `TRUE`/`FALSE`;
80
+ * SQLite passes `['1','0']`).
81
+ * - `nonStringTimestampDefault`: what a non-string TIMESTAMP default falls
82
+ * back to (default `CURRENT_TIMESTAMP`).
83
+ */
84
+ export declare function formatDefaultValue(value: unknown, type: string, options?: {
85
+ booleanLiterals?: [trueLiteral: string, falseLiteral: string];
86
+ nonStringTimestampDefault?: string;
87
+ }): string;
88
+ /**
89
+ * Conservative identifier allowlist for values that will be interpolated into
90
+ * SQL contexts where quoting is not sufficient — specifically the JSON-path
91
+ * segment of a JSON-path index expression, which is embedded as a *string
92
+ * literal* into a dialect function/operator (`json_extract("col", '$.path')`,
93
+ * `"col"->>'path'`).
94
+ *
95
+ * Allows a dotted path of simple identifier segments (e.g. `a`, `a.b.c`).
96
+ * A JSON path / column that doesn't match is rejected so it can be quoted as
97
+ * a plain literal instead of being interpolated raw.
98
+ */
99
+ export declare function isSafeIdentifierPath(value: string): boolean;
100
+ /**
101
+ * Allowlist for a single SQL identifier with no dots (e.g. a column name).
102
+ * Stricter than {@link isSafeIdentifierPath}: use this for the *column* of a
103
+ * JSON-path index (a real column name is always a simple identifier), and
104
+ * {@link isSafeIdentifierPath} for the dotted *path* segment.
105
+ */
106
+ export declare function isSafeIdentifier(value: string): boolean;
107
+ //# sourceMappingURL=sql-identifiers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-identifiers.d.ts","sourceRoot":"","sources":["../../src/schema/sql-identifiers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD;AA6BD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAiG/D;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,OAAO,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IACP,eAAe,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;IAC9D,yBAAyB,CAAC,EAAE,MAAM,CAAC;CAC/B,GACL,MAAM,CAkER;AAiCD;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEvD"}
@@ -0,0 +1,190 @@
1
+ function quoteIdentifier(name) {
2
+ return `"${String(name).replace(/"/g, '""')}"`;
3
+ }
4
+ function quoteStringLiteral(value) {
5
+ return `'${String(value).replace(/'/g, "''")}'`;
6
+ }
7
+ const SQL_DEFAULT_KEYWORDS = /* @__PURE__ */ new Set([
8
+ "current_timestamp",
9
+ "current_date",
10
+ "current_time",
11
+ "localtime",
12
+ "localtimestamp",
13
+ "now()",
14
+ "uuid_generate_v4()",
15
+ "gen_random_uuid()",
16
+ "datetime('now')",
17
+ "date('now')",
18
+ "time('now')"
19
+ ]);
20
+ function isSafeSqlFunctionDefault(value) {
21
+ const trimmed = value.trim();
22
+ if (!/^[A-Za-z_][A-Za-z0-9_.]*\s*\(.*\)$/s.test(trimmed)) {
23
+ return false;
24
+ }
25
+ let pos = 0;
26
+ const n = trimmed.length;
27
+ const skipWs = () => {
28
+ while (pos < n && /\s/.test(trimmed[pos])) pos++;
29
+ };
30
+ const parseArgument = () => {
31
+ skipWs();
32
+ if (pos >= n) return false;
33
+ const ch = trimmed[pos];
34
+ if (ch === "'") {
35
+ pos++;
36
+ while (pos < n) {
37
+ if (trimmed[pos] === "'") {
38
+ if (trimmed[pos + 1] === "'") {
39
+ pos += 2;
40
+ continue;
41
+ }
42
+ pos++;
43
+ return true;
44
+ }
45
+ pos++;
46
+ }
47
+ return false;
48
+ }
49
+ if (ch === "+" || ch === "-" || /[0-9]/.test(ch)) {
50
+ const numMatch = /^[+-]?(?:\d+\.?\d*|\.\d+)/.exec(trimmed.slice(pos));
51
+ if (!numMatch) return false;
52
+ pos += numMatch[0].length;
53
+ return true;
54
+ }
55
+ if (/[A-Za-z_]/.test(ch)) {
56
+ const identMatch = /^[A-Za-z_][A-Za-z0-9_.]*/.exec(trimmed.slice(pos));
57
+ if (!identMatch) return false;
58
+ pos += identMatch[0].length;
59
+ skipWs();
60
+ if (trimmed[pos] === "(") {
61
+ return parseCall();
62
+ }
63
+ const kw = identMatch[0].toLowerCase();
64
+ return kw === "null" || kw === "true" || kw === "false";
65
+ }
66
+ return false;
67
+ };
68
+ const parseCall = () => {
69
+ skipWs();
70
+ if (trimmed[pos] !== "(") return false;
71
+ pos++;
72
+ skipWs();
73
+ if (trimmed[pos] === ")") {
74
+ pos++;
75
+ return true;
76
+ }
77
+ while (true) {
78
+ if (!parseArgument()) return false;
79
+ skipWs();
80
+ if (trimmed[pos] === ",") {
81
+ pos++;
82
+ continue;
83
+ }
84
+ if (trimmed[pos] === ")") {
85
+ pos++;
86
+ return true;
87
+ }
88
+ return false;
89
+ }
90
+ };
91
+ const topIdent = /^[A-Za-z_][A-Za-z0-9_.]*/.exec(trimmed);
92
+ if (!topIdent) return false;
93
+ pos = topIdent[0].length;
94
+ skipWs();
95
+ if (!parseCall()) return false;
96
+ skipWs();
97
+ return pos === n;
98
+ }
99
+ function formatDefaultValue(value, type, options = {}) {
100
+ const [trueLiteral, falseLiteral] = options.booleanLiterals ?? [
101
+ "TRUE",
102
+ "FALSE"
103
+ ];
104
+ const nonStringTimestampDefault = options.nonStringTimestampDefault ?? "CURRENT_TIMESTAMP";
105
+ const upperTypeEarly = type.toUpperCase();
106
+ const isJsonType = upperTypeEarly === "JSON" || upperTypeEarly === "JSONB";
107
+ if ((value === null || value === void 0) && !isJsonType) {
108
+ return "NULL";
109
+ }
110
+ if (typeof value === "string") {
111
+ const candidate = value.trim();
112
+ if (SQL_DEFAULT_KEYWORDS.has(candidate.toLowerCase())) {
113
+ return value;
114
+ }
115
+ if (isSafeSqlFunctionDefault(value)) {
116
+ return value;
117
+ }
118
+ }
119
+ switch (upperTypeEarly) {
120
+ case "TEXT":
121
+ case "VARCHAR":
122
+ case "CHAR":
123
+ case "CLOB":
124
+ case "STRING":
125
+ return quoteStringLiteral(String(value));
126
+ case "INTEGER":
127
+ case "INT":
128
+ case "BIGINT":
129
+ case "SMALLINT":
130
+ return String(Math.floor(Number(value) || 0));
131
+ case "REAL":
132
+ case "FLOAT":
133
+ case "DOUBLE":
134
+ case "DOUBLE PRECISION":
135
+ case "DECIMAL":
136
+ case "NUMERIC":
137
+ return String(Number(value) || 0);
138
+ case "BOOLEAN":
139
+ case "BOOL":
140
+ return value ? trueLiteral : falseLiteral;
141
+ case "TIMESTAMP":
142
+ case "DATETIME":
143
+ case "DATE":
144
+ case "TIME":
145
+ if (typeof value === "string") {
146
+ return quoteStringLiteral(value);
147
+ }
148
+ return nonStringTimestampDefault;
149
+ case "JSON":
150
+ case "JSONB":
151
+ return formatJsonDefault(value);
152
+ default:
153
+ return quoteStringLiteral(String(value));
154
+ }
155
+ }
156
+ function formatJsonDefault(value) {
157
+ if (value === null || value === void 0) {
158
+ return "'null'";
159
+ }
160
+ if (typeof value === "string") {
161
+ if (value === "") {
162
+ return "'null'";
163
+ }
164
+ if (value === "[object Object]") {
165
+ return "'{}'";
166
+ }
167
+ try {
168
+ JSON.parse(value);
169
+ return quoteStringLiteral(value);
170
+ } catch {
171
+ return quoteStringLiteral(JSON.stringify(value));
172
+ }
173
+ }
174
+ return quoteStringLiteral(JSON.stringify(value));
175
+ }
176
+ function isSafeIdentifierPath(value) {
177
+ return /^[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(value);
178
+ }
179
+ function isSafeIdentifier(value) {
180
+ return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);
181
+ }
182
+ export {
183
+ formatDefaultValue,
184
+ isSafeIdentifier,
185
+ isSafeIdentifierPath,
186
+ isSafeSqlFunctionDefault,
187
+ quoteIdentifier,
188
+ quoteStringLiteral
189
+ };
190
+ //# sourceMappingURL=sql-identifiers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-identifiers.js","sources":["../../src/schema/sql-identifiers.ts"],"sourcesContent":["/**\n * Shared SQL identifier / literal / default-value formatters.\n *\n * SMRT's schema + migration layers previously re-implemented identifier\n * quoting and default-value formatting in several places, and a few of those\n * copies interpolated values raw (`\"${name}\"`, `'${path}'`) or treated any\n * string containing `(` as raw SQL. Those unsafe copies allowed a developer\n * free-string (a `tableName`, a `@meta` field name used as a JSON path, a\n * column default) that happened to contain a `\"` or `'` to break out of the\n * intended token and inject arbitrary DDL.\n *\n * This module is the single safe implementation that every DDL strategy,\n * the schema generator/aggregator, the registry schema-builder, and the\n * migration generator route through. It matches the escaping semantics that\n * `migrations/differ.ts` and `schema/schema-manager.ts` already used\n * correctly, so the codebase converges on one behaviour.\n *\n * IMPORTANT: keep this a pure, dependency-free utility (no runtime imports of\n * registry/collection/object). It is imported by `schema/ddl/*`,\n * `schema/index-utils.ts`, `schema/schema-aggregator.ts`,\n * `schema/generator.ts`, `registry/schema-builder.ts`, and\n * `migrations/generator.ts`.\n */\n\n/**\n * Quote a SQL identifier (table/column/index/trigger name) by wrapping it in\n * double quotes and escaping any embedded double quote as `\"\"`.\n *\n * This is the ANSI / PostgreSQL / SQLite / DuckDB delimited-identifier rule.\n * A name containing a `\"` is rendered as a single valid token rather than\n * closing the identifier early and leaking the remainder into the statement.\n *\n * @example\n * quoteIdentifier('users') // => \"users\"\n * quoteIdentifier('we\"ird') // => \"we\"\"ird\" (single valid token)\n */\nexport function quoteIdentifier(name: string): string {\n return `\"${String(name).replace(/\"/g, '\"\"')}\"`;\n}\n\n/**\n * Quote a SQL string literal by wrapping it in single quotes and escaping any\n * embedded single quote as `''`.\n *\n * @example\n * quoteStringLiteral(\"O'Hara\") // => 'O''Hara'\n */\nexport function quoteStringLiteral(value: string): string {\n return `'${String(value).replace(/'/g, \"''\")}'`;\n}\n\n/**\n * SQL keyword / niladic-function defaults that are passed through verbatim.\n *\n * Compared case-insensitively against the *entire* trimmed value. This is an\n * explicit allowlist — unlike the old \"value contains `(`\" heuristic, an\n * arbitrary string that merely contains a parenthesis is NOT treated as raw\n * SQL.\n *\n * Deliberately excludes `null`/`NULL`: a literal string `\"null\"` used as a\n * default for a TEXT column must be stored as the string, not folded into the\n * SQL NULL keyword. Real SQL NULL is produced from a JS `null`/`undefined`\n * value (see formatDefaultValue), not from the string `\"null\"`.\n */\nconst SQL_DEFAULT_KEYWORDS = new Set<string>([\n 'current_timestamp',\n 'current_date',\n 'current_time',\n 'localtime',\n 'localtimestamp',\n 'now()',\n 'uuid_generate_v4()',\n 'gen_random_uuid()',\n \"datetime('now')\",\n \"date('now')\",\n \"time('now')\",\n]);\n\n/**\n * Validate that a string is a well-formed, injection-safe SQL scalar\n * function-call default, e.g. `datetime('now')` or `coalesce(0, 1)`.\n *\n * The old formatters treated ANY string containing `(` as raw SQL, so a\n * \"default\" like `f(x') ; DROP TABLE t; --` was passed through untouched and\n * injected. This validator instead requires the value to be *only* a function\n * call whose arguments are themselves safe literals (numbers, properly-quoted\n * string literals, the NULL keyword, or nested function calls). Anything that\n * doesn't parse cleanly — stray quotes, statement terminators, trailing\n * tokens — is rejected and the caller falls back to quoting it as data.\n *\n * @internal exported for testing\n */\nexport function isSafeSqlFunctionDefault(value: string): boolean {\n const trimmed = value.trim();\n // Fast structural pre-check: identifier ... ( ... ) and nothing after.\n if (!/^[A-Za-z_][A-Za-z0-9_.]*\\s*\\(.*\\)$/s.test(trimmed)) {\n return false;\n }\n\n let pos = 0;\n const n = trimmed.length;\n\n const skipWs = () => {\n while (pos < n && /\\s/.test(trimmed[pos])) pos++;\n };\n\n // Parse a single argument: a number, a single-quoted string literal, the\n // NULL keyword, or a nested function call. Returns true on success and\n // advances `pos`.\n const parseArgument = (): boolean => {\n skipWs();\n if (pos >= n) return false;\n const ch = trimmed[pos];\n\n // Single-quoted string literal with doubled-quote escaping.\n if (ch === \"'\") {\n pos++; // opening quote\n while (pos < n) {\n if (trimmed[pos] === \"'\") {\n if (trimmed[pos + 1] === \"'\") {\n pos += 2; // escaped quote\n continue;\n }\n pos++; // closing quote\n return true;\n }\n pos++;\n }\n return false; // unterminated string\n }\n\n // Number (integer or decimal, optional sign).\n if (ch === '+' || ch === '-' || /[0-9]/.test(ch)) {\n const numMatch = /^[+-]?(?:\\d+\\.?\\d*|\\.\\d+)/.exec(trimmed.slice(pos));\n if (!numMatch) return false;\n pos += numMatch[0].length;\n return true;\n }\n\n // Identifier-led: either the NULL/TRUE/FALSE keyword or a nested call.\n if (/[A-Za-z_]/.test(ch)) {\n const identMatch = /^[A-Za-z_][A-Za-z0-9_.]*/.exec(trimmed.slice(pos));\n if (!identMatch) return false;\n pos += identMatch[0].length;\n skipWs();\n if (trimmed[pos] === '(') {\n return parseCall();\n }\n // Bare identifier is only allowed for a small set of literal keywords.\n const kw = identMatch[0].toLowerCase();\n return kw === 'null' || kw === 'true' || kw === 'false';\n }\n\n return false;\n };\n\n // Parse `( arg, arg, ... )` starting at the current `(`. Advances `pos`.\n const parseCall = (): boolean => {\n skipWs();\n if (trimmed[pos] !== '(') return false;\n pos++; // consume '('\n skipWs();\n if (trimmed[pos] === ')') {\n pos++;\n return true; // empty arg list, e.g. now()\n }\n while (true) {\n if (!parseArgument()) return false;\n skipWs();\n if (trimmed[pos] === ',') {\n pos++;\n continue;\n }\n if (trimmed[pos] === ')') {\n pos++;\n return true;\n }\n return false; // unexpected token between args\n }\n };\n\n // Top-level: identifier then a call, consuming the whole string.\n const topIdent = /^[A-Za-z_][A-Za-z0-9_.]*/.exec(trimmed);\n if (!topIdent) return false;\n pos = topIdent[0].length;\n skipWs();\n if (!parseCall()) return false;\n skipWs();\n return pos === n; // no trailing tokens\n}\n\n/**\n * Format a column default value into a safe SQL fragment for a `DEFAULT`\n * clause.\n *\n * Behaviour (converging the previously-divergent copies):\n * - JS `null`/`undefined` => the SQL `NULL` keyword.\n * - A string that exactly matches a known SQL keyword/niladic-function default\n * (case-insensitive, e.g. `current_timestamp`, `uuid_generate_v4()`) =>\n * passed through verbatim.\n * - A string that is a well-formed, injection-safe SQL function call\n * (e.g. `datetime('now')`) => passed through verbatim.\n * - Everything else is formatted *by column type* and always quoted/escaped\n * as data. A literal string `\"null\"`/`\"NULL\"` is therefore stored as the\n * string for a TEXT column, NOT folded to SQL NULL.\n *\n * @param value - The raw default value (any JS value).\n * @param type - The column's SQL type (case-insensitive). Controls literal\n * formatting (TEXT/VARCHAR quote, INTEGER/REAL numeric, BOOLEAN, JSON, ...).\n * @param options - Engine-specific overrides:\n * - `booleanLiterals`: how to render boolean true/false (default `TRUE`/`FALSE`;\n * SQLite passes `['1','0']`).\n * - `nonStringTimestampDefault`: what a non-string TIMESTAMP default falls\n * back to (default `CURRENT_TIMESTAMP`).\n */\nexport function formatDefaultValue(\n value: unknown,\n type: string,\n options: {\n booleanLiterals?: [trueLiteral: string, falseLiteral: string];\n nonStringTimestampDefault?: string;\n } = {},\n): string {\n const [trueLiteral, falseLiteral] = options.booleanLiterals ?? [\n 'TRUE',\n 'FALSE',\n ];\n const nonStringTimestampDefault =\n options.nonStringTimestampDefault ?? 'CURRENT_TIMESTAMP';\n\n const upperTypeEarly = type.toUpperCase();\n const isJsonType = upperTypeEarly === 'JSON' || upperTypeEarly === 'JSONB';\n\n // JS null/undefined => SQL NULL — EXCEPT for JSON columns, where a null\n // default means the JSON `null` literal (`'null'`), not the SQL NULL keyword.\n // (formatJsonDefault handles the null/undefined case.)\n if ((value === null || value === undefined) && !isJsonType) {\n return 'NULL';\n }\n\n // Allowlisted SQL keyword / niladic-function defaults, plus well-formed,\n // injection-safe function calls (e.g. datetime('now')) — passed through.\n if (typeof value === 'string') {\n const candidate = value.trim();\n if (SQL_DEFAULT_KEYWORDS.has(candidate.toLowerCase())) {\n return value;\n }\n if (isSafeSqlFunctionDefault(value)) {\n return value;\n }\n }\n\n switch (upperTypeEarly) {\n case 'TEXT':\n case 'VARCHAR':\n case 'CHAR':\n case 'CLOB':\n case 'STRING':\n return quoteStringLiteral(String(value));\n case 'INTEGER':\n case 'INT':\n case 'BIGINT':\n case 'SMALLINT':\n return String(Math.floor(Number(value) || 0));\n case 'REAL':\n case 'FLOAT':\n case 'DOUBLE':\n case 'DOUBLE PRECISION':\n case 'DECIMAL':\n case 'NUMERIC':\n return String(Number(value) || 0);\n case 'BOOLEAN':\n case 'BOOL':\n return value ? trueLiteral : falseLiteral;\n case 'TIMESTAMP':\n case 'DATETIME':\n case 'DATE':\n case 'TIME':\n if (typeof value === 'string') {\n return quoteStringLiteral(value);\n }\n return nonStringTimestampDefault;\n case 'JSON':\n case 'JSONB':\n return formatJsonDefault(value);\n default:\n return quoteStringLiteral(String(value));\n }\n}\n\n/**\n * Format a JSON column default, ensuring the emitted literal is valid JSON.\n *\n * Mirrors the long-standing behaviour from base-strategy/generator/\n * schema-builder (issue #735): empty string and `[object Object]` are\n * sanitised, valid JSON strings are used as-is, and anything else is\n * JSON.stringify'd. The result is always a single-quoted, escaped SQL literal.\n */\nfunction formatJsonDefault(value: unknown): string {\n if (value === null || value === undefined) {\n return \"'null'\";\n }\n\n if (typeof value === 'string') {\n if (value === '') {\n return \"'null'\";\n }\n if (value === '[object Object]') {\n return \"'{}'\";\n }\n try {\n JSON.parse(value);\n return quoteStringLiteral(value);\n } catch {\n return quoteStringLiteral(JSON.stringify(value));\n }\n }\n\n return quoteStringLiteral(JSON.stringify(value));\n}\n\n/**\n * Conservative identifier allowlist for values that will be interpolated into\n * SQL contexts where quoting is not sufficient — specifically the JSON-path\n * segment of a JSON-path index expression, which is embedded as a *string\n * literal* into a dialect function/operator (`json_extract(\"col\", '$.path')`,\n * `\"col\"->>'path'`).\n *\n * Allows a dotted path of simple identifier segments (e.g. `a`, `a.b.c`).\n * A JSON path / column that doesn't match is rejected so it can be quoted as\n * a plain literal instead of being interpolated raw.\n */\nexport function isSafeIdentifierPath(value: string): boolean {\n return /^[A-Za-z_][A-Za-z0-9_]*(?:\\.[A-Za-z_][A-Za-z0-9_]*)*$/.test(value);\n}\n\n/**\n * Allowlist for a single SQL identifier with no dots (e.g. a column name).\n * Stricter than {@link isSafeIdentifierPath}: use this for the *column* of a\n * JSON-path index (a real column name is always a simple identifier), and\n * {@link isSafeIdentifierPath} for the dotted *path* segment.\n */\nexport function isSafeIdentifier(value: string): boolean {\n return /^[A-Za-z_][A-Za-z0-9_]*$/.test(value);\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { DatabaseInterface } from '@happyvertical/sql';
2
+ /**
3
+ * Verify that a model table already exists.
4
+ *
5
+ * Runtime no longer creates application tables implicitly. This helper is the
6
+ * shared fail-fast path used by collections and objects before they touch app
7
+ * tables, while still caching successful checks to avoid repeated round-trips.
8
+ */
9
+ export declare function verifyPersistenceTable(db: DatabaseInterface, tableName: string, className: string): Promise<void>;
10
+ //# sourceMappingURL=table-verifier.d.ts.map