@happyvertical/smrt-core 0.37.8 → 0.37.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/AGENTS.md +10 -0
  2. package/dist/change-feed.d.ts +261 -0
  3. package/dist/change-feed.d.ts.map +1 -0
  4. package/dist/change-feed.js +482 -0
  5. package/dist/change-feed.js.map +1 -0
  6. package/dist/class.d.ts.map +1 -1
  7. package/dist/class.js +4 -2
  8. package/dist/class.js.map +1 -1
  9. package/dist/collection.js +2 -2
  10. package/dist/consumer-plugin/index.d.ts.map +1 -1
  11. package/dist/consumer-plugin/index.js +24 -19
  12. package/dist/consumer-plugin/index.js.map +1 -1
  13. package/dist/dispatch/bus.js +2 -2
  14. package/dist/dispatch/index.js +1 -1
  15. package/dist/generated-client-runtime.d.ts +26 -0
  16. package/dist/generated-client-runtime.d.ts.map +1 -0
  17. package/dist/generated-client-runtime.js +88 -0
  18. package/dist/generated-client-runtime.js.map +1 -0
  19. package/dist/generators/changes-route.d.ts +52 -0
  20. package/dist/generators/changes-route.d.ts.map +1 -0
  21. package/dist/generators/changes-route.js +111 -0
  22. package/dist/generators/changes-route.js.map +1 -0
  23. package/dist/generators/rest.d.ts +28 -0
  24. package/dist/generators/rest.d.ts.map +1 -1
  25. package/dist/generators/rest.js +53 -15
  26. package/dist/generators/rest.js.map +1 -1
  27. package/dist/index.d.ts +1 -0
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +6 -5
  30. package/dist/manifest/static-manifest.js +2 -2
  31. package/dist/manifest/static-manifest.js.map +1 -1
  32. package/dist/manifest/store.js +1 -1
  33. package/dist/manifest/store.js.map +1 -1
  34. package/dist/manifest/test-manifest-stub.d.ts.map +1 -1
  35. package/dist/manifest/test-manifest-stub.js +502 -2
  36. package/dist/manifest/test-manifest-stub.js.map +1 -1
  37. package/dist/manifest.json +2 -2
  38. package/dist/object.js +2 -2
  39. package/dist/prebuild/index.d.ts.map +1 -1
  40. package/dist/prebuild/index.js +66 -7
  41. package/dist/prebuild/index.js.map +1 -1
  42. package/dist/smrt-knowledge.json +6 -6
  43. package/dist/system/schema.d.ts +24 -1
  44. package/dist/system/schema.d.ts.map +1 -1
  45. package/dist/system/schema.js +45 -3
  46. package/dist/system/schema.js.map +1 -1
  47. package/dist/testing/database.js +1 -1
  48. package/dist/vite-plugin/changes-route.d.ts +10 -0
  49. package/dist/vite-plugin/changes-route.d.ts.map +1 -0
  50. package/dist/vite-plugin/changes-route.js +174 -0
  51. package/dist/vite-plugin/changes-route.js.map +1 -0
  52. package/dist/vite-plugin/index.d.ts +7 -0
  53. package/dist/vite-plugin/index.d.ts.map +1 -1
  54. package/dist/vite-plugin/index.js +148 -21
  55. package/dist/vite-plugin/index.js.map +1 -1
  56. package/dist/vite-plugin/route-header.d.ts +10 -0
  57. package/dist/vite-plugin/route-header.d.ts.map +1 -0
  58. package/dist/vite-plugin/route-header.js +14 -0
  59. package/dist/vite-plugin/route-header.js.map +1 -0
  60. package/dist/vite-plugin/sveltekit-generator.d.ts +7 -0
  61. package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
  62. package/dist/vite-plugin/sveltekit-generator.js +5 -3
  63. package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
  64. package/dist/vite-plugin/sync-apply-route.d.ts.map +1 -1
  65. package/dist/vite-plugin/sync-apply-route.js +2 -2
  66. package/dist/vite-plugin/sync-apply-route.js.map +1 -1
  67. package/dist/vite-plugin/web-collections.d.ts +73 -0
  68. package/dist/vite-plugin/web-collections.d.ts.map +1 -0
  69. package/dist/vite-plugin/web-collections.js +174 -0
  70. package/dist/vite-plugin/web-collections.js.map +1 -0
  71. package/package.json +4 -4
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "version": "1.0.0",
3
- "timestamp": 1783059743436,
3
+ "timestamp": 1783100539798,
4
4
  "packageName": "@happyvertical/smrt-core",
5
- "packageVersion": "0.37.8",
5
+ "packageVersion": "0.37.10",
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;AAmPD;;GAEG;AACH,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,IAAI,CAAC,CAkBf"}
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;AAG5D,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;AA8TD;;GAEG;AACH,wBAAsB,2BAA2B,CAC/C,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,IAAI,CAAC,CAkBf"}
@@ -1,3 +1,4 @@
1
+ import { selectWebCollectionEntries } from "../vite-plugin/web-collections.js";
1
2
  import * as fs from "node:fs";
2
3
  import * as path from "node:path";
3
4
  //#region src/prebuild/index.ts
@@ -102,19 +103,26 @@ declare module '@smrt/manifest' {
102
103
  * Auto-generated API client module declaration
103
104
  */
104
105
  declare module '@smrt/client' {
105
- export interface ApiResponse<T = any> {
106
- id?: string;
107
- data?: T;
106
+ /** Shape of a JSON error body carried by a rejected request (SmrtClientError.body). */
107
+ export interface ApiError {
108
108
  error?: string;
109
109
  message?: string;
110
110
  }
111
111
 
112
+ /** Typed error thrown by every fetcher on a non-2xx response (#1796). */
113
+ export interface SmrtClientError extends Error {
114
+ name: 'SmrtClientError';
115
+ status: number;
116
+ body?: ApiError | string;
117
+ }
118
+
112
119
  export interface CrudOperations<T = any> {
113
- list(params?: Record<string, any>): Promise<ApiResponse<T[]>>;
114
- get(id: string): Promise<ApiResponse<T>>;
115
- create(data: Partial<T>): Promise<ApiResponse<T>>;
116
- update(id: string, data: Partial<T>): Promise<ApiResponse<T>>;
120
+ list(params?: Record<string, any>): Promise<T[]>;
121
+ get(id: string): Promise<T>;
122
+ create(data: Partial<T>): Promise<T>;
123
+ update(id: string, data: Partial<T>): Promise<T>;
117
124
  delete(id: string): Promise<boolean>;
125
+ search(query: string): Promise<T[]>;
118
126
  }
119
127
 
120
128
  export interface ApiClient {
@@ -179,12 +187,63 @@ ${Object.values(manifest.objects).map((obj) => ` export type ${obj.className}Da
179
187
  json(data: any, init?: { status?: number }): Response;
180
188
  status(code: number): Response;
181
189
  }
190
+ }`;
191
+ const webDeclaration = `/**
192
+ * Auto-generated web collection-definition module declaration (#1761)
193
+ */
194
+ declare module '@smrt/web' {
195
+ export interface SmrtWebFieldDefinition {
196
+ type: string;
197
+ required?: boolean;
198
+ default?: unknown;
199
+ }
200
+
201
+ export type SmrtWebRelationshipKind =
202
+ | 'foreignKey'
203
+ | 'crossPackageRef'
204
+ | 'oneToMany'
205
+ | 'manyToMany';
206
+
207
+ /**
208
+ * A manifest-derived edge to a sibling REST collection. Mutating this
209
+ * collection invalidates the caches of the collections named by these edges
210
+ * (#1761 relationship-derived invalidation).
211
+ */
212
+ export interface SmrtWebRelationship {
213
+ field: string;
214
+ kind: SmrtWebRelationshipKind;
215
+ relatedCollection: string;
216
+ }
217
+
218
+ export interface SmrtWebCollectionDefinition<TData = Record<string, unknown>> {
219
+ name: string;
220
+ className: string;
221
+ endpoint: string;
222
+ idField: string;
223
+ actions: string[];
224
+ fields: Record<string, SmrtWebFieldDefinition>;
225
+ /** Manifest-derived relationship edges to sibling REST collections. */
226
+ relationships: SmrtWebRelationship[];
227
+ /** Phantom row-type carrier for inference — never present at runtime. */
228
+ _row?: TData;
229
+ }
230
+
231
+ export interface SmrtWebCollectionDefinitions {
232
+ ${selectWebCollectionEntries(manifest).map(({ collection, obj }) => ` ${collection}: SmrtWebCollectionDefinition<import('./smrt-objects').${obj.className}Data>;`).join("\n")}
233
+ }
234
+
235
+ export const collectionDefinitions: SmrtWebCollectionDefinitions;
236
+ export function getCollectionDefinition<
237
+ K extends keyof SmrtWebCollectionDefinitions,
238
+ >(name: K): SmrtWebCollectionDefinitions[K];
239
+ export default collectionDefinitions;
182
240
  }`;
183
241
  fs.writeFileSync(path.join(outDir, "smrt-manifest.d.ts"), manifestDeclaration);
184
242
  fs.writeFileSync(path.join(outDir, "smrt-client.d.ts"), clientDeclaration);
185
243
  fs.writeFileSync(path.join(outDir, "smrt-routes.d.ts"), routesDeclaration);
186
244
  fs.writeFileSync(path.join(outDir, "smrt-mcp.d.ts"), mcpDeclaration);
187
245
  fs.writeFileSync(path.join(outDir, "smrt-types.d.ts"), typesDeclaration);
246
+ fs.writeFileSync(path.join(outDir, "smrt-web.d.ts"), webDeclaration);
188
247
  }
189
248
  /**
190
249
  * Map SMRT field types to TypeScript types
@@ -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":";;;;;;;;;;AAyBA,eAAsB,qBACpB,SACe;CACf,MAAM,EACJ,UAAU,eACV,QACA,wBAAwB,MACxB,qBAAqB,MACrB,cAAc,QAAQ,IAAI,MACxB;CAGJ,MAAM,WACJ,OAAO,kBAAkB,WACrB,KAAK,MAAM,GAAG,aAAa,eAAe,OAAO,CAAC,IAClD;CAGN,MAAM,aAAa,KAAK,WAAW,MAAM,IACrC,SACA,KAAK,KAAK,aAAa,MAAM;CACjC,GAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;CAE5C,QAAQ,IAAI,8CAA8C,YAAY;CAEtE,IAAI,oBACF,MAAM,+BAA+B,UAAU,UAAU;CAG3D,IAAI,uBACF,MAAM,kCAAkC,UAAU,UAAU;CAG9D,QAAQ,IACN,8CAA8C,OAAO,KAAK,SAAS,OAAO,CAAC,CAAC,OAAO,cACrF;AACF;;;;AAKA,eAAe,+BACb,UACA,QACe;CACf,MAAM,aAAuB,CAAC;CAG9B,KAAK,MAAM,CAAC,aAAa,eAAe,OAAO,QAAQ,SAAS,OAAO,GAAG;EACxE,MAAM,SAAS,WAAW,UAAU,CAAC;EACrC,MAAM,gBAA0B,CAAC;EAGjC,cAAc,KAAK,gBAAgB;EACnC,cAAc,KAAK,wBAAwB;EAC3C,cAAc,KAAK,wBAAwB;EAG3C,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,MAAM,GAAG;GAC1D,MAAM,OAAO,yBAAyB,SAAS,IAAI;GACnD,MAAM,WAAW,CAAC,SAAS,WAAW,MAAM;GAC5C,cAAc,KAAK,KAAK,YAAY,SAAS,IAAI,KAAK,EAAE;EAC1D;EAEA,MAAM,eAAe,oBAAoB,WAAW,UAAU;EAChE,cAAc,KAAK,IAAI,EAAE;;EAEvB,WAAW,KAAK,YAAY;CAC9B;CAGA,MAAM,qBAAqB;;;;;;;EAO3B,WAAW,KAAK,MAAM,EAAE;;CAGxB,GAAG,cAAc,KAAK,KAAK,QAAQ,mBAAmB,GAAG,kBAAkB;AAC7E;;;;AAKA,eAAe,kCACb,UACA,QACe;CAEf,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4D5B,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;EAVC,CAHzB,GAAG,IAAI,IAAI,OAAO,OAAO,SAAS,OAAO,CAAC,CAAC,KAAK,QAAQ,IAAI,UAAU,CAAC,CAG9C,CAAA,CACxB,KAAK,eAAe;EACnB,MAAM,WAAW,OAAO,QAAQ,SAAS,OAAO,CAAC,CAAC,MAC/C,GAAG,SAAS,IAAI,eAAe,UAClC,CAAC,GAAG,EAAE,CAAC;EAEP,OAAO,OAAO,WAAW,mBADH,WAAW,GAAG,SAAS,QAAQ,MACK;CAC5D,CAAC,CAAC,CACD,KAAK,IAsBR,EAAmB;;;;;;CAQnB,MAAM,oBAAoB;;;;;;;;;;;;;;CAgB1B,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;CA+BvB,MAAM,mBAAmB;;;;EAPH,OAAO,OAAO,SAAS,OAAO,CAAC,CAClD,KACE,QACC,iBAAiB,IAAI,UAAU,kCAAkC,IAAI,UAAU,MACnF,CAAC,CACA,KAAK,IAMR,EAAc;;;;;;;;;;;;;CAed,GAAG,cACD,KAAK,KAAK,QAAQ,oBAAoB,GACtC,mBACF;CACA,GAAG,cAAc,KAAK,KAAK,QAAQ,kBAAkB,GAAG,iBAAiB;CACzE,GAAG,cAAc,KAAK,KAAK,QAAQ,kBAAkB,GAAG,iBAAiB;CACzE,GAAG,cAAc,KAAK,KAAK,QAAQ,eAAe,GAAG,cAAc;CACnE,GAAG,cAAc,KAAK,KAAK,QAAQ,iBAAiB,GAAG,gBAAgB;AACzE;;;;AAKA,SAAS,yBAAyB,UAA0B;CAC1D,QAAQ,UAAR;EACE,KAAK,QACH,OAAO;EACT,KAAK;EACL,KAAK,WACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,KAAK,YACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,cACH,OAAO;EACT,SACE,OAAO;CACX;AACF;;;;AAKA,eAAsB,4BACpB,MACe;CACf,MAAM,eAAe,KAAK;CAC1B,MAAM,SAAS,KAAK,MAAM;CAE1B,IAAI,CAAC,cAAc;EACjB,QAAQ,MAAM,2DAA2D;EACzE,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI,CAAC,GAAG,WAAW,YAAY,GAAG;EAChC,QAAQ,MAAM,4BAA4B,cAAc;EACxD,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,qBAAqB;EACzB,UAAU;EACV;CACF,CAAC;AACH"}
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';\nimport { selectWebCollectionEntries } from '../vite-plugin/web-collections.js';\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 // Generate web collection-definition module declaration (#1761). Selection\n // shares selectWebCollectionEntries with the vite-plugin runtime module and\n // its virt-web d.ts, so this `tsc`-only consumer declaration cannot drift\n // from the emitted values.\n const webCollectionEntries = selectWebCollectionEntries(manifest)\n .map(\n ({ collection, obj }) =>\n ` ${collection}: SmrtWebCollectionDefinition<import('./smrt-objects').${obj.className}Data>;`,\n )\n .join('\\n');\n\n const webDeclaration = `/**\n * Auto-generated web collection-definition module declaration (#1761)\n */\ndeclare module '@smrt/web' {\n export interface SmrtWebFieldDefinition {\n type: string;\n required?: boolean;\n default?: unknown;\n }\n\n export type SmrtWebRelationshipKind =\n | 'foreignKey'\n | 'crossPackageRef'\n | 'oneToMany'\n | 'manyToMany';\n\n /**\n * A manifest-derived edge to a sibling REST collection. Mutating this\n * collection invalidates the caches of the collections named by these edges\n * (#1761 relationship-derived invalidation).\n */\n export interface SmrtWebRelationship {\n field: string;\n kind: SmrtWebRelationshipKind;\n relatedCollection: string;\n }\n\n export interface SmrtWebCollectionDefinition<TData = Record<string, unknown>> {\n name: string;\n className: string;\n endpoint: string;\n idField: string;\n actions: string[];\n fields: Record<string, SmrtWebFieldDefinition>;\n /** Manifest-derived relationship edges to sibling REST collections. */\n relationships: SmrtWebRelationship[];\n /** Phantom row-type carrier for inference — never present at runtime. */\n _row?: TData;\n }\n\n export interface SmrtWebCollectionDefinitions {\n${webCollectionEntries}\n }\n\n export const collectionDefinitions: SmrtWebCollectionDefinitions;\n export function getCollectionDefinition<\n K extends keyof SmrtWebCollectionDefinitions,\n >(name: K): SmrtWebCollectionDefinitions[K];\n export default collectionDefinitions;\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 fs.writeFileSync(path.join(outDir, 'smrt-web.d.ts'), webDeclaration);\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":";;;;;;;;;;;AA0BA,eAAsB,qBACpB,SACe;CACf,MAAM,EACJ,UAAU,eACV,QACA,wBAAwB,MACxB,qBAAqB,MACrB,cAAc,QAAQ,IAAI,MACxB;CAGJ,MAAM,WACJ,OAAO,kBAAkB,WACrB,KAAK,MAAM,GAAG,aAAa,eAAe,OAAO,CAAC,IAClD;CAGN,MAAM,aAAa,KAAK,WAAW,MAAM,IACrC,SACA,KAAK,KAAK,aAAa,MAAM;CACjC,GAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;CAE5C,QAAQ,IAAI,8CAA8C,YAAY;CAEtE,IAAI,oBACF,MAAM,+BAA+B,UAAU,UAAU;CAG3D,IAAI,uBACF,MAAM,kCAAkC,UAAU,UAAU;CAG9D,QAAQ,IACN,8CAA8C,OAAO,KAAK,SAAS,OAAO,CAAC,CAAC,OAAO,cACrF;AACF;;;;AAKA,eAAe,+BACb,UACA,QACe;CACf,MAAM,aAAuB,CAAC;CAG9B,KAAK,MAAM,CAAC,aAAa,eAAe,OAAO,QAAQ,SAAS,OAAO,GAAG;EACxE,MAAM,SAAS,WAAW,UAAU,CAAC;EACrC,MAAM,gBAA0B,CAAC;EAGjC,cAAc,KAAK,gBAAgB;EACnC,cAAc,KAAK,wBAAwB;EAC3C,cAAc,KAAK,wBAAwB;EAG3C,KAAK,MAAM,CAAC,WAAW,aAAa,OAAO,QAAQ,MAAM,GAAG;GAC1D,MAAM,OAAO,yBAAyB,SAAS,IAAI;GACnD,MAAM,WAAW,CAAC,SAAS,WAAW,MAAM;GAC5C,cAAc,KAAK,KAAK,YAAY,SAAS,IAAI,KAAK,EAAE;EAC1D;EAEA,MAAM,eAAe,oBAAoB,WAAW,UAAU;EAChE,cAAc,KAAK,IAAI,EAAE;;EAEvB,WAAW,KAAK,YAAY;CAC9B;CAGA,MAAM,qBAAqB;;;;;;;EAO3B,WAAW,KAAK,MAAM,EAAE;;CAGxB,GAAG,cAAc,KAAK,KAAK,QAAQ,mBAAmB,GAAG,kBAAkB;AAC7E;;;;AAKA,eAAe,kCACb,UACA,QACe;CAEf,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiE5B,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAfC,CAHzB,GAAG,IAAI,IAAI,OAAO,OAAO,SAAS,OAAO,CAAC,CAAC,KAAK,QAAQ,IAAI,UAAU,CAAC,CAG9C,CAAA,CACxB,KAAK,eAAe;EACnB,MAAM,WAAW,OAAO,QAAQ,SAAS,OAAO,CAAC,CAAC,MAC/C,GAAG,SAAS,IAAI,eAAe,UAClC,CAAC,GAAG,EAAE,CAAC;EAEP,OAAO,OAAO,WAAW,mBADH,WAAW,GAAG,SAAS,QAAQ,MACK;CAC5D,CAAC,CAAC,CACD,KAAK,IAkCR,EAAmB;;;;;;CAQnB,MAAM,oBAAoB;;;;;;;;;;;;;;CAgB1B,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;CA+BvB,MAAM,mBAAmB;;;;EAPH,OAAO,OAAO,SAAS,OAAO,CAAC,CAClD,KACE,QACC,iBAAiB,IAAI,UAAU,kCAAkC,IAAI,UAAU,MACnF,CAAC,CACA,KAAK,IAMR,EAAc;;;;;;;;;;;;;CAyBd,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAPM,2BAA2B,QAAQ,CAAC,CAC9D,KACE,EAAE,YAAY,UACb,OAAO,WAAW,yDAAyD,IAAI,UAAU,OAC7F,CAAC,CACA,KAAK,IA2CR,EAAqB;;;;;;;;;CAWrB,GAAG,cACD,KAAK,KAAK,QAAQ,oBAAoB,GACtC,mBACF;CACA,GAAG,cAAc,KAAK,KAAK,QAAQ,kBAAkB,GAAG,iBAAiB;CACzE,GAAG,cAAc,KAAK,KAAK,QAAQ,kBAAkB,GAAG,iBAAiB;CACzE,GAAG,cAAc,KAAK,KAAK,QAAQ,eAAe,GAAG,cAAc;CACnE,GAAG,cAAc,KAAK,KAAK,QAAQ,iBAAiB,GAAG,gBAAgB;CACvE,GAAG,cAAc,KAAK,KAAK,QAAQ,eAAe,GAAG,cAAc;AACrE;;;;AAKA,SAAS,yBAAyB,UAA0B;CAC1D,QAAQ,UAAR;EACE,KAAK,QACH,OAAO;EACT,KAAK;EACL,KAAK,WACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,KAAK,YACH,OAAO;EACT,KAAK,QACH,OAAO;EACT,KAAK,cACH,OAAO;EACT,SACE,OAAO;CACX;AACF;;;;AAKA,eAAsB,4BACpB,MACe;CACf,MAAM,eAAe,KAAK;CAC1B,MAAM,SAAS,KAAK,MAAM;CAE1B,IAAI,CAAC,cAAc;EACjB,QAAQ,MAAM,2DAA2D;EACzE,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI,CAAC,GAAG,WAAW,YAAY,GAAG;EAChC,QAAQ,MAAM,4BAA4B,cAAc;EACxD,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,qBAAqB;EACzB,UAAU;EACV;CACF,CAAC;AACH"}
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-07-03T06:22:21.290Z",
3
+ "generatedAt": "2026-07-03T17:42:17.116Z",
4
4
  "packageName": "@happyvertical/smrt-core",
5
- "packageVersion": "0.37.8",
5
+ "packageVersion": "0.37.10",
6
6
  "sourceManifestPath": "dist/manifest.json",
7
7
  "agentDocPath": "AGENTS.md",
8
8
  "sourceHashes": {
9
- "manifest": "6eb936bc13c04bf302820a0f448401920e94bc0c83d202cf938c1cc995dfb031",
10
- "packageJson": "d9ca665cf6cd288680ab2375245af2978934a8456dbc55ac6cc82dec15309882",
11
- "agents": "30b544c7ce9bfc7680b4c6ac001076f245816b2a5c379afc88e6c6c218a82ac9"
9
+ "manifest": "05f4b729a5a8d28d803e2312d388ad4ce6f8cf4860a35e7263e2edd75eb0889a",
10
+ "packageJson": "cf21408f71e3905d6bba0ffee1046681326545063d6def3887ff3f92e5d1f322",
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 (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"
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
  }
@@ -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.6.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,UAW7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,UAAU,CAAC"}
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"}
@@ -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.6.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"}
@@ -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,10 @@
1
+ import { SmartObjectManifest } from '../scanner/types';
2
+ import { SvelteKitOptions } from './sveltekit-generator.js';
3
+ /**
4
+ * Generate the `_changes/+server.ts` route. Returns true when a route was
5
+ * written. Disabled with `sveltekit: { changesRoute: { enabled: false } }`;
6
+ * skipped (with a log line) when the manifest has no objects to anchor the
7
+ * database on.
8
+ */
9
+ export declare function generateChangesRoute(projectRoot: string, manifest: SmartObjectManifest, options: SvelteKitOptions): boolean;
10
+ //# sourceMappingURL=changes-route.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changes-route.d.ts","sourceRoot":"","sources":["../../src/vite-plugin/changes-route.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,OAAO,KAAK,EAEV,mBAAmB,EACpB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAyCjE;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,EAAE,gBAAgB,GACxB,OAAO,CA0BT"}