@happyvertical/smrt-core 0.37.7 → 0.37.9
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 +25 -6
- package/dist/change-feed.d.ts +261 -0
- package/dist/change-feed.d.ts.map +1 -0
- package/dist/change-feed.js +482 -0
- package/dist/change-feed.js.map +1 -0
- package/dist/class.d.ts.map +1 -1
- package/dist/class.js +4 -2
- package/dist/class.js.map +1 -1
- package/dist/collection.js +2 -2
- package/dist/consumer-plugin/index.d.ts.map +1 -1
- package/dist/consumer-plugin/index.js +24 -19
- package/dist/consumer-plugin/index.js.map +1 -1
- package/dist/dispatch/bus.js +2 -2
- package/dist/dispatch/index.d.ts +1 -1
- package/dist/dispatch/index.d.ts.map +1 -1
- package/dist/dispatch/index.js +2 -2
- package/dist/dispatch/tenant-resolver.d.ts +23 -0
- package/dist/dispatch/tenant-resolver.d.ts.map +1 -1
- package/dist/dispatch/tenant-resolver.js +33 -1
- package/dist/dispatch/tenant-resolver.js.map +1 -1
- package/dist/generated-client-runtime.d.ts +26 -0
- package/dist/generated-client-runtime.d.ts.map +1 -0
- package/dist/generated-client-runtime.js +88 -0
- package/dist/generated-client-runtime.js.map +1 -0
- package/dist/generators/changes-route.d.ts +52 -0
- package/dist/generators/changes-route.d.ts.map +1 -0
- package/dist/generators/changes-route.js +111 -0
- package/dist/generators/changes-route.js.map +1 -0
- package/dist/generators/conditional-get.d.ts +12 -0
- package/dist/generators/conditional-get.d.ts.map +1 -1
- package/dist/generators/conditional-get.js +30 -2
- package/dist/generators/conditional-get.js.map +1 -1
- package/dist/generators/rest.d.ts +58 -0
- package/dist/generators/rest.d.ts.map +1 -1
- package/dist/generators/rest.js +114 -21
- package/dist/generators/rest.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -5
- package/dist/manifest/manifest-loader.d.ts +10 -1
- package/dist/manifest/manifest-loader.d.ts.map +1 -1
- package/dist/manifest/manifest-loader.js +18 -5
- package/dist/manifest/manifest-loader.js.map +1 -1
- package/dist/manifest/static-manifest.js +2 -2
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest/test-manifest-stub.d.ts.map +1 -1
- package/dist/manifest/test-manifest-stub.js +880 -105
- package/dist/manifest/test-manifest-stub.js.map +1 -1
- package/dist/manifest.json +2 -2
- package/dist/object.js +2 -2
- package/dist/prebuild/index.d.ts.map +1 -1
- package/dist/prebuild/index.js +14 -7
- package/dist/prebuild/index.js.map +1 -1
- package/dist/registry/shared-state.d.ts +8 -1
- package/dist/registry/shared-state.d.ts.map +1 -1
- package/dist/registry/shared-state.js +11 -3
- package/dist/registry/shared-state.js.map +1 -1
- package/dist/smrt-knowledge.json +6 -6
- package/dist/system/schema.d.ts +24 -1
- package/dist/system/schema.d.ts.map +1 -1
- package/dist/system/schema.js +45 -3
- package/dist/system/schema.js.map +1 -1
- package/dist/testing/database.js +1 -1
- package/dist/utils/stack-frames.d.ts +50 -0
- package/dist/utils/stack-frames.d.ts.map +1 -0
- package/dist/utils/stack-frames.js +64 -0
- package/dist/utils/stack-frames.js.map +1 -0
- package/dist/vite-plugin/changes-route.d.ts +10 -0
- package/dist/vite-plugin/changes-route.d.ts.map +1 -0
- package/dist/vite-plugin/changes-route.js +174 -0
- package/dist/vite-plugin/changes-route.js.map +1 -0
- package/dist/vite-plugin/index.d.ts +7 -0
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +38 -20
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/route-header.d.ts +10 -0
- package/dist/vite-plugin/route-header.d.ts.map +1 -0
- package/dist/vite-plugin/route-header.js +14 -0
- package/dist/vite-plugin/route-header.js.map +1 -0
- package/dist/vite-plugin/sveltekit-generator.d.ts +7 -0
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.js +46 -20
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/dist/vite-plugin/sync-apply-route.d.ts.map +1 -1
- package/dist/vite-plugin/sync-apply-route.js +2 -2
- package/dist/vite-plugin/sync-apply-route.js.map +1 -1
- package/package.json +4 -4
package/dist/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": "1.0.0",
|
|
3
|
-
"timestamp":
|
|
3
|
+
"timestamp": 1783086137186,
|
|
4
4
|
"packageName": "@happyvertical/smrt-core",
|
|
5
|
-
"packageVersion": "0.37.
|
|
5
|
+
"packageVersion": "0.37.9",
|
|
6
6
|
"objects": {
|
|
7
7
|
"@happyvertical/smrt-core:SmrtClass": {
|
|
8
8
|
"name": "smrtclass",
|
package/dist/object.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { SmrtClass } from "./class.js";
|
|
2
1
|
import { broadcastCacheInvalidation, hasCrossProcessCacheInterest, invalidateCollectionCache, resolveDbCacheKey } from "./collection-cache.js";
|
|
2
|
+
import { GlobalInterceptors, createInterceptorContext } from "./interceptors.js";
|
|
3
|
+
import { SmrtClass } from "./class.js";
|
|
3
4
|
import { EmbeddingProvider } from "./embeddings/provider.js";
|
|
4
5
|
import { EmbeddingStorage } from "./embeddings/storage.js";
|
|
5
|
-
import { GlobalInterceptors, createInterceptorContext } from "./interceptors.js";
|
|
6
6
|
import { DatabaseError, ErrorUtils, RuntimeError, SmrtError, TenantIsolationError, ValidationError } from "./errors.js";
|
|
7
7
|
import { toSnakeCase } from "./utils/naming.js";
|
|
8
8
|
import { ContentHasher } from "./embeddings/hash.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prebuild/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACvC,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,sCAAsC;IACtC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,CAAC,CAkCf;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/prebuild/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAE5D,MAAM,WAAW,eAAe;IAC9B,+CAA+C;IAC/C,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CAAC;IACvC,2CAA2C;IAC3C,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,sCAAsC;IACtC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,qDAAqD;IACrD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,CAAC,CAkCf;AA+PD;;GAEG;AACH,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,IAAI,CAAC,CAkBf"}
|
package/dist/prebuild/index.js
CHANGED
|
@@ -102,19 +102,26 @@ declare module '@smrt/manifest' {
|
|
|
102
102
|
* Auto-generated API client module declaration
|
|
103
103
|
*/
|
|
104
104
|
declare module '@smrt/client' {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
data?: T;
|
|
105
|
+
/** Shape of a JSON error body carried by a rejected request (SmrtClientError.body). */
|
|
106
|
+
export interface ApiError {
|
|
108
107
|
error?: string;
|
|
109
108
|
message?: string;
|
|
110
109
|
}
|
|
111
110
|
|
|
111
|
+
/** Typed error thrown by every fetcher on a non-2xx response (#1796). */
|
|
112
|
+
export interface SmrtClientError extends Error {
|
|
113
|
+
name: 'SmrtClientError';
|
|
114
|
+
status: number;
|
|
115
|
+
body?: ApiError | string;
|
|
116
|
+
}
|
|
117
|
+
|
|
112
118
|
export interface CrudOperations<T = any> {
|
|
113
|
-
list(params?: Record<string, any>): Promise<
|
|
114
|
-
get(id: string): Promise<
|
|
115
|
-
create(data: Partial<T>): Promise<
|
|
116
|
-
update(id: string, data: Partial<T>): Promise<
|
|
119
|
+
list(params?: Record<string, any>): Promise<T[]>;
|
|
120
|
+
get(id: string): Promise<T>;
|
|
121
|
+
create(data: Partial<T>): Promise<T>;
|
|
122
|
+
update(id: string, data: Partial<T>): Promise<T>;
|
|
117
123
|
delete(id: string): Promise<boolean>;
|
|
124
|
+
search(query: string): Promise<T[]>;
|
|
118
125
|
}
|
|
119
126
|
|
|
120
127
|
export interface ApiClient {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/prebuild/index.ts"],"sourcesContent":["/**\n * Pre-build utilities for generating TypeScript declarations\n * Solves virtual module resolution by creating physical .d.ts files\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { SmartObjectManifest } from '../scanner/types';\n\nexport interface PrebuildOptions {\n /** Path to manifest file or manifest object */\n manifest: string | SmartObjectManifest;\n /** Output directory for generated types */\n outDir: string;\n /** Include virtual module declarations */\n includeVirtualModules?: boolean;\n /** Include object type definitions */\n includeObjectTypes?: boolean;\n /** Project root path for resolving relative paths */\n projectRoot?: string;\n}\n\n/**\n * Generate TypeScript declaration files from SMRT manifest\n */\nexport async function generateDeclarations(\n options: PrebuildOptions,\n): Promise<void> {\n const {\n manifest: manifestInput,\n outDir,\n includeVirtualModules = true,\n includeObjectTypes = true,\n projectRoot = process.cwd(),\n } = options;\n\n // Load manifest\n const manifest: SmartObjectManifest =\n typeof manifestInput === 'string'\n ? JSON.parse(fs.readFileSync(manifestInput, 'utf-8'))\n : manifestInput;\n\n // Ensure output directory exists\n const fullOutDir = path.isAbsolute(outDir)\n ? outDir\n : path.join(projectRoot, outDir);\n fs.mkdirSync(fullOutDir, { recursive: true });\n\n console.log(`[smrt:prebuild] Generating declarations to ${fullOutDir}`);\n\n if (includeObjectTypes) {\n await generateObjectTypeDeclarations(manifest, fullOutDir);\n }\n\n if (includeVirtualModules) {\n await generateVirtualModuleDeclarations(manifest, fullOutDir);\n }\n\n console.log(\n `[smrt:prebuild] Generated declarations for ${Object.keys(manifest.objects).length} SMRT objects`,\n );\n}\n\n/**\n * Generate TypeScript interfaces for SMRT objects\n */\nasync function generateObjectTypeDeclarations(\n manifest: SmartObjectManifest,\n outDir: string,\n): Promise<void> {\n const interfaces: string[] = [];\n\n // Generate interfaces for each discovered SMRT object\n for (const [_objectName, objectMeta] of Object.entries(manifest.objects)) {\n const fields = objectMeta.fields || {};\n const propertyLines: string[] = [];\n\n // Add standard SmrtObject properties\n propertyLines.push(' id?: string;');\n propertyLines.push(' created_at?: string;');\n propertyLines.push(' updated_at?: string;');\n\n // Add object-specific properties\n for (const [fieldName, fieldDef] of Object.entries(fields)) {\n const type = mapFieldTypeToTypeScript(fieldDef.type);\n const optional = !fieldDef.required ? '?' : '';\n propertyLines.push(` ${fieldName}${optional}: ${type};`);\n }\n\n const interfaceDef = `export interface ${objectMeta.className}Data {\n${propertyLines.join('\\n')}\n}`;\n interfaces.push(interfaceDef);\n }\n\n // Write object types file\n const objectTypesContent = `/**\n * Auto-generated TypeScript interfaces for SMRT objects\n * Generated at build time from @smrt() decorated classes\n *\n * DO NOT EDIT THIS FILE MANUALLY\n */\n\n${interfaces.join('\\n\\n')}\n`;\n\n fs.writeFileSync(path.join(outDir, 'smrt-objects.d.ts'), objectTypesContent);\n}\n\n/**\n * Generate virtual module declarations\n */\nasync function generateVirtualModuleDeclarations(\n manifest: SmartObjectManifest,\n outDir: string,\n): Promise<void> {\n // Generate manifest module declaration\n const manifestDeclaration = `/**\n * Auto-generated manifest module declaration\n */\ndeclare module '@smrt/manifest' {\n export interface SmrtObjectField {\n type: string;\n required?: boolean;\n default?: any;\n }\n\n export interface SmrtObjectMethod {\n name: string;\n parameters: Array<{\n name: string;\n type: string;\n optional?: boolean;\n default?: any;\n }>;\n returnType: string;\n async: boolean;\n isStatic: boolean;\n isPublic: boolean;\n }\n\n export interface SmrtObjectDefinition {\n name: string;\n className: string;\n collection: string;\n filePath: string;\n fields: Record<string, SmrtObjectField>;\n methods: Record<string, SmrtObjectMethod>;\n decoratorConfig: any;\n extends?: string;\n }\n\n export interface SmrtManifest {\n version: string;\n timestamp: number;\n objects: Record<string, SmrtObjectDefinition>;\n }\n\n export const manifest: SmrtManifest;\n export default manifest;\n}`;\n\n // Generate client module declaration\n const collectionNames = [\n ...new Set(Object.values(manifest.objects).map((obj) => obj.collection)),\n ];\n\n const apiClientInterface = collectionNames\n .map((collection) => {\n const dataType = Object.entries(manifest.objects).find(\n ([, obj]) => obj.collection === collection,\n )?.[1].className;\n const interfaceName = dataType ? `${dataType}Data` : 'any';\n return ` ${collection}: CrudOperations<${interfaceName}>;`;\n })\n .join('\\n');\n\n const clientDeclaration = `/**\n * Auto-generated API client module declaration\n */\ndeclare module '@smrt/client' {\n export interface ApiResponse<T = any> {\n id?: string;\n data?: T;\n error?: string;\n message?: string;\n }\n\n export interface CrudOperations<T = any> {\n list(params?: Record<string, any>): Promise<ApiResponse<T[]>>;\n get(id: string): Promise<ApiResponse<T>>;\n create(data: Partial<T>): Promise<ApiResponse<T>>;\n update(id: string, data: Partial<T>): Promise<ApiResponse<T>>;\n delete(id: string): Promise<boolean>;\n }\n\n export interface ApiClient {\n${apiClientInterface}\n }\n\n export function createClient(basePath?: string): ApiClient;\n export default createClient;\n}`;\n\n // Generate routes module declaration\n const routesDeclaration = `/**\n * Auto-generated routes module declaration\n */\ndeclare module '@smrt/routes' {\n export interface RouteApp {\n get(path: string, handler: (req: any, res: any) => void): void;\n post(path: string, handler: (req: any, res: any) => void): void;\n put(path: string, handler: (req: any, res: any) => void): void;\n delete(path: string, handler: (req: any, res: any) => void): void;\n }\n\n export function setupRoutes(app: RouteApp): void;\n export default setupRoutes;\n}`;\n\n // Generate MCP module declaration\n const mcpDeclaration = `/**\n * Auto-generated MCP module declaration\n */\ndeclare module '@smrt/mcp' {\n export interface McpTool {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, any>;\n required?: string[];\n };\n }\n\n export const tools: McpTool[];\n export function createMCPServer(): {\n name: string;\n version: string;\n tools: McpTool[]\n };\n export default createMCPServer;\n}`;\n\n // Generate types module declaration with object imports\n const objectImports = Object.values(manifest.objects)\n .map(\n (obj) =>\n ` export type ${obj.className}Data = import('./smrt-objects').${obj.className}Data;`,\n )\n .join('\\n');\n\n const typesDeclaration = `/**\n * Auto-generated types module declaration\n */\ndeclare module '@smrt/types' {\n${objectImports}\n\n export interface Request {\n params: Record<string, string>;\n query: Record<string, any>;\n json(): Promise<any>;\n }\n\n export interface Response {\n json(data: any, init?: { status?: number }): Response;\n status(code: number): Response;\n }\n}`;\n\n // Write all virtual module declarations\n fs.writeFileSync(\n path.join(outDir, 'smrt-manifest.d.ts'),\n manifestDeclaration,\n );\n fs.writeFileSync(path.join(outDir, 'smrt-client.d.ts'), clientDeclaration);\n fs.writeFileSync(path.join(outDir, 'smrt-routes.d.ts'), routesDeclaration);\n fs.writeFileSync(path.join(outDir, 'smrt-mcp.d.ts'), mcpDeclaration);\n fs.writeFileSync(path.join(outDir, 'smrt-types.d.ts'), typesDeclaration);\n}\n\n/**\n * Map SMRT field types to TypeScript types\n */\nfunction mapFieldTypeToTypeScript(smrtType: string): string {\n switch (smrtType) {\n case 'text':\n return 'string';\n case 'decimal':\n case 'integer':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'datetime':\n return 'string | Date';\n case 'json':\n return 'any';\n case 'foreignKey':\n return 'string';\n default:\n return 'any';\n }\n}\n\n/**\n * CLI command for generating declarations\n */\nexport async function generateDeclarationsFromCLI(\n args: string[],\n): Promise<void> {\n const manifestPath = args[0];\n const outDir = args[1] || 'src/types/generated';\n\n if (!manifestPath) {\n console.error('Usage: generate-declarations <manifest-path> [output-dir]');\n process.exit(1);\n }\n\n if (!fs.existsSync(manifestPath)) {\n console.error(`Manifest file not found: ${manifestPath}`);\n process.exit(1);\n }\n\n await generateDeclarations({\n manifest: manifestPath,\n outDir,\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/prebuild/index.ts"],"sourcesContent":["/**\n * Pre-build utilities for generating TypeScript declarations\n * Solves virtual module resolution by creating physical .d.ts files\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { SmartObjectManifest } from '../scanner/types';\n\nexport interface PrebuildOptions {\n /** Path to manifest file or manifest object */\n manifest: string | SmartObjectManifest;\n /** Output directory for generated types */\n outDir: string;\n /** Include virtual module declarations */\n includeVirtualModules?: boolean;\n /** Include object type definitions */\n includeObjectTypes?: boolean;\n /** Project root path for resolving relative paths */\n projectRoot?: string;\n}\n\n/**\n * Generate TypeScript declaration files from SMRT manifest\n */\nexport async function generateDeclarations(\n options: PrebuildOptions,\n): Promise<void> {\n const {\n manifest: manifestInput,\n outDir,\n includeVirtualModules = true,\n includeObjectTypes = true,\n projectRoot = process.cwd(),\n } = options;\n\n // Load manifest\n const manifest: SmartObjectManifest =\n typeof manifestInput === 'string'\n ? JSON.parse(fs.readFileSync(manifestInput, 'utf-8'))\n : manifestInput;\n\n // Ensure output directory exists\n const fullOutDir = path.isAbsolute(outDir)\n ? outDir\n : path.join(projectRoot, outDir);\n fs.mkdirSync(fullOutDir, { recursive: true });\n\n console.log(`[smrt:prebuild] Generating declarations to ${fullOutDir}`);\n\n if (includeObjectTypes) {\n await generateObjectTypeDeclarations(manifest, fullOutDir);\n }\n\n if (includeVirtualModules) {\n await generateVirtualModuleDeclarations(manifest, fullOutDir);\n }\n\n console.log(\n `[smrt:prebuild] Generated declarations for ${Object.keys(manifest.objects).length} SMRT objects`,\n );\n}\n\n/**\n * Generate TypeScript interfaces for SMRT objects\n */\nasync function generateObjectTypeDeclarations(\n manifest: SmartObjectManifest,\n outDir: string,\n): Promise<void> {\n const interfaces: string[] = [];\n\n // Generate interfaces for each discovered SMRT object\n for (const [_objectName, objectMeta] of Object.entries(manifest.objects)) {\n const fields = objectMeta.fields || {};\n const propertyLines: string[] = [];\n\n // Add standard SmrtObject properties\n propertyLines.push(' id?: string;');\n propertyLines.push(' created_at?: string;');\n propertyLines.push(' updated_at?: string;');\n\n // Add object-specific properties\n for (const [fieldName, fieldDef] of Object.entries(fields)) {\n const type = mapFieldTypeToTypeScript(fieldDef.type);\n const optional = !fieldDef.required ? '?' : '';\n propertyLines.push(` ${fieldName}${optional}: ${type};`);\n }\n\n const interfaceDef = `export interface ${objectMeta.className}Data {\n${propertyLines.join('\\n')}\n}`;\n interfaces.push(interfaceDef);\n }\n\n // Write object types file\n const objectTypesContent = `/**\n * Auto-generated TypeScript interfaces for SMRT objects\n * Generated at build time from @smrt() decorated classes\n *\n * DO NOT EDIT THIS FILE MANUALLY\n */\n\n${interfaces.join('\\n\\n')}\n`;\n\n fs.writeFileSync(path.join(outDir, 'smrt-objects.d.ts'), objectTypesContent);\n}\n\n/**\n * Generate virtual module declarations\n */\nasync function generateVirtualModuleDeclarations(\n manifest: SmartObjectManifest,\n outDir: string,\n): Promise<void> {\n // Generate manifest module declaration\n const manifestDeclaration = `/**\n * Auto-generated manifest module declaration\n */\ndeclare module '@smrt/manifest' {\n export interface SmrtObjectField {\n type: string;\n required?: boolean;\n default?: any;\n }\n\n export interface SmrtObjectMethod {\n name: string;\n parameters: Array<{\n name: string;\n type: string;\n optional?: boolean;\n default?: any;\n }>;\n returnType: string;\n async: boolean;\n isStatic: boolean;\n isPublic: boolean;\n }\n\n export interface SmrtObjectDefinition {\n name: string;\n className: string;\n collection: string;\n filePath: string;\n fields: Record<string, SmrtObjectField>;\n methods: Record<string, SmrtObjectMethod>;\n decoratorConfig: any;\n extends?: string;\n }\n\n export interface SmrtManifest {\n version: string;\n timestamp: number;\n objects: Record<string, SmrtObjectDefinition>;\n }\n\n export const manifest: SmrtManifest;\n export default manifest;\n}`;\n\n // Generate client module declaration\n const collectionNames = [\n ...new Set(Object.values(manifest.objects).map((obj) => obj.collection)),\n ];\n\n const apiClientInterface = collectionNames\n .map((collection) => {\n const dataType = Object.entries(manifest.objects).find(\n ([, obj]) => obj.collection === collection,\n )?.[1].className;\n const interfaceName = dataType ? `${dataType}Data` : 'any';\n return ` ${collection}: CrudOperations<${interfaceName}>;`;\n })\n .join('\\n');\n\n // Wire-shape policy (#1797): the server returns BARE JSON — a bare array for\n // list/search, a bare object for get/create/update — with snake_case field\n // names (created_at, updated_at). These declarations match that shape (no\n // envelope, no camelCase) and are byte-identical to the vite-plugin client\n // declaration. Fetchers reject on non-2xx with a SmrtClientError (#1796).\n const clientDeclaration = `/**\n * Auto-generated API client module declaration\n */\ndeclare module '@smrt/client' {\n /** Shape of a JSON error body carried by a rejected request (SmrtClientError.body). */\n export interface ApiError {\n error?: string;\n message?: string;\n }\n\n /** Typed error thrown by every fetcher on a non-2xx response (#1796). */\n export interface SmrtClientError extends Error {\n name: 'SmrtClientError';\n status: number;\n body?: ApiError | string;\n }\n\n export interface CrudOperations<T = any> {\n list(params?: Record<string, any>): Promise<T[]>;\n get(id: string): Promise<T>;\n create(data: Partial<T>): Promise<T>;\n update(id: string, data: Partial<T>): Promise<T>;\n delete(id: string): Promise<boolean>;\n search(query: string): Promise<T[]>;\n }\n\n export interface ApiClient {\n${apiClientInterface}\n }\n\n export function createClient(basePath?: string): ApiClient;\n export default createClient;\n}`;\n\n // Generate routes module declaration\n const routesDeclaration = `/**\n * Auto-generated routes module declaration\n */\ndeclare module '@smrt/routes' {\n export interface RouteApp {\n get(path: string, handler: (req: any, res: any) => void): void;\n post(path: string, handler: (req: any, res: any) => void): void;\n put(path: string, handler: (req: any, res: any) => void): void;\n delete(path: string, handler: (req: any, res: any) => void): void;\n }\n\n export function setupRoutes(app: RouteApp): void;\n export default setupRoutes;\n}`;\n\n // Generate MCP module declaration\n const mcpDeclaration = `/**\n * Auto-generated MCP module declaration\n */\ndeclare module '@smrt/mcp' {\n export interface McpTool {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, any>;\n required?: string[];\n };\n }\n\n export const tools: McpTool[];\n export function createMCPServer(): {\n name: string;\n version: string;\n tools: McpTool[]\n };\n export default createMCPServer;\n}`;\n\n // Generate types module declaration with object imports\n const objectImports = Object.values(manifest.objects)\n .map(\n (obj) =>\n ` export type ${obj.className}Data = import('./smrt-objects').${obj.className}Data;`,\n )\n .join('\\n');\n\n const typesDeclaration = `/**\n * Auto-generated types module declaration\n */\ndeclare module '@smrt/types' {\n${objectImports}\n\n export interface Request {\n params: Record<string, string>;\n query: Record<string, any>;\n json(): Promise<any>;\n }\n\n export interface Response {\n json(data: any, init?: { status?: number }): Response;\n status(code: number): Response;\n }\n}`;\n\n // Write all virtual module declarations\n fs.writeFileSync(\n path.join(outDir, 'smrt-manifest.d.ts'),\n manifestDeclaration,\n );\n fs.writeFileSync(path.join(outDir, 'smrt-client.d.ts'), clientDeclaration);\n fs.writeFileSync(path.join(outDir, 'smrt-routes.d.ts'), routesDeclaration);\n fs.writeFileSync(path.join(outDir, 'smrt-mcp.d.ts'), mcpDeclaration);\n fs.writeFileSync(path.join(outDir, 'smrt-types.d.ts'), typesDeclaration);\n}\n\n/**\n * Map SMRT field types to TypeScript types\n */\nfunction mapFieldTypeToTypeScript(smrtType: string): string {\n switch (smrtType) {\n case 'text':\n return 'string';\n case 'decimal':\n case 'integer':\n return 'number';\n case 'boolean':\n return 'boolean';\n case 'datetime':\n return 'string | Date';\n case 'json':\n return 'any';\n case 'foreignKey':\n return 'string';\n default:\n return 'any';\n }\n}\n\n/**\n * CLI command for generating declarations\n */\nexport async function generateDeclarationsFromCLI(\n args: string[],\n): Promise<void> {\n const manifestPath = args[0];\n const outDir = args[1] || 'src/types/generated';\n\n if (!manifestPath) {\n console.error('Usage: generate-declarations <manifest-path> [output-dir]');\n process.exit(1);\n }\n\n if (!fs.existsSync(manifestPath)) {\n console.error(`Manifest file not found: ${manifestPath}`);\n process.exit(1);\n }\n\n await generateDeclarations({\n manifest: manifestPath,\n outDir,\n });\n}\n"],"mappings":"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"}
|
|
@@ -43,9 +43,16 @@ export declare function getInheritanceConfig(): {
|
|
|
43
43
|
* This allows the same class to be re-registered when modules are re-evaluated
|
|
44
44
|
* (e.g., during vitest test file collection with isolation enabled).
|
|
45
45
|
*
|
|
46
|
+
* Frames from decorator-lowering runtime helpers (`@oxc-project/runtime` under
|
|
47
|
+
* Vite 8, `tslib`/`@swc/helpers`/`@babel/runtime` elsewhere) are skipped too, so
|
|
48
|
+
* the lowered `@smrt()` application resolves to the consumer module that declares
|
|
49
|
+
* the class rather than the helper frame the compiler inserted (#1785).
|
|
50
|
+
*
|
|
51
|
+
* @param stackOverride - Optional stack string to parse instead of a fresh
|
|
52
|
+
* `Error().stack`. Used by tests to simulate a lowered call pattern.
|
|
46
53
|
* @returns The file path where the @smrt() decorator was called, or undefined
|
|
47
54
|
*/
|
|
48
|
-
export declare function getSourceFileFromStack(): string | undefined;
|
|
55
|
+
export declare function getSourceFileFromStack(stackOverride?: string): string | undefined;
|
|
49
56
|
export declare function getClasses(): Map<string, RegisteredClass>;
|
|
50
57
|
export declare function getCollections(): Map<string, typeof SmrtCollection>;
|
|
51
58
|
export declare function getCollectionTableNames(): Map<string, string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-state.d.ts","sourceRoot":"","sources":["../../src/registry/shared-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAMpD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"shared-state.d.ts","sourceRoot":"","sources":["../../src/registry/shared-state.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAMpD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC;AAGtE,OAAO,CAAC,MAAM,CAAC;IAEb,IAAI,qBAAqB,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,GAAG,SAAS,CAAC;IAEpE,IAAI,yBAAyB,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,cAAc,CAAC,GAAG,SAAS,CAAC;IAO9E,IAAI,6BAA6B,EAE7B,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,GACrC,SAAS,CAAC;IAEd,IAAI,2BAA2B,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAErE,IAAI,sBAAsB,EAAE,MAAM,GAAG,SAAS,CAAC;IAE/C,IAAI,mCAAmC,EACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAC1B,SAAS,CAAC;IAEd,IAAI,6BAA6B,EAC7B,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,GACjD,SAAS,CAAC;IAEd,IAAI,+BAA+B,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAE7D,IAAI,kCAAkC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IAMxE,IAAI,gCAAgC,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;IAElE,IAAI,8BAA8B,EAC9B,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,GACtC,SAAS,CAAC;IAEd,IAAI,mCAAmC,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CAC3E;AAED;;;;;GAKG;AACH,eAAO,MAAM,eAAe,SAEoB,CAAC;AAMjD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAQnD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB;;;EAOnC;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,CAAC,EAAE,MAAM,GACrB,MAAM,GAAG,SAAS,CA0CpB;AAID,wBAAgB,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAKzD;AAED,wBAAgB,cAAc,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,cAAc,CAAC,CAQnE;AAED,wBAAgB,uBAAuB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAK7D;AAGD,wBAAgB,kBAAkB,IAAI,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC,CAS1E;AAED,wBAAgB,gBAAgB,IAAI,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAK1D;AAED,wBAAgB,WAAW,IAAI,MAAM,CAKpC;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE/C;AAED,wBAAgB,kBAAkB,IAAI,GAAG,CACvC,MAAM,EACN,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CACrC,CAQA;AAED,wBAAgB,oBAAoB,IAAI,GAAG,CAAC,MAAM,CAAC,CAKlD;AAQD,wBAAgB,qBAAqB,IAAI,OAAO,CAAC,MAAM,CAAC,CAKvD;AAED,wBAAgB,mBAAmB,IAAI,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAQ5E;AAED,wBAAgB,mBAAmB,IAAI,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAShE;AAED,wBAAgB,wBAAwB,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAK/D"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getSmrtModuleConfig } from "../config/global-config.js";
|
|
2
2
|
import { LRUCache } from "../utils/lru-cache.js";
|
|
3
|
+
import { isDecoratorRuntimeFramePath } from "../utils/stack-frames.js";
|
|
3
4
|
import { createLogger } from "@happyvertical/logger";
|
|
4
5
|
//#region src/registry/shared-state.ts
|
|
5
6
|
/**
|
|
@@ -49,16 +50,23 @@ function getInheritanceConfig() {
|
|
|
49
50
|
* This allows the same class to be re-registered when modules are re-evaluated
|
|
50
51
|
* (e.g., during vitest test file collection with isolation enabled).
|
|
51
52
|
*
|
|
53
|
+
* Frames from decorator-lowering runtime helpers (`@oxc-project/runtime` under
|
|
54
|
+
* Vite 8, `tslib`/`@swc/helpers`/`@babel/runtime` elsewhere) are skipped too, so
|
|
55
|
+
* the lowered `@smrt()` application resolves to the consumer module that declares
|
|
56
|
+
* the class rather than the helper frame the compiler inserted (#1785).
|
|
57
|
+
*
|
|
58
|
+
* @param stackOverride - Optional stack string to parse instead of a fresh
|
|
59
|
+
* `Error().stack`. Used by tests to simulate a lowered call pattern.
|
|
52
60
|
* @returns The file path where the @smrt() decorator was called, or undefined
|
|
53
61
|
*/
|
|
54
|
-
function getSourceFileFromStack() {
|
|
55
|
-
const stackLines = ((/* @__PURE__ */ new Error()).stack
|
|
62
|
+
function getSourceFileFromStack(stackOverride) {
|
|
63
|
+
const stackLines = (stackOverride ?? (/* @__PURE__ */ new Error()).stack ?? "").split("\n");
|
|
56
64
|
for (const line of stackLines) {
|
|
57
65
|
const fileMatch = line.match(/(?:file:\/\/)?([^)\s]+\.(?:js|ts|mjs|mts|jsx|tsx|cjs|cts))(?::\d+:\d+)?/);
|
|
58
66
|
if (fileMatch) {
|
|
59
67
|
const normalizedPath = fileMatch[1].replace(/^file:\/+/, "").replace(/\\/g, "/");
|
|
60
68
|
const lowerPath = normalizedPath.toLowerCase();
|
|
61
|
-
if (lowerPath.includes("/node_modules/@happyvertical/smrt-core/") || lowerPath.includes("/packages/core/src/") && !lowerPath.includes("__tests__") || lowerPath.includes("/manifest/manifest-loader")) continue;
|
|
69
|
+
if (lowerPath.includes("/node_modules/@happyvertical/smrt-core/") || lowerPath.includes("/packages/core/src/") && !lowerPath.includes("__tests__") || lowerPath.includes("/manifest/manifest-loader") || isDecoratorRuntimeFramePath(normalizedPath)) continue;
|
|
62
70
|
return normalizedPath;
|
|
63
71
|
}
|
|
64
72
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-state.js","names":[],"sources":["../../src/registry/shared-state.ts"],"sourcesContent":["/**\n * Shared state accessors for the SMRT ObjectRegistry.\n *\n * All registry sub-modules share state via globalThis to ensure\n * cross-module consistency in monorepo environments.\n *\n * Extracted from registry.ts as part of issue #1006.\n * @see https://github.com/happyvertical/smrt/issues/1006\n * @see https://github.com/happyvertical/smrt/issues/543\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport type { SmrtCollection } from '../collection';\nimport {\n getSmrtModuleConfig,\n type SmrtGlobalConfig,\n} from '../config/global-config.js';\nimport type { SmrtObject } from '../object';\nimport { LRUCache } from '../utils/lru-cache';\nimport type { RegisteredClass, SmrtObjectConstructor } from './types';\n\n// Re-export the globalThis augmentation so it's visible everywhere\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtRegistryClasses: Map<string, RegisteredClass> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryCollections: Map<string, typeof SmrtCollection> | undefined;\n // eslint-disable-next-line no-var\n // Heterogeneous cache: stores collections of many different item types keyed\n // by class name. `SmrtCollection` is invariant in its item type (it has\n // `create(options: SmrtCreateInput<ModelType>)`), so this cannot be narrowed\n // to `SmrtCollection<SmrtObject>` — the type argument stays `any` (read sites\n // cast back to the concrete collection type).\n var __smrtRegistryCollectionCache:\n // biome-ignore lint/suspicious/noExplicitAny: heterogeneous cache keyed by class name; `SmrtCollection` is invariant in `ModelType` so the type arg cannot narrow to `SmrtObject` (read sites cast back). S4 #1579.\n | LRUCache<string, SmrtCollection<any>>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryDbInstanceIds: WeakMap<object, number> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryNextDbId: number | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryInheritanceChainCache:\n | LRUCache<string, string[]>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryFieldDecorators:\n | Map<string, Map<string, Record<string, unknown>>>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryStiSiblingsLoaded: Set<string> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryCollectionTableNames: Map<string, string> | undefined;\n // Release B (#1133) removed __smrtRegistryClassNameMap — case-insensitive\n // lookups now iterate the `classes` Map directly. The globalThis slot is\n // left undeclared so any leftover code that reaches for it gets a clean\n // TS error instead of silently stale data.\n // eslint-disable-next-line no-var\n var __smrtRegistrySchemasInitialized: WeakSet<object> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryConstructorIndex:\n | WeakMap<SmrtObjectConstructor, string>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryDiscoveryAttemptCache: Map<string, boolean> | undefined;\n}\n\n/**\n * Cached verbose flag evaluated once at module load time.\n * Checks SMRT_VERBOSE=true or DEBUG containing 'smrt'.\n * Avoids repeated env var parsing on every log statement.\n * @see https://github.com/happyvertical/smrt/issues/782\n */\nexport const VERBOSE_ENABLED =\n process.env.SMRT_VERBOSE === 'true' ||\n (process.env.DEBUG?.includes('smrt') ?? false);\n\n// verboseLog() is gated by VERBOSE_ENABLED, so the level must allow debug when\n// it's set (a fixed 'info' would filter the trace out and break SMRT_VERBOSE).\nconst logger = createLogger({ level: VERBOSE_ENABLED ? 'debug' : 'info' });\n\n/**\n * Log a message only when verbose mode is enabled.\n * Used throughout registry modules to reduce noise during normal operation.\n * @param args - Arguments to pass to console.log\n */\nexport function verboseLog(...args: unknown[]): void {\n if (VERBOSE_ENABLED) {\n const [message, ...rest] = args;\n logger.debug(\n typeof message === 'string' ? message : String(message),\n rest.length > 0 ? { args: rest } : undefined,\n );\n }\n}\n\n/**\n * Get inheritance config synchronously.\n * Uses the config package's sync accessor which returns already-loaded config.\n */\nexport function getInheritanceConfig() {\n const config = getSmrtModuleConfig<SmrtGlobalConfig>('smrt', {});\n return {\n cacheSize: config.inheritance?.cacheSize ?? 200,\n onMissingAncestor:\n config.inheritance?.onMissingAncestor ?? ('warn' as 'warn' | 'error'),\n };\n}\n\n/**\n * Extract the source file path from the call stack.\n *\n * Used to identify where a class was defined for collision detection.\n * This allows the same class to be re-registered when modules are re-evaluated\n * (e.g., during vitest test file collection with isolation enabled).\n *\n * @returns The file path where the @smrt() decorator was called, or undefined\n */\nexport function getSourceFileFromStack(): string | undefined {\n const error = new Error();\n const stack = error.stack || '';\n const stackLines = stack.split('\\n');\n\n // Look for the first file path that's NOT from smrt-core internal files\n // Stack trace format: \" at FunctionName (file:///path/to/file.ts:line:col)\"\n // or \" at file:///path/to/file.ts:line:col\"\n for (const line of stackLines) {\n // Match file paths in stack trace (include all JS/TS file extensions)\n const fileMatch = line.match(\n /(?:file:\\/\\/)?([^)\\s]+\\.(?:js|ts|mjs|mts|jsx|tsx|cjs|cts))(?::\\d+:\\d+)?/,\n );\n if (fileMatch) {\n const rawPath = fileMatch[1];\n // Normalize the path:\n // - remove any leading file:// or file:/// prefix\n // - convert Windows backslashes to POSIX-style forward slashes\n const normalizedPath = rawPath\n .replace(/^file:\\/+/, '')\n .replace(/\\\\/g, '/');\n const lowerPath = normalizedPath.toLowerCase();\n\n // Skip smrt-core internal files using specific path patterns\n // to avoid false positives with user files containing \"registry\" in name\n if (\n // Installed package form: node_modules/@happyvertical/smrt-core/...\n lowerPath.includes('/node_modules/@happyvertical/smrt-core/') ||\n // Monorepo/workspace form: packages/core/src/...\n (lowerPath.includes('/packages/core/src/') &&\n !lowerPath.includes('__tests__')) ||\n // Specific manifest loader internals\n lowerPath.includes('/manifest/manifest-loader')\n ) {\n continue;\n }\n return normalizedPath;\n }\n }\n return undefined;\n}\n\n// ── Shared state accessor functions ──────────────────────────────────────\n\nexport function getClasses(): Map<string, RegisteredClass> {\n if (!globalThis.__smrtRegistryClasses) {\n globalThis.__smrtRegistryClasses = new Map<string, RegisteredClass>();\n }\n return globalThis.__smrtRegistryClasses;\n}\n\nexport function getCollections(): Map<string, typeof SmrtCollection> {\n if (!globalThis.__smrtRegistryCollections) {\n globalThis.__smrtRegistryCollections = new Map<\n string,\n typeof SmrtCollection\n >();\n }\n return globalThis.__smrtRegistryCollections;\n}\n\nexport function getCollectionTableNames(): Map<string, string> {\n if (!globalThis.__smrtRegistryCollectionTableNames) {\n globalThis.__smrtRegistryCollectionTableNames = new Map<string, string>();\n }\n return globalThis.__smrtRegistryCollectionTableNames;\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: heterogeneous cache; `SmrtCollection` is invariant in `ModelType` so the type arg cannot narrow to `SmrtObject` (read sites cast back). S4 #1579.\nexport function getCollectionCache(): LRUCache<string, SmrtCollection<any>> {\n if (!globalThis.__smrtRegistryCollectionCache) {\n globalThis.__smrtRegistryCollectionCache = new LRUCache<\n string,\n // biome-ignore lint/suspicious/noExplicitAny: same heterogeneous-cache invariance as the return type above. S4 #1579.\n SmrtCollection<any>\n >(100);\n }\n return globalThis.__smrtRegistryCollectionCache;\n}\n\nexport function getDbInstanceIds(): WeakMap<object, number> {\n if (!globalThis.__smrtRegistryDbInstanceIds) {\n globalThis.__smrtRegistryDbInstanceIds = new WeakMap<object, number>();\n }\n return globalThis.__smrtRegistryDbInstanceIds;\n}\n\nexport function getNextDbId(): number {\n if (globalThis.__smrtRegistryNextDbId === undefined) {\n globalThis.__smrtRegistryNextDbId = 1;\n }\n return globalThis.__smrtRegistryNextDbId;\n}\n\nexport function setNextDbId(value: number): void {\n globalThis.__smrtRegistryNextDbId = value;\n}\n\nexport function getFieldDecorators(): Map<\n string,\n Map<string, Record<string, unknown>>\n> {\n if (!globalThis.__smrtRegistryFieldDecorators) {\n globalThis.__smrtRegistryFieldDecorators = new Map<\n string,\n Map<string, Record<string, unknown>>\n >();\n }\n return globalThis.__smrtRegistryFieldDecorators;\n}\n\nexport function getStiSiblingsLoaded(): Set<string> {\n if (!globalThis.__smrtRegistryStiSiblingsLoaded) {\n globalThis.__smrtRegistryStiSiblingsLoaded = new Set<string>();\n }\n return globalThis.__smrtRegistryStiSiblingsLoaded;\n}\n\n// getClassNameMap removed in Release B (#1133). The eagerly-maintained\n// lowercase-simple-name → qualified-key[] index was replaced by on-demand\n// iteration over the `classes` Map in name-resolver.ts. Any remaining\n// consumer should switch to `findClassesByName`, `getCanonicalClassName`,\n// or `findClass`.\n\nexport function getSchemasInitialized(): WeakSet<object> {\n if (!globalThis.__smrtRegistrySchemasInitialized) {\n globalThis.__smrtRegistrySchemasInitialized = new WeakSet<object>();\n }\n return globalThis.__smrtRegistrySchemasInitialized;\n}\n\nexport function getConstructorIndex(): WeakMap<SmrtObjectConstructor, string> {\n if (!globalThis.__smrtRegistryConstructorIndex) {\n globalThis.__smrtRegistryConstructorIndex = new WeakMap<\n SmrtObjectConstructor,\n string\n >();\n }\n return globalThis.__smrtRegistryConstructorIndex;\n}\n\nexport function getInheritanceCache(): LRUCache<string, string[]> {\n if (!globalThis.__smrtRegistryInheritanceChainCache) {\n const config = getInheritanceConfig();\n globalThis.__smrtRegistryInheritanceChainCache = new LRUCache<\n string,\n string[]\n >(config.cacheSize);\n }\n return globalThis.__smrtRegistryInheritanceChainCache;\n}\n\nexport function getDiscoveryAttemptCache(): Map<string, boolean> {\n if (!globalThis.__smrtRegistryDiscoveryAttemptCache) {\n globalThis.__smrtRegistryDiscoveryAttemptCache = new Map<string, boolean>();\n }\n return globalThis.__smrtRegistryDiscoveryAttemptCache;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAyEA,IAAa,kBACX,QAAQ,IAAI,iBAAiB,WAC5B,QAAQ,IAAI,OAAO,SAAS,MAAM,KAAK;AAI1C,IAAM,SAAS,aAAa,EAAE,OAAO,kBAAkB,UAAU,OAAO,CAAC;;;;;;AAOzE,SAAgB,WAAW,GAAG,MAAuB;CACnD,IAAI,iBAAiB;EACnB,MAAM,CAAC,SAAS,GAAG,QAAQ;EAC3B,OAAO,MACL,OAAO,YAAY,WAAW,UAAU,OAAO,OAAO,GACtD,KAAK,SAAS,IAAI,EAAE,MAAM,KAAK,IAAI,KAAA,CACrC;CACF;AACF;;;;;AAMA,SAAgB,uBAAuB;CACrC,MAAM,SAAS,oBAAsC,QAAQ,CAAC,CAAC;CAC/D,OAAO;EACL,WAAW,OAAO,aAAa,aAAa;EAC5C,mBACE,OAAO,aAAa,qBAAsB;CAC9C;AACF;;;;;;;;;;AAWA,SAAgB,yBAA6C;CAG3D,MAAM,+BADQ,IADI,MACJ,EAAA,CAAM,SAAS,GAAA,CACJ,MAAM,IAAI;CAKnC,KAAK,MAAM,QAAQ,YAAY;EAE7B,MAAM,YAAY,KAAK,MACrB,yEACF;EACA,IAAI,WAAW;GAKb,MAAM,iBAJU,UAAU,EAIH,CACpB,QAAQ,aAAa,EAAE,CAAC,CACxB,QAAQ,OAAO,GAAG;GACrB,MAAM,YAAY,eAAe,YAAY;GAI7C,IAEE,UAAU,SAAS,yCAAyC,KAE3D,UAAU,SAAS,qBAAqB,KACvC,CAAC,UAAU,SAAS,WAAW,KAEjC,UAAU,SAAS,2BAA2B,GAE9C;GAEF,OAAO;EACT;CACF;AAEF;AAIA,SAAgB,aAA2C;CACzD,IAAI,CAAC,WAAW,uBACd,WAAW,wCAAwB,IAAI,IAA6B;CAEtE,OAAO,WAAW;AACpB;AAEA,SAAgB,iBAAqD;CACnE,IAAI,CAAC,WAAW,2BACd,WAAW,4CAA4B,IAAI,IAGzC;CAEJ,OAAO,WAAW;AACpB;AAEA,SAAgB,0BAA+C;CAC7D,IAAI,CAAC,WAAW,oCACd,WAAW,qDAAqC,IAAI,IAAoB;CAE1E,OAAO,WAAW;AACpB;AAGA,SAAgB,qBAA4D;CAC1E,IAAI,CAAC,WAAW,+BACd,WAAW,gCAAgC,IAAI,SAI7C,GAAG;CAEP,OAAO,WAAW;AACpB;AAEA,SAAgB,mBAA4C;CAC1D,IAAI,CAAC,WAAW,6BACd,WAAW,8CAA8B,IAAI,QAAwB;CAEvE,OAAO,WAAW;AACpB;AAEA,SAAgB,cAAsB;CACpC,IAAI,WAAW,2BAA2B,KAAA,GACxC,WAAW,yBAAyB;CAEtC,OAAO,WAAW;AACpB;AAEA,SAAgB,YAAY,OAAqB;CAC/C,WAAW,yBAAyB;AACtC;AAEA,SAAgB,qBAGd;CACA,IAAI,CAAC,WAAW,+BACd,WAAW,gDAAgC,IAAI,IAG7C;CAEJ,OAAO,WAAW;AACpB;AAEA,SAAgB,uBAAoC;CAClD,IAAI,CAAC,WAAW,iCACd,WAAW,kDAAkC,IAAI,IAAY;CAE/D,OAAO,WAAW;AACpB;AAeA,SAAgB,sBAA8D;CAC5E,IAAI,CAAC,WAAW,gCACd,WAAW,iDAAiC,IAAI,QAG9C;CAEJ,OAAO,WAAW;AACpB;AAEA,SAAgB,sBAAkD;CAChE,IAAI,CAAC,WAAW,qCAAqC;EACnD,MAAM,SAAS,qBAAqB;EACpC,WAAW,sCAAsC,IAAI,SAGnD,OAAO,SAAS;CACpB;CACA,OAAO,WAAW;AACpB;AAEA,SAAgB,2BAAiD;CAC/D,IAAI,CAAC,WAAW,qCACd,WAAW,sDAAsC,IAAI,IAAqB;CAE5E,OAAO,WAAW;AACpB"}
|
|
1
|
+
{"version":3,"file":"shared-state.js","names":[],"sources":["../../src/registry/shared-state.ts"],"sourcesContent":["/**\n * Shared state accessors for the SMRT ObjectRegistry.\n *\n * All registry sub-modules share state via globalThis to ensure\n * cross-module consistency in monorepo environments.\n *\n * Extracted from registry.ts as part of issue #1006.\n * @see https://github.com/happyvertical/smrt/issues/1006\n * @see https://github.com/happyvertical/smrt/issues/543\n */\n\nimport { createLogger } from '@happyvertical/logger';\nimport type { SmrtCollection } from '../collection';\nimport {\n getSmrtModuleConfig,\n type SmrtGlobalConfig,\n} from '../config/global-config.js';\nimport type { SmrtObject } from '../object';\nimport { LRUCache } from '../utils/lru-cache';\nimport { isDecoratorRuntimeFramePath } from '../utils/stack-frames';\nimport type { RegisteredClass, SmrtObjectConstructor } from './types';\n\n// Re-export the globalThis augmentation so it's visible everywhere\ndeclare global {\n // eslint-disable-next-line no-var\n var __smrtRegistryClasses: Map<string, RegisteredClass> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryCollections: Map<string, typeof SmrtCollection> | undefined;\n // eslint-disable-next-line no-var\n // Heterogeneous cache: stores collections of many different item types keyed\n // by class name. `SmrtCollection` is invariant in its item type (it has\n // `create(options: SmrtCreateInput<ModelType>)`), so this cannot be narrowed\n // to `SmrtCollection<SmrtObject>` — the type argument stays `any` (read sites\n // cast back to the concrete collection type).\n var __smrtRegistryCollectionCache:\n // biome-ignore lint/suspicious/noExplicitAny: heterogeneous cache keyed by class name; `SmrtCollection` is invariant in `ModelType` so the type arg cannot narrow to `SmrtObject` (read sites cast back). S4 #1579.\n | LRUCache<string, SmrtCollection<any>>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryDbInstanceIds: WeakMap<object, number> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryNextDbId: number | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryInheritanceChainCache:\n | LRUCache<string, string[]>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryFieldDecorators:\n | Map<string, Map<string, Record<string, unknown>>>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryStiSiblingsLoaded: Set<string> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryCollectionTableNames: Map<string, string> | undefined;\n // Release B (#1133) removed __smrtRegistryClassNameMap — case-insensitive\n // lookups now iterate the `classes` Map directly. The globalThis slot is\n // left undeclared so any leftover code that reaches for it gets a clean\n // TS error instead of silently stale data.\n // eslint-disable-next-line no-var\n var __smrtRegistrySchemasInitialized: WeakSet<object> | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryConstructorIndex:\n | WeakMap<SmrtObjectConstructor, string>\n | undefined;\n // eslint-disable-next-line no-var\n var __smrtRegistryDiscoveryAttemptCache: Map<string, boolean> | undefined;\n}\n\n/**\n * Cached verbose flag evaluated once at module load time.\n * Checks SMRT_VERBOSE=true or DEBUG containing 'smrt'.\n * Avoids repeated env var parsing on every log statement.\n * @see https://github.com/happyvertical/smrt/issues/782\n */\nexport const VERBOSE_ENABLED =\n process.env.SMRT_VERBOSE === 'true' ||\n (process.env.DEBUG?.includes('smrt') ?? false);\n\n// verboseLog() is gated by VERBOSE_ENABLED, so the level must allow debug when\n// it's set (a fixed 'info' would filter the trace out and break SMRT_VERBOSE).\nconst logger = createLogger({ level: VERBOSE_ENABLED ? 'debug' : 'info' });\n\n/**\n * Log a message only when verbose mode is enabled.\n * Used throughout registry modules to reduce noise during normal operation.\n * @param args - Arguments to pass to console.log\n */\nexport function verboseLog(...args: unknown[]): void {\n if (VERBOSE_ENABLED) {\n const [message, ...rest] = args;\n logger.debug(\n typeof message === 'string' ? message : String(message),\n rest.length > 0 ? { args: rest } : undefined,\n );\n }\n}\n\n/**\n * Get inheritance config synchronously.\n * Uses the config package's sync accessor which returns already-loaded config.\n */\nexport function getInheritanceConfig() {\n const config = getSmrtModuleConfig<SmrtGlobalConfig>('smrt', {});\n return {\n cacheSize: config.inheritance?.cacheSize ?? 200,\n onMissingAncestor:\n config.inheritance?.onMissingAncestor ?? ('warn' as 'warn' | 'error'),\n };\n}\n\n/**\n * Extract the source file path from the call stack.\n *\n * Used to identify where a class was defined for collision detection.\n * This allows the same class to be re-registered when modules are re-evaluated\n * (e.g., during vitest test file collection with isolation enabled).\n *\n * Frames from decorator-lowering runtime helpers (`@oxc-project/runtime` under\n * Vite 8, `tslib`/`@swc/helpers`/`@babel/runtime` elsewhere) are skipped too, so\n * the lowered `@smrt()` application resolves to the consumer module that declares\n * the class rather than the helper frame the compiler inserted (#1785).\n *\n * @param stackOverride - Optional stack string to parse instead of a fresh\n * `Error().stack`. Used by tests to simulate a lowered call pattern.\n * @returns The file path where the @smrt() decorator was called, or undefined\n */\nexport function getSourceFileFromStack(\n stackOverride?: string,\n): string | undefined {\n const stack = stackOverride ?? new Error().stack ?? '';\n const stackLines = stack.split('\\n');\n\n // Look for the first file path that's NOT from smrt-core internal files\n // Stack trace format: \" at FunctionName (file:///path/to/file.ts:line:col)\"\n // or \" at file:///path/to/file.ts:line:col\"\n for (const line of stackLines) {\n // Match file paths in stack trace (include all JS/TS file extensions)\n const fileMatch = line.match(\n /(?:file:\\/\\/)?([^)\\s]+\\.(?:js|ts|mjs|mts|jsx|tsx|cjs|cts))(?::\\d+:\\d+)?/,\n );\n if (fileMatch) {\n const rawPath = fileMatch[1];\n // Normalize the path:\n // - remove any leading file:// or file:/// prefix\n // - convert Windows backslashes to POSIX-style forward slashes\n const normalizedPath = rawPath\n .replace(/^file:\\/+/, '')\n .replace(/\\\\/g, '/');\n const lowerPath = normalizedPath.toLowerCase();\n\n // Skip smrt-core internal files using specific path patterns\n // to avoid false positives with user files containing \"registry\" in name\n if (\n // Installed package form: node_modules/@happyvertical/smrt-core/...\n lowerPath.includes('/node_modules/@happyvertical/smrt-core/') ||\n // Monorepo/workspace form: packages/core/src/...\n (lowerPath.includes('/packages/core/src/') &&\n !lowerPath.includes('__tests__')) ||\n // Specific manifest loader internals\n lowerPath.includes('/manifest/manifest-loader') ||\n // Decorator-lowering runtime helper (oxc/tslib/swc/babel) — the frame\n // the compiler inserts between @smrt() and the declaring module (#1785)\n isDecoratorRuntimeFramePath(normalizedPath)\n ) {\n continue;\n }\n return normalizedPath;\n }\n }\n return undefined;\n}\n\n// ── Shared state accessor functions ──────────────────────────────────────\n\nexport function getClasses(): Map<string, RegisteredClass> {\n if (!globalThis.__smrtRegistryClasses) {\n globalThis.__smrtRegistryClasses = new Map<string, RegisteredClass>();\n }\n return globalThis.__smrtRegistryClasses;\n}\n\nexport function getCollections(): Map<string, typeof SmrtCollection> {\n if (!globalThis.__smrtRegistryCollections) {\n globalThis.__smrtRegistryCollections = new Map<\n string,\n typeof SmrtCollection\n >();\n }\n return globalThis.__smrtRegistryCollections;\n}\n\nexport function getCollectionTableNames(): Map<string, string> {\n if (!globalThis.__smrtRegistryCollectionTableNames) {\n globalThis.__smrtRegistryCollectionTableNames = new Map<string, string>();\n }\n return globalThis.__smrtRegistryCollectionTableNames;\n}\n\n// biome-ignore lint/suspicious/noExplicitAny: heterogeneous cache; `SmrtCollection` is invariant in `ModelType` so the type arg cannot narrow to `SmrtObject` (read sites cast back). S4 #1579.\nexport function getCollectionCache(): LRUCache<string, SmrtCollection<any>> {\n if (!globalThis.__smrtRegistryCollectionCache) {\n globalThis.__smrtRegistryCollectionCache = new LRUCache<\n string,\n // biome-ignore lint/suspicious/noExplicitAny: same heterogeneous-cache invariance as the return type above. S4 #1579.\n SmrtCollection<any>\n >(100);\n }\n return globalThis.__smrtRegistryCollectionCache;\n}\n\nexport function getDbInstanceIds(): WeakMap<object, number> {\n if (!globalThis.__smrtRegistryDbInstanceIds) {\n globalThis.__smrtRegistryDbInstanceIds = new WeakMap<object, number>();\n }\n return globalThis.__smrtRegistryDbInstanceIds;\n}\n\nexport function getNextDbId(): number {\n if (globalThis.__smrtRegistryNextDbId === undefined) {\n globalThis.__smrtRegistryNextDbId = 1;\n }\n return globalThis.__smrtRegistryNextDbId;\n}\n\nexport function setNextDbId(value: number): void {\n globalThis.__smrtRegistryNextDbId = value;\n}\n\nexport function getFieldDecorators(): Map<\n string,\n Map<string, Record<string, unknown>>\n> {\n if (!globalThis.__smrtRegistryFieldDecorators) {\n globalThis.__smrtRegistryFieldDecorators = new Map<\n string,\n Map<string, Record<string, unknown>>\n >();\n }\n return globalThis.__smrtRegistryFieldDecorators;\n}\n\nexport function getStiSiblingsLoaded(): Set<string> {\n if (!globalThis.__smrtRegistryStiSiblingsLoaded) {\n globalThis.__smrtRegistryStiSiblingsLoaded = new Set<string>();\n }\n return globalThis.__smrtRegistryStiSiblingsLoaded;\n}\n\n// getClassNameMap removed in Release B (#1133). The eagerly-maintained\n// lowercase-simple-name → qualified-key[] index was replaced by on-demand\n// iteration over the `classes` Map in name-resolver.ts. Any remaining\n// consumer should switch to `findClassesByName`, `getCanonicalClassName`,\n// or `findClass`.\n\nexport function getSchemasInitialized(): WeakSet<object> {\n if (!globalThis.__smrtRegistrySchemasInitialized) {\n globalThis.__smrtRegistrySchemasInitialized = new WeakSet<object>();\n }\n return globalThis.__smrtRegistrySchemasInitialized;\n}\n\nexport function getConstructorIndex(): WeakMap<SmrtObjectConstructor, string> {\n if (!globalThis.__smrtRegistryConstructorIndex) {\n globalThis.__smrtRegistryConstructorIndex = new WeakMap<\n SmrtObjectConstructor,\n string\n >();\n }\n return globalThis.__smrtRegistryConstructorIndex;\n}\n\nexport function getInheritanceCache(): LRUCache<string, string[]> {\n if (!globalThis.__smrtRegistryInheritanceChainCache) {\n const config = getInheritanceConfig();\n globalThis.__smrtRegistryInheritanceChainCache = new LRUCache<\n string,\n string[]\n >(config.cacheSize);\n }\n return globalThis.__smrtRegistryInheritanceChainCache;\n}\n\nexport function getDiscoveryAttemptCache(): Map<string, boolean> {\n if (!globalThis.__smrtRegistryDiscoveryAttemptCache) {\n globalThis.__smrtRegistryDiscoveryAttemptCache = new Map<string, boolean>();\n }\n return globalThis.__smrtRegistryDiscoveryAttemptCache;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA0EA,IAAa,kBACX,QAAQ,IAAI,iBAAiB,WAC5B,QAAQ,IAAI,OAAO,SAAS,MAAM,KAAK;AAI1C,IAAM,SAAS,aAAa,EAAE,OAAO,kBAAkB,UAAU,OAAO,CAAC;;;;;;AAOzE,SAAgB,WAAW,GAAG,MAAuB;CACnD,IAAI,iBAAiB;EACnB,MAAM,CAAC,SAAS,GAAG,QAAQ;EAC3B,OAAO,MACL,OAAO,YAAY,WAAW,UAAU,OAAO,OAAO,GACtD,KAAK,SAAS,IAAI,EAAE,MAAM,KAAK,IAAI,KAAA,CACrC;CACF;AACF;;;;;AAMA,SAAgB,uBAAuB;CACrC,MAAM,SAAS,oBAAsC,QAAQ,CAAC,CAAC;CAC/D,OAAO;EACL,WAAW,OAAO,aAAa,aAAa;EAC5C,mBACE,OAAO,aAAa,qBAAsB;CAC9C;AACF;;;;;;;;;;;;;;;;;AAkBA,SAAgB,uBACd,eACoB;CAEpB,MAAM,cADQ,kCAAiB,IAAI,MAAM,EAAA,CAAE,SAAS,GAAA,CAC3B,MAAM,IAAI;CAKnC,KAAK,MAAM,QAAQ,YAAY;EAE7B,MAAM,YAAY,KAAK,MACrB,yEACF;EACA,IAAI,WAAW;GAKb,MAAM,iBAJU,UAAU,EAIH,CACpB,QAAQ,aAAa,EAAE,CAAC,CACxB,QAAQ,OAAO,GAAG;GACrB,MAAM,YAAY,eAAe,YAAY;GAI7C,IAEE,UAAU,SAAS,yCAAyC,KAE3D,UAAU,SAAS,qBAAqB,KACvC,CAAC,UAAU,SAAS,WAAW,KAEjC,UAAU,SAAS,2BAA2B,KAG9C,4BAA4B,cAAc,GAE1C;GAEF,OAAO;EACT;CACF;AAEF;AAIA,SAAgB,aAA2C;CACzD,IAAI,CAAC,WAAW,uBACd,WAAW,wCAAwB,IAAI,IAA6B;CAEtE,OAAO,WAAW;AACpB;AAEA,SAAgB,iBAAqD;CACnE,IAAI,CAAC,WAAW,2BACd,WAAW,4CAA4B,IAAI,IAGzC;CAEJ,OAAO,WAAW;AACpB;AAEA,SAAgB,0BAA+C;CAC7D,IAAI,CAAC,WAAW,oCACd,WAAW,qDAAqC,IAAI,IAAoB;CAE1E,OAAO,WAAW;AACpB;AAGA,SAAgB,qBAA4D;CAC1E,IAAI,CAAC,WAAW,+BACd,WAAW,gCAAgC,IAAI,SAI7C,GAAG;CAEP,OAAO,WAAW;AACpB;AAEA,SAAgB,mBAA4C;CAC1D,IAAI,CAAC,WAAW,6BACd,WAAW,8CAA8B,IAAI,QAAwB;CAEvE,OAAO,WAAW;AACpB;AAEA,SAAgB,cAAsB;CACpC,IAAI,WAAW,2BAA2B,KAAA,GACxC,WAAW,yBAAyB;CAEtC,OAAO,WAAW;AACpB;AAEA,SAAgB,YAAY,OAAqB;CAC/C,WAAW,yBAAyB;AACtC;AAEA,SAAgB,qBAGd;CACA,IAAI,CAAC,WAAW,+BACd,WAAW,gDAAgC,IAAI,IAG7C;CAEJ,OAAO,WAAW;AACpB;AAEA,SAAgB,uBAAoC;CAClD,IAAI,CAAC,WAAW,iCACd,WAAW,kDAAkC,IAAI,IAAY;CAE/D,OAAO,WAAW;AACpB;AAeA,SAAgB,sBAA8D;CAC5E,IAAI,CAAC,WAAW,gCACd,WAAW,iDAAiC,IAAI,QAG9C;CAEJ,OAAO,WAAW;AACpB;AAEA,SAAgB,sBAAkD;CAChE,IAAI,CAAC,WAAW,qCAAqC;EACnD,MAAM,SAAS,qBAAqB;EACpC,WAAW,sCAAsC,IAAI,SAGnD,OAAO,SAAS;CACpB;CACA,OAAO,WAAW;AACpB;AAEA,SAAgB,2BAAiD;CAC/D,IAAI,CAAC,WAAW,qCACd,WAAW,sDAAsC,IAAI,IAAqB;CAE5E,OAAO,WAAW;AACpB"}
|
package/dist/smrt-knowledge.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"generatedAt": "2026-07-
|
|
3
|
+
"generatedAt": "2026-07-03T13:42:15.146Z",
|
|
4
4
|
"packageName": "@happyvertical/smrt-core",
|
|
5
|
-
"packageVersion": "0.37.
|
|
5
|
+
"packageVersion": "0.37.9",
|
|
6
6
|
"sourceManifestPath": "dist/manifest.json",
|
|
7
7
|
"agentDocPath": "AGENTS.md",
|
|
8
8
|
"sourceHashes": {
|
|
9
|
-
"manifest": "
|
|
10
|
-
"packageJson": "
|
|
11
|
-
"agents": "
|
|
9
|
+
"manifest": "0e59225df9b49aa927903db71737889bf25f9a95a8aae9117949a793c5dc04fb",
|
|
10
|
+
"packageJson": "475794e43ab597082f1e6225594c5d810985e63c87df0666c67502a8bf42d30c",
|
|
11
|
+
"agents": "ec7122d3c53ba33c081de4acf740e1e2e6c013a330d4b778ab9e04cb39e5e6a3"
|
|
12
12
|
},
|
|
13
13
|
"exports": [
|
|
14
14
|
".",
|
|
@@ -323,5 +323,5 @@
|
|
|
323
323
|
"polymorphicAssociations": 1,
|
|
324
324
|
"uuidColumns": 3
|
|
325
325
|
},
|
|
326
|
-
"agentDoc": "# @happyvertical/smrt-core\n\nORM, code generation, AI integration, and the DispatchBus. Everything else builds on this.\n\n## Key Classes\n\n| Class | File | Purpose |\n|-------|------|---------|\n| SmrtObject | `src/object.ts` | Base persistent object — save, delete, is(), do(), loadFromId/Slug |\n| SmrtCollection | `src/collection.ts` | CRUD collection — list, get, create, delete, getOrUpsert |\n| ObjectRegistry | `src/registry.ts` | Global singleton (globalThis) — class metadata, fields, STI chains, manifests |\n| DispatchBus | `src/dispatch/bus.ts` | Inter-agent messaging — emit, subscribe (persistent), process |\n| GlobalInterceptors | `src/interceptors.ts` | Plugin system — beforeList/Get/Save/Delete hooks (used by tenancy) |\n\n## SmrtObject Lifecycle\n\n`constructor(options)` → `initialize()` → ready for `save()`/`delete()`/`loadFromId()`\n\n- `initialize()`: loads field initializers, applies option values (options override initializers), loads from DB if id/slug provided\n- `save()`: upsert with STI validation, interceptor execution, auto-embeddings. Persisted objects (`isPersisted` — set by DB hydration and successful saves) upsert on `['id']` so natural-key edits (e.g. slug renames) update in place; new objects upsert on the natural-key conflict columns for ingestion-style dedup (#1472)\n- `is(criteria)` / `do(instructions)` / `describe()`: AI operations via function calling. They inject the object's own `toPublicJSON()` (sensitive fields stripped) as a \"content body\" so the model reasons over the instance. Options: `includeData: false` skips injection (for callers that already curate the relevant fields into the instruction); `maxDataLength` overrides the truncation budget. Neither key is forwarded to `ai.message()`. (#1567)\n- `getSlug()`: auto-generates from name → title → label → id\n- `loadRelated(fieldName)`: lazy-loads relationships (cached in `_loadedRelationships` Map)\n\n## SmrtCollection Query\n\n```typescript\nawait collection.list({\n where: { status: 'active', price: { op: '>', value: 10 } },\n limit: 50, offset: 0, orderBy: 'created_at DESC'\n});\n```\n\n**WHERE operators**: `=`, `>`, `<`, `>=`, `<=`, `!=`, `in`, `not in`, `like`, `is null`, `is not null`. Arrays auto-detect `IN`. Dot notation for JSON paths: `metadata.userId`.\n\nSTI child collections auto-filter by `_meta_type`.\n\n## @smrt() Decorator Options\n\nKey options: `tableName`, `tableStrategy` ('cti'|'sti'), `conflictColumns`, `api`/`mcp`/`cli` (generation config), `ai` (callable methods), `hooks` (beforeSave/afterSave/beforeDelete/afterDelete), `embeddings` (auto-generate), `tenantScoped`, `agent`.\n\nRegistration sets `SMRT_TABLE_NAME` static property (survives minification).\n\n## Domain Knowledge Artifacts\n\n`smrtPlugin()` writes runtime manifests and agent/developer knowledge artifacts:\n\n- local dev/build: `.smrt/manifest.json` and `.smrt/smrt-knowledge.json`\n- package build: `dist/manifest.json` and `dist/smrt-knowledge.json`\n\nKeep `manifest.json` runtime-focused. `smrt-knowledge.json` is the deterministic\nagent contract for downstream review and architecture tools.\n\nConfig precedence for knowledge is defaults → top-level `knowledge` in\n`smrt.config.ts` → `packages[packageName].knowledge` → plugin option →\nobject-level `@smrt({ knowledge })`.\n\nObject-level `knowledge: false` excludes an object from authored context only;\nit must not change runtime manifest registration. Use\n`knowledge: { tags, summary, risks }` for review-sensitive domain objects.\n\nHTTP knowledge routes are disabled by default. If `knowledge.api.enabled` is\ntrue, generated SvelteKit routes must stay GET-only and guarded by dev mode or\nadmin auth.\n\n## DispatchBus\n\n- `emit(signalType, payload, metadata)` → creates persistent Dispatch record\n- `on(pattern, handler)` → in-memory handler (immediate)\n- `subscribe({ signalType, subscriber })` → persistent subscription (survives restarts)\n- `process(subscriberName, handler)` → process pending dispatches\n- Wildcards: `campaign.*` matches `campaign.completed` (single segment only)\n- Tables: `_smrt_dispatch`, `_smrt_dispatch_subscriptions`\n- Status: `pending → processing → completed` (or `failed`)\n\n## Single Table Inheritance (STI)\n\n- Base: `@smrt({ tableStrategy: 'sti' })` — children inherit, share one table\n- Discriminator: `_meta_type` column with qualified names (`@happyvertical/smrt-content:Article`)\n- Child fields: `@meta()` decorator → stored in `_meta_data` JSONB (not as columns)\n- Polymorphic queries: collection loads `_meta_type`, creates correct subclass dynamically\n- Validation: fail-fast on save if `_meta_type` missing or mismatched\n\n## Code Generators\n\n| Generator | Location | Output |\n|-----------|----------|--------|\n| REST API | `src/generators/rest.ts` | OpenAPI-compliant CRUD endpoints |\n| CLI | `src/generators/cli.ts` | `objectname:action` admin commands — writable allowlist, exhaustive-include, `--from-file`, fail-closed tenant context |\n| MCP Server | `src/generators/mcp.ts` | Model Context Protocol tools |\n\nGenerated reads (`list`/`get`) on the REST and SvelteKit generators support conditional GET (#1757, helpers in `src/generators/conditional-get.ts`): strong body-hash ETag, `If-None-Match` → 304 with an empty body, `private, no-cache` by default. Public models may opt into shared caching via `@smrt({ api: { public: true | 'read', cache: { sMaxage } } })` → `public, max-age=0, s-maxage=<n>`; non-public models never emit shared-cache headers. Tenant-scoped models (any mode) never emit them either — bodies vary with session-cookie tenant context that URL-keyed shared caches cannot see; `sMaxage` is neutralized to `private, no-cache` with a one-time warning.\n\n## Child Accessors (R10)\n\n`src/child-accessors.ts` installs a consistent `get<FieldName>()` instance method for every `@oneToMany` field at `@smrt()` registration time (e.g. `@oneToMany('OrderItem') items` → `order.getItems()`), delegating to `loadRelatedMany`. Two invariants:\n\n- **Additive** — never overwrites a hand-rolled method of the same name (checks the whole prototype chain). `Profile.getMetadata()` (key-value) and `ProfileRelationship.getTerms()` are preserved.\n- **Runtime-only** — attached to the prototype, invisible to the build-time manifest, so it never leaks into the REST/CLI/MCP surface.\n\nWhen the target declares multiple FKs back to the parent, annotate `@oneToMany(Target, { foreignKey: '<inverseField>' })`; `loadRelatedMany` and the eager `include:` loader both honor it (else first-match).\n\n## Vite Plugin\n\n```typescript\n// vite.config.ts — required for @smrt() decorators\nexport default defineConfig({\n esbuild: {\n tsconfigRaw: {\n compilerOptions: { experimentalDecorators: true, emitDecoratorMetadata: true }\n }\n }\n});\n```\n\n## Gotchas\n\n- **Never override toJSON()** — handles STI discriminator + meta field extraction. Use `transformJSON()`\n- **Property init order**: TypeScript initializers run first, then `initialize()` applies option values (options win)\n- **No runtime schema creation**: application tables must be prepared explicitly via migrations/tooling; runtime only verifies and fails clearly\n- **Retry logic**: `db.get()` (3 retries, 250ms) and `db.upsert()` (3 retries, 500ms) have built-in retry\n- **Field caching**: `_cachedFields` populated during `Collection.create()` — eliminates async `getFields()` per query\n- **Smart cloning**: arrays/objects shallow-cloned in property init to prevent aliasing (Issue #22)\n- **Table verification cache**: `isTableVerified(dbUrl, tableName)` avoids redundant `tableExists()` calls\n- **Manifest required**: build-time AST scanning creates manifest. Without vitest plugin → \"No field metadata\"\n- **Vite plugin loads scanner from `dist/` first**: `src/vite-plugin/import-build-aware.ts` prefers `dist/` when it exists on disk; it only falls back to `src/` on fresh clones. So if you edit `src/scanner/*.ts` or `src/schema/generator.ts` and want those edits reflected in consumer manifest generation, you must rebuild (`pnpm build` or have `pnpm dev` / `pnpm build:watch` running in core). This is intentional — sniffing `.ts` vs `.js` via `import.meta.url` was non-deterministic under tsx and broke 12–13 publishes (#1139).\n"
|
|
326
|
+
"agentDoc": "# @happyvertical/smrt-core\n\nORM, code generation, AI integration, and the DispatchBus. Everything else builds on this.\n\n## Key Classes\n\n| Class | File | Purpose |\n|-------|------|---------|\n| SmrtObject | `src/object.ts` | Base persistent object — save, delete, is(), do(), loadFromId/Slug |\n| SmrtCollection | `src/collection.ts` | CRUD collection — list, get, create, delete, getOrUpsert |\n| ObjectRegistry | `src/registry.ts` | Global singleton (globalThis) — class metadata, fields, STI chains, manifests |\n| DispatchBus | `src/dispatch/bus.ts` | Inter-agent messaging — emit, subscribe (persistent), process |\n| GlobalInterceptors | `src/interceptors.ts` | Plugin system — beforeList/Get/Save/Delete hooks (used by tenancy) |\n\n## SmrtObject Lifecycle\n\n`constructor(options)` → `initialize()` → ready for `save()`/`delete()`/`loadFromId()`\n\n- `initialize()`: loads field initializers, applies option values (options override initializers), loads from DB if id/slug provided\n- `save()`: upsert with STI validation, interceptor execution, auto-embeddings. Persisted objects (`isPersisted` — set by DB hydration and successful saves) upsert on `['id']` so natural-key edits (e.g. slug renames) update in place; new objects upsert on the natural-key conflict columns for ingestion-style dedup (#1472)\n- `is(criteria)` / `do(instructions)` / `describe()`: AI operations via function calling. They inject the object's own `toPublicJSON()` (sensitive fields stripped) as a \"content body\" so the model reasons over the instance. Options: `includeData: false` skips injection (for callers that already curate the relevant fields into the instruction); `maxDataLength` overrides the truncation budget. Neither key is forwarded to `ai.message()`. (#1567)\n- `getSlug()`: auto-generates from name → title → label → id\n- `loadRelated(fieldName)`: lazy-loads relationships (cached in `_loadedRelationships` Map)\n\n## SmrtCollection Query\n\n```typescript\nawait collection.list({\n where: { status: 'active', price: { op: '>', value: 10 } },\n limit: 50, offset: 0, orderBy: 'created_at DESC'\n});\n```\n\n**WHERE operators**: `=`, `>`, `<`, `>=`, `<=`, `!=`, `in`, `not in`, `like`, `is null`, `is not null`. Arrays auto-detect `IN`. Dot notation for JSON paths: `metadata.userId`.\n\nSTI child collections auto-filter by `_meta_type`.\n\n## @smrt() Decorator Options\n\nKey options: `tableName`, `tableStrategy` ('cti'|'sti'), `conflictColumns`, `api`/`mcp`/`cli` (generation config), `ai` (callable methods), `hooks` (beforeSave/afterSave/beforeDelete/afterDelete), `embeddings` (auto-generate), `tenantScoped`, `agent`.\n\nRegistration sets `SMRT_TABLE_NAME` static property (survives minification).\n\n## Domain Knowledge Artifacts\n\n`smrtPlugin()` writes runtime manifests and agent/developer knowledge artifacts:\n\n- local dev/build: `.smrt/manifest.json` and `.smrt/smrt-knowledge.json`\n- package build: `dist/manifest.json` and `dist/smrt-knowledge.json`\n\nKeep `manifest.json` runtime-focused. `smrt-knowledge.json` is the deterministic\nagent contract for downstream review and architecture tools.\n\nConfig precedence for knowledge is defaults → top-level `knowledge` in\n`smrt.config.ts` → `packages[packageName].knowledge` → plugin option →\nobject-level `@smrt({ knowledge })`.\n\nObject-level `knowledge: false` excludes an object from authored context only;\nit must not change runtime manifest registration. Use\n`knowledge: { tags, summary, risks }` for review-sensitive domain objects.\n\nHTTP knowledge routes are disabled by default. If `knowledge.api.enabled` is\ntrue, generated SvelteKit routes must stay GET-only and guarded by dev mode or\nadmin auth.\n\n## DispatchBus\n\n- `emit(signalType, payload, metadata)` → creates persistent Dispatch record\n- `on(pattern, handler)` → in-memory handler (immediate)\n- `subscribe({ signalType, subscriber })` → persistent subscription (survives restarts)\n- `process(subscriberName, handler)` → process pending dispatches\n- Wildcards: `campaign.*` matches `campaign.completed` (single segment only)\n- Tables: `_smrt_dispatch`, `_smrt_dispatch_subscriptions`\n- Status: `pending → processing → completed` (or `failed`)\n\n## Change Feed (#1758)\n\nAdapter-agnostic change-observation spine (`src/change-feed.ts`) — the server half of the client/mobile sync contract (PRD #1755):\n\n- `_smrt_changes` system table: one append per framework save/delete via a GlobalInterceptors writer registered at framework init. Deletes are tombstones (`operation: 'delete'`). `_smrt_*` tables are skipped. Feed-append failures log and never fail the user's write. No dirty-check: a field-unchanged `.save()` appends a spurious `update` entry (diff-aware paths like `getOrUpsert`/sync-apply short-circuit before `save()` and append nothing); subscribers must tolerate spurious entries — they are convergent.\n- Sequences: allocated as `MAX(seq)+1` inside the INSERT with conflict retry — committed rows stay contiguous, so commit order == seq order on SQLite/Postgres/DuckDB (deliberately NOT identity/serial: those allocate before commit and break the cursor guarantee under concurrent writers).\n- `getChangesSince(db, { since, tables?, tenantId?, limit? }) → { changes, cursor, resyncRequired? }`: strictly monotonic cursor; polling with returned cursors misses no committed change and never repeats one. A cursor that cannot be served incrementally — pruned below the retained `[floor..horizon]` run, or foreign/ahead of the horizon — gets `resyncRequired: true` with empty `changes` and an unadvanced cursor; detection runs on the UNFILTERED log so `tables`/`tenantId` filters never trigger or mask it. `getTenantScopedChangesSince()` resolves tenant via the DispatchBus resolver hook (fail-closed: tenancy on + no context → global rows only; tenant `T` sees `T` + global rows, never another tenant).\n- Generated `_changes` routes: REST (`GET {basePath}/_changes`, requires `authMiddleware`, otherwise 401 — per-model `api.public` does NOT apply) and SvelteKit (`{routesDir}/_changes/+server.ts`, requires an authenticated principal on `locals`; opt out via `sveltekit.changesRoute.enabled: false`). Query params: `since`, `tables` (comma-separated), `limit`. Responses stay HTTP 200 in the resync state — `resyncRequired` is protocol state, not an error.\n- Retention: `pruneChangeFeed(db, { maxAgeMs?, maxRows? })` — schedule it. Pruning deletes oldest-first and always retains the newest entry (a non-empty feed is never emptied), which is what makes pruned-cursor detection provable and keeps caught-up consumers polling normally. Raw-SQL writes are invisible to the feed (same documented gap as the #1499 cache); `bumpChangeFeed(db, { table, rowId? })` is the manual escape hatch.\n\n## Single Table Inheritance (STI)\n\n- Base: `@smrt({ tableStrategy: 'sti' })` — children inherit, share one table\n- Discriminator: `_meta_type` column with qualified names (`@happyvertical/smrt-content:Article`)\n- Child fields: `@meta()` decorator → stored in `_meta_data` JSONB (not as columns)\n- Polymorphic queries: collection loads `_meta_type`, creates correct subclass dynamically\n- Validation: fail-fast on save if `_meta_type` missing or mismatched\n\n## Code Generators\n\n| Generator | Location | Output |\n|-----------|----------|--------|\n| REST API | `src/generators/rest.ts` | OpenAPI-compliant CRUD endpoints |\n| CLI | `src/generators/cli.ts` | `objectname:action` admin commands — writable allowlist, exhaustive-include, `--from-file`, fail-closed tenant context |\n| MCP Server | `src/generators/mcp.ts` | Model Context Protocol tools |\n\nGenerated reads (`list`/`get`) on the REST and SvelteKit generators support conditional GET (#1757, helpers in `src/generators/conditional-get.ts`): strong body-hash ETag, `If-None-Match` → 304 with an empty body, `private, no-cache` by default. Public models may opt into shared caching via `@smrt({ api: { public: true | 'read', cache: { sMaxage } } })` → `public, max-age=0, s-maxage=<n>`; non-public models never emit shared-cache headers. Tenant-scoped models (any mode) never emit them either — bodies vary with session-cookie tenant context that URL-keyed shared caches cannot see; `sMaxage` is neutralized to `private, no-cache` with a one-time warning.\n\n## Child Accessors (R10)\n\n`src/child-accessors.ts` installs a consistent `get<FieldName>()` instance method for every `@oneToMany` field at `@smrt()` registration time (e.g. `@oneToMany('OrderItem') items` → `order.getItems()`), delegating to `loadRelatedMany`. Two invariants:\n\n- **Additive** — never overwrites a hand-rolled method of the same name (checks the whole prototype chain). `Profile.getMetadata()` (key-value) and `ProfileRelationship.getTerms()` are preserved.\n- **Runtime-only** — attached to the prototype, invisible to the build-time manifest, so it never leaks into the REST/CLI/MCP surface.\n\nWhen the target declares multiple FKs back to the parent, annotate `@oneToMany(Target, { foreignKey: '<inverseField>' })`; `loadRelatedMany` and the eager `include:` loader both honor it (else first-match).\n\n## Vite Plugin\n\n```typescript\n// vite.config.ts — required for @smrt() decorators (Vite 8+, oxc transform)\nexport default defineConfig({\n oxc: {\n decorator: {\n legacy: true,\n emitDecoratorMetadata: true,\n },\n },\n});\n```\n\nUnder Vite 8 the oxc transform does not honor the pre-Vite-8 `esbuild.tsconfigRaw`\nrecipe (or tsconfig `experimentalDecorators` reached through SvelteKit's\n`extends \"./.svelte-kit/tsconfig.json\"` chain), so that recipe throws\n`SyntaxError: Invalid or unexpected token` on the first SSR request. Configure\ndecorators through `oxc.decorator` instead. Consumers still pinned on vite<8 need\nthe legacy `esbuild.tsconfigRaw` form with `experimentalDecorators: true,\nemitDecoratorMetadata: true`.\n\n## Gotchas\n\n- **Never override toJSON()** — handles STI discriminator + meta field extraction. Use `transformJSON()`\n- **Property init order**: TypeScript initializers run first, then `initialize()` applies option values (options win)\n- **No runtime schema creation**: application tables must be prepared explicitly via migrations/tooling; runtime only verifies and fails clearly\n- **Retry logic**: `db.get()` (3 retries, 250ms) and `db.upsert()` (3 retries, 500ms) have built-in retry\n- **Field caching**: `_cachedFields` populated during `Collection.create()` — eliminates async `getFields()` per query\n- **Smart cloning**: arrays/objects shallow-cloned in property init to prevent aliasing (Issue #22)\n- **Table verification cache**: `isTableVerified(dbUrl, tableName)` avoids redundant `tableExists()` calls\n- **Manifest required**: build-time AST scanning creates manifest. Without vitest plugin → \"No field metadata\"\n- **Vite plugin loads scanner from `dist/` first**: `src/vite-plugin/import-build-aware.ts` prefers `dist/` when it exists on disk; it only falls back to `src/` on fresh clones. So if you edit `src/scanner/*.ts` or `src/schema/generator.ts` and want those edits reflected in consumer manifest generation, you must rebuild (`pnpm build` or have `pnpm dev` / `pnpm build:watch` running in core). This is intentional — sniffing `.ts` vs `.js` via `import.meta.url` was non-deterministic under tsx and broke 12–13 publishes (#1139).\n"
|
|
327
327
|
}
|
package/dist/system/schema.d.ts
CHANGED
|
@@ -49,6 +49,29 @@ export declare const CREATE_SMRT_DISPATCH_SUBSCRIPTIONS_TABLE = "\nCREATE TABLE
|
|
|
49
49
|
* Stores normalized AI usage records for reporting and billing hooks
|
|
50
50
|
*/
|
|
51
51
|
export declare const CREATE_SMRT_AI_USAGE_TABLE = "\nCREATE TABLE IF NOT EXISTS _smrt_ai_usage (\n id TEXT PRIMARY KEY,\n provider TEXT NOT NULL,\n model TEXT NOT NULL,\n operation TEXT NOT NULL,\n prompt_tokens INTEGER,\n completion_tokens INTEGER,\n total_tokens INTEGER,\n estimated_cost REAL,\n duration INTEGER NOT NULL,\n class_name TEXT,\n tenant_id TEXT,\n tags TEXT,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_created\n ON _smrt_ai_usage(created_at);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_class\n ON _smrt_ai_usage(class_name);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_tenant\n ON _smrt_ai_usage(tenant_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_provider_model\n ON _smrt_ai_usage(provider, model);\n";
|
|
52
|
+
/**
|
|
53
|
+
* Append-only change feed (issue #1758)
|
|
54
|
+
*
|
|
55
|
+
* One row per framework save/delete: monotonic per-database sequence,
|
|
56
|
+
* table, row id, operation (create/update/delete — deletes are tombstones),
|
|
57
|
+
* tenant, timestamp. Written by the change-feed interceptor and read
|
|
58
|
+
* through `getChangesSince()`.
|
|
59
|
+
*
|
|
60
|
+
* `seq` is deliberately a plain BIGINT PRIMARY KEY rather than a native
|
|
61
|
+
* AUTOINCREMENT/identity/serial column: the appender allocates
|
|
62
|
+
* `COALESCE(MAX(seq), 0) + 1` inside the INSERT (with a conflict retry),
|
|
63
|
+
* which keeps committed sequences contiguous so commit order equals
|
|
64
|
+
* sequence order on every engine. Native identity columns allocate before
|
|
65
|
+
* commit, so under concurrent writers on MVCC engines a reader could
|
|
66
|
+
* observe seq N+1 while seq N is still uncommitted and advance its cursor
|
|
67
|
+
* past it — breaking the feed's no-missed-changes cursor guarantee. The
|
|
68
|
+
* plain column also keeps this DDL portable across SQLite, Postgres and
|
|
69
|
+
* DuckDB with no per-engine branching. See `change-feed.ts`.
|
|
70
|
+
*
|
|
71
|
+
* `row_id` is nullable: manual bumps (`bumpChangeFeed`) may record a
|
|
72
|
+
* table-level change without a specific row.
|
|
73
|
+
*/
|
|
74
|
+
export declare const CREATE_SMRT_CHANGES_TABLE = "\nCREATE TABLE IF NOT EXISTS _smrt_changes (\n seq BIGINT PRIMARY KEY,\n table_name TEXT NOT NULL,\n row_id TEXT,\n operation TEXT NOT NULL,\n tenant_id TEXT,\n created_at TIMESTAMP NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_changes_table_seq\n ON _smrt_changes(table_name, seq);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_changes_tenant_seq\n ON _smrt_changes(tenant_id, seq);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_changes_created_at\n ON _smrt_changes(created_at);\n";
|
|
52
75
|
/**
|
|
53
76
|
* Data backfill tracking
|
|
54
77
|
*
|
|
@@ -65,5 +88,5 @@ export declare const ALL_SYSTEM_TABLES: string[];
|
|
|
65
88
|
/**
|
|
66
89
|
* Current SMRT system schema version
|
|
67
90
|
*/
|
|
68
|
-
export declare const SMRT_SCHEMA_VERSION = "1.
|
|
91
|
+
export declare const SMRT_SCHEMA_VERSION = "1.7.0";
|
|
69
92
|
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/system/schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,eAAO,MAAM,0BAA0B,+5BA+BtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,4BAA4B,4MAQxC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mCAAmC,o0BA4B/C,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,0BAA0B,6OAUtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,sgBAoBrC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,4BAA4B,u0BA2BxC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,0BAA0B,kmCAwCtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wCAAwC,69CAkCpD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,0BAA0B,swBA4BtC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,gLAOvC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/system/schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;GAGG;AACH,eAAO,MAAM,0BAA0B,+5BA+BtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,4BAA4B,4MAQxC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mCAAmC,o0BA4B/C,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,0BAA0B,6OAUtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,yBAAyB,sgBAoBrC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,4BAA4B,u0BA2BxC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,0BAA0B,kmCAwCtC,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,wCAAwC,69CAkCpD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,0BAA0B,swBA4BtC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,yBAAyB,weAkBrC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,gLAOvC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,UAY7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,UAAU,CAAC"}
|
package/dist/system/schema.js
CHANGED
|
@@ -277,6 +277,47 @@ CREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_provider_model
|
|
|
277
277
|
ON _smrt_ai_usage(provider, model);
|
|
278
278
|
`;
|
|
279
279
|
/**
|
|
280
|
+
* Append-only change feed (issue #1758)
|
|
281
|
+
*
|
|
282
|
+
* One row per framework save/delete: monotonic per-database sequence,
|
|
283
|
+
* table, row id, operation (create/update/delete — deletes are tombstones),
|
|
284
|
+
* tenant, timestamp. Written by the change-feed interceptor and read
|
|
285
|
+
* through `getChangesSince()`.
|
|
286
|
+
*
|
|
287
|
+
* `seq` is deliberately a plain BIGINT PRIMARY KEY rather than a native
|
|
288
|
+
* AUTOINCREMENT/identity/serial column: the appender allocates
|
|
289
|
+
* `COALESCE(MAX(seq), 0) + 1` inside the INSERT (with a conflict retry),
|
|
290
|
+
* which keeps committed sequences contiguous so commit order equals
|
|
291
|
+
* sequence order on every engine. Native identity columns allocate before
|
|
292
|
+
* commit, so under concurrent writers on MVCC engines a reader could
|
|
293
|
+
* observe seq N+1 while seq N is still uncommitted and advance its cursor
|
|
294
|
+
* past it — breaking the feed's no-missed-changes cursor guarantee. The
|
|
295
|
+
* plain column also keeps this DDL portable across SQLite, Postgres and
|
|
296
|
+
* DuckDB with no per-engine branching. See `change-feed.ts`.
|
|
297
|
+
*
|
|
298
|
+
* `row_id` is nullable: manual bumps (`bumpChangeFeed`) may record a
|
|
299
|
+
* table-level change without a specific row.
|
|
300
|
+
*/
|
|
301
|
+
var CREATE_SMRT_CHANGES_TABLE = `
|
|
302
|
+
CREATE TABLE IF NOT EXISTS _smrt_changes (
|
|
303
|
+
seq BIGINT PRIMARY KEY,
|
|
304
|
+
table_name TEXT NOT NULL,
|
|
305
|
+
row_id TEXT,
|
|
306
|
+
operation TEXT NOT NULL,
|
|
307
|
+
tenant_id TEXT,
|
|
308
|
+
created_at TIMESTAMP NOT NULL
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
CREATE INDEX IF NOT EXISTS idx_smrt_changes_table_seq
|
|
312
|
+
ON _smrt_changes(table_name, seq);
|
|
313
|
+
|
|
314
|
+
CREATE INDEX IF NOT EXISTS idx_smrt_changes_tenant_seq
|
|
315
|
+
ON _smrt_changes(tenant_id, seq);
|
|
316
|
+
|
|
317
|
+
CREATE INDEX IF NOT EXISTS idx_smrt_changes_created_at
|
|
318
|
+
ON _smrt_changes(created_at);
|
|
319
|
+
`;
|
|
320
|
+
/**
|
|
280
321
|
* Data backfill tracking
|
|
281
322
|
*
|
|
282
323
|
* Distinct from `_smrt_schema_migrations` — backfills are app-specific
|
|
@@ -305,13 +346,14 @@ var ALL_SYSTEM_TABLES = [
|
|
|
305
346
|
CREATE_SMRT_EMBEDDINGS_TABLE,
|
|
306
347
|
CREATE_SMRT_DISPATCH_TABLE,
|
|
307
348
|
CREATE_SMRT_DISPATCH_SUBSCRIPTIONS_TABLE,
|
|
308
|
-
CREATE_SMRT_AI_USAGE_TABLE
|
|
349
|
+
CREATE_SMRT_AI_USAGE_TABLE,
|
|
350
|
+
CREATE_SMRT_CHANGES_TABLE
|
|
309
351
|
];
|
|
310
352
|
/**
|
|
311
353
|
* Current SMRT system schema version
|
|
312
354
|
*/
|
|
313
|
-
var SMRT_SCHEMA_VERSION = "1.
|
|
355
|
+
var SMRT_SCHEMA_VERSION = "1.7.0";
|
|
314
356
|
//#endregion
|
|
315
|
-
export { ALL_SYSTEM_TABLES, CREATE_SMRT_AI_USAGE_TABLE, CREATE_SMRT_BACKFILLS_TABLE, CREATE_SMRT_CONTEXTS_TABLE, CREATE_SMRT_DISPATCH_SUBSCRIPTIONS_TABLE, CREATE_SMRT_DISPATCH_TABLE, CREATE_SMRT_EMBEDDINGS_TABLE, CREATE_SMRT_MIGRATIONS_TABLE, CREATE_SMRT_REGISTRY_TABLE, CREATE_SMRT_SCHEMA_MIGRATIONS_TABLE, CREATE_SMRT_SIGNALS_TABLE, SMRT_SCHEMA_VERSION };
|
|
357
|
+
export { ALL_SYSTEM_TABLES, CREATE_SMRT_AI_USAGE_TABLE, CREATE_SMRT_BACKFILLS_TABLE, CREATE_SMRT_CHANGES_TABLE, CREATE_SMRT_CONTEXTS_TABLE, CREATE_SMRT_DISPATCH_SUBSCRIPTIONS_TABLE, CREATE_SMRT_DISPATCH_TABLE, CREATE_SMRT_EMBEDDINGS_TABLE, CREATE_SMRT_MIGRATIONS_TABLE, CREATE_SMRT_REGISTRY_TABLE, CREATE_SMRT_SCHEMA_MIGRATIONS_TABLE, CREATE_SMRT_SIGNALS_TABLE, SMRT_SCHEMA_VERSION };
|
|
316
358
|
|
|
317
359
|
//# sourceMappingURL=schema.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","names":[],"sources":["../../src/system/schema.ts"],"sourcesContent":["/**\n * SMRT System Tables Schema\n *\n * System tables use _smrt_ prefix to avoid conflicts with user tables.\n * All system tables are created in the same database as user data.\n */\n\n/**\n * Context memory storage\n * Stores remembered context (learned strategies, patterns, selectors) for reuse\n */\nexport const CREATE_SMRT_CONTEXTS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_contexts (\n id TEXT PRIMARY KEY,\n owner_class TEXT NOT NULL,\n owner_id TEXT NOT NULL,\n scope TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT,\n metadata TEXT,\n version INTEGER DEFAULT 1,\n confidence REAL DEFAULT 1.0,\n success_count INTEGER DEFAULT 0,\n failure_count INTEGER DEFAULT 0,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n last_used_at TIMESTAMP,\n expires_at TIMESTAMP,\n UNIQUE(owner_class, owner_id, scope, key, version)\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_owner\n ON _smrt_contexts(owner_class, owner_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_scope\n ON _smrt_contexts(scope);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_confidence\n ON _smrt_contexts(confidence);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_last_used\n ON _smrt_contexts(last_used_at);\n`;\n\n/**\n * Schema version tracking\n * Records which SMRT framework versions have been applied\n */\nexport const CREATE_SMRT_MIGRATIONS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_migrations (\n id TEXT PRIMARY KEY,\n version TEXT NOT NULL UNIQUE,\n applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n description TEXT,\n checksum TEXT\n);\n`;\n\n/**\n * Schema migration tracking\n * Tracks applied schema migrations for idempotency, audit, and rollback\n */\nexport const CREATE_SMRT_SCHEMA_MIGRATIONS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_schema_migrations (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n version TEXT NOT NULL,\n checksum TEXT NOT NULL,\n applied_checksum TEXT,\n applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n execution_time_ms INTEGER,\n package_name TEXT,\n source_file TEXT,\n status TEXT NOT NULL DEFAULT 'pending',\n error_message TEXT,\n attempts INTEGER DEFAULT 0,\n is_reversible INTEGER DEFAULT 1,\n rolled_back_at TIMESTAMP,\n applied_by TEXT,\n batch INTEGER\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_schema_migrations_status\n ON _smrt_schema_migrations(status);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_schema_migrations_applied_at\n ON _smrt_schema_migrations(applied_at);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_schema_migrations_batch\n ON _smrt_schema_migrations(batch);\n`;\n\n/**\n * Runtime object registry persistence\n * Stores metadata about registered SMRT objects for introspection\n */\nexport const CREATE_SMRT_REGISTRY_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_registry (\n class_name TEXT PRIMARY KEY,\n schema_version TEXT,\n fields TEXT,\n relationships TEXT,\n config TEXT,\n manifest TEXT,\n last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n`;\n\n/**\n * Signal history/audit log\n * Optional persistence of signals for debugging and auditing\n */\nexport const CREATE_SMRT_SIGNALS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_signals (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n source_class TEXT,\n source_id TEXT,\n target_class TEXT,\n target_id TEXT,\n payload TEXT,\n timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_signals_source\n ON _smrt_signals(source_class, source_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_signals_type\n ON _smrt_signals(type);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_signals_timestamp\n ON _smrt_signals(timestamp);\n`;\n\n/**\n * Embedding storage for semantic search\n * Stores embedding vectors for SMRT objects to enable vector similarity search\n */\nexport const CREATE_SMRT_EMBEDDINGS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_embeddings (\n id TEXT PRIMARY KEY,\n object_class TEXT NOT NULL,\n object_id TEXT NOT NULL,\n field_name TEXT NOT NULL,\n content_hash TEXT NOT NULL,\n embedding TEXT NOT NULL,\n model TEXT NOT NULL,\n dimensions INTEGER NOT NULL,\n provider TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(object_class, object_id, field_name, model)\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_object\n ON _smrt_embeddings(object_class, object_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_class\n ON _smrt_embeddings(object_class);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_hash\n ON _smrt_embeddings(content_hash);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_model\n ON _smrt_embeddings(model);\n`;\n\n/**\n * Dispatch queue for inter-agent communication\n * Stores dispatch messages for asynchronous agent-to-agent signaling\n */\nexport const CREATE_SMRT_DISPATCH_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_dispatch (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n source TEXT NOT NULL,\n source_id TEXT,\n payload TEXT,\n status TEXT NOT NULL DEFAULT 'pending',\n attempts INTEGER DEFAULT 0,\n last_error TEXT,\n processed_at TIMESTAMP,\n processed_by TEXT,\n target_subscriber TEXT,\n correlation_id TEXT,\n tenant_id TEXT,\n metadata TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_status\n ON _smrt_dispatch(status);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_tenant_id\n ON _smrt_dispatch(tenant_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_type\n ON _smrt_dispatch(type);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_source\n ON _smrt_dispatch(source);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_created\n ON _smrt_dispatch(created_at);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_target\n ON _smrt_dispatch(target_subscriber);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_correlation\n ON _smrt_dispatch(correlation_id);\n`;\n\n/**\n * Dispatch subscriptions for persistent handlers\n * Stores subscriptions to dispatch types for agent processing\n */\nexport const CREATE_SMRT_DISPATCH_SUBSCRIPTIONS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_dispatch_subscriptions (\n id TEXT PRIMARY KEY,\n signal_type TEXT NOT NULL,\n subscriber TEXT NOT NULL,\n handler TEXT NOT NULL DEFAULT 'handleDispatch',\n delivery TEXT NOT NULL DEFAULT 'compete',\n enabled INTEGER DEFAULT 1,\n tenant_id TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\n-- Subscription identity is tenant-scoped (S5 #1398): the same\n-- (signal_type, subscriber) pair may exist independently in different tenants,\n-- so tenant B can no longer overwrite/delete/enable tenant A's subscription.\n-- A named UNIQUE index (rather than an inline UNIQUE constraint) is used so the\n-- compatibility migration can additively reshape existing tables. NULL tenant_id\n-- (global subscriptions) is deduped at the application layer by the NULL-aware\n-- upsert in @happyvertical/sql.\nCREATE UNIQUE INDEX IF NOT EXISTS uq_smrt_dispatch_subs_tenant_signal_subscriber\n ON _smrt_dispatch_subscriptions(tenant_id, signal_type, subscriber);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_subscriber\n ON _smrt_dispatch_subscriptions(subscriber);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_tenant_id\n ON _smrt_dispatch_subscriptions(tenant_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_signal_type\n ON _smrt_dispatch_subscriptions(signal_type);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_enabled\n ON _smrt_dispatch_subscriptions(enabled);\n`;\n\n/**\n * AI usage telemetry storage\n * Stores normalized AI usage records for reporting and billing hooks\n */\nexport const CREATE_SMRT_AI_USAGE_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_ai_usage (\n id TEXT PRIMARY KEY,\n provider TEXT NOT NULL,\n model TEXT NOT NULL,\n operation TEXT NOT NULL,\n prompt_tokens INTEGER,\n completion_tokens INTEGER,\n total_tokens INTEGER,\n estimated_cost REAL,\n duration INTEGER NOT NULL,\n class_name TEXT,\n tenant_id TEXT,\n tags TEXT,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_created\n ON _smrt_ai_usage(created_at);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_class\n ON _smrt_ai_usage(class_name);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_tenant\n ON _smrt_ai_usage(tenant_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_provider_model\n ON _smrt_ai_usage(provider, model);\n`;\n\n/**\n * Data backfill tracking\n *\n * Distinct from `_smrt_schema_migrations` — backfills are app-specific\n * data corrections (slug rewrites, model splits, lookup-table seeds) that\n * don't have schema diffs or rollback semantics. Apps register backfills\n * by name and the tracker handles idempotency.\n */\nexport const CREATE_SMRT_BACKFILLS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_backfills (\n name TEXT PRIMARY KEY,\n applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n description TEXT,\n package_name TEXT\n);\n`;\n\n/**\n * All system table creation statements\n */\nexport const ALL_SYSTEM_TABLES = [\n CREATE_SMRT_CONTEXTS_TABLE,\n CREATE_SMRT_MIGRATIONS_TABLE,\n CREATE_SMRT_SCHEMA_MIGRATIONS_TABLE,\n CREATE_SMRT_BACKFILLS_TABLE,\n CREATE_SMRT_REGISTRY_TABLE,\n CREATE_SMRT_SIGNALS_TABLE,\n CREATE_SMRT_EMBEDDINGS_TABLE,\n CREATE_SMRT_DISPATCH_TABLE,\n CREATE_SMRT_DISPATCH_SUBSCRIPTIONS_TABLE,\n CREATE_SMRT_AI_USAGE_TABLE,\n];\n\n/**\n * Current SMRT system schema version\n */\nexport const SMRT_SCHEMA_VERSION = '1.6.1';\n"],"mappings":";;;;;;;;;;;AAWA,IAAa,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqC1C,IAAa,+BAA+B;;;;;;;;;;;;;AAc5C,IAAa,sCAAsC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCnD,IAAa,6BAA6B;;;;;;;;;;;;;;;AAgB1C,IAAa,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;AA0BzC,IAAa,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiC5C,IAAa,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8C1C,IAAa,2CAA2C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCxD,IAAa,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsC1C,IAAa,8BAA8B;;;;;;;;;;;AAY3C,IAAa,oBAAoB;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;AAKA,IAAa,sBAAsB"}
|
|
1
|
+
{"version":3,"file":"schema.js","names":[],"sources":["../../src/system/schema.ts"],"sourcesContent":["/**\n * SMRT System Tables Schema\n *\n * System tables use _smrt_ prefix to avoid conflicts with user tables.\n * All system tables are created in the same database as user data.\n */\n\n/**\n * Context memory storage\n * Stores remembered context (learned strategies, patterns, selectors) for reuse\n */\nexport const CREATE_SMRT_CONTEXTS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_contexts (\n id TEXT PRIMARY KEY,\n owner_class TEXT NOT NULL,\n owner_id TEXT NOT NULL,\n scope TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT,\n metadata TEXT,\n version INTEGER DEFAULT 1,\n confidence REAL DEFAULT 1.0,\n success_count INTEGER DEFAULT 0,\n failure_count INTEGER DEFAULT 0,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n last_used_at TIMESTAMP,\n expires_at TIMESTAMP,\n UNIQUE(owner_class, owner_id, scope, key, version)\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_owner\n ON _smrt_contexts(owner_class, owner_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_scope\n ON _smrt_contexts(scope);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_confidence\n ON _smrt_contexts(confidence);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_contexts_last_used\n ON _smrt_contexts(last_used_at);\n`;\n\n/**\n * Schema version tracking\n * Records which SMRT framework versions have been applied\n */\nexport const CREATE_SMRT_MIGRATIONS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_migrations (\n id TEXT PRIMARY KEY,\n version TEXT NOT NULL UNIQUE,\n applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n description TEXT,\n checksum TEXT\n);\n`;\n\n/**\n * Schema migration tracking\n * Tracks applied schema migrations for idempotency, audit, and rollback\n */\nexport const CREATE_SMRT_SCHEMA_MIGRATIONS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_schema_migrations (\n id TEXT PRIMARY KEY,\n name TEXT NOT NULL UNIQUE,\n version TEXT NOT NULL,\n checksum TEXT NOT NULL,\n applied_checksum TEXT,\n applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n execution_time_ms INTEGER,\n package_name TEXT,\n source_file TEXT,\n status TEXT NOT NULL DEFAULT 'pending',\n error_message TEXT,\n attempts INTEGER DEFAULT 0,\n is_reversible INTEGER DEFAULT 1,\n rolled_back_at TIMESTAMP,\n applied_by TEXT,\n batch INTEGER\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_schema_migrations_status\n ON _smrt_schema_migrations(status);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_schema_migrations_applied_at\n ON _smrt_schema_migrations(applied_at);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_schema_migrations_batch\n ON _smrt_schema_migrations(batch);\n`;\n\n/**\n * Runtime object registry persistence\n * Stores metadata about registered SMRT objects for introspection\n */\nexport const CREATE_SMRT_REGISTRY_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_registry (\n class_name TEXT PRIMARY KEY,\n schema_version TEXT,\n fields TEXT,\n relationships TEXT,\n config TEXT,\n manifest TEXT,\n last_updated TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n`;\n\n/**\n * Signal history/audit log\n * Optional persistence of signals for debugging and auditing\n */\nexport const CREATE_SMRT_SIGNALS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_signals (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n source_class TEXT,\n source_id TEXT,\n target_class TEXT,\n target_id TEXT,\n payload TEXT,\n timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_signals_source\n ON _smrt_signals(source_class, source_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_signals_type\n ON _smrt_signals(type);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_signals_timestamp\n ON _smrt_signals(timestamp);\n`;\n\n/**\n * Embedding storage for semantic search\n * Stores embedding vectors for SMRT objects to enable vector similarity search\n */\nexport const CREATE_SMRT_EMBEDDINGS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_embeddings (\n id TEXT PRIMARY KEY,\n object_class TEXT NOT NULL,\n object_id TEXT NOT NULL,\n field_name TEXT NOT NULL,\n content_hash TEXT NOT NULL,\n embedding TEXT NOT NULL,\n model TEXT NOT NULL,\n dimensions INTEGER NOT NULL,\n provider TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n UNIQUE(object_class, object_id, field_name, model)\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_object\n ON _smrt_embeddings(object_class, object_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_class\n ON _smrt_embeddings(object_class);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_hash\n ON _smrt_embeddings(content_hash);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_embeddings_model\n ON _smrt_embeddings(model);\n`;\n\n/**\n * Dispatch queue for inter-agent communication\n * Stores dispatch messages for asynchronous agent-to-agent signaling\n */\nexport const CREATE_SMRT_DISPATCH_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_dispatch (\n id TEXT PRIMARY KEY,\n type TEXT NOT NULL,\n source TEXT NOT NULL,\n source_id TEXT,\n payload TEXT,\n status TEXT NOT NULL DEFAULT 'pending',\n attempts INTEGER DEFAULT 0,\n last_error TEXT,\n processed_at TIMESTAMP,\n processed_by TEXT,\n target_subscriber TEXT,\n correlation_id TEXT,\n tenant_id TEXT,\n metadata TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_status\n ON _smrt_dispatch(status);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_tenant_id\n ON _smrt_dispatch(tenant_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_type\n ON _smrt_dispatch(type);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_source\n ON _smrt_dispatch(source);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_created\n ON _smrt_dispatch(created_at);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_target\n ON _smrt_dispatch(target_subscriber);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_correlation\n ON _smrt_dispatch(correlation_id);\n`;\n\n/**\n * Dispatch subscriptions for persistent handlers\n * Stores subscriptions to dispatch types for agent processing\n */\nexport const CREATE_SMRT_DISPATCH_SUBSCRIPTIONS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_dispatch_subscriptions (\n id TEXT PRIMARY KEY,\n signal_type TEXT NOT NULL,\n subscriber TEXT NOT NULL,\n handler TEXT NOT NULL DEFAULT 'handleDispatch',\n delivery TEXT NOT NULL DEFAULT 'compete',\n enabled INTEGER DEFAULT 1,\n tenant_id TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n);\n\n-- Subscription identity is tenant-scoped (S5 #1398): the same\n-- (signal_type, subscriber) pair may exist independently in different tenants,\n-- so tenant B can no longer overwrite/delete/enable tenant A's subscription.\n-- A named UNIQUE index (rather than an inline UNIQUE constraint) is used so the\n-- compatibility migration can additively reshape existing tables. NULL tenant_id\n-- (global subscriptions) is deduped at the application layer by the NULL-aware\n-- upsert in @happyvertical/sql.\nCREATE UNIQUE INDEX IF NOT EXISTS uq_smrt_dispatch_subs_tenant_signal_subscriber\n ON _smrt_dispatch_subscriptions(tenant_id, signal_type, subscriber);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_subscriber\n ON _smrt_dispatch_subscriptions(subscriber);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_tenant_id\n ON _smrt_dispatch_subscriptions(tenant_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_signal_type\n ON _smrt_dispatch_subscriptions(signal_type);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_dispatch_subs_enabled\n ON _smrt_dispatch_subscriptions(enabled);\n`;\n\n/**\n * AI usage telemetry storage\n * Stores normalized AI usage records for reporting and billing hooks\n */\nexport const CREATE_SMRT_AI_USAGE_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_ai_usage (\n id TEXT PRIMARY KEY,\n provider TEXT NOT NULL,\n model TEXT NOT NULL,\n operation TEXT NOT NULL,\n prompt_tokens INTEGER,\n completion_tokens INTEGER,\n total_tokens INTEGER,\n estimated_cost REAL,\n duration INTEGER NOT NULL,\n class_name TEXT,\n tenant_id TEXT,\n tags TEXT,\n created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_created\n ON _smrt_ai_usage(created_at);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_class\n ON _smrt_ai_usage(class_name);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_tenant\n ON _smrt_ai_usage(tenant_id);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_ai_usage_provider_model\n ON _smrt_ai_usage(provider, model);\n`;\n\n/**\n * Append-only change feed (issue #1758)\n *\n * One row per framework save/delete: monotonic per-database sequence,\n * table, row id, operation (create/update/delete — deletes are tombstones),\n * tenant, timestamp. Written by the change-feed interceptor and read\n * through `getChangesSince()`.\n *\n * `seq` is deliberately a plain BIGINT PRIMARY KEY rather than a native\n * AUTOINCREMENT/identity/serial column: the appender allocates\n * `COALESCE(MAX(seq), 0) + 1` inside the INSERT (with a conflict retry),\n * which keeps committed sequences contiguous so commit order equals\n * sequence order on every engine. Native identity columns allocate before\n * commit, so under concurrent writers on MVCC engines a reader could\n * observe seq N+1 while seq N is still uncommitted and advance its cursor\n * past it — breaking the feed's no-missed-changes cursor guarantee. The\n * plain column also keeps this DDL portable across SQLite, Postgres and\n * DuckDB with no per-engine branching. See `change-feed.ts`.\n *\n * `row_id` is nullable: manual bumps (`bumpChangeFeed`) may record a\n * table-level change without a specific row.\n */\nexport const CREATE_SMRT_CHANGES_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_changes (\n seq BIGINT PRIMARY KEY,\n table_name TEXT NOT NULL,\n row_id TEXT,\n operation TEXT NOT NULL,\n tenant_id TEXT,\n created_at TIMESTAMP NOT NULL\n);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_changes_table_seq\n ON _smrt_changes(table_name, seq);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_changes_tenant_seq\n ON _smrt_changes(tenant_id, seq);\n\nCREATE INDEX IF NOT EXISTS idx_smrt_changes_created_at\n ON _smrt_changes(created_at);\n`;\n\n/**\n * Data backfill tracking\n *\n * Distinct from `_smrt_schema_migrations` — backfills are app-specific\n * data corrections (slug rewrites, model splits, lookup-table seeds) that\n * don't have schema diffs or rollback semantics. Apps register backfills\n * by name and the tracker handles idempotency.\n */\nexport const CREATE_SMRT_BACKFILLS_TABLE = `\nCREATE TABLE IF NOT EXISTS _smrt_backfills (\n name TEXT PRIMARY KEY,\n applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n description TEXT,\n package_name TEXT\n);\n`;\n\n/**\n * All system table creation statements\n */\nexport const ALL_SYSTEM_TABLES = [\n CREATE_SMRT_CONTEXTS_TABLE,\n CREATE_SMRT_MIGRATIONS_TABLE,\n CREATE_SMRT_SCHEMA_MIGRATIONS_TABLE,\n CREATE_SMRT_BACKFILLS_TABLE,\n CREATE_SMRT_REGISTRY_TABLE,\n CREATE_SMRT_SIGNALS_TABLE,\n CREATE_SMRT_EMBEDDINGS_TABLE,\n CREATE_SMRT_DISPATCH_TABLE,\n CREATE_SMRT_DISPATCH_SUBSCRIPTIONS_TABLE,\n CREATE_SMRT_AI_USAGE_TABLE,\n CREATE_SMRT_CHANGES_TABLE,\n];\n\n/**\n * Current SMRT system schema version\n */\nexport const SMRT_SCHEMA_VERSION = '1.7.0';\n"],"mappings":";;;;;;;;;;;AAWA,IAAa,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqC1C,IAAa,+BAA+B;;;;;;;;;;;;;AAc5C,IAAa,sCAAsC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCnD,IAAa,6BAA6B;;;;;;;;;;;;;;;AAgB1C,IAAa,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;AA0BzC,IAAa,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiC5C,IAAa,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8C1C,IAAa,2CAA2C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwCxD,IAAa,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoD1C,IAAa,4BAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BzC,IAAa,8BAA8B;;;;;;;;;;;AAY3C,IAAa,oBAAoB;CAC/B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;;AAKA,IAAa,sBAAsB"}
|
package/dist/testing/database.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ensureLegacySystemTableCompatibility } from "../system/compatibility.js";
|
|
2
1
|
import { ALL_SYSTEM_TABLES } from "../system/schema.js";
|
|
2
|
+
import { ensureLegacySystemTableCompatibility } from "../system/compatibility.js";
|
|
3
3
|
import { SchemaGenerator } from "../schema/generator.js";
|
|
4
4
|
import { isCollectionRegistration, resolveCollectionItemClassName, resolveRelatedRegistration } from "../registry/collection-resolution.js";
|
|
5
5
|
import { ObjectRegistry } from "../registry.js";
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decorator-runtime stack-frame detection (#1785).
|
|
3
|
+
*
|
|
4
|
+
* ObjectRegistry attributes a `@smrt()` class to its declaring package by
|
|
5
|
+
* walking the registration call stack for the first frame OUTSIDE smrt-core —
|
|
6
|
+
* the module that applied the decorator. Decorator *lowering* breaks that
|
|
7
|
+
* assumption: the compiler inserts a runtime-helper frame between the `@smrt()`
|
|
8
|
+
* decorator and the consumer module that declares the class.
|
|
9
|
+
*
|
|
10
|
+
* Under Vite 8, oxc's legacy-decorator lowering routes the decorator application
|
|
11
|
+
* through `@oxc-project/runtime`'s helper (e.g.
|
|
12
|
+
* `.../@oxc-project/runtime/src/helpers/esm/applyDecoratedDescriptor.js`), so a
|
|
13
|
+
* naive walk stops at that frame and misattributes the class to
|
|
14
|
+
* `@oxc-project/runtime` instead of the consumer package. `tslib` (tsc's
|
|
15
|
+
* `__decorate`), `@swc/helpers`, and `@babel/runtime` insert the identical kind
|
|
16
|
+
* of frame under their respective toolchains.
|
|
17
|
+
*
|
|
18
|
+
* These are pure plumbing packages — a class is applied THROUGH them, never
|
|
19
|
+
* DECLARED in them — so both stack walks (source-file identity in
|
|
20
|
+
* `registry/shared-state.ts` and package attribution in
|
|
21
|
+
* `manifest/manifest-loader.ts`) skip them and continue to the real declaring
|
|
22
|
+
* module. Kept as a leaf module (no smrt imports) so both walkers can share it
|
|
23
|
+
* without risking an import cycle.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Runtime-helper packages that decorator lowering routes the decorator
|
|
27
|
+
* application through. Frames from these packages are never the module that
|
|
28
|
+
* declares a decorated class.
|
|
29
|
+
*/
|
|
30
|
+
export declare const DECORATOR_RUNTIME_PACKAGES: readonly string[];
|
|
31
|
+
/**
|
|
32
|
+
* Whether a file path (normalized to forward slashes) lives inside a
|
|
33
|
+
* decorator-runtime helper package — i.e. a stack frame introduced by decorator
|
|
34
|
+
* lowering rather than the module that declares the decorated class.
|
|
35
|
+
*
|
|
36
|
+
* Matches both the plain installed form (`.../node_modules/tslib/...`) and the
|
|
37
|
+
* pnpm form (`.../node_modules/.pnpm/tslib@2/node_modules/tslib/...`) because
|
|
38
|
+
* the real `/<package>/` segment appears in both. Case-insensitive.
|
|
39
|
+
*
|
|
40
|
+
* @param path - A file path from a stack frame (already normalized to `/`).
|
|
41
|
+
*/
|
|
42
|
+
export declare function isDecoratorRuntimeFramePath(path: string): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Whether a scoped package name (as captured from a `node_modules/@scope/name`
|
|
45
|
+
* stack match) is a decorator-runtime helper package.
|
|
46
|
+
*
|
|
47
|
+
* @param name - A scoped package name, e.g. `@oxc-project/runtime`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function isDecoratorRuntimePackageName(name: string): boolean;
|
|
50
|
+
//# sourceMappingURL=stack-frames.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stack-frames.d.ts","sourceRoot":"","sources":["../../src/utils/stack-frames.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAKvD,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGjE;AAED;;;;;GAKG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnE"}
|