@happyvertical/smrt-core 0.51.24 → 0.51.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +17 -0
- package/agents/schema-paths.md +7 -0
- package/dist/index.js +1 -1
- package/dist/manifest/manifest-loader.d.ts +12 -0
- package/dist/manifest/manifest-loader.d.ts.map +1 -1
- package/dist/manifest/manifest-loader.js +54 -10
- package/dist/manifest/manifest-loader.js.map +1 -1
- package/dist/manifest/static-manifest.js +1 -1
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.d.ts.map +1 -1
- package/dist/manifest/store.js +7 -2
- package/dist/manifest/store.js.map +1 -1
- package/dist/manifest.json +1 -1
- package/dist/migrations/differ.d.ts +6 -3
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +11 -5
- package/dist/migrations/differ.js.map +1 -1
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +26 -26
- package/dist/object.js.map +1 -1
- package/dist/registry/__tests__/helpers/consumer-workspace.d.ts +42 -0
- package/dist/registry/__tests__/helpers/consumer-workspace.d.ts.map +1 -0
- package/dist/registry/class-registration.d.ts +15 -0
- package/dist/registry/class-registration.d.ts.map +1 -1
- package/dist/registry/class-registration.js +171 -21
- package/dist/registry/class-registration.js.map +1 -1
- package/dist/registry/collection-resolution.d.ts +8 -2
- package/dist/registry/collection-resolution.d.ts.map +1 -1
- package/dist/registry/collection-resolution.js +12 -2
- package/dist/registry/collection-resolution.js.map +1 -1
- package/dist/registry/collision-policy.d.ts +10 -0
- package/dist/registry/collision-policy.d.ts.map +1 -1
- package/dist/registry/collision-policy.js +7 -1
- package/dist/registry/collision-policy.js.map +1 -1
- package/dist/registry/inheritance-resolver.js +1 -1
- package/dist/registry/name-resolver.d.ts.map +1 -1
- package/dist/registry/name-resolver.js +11 -0
- package/dist/registry/name-resolver.js.map +1 -1
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js +71 -31
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/registry/shared-state.d.ts.map +1 -1
- package/dist/registry/shared-state.js +3 -4
- package/dist/registry/shared-state.js.map +1 -1
- package/dist/registry/types.d.ts +3 -1
- package/dist/registry/types.d.ts.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +25 -8
- package/dist/registry.js.map +1 -1
- package/dist/scanner/manifest-generator.js +1 -1
- package/dist/smrt-knowledge.json +7 -7
- package/dist/testing/database.js +1 -1
- package/dist/utils/stack-frames.d.ts +26 -0
- package/dist/utils/stack-frames.d.ts.map +1 -1
- package/dist/utils/stack-frames.js +79 -2
- package/dist/utils/stack-frames.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface ConsumerWorkspace {
|
|
2
|
+
root: string;
|
|
3
|
+
path: (...segments: string[]) => string;
|
|
4
|
+
write: (relativePath: string, content: string) => string;
|
|
5
|
+
writePackage: (relativeDir: string, name: string) => void;
|
|
6
|
+
/** A fixture module declaring one class via `define({ smrt, SmrtObject })`. */
|
|
7
|
+
writeModel: (relativePath: string, className: string, config?: Record<string, unknown>) => string;
|
|
8
|
+
dispose: () => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function createConsumerWorkspace(prefix: string): ConsumerWorkspace;
|
|
11
|
+
/** A manifest entry as an app's scanner writes it. */
|
|
12
|
+
export declare function manifestEntry(options: {
|
|
13
|
+
className: string;
|
|
14
|
+
packageName: string;
|
|
15
|
+
filePath: string;
|
|
16
|
+
tableName: string;
|
|
17
|
+
fields: Record<string, {
|
|
18
|
+
type: string;
|
|
19
|
+
}>;
|
|
20
|
+
}): {
|
|
21
|
+
name: string;
|
|
22
|
+
className: string;
|
|
23
|
+
qualifiedName: string;
|
|
24
|
+
packageName: string;
|
|
25
|
+
filePath: string;
|
|
26
|
+
extends: string;
|
|
27
|
+
fields: Record<string, {
|
|
28
|
+
type: string;
|
|
29
|
+
}>;
|
|
30
|
+
methods: {};
|
|
31
|
+
decoratorConfig: {
|
|
32
|
+
tableName: string;
|
|
33
|
+
};
|
|
34
|
+
schema: {
|
|
35
|
+
tableName: string;
|
|
36
|
+
ddl: string;
|
|
37
|
+
columns: {};
|
|
38
|
+
indexes: never[];
|
|
39
|
+
version: string;
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=consumer-workspace.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consumer-workspace.d.ts","sourceRoot":"","sources":["../../../../src/registry/__tests__/helpers/consumer-workspace.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,KAAK,MAAM,CAAC;IACxC,KAAK,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IACzD,YAAY,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1D,+EAA+E;IAC/E,UAAU,EAAE,CACV,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,CAiCzE;AAED,sDAAsD;AACtD,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1C;;;;;;;;cADgC,MAAM;;;;;;;;;;;;;EAqBtC"}
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import { SmrtObject } from '../object';
|
|
2
2
|
import { SmartObjectDefinition } from '../scanner/types.js';
|
|
3
3
|
import { RegisteredClass, RegisteredField, SmartObjectConfig } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Shared bundled-context detector. Source files in these output
|
|
6
|
+
* directories come from a bundler (Vite library mode, webpack, Next.js,
|
|
7
|
+
* Nuxt, svelte-kit) that can duplicate module code across chunks.
|
|
8
|
+
*/
|
|
9
|
+
export declare function isBundledOutputPath(sourceFile: string | undefined): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Whether two source paths name the same file. A stack frame reports an
|
|
12
|
+
* absolute path; a manifest's `filePath` is absolute or relative to the
|
|
13
|
+
* project or workspace root it was scanned from (anytown scans
|
|
14
|
+
* `packages/cloud-network/src/models/*.ts` into the dashboard's manifest,
|
|
15
|
+
* #3110), so a relative path is resolved against those roots, never matched by
|
|
16
|
+
* suffix alone.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isSameSourcePath(left: string | undefined, right: string | undefined): boolean;
|
|
4
19
|
/**
|
|
5
20
|
* Register (or re-register) a class. Every registration can add, replace or
|
|
6
21
|
* mutate a registered class in place, so it invalidates the generation-keyed
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"class-registration.d.ts","sourceRoot":"","sources":["../../src/registry/class-registration.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"class-registration.d.ts","sourceRoot":"","sources":["../../src/registry/class-registration.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAuBH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAKV,qBAAqB,EAGtB,MAAM,qBAAqB,CAAC;AA2C7B,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,iBAAiB,EAElB,MAAM,YAAY,CAAC;AAkDpB;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAS3E;AA4BD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,OAAO,CAeT;AA+mBD;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,OAAO,UAAU,EACvB,MAAM,GAAE,iBAAsB,GAC7B,IAAI,CAON;AA8gCD,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,MAAM,EAMlB,qBAAqB,EAAE,WAAW,CAAC,eAAe,CAAC,uBAAuB,CAAC,CAAC,GAC3E,IAAI,CAeN;AA+BD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,EACpC,kBAAkB,EAAE,eAAe,CAAC,oBAAoB,CAAC,GAAG,SAAS,GACpE,IAAI,CAyCN;AAoHD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,eAAe,EACzB,gBAAgB,CAAC,EAAE;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,GACA,IAAI,CA4CN;AAkLD,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,qBAAqB,EAChC,mBAAmB,CAAC,EAAE,MAAM,GAC3B,IAAI,CAQN"}
|
|
@@ -2,12 +2,12 @@ import { ConfigurationError } from "../errors.js";
|
|
|
2
2
|
import { toSnakeCase } from "../utils/naming.js";
|
|
3
3
|
import { declareChangeFeedSensitiveTable, setChangeFeedSensitiveClassResolver } from "../change-feed-sensitivity.js";
|
|
4
4
|
import { bumpRegistryGeneration } from "./generation.js";
|
|
5
|
-
import { createQualifiedName, isQualifiedName } from "../utils/qualified-names.js";
|
|
6
|
-
import { cloneManifestSchemaColumns } from "../manifest/store.js";
|
|
5
|
+
import { createQualifiedName, isQualifiedName, parseQualifiedName } from "../utils/qualified-names.js";
|
|
7
6
|
import { getClasses, getCollections, getConstructorFieldDecorators, getConstructorIndex, getConstructorTenantScopedDeclarations, getInheritanceCache, getLegacyFieldDecorators, getSourceFileFromStack, getStiSiblingsLoaded, verboseLog } from "./shared-state.js";
|
|
8
|
-
import {
|
|
7
|
+
import { cloneManifestSchemaColumns, findWorkspaceRootSync, getLocalTestManifestCache, getManifestCache, getNodeBuiltins, getStaticManifestCache, getTestManifestCache } from "../manifest/store.js";
|
|
8
|
+
import { discoverManifestSync, discoverSTISiblingsSync, getPackageName, lookupInManifest, readProjectManifestSync } from "../manifest/manifest-loader.js";
|
|
9
|
+
import { findClass, findClassesByName, getCanonicalClassName, hasClassCaseInsensitive, qualifyExtendsName } from "./name-resolver.js";
|
|
9
10
|
import { buildInheritanceChain } from "./inheritance-resolver.js";
|
|
10
|
-
import { discoverManifestSync, discoverSTISiblingsSync, getPackageName, lookupInManifest } from "../manifest/manifest-loader.js";
|
|
11
11
|
import { compileValidators } from "./validator.js";
|
|
12
12
|
import { tableNameFromClass } from "../utils.js";
|
|
13
13
|
import { SmrtObject } from "../object.js";
|
|
@@ -44,6 +44,25 @@ function pluralizeCollection(className) {
|
|
|
44
44
|
return `${lower}s`;
|
|
45
45
|
}
|
|
46
46
|
/**
|
|
47
|
+
* The collection an STI subtype shares with its STI base, for a class that
|
|
48
|
+
* registers with no manifest `collection` (#3125). The manifest generator
|
|
49
|
+
* gives every STI subtype its base's collection (`findSTIBase`: the oldest
|
|
50
|
+
* ancestor declaring `tableStrategy: 'sti'`); a subtype decorated before its
|
|
51
|
+
* manifest loads — a consumer's bundled model chunk — must resolve the same
|
|
52
|
+
* one, or permission slugs and route segments derived from it name a
|
|
53
|
+
* collection nothing catalogues. Ancestors resolve by constructor identity, so
|
|
54
|
+
* a same-named class in another package cannot stand in for the base.
|
|
55
|
+
*/
|
|
56
|
+
function inheritedStiCollection(ctor) {
|
|
57
|
+
let base;
|
|
58
|
+
for (let parent = Object.getPrototypeOf(ctor); parent && parent !== Function.prototype; parent = Object.getPrototypeOf(parent)) {
|
|
59
|
+
const key = getConstructorIndex().get(parent);
|
|
60
|
+
const registered = key ? getClasses().get(key) : void 0;
|
|
61
|
+
if (registered?.config?.tableStrategy === "sti") base = registered;
|
|
62
|
+
}
|
|
63
|
+
return base?.collection;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
47
66
|
* Shared bundled-context detector. Source files in these output
|
|
48
67
|
* directories come from a bundler (Vite library mode, webpack, Next.js,
|
|
49
68
|
* Nuxt, svelte-kit) that can duplicate module code across chunks.
|
|
@@ -52,12 +71,100 @@ function isBundledOutputPath(sourceFile) {
|
|
|
52
71
|
if (!sourceFile) return false;
|
|
53
72
|
return sourceFile.includes(".svelte-kit/output/") || sourceFile.includes("/dist/") || sourceFile.includes("/build/") || sourceFile.includes(".next/") || sourceFile.includes(".nuxt/");
|
|
54
73
|
}
|
|
74
|
+
function normalizeSourcePath(path) {
|
|
75
|
+
return path.replace(/^file:\/\//, "").replace(/\\/g, "/").replace(/^\.\//, "");
|
|
76
|
+
}
|
|
77
|
+
function isAbsoluteSourcePath(path) {
|
|
78
|
+
return path.startsWith("/") || /^[a-z]:\//i.test(path);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Directories a relative manifest `filePath` can be relative to: the scanned
|
|
82
|
+
* project (the process's working directory) and its workspace root.
|
|
83
|
+
*/
|
|
84
|
+
function relativeSourceRoots() {
|
|
85
|
+
if (typeof process === "undefined" || typeof process.cwd !== "function") return [];
|
|
86
|
+
const cwd = normalizeSourcePath(process.cwd());
|
|
87
|
+
const workspaceRoot = findWorkspaceRootSync(process.cwd());
|
|
88
|
+
return workspaceRoot && normalizeSourcePath(workspaceRoot) !== cwd ? [cwd, normalizeSourcePath(workspaceRoot)] : [cwd];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Whether two source paths name the same file. A stack frame reports an
|
|
92
|
+
* absolute path; a manifest's `filePath` is absolute or relative to the
|
|
93
|
+
* project or workspace root it was scanned from (anytown scans
|
|
94
|
+
* `packages/cloud-network/src/models/*.ts` into the dashboard's manifest,
|
|
95
|
+
* #3110), so a relative path is resolved against those roots, never matched by
|
|
96
|
+
* suffix alone.
|
|
97
|
+
*/
|
|
98
|
+
function isSameSourcePath(left, right) {
|
|
99
|
+
if (!left || !right) return false;
|
|
100
|
+
const a = normalizeSourcePath(left);
|
|
101
|
+
const b = normalizeSourcePath(right);
|
|
102
|
+
if (a === b) return true;
|
|
103
|
+
const aAbsolute = isAbsoluteSourcePath(a);
|
|
104
|
+
if (aAbsolute === isAbsoluteSourcePath(b)) return false;
|
|
105
|
+
const [absolute, relative] = aAbsolute ? [a, b] : [b, a];
|
|
106
|
+
const path = getNodeBuiltins()?.path;
|
|
107
|
+
if (!path) return false;
|
|
108
|
+
return relativeSourceRoots().some((root) => normalizeSourcePath(path.resolve(root, relative)) === absolute);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The package whose loaded manifest describes the class declared in
|
|
112
|
+
* `sourceFile` — the class's identity when its nearest `package.json` is not
|
|
113
|
+
* the package that scanned it (#3110). An app manifest commonly scans workspace
|
|
114
|
+
* packages' sources under the app's own package name (anytown's
|
|
115
|
+
* `packages/cloud-network` models are `@anytown/dashboard:*`), so the
|
|
116
|
+
* stack-derived package of a source-consumed class (Vite dev server, Vitest,
|
|
117
|
+
* tsx) never declares it.
|
|
118
|
+
*
|
|
119
|
+
* When several packages' entries describe the file, the stack-derived package
|
|
120
|
+
* wins if it is one of them; otherwise a single candidate, or the one owned by
|
|
121
|
+
* the manifest it appears in. Anything else is ambiguous and yields
|
|
122
|
+
* `undefined`.
|
|
123
|
+
*/
|
|
124
|
+
function findManifestPackageForSource(name, sourceFile, stackPackageName) {
|
|
125
|
+
if (!sourceFile) return void 0;
|
|
126
|
+
const manifests = /* @__PURE__ */ new Set();
|
|
127
|
+
for (const manifest of [
|
|
128
|
+
getLocalTestManifestCache(),
|
|
129
|
+
getTestManifestCache(),
|
|
130
|
+
getStaticManifestCache(),
|
|
131
|
+
...getManifestCache().values()
|
|
132
|
+
]) if (manifest?.objects) manifests.add(manifest);
|
|
133
|
+
const resolved = resolveSourceManifestPackage(name, sourceFile, stackPackageName, manifests);
|
|
134
|
+
if (resolved) return resolved;
|
|
135
|
+
const projectManifest = readProjectManifestSync();
|
|
136
|
+
return projectManifest?.objects && !manifests.has(projectManifest) ? resolveSourceManifestPackage(name, sourceFile, stackPackageName, /* @__PURE__ */ new Set([projectManifest])) : void 0;
|
|
137
|
+
}
|
|
138
|
+
function resolveSourceManifestPackage(name, sourceFile, stackPackageName, manifests) {
|
|
139
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
140
|
+
const ownedByTheirManifest = /* @__PURE__ */ new Set();
|
|
141
|
+
for (const manifest of manifests) for (const [key, entry] of Object.entries(manifest.objects)) {
|
|
142
|
+
const qualified = isQualifiedName(key) ? parseQualifiedName(key) : void 0;
|
|
143
|
+
if ((entry.className ?? qualified?.className ?? key) !== name) continue;
|
|
144
|
+
if (!isSameSourcePath(sourceFile, entry.filePath)) continue;
|
|
145
|
+
const entryPackage = entry.packageName ?? qualified?.packageName ?? manifest.packageName;
|
|
146
|
+
if (!entryPackage) continue;
|
|
147
|
+
if (!candidates.has(entryPackage)) candidates.set(entryPackage, {
|
|
148
|
+
...entry,
|
|
149
|
+
packageName: entryPackage
|
|
150
|
+
});
|
|
151
|
+
if (entryPackage === manifest.packageName) ownedByTheirManifest.add(entryPackage);
|
|
152
|
+
}
|
|
153
|
+
const pick = (packageName) => ({
|
|
154
|
+
packageName,
|
|
155
|
+
entry: candidates.get(packageName)
|
|
156
|
+
});
|
|
157
|
+
if (stackPackageName && candidates.has(stackPackageName)) return pick(stackPackageName);
|
|
158
|
+
if (candidates.size === 1) return pick([...candidates.keys()][0]);
|
|
159
|
+
if (ownedByTheirManifest.size === 1) return pick([...ownedByTheirManifest][0]);
|
|
160
|
+
}
|
|
55
161
|
/**
|
|
56
162
|
* Build `CollisionInputs` for the decorator-origin path (`register()`).
|
|
57
163
|
* Manifest-origin inputs are built separately inside `registerFromManifest`.
|
|
58
164
|
*/
|
|
59
165
|
function buildDecoratorCollisionInputs(args) {
|
|
60
|
-
const { ctor, name, newPackageName, newSourceFile, newInBundledContext, existing, existingKey, matchKind } = args;
|
|
166
|
+
const { ctor, name, newPackageName, newSourceFile, newInBundledContext, newDeclaredTableName, existing, existingKey, matchKind } = args;
|
|
167
|
+
const existingTableName = existing.schema?.tableName || existing.config?.tableName;
|
|
61
168
|
let newExtendsExisting = false;
|
|
62
169
|
let existingExtendsNew = false;
|
|
63
170
|
try {
|
|
@@ -65,7 +172,7 @@ function buildDecoratorCollisionInputs(args) {
|
|
|
65
172
|
existingExtendsNew = existing.constructor.prototype instanceof ctor;
|
|
66
173
|
} catch {}
|
|
67
174
|
const bothSourceFilesKnown = !!(newSourceFile && existing.sourceFilePath);
|
|
68
|
-
const sameSourceFile = bothSourceFilesKnown && newSourceFile
|
|
175
|
+
const sameSourceFile = bothSourceFilesKnown && isSameSourcePath(newSourceFile, existing.sourceFilePath);
|
|
69
176
|
const existingIsPackageQualified = !!existing.packageName?.startsWith("@");
|
|
70
177
|
const samePackage = !!newPackageName && !!existing.packageName && newPackageName === existing.packageName;
|
|
71
178
|
const hasNewQualifiedKey = !!newPackageName;
|
|
@@ -88,7 +195,8 @@ function buildDecoratorCollisionInputs(args) {
|
|
|
88
195
|
existingHasAnyPackage: !!existing.packageName,
|
|
89
196
|
hasManifestContent: false,
|
|
90
197
|
registrationKeyDiffersFromExistingKey: false,
|
|
91
|
-
existingHasNoPackage: !existing.packageName
|
|
198
|
+
existingHasNoPackage: !existing.packageName,
|
|
199
|
+
declaresDifferentTable: !!newDeclaredTableName && !!existingTableName && newDeclaredTableName !== existingTableName
|
|
92
200
|
};
|
|
93
201
|
}
|
|
94
202
|
/**
|
|
@@ -116,8 +224,10 @@ function applyRegisterCollisionPolicy(args) {
|
|
|
116
224
|
const matchSuffix = args.matchKind === "case-insensitive" ? ` (case-insensitive match with "${args.existingKey}")` : "";
|
|
117
225
|
throw new Error(`SMRT Class Name Collision: "${args.name}"${matchSuffix}\n\nA class with this name is already registered, but with a different constructor.\nThis usually happens when:\n 1. Multiple test files define classes with the same name\n 2. Different packages export classes with the same name\n\nThe collision will cause the wrong field definitions to be used,\nleading to properties not being initialized correctly.\n\nTo fix:\n - Use unique class names (e.g., ${args.name}_UniqueId)\n - Or use @smrt({ name: 'unique_name' }) to override the registration name`);
|
|
118
226
|
}
|
|
119
|
-
case "
|
|
120
|
-
|
|
227
|
+
case "coexist-qualified":
|
|
228
|
+
verboseLog(`[registry] ${decision.scenario}: '${args.name}' coexists with '${args.existingKey}' (${decision.reason})`);
|
|
229
|
+
return false;
|
|
230
|
+
case "merge-manifest": throw new Error(`decideCollisionPolicy returned '${decision.policy}' for decorator origin (scenario: ${decision.scenario}). This is a bug.`);
|
|
121
231
|
}
|
|
122
232
|
}
|
|
123
233
|
/**
|
|
@@ -155,7 +265,8 @@ function buildManifestCollisionInputs(args) {
|
|
|
155
265
|
existingHasAnyPackage: !!existing.packageName,
|
|
156
266
|
hasManifestContent: !!(objectDef.fields || objectDef.methods || objectDef.schema || objectDef.decoratorConfig),
|
|
157
267
|
registrationKeyDiffersFromExistingKey: registrationKey !== existingKey,
|
|
158
|
-
existingHasNoPackage: !existing.packageName
|
|
268
|
+
existingHasNoPackage: !existing.packageName,
|
|
269
|
+
declaresDifferentTable: false
|
|
159
270
|
};
|
|
160
271
|
}
|
|
161
272
|
/**
|
|
@@ -194,9 +305,22 @@ function applyManifestCollisionPolicy(args) {
|
|
|
194
305
|
}
|
|
195
306
|
}
|
|
196
307
|
function resolveTableName(ctor, name, config) {
|
|
197
|
-
const manifestEntry = config._manifest ? lookupRegistrationManifest(config._manifest, name, config.packageName, config._manifestKey) : discoverManifestSync(name);
|
|
308
|
+
const manifestEntry = config._manifest ? lookupRegistrationManifest(config._manifest, name, config.packageName, config._manifestKey) : discoverManifestSync(config.packageName ? createQualifiedName(config.packageName, name) : name);
|
|
198
309
|
return manifestEntry?.schema?.tableName || manifestEntry?.decoratorConfig?.tableName || config.tableName || tableNameFromClass(ctor);
|
|
199
310
|
}
|
|
311
|
+
/**
|
|
312
|
+
* Whether `packageName` itself declares the class `qualifiedKey` names: it is
|
|
313
|
+
* registered under that key (a manifest stub or an earlier registration), or
|
|
314
|
+
* a manifest owned by that package describes it. A stack-derived package is
|
|
315
|
+
* only trusted as a class's identity on this evidence — a consumer bundle
|
|
316
|
+
* that inlined a dependency reports the consumer's package, whose manifest
|
|
317
|
+
* describes the dependency's classes under the dependency's name (#3098).
|
|
318
|
+
*/
|
|
319
|
+
function packageDeclaresClass(packageName, qualifiedKey) {
|
|
320
|
+
if (getClasses().has(qualifiedKey)) return true;
|
|
321
|
+
const entry = discoverManifestSync(qualifiedKey);
|
|
322
|
+
return !!entry && (entry.packageName === void 0 || entry.packageName === packageName);
|
|
323
|
+
}
|
|
200
324
|
function lookupRegistrationManifest(manifest, name, _packageName, manifestKey) {
|
|
201
325
|
if (manifestKey) return manifest.objects[manifestKey];
|
|
202
326
|
return lookupInManifest(manifest, name);
|
|
@@ -320,7 +444,8 @@ function registerUntracked(ctor, config = {}) {
|
|
|
320
444
|
existing.qualifiedName = nextPackageName ? createQualifiedName(nextPackageName, name) : void 0;
|
|
321
445
|
const nextTableName = resolveTableName(ctor, name, {
|
|
322
446
|
...existing.config,
|
|
323
|
-
...config
|
|
447
|
+
...config,
|
|
448
|
+
...nextPackageName ? { packageName: nextPackageName } : {}
|
|
324
449
|
});
|
|
325
450
|
existing.config = {
|
|
326
451
|
...existing.config,
|
|
@@ -339,6 +464,7 @@ function registerUntracked(ctor, config = {}) {
|
|
|
339
464
|
version: ""
|
|
340
465
|
};
|
|
341
466
|
existing.schema.tableName = nextTableName;
|
|
467
|
+
if (existing.constructor._isManifestStub === true && newSourceFile) existing.sourceFilePath = newSourceFile;
|
|
342
468
|
existing.constructor = ctor;
|
|
343
469
|
setSmrtTableName(ctor, nextTableName);
|
|
344
470
|
declareSensitiveTable(existing.config, nextTableName, existing.schema?.tableName);
|
|
@@ -359,7 +485,6 @@ function registerUntracked(ctor, config = {}) {
|
|
|
359
485
|
return nextKey;
|
|
360
486
|
}
|
|
361
487
|
const newSourceFile = getSourceFileFromStack();
|
|
362
|
-
const newPackageName = explicitPackageName || getPackageName(ctor, true) || void 0;
|
|
363
488
|
const newInBundledContext = isBundledOutputPath(newSourceFile);
|
|
364
489
|
const constructorKey = getConstructorIndex().get(ctor);
|
|
365
490
|
const constructorEntry = constructorKey ? getClasses().get(constructorKey) : void 0;
|
|
@@ -385,15 +510,23 @@ function registerUntracked(ctor, config = {}) {
|
|
|
385
510
|
upsertExistingEntry(constructorKey, constructorEntry);
|
|
386
511
|
return;
|
|
387
512
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
513
|
+
const stackPackageName = explicitPackageName ? void 0 : getPackageName(ctor, true) || void 0;
|
|
514
|
+
const stackPackageDeclaresClass = !!stackPackageName && packageDeclaresClass(stackPackageName, createQualifiedName(stackPackageName, name));
|
|
515
|
+
const sourceManifestPackage = explicitPackageName || stackPackageDeclaresClass ? void 0 : findManifestPackageForSource(name, newSourceFile, stackPackageName);
|
|
516
|
+
const newPackageName = explicitPackageName || sourceManifestPackage?.packageName || stackPackageName;
|
|
517
|
+
const ownQualifiedKey = newPackageName ? createQualifiedName(newPackageName, name) : void 0;
|
|
518
|
+
const ownPackageDeclaresClass = !!ownQualifiedKey && (!!explicitPackageName || stackPackageDeclaresClass || !!sourceManifestPackage);
|
|
519
|
+
const belongsToAnotherPackage = (existing) => ownPackageDeclaresClass && !!existing.packageName && existing.packageName !== newPackageName;
|
|
520
|
+
const exactExisting = getClasses().get(name);
|
|
521
|
+
if (exactExisting && !belongsToAnotherPackage(exactExisting)) {
|
|
522
|
+
const existing = exactExisting;
|
|
391
523
|
if (applyRegisterCollisionPolicy({
|
|
392
524
|
ctor,
|
|
393
525
|
name,
|
|
394
526
|
newPackageName,
|
|
395
527
|
newSourceFile,
|
|
396
528
|
newInBundledContext,
|
|
529
|
+
newDeclaredTableName: config.tableName,
|
|
397
530
|
existing,
|
|
398
531
|
existingKey: name,
|
|
399
532
|
matchKind: "exact-key",
|
|
@@ -406,23 +539,39 @@ function registerUntracked(ctor, config = {}) {
|
|
|
406
539
|
const nameMatches = existing.name?.toLowerCase() === lowerName;
|
|
407
540
|
if (!(keyMatches || nameMatches) || existingKey === name) continue;
|
|
408
541
|
if (explicitPackageName && existing.packageName && explicitPackageName !== existing.packageName && isQualifiedName(existingKey)) continue;
|
|
542
|
+
if (belongsToAnotherPackage(existing)) continue;
|
|
409
543
|
if (applyRegisterCollisionPolicy({
|
|
410
544
|
ctor,
|
|
411
545
|
name,
|
|
412
546
|
newPackageName,
|
|
413
547
|
newSourceFile,
|
|
414
548
|
newInBundledContext,
|
|
549
|
+
newDeclaredTableName: config.tableName,
|
|
415
550
|
existing,
|
|
416
551
|
existingKey,
|
|
417
552
|
matchKind: "case-insensitive",
|
|
418
553
|
upsertExistingEntry
|
|
419
554
|
})) return;
|
|
420
555
|
}
|
|
421
|
-
const packageNameFromStack =
|
|
556
|
+
const packageNameFromStack = newPackageName;
|
|
422
557
|
const sourceFilePath = getSourceFileFromStack();
|
|
558
|
+
const unknownPackageManifestEntry = () => {
|
|
559
|
+
const entry = discoverManifestSync(name);
|
|
560
|
+
if (!entry?.packageName || newInBundledContext || isSameSourcePath(newSourceFile, entry.filePath)) return entry;
|
|
561
|
+
};
|
|
562
|
+
const simpleNameManifestFallback = () => {
|
|
563
|
+
if (findClassesByName(name).some((candidate) => !!candidate.packageName && candidate.packageName !== newPackageName)) return;
|
|
564
|
+
const entry = discoverManifestSync(name);
|
|
565
|
+
if (!(!!entry?.packageName && entry.packageName !== newPackageName)) return entry;
|
|
566
|
+
if (isSameSourcePath(newSourceFile, entry?.filePath)) return entry;
|
|
567
|
+
if (ownPackageDeclaresClass) return void 0;
|
|
568
|
+
const entryTable = entry?.schema?.tableName || entry?.decoratorConfig?.tableName;
|
|
569
|
+
const declaresOtherTable = !!config.tableName && !!entryTable && config.tableName !== entryTable;
|
|
570
|
+
return !newInBundledContext || declaresOtherTable ? void 0 : entry;
|
|
571
|
+
};
|
|
423
572
|
let manifestEntry;
|
|
424
573
|
if (config._manifest) manifestEntry = lookupRegistrationManifest(config._manifest, name, explicitPackageName, config._manifestKey);
|
|
425
|
-
if (!manifestEntry) manifestEntry = discoverManifestSync(
|
|
574
|
+
if (!manifestEntry) manifestEntry = ownQualifiedKey ? discoverManifestSync(ownQualifiedKey) ?? sourceManifestPackage?.entry ?? simpleNameManifestFallback() : unknownPackageManifestEntry();
|
|
426
575
|
const runtimeTenantScopedDeclaration = getConstructorTenantScopedDeclarations().get(ctor);
|
|
427
576
|
if (manifestEntry && config.tenantScoped === void 0 && normalizeTenantScopedConfig(manifestEntry.decoratorConfig?.tenantScoped) === void 0 && runtimeTenantScopedDeclaration) throw new ConfigurationError(`Manifest for '${name}' omits or disables tenantScoped but its runtime constructor is decorated with @TenantScoped(). Regenerate the manifest so tenancy schema and runtime enforcement agree.`, "CONFIG_TENANT_MANIFEST_CONFLICT");
|
|
428
577
|
const fields = /* @__PURE__ */ new Map();
|
|
@@ -552,7 +701,7 @@ function registerUntracked(ctor, config = {}) {
|
|
|
552
701
|
if (childStrategy !== void 0 && parentStrategy !== childStrategy) throw ConfigurationError.incompatibleStrategy(name, childStrategy, parentName, parentStrategy);
|
|
553
702
|
}
|
|
554
703
|
}
|
|
555
|
-
const tableName =
|
|
704
|
+
const tableName = manifestEntry?.schema?.tableName || manifestEntry?.decoratorConfig?.tableName || config.tableName || tableNameFromClass(ctor);
|
|
556
705
|
setSmrtTableName(ctor, tableName);
|
|
557
706
|
let schema;
|
|
558
707
|
if (manifestEntry?.schema) {
|
|
@@ -622,7 +771,7 @@ function registerUntracked(ctor, config = {}) {
|
|
|
622
771
|
validators,
|
|
623
772
|
validationRules,
|
|
624
773
|
tools: manifestEntry?.tools,
|
|
625
|
-
collection: manifestEntry?.collection ?? pluralizeCollection(name),
|
|
774
|
+
collection: manifestEntry?.collection ?? inheritedStiCollection(ctor) ?? pluralizeCollection(name),
|
|
626
775
|
collectionConstructor: promotedCollectionConstructor,
|
|
627
776
|
packageName,
|
|
628
777
|
sourceFilePath,
|
|
@@ -889,6 +1038,7 @@ function mergeManifestIntoExistingRegistration(existing, objectDef, packageName)
|
|
|
889
1038
|
} else if (!existing.packageName && objectDef.packageName) existing.packageName = objectDef.packageName;
|
|
890
1039
|
if (objectDef.extends) existing.extends = packageName ? qualifyExtendsName(objectDef.extends, packageName) : objectDef.extends;
|
|
891
1040
|
if (!existing.sourceFilePath && objectDef.filePath) existing.sourceFilePath = objectDef.filePath;
|
|
1041
|
+
if (objectDef.collection) existing.collection = objectDef.collection;
|
|
892
1042
|
existing.visibility = objectDef.visibility || manifestConfig.visibility || existing.visibility;
|
|
893
1043
|
invalidateInheritanceEntries(existing);
|
|
894
1044
|
}
|
|
@@ -1027,6 +1177,6 @@ function registerFromManifestUntracked(name, objectDef, manifestPackageName) {
|
|
|
1027
1177
|
}
|
|
1028
1178
|
}
|
|
1029
1179
|
//#endregion
|
|
1030
|
-
export { ensureTenantScopedField, invalidateInheritanceEntries, register, registerCollection, registerFromManifest };
|
|
1180
|
+
export { ensureTenantScopedField, invalidateInheritanceEntries, isBundledOutputPath, isSameSourcePath, register, registerCollection, registerFromManifest };
|
|
1031
1181
|
|
|
1032
1182
|
//# sourceMappingURL=class-registration.js.map
|