@happyvertical/smrt-core 0.40.70 → 0.42.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 (193) hide show
  1. package/AGENTS.md +3 -3
  2. package/agents/change-feed.md +1 -1
  3. package/agents/schema-paths.md +561 -32
  4. package/dist/browser.js +5 -4
  5. package/dist/cascade.d.ts +120 -0
  6. package/dist/cascade.d.ts.map +1 -0
  7. package/dist/cascade.js +430 -0
  8. package/dist/cascade.js.map +1 -0
  9. package/dist/change-feed.d.ts +34 -2
  10. package/dist/change-feed.d.ts.map +1 -1
  11. package/dist/change-feed.js +54 -13
  12. package/dist/change-feed.js.map +1 -1
  13. package/dist/change-signals.d.ts.map +1 -1
  14. package/dist/change-signals.js +8 -1
  15. package/dist/change-signals.js.map +1 -1
  16. package/dist/class.d.ts +36 -3
  17. package/dist/class.d.ts.map +1 -1
  18. package/dist/class.js +69 -14
  19. package/dist/class.js.map +1 -1
  20. package/dist/collection-cache.js +0 -0
  21. package/dist/collection-cache.js.map +1 -1
  22. package/dist/collection.d.ts +29 -0
  23. package/dist/collection.d.ts.map +1 -1
  24. package/dist/collection.js +68 -16
  25. package/dist/collection.js.map +1 -1
  26. package/dist/config.d.ts +10 -0
  27. package/dist/config.d.ts.map +1 -1
  28. package/dist/config.js.map +1 -1
  29. package/dist/decorators/index.d.ts +63 -5
  30. package/dist/decorators/index.d.ts.map +1 -1
  31. package/dist/decorators/index.js +37 -5
  32. package/dist/decorators/index.js.map +1 -1
  33. package/dist/dispatch/collections/Dispatches.d.ts.map +1 -1
  34. package/dist/dispatch/collections/Dispatches.js +21 -6
  35. package/dist/dispatch/collections/Dispatches.js.map +1 -1
  36. package/dist/dispatch/models/Dispatch.d.ts +1 -1
  37. package/dist/dispatch/models/Dispatch.d.ts.map +1 -1
  38. package/dist/dispatch/models/Dispatch.js +3 -2
  39. package/dist/dispatch/models/Dispatch.js.map +1 -1
  40. package/dist/dispatch/models/DispatchSubscription.d.ts +1 -1
  41. package/dist/dispatch/models/DispatchSubscription.d.ts.map +1 -1
  42. package/dist/dispatch/models/DispatchSubscription.js +2 -1
  43. package/dist/dispatch/models/DispatchSubscription.js.map +1 -1
  44. package/dist/dispatch/types.d.ts +5 -0
  45. package/dist/dispatch/types.d.ts.map +1 -1
  46. package/dist/embedded-write-queue.d.ts +46 -0
  47. package/dist/embedded-write-queue.d.ts.map +1 -0
  48. package/dist/embedded-write-queue.js +66 -0
  49. package/dist/embedded-write-queue.js.map +1 -0
  50. package/dist/embeddings/storage.d.ts +7 -0
  51. package/dist/embeddings/storage.d.ts.map +1 -1
  52. package/dist/embeddings/storage.js +31 -13
  53. package/dist/embeddings/storage.js.map +1 -1
  54. package/dist/errors.d.ts +1 -1
  55. package/dist/hierarchical.js +1 -1
  56. package/dist/index.d.ts +2 -1
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +8 -6
  59. package/dist/interceptors.d.ts +21 -0
  60. package/dist/interceptors.d.ts.map +1 -1
  61. package/dist/interceptors.js +27 -1
  62. package/dist/interceptors.js.map +1 -1
  63. package/dist/learning/memory.d.ts.map +1 -1
  64. package/dist/learning/memory.js +5 -4
  65. package/dist/learning/memory.js.map +1 -1
  66. package/dist/manifest/generator.d.ts.map +1 -1
  67. package/dist/manifest/generator.js +4 -7
  68. package/dist/manifest/generator.js.map +1 -1
  69. package/dist/manifest/static-manifest.js +10 -10
  70. package/dist/manifest/static-manifest.js.map +1 -1
  71. package/dist/manifest/store.js +1 -1
  72. package/dist/manifest/store.js.map +1 -1
  73. package/dist/manifest.json +19 -19
  74. package/dist/migrations/differ.d.ts +9 -1
  75. package/dist/migrations/differ.d.ts.map +1 -1
  76. package/dist/migrations/differ.js +12 -7
  77. package/dist/migrations/differ.js.map +1 -1
  78. package/dist/migrations/index.d.ts +2 -0
  79. package/dist/migrations/index.d.ts.map +1 -1
  80. package/dist/migrations/index.js +3 -1
  81. package/dist/migrations/integer-width.d.ts +99 -0
  82. package/dist/migrations/integer-width.d.ts.map +1 -0
  83. package/dist/migrations/integer-width.js +237 -0
  84. package/dist/migrations/integer-width.js.map +1 -0
  85. package/dist/migrations/minor-units.d.ts +162 -0
  86. package/dist/migrations/minor-units.d.ts.map +1 -0
  87. package/dist/migrations/minor-units.js +383 -0
  88. package/dist/migrations/minor-units.js.map +1 -0
  89. package/dist/migrations/tracker.d.ts.map +1 -1
  90. package/dist/migrations/tracker.js +28 -20
  91. package/dist/migrations/tracker.js.map +1 -1
  92. package/dist/migrations.js +3 -1
  93. package/dist/object.d.ts +98 -5
  94. package/dist/object.d.ts.map +1 -1
  95. package/dist/object.js +138 -30
  96. package/dist/object.js.map +1 -1
  97. package/dist/query-bounds.d.ts +5 -1
  98. package/dist/query-bounds.d.ts.map +1 -1
  99. package/dist/query-bounds.js +5 -1
  100. package/dist/query-bounds.js.map +1 -1
  101. package/dist/registry/class-registration.d.ts.map +1 -1
  102. package/dist/registry/class-registration.js +3 -1
  103. package/dist/registry/class-registration.js.map +1 -1
  104. package/dist/registry/manifest-field-merge.d.ts +12 -0
  105. package/dist/registry/manifest-field-merge.d.ts.map +1 -1
  106. package/dist/registry/manifest-field-merge.js +14 -2
  107. package/dist/registry/manifest-field-merge.js.map +1 -1
  108. package/dist/registry/schema-builder.d.ts +22 -1
  109. package/dist/registry/schema-builder.d.ts.map +1 -1
  110. package/dist/registry/schema-builder.js +205 -165
  111. package/dist/registry/schema-builder.js.map +1 -1
  112. package/dist/registry/types.d.ts +3 -2
  113. package/dist/registry/types.d.ts.map +1 -1
  114. package/dist/registry.d.ts +41 -46
  115. package/dist/registry.d.ts.map +1 -1
  116. package/dist/registry.js +61 -83
  117. package/dist/registry.js.map +1 -1
  118. package/dist/scanner/manifest-generator.d.ts +45 -0
  119. package/dist/scanner/manifest-generator.d.ts.map +1 -1
  120. package/dist/scanner/manifest-generator.js +92 -28
  121. package/dist/scanner/manifest-generator.js.map +1 -1
  122. package/dist/schema/conflict-target.d.ts +104 -0
  123. package/dist/schema/conflict-target.d.ts.map +1 -0
  124. package/dist/schema/conflict-target.js +129 -0
  125. package/dist/schema/conflict-target.js.map +1 -0
  126. package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -1
  127. package/dist/schema/ddl/duckdb-strategy.js +3 -1
  128. package/dist/schema/ddl/duckdb-strategy.js.map +1 -1
  129. package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -1
  130. package/dist/schema/ddl/postgres-strategy.js +14 -1
  131. package/dist/schema/ddl/postgres-strategy.js.map +1 -1
  132. package/dist/schema/generator.d.ts +245 -41
  133. package/dist/schema/generator.d.ts.map +1 -1
  134. package/dist/schema/generator.js +397 -241
  135. package/dist/schema/generator.js.map +1 -1
  136. package/dist/schema/index-utils.d.ts +120 -0
  137. package/dist/schema/index-utils.d.ts.map +1 -1
  138. package/dist/schema/index-utils.js +242 -1
  139. package/dist/schema/index-utils.js.map +1 -1
  140. package/dist/schema/index.d.ts +2 -3
  141. package/dist/schema/index.d.ts.map +1 -1
  142. package/dist/schema/index.js +2 -3
  143. package/dist/schema/live-parity.d.ts +3 -1
  144. package/dist/schema/live-parity.d.ts.map +1 -1
  145. package/dist/schema/live-parity.js +23 -2
  146. package/dist/schema/live-parity.js.map +1 -1
  147. package/dist/schema/system-table-shapes.d.ts +13 -0
  148. package/dist/schema/system-table-shapes.d.ts.map +1 -1
  149. package/dist/schema/system-table-shapes.js +14 -1
  150. package/dist/schema/system-table-shapes.js.map +1 -1
  151. package/dist/schema/types.d.ts +17 -10
  152. package/dist/schema/types.d.ts.map +1 -1
  153. package/dist/schema/utils.d.ts +1 -1
  154. package/dist/schema/utils.d.ts.map +1 -1
  155. package/dist/schema/utils.js +3 -3
  156. package/dist/schema/utils.js.map +1 -1
  157. package/dist/schema.js +2 -3
  158. package/dist/smrt-knowledge.json +9 -9
  159. package/dist/system/compatibility.d.ts +42 -0
  160. package/dist/system/compatibility.d.ts.map +1 -1
  161. package/dist/system/compatibility.js +182 -9
  162. package/dist/system/compatibility.js.map +1 -1
  163. package/dist/system/index.d.ts +1 -0
  164. package/dist/system/index.d.ts.map +1 -1
  165. package/dist/system/index.js +3 -2
  166. package/dist/system/retention.d.ts +237 -0
  167. package/dist/system/retention.d.ts.map +1 -0
  168. package/dist/system/retention.js +497 -0
  169. package/dist/system/retention.js.map +1 -0
  170. package/dist/system/schema.d.ts +102 -16
  171. package/dist/system/schema.d.ts.map +1 -1
  172. package/dist/system/schema.js +85 -47
  173. package/dist/system/schema.js.map +1 -1
  174. package/dist/system/types.d.ts +0 -2
  175. package/dist/system/types.d.ts.map +1 -1
  176. package/dist/testing/database.d.ts.map +1 -1
  177. package/dist/testing/database.js.map +1 -1
  178. package/dist/utils/safe-integer.d.ts +19 -0
  179. package/dist/utils/safe-integer.d.ts.map +1 -0
  180. package/dist/utils/safe-integer.js +31 -0
  181. package/dist/utils/safe-integer.js.map +1 -0
  182. package/dist/utils.d.ts +2 -1
  183. package/dist/utils.d.ts.map +1 -1
  184. package/dist/utils.js +6 -3
  185. package/dist/utils.js.map +1 -1
  186. package/dist/vite-plugin/index.d.ts.map +1 -1
  187. package/dist/vite-plugin/index.js +4 -62
  188. package/dist/vite-plugin/index.js.map +1 -1
  189. package/package.json +4 -4
  190. package/dist/schema/override-system.d.ts +0 -43
  191. package/dist/schema/override-system.d.ts.map +0 -1
  192. package/dist/schema/override-system.js +0 -205
  193. package/dist/schema/override-system.js.map +0 -1
@@ -0,0 +1,31 @@
1
+ //#region src/utils/safe-integer.ts
2
+ /**
3
+ * Convert a database integer to JavaScript's only safe integer representation.
4
+ *
5
+ * PostgreSQL surfaces `int8` as a string and DuckDB can surface it as a
6
+ * `bigint`; SQLite returns a number that may already be outside the safe
7
+ * range. SMRT's public model and generated API contracts use `number`, so
8
+ * preserving an out-of-range value would silently change it. Reject it at the
9
+ * read boundary instead.
10
+ */
11
+ function toSafeInteger(value, context = "integer") {
12
+ const parsed = Number(value);
13
+ if (!Number.isSafeInteger(parsed)) throw new RangeError(`${context} must be a JavaScript safe integer; received ${String(value)}`);
14
+ return parsed;
15
+ }
16
+ /**
17
+ * Convert an integer-backed database boolean to a JavaScript boolean.
18
+ *
19
+ * System tables use integer flags so PostgreSQL and DuckDB surface the same
20
+ * values as SQLite. Do not use JavaScript truthiness here: PostgreSQL returns
21
+ * BIGINT values as strings, and `Boolean('0')` is true.
22
+ */
23
+ function toSafeBooleanInteger(value, context = "boolean integer") {
24
+ if (value === 0 || value === "0" || value === 0n) return false;
25
+ if (value === 1 || value === "1" || value === 1n) return true;
26
+ throw new RangeError(`${context} must be an integer boolean (0 or 1); received ${String(value)}`);
27
+ }
28
+ //#endregion
29
+ export { toSafeBooleanInteger, toSafeInteger };
30
+
31
+ //# sourceMappingURL=safe-integer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-integer.js","names":[],"sources":["../../src/utils/safe-integer.ts"],"sourcesContent":["/**\n * Convert a database integer to JavaScript's only safe integer representation.\n *\n * PostgreSQL surfaces `int8` as a string and DuckDB can surface it as a\n * `bigint`; SQLite returns a number that may already be outside the safe\n * range. SMRT's public model and generated API contracts use `number`, so\n * preserving an out-of-range value would silently change it. Reject it at the\n * read boundary instead.\n */\nexport function toSafeInteger(value: unknown, context = 'integer'): number {\n const parsed = Number(value);\n if (!Number.isSafeInteger(parsed)) {\n throw new RangeError(\n `${context} must be a JavaScript safe integer; received ${String(value)}`,\n );\n }\n return parsed;\n}\n\n/**\n * Convert an integer-backed database boolean to a JavaScript boolean.\n *\n * System tables use integer flags so PostgreSQL and DuckDB surface the same\n * values as SQLite. Do not use JavaScript truthiness here: PostgreSQL returns\n * BIGINT values as strings, and `Boolean('0')` is true.\n */\nexport function toSafeBooleanInteger(\n value: unknown,\n context = 'boolean integer',\n): boolean {\n if (value === 0 || value === '0' || value === 0n) {\n return false;\n }\n if (value === 1 || value === '1' || value === 1n) {\n return true;\n }\n throw new RangeError(\n `${context} must be an integer boolean (0 or 1); received ${String(value)}`,\n );\n}\n"],"mappings":";;;;;;;;;;AASA,SAAgB,cAAc,OAAgB,UAAU,WAAmB;CACzE,MAAM,SAAS,OAAO,KAAK;CAC3B,IAAI,CAAC,OAAO,cAAc,MAAM,GAC9B,MAAM,IAAI,WACR,GAAG,QAAQ,+CAA+C,OAAO,KAAK,GACxE;CAEF,OAAO;AACT;;;;;;;;AASA,SAAgB,qBACd,OACA,UAAU,mBACD;CACT,IAAI,UAAU,KAAK,UAAU,OAAO,UAAU,IAC5C,OAAO;CAET,IAAI,UAAU,KAAK,UAAU,OAAO,UAAU,IAC5C,OAAO;CAET,MAAM,IAAI,WACR,GAAG,QAAQ,iDAAiD,OAAO,KAAK,GAC1E;AACF"}
package/dist/utils.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { SmrtObject } from './object';
2
2
  import { classnameToTablename, pluralize, toSnakeCase } from './utils/naming.js';
3
- export { classnameToTablename, pluralize, toSnakeCase };
3
+ import { toSafeBooleanInteger, toSafeInteger } from './utils/safe-integer.js';
4
+ export { classnameToTablename, pluralize, toSafeBooleanInteger, toSafeInteger, toSnakeCase, };
4
5
  /**
5
6
  * Converts a snake_case string to camelCase
6
7
  *
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAG3C,OAAO,EACL,oBAAoB,EACpB,SAAS,EACT,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAQ3B,OAAO,EAAE,oBAAoB,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC;AAExD;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,WAItC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,QAK/C;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,UAK/C;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,UAAU,EAC/C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,oCAmCjC;AAMD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAMpE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAa5E;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAEhC,SAAS,EAAE,QAAQ,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,UAAU,CAAC,UAiB7D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAC1B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,CAAC,CAuHxD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,CAE9D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,2BAc1D"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAG3C,OAAO,EACL,oBAAoB,EACpB,SAAS,EACT,WAAW,EACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAQ9E,OAAO,EACL,oBAAoB,EACpB,SAAS,EACT,oBAAoB,EACpB,aAAa,EACb,WAAW,GACZ,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,WAItC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,QAK/C;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,UAK/C;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,UAAU,EAC/C,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,oCAmCjC;AAMD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAMpE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,6BAA6B,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAa5E;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,kBAAkB,CAEhC,SAAS,EAAE,QAAQ,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,UAAU,CAAC,UAiB7D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAC1B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3D,IAAI,EAAE,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAAG,CAAC,CAqIxD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,CAE9D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,2BAc1D"}
package/dist/utils.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { classnameToTablename, pluralize, toSnakeCase } from "./utils/naming.js";
2
+ import { toSafeBooleanInteger, toSafeInteger } from "./utils/safe-integer.js";
2
3
  import { ObjectRegistry } from "./registry.js";
3
4
  import { createLogger } from "@happyvertical/logger";
4
5
  //#region src/utils.ts
@@ -247,14 +248,16 @@ function formatDataJs(data, fields) {
247
248
  normalizedData[outputKey] = value;
248
249
  }
249
250
  else if (fieldType === "integer") {
250
- const parsed = Number.parseInt(value, 10);
251
- normalizedData[outputKey] = Number.isNaN(parsed) ? value : parsed;
251
+ const parsed = Number(value);
252
+ normalizedData[outputKey] = Number.isFinite(parsed) && Number.isInteger(parsed) ? toSafeInteger(value, `Integer field ${outputKey}`) : value;
252
253
  } else if (fieldType === "real" || fieldType === "decimal") {
253
254
  const parsed = Number.parseFloat(value);
254
255
  normalizedData[outputKey] = Number.isNaN(parsed) ? value : parsed;
255
256
  } else normalizedData[outputKey] = value;
256
257
  else if (typeof value === "number") if (fieldType === "boolean") normalizedData[outputKey] = value === 1;
258
+ else if (fieldType === "integer" && Number.isInteger(value)) normalizedData[outputKey] = toSafeInteger(value, `Integer field ${outputKey}`);
257
259
  else normalizedData[outputKey] = value;
260
+ else if (typeof value === "bigint" && fieldType === "integer") normalizedData[outputKey] = toSafeInteger(value, `Integer field ${outputKey}`);
258
261
  else normalizedData[outputKey] = value;
259
262
  }
260
263
  if (process.env.DEBUG_STI) logger.debug("[formatDataJs] Output normalizedData", {
@@ -290,6 +293,6 @@ function formatDataSql(data) {
290
293
  return normalizedData;
291
294
  }
292
295
  //#endregion
293
- export { classnameToTablename, dateAsObject, dateAsString, fieldsFromClass, formatDataJs, formatDataSql, generateTableRenameMigrations, isDateField, isFieldInstance, keysToCamelCase, keysToSnakeCase, legacyClassnameToTablename, pluralize, tableNameFromClass, toCamelCase, toSnakeCase };
296
+ export { classnameToTablename, dateAsObject, dateAsString, fieldsFromClass, formatDataJs, formatDataSql, generateTableRenameMigrations, isDateField, isFieldInstance, keysToCamelCase, keysToSnakeCase, legacyClassnameToTablename, pluralize, tableNameFromClass, toCamelCase, toSafeBooleanInteger, toSafeInteger, toSnakeCase };
294
297
 
295
298
  //# sourceMappingURL=utils.js.map
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import { createLogger } from '@happyvertical/logger';\nimport type { SmrtObject } from './object';\nimport { ObjectRegistry } from './registry';\nimport type { SmrtObjectConstructor } from './registry/types';\nimport {\n classnameToTablename,\n pluralize,\n toSnakeCase,\n} from './utils/naming.js';\n\n// formatDataJs' debug traces are gated by DEBUG_STI, so the level must allow\n// debug when it's set (a fixed 'info' would filter them out and break the flag).\nconst logger = createLogger({\n level: process.env.DEBUG_STI ? 'debug' : 'info',\n});\n\nexport { classnameToTablename, pluralize, toSnakeCase };\n\n/**\n * Converts a snake_case string to camelCase\n *\n * @param str - String in snake_case format\n * @returns String in camelCase format\n * @example\n * ```typescript\n * toCamelCase('meetings_url'); // 'meetingsUrl'\n * toCamelCase('created_at'); // 'createdAt'\n * toCamelCase('id'); // 'id'\n * ```\n */\nexport function toCamelCase(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\n/**\n * Converts all keys in an object from camelCase to snake_case\n *\n * @param obj - Object with camelCase keys\n * @returns Object with snake_case keys\n */\nexport function keysToSnakeCase(\n obj: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[toSnakeCase(key)] = value;\n }\n return result;\n}\n\n/**\n * Converts all keys in an object from snake_case to camelCase\n *\n * @param obj - Object with snake_case keys\n * @returns Object with camelCase keys\n */\nexport function keysToCamelCase(\n obj: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[toCamelCase(key)] = value;\n }\n return result;\n}\n\n/**\n * Checks if a field name indicates a date field based on naming conventions\n *\n * Recognizes common date field patterns like '_at', '_date', and 'date'.\n * Used for automatic type inference during schema generation.\n *\n * @param key - Field name to check\n * @returns Boolean indicating if the field is likely a date field\n * @example\n * ```typescript\n * isDateField('created_at'); // true\n * isDateField('updated_date'); // true\n * isDateField('name'); // false\n * ```\n */\nexport function isDateField(key: string) {\n // Fallback pattern matching when schema is not available\n // Primary date detection should use schema field types\n return key.endsWith('_date') || key.endsWith('_at') || key === 'date';\n}\n\n/**\n * Converts a date string to a Date object\n *\n * @param date - Date as string or Date object\n * @returns Date object\n */\nexport function dateAsString(date: Date | string) {\n if (typeof date === 'string') {\n return new Date(date);\n }\n return date;\n}\n\n/**\n * Converts a Date object to an ISO string\n *\n * @param date - Date as Date object or string\n * @returns ISO date string or the original string\n */\nexport function dateAsObject(date: Date | string) {\n if (date instanceof Date) {\n return date.toISOString();\n }\n return date;\n}\n\n/**\n * Extracts field definitions from a class constructor\n *\n * Uses ObjectRegistry cached fields from AST manifest exclusively.\n * No runtime introspection fallback - classes must be decorated with @smrt()\n * for schema generation to work.\n *\n * @param ClassType - Class constructor to extract fields from\n * @param values - Optional values to set for the fields\n * @returns Object containing field definitions with names, types, and values\n * @throws {Error} If the class is not registered in ObjectRegistry\n * @example\n * ```typescript\n * @smrt()\n * class Product extends SmrtObject {\n * name: string = '';\n * price: number = 0.0;\n * }\n *\n * const fields = await fieldsFromClass(Product);\n * console.log(fields.name.type); // 'TEXT'\n * console.log(fields.price.type); // 'REAL'\n * ```\n */\nexport async function fieldsFromClass(\n ClassType: new (...args: never[]) => SmrtObject,\n values?: Record<string, unknown>,\n) {\n // `getClassByConstructor` expects the registry's `SmrtObjectConstructor`\n // (`new (...args: any[]) => SmrtObject`); the narrower `never[]` param type\n // is contravariantly assignable, so this widening cast is purely structural.\n const className =\n ObjectRegistry.getClassByConstructor(ClassType as SmrtObjectConstructor)\n ?.name || ClassType.name;\n // NEW: Use getAllFields() to include inherited fields from parent classes\n const cachedFields = await ObjectRegistry.getAllFields(className);\n\n // Phase 2: AST manifest only - no runtime introspection fallback\n if (cachedFields.size === 0) {\n // Return empty fields for unregistered classes (for backward compatibility)\n // generateSchema() will throw if it needs field definitions\n return {};\n }\n\n // Use cached field definitions from AST manifest\n const fields: Record<string, unknown> = {};\n\n // Add/override with fields from cached registry\n for (const [key, field] of cachedFields.entries()) {\n const meta = { ...(field._meta || {}) };\n delete meta.__smrtSystemField;\n\n fields[key] = {\n name: key,\n type: field.type || 'TEXT',\n _meta: meta,\n ...(values && key in values ? { value: values[key] } : {}),\n };\n }\n\n return fields;\n}\n\n// NOTE: generateSchema moved to schema/utils.ts\n// to prevent bundling Node.js-only code (SchemaGenerator with node:crypto) in browser builds.\n// Import from './schema/utils' in Node.js code that needs schema generation.\n\n/**\n * Returns the old (incorrect) pluralized table name for migration purposes.\n *\n * This function replicates the previous buggy behavior where 'y' → 'ies'\n * transformation was applied AFTER adding 's', resulting in incorrect\n * pluralization (e.g., 'currency' → 'currencys' instead of 'currencies').\n *\n * Use this to generate migration SQL that renames old tables to new names.\n *\n * @param className - Name of the class\n * @returns The incorrectly pluralized table name (old behavior)\n * @example\n * ```typescript\n * // Generate migration for a class\n * const oldName = legacyClassnameToTablename('Currency'); // 'currencys'\n * const newName = classnameToTablename('Currency'); // 'currencies'\n * console.log(`ALTER TABLE ${oldName} RENAME TO ${newName};`);\n * ```\n */\nexport function legacyClassnameToTablename(className: string): string {\n return className\n .replace(/([a-z])([A-Z])/g, '$1_$2')\n .toLowerCase()\n .replace(/([^s])$/, '$1s')\n .replace(/y$/, 'ies'); // This runs AFTER 's' is added, so it's a no-op for 'y' words\n}\n\n/**\n * Generates migration SQL for renaming tables from old to new naming convention.\n *\n * Returns an array of SQL statements to rename tables that were affected\n * by the pluralization bug (Issue #839).\n *\n * @param classNames - Array of class names to check for migration\n * @returns Array of SQL RENAME statements (empty if no changes needed)\n * @example\n * ```typescript\n * const migrations = generateTableRenameMigrations([\n * 'Currency',\n * 'JournalEntry',\n * 'Product' // Not affected\n * ]);\n * // Returns:\n * // [\n * // 'ALTER TABLE currencys RENAME TO currencies;',\n * // 'ALTER TABLE journal_entrys RENAME TO journal_entries;'\n * // ]\n * ```\n */\nexport function generateTableRenameMigrations(classNames: string[]): string[] {\n const migrations: string[] = [];\n\n for (const className of classNames) {\n const oldName = legacyClassnameToTablename(className);\n const newName = classnameToTablename(className);\n\n if (oldName !== newName) {\n migrations.push(`ALTER TABLE ${oldName} RENAME TO ${newName};`);\n }\n }\n\n return migrations;\n}\n\n/**\n * Generates a table name from a class constructor\n *\n * Checks for SMRT_TABLE_NAME static property first (set by @smrt() decorator),\n * which survives code minification. Falls back to deriving from ClassType.name\n * for backward compatibility.\n *\n * @param ClassType - Class constructor or function\n * @returns Pluralized snake_case table name\n * @example\n * ```typescript\n * // With @smrt() decorator (recommended)\n * @smrt()\n * class Product extends SmrtObject { }\n * tableNameFromClass(Product); // \"products\" (captured before minification)\n *\n * // Without decorator (fallback)\n * class Category extends SmrtObject { }\n * tableNameFromClass(Category); // \"categories\" (derived from runtime name)\n * ```\n */\nexport function tableNameFromClass(\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n ClassType: Function | (new (...args: never[]) => SmrtObject),\n) {\n // Check for SMRT_TABLE_NAME property set by @smrt() decorator (survives minification)\n if ('SMRT_TABLE_NAME' in ClassType) {\n // The `in` guard above proves the static decorator property exists; read it\n // through a typed shape rather than `any`.\n return (ClassType as { SMRT_TABLE_NAME: string }).SMRT_TABLE_NAME;\n }\n\n // Fallback: derive from class name (breaks with minification)\n const snakeCase = ClassType.name\n // Insert underscore between lower & upper case letters\n .replace(/([a-z])([A-Z])/g, '$1_$2')\n // Convert to lowercase\n .toLowerCase();\n\n return pluralize(snakeCase);\n}\n\n/**\n * Formats data for JavaScript by converting date strings to Date objects\n * and snake_case column names to camelCase properties\n *\n * Generic over the input row type `T` so callers preserve their (typically\n * loose) row typing across the hydration boundary — this matches the historic\n * behavior where a `Record<string, any>` / `any` row produced an equally loose\n * result. Keys are re-cased and values are type-converted at runtime; the\n * return is the same `Record` shape, so it is reasserted as `T` at this DB\n * boundary rather than widened to an unrelated type.\n *\n * @param data - Object with data to format (snake_case column names from DB)\n * @param fields - Optional field definitions to determine types (from fieldsFromClass)\n * @returns Object with properly typed values and camelCase property names for JavaScript\n */\nexport function formatDataJs<\n T extends Record<string, unknown> = Record<string, unknown>,\n>(data: T, fields?: Record<string, { type?: string }>): T {\n const normalizedData: Record<string, unknown> = {};\n\n if (process.env.DEBUG_STI) {\n logger.debug('[formatDataJs] Input data', {\n hasMetaData: !!data._meta_data,\n metaType: data._meta_type,\n keys: Object.keys(data),\n });\n }\n\n // STI: If _meta_data exists, merge it into a FRESH object first so meta\n // fields are available during formatting. This must never mutate the caller's\n // `data` argument — `formatDataJs` is a public export and external callers may\n // pass shared objects that would otherwise get silent field injection (#1378).\n let mergedData: Record<string, unknown> = data;\n if (data._meta_data) {\n // `_meta_data` is the STI meta column: a JSON string from the DB or an\n // already-parsed object. Either way it is an object of meta fields; type it\n // as such at this boundary so the merge/key-walk below stays sound.\n const metaData: Record<string, unknown> =\n typeof data._meta_data === 'string'\n ? JSON.parse(data._meta_data)\n : (data._meta_data as Record<string, unknown>);\n\n if (process.env.DEBUG_STI) {\n logger.debug('[formatDataJs] Merging _meta_data', {\n metaDataKeys: Object.keys(metaData),\n metaData,\n });\n }\n\n // Merge meta fields into a copy (will be formatted below). Spreading\n // `metaData` last preserves the original precedence of Object.assign.\n mergedData = { ...data, ...metaData };\n\n if (process.env.DEBUG_STI) {\n logger.debug('[formatDataJs] After merge, data keys', {\n keys: Object.keys(mergedData),\n });\n }\n }\n\n for (const [key, value] of Object.entries(mergedData)) {\n // Preserve keys with leading underscore (e.g., _meta_type, _meta_data)\n // These are special STI/framework fields that should not be camelCased\n const camelKey = key.startsWith('_') ? key : toCamelCase(key);\n\n // Determine the actual output key based on what's in fields\n // If the original key (snake_case) is in fields, use it; otherwise use camelCase\n // This supports both conventions (e.g., publish_date vs publishDate)\n let outputKey = camelKey;\n if (fields && key in fields && !(camelKey in fields)) {\n // Original key exists in fields but camelCase doesn't - use original (snake_case)\n outputKey = key;\n }\n\n // Get field type from fields, trying both key variants\n const fieldDef = fields?.[outputKey] ?? fields?.[camelKey] ?? fields?.[key];\n const fieldType = fieldDef?.type?.toLowerCase();\n\n if (value instanceof Date) {\n normalizedData[outputKey] = value;\n } else if (typeof value === 'string') {\n // Use field definitions if available, otherwise fall back to name patterns\n const isDate = fieldType === 'datetime' || isDateField(key);\n\n if (isDate) {\n const parsedDate = value.trim() ? new Date(value) : null;\n normalizedData[outputKey] =\n parsedDate && !Number.isNaN(parsedDate.getTime())\n ? parsedDate\n : value;\n } else if (fieldType === 'json') {\n // Parse JSON strings back to objects\n try {\n normalizedData[outputKey] = JSON.parse(value);\n } catch {\n // Keep as string if parsing fails\n normalizedData[outputKey] = value;\n }\n } else if (fieldType === 'integer') {\n // Convert string numbers to integers for INTEGER fields\n // SQLite may return \"2.0\" as a string in some cases\n const parsed = Number.parseInt(value, 10);\n normalizedData[outputKey] = Number.isNaN(parsed) ? value : parsed;\n } else if (fieldType === 'real' || fieldType === 'decimal') {\n // Convert string numbers to floats for REAL/DECIMAL fields\n const parsed = Number.parseFloat(value);\n normalizedData[outputKey] = Number.isNaN(parsed) ? value : parsed;\n } else {\n normalizedData[outputKey] = value;\n }\n } else if (typeof value === 'number') {\n if (fieldType === 'boolean') {\n // Convert SQLite integers (0/1) to booleans for boolean fields\n normalizedData[outputKey] = value === 1;\n } else {\n // Pass through numeric values as-is\n // Note: In JavaScript, 2.0 === 2 so no conversion needed\n // Non-integers in INTEGER fields (e.g., 2.9) are kept to surface data issues\n normalizedData[outputKey] = value;\n }\n } else {\n normalizedData[outputKey] = value;\n }\n }\n\n if (process.env.DEBUG_STI) {\n logger.debug('[formatDataJs] Output normalizedData', {\n keys: Object.keys(normalizedData),\n metaType: normalizedData._meta_type,\n });\n }\n\n // Reassert the re-cased/type-converted row as the caller's row type `T`.\n // The output is structurally a `Record<string, unknown>`; this boundary cast\n // preserves the historic loose-in/loose-out hydration contract without `any`.\n return normalizedData as T;\n}\n\n/**\n * Type guard to check if a value is a Field instance\n *\n * @deprecated Field helpers have been removed. This function always returns false.\n * @param value - Value to check\n * @returns Always false (Field class no longer exists)\n */\nexport function isFieldInstance(value: unknown): value is never {\n return false;\n}\n\n/**\n * Formats data for SQL by converting Date objects to ISO strings\n * and camelCase property names to snake_case column names\n *\n * @param data - Object with data to format (camelCase property names from JavaScript)\n * @returns Object with properly formatted values and snake_case column names for SQL\n */\nexport function formatDataSql(data: Record<string, unknown>) {\n const normalizedData: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n // Convert camelCase to snake_case for SQL\n const snakeKey = toSnakeCase(key);\n\n // Field helpers removed - no need to extract values (deprecated code removed)\n if (value instanceof Date) {\n normalizedData[snakeKey] = value.toISOString(); // Postgres accepts ISO format with timezone\n } else {\n normalizedData[snakeKey] = value;\n }\n }\n return normalizedData;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import { createLogger } from '@happyvertical/logger';\nimport type { SmrtObject } from './object';\nimport { ObjectRegistry } from './registry';\nimport type { SmrtObjectConstructor } from './registry/types';\nimport {\n classnameToTablename,\n pluralize,\n toSnakeCase,\n} from './utils/naming.js';\nimport { toSafeBooleanInteger, toSafeInteger } from './utils/safe-integer.js';\n\n// formatDataJs' debug traces are gated by DEBUG_STI, so the level must allow\n// debug when it's set (a fixed 'info' would filter them out and break the flag).\nconst logger = createLogger({\n level: process.env.DEBUG_STI ? 'debug' : 'info',\n});\n\nexport {\n classnameToTablename,\n pluralize,\n toSafeBooleanInteger,\n toSafeInteger,\n toSnakeCase,\n};\n\n/**\n * Converts a snake_case string to camelCase\n *\n * @param str - String in snake_case format\n * @returns String in camelCase format\n * @example\n * ```typescript\n * toCamelCase('meetings_url'); // 'meetingsUrl'\n * toCamelCase('created_at'); // 'createdAt'\n * toCamelCase('id'); // 'id'\n * ```\n */\nexport function toCamelCase(str: string): string {\n return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());\n}\n\n/**\n * Converts all keys in an object from camelCase to snake_case\n *\n * @param obj - Object with camelCase keys\n * @returns Object with snake_case keys\n */\nexport function keysToSnakeCase(\n obj: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[toSnakeCase(key)] = value;\n }\n return result;\n}\n\n/**\n * Converts all keys in an object from snake_case to camelCase\n *\n * @param obj - Object with snake_case keys\n * @returns Object with camelCase keys\n */\nexport function keysToCamelCase(\n obj: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n result[toCamelCase(key)] = value;\n }\n return result;\n}\n\n/**\n * Checks if a field name indicates a date field based on naming conventions\n *\n * Recognizes common date field patterns like '_at', '_date', and 'date'.\n * Used for automatic type inference during schema generation.\n *\n * @param key - Field name to check\n * @returns Boolean indicating if the field is likely a date field\n * @example\n * ```typescript\n * isDateField('created_at'); // true\n * isDateField('updated_date'); // true\n * isDateField('name'); // false\n * ```\n */\nexport function isDateField(key: string) {\n // Fallback pattern matching when schema is not available\n // Primary date detection should use schema field types\n return key.endsWith('_date') || key.endsWith('_at') || key === 'date';\n}\n\n/**\n * Converts a date string to a Date object\n *\n * @param date - Date as string or Date object\n * @returns Date object\n */\nexport function dateAsString(date: Date | string) {\n if (typeof date === 'string') {\n return new Date(date);\n }\n return date;\n}\n\n/**\n * Converts a Date object to an ISO string\n *\n * @param date - Date as Date object or string\n * @returns ISO date string or the original string\n */\nexport function dateAsObject(date: Date | string) {\n if (date instanceof Date) {\n return date.toISOString();\n }\n return date;\n}\n\n/**\n * Extracts field definitions from a class constructor\n *\n * Uses ObjectRegistry cached fields from AST manifest exclusively.\n * No runtime introspection fallback - classes must be decorated with @smrt()\n * for schema generation to work.\n *\n * @param ClassType - Class constructor to extract fields from\n * @param values - Optional values to set for the fields\n * @returns Object containing field definitions with names, types, and values\n * @throws {Error} If the class is not registered in ObjectRegistry\n * @example\n * ```typescript\n * @smrt()\n * class Product extends SmrtObject {\n * name: string = '';\n * price: number = 0.0;\n * }\n *\n * const fields = await fieldsFromClass(Product);\n * console.log(fields.name.type); // 'TEXT'\n * console.log(fields.price.type); // 'REAL'\n * ```\n */\nexport async function fieldsFromClass(\n ClassType: new (...args: never[]) => SmrtObject,\n values?: Record<string, unknown>,\n) {\n // `getClassByConstructor` expects the registry's `SmrtObjectConstructor`\n // (`new (...args: any[]) => SmrtObject`); the narrower `never[]` param type\n // is contravariantly assignable, so this widening cast is purely structural.\n const className =\n ObjectRegistry.getClassByConstructor(ClassType as SmrtObjectConstructor)\n ?.name || ClassType.name;\n // NEW: Use getAllFields() to include inherited fields from parent classes\n const cachedFields = await ObjectRegistry.getAllFields(className);\n\n // Phase 2: AST manifest only - no runtime introspection fallback\n if (cachedFields.size === 0) {\n // Return empty fields for unregistered classes (for backward compatibility)\n // generateSchema() will throw if it needs field definitions\n return {};\n }\n\n // Use cached field definitions from AST manifest\n const fields: Record<string, unknown> = {};\n\n // Add/override with fields from cached registry\n for (const [key, field] of cachedFields.entries()) {\n const meta = { ...(field._meta || {}) };\n delete meta.__smrtSystemField;\n\n fields[key] = {\n name: key,\n type: field.type || 'TEXT',\n _meta: meta,\n ...(values && key in values ? { value: values[key] } : {}),\n };\n }\n\n return fields;\n}\n\n// NOTE: generateSchema moved to schema/utils.ts\n// to prevent bundling Node.js-only code (SchemaGenerator with node:crypto) in browser builds.\n// Import from './schema/utils' in Node.js code that needs schema generation.\n\n/**\n * Returns the old (incorrect) pluralized table name for migration purposes.\n *\n * This function replicates the previous buggy behavior where 'y' → 'ies'\n * transformation was applied AFTER adding 's', resulting in incorrect\n * pluralization (e.g., 'currency' → 'currencys' instead of 'currencies').\n *\n * Use this to generate migration SQL that renames old tables to new names.\n *\n * @param className - Name of the class\n * @returns The incorrectly pluralized table name (old behavior)\n * @example\n * ```typescript\n * // Generate migration for a class\n * const oldName = legacyClassnameToTablename('Currency'); // 'currencys'\n * const newName = classnameToTablename('Currency'); // 'currencies'\n * console.log(`ALTER TABLE ${oldName} RENAME TO ${newName};`);\n * ```\n */\nexport function legacyClassnameToTablename(className: string): string {\n return className\n .replace(/([a-z])([A-Z])/g, '$1_$2')\n .toLowerCase()\n .replace(/([^s])$/, '$1s')\n .replace(/y$/, 'ies'); // This runs AFTER 's' is added, so it's a no-op for 'y' words\n}\n\n/**\n * Generates migration SQL for renaming tables from old to new naming convention.\n *\n * Returns an array of SQL statements to rename tables that were affected\n * by the pluralization bug (Issue #839).\n *\n * @param classNames - Array of class names to check for migration\n * @returns Array of SQL RENAME statements (empty if no changes needed)\n * @example\n * ```typescript\n * const migrations = generateTableRenameMigrations([\n * 'Currency',\n * 'JournalEntry',\n * 'Product' // Not affected\n * ]);\n * // Returns:\n * // [\n * // 'ALTER TABLE currencys RENAME TO currencies;',\n * // 'ALTER TABLE journal_entrys RENAME TO journal_entries;'\n * // ]\n * ```\n */\nexport function generateTableRenameMigrations(classNames: string[]): string[] {\n const migrations: string[] = [];\n\n for (const className of classNames) {\n const oldName = legacyClassnameToTablename(className);\n const newName = classnameToTablename(className);\n\n if (oldName !== newName) {\n migrations.push(`ALTER TABLE ${oldName} RENAME TO ${newName};`);\n }\n }\n\n return migrations;\n}\n\n/**\n * Generates a table name from a class constructor\n *\n * Checks for SMRT_TABLE_NAME static property first (set by @smrt() decorator),\n * which survives code minification. Falls back to deriving from ClassType.name\n * for backward compatibility.\n *\n * @param ClassType - Class constructor or function\n * @returns Pluralized snake_case table name\n * @example\n * ```typescript\n * // With @smrt() decorator (recommended)\n * @smrt()\n * class Product extends SmrtObject { }\n * tableNameFromClass(Product); // \"products\" (captured before minification)\n *\n * // Without decorator (fallback)\n * class Category extends SmrtObject { }\n * tableNameFromClass(Category); // \"categories\" (derived from runtime name)\n * ```\n */\nexport function tableNameFromClass(\n // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\n ClassType: Function | (new (...args: never[]) => SmrtObject),\n) {\n // Check for SMRT_TABLE_NAME property set by @smrt() decorator (survives minification)\n if ('SMRT_TABLE_NAME' in ClassType) {\n // The `in` guard above proves the static decorator property exists; read it\n // through a typed shape rather than `any`.\n return (ClassType as { SMRT_TABLE_NAME: string }).SMRT_TABLE_NAME;\n }\n\n // Fallback: derive from class name (breaks with minification)\n const snakeCase = ClassType.name\n // Insert underscore between lower & upper case letters\n .replace(/([a-z])([A-Z])/g, '$1_$2')\n // Convert to lowercase\n .toLowerCase();\n\n return pluralize(snakeCase);\n}\n\n/**\n * Formats data for JavaScript by converting date strings to Date objects\n * and snake_case column names to camelCase properties\n *\n * Generic over the input row type `T` so callers preserve their (typically\n * loose) row typing across the hydration boundary — this matches the historic\n * behavior where a `Record<string, any>` / `any` row produced an equally loose\n * result. Keys are re-cased and values are type-converted at runtime; the\n * return is the same `Record` shape, so it is reasserted as `T` at this DB\n * boundary rather than widened to an unrelated type.\n *\n * @param data - Object with data to format (snake_case column names from DB)\n * @param fields - Optional field definitions to determine types (from fieldsFromClass)\n * @returns Object with properly typed values and camelCase property names for JavaScript\n */\nexport function formatDataJs<\n T extends Record<string, unknown> = Record<string, unknown>,\n>(data: T, fields?: Record<string, { type?: string }>): T {\n const normalizedData: Record<string, unknown> = {};\n\n if (process.env.DEBUG_STI) {\n logger.debug('[formatDataJs] Input data', {\n hasMetaData: !!data._meta_data,\n metaType: data._meta_type,\n keys: Object.keys(data),\n });\n }\n\n // STI: If _meta_data exists, merge it into a FRESH object first so meta\n // fields are available during formatting. This must never mutate the caller's\n // `data` argument — `formatDataJs` is a public export and external callers may\n // pass shared objects that would otherwise get silent field injection (#1378).\n let mergedData: Record<string, unknown> = data;\n if (data._meta_data) {\n // `_meta_data` is the STI meta column: a JSON string from the DB or an\n // already-parsed object. Either way it is an object of meta fields; type it\n // as such at this boundary so the merge/key-walk below stays sound.\n const metaData: Record<string, unknown> =\n typeof data._meta_data === 'string'\n ? JSON.parse(data._meta_data)\n : (data._meta_data as Record<string, unknown>);\n\n if (process.env.DEBUG_STI) {\n logger.debug('[formatDataJs] Merging _meta_data', {\n metaDataKeys: Object.keys(metaData),\n metaData,\n });\n }\n\n // Merge meta fields into a copy (will be formatted below). Spreading\n // `metaData` last preserves the original precedence of Object.assign.\n mergedData = { ...data, ...metaData };\n\n if (process.env.DEBUG_STI) {\n logger.debug('[formatDataJs] After merge, data keys', {\n keys: Object.keys(mergedData),\n });\n }\n }\n\n for (const [key, value] of Object.entries(mergedData)) {\n // Preserve keys with leading underscore (e.g., _meta_type, _meta_data)\n // These are special STI/framework fields that should not be camelCased\n const camelKey = key.startsWith('_') ? key : toCamelCase(key);\n\n // Determine the actual output key based on what's in fields\n // If the original key (snake_case) is in fields, use it; otherwise use camelCase\n // This supports both conventions (e.g., publish_date vs publishDate)\n let outputKey = camelKey;\n if (fields && key in fields && !(camelKey in fields)) {\n // Original key exists in fields but camelCase doesn't - use original (snake_case)\n outputKey = key;\n }\n\n // Get field type from fields, trying both key variants\n const fieldDef = fields?.[outputKey] ?? fields?.[camelKey] ?? fields?.[key];\n const fieldType = fieldDef?.type?.toLowerCase();\n\n if (value instanceof Date) {\n normalizedData[outputKey] = value;\n } else if (typeof value === 'string') {\n // Use field definitions if available, otherwise fall back to name patterns\n const isDate = fieldType === 'datetime' || isDateField(key);\n\n if (isDate) {\n const parsedDate = value.trim() ? new Date(value) : null;\n normalizedData[outputKey] =\n parsedDate && !Number.isNaN(parsedDate.getTime())\n ? parsedDate\n : value;\n } else if (fieldType === 'json') {\n // Parse JSON strings back to objects\n try {\n normalizedData[outputKey] = JSON.parse(value);\n } catch {\n // Keep as string if parsing fails\n normalizedData[outputKey] = value;\n }\n } else if (fieldType === 'integer') {\n // PostgreSQL returns int8 as strings. SQLite may spell an integer as\n // \"2.0\"; reject values JavaScript cannot represent exactly rather than\n // rounding them while hydrating a model.\n const parsed = Number(value);\n normalizedData[outputKey] =\n Number.isFinite(parsed) && Number.isInteger(parsed)\n ? toSafeInteger(value, `Integer field ${outputKey}`)\n : value;\n } else if (fieldType === 'real' || fieldType === 'decimal') {\n // Convert string numbers to floats for REAL/DECIMAL fields\n const parsed = Number.parseFloat(value);\n normalizedData[outputKey] = Number.isNaN(parsed) ? value : parsed;\n } else {\n normalizedData[outputKey] = value;\n }\n } else if (typeof value === 'number') {\n if (fieldType === 'boolean') {\n // Convert SQLite integers (0/1) to booleans for boolean fields\n normalizedData[outputKey] = value === 1;\n } else if (fieldType === 'integer' && Number.isInteger(value)) {\n normalizedData[outputKey] = toSafeInteger(\n value,\n `Integer field ${outputKey}`,\n );\n } else {\n // Pass through numeric values as-is\n // Note: In JavaScript, 2.0 === 2 so no conversion needed\n // Non-integers in INTEGER fields (e.g., 2.9) are kept to surface data issues\n normalizedData[outputKey] = value;\n }\n } else if (typeof value === 'bigint' && fieldType === 'integer') {\n normalizedData[outputKey] = toSafeInteger(\n value,\n `Integer field ${outputKey}`,\n );\n } else {\n normalizedData[outputKey] = value;\n }\n }\n\n if (process.env.DEBUG_STI) {\n logger.debug('[formatDataJs] Output normalizedData', {\n keys: Object.keys(normalizedData),\n metaType: normalizedData._meta_type,\n });\n }\n\n // Reassert the re-cased/type-converted row as the caller's row type `T`.\n // The output is structurally a `Record<string, unknown>`; this boundary cast\n // preserves the historic loose-in/loose-out hydration contract without `any`.\n return normalizedData as T;\n}\n\n/**\n * Type guard to check if a value is a Field instance\n *\n * @deprecated Field helpers have been removed. This function always returns false.\n * @param value - Value to check\n * @returns Always false (Field class no longer exists)\n */\nexport function isFieldInstance(value: unknown): value is never {\n return false;\n}\n\n/**\n * Formats data for SQL by converting Date objects to ISO strings\n * and camelCase property names to snake_case column names\n *\n * @param data - Object with data to format (camelCase property names from JavaScript)\n * @returns Object with properly formatted values and snake_case column names for SQL\n */\nexport function formatDataSql(data: Record<string, unknown>) {\n const normalizedData: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n // Convert camelCase to snake_case for SQL\n const snakeKey = toSnakeCase(key);\n\n // Field helpers removed - no need to extract values (deprecated code removed)\n if (value instanceof Date) {\n normalizedData[snakeKey] = value.toISOString(); // Postgres accepts ISO format with timezone\n } else {\n normalizedData[snakeKey] = value;\n }\n }\n return normalizedData;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/index.ts"],"names":[],"mappings":"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"}
@@ -22,7 +22,6 @@ var VIRTUAL_MODULES = {
22
22
  "@happyvertical/smrt-virt-mcp": "smrt:mcp",
23
23
  "@happyvertical/smrt-virt-types": "smrt:types",
24
24
  "@happyvertical/smrt-virt-manifest": "smrt:manifest",
25
- "@happyvertical/smrt-virt-schema": "smrt:schema",
26
25
  "@happyvertical/smrt-virt-ui": "smrt:ui",
27
26
  "@happyvertical/smrt-virt-cli": "smrt:cli",
28
27
  "@happyvertical/smrt-virt-web": "smrt:web"
@@ -447,7 +446,6 @@ function smrtPlugin(options = {}) {
447
446
  case "smrt:mcp": return await generateMCPModule(manifest);
448
447
  case "smrt:types": return await generateTypesModule(manifest, pluginMode);
449
448
  case "smrt:manifest": return generateManifestModule(manifest);
450
- case "smrt:schema": return await generateSchemaModule(manifest);
451
449
  case "smrt:ui": return await loadDefaultUI();
452
450
  case "smrt:index-html": return await loadDefaultHTML();
453
451
  case "smrt:cli": return await generateCLIModule(manifest);
@@ -557,15 +555,10 @@ function smrtPlugin(options = {}) {
557
555
  source: "../scanner/index.ts",
558
556
  dist: "../scanner.js"
559
557
  });
560
- const manifestGen = new ManifestGenerator();
561
- manifestGen.normalizeReportTenantScope(newManifest);
562
- manifestGen.injectTenantScopedFields(newManifest);
563
- manifestGen.mergeInheritedFields(newManifest);
564
- manifestGen.normalizeReportObjects(newManifest);
565
- manifestGen.generateValidationRules(newManifest);
566
- manifestGen.generateSchemas(newManifest);
567
- manifestGen.assertTenantScopedSchemaContract(newManifest);
568
- manifestGen.generateAgentManifests(newManifest, packageName, packageJson);
558
+ new ManifestGenerator().applyGenerationPasses(newManifest, {
559
+ packageName,
560
+ packageJson
561
+ });
569
562
  const elapsed = performance.now() - startTime;
570
563
  const objectCount = Object.keys(newManifest.objects).length;
571
564
  if (objectCount > 0) {
@@ -1354,57 +1347,6 @@ export default setupCLI;
1354
1347
  `;
1355
1348
  }
1356
1349
  }
1357
- /**
1358
- * Generate virtual schema module with JSON manifests
1359
- */
1360
- async function generateSchemaModule(manifest) {
1361
- try {
1362
- const { SchemaGenerator } = await importBuildAwareModule({
1363
- source: "../schema/generator.ts",
1364
- dist: "../schema/generator.js"
1365
- });
1366
- const schemaGenerator = new SchemaGenerator();
1367
- const schemas = {};
1368
- for (const [className, objectDef] of Object.entries(manifest.objects)) schemas[className] = schemaGenerator.generateSchema(objectDef);
1369
- const schemaManifest = {
1370
- version: "1.0.0",
1371
- timestamp: 0,
1372
- packageName: manifest.packageName || "unknown",
1373
- schemas,
1374
- dependencies: Array.from(new Set(Object.values(schemas).flatMap((s) => s.dependencies || [])))
1375
- };
1376
- return `// Auto-generated schema manifest from SMRT objects
1377
- // This file is generated automatically - do not edit
1378
-
1379
- // Schema manifest as JSON for SQL adapters
1380
- export const schemaManifest = ${JSON.stringify(schemaManifest, null, 2)};
1381
-
1382
- // Schema registry for runtime access
1383
- export const schemas = schemaManifest.schemas;
1384
-
1385
- // Schema lookup function
1386
- export function getSchema(className: string) {
1387
- return schemas[className];
1388
- }
1389
-
1390
- // All schemas as array for dependency resolution
1391
- export const allSchemas = Object.values(schemas);
1392
-
1393
- // Package information
1394
- export const packageName = schemaManifest.packageName;
1395
- export const dependencies = schemaManifest.dependencies;
1396
-
1397
- export default schemaManifest;`;
1398
- } catch (error) {
1399
- console.error("[smrt] Error generating schema module:", error);
1400
- return `// Error generating schema module
1401
- export const schemaManifest = { schemas: {}, dependencies: [] };
1402
- export const schemas = {};
1403
- export function getSchema() { return null; }
1404
- export const allSchemas = [];
1405
- export default {};`;
1406
- }
1407
- }
1408
1350
  //#endregion
1409
1351
  export { findCliApiCoherenceViolations, generateInlineRegisterModule, generateSvelteKitRoutes, generateTypeDeclarationFile, isRegisterShimModuleId, loadVerifiedSmrtGenerationSnapshot, methodNameToKebab, resolveApiActionRouteConfig, resolveApiActionSet, serializeSmrtGenerationSnapshot, sha256SmrtGenerationSnapshot, smrtPlugin, validateCliIncludeAgainstApi };
1410
1352