@happyvertical/smrt-core 0.49.3 → 0.49.4
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/README.md +4 -0
- package/agents/change-feed.md +19 -0
- package/agents/collection-reads.md +21 -0
- package/agents/generators.md +5 -0
- package/agents/memory.md +23 -3
- package/agents/null-equal-indexes.md +88 -0
- package/agents/object-runtime.md +85 -0
- package/agents/schema-paths.md +7 -7
- package/dist/browser.js +2 -2
- package/dist/cascade.d.ts.map +1 -1
- package/dist/cascade.js +13 -10
- package/dist/cascade.js.map +1 -1
- package/dist/change-feed.d.ts +22 -0
- package/dist/change-feed.d.ts.map +1 -1
- package/dist/change-feed.js +147 -6
- package/dist/change-feed.js.map +1 -1
- package/dist/class.js +1 -1
- package/dist/collection-cache.d.ts +10 -1
- package/dist/collection-cache.d.ts.map +1 -1
- package/dist/collection-cache.js +65 -10
- package/dist/collection-cache.js.map +1 -1
- package/dist/collection.d.ts +69 -2
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +142 -9
- package/dist/collection.js.map +1 -1
- package/dist/consumer-plugin/artifact-publication.d.ts +27 -0
- package/dist/consumer-plugin/artifact-publication.d.ts.map +1 -0
- package/dist/consumer-plugin/artifact-publication.js +80 -0
- package/dist/consumer-plugin/artifact-publication.js.map +1 -0
- package/dist/consumer-plugin/index.d.ts.map +1 -1
- package/dist/consumer-plugin/index.js +37 -4
- package/dist/consumer-plugin/index.js.map +1 -1
- package/dist/db-errors.js +1 -1
- package/dist/db-errors.js.map +1 -1
- package/dist/embedded-write-queue.d.ts +1 -1
- package/dist/embedded-write-queue.d.ts.map +1 -1
- package/dist/embedded-write-queue.js +16 -6
- package/dist/embedded-write-queue.js.map +1 -1
- package/dist/embeddings/storage.d.ts +16 -0
- package/dist/embeddings/storage.d.ts.map +1 -1
- package/dist/embeddings/storage.js +40 -0
- package/dist/embeddings/storage.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/interceptors.d.ts +22 -0
- package/dist/interceptors.d.ts.map +1 -1
- package/dist/interceptors.js +12 -0
- package/dist/interceptors.js.map +1 -1
- package/dist/junction.d.ts +12 -3
- package/dist/junction.d.ts.map +1 -1
- package/dist/junction.js +34 -4
- package/dist/junction.js.map +1 -1
- package/dist/knowledge-config.d.ts +6 -0
- package/dist/knowledge-config.d.ts.map +1 -0
- package/dist/knowledge-config.js +48 -0
- package/dist/knowledge-config.js.map +1 -0
- package/dist/manifest/static-manifest.d.ts.map +1 -1
- package/dist/manifest/static-manifest.js +91 -1
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +1 -1
- package/dist/manifest/store.js.map +1 -1
- package/dist/manifest.json +104 -1
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +2 -0
- package/dist/migrations/differ.js.map +1 -1
- package/dist/migrations/generator.d.ts.map +1 -1
- package/dist/migrations/generator.js +2 -0
- package/dist/migrations/generator.js.map +1 -1
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/migrations/index.js +2 -1
- package/dist/migrations/null-equal-indexes.d.ts +37 -0
- package/dist/migrations/null-equal-indexes.d.ts.map +1 -0
- package/dist/migrations/null-equal-indexes.js +133 -0
- package/dist/migrations/null-equal-indexes.js.map +1 -0
- package/dist/migrations/tracker.d.ts.map +1 -1
- package/dist/migrations/tracker.js +6 -0
- package/dist/migrations/tracker.js.map +1 -1
- package/dist/migrations.js +2 -1
- package/dist/object.d.ts +33 -0
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +316 -46
- package/dist/object.js.map +1 -1
- package/dist/plain-json.d.ts +6 -0
- package/dist/plain-json.d.ts.map +1 -0
- package/dist/plain-json.js +41 -0
- package/dist/plain-json.js.map +1 -0
- package/dist/postgres-permissions.d.ts.map +1 -1
- package/dist/postgres-permissions.js +6 -2
- package/dist/postgres-permissions.js.map +1 -1
- package/dist/prebuild/index.d.ts.map +1 -1
- package/dist/prebuild/index.js +3 -3
- package/dist/prebuild/index.js.map +1 -1
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +4 -1
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js +4 -3
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/registry/validator.d.ts +1 -0
- package/dist/registry/validator.d.ts.map +1 -1
- package/dist/registry/validator.js +8 -1
- package/dist/registry/validator.js.map +1 -1
- package/dist/registry.js +1 -1
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +21 -0
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/scanner/types.d.ts +2 -0
- package/dist/scanner/types.d.ts.map +1 -1
- package/dist/scanner/types.js.map +1 -1
- package/dist/schema/conflict-target.d.ts +8 -2
- package/dist/schema/conflict-target.d.ts.map +1 -1
- package/dist/schema/conflict-target.js +11 -3
- package/dist/schema/conflict-target.js.map +1 -1
- package/dist/schema/ddl/null-equal-index.d.ts +5 -0
- package/dist/schema/ddl/null-equal-index.d.ts.map +1 -0
- package/dist/schema/ddl/null-equal-index.js +17 -0
- package/dist/schema/ddl/null-equal-index.js.map +1 -0
- package/dist/schema/ddl/postgres-strategy.d.ts +2 -1
- package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/postgres-strategy.js +7 -0
- package/dist/schema/ddl/postgres-strategy.js.map +1 -1
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +8 -3
- package/dist/schema/generator.js.map +1 -1
- package/dist/schema/manifest-schema.d.ts +2 -1
- package/dist/schema/manifest-schema.d.ts.map +1 -1
- package/dist/schema/manifest-schema.js +12 -4
- package/dist/schema/manifest-schema.js.map +1 -1
- package/dist/schema/schema-manager.d.ts.map +1 -1
- package/dist/schema/schema-manager.js +1 -1
- package/dist/schema/schema-manager.js.map +1 -1
- package/dist/schema/types.d.ts +2 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/scripts/browser-plain-json-regression.mjs +85 -0
- package/dist/smrt-knowledge.json +73 -15
- package/dist/system/schema.d.ts +14 -3
- package/dist/system/schema.d.ts.map +1 -1
- package/dist/system/schema.js +120 -2
- package/dist/system/schema.js.map +1 -1
- package/dist/vite-plugin/api-client-entries.d.ts.map +1 -1
- package/dist/vite-plugin/api-client-entries.js +9 -7
- package/dist/vite-plugin/api-client-entries.js.map +1 -1
- package/dist/vite-plugin/index.d.ts +24 -0
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +47 -64
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.js +2 -2
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/dist/vite-plugin/web-collections.d.ts +10 -0
- package/dist/vite-plugin/web-collections.d.ts.map +1 -1
- package/dist/vite-plugin/web-collections.js +14 -2
- package/dist/vite-plugin/web-collections.js.map +1 -1
- package/package.json +5 -4
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
export interface ArtifactFile {
|
|
3
|
+
path: string;
|
|
4
|
+
content: string;
|
|
5
|
+
}
|
|
6
|
+
export interface ArtifactFilesystem {
|
|
7
|
+
existsSync: typeof fs.existsSync;
|
|
8
|
+
renameSync: typeof fs.renameSync;
|
|
9
|
+
statSync: typeof fs.statSync;
|
|
10
|
+
unlinkSync: typeof fs.unlinkSync;
|
|
11
|
+
writeFileSync: typeof fs.writeFileSync;
|
|
12
|
+
chmodSync: typeof fs.chmodSync;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Stage and publish a related artifact set with synchronous-failure recovery.
|
|
16
|
+
*
|
|
17
|
+
* Every existing target is first renamed to an adjacent backup, preserving its
|
|
18
|
+
* mode, before a staged replacement is renamed into place. If a later rename
|
|
19
|
+
* fails, rollback restores backups using rename only; it never truncates an
|
|
20
|
+
* old target by rewriting it. A rollback failure retains its backup for manual
|
|
21
|
+
* recovery and is included with the original publication failure.
|
|
22
|
+
*
|
|
23
|
+
* There is no portable multi-file filesystem transaction: a process crash or a
|
|
24
|
+
* concurrent reader between individual renames can still observe a mixed set.
|
|
25
|
+
*/
|
|
26
|
+
export declare function publishArtifactFiles(files: ArtifactFile[], filesystem?: ArtifactFilesystem): void;
|
|
27
|
+
//# sourceMappingURL=artifact-publication.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-publication.d.ts","sourceRoot":"","sources":["../../src/consumer-plugin/artifact-publication.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC;IACjC,UAAU,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC;IACjC,QAAQ,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC;IAC7B,UAAU,EAAE,OAAO,EAAE,CAAC,UAAU,CAAC;IACjC,aAAa,EAAE,OAAO,EAAE,CAAC,aAAa,CAAC;IACvC,SAAS,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC;CAChC;AAQD;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,YAAY,EAAE,EACrB,UAAU,GAAE,kBAAuB,GAClC,IAAI,CAsFN"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
//#region src/consumer-plugin/artifact-publication.ts
|
|
3
|
+
/**
|
|
4
|
+
* Stage and publish a related artifact set with synchronous-failure recovery.
|
|
5
|
+
*
|
|
6
|
+
* Every existing target is first renamed to an adjacent backup, preserving its
|
|
7
|
+
* mode, before a staged replacement is renamed into place. If a later rename
|
|
8
|
+
* fails, rollback restores backups using rename only; it never truncates an
|
|
9
|
+
* old target by rewriting it. A rollback failure retains its backup for manual
|
|
10
|
+
* recovery and is included with the original publication failure.
|
|
11
|
+
*
|
|
12
|
+
* There is no portable multi-file filesystem transaction: a process crash or a
|
|
13
|
+
* concurrent reader between individual renames can still observe a mixed set.
|
|
14
|
+
*/
|
|
15
|
+
function publishArtifactFiles(files, filesystem = fs) {
|
|
16
|
+
const unique = `${process.pid}-${Date.now()}`;
|
|
17
|
+
const staged = [];
|
|
18
|
+
const backed = [];
|
|
19
|
+
const published = [];
|
|
20
|
+
let retainBackups = false;
|
|
21
|
+
try {
|
|
22
|
+
for (const [index, file] of files.entries()) {
|
|
23
|
+
const exists = filesystem.existsSync(file.path);
|
|
24
|
+
const stats = exists ? filesystem.statSync(file.path) : void 0;
|
|
25
|
+
if (stats && !stats.isFile()) throw new Error(`Artifact target is not a file: ${file.path}`);
|
|
26
|
+
const mode = stats ? stats.mode & 511 : void 0;
|
|
27
|
+
const stagedFile = {
|
|
28
|
+
path: file.path,
|
|
29
|
+
stagedPath: `${file.path}.smrt-${unique}-${index}.tmp`,
|
|
30
|
+
backupPath: exists ? `${file.path}.smrt-${unique}-${index}.bak` : void 0
|
|
31
|
+
};
|
|
32
|
+
staged.push(stagedFile);
|
|
33
|
+
filesystem.writeFileSync(stagedFile.stagedPath, file.content, "utf-8");
|
|
34
|
+
if (mode !== void 0) filesystem.chmodSync(stagedFile.stagedPath, mode);
|
|
35
|
+
}
|
|
36
|
+
for (const file of staged) {
|
|
37
|
+
if (!file.backupPath) continue;
|
|
38
|
+
filesystem.renameSync(file.path, file.backupPath);
|
|
39
|
+
backed.push(file);
|
|
40
|
+
}
|
|
41
|
+
for (const file of staged) {
|
|
42
|
+
filesystem.renameSync(file.stagedPath, file.path);
|
|
43
|
+
published.push(file);
|
|
44
|
+
}
|
|
45
|
+
} catch (publicationError) {
|
|
46
|
+
const rollbackErrors = [];
|
|
47
|
+
for (const file of backed.reverse()) try {
|
|
48
|
+
if (!file.backupPath) throw new Error(`Missing artifact backup: ${file.path}`);
|
|
49
|
+
filesystem.renameSync(file.backupPath, file.path);
|
|
50
|
+
} catch (rollbackError) {
|
|
51
|
+
rollbackErrors.push(rollbackError);
|
|
52
|
+
}
|
|
53
|
+
for (const file of published) {
|
|
54
|
+
if (file.backupPath) continue;
|
|
55
|
+
try {
|
|
56
|
+
if (filesystem.existsSync(file.path)) filesystem.unlinkSync(file.path);
|
|
57
|
+
} catch (rollbackError) {
|
|
58
|
+
rollbackErrors.push(rollbackError);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (rollbackErrors.length > 0) {
|
|
62
|
+
retainBackups = true;
|
|
63
|
+
throw new AggregateError([publicationError, ...rollbackErrors], "Artifact publication failed and rollback retained recovery backups");
|
|
64
|
+
}
|
|
65
|
+
throw publicationError;
|
|
66
|
+
} finally {
|
|
67
|
+
for (const file of staged) {
|
|
68
|
+
try {
|
|
69
|
+
if (filesystem.existsSync(file.stagedPath)) filesystem.unlinkSync(file.stagedPath);
|
|
70
|
+
} catch {}
|
|
71
|
+
if (!retainBackups) try {
|
|
72
|
+
if (file.backupPath && filesystem.existsSync(file.backupPath)) filesystem.unlinkSync(file.backupPath);
|
|
73
|
+
} catch {}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//#endregion
|
|
78
|
+
export { publishArtifactFiles };
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=artifact-publication.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artifact-publication.js","names":[],"sources":["../../src/consumer-plugin/artifact-publication.ts"],"sourcesContent":["import * as fs from 'node:fs';\n\nexport interface ArtifactFile {\n path: string;\n content: string;\n}\n\nexport interface ArtifactFilesystem {\n existsSync: typeof fs.existsSync;\n renameSync: typeof fs.renameSync;\n statSync: typeof fs.statSync;\n unlinkSync: typeof fs.unlinkSync;\n writeFileSync: typeof fs.writeFileSync;\n chmodSync: typeof fs.chmodSync;\n}\n\ninterface StagedArtifact {\n path: string;\n stagedPath: string;\n backupPath?: string;\n}\n\n/**\n * Stage and publish a related artifact set with synchronous-failure recovery.\n *\n * Every existing target is first renamed to an adjacent backup, preserving its\n * mode, before a staged replacement is renamed into place. If a later rename\n * fails, rollback restores backups using rename only; it never truncates an\n * old target by rewriting it. A rollback failure retains its backup for manual\n * recovery and is included with the original publication failure.\n *\n * There is no portable multi-file filesystem transaction: a process crash or a\n * concurrent reader between individual renames can still observe a mixed set.\n */\nexport function publishArtifactFiles(\n files: ArtifactFile[],\n filesystem: ArtifactFilesystem = fs,\n): void {\n const unique = `${process.pid}-${Date.now()}`;\n const staged: StagedArtifact[] = [];\n const backed: StagedArtifact[] = [];\n const published: StagedArtifact[] = [];\n let retainBackups = false;\n\n try {\n for (const [index, file] of files.entries()) {\n const exists = filesystem.existsSync(file.path);\n const stats = exists ? filesystem.statSync(file.path) : undefined;\n if (stats && !stats.isFile()) {\n throw new Error(`Artifact target is not a file: ${file.path}`);\n }\n const mode = stats ? stats.mode & 0o777 : undefined;\n const stagedFile = {\n path: file.path,\n stagedPath: `${file.path}.smrt-${unique}-${index}.tmp`,\n backupPath: exists\n ? `${file.path}.smrt-${unique}-${index}.bak`\n : undefined,\n };\n // Register before the write: a failed write can still leave a partial\n // temporary file, which the finally cleanup must see.\n staged.push(stagedFile);\n filesystem.writeFileSync(stagedFile.stagedPath, file.content, 'utf-8');\n if (mode !== undefined) filesystem.chmodSync(stagedFile.stagedPath, mode);\n }\n for (const file of staged) {\n if (!file.backupPath) continue;\n filesystem.renameSync(file.path, file.backupPath);\n backed.push(file);\n }\n for (const file of staged) {\n filesystem.renameSync(file.stagedPath, file.path);\n published.push(file);\n }\n } catch (publicationError) {\n const rollbackErrors: unknown[] = [];\n for (const file of backed.reverse()) {\n try {\n if (!file.backupPath) {\n throw new Error(`Missing artifact backup: ${file.path}`);\n }\n // Replacing a published staged file is safe: its authoritative prior\n // contents are held by backupPath until this rename succeeds.\n filesystem.renameSync(file.backupPath, file.path);\n } catch (rollbackError) {\n rollbackErrors.push(rollbackError);\n }\n }\n for (const file of published) {\n if (file.backupPath) continue;\n try {\n if (filesystem.existsSync(file.path)) filesystem.unlinkSync(file.path);\n } catch (rollbackError) {\n rollbackErrors.push(rollbackError);\n }\n }\n if (rollbackErrors.length > 0) {\n retainBackups = true;\n throw new AggregateError(\n [publicationError, ...rollbackErrors],\n 'Artifact publication failed and rollback retained recovery backups',\n );\n }\n throw publicationError;\n } finally {\n for (const file of staged) {\n try {\n if (filesystem.existsSync(file.stagedPath))\n filesystem.unlinkSync(file.stagedPath);\n } catch {\n // A staging file is not an authoritative artifact.\n }\n if (!retainBackups) {\n try {\n if (file.backupPath && filesystem.existsSync(file.backupPath)) {\n filesystem.unlinkSync(file.backupPath);\n }\n } catch {\n // A failed cleanup leaves a recoverable backup behind.\n }\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAkCA,SAAgB,qBACd,OACA,aAAiC,IAC3B;CACN,MAAM,SAAS,GAAG,QAAQ,IAAI,GAAG,KAAK,IAAI;CAC1C,MAAM,SAA2B,CAAC;CAClC,MAAM,SAA2B,CAAC;CAClC,MAAM,YAA8B,CAAC;CACrC,IAAI,gBAAgB;CAEpB,IAAI;EACF,KAAK,MAAM,CAAC,OAAO,SAAS,MAAM,QAAQ,GAAG;GAC3C,MAAM,SAAS,WAAW,WAAW,KAAK,IAAI;GAC9C,MAAM,QAAQ,SAAS,WAAW,SAAS,KAAK,IAAI,IAAI,KAAA;GACxD,IAAI,SAAS,CAAC,MAAM,OAAO,GACzB,MAAM,IAAI,MAAM,kCAAkC,KAAK,MAAM;GAE/D,MAAM,OAAO,QAAQ,MAAM,OAAO,MAAQ,KAAA;GAC1C,MAAM,aAAa;IACjB,MAAM,KAAK;IACX,YAAY,GAAG,KAAK,KAAK,QAAQ,OAAO,GAAG,MAAM;IACjD,YAAY,SACR,GAAG,KAAK,KAAK,QAAQ,OAAO,GAAG,MAAM,QACrC,KAAA;GACN;GAGA,OAAO,KAAK,UAAU;GACtB,WAAW,cAAc,WAAW,YAAY,KAAK,SAAS,OAAO;GACrE,IAAI,SAAS,KAAA,GAAW,WAAW,UAAU,WAAW,YAAY,IAAI;EAC1E;EACA,KAAK,MAAM,QAAQ,QAAQ;GACzB,IAAI,CAAC,KAAK,YAAY;GACtB,WAAW,WAAW,KAAK,MAAM,KAAK,UAAU;GAChD,OAAO,KAAK,IAAI;EAClB;EACA,KAAK,MAAM,QAAQ,QAAQ;GACzB,WAAW,WAAW,KAAK,YAAY,KAAK,IAAI;GAChD,UAAU,KAAK,IAAI;EACrB;CACF,SAAS,kBAAkB;EACzB,MAAM,iBAA4B,CAAC;EACnC,KAAK,MAAM,QAAQ,OAAO,QAAQ,GAChC,IAAI;GACF,IAAI,CAAC,KAAK,YACR,MAAM,IAAI,MAAM,4BAA4B,KAAK,MAAM;GAIzD,WAAW,WAAW,KAAK,YAAY,KAAK,IAAI;EAClD,SAAS,eAAe;GACtB,eAAe,KAAK,aAAa;EACnC;EAEF,KAAK,MAAM,QAAQ,WAAW;GAC5B,IAAI,KAAK,YAAY;GACrB,IAAI;IACF,IAAI,WAAW,WAAW,KAAK,IAAI,GAAG,WAAW,WAAW,KAAK,IAAI;GACvE,SAAS,eAAe;IACtB,eAAe,KAAK,aAAa;GACnC;EACF;EACA,IAAI,eAAe,SAAS,GAAG;GAC7B,gBAAgB;GAChB,MAAM,IAAI,eACR,CAAC,kBAAkB,GAAG,cAAc,GACpC,oEACF;EACF;EACA,MAAM;CACR,UAAU;EACR,KAAK,MAAM,QAAQ,QAAQ;GACzB,IAAI;IACF,IAAI,WAAW,WAAW,KAAK,UAAU,GACvC,WAAW,WAAW,KAAK,UAAU;GACzC,QAAQ,CAER;GACA,IAAI,CAAC,eACH,IAAI;IACF,IAAI,KAAK,cAAc,WAAW,WAAW,KAAK,UAAU,GAC1D,WAAW,WAAW,KAAK,UAAU;GAEzC,QAAQ,CAER;EAEJ;CACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consumer-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consumer-plugin/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,EAEL,KAAK,6BAA6B,EACnC,MAAM,2BAA2B,CAAC;AAUnC,OAAO,EACL,kCAAkC,EAClC,KAAK,sCAAsC,EAC3C,KAAK,8BAA8B,EACnC,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAC/B,+BAA+B,EAC/B,4BAA4B,GAC7B,MAAM,2BAA2B,CAAC;AAoDnC,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,uCAAuC;IACvC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wBAAwB;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,6BAA6B,CAAC;IACnD,iCAAiC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oDAAoD;IACpD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4BAA4B;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAiBD;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,MAAM,CAoKtE"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import "../scanner/types.js";
|
|
2
|
+
import { buildDomainKnowledgeManifest } from "../knowledge.js";
|
|
2
3
|
import { loadVerifiedSmrtGenerationSnapshot, serializeSmrtGenerationSnapshot, sha256SmrtGenerationSnapshot } from "../generation-snapshot.js";
|
|
4
|
+
import { resolveFileKnowledgeConfig } from "../knowledge-config.js";
|
|
3
5
|
import { generateClientModule } from "../vite-plugin/generated-client.js";
|
|
4
6
|
import { generateDeclarations } from "../prebuild/index.js";
|
|
7
|
+
import { publishArtifactFiles } from "./artifact-publication.js";
|
|
5
8
|
import * as fs from "node:fs";
|
|
6
9
|
import * as path from "node:path";
|
|
7
10
|
//#region src/consumer-plugin/index.ts
|
|
@@ -24,6 +27,7 @@ function smrtConsumer(options = {}) {
|
|
|
24
27
|
let smrtPackages = [];
|
|
25
28
|
let typeManifest = null;
|
|
26
29
|
let typesGenerated = false;
|
|
30
|
+
let producerApi;
|
|
27
31
|
function loadGenerationSnapshot() {
|
|
28
32
|
if (!generationSnapshot) throw new Error("[smrt:consumer] Generation snapshot is not configured");
|
|
29
33
|
return loadVerifiedSmrtGenerationSnapshot(generationSnapshot, projectRoot, "dependencies");
|
|
@@ -33,6 +37,9 @@ function smrtConsumer(options = {}) {
|
|
|
33
37
|
config() {
|
|
34
38
|
return { build: { rollupOptions: { external: [/\.node$/] } } };
|
|
35
39
|
},
|
|
40
|
+
configResolved(resolvedConfig) {
|
|
41
|
+
producerApi = (resolvedConfig.plugins ?? []).find((plugin) => plugin?.name === "smrt-auto-service")?.api;
|
|
42
|
+
},
|
|
36
43
|
async buildStart() {
|
|
37
44
|
console.log("[smrt:consumer] Initializing SMRT consumer plugin");
|
|
38
45
|
if (generationSnapshot) {
|
|
@@ -50,7 +57,8 @@ function smrtConsumer(options = {}) {
|
|
|
50
57
|
if (smrtPackages.length > 0) {
|
|
51
58
|
console.log(`[smrt:consumer] Found SMRT packages: ${smrtPackages.join(", ")}`);
|
|
52
59
|
typeManifest = await aggregateTypeManifests(smrtPackages, projectRoot);
|
|
53
|
-
await
|
|
60
|
+
const agentSurface = producerApi ? await producerApi.resolveKnowledgeAgentSurface() : void 0;
|
|
61
|
+
await saveAggregatedManifest(typeManifest, projectRoot, producerApi?.resolveKnowledgeConfig, agentSurface);
|
|
54
62
|
await generateRegistrationFile(typeManifest, projectRoot);
|
|
55
63
|
if (generateTypes && !typesGenerated) {
|
|
56
64
|
await generateProjectTypes(typeManifest, typesDir, projectRoot);
|
|
@@ -220,7 +228,7 @@ function determineImportPath(packageJson) {
|
|
|
220
228
|
* external-package entries it owns and preserves everything else already in
|
|
221
229
|
* the file (including the top-level `packageName` the local write sets).
|
|
222
230
|
*/
|
|
223
|
-
async function saveAggregatedManifest(manifest, projectRoot) {
|
|
231
|
+
async function saveAggregatedManifest(manifest, projectRoot, resolveKnowledgeConfig, agentSurface) {
|
|
224
232
|
const smrtDir = path.join(projectRoot, ".smrt");
|
|
225
233
|
const manifestPath = path.join(smrtDir, "manifest.json");
|
|
226
234
|
try {
|
|
@@ -238,10 +246,35 @@ async function saveAggregatedManifest(manifest, projectRoot) {
|
|
|
238
246
|
}
|
|
239
247
|
};
|
|
240
248
|
} catch {}
|
|
241
|
-
|
|
249
|
+
const knowledgePath = path.join(smrtDir, "smrt-knowledge.json");
|
|
250
|
+
const packageJsonPath = path.join(projectRoot, "package.json");
|
|
251
|
+
const packageJson = fs.existsSync(packageJsonPath) ? JSON.parse(fs.readFileSync(packageJsonPath, "utf-8")) : void 0;
|
|
252
|
+
const knowledgeConfig = resolveKnowledgeConfig ? await resolveKnowledgeConfig(merged) : await resolveFileKnowledgeConfig(projectRoot, merged.packageName ?? packageJson?.name);
|
|
253
|
+
if (knowledgeConfig.enabled === false) {
|
|
254
|
+
publishArtifactFiles([{
|
|
255
|
+
path: manifestPath,
|
|
256
|
+
content: JSON.stringify(merged, null, 2)
|
|
257
|
+
}]);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const knowledge = buildDomainKnowledgeManifest({
|
|
261
|
+
manifest: merged,
|
|
262
|
+
rootDir: projectRoot,
|
|
263
|
+
packageJson,
|
|
264
|
+
manifestPath,
|
|
265
|
+
config: knowledgeConfig,
|
|
266
|
+
agentSurface
|
|
267
|
+
});
|
|
268
|
+
publishArtifactFiles([{
|
|
269
|
+
path: knowledgePath,
|
|
270
|
+
content: JSON.stringify(knowledge, null, 2)
|
|
271
|
+
}, {
|
|
272
|
+
path: manifestPath,
|
|
273
|
+
content: JSON.stringify(merged, null, 2)
|
|
274
|
+
}]);
|
|
242
275
|
console.log(`[smrt:consumer] Saved aggregated manifest to .smrt/manifest.json (${Object.keys(merged.objects).length} objects)`);
|
|
243
276
|
} catch (error) {
|
|
244
|
-
|
|
277
|
+
throw new Error("[smrt:consumer] Failed to save aggregated manifest", { cause: error });
|
|
245
278
|
}
|
|
246
279
|
}
|
|
247
280
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/consumer-plugin/index.ts"],"sourcesContent":["/**\n * Vite plugin for consuming SMRT packages\n * Solves virtual module resolution in downstream projects\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { Plugin } from 'vite';\nimport {\n loadVerifiedSmrtGenerationSnapshot,\n type SmrtGenerationSnapshotOptions,\n} from '../generation-snapshot.js';\nimport { generateDeclarations } from '../prebuild/index.js';\nimport type { SmartObjectManifest } from '../scanner/types.js';\nimport { MANIFEST_TIMESTAMP } from '../scanner/types.js';\nimport { generateClientModule } from '../vite-plugin/generated-client.js';\n\nexport {\n loadVerifiedSmrtGenerationSnapshot,\n type SerializeSmrtGenerationSnapshotOptions,\n type SmrtGenerationSnapshotArtifact,\n type SmrtGenerationSnapshotOptions,\n type SmrtGenerationSnapshotView,\n serializeSmrtGenerationSnapshot,\n sha256SmrtGenerationSnapshot,\n} from '../generation-snapshot.js';\n\n/**\n * Loosely-typed view of an object definition as carried by an external\n * package's static manifest. The static manifests are read from JSON at the\n * package boundary, so only the fields this plugin consumes are typed; the\n * index signature preserves any additional fields (e.g. for spreads). This is\n * a structural superset of a manifest `SmartObjectDefinition` plus the\n * consumer-only `hasCollection` marker.\n */\ninterface ConsumerObjectDefinition {\n className?: string;\n packageName?: string;\n packageVersion?: string;\n qualifiedName?: string;\n importPath?: string;\n exportName?: string;\n collectionExportName?: string;\n hasCollection?: boolean;\n collection?: string;\n extends?: string;\n extendsQualified?: string;\n extendsTypeArg?: string;\n [key: string]: unknown;\n}\n\n/**\n * Aggregated manifest assembled by the consumer plugin from one or more\n * external package manifests. Loosely typed because the inputs originate from\n * JSON read at the package boundary.\n */\ninterface ConsumerManifest {\n version: string;\n timestamp: number;\n packageName?: string;\n packageVersion?: string;\n objects: Record<string, ConsumerObjectDefinition>;\n}\n\n/**\n * Minimal structural shape of a parsed `package.json` consumed here (name,\n * version, and the export map used to derive import paths). The index\n * signature keeps the remaining fields accessible.\n */\ninterface ConsumerPackageJson {\n name?: string;\n version?: string;\n main?: string;\n exports?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nexport interface SmrtConsumerOptions {\n /** SMRT packages to scan (e.g., ['@my-org/products', '@my-org/content']) */\n packages?: string[];\n /** Generate TypeScript declarations */\n generateTypes?: boolean;\n /** Output directory for generated types */\n typesDir?: string;\n /** Project root path */\n projectRoot?: string;\n /**\n * Reuse an immutable, verified aggregated manifest instead of discovering\n * packages or writing `.smrt/manifest.json`. Registration and generated\n * types still consume the verified manifest.\n */\n generationSnapshot?: SmrtGenerationSnapshotOptions;\n /** SvelteKit integration mode */\n svelteKit?: boolean;\n /**\n * Apply kebab-case to generated custom-method URL segments. This must match\n * the producer plugin's `svelteKit.kebabRoutes` setting.\n */\n kebabRoutes?: boolean;\n /** Use static types only (for federation builds) */\n staticTypes?: boolean;\n /** Disable file scanning */\n disableScanning?: boolean;\n}\n\n// Distinct resolved ids per plugin (#1795). smrtPlugin resolves\n// `@happyvertical/smrt-virt-*` to `\\0smrt:*`; if this consumer plugin also\n// resolved its `@smrt/*` specifiers to `\\0smrt:*` the two virtual modules would\n// share a rollup id, and in standalone/federation builds the consumer's\n// fallback `load` would non-deterministically win and shadow smrtPlugin's real\n// module. Namespacing the consumer ids (`\\0smrt-consumer:*`) keeps them\n// separate so each plugin only ever loads its own module.\nconst VIRTUAL_MODULES = {\n '@smrt/routes': 'smrt-consumer:routes',\n '@smrt/client': 'smrt-consumer:client',\n '@smrt/mcp': 'smrt-consumer:mcp',\n '@smrt/types': 'smrt-consumer:types',\n '@smrt/manifest': 'smrt-consumer:manifest',\n};\n\n/**\n * Consumer plugin for projects that use SMRT packages\n */\nexport function smrtConsumer(options: SmrtConsumerOptions = {}): Plugin {\n const {\n packages = [],\n generateTypes = true,\n typesDir = 'src/types/smrt-generated',\n projectRoot = process.cwd(),\n generationSnapshot,\n disableScanning = false,\n kebabRoutes = false,\n } = options;\n\n let smrtPackages: string[] = [];\n let typeManifest: ConsumerManifest | null = null;\n let typesGenerated = false;\n\n function loadGenerationSnapshot(): ConsumerManifest {\n if (!generationSnapshot) {\n throw new Error('[smrt:consumer] Generation snapshot is not configured');\n }\n return loadVerifiedSmrtGenerationSnapshot<ConsumerManifest>(\n generationSnapshot,\n projectRoot,\n 'dependencies',\n );\n }\n\n return {\n name: 'smrt-consumer',\n\n config() {\n return {\n build: {\n rollupOptions: {\n // Runtime registration evaluates provider entry points so their\n // exact constructors can be registered. Leave optional native\n // provider binaries to Node instead of parsing them as JavaScript.\n external: [/\\.node$/],\n },\n },\n };\n },\n\n async buildStart() {\n console.log('[smrt:consumer] Initializing SMRT consumer plugin');\n\n if (generationSnapshot) {\n typeManifest = loadGenerationSnapshot();\n console.log(\n `[smrt:consumer] Reusing verified generation snapshot (${generationSnapshot.provenance})`,\n );\n await generateRegistrationFile(typeManifest, projectRoot);\n if (generateTypes && !typesGenerated) {\n await generateProjectTypes(typeManifest, typesDir, projectRoot);\n typesGenerated = true;\n }\n return;\n }\n\n // Discover SMRT packages if not explicitly specified\n if (packages.length === 0 && !disableScanning) {\n smrtPackages = await discoverSmrtPackages(projectRoot);\n } else {\n smrtPackages = packages;\n }\n\n if (smrtPackages.length > 0) {\n console.log(\n `[smrt:consumer] Found SMRT packages: ${smrtPackages.join(', ')}`,\n );\n\n // Aggregate type manifests from discovered packages\n typeManifest = await aggregateTypeManifests(smrtPackages, projectRoot);\n\n // Save aggregated manifest for CLI discovery\n await saveAggregatedManifest(typeManifest, projectRoot);\n\n // Generate registration file for CLI class loading\n await generateRegistrationFile(typeManifest, projectRoot);\n\n // Generate types if requested\n if (generateTypes && !typesGenerated) {\n await generateProjectTypes(typeManifest, typesDir, projectRoot);\n typesGenerated = true;\n }\n } else {\n console.log('[smrt:consumer] No SMRT packages found');\n typeManifest = {\n version: '1.0.0',\n timestamp: MANIFEST_TIMESTAMP,\n objects: {},\n };\n }\n },\n\n resolveId(id, _importer) {\n // Resolve virtual modules to generated type declarations\n if (id in VIRTUAL_MODULES) {\n const typeFileName = getTypeFileName(id);\n const typePath = path.join(projectRoot, typesDir, typeFileName);\n\n // If types file exists, resolve to it\n if (fs.existsSync(typePath)) {\n return typePath;\n }\n\n // Otherwise use virtual module ID for runtime resolution\n return `\\0${VIRTUAL_MODULES[id as keyof typeof VIRTUAL_MODULES]}`;\n }\n return null;\n },\n\n async load(id) {\n // Handle virtual modules if types aren't available\n const cleanId = id.startsWith('\\0') ? id.slice(1) : id;\n\n if (!typeManifest) {\n typeManifest = generationSnapshot\n ? loadGenerationSnapshot()\n : {\n version: '1.0.0',\n timestamp: MANIFEST_TIMESTAMP,\n objects: {},\n };\n }\n\n switch (cleanId) {\n case 'smrt-consumer:routes':\n return generateFallbackRoutesModule();\n\n case 'smrt-consumer:client':\n return generateFallbackClientModule(typeManifest, { kebabRoutes });\n\n case 'smrt-consumer:mcp':\n return generateFallbackMcpModule();\n\n case 'smrt-consumer:types':\n return generateFallbackTypesModule(typeManifest);\n\n case 'smrt-consumer:manifest':\n return generateFallbackManifestModule(typeManifest);\n\n default:\n return null;\n }\n },\n };\n}\n\n/**\n * Discover SMRT packages from a consumer app's dependencies.\n *\n * Intentional split (#1579): this **consumer-plugin** path is async and\n * resolves SMRT packages from the downstream app's `package.json` dependency\n * names (`@have/`/`smrt` heuristic + `hasSmrtManifest` probe) inside the Vite\n * consumer plugin. It is deliberately separate from the build-time\n * `discoverSmrtPackages()` in `src/manifest/discover-smrt-packages.ts` — a\n * synchronous, lockfile-cached `node_modules` manifest scan used for manifest\n * generation. Different inputs, contexts, and lifecycles, not duplicated logic.\n */\nasync function discoverSmrtPackages(projectRoot: string): Promise<string[]> {\n const packages: string[] = [];\n const nodeModulesPath = path.join(projectRoot, 'node_modules');\n\n if (!fs.existsSync(nodeModulesPath)) {\n return packages;\n }\n\n try {\n // Check package.json for workspace dependencies\n const packageJsonPath = path.join(projectRoot, 'package.json');\n if (fs.existsSync(packageJsonPath)) {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));\n const allDeps = {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n ...packageJson.peerDependencies,\n };\n\n // Look for packages that likely contain SMRT objects\n for (const [name, version] of Object.entries(allDeps)) {\n if (\n typeof version === 'string' &&\n (name.includes('smrt') ||\n name.includes('@have/') ||\n (await hasSmrtManifest(nodeModulesPath, name)))\n ) {\n packages.push(name);\n }\n }\n }\n } catch (error) {\n console.warn('[smrt:consumer] Error discovering packages:', error);\n }\n\n return packages;\n}\n\n/**\n * Check if a package has SMRT manifest\n */\nasync function hasSmrtManifest(\n nodeModulesPath: string,\n packageName: string,\n): Promise<boolean> {\n const packagePath = path.join(nodeModulesPath, packageName);\n const manifestPath = path.join(\n packagePath,\n 'dist',\n 'manifest',\n 'static-manifest.js',\n );\n return fs.existsSync(manifestPath);\n}\n\n/**\n * Aggregate type manifests from multiple packages\n */\nasync function aggregateTypeManifests(\n packages: string[],\n projectRoot: string,\n): Promise<ConsumerManifest> {\n const aggregatedManifest: ConsumerManifest = {\n version: '1.0.0',\n timestamp: MANIFEST_TIMESTAMP,\n objects: {},\n };\n\n for (const packageName of packages) {\n try {\n const packageDir = path.join(projectRoot, 'node_modules', packageName);\n\n // Load package.json for version and export information\n const packageJsonPath = path.join(packageDir, 'package.json');\n let packageJson: ConsumerPackageJson;\n try {\n const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf-8');\n packageJson = JSON.parse(packageJsonContent) as ConsumerPackageJson;\n } catch {\n console.warn(\n `[smrt:consumer] Could not read package.json for ${packageName}`,\n );\n continue;\n }\n\n // Try multiple manifest locations\n const manifestCandidates = [\n path.join(packageDir, 'dist', 'manifest', 'static-manifest.js'),\n path.join(packageDir, 'dist', 'manifest.json'),\n path.join(packageDir, 'manifest.json'),\n ];\n\n for (const manifestPath of manifestCandidates) {\n if (fs.existsSync(manifestPath)) {\n // Import or read the manifest\n let manifest: Partial<ConsumerManifest> | undefined;\n if (manifestPath.endsWith('.js')) {\n const manifestModule = await import(manifestPath);\n manifest = manifestModule.staticManifest || manifestModule.default;\n } else {\n const manifestContent = fs.readFileSync(manifestPath, 'utf-8');\n manifest = JSON.parse(manifestContent) as Partial<ConsumerManifest>;\n }\n\n if (manifest?.objects) {\n console.log(\n `[smrt:consumer] Loaded manifest from ${packageName} (${Object.keys(manifest.objects).length} objects)`,\n );\n\n // ENHANCED: Preserve package metadata for each object\n for (const [objectName, objectDef] of Object.entries(\n manifest.objects,\n )) {\n const def = objectDef;\n\n aggregatedManifest.objects[objectName] = {\n ...def,\n // Ensure package metadata is preserved/set\n packageName:\n def.packageName || manifest.packageName || packageName,\n packageVersion:\n def.packageVersion ||\n manifest.packageVersion ||\n packageJson.version,\n // Add fallback import paths if missing\n importPath: def.importPath || determineImportPath(packageJson),\n exportName: def.exportName || def.className || objectName,\n collectionExportName:\n def.collectionExportName ||\n `${def.className || objectName}Collection`,\n };\n }\n\n break; // Use first found manifest for this package\n }\n }\n }\n } catch (error) {\n console.warn(\n `[smrt:consumer] Error loading manifest from ${packageName}:`,\n error,\n );\n }\n }\n\n return aggregatedManifest;\n}\n\n/**\n * Determine import path from package.json\n */\nfunction determineImportPath(packageJson: ConsumerPackageJson): string {\n const packageName = packageJson.name;\n\n if (!packageName) {\n throw new Error('Package name not found in package.json');\n }\n\n // Strategy 1: Check for specific exports\n if (packageJson.exports) {\n // Check for objects export\n if (packageJson.exports['./objects']) {\n return `${packageName}/objects`;\n }\n\n // Check for main export\n const mainExport = packageJson.exports['.'];\n if (mainExport) {\n // Handle conditional exports\n if (typeof mainExport === 'object' && mainExport !== null) {\n const conditional = mainExport as Record<string, unknown>;\n if (conditional.import) {\n return packageName;\n }\n if (conditional.default) {\n return packageName;\n }\n }\n return packageName;\n }\n }\n\n // Strategy 2: Check main field\n if (packageJson.main) {\n return packageName;\n }\n\n // Strategy 3: Fallback to package name\n return packageName;\n}\n\n/**\n * Save aggregated manifest to .smrt/manifest.json for CLI discovery.\n *\n * Merge-preserving: `smrtPlugin()` writes the project's own scanned objects\n * to the same file (`writeLocalManifest`, issue #963), and both writes happen\n * in parallel `buildStart` hooks — so a plain overwrite here would clobber\n * the local objects whenever this plugin's write lands last (issue #1760\n * review). Local field metadata would then silently vanish from CLI schema\n * commands and from server runtimes that seed `.smrt/manifest.json`, dropping\n * domain columns on write. This function therefore only ADDS/refreshes the\n * external-package entries it owns and preserves everything else already in\n * the file (including the top-level `packageName` the local write sets).\n */\nasync function saveAggregatedManifest(\n manifest: ConsumerManifest,\n projectRoot: string,\n): Promise<void> {\n const smrtDir = path.join(projectRoot, '.smrt');\n const manifestPath = path.join(smrtDir, 'manifest.json');\n\n try {\n // Create .smrt directory if it doesn't exist\n if (!fs.existsSync(smrtDir)) {\n fs.mkdirSync(smrtDir, { recursive: true });\n }\n\n // Merge with whatever is on disk: existing entries (typically the local\n // project's objects written by smrtPlugin) are preserved; aggregated\n // external entries win for the qualified names this plugin owns.\n let merged: ConsumerManifest = manifest;\n if (fs.existsSync(manifestPath)) {\n try {\n const existing = JSON.parse(\n fs.readFileSync(manifestPath, 'utf-8'),\n ) as Partial<ConsumerManifest>;\n if (existing && typeof existing.objects === 'object') {\n merged = {\n ...existing,\n ...manifest,\n // The aggregated manifest carries no packageName; keep the local\n // project's (used as the manifest cache key at runtime).\n ...(existing.packageName\n ? { packageName: existing.packageName }\n : {}),\n objects: { ...existing.objects, ...manifest.objects },\n };\n }\n } catch {\n // Unreadable/corrupt existing file — fall back to a plain write.\n }\n }\n\n // Write manifest\n fs.writeFileSync(manifestPath, JSON.stringify(merged, null, 2), 'utf-8');\n\n console.log(\n `[smrt:consumer] Saved aggregated manifest to .smrt/manifest.json (${Object.keys(merged.objects).length} objects)`,\n );\n } catch (error) {\n console.warn('[smrt:consumer] Failed to save aggregated manifest:', error);\n }\n}\n\n/**\n * Generate registration file for CLI class loading\n *\n * Creates .smrt/register.js with static imports and registrations\n * for all external SMRT objects discovered during build.\n */\nasync function generateRegistrationFile(\n manifest: ConsumerManifest,\n projectRoot: string,\n): Promise<void> {\n const smrtDir = path.join(projectRoot, '.smrt');\n const registerPath = path.join(smrtDir, 'register.js');\n\n // Bind every imported symbol to a generated local name. Aggregated manifests\n // may contain same-named exports from different packages (and may list a\n // collection both beside its object and as its own manifest entry), so using\n // provider export names as local bindings can produce invalid duplicate\n // imports in a production consumer bundle.\n const importBindings = new Map<string, string>();\n const importsByPath = new Map<string, Map<string, string>>();\n let nextImportBinding = 0;\n const getImportBinding = (importPath: string, exportName: string): string => {\n const key = `${importPath}\\0${exportName}`;\n const existing = importBindings.get(key);\n if (existing) return existing;\n const binding = `__smrt_consumer_${nextImportBinding++}`;\n importBindings.set(key, binding);\n const specifiers =\n importsByPath.get(importPath) ?? new Map<string, string>();\n specifiers.set(exportName, binding);\n importsByPath.set(importPath, specifiers);\n return binding;\n };\n\n const registrations: string[] = [];\n const registrationManifests: Record<string, ConsumerManifest> = {};\n let importedEntryCount = 0;\n let registeredObjectCount = 0;\n\n const manifestObjects = manifest.objects;\n const manifestObjectLookup = new Map<string, ConsumerObjectDefinition>();\n for (const [key, def] of Object.entries(manifestObjects)) {\n const candidate = def;\n const lookupKeys = [\n key,\n key.includes(':') ? key.split(':').pop() : undefined,\n candidate.qualifiedName,\n candidate.className,\n candidate.exportName,\n ];\n\n for (const lookupKey of lookupKeys) {\n if (lookupKey && !manifestObjectLookup.has(lookupKey)) {\n manifestObjectLookup.set(lookupKey, candidate);\n }\n }\n }\n\n const collectionClassMemo = new WeakMap<object, boolean>();\n\n const isCollectionClass = (\n def: ConsumerObjectDefinition | undefined,\n seen = new Set<string>(),\n ): boolean => {\n if (!def || typeof def !== 'object') {\n return false;\n }\n\n const cached = collectionClassMemo.get(def);\n if (cached !== undefined) {\n return cached;\n }\n\n if (\n def?.extends === 'SmrtCollection' ||\n def?.extendsTypeArg !== undefined\n ) {\n collectionClassMemo.set(def, true);\n return true;\n }\n\n const parentName = def?.extendsQualified || def?.extends;\n if (!parentName || seen.has(parentName)) {\n collectionClassMemo.set(def, false);\n return false;\n }\n seen.add(parentName);\n\n const parentDef = manifestObjectLookup.get(parentName);\n const isCollection = parentDef ? isCollectionClass(parentDef, seen) : false;\n collectionClassMemo.set(def, isCollection);\n\n return isCollection;\n };\n\n for (const [objectName, objectDef] of Object.entries(manifestObjects)) {\n const def = objectDef;\n\n // Skip local objects (they're imported from local entry point)\n if (!def.packageName || def.packageName === manifest.packageName) {\n continue;\n }\n\n const importPath = def.importPath || def.packageName;\n const exportName = def.exportName || def.className || objectName;\n const collectionExportName = def.collectionExportName;\n const hasCollection = def.hasCollection; // Check if collection class actually exists\n const tableName = def.collection || objectName.toLowerCase();\n\n const exportBinding = getImportBinding(importPath, exportName);\n const collectionBinding =\n hasCollection && collectionExportName\n ? getImportBinding(importPath, collectionExportName)\n : undefined;\n importedEntryCount++;\n\n if (isCollectionClass(def)) {\n continue;\n }\n\n const logicalName = def.className || exportName;\n registrationManifests[objectName] = {\n ...manifest,\n packageName: def.packageName,\n packageVersion: def.packageVersion || manifest.packageVersion,\n objects: { [objectName]: def },\n };\n\n // Import evaluation triggers the provider decorator first. The explicit\n // constructor/package/key tuple then promotes that exact constructor with\n // its isolated manifest, which is stable across Rollup name deconfliction.\n registrations.push(\n `if (${exportBinding}) ObjectRegistry.register(${exportBinding}, { name: ${JSON.stringify(logicalName)}, packageName: ${JSON.stringify(def.packageName)}, _manifest: smrtRegistrationManifests[${JSON.stringify(objectName)}], _manifestKey: ${JSON.stringify(objectName)} });`,\n );\n\n // Only register collection if it exists\n if (collectionBinding) {\n registrations.push(\n `if (${collectionBinding}) ObjectRegistry.registerCollection('${tableName}', ${collectionBinding});`,\n );\n }\n\n registeredObjectCount++;\n }\n\n // Skip generation if no external entries\n if (importedEntryCount === 0) {\n console.log('[smrt:consumer] No external entries - skipping register.js');\n return;\n }\n\n const registeredObjectLabel =\n registeredObjectCount === 1 ? 'object' : 'objects';\n const sortedImports = Array.from(importsByPath.entries()).sort(\n ([left], [right]) => left.localeCompare(right),\n );\n const imports = sortedImports.map(\n ([importPath], index) =>\n `import * as __smrt_provider_${index} from '${importPath}';`,\n );\n const importDeclarations = sortedImports.flatMap(([, specifiers], index) =>\n Array.from(specifiers.entries())\n .sort(([left], [right]) => left.localeCompare(right))\n .map(\n ([exportName, binding]) =>\n `const ${binding} = getSmrtExport(__smrt_provider_${index}, ${JSON.stringify(exportName)});`,\n ),\n );\n const registrationManifestLiteral = JSON.stringify(\n JSON.stringify(registrationManifests),\n );\n\n // Generate file content\n const content = `/**\n * Auto-generated by @happyvertical/smrt-core/consumer-plugin\n * DO NOT EDIT - This file is regenerated on every build\n *\n * Registers SMRT objects from external packages for CLI discovery.\n * Generated at: ${new Date().toISOString()}\n */\n\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n${imports.join('\\n')}\n\n/**\n * @param {Record<string, unknown>} provider\n * @param {string} exportName\n * @returns {any}\n */\nconst getSmrtExport = (provider, exportName) =>\n typeof provider[exportName] === 'function' ? provider[exportName] : undefined;\n${importDeclarations.join('\\n')}\n\nconst smrtRegistrationManifests = JSON.parse(${registrationManifestLiteral});\n\n// Register all objects (executed during module evaluation)\n${registrations.join('\\n')}\n\nexport function registerAll() {\n // Objects are already registered during module evaluation\n console.log('[smrt:register] Registered ${registeredObjectCount} external ${registeredObjectLabel}');\n}\n`;\n\n // Create .smrt directory if needed\n if (!fs.existsSync(smrtDir)) {\n fs.mkdirSync(smrtDir, { recursive: true });\n }\n\n // Write registration file\n fs.writeFileSync(registerPath, content, 'utf-8');\n\n console.log(\n `[smrt:consumer] Generated .smrt/register.js with ${importedEntryCount} external entries (${registeredObjectCount} registered ${registeredObjectLabel})`,\n );\n}\n\n/**\n * Generate project-specific types\n */\nasync function generateProjectTypes(\n typeManifest: ConsumerManifest,\n typesDir: string,\n projectRoot: string,\n): Promise<void> {\n if (!typeManifest || Object.keys(typeManifest.objects).length === 0) {\n console.log(\n '[smrt:consumer] No SMRT objects found, skipping type generation',\n );\n return;\n }\n\n await generateDeclarations({\n // The aggregated manifest is a runtime SMRT manifest assembled from external\n // package manifests; it is intentionally typed loosely at the JSON boundary,\n // so narrow it to the declaration generator's strict manifest shape here.\n manifest: typeManifest as unknown as SmartObjectManifest,\n outDir: typesDir,\n projectRoot,\n includeVirtualModules: true,\n includeObjectTypes: true,\n });\n\n console.log(\n `[smrt:consumer] Generated types for ${Object.keys(typeManifest.objects).length} objects`,\n );\n}\n\n/**\n * Get type file name for virtual module\n */\nfunction getTypeFileName(virtualModule: string): string {\n const moduleMap: Record<string, string> = {\n '@smrt/routes': 'smrt-routes.d.ts',\n '@smrt/client': 'smrt-client.d.ts',\n '@smrt/mcp': 'smrt-mcp.d.ts',\n '@smrt/types': 'smrt-types.d.ts',\n '@smrt/manifest': 'smrt-manifest.d.ts',\n };\n return moduleMap[virtualModule] || 'smrt-unknown.d.ts';\n}\n\n/**\n * Fallback modules for when types aren't available\n */\nfunction generateFallbackRoutesModule(): string {\n return `\n// Fallback routes module\nexport function setupRoutes(app) {\n console.warn('[smrt:consumer] No routes available - SMRT packages may not be properly configured');\n}\nexport default setupRoutes;\n`;\n}\n\nfunction generateFallbackClientModule(\n manifest: ConsumerManifest,\n options: { kebabRoutes?: boolean } = {},\n): string {\n const objects = Object.entries(manifest?.objects || {});\n if (objects.length === 0) {\n return `\n// Fallback client module\nexport function createClient(basePath = '/api/v1') {\n console.warn('[smrt:consumer] No API client available - SMRT packages may not be properly configured');\n return {};\n}\nexport default createClient;\n`;\n }\n\n return generateClientModule(manifest as unknown as SmartObjectManifest, {\n kebabRoutes: options.kebabRoutes,\n });\n}\n\nfunction generateFallbackMcpModule(): string {\n return `\n// Fallback MCP module\nexport const tools = [];\nexport function createMCPServer() {\n console.warn('[smrt:consumer] No MCP tools available - SMRT packages may not be properly configured');\n return { name: 'smrt-consumer', version: '1.0.0', tools: [] };\n}\nexport default createMCPServer;\n`;\n}\n\nfunction generateFallbackTypesModule(manifest: ConsumerManifest): string {\n const objects = Object.entries(manifest?.objects || {});\n if (objects.length === 0) {\n return `// No types available`;\n }\n\n // Generate basic interfaces\n const interfaces = objects.map(([_name, obj]) => {\n return `export interface ${obj.className}Data {\n id?: string;\n created_at?: string;\n updated_at?: string;\n [key: string]: any;\n}`;\n });\n\n return interfaces.join('\\n\\n');\n}\n\nfunction generateFallbackManifestModule(manifest: ConsumerManifest): string {\n return `\n// Auto-generated manifest from SMRT consumer\nexport const manifest = ${JSON.stringify(manifest, null, 2)};\nexport default manifest;\n`;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/consumer-plugin/index.ts"],"sourcesContent":["/**\n * Vite plugin for consuming SMRT packages\n * Solves virtual module resolution in downstream projects\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport type { DomainKnowledgeAgentSurface } from '@happyvertical/smrt-types';\nimport type { Plugin } from 'vite';\nimport {\n loadVerifiedSmrtGenerationSnapshot,\n type SmrtGenerationSnapshotOptions,\n} from '../generation-snapshot.js';\nimport { buildDomainKnowledgeManifest } from '../knowledge.js';\nimport { resolveFileKnowledgeConfig } from '../knowledge-config.js';\nimport { generateDeclarations } from '../prebuild/index.js';\nimport type { SmartObjectManifest } from '../scanner/types.js';\nimport { MANIFEST_TIMESTAMP } from '../scanner/types.js';\nimport { generateClientModule } from '../vite-plugin/generated-client.js';\nimport type { SmrtPluginApi } from '../vite-plugin/index.js';\nimport { publishArtifactFiles } from './artifact-publication.js';\n\nexport {\n loadVerifiedSmrtGenerationSnapshot,\n type SerializeSmrtGenerationSnapshotOptions,\n type SmrtGenerationSnapshotArtifact,\n type SmrtGenerationSnapshotOptions,\n type SmrtGenerationSnapshotView,\n serializeSmrtGenerationSnapshot,\n sha256SmrtGenerationSnapshot,\n} from '../generation-snapshot.js';\n\n/**\n * Loosely-typed view of an object definition as carried by an external\n * package's static manifest. The static manifests are read from JSON at the\n * package boundary, so only the fields this plugin consumes are typed; the\n * index signature preserves any additional fields (e.g. for spreads). This is\n * a structural superset of a manifest `SmartObjectDefinition` plus the\n * consumer-only `hasCollection` marker.\n */\ninterface ConsumerObjectDefinition {\n className?: string;\n packageName?: string;\n packageVersion?: string;\n qualifiedName?: string;\n importPath?: string;\n exportName?: string;\n collectionExportName?: string;\n hasCollection?: boolean;\n collection?: string;\n extends?: string;\n extendsQualified?: string;\n extendsTypeArg?: string;\n [key: string]: unknown;\n}\n\n/**\n * Aggregated manifest assembled by the consumer plugin from one or more\n * external package manifests. Loosely typed because the inputs originate from\n * JSON read at the package boundary.\n */\ninterface ConsumerManifest {\n version: string;\n timestamp: number;\n packageName?: string;\n packageVersion?: string;\n objects: Record<string, ConsumerObjectDefinition>;\n}\n\n/**\n * Minimal structural shape of a parsed `package.json` consumed here (name,\n * version, and the export map used to derive import paths). The index\n * signature keeps the remaining fields accessible.\n */\ninterface ConsumerPackageJson {\n name?: string;\n version?: string;\n main?: string;\n exports?: Record<string, unknown>;\n [key: string]: unknown;\n}\n\nexport interface SmrtConsumerOptions {\n /** SMRT packages to scan (e.g., ['@my-org/products', '@my-org/content']) */\n packages?: string[];\n /** Generate TypeScript declarations */\n generateTypes?: boolean;\n /** Output directory for generated types */\n typesDir?: string;\n /** Project root path */\n projectRoot?: string;\n /**\n * Reuse an immutable, verified aggregated manifest instead of discovering\n * packages or writing `.smrt/manifest.json`. Registration and generated\n * types still consume the verified manifest.\n */\n generationSnapshot?: SmrtGenerationSnapshotOptions;\n /** SvelteKit integration mode */\n svelteKit?: boolean;\n /**\n * Apply kebab-case to generated custom-method URL segments. This must match\n * the producer plugin's `svelteKit.kebabRoutes` setting.\n */\n kebabRoutes?: boolean;\n /** Use static types only (for federation builds) */\n staticTypes?: boolean;\n /** Disable file scanning */\n disableScanning?: boolean;\n}\n\n// Distinct resolved ids per plugin (#1795). smrtPlugin resolves\n// `@happyvertical/smrt-virt-*` to `\\0smrt:*`; if this consumer plugin also\n// resolved its `@smrt/*` specifiers to `\\0smrt:*` the two virtual modules would\n// share a rollup id, and in standalone/federation builds the consumer's\n// fallback `load` would non-deterministically win and shadow smrtPlugin's real\n// module. Namespacing the consumer ids (`\\0smrt-consumer:*`) keeps them\n// separate so each plugin only ever loads its own module.\nconst VIRTUAL_MODULES = {\n '@smrt/routes': 'smrt-consumer:routes',\n '@smrt/client': 'smrt-consumer:client',\n '@smrt/mcp': 'smrt-consumer:mcp',\n '@smrt/types': 'smrt-consumer:types',\n '@smrt/manifest': 'smrt-consumer:manifest',\n};\n\n/**\n * Consumer plugin for projects that use SMRT packages\n */\nexport function smrtConsumer(options: SmrtConsumerOptions = {}): Plugin {\n const {\n packages = [],\n generateTypes = true,\n typesDir = 'src/types/smrt-generated',\n projectRoot = process.cwd(),\n generationSnapshot,\n disableScanning = false,\n kebabRoutes = false,\n } = options;\n\n let smrtPackages: string[] = [];\n let typeManifest: ConsumerManifest | null = null;\n let typesGenerated = false;\n let producerApi: SmrtPluginApi | undefined;\n\n function loadGenerationSnapshot(): ConsumerManifest {\n if (!generationSnapshot) {\n throw new Error('[smrt:consumer] Generation snapshot is not configured');\n }\n return loadVerifiedSmrtGenerationSnapshot<ConsumerManifest>(\n generationSnapshot,\n projectRoot,\n 'dependencies',\n );\n }\n\n return {\n name: 'smrt-consumer',\n\n config() {\n return {\n build: {\n rollupOptions: {\n // Runtime registration evaluates provider entry points so their\n // exact constructors can be registered. Leave optional native\n // provider binaries to Node instead of parsing them as JavaScript.\n external: [/\\.node$/],\n },\n },\n };\n },\n\n configResolved(resolvedConfig) {\n producerApi = (resolvedConfig.plugins ?? []).find(\n (plugin) => plugin?.name === 'smrt-auto-service',\n )?.api as SmrtPluginApi | undefined;\n },\n\n async buildStart() {\n console.log('[smrt:consumer] Initializing SMRT consumer plugin');\n\n if (generationSnapshot) {\n typeManifest = loadGenerationSnapshot();\n console.log(\n `[smrt:consumer] Reusing verified generation snapshot (${generationSnapshot.provenance})`,\n );\n await generateRegistrationFile(typeManifest, projectRoot);\n if (generateTypes && !typesGenerated) {\n await generateProjectTypes(typeManifest, typesDir, projectRoot);\n typesGenerated = true;\n }\n return;\n }\n\n // Discover SMRT packages if not explicitly specified\n if (packages.length === 0 && !disableScanning) {\n smrtPackages = await discoverSmrtPackages(projectRoot);\n } else {\n smrtPackages = packages;\n }\n\n if (smrtPackages.length > 0) {\n console.log(\n `[smrt:consumer] Found SMRT packages: ${smrtPackages.join(', ')}`,\n );\n\n // Aggregate type manifests from discovered packages\n typeManifest = await aggregateTypeManifests(smrtPackages, projectRoot);\n // Wait before reading .smrt/manifest.json: a producer's parallel\n // buildStart writes its current local manifest after scanning. Reading\n // first could merge an older local manifest with a newer surface.\n const agentSurface = producerApi\n ? await producerApi.resolveKnowledgeAgentSurface()\n : undefined;\n\n // Save aggregated manifest for CLI discovery\n await saveAggregatedManifest(\n typeManifest,\n projectRoot,\n producerApi?.resolveKnowledgeConfig,\n agentSurface,\n );\n\n // Generate registration file for CLI class loading\n await generateRegistrationFile(typeManifest, projectRoot);\n\n // Generate types if requested\n if (generateTypes && !typesGenerated) {\n await generateProjectTypes(typeManifest, typesDir, projectRoot);\n typesGenerated = true;\n }\n } else {\n console.log('[smrt:consumer] No SMRT packages found');\n typeManifest = {\n version: '1.0.0',\n timestamp: MANIFEST_TIMESTAMP,\n objects: {},\n };\n }\n },\n\n resolveId(id, _importer) {\n // Resolve virtual modules to generated type declarations\n if (id in VIRTUAL_MODULES) {\n const typeFileName = getTypeFileName(id);\n const typePath = path.join(projectRoot, typesDir, typeFileName);\n\n // If types file exists, resolve to it\n if (fs.existsSync(typePath)) {\n return typePath;\n }\n\n // Otherwise use virtual module ID for runtime resolution\n return `\\0${VIRTUAL_MODULES[id as keyof typeof VIRTUAL_MODULES]}`;\n }\n return null;\n },\n\n async load(id) {\n // Handle virtual modules if types aren't available\n const cleanId = id.startsWith('\\0') ? id.slice(1) : id;\n\n if (!typeManifest) {\n typeManifest = generationSnapshot\n ? loadGenerationSnapshot()\n : {\n version: '1.0.0',\n timestamp: MANIFEST_TIMESTAMP,\n objects: {},\n };\n }\n\n switch (cleanId) {\n case 'smrt-consumer:routes':\n return generateFallbackRoutesModule();\n\n case 'smrt-consumer:client':\n return generateFallbackClientModule(typeManifest, { kebabRoutes });\n\n case 'smrt-consumer:mcp':\n return generateFallbackMcpModule();\n\n case 'smrt-consumer:types':\n return generateFallbackTypesModule(typeManifest);\n\n case 'smrt-consumer:manifest':\n return generateFallbackManifestModule(typeManifest);\n\n default:\n return null;\n }\n },\n };\n}\n\n/**\n * Discover SMRT packages from a consumer app's dependencies.\n *\n * Intentional split (#1579): this **consumer-plugin** path is async and\n * resolves SMRT packages from the downstream app's `package.json` dependency\n * names (`@have/`/`smrt` heuristic + `hasSmrtManifest` probe) inside the Vite\n * consumer plugin. It is deliberately separate from the build-time\n * `discoverSmrtPackages()` in `src/manifest/discover-smrt-packages.ts` — a\n * synchronous, lockfile-cached `node_modules` manifest scan used for manifest\n * generation. Different inputs, contexts, and lifecycles, not duplicated logic.\n */\nasync function discoverSmrtPackages(projectRoot: string): Promise<string[]> {\n const packages: string[] = [];\n const nodeModulesPath = path.join(projectRoot, 'node_modules');\n\n if (!fs.existsSync(nodeModulesPath)) {\n return packages;\n }\n\n try {\n // Check package.json for workspace dependencies\n const packageJsonPath = path.join(projectRoot, 'package.json');\n if (fs.existsSync(packageJsonPath)) {\n const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));\n const allDeps = {\n ...packageJson.dependencies,\n ...packageJson.devDependencies,\n ...packageJson.peerDependencies,\n };\n\n // Look for packages that likely contain SMRT objects\n for (const [name, version] of Object.entries(allDeps)) {\n if (\n typeof version === 'string' &&\n (name.includes('smrt') ||\n name.includes('@have/') ||\n (await hasSmrtManifest(nodeModulesPath, name)))\n ) {\n packages.push(name);\n }\n }\n }\n } catch (error) {\n console.warn('[smrt:consumer] Error discovering packages:', error);\n }\n\n return packages;\n}\n\n/**\n * Check if a package has SMRT manifest\n */\nasync function hasSmrtManifest(\n nodeModulesPath: string,\n packageName: string,\n): Promise<boolean> {\n const packagePath = path.join(nodeModulesPath, packageName);\n const manifestPath = path.join(\n packagePath,\n 'dist',\n 'manifest',\n 'static-manifest.js',\n );\n return fs.existsSync(manifestPath);\n}\n\n/**\n * Aggregate type manifests from multiple packages\n */\nasync function aggregateTypeManifests(\n packages: string[],\n projectRoot: string,\n): Promise<ConsumerManifest> {\n const aggregatedManifest: ConsumerManifest = {\n version: '1.0.0',\n timestamp: MANIFEST_TIMESTAMP,\n objects: {},\n };\n\n for (const packageName of packages) {\n try {\n const packageDir = path.join(projectRoot, 'node_modules', packageName);\n\n // Load package.json for version and export information\n const packageJsonPath = path.join(packageDir, 'package.json');\n let packageJson: ConsumerPackageJson;\n try {\n const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf-8');\n packageJson = JSON.parse(packageJsonContent) as ConsumerPackageJson;\n } catch {\n console.warn(\n `[smrt:consumer] Could not read package.json for ${packageName}`,\n );\n continue;\n }\n\n // Try multiple manifest locations\n const manifestCandidates = [\n path.join(packageDir, 'dist', 'manifest', 'static-manifest.js'),\n path.join(packageDir, 'dist', 'manifest.json'),\n path.join(packageDir, 'manifest.json'),\n ];\n\n for (const manifestPath of manifestCandidates) {\n if (fs.existsSync(manifestPath)) {\n // Import or read the manifest\n let manifest: Partial<ConsumerManifest> | undefined;\n if (manifestPath.endsWith('.js')) {\n const manifestModule = await import(manifestPath);\n manifest = manifestModule.staticManifest || manifestModule.default;\n } else {\n const manifestContent = fs.readFileSync(manifestPath, 'utf-8');\n manifest = JSON.parse(manifestContent) as Partial<ConsumerManifest>;\n }\n\n if (manifest?.objects) {\n console.log(\n `[smrt:consumer] Loaded manifest from ${packageName} (${Object.keys(manifest.objects).length} objects)`,\n );\n\n // ENHANCED: Preserve package metadata for each object\n for (const [objectName, objectDef] of Object.entries(\n manifest.objects,\n )) {\n const def = objectDef;\n\n aggregatedManifest.objects[objectName] = {\n ...def,\n // Ensure package metadata is preserved/set\n packageName:\n def.packageName || manifest.packageName || packageName,\n packageVersion:\n def.packageVersion ||\n manifest.packageVersion ||\n packageJson.version,\n // Add fallback import paths if missing\n importPath: def.importPath || determineImportPath(packageJson),\n exportName: def.exportName || def.className || objectName,\n collectionExportName:\n def.collectionExportName ||\n `${def.className || objectName}Collection`,\n };\n }\n\n break; // Use first found manifest for this package\n }\n }\n }\n } catch (error) {\n console.warn(\n `[smrt:consumer] Error loading manifest from ${packageName}:`,\n error,\n );\n }\n }\n\n return aggregatedManifest;\n}\n\n/**\n * Determine import path from package.json\n */\nfunction determineImportPath(packageJson: ConsumerPackageJson): string {\n const packageName = packageJson.name;\n\n if (!packageName) {\n throw new Error('Package name not found in package.json');\n }\n\n // Strategy 1: Check for specific exports\n if (packageJson.exports) {\n // Check for objects export\n if (packageJson.exports['./objects']) {\n return `${packageName}/objects`;\n }\n\n // Check for main export\n const mainExport = packageJson.exports['.'];\n if (mainExport) {\n // Handle conditional exports\n if (typeof mainExport === 'object' && mainExport !== null) {\n const conditional = mainExport as Record<string, unknown>;\n if (conditional.import) {\n return packageName;\n }\n if (conditional.default) {\n return packageName;\n }\n }\n return packageName;\n }\n }\n\n // Strategy 2: Check main field\n if (packageJson.main) {\n return packageName;\n }\n\n // Strategy 3: Fallback to package name\n return packageName;\n}\n\n/**\n * Save aggregated manifest to .smrt/manifest.json for CLI discovery.\n *\n * Merge-preserving: `smrtPlugin()` writes the project's own scanned objects\n * to the same file (`writeLocalManifest`, issue #963), and both writes happen\n * in parallel `buildStart` hooks — so a plain overwrite here would clobber\n * the local objects whenever this plugin's write lands last (issue #1760\n * review). Local field metadata would then silently vanish from CLI schema\n * commands and from server runtimes that seed `.smrt/manifest.json`, dropping\n * domain columns on write. This function therefore only ADDS/refreshes the\n * external-package entries it owns and preserves everything else already in\n * the file (including the top-level `packageName` the local write sets).\n */\nasync function saveAggregatedManifest(\n manifest: ConsumerManifest,\n projectRoot: string,\n resolveKnowledgeConfig?: SmrtPluginApi['resolveKnowledgeConfig'],\n agentSurface?: DomainKnowledgeAgentSurface,\n): Promise<void> {\n const smrtDir = path.join(projectRoot, '.smrt');\n const manifestPath = path.join(smrtDir, 'manifest.json');\n\n try {\n // Create .smrt directory if it doesn't exist\n if (!fs.existsSync(smrtDir)) {\n fs.mkdirSync(smrtDir, { recursive: true });\n }\n\n // Merge with whatever is on disk: existing entries (typically the local\n // project's objects written by smrtPlugin) are preserved; aggregated\n // external entries win for the qualified names this plugin owns.\n let merged: ConsumerManifest = manifest;\n if (fs.existsSync(manifestPath)) {\n try {\n const existing = JSON.parse(\n fs.readFileSync(manifestPath, 'utf-8'),\n ) as Partial<ConsumerManifest>;\n if (existing && typeof existing.objects === 'object') {\n merged = {\n ...existing,\n ...manifest,\n // The aggregated manifest carries no packageName; keep the local\n // project's (used as the manifest cache key at runtime).\n ...(existing.packageName\n ? { packageName: existing.packageName }\n : {}),\n objects: { ...existing.objects, ...manifest.objects },\n };\n }\n } catch {\n // Unreadable/corrupt existing file — fall back to a plain write.\n }\n }\n\n // smrtPlugin writes the local knowledge artifact before this consumer\n // plugin merges external package entries into the same manifest. Refresh\n // the artifact from the merged manifest so its source hash always names\n // the manifest that CLI discovery and server runtimes actually consume.\n // The consumer deliberately does not load the scanner. Only carry a\n // surface from the current producer scan: a prior artifact can describe\n // declarations that have since changed while retaining the same path.\n // Re-hashing that current path under a stale declaration would make an\n // incorrect agent contract look fresh.\n const knowledgePath = path.join(smrtDir, 'smrt-knowledge.json');\n const packageJsonPath = path.join(projectRoot, 'package.json');\n const packageJson = fs.existsSync(packageJsonPath)\n ? JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'))\n : undefined;\n const knowledgeConfig = resolveKnowledgeConfig\n ? await resolveKnowledgeConfig(merged as unknown as SmartObjectManifest)\n : await resolveFileKnowledgeConfig(\n projectRoot,\n merged.packageName ?? packageJson?.name,\n );\n if (knowledgeConfig.enabled === false) {\n publishArtifactFiles([\n { path: manifestPath, content: JSON.stringify(merged, null, 2) },\n ]);\n return;\n }\n const knowledge = buildDomainKnowledgeManifest({\n manifest: merged as unknown as SmartObjectManifest,\n rootDir: projectRoot,\n packageJson,\n manifestPath,\n config: knowledgeConfig,\n agentSurface,\n });\n // Stage both artifacts before replacing either. Renames are individually\n // atomic; if a synchronous later rename fails, restore every earlier\n // replacement. A process crash between renames cannot be made pair-atomic\n // with ordinary filesystem operations, so the next generation remains the\n // freshness repair path for that distinct failure mode.\n publishArtifactFiles([\n { path: knowledgePath, content: JSON.stringify(knowledge, null, 2) },\n { path: manifestPath, content: JSON.stringify(merged, null, 2) },\n ]);\n\n console.log(\n `[smrt:consumer] Saved aggregated manifest to .smrt/manifest.json (${Object.keys(merged.objects).length} objects)`,\n );\n } catch (error) {\n throw new Error('[smrt:consumer] Failed to save aggregated manifest', {\n cause: error,\n });\n }\n}\n\n/**\n * Generate registration file for CLI class loading\n *\n * Creates .smrt/register.js with static imports and registrations\n * for all external SMRT objects discovered during build.\n */\nasync function generateRegistrationFile(\n manifest: ConsumerManifest,\n projectRoot: string,\n): Promise<void> {\n const smrtDir = path.join(projectRoot, '.smrt');\n const registerPath = path.join(smrtDir, 'register.js');\n\n // Bind every imported symbol to a generated local name. Aggregated manifests\n // may contain same-named exports from different packages (and may list a\n // collection both beside its object and as its own manifest entry), so using\n // provider export names as local bindings can produce invalid duplicate\n // imports in a production consumer bundle.\n const importBindings = new Map<string, string>();\n const importsByPath = new Map<string, Map<string, string>>();\n let nextImportBinding = 0;\n const getImportBinding = (importPath: string, exportName: string): string => {\n const key = `${importPath}\\0${exportName}`;\n const existing = importBindings.get(key);\n if (existing) return existing;\n const binding = `__smrt_consumer_${nextImportBinding++}`;\n importBindings.set(key, binding);\n const specifiers =\n importsByPath.get(importPath) ?? new Map<string, string>();\n specifiers.set(exportName, binding);\n importsByPath.set(importPath, specifiers);\n return binding;\n };\n\n const registrations: string[] = [];\n const registrationManifests: Record<string, ConsumerManifest> = {};\n let importedEntryCount = 0;\n let registeredObjectCount = 0;\n\n const manifestObjects = manifest.objects;\n const manifestObjectLookup = new Map<string, ConsumerObjectDefinition>();\n for (const [key, def] of Object.entries(manifestObjects)) {\n const candidate = def;\n const lookupKeys = [\n key,\n key.includes(':') ? key.split(':').pop() : undefined,\n candidate.qualifiedName,\n candidate.className,\n candidate.exportName,\n ];\n\n for (const lookupKey of lookupKeys) {\n if (lookupKey && !manifestObjectLookup.has(lookupKey)) {\n manifestObjectLookup.set(lookupKey, candidate);\n }\n }\n }\n\n const collectionClassMemo = new WeakMap<object, boolean>();\n\n const isCollectionClass = (\n def: ConsumerObjectDefinition | undefined,\n seen = new Set<string>(),\n ): boolean => {\n if (!def || typeof def !== 'object') {\n return false;\n }\n\n const cached = collectionClassMemo.get(def);\n if (cached !== undefined) {\n return cached;\n }\n\n if (\n def?.extends === 'SmrtCollection' ||\n def?.extendsTypeArg !== undefined\n ) {\n collectionClassMemo.set(def, true);\n return true;\n }\n\n const parentName = def?.extendsQualified || def?.extends;\n if (!parentName || seen.has(parentName)) {\n collectionClassMemo.set(def, false);\n return false;\n }\n seen.add(parentName);\n\n const parentDef = manifestObjectLookup.get(parentName);\n const isCollection = parentDef ? isCollectionClass(parentDef, seen) : false;\n collectionClassMemo.set(def, isCollection);\n\n return isCollection;\n };\n\n for (const [objectName, objectDef] of Object.entries(manifestObjects)) {\n const def = objectDef;\n\n // Skip local objects (they're imported from local entry point)\n if (!def.packageName || def.packageName === manifest.packageName) {\n continue;\n }\n\n const importPath = def.importPath || def.packageName;\n const exportName = def.exportName || def.className || objectName;\n const collectionExportName = def.collectionExportName;\n const hasCollection = def.hasCollection; // Check if collection class actually exists\n const tableName = def.collection || objectName.toLowerCase();\n\n const exportBinding = getImportBinding(importPath, exportName);\n const collectionBinding =\n hasCollection && collectionExportName\n ? getImportBinding(importPath, collectionExportName)\n : undefined;\n importedEntryCount++;\n\n if (isCollectionClass(def)) {\n continue;\n }\n\n const logicalName = def.className || exportName;\n registrationManifests[objectName] = {\n ...manifest,\n packageName: def.packageName,\n packageVersion: def.packageVersion || manifest.packageVersion,\n objects: { [objectName]: def },\n };\n\n // Import evaluation triggers the provider decorator first. The explicit\n // constructor/package/key tuple then promotes that exact constructor with\n // its isolated manifest, which is stable across Rollup name deconfliction.\n registrations.push(\n `if (${exportBinding}) ObjectRegistry.register(${exportBinding}, { name: ${JSON.stringify(logicalName)}, packageName: ${JSON.stringify(def.packageName)}, _manifest: smrtRegistrationManifests[${JSON.stringify(objectName)}], _manifestKey: ${JSON.stringify(objectName)} });`,\n );\n\n // Only register collection if it exists\n if (collectionBinding) {\n registrations.push(\n `if (${collectionBinding}) ObjectRegistry.registerCollection('${tableName}', ${collectionBinding});`,\n );\n }\n\n registeredObjectCount++;\n }\n\n // Skip generation if no external entries\n if (importedEntryCount === 0) {\n console.log('[smrt:consumer] No external entries - skipping register.js');\n return;\n }\n\n const registeredObjectLabel =\n registeredObjectCount === 1 ? 'object' : 'objects';\n const sortedImports = Array.from(importsByPath.entries()).sort(\n ([left], [right]) => left.localeCompare(right),\n );\n const imports = sortedImports.map(\n ([importPath], index) =>\n `import * as __smrt_provider_${index} from '${importPath}';`,\n );\n const importDeclarations = sortedImports.flatMap(([, specifiers], index) =>\n Array.from(specifiers.entries())\n .sort(([left], [right]) => left.localeCompare(right))\n .map(\n ([exportName, binding]) =>\n `const ${binding} = getSmrtExport(__smrt_provider_${index}, ${JSON.stringify(exportName)});`,\n ),\n );\n const registrationManifestLiteral = JSON.stringify(\n JSON.stringify(registrationManifests),\n );\n\n // Generate file content\n const content = `/**\n * Auto-generated by @happyvertical/smrt-core/consumer-plugin\n * DO NOT EDIT - This file is regenerated on every build\n *\n * Registers SMRT objects from external packages for CLI discovery.\n * Generated at: ${new Date().toISOString()}\n */\n\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n${imports.join('\\n')}\n\n/**\n * @param {Record<string, unknown>} provider\n * @param {string} exportName\n * @returns {any}\n */\nconst getSmrtExport = (provider, exportName) =>\n typeof provider[exportName] === 'function' ? provider[exportName] : undefined;\n${importDeclarations.join('\\n')}\n\nconst smrtRegistrationManifests = JSON.parse(${registrationManifestLiteral});\n\n// Register all objects (executed during module evaluation)\n${registrations.join('\\n')}\n\nexport function registerAll() {\n // Objects are already registered during module evaluation\n console.log('[smrt:register] Registered ${registeredObjectCount} external ${registeredObjectLabel}');\n}\n`;\n\n // Create .smrt directory if needed\n if (!fs.existsSync(smrtDir)) {\n fs.mkdirSync(smrtDir, { recursive: true });\n }\n\n // Write registration file\n fs.writeFileSync(registerPath, content, 'utf-8');\n\n console.log(\n `[smrt:consumer] Generated .smrt/register.js with ${importedEntryCount} external entries (${registeredObjectCount} registered ${registeredObjectLabel})`,\n );\n}\n\n/**\n * Generate project-specific types\n */\nasync function generateProjectTypes(\n typeManifest: ConsumerManifest,\n typesDir: string,\n projectRoot: string,\n): Promise<void> {\n if (!typeManifest || Object.keys(typeManifest.objects).length === 0) {\n console.log(\n '[smrt:consumer] No SMRT objects found, skipping type generation',\n );\n return;\n }\n\n await generateDeclarations({\n // The aggregated manifest is a runtime SMRT manifest assembled from external\n // package manifests; it is intentionally typed loosely at the JSON boundary,\n // so narrow it to the declaration generator's strict manifest shape here.\n manifest: typeManifest as unknown as SmartObjectManifest,\n outDir: typesDir,\n projectRoot,\n includeVirtualModules: true,\n includeObjectTypes: true,\n });\n\n console.log(\n `[smrt:consumer] Generated types for ${Object.keys(typeManifest.objects).length} objects`,\n );\n}\n\n/**\n * Get type file name for virtual module\n */\nfunction getTypeFileName(virtualModule: string): string {\n const moduleMap: Record<string, string> = {\n '@smrt/routes': 'smrt-routes.d.ts',\n '@smrt/client': 'smrt-client.d.ts',\n '@smrt/mcp': 'smrt-mcp.d.ts',\n '@smrt/types': 'smrt-types.d.ts',\n '@smrt/manifest': 'smrt-manifest.d.ts',\n };\n return moduleMap[virtualModule] || 'smrt-unknown.d.ts';\n}\n\n/**\n * Fallback modules for when types aren't available\n */\nfunction generateFallbackRoutesModule(): string {\n return `\n// Fallback routes module\nexport function setupRoutes(app) {\n console.warn('[smrt:consumer] No routes available - SMRT packages may not be properly configured');\n}\nexport default setupRoutes;\n`;\n}\n\nfunction generateFallbackClientModule(\n manifest: ConsumerManifest,\n options: { kebabRoutes?: boolean } = {},\n): string {\n const objects = Object.entries(manifest?.objects || {});\n if (objects.length === 0) {\n return `\n// Fallback client module\nexport function createClient(basePath = '/api/v1') {\n console.warn('[smrt:consumer] No API client available - SMRT packages may not be properly configured');\n return {};\n}\nexport default createClient;\n`;\n }\n\n return generateClientModule(manifest as unknown as SmartObjectManifest, {\n kebabRoutes: options.kebabRoutes,\n });\n}\n\nfunction generateFallbackMcpModule(): string {\n return `\n// Fallback MCP module\nexport const tools = [];\nexport function createMCPServer() {\n console.warn('[smrt:consumer] No MCP tools available - SMRT packages may not be properly configured');\n return { name: 'smrt-consumer', version: '1.0.0', tools: [] };\n}\nexport default createMCPServer;\n`;\n}\n\nfunction generateFallbackTypesModule(manifest: ConsumerManifest): string {\n const objects = Object.entries(manifest?.objects || {});\n if (objects.length === 0) {\n return `// No types available`;\n }\n\n // Generate basic interfaces\n const interfaces = objects.map(([_name, obj]) => {\n return `export interface ${obj.className}Data {\n id?: string;\n created_at?: string;\n updated_at?: string;\n [key: string]: any;\n}`;\n });\n\n return interfaces.join('\\n\\n');\n}\n\nfunction generateFallbackManifestModule(manifest: ConsumerManifest): string {\n return `\n// Auto-generated manifest from SMRT consumer\nexport const manifest = ${JSON.stringify(manifest, null, 2)};\nexport default manifest;\n`;\n}\n"],"mappings":"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"}
|
package/dist/db-errors.js
CHANGED
|
@@ -128,7 +128,7 @@ var DIALECT_MESSAGE_RULES = [
|
|
|
128
128
|
},
|
|
129
129
|
{
|
|
130
130
|
kind: "unique_violation",
|
|
131
|
-
pattern: /UNIQUE constraint failed|violates unique constraint|violates primary key constraint|duplicate key value violates/i
|
|
131
|
+
pattern: /UNIQUE constraint failed|violates unique constraint|violates primary key constraint|duplicate key value violates|PRIMARY KEY or UNIQUE constraint violation/i
|
|
132
132
|
},
|
|
133
133
|
{
|
|
134
134
|
kind: "invalid_input",
|
package/dist/db-errors.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db-errors.js","names":[],"sources":["../src/db-errors.ts"],"sourcesContent":["/**\n * Driver-error classification for the persistence path (#2366).\n *\n * `@happyvertical/sql` does not normalize driver errors: every adapter catches\n * the driver failure and rethrows\n * `DatabaseError('Failed to upsert record into table', { …, originalError })`,\n * where `originalError` is the **string** produced by the SDK's `formatDbError`\n * (`\"<driver message>, code=<driver code>, detail=…, hint=…\"`). The typed\n * message therefore carries none of the constraint wording, which is why\n * matching on `error.message` alone silently stopped recognizing constraint\n * violations and let {@link ErrorUtils.withRetry} retry them.\n *\n * This module walks the whole causal chain — `cause`, `context.originalError`\n * (Error *or* formatted string), `originalError`, `details.causeMessages`, and\n * `AggregateError.errors` — collecting every driver code and message it finds,\n * then classifies on the codes first (PostgreSQL SQLSTATE, SQLite result\n * codes, Node network errno) and only falls back to dialect wording for DuckDB,\n * which reports no machine-readable code.\n *\n * The classification separates two independent questions:\n * - `deterministic` — a retry cannot change the outcome (constraint violation,\n * bad input syntax, aborted transaction, missing table). Fail fast.\n * - `retryable` — a retry plausibly can succeed (serialization failure,\n * deadlock, lock timeout, connection loss, `SQLITE_BUSY`).\n *\n * They are not complements: `kind: 'unknown'` means \"no opinion\", leaving the\n * caller's own default policy in charge.\n *\n * @module\n */\n\n/** Coarse classification of a database driver failure. */\nexport type DatabaseErrorKind =\n | 'unique_violation'\n | 'not_null_violation'\n | 'foreign_key_violation'\n | 'check_violation'\n | 'exclusion_violation'\n | 'invalid_input'\n | 'aborted_transaction'\n | 'undefined_object'\n | 'insufficient_privilege'\n | 'syntax_error'\n | 'transient'\n | 'unknown';\n\n/** Structured result of {@link classifyDatabaseError}. */\nexport interface DatabaseErrorClassification {\n /** What kind of failure this is, or `'unknown'` when nothing matched. */\n kind: DatabaseErrorKind;\n /** True when retrying cannot change the outcome. */\n deterministic: boolean;\n /** True when retrying plausibly can succeed. */\n retryable: boolean;\n /** PostgreSQL SQLSTATE recovered from the chain, when present. */\n sqlstate?: string;\n /** Raw driver code that produced the classification, when present. */\n driverCode?: string;\n /** Every driver code found anywhere in the chain, in discovery order. */\n driverCodes: readonly string[];\n /**\n * Every message found anywhere in the chain, in discovery order. Includes\n * the SDK's formatted `originalError` strings and the driver's own\n * `detail`/`hint` fields, which carry the PostgreSQL\n * `Key (col)=(value) already exists.` line used for column recovery.\n */\n driverMessages: readonly string[];\n /**\n * Column named directly by the driver (`node-postgres` sets `error.column`\n * on a `23502`), when one was reported. More reliable than parsing it back\n * out of the message.\n */\n column?: string;\n /** Constraint named directly by the driver (`error.constraint`). */\n constraint?: string;\n}\n\n/** Maximum chain depth walked, guarding against pathological nesting. */\nconst MAX_CHAIN_DEPTH = 12;\n\n/** `formatDbError` renders driver codes as `code=<value>` inside its string. */\nconst FORMATTED_CODE_PATTERN = /(?:^|[\\s,;([{])code=([A-Za-z0-9_]+)/g;\n\n/**\n * PostgreSQL SQLSTATE codes whose failure is fully determined by the statement\n * and its data. Retrying re-executes the same deterministic rejection.\n */\nconst DETERMINISTIC_SQLSTATES = new Map<string, DatabaseErrorKind>([\n // Class 23 — integrity constraint violation\n ['23000', 'check_violation'], // integrity_constraint_violation (generic)\n ['23001', 'foreign_key_violation'], // restrict_violation\n ['23502', 'not_null_violation'],\n ['23503', 'foreign_key_violation'],\n ['23505', 'unique_violation'],\n ['23514', 'check_violation'],\n // An EXCLUDE constraint is not a unique index: the conflicting rows differ on\n // the constrained columns and merely overlap under the operator. Reporting it\n // as `unique_violation` would make `save()` raise\n // VALIDATION_UNIQUE_CONSTRAINT naming a field that did not collide, so it\n // keeps its own kind and stays on the generic DatabaseError path.\n ['23P01', 'exclusion_violation'],\n // Class 22 — data exception (bad literal, overflow, bad uuid cast)\n ['22001', 'invalid_input'], // string_data_right_truncation\n ['22003', 'invalid_input'], // numeric_value_out_of_range\n ['22007', 'invalid_input'], // invalid_datetime_format\n ['22008', 'invalid_input'], // datetime_field_overflow\n ['22023', 'invalid_input'], // invalid_parameter_value\n ['22P02', 'invalid_input'], // invalid_text_representation — the bad-uuid case\n ['22P03', 'invalid_input'], // invalid_binary_representation\n // Class 25 — invalid transaction state. Only `25P02` belongs here:\n // `25P03 idle_in_transaction_session_timeout` means the server *terminated\n // the session*, which is a connection-level failure a fresh connection can\n // survive, so it lives in TRANSIENT_SQLSTATES instead. Calling it\n // deterministic would both block that retry and make\n // `isAbortedTransactionError()` report a timed-out session as an OIDC race\n // conflict in the profiles coordinator.\n ['25P02', 'aborted_transaction'], // in_failed_sql_transaction\n // Class 42 — syntax error or access rule violation\n ['42501', 'insufficient_privilege'],\n ['42601', 'syntax_error'],\n ['42703', 'undefined_object'], // undefined_column\n ['42804', 'invalid_input'], // datatype_mismatch\n ['42883', 'undefined_object'], // undefined_function\n ['42P01', 'undefined_object'], // undefined_table\n ['42P07', 'undefined_object'], // duplicate_table\n // invalid_column_reference — \"no unique or exclusion constraint matching the\n // ON CONFLICT specification\". A conflict target that does not match a real\n // unique index is a schema defect, not contention; retrying it four times\n // only delays the report (see epic #2382 finding A4).\n ['42P10', 'undefined_object'],\n ['3D000', 'undefined_object'], // invalid_catalog_name\n ['3F000', 'undefined_object'], // invalid_schema_name\n]);\n\n/**\n * PostgreSQL SQLSTATE codes describing a contention or availability failure.\n * The same statement may well succeed on a later attempt.\n */\nconst TRANSIENT_SQLSTATES = new Set([\n '25P03', // idle_in_transaction_session_timeout — the session was terminated\n '40001', // serialization_failure\n '40003', // statement_completion_unknown\n '40P01', // deadlock_detected\n '53200', // out_of_memory\n '53300', // too_many_connections\n '55P03', // lock_not_available\n '57014', // query_canceled\n '57P01', // admin_shutdown\n '57P02', // crash_shutdown\n '57P03', // cannot_connect_now\n '58030', // io_error\n '08000', // connection_exception\n '08001', // sqlclient_unable_to_establish_sqlconnection\n '08003', // connection_does_not_exist\n '08004', // sqlserver_rejected_establishment_of_sqlconnection\n '08006', // connection_failure\n '08007', // transaction_resolution_unknown\n '08P01', // protocol_violation\n]);\n\n/**\n * SQLite extended result codes. `better-sqlite3`/`node:sqlite` frequently\n * report the bare `SQLITE_CONSTRAINT` parent code, so the constraint sub-kind\n * is resolved from the message instead (see {@link classifyDialectMessage}).\n */\nconst DETERMINISTIC_SQLITE_CODES = new Map<string, DatabaseErrorKind>([\n ['SQLITE_CONSTRAINT_UNIQUE', 'unique_violation'],\n ['SQLITE_CONSTRAINT_PRIMARYKEY', 'unique_violation'],\n ['SQLITE_CONSTRAINT_ROWID', 'unique_violation'],\n ['SQLITE_CONSTRAINT_NOTNULL', 'not_null_violation'],\n ['SQLITE_CONSTRAINT_FOREIGNKEY', 'foreign_key_violation'],\n ['SQLITE_CONSTRAINT_CHECK', 'check_violation'],\n ['SQLITE_CONSTRAINT_TRIGGER', 'check_violation'],\n ['SQLITE_MISMATCH', 'invalid_input'],\n ['SQLITE_RANGE', 'invalid_input'],\n ['SQLITE_TOOBIG', 'invalid_input'],\n ['SQLITE_READONLY', 'insufficient_privilege'],\n ['SQLITE_AUTH', 'insufficient_privilege'],\n]);\n\n/** SQLite result codes that clear once contention or a lock resolves. */\nconst TRANSIENT_SQLITE_CODES = new Set([\n 'SQLITE_BUSY',\n 'SQLITE_BUSY_SNAPSHOT',\n 'SQLITE_BUSY_TIMEOUT',\n 'SQLITE_LOCKED',\n 'SQLITE_LOCKED_SHAREDCACHE',\n 'SQLITE_PROTOCOL',\n]);\n\n/** Node/libuv socket errno values raised when a pooled connection drops. */\nconst TRANSIENT_NETWORK_CODES = new Set([\n 'ECONNRESET',\n 'ECONNREFUSED',\n 'ECONNABORTED',\n 'EPIPE',\n 'ETIMEDOUT',\n 'ENETUNREACH',\n 'ENETRESET',\n 'EHOSTUNREACH',\n 'EAI_AGAIN',\n 'ENOTFOUND',\n]);\n\n/**\n * Dialect wording, in precedence order. Only DuckDB has no machine-readable\n * code, but SQLite's bare `SQLITE_CONSTRAINT` and PostgreSQL errors that have\n * lost their code through string formatting are recovered here too.\n *\n * NOT NULL is tested before UNIQUE because DuckDB phrases both with the same\n * `Constraint Error … violates` shape, and foreign-key/check wording must not\n * be mistaken for a unique violation (#1578).\n */\nconst DIALECT_MESSAGE_RULES: ReadonlyArray<{\n kind: DatabaseErrorKind;\n pattern: RegExp;\n}> = [\n {\n kind: 'aborted_transaction',\n pattern: /current transaction is aborted/i,\n },\n {\n kind: 'not_null_violation',\n pattern:\n /NOT NULL constraint failed|null value in column .* violates not-null/i,\n },\n {\n // SQLite reports `FOREIGN KEY constraint failed` under the bare\n // `SQLITE_CONSTRAINT` parent code, so without this arm the sub-kind\n // recovery below would mislabel every SQLite FK failure.\n kind: 'foreign_key_violation',\n pattern: /violates foreign key constraint|FOREIGN KEY constraint failed/i,\n },\n {\n kind: 'check_violation',\n pattern: /violates check constraint|CHECK constraint failed/i,\n },\n {\n kind: 'unique_violation',\n pattern:\n /UNIQUE constraint failed|violates unique constraint|violates primary key constraint|duplicate key value violates/i,\n },\n {\n kind: 'invalid_input',\n pattern: /invalid input syntax for type|value too long for type/i,\n },\n {\n // Deliberately narrow: a bare /does not exist/ matches far too much to be\n // safe as a fail-fast signal.\n kind: 'undefined_object',\n pattern:\n /no such table|no such column|(?:relation|column|table|function) \"[^\"]*\" does not exist/i,\n },\n {\n kind: 'transient',\n pattern:\n /database is locked|database table is locked|connection terminated|server closed the connection|too many clients|deadlock detected|could not serialize access/i,\n },\n];\n\n/** SMRT-level codes that already express a driver classification. */\nconst SMRT_CODE_KINDS = new Map<string, DatabaseErrorKind>([\n ['VALIDATION_UNIQUE_CONSTRAINT', 'unique_violation'],\n ['DB_CONSTRAINT_VIOLATION', 'check_violation'],\n]);\n\n/**\n * Reporting precedence among deterministic kinds, most specific first.\n *\n * `aborted_transaction` ranks last on purpose: PostgreSQL reports `25P02` for\n * every statement issued after the transaction already failed, so a chain\n * carrying both it and the constraint that caused the abort must name the\n * constraint. Every kind here is non-retryable, so ranking changes only what\n * is reported, never whether the operation is retried.\n */\nconst DETERMINISTIC_KIND_RANK = new Map<DatabaseErrorKind, number>([\n ['unique_violation', 0],\n ['not_null_violation', 1],\n ['foreign_key_violation', 2],\n ['check_violation', 3],\n ['exclusion_violation', 4],\n ['invalid_input', 5],\n ['undefined_object', 6],\n ['insufficient_privilege', 7],\n ['syntax_error', 8],\n ['aborted_transaction', 9],\n]);\n\nfunction rankKind(kind: DatabaseErrorKind): number {\n return DETERMINISTIC_KIND_RANK.get(kind) ?? Number.MAX_SAFE_INTEGER;\n}\n\ninterface ChainSignals {\n codes: string[];\n messages: string[];\n columns: string[];\n constraints: string[];\n}\n\nfunction pushUnique(collection: string[], value: unknown): void {\n if (typeof value !== 'string') return;\n const entry = value.trim();\n if (entry && !collection.includes(entry)) {\n collection.push(entry);\n }\n}\n\nfunction pushCode(signals: ChainSignals, value: unknown): void {\n pushUnique(signals.codes, value);\n}\n\nfunction pushMessage(signals: ChainSignals, value: unknown): void {\n if (typeof value !== 'string') return;\n const message = value.trim();\n if (!message || signals.messages.includes(message)) return;\n signals.messages.push(message);\n\n // `formatDbError` folds the driver's own `code` into its string, so the\n // machine-readable classification survives even after stringification.\n FORMATTED_CODE_PATTERN.lastIndex = 0;\n let match = FORMATTED_CODE_PATTERN.exec(message);\n while (match) {\n pushCode(signals, match[1]);\n match = FORMATTED_CODE_PATTERN.exec(message);\n }\n}\n\n/**\n * Walks an error's causal chain, collecting every driver code and message.\n *\n * Visits `cause`, `context.originalError`, `originalError`,\n * `details.causeMessage`/`details.causeMessages` and `AggregateError.errors`,\n * because the SDK, core and consumer wrappers each nest the driver error\n * differently. Cycles and depth are bounded.\n */\nfunction collectChainSignals(error: unknown): ChainSignals {\n const signals: ChainSignals = {\n codes: [],\n messages: [],\n columns: [],\n constraints: [],\n };\n const seen = new Set<unknown>();\n const pending: Array<{ value: unknown; depth: number }> = [\n { value: error, depth: 0 },\n ];\n\n while (pending.length > 0) {\n const entry = pending.pop();\n if (!entry) break;\n const { value, depth } = entry;\n if (value === null || value === undefined || depth > MAX_CHAIN_DEPTH) {\n continue;\n }\n\n if (typeof value === 'string') {\n pushMessage(signals, value);\n continue;\n }\n if (typeof value !== 'object' || seen.has(value)) continue;\n seen.add(value);\n\n const node = value as {\n code?: unknown;\n sqlState?: unknown;\n sqlstate?: unknown;\n message?: unknown;\n detail?: unknown;\n hint?: unknown;\n column?: unknown;\n constraint?: unknown;\n cause?: unknown;\n context?: { originalError?: unknown } | unknown;\n originalError?: unknown;\n details?: unknown;\n errors?: unknown;\n };\n\n pushCode(signals, node.code);\n pushCode(signals, node.sqlState);\n pushCode(signals, node.sqlstate);\n pushMessage(signals, node.message);\n // `node-postgres` surfaces the PostgreSQL error fields as properties. On\n // the direct INSERT path nothing stringifies them into the message, so the\n // column would otherwise be unrecoverable (`error.detail` carries\n // \"Key (sku)=(s1) already exists.\" for a 23505; `error.column` names the\n // column outright for a 23502).\n pushMessage(signals, node.detail);\n pushMessage(signals, node.hint);\n pushUnique(signals.columns, node.column);\n pushUnique(signals.constraints, node.constraint);\n\n const details = node.details as\n | { causeMessage?: unknown; causeMessages?: unknown }\n | undefined;\n if (details && typeof details === 'object') {\n pushMessage(signals, details.causeMessage);\n if (Array.isArray(details.causeMessages)) {\n for (const causeMessage of details.causeMessages) {\n pushMessage(signals, causeMessage);\n }\n }\n }\n\n const context = node.context as { originalError?: unknown } | undefined;\n const next = [\n node.cause,\n node.originalError,\n context && typeof context === 'object'\n ? context.originalError\n : undefined,\n ];\n for (const child of next) {\n if (child !== null && child !== undefined) {\n pending.push({ value: child, depth: depth + 1 });\n }\n }\n if (Array.isArray(node.errors)) {\n for (const child of node.errors) {\n pending.push({ value: child, depth: depth + 1 });\n }\n }\n }\n\n return signals;\n}\n\n/**\n * Classifies a raw dialect message. Exposed so\n * `SmrtObject.classifyConstraintError()` and this module share one set of\n * regexes rather than drifting apart.\n */\nexport function classifyDialectMessage(message: string): DatabaseErrorKind {\n if (!message) return 'unknown';\n for (const rule of DIALECT_MESSAGE_RULES) {\n if (rule.pattern.test(message)) return rule.kind;\n }\n return 'unknown';\n}\n\nfunction isSqlState(code: string): boolean {\n return /^[0-9A-Z]{5}$/.test(code);\n}\n\n/**\n * Classifies a database failure by walking its causal chain.\n *\n * Codes win over wording: a PostgreSQL SQLSTATE or SQLite result code is\n * authoritative, and dialect message matching only runs when no code in the\n * chain was recognized (DuckDB, or an error already flattened to a string).\n *\n * Constraint violations are reported ahead of `aborted_transaction` so a chain\n * carrying both surfaces the root cause rather than the downstream symptom;\n * both are deterministic, so retry behavior is identical either way.\n *\n * @param error - Any thrown value. Non-database errors classify as `'unknown'`.\n * @returns The structured classification; never throws.\n */\nexport function classifyDatabaseError(\n error: unknown,\n): DatabaseErrorClassification {\n const signals = collectChainSignals(error);\n const sqlstate = signals.codes.find(\n (code) =>\n isSqlState(code) &&\n (DETERMINISTIC_SQLSTATES.has(code) || TRANSIENT_SQLSTATES.has(code)),\n );\n\n const base = {\n driverCodes: signals.codes,\n driverMessages: signals.messages,\n ...(sqlstate ? { sqlstate } : {}),\n ...(signals.columns[0] ? { column: signals.columns[0] } : {}),\n ...(signals.constraints[0] ? { constraint: signals.constraints[0] } : {}),\n };\n\n // Deterministic codes first: a chain that carries both a constraint code and\n // a downstream contention code must fail fast, not retry.\n const deterministicCandidates: Array<{\n code: string;\n kind: DatabaseErrorKind;\n }> = [];\n for (const code of signals.codes) {\n const kind =\n DETERMINISTIC_SQLSTATES.get(code) ??\n DETERMINISTIC_SQLITE_CODES.get(code) ??\n // SMRT's own typed codes: a `ValidationError` already carrying\n // `VALIDATION_UNIQUE_CONSTRAINT` is the classified form of the same\n // driver failure.\n SMRT_CODE_KINDS.get(code) ??\n // A bare `SQLITE_CONSTRAINT` carries no sub-kind; recover it from the\n // message, which SQLite always phrases explicitly.\n (code === 'SQLITE_CONSTRAINT'\n ? (signals.messages\n .map(classifyDialectMessage)\n .find((candidate) => DETERMINISTIC_KIND_RANK.has(candidate)) ??\n 'check_violation')\n : undefined);\n if (kind) deterministicCandidates.push({ code, kind });\n }\n\n if (deterministicCandidates.length > 0) {\n // Rank rather than take the first discovered: an error whose chain holds\n // both `23505` and the `25P02` it caused must report the root constraint,\n // not the downstream symptom.\n const best = deterministicCandidates.reduce((winner, candidate) =>\n rankKind(candidate.kind) < rankKind(winner.kind) ? candidate : winner,\n );\n return {\n ...base,\n kind: best.kind,\n deterministic: true,\n retryable: false,\n driverCode: best.code,\n };\n }\n\n for (const code of signals.codes) {\n if (TRANSIENT_SQLSTATES.has(code) || TRANSIENT_SQLITE_CODES.has(code)) {\n return {\n ...base,\n kind: 'transient',\n deterministic: false,\n retryable: true,\n driverCode: code,\n };\n }\n if (TRANSIENT_NETWORK_CODES.has(code)) {\n return {\n ...base,\n kind: 'transient',\n deterministic: false,\n retryable: true,\n driverCode: code,\n };\n }\n }\n\n // No recognized code anywhere — fall back to dialect wording. This is the\n // only path DuckDB ever takes.\n const messageKinds = signals.messages\n .map(classifyDialectMessage)\n .filter((kind) => kind !== 'unknown');\n const deterministicFromMessage = messageKinds\n .filter((kind) => DETERMINISTIC_KIND_RANK.has(kind))\n .sort((left, right) => rankKind(left) - rankKind(right))[0];\n if (deterministicFromMessage) {\n return {\n ...base,\n kind: deterministicFromMessage,\n deterministic: true,\n retryable: false,\n };\n }\n if (messageKinds.includes('transient')) {\n return {\n ...base,\n kind: 'transient',\n deterministic: false,\n retryable: true,\n };\n }\n\n return {\n ...base,\n kind: 'unknown',\n deterministic: false,\n retryable: false,\n };\n}\n\n/**\n * True when retrying the statement cannot change the outcome.\n *\n * Used by {@link ErrorUtils.withRetry} to fail fast instead of burning the\n * full 500 + 1000 + 2000 ms backoff on a constraint violation.\n */\nexport function isDeterministicDatabaseError(error: unknown): boolean {\n return classifyDatabaseError(error).deterministic;\n}\n\n/** True when the failure is contention or availability and may clear. */\nexport function isTransientDatabaseError(error: unknown): boolean {\n return classifyDatabaseError(error).retryable;\n}\n\n/** True when a unique or primary-key constraint was violated. */\nexport function isUniqueViolationError(error: unknown): boolean {\n return classifyDatabaseError(error).kind === 'unique_violation';\n}\n\n/** True when a NOT NULL constraint was violated. */\nexport function isNotNullViolationError(error: unknown): boolean {\n return classifyDatabaseError(error).kind === 'not_null_violation';\n}\n\n/**\n * True when the failure came from running inside an already-aborted\n * PostgreSQL transaction (SQLSTATE `25P02`).\n *\n * Detected independently of {@link DatabaseErrorKind} so a chain that also\n * carries the root constraint code still answers `true` here.\n */\nexport function isAbortedTransactionError(error: unknown): boolean {\n const signals = collectChainSignals(error);\n // `25P02` only. `25P03` is a terminated session, not a failed transaction,\n // and reporting it here would make the profiles coordinator treat an\n // idle-timeout disconnect as a provisioning race conflict.\n if (signals.codes.includes('25P02')) {\n return true;\n }\n return signals.messages.some((message) =>\n /current transaction is aborted/i.test(message),\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"db-errors.js","names":[],"sources":["../src/db-errors.ts"],"sourcesContent":["/**\n * Driver-error classification for the persistence path (#2366).\n *\n * `@happyvertical/sql` does not normalize driver errors: every adapter catches\n * the driver failure and rethrows\n * `DatabaseError('Failed to upsert record into table', { …, originalError })`,\n * where `originalError` is the **string** produced by the SDK's `formatDbError`\n * (`\"<driver message>, code=<driver code>, detail=…, hint=…\"`). The typed\n * message therefore carries none of the constraint wording, which is why\n * matching on `error.message` alone silently stopped recognizing constraint\n * violations and let {@link ErrorUtils.withRetry} retry them.\n *\n * This module walks the whole causal chain — `cause`, `context.originalError`\n * (Error *or* formatted string), `originalError`, `details.causeMessages`, and\n * `AggregateError.errors` — collecting every driver code and message it finds,\n * then classifies on the codes first (PostgreSQL SQLSTATE, SQLite result\n * codes, Node network errno) and only falls back to dialect wording for DuckDB,\n * which reports no machine-readable code.\n *\n * The classification separates two independent questions:\n * - `deterministic` — a retry cannot change the outcome (constraint violation,\n * bad input syntax, aborted transaction, missing table). Fail fast.\n * - `retryable` — a retry plausibly can succeed (serialization failure,\n * deadlock, lock timeout, connection loss, `SQLITE_BUSY`).\n *\n * They are not complements: `kind: 'unknown'` means \"no opinion\", leaving the\n * caller's own default policy in charge.\n *\n * @module\n */\n\n/** Coarse classification of a database driver failure. */\nexport type DatabaseErrorKind =\n | 'unique_violation'\n | 'not_null_violation'\n | 'foreign_key_violation'\n | 'check_violation'\n | 'exclusion_violation'\n | 'invalid_input'\n | 'aborted_transaction'\n | 'undefined_object'\n | 'insufficient_privilege'\n | 'syntax_error'\n | 'transient'\n | 'unknown';\n\n/** Structured result of {@link classifyDatabaseError}. */\nexport interface DatabaseErrorClassification {\n /** What kind of failure this is, or `'unknown'` when nothing matched. */\n kind: DatabaseErrorKind;\n /** True when retrying cannot change the outcome. */\n deterministic: boolean;\n /** True when retrying plausibly can succeed. */\n retryable: boolean;\n /** PostgreSQL SQLSTATE recovered from the chain, when present. */\n sqlstate?: string;\n /** Raw driver code that produced the classification, when present. */\n driverCode?: string;\n /** Every driver code found anywhere in the chain, in discovery order. */\n driverCodes: readonly string[];\n /**\n * Every message found anywhere in the chain, in discovery order. Includes\n * the SDK's formatted `originalError` strings and the driver's own\n * `detail`/`hint` fields, which carry the PostgreSQL\n * `Key (col)=(value) already exists.` line used for column recovery.\n */\n driverMessages: readonly string[];\n /**\n * Column named directly by the driver (`node-postgres` sets `error.column`\n * on a `23502`), when one was reported. More reliable than parsing it back\n * out of the message.\n */\n column?: string;\n /** Constraint named directly by the driver (`error.constraint`). */\n constraint?: string;\n}\n\n/** Maximum chain depth walked, guarding against pathological nesting. */\nconst MAX_CHAIN_DEPTH = 12;\n\n/** `formatDbError` renders driver codes as `code=<value>` inside its string. */\nconst FORMATTED_CODE_PATTERN = /(?:^|[\\s,;([{])code=([A-Za-z0-9_]+)/g;\n\n/**\n * PostgreSQL SQLSTATE codes whose failure is fully determined by the statement\n * and its data. Retrying re-executes the same deterministic rejection.\n */\nconst DETERMINISTIC_SQLSTATES = new Map<string, DatabaseErrorKind>([\n // Class 23 — integrity constraint violation\n ['23000', 'check_violation'], // integrity_constraint_violation (generic)\n ['23001', 'foreign_key_violation'], // restrict_violation\n ['23502', 'not_null_violation'],\n ['23503', 'foreign_key_violation'],\n ['23505', 'unique_violation'],\n ['23514', 'check_violation'],\n // An EXCLUDE constraint is not a unique index: the conflicting rows differ on\n // the constrained columns and merely overlap under the operator. Reporting it\n // as `unique_violation` would make `save()` raise\n // VALIDATION_UNIQUE_CONSTRAINT naming a field that did not collide, so it\n // keeps its own kind and stays on the generic DatabaseError path.\n ['23P01', 'exclusion_violation'],\n // Class 22 — data exception (bad literal, overflow, bad uuid cast)\n ['22001', 'invalid_input'], // string_data_right_truncation\n ['22003', 'invalid_input'], // numeric_value_out_of_range\n ['22007', 'invalid_input'], // invalid_datetime_format\n ['22008', 'invalid_input'], // datetime_field_overflow\n ['22023', 'invalid_input'], // invalid_parameter_value\n ['22P02', 'invalid_input'], // invalid_text_representation — the bad-uuid case\n ['22P03', 'invalid_input'], // invalid_binary_representation\n // Class 25 — invalid transaction state. Only `25P02` belongs here:\n // `25P03 idle_in_transaction_session_timeout` means the server *terminated\n // the session*, which is a connection-level failure a fresh connection can\n // survive, so it lives in TRANSIENT_SQLSTATES instead. Calling it\n // deterministic would both block that retry and make\n // `isAbortedTransactionError()` report a timed-out session as an OIDC race\n // conflict in the profiles coordinator.\n ['25P02', 'aborted_transaction'], // in_failed_sql_transaction\n // Class 42 — syntax error or access rule violation\n ['42501', 'insufficient_privilege'],\n ['42601', 'syntax_error'],\n ['42703', 'undefined_object'], // undefined_column\n ['42804', 'invalid_input'], // datatype_mismatch\n ['42883', 'undefined_object'], // undefined_function\n ['42P01', 'undefined_object'], // undefined_table\n ['42P07', 'undefined_object'], // duplicate_table\n // invalid_column_reference — \"no unique or exclusion constraint matching the\n // ON CONFLICT specification\". A conflict target that does not match a real\n // unique index is a schema defect, not contention; retrying it four times\n // only delays the report (see epic #2382 finding A4).\n ['42P10', 'undefined_object'],\n ['3D000', 'undefined_object'], // invalid_catalog_name\n ['3F000', 'undefined_object'], // invalid_schema_name\n]);\n\n/**\n * PostgreSQL SQLSTATE codes describing a contention or availability failure.\n * The same statement may well succeed on a later attempt.\n */\nconst TRANSIENT_SQLSTATES = new Set([\n '25P03', // idle_in_transaction_session_timeout — the session was terminated\n '40001', // serialization_failure\n '40003', // statement_completion_unknown\n '40P01', // deadlock_detected\n '53200', // out_of_memory\n '53300', // too_many_connections\n '55P03', // lock_not_available\n '57014', // query_canceled\n '57P01', // admin_shutdown\n '57P02', // crash_shutdown\n '57P03', // cannot_connect_now\n '58030', // io_error\n '08000', // connection_exception\n '08001', // sqlclient_unable_to_establish_sqlconnection\n '08003', // connection_does_not_exist\n '08004', // sqlserver_rejected_establishment_of_sqlconnection\n '08006', // connection_failure\n '08007', // transaction_resolution_unknown\n '08P01', // protocol_violation\n]);\n\n/**\n * SQLite extended result codes. `better-sqlite3`/`node:sqlite` frequently\n * report the bare `SQLITE_CONSTRAINT` parent code, so the constraint sub-kind\n * is resolved from the message instead (see {@link classifyDialectMessage}).\n */\nconst DETERMINISTIC_SQLITE_CODES = new Map<string, DatabaseErrorKind>([\n ['SQLITE_CONSTRAINT_UNIQUE', 'unique_violation'],\n ['SQLITE_CONSTRAINT_PRIMARYKEY', 'unique_violation'],\n ['SQLITE_CONSTRAINT_ROWID', 'unique_violation'],\n ['SQLITE_CONSTRAINT_NOTNULL', 'not_null_violation'],\n ['SQLITE_CONSTRAINT_FOREIGNKEY', 'foreign_key_violation'],\n ['SQLITE_CONSTRAINT_CHECK', 'check_violation'],\n ['SQLITE_CONSTRAINT_TRIGGER', 'check_violation'],\n ['SQLITE_MISMATCH', 'invalid_input'],\n ['SQLITE_RANGE', 'invalid_input'],\n ['SQLITE_TOOBIG', 'invalid_input'],\n ['SQLITE_READONLY', 'insufficient_privilege'],\n ['SQLITE_AUTH', 'insufficient_privilege'],\n]);\n\n/** SQLite result codes that clear once contention or a lock resolves. */\nconst TRANSIENT_SQLITE_CODES = new Set([\n 'SQLITE_BUSY',\n 'SQLITE_BUSY_SNAPSHOT',\n 'SQLITE_BUSY_TIMEOUT',\n 'SQLITE_LOCKED',\n 'SQLITE_LOCKED_SHAREDCACHE',\n 'SQLITE_PROTOCOL',\n]);\n\n/** Node/libuv socket errno values raised when a pooled connection drops. */\nconst TRANSIENT_NETWORK_CODES = new Set([\n 'ECONNRESET',\n 'ECONNREFUSED',\n 'ECONNABORTED',\n 'EPIPE',\n 'ETIMEDOUT',\n 'ENETUNREACH',\n 'ENETRESET',\n 'EHOSTUNREACH',\n 'EAI_AGAIN',\n 'ENOTFOUND',\n]);\n\n/**\n * Dialect wording, in precedence order. Only DuckDB has no machine-readable\n * code, but SQLite's bare `SQLITE_CONSTRAINT` and PostgreSQL errors that have\n * lost their code through string formatting are recovered here too.\n *\n * NOT NULL is tested before UNIQUE because DuckDB phrases both with the same\n * `Constraint Error … violates` shape, and foreign-key/check wording must not\n * be mistaken for a unique violation (#1578).\n */\nconst DIALECT_MESSAGE_RULES: ReadonlyArray<{\n kind: DatabaseErrorKind;\n pattern: RegExp;\n}> = [\n {\n kind: 'aborted_transaction',\n pattern: /current transaction is aborted/i,\n },\n {\n kind: 'not_null_violation',\n pattern:\n /NOT NULL constraint failed|null value in column .* violates not-null/i,\n },\n {\n // SQLite reports `FOREIGN KEY constraint failed` under the bare\n // `SQLITE_CONSTRAINT` parent code, so without this arm the sub-kind\n // recovery below would mislabel every SQLite FK failure.\n kind: 'foreign_key_violation',\n pattern: /violates foreign key constraint|FOREIGN KEY constraint failed/i,\n },\n {\n kind: 'check_violation',\n pattern: /violates check constraint|CHECK constraint failed/i,\n },\n {\n kind: 'unique_violation',\n pattern:\n /UNIQUE constraint failed|violates unique constraint|violates primary key constraint|duplicate key value violates|PRIMARY KEY or UNIQUE constraint violation/i,\n },\n {\n kind: 'invalid_input',\n pattern: /invalid input syntax for type|value too long for type/i,\n },\n {\n // Deliberately narrow: a bare /does not exist/ matches far too much to be\n // safe as a fail-fast signal.\n kind: 'undefined_object',\n pattern:\n /no such table|no such column|(?:relation|column|table|function) \"[^\"]*\" does not exist/i,\n },\n {\n kind: 'transient',\n pattern:\n /database is locked|database table is locked|connection terminated|server closed the connection|too many clients|deadlock detected|could not serialize access/i,\n },\n];\n\n/** SMRT-level codes that already express a driver classification. */\nconst SMRT_CODE_KINDS = new Map<string, DatabaseErrorKind>([\n ['VALIDATION_UNIQUE_CONSTRAINT', 'unique_violation'],\n ['DB_CONSTRAINT_VIOLATION', 'check_violation'],\n]);\n\n/**\n * Reporting precedence among deterministic kinds, most specific first.\n *\n * `aborted_transaction` ranks last on purpose: PostgreSQL reports `25P02` for\n * every statement issued after the transaction already failed, so a chain\n * carrying both it and the constraint that caused the abort must name the\n * constraint. Every kind here is non-retryable, so ranking changes only what\n * is reported, never whether the operation is retried.\n */\nconst DETERMINISTIC_KIND_RANK = new Map<DatabaseErrorKind, number>([\n ['unique_violation', 0],\n ['not_null_violation', 1],\n ['foreign_key_violation', 2],\n ['check_violation', 3],\n ['exclusion_violation', 4],\n ['invalid_input', 5],\n ['undefined_object', 6],\n ['insufficient_privilege', 7],\n ['syntax_error', 8],\n ['aborted_transaction', 9],\n]);\n\nfunction rankKind(kind: DatabaseErrorKind): number {\n return DETERMINISTIC_KIND_RANK.get(kind) ?? Number.MAX_SAFE_INTEGER;\n}\n\ninterface ChainSignals {\n codes: string[];\n messages: string[];\n columns: string[];\n constraints: string[];\n}\n\nfunction pushUnique(collection: string[], value: unknown): void {\n if (typeof value !== 'string') return;\n const entry = value.trim();\n if (entry && !collection.includes(entry)) {\n collection.push(entry);\n }\n}\n\nfunction pushCode(signals: ChainSignals, value: unknown): void {\n pushUnique(signals.codes, value);\n}\n\nfunction pushMessage(signals: ChainSignals, value: unknown): void {\n if (typeof value !== 'string') return;\n const message = value.trim();\n if (!message || signals.messages.includes(message)) return;\n signals.messages.push(message);\n\n // `formatDbError` folds the driver's own `code` into its string, so the\n // machine-readable classification survives even after stringification.\n FORMATTED_CODE_PATTERN.lastIndex = 0;\n let match = FORMATTED_CODE_PATTERN.exec(message);\n while (match) {\n pushCode(signals, match[1]);\n match = FORMATTED_CODE_PATTERN.exec(message);\n }\n}\n\n/**\n * Walks an error's causal chain, collecting every driver code and message.\n *\n * Visits `cause`, `context.originalError`, `originalError`,\n * `details.causeMessage`/`details.causeMessages` and `AggregateError.errors`,\n * because the SDK, core and consumer wrappers each nest the driver error\n * differently. Cycles and depth are bounded.\n */\nfunction collectChainSignals(error: unknown): ChainSignals {\n const signals: ChainSignals = {\n codes: [],\n messages: [],\n columns: [],\n constraints: [],\n };\n const seen = new Set<unknown>();\n const pending: Array<{ value: unknown; depth: number }> = [\n { value: error, depth: 0 },\n ];\n\n while (pending.length > 0) {\n const entry = pending.pop();\n if (!entry) break;\n const { value, depth } = entry;\n if (value === null || value === undefined || depth > MAX_CHAIN_DEPTH) {\n continue;\n }\n\n if (typeof value === 'string') {\n pushMessage(signals, value);\n continue;\n }\n if (typeof value !== 'object' || seen.has(value)) continue;\n seen.add(value);\n\n const node = value as {\n code?: unknown;\n sqlState?: unknown;\n sqlstate?: unknown;\n message?: unknown;\n detail?: unknown;\n hint?: unknown;\n column?: unknown;\n constraint?: unknown;\n cause?: unknown;\n context?: { originalError?: unknown } | unknown;\n originalError?: unknown;\n details?: unknown;\n errors?: unknown;\n };\n\n pushCode(signals, node.code);\n pushCode(signals, node.sqlState);\n pushCode(signals, node.sqlstate);\n pushMessage(signals, node.message);\n // `node-postgres` surfaces the PostgreSQL error fields as properties. On\n // the direct INSERT path nothing stringifies them into the message, so the\n // column would otherwise be unrecoverable (`error.detail` carries\n // \"Key (sku)=(s1) already exists.\" for a 23505; `error.column` names the\n // column outright for a 23502).\n pushMessage(signals, node.detail);\n pushMessage(signals, node.hint);\n pushUnique(signals.columns, node.column);\n pushUnique(signals.constraints, node.constraint);\n\n const details = node.details as\n | { causeMessage?: unknown; causeMessages?: unknown }\n | undefined;\n if (details && typeof details === 'object') {\n pushMessage(signals, details.causeMessage);\n if (Array.isArray(details.causeMessages)) {\n for (const causeMessage of details.causeMessages) {\n pushMessage(signals, causeMessage);\n }\n }\n }\n\n const context = node.context as { originalError?: unknown } | undefined;\n const next = [\n node.cause,\n node.originalError,\n context && typeof context === 'object'\n ? context.originalError\n : undefined,\n ];\n for (const child of next) {\n if (child !== null && child !== undefined) {\n pending.push({ value: child, depth: depth + 1 });\n }\n }\n if (Array.isArray(node.errors)) {\n for (const child of node.errors) {\n pending.push({ value: child, depth: depth + 1 });\n }\n }\n }\n\n return signals;\n}\n\n/**\n * Classifies a raw dialect message. Exposed so\n * `SmrtObject.classifyConstraintError()` and this module share one set of\n * regexes rather than drifting apart.\n */\nexport function classifyDialectMessage(message: string): DatabaseErrorKind {\n if (!message) return 'unknown';\n for (const rule of DIALECT_MESSAGE_RULES) {\n if (rule.pattern.test(message)) return rule.kind;\n }\n return 'unknown';\n}\n\nfunction isSqlState(code: string): boolean {\n return /^[0-9A-Z]{5}$/.test(code);\n}\n\n/**\n * Classifies a database failure by walking its causal chain.\n *\n * Codes win over wording: a PostgreSQL SQLSTATE or SQLite result code is\n * authoritative, and dialect message matching only runs when no code in the\n * chain was recognized (DuckDB, or an error already flattened to a string).\n *\n * Constraint violations are reported ahead of `aborted_transaction` so a chain\n * carrying both surfaces the root cause rather than the downstream symptom;\n * both are deterministic, so retry behavior is identical either way.\n *\n * @param error - Any thrown value. Non-database errors classify as `'unknown'`.\n * @returns The structured classification; never throws.\n */\nexport function classifyDatabaseError(\n error: unknown,\n): DatabaseErrorClassification {\n const signals = collectChainSignals(error);\n const sqlstate = signals.codes.find(\n (code) =>\n isSqlState(code) &&\n (DETERMINISTIC_SQLSTATES.has(code) || TRANSIENT_SQLSTATES.has(code)),\n );\n\n const base = {\n driverCodes: signals.codes,\n driverMessages: signals.messages,\n ...(sqlstate ? { sqlstate } : {}),\n ...(signals.columns[0] ? { column: signals.columns[0] } : {}),\n ...(signals.constraints[0] ? { constraint: signals.constraints[0] } : {}),\n };\n\n // Deterministic codes first: a chain that carries both a constraint code and\n // a downstream contention code must fail fast, not retry.\n const deterministicCandidates: Array<{\n code: string;\n kind: DatabaseErrorKind;\n }> = [];\n for (const code of signals.codes) {\n const kind =\n DETERMINISTIC_SQLSTATES.get(code) ??\n DETERMINISTIC_SQLITE_CODES.get(code) ??\n // SMRT's own typed codes: a `ValidationError` already carrying\n // `VALIDATION_UNIQUE_CONSTRAINT` is the classified form of the same\n // driver failure.\n SMRT_CODE_KINDS.get(code) ??\n // A bare `SQLITE_CONSTRAINT` carries no sub-kind; recover it from the\n // message, which SQLite always phrases explicitly.\n (code === 'SQLITE_CONSTRAINT'\n ? (signals.messages\n .map(classifyDialectMessage)\n .find((candidate) => DETERMINISTIC_KIND_RANK.has(candidate)) ??\n 'check_violation')\n : undefined);\n if (kind) deterministicCandidates.push({ code, kind });\n }\n\n if (deterministicCandidates.length > 0) {\n // Rank rather than take the first discovered: an error whose chain holds\n // both `23505` and the `25P02` it caused must report the root constraint,\n // not the downstream symptom.\n const best = deterministicCandidates.reduce((winner, candidate) =>\n rankKind(candidate.kind) < rankKind(winner.kind) ? candidate : winner,\n );\n return {\n ...base,\n kind: best.kind,\n deterministic: true,\n retryable: false,\n driverCode: best.code,\n };\n }\n\n for (const code of signals.codes) {\n if (TRANSIENT_SQLSTATES.has(code) || TRANSIENT_SQLITE_CODES.has(code)) {\n return {\n ...base,\n kind: 'transient',\n deterministic: false,\n retryable: true,\n driverCode: code,\n };\n }\n if (TRANSIENT_NETWORK_CODES.has(code)) {\n return {\n ...base,\n kind: 'transient',\n deterministic: false,\n retryable: true,\n driverCode: code,\n };\n }\n }\n\n // No recognized code anywhere — fall back to dialect wording. This is the\n // only path DuckDB ever takes.\n const messageKinds = signals.messages\n .map(classifyDialectMessage)\n .filter((kind) => kind !== 'unknown');\n const deterministicFromMessage = messageKinds\n .filter((kind) => DETERMINISTIC_KIND_RANK.has(kind))\n .sort((left, right) => rankKind(left) - rankKind(right))[0];\n if (deterministicFromMessage) {\n return {\n ...base,\n kind: deterministicFromMessage,\n deterministic: true,\n retryable: false,\n };\n }\n if (messageKinds.includes('transient')) {\n return {\n ...base,\n kind: 'transient',\n deterministic: false,\n retryable: true,\n };\n }\n\n return {\n ...base,\n kind: 'unknown',\n deterministic: false,\n retryable: false,\n };\n}\n\n/**\n * True when retrying the statement cannot change the outcome.\n *\n * Used by {@link ErrorUtils.withRetry} to fail fast instead of burning the\n * full 500 + 1000 + 2000 ms backoff on a constraint violation.\n */\nexport function isDeterministicDatabaseError(error: unknown): boolean {\n return classifyDatabaseError(error).deterministic;\n}\n\n/** True when the failure is contention or availability and may clear. */\nexport function isTransientDatabaseError(error: unknown): boolean {\n return classifyDatabaseError(error).retryable;\n}\n\n/** True when a unique or primary-key constraint was violated. */\nexport function isUniqueViolationError(error: unknown): boolean {\n return classifyDatabaseError(error).kind === 'unique_violation';\n}\n\n/** True when a NOT NULL constraint was violated. */\nexport function isNotNullViolationError(error: unknown): boolean {\n return classifyDatabaseError(error).kind === 'not_null_violation';\n}\n\n/**\n * True when the failure came from running inside an already-aborted\n * PostgreSQL transaction (SQLSTATE `25P02`).\n *\n * Detected independently of {@link DatabaseErrorKind} so a chain that also\n * carries the root constraint code still answers `true` here.\n */\nexport function isAbortedTransactionError(error: unknown): boolean {\n const signals = collectChainSignals(error);\n // `25P02` only. `25P03` is a terminated session, not a failed transaction,\n // and reporting it here would make the profiles coordinator treat an\n // idle-timeout disconnect as a provisioning race conflict.\n if (signals.codes.includes('25P02')) {\n return true;\n }\n return signals.messages.some((message) =>\n /current transaction is aborted/i.test(message),\n );\n}\n"],"mappings":"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"}
|
|
@@ -37,6 +37,6 @@ export declare function withEmbeddedWriteQueue<T>(db: QueueableDatabase, seriali
|
|
|
37
37
|
* change-feed appends inside the callback do not queue behind their own root
|
|
38
38
|
* transaction.
|
|
39
39
|
*/
|
|
40
|
-
export declare function withEmbeddedWriteTransaction<T>(db: DatabaseInterface, serialize: boolean, operation: (transaction: DatabaseInterface) => Promise<T
|
|
40
|
+
export declare function withEmbeddedWriteTransaction<T>(db: DatabaseInterface, serialize: boolean, operation: (transaction: DatabaseInterface) => Promise<T>, reuseUnsupportedNested?: boolean): Promise<T>;
|
|
41
41
|
export {};
|
|
42
42
|
//# sourceMappingURL=embedded-write-queue.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embedded-write-queue.d.ts","sourceRoot":"","sources":["../src/embedded-write-queue.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"embedded-write-queue.d.ts","sourceRoot":"","sources":["../src/embedded-write-queue.ts"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,iBAAiB,EAEvB,MAAM,oBAAoB,CAAC;AA0C5B,UAAU,iBAAiB;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAEjE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAEjE;AAED;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,iBAAiB,GAAG,OAAO,CAG3E;AA0BD;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,CAAC,EAC5C,EAAE,EAAE,iBAAiB,EACrB,SAAS,EAAE,OAAO,EAClB,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC,CAwBZ;AAED;;;;;GAKG;AACH,wBAAsB,4BAA4B,CAAC,CAAC,EAClD,EAAE,EAAE,iBAAiB,EACrB,SAAS,EAAE,OAAO,EAClB,SAAS,EAAE,CAAC,WAAW,EAAE,iBAAiB,KAAK,OAAO,CAAC,CAAC,CAAC,EACzD,sBAAsB,UAAQ,GAC7B,OAAO,CAAC,CAAC,CAAC,CAgCZ"}
|