@happyvertical/smrt-core 0.36.5 → 0.36.6

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 (104) hide show
  1. package/dist/collection.d.ts +1 -1
  2. package/dist/config.d.ts +1 -1
  3. package/dist/config.d.ts.map +1 -1
  4. package/dist/config.js.map +1 -1
  5. package/dist/consumer-plugin/index.d.ts.map +1 -1
  6. package/dist/consumer-plugin/index.js +7 -3
  7. package/dist/consumer-plugin/index.js.map +1 -1
  8. package/dist/database.d.ts +3 -3
  9. package/dist/database.d.ts.map +1 -1
  10. package/dist/database.js.map +1 -1
  11. package/dist/embeddings/provider.d.ts +14 -2
  12. package/dist/embeddings/provider.d.ts.map +1 -1
  13. package/dist/embeddings/provider.js +3 -3
  14. package/dist/embeddings/provider.js.map +1 -1
  15. package/dist/embeddings/storage.js.map +1 -1
  16. package/dist/errors.d.ts +22 -22
  17. package/dist/errors.d.ts.map +1 -1
  18. package/dist/errors.js +5 -4
  19. package/dist/errors.js.map +1 -1
  20. package/dist/knowledge.d.ts +12 -1
  21. package/dist/knowledge.d.ts.map +1 -1
  22. package/dist/knowledge.js.map +1 -1
  23. package/dist/lazy-config.d.ts.map +1 -1
  24. package/dist/lazy-config.js.map +1 -1
  25. package/dist/manifest/generator.d.ts.map +1 -1
  26. package/dist/manifest/generator.js +14 -12
  27. package/dist/manifest/generator.js.map +1 -1
  28. package/dist/manifest/manager.d.ts +3 -3
  29. package/dist/manifest/manager.d.ts.map +1 -1
  30. package/dist/manifest/manager.js.map +1 -1
  31. package/dist/manifest/manifest-loader.d.ts +3 -2
  32. package/dist/manifest/manifest-loader.d.ts.map +1 -1
  33. package/dist/manifest/manifest-loader.js +3 -2
  34. package/dist/manifest/manifest-loader.js.map +1 -1
  35. package/dist/manifest/static-manifest.js +2 -2
  36. package/dist/manifest/static-manifest.js.map +1 -1
  37. package/dist/manifest/store.js +2 -2
  38. package/dist/manifest/test-manifest-stub.js +2 -2
  39. package/dist/manifest/test-manifest-stub.js.map +1 -1
  40. package/dist/manifest.json +2 -2
  41. package/dist/mcp-advisor/index.d.ts +1 -1
  42. package/dist/mcp-advisor/index.d.ts.map +1 -1
  43. package/dist/mcp-advisor/tools/get-object-config.d.ts.map +1 -1
  44. package/dist/mcp-advisor/types.d.ts +5 -5
  45. package/dist/mcp-advisor/types.d.ts.map +1 -1
  46. package/dist/migrations/differ.js.map +1 -1
  47. package/dist/scanner/manifest-generator.d.ts +11 -3
  48. package/dist/scanner/manifest-generator.d.ts.map +1 -1
  49. package/dist/scanner/manifest-generator.js +19 -15
  50. package/dist/scanner/manifest-generator.js.map +1 -1
  51. package/dist/scanner/types.d.ts +60 -6
  52. package/dist/scanner/types.d.ts.map +1 -1
  53. package/dist/schema/code-generator.d.ts.map +1 -1
  54. package/dist/schema/ddl/base-strategy.d.ts +2 -2
  55. package/dist/schema/ddl/base-strategy.d.ts.map +1 -1
  56. package/dist/schema/ddl/base-strategy.js.map +1 -1
  57. package/dist/schema/ddl/sqlite-strategy.d.ts +1 -1
  58. package/dist/schema/ddl/sqlite-strategy.d.ts.map +1 -1
  59. package/dist/schema/ddl/sqlite-strategy.js.map +1 -1
  60. package/dist/schema/ddl/types.d.ts +1 -1
  61. package/dist/schema/ddl/types.d.ts.map +1 -1
  62. package/dist/schema/generator.d.ts +27 -4
  63. package/dist/schema/generator.d.ts.map +1 -1
  64. package/dist/schema/generator.js +3 -2
  65. package/dist/schema/generator.js.map +1 -1
  66. package/dist/schema/override-system.d.ts +3 -3
  67. package/dist/schema/override-system.d.ts.map +1 -1
  68. package/dist/schema/schema-aggregator.d.ts.map +1 -1
  69. package/dist/schema/schema-manager.d.ts.map +1 -1
  70. package/dist/schema/schema-manager.js +12 -4
  71. package/dist/schema/schema-manager.js.map +1 -1
  72. package/dist/schema/types.d.ts +1 -1
  73. package/dist/schema/types.d.ts.map +1 -1
  74. package/dist/schema/utils.d.ts +6 -1
  75. package/dist/schema/utils.d.ts.map +1 -1
  76. package/dist/schema/utils.js +2 -1
  77. package/dist/schema/utils.js.map +1 -1
  78. package/dist/signals/sanitizer.d.ts +1 -1
  79. package/dist/signals/sanitizer.d.ts.map +1 -1
  80. package/dist/signals/sanitizer.js +12 -2
  81. package/dist/signals/sanitizer.js.map +1 -1
  82. package/dist/smrt-knowledge.json +4 -4
  83. package/dist/system/types.d.ts +2 -2
  84. package/dist/system/types.d.ts.map +1 -1
  85. package/dist/system-fields.d.ts +5 -43
  86. package/dist/system-fields.d.ts.map +1 -1
  87. package/dist/system-fields.js +2 -1
  88. package/dist/system-fields.js.map +1 -1
  89. package/dist/test-utils.d.ts +39 -13
  90. package/dist/test-utils.d.ts.map +1 -1
  91. package/dist/testing/database.d.ts.map +1 -1
  92. package/dist/testing/database.js.map +1 -1
  93. package/dist/utils/json.js.map +1 -1
  94. package/dist/utils.d.ts +16 -8
  95. package/dist/utils.d.ts.map +1 -1
  96. package/dist/utils.js.map +1 -1
  97. package/dist/vite-plugin/index.d.ts.map +1 -1
  98. package/dist/vite-plugin/index.js +9 -7
  99. package/dist/vite-plugin/index.js.map +1 -1
  100. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  101. package/dist/vite-plugin/sveltekit-generator.js +4 -3
  102. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  103. package/dist/vite-plugin/templates/default-ui.ts +20 -6
  104. package/package.json +4 -4
package/dist/utils.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { SmrtObject } from './object';
1
2
  import { classnameToTablename, pluralize, toSnakeCase } from './utils/naming.js';
2
3
  export { classnameToTablename, pluralize, toSnakeCase };
3
4
  /**
@@ -19,14 +20,14 @@ export declare function toCamelCase(str: string): string;
19
20
  * @param obj - Object with camelCase keys
20
21
  * @returns Object with snake_case keys
21
22
  */
22
- export declare function keysToSnakeCase(obj: Record<string, any>): Record<string, any>;
23
+ export declare function keysToSnakeCase(obj: Record<string, unknown>): Record<string, unknown>;
23
24
  /**
24
25
  * Converts all keys in an object from snake_case to camelCase
25
26
  *
26
27
  * @param obj - Object with snake_case keys
27
28
  * @returns Object with camelCase keys
28
29
  */
29
- export declare function keysToCamelCase(obj: Record<string, any>): Record<string, any>;
30
+ export declare function keysToCamelCase(obj: Record<string, unknown>): Record<string, unknown>;
30
31
  /**
31
32
  * Checks if a field name indicates a date field based on naming conventions
32
33
  *
@@ -81,7 +82,7 @@ export declare function dateAsObject(date: Date | string): string;
81
82
  * console.log(fields.price.type); // 'REAL'
82
83
  * ```
83
84
  */
84
- export declare function fieldsFromClass(ClassType: new (...args: any[]) => any, values?: Record<string, any>): Promise<Record<string, any>>;
85
+ export declare function fieldsFromClass(ClassType: new (...args: never[]) => SmrtObject, values?: Record<string, unknown>): Promise<Record<string, unknown>>;
85
86
  /**
86
87
  * Returns the old (incorrect) pluralized table name for migration purposes.
87
88
  *
@@ -146,18 +147,25 @@ export declare function generateTableRenameMigrations(classNames: string[]): str
146
147
  * tableNameFromClass(Category); // "categories" (derived from runtime name)
147
148
  * ```
148
149
  */
149
- export declare function tableNameFromClass(ClassType: Function | (new (...args: any[]) => any)): any;
150
+ export declare function tableNameFromClass(ClassType: Function | (new (...args: never[]) => SmrtObject)): string;
150
151
  /**
151
152
  * Formats data for JavaScript by converting date strings to Date objects
152
153
  * and snake_case column names to camelCase properties
153
154
  *
155
+ * Generic over the input row type `T` so callers preserve their (typically
156
+ * loose) row typing across the hydration boundary — this matches the historic
157
+ * behavior where a `Record<string, any>` / `any` row produced an equally loose
158
+ * result. Keys are re-cased and values are type-converted at runtime; the
159
+ * return is the same `Record` shape, so it is reasserted as `T` at this DB
160
+ * boundary rather than widened to an unrelated type.
161
+ *
154
162
  * @param data - Object with data to format (snake_case column names from DB)
155
163
  * @param fields - Optional field definitions to determine types (from fieldsFromClass)
156
164
  * @returns Object with properly typed values and camelCase property names for JavaScript
157
165
  */
158
- export declare function formatDataJs(data: Record<string, any>, fields?: Record<string, {
166
+ export declare function formatDataJs<T extends Record<string, unknown> = Record<string, unknown>>(data: T, fields?: Record<string, {
159
167
  type?: string;
160
- }>): Record<string, any>;
168
+ }>): T;
161
169
  /**
162
170
  * Type guard to check if a value is a Field instance
163
171
  *
@@ -165,7 +173,7 @@ export declare function formatDataJs(data: Record<string, any>, fields?: Record<
165
173
  * @param value - Value to check
166
174
  * @returns Always false (Field class no longer exists)
167
175
  */
168
- export declare function isFieldInstance(value: any): value is never;
176
+ export declare function isFieldInstance(value: unknown): value is never;
169
177
  /**
170
178
  * Formats data for SQL by converting Date objects to ISO strings
171
179
  * and camelCase property names to snake_case column names
@@ -173,5 +181,5 @@ export declare function isFieldInstance(value: any): value is never;
173
181
  * @param data - Object with data to format (camelCase property names from JavaScript)
174
182
  * @returns Object with properly formatted values and snake_case column names for SQL
175
183
  */
176
- export declare function formatDataSql(data: Record<string, any>): Record<string, any>;
184
+ export declare function formatDataSql(data: Record<string, unknown>): Record<string, unknown>;
177
185
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,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,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAM7E;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAM7E;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,GAAG,EAAE,KAAK,GAAG,EACtC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,gCAgC7B;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,GAAG,EAAE,KAAK,GAAG,CAAC,OAepD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,uBAkH3C;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,KAAK,CAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,uBActD"}
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"}
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sources":["../src/utils.ts"],"sourcesContent":["import { createLogger } from '@happyvertical/logger';\nimport { ObjectRegistry } from './registry';\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(obj: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\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(obj: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\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: any[]) => any,\n values?: Record<string, any>,\n) {\n const className =\n ObjectRegistry.getClassByConstructor(ClassType as any)?.name ||\n 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, any> = {};\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: any[]) => any),\n) {\n // Check for SMRT_TABLE_NAME property set by @smrt() decorator (survives minification)\n if ('SMRT_TABLE_NAME' in ClassType) {\n return (ClassType as any).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 * @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 data: Record<string, any>,\n fields?: Record<string, { type?: string }>,\n) {\n const normalizedData: Record<string, any> = {};\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, any> = data;\n if (data._meta_data) {\n const metaData =\n typeof data._meta_data === 'string'\n ? JSON.parse(data._meta_data)\n : data._meta_data;\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 return normalizedData;\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: any): 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, any>) {\n const normalizedData: Record<string, any> = {};\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"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","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"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAelD,YAAY,EACV,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,6BAA6B,EAC7B,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAElC,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,6BAA6B;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qBAAqB;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC;;OAEG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,8CAA8C;IAC9C,SAAS,CAAC,EAAE;QACV,yDAAyD;QACzD,OAAO,EAAE,OAAO,CAAC;QACjB,wEAAwE;QACxE,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,qEAAqE;QACrE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mEAAmE;QACnE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mDAAmD;QACnD,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB;;;;WAIG;QACH,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,CAAC;IACF,mEAAmE;IACnE,SAAS,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;IAC1C;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAsBD,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAg2BlE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EACV,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,MAAM,EAAiC,MAAM,MAAM,CAAC;AAgBlE,YAAY,EACV,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACL,6BAA6B,EAC7B,uBAAuB,EACvB,iBAAiB,EACjB,mBAAmB,EACnB,4BAA4B,GAC7B,MAAM,0BAA0B,CAAC;AAalC,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,6BAA6B;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,qBAAqB;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4EAA4E;IAC5E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,wEAAwE;IACxE,IAAI,CAAC,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC;;OAEG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,8CAA8C;IAC9C,SAAS,CAAC,EAAE;QACV,yDAAyD;QACzD,OAAO,EAAE,OAAO,CAAC;QACjB,wEAAwE;QACxE,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,qEAAqE;QACrE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mEAAmE;QACnE,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,mDAAmD;QACnD,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB;;;;WAIG;QACH,WAAW,CAAC,EAAE,OAAO,CAAC;KACvB,CAAC;IACF,mEAAmE;IACnE,SAAS,CAAC,EAAE,qBAAqB,GAAG,KAAK,CAAC;IAC1C;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAsBD,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAw2BlE"}
@@ -169,11 +169,13 @@ function smrtPlugin(options = {}) {
169
169
  function preserveKnowledgeGeneratedAt(outputPath, nextKnowledge) {
170
170
  if (!existsSync(outputPath)) return nextKnowledge;
171
171
  try {
172
- const current = JSON.parse(readFileSync(outputPath, "utf8"));
172
+ const current = JSON.parse(
173
+ readFileSync(outputPath, "utf8")
174
+ );
173
175
  if (semanticKnowledgeJson(current) === semanticKnowledgeJson(nextKnowledge)) {
174
176
  return {
175
177
  ...nextKnowledge,
176
- generatedAt: current.generatedAt
178
+ generatedAt: current.generatedAt ?? nextKnowledge.generatedAt
177
179
  };
178
180
  }
179
181
  } catch {
@@ -494,13 +496,15 @@ function smrtPlugin(options = {}) {
494
496
  }
495
497
  },
496
498
  async closeBundle() {
497
- if (!manifest || !config.build?.lib) {
499
+ if (!manifest || !config?.build?.lib) {
498
500
  return;
499
501
  }
500
502
  try {
501
503
  const { writeFileSync, mkdirSync } = await import("node:fs");
502
504
  const { resolve, dirname: dirname2 } = await import("node:path");
503
- const outDir = config.build?.rollupOptions?.output?.dir || config.build?.outDir || "dist";
505
+ const rollupOutput = config.build?.rollupOptions?.output;
506
+ const rollupOutputDir = Array.isArray(rollupOutput) ? void 0 : rollupOutput?.dir;
507
+ const outDir = rollupOutputDir || config.build?.outDir || "dist";
504
508
  const manifestPath = resolve(projectRoot, outDir, "manifest.json");
505
509
  const knowledgePath = resolve(
506
510
  projectRoot,
@@ -1354,9 +1358,7 @@ async function generateSchemaModule(manifest) {
1354
1358
  packageName: manifest.packageName || "unknown",
1355
1359
  schemas,
1356
1360
  dependencies: Array.from(
1357
- new Set(
1358
- Object.values(schemas).flatMap((s) => s.dependencies || [])
1359
- )
1361
+ new Set(Object.values(schemas).flatMap((s) => s.dependencies || []))
1360
1362
  )
1361
1363
  };
1362
1364
  return `// Auto-generated schema manifest from SMRT objects