@happyvertical/smrt-core 0.51.38 → 0.52.0
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 +8 -0
- package/agents/schema-paths.md +90 -0
- package/dist/cascade.d.ts.map +1 -1
- package/dist/cascade.js +2 -1
- package/dist/cascade.js.map +1 -1
- package/dist/collection.d.ts +10 -1
- package/dist/collection.d.ts.map +1 -1
- package/dist/collection.js +14 -4
- package/dist/collection.js.map +1 -1
- package/dist/errors.d.ts +18 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +21 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/manifest/static-manifest.d.ts.map +1 -1
- package/dist/manifest/static-manifest.js +9 -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 +9 -1
- package/dist/migrations/differ.d.ts +58 -0
- package/dist/migrations/differ.d.ts.map +1 -1
- package/dist/migrations/differ.js +187 -32
- package/dist/migrations/differ.js.map +1 -1
- package/dist/migrations/null-equal-indexes.js +1 -1
- package/dist/migrations/orchestrate.d.ts +7 -0
- package/dist/migrations/orchestrate.d.ts.map +1 -1
- package/dist/migrations/orchestrate.js +4 -2
- package/dist/migrations/orchestrate.js.map +1 -1
- package/dist/migrations/tracker.d.ts +29 -1
- package/dist/migrations/tracker.d.ts.map +1 -1
- package/dist/migrations/tracker.js +92 -7
- package/dist/migrations/tracker.js.map +1 -1
- package/dist/object.d.ts +128 -0
- package/dist/object.d.ts.map +1 -1
- package/dist/object.js +333 -44
- package/dist/object.js.map +1 -1
- package/dist/registry/schema-builder.d.ts.map +1 -1
- package/dist/registry/schema-builder.js +7 -5
- package/dist/registry/schema-builder.js.map +1 -1
- package/dist/registry.d.ts +68 -4
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +119 -8
- package/dist/registry.js.map +1 -1
- package/dist/scanner/index.d.ts +1 -1
- package/dist/scanner/index.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.d.ts +3 -2
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +12 -8
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/scanner/types.d.ts +21 -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 +68 -11
- package/dist/schema/conflict-target.d.ts.map +1 -1
- package/dist/schema/conflict-target.js +68 -12
- package/dist/schema/conflict-target.js.map +1 -1
- package/dist/schema/foreign-key-ddl.d.ts +17 -1
- package/dist/schema/foreign-key-ddl.d.ts.map +1 -1
- package/dist/schema/foreign-key-ddl.js +29 -3
- package/dist/schema/foreign-key-ddl.js.map +1 -1
- package/dist/schema/foreign-key-policy.d.ts +8 -0
- package/dist/schema/foreign-key-policy.d.ts.map +1 -1
- package/dist/schema/foreign-key-policy.js +2 -1
- package/dist/schema/foreign-key-policy.js.map +1 -1
- package/dist/schema/generator.d.ts +7 -0
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +18 -10
- package/dist/schema/generator.js.map +1 -1
- package/dist/schema/text-cast-probe.d.ts +50 -1
- package/dist/schema/text-cast-probe.d.ts.map +1 -1
- package/dist/schema/text-cast-probe.js +46 -11
- package/dist/schema/text-cast-probe.js.map +1 -1
- package/dist/schema/types.d.ts +6 -0
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +12 -7
- package/package.json +10 -10
package/dist/registry.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { ConfigurationError } from "./errors.js";
|
|
2
|
+
import { defaultConflictColumns, resolveOwnershipTenantColumn, resolveTenantColumn } from "./schema/conflict-target.js";
|
|
2
3
|
import { classnameToTablename, toSnakeCase } from "./utils/naming.js";
|
|
4
|
+
import { buildCascadePlan } from "./cascade.js";
|
|
3
5
|
import { applyOneToManyChildAccessors } from "./child-accessors.js";
|
|
4
6
|
import { applyPendingDecoratorRegistrations } from "./decorators/compatibility.js";
|
|
5
7
|
import { bumpRegistryGeneration, getRegistryGeneration } from "./registry/generation.js";
|
|
6
8
|
import { createQualifiedName, isQualifiedName, parseQualifiedName } from "./utils/qualified-names.js";
|
|
7
9
|
import { LRUCache } from "./utils/lru-cache.js";
|
|
8
10
|
import { getClasses, getCollectionCache, getCollectionTableNames, getCollections, getConstructorFieldDecorators, getConstructorTenantScopedDeclarations, getDbInstanceIds, getDiscoveryAttemptCache, getFieldDecorators, getInheritanceCache, getLegacyFieldDecorators, getMethodDecorators, getNextDbId, getSourceFileFromStack, getStiSiblingsLoaded, setNextDbId, verboseLog } from "./registry/shared-state.js";
|
|
9
|
-
import { defaultConflictColumns, resolveTenantColumn } from "./schema/conflict-target.js";
|
|
10
11
|
import { clearRegistryDiagnostics, flushRegistryDiagnostics, getRegistryDiagnostics, recordRegistryDiagnostic } from "./registry/diagnostics.js";
|
|
11
12
|
import { cloneManifestSchemaColumns, discoverCachedManifestSync, getManifestCache, getNodeBuiltins, loadExternalManifestSyncWithNode } from "./manifest/store.js";
|
|
12
13
|
import { getDefaultCompositeSource } from "./manifest/sources/composite.js";
|
|
@@ -99,11 +100,17 @@ function applyManifestFieldColumnMetadata(columns, fieldEntries) {
|
|
|
99
100
|
const columnName = toSnakeCase(fieldName);
|
|
100
101
|
const existingColumn = columns[columnName];
|
|
101
102
|
if (!existingColumn) continue;
|
|
102
|
-
|
|
103
|
+
const column = {
|
|
103
104
|
...existingColumn,
|
|
104
105
|
...sqlType ? { type: String(sqlType).toUpperCase() } : {},
|
|
105
106
|
...referenceKind ? { referenceKind } : {}
|
|
106
107
|
};
|
|
108
|
+
if (String(column.type).toUpperCase() === "UUID") {
|
|
109
|
+
const loose = column;
|
|
110
|
+
if (loose.default === "") delete loose.default;
|
|
111
|
+
if (loose.defaultValue === "") delete loose.defaultValue;
|
|
112
|
+
}
|
|
113
|
+
columns[columnName] = column;
|
|
107
114
|
}
|
|
108
115
|
}
|
|
109
116
|
async function discoverInstalledSmrtPackages() {
|
|
@@ -143,6 +150,22 @@ function resolveManifestByUpwardSearch(builtins, missingPath) {
|
|
|
143
150
|
return null;
|
|
144
151
|
}
|
|
145
152
|
/**
|
|
153
|
+
* The absolute filesystem path behind a Vite dev-server URL
|
|
154
|
+
* (`http(s)://host/@fs/<absolute path>`), or `null` for any other string.
|
|
155
|
+
*/
|
|
156
|
+
function viteFsUrlToPath(urlString) {
|
|
157
|
+
if (!/^https?:\/\//i.test(urlString)) return null;
|
|
158
|
+
let pathname;
|
|
159
|
+
try {
|
|
160
|
+
pathname = new URL(urlString).pathname;
|
|
161
|
+
} catch {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
if (!pathname.startsWith("/@fs/")) return null;
|
|
165
|
+
const decoded = decodeURIComponent(pathname.slice(4));
|
|
166
|
+
return /^\/[A-Za-z]:\//.test(decoded) ? decoded.slice(1) : decoded;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
146
169
|
* Central registry for all SMRT objects
|
|
147
170
|
*
|
|
148
171
|
* Uses globalThis for cross-module state sharing, ensuring all module instances
|
|
@@ -1069,8 +1092,10 @@ var ObjectRegistry = class ObjectRegistry {
|
|
|
1069
1092
|
let manifest = null;
|
|
1070
1093
|
try {
|
|
1071
1094
|
const urlString = manifestUrl instanceof URL ? manifestUrl.href : manifestUrl;
|
|
1095
|
+
const devServerPath = viteFsUrlToPath(urlString);
|
|
1072
1096
|
let filePath;
|
|
1073
|
-
if (
|
|
1097
|
+
if (devServerPath) filePath = devServerPath;
|
|
1098
|
+
else if (manifestUrl instanceof URL) filePath = builtins.url.fileURLToPath(manifestUrl);
|
|
1074
1099
|
else if (urlString.startsWith("file:")) filePath = builtins.url.fileURLToPath(urlString);
|
|
1075
1100
|
else filePath = urlString;
|
|
1076
1101
|
if (!builtins.fs.existsSync(filePath)) {
|
|
@@ -2004,10 +2029,11 @@ var ObjectRegistry = class ObjectRegistry {
|
|
|
2004
2029
|
* primary-key column(s) — the only unique key such a table has;
|
|
2005
2030
|
* 4. the strategy default — CTI `['slug', 'context']`, STI
|
|
2006
2031
|
* `['slug', 'context', '_meta_type']` — led by the tenant column when
|
|
2007
|
-
* the schema owner is tenant-scoped (#2360)
|
|
2008
|
-
*
|
|
2009
|
-
*
|
|
2010
|
-
*
|
|
2032
|
+
* the schema owner is tenant-scoped (#2360) or carries an undeclared
|
|
2033
|
+
* `tenantId` field ({@link ObjectRegistry.getOwnershipTenantColumn}):
|
|
2034
|
+
* tenant-owned rows are unique per tenant, and without the tenant column
|
|
2035
|
+
* a second tenant's `save()` of the same natural key updated the first
|
|
2036
|
+
* tenant's row through `DO UPDATE SET`.
|
|
2011
2037
|
*
|
|
2012
2038
|
* STI subclasses share a table, so the discriminator participates in
|
|
2013
2039
|
* identity — two subtypes can coexist with the same (slug, context). The
|
|
@@ -2053,7 +2079,92 @@ var ObjectRegistry = class ObjectRegistry {
|
|
|
2053
2079
|
}
|
|
2054
2080
|
const primaryKeyColumns = Array.from(owner.fields.entries()).filter(([, field]) => field?._meta?.primaryKey === true).map(([fieldName]) => toSnakeCase(fieldName));
|
|
2055
2081
|
if (primaryKeyColumns.length > 0) return primaryKeyColumns;
|
|
2056
|
-
return defaultConflictColumns(tableStrategy, ObjectRegistry.
|
|
2082
|
+
return defaultConflictColumns(tableStrategy, ObjectRegistry.getOwnershipTenantColumn(className));
|
|
2083
|
+
}
|
|
2084
|
+
/**
|
|
2085
|
+
* The column that owns a table's rows for natural-key identity: the
|
|
2086
|
+
* declared tenant column ({@link ObjectRegistry.getTenantColumn}), else
|
|
2087
|
+
* `tenant_id` when the schema owner has a `tenantId` field but declares no
|
|
2088
|
+
* tenancy — the shape a consumer registers with the tenancy interceptor at
|
|
2089
|
+
* runtime. The default conflict target and unique index lead with it, so a
|
|
2090
|
+
* second tenant's same-slug row is a second row rather than an upsert onto
|
|
2091
|
+
* the first tenant's. See `resolveOwnershipTenantColumn()`.
|
|
2092
|
+
*
|
|
2093
|
+
* @param className - Name of the class (simple or qualified)
|
|
2094
|
+
* @returns The snake_case ownership column, or `undefined`
|
|
2095
|
+
*/
|
|
2096
|
+
static getOwnershipTenantColumn(className) {
|
|
2097
|
+
const declared = ObjectRegistry.getTenantColumn(className);
|
|
2098
|
+
const registered = ObjectRegistry.findClass(className);
|
|
2099
|
+
if (!registered) return declared;
|
|
2100
|
+
const ownerName = ObjectRegistry.getTableStrategy(className) === "sti" ? ObjectRegistry.getSTIBase(className) : null;
|
|
2101
|
+
const owner = ownerName && ownerName !== className ? ObjectRegistry.findClass(ownerName) ?? registered : registered;
|
|
2102
|
+
return resolveOwnershipTenantColumn(declared, (columnName) => [...owner.fields.entries()].some(([fieldName, field]) => field?.type !== "oneToMany" && field?.type !== "manyToMany" && toSnakeCase(fieldName) === columnName));
|
|
2103
|
+
}
|
|
2104
|
+
/**
|
|
2105
|
+
* Every typed reference (`@foreignKey` / `@crossPackageRef`) that points at
|
|
2106
|
+
* `className`, resolved the same way `SmrtObject.delete()` resolves them for
|
|
2107
|
+
* cascades: the referencing class, its table and column, and the resolved
|
|
2108
|
+
* `onDelete` action. Use it to re-point references before replacing one row
|
|
2109
|
+
* with another (a tag merge) instead of letting a delete cascade them away.
|
|
2110
|
+
*
|
|
2111
|
+
* @param className - Name of the referenced class (simple or qualified)
|
|
2112
|
+
*/
|
|
2113
|
+
static getIncomingReferences(className) {
|
|
2114
|
+
return buildCascadePlan(ObjectRegistry, className).references.map((reference) => ({
|
|
2115
|
+
className: reference.className,
|
|
2116
|
+
tableName: reference.tableName,
|
|
2117
|
+
column: reference.column,
|
|
2118
|
+
onDelete: reference.action
|
|
2119
|
+
}));
|
|
2120
|
+
}
|
|
2121
|
+
/**
|
|
2122
|
+
* Runtime ownership-column sources (see
|
|
2123
|
+
* {@link ObjectRegistry.registerOwnershipColumnSource}).
|
|
2124
|
+
*/
|
|
2125
|
+
static ownershipColumnSources = /* @__PURE__ */ new Set();
|
|
2126
|
+
/**
|
|
2127
|
+
* Register a source of RUNTIME ownership columns: columns that record a
|
|
2128
|
+
* row's owner for a class whose manifest does not say so — the tenancy
|
|
2129
|
+
* package's `registerTenantScopedClass('Team', { field: 'organizationId' })`
|
|
2130
|
+
* is the canonical producer.
|
|
2131
|
+
*
|
|
2132
|
+
* Core reads these only to protect ownership on save
|
|
2133
|
+
* ({@link ObjectRegistry.getOwnershipColumns}); they never change the
|
|
2134
|
+
* conflict target or the schema, which are build-time contracts a runtime
|
|
2135
|
+
* registration cannot alter without breaking `ON CONFLICT` binding. The
|
|
2136
|
+
* source is consulted on every lookup, so registrations that bind later (a
|
|
2137
|
+
* selector registered before its class) or are removed take effect
|
|
2138
|
+
* immediately.
|
|
2139
|
+
*
|
|
2140
|
+
* @param source - maps a qualified class name to its runtime ownership
|
|
2141
|
+
* columns (snake_case), or `undefined` when it has none
|
|
2142
|
+
* @returns a disposer that removes the source
|
|
2143
|
+
*/
|
|
2144
|
+
static registerOwnershipColumnSource(source) {
|
|
2145
|
+
ObjectRegistry.ownershipColumnSources.add(source);
|
|
2146
|
+
return () => {
|
|
2147
|
+
ObjectRegistry.ownershipColumnSources.delete(source);
|
|
2148
|
+
};
|
|
2149
|
+
}
|
|
2150
|
+
/**
|
|
2151
|
+
* Every column that records a row's owner for natural-key save protection:
|
|
2152
|
+
* `tenant_id`, the declared tenant column, each `@tenantId`-marked field's
|
|
2153
|
+
* column, and every runtime ownership column a registered source reports
|
|
2154
|
+
* ({@link ObjectRegistry.registerOwnershipColumnSource}) — so a runtime
|
|
2155
|
+
* tenancy registration on a custom field (`organizationId`) is compared like
|
|
2156
|
+
* `tenant_id`.
|
|
2157
|
+
*
|
|
2158
|
+
* @param className - Name of the class (simple or qualified)
|
|
2159
|
+
*/
|
|
2160
|
+
static getOwnershipColumns(className) {
|
|
2161
|
+
const columns = /* @__PURE__ */ new Set(["tenant_id"]);
|
|
2162
|
+
const declared = ObjectRegistry.getTenantColumn(className);
|
|
2163
|
+
if (declared) columns.add(declared);
|
|
2164
|
+
for (const [name, field] of ObjectRegistry.getFields(className)) if (field.__tenancy?.isTenantIdField || field._meta?.__tenancy?.isTenantIdField) columns.add(toSnakeCase(name));
|
|
2165
|
+
const qualifiedName = ObjectRegistry.findClass(className)?.qualifiedName ?? className;
|
|
2166
|
+
for (const source of ObjectRegistry.ownershipColumnSources) for (const column of source(qualifiedName) ?? []) if (column) columns.add(column);
|
|
2167
|
+
return columns;
|
|
2057
2168
|
}
|
|
2058
2169
|
/**
|
|
2059
2170
|
* The tenant column of the table a class writes to, or `undefined` when its
|