@happyvertical/smrt-core 0.40.70 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -2
- package/agents/change-feed.md +1 -1
- package/agents/schema-paths.md +487 -29
- package/dist/browser.js +5 -4
- package/dist/cascade.d.ts +120 -0
- package/dist/cascade.d.ts.map +1 -0
- package/dist/cascade.js +430 -0
- package/dist/cascade.js.map +1 -0
- package/dist/change-feed.d.ts +34 -2
- package/dist/change-feed.d.ts.map +1 -1
- package/dist/change-feed.js +52 -11
- package/dist/change-feed.js.map +1 -1
- package/dist/class.d.ts +36 -3
- package/dist/class.d.ts.map +1 -1
- package/dist/class.js +59 -5
- package/dist/class.js.map +1 -1
- package/dist/collection-cache.js +0 -0
- package/dist/collection-cache.js.map +1 -1
- package/dist/collection.d.ts +29 -0
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +66 -14
- package/dist/collection.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/decorators/index.d.ts +63 -5
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js +37 -5
- package/dist/decorators/index.js.map +1 -1
- package/dist/dispatch/collections/Dispatches.d.ts.map +1 -1
- package/dist/dispatch/collections/Dispatches.js +19 -4
- package/dist/dispatch/collections/Dispatches.js.map +1 -1
- package/dist/dispatch/types.d.ts +5 -0
- package/dist/dispatch/types.d.ts.map +1 -1
- package/dist/embedded-write-queue.d.ts +46 -0
- package/dist/embedded-write-queue.d.ts.map +1 -0
- package/dist/embedded-write-queue.js +66 -0
- package/dist/embedded-write-queue.js.map +1 -0
- package/dist/embeddings/storage.d.ts +7 -0
- package/dist/embeddings/storage.d.ts.map +1 -1
- package/dist/embeddings/storage.js +29 -12
- package/dist/embeddings/storage.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/hierarchical.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -6
- package/dist/interceptors.d.ts +21 -0
- package/dist/interceptors.d.ts.map +1 -1
- package/dist/interceptors.js +27 -1
- package/dist/interceptors.js.map +1 -1
- package/dist/manifest/generator.d.ts.map +1 -1
- package/dist/manifest/generator.js +4 -7
- package/dist/manifest/generator.js.map +1 -1
- package/dist/manifest/static-manifest.js +10 -10
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest/store.js.map +1 -1
- package/dist/manifest.json +19 -19
- package/dist/migrations/differ.d.ts +9 -1
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +10 -5
- package/dist/migrations/differ.js.map +1 -1
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +2 -1
- package/dist/migrations/minor-units.d.ts +162 -0
- package/dist/migrations/minor-units.d.ts.map +1 -0
- package/dist/migrations/minor-units.js +381 -0
- package/dist/migrations/minor-units.js.map +1 -0
- package/dist/migrations.js +2 -1
- package/dist/object.d.ts +98 -5
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +138 -30
- package/dist/object.js.map +1 -1
- package/dist/query-bounds.d.ts +5 -1
- package/dist/query-bounds.d.ts.map +1 -1
- package/dist/query-bounds.js +5 -1
- package/dist/query-bounds.js.map +1 -1
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +3 -1
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/manifest-field-merge.d.ts +12 -0
- package/dist/registry/manifest-field-merge.d.ts.map +1 -1
- package/dist/registry/manifest-field-merge.js +14 -2
- package/dist/registry/manifest-field-merge.js.map +1 -1
- package/dist/registry/schema-builder.d.ts +22 -1
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js +205 -165
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/registry/types.d.ts +3 -2
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry.d.ts +41 -46
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +61 -83
- package/dist/registry.js.map +1 -1
- package/dist/scanner/manifest-generator.d.ts +45 -0
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +92 -28
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/schema/conflict-target.d.ts +104 -0
- package/dist/schema/conflict-target.d.ts.map +1 -0
- package/dist/schema/conflict-target.js +129 -0
- package/dist/schema/conflict-target.js.map +1 -0
- package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/duckdb-strategy.js +2 -1
- package/dist/schema/ddl/duckdb-strategy.js.map +1 -1
- package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/postgres-strategy.js +12 -1
- package/dist/schema/ddl/postgres-strategy.js.map +1 -1
- package/dist/schema/generator.d.ts +252 -16
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +411 -106
- package/dist/schema/generator.js.map +1 -1
- package/dist/schema/index-utils.d.ts +120 -0
- package/dist/schema/index-utils.d.ts.map +1 -1
- package/dist/schema/index-utils.js +242 -1
- package/dist/schema/index-utils.js.map +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +2 -2
- package/dist/schema/live-parity.d.ts.map +1 -1
- package/dist/schema/live-parity.js +4 -2
- package/dist/schema/live-parity.js.map +1 -1
- package/dist/schema/system-table-shapes.d.ts +13 -0
- package/dist/schema/system-table-shapes.d.ts.map +1 -1
- package/dist/schema/system-table-shapes.js +14 -1
- package/dist/schema/system-table-shapes.js.map +1 -1
- package/dist/schema/utils.d.ts +1 -1
- package/dist/schema/utils.d.ts.map +1 -1
- package/dist/schema/utils.js +3 -3
- package/dist/schema/utils.js.map +1 -1
- package/dist/schema.js +2 -2
- package/dist/smrt-knowledge.json +9 -9
- package/dist/system/compatibility.d.ts +42 -0
- package/dist/system/compatibility.d.ts.map +1 -1
- package/dist/system/compatibility.js +182 -9
- package/dist/system/compatibility.js.map +1 -1
- package/dist/system/index.d.ts +1 -0
- package/dist/system/index.d.ts.map +1 -1
- package/dist/system/index.js +3 -2
- package/dist/system/retention.d.ts +237 -0
- package/dist/system/retention.d.ts.map +1 -0
- package/dist/system/retention.js +497 -0
- package/dist/system/retention.js.map +1 -0
- package/dist/system/schema.d.ts +100 -15
- package/dist/system/schema.d.ts.map +1 -1
- package/dist/system/schema.js +81 -45
- package/dist/system/schema.js.map +1 -1
- package/dist/system/types.d.ts +0 -2
- package/dist/system/types.d.ts.map +1 -1
- package/dist/testing/database.d.ts.map +1 -1
- package/dist/testing/database.js.map +1 -1
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +4 -9
- package/dist/vite-plugin/index.js.map +1 -1
- package/package.json +4 -4
package/dist/interceptors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"interceptors.js","names":[],"sources":["../src/interceptors.ts"],"sourcesContent":["/**\n * Global Interceptors System for SMRT Collections and Objects\n *\n * Provides a plugin architecture for intercepting collection and object operations.\n * This enables external packages (like @happyvertical/smrt-tenancy) to register\n * interceptors without creating circular dependencies.\n *\n * Pattern: Static methods on GlobalInterceptors class with globalThis state\n * (matches ObjectRegistry pattern for cross-module state sharing)\n *\n * @example Basic interceptor registration\n * ```typescript\n * import { GlobalInterceptors } from '@happyvertical/smrt-core';\n *\n * // Register a beforeList interceptor\n * GlobalInterceptors.register({\n * beforeList(className, options) {\n * console.log(`Listing ${className}`);\n * return options; // Return modified options\n * }\n * });\n * ```\n *\n * @example Tenancy interceptor (from smrt-tenancy)\n * ```typescript\n * GlobalInterceptors.register({\n * beforeList(className, options, context) {\n * const tenantId = getCurrentTenantId();\n * if (tenantId && isTenantScoped(className)) {\n * return {\n * ...options,\n * where: { ...options.where, tenantId }\n * };\n * }\n * return options;\n * },\n * beforeSave(instance, context) {\n * // Validate tenant on save\n * }\n * });\n * ```\n *\n * @see https://github.com/happyvertical/smrt/issues/675\n */\n\nimport type { SmrtObject } from './object.js';\n\n/**\n * Context passed to interceptors with operation metadata\n */\nexport interface InterceptorContext {\n /** Name of the SMRT class being operated on */\n className: string;\n /** Name of the collection (if applicable) */\n collectionName?: string;\n /** Timestamp of the operation */\n timestamp: Date;\n /** Operation type for debugging */\n operation: 'list' | 'get' | 'query' | 'save' | 'delete';\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Options for list operations\n */\nexport interface ListOptions {\n where?: Record<string, unknown>;\n orderBy?: string | string[];\n limit?: number;\n offset?: number;\n select?: readonly string[];\n include?: string[];\n [key: string]: unknown;\n}\n\n/**\n * Options for query operations (raw SQL)\n */\nexport interface QueryOptions {\n /** The raw SQL query */\n sql: string;\n /** Query parameters */\n params: unknown[];\n /** If true, allow raw SQL on tenant-scoped classes (requires explicit opt-in) */\n allowRawOnTenantScoped?: boolean;\n}\n\n/**\n * Result from query interceptor - can modify SQL or throw to block\n */\nexport interface QueryInterceptResult {\n sql: string;\n params: unknown[];\n}\n\n/**\n * Interface for collection/object interceptors\n *\n * All methods are optional. Return void to pass through unchanged,\n * or return modified data to transform the operation.\n */\nexport interface CollectionInterceptor {\n /** Unique identifier for this interceptor (for debugging/unregistration) */\n name?: string;\n\n /** Priority for execution order (higher = earlier). Default: 0 */\n priority?: number;\n\n /**\n * Called before list() operations\n * @param className - Name of the class being listed\n * @param options - List options (where, orderBy, limit, select, etc.)\n * @param context - Operation context\n * @returns Modified options or void to pass through\n */\n beforeList?(\n className: string,\n options: ListOptions,\n context: InterceptorContext,\n ): ListOptions | Promise<ListOptions> | void | Promise<void>;\n\n /**\n * Called after list() operations with results\n * @param className - Name of the class\n * @param results - Array of hydrated instances\n * @param context - Operation context\n * @returns Modified results or void to pass through\n */\n afterList?<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): T[] | Promise<T[]> | void | Promise<void>;\n\n /**\n * Called before get() operations\n * @param className - Name of the class\n * @param filter - ID, slug, or filter object\n * @param context - Operation context\n * @returns Modified filter or void to pass through\n */\n beforeGet?(\n className: string,\n filter: string | Record<string, unknown>,\n context: InterceptorContext,\n ):\n | string\n | Record<string, unknown>\n | Promise<string | Record<string, unknown>>\n | void\n | Promise<void>;\n\n /**\n * Called after get() operations with result\n * @param className - Name of the class\n * @param instance - Hydrated instance or null if not found\n * @param context - Operation context\n * @returns Modified instance, null, or void to pass through\n */\n afterGet?<T extends SmrtObject>(\n className: string,\n instance: T | null,\n context: InterceptorContext,\n ): T | null | Promise<T | null> | void | Promise<void>;\n\n /**\n * Called before query() operations (raw SQL)\n * @param className - Name of the class\n * @param options - Query options with SQL and params\n * @param context - Operation context\n * @returns Modified query options, or throw to block the query\n */\n beforeQuery?(\n className: string,\n options: QueryOptions,\n context: InterceptorContext,\n ):\n | QueryInterceptResult\n | Promise<QueryInterceptResult>\n | void\n | Promise<void>;\n\n /**\n * Called after query() operations with results\n * @param className - Name of the class\n * @param results - Array of hydrated instances\n * @param context - Operation context\n * @returns Modified results or void to pass through\n */\n afterQuery?<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): T[] | Promise<T[]> | void | Promise<void>;\n\n /**\n * Called before save() operations\n * @param instance - The object being saved\n * @param context - Operation context\n * @returns void (modify instance in place) or throw to block\n */\n beforeSave?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n\n /**\n * Called after save() operations\n * @param instance - The saved object\n * @param context - Operation context\n */\n afterSave?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n\n /**\n * Called before delete() operations\n * @param instance - The object being deleted\n * @param context - Operation context\n * @returns void or throw to block\n */\n beforeDelete?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n\n /**\n * Called after delete() operations\n * @param instance - The deleted object\n * @param context - Operation context\n */\n afterDelete?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n}\n\n/**\n * Extend globalThis to include interceptors state.\n * Using globalThis ensures all module instances share the same interceptors,\n * which is critical in monorepos where the same package can be loaded\n * from different paths.\n *\n * @see ObjectRegistry for similar pattern\n */\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtInterceptors: unknown[] | undefined;\n}\n\n/**\n * Global Interceptors Registry\n *\n * Manages collection/object interceptors for the SMRT framework.\n * External packages can register interceptors to hook into operations\n * without modifying core code.\n */\nexport class GlobalInterceptors {\n /**\n * Get the global interceptors array (lazy-initialized)\n */\n private static get interceptors(): CollectionInterceptor[] {\n if (!globalThis.__smrtInterceptors) {\n globalThis.__smrtInterceptors = [];\n }\n return globalThis.__smrtInterceptors as CollectionInterceptor[];\n }\n\n /**\n * Register an interceptor\n *\n * @param interceptor - The interceptor to register\n * @example\n * ```typescript\n * GlobalInterceptors.register({\n * name: 'tenancy',\n * priority: 100,\n * beforeList(className, options) {\n * // Add tenant filtering\n * return { ...options, where: { ...options.where, tenantId: 'xxx' } };\n * }\n * });\n * ```\n */\n static register(interceptor: CollectionInterceptor): void {\n // Check for duplicate by name\n if (interceptor.name) {\n const existing = this.interceptors.findIndex(\n (i) => i.name === interceptor.name,\n );\n if (existing >= 0) {\n // Replace existing interceptor with same name\n this.interceptors[existing] = interceptor;\n this.sortByPriority();\n return;\n }\n }\n\n this.interceptors.push(interceptor);\n this.sortByPriority();\n }\n\n /**\n * Unregister an interceptor by reference or name\n *\n * @param interceptorOrName - The interceptor instance or name to remove\n * @returns true if removed, false if not found\n */\n static unregister(\n interceptorOrName: CollectionInterceptor | string,\n ): boolean {\n const idx =\n typeof interceptorOrName === 'string'\n ? this.interceptors.findIndex((i) => i.name === interceptorOrName)\n : this.interceptors.indexOf(interceptorOrName);\n\n if (idx >= 0) {\n this.interceptors.splice(idx, 1);\n return true;\n }\n return false;\n }\n\n /**\n * Clear all interceptors (useful for testing)\n */\n static clear(): void {\n globalThis.__smrtInterceptors = [];\n }\n\n /**\n * Get all registered interceptors (readonly)\n */\n static getAll(): readonly CollectionInterceptor[] {\n return [...this.interceptors];\n }\n\n /**\n * Check if any interceptors are registered\n */\n static hasInterceptors(): boolean {\n return this.interceptors.length > 0;\n }\n\n /**\n * Sort interceptors by priority (higher priority first)\n */\n private static sortByPriority(): void {\n this.interceptors.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Execution methods - called by SmrtCollection and SmrtObject\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Execute beforeList interceptors\n * @internal Called by SmrtCollection.list()\n */\n static async executeBeforeList(\n className: string,\n options: ListOptions,\n context: InterceptorContext,\n ): Promise<ListOptions> {\n let result = options;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeList) {\n const modified = await interceptor.beforeList(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute afterList interceptors\n * @internal Called by SmrtCollection.list()\n */\n static async executeAfterList<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): Promise<T[]> {\n let result = results;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.afterList) {\n const modified = await interceptor.afterList(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified as T[];\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute beforeGet interceptors\n * @internal Called by SmrtCollection.get()\n */\n static async executeBeforeGet(\n className: string,\n filter: string | Record<string, unknown>,\n context: InterceptorContext,\n ): Promise<string | Record<string, unknown>> {\n let result = filter;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeGet) {\n const modified = await interceptor.beforeGet(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute afterGet interceptors\n * @internal Called by SmrtCollection.get()\n */\n static async executeAfterGet<T extends SmrtObject>(\n className: string,\n instance: T | null,\n context: InterceptorContext,\n ): Promise<T | null> {\n let result = instance;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.afterGet) {\n const modified = await interceptor.afterGet(className, result, context);\n if (modified !== undefined) {\n result = modified as T | null;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute beforeQuery interceptors\n * @internal Called by SmrtCollection.query()\n */\n static async executeBeforeQuery(\n className: string,\n options: QueryOptions,\n context: InterceptorContext,\n ): Promise<QueryInterceptResult> {\n let result: QueryInterceptResult = {\n sql: options.sql,\n params: options.params,\n };\n\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeQuery) {\n const modified = await interceptor.beforeQuery(\n className,\n { ...options, ...result },\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute afterQuery interceptors\n * @internal Called by SmrtCollection.query()\n */\n static async executeAfterQuery<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): Promise<T[]> {\n let result = results;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.afterQuery) {\n const modified = await interceptor.afterQuery(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified as T[];\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute beforeSave interceptors\n * @internal Called by SmrtObject.save()\n */\n static async executeBeforeSave(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeSave) {\n await interceptor.beforeSave(instance, context);\n }\n }\n }\n\n /**\n * Execute afterSave interceptors\n * @internal Called by SmrtObject.save()\n */\n static async executeAfterSave(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.afterSave) {\n await interceptor.afterSave(instance, context);\n }\n }\n }\n\n /**\n * Execute beforeDelete interceptors\n * @internal Called by SmrtObject.delete()\n */\n static async executeBeforeDelete(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeDelete) {\n await interceptor.beforeDelete(instance, context);\n }\n }\n }\n\n /**\n * Execute afterDelete interceptors\n * @internal Called by SmrtObject.delete()\n */\n static async executeAfterDelete(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.afterDelete) {\n await interceptor.afterDelete(instance, context);\n }\n }\n }\n}\n\n/**\n * Helper function to create an interceptor context\n * @internal\n */\nexport function createInterceptorContext(\n className: string,\n operation: InterceptorContext['operation'],\n collectionName?: string,\n metadata?: Record<string, unknown>,\n): InterceptorContext {\n return {\n className,\n collectionName,\n operation,\n timestamp: new Date(),\n metadata,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"interceptors.js","names":[],"sources":["../src/interceptors.ts"],"sourcesContent":["/**\n * Global Interceptors System for SMRT Collections and Objects\n *\n * Provides a plugin architecture for intercepting collection and object operations.\n * This enables external packages (like @happyvertical/smrt-tenancy) to register\n * interceptors without creating circular dependencies.\n *\n * Pattern: Static methods on GlobalInterceptors class with globalThis state\n * (matches ObjectRegistry pattern for cross-module state sharing)\n *\n * @example Basic interceptor registration\n * ```typescript\n * import { GlobalInterceptors } from '@happyvertical/smrt-core';\n *\n * // Register a beforeList interceptor\n * GlobalInterceptors.register({\n * beforeList(className, options) {\n * console.log(`Listing ${className}`);\n * return options; // Return modified options\n * }\n * });\n * ```\n *\n * @example Tenancy interceptor (from smrt-tenancy)\n * ```typescript\n * GlobalInterceptors.register({\n * beforeList(className, options, context) {\n * const tenantId = getCurrentTenantId();\n * if (tenantId && isTenantScoped(className)) {\n * return {\n * ...options,\n * where: { ...options.where, tenantId }\n * };\n * }\n * return options;\n * },\n * beforeSave(instance, context) {\n * // Validate tenant on save\n * }\n * });\n * ```\n *\n * @see https://github.com/happyvertical/smrt/issues/675\n */\n\nimport type { SmrtObject } from './object.js';\n\n/**\n * Context passed to interceptors with operation metadata\n */\nexport interface InterceptorContext {\n /** Name of the SMRT class being operated on */\n className: string;\n /** Name of the collection (if applicable) */\n collectionName?: string;\n /** Timestamp of the operation */\n timestamp: Date;\n /** Operation type for debugging */\n operation: 'list' | 'get' | 'query' | 'save' | 'delete';\n /** Additional metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Options for list operations\n */\nexport interface ListOptions {\n where?: Record<string, unknown>;\n orderBy?: string | string[];\n limit?: number;\n offset?: number;\n select?: readonly string[];\n include?: string[];\n [key: string]: unknown;\n}\n\n/**\n * Options for query operations (raw SQL)\n */\nexport interface QueryOptions {\n /** The raw SQL query */\n sql: string;\n /** Query parameters */\n params: unknown[];\n /** If true, allow raw SQL on tenant-scoped classes (requires explicit opt-in) */\n allowRawOnTenantScoped?: boolean;\n}\n\n/**\n * Result from query interceptor - can modify SQL or throw to block\n */\nexport interface QueryInterceptResult {\n sql: string;\n params: unknown[];\n}\n\n/**\n * Interface for collection/object interceptors\n *\n * All methods are optional. Return void to pass through unchanged,\n * or return modified data to transform the operation.\n */\nexport interface CollectionInterceptor {\n /** Unique identifier for this interceptor (for debugging/unregistration) */\n name?: string;\n\n /** Priority for execution order (higher = earlier). Default: 0 */\n priority?: number;\n\n /**\n * Called before list() operations\n * @param className - Name of the class being listed\n * @param options - List options (where, orderBy, limit, select, etc.)\n * @param context - Operation context\n * @returns Modified options or void to pass through\n */\n beforeList?(\n className: string,\n options: ListOptions,\n context: InterceptorContext,\n ): ListOptions | Promise<ListOptions> | void | Promise<void>;\n\n /**\n * Called after list() operations with results\n * @param className - Name of the class\n * @param results - Array of hydrated instances\n * @param context - Operation context\n * @returns Modified results or void to pass through\n */\n afterList?<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): T[] | Promise<T[]> | void | Promise<void>;\n\n /**\n * Called before get() operations\n * @param className - Name of the class\n * @param filter - ID, slug, or filter object\n * @param context - Operation context\n * @returns Modified filter or void to pass through\n */\n beforeGet?(\n className: string,\n filter: string | Record<string, unknown>,\n context: InterceptorContext,\n ):\n | string\n | Record<string, unknown>\n | Promise<string | Record<string, unknown>>\n | void\n | Promise<void>;\n\n /**\n * Called after get() operations with result\n * @param className - Name of the class\n * @param instance - Hydrated instance or null if not found\n * @param context - Operation context\n * @returns Modified instance, null, or void to pass through\n */\n afterGet?<T extends SmrtObject>(\n className: string,\n instance: T | null,\n context: InterceptorContext,\n ): T | null | Promise<T | null> | void | Promise<void>;\n\n /**\n * Called before query() operations (raw SQL)\n * @param className - Name of the class\n * @param options - Query options with SQL and params\n * @param context - Operation context\n * @returns Modified query options, or throw to block the query\n */\n beforeQuery?(\n className: string,\n options: QueryOptions,\n context: InterceptorContext,\n ):\n | QueryInterceptResult\n | Promise<QueryInterceptResult>\n | void\n | Promise<void>;\n\n /**\n * Called after query() operations with results\n * @param className - Name of the class\n * @param results - Array of hydrated instances\n * @param context - Operation context\n * @returns Modified results or void to pass through\n */\n afterQuery?<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): T[] | Promise<T[]> | void | Promise<void>;\n\n /**\n * Called before save() operations\n * @param instance - The object being saved\n * @param context - Operation context\n * @returns void (modify instance in place) or throw to block\n */\n beforeSave?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n\n /**\n * Called after save() operations\n * @param instance - The saved object\n * @param context - Operation context\n */\n afterSave?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n\n /**\n * Called before delete() operations\n * @param instance - The object being deleted\n * @param context - Operation context\n * @returns void or throw to block\n */\n beforeDelete?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n\n /**\n * Called after delete() operations\n * @param instance - The deleted object\n * @param context - Operation context\n */\n afterDelete?(\n instance: SmrtObject,\n context: InterceptorContext,\n ): void | Promise<void>;\n}\n\n/**\n * Extend globalThis to include interceptors state.\n * Using globalThis ensures all module instances share the same interceptors,\n * which is critical in monorepos where the same package can be loaded\n * from different paths.\n *\n * @see ObjectRegistry for similar pattern\n */\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtInterceptors: unknown[] | undefined;\n}\n\n/**\n * Global Interceptors Registry\n *\n * Manages collection/object interceptors for the SMRT framework.\n * External packages can register interceptors to hook into operations\n * without modifying core code.\n */\nexport class GlobalInterceptors {\n /**\n * Get the global interceptors array (lazy-initialized)\n */\n private static get interceptors(): CollectionInterceptor[] {\n if (!globalThis.__smrtInterceptors) {\n globalThis.__smrtInterceptors = [];\n }\n return globalThis.__smrtInterceptors as CollectionInterceptor[];\n }\n\n /**\n * Register an interceptor\n *\n * @param interceptor - The interceptor to register\n * @example\n * ```typescript\n * GlobalInterceptors.register({\n * name: 'tenancy',\n * priority: 100,\n * beforeList(className, options) {\n * // Add tenant filtering\n * return { ...options, where: { ...options.where, tenantId: 'xxx' } };\n * }\n * });\n * ```\n */\n static register(interceptor: CollectionInterceptor): void {\n // Check for duplicate by name\n if (interceptor.name) {\n const existing = this.interceptors.findIndex(\n (i) => i.name === interceptor.name,\n );\n if (existing >= 0) {\n // Replace existing interceptor with same name\n this.interceptors[existing] = interceptor;\n this.sortByPriority();\n return;\n }\n }\n\n this.interceptors.push(interceptor);\n this.sortByPriority();\n }\n\n /**\n * Unregister an interceptor by reference or name\n *\n * @param interceptorOrName - The interceptor instance or name to remove\n * @returns true if removed, false if not found\n */\n static unregister(\n interceptorOrName: CollectionInterceptor | string,\n ): boolean {\n const idx =\n typeof interceptorOrName === 'string'\n ? this.interceptors.findIndex((i) => i.name === interceptorOrName)\n : this.interceptors.indexOf(interceptorOrName);\n\n if (idx >= 0) {\n this.interceptors.splice(idx, 1);\n return true;\n }\n return false;\n }\n\n /**\n * Clear all interceptors (useful for testing)\n */\n static clear(): void {\n globalThis.__smrtInterceptors = [];\n }\n\n /**\n * Get all registered interceptors (readonly)\n */\n static getAll(): readonly CollectionInterceptor[] {\n return [...this.interceptors];\n }\n\n /**\n * Check if any interceptors are registered\n */\n static hasInterceptors(): boolean {\n return this.interceptors.length > 0;\n }\n\n /**\n * Sort interceptors by priority (higher priority first)\n */\n private static sortByPriority(): void {\n this.interceptors.sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Execution methods - called by SmrtCollection and SmrtObject\n // ─────────────────────────────────────────────────────────────────────────\n\n /**\n * Execute beforeList interceptors\n * @internal Called by SmrtCollection.list()\n */\n static async executeBeforeList(\n className: string,\n options: ListOptions,\n context: InterceptorContext,\n ): Promise<ListOptions> {\n let result = options;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeList) {\n const modified = await interceptor.beforeList(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute afterList interceptors\n * @internal Called by SmrtCollection.list()\n */\n static async executeAfterList<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): Promise<T[]> {\n let result = results;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.afterList) {\n const modified = await interceptor.afterList(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified as T[];\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute beforeGet interceptors\n * @internal Called by SmrtCollection.get()\n */\n static async executeBeforeGet(\n className: string,\n filter: string | Record<string, unknown>,\n context: InterceptorContext,\n ): Promise<string | Record<string, unknown>> {\n let result = filter;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeGet) {\n const modified = await interceptor.beforeGet(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute afterGet interceptors\n * @internal Called by SmrtCollection.get()\n */\n static async executeAfterGet<T extends SmrtObject>(\n className: string,\n instance: T | null,\n context: InterceptorContext,\n ): Promise<T | null> {\n let result = instance;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.afterGet) {\n const modified = await interceptor.afterGet(className, result, context);\n if (modified !== undefined) {\n result = modified as T | null;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute beforeQuery interceptors\n * @internal Called by SmrtCollection.query()\n */\n static async executeBeforeQuery(\n className: string,\n options: QueryOptions,\n context: InterceptorContext,\n ): Promise<QueryInterceptResult> {\n let result: QueryInterceptResult = {\n sql: options.sql,\n params: options.params,\n };\n\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeQuery) {\n const modified = await interceptor.beforeQuery(\n className,\n { ...options, ...result },\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified;\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute afterQuery interceptors\n * @internal Called by SmrtCollection.query()\n */\n static async executeAfterQuery<T extends SmrtObject>(\n className: string,\n results: T[],\n context: InterceptorContext,\n ): Promise<T[]> {\n let result = results;\n\n for (const interceptor of this.interceptors) {\n if (interceptor.afterQuery) {\n const modified = await interceptor.afterQuery(\n className,\n result,\n context,\n );\n if (modified !== undefined && modified !== null) {\n result = modified as T[];\n }\n }\n }\n\n return result;\n }\n\n /**\n * Execute beforeSave interceptors\n * @internal Called by SmrtObject.save()\n */\n static async executeBeforeSave(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeSave) {\n await interceptor.beforeSave(instance, context);\n }\n }\n }\n\n /**\n * Execute afterSave interceptors\n * @internal Called by SmrtObject.save()\n */\n static async executeAfterSave(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.afterSave) {\n await interceptor.afterSave(instance, context);\n }\n }\n }\n\n /**\n * Execute beforeDelete interceptors\n * @internal Called by SmrtObject.delete()\n */\n static async executeBeforeDelete(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.beforeDelete) {\n await interceptor.beforeDelete(instance, context);\n }\n }\n }\n\n /**\n * Execute afterDelete interceptors\n * @internal Called by SmrtObject.delete()\n */\n static async executeAfterDelete(\n instance: SmrtObject,\n context: InterceptorContext,\n ): Promise<void> {\n for (const interceptor of this.interceptors) {\n if (interceptor.afterDelete) {\n await interceptor.afterDelete(instance, context);\n }\n }\n }\n}\n\n/**\n * Canonical UUID shape used by `SmrtCollection.get()` to decide whether a\n * string filter is an id or a slug.\n */\nconst GET_STRING_FILTER_UUID_PATTERN =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n\n/**\n * Resolve a string filter to the WHERE shape `SmrtCollection.get()` gives it:\n * a UUID string becomes `{ id }`, anything else becomes the natural-key lookup\n * `{ slug, context: '' }`.\n *\n * This is the single source of truth for that string-filter contract. Any\n * `beforeGet` interceptor that rewrites a string filter into an object filter\n * MUST resolve it through this helper first — the tenancy interceptor used to\n * rewrite every string to `{ id: filter, tenantId }`, which broke get-by-slug\n * under an active tenant context (null on SQLite, a uuid cast error on\n * PostgreSQL native-uuid id columns). See #2365.\n *\n * @param filter - The string filter passed to `get()`\n * @returns The object filter `get()` would build for that string\n */\nexport function resolveGetStringFilter(\n filter: string,\n): { id: string } | { slug: string; context: string } {\n return GET_STRING_FILTER_UUID_PATTERN.test(filter)\n ? { id: filter }\n : { slug: filter, context: '' };\n}\n\n/**\n * Helper function to create an interceptor context\n * @internal\n */\nexport function createInterceptorContext(\n className: string,\n operation: InterceptorContext['operation'],\n collectionName?: string,\n metadata?: Record<string, unknown>,\n): InterceptorContext {\n return {\n className,\n collectionName,\n operation,\n timestamp: new Date(),\n metadata,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/manifest/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAwB/D;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IAErC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAG9B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAG/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAC;CACrC;AAuCD;;;;;GAKG;AACH,qBAAa,eAAe;IAC1B;;OAEG;IACG,QAAQ,CACZ,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,mBAAmB,CAAC;IAkC/B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,iBAAiB;IAmBzB;;OAEG;YACW,aAAa;IAc3B;;OAEG;YACW,gBAAgB;IAoD9B;;;;;OAKG;YACW,oBAAoB;
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/manifest/generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAwB/D;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IAErC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAG9B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAG/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAGlB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IAGpB;;OAEG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IAEpB;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,EAAE,CAAC;CACrC;AAuCD;;;;;GAKG;AACH,qBAAa,eAAe;IAC1B;;OAEG;IACG,QAAQ,CACZ,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,mBAAmB,CAAC;IAkC/B,OAAO,CAAC,kBAAkB;IAa1B,OAAO,CAAC,iBAAiB;IAmBzB;;OAEG;YACW,aAAa;IAc3B;;OAEG;YACW,gBAAgB;IAoD9B;;;;;OAKG;YACW,oBAAoB;IAuElC;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAiCtB;;OAEG;YACW,WAAW;IAyCzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAuCxB;;OAEG;YACW,yBAAyB;IA6EvC;;OAEG;YACW,uBAAuB;IAwCrC;;OAEG;IACH,OAAO,CAAC,eAAe;IAavB;;OAEG;IACH,OAAO,CAAC,mBAAmB;CAuB5B"}
|
|
@@ -149,13 +149,10 @@ var ManifestBuilder = class {
|
|
|
149
149
|
typeAliases: results.typeAliases
|
|
150
150
|
});
|
|
151
151
|
if (config.smrtDependencies && config.smrtDependencies.length > 0) manifest.smrtDependencies = config.smrtDependencies;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
manifestGen.generateSchemas(manifest);
|
|
157
|
-
manifestGen.assertTenantScopedSchemaContract(manifest);
|
|
158
|
-
manifestGen.generateAgentManifests(manifest, packageName, packageJson);
|
|
152
|
+
new ManifestGenerator().applyGenerationPasses(manifest, {
|
|
153
|
+
packageName,
|
|
154
|
+
packageJson
|
|
155
|
+
});
|
|
159
156
|
return manifest;
|
|
160
157
|
}
|
|
161
158
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.js","names":[],"sources":["../../src/manifest/generator.ts"],"sourcesContent":["/**\n * Manifest generation orchestrator\n * Consolidates manifest generation logic for both build and test manifests\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, isAbsolute, relative, resolve } from 'node:path';\nimport { createLogger } from '@happyvertical/logger';\nimport { ManifestGenerator } from '../scanner/manifest-generator.js';\nimport type { SmartObjectManifest } from '../scanner/types.js';\nimport { MANIFEST_TIMESTAMP } from '../scanner/types.js';\nimport { importWorkspaceModule } from '../utils/import-workspace-module.js';\nimport type { ScannerModule } from '../utils/scanner-module.js';\nimport {\n discoverSmrtPackages,\n resolveManifestPath,\n} from './discover-smrt-packages.js';\nimport { ManifestManager } from './manager.js';\n\n// Build-time manifest-generation diagnostics route through the shared logger\n// (S14 / dim-9, #1579) so consumers can control verbosity instead of receiving\n// unconditional stdout writes.\nconst logger = createLogger({ level: 'info' });\n\nasync function importScanner() {\n return importWorkspaceModule<ScannerModule>({\n packageName: '@happyvertical/smrt-scanner',\n distEntry: 'packages/scanner/dist/index.js',\n sourceEntry: 'packages/scanner/src/index.ts',\n purpose: 'manifest generation',\n });\n}\n\n/**\n * Options for ManifestBuilder.generate()\n *\n * Controls file discovery, scanner configuration, output generation, and metadata injection\n */\nexport interface ManifestBuilderOptions {\n // File Discovery\n include?: string[];\n exclude?: string[];\n followImports?: boolean;\n /**\n * Follow symbolic links while discovering sources. Defaults to `false` —\n * see `OxcScannerOptions.followSymbolicLinks` (#2275).\n */\n followSymbolicLinks?: boolean;\n\n // Scanner Configuration\n baseClasses?: string[];\n loadViteConfig?: boolean;\n discoverExternalPackages?: boolean;\n includeExternalBaseClasses?: boolean;\n includePrivateMethods?: boolean;\n includeStaticMethods?: boolean;\n\n // Output Configuration\n outputDir?: string;\n outputName?: string;\n generateTypeStub?: boolean;\n stubName?: string;\n\n // Metadata\n injectPackageInfo?: boolean;\n moduleType?: string;\n\n // Tree Shaking (External Object Filtering)\n /**\n * @deprecated Tree shaking is no longer needed. The AST scanner no longer includes external objects in the manifest by default.\n */\n treeShake?: boolean;\n\n /**\n * @deprecated Whitelists are no longer needed. The AST scanner no longer includes external objects in the manifest by default.\n */\n externalObjectsWhitelist?: string[];\n}\n\ninterface ScannerConfig {\n baseClasses: string[];\n includePrivateMethods: boolean;\n includeStaticMethods: boolean;\n followImports: boolean;\n smrtDependencies?: string[];\n}\n\ninterface PackageInfo {\n name?: string;\n version?: string;\n}\n\n/**\n * Subset of the smrt-auto-service Vite plugin options we probe when reading\n * baseClasses out of a project's vite.config.ts.\n */\ninterface VitePluginOptions {\n baseClasses?: string[];\n followImports?: boolean;\n}\n\n/**\n * Structural shape of a loaded Vite plugin entry as probed by\n * loadViteConfigBaseClasses(). The plugin may expose its options directly, on\n * `_options`, or via an `api` object/function — all optional and discovered at\n * runtime, so each is modeled as optional here.\n */\ninterface VitePluginProbe {\n name?: string;\n options?: VitePluginOptions;\n _options?: VitePluginOptions;\n api?:\n | { options?: VitePluginOptions }\n | (() => { options?: VitePluginOptions });\n}\n\n/**\n * Orchestrates manifest generation with configurable options\n *\n * Consolidates logic from generate-manifest.js and generate-test-manifest.js\n * into a single, testable, and maintainable service.\n */\nexport class ManifestBuilder {\n /**\n * Generate manifest with specified options\n */\n async generate(\n options: ManifestBuilderOptions = {},\n ): Promise<SmartObjectManifest> {\n // 1. Discover files to scan\n const files = await this.discoverFiles(options);\n\n if (files.length === 0) {\n logger.info('[smrt] No source files found');\n return this.createEmptyManifest(options);\n }\n\n logger.info(`[smrt] Scanning ${files.length} source file(s)...`);\n\n // 2. Configure scanner (baseClasses, external packages, vite config)\n const scannerConfig = await this.configureScanner(options);\n\n // 3. Scan files with OXC scanner\n const scannedManifest = await this.scanAndBuildManifest(\n scannerConfig,\n options,\n );\n\n // 4. Enrich manifest with metadata (smrtDependencies, packageInfo)\n const manifest = this.enrichManifest(\n scannedManifest,\n options,\n scannerConfig,\n );\n\n // 5. Write output files\n this.normalizeFilePaths(manifest);\n await this.writeOutput(manifest, options);\n\n return manifest;\n }\n\n private normalizeFilePaths(manifest: SmartObjectManifest): void {\n const workspaceRoot = this.findWorkspaceRoot(process.cwd());\n\n for (const obj of Object.values(manifest.objects || {})) {\n if (obj.filePath && isAbsolute(obj.filePath)) {\n obj.filePath = relative(workspaceRoot, obj.filePath).replace(\n /\\\\/g,\n '/',\n );\n }\n }\n }\n\n private findWorkspaceRoot(startDir: string): string {\n let current = resolve(startDir);\n\n while (true) {\n if (\n existsSync(resolve(current, 'pnpm-workspace.yaml')) ||\n existsSync(resolve(current, '.git'))\n ) {\n return current;\n }\n\n const parent = dirname(current);\n if (parent === current) {\n return resolve(startDir);\n }\n current = parent;\n }\n }\n\n /**\n * Discover source files based on include/exclude patterns\n */\n private async discoverFiles(\n options: ManifestBuilderOptions,\n ): Promise<string[]> {\n const include = options.include || ['src/**/*.ts'];\n const exclude = options.exclude || ['src/**/*.d.ts', 'node_modules/**'];\n const { discoverSourceFiles } = await importScanner();\n return discoverSourceFiles({\n cwd: process.cwd(),\n include,\n exclude,\n followSymbolicLinks: options.followSymbolicLinks ?? false,\n });\n }\n\n /**\n * Configure AST scanner with baseClasses, external packages, and vite config\n */\n private async configureScanner(\n options: ManifestBuilderOptions,\n ): Promise<ScannerConfig> {\n let baseClasses = options.baseClasses || [\n 'SmrtObject',\n 'SmrtClass',\n 'SmrtCollection',\n ];\n\n // Load from vite.config.ts if requested\n if (options.loadViteConfig) {\n const viteBaseClasses = await this.loadViteConfigBaseClasses();\n if (viteBaseClasses && viteBaseClasses.length > 0) {\n baseClasses = viteBaseClasses;\n logger.info(\n `[smrt] Using baseClasses from vite.config.ts: ${baseClasses.join(', ')}`,\n );\n }\n }\n\n // Discover external SMRT packages\n let smrtDependencies: string[] = [];\n if (options.discoverExternalPackages) {\n logger.info('[smrt] Discovering external SMRT packages...');\n smrtDependencies = discoverSmrtPackages();\n logger.info(\n `[smrt] Found ${smrtDependencies.length} SMRT package(s): ${smrtDependencies.join(', ')}`,\n );\n\n // Optionally add external base classes\n if (options.includeExternalBaseClasses) {\n const externalClasses =\n await this.loadExternalBaseClasses(smrtDependencies);\n if (externalClasses.length > 0) {\n baseClasses = [...baseClasses, ...externalClasses];\n logger.info(\n `[smrt] Added ${externalClasses.length} external base class(es) to scanner`,\n );\n logger.info(`[smrt] Total baseClasses: ${baseClasses.length}`);\n }\n }\n }\n\n return {\n baseClasses,\n includePrivateMethods: options.includePrivateMethods ?? false,\n includeStaticMethods: options.includeStaticMethods ?? true,\n followImports: options.followImports ?? false,\n smrtDependencies,\n };\n }\n\n /**\n * Scan files using OxcScanner and build manifest via ManifestAdapter.\n *\n * This replaces the old ASTScanner-based scanFiles() method.\n * Uses the same OXC pipeline as the Vite plugin's scanWithOxc().\n */\n private async scanAndBuildManifest(\n config: ScannerConfig,\n options: ManifestBuilderOptions,\n ): Promise<SmartObjectManifest> {\n const { OxcScanner, ManifestAdapter } = await importScanner();\n\n const scanner = new OxcScanner({\n cwd: process.cwd(),\n include: options.include || ['src/**/*.ts'],\n exclude: options.exclude || ['src/**/*.d.ts', 'node_modules/**'],\n baseClasses: config.baseClasses,\n includePrivateMethods: config.includePrivateMethods,\n includeStaticMethods: config.includeStaticMethods,\n followImports: config.followImports,\n followSymbolicLinks: options.followSymbolicLinks,\n });\n\n const { results, resolved } = await scanner.scanAndResolve();\n const scanErrors = results.errors.filter(\n (diagnostic) => diagnostic.severity === 'error',\n );\n if (scanErrors.length > 0) {\n const detail = scanErrors\n .map((diagnostic) => {\n const where = diagnostic.line\n ? `${diagnostic.filePath}:${diagnostic.line}`\n : diagnostic.filePath;\n return `${where} — ${diagnostic.message}`;\n })\n .join('; ');\n throw new Error(\n `[smrt] Build aborted due to ${scanErrors.length} scan error(s): ${detail}`,\n );\n }\n\n // Read package.json for metadata\n let packageName: string | undefined;\n let packageVersion: string | undefined;\n let packageJson: { name?: string; version?: string } | undefined;\n try {\n const pkgPath = resolve(process.cwd(), 'package.json');\n const pkgContent = readFileSync(pkgPath, 'utf-8');\n packageJson = JSON.parse(pkgContent);\n packageName = packageJson?.name || undefined;\n packageVersion = packageJson?.version || undefined;\n } catch {\n // package.json not found - continue without packageName\n }\n\n // Convert to manifest format\n const adapter = new ManifestAdapter();\n const manifest = adapter.toManifest(resolved, {\n packageName,\n packageVersion,\n typeAliases: results.typeAliases,\n });\n\n // Set smrtDependencies BEFORE mergeInheritedFields so external packages can be loaded\n if (config.smrtDependencies && config.smrtDependencies.length > 0) {\n manifest.smrtDependencies = config.smrtDependencies;\n }\n\n // Run manifest generation passes (same as Vite plugin path)\n const manifestGen = new ManifestGenerator();\n manifestGen.injectTenantScopedFields(manifest);\n manifestGen.mergeInheritedFields(manifest);\n manifestGen.generateValidationRules(manifest);\n manifestGen.generateSchemas(manifest);\n manifestGen.assertTenantScopedSchemaContract(manifest);\n manifestGen.generateAgentManifests(manifest, packageName, packageJson);\n\n return manifest;\n }\n\n /**\n * Enrich manifest with metadata (moduleType, smrtDependencies, packageInfo).\n *\n * Note: As of Issue #1013, this method no longer merges external package\n * objects into the manifest. Dependencies are referenced via\n * `smrtDependencies` and resolved at runtime by manifest-loader.ts.\n */\n private enrichManifest(\n manifest: SmartObjectManifest,\n options: ManifestBuilderOptions,\n scannerConfig: ScannerConfig,\n ): SmartObjectManifest {\n // Add module type\n manifest.moduleType = options.moduleType || 'smrt';\n\n // Record dependency references (not their objects) so runtime\n // manifest-loader.ts can discover and load them on demand.\n if (\n scannerConfig.smrtDependencies &&\n scannerConfig.smrtDependencies.length > 0\n ) {\n manifest.smrtDependencies = scannerConfig.smrtDependencies;\n }\n\n // Inject package info\n if (options.injectPackageInfo) {\n const packageInfo = this.readPackageJson();\n if (packageInfo.name) {\n manifest.packageName = packageInfo.name;\n logger.info(`[smrt] Injected package name: ${packageInfo.name}`);\n } else {\n logger.warn(\n '[smrt] Warning: Could not read package.json, packageName will be undefined',\n );\n }\n }\n\n return manifest;\n }\n\n /**\n * Write manifest JSON and optional TypeScript stub\n */\n private async writeOutput(\n manifest: SmartObjectManifest,\n options: ManifestBuilderOptions,\n ): Promise<void> {\n const manager = new ManifestManager(process.cwd());\n const isTest =\n options.outputName?.includes('test') ||\n options.stubName?.includes('test');\n const mode = isTest ? 'dev' : 'build';\n\n // 1. Always write to the unified location via ManifestManager\n manager.write(manifest, mode);\n\n // 2. Legacy/Explicit Output (if requested or for stubs)\n const outputDir = options.outputDir || 'src/manifest';\n const outputName = options.outputName || 'manifest.json';\n\n // Ensure output directory exists for legacy/stubs\n mkdirSync(outputDir, { recursive: true });\n\n // Write JSON manifest to legacy location if different from unified path\n const manifestPath = resolve(outputDir, outputName);\n const unifiedPath = manager.getOutputPath(mode);\n\n if (resolve(manifestPath) !== resolve(unifiedPath)) {\n writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));\n }\n\n // Write TypeScript stub if requested\n if (options.generateTypeStub) {\n const stubName = options.stubName || 'manifest.ts';\n const tsContent = this.generateTypeStub(manifest, stubName);\n const stubPath = resolve(outputDir, stubName);\n writeFileSync(stubPath, tsContent);\n }\n\n const objectCount = Object.keys(manifest.objects).length;\n logger.info(`[smrt] ✅ Generated manifest with ${objectCount} object(s)`);\n logger.info(`[smrt] Unified: ${unifiedPath}`);\n }\n\n /**\n * Generate TypeScript stub file with manifest constant\n */\n private generateTypeStub(\n manifest: SmartObjectManifest,\n filename: string,\n ): string {\n const isTestManifest = filename.includes('test');\n\n const comment = isTestManifest\n ? `/**\n * Test manifest stub\n * This file is replaced by generate-test-manifest.js during pretest\n * If tests run without pretest, this empty manifest is used\n *\n * DO NOT EDIT MANUALLY - This file is automatically generated\n */`\n : `/**\n * Auto-generated static manifest\n * Generated at build time from SMRT object scanning\n * DO NOT EDIT - This file is automatically generated\n */`;\n\n const exportName = isTestManifest ? 'testManifest' : 'staticManifest';\n\n // Use relative import for core package (to avoid self-reference during build),\n // package import for all other packages\n const isCorePackage = manifest.packageName === '@happyvertical/smrt-core';\n const importPath = isCorePackage\n ? '../scanner/types.js'\n : '@happyvertical/smrt-core/scanner/types';\n\n return `${comment}\n\nimport type { SmartObjectManifest } from '${importPath}';\n\nexport const ${exportName}: SmartObjectManifest = ${JSON.stringify(manifest, null, 2)} as const;\n\nexport default ${exportName};\n`;\n }\n\n /**\n * Load baseClasses from vite.config.ts\n */\n private async loadViteConfigBaseClasses(): Promise<string[] | null> {\n try {\n const viteConfigPath = resolve(process.cwd(), 'vite.config.ts');\n if (!existsSync(viteConfigPath)) {\n logger.info('[smrt] vite.config.ts not found');\n return null;\n }\n\n logger.info('[smrt] Found vite.config.ts, attempting to load...');\n\n // Use vite to load config which handles TypeScript\n const { loadConfigFromFile } = await import('vite');\n const loaded = await loadConfigFromFile(\n { command: 'build', mode: 'test' },\n viteConfigPath,\n );\n\n if (!loaded?.config?.plugins) {\n logger.info('[smrt] No plugins found in vite config');\n return null;\n }\n\n // Vite's loaded plugins are a recursive PluginOption[] (plugins, arrays,\n // falsy values, promises). We only probe a few optional fields, so model\n // them structurally and narrow defensively instead of using `any`.\n const plugins = loaded.config.plugins as readonly VitePluginProbe[];\n\n logger.info('[smrt] Vite config loaded', {\n plugins: plugins.map((p) => p?.name),\n });\n\n // Find smrtPlugin in plugins array\n const smrtPlugin = plugins.find((p) => p?.name === 'smrt-auto-service');\n\n if (!smrtPlugin) {\n logger.info(\n '[smrt] smrt-auto-service plugin not found in plugins array',\n );\n return null;\n }\n\n logger.info('[smrt] Found smrt-auto-service plugin');\n\n // Try different ways to access options\n const api = smrtPlugin.api;\n let opts: VitePluginOptions | undefined =\n (typeof api === 'object' && api !== null ? api.options : undefined) ||\n smrtPlugin.options ||\n smrtPlugin._options;\n\n if (!opts && typeof api === 'function') {\n try {\n opts = api().options;\n } catch (e) {\n logger.info('[smrt] Could not call plugin.api()');\n }\n }\n\n if (opts?.baseClasses) {\n logger.info('[smrt] Plugin options', {\n baseClasses: opts.baseClasses,\n followImports: opts.followImports,\n });\n return opts.baseClasses;\n }\n\n logger.info('[smrt] Could not access plugin options');\n return null;\n } catch (error) {\n logger.warn('[smrt] Error loading vite.config.ts', {\n error: error instanceof Error ? error.message : String(error),\n });\n logger.info('[smrt] Using default baseClasses');\n return null;\n }\n }\n\n /**\n * Load base classes from external SMRT package manifests\n */\n private async loadExternalBaseClasses(packages: string[]): Promise<string[]> {\n const externalBaseClasses: string[] = [];\n\n for (const pkgName of packages) {\n // Resolve the manifest the same way discovery does (honors `.smrt/` and\n // `src/manifest/`, not just `dist/`) so source-only workspace packages\n // still contribute base classes (#1378).\n const manifestPath = resolveManifestPath(pkgName, process.cwd());\n if (!manifestPath) {\n logger.info(`[smrt] ${pkgName}: no SMRT manifest resolved`);\n continue;\n }\n\n try {\n const manifestContent = readFileSync(manifestPath, 'utf-8');\n const manifest: SmartObjectManifest = JSON.parse(manifestContent);\n\n // Extract all class names from this package\n const foundClassNames: string[] = [];\n for (const objDef of Object.values(manifest.objects)) {\n if (objDef.className) {\n const className = objDef.className;\n foundClassNames.push(className);\n externalBaseClasses.push(className);\n }\n }\n\n logger.info(\n `[smrt] ${pkgName}: found ${foundClassNames.length} class(es) - ${foundClassNames.join(', ')}`,\n );\n } catch (error) {\n logger.info(\n `[smrt] ${pkgName}: manifest not found or invalid - ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n return externalBaseClasses;\n }\n\n /**\n * Read package.json from current working directory\n */\n private readPackageJson(): PackageInfo {\n try {\n const packageJsonPath = resolve(process.cwd(), 'package.json');\n const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n return {\n name: packageJson.name,\n version: packageJson.version,\n };\n } catch (error) {\n return {};\n }\n }\n\n /**\n * Create empty manifest when no files found\n */\n private createEmptyManifest(\n options: ManifestBuilderOptions,\n ): SmartObjectManifest {\n const manifest: SmartObjectManifest = {\n version: '1.0.0',\n timestamp: MANIFEST_TIMESTAMP,\n objects: {},\n moduleType: options.moduleType || 'smrt',\n };\n\n if (options.discoverExternalPackages) {\n manifest.smrtDependencies = discoverSmrtPackages();\n }\n\n if (options.injectPackageInfo) {\n const packageInfo = this.readPackageJson();\n if (packageInfo.name) {\n manifest.packageName = packageInfo.name;\n }\n }\n\n return manifest;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generator.js","names":[],"sources":["../../src/manifest/generator.ts"],"sourcesContent":["/**\n * Manifest generation orchestrator\n * Consolidates manifest generation logic for both build and test manifests\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, isAbsolute, relative, resolve } from 'node:path';\nimport { createLogger } from '@happyvertical/logger';\nimport { ManifestGenerator } from '../scanner/manifest-generator.js';\nimport type { SmartObjectManifest } from '../scanner/types.js';\nimport { MANIFEST_TIMESTAMP } from '../scanner/types.js';\nimport { importWorkspaceModule } from '../utils/import-workspace-module.js';\nimport type { ScannerModule } from '../utils/scanner-module.js';\nimport {\n discoverSmrtPackages,\n resolveManifestPath,\n} from './discover-smrt-packages.js';\nimport { ManifestManager } from './manager.js';\n\n// Build-time manifest-generation diagnostics route through the shared logger\n// (S14 / dim-9, #1579) so consumers can control verbosity instead of receiving\n// unconditional stdout writes.\nconst logger = createLogger({ level: 'info' });\n\nasync function importScanner() {\n return importWorkspaceModule<ScannerModule>({\n packageName: '@happyvertical/smrt-scanner',\n distEntry: 'packages/scanner/dist/index.js',\n sourceEntry: 'packages/scanner/src/index.ts',\n purpose: 'manifest generation',\n });\n}\n\n/**\n * Options for ManifestBuilder.generate()\n *\n * Controls file discovery, scanner configuration, output generation, and metadata injection\n */\nexport interface ManifestBuilderOptions {\n // File Discovery\n include?: string[];\n exclude?: string[];\n followImports?: boolean;\n /**\n * Follow symbolic links while discovering sources. Defaults to `false` —\n * see `OxcScannerOptions.followSymbolicLinks` (#2275).\n */\n followSymbolicLinks?: boolean;\n\n // Scanner Configuration\n baseClasses?: string[];\n loadViteConfig?: boolean;\n discoverExternalPackages?: boolean;\n includeExternalBaseClasses?: boolean;\n includePrivateMethods?: boolean;\n includeStaticMethods?: boolean;\n\n // Output Configuration\n outputDir?: string;\n outputName?: string;\n generateTypeStub?: boolean;\n stubName?: string;\n\n // Metadata\n injectPackageInfo?: boolean;\n moduleType?: string;\n\n // Tree Shaking (External Object Filtering)\n /**\n * @deprecated Tree shaking is no longer needed. The AST scanner no longer includes external objects in the manifest by default.\n */\n treeShake?: boolean;\n\n /**\n * @deprecated Whitelists are no longer needed. The AST scanner no longer includes external objects in the manifest by default.\n */\n externalObjectsWhitelist?: string[];\n}\n\ninterface ScannerConfig {\n baseClasses: string[];\n includePrivateMethods: boolean;\n includeStaticMethods: boolean;\n followImports: boolean;\n smrtDependencies?: string[];\n}\n\ninterface PackageInfo {\n name?: string;\n version?: string;\n}\n\n/**\n * Subset of the smrt-auto-service Vite plugin options we probe when reading\n * baseClasses out of a project's vite.config.ts.\n */\ninterface VitePluginOptions {\n baseClasses?: string[];\n followImports?: boolean;\n}\n\n/**\n * Structural shape of a loaded Vite plugin entry as probed by\n * loadViteConfigBaseClasses(). The plugin may expose its options directly, on\n * `_options`, or via an `api` object/function — all optional and discovered at\n * runtime, so each is modeled as optional here.\n */\ninterface VitePluginProbe {\n name?: string;\n options?: VitePluginOptions;\n _options?: VitePluginOptions;\n api?:\n | { options?: VitePluginOptions }\n | (() => { options?: VitePluginOptions });\n}\n\n/**\n * Orchestrates manifest generation with configurable options\n *\n * Consolidates logic from generate-manifest.js and generate-test-manifest.js\n * into a single, testable, and maintainable service.\n */\nexport class ManifestBuilder {\n /**\n * Generate manifest with specified options\n */\n async generate(\n options: ManifestBuilderOptions = {},\n ): Promise<SmartObjectManifest> {\n // 1. Discover files to scan\n const files = await this.discoverFiles(options);\n\n if (files.length === 0) {\n logger.info('[smrt] No source files found');\n return this.createEmptyManifest(options);\n }\n\n logger.info(`[smrt] Scanning ${files.length} source file(s)...`);\n\n // 2. Configure scanner (baseClasses, external packages, vite config)\n const scannerConfig = await this.configureScanner(options);\n\n // 3. Scan files with OXC scanner\n const scannedManifest = await this.scanAndBuildManifest(\n scannerConfig,\n options,\n );\n\n // 4. Enrich manifest with metadata (smrtDependencies, packageInfo)\n const manifest = this.enrichManifest(\n scannedManifest,\n options,\n scannerConfig,\n );\n\n // 5. Write output files\n this.normalizeFilePaths(manifest);\n await this.writeOutput(manifest, options);\n\n return manifest;\n }\n\n private normalizeFilePaths(manifest: SmartObjectManifest): void {\n const workspaceRoot = this.findWorkspaceRoot(process.cwd());\n\n for (const obj of Object.values(manifest.objects || {})) {\n if (obj.filePath && isAbsolute(obj.filePath)) {\n obj.filePath = relative(workspaceRoot, obj.filePath).replace(\n /\\\\/g,\n '/',\n );\n }\n }\n }\n\n private findWorkspaceRoot(startDir: string): string {\n let current = resolve(startDir);\n\n while (true) {\n if (\n existsSync(resolve(current, 'pnpm-workspace.yaml')) ||\n existsSync(resolve(current, '.git'))\n ) {\n return current;\n }\n\n const parent = dirname(current);\n if (parent === current) {\n return resolve(startDir);\n }\n current = parent;\n }\n }\n\n /**\n * Discover source files based on include/exclude patterns\n */\n private async discoverFiles(\n options: ManifestBuilderOptions,\n ): Promise<string[]> {\n const include = options.include || ['src/**/*.ts'];\n const exclude = options.exclude || ['src/**/*.d.ts', 'node_modules/**'];\n const { discoverSourceFiles } = await importScanner();\n return discoverSourceFiles({\n cwd: process.cwd(),\n include,\n exclude,\n followSymbolicLinks: options.followSymbolicLinks ?? false,\n });\n }\n\n /**\n * Configure AST scanner with baseClasses, external packages, and vite config\n */\n private async configureScanner(\n options: ManifestBuilderOptions,\n ): Promise<ScannerConfig> {\n let baseClasses = options.baseClasses || [\n 'SmrtObject',\n 'SmrtClass',\n 'SmrtCollection',\n ];\n\n // Load from vite.config.ts if requested\n if (options.loadViteConfig) {\n const viteBaseClasses = await this.loadViteConfigBaseClasses();\n if (viteBaseClasses && viteBaseClasses.length > 0) {\n baseClasses = viteBaseClasses;\n logger.info(\n `[smrt] Using baseClasses from vite.config.ts: ${baseClasses.join(', ')}`,\n );\n }\n }\n\n // Discover external SMRT packages\n let smrtDependencies: string[] = [];\n if (options.discoverExternalPackages) {\n logger.info('[smrt] Discovering external SMRT packages...');\n smrtDependencies = discoverSmrtPackages();\n logger.info(\n `[smrt] Found ${smrtDependencies.length} SMRT package(s): ${smrtDependencies.join(', ')}`,\n );\n\n // Optionally add external base classes\n if (options.includeExternalBaseClasses) {\n const externalClasses =\n await this.loadExternalBaseClasses(smrtDependencies);\n if (externalClasses.length > 0) {\n baseClasses = [...baseClasses, ...externalClasses];\n logger.info(\n `[smrt] Added ${externalClasses.length} external base class(es) to scanner`,\n );\n logger.info(`[smrt] Total baseClasses: ${baseClasses.length}`);\n }\n }\n }\n\n return {\n baseClasses,\n includePrivateMethods: options.includePrivateMethods ?? false,\n includeStaticMethods: options.includeStaticMethods ?? true,\n followImports: options.followImports ?? false,\n smrtDependencies,\n };\n }\n\n /**\n * Scan files using OxcScanner and build manifest via ManifestAdapter.\n *\n * This replaces the old ASTScanner-based scanFiles() method.\n * Uses the same OXC pipeline as the Vite plugin's scanWithOxc().\n */\n private async scanAndBuildManifest(\n config: ScannerConfig,\n options: ManifestBuilderOptions,\n ): Promise<SmartObjectManifest> {\n const { OxcScanner, ManifestAdapter } = await importScanner();\n\n const scanner = new OxcScanner({\n cwd: process.cwd(),\n include: options.include || ['src/**/*.ts'],\n exclude: options.exclude || ['src/**/*.d.ts', 'node_modules/**'],\n baseClasses: config.baseClasses,\n includePrivateMethods: config.includePrivateMethods,\n includeStaticMethods: config.includeStaticMethods,\n followImports: config.followImports,\n followSymbolicLinks: options.followSymbolicLinks,\n });\n\n const { results, resolved } = await scanner.scanAndResolve();\n const scanErrors = results.errors.filter(\n (diagnostic) => diagnostic.severity === 'error',\n );\n if (scanErrors.length > 0) {\n const detail = scanErrors\n .map((diagnostic) => {\n const where = diagnostic.line\n ? `${diagnostic.filePath}:${diagnostic.line}`\n : diagnostic.filePath;\n return `${where} — ${diagnostic.message}`;\n })\n .join('; ');\n throw new Error(\n `[smrt] Build aborted due to ${scanErrors.length} scan error(s): ${detail}`,\n );\n }\n\n // Read package.json for metadata\n let packageName: string | undefined;\n let packageVersion: string | undefined;\n let packageJson: { name?: string; version?: string } | undefined;\n try {\n const pkgPath = resolve(process.cwd(), 'package.json');\n const pkgContent = readFileSync(pkgPath, 'utf-8');\n packageJson = JSON.parse(pkgContent);\n packageName = packageJson?.name || undefined;\n packageVersion = packageJson?.version || undefined;\n } catch {\n // package.json not found - continue without packageName\n }\n\n // Convert to manifest format\n const adapter = new ManifestAdapter();\n const manifest = adapter.toManifest(resolved, {\n packageName,\n packageVersion,\n typeAliases: results.typeAliases,\n });\n\n // Set smrtDependencies BEFORE mergeInheritedFields so external packages can be loaded\n if (config.smrtDependencies && config.smrtDependencies.length > 0) {\n manifest.smrtDependencies = config.smrtDependencies;\n }\n\n // Run THE manifest generation pass sequence (shared with\n // ManifestGenerator.generateManifest() and the Vite plugin, #2360 — this\n // path used to skip the report and conflict-key passes).\n const manifestGen = new ManifestGenerator();\n manifestGen.applyGenerationPasses(manifest, { packageName, packageJson });\n\n return manifest;\n }\n\n /**\n * Enrich manifest with metadata (moduleType, smrtDependencies, packageInfo).\n *\n * Note: As of Issue #1013, this method no longer merges external package\n * objects into the manifest. Dependencies are referenced via\n * `smrtDependencies` and resolved at runtime by manifest-loader.ts.\n */\n private enrichManifest(\n manifest: SmartObjectManifest,\n options: ManifestBuilderOptions,\n scannerConfig: ScannerConfig,\n ): SmartObjectManifest {\n // Add module type\n manifest.moduleType = options.moduleType || 'smrt';\n\n // Record dependency references (not their objects) so runtime\n // manifest-loader.ts can discover and load them on demand.\n if (\n scannerConfig.smrtDependencies &&\n scannerConfig.smrtDependencies.length > 0\n ) {\n manifest.smrtDependencies = scannerConfig.smrtDependencies;\n }\n\n // Inject package info\n if (options.injectPackageInfo) {\n const packageInfo = this.readPackageJson();\n if (packageInfo.name) {\n manifest.packageName = packageInfo.name;\n logger.info(`[smrt] Injected package name: ${packageInfo.name}`);\n } else {\n logger.warn(\n '[smrt] Warning: Could not read package.json, packageName will be undefined',\n );\n }\n }\n\n return manifest;\n }\n\n /**\n * Write manifest JSON and optional TypeScript stub\n */\n private async writeOutput(\n manifest: SmartObjectManifest,\n options: ManifestBuilderOptions,\n ): Promise<void> {\n const manager = new ManifestManager(process.cwd());\n const isTest =\n options.outputName?.includes('test') ||\n options.stubName?.includes('test');\n const mode = isTest ? 'dev' : 'build';\n\n // 1. Always write to the unified location via ManifestManager\n manager.write(manifest, mode);\n\n // 2. Legacy/Explicit Output (if requested or for stubs)\n const outputDir = options.outputDir || 'src/manifest';\n const outputName = options.outputName || 'manifest.json';\n\n // Ensure output directory exists for legacy/stubs\n mkdirSync(outputDir, { recursive: true });\n\n // Write JSON manifest to legacy location if different from unified path\n const manifestPath = resolve(outputDir, outputName);\n const unifiedPath = manager.getOutputPath(mode);\n\n if (resolve(manifestPath) !== resolve(unifiedPath)) {\n writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));\n }\n\n // Write TypeScript stub if requested\n if (options.generateTypeStub) {\n const stubName = options.stubName || 'manifest.ts';\n const tsContent = this.generateTypeStub(manifest, stubName);\n const stubPath = resolve(outputDir, stubName);\n writeFileSync(stubPath, tsContent);\n }\n\n const objectCount = Object.keys(manifest.objects).length;\n logger.info(`[smrt] ✅ Generated manifest with ${objectCount} object(s)`);\n logger.info(`[smrt] Unified: ${unifiedPath}`);\n }\n\n /**\n * Generate TypeScript stub file with manifest constant\n */\n private generateTypeStub(\n manifest: SmartObjectManifest,\n filename: string,\n ): string {\n const isTestManifest = filename.includes('test');\n\n const comment = isTestManifest\n ? `/**\n * Test manifest stub\n * This file is replaced by generate-test-manifest.js during pretest\n * If tests run without pretest, this empty manifest is used\n *\n * DO NOT EDIT MANUALLY - This file is automatically generated\n */`\n : `/**\n * Auto-generated static manifest\n * Generated at build time from SMRT object scanning\n * DO NOT EDIT - This file is automatically generated\n */`;\n\n const exportName = isTestManifest ? 'testManifest' : 'staticManifest';\n\n // Use relative import for core package (to avoid self-reference during build),\n // package import for all other packages\n const isCorePackage = manifest.packageName === '@happyvertical/smrt-core';\n const importPath = isCorePackage\n ? '../scanner/types.js'\n : '@happyvertical/smrt-core/scanner/types';\n\n return `${comment}\n\nimport type { SmartObjectManifest } from '${importPath}';\n\nexport const ${exportName}: SmartObjectManifest = ${JSON.stringify(manifest, null, 2)} as const;\n\nexport default ${exportName};\n`;\n }\n\n /**\n * Load baseClasses from vite.config.ts\n */\n private async loadViteConfigBaseClasses(): Promise<string[] | null> {\n try {\n const viteConfigPath = resolve(process.cwd(), 'vite.config.ts');\n if (!existsSync(viteConfigPath)) {\n logger.info('[smrt] vite.config.ts not found');\n return null;\n }\n\n logger.info('[smrt] Found vite.config.ts, attempting to load...');\n\n // Use vite to load config which handles TypeScript\n const { loadConfigFromFile } = await import('vite');\n const loaded = await loadConfigFromFile(\n { command: 'build', mode: 'test' },\n viteConfigPath,\n );\n\n if (!loaded?.config?.plugins) {\n logger.info('[smrt] No plugins found in vite config');\n return null;\n }\n\n // Vite's loaded plugins are a recursive PluginOption[] (plugins, arrays,\n // falsy values, promises). We only probe a few optional fields, so model\n // them structurally and narrow defensively instead of using `any`.\n const plugins = loaded.config.plugins as readonly VitePluginProbe[];\n\n logger.info('[smrt] Vite config loaded', {\n plugins: plugins.map((p) => p?.name),\n });\n\n // Find smrtPlugin in plugins array\n const smrtPlugin = plugins.find((p) => p?.name === 'smrt-auto-service');\n\n if (!smrtPlugin) {\n logger.info(\n '[smrt] smrt-auto-service plugin not found in plugins array',\n );\n return null;\n }\n\n logger.info('[smrt] Found smrt-auto-service plugin');\n\n // Try different ways to access options\n const api = smrtPlugin.api;\n let opts: VitePluginOptions | undefined =\n (typeof api === 'object' && api !== null ? api.options : undefined) ||\n smrtPlugin.options ||\n smrtPlugin._options;\n\n if (!opts && typeof api === 'function') {\n try {\n opts = api().options;\n } catch (e) {\n logger.info('[smrt] Could not call plugin.api()');\n }\n }\n\n if (opts?.baseClasses) {\n logger.info('[smrt] Plugin options', {\n baseClasses: opts.baseClasses,\n followImports: opts.followImports,\n });\n return opts.baseClasses;\n }\n\n logger.info('[smrt] Could not access plugin options');\n return null;\n } catch (error) {\n logger.warn('[smrt] Error loading vite.config.ts', {\n error: error instanceof Error ? error.message : String(error),\n });\n logger.info('[smrt] Using default baseClasses');\n return null;\n }\n }\n\n /**\n * Load base classes from external SMRT package manifests\n */\n private async loadExternalBaseClasses(packages: string[]): Promise<string[]> {\n const externalBaseClasses: string[] = [];\n\n for (const pkgName of packages) {\n // Resolve the manifest the same way discovery does (honors `.smrt/` and\n // `src/manifest/`, not just `dist/`) so source-only workspace packages\n // still contribute base classes (#1378).\n const manifestPath = resolveManifestPath(pkgName, process.cwd());\n if (!manifestPath) {\n logger.info(`[smrt] ${pkgName}: no SMRT manifest resolved`);\n continue;\n }\n\n try {\n const manifestContent = readFileSync(manifestPath, 'utf-8');\n const manifest: SmartObjectManifest = JSON.parse(manifestContent);\n\n // Extract all class names from this package\n const foundClassNames: string[] = [];\n for (const objDef of Object.values(manifest.objects)) {\n if (objDef.className) {\n const className = objDef.className;\n foundClassNames.push(className);\n externalBaseClasses.push(className);\n }\n }\n\n logger.info(\n `[smrt] ${pkgName}: found ${foundClassNames.length} class(es) - ${foundClassNames.join(', ')}`,\n );\n } catch (error) {\n logger.info(\n `[smrt] ${pkgName}: manifest not found or invalid - ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n return externalBaseClasses;\n }\n\n /**\n * Read package.json from current working directory\n */\n private readPackageJson(): PackageInfo {\n try {\n const packageJsonPath = resolve(process.cwd(), 'package.json');\n const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));\n return {\n name: packageJson.name,\n version: packageJson.version,\n };\n } catch (error) {\n return {};\n }\n }\n\n /**\n * Create empty manifest when no files found\n */\n private createEmptyManifest(\n options: ManifestBuilderOptions,\n ): SmartObjectManifest {\n const manifest: SmartObjectManifest = {\n version: '1.0.0',\n timestamp: MANIFEST_TIMESTAMP,\n objects: {},\n moduleType: options.moduleType || 'smrt',\n };\n\n if (options.discoverExternalPackages) {\n manifest.smrtDependencies = discoverSmrtPackages();\n }\n\n if (options.injectPackageInfo) {\n const packageInfo = this.readPackageJson();\n if (packageInfo.name) {\n manifest.packageName = packageInfo.name;\n }\n }\n\n return manifest;\n }\n}\n"],"mappings":"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"}
|
|
@@ -3,7 +3,7 @@ var staticManifest = {
|
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"timestamp": 0,
|
|
5
5
|
"packageName": "@happyvertical/smrt-core",
|
|
6
|
-
"packageVersion": "0.
|
|
6
|
+
"packageVersion": "0.41.0",
|
|
7
7
|
"objects": {
|
|
8
8
|
"@happyvertical/smrt-core:SmrtClass": {
|
|
9
9
|
"name": "smrtclass",
|
|
@@ -97,12 +97,12 @@ var staticManifest = {
|
|
|
97
97
|
}
|
|
98
98
|
},
|
|
99
99
|
"indexes": [{
|
|
100
|
-
"name": "smrt_classes_id_idx",
|
|
101
|
-
"columns": ["id"]
|
|
102
|
-
}, {
|
|
103
100
|
"name": "smrt_classes_slug_context_idx",
|
|
104
101
|
"columns": ["slug", "context"],
|
|
105
102
|
"unique": true
|
|
103
|
+
}, {
|
|
104
|
+
"name": "smrt_classes_created_at_idx",
|
|
105
|
+
"columns": ["created_at"]
|
|
106
106
|
}],
|
|
107
107
|
"version": "234a72b0"
|
|
108
108
|
}
|
|
@@ -512,12 +512,12 @@ var staticManifest = {
|
|
|
512
512
|
}
|
|
513
513
|
},
|
|
514
514
|
"indexes": [{
|
|
515
|
-
"name": "smrt_collections_id_idx",
|
|
516
|
-
"columns": ["id"]
|
|
517
|
-
}, {
|
|
518
515
|
"name": "smrt_collections_slug_context_idx",
|
|
519
516
|
"columns": ["slug", "context"],
|
|
520
517
|
"unique": true
|
|
518
|
+
}, {
|
|
519
|
+
"name": "smrt_collections_created_at_idx",
|
|
520
|
+
"columns": ["created_at"]
|
|
521
521
|
}],
|
|
522
522
|
"version": "295b0a80"
|
|
523
523
|
}
|
|
@@ -1158,12 +1158,12 @@ var staticManifest = {
|
|
|
1158
1158
|
}
|
|
1159
1159
|
},
|
|
1160
1160
|
"indexes": [{
|
|
1161
|
-
"name": "smrt_objects_id_idx",
|
|
1162
|
-
"columns": ["id"]
|
|
1163
|
-
}, {
|
|
1164
1161
|
"name": "smrt_objects_slug_context_idx",
|
|
1165
1162
|
"columns": ["slug", "context"],
|
|
1166
1163
|
"unique": true
|
|
1164
|
+
}, {
|
|
1165
|
+
"name": "smrt_objects_created_at_idx",
|
|
1166
|
+
"columns": ["created_at"]
|
|
1167
1167
|
}],
|
|
1168
1168
|
"version": "204bf3c4"
|
|
1169
1169
|
}
|