@happyvertical/smrt-core 0.40.69 → 0.41.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 (237) hide show
  1. package/AGENTS.md +29 -4
  2. package/README.md +20 -1
  3. package/agents/change-feed.md +1 -1
  4. package/agents/query-bounds.md +45 -0
  5. package/agents/schema-paths.md +786 -0
  6. package/dist/browser.d.ts +1 -0
  7. package/dist/browser.d.ts.map +1 -1
  8. package/dist/browser.js +5 -3
  9. package/dist/cascade.d.ts +120 -0
  10. package/dist/cascade.d.ts.map +1 -0
  11. package/dist/cascade.js +430 -0
  12. package/dist/cascade.js.map +1 -0
  13. package/dist/change-feed.d.ts +34 -2
  14. package/dist/change-feed.d.ts.map +1 -1
  15. package/dist/change-feed.js +52 -11
  16. package/dist/change-feed.js.map +1 -1
  17. package/dist/class.d.ts +36 -3
  18. package/dist/class.d.ts.map +1 -1
  19. package/dist/class.js +87 -9
  20. package/dist/class.js.map +1 -1
  21. package/dist/collection-cache.js +0 -0
  22. package/dist/collection-cache.js.map +1 -1
  23. package/dist/collection.d.ts +130 -2
  24. package/dist/collection.d.ts.map +1 -1
  25. package/dist/collection.js +290 -57
  26. package/dist/collection.js.map +1 -1
  27. package/dist/config.d.ts +10 -0
  28. package/dist/config.d.ts.map +1 -1
  29. package/dist/config.js.map +1 -1
  30. package/dist/database.d.ts +8 -0
  31. package/dist/database.d.ts.map +1 -1
  32. package/dist/database.js +16 -8
  33. package/dist/database.js.map +1 -1
  34. package/dist/db-errors.d.ts +105 -0
  35. package/dist/db-errors.d.ts.map +1 -0
  36. package/dist/db-errors.js +382 -0
  37. package/dist/db-errors.js.map +1 -0
  38. package/dist/decorators/index.d.ts +80 -6
  39. package/dist/decorators/index.d.ts.map +1 -1
  40. package/dist/decorators/index.js +102 -12
  41. package/dist/decorators/index.js.map +1 -1
  42. package/dist/dispatch/bus.d.ts.map +1 -1
  43. package/dist/dispatch/bus.js +4 -3
  44. package/dist/dispatch/bus.js.map +1 -1
  45. package/dist/dispatch/collections/Dispatches.d.ts.map +1 -1
  46. package/dist/dispatch/collections/Dispatches.js +19 -4
  47. package/dist/dispatch/collections/Dispatches.js.map +1 -1
  48. package/dist/dispatch/types.d.ts +5 -0
  49. package/dist/dispatch/types.d.ts.map +1 -1
  50. package/dist/embedded-write-queue.d.ts +46 -0
  51. package/dist/embedded-write-queue.d.ts.map +1 -0
  52. package/dist/embedded-write-queue.js +66 -0
  53. package/dist/embedded-write-queue.js.map +1 -0
  54. package/dist/embeddings/storage.d.ts +7 -0
  55. package/dist/embeddings/storage.d.ts.map +1 -1
  56. package/dist/embeddings/storage.js +29 -12
  57. package/dist/embeddings/storage.js.map +1 -1
  58. package/dist/errors.d.ts +31 -3
  59. package/dist/errors.d.ts.map +1 -1
  60. package/dist/errors.js +34 -2
  61. package/dist/errors.js.map +1 -1
  62. package/dist/generators/changes-route.d.ts.map +1 -1
  63. package/dist/generators/changes-route.js +6 -3
  64. package/dist/generators/changes-route.js.map +1 -1
  65. package/dist/generators/mcp-runtime-template.d.ts +8 -0
  66. package/dist/generators/mcp-runtime-template.d.ts.map +1 -1
  67. package/dist/generators/mcp-runtime-template.js +38 -4
  68. package/dist/generators/mcp-runtime-template.js.map +1 -1
  69. package/dist/generators/mcp.d.ts +16 -0
  70. package/dist/generators/mcp.d.ts.map +1 -1
  71. package/dist/generators/mcp.js +41 -3
  72. package/dist/generators/mcp.js.map +1 -1
  73. package/dist/generators/rest.d.ts +22 -0
  74. package/dist/generators/rest.d.ts.map +1 -1
  75. package/dist/generators/rest.js +34 -3
  76. package/dist/generators/rest.js.map +1 -1
  77. package/dist/hierarchical.js +1 -1
  78. package/dist/index.d.ts +7 -1
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +12 -5
  81. package/dist/interceptors.d.ts +21 -0
  82. package/dist/interceptors.d.ts.map +1 -1
  83. package/dist/interceptors.js +27 -1
  84. package/dist/interceptors.js.map +1 -1
  85. package/dist/manifest/generator.d.ts.map +1 -1
  86. package/dist/manifest/generator.js +4 -7
  87. package/dist/manifest/generator.js.map +1 -1
  88. package/dist/manifest/static-manifest.js +10 -10
  89. package/dist/manifest/static-manifest.js.map +1 -1
  90. package/dist/manifest/store.js +1 -1
  91. package/dist/manifest/store.js.map +1 -1
  92. package/dist/manifest.json +19 -19
  93. package/dist/migrations/differ.d.ts +211 -9
  94. package/dist/migrations/differ.d.ts.map +1 -1
  95. package/dist/migrations/differ.js +613 -50
  96. package/dist/migrations/differ.js.map +1 -1
  97. package/dist/migrations/generator.d.ts +31 -4
  98. package/dist/migrations/generator.d.ts.map +1 -1
  99. package/dist/migrations/generator.js +49 -5
  100. package/dist/migrations/generator.js.map +1 -1
  101. package/dist/migrations/index.d.ts +4 -2
  102. package/dist/migrations/index.d.ts.map +1 -1
  103. package/dist/migrations/index.js +6 -3
  104. package/dist/migrations/minor-units.d.ts +162 -0
  105. package/dist/migrations/minor-units.d.ts.map +1 -0
  106. package/dist/migrations/minor-units.js +381 -0
  107. package/dist/migrations/minor-units.js.map +1 -0
  108. package/dist/migrations/orchestrate.js +35 -6
  109. package/dist/migrations/orchestrate.js.map +1 -1
  110. package/dist/migrations/sqlite-rebuild.d.ts +142 -0
  111. package/dist/migrations/sqlite-rebuild.d.ts.map +1 -0
  112. package/dist/migrations/sqlite-rebuild.js +514 -0
  113. package/dist/migrations/sqlite-rebuild.js.map +1 -0
  114. package/dist/migrations/tracker.d.ts +114 -1
  115. package/dist/migrations/tracker.d.ts.map +1 -1
  116. package/dist/migrations/tracker.js +331 -16
  117. package/dist/migrations/tracker.js.map +1 -1
  118. package/dist/migrations/types.d.ts +19 -4
  119. package/dist/migrations/types.d.ts.map +1 -1
  120. package/dist/migrations.js +6 -3
  121. package/dist/object.d.ts +142 -10
  122. package/dist/object.d.ts.map +1 -1
  123. package/dist/object.js +196 -41
  124. package/dist/object.js.map +1 -1
  125. package/dist/postgres-timeouts.d.ts +240 -0
  126. package/dist/postgres-timeouts.d.ts.map +1 -0
  127. package/dist/postgres-timeouts.js +204 -0
  128. package/dist/postgres-timeouts.js.map +1 -0
  129. package/dist/query-bounds.d.ts +101 -0
  130. package/dist/query-bounds.d.ts.map +1 -0
  131. package/dist/query-bounds.js +177 -0
  132. package/dist/query-bounds.js.map +1 -0
  133. package/dist/registry/class-registration.d.ts.map +1 -1
  134. package/dist/registry/class-registration.js +3 -1
  135. package/dist/registry/class-registration.js.map +1 -1
  136. package/dist/registry/manifest-field-merge.d.ts +12 -0
  137. package/dist/registry/manifest-field-merge.d.ts.map +1 -1
  138. package/dist/registry/manifest-field-merge.js +14 -2
  139. package/dist/registry/manifest-field-merge.js.map +1 -1
  140. package/dist/registry/schema-builder.d.ts +22 -1
  141. package/dist/registry/schema-builder.d.ts.map +1 -1
  142. package/dist/registry/schema-builder.js +205 -165
  143. package/dist/registry/schema-builder.js.map +1 -1
  144. package/dist/registry/types.d.ts +35 -3
  145. package/dist/registry/types.d.ts.map +1 -1
  146. package/dist/registry.d.ts +41 -46
  147. package/dist/registry.d.ts.map +1 -1
  148. package/dist/registry.js +61 -83
  149. package/dist/registry.js.map +1 -1
  150. package/dist/scanner/manifest-generator.d.ts +45 -0
  151. package/dist/scanner/manifest-generator.d.ts.map +1 -1
  152. package/dist/scanner/manifest-generator.js +92 -28
  153. package/dist/scanner/manifest-generator.js.map +1 -1
  154. package/dist/scanner/types.d.ts +5 -0
  155. package/dist/scanner/types.d.ts.map +1 -1
  156. package/dist/scanner/types.js.map +1 -1
  157. package/dist/schema/conflict-target.d.ts +104 -0
  158. package/dist/schema/conflict-target.d.ts.map +1 -0
  159. package/dist/schema/conflict-target.js +129 -0
  160. package/dist/schema/conflict-target.js.map +1 -0
  161. package/dist/schema/ddl/base-strategy.d.ts.map +1 -1
  162. package/dist/schema/ddl/base-strategy.js +2 -2
  163. package/dist/schema/ddl/base-strategy.js.map +1 -1
  164. package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -1
  165. package/dist/schema/ddl/duckdb-strategy.js +2 -1
  166. package/dist/schema/ddl/duckdb-strategy.js.map +1 -1
  167. package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -1
  168. package/dist/schema/ddl/postgres-strategy.js +12 -1
  169. package/dist/schema/ddl/postgres-strategy.js.map +1 -1
  170. package/dist/schema/generator.d.ts +307 -6
  171. package/dist/schema/generator.d.ts.map +1 -1
  172. package/dist/schema/generator.js +510 -87
  173. package/dist/schema/generator.js.map +1 -1
  174. package/dist/schema/index-utils.d.ts +120 -0
  175. package/dist/schema/index-utils.d.ts.map +1 -1
  176. package/dist/schema/index-utils.js +242 -1
  177. package/dist/schema/index-utils.js.map +1 -1
  178. package/dist/schema/index.d.ts +3 -0
  179. package/dist/schema/index.d.ts.map +1 -1
  180. package/dist/schema/index.js +4 -1
  181. package/dist/schema/live-parity.d.ts +90 -0
  182. package/dist/schema/live-parity.d.ts.map +1 -0
  183. package/dist/schema/live-parity.js +602 -0
  184. package/dist/schema/live-parity.js.map +1 -0
  185. package/dist/schema/manifest-schema.d.ts +121 -0
  186. package/dist/schema/manifest-schema.d.ts.map +1 -0
  187. package/dist/schema/manifest-schema.js +267 -0
  188. package/dist/schema/manifest-schema.js.map +1 -0
  189. package/dist/schema/schema-aggregator.d.ts +24 -10
  190. package/dist/schema/schema-aggregator.d.ts.map +1 -1
  191. package/dist/schema/schema-aggregator.js +35 -90
  192. package/dist/schema/schema-aggregator.js.map +1 -1
  193. package/dist/schema/system-table-shapes.d.ts +65 -0
  194. package/dist/schema/system-table-shapes.d.ts.map +1 -0
  195. package/dist/schema/system-table-shapes.js +187 -0
  196. package/dist/schema/system-table-shapes.js.map +1 -0
  197. package/dist/schema/types.d.ts +103 -4
  198. package/dist/schema/types.d.ts.map +1 -1
  199. package/dist/schema/utils.d.ts +2 -1
  200. package/dist/schema/utils.d.ts.map +1 -1
  201. package/dist/schema/utils.js +5 -3
  202. package/dist/schema/utils.js.map +1 -1
  203. package/dist/schema.js +4 -1
  204. package/dist/smrt-knowledge.json +20 -8
  205. package/dist/sync/apply.d.ts.map +1 -1
  206. package/dist/sync/apply.js +9 -16
  207. package/dist/sync/apply.js.map +1 -1
  208. package/dist/system/compatibility.d.ts +42 -0
  209. package/dist/system/compatibility.d.ts.map +1 -1
  210. package/dist/system/compatibility.js +182 -9
  211. package/dist/system/compatibility.js.map +1 -1
  212. package/dist/system/index.d.ts +1 -0
  213. package/dist/system/index.d.ts.map +1 -1
  214. package/dist/system/index.js +3 -2
  215. package/dist/system/retention.d.ts +237 -0
  216. package/dist/system/retention.d.ts.map +1 -0
  217. package/dist/system/retention.js +497 -0
  218. package/dist/system/retention.js.map +1 -0
  219. package/dist/system/schema.d.ts +100 -15
  220. package/dist/system/schema.d.ts.map +1 -1
  221. package/dist/system/schema.js +81 -45
  222. package/dist/system/schema.js.map +1 -1
  223. package/dist/system/types.d.ts +0 -2
  224. package/dist/system/types.d.ts.map +1 -1
  225. package/dist/testing/database.d.ts.map +1 -1
  226. package/dist/testing/database.js +1 -0
  227. package/dist/testing/database.js.map +1 -1
  228. package/dist/vite-plugin/index.d.ts.map +1 -1
  229. package/dist/vite-plugin/index.js +4 -9
  230. package/dist/vite-plugin/index.js.map +1 -1
  231. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  232. package/dist/vite-plugin/sveltekit-generator.js +71 -5
  233. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  234. package/dist/vite-plugin/web-collections.d.ts.map +1 -1
  235. package/dist/vite-plugin/web-collections.js +6 -4
  236. package/dist/vite-plugin/web-collections.js.map +1 -1
  237. package/package.json +5 -5
package/dist/config.d.ts CHANGED
@@ -2,6 +2,7 @@ import { LoggerConfig } from '@happyvertical/logger';
2
2
  import { AiUsageHandler, SignalAdapter } from '@happyvertical/smrt-types';
3
3
  import { SignalBus } from './signals/bus.js';
4
4
  import { SanitizationConfig } from './signals/sanitizer.js';
5
+ import { RetentionPolicy } from './system/retention.js';
5
6
  /**
6
7
  * Metrics adapter configuration
7
8
  */
@@ -88,6 +89,15 @@ export interface GlobalSignalConfig {
88
89
  * Set to false to disable sanitization
89
90
  */
90
91
  sanitization?: SanitizationConfig | false;
92
+ /**
93
+ * System-table retention policy (#2375).
94
+ *
95
+ * Consumed by `smrt db:prune` and by the jobs `TaskRunner`'s periodic
96
+ * sweep. Defaults come from `DEFAULT_RETENTION_POLICY`; set
97
+ * `retention: { enabled: false }` to opt a deployment out entirely, or an
98
+ * individual table to `false` to keep only that one unbounded.
99
+ */
100
+ retention?: RetentionPolicy;
91
101
  /** Custom signal configuration */
92
102
  signals?: {
93
103
  /** Shared signal bus instance */
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,8BAA8B;IAC9B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,yDAAyD;IACzD,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE9D,gCAAgC;IAChC,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,YAAY,CAAC;IAEtB;;;OAGG;IACH,EAAE,CAAC,EAAE,QAAQ,CAAC;IAEd;;OAEG;IACH,KAAK,CAAC,EAAE,aAAa,CAAC;IAEtB;;;OAGG;IACH,YAAY,CAAC,EAAE,kBAAkB,GAAG,KAAK,CAAC;IAE1C,kCAAkC;IAClC,OAAO,CAAC,EAAE;QACR,iCAAiC;QACjC,GAAG,CAAC,EAAE,SAAS,CAAC;QAChB,iCAAiC;QACjC,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;KAC5B,CAAC;CACH;AAmDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,iBAAS,MAAM,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAEjD;kBAFQ,MAAM;qBAOI,IAAI;sBAUH,kBAAkB;wBAQhB,MAAM;;AAI5B,OAAO,EACL,KAAK,aAAa,EAClB,WAAW,EACX,aAAa,EACb,WAAW,EACX,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,8BAA8B;IAC9B,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,4EAA4E;IAC5E,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,yDAAyD;IACzD,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAE9D,gCAAgC;IAChC,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAC;CAC7B;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,OAAO,CAAC,EAAE,YAAY,CAAC;IAEvB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,YAAY,CAAC;IAEtB;;;OAGG;IACH,EAAE,CAAC,EAAE,QAAQ,CAAC;IAEd;;OAEG;IACH,KAAK,CAAC,EAAE,aAAa,CAAC;IAEtB;;;OAGG;IACH,YAAY,CAAC,EAAE,kBAAkB,GAAG,KAAK,CAAC;IAE1C;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAE5B,kCAAkC;IAClC,OAAO,CAAC,EAAE;QACR,iCAAiC;QACjC,GAAG,CAAC,EAAE,SAAS,CAAC;QAChB,iCAAiC;QACjC,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;KAC5B,CAAC;CACH;AAmDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,iBAAS,MAAM,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAEjD;kBAFQ,MAAM;qBAOI,IAAI;sBAUH,kBAAkB;wBAQhB,MAAM;;AAI5B,OAAO,EACL,KAAK,aAAa,EAClB,WAAW,EACX,aAAa,EACb,WAAW,EACX,oBAAoB,GACrB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,MAAM,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Global SMRT configuration system\n *\n * Provides application-level defaults for signal adapters.\n * Configuration follows a three-tier pattern:\n * 1. Global defaults (via smrt.configure())\n * 2. Per-instance overrides (via SmrtClassOptions)\n * 3. Runtime behavior (from merged config)\n */\n\nimport type { LoggerConfig } from '@happyvertical/logger';\nimport type { AiUsageHandler, SignalAdapter } from '@happyvertical/smrt-types';\nimport type { SignalBus } from './signals/bus.js';\nimport type { SanitizationConfig } from './signals/sanitizer.js';\n\n/**\n * Metrics adapter configuration\n */\nexport interface MetricsConfig {\n /** Enable metrics tracking */\n enabled: boolean;\n}\n\n/**\n * Pub/Sub adapter configuration\n */\nexport interface PubSubConfig {\n /** Enable pub/sub broadcasting */\n enabled: boolean;\n}\n\n/**\n * AI provider configuration\n *\n * Global defaults for AI client initialization.\n * Provides fallback values when AI options are not specified per-instance.\n */\nexport interface AIConfig {\n /**\n * Default AI provider to use\n * Examples: 'openai', 'anthropic', 'claude-cli', 'gemini', etc.\n */\n provider?: string;\n\n /**\n * Default model to use with the provider\n * Examples: 'gpt-4', 'claude-3-opus', 'sonnet', etc.\n */\n model?: string;\n\n /**\n * Default API key for the provider\n * Can be overridden by environment variables or instance options\n */\n apiKey?: string;\n\n /**\n * Additional provider-specific options\n */\n [key: string]: unknown;\n}\n\n/**\n * AI usage tracking configuration\n */\nexport interface AiUsageConfig {\n /** Enable normalized AI usage tracking (default: true) */\n enabled?: boolean;\n\n /** Enable persistence to _smrt_ai_usage when a DB exists (default: true) */\n persist?: boolean;\n\n /** Enable best-effort cost estimation (default: true) */\n estimateCosts?: boolean;\n\n /** Override USD-per-1K-token cost rates */\n costRates?: Record<string, { input: number; output: number }>;\n\n /** Additional usage handlers */\n handlers?: AiUsageHandler[];\n}\n\n/**\n * Global signal configuration\n *\n * Application-level defaults for signal adapters.\n * These can be overridden per-instance via SmrtClassOptions.\n */\nexport interface GlobalSignalConfig {\n /** Logging configuration (default: true with console, info level) */\n logging?: LoggerConfig;\n\n /** Metrics configuration (default: undefined/disabled) */\n metrics?: MetricsConfig;\n\n /** Pub/Sub configuration (default: undefined/disabled) */\n pubsub?: PubSubConfig;\n\n /**\n * AI provider configuration (default: undefined)\n * Provides global defaults for AI client initialization\n */\n ai?: AIConfig;\n\n /**\n * AI usage tracking configuration (default: enabled)\n */\n usage?: AiUsageConfig;\n\n /**\n * Signal sanitization configuration (default: enabled with standard redactions)\n * Set to false to disable sanitization\n */\n sanitization?: SanitizationConfig | false;\n\n /** Custom signal configuration */\n signals?: {\n /** Shared signal bus instance */\n bus?: SignalBus;\n /** Additional custom adapters */\n adapters?: SignalAdapter[];\n };\n}\n\n/**\n * Singleton configuration manager\n *\n * Manages global SMRT configuration with sensible defaults.\n */\nclass SmrtConfig {\n private static instance: SmrtConfig;\n private config: GlobalSignalConfig = {\n logging: true, // Default: console logging at info level\n };\n\n private constructor() {}\n\n /**\n * Get singleton instance\n */\n static getInstance(): SmrtConfig {\n if (!SmrtConfig.instance) {\n SmrtConfig.instance = new SmrtConfig();\n }\n return SmrtConfig.instance;\n }\n\n /**\n * Configure global defaults\n *\n * @param config - Configuration to apply\n */\n configure(config: GlobalSignalConfig): void {\n this.config = { ...this.config, ...config };\n }\n\n /**\n * Get current configuration\n *\n * @returns Current global configuration\n */\n getConfig(): GlobalSignalConfig {\n return { ...this.config };\n }\n\n /**\n * Reset to default configuration\n */\n reset(): void {\n this.config = { logging: true };\n }\n}\n\n/**\n * Global configuration API\n *\n * Callable function with attached methods for managing SMRT configuration.\n *\n * @example\n * ```typescript\n * import { config } from '@happyvertical/smrt-core';\n *\n * // Set application-level defaults\n * config({\n * logging: { level: 'debug' },\n * metrics: { enabled: true },\n * pubsub: { enabled: false },\n * ai: {\n * provider: 'claude-cli',\n * model: 'sonnet'\n * }\n * });\n *\n * // Reset to defaults\n * config.reset();\n *\n * // Get current configuration\n * const current = config.toJSON();\n *\n * // Auto-convert to string\n * console.log(`Config: ${config}`);\n *\n * // Auto-convert to JSON\n * JSON.stringify(config);\n *\n * // All SmrtClass instances now use these defaults\n * const product = new Product({ name: 'Widget' });\n * await product.initialize();\n * // product has logging at debug level, metrics enabled, and uses claude-cli by default\n * ```\n */\nfunction config(options: GlobalSignalConfig): void {\n SmrtConfig.getInstance().configure(options);\n}\n\n/**\n * Reset configuration to defaults\n */\nconfig.reset = (): void => {\n SmrtConfig.getInstance().reset();\n};\n\n/**\n * Get current configuration as object\n * Called automatically by JSON.stringify()\n *\n * @returns Current global configuration\n */\nconfig.toJSON = (): GlobalSignalConfig => SmrtConfig.getInstance().getConfig();\n\n/**\n * Convert configuration to string\n * Called automatically in string contexts\n *\n * @returns JSON string representation of configuration\n */\nconfig.toString = (): string =>\n JSON.stringify(SmrtConfig.getInstance().getConfig(), null, 2);\n\n// Re-export env-config utilities from @happyvertical/utils\nexport {\n type ConfigOptions,\n convertType,\n loadEnvConfig,\n toCamelCase,\n toScreamingSnakeCase,\n} from '@happyvertical/utils';\nexport { config };\n"],"mappings":";;;;;;;AAiIA,IAAM,aAAN,MAAM,WAAW;CACf,OAAe;CACf,SAAqC,EACnC,SAAS,KACX;CAEA,cAAsB,CAAC;;;;CAKvB,OAAO,cAA0B;EAC/B,IAAI,CAAC,WAAW,UACd,WAAW,WAAW,IAAI,WAAW;EAEvC,OAAO,WAAW;CACpB;;;;;;CAOA,UAAU,QAAkC;EAC1C,KAAK,SAAS;GAAE,GAAG,KAAK;GAAQ,GAAG;EAAO;CAC5C;;;;;;CAOA,YAAgC;EAC9B,OAAO,EAAE,GAAG,KAAK,OAAO;CAC1B;;;;CAKA,QAAc;EACZ,KAAK,SAAS,EAAE,SAAS,KAAK;CAChC;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAS,OAAO,SAAmC;CACjD,WAAW,YAAY,CAAC,CAAC,UAAU,OAAO;AAC5C;;;;AAKA,OAAO,cAAoB;CACzB,WAAW,YAAY,CAAC,CAAC,MAAM;AACjC;;;;;;;AAQA,OAAO,eAAmC,WAAW,YAAY,CAAC,CAAC,UAAU;;;;;;;AAQ7E,OAAO,iBACL,KAAK,UAAU,WAAW,YAAY,CAAC,CAAC,UAAU,GAAG,MAAM,CAAC"}
1
+ {"version":3,"file":"config.js","names":[],"sources":["../src/config.ts"],"sourcesContent":["/**\n * Global SMRT configuration system\n *\n * Provides application-level defaults for signal adapters.\n * Configuration follows a three-tier pattern:\n * 1. Global defaults (via smrt.configure())\n * 2. Per-instance overrides (via SmrtClassOptions)\n * 3. Runtime behavior (from merged config)\n */\n\nimport type { LoggerConfig } from '@happyvertical/logger';\nimport type { AiUsageHandler, SignalAdapter } from '@happyvertical/smrt-types';\nimport type { SignalBus } from './signals/bus.js';\nimport type { SanitizationConfig } from './signals/sanitizer.js';\nimport type { RetentionPolicy } from './system/retention.js';\n\n/**\n * Metrics adapter configuration\n */\nexport interface MetricsConfig {\n /** Enable metrics tracking */\n enabled: boolean;\n}\n\n/**\n * Pub/Sub adapter configuration\n */\nexport interface PubSubConfig {\n /** Enable pub/sub broadcasting */\n enabled: boolean;\n}\n\n/**\n * AI provider configuration\n *\n * Global defaults for AI client initialization.\n * Provides fallback values when AI options are not specified per-instance.\n */\nexport interface AIConfig {\n /**\n * Default AI provider to use\n * Examples: 'openai', 'anthropic', 'claude-cli', 'gemini', etc.\n */\n provider?: string;\n\n /**\n * Default model to use with the provider\n * Examples: 'gpt-4', 'claude-3-opus', 'sonnet', etc.\n */\n model?: string;\n\n /**\n * Default API key for the provider\n * Can be overridden by environment variables or instance options\n */\n apiKey?: string;\n\n /**\n * Additional provider-specific options\n */\n [key: string]: unknown;\n}\n\n/**\n * AI usage tracking configuration\n */\nexport interface AiUsageConfig {\n /** Enable normalized AI usage tracking (default: true) */\n enabled?: boolean;\n\n /** Enable persistence to _smrt_ai_usage when a DB exists (default: true) */\n persist?: boolean;\n\n /** Enable best-effort cost estimation (default: true) */\n estimateCosts?: boolean;\n\n /** Override USD-per-1K-token cost rates */\n costRates?: Record<string, { input: number; output: number }>;\n\n /** Additional usage handlers */\n handlers?: AiUsageHandler[];\n}\n\n/**\n * Global signal configuration\n *\n * Application-level defaults for signal adapters.\n * These can be overridden per-instance via SmrtClassOptions.\n */\nexport interface GlobalSignalConfig {\n /** Logging configuration (default: true with console, info level) */\n logging?: LoggerConfig;\n\n /** Metrics configuration (default: undefined/disabled) */\n metrics?: MetricsConfig;\n\n /** Pub/Sub configuration (default: undefined/disabled) */\n pubsub?: PubSubConfig;\n\n /**\n * AI provider configuration (default: undefined)\n * Provides global defaults for AI client initialization\n */\n ai?: AIConfig;\n\n /**\n * AI usage tracking configuration (default: enabled)\n */\n usage?: AiUsageConfig;\n\n /**\n * Signal sanitization configuration (default: enabled with standard redactions)\n * Set to false to disable sanitization\n */\n sanitization?: SanitizationConfig | false;\n\n /**\n * System-table retention policy (#2375).\n *\n * Consumed by `smrt db:prune` and by the jobs `TaskRunner`'s periodic\n * sweep. Defaults come from `DEFAULT_RETENTION_POLICY`; set\n * `retention: { enabled: false }` to opt a deployment out entirely, or an\n * individual table to `false` to keep only that one unbounded.\n */\n retention?: RetentionPolicy;\n\n /** Custom signal configuration */\n signals?: {\n /** Shared signal bus instance */\n bus?: SignalBus;\n /** Additional custom adapters */\n adapters?: SignalAdapter[];\n };\n}\n\n/**\n * Singleton configuration manager\n *\n * Manages global SMRT configuration with sensible defaults.\n */\nclass SmrtConfig {\n private static instance: SmrtConfig;\n private config: GlobalSignalConfig = {\n logging: true, // Default: console logging at info level\n };\n\n private constructor() {}\n\n /**\n * Get singleton instance\n */\n static getInstance(): SmrtConfig {\n if (!SmrtConfig.instance) {\n SmrtConfig.instance = new SmrtConfig();\n }\n return SmrtConfig.instance;\n }\n\n /**\n * Configure global defaults\n *\n * @param config - Configuration to apply\n */\n configure(config: GlobalSignalConfig): void {\n this.config = { ...this.config, ...config };\n }\n\n /**\n * Get current configuration\n *\n * @returns Current global configuration\n */\n getConfig(): GlobalSignalConfig {\n return { ...this.config };\n }\n\n /**\n * Reset to default configuration\n */\n reset(): void {\n this.config = { logging: true };\n }\n}\n\n/**\n * Global configuration API\n *\n * Callable function with attached methods for managing SMRT configuration.\n *\n * @example\n * ```typescript\n * import { config } from '@happyvertical/smrt-core';\n *\n * // Set application-level defaults\n * config({\n * logging: { level: 'debug' },\n * metrics: { enabled: true },\n * pubsub: { enabled: false },\n * ai: {\n * provider: 'claude-cli',\n * model: 'sonnet'\n * }\n * });\n *\n * // Reset to defaults\n * config.reset();\n *\n * // Get current configuration\n * const current = config.toJSON();\n *\n * // Auto-convert to string\n * console.log(`Config: ${config}`);\n *\n * // Auto-convert to JSON\n * JSON.stringify(config);\n *\n * // All SmrtClass instances now use these defaults\n * const product = new Product({ name: 'Widget' });\n * await product.initialize();\n * // product has logging at debug level, metrics enabled, and uses claude-cli by default\n * ```\n */\nfunction config(options: GlobalSignalConfig): void {\n SmrtConfig.getInstance().configure(options);\n}\n\n/**\n * Reset configuration to defaults\n */\nconfig.reset = (): void => {\n SmrtConfig.getInstance().reset();\n};\n\n/**\n * Get current configuration as object\n * Called automatically by JSON.stringify()\n *\n * @returns Current global configuration\n */\nconfig.toJSON = (): GlobalSignalConfig => SmrtConfig.getInstance().getConfig();\n\n/**\n * Convert configuration to string\n * Called automatically in string contexts\n *\n * @returns JSON string representation of configuration\n */\nconfig.toString = (): string =>\n JSON.stringify(SmrtConfig.getInstance().getConfig(), null, 2);\n\n// Re-export env-config utilities from @happyvertical/utils\nexport {\n type ConfigOptions,\n convertType,\n loadEnvConfig,\n toCamelCase,\n toScreamingSnakeCase,\n} from '@happyvertical/utils';\nexport { config };\n"],"mappings":";;;;;;;AA4IA,IAAM,aAAN,MAAM,WAAW;CACf,OAAe;CACf,SAAqC,EACnC,SAAS,KACX;CAEA,cAAsB,CAAC;;;;CAKvB,OAAO,cAA0B;EAC/B,IAAI,CAAC,WAAW,UACd,WAAW,WAAW,IAAI,WAAW;EAEvC,OAAO,WAAW;CACpB;;;;;;CAOA,UAAU,QAAkC;EAC1C,KAAK,SAAS;GAAE,GAAG,KAAK;GAAQ,GAAG;EAAO;CAC5C;;;;;;CAOA,YAAgC;EAC9B,OAAO,EAAE,GAAG,KAAK,OAAO;CAC1B;;;;CAKA,QAAc;EACZ,KAAK,SAAS,EAAE,SAAS,KAAK;CAChC;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCA,SAAS,OAAO,SAAmC;CACjD,WAAW,YAAY,CAAC,CAAC,UAAU,OAAO;AAC5C;;;;AAKA,OAAO,cAAoB;CACzB,WAAW,YAAY,CAAC,CAAC,MAAM;AACjC;;;;;;;AAQA,OAAO,eAAmC,WAAW,YAAY,CAAC,CAAC,UAAU;;;;;;;AAQ7E,OAAO,iBACL,KAAK,UAAU,WAAW,YAAY,CAAC,CAAC,UAAU,GAAG,MAAM,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { DatabaseInterface, SchemasOption } from '@happyvertical/sql';
2
+ import { PostgresTimeoutConfig } from './postgres-timeouts.js';
2
3
  /**
3
4
  * Unified type for all database configuration formats
4
5
  *
@@ -28,6 +29,13 @@ export type DatabaseConfig = string | {
28
29
  url?: string;
29
30
  type?: 'sqlite' | 'postgres' | 'duckdb' | 'json';
30
31
  authToken?: string;
32
+ /**
33
+ * Runtime PostgreSQL timeouts for this connection (#2377). Ignored by
34
+ * every other engine.
35
+ *
36
+ * @see {@link PostgresTimeoutConfig}
37
+ */
38
+ timeouts?: PostgresTimeoutConfig;
31
39
  [key: string]: unknown;
32
40
  } | DatabaseInterface;
33
41
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAG3E;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,cAAc,GACtB,MAAM,GACN;IACE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,GACD,iBAAiB,CAAC;AAEtB;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,8BAA8B,EAC9B,CAAC,MAAM,cAAc,GAAG,SAAS,CAAC,GAClC,SAAS,CAAC;CACf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,iBAAiB,CAO5B;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;;OAKG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,iBAAiB,CAAC,CAkC5B"}
1
+ {"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAE3E,OAAO,EAEL,KAAK,qBAAqB,EAC3B,MAAM,wBAAwB,CAAC;AAEhC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,MAAM,cAAc,GACtB,MAAM,GACN;IACE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,GACD,iBAAiB,CAAC;AAEtB;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,8BAA8B,EAC9B,CAAC,MAAM,cAAc,GAAG,SAAS,CAAC,GAClC,SAAS,CAAC;CACf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,iBAAiB,CAO5B;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd;;;;;OAKG;IACH,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,iBAAiB,CAAC,CAqC5B"}
package/dist/database.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { applyPostgresRuntimeTimeouts } from "./postgres-timeouts.js";
1
2
  import { getDatabase } from "@happyvertical/sql";
2
3
  //#region src/database.ts
3
4
  /**
@@ -49,19 +50,26 @@ function isDatabaseInterface(value) {
49
50
  async function resolveDatabase(config, options = {}) {
50
51
  const { dbid, schemas } = options;
51
52
  if (isDatabaseInterface(config)) return config;
52
- if (typeof config === "string") return getDatabase({
53
- url: config,
54
- schemas,
55
- ...config === ":memory:" ? {} : { dbid: dbid ?? `smrt:${config}` }
56
- });
53
+ if (typeof config === "string") {
54
+ const isMemoryDb = config === ":memory:";
55
+ const bounded = applyPostgresRuntimeTimeouts({ url: config });
56
+ return getDatabase({
57
+ ...bounded,
58
+ schemas,
59
+ ...isMemoryDb ? {} : { dbid: dbid ?? `smrt:${bounded.url}` }
60
+ });
61
+ }
57
62
  const canUseMemory = !config.type || config.type === "sqlite";
58
63
  const dbUrl = config.url || (canUseMemory ? ":memory:" : "");
59
64
  const isMemoryDb = dbUrl === ":memory:";
60
- return getDatabase({
65
+ const bounded = applyPostgresRuntimeTimeouts({
61
66
  ...config,
62
- url: dbUrl,
67
+ url: dbUrl
68
+ });
69
+ return getDatabase({
70
+ ...bounded,
63
71
  schemas,
64
- ...isMemoryDb ? {} : { dbid: dbid ?? `smrt:${dbUrl}` }
72
+ ...isMemoryDb ? {} : { dbid: dbid ?? `smrt:${bounded.url}` }
65
73
  });
66
74
  }
67
75
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"database.js","names":[],"sources":["../src/database.ts"],"sourcesContent":["/**\n * Database configuration and resolution utilities\n *\n * This module provides a unified type for database configuration options\n * and a utility function to resolve any config format to a DatabaseInterface.\n *\n * @module\n */\n\nimport type { DatabaseInterface, SchemasOption } from '@happyvertical/sql';\nimport { getDatabase } from '@happyvertical/sql';\n\n/**\n * Unified type for all database configuration formats\n *\n * Supports three formats:\n * - **String**: Connection URL (e.g., 'products.db', ':memory:', 'postgres://...')\n * - **Config object**: Full configuration with type, url, and options\n * - **DatabaseInterface**: Pre-initialized database instance\n *\n * @example\n * ```typescript\n * // String shortcut\n * const config: DatabaseConfig = 'products.db';\n *\n * // Config object\n * const config: DatabaseConfig = {\n * type: 'sqlite',\n * url: 'products.db',\n * authToken: 'token'\n * };\n *\n * // Pre-initialized instance\n * const db = await getDatabase({ type: 'sqlite', url: ':memory:' });\n * const config: DatabaseConfig = db;\n * ```\n */\nexport type DatabaseConfig =\n | string\n | {\n url?: string;\n type?: 'sqlite' | 'postgres' | 'duckdb' | 'json';\n authToken?: string;\n [key: string]: unknown;\n }\n | DatabaseInterface;\n\n/**\n * Canonical declaration of the request-scoped database global.\n *\n * The value is installed by whichever package owns request scoping (today\n * `@happyvertical/smrt-users`, via its session permission context) and is read\n * by the SvelteKit runtime config that `smrt-core`'s vite plugin generates into\n * consumer apps. Both sides used to `declare global` this name independently\n * with different types — `DatabaseConfig` here, a package-private\n * `QueryableDatabase` there — and TypeScript requires merged `var` declarations\n * to be *identical*, not merely compatible. Consumers whose program contained\n * both then failed to type-check (#2342).\n *\n * `smrt-core` owns the declaration because it is the base package every other\n * SMRT package and consumer already depends on. Writers should keep assigning a\n * live `DatabaseInterface`; it satisfies `DatabaseConfig` through that arm of\n * the union. Never redeclare this global elsewhere.\n */\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtGetRequestScopedDatabase:\n | (() => DatabaseConfig | undefined)\n | undefined;\n}\n\n/**\n * Type guard to check if a value is a DatabaseInterface instance\n *\n * @param value - Value to check\n * @returns True if value has query() and close() methods\n *\n * @example\n * ```typescript\n * if (isDatabaseInterface(config)) {\n * // config is DatabaseInterface\n * await config.query('SELECT 1');\n * }\n * ```\n */\nexport function isDatabaseInterface(\n value: unknown,\n): value is DatabaseInterface {\n return (\n value !== null &&\n typeof value === 'object' &&\n 'query' in value &&\n typeof (value as { query: unknown }).query === 'function'\n );\n}\n\n/**\n * Options for resolving a database configuration\n */\nexport interface ResolveDatabaseOptions {\n /**\n * Cache key for connection pooling.\n * If provided, the same dbid returns the same cached connection.\n */\n dbid?: string;\n\n /**\n * Optional pre-generated schemas to pass to the database adapter.\n *\n * Intended for explicit tooling and test utilities that bootstrap schema\n * ahead of runtime. Core runtime no longer passes these automatically.\n */\n schemas?: SchemasOption;\n}\n\n/**\n * Resolve any DatabaseConfig format to a DatabaseInterface instance\n *\n * This utility function normalizes the three config formats:\n * 1. **String URL**: Passed to getDatabase() with auto-detected type\n * 2. **Config object**: Passed to getDatabase() directly\n * 3. **DatabaseInterface**: Returned as-is\n *\n * @param config - Database configuration in any supported format\n * @param options - Resolution options (dbid for caching, optional explicit schemas)\n * @returns Promise resolving to a DatabaseInterface instance\n *\n * @example\n * ```typescript\n * // String URL\n * const db = await resolveDatabase('products.db');\n *\n * // Config object\n * const db = await resolveDatabase({\n * type: 'postgres',\n * url: 'postgres://localhost/mydb'\n * });\n *\n * // Pre-initialized instance (returned as-is)\n * const existingDb = await getDatabase({ url: ':memory:' });\n * const db = await resolveDatabase(existingDb);\n * console.log(db === existingDb); // true\n * ```\n */\nexport async function resolveDatabase(\n config: DatabaseConfig,\n options: ResolveDatabaseOptions = {},\n): Promise<DatabaseInterface> {\n const { dbid, schemas } = options;\n\n // Already a DatabaseInterface instance - return as-is\n if (isDatabaseInterface(config)) {\n return config;\n }\n\n // String URL shortcut\n if (typeof config === 'string') {\n const isMemoryDb = config === ':memory:';\n return getDatabase({\n url: config,\n schemas,\n ...(isMemoryDb ? {} : { dbid: dbid ?? `smrt:${config}` }),\n });\n }\n\n // Config object\n // Only default to :memory: for SQLite (or unspecified type which defaults to SQLite)\n // Other adapters (json, postgres, duckdb) require explicit URLs\n const canUseMemory = !config.type || config.type === 'sqlite';\n const dbUrl = config.url || (canUseMemory ? ':memory:' : '');\n const isMemoryDb = dbUrl === ':memory:';\n // `config` is the loosely-typed config-object variant of `DatabaseConfig`\n // (it carries an open `[key: string]: unknown` index for adapter-specific\n // options). Cast the merged options to `getDatabase`'s own parameter type at\n // this boundary; the adapter validates the concrete shape at runtime.\n return getDatabase({\n ...config,\n url: dbUrl,\n schemas,\n ...(isMemoryDb ? {} : { dbid: dbid ?? `smrt:${dbUrl}` }),\n } as Parameters<typeof getDatabase>[0]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAqFA,SAAgB,oBACd,OAC4B;CAC5B,OACE,UAAU,QACV,OAAO,UAAU,YACjB,WAAW,SACX,OAAQ,MAA6B,UAAU;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,eAAsB,gBACpB,QACA,UAAkC,CAAC,GACP;CAC5B,MAAM,EAAE,MAAM,YAAY;CAG1B,IAAI,oBAAoB,MAAM,GAC5B,OAAO;CAIT,IAAI,OAAO,WAAW,UAEpB,OAAO,YAAY;EACjB,KAAK;EACL;EACA,GAJiB,WAAW,aAIX,CAAC,IAAI,EAAE,MAAM,QAAQ,QAAQ,SAAS;CACzD,CAAC;CAMH,MAAM,eAAe,CAAC,OAAO,QAAQ,OAAO,SAAS;CACrD,MAAM,QAAQ,OAAO,QAAQ,eAAe,aAAa;CACzD,MAAM,aAAa,UAAU;CAK7B,OAAO,YAAY;EACjB,GAAG;EACH,KAAK;EACL;EACA,GAAI,aAAa,CAAC,IAAI,EAAE,MAAM,QAAQ,QAAQ,QAAQ;CACxD,CAAsC;AACxC"}
1
+ {"version":3,"file":"database.js","names":[],"sources":["../src/database.ts"],"sourcesContent":["/**\n * Database configuration and resolution utilities\n *\n * This module provides a unified type for database configuration options\n * and a utility function to resolve any config format to a DatabaseInterface.\n *\n * @module\n */\n\nimport type { DatabaseInterface, SchemasOption } from '@happyvertical/sql';\nimport { getDatabase } from '@happyvertical/sql';\nimport {\n applyPostgresRuntimeTimeouts,\n type PostgresTimeoutConfig,\n} from './postgres-timeouts.js';\n\n/**\n * Unified type for all database configuration formats\n *\n * Supports three formats:\n * - **String**: Connection URL (e.g., 'products.db', ':memory:', 'postgres://...')\n * - **Config object**: Full configuration with type, url, and options\n * - **DatabaseInterface**: Pre-initialized database instance\n *\n * @example\n * ```typescript\n * // String shortcut\n * const config: DatabaseConfig = 'products.db';\n *\n * // Config object\n * const config: DatabaseConfig = {\n * type: 'sqlite',\n * url: 'products.db',\n * authToken: 'token'\n * };\n *\n * // Pre-initialized instance\n * const db = await getDatabase({ type: 'sqlite', url: ':memory:' });\n * const config: DatabaseConfig = db;\n * ```\n */\nexport type DatabaseConfig =\n | string\n | {\n url?: string;\n type?: 'sqlite' | 'postgres' | 'duckdb' | 'json';\n authToken?: string;\n /**\n * Runtime PostgreSQL timeouts for this connection (#2377). Ignored by\n * every other engine.\n *\n * @see {@link PostgresTimeoutConfig}\n */\n timeouts?: PostgresTimeoutConfig;\n [key: string]: unknown;\n }\n | DatabaseInterface;\n\n/**\n * Canonical declaration of the request-scoped database global.\n *\n * The value is installed by whichever package owns request scoping (today\n * `@happyvertical/smrt-users`, via its session permission context) and is read\n * by the SvelteKit runtime config that `smrt-core`'s vite plugin generates into\n * consumer apps. Both sides used to `declare global` this name independently\n * with different types — `DatabaseConfig` here, a package-private\n * `QueryableDatabase` there — and TypeScript requires merged `var` declarations\n * to be *identical*, not merely compatible. Consumers whose program contained\n * both then failed to type-check (#2342).\n *\n * `smrt-core` owns the declaration because it is the base package every other\n * SMRT package and consumer already depends on. Writers should keep assigning a\n * live `DatabaseInterface`; it satisfies `DatabaseConfig` through that arm of\n * the union. Never redeclare this global elsewhere.\n */\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtGetRequestScopedDatabase:\n | (() => DatabaseConfig | undefined)\n | undefined;\n}\n\n/**\n * Type guard to check if a value is a DatabaseInterface instance\n *\n * @param value - Value to check\n * @returns True if value has query() and close() methods\n *\n * @example\n * ```typescript\n * if (isDatabaseInterface(config)) {\n * // config is DatabaseInterface\n * await config.query('SELECT 1');\n * }\n * ```\n */\nexport function isDatabaseInterface(\n value: unknown,\n): value is DatabaseInterface {\n return (\n value !== null &&\n typeof value === 'object' &&\n 'query' in value &&\n typeof (value as { query: unknown }).query === 'function'\n );\n}\n\n/**\n * Options for resolving a database configuration\n */\nexport interface ResolveDatabaseOptions {\n /**\n * Cache key for connection pooling.\n * If provided, the same dbid returns the same cached connection.\n */\n dbid?: string;\n\n /**\n * Optional pre-generated schemas to pass to the database adapter.\n *\n * Intended for explicit tooling and test utilities that bootstrap schema\n * ahead of runtime. Core runtime no longer passes these automatically.\n */\n schemas?: SchemasOption;\n}\n\n/**\n * Resolve any DatabaseConfig format to a DatabaseInterface instance\n *\n * This utility function normalizes the three config formats:\n * 1. **String URL**: Passed to getDatabase() with auto-detected type\n * 2. **Config object**: Passed to getDatabase() directly\n * 3. **DatabaseInterface**: Returned as-is\n *\n * @param config - Database configuration in any supported format\n * @param options - Resolution options (dbid for caching, optional explicit schemas)\n * @returns Promise resolving to a DatabaseInterface instance\n *\n * @example\n * ```typescript\n * // String URL\n * const db = await resolveDatabase('products.db');\n *\n * // Config object\n * const db = await resolveDatabase({\n * type: 'postgres',\n * url: 'postgres://localhost/mydb'\n * });\n *\n * // Pre-initialized instance (returned as-is)\n * const existingDb = await getDatabase({ url: ':memory:' });\n * const db = await resolveDatabase(existingDb);\n * console.log(db === existingDb); // true\n * ```\n */\nexport async function resolveDatabase(\n config: DatabaseConfig,\n options: ResolveDatabaseOptions = {},\n): Promise<DatabaseInterface> {\n const { dbid, schemas } = options;\n\n // Already a DatabaseInterface instance - return as-is\n if (isDatabaseInterface(config)) {\n return config;\n }\n\n // String URL shortcut\n if (typeof config === 'string') {\n const isMemoryDb = config === ':memory:';\n // The dbid is derived from the *bounded* URL so two configurations that\n // differ only in their PostgreSQL timeouts cannot share one pool (#2377).\n const bounded = applyPostgresRuntimeTimeouts({ url: config });\n return getDatabase({\n ...bounded,\n schemas,\n ...(isMemoryDb ? {} : { dbid: dbid ?? `smrt:${bounded.url}` }),\n } as Parameters<typeof getDatabase>[0]);\n }\n\n // Config object\n // Only default to :memory: for SQLite (or unspecified type which defaults to SQLite)\n // Other adapters (json, postgres, duckdb) require explicit URLs\n const canUseMemory = !config.type || config.type === 'sqlite';\n const dbUrl = config.url || (canUseMemory ? ':memory:' : '');\n const isMemoryDb = dbUrl === ':memory:';\n const bounded = applyPostgresRuntimeTimeouts({ ...config, url: dbUrl });\n // `config` is the loosely-typed config-object variant of `DatabaseConfig`\n // (it carries an open `[key: string]: unknown` index for adapter-specific\n // options). Cast the merged options to `getDatabase`'s own parameter type at\n // this boundary; the adapter validates the concrete shape at runtime.\n return getDatabase({\n ...bounded,\n schemas,\n ...(isMemoryDb ? {} : { dbid: dbid ?? `smrt:${bounded.url}` }),\n } as Parameters<typeof getDatabase>[0]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAgGA,SAAgB,oBACd,OAC4B;CAC5B,OACE,UAAU,QACV,OAAO,UAAU,YACjB,WAAW,SACX,OAAQ,MAA6B,UAAU;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,eAAsB,gBACpB,QACA,UAAkC,CAAC,GACP;CAC5B,MAAM,EAAE,MAAM,YAAY;CAG1B,IAAI,oBAAoB,MAAM,GAC5B,OAAO;CAIT,IAAI,OAAO,WAAW,UAAU;EAC9B,MAAM,aAAa,WAAW;EAG9B,MAAM,UAAU,6BAA6B,EAAE,KAAK,OAAO,CAAC;EAC5D,OAAO,YAAY;GACjB,GAAG;GACH;GACA,GAAI,aAAa,CAAC,IAAI,EAAE,MAAM,QAAQ,QAAQ,QAAQ,MAAM;EAC9D,CAAsC;CACxC;CAKA,MAAM,eAAe,CAAC,OAAO,QAAQ,OAAO,SAAS;CACrD,MAAM,QAAQ,OAAO,QAAQ,eAAe,aAAa;CACzD,MAAM,aAAa,UAAU;CAC7B,MAAM,UAAU,6BAA6B;EAAE,GAAG;EAAQ,KAAK;CAAM,CAAC;CAKtE,OAAO,YAAY;EACjB,GAAG;EACH;EACA,GAAI,aAAa,CAAC,IAAI,EAAE,MAAM,QAAQ,QAAQ,QAAQ,MAAM;CAC9D,CAAsC;AACxC"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Driver-error classification for the persistence path (#2366).
3
+ *
4
+ * `@happyvertical/sql` does not normalize driver errors: every adapter catches
5
+ * the driver failure and rethrows
6
+ * `DatabaseError('Failed to upsert record into table', { …, originalError })`,
7
+ * where `originalError` is the **string** produced by the SDK's `formatDbError`
8
+ * (`"<driver message>, code=<driver code>, detail=…, hint=…"`). The typed
9
+ * message therefore carries none of the constraint wording, which is why
10
+ * matching on `error.message` alone silently stopped recognizing constraint
11
+ * violations and let {@link ErrorUtils.withRetry} retry them.
12
+ *
13
+ * This module walks the whole causal chain — `cause`, `context.originalError`
14
+ * (Error *or* formatted string), `originalError`, `details.causeMessages`, and
15
+ * `AggregateError.errors` — collecting every driver code and message it finds,
16
+ * then classifies on the codes first (PostgreSQL SQLSTATE, SQLite result
17
+ * codes, Node network errno) and only falls back to dialect wording for DuckDB,
18
+ * which reports no machine-readable code.
19
+ *
20
+ * The classification separates two independent questions:
21
+ * - `deterministic` — a retry cannot change the outcome (constraint violation,
22
+ * bad input syntax, aborted transaction, missing table). Fail fast.
23
+ * - `retryable` — a retry plausibly can succeed (serialization failure,
24
+ * deadlock, lock timeout, connection loss, `SQLITE_BUSY`).
25
+ *
26
+ * They are not complements: `kind: 'unknown'` means "no opinion", leaving the
27
+ * caller's own default policy in charge.
28
+ *
29
+ * @module
30
+ */
31
+ /** Coarse classification of a database driver failure. */
32
+ export type DatabaseErrorKind = 'unique_violation' | 'not_null_violation' | 'foreign_key_violation' | 'check_violation' | 'exclusion_violation' | 'invalid_input' | 'aborted_transaction' | 'undefined_object' | 'insufficient_privilege' | 'syntax_error' | 'transient' | 'unknown';
33
+ /** Structured result of {@link classifyDatabaseError}. */
34
+ export interface DatabaseErrorClassification {
35
+ /** What kind of failure this is, or `'unknown'` when nothing matched. */
36
+ kind: DatabaseErrorKind;
37
+ /** True when retrying cannot change the outcome. */
38
+ deterministic: boolean;
39
+ /** True when retrying plausibly can succeed. */
40
+ retryable: boolean;
41
+ /** PostgreSQL SQLSTATE recovered from the chain, when present. */
42
+ sqlstate?: string;
43
+ /** Raw driver code that produced the classification, when present. */
44
+ driverCode?: string;
45
+ /** Every driver code found anywhere in the chain, in discovery order. */
46
+ driverCodes: readonly string[];
47
+ /**
48
+ * Every message found anywhere in the chain, in discovery order. Includes
49
+ * the SDK's formatted `originalError` strings and the driver's own
50
+ * `detail`/`hint` fields, which carry the PostgreSQL
51
+ * `Key (col)=(value) already exists.` line used for column recovery.
52
+ */
53
+ driverMessages: readonly string[];
54
+ /**
55
+ * Column named directly by the driver (`node-postgres` sets `error.column`
56
+ * on a `23502`), when one was reported. More reliable than parsing it back
57
+ * out of the message.
58
+ */
59
+ column?: string;
60
+ /** Constraint named directly by the driver (`error.constraint`). */
61
+ constraint?: string;
62
+ }
63
+ /**
64
+ * Classifies a raw dialect message. Exposed so
65
+ * `SmrtObject.classifyConstraintError()` and this module share one set of
66
+ * regexes rather than drifting apart.
67
+ */
68
+ export declare function classifyDialectMessage(message: string): DatabaseErrorKind;
69
+ /**
70
+ * Classifies a database failure by walking its causal chain.
71
+ *
72
+ * Codes win over wording: a PostgreSQL SQLSTATE or SQLite result code is
73
+ * authoritative, and dialect message matching only runs when no code in the
74
+ * chain was recognized (DuckDB, or an error already flattened to a string).
75
+ *
76
+ * Constraint violations are reported ahead of `aborted_transaction` so a chain
77
+ * carrying both surfaces the root cause rather than the downstream symptom;
78
+ * both are deterministic, so retry behavior is identical either way.
79
+ *
80
+ * @param error - Any thrown value. Non-database errors classify as `'unknown'`.
81
+ * @returns The structured classification; never throws.
82
+ */
83
+ export declare function classifyDatabaseError(error: unknown): DatabaseErrorClassification;
84
+ /**
85
+ * True when retrying the statement cannot change the outcome.
86
+ *
87
+ * Used by {@link ErrorUtils.withRetry} to fail fast instead of burning the
88
+ * full 500 + 1000 + 2000 ms backoff on a constraint violation.
89
+ */
90
+ export declare function isDeterministicDatabaseError(error: unknown): boolean;
91
+ /** True when the failure is contention or availability and may clear. */
92
+ export declare function isTransientDatabaseError(error: unknown): boolean;
93
+ /** True when a unique or primary-key constraint was violated. */
94
+ export declare function isUniqueViolationError(error: unknown): boolean;
95
+ /** True when a NOT NULL constraint was violated. */
96
+ export declare function isNotNullViolationError(error: unknown): boolean;
97
+ /**
98
+ * True when the failure came from running inside an already-aborted
99
+ * PostgreSQL transaction (SQLSTATE `25P02`).
100
+ *
101
+ * Detected independently of {@link DatabaseErrorKind} so a chain that also
102
+ * carries the root constraint code still answers `true` here.
103
+ */
104
+ export declare function isAbortedTransactionError(error: unknown): boolean;
105
+ //# sourceMappingURL=db-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db-errors.d.ts","sourceRoot":"","sources":["../src/db-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,0DAA0D;AAC1D,MAAM,MAAM,iBAAiB,GACzB,kBAAkB,GAClB,oBAAoB,GACpB,uBAAuB,GACvB,iBAAiB,GACjB,qBAAqB,GACrB,eAAe,GACf,qBAAqB,GACrB,kBAAkB,GAClB,wBAAwB,GACxB,cAAc,GACd,WAAW,GACX,SAAS,CAAC;AAEd,0DAA0D;AAC1D,MAAM,WAAW,2BAA2B;IAC1C,yEAAyE;IACzE,IAAI,EAAE,iBAAiB,CAAC;IACxB,oDAAoD;IACpD,aAAa,EAAE,OAAO,CAAC;IACvB,gDAAgD;IAChD,SAAS,EAAE,OAAO,CAAC;IACnB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B;;;;;OAKG;IACH,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAgWD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,iBAAiB,CAMzE;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,OAAO,GACb,2BAA2B,CA6G7B;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEpE;AAED,yEAAyE;AACzE,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAEhE;AAED,iEAAiE;AACjE,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE9D;AAED,oDAAoD;AACpD,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE/D;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAWjE"}