@happyvertical/smrt-core 0.51.24 → 0.51.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/AGENTS.md +17 -0
  2. package/dist/index.js +1 -1
  3. package/dist/manifest/manifest-loader.d.ts +12 -0
  4. package/dist/manifest/manifest-loader.d.ts.map +1 -1
  5. package/dist/manifest/manifest-loader.js +54 -10
  6. package/dist/manifest/manifest-loader.js.map +1 -1
  7. package/dist/manifest/static-manifest.js +1 -1
  8. package/dist/manifest/static-manifest.js.map +1 -1
  9. package/dist/manifest/store.d.ts.map +1 -1
  10. package/dist/manifest/store.js +7 -2
  11. package/dist/manifest/store.js.map +1 -1
  12. package/dist/manifest.json +1 -1
  13. package/dist/object.d.ts.map +1 -1
  14. package/dist/object.js +26 -26
  15. package/dist/object.js.map +1 -1
  16. package/dist/registry/__tests__/helpers/consumer-workspace.d.ts +42 -0
  17. package/dist/registry/__tests__/helpers/consumer-workspace.d.ts.map +1 -0
  18. package/dist/registry/class-registration.d.ts +15 -0
  19. package/dist/registry/class-registration.d.ts.map +1 -1
  20. package/dist/registry/class-registration.js +150 -20
  21. package/dist/registry/class-registration.js.map +1 -1
  22. package/dist/registry/collection-resolution.d.ts +8 -2
  23. package/dist/registry/collection-resolution.d.ts.map +1 -1
  24. package/dist/registry/collection-resolution.js +12 -2
  25. package/dist/registry/collection-resolution.js.map +1 -1
  26. package/dist/registry/collision-policy.d.ts +10 -0
  27. package/dist/registry/collision-policy.d.ts.map +1 -1
  28. package/dist/registry/collision-policy.js +7 -1
  29. package/dist/registry/collision-policy.js.map +1 -1
  30. package/dist/registry/inheritance-resolver.js +1 -1
  31. package/dist/registry/name-resolver.d.ts.map +1 -1
  32. package/dist/registry/name-resolver.js +11 -0
  33. package/dist/registry/name-resolver.js.map +1 -1
  34. package/dist/registry/schema-builder.d.ts.map +1 -1
  35. package/dist/registry/schema-builder.js +71 -31
  36. package/dist/registry/schema-builder.js.map +1 -1
  37. package/dist/registry/shared-state.d.ts.map +1 -1
  38. package/dist/registry/shared-state.js +3 -4
  39. package/dist/registry/shared-state.js.map +1 -1
  40. package/dist/registry.d.ts.map +1 -1
  41. package/dist/registry.js +25 -8
  42. package/dist/registry.js.map +1 -1
  43. package/dist/scanner/manifest-generator.js +1 -1
  44. package/dist/smrt-knowledge.json +5 -5
  45. package/dist/testing/database.js +1 -1
  46. package/dist/utils/stack-frames.d.ts +26 -0
  47. package/dist/utils/stack-frames.d.ts.map +1 -1
  48. package/dist/utils/stack-frames.js +79 -2
  49. package/dist/utils/stack-frames.js.map +1 -1
  50. 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;AAcH,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAKV,qBAAqB,EAGtB,MAAM,qBAAqB,CAAC;AAyC7B,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,iBAAiB,EAElB,MAAM,YAAY,CAAC;AA2gBpB;;;;GAIG;AACH,wBAAgB,QAAQ,CACtB,IAAI,EAAE,OAAO,UAAU,EACvB,MAAM,GAAE,iBAAsB,GAC7B,IAAI,CAON;AA+4BD,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;AA2KD,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,qBAAqB,EAChC,mBAAmB,CAAC,EAAE,MAAM,GAC3B,IAAI,CAQN"}
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;AA0BpB;;;;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;AA0gCD,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;AA2KD,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 { findClass, getCanonicalClassName, hasClassCaseInsensitive, qualifyExtendsName } from "./name-resolver.js";
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";
@@ -52,12 +52,100 @@ function isBundledOutputPath(sourceFile) {
52
52
  if (!sourceFile) return false;
53
53
  return sourceFile.includes(".svelte-kit/output/") || sourceFile.includes("/dist/") || sourceFile.includes("/build/") || sourceFile.includes(".next/") || sourceFile.includes(".nuxt/");
54
54
  }
55
+ function normalizeSourcePath(path) {
56
+ return path.replace(/^file:\/\//, "").replace(/\\/g, "/").replace(/^\.\//, "");
57
+ }
58
+ function isAbsoluteSourcePath(path) {
59
+ return path.startsWith("/") || /^[a-z]:\//i.test(path);
60
+ }
61
+ /**
62
+ * Directories a relative manifest `filePath` can be relative to: the scanned
63
+ * project (the process's working directory) and its workspace root.
64
+ */
65
+ function relativeSourceRoots() {
66
+ if (typeof process === "undefined" || typeof process.cwd !== "function") return [];
67
+ const cwd = normalizeSourcePath(process.cwd());
68
+ const workspaceRoot = findWorkspaceRootSync(process.cwd());
69
+ return workspaceRoot && normalizeSourcePath(workspaceRoot) !== cwd ? [cwd, normalizeSourcePath(workspaceRoot)] : [cwd];
70
+ }
71
+ /**
72
+ * Whether two source paths name the same file. A stack frame reports an
73
+ * absolute path; a manifest's `filePath` is absolute or relative to the
74
+ * project or workspace root it was scanned from (anytown scans
75
+ * `packages/cloud-network/src/models/*.ts` into the dashboard's manifest,
76
+ * #3110), so a relative path is resolved against those roots, never matched by
77
+ * suffix alone.
78
+ */
79
+ function isSameSourcePath(left, right) {
80
+ if (!left || !right) return false;
81
+ const a = normalizeSourcePath(left);
82
+ const b = normalizeSourcePath(right);
83
+ if (a === b) return true;
84
+ const aAbsolute = isAbsoluteSourcePath(a);
85
+ if (aAbsolute === isAbsoluteSourcePath(b)) return false;
86
+ const [absolute, relative] = aAbsolute ? [a, b] : [b, a];
87
+ const path = getNodeBuiltins()?.path;
88
+ if (!path) return false;
89
+ return relativeSourceRoots().some((root) => normalizeSourcePath(path.resolve(root, relative)) === absolute);
90
+ }
91
+ /**
92
+ * The package whose loaded manifest describes the class declared in
93
+ * `sourceFile` — the class's identity when its nearest `package.json` is not
94
+ * the package that scanned it (#3110). An app manifest commonly scans workspace
95
+ * packages' sources under the app's own package name (anytown's
96
+ * `packages/cloud-network` models are `@anytown/dashboard:*`), so the
97
+ * stack-derived package of a source-consumed class (Vite dev server, Vitest,
98
+ * tsx) never declares it.
99
+ *
100
+ * When several packages' entries describe the file, the stack-derived package
101
+ * wins if it is one of them; otherwise a single candidate, or the one owned by
102
+ * the manifest it appears in. Anything else is ambiguous and yields
103
+ * `undefined`.
104
+ */
105
+ function findManifestPackageForSource(name, sourceFile, stackPackageName) {
106
+ if (!sourceFile) return void 0;
107
+ const manifests = /* @__PURE__ */ new Set();
108
+ for (const manifest of [
109
+ getLocalTestManifestCache(),
110
+ getTestManifestCache(),
111
+ getStaticManifestCache(),
112
+ ...getManifestCache().values()
113
+ ]) if (manifest?.objects) manifests.add(manifest);
114
+ const resolved = resolveSourceManifestPackage(name, sourceFile, stackPackageName, manifests);
115
+ if (resolved) return resolved;
116
+ const projectManifest = readProjectManifestSync();
117
+ return projectManifest?.objects && !manifests.has(projectManifest) ? resolveSourceManifestPackage(name, sourceFile, stackPackageName, /* @__PURE__ */ new Set([projectManifest])) : void 0;
118
+ }
119
+ function resolveSourceManifestPackage(name, sourceFile, stackPackageName, manifests) {
120
+ const candidates = /* @__PURE__ */ new Map();
121
+ const ownedByTheirManifest = /* @__PURE__ */ new Set();
122
+ for (const manifest of manifests) for (const [key, entry] of Object.entries(manifest.objects)) {
123
+ const qualified = isQualifiedName(key) ? parseQualifiedName(key) : void 0;
124
+ if ((entry.className ?? qualified?.className ?? key) !== name) continue;
125
+ if (!isSameSourcePath(sourceFile, entry.filePath)) continue;
126
+ const entryPackage = entry.packageName ?? qualified?.packageName ?? manifest.packageName;
127
+ if (!entryPackage) continue;
128
+ if (!candidates.has(entryPackage)) candidates.set(entryPackage, {
129
+ ...entry,
130
+ packageName: entryPackage
131
+ });
132
+ if (entryPackage === manifest.packageName) ownedByTheirManifest.add(entryPackage);
133
+ }
134
+ const pick = (packageName) => ({
135
+ packageName,
136
+ entry: candidates.get(packageName)
137
+ });
138
+ if (stackPackageName && candidates.has(stackPackageName)) return pick(stackPackageName);
139
+ if (candidates.size === 1) return pick([...candidates.keys()][0]);
140
+ if (ownedByTheirManifest.size === 1) return pick([...ownedByTheirManifest][0]);
141
+ }
55
142
  /**
56
143
  * Build `CollisionInputs` for the decorator-origin path (`register()`).
57
144
  * Manifest-origin inputs are built separately inside `registerFromManifest`.
58
145
  */
59
146
  function buildDecoratorCollisionInputs(args) {
60
- const { ctor, name, newPackageName, newSourceFile, newInBundledContext, existing, existingKey, matchKind } = args;
147
+ const { ctor, name, newPackageName, newSourceFile, newInBundledContext, newDeclaredTableName, existing, existingKey, matchKind } = args;
148
+ const existingTableName = existing.schema?.tableName || existing.config?.tableName;
61
149
  let newExtendsExisting = false;
62
150
  let existingExtendsNew = false;
63
151
  try {
@@ -65,7 +153,7 @@ function buildDecoratorCollisionInputs(args) {
65
153
  existingExtendsNew = existing.constructor.prototype instanceof ctor;
66
154
  } catch {}
67
155
  const bothSourceFilesKnown = !!(newSourceFile && existing.sourceFilePath);
68
- const sameSourceFile = bothSourceFilesKnown && newSourceFile === existing.sourceFilePath;
156
+ const sameSourceFile = bothSourceFilesKnown && isSameSourcePath(newSourceFile, existing.sourceFilePath);
69
157
  const existingIsPackageQualified = !!existing.packageName?.startsWith("@");
70
158
  const samePackage = !!newPackageName && !!existing.packageName && newPackageName === existing.packageName;
71
159
  const hasNewQualifiedKey = !!newPackageName;
@@ -88,7 +176,8 @@ function buildDecoratorCollisionInputs(args) {
88
176
  existingHasAnyPackage: !!existing.packageName,
89
177
  hasManifestContent: false,
90
178
  registrationKeyDiffersFromExistingKey: false,
91
- existingHasNoPackage: !existing.packageName
179
+ existingHasNoPackage: !existing.packageName,
180
+ declaresDifferentTable: !!newDeclaredTableName && !!existingTableName && newDeclaredTableName !== existingTableName
92
181
  };
93
182
  }
94
183
  /**
@@ -116,8 +205,10 @@ function applyRegisterCollisionPolicy(args) {
116
205
  const matchSuffix = args.matchKind === "case-insensitive" ? ` (case-insensitive match with "${args.existingKey}")` : "";
117
206
  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
207
  }
119
- case "merge-manifest":
120
- case "coexist-qualified": throw new Error(`decideCollisionPolicy returned '${decision.policy}' for decorator origin (scenario: ${decision.scenario}). This is a bug.`);
208
+ case "coexist-qualified":
209
+ verboseLog(`[registry] ${decision.scenario}: '${args.name}' coexists with '${args.existingKey}' (${decision.reason})`);
210
+ return false;
211
+ case "merge-manifest": throw new Error(`decideCollisionPolicy returned '${decision.policy}' for decorator origin (scenario: ${decision.scenario}). This is a bug.`);
121
212
  }
122
213
  }
123
214
  /**
@@ -155,7 +246,8 @@ function buildManifestCollisionInputs(args) {
155
246
  existingHasAnyPackage: !!existing.packageName,
156
247
  hasManifestContent: !!(objectDef.fields || objectDef.methods || objectDef.schema || objectDef.decoratorConfig),
157
248
  registrationKeyDiffersFromExistingKey: registrationKey !== existingKey,
158
- existingHasNoPackage: !existing.packageName
249
+ existingHasNoPackage: !existing.packageName,
250
+ declaresDifferentTable: false
159
251
  };
160
252
  }
161
253
  /**
@@ -194,9 +286,22 @@ function applyManifestCollisionPolicy(args) {
194
286
  }
195
287
  }
196
288
  function resolveTableName(ctor, name, config) {
197
- const manifestEntry = config._manifest ? lookupRegistrationManifest(config._manifest, name, config.packageName, config._manifestKey) : discoverManifestSync(name);
289
+ const manifestEntry = config._manifest ? lookupRegistrationManifest(config._manifest, name, config.packageName, config._manifestKey) : discoverManifestSync(config.packageName ? createQualifiedName(config.packageName, name) : name);
198
290
  return manifestEntry?.schema?.tableName || manifestEntry?.decoratorConfig?.tableName || config.tableName || tableNameFromClass(ctor);
199
291
  }
292
+ /**
293
+ * Whether `packageName` itself declares the class `qualifiedKey` names: it is
294
+ * registered under that key (a manifest stub or an earlier registration), or
295
+ * a manifest owned by that package describes it. A stack-derived package is
296
+ * only trusted as a class's identity on this evidence — a consumer bundle
297
+ * that inlined a dependency reports the consumer's package, whose manifest
298
+ * describes the dependency's classes under the dependency's name (#3098).
299
+ */
300
+ function packageDeclaresClass(packageName, qualifiedKey) {
301
+ if (getClasses().has(qualifiedKey)) return true;
302
+ const entry = discoverManifestSync(qualifiedKey);
303
+ return !!entry && (entry.packageName === void 0 || entry.packageName === packageName);
304
+ }
200
305
  function lookupRegistrationManifest(manifest, name, _packageName, manifestKey) {
201
306
  if (manifestKey) return manifest.objects[manifestKey];
202
307
  return lookupInManifest(manifest, name);
@@ -320,7 +425,8 @@ function registerUntracked(ctor, config = {}) {
320
425
  existing.qualifiedName = nextPackageName ? createQualifiedName(nextPackageName, name) : void 0;
321
426
  const nextTableName = resolveTableName(ctor, name, {
322
427
  ...existing.config,
323
- ...config
428
+ ...config,
429
+ ...nextPackageName ? { packageName: nextPackageName } : {}
324
430
  });
325
431
  existing.config = {
326
432
  ...existing.config,
@@ -339,6 +445,7 @@ function registerUntracked(ctor, config = {}) {
339
445
  version: ""
340
446
  };
341
447
  existing.schema.tableName = nextTableName;
448
+ if (existing.constructor._isManifestStub === true && newSourceFile) existing.sourceFilePath = newSourceFile;
342
449
  existing.constructor = ctor;
343
450
  setSmrtTableName(ctor, nextTableName);
344
451
  declareSensitiveTable(existing.config, nextTableName, existing.schema?.tableName);
@@ -359,7 +466,6 @@ function registerUntracked(ctor, config = {}) {
359
466
  return nextKey;
360
467
  }
361
468
  const newSourceFile = getSourceFileFromStack();
362
- const newPackageName = explicitPackageName || getPackageName(ctor, true) || void 0;
363
469
  const newInBundledContext = isBundledOutputPath(newSourceFile);
364
470
  const constructorKey = getConstructorIndex().get(ctor);
365
471
  const constructorEntry = constructorKey ? getClasses().get(constructorKey) : void 0;
@@ -385,15 +491,23 @@ function registerUntracked(ctor, config = {}) {
385
491
  upsertExistingEntry(constructorKey, constructorEntry);
386
492
  return;
387
493
  }
388
- if (getClasses().has(name)) {
389
- const existing = getClasses().get(name);
390
- if (!existing) throw new Error(`Registry inconsistency: ${name} exists in classes Map but get() returned undefined`);
494
+ const stackPackageName = explicitPackageName ? void 0 : getPackageName(ctor, true) || void 0;
495
+ const stackPackageDeclaresClass = !!stackPackageName && packageDeclaresClass(stackPackageName, createQualifiedName(stackPackageName, name));
496
+ const sourceManifestPackage = explicitPackageName || stackPackageDeclaresClass ? void 0 : findManifestPackageForSource(name, newSourceFile, stackPackageName);
497
+ const newPackageName = explicitPackageName || sourceManifestPackage?.packageName || stackPackageName;
498
+ const ownQualifiedKey = newPackageName ? createQualifiedName(newPackageName, name) : void 0;
499
+ const ownPackageDeclaresClass = !!ownQualifiedKey && (!!explicitPackageName || stackPackageDeclaresClass || !!sourceManifestPackage);
500
+ const belongsToAnotherPackage = (existing) => ownPackageDeclaresClass && !!existing.packageName && existing.packageName !== newPackageName;
501
+ const exactExisting = getClasses().get(name);
502
+ if (exactExisting && !belongsToAnotherPackage(exactExisting)) {
503
+ const existing = exactExisting;
391
504
  if (applyRegisterCollisionPolicy({
392
505
  ctor,
393
506
  name,
394
507
  newPackageName,
395
508
  newSourceFile,
396
509
  newInBundledContext,
510
+ newDeclaredTableName: config.tableName,
397
511
  existing,
398
512
  existingKey: name,
399
513
  matchKind: "exact-key",
@@ -406,23 +520,39 @@ function registerUntracked(ctor, config = {}) {
406
520
  const nameMatches = existing.name?.toLowerCase() === lowerName;
407
521
  if (!(keyMatches || nameMatches) || existingKey === name) continue;
408
522
  if (explicitPackageName && existing.packageName && explicitPackageName !== existing.packageName && isQualifiedName(existingKey)) continue;
523
+ if (belongsToAnotherPackage(existing)) continue;
409
524
  if (applyRegisterCollisionPolicy({
410
525
  ctor,
411
526
  name,
412
527
  newPackageName,
413
528
  newSourceFile,
414
529
  newInBundledContext,
530
+ newDeclaredTableName: config.tableName,
415
531
  existing,
416
532
  existingKey,
417
533
  matchKind: "case-insensitive",
418
534
  upsertExistingEntry
419
535
  })) return;
420
536
  }
421
- const packageNameFromStack = explicitPackageName || getPackageName(ctor, true) || void 0;
537
+ const packageNameFromStack = newPackageName;
422
538
  const sourceFilePath = getSourceFileFromStack();
539
+ const unknownPackageManifestEntry = () => {
540
+ const entry = discoverManifestSync(name);
541
+ if (!entry?.packageName || newInBundledContext || isSameSourcePath(newSourceFile, entry.filePath)) return entry;
542
+ };
543
+ const simpleNameManifestFallback = () => {
544
+ if (findClassesByName(name).some((candidate) => !!candidate.packageName && candidate.packageName !== newPackageName)) return;
545
+ const entry = discoverManifestSync(name);
546
+ if (!(!!entry?.packageName && entry.packageName !== newPackageName)) return entry;
547
+ if (isSameSourcePath(newSourceFile, entry?.filePath)) return entry;
548
+ if (ownPackageDeclaresClass) return void 0;
549
+ const entryTable = entry?.schema?.tableName || entry?.decoratorConfig?.tableName;
550
+ const declaresOtherTable = !!config.tableName && !!entryTable && config.tableName !== entryTable;
551
+ return !newInBundledContext || declaresOtherTable ? void 0 : entry;
552
+ };
423
553
  let manifestEntry;
424
554
  if (config._manifest) manifestEntry = lookupRegistrationManifest(config._manifest, name, explicitPackageName, config._manifestKey);
425
- if (!manifestEntry) manifestEntry = discoverManifestSync(name);
555
+ if (!manifestEntry) manifestEntry = ownQualifiedKey ? discoverManifestSync(ownQualifiedKey) ?? sourceManifestPackage?.entry ?? simpleNameManifestFallback() : unknownPackageManifestEntry();
426
556
  const runtimeTenantScopedDeclaration = getConstructorTenantScopedDeclarations().get(ctor);
427
557
  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
558
  const fields = /* @__PURE__ */ new Map();
@@ -552,7 +682,7 @@ function registerUntracked(ctor, config = {}) {
552
682
  if (childStrategy !== void 0 && parentStrategy !== childStrategy) throw ConfigurationError.incompatibleStrategy(name, childStrategy, parentName, parentStrategy);
553
683
  }
554
684
  }
555
- const tableName = resolveTableName(ctor, name, config);
685
+ const tableName = manifestEntry?.schema?.tableName || manifestEntry?.decoratorConfig?.tableName || config.tableName || tableNameFromClass(ctor);
556
686
  setSmrtTableName(ctor, tableName);
557
687
  let schema;
558
688
  if (manifestEntry?.schema) {
@@ -1027,6 +1157,6 @@ function registerFromManifestUntracked(name, objectDef, manifestPackageName) {
1027
1157
  }
1028
1158
  }
1029
1159
  //#endregion
1030
- export { ensureTenantScopedField, invalidateInheritanceEntries, register, registerCollection, registerFromManifest };
1160
+ export { ensureTenantScopedField, invalidateInheritanceEntries, isBundledOutputPath, isSameSourcePath, register, registerCollection, registerFromManifest };
1031
1161
 
1032
1162
  //# sourceMappingURL=class-registration.js.map