@happyvertical/smrt-core 0.47.2 → 0.49.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 (110) hide show
  1. package/agents/generators.md +12 -0
  2. package/agents/object-runtime.md +7 -0
  3. package/agents/schema-paths.md +62 -0
  4. package/agents/system-diagnostics.md +6 -1
  5. package/dist/cascade.d.ts.map +1 -1
  6. package/dist/cascade.js +9 -6
  7. package/dist/cascade.js.map +1 -1
  8. package/dist/collection.d.ts +0 -1
  9. package/dist/collection.d.ts.map +1 -1
  10. package/dist/collection.js +35 -45
  11. package/dist/collection.js.map +1 -1
  12. package/dist/decorators/compatibility.d.ts +1 -1
  13. package/dist/decorators/compatibility.d.ts.map +1 -1
  14. package/dist/decorators/compatibility.js +2 -2
  15. package/dist/decorators/compatibility.js.map +1 -1
  16. package/dist/decorators/index.d.ts.map +1 -1
  17. package/dist/decorators/index.js +33 -24
  18. package/dist/decorators/index.js.map +1 -1
  19. package/dist/interceptors.d.ts +3 -1
  20. package/dist/interceptors.d.ts.map +1 -1
  21. package/dist/interceptors.js +3 -2
  22. package/dist/interceptors.js.map +1 -1
  23. package/dist/manifest/static-manifest.js +1 -1
  24. package/dist/manifest/static-manifest.js.map +1 -1
  25. package/dist/manifest/store.js +1 -1
  26. package/dist/manifest.json +1 -1
  27. package/dist/migrations/differ.d.ts +10 -4
  28. package/dist/migrations/differ.d.ts.map +1 -1
  29. package/dist/migrations/differ.js +173 -5
  30. package/dist/migrations/differ.js.map +1 -1
  31. package/dist/object.d.ts +1 -5
  32. package/dist/object.d.ts.map +1 -1
  33. package/dist/object.js +28 -37
  34. package/dist/object.js.map +1 -1
  35. package/dist/registry/class-registration.d.ts +2 -1
  36. package/dist/registry/class-registration.d.ts.map +1 -1
  37. package/dist/registry/class-registration.js +47 -16
  38. package/dist/registry/class-registration.js.map +1 -1
  39. package/dist/registry/name-resolver.d.ts.map +1 -1
  40. package/dist/registry/name-resolver.js +3 -3
  41. package/dist/registry/name-resolver.js.map +1 -1
  42. package/dist/registry/relationship-graph.d.ts +2 -0
  43. package/dist/registry/relationship-graph.d.ts.map +1 -1
  44. package/dist/registry/relationship-graph.js +60 -5
  45. package/dist/registry/relationship-graph.js.map +1 -1
  46. package/dist/registry/schema-builder.d.ts.map +1 -1
  47. package/dist/registry/schema-builder.js +4 -1
  48. package/dist/registry/schema-builder.js.map +1 -1
  49. package/dist/registry/shared-state.d.ts +18 -0
  50. package/dist/registry/shared-state.d.ts.map +1 -1
  51. package/dist/registry/shared-state.js +25 -1
  52. package/dist/registry/shared-state.js.map +1 -1
  53. package/dist/registry/types.d.ts +8 -0
  54. package/dist/registry/types.d.ts.map +1 -1
  55. package/dist/registry.d.ts +32 -5
  56. package/dist/registry.d.ts.map +1 -1
  57. package/dist/registry.js +120 -12
  58. package/dist/registry.js.map +1 -1
  59. package/dist/relationship-loader.d.ts +6 -0
  60. package/dist/relationship-loader.d.ts.map +1 -0
  61. package/dist/relationship-loader.js +35 -0
  62. package/dist/relationship-loader.js.map +1 -0
  63. package/dist/scanner/manifest-generator.d.ts.map +1 -1
  64. package/dist/scanner/manifest-generator.js +6 -3
  65. package/dist/scanner/manifest-generator.js.map +1 -1
  66. package/dist/schema/foreign-key-ddl.d.ts +8 -0
  67. package/dist/schema/foreign-key-ddl.d.ts.map +1 -1
  68. package/dist/schema/foreign-key-ddl.js +12 -1
  69. package/dist/schema/foreign-key-ddl.js.map +1 -1
  70. package/dist/schema/foreign-key-orphan-report.d.ts.map +1 -1
  71. package/dist/schema/foreign-key-orphan-report.js +18 -1
  72. package/dist/schema/foreign-key-orphan-report.js.map +1 -1
  73. package/dist/schema/generator.d.ts +2 -0
  74. package/dist/schema/generator.d.ts.map +1 -1
  75. package/dist/schema/generator.js +16 -9
  76. package/dist/schema/generator.js.map +1 -1
  77. package/dist/schema/index.d.ts +1 -1
  78. package/dist/schema/index.d.ts.map +1 -1
  79. package/dist/schema/index.js +2 -2
  80. package/dist/schema/live-parity.d.ts.map +1 -1
  81. package/dist/schema/live-parity.js +74 -0
  82. package/dist/schema/live-parity.js.map +1 -1
  83. package/dist/schema/text-cast-probe.d.ts +66 -0
  84. package/dist/schema/text-cast-probe.d.ts.map +1 -0
  85. package/dist/schema/text-cast-probe.js +142 -0
  86. package/dist/schema/text-cast-probe.js.map +1 -0
  87. package/dist/schema/types.d.ts +28 -0
  88. package/dist/schema/types.d.ts.map +1 -1
  89. package/dist/schema/utils.d.ts.map +1 -1
  90. package/dist/schema/utils.js +2 -1
  91. package/dist/schema/utils.js.map +1 -1
  92. package/dist/schema.js +2 -2
  93. package/dist/smrt-knowledge.json +11 -11
  94. package/dist/system/diagnostics.d.ts +22 -0
  95. package/dist/system/diagnostics.d.ts.map +1 -1
  96. package/dist/system/diagnostics.js +88 -31
  97. package/dist/system/diagnostics.js.map +1 -1
  98. package/dist/test-utils.d.ts.map +1 -1
  99. package/dist/utils.d.ts.map +1 -1
  100. package/dist/utils.js +2 -1
  101. package/dist/utils.js.map +1 -1
  102. package/dist/vite-plugin/dev-plane-route.d.ts +5 -0
  103. package/dist/vite-plugin/dev-plane-route.d.ts.map +1 -0
  104. package/dist/vite-plugin/dev-plane-route.js +91 -0
  105. package/dist/vite-plugin/dev-plane-route.js.map +1 -0
  106. package/dist/vite-plugin/sveltekit-generator.d.ts +8 -0
  107. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  108. package/dist/vite-plugin/sveltekit-generator.js +2 -0
  109. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  110. package/package.json +4 -4
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';\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
+ {"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 registered = ObjectRegistry.getClassByConstructor(\n ClassType as SmrtObjectConstructor,\n );\n // A collection has the exact item constructor even when relation metadata\n // uses a legacy simple name. Keep that constructor's qualified identity for\n // field lookup so a same-named class in another package cannot supply an\n // empty or unrelated field bag.\n const className =\n registered?.qualifiedName &&\n registered.name &&\n ObjectRegistry.findClassesByName(registered.name).length > 1\n ? registered.qualifiedName\n : (registered?.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":";;;;;AAaA,IAAM,SAAS,aAAa,EAC1B,OAAO,QAAQ,IAAI,YAAY,UAAU,OAC3C,CAAC;;;;;;;;;;;;;AAsBD,SAAgB,YAAY,KAAqB;CAC/C,OAAO,IAAI,QAAQ,cAAc,GAAG,WAAW,OAAO,YAAY,CAAC;AACrE;;;;;;;AAQA,SAAgB,gBACd,KACyB;CACzB,MAAM,SAAkC,CAAC;CACzC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAC3C,OAAO,YAAY,GAAG,KAAK;CAE7B,OAAO;AACT;;;;;;;AAQA,SAAgB,gBACd,KACyB;CACzB,MAAM,SAAkC,CAAC;CACzC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,GAAG,GAC3C,OAAO,YAAY,GAAG,KAAK;CAE7B,OAAO;AACT;;;;;;;;;;;;;;;;AAiBA,SAAgB,YAAY,KAAa;CAGvC,OAAO,IAAI,SAAS,OAAO,KAAK,IAAI,SAAS,KAAK,KAAK,QAAQ;AACjE;;;;;;;AAQA,SAAgB,aAAa,MAAqB;CAChD,IAAI,OAAO,SAAS,UAClB,OAAO,IAAI,KAAK,IAAI;CAEtB,OAAO;AACT;;;;;;;AAQA,SAAgB,aAAa,MAAqB;CAChD,IAAI,gBAAgB,MAClB,OAAO,KAAK,YAAY;CAE1B,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,eAAsB,gBACpB,WACA,QACA;CAIA,MAAM,aAAa,eAAe,sBAChC,SACF;CAKA,MAAM,YACJ,YAAY,iBACZ,WAAW,QACX,eAAe,kBAAkB,WAAW,IAAI,CAAC,CAAC,SAAS,IACvD,WAAW,gBACV,YAAY,QAAQ,UAAU;CAErC,MAAM,eAAe,MAAM,eAAe,aAAa,SAAS;CAGhE,IAAI,aAAa,SAAS,GAGxB,OAAO,CAAC;CAIV,MAAM,SAAkC,CAAC;CAGzC,KAAK,MAAM,CAAC,KAAK,UAAU,aAAa,QAAQ,GAAG;EACjD,MAAM,OAAO,EAAE,GAAI,MAAM,SAAS,CAAC,EAAG;EACtC,OAAO,KAAK;EAEZ,OAAO,OAAO;GACZ,MAAM;GACN,MAAM,MAAM,QAAQ;GACpB,OAAO;GACP,GAAI,UAAU,OAAO,SAAS,EAAE,OAAO,OAAO,KAAK,IAAI,CAAC;EAC1D;CACF;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,2BAA2B,WAA2B;CACpE,OAAO,UACJ,QAAQ,mBAAmB,OAAO,CAAC,CACnC,YAAY,CAAC,CACb,QAAQ,WAAW,KAAK,CAAC,CACzB,QAAQ,MAAM,KAAK;AACxB;;;;;;;;;;;;;;;;;;;;;;;AAwBA,SAAgB,8BAA8B,YAAgC;CAC5E,MAAM,aAAuB,CAAC;CAE9B,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,UAAU,2BAA2B,SAAS;EACpD,MAAM,UAAU,qBAAqB,SAAS;EAE9C,IAAI,YAAY,SACd,WAAW,KAAK,eAAe,QAAQ,aAAa,QAAQ,EAAE;CAElE;CAEA,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,mBAEd,WACA;CAEA,IAAI,qBAAqB,WAGvB,OAAQ,UAA0C;CAUpD,OAAO,UANW,UAAU,KAEzB,QAAQ,mBAAmB,OAAO,CAAC,CAEnC,YAEc,CAAS;AAC5B;;;;;;;;;;;;;;;;AAiBA,SAAgB,aAEd,MAAS,QAA+C;CACxD,MAAM,iBAA0C,CAAC;CAEjD,IAAI,QAAQ,IAAI,WACd,OAAO,MAAM,6BAA6B;EACxC,aAAa,CAAC,CAAC,KAAK;EACpB,UAAU,KAAK;EACf,MAAM,OAAO,KAAK,IAAI;CACxB,CAAC;CAOH,IAAI,aAAsC;CAC1C,IAAI,KAAK,YAAY;EAInB,MAAM,WACJ,OAAO,KAAK,eAAe,WACvB,KAAK,MAAM,KAAK,UAAU,IACzB,KAAK;EAEZ,IAAI,QAAQ,IAAI,WACd,OAAO,MAAM,qCAAqC;GAChD,cAAc,OAAO,KAAK,QAAQ;GAClC;EACF,CAAC;EAKH,aAAa;GAAE,GAAG;GAAM,GAAG;EAAS;EAEpC,IAAI,QAAQ,IAAI,WACd,OAAO,MAAM,yCAAyC,EACpD,MAAM,OAAO,KAAK,UAAU,EAC9B,CAAC;CAEL;CAEA,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,UAAU,GAAG;EAGrD,MAAM,WAAW,IAAI,WAAW,GAAG,IAAI,MAAM,YAAY,GAAG;EAK5D,IAAI,YAAY;EAChB,IAAI,UAAU,OAAO,UAAU,EAAE,YAAY,SAE3C,YAAY;EAKd,MAAM,aADW,SAAS,cAAc,SAAS,aAAa,SAAS,KAAA,EAC3C,MAAM,YAAY;EAE9C,IAAI,iBAAiB,MACnB,eAAe,aAAa;OACvB,IAAI,OAAO,UAAU,UAI1B,IAFe,cAAc,cAAc,YAAY,GAAG,GAE9C;GACV,MAAM,aAAa,MAAM,KAAK,IAAI,IAAI,KAAK,KAAK,IAAI;GACpD,eAAe,aACb,cAAc,CAAC,OAAO,MAAM,WAAW,QAAQ,CAAC,IAC5C,aACA;EACR,OAAO,IAAI,cAAc,QAEvB,IAAI;GACF,eAAe,aAAa,KAAK,MAAM,KAAK;EAC9C,QAAQ;GAEN,eAAe,aAAa;EAC9B;OACK,IAAI,cAAc,WAAW;GAIlC,MAAM,SAAS,OAAO,KAAK;GAC3B,eAAe,aACb,OAAO,SAAS,MAAM,KAAK,OAAO,UAAU,MAAM,IAC9C,cAAc,OAAO,iBAAiB,WAAW,IACjD;EACR,OAAO,IAAI,cAAc,UAAU,cAAc,WAAW;GAE1D,MAAM,SAAS,OAAO,WAAW,KAAK;GACtC,eAAe,aAAa,OAAO,MAAM,MAAM,IAAI,QAAQ;EAC7D,OACE,eAAe,aAAa;OAEzB,IAAI,OAAO,UAAU,UAC1B,IAAI,cAAc,WAEhB,eAAe,aAAa,UAAU;OACjC,IAAI,cAAc,aAAa,OAAO,UAAU,KAAK,GAC1D,eAAe,aAAa,cAC1B,OACA,iBAAiB,WACnB;OAKA,eAAe,aAAa;OAEzB,IAAI,OAAO,UAAU,YAAY,cAAc,WACpD,eAAe,aAAa,cAC1B,OACA,iBAAiB,WACnB;OAEA,eAAe,aAAa;CAEhC;CAEA,IAAI,QAAQ,IAAI,WACd,OAAO,MAAM,wCAAwC;EACnD,MAAM,OAAO,KAAK,cAAc;EAChC,UAAU,eAAe;CAC3B,CAAC;CAMH,OAAO;AACT;;;;;;;;AASA,SAAgB,gBAAgB,OAAgC;CAC9D,OAAO;AACT;;;;;;;;AASA,SAAgB,cAAc,MAA+B;CAC3D,MAAM,iBAA0C,CAAC;CACjD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,IAAI,GAAG;EAE/C,MAAM,WAAW,YAAY,GAAG;EAGhC,IAAI,iBAAiB,MACnB,eAAe,YAAY,MAAM,YAAY;OAE7C,eAAe,YAAY;CAE/B;CACA,OAAO;AACT"}
@@ -0,0 +1,5 @@
1
+ import { SvelteKitOptions } from './sveltekit-generator.js';
2
+ /** Whether `@happyvertical/smrt-dev-mcp/dev-plane` resolves from the consumer. */
3
+ export declare function consumerHasSmrtDevMcp(projectRoot: string): boolean;
4
+ export declare function generateDevPlaneRoute(projectRoot: string, options: SvelteKitOptions): boolean;
5
+ //# sourceMappingURL=dev-plane-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-plane-route.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/dev-plane-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAOH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAEjE,kFAAkF;AAClF,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAUlE;AAED,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CA8BT"}
@@ -0,0 +1,91 @@
1
+ import { AUTO_GENERATED_ROUTE_HEADER } from "./route-header.js";
2
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
3
+ import { createRequire } from "node:module";
4
+ import { join } from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+ //#region src/vite-plugin/dev-plane-route.ts
7
+ /**
8
+ * Generated `_dev/[...tool]/+server.ts` route: the in-app runtime dev-plane
9
+ * (#2782). Opt-in via `sveltekit: { devPlaneRoute: { enabled: true } }`.
10
+ *
11
+ * The route is dev-only twice over: it 404s unless SvelteKit's `dev` flag is
12
+ * set, and it refuses to start without `SMRT_DEV_MCP_TOKEN`. Everything else
13
+ * — loopback checks, bearer auth, the positive read-only catalog, the MCP
14
+ * endpoint — lives in `@happyvertical/smrt-dev-mcp/dev-plane`, which the
15
+ * generated file imports at consumer runtime. Like `_resources`, the route is
16
+ * only emitted when that subpath resolves from the consumer.
17
+ */
18
+ /** Whether `@happyvertical/smrt-dev-mcp/dev-plane` resolves from the consumer. */
19
+ function consumerHasSmrtDevMcp(projectRoot) {
20
+ try {
21
+ createRequire(pathToFileURL(join(projectRoot, "package.json")).href).resolve("@happyvertical/smrt-dev-mcp/dev-plane");
22
+ return true;
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
27
+ function generateDevPlaneRoute(projectRoot, options) {
28
+ if (options.devPlaneRoute?.enabled !== true) return false;
29
+ if (!consumerHasSmrtDevMcp(projectRoot)) {
30
+ console.log("[smrt] Skipping _dev route - @happyvertical/smrt-dev-mcp/dev-plane is not resolvable; install @happyvertical/smrt-dev-mcp as a devDependency");
31
+ return false;
32
+ }
33
+ const configPath = options.configPath || "src/lib/server";
34
+ const configFileName = (options.configFileName || "smrt.ts").replace(/\.ts$/, "");
35
+ const routeDir = join(projectRoot, options.routesDir, "_dev", "[...tool]");
36
+ if (!existsSync(routeDir)) mkdirSync(routeDir, { recursive: true });
37
+ const filePath = join(routeDir, "+server.ts");
38
+ writeFileSync(filePath, generateDevPlaneRouteTemplate(`$lib/${configPath.replace(/^src\/lib\//, "")}/${configFileName}`), "utf-8");
39
+ console.log(`[smrt] Generated: ${filePath}`);
40
+ return true;
41
+ }
42
+ function generateDevPlaneRouteTemplate(configImport) {
43
+ return `${AUTO_GENERATED_ROUTE_HEADER}
44
+ // DO NOT EDIT - changes will be overwritten
45
+ //
46
+ // In-app runtime dev-plane (#2782). Dev mode only. Serves the read-only
47
+ // runtime tools over JSON (GET|POST /_dev/<tool>, GET /_dev for the catalog)
48
+ // and MCP (POST /_dev/mcp) against this app's live registry and database.
49
+ // Requires SMRT_DEV_MCP_TOKEN; every request must present it as a bearer
50
+ // token from loopback. Never mounted in production builds.
51
+ import { dev } from '$app/environment';
52
+ import { error } from '@sveltejs/kit';
53
+ import type { RequestHandler } from './$types';
54
+ import { createDevPlane, type DevPlane } from '@happyvertical/smrt-dev-mcp/dev-plane';
55
+ import { getSmrtConfig } from '${configImport}';
56
+
57
+ let plane: DevPlane | null = null;
58
+
59
+ function getPlane(): DevPlane | null {
60
+ if (!dev) return null;
61
+ const token = process.env.SMRT_DEV_MCP_TOKEN?.trim();
62
+ if (!token) return null;
63
+ if (!plane) {
64
+ // The default config's db is what the app itself opens; passing its
65
+ // url/type makes the SDK connection cache hand the plane the same handle.
66
+ const db = getSmrtConfig('__smrt_dev_plane__').db as
67
+ | { url?: string; type?: string }
68
+ | undefined;
69
+ plane = createDevPlane({ token, projectRoot: process.cwd(), db });
70
+ }
71
+ return plane;
72
+ }
73
+
74
+ const handle: RequestHandler = async ({ request, url }) => {
75
+ if (!dev) throw error(404, 'Not found');
76
+ const active = getPlane();
77
+ if (!active) {
78
+ throw error(503, 'SMRT_DEV_MCP_TOKEN is not set; the dev-plane is disabled');
79
+ }
80
+ const base = url.pathname.slice(0, url.pathname.indexOf('/_dev') + '/_dev'.length);
81
+ return active.handleRequest(request, base);
82
+ };
83
+
84
+ export const GET = handle;
85
+ export const POST = handle;
86
+ `;
87
+ }
88
+ //#endregion
89
+ export { consumerHasSmrtDevMcp, generateDevPlaneRoute };
90
+
91
+ //# sourceMappingURL=dev-plane-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-plane-route.js","names":[],"sources":["../../src/vite-plugin/dev-plane-route.ts"],"sourcesContent":["/**\n * Generated `_dev/[...tool]/+server.ts` route: the in-app runtime dev-plane\n * (#2782). Opt-in via `sveltekit: { devPlaneRoute: { enabled: true } }`.\n *\n * The route is dev-only twice over: it 404s unless SvelteKit's `dev` flag is\n * set, and it refuses to start without `SMRT_DEV_MCP_TOKEN`. Everything else\n * — loopback checks, bearer auth, the positive read-only catalog, the MCP\n * endpoint — lives in `@happyvertical/smrt-dev-mcp/dev-plane`, which the\n * generated file imports at consumer runtime. Like `_resources`, the route is\n * only emitted when that subpath resolves from the consumer.\n */\n\nimport { existsSync, mkdirSync, writeFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { AUTO_GENERATED_ROUTE_HEADER } from './route-header.js';\nimport type { SvelteKitOptions } from './sveltekit-generator.js';\n\n/** Whether `@happyvertical/smrt-dev-mcp/dev-plane` resolves from the consumer. */\nexport function consumerHasSmrtDevMcp(projectRoot: string): boolean {\n try {\n const consumerRequire = createRequire(\n pathToFileURL(join(projectRoot, 'package.json')).href,\n );\n consumerRequire.resolve('@happyvertical/smrt-dev-mcp/dev-plane');\n return true;\n } catch {\n return false;\n }\n}\n\nexport function generateDevPlaneRoute(\n projectRoot: string,\n options: SvelteKitOptions,\n): boolean {\n if (options.devPlaneRoute?.enabled !== true) {\n return false;\n }\n if (!consumerHasSmrtDevMcp(projectRoot)) {\n console.log(\n '[smrt] Skipping _dev route - @happyvertical/smrt-dev-mcp/dev-plane is ' +\n 'not resolvable; install @happyvertical/smrt-dev-mcp as a devDependency',\n );\n return false;\n }\n const configPath = options.configPath || 'src/lib/server';\n const configFileName = (options.configFileName || 'smrt.ts').replace(\n /\\.ts$/,\n '',\n );\n const routeDir = join(projectRoot, options.routesDir, '_dev', '[...tool]');\n if (!existsSync(routeDir)) {\n mkdirSync(routeDir, { recursive: true });\n }\n const filePath = join(routeDir, '+server.ts');\n writeFileSync(\n filePath,\n generateDevPlaneRouteTemplate(\n `$lib/${configPath.replace(/^src\\/lib\\//, '')}/${configFileName}`,\n ),\n 'utf-8',\n );\n console.log(`[smrt] Generated: ${filePath}`);\n return true;\n}\n\nfunction generateDevPlaneRouteTemplate(configImport: string): string {\n return `${AUTO_GENERATED_ROUTE_HEADER}\n// DO NOT EDIT - changes will be overwritten\n//\n// In-app runtime dev-plane (#2782). Dev mode only. Serves the read-only\n// runtime tools over JSON (GET|POST /_dev/<tool>, GET /_dev for the catalog)\n// and MCP (POST /_dev/mcp) against this app's live registry and database.\n// Requires SMRT_DEV_MCP_TOKEN; every request must present it as a bearer\n// token from loopback. Never mounted in production builds.\nimport { dev } from '$app/environment';\nimport { error } from '@sveltejs/kit';\nimport type { RequestHandler } from './$types';\nimport { createDevPlane, type DevPlane } from '@happyvertical/smrt-dev-mcp/dev-plane';\nimport { getSmrtConfig } from '${configImport}';\n\nlet plane: DevPlane | null = null;\n\nfunction getPlane(): DevPlane | null {\n if (!dev) return null;\n const token = process.env.SMRT_DEV_MCP_TOKEN?.trim();\n if (!token) return null;\n if (!plane) {\n // The default config's db is what the app itself opens; passing its\n // url/type makes the SDK connection cache hand the plane the same handle.\n const db = getSmrtConfig('__smrt_dev_plane__').db as\n | { url?: string; type?: string }\n | undefined;\n plane = createDevPlane({ token, projectRoot: process.cwd(), db });\n }\n return plane;\n}\n\nconst handle: RequestHandler = async ({ request, url }) => {\n if (!dev) throw error(404, 'Not found');\n const active = getPlane();\n if (!active) {\n throw error(503, 'SMRT_DEV_MCP_TOKEN is not set; the dev-plane is disabled');\n }\n const base = url.pathname.slice(0, url.pathname.indexOf('/_dev') + '/_dev'.length);\n return active.handleRequest(request, base);\n};\n\nexport const GET = handle;\nexport const POST = handle;\n`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAoBA,SAAgB,sBAAsB,aAA8B;CAClE,IAAI;EAIF,cAFE,cAAc,KAAK,aAAa,cAAc,CAAC,CAAC,CAAC,IAEnD,CAAA,CAAgB,QAAQ,uCAAuC;EAC/D,OAAO;CACT,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,sBACd,aACA,SACS;CACT,IAAI,QAAQ,eAAe,YAAY,MACrC,OAAO;CAET,IAAI,CAAC,sBAAsB,WAAW,GAAG;EACvC,QAAQ,IACN,8IAEF;EACA,OAAO;CACT;CACA,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,kBAAkB,QAAQ,kBAAkB,UAAA,CAAW,QAC3D,SACA,EACF;CACA,MAAM,WAAW,KAAK,aAAa,QAAQ,WAAW,QAAQ,WAAW;CACzE,IAAI,CAAC,WAAW,QAAQ,GACtB,UAAU,UAAU,EAAE,WAAW,KAAK,CAAC;CAEzC,MAAM,WAAW,KAAK,UAAU,YAAY;CAC5C,cACE,UACA,8BACE,QAAQ,WAAW,QAAQ,eAAe,EAAE,EAAE,GAAG,gBACnD,GACA,OACF;CACA,QAAQ,IAAI,qBAAqB,UAAU;CAC3C,OAAO;AACT;AAEA,SAAS,8BAA8B,cAA8B;CACnE,OAAO,GAAG,4BAA4B;;;;;;;;;;;;iCAYP,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgC9C"}
@@ -23,6 +23,14 @@ export interface SvelteKitOptions {
23
23
  changesRoute?: {
24
24
  enabled?: boolean;
25
25
  };
26
+ /**
27
+ * Opt-in in-app runtime dev-plane route (`_dev/[...tool]`, #2782). Dev
28
+ * mode only; requires `@happyvertical/smrt-dev-mcp` in the consumer and
29
+ * `SMRT_DEV_MCP_TOKEN` at runtime.
30
+ */
31
+ devPlaneRoute?: {
32
+ enabled?: boolean;
33
+ };
26
34
  /**
27
35
  * Live `_events` SSE route generation (#1763). Enabled by default (the route
28
36
  * is auth-guarded fail-closed, same-origin only); set `enabled: false` to
@@ -1 +1 @@
1
- {"version":3,"file":"sveltekit-generator.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/sveltekit-generator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sveltekit-generator.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/sveltekit-generator.ts"],"names":[],"mappings":"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"}
@@ -4,6 +4,7 @@ import { isFrameworkBaseClass } from "../registry/framework-base-classes.js";
4
4
  import { generateConditionalGetRouteHelper } from "../generators/conditional-get.js";
5
5
  import { AUTO_GENERATED_ROUTE_HEADER } from "./route-header.js";
6
6
  import { generateChangesRoute } from "./changes-route.js";
7
+ import { generateDevPlaneRoute } from "./dev-plane-route.js";
7
8
  import { generateEventsRoute } from "./events-route.js";
8
9
  import { generateResourcesRoute } from "./resources-route.js";
9
10
  import { generateSyncApplyRoute } from "./sync-apply-route.js";
@@ -784,6 +785,7 @@ async function generateSvelteKitRoutes(projectRoot, manifest, options) {
784
785
  if (options.knowledge?.api?.enabled) generatedRoutePaths.push(generateKnowledgeRoute(projectRoot, options));
785
786
  if (generateSyncApplyRoute(projectRoot, manifest, options)) generatedRoutePaths.push(join(projectRoot, options.routesDir, "sync", "apply", "+server.ts"));
786
787
  if (generateChangesRoute(projectRoot, manifest, options)) generatedRoutePaths.push(join(projectRoot, options.routesDir, "_changes", "+server.ts"));
788
+ if (generateDevPlaneRoute(projectRoot, options)) generatedRoutePaths.push(join(projectRoot, options.routesDir, "_dev", "[...tool]", "+server.ts"));
787
789
  if (generateEventsRoute(projectRoot, manifest, options, computeWebManifestHash(manifest))) generatedRoutePaths.push(join(projectRoot, options.routesDir, "_events", "+server.ts"));
788
790
  if (generateResourcesRoute(projectRoot, options)) generatedRoutePaths.push(join(projectRoot, options.routesDir, "_resources", "+server.ts"));
789
791
  updateGitignore(projectRoot, generatedRoutePaths);