velocious 1.0.592 → 1.0.594
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 +11 -2
- package/build/configuration-types.js +1 -1
- package/build/configuration.js +68 -13
- package/build/database/drivers/base.js +83 -34
- package/build/database/drivers/mssql/index.js +32 -0
- package/build/database/drivers/mysql/index.js +17 -0
- package/build/database/drivers/pgsql/index.js +11 -0
- package/build/database/drivers/sqlite/base.js +12 -0
- package/build/database/drivers/sqlite/connection-sql-js.js +3 -2
- package/build/database/drivers/sqlite/index.js +7 -1
- package/build/database/drivers/sqlite/index.native.js +9 -3
- package/build/database/drivers/sqlite/index.web.js +6 -3
- package/build/database/migrator.js +90 -24
- package/build/database/operation.js +55 -4
- package/build/database/query/join-object.js +7 -4
- package/build/database/query/model-class-query.js +38 -7
- package/build/database/query/preloader/belongs-to.js +9 -6
- package/build/database/query/preloader/has-many.js +25 -14
- package/build/database/query/preloader/has-one.js +7 -4
- package/build/database/query/preloader/query-for-model.js +16 -1
- package/build/database/query/preloader.js +6 -4
- package/build/database/query/query-data.js +3 -3
- package/build/database/query/where-model-class-hash.js +8 -4
- package/build/database/query/with-count.js +4 -3
- package/build/database/record/index.js +148 -5
- package/build/database/record/instance-relationships/base.js +13 -0
- package/build/database/record/instance-relationships/belongs-to.js +1 -1
- package/build/database/record/instance-relationships/has-many.js +5 -2
- package/build/database/record/instance-relationships/has-one.js +1 -1
- package/build/database/record/relationships/base.js +17 -0
- package/build/database/use-database.js +81 -28
- package/build/environment-handlers/base.js +18 -0
- package/build/environment-handlers/node.js +32 -0
- package/build/src/configuration-types.d.ts +2 -2
- package/build/src/configuration-types.js +2 -2
- package/build/src/configuration.d.ts +13 -4
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +56 -16
- package/build/src/database/drivers/base.d.ts +22 -0
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +81 -37
- package/build/src/database/drivers/mssql/index.d.ts +7 -0
- package/build/src/database/drivers/mssql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/index.js +15 -1
- package/build/src/database/drivers/mysql/index.d.ts +7 -0
- package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/index.js +15 -1
- package/build/src/database/drivers/pgsql/index.d.ts +6 -0
- package/build/src/database/drivers/pgsql/index.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/index.js +10 -1
- package/build/src/database/drivers/sqlite/base.d.ts +7 -0
- package/build/src/database/drivers/sqlite/base.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/base.js +11 -1
- package/build/src/database/drivers/sqlite/connection-sql-js.d.ts +4 -1
- package/build/src/database/drivers/sqlite/connection-sql-js.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/connection-sql-js.js +4 -3
- package/build/src/database/drivers/sqlite/index.d.ts +2 -1
- package/build/src/database/drivers/sqlite/index.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/index.js +7 -2
- package/build/src/database/drivers/sqlite/index.native.d.ts +2 -1
- package/build/src/database/drivers/sqlite/index.native.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/index.native.js +9 -4
- package/build/src/database/drivers/sqlite/index.web.d.ts +2 -1
- package/build/src/database/drivers/sqlite/index.web.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/index.web.js +7 -4
- package/build/src/database/migrator.d.ts +34 -0
- package/build/src/database/migrator.d.ts.map +1 -1
- package/build/src/database/migrator.js +83 -23
- package/build/src/database/operation.d.ts +15 -1
- package/build/src/database/operation.d.ts.map +1 -1
- package/build/src/database/operation.js +52 -5
- package/build/src/database/query/join-object.d.ts.map +1 -1
- package/build/src/database/query/join-object.js +7 -5
- package/build/src/database/query/model-class-query.d.ts +6 -0
- package/build/src/database/query/model-class-query.d.ts.map +1 -1
- package/build/src/database/query/model-class-query.js +33 -8
- package/build/src/database/query/preloader/belongs-to.d.ts.map +1 -1
- package/build/src/database/query/preloader/belongs-to.js +9 -7
- package/build/src/database/query/preloader/has-many.d.ts.map +1 -1
- package/build/src/database/query/preloader/has-many.js +23 -16
- package/build/src/database/query/preloader/has-one.d.ts.map +1 -1
- package/build/src/database/query/preloader/has-one.js +7 -5
- package/build/src/database/query/preloader/query-for-model.d.ts +8 -0
- package/build/src/database/query/preloader/query-for-model.d.ts.map +1 -1
- package/build/src/database/query/preloader/query-for-model.js +15 -2
- package/build/src/database/query/preloader.d.ts.map +1 -1
- package/build/src/database/query/preloader.js +6 -5
- package/build/src/database/query/query-data.js +4 -4
- package/build/src/database/query/where-model-class-hash.d.ts.map +1 -1
- package/build/src/database/query/where-model-class-hash.js +7 -5
- package/build/src/database/query/with-count.js +5 -4
- package/build/src/database/record/index.d.ts +95 -0
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +126 -6
- package/build/src/database/record/instance-relationships/base.d.ts +6 -0
- package/build/src/database/record/instance-relationships/base.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/base.js +12 -1
- package/build/src/database/record/instance-relationships/belongs-to.js +2 -2
- package/build/src/database/record/instance-relationships/has-many.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/has-many.js +6 -3
- package/build/src/database/record/instance-relationships/has-one.js +2 -2
- package/build/src/database/record/relationships/base.d.ts +11 -0
- package/build/src/database/record/relationships/base.d.ts.map +1 -1
- package/build/src/database/record/relationships/base.js +15 -1
- package/build/src/database/use-database.d.ts +33 -5
- package/build/src/database/use-database.d.ts.map +1 -1
- package/build/src/database/use-database.js +81 -30
- package/build/src/environment-handlers/base.d.ts +16 -0
- package/build/src/environment-handlers/base.d.ts.map +1 -1
- package/build/src/environment-handlers/base.js +17 -1
- package/build/src/environment-handlers/node.d.ts +20 -0
- package/build/src/environment-handlers/node.d.ts.map +1 -1
- package/build/src/environment-handlers/node.js +29 -1
- package/build/src/sync/server-change-feed.d.ts +14 -2
- package/build/src/sync/server-change-feed.d.ts.map +1 -1
- package/build/src/sync/server-change-feed.js +50 -10
- package/build/src/sync/sync-scope-store.d.ts +13 -2
- package/build/src/sync/sync-scope-store.d.ts.map +1 -1
- package/build/src/sync/sync-scope-store.js +48 -7
- package/build/src/tenants/frontend-tenant-sqlite-lifecycle.d.ts +43 -1
- package/build/src/tenants/frontend-tenant-sqlite-lifecycle.d.ts.map +1 -1
- package/build/src/tenants/frontend-tenant-sqlite-lifecycle.js +141 -17
- package/build/src/tenants/tenant-handle.d.ts +36 -0
- package/build/src/tenants/tenant-handle.d.ts.map +1 -1
- package/build/src/tenants/tenant-handle.js +73 -3
- package/build/src/testing/browser-test-app.js +3 -1
- package/build/src/testing/browser-use-database-hook-scenarios.d.ts +16 -0
- package/build/src/testing/browser-use-database-hook-scenarios.d.ts.map +1 -0
- package/build/src/testing/browser-use-database-hook-scenarios.js +182 -0
- package/build/src/testing/shared-transaction-broker.d.ts +14 -0
- package/build/src/testing/shared-transaction-broker.d.ts.map +1 -1
- package/build/src/testing/shared-transaction-broker.js +44 -7
- package/build/src/testing/shared-transaction-connection-coordinator.d.ts +5 -4
- package/build/src/testing/shared-transaction-connection-coordinator.d.ts.map +1 -1
- package/build/src/testing/shared-transaction-connection-coordinator.js +21 -10
- package/build/src/utils/model-scope.d.ts +2 -2
- package/build/src/utils/model-scope.d.ts.map +1 -1
- package/build/src/utils/model-scope.js +5 -3
- package/build/src/utils/ransack.d.ts.map +1 -1
- package/build/src/utils/ransack.js +5 -3
- package/build/sync/server-change-feed.js +58 -8
- package/build/sync/sync-scope-store.js +55 -5
- package/build/tenants/frontend-tenant-sqlite-lifecycle.js +150 -15
- package/build/tenants/tenant-handle.js +90 -6
- package/build/testing/browser-test-app.js +2 -0
- package/build/testing/browser-use-database-hook-scenarios.js +204 -0
- package/build/testing/shared-transaction-broker.js +48 -7
- package/build/testing/shared-transaction-connection-coordinator.js +23 -9
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/utils/model-scope.js +5 -2
- package/build/utils/ransack.js +5 -2
- package/package.json +1 -1
- package/scripts/browser-test-session.js +489 -0
- package/scripts/prewarm-chromedriver.js +3 -22
- package/scripts/test-browser.js +52 -16
- package/src/configuration-types.js +1 -1
- package/src/configuration.js +68 -13
- package/src/database/drivers/base.js +83 -34
- package/src/database/drivers/mssql/index.js +32 -0
- package/src/database/drivers/mysql/index.js +17 -0
- package/src/database/drivers/pgsql/index.js +11 -0
- package/src/database/drivers/sqlite/base.js +12 -0
- package/src/database/drivers/sqlite/connection-sql-js.js +3 -2
- package/src/database/drivers/sqlite/index.js +7 -1
- package/src/database/drivers/sqlite/index.native.js +9 -3
- package/src/database/drivers/sqlite/index.web.js +6 -3
- package/src/database/migrator.js +90 -24
- package/src/database/operation.js +55 -4
- package/src/database/query/join-object.js +7 -4
- package/src/database/query/model-class-query.js +38 -7
- package/src/database/query/preloader/belongs-to.js +9 -6
- package/src/database/query/preloader/has-many.js +25 -14
- package/src/database/query/preloader/has-one.js +7 -4
- package/src/database/query/preloader/query-for-model.js +16 -1
- package/src/database/query/preloader.js +6 -4
- package/src/database/query/query-data.js +3 -3
- package/src/database/query/where-model-class-hash.js +8 -4
- package/src/database/query/with-count.js +4 -3
- package/src/database/record/index.js +148 -5
- package/src/database/record/instance-relationships/base.js +13 -0
- package/src/database/record/instance-relationships/belongs-to.js +1 -1
- package/src/database/record/instance-relationships/has-many.js +5 -2
- package/src/database/record/instance-relationships/has-one.js +1 -1
- package/src/database/record/relationships/base.js +17 -0
- package/src/database/use-database.js +81 -28
- package/src/environment-handlers/base.js +18 -0
- package/src/environment-handlers/node.js +32 -0
- package/src/sync/server-change-feed.js +58 -8
- package/src/sync/sync-scope-store.js +55 -5
- package/src/tenants/frontend-tenant-sqlite-lifecycle.js +150 -15
- package/src/tenants/tenant-handle.js +90 -6
- package/src/testing/browser-test-app.js +2 -0
- package/src/testing/browser-use-database-hook-scenarios.js +204 -0
- package/src/testing/shared-transaction-broker.js +48 -7
- package/src/testing/shared-transaction-connection-coordinator.js +23 -9
- package/src/utils/model-scope.js +5 -2
- package/src/utils/ransack.js +5 -2
|
@@ -145,18 +145,75 @@ export default class VelociousDatabaseMigrator {
|
|
|
145
145
|
* @returns {Promise<void>} - Resolves when complete.
|
|
146
146
|
*/
|
|
147
147
|
async migrateFilesFromRequireContext(requireContext) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
const files = this.migrationsFromRequireContext(requireContext)
|
|
149
|
+
|
|
150
|
+
await this.configuration.ensureConnections({databaseIdentifiers: this.databaseIdentifiers, name: "Database migrator: migrate require-context files"}, async () => {
|
|
151
|
+
for (const migration of files) {
|
|
152
|
+
await this.runMigrationFile({
|
|
153
|
+
migration,
|
|
154
|
+
requireMigration: async () => requireContext(migration.file).default
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
await this._afterMigrations()
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Migrates exactly one already-captured physical database. This is the
|
|
164
|
+
* frontend/tenant counterpart to the ambient multi-database entrypoints:
|
|
165
|
+
* callers own the captured connection and no configuration fallback is read.
|
|
166
|
+
* @param {object} args - Captured migration arguments.
|
|
167
|
+
* @param {import("../configuration-types.js").DatabaseConfigurationType} args.databaseConfiguration - Captured physical database configuration.
|
|
168
|
+
* @param {string} args.databaseIdentifier - Logical database identifier.
|
|
169
|
+
* @param {import("./drivers/base.js").default} args.db - Captured physical connection.
|
|
170
|
+
* @param {import("./migrator/types.js").RequireMigrationContextType} args.requireContext - Frontend migration require context.
|
|
171
|
+
* @returns {Promise<number>} - Number of newly applied migrations.
|
|
172
|
+
*/
|
|
173
|
+
async migrateRequireContextForDatabase({databaseConfiguration, databaseIdentifier, db, requireContext}) {
|
|
174
|
+
if (!databaseConfiguration.migrations) return 0
|
|
175
|
+
|
|
176
|
+
await MigrationsLedger.ensureTable(db)
|
|
177
|
+
const appliedVersions = new Set(await MigrationsLedger.appliedVersions(db))
|
|
178
|
+
const migrations = this.migrationsFromRequireContext(requireContext)
|
|
179
|
+
let appliedCount = 0
|
|
180
|
+
|
|
181
|
+
for (const migration of migrations) {
|
|
182
|
+
const version = `${migration.date}`
|
|
183
|
+
|
|
184
|
+
if (appliedVersions.has(version)) continue
|
|
185
|
+
|
|
186
|
+
const MigrationClass = requireContext(migration.file).default
|
|
187
|
+
|
|
188
|
+
if (!MigrationClass || typeof MigrationClass !== "function") {
|
|
189
|
+
throw new Error(`Migration ${migration.file} must export a default migration class. Type: ${typeof MigrationClass}`)
|
|
190
|
+
}
|
|
191
|
+
if (!(MigrationClass.getDatabaseIdentifiers() || ["default"]).includes(databaseIdentifier)) continue
|
|
192
|
+
|
|
193
|
+
const migrationInstance = new MigrationClass({configuration: this.configuration, databaseIdentifier, db})
|
|
194
|
+
|
|
195
|
+
await this.runMigrationUp({migration, migrationInstance})
|
|
196
|
+
await MigrationsLedger.recordVersion(db, version)
|
|
197
|
+
appliedVersions.add(version)
|
|
198
|
+
appliedCount++
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return appliedCount
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Parses and orders migrations from a browser/native require context.
|
|
206
|
+
* @param {import("./migrator/types.js").RequireMigrationContextType} requireContext - Migration require context.
|
|
207
|
+
* @returns {import("./migrator/types.js").MigrationObjectType[]} - Ordered migrations.
|
|
208
|
+
*/
|
|
209
|
+
migrationsFromRequireContext(requireContext) {
|
|
210
|
+
const migrations = []
|
|
152
211
|
|
|
153
212
|
for (const file of requireContext.keys()) {
|
|
154
|
-
// "13,14" because somes "require-context"-npm-module deletes first character!?
|
|
155
213
|
const match = file.match(/(\d{13,14})-(.+)\.js$/)
|
|
156
214
|
|
|
157
215
|
if (!match) continue
|
|
158
216
|
|
|
159
|
-
// Fix require-context-npm-module deletes first character
|
|
160
217
|
let fileName = file
|
|
161
218
|
let dateNumber = match[1]
|
|
162
219
|
|
|
@@ -165,30 +222,39 @@ export default class VelociousDatabaseMigrator {
|
|
|
165
222
|
fileName = `2${fileName}`
|
|
166
223
|
}
|
|
167
224
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const migrationName = match[2]
|
|
171
|
-
const migrationClassName = inflection.camelize(migrationName.replaceAll("-", "_"))
|
|
172
|
-
|
|
173
|
-
files.push({
|
|
225
|
+
migrations.push({
|
|
226
|
+
date: parseInt(dateNumber),
|
|
174
227
|
file: fileName,
|
|
175
|
-
|
|
176
|
-
migrationClassName
|
|
228
|
+
migrationClassName: inflection.camelize(match[2].replaceAll("-", "_"))
|
|
177
229
|
})
|
|
178
230
|
}
|
|
179
231
|
|
|
180
|
-
|
|
232
|
+
return migrations.sort((migration1, migration2) => migration1.date - migration2.date)
|
|
233
|
+
}
|
|
181
234
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
235
|
+
/**
|
|
236
|
+
* Runs one migration's upward implementation.
|
|
237
|
+
* @param {object} args - Migration arguments.
|
|
238
|
+
* @param {import("./migrator/types.js").MigrationObjectType} args.migration - Migration descriptor.
|
|
239
|
+
* @param {import("./migration/index.js").default} args.migrationInstance - Migration instance.
|
|
240
|
+
* @returns {Promise<void>} - Resolves after the migration succeeds.
|
|
241
|
+
*/
|
|
242
|
+
async runMigrationUp({migration, migrationInstance}) {
|
|
243
|
+
try {
|
|
244
|
+
await migrationInstance.change()
|
|
245
|
+
} catch (changeError) {
|
|
246
|
+
if (!(changeError instanceof NotImplementedError)) throw changeError
|
|
247
|
+
|
|
248
|
+
try {
|
|
249
|
+
await migrationInstance.up()
|
|
250
|
+
} catch (upError) {
|
|
251
|
+
if (upError instanceof NotImplementedError) {
|
|
252
|
+
throw new Error(`'change' or 'up' didn't exist on migration: ${migration.file}`, {cause: upError})
|
|
253
|
+
}
|
|
189
254
|
|
|
190
|
-
|
|
191
|
-
|
|
255
|
+
throw upError
|
|
256
|
+
}
|
|
257
|
+
}
|
|
192
258
|
}
|
|
193
259
|
|
|
194
260
|
/**
|
|
@@ -17,9 +17,10 @@ export default class VelociousDatabaseOperation {
|
|
|
17
17
|
* @param {import("./drivers/base.js").default} args.connection - Pinned physical connection.
|
|
18
18
|
* @param {string} args.databaseIdentifier - Singular database identifier.
|
|
19
19
|
* @param {symbol} args.owner - Opaque pool lease owner.
|
|
20
|
+
* @param {string} [args.schemaGeneration] - Tenant schema generation owning record metadata.
|
|
20
21
|
* @param {object | undefined} args.tenant - Tenant descriptor captured by the owning handle.
|
|
21
22
|
*/
|
|
22
|
-
constructor({configuration, databaseConfiguration, configurationReuseKey, connection, databaseIdentifier, enforceCurrentTenantReuseKey = true, owner, tenant}) {
|
|
23
|
+
constructor({configuration, databaseConfiguration, configurationReuseKey, connection, databaseIdentifier, enforceCurrentTenantReuseKey = true, owner, schemaGeneration, tenant}) {
|
|
23
24
|
this._active = true
|
|
24
25
|
this._configuration = configuration
|
|
25
26
|
this._databaseConfiguration = databaseConfiguration || configuration.resolveDatabaseConfiguration(databaseIdentifier, tenant)
|
|
@@ -27,7 +28,10 @@ export default class VelociousDatabaseOperation {
|
|
|
27
28
|
this._databaseIdentifier = databaseIdentifier
|
|
28
29
|
this._enforceCurrentTenantReuseKey = enforceCurrentTenantReuseKey
|
|
29
30
|
this._physicalConnection = connection
|
|
31
|
+
this._schemaGeneration = schemaGeneration
|
|
30
32
|
this._tenant = tenant
|
|
33
|
+
/** @type {WeakMap<typeof import("./record/index.js").default, typeof import("./record/index.js").default>} */
|
|
34
|
+
this._boundModelClasses = new WeakMap()
|
|
31
35
|
this._connection = new OperationConnection({
|
|
32
36
|
connection,
|
|
33
37
|
operation: this,
|
|
@@ -45,12 +49,57 @@ export default class VelociousDatabaseOperation {
|
|
|
45
49
|
this.assertActive()
|
|
46
50
|
this.assertModel(ModelClass)
|
|
47
51
|
|
|
48
|
-
return ModelClass._newQuery({
|
|
52
|
+
return this.modelClass(ModelClass)._newQuery({
|
|
49
53
|
driver: this.connection(),
|
|
50
54
|
operation: this
|
|
51
55
|
})
|
|
52
56
|
}
|
|
53
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Returns a model-class view whose schema metadata is bound to this physical
|
|
60
|
+
* database generation. Construction still produces the application's original
|
|
61
|
+
* model class, so lifecycle callbacks and model registries retain class identity.
|
|
62
|
+
* @template {typeof import("./record/index.js").default} MC
|
|
63
|
+
* @param {MC} ModelClass - Canonical model class.
|
|
64
|
+
* @returns {MC} - Operation-bound model class.
|
|
65
|
+
*/
|
|
66
|
+
modelClass(ModelClass) {
|
|
67
|
+
if (!this._schemaGeneration) return ModelClass
|
|
68
|
+
|
|
69
|
+
const canonicalModelClass = /** @type {MC} */ (ModelClass._recordMetadataModelClass || ModelClass)
|
|
70
|
+
|
|
71
|
+
const existing = this._boundModelClasses.get(canonicalModelClass)
|
|
72
|
+
|
|
73
|
+
if (existing) return /** @type {MC} */ (existing)
|
|
74
|
+
|
|
75
|
+
const databaseIdentity = this.databaseIdentity()
|
|
76
|
+
const metadataKey = `${databaseIdentity.length}:${databaseIdentity}:${this._schemaGeneration}`
|
|
77
|
+
const metadataProperties = canonicalModelClass.recordMetadataPropertyNames()
|
|
78
|
+
const boundModelClass = new Proxy(canonicalModelClass, {
|
|
79
|
+
construct: (target, args, newTarget) => Reflect.construct(target, args, newTarget),
|
|
80
|
+
get: (target, property, receiver) => {
|
|
81
|
+
if (property === "_recordMetadataModelClass") return target
|
|
82
|
+
if (property === "_recordMetadataBinder") return (/** @type {typeof import("./record/index.js").default} */ targetModelClass) => this.modelClass(targetModelClass)
|
|
83
|
+
if (property === "_recordMetadataOperation") return this
|
|
84
|
+
if (typeof property === "string" && metadataProperties.has(property)) return target.recordMetadataValue(metadataKey, property)
|
|
85
|
+
|
|
86
|
+
return Reflect.get(target, property, receiver)
|
|
87
|
+
},
|
|
88
|
+
set: (target, property, value, receiver) => {
|
|
89
|
+
if (typeof property === "string" && metadataProperties.has(property)) {
|
|
90
|
+
target.setRecordMetadataValue(metadataKey, property, value)
|
|
91
|
+
return true
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return Reflect.set(target, property, value, receiver)
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
this._boundModelClasses.set(canonicalModelClass, boundModelClass)
|
|
99
|
+
|
|
100
|
+
return /** @type {MC} */ (boundModelClass)
|
|
101
|
+
}
|
|
102
|
+
|
|
54
103
|
/**
|
|
55
104
|
* Verifies that a model belongs to this operation's configuration and database.
|
|
56
105
|
* @param {typeof import("./record/index.js").default} ModelClass - Model class to verify.
|
|
@@ -162,9 +211,11 @@ export default class VelociousDatabaseOperation {
|
|
|
162
211
|
async ensureModelInitialized(ModelClass) {
|
|
163
212
|
this.assertActive()
|
|
164
213
|
|
|
165
|
-
|
|
214
|
+
const boundModelClass = this.modelClass(ModelClass)
|
|
215
|
+
|
|
216
|
+
if (boundModelClass.isInitialized()) this.assertModel(ModelClass)
|
|
166
217
|
|
|
167
|
-
await
|
|
218
|
+
await boundModelClass.ensureInitialized({
|
|
168
219
|
configuration: this._configuration,
|
|
169
220
|
connection: this.connection()
|
|
170
221
|
})
|
|
@@ -54,12 +54,15 @@ export default class VelociousDatabaseQueryJoinObject extends JoinBase {
|
|
|
54
54
|
for (const joinKey in join) {
|
|
55
55
|
const joinValue = join[joinKey]
|
|
56
56
|
const relationship = modelClass.getRelationshipByName(joinKey)
|
|
57
|
-
const
|
|
57
|
+
const rawTargetModelClass = relationship.getTargetModelClass()
|
|
58
58
|
|
|
59
|
-
if (!
|
|
59
|
+
if (!rawTargetModelClass) {
|
|
60
60
|
throw new Error(`Relationship ${modelClass.name}#${joinKey} has no target model class`)
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
const targetModelClass = query.bindModelClass(rawTargetModelClass)
|
|
64
|
+
const foreignKey = relationship.getForeignKeyForModelClasses({modelClass, targetModelClass})
|
|
65
|
+
|
|
63
66
|
const joinPath = path.concat([joinKey])
|
|
64
67
|
const parentTableRef = query.getJoinTableReference(path)
|
|
65
68
|
const targetEntry = query._registerJoinPath(joinPath)
|
|
@@ -80,9 +83,9 @@ export default class VelociousDatabaseQueryJoinObject extends JoinBase {
|
|
|
80
83
|
|
|
81
84
|
if (relationship.getType() == "belongsTo") {
|
|
82
85
|
sql += `${conn.quoteTable(targetTableRef)}.${conn.quoteColumn(relationship.getPrimaryKey())} = `
|
|
83
|
-
sql += `${conn.quoteTable(parentTableRef)}.${conn.quoteColumn(
|
|
86
|
+
sql += `${conn.quoteTable(parentTableRef)}.${conn.quoteColumn(foreignKey)}`
|
|
84
87
|
} else if (relationship.getType() == "hasMany" || relationship.getType() == "hasOne") {
|
|
85
|
-
sql += `${conn.quoteTable(targetTableRef)}.${conn.quoteColumn(
|
|
88
|
+
sql += `${conn.quoteTable(targetTableRef)}.${conn.quoteColumn(foreignKey)} = `
|
|
86
89
|
sql += `${conn.quoteTable(parentTableRef)}.${conn.quoteColumn(relationship.getPrimaryKey())}`
|
|
87
90
|
} else {
|
|
88
91
|
throw new Error(`Unknown relationship type: ${relationship.getType()}`)
|
|
@@ -87,6 +87,22 @@ function normalizeScopePath(path) {
|
|
|
87
87
|
return [...path]
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Narrows a shared model-scope descriptor to the backend model class required by ModelClassQuery.
|
|
92
|
+
* @param {import("../../utils/model-scope.js").ModelScopeDescriptor} scopeDescriptor - Shared scope descriptor.
|
|
93
|
+
* @returns {typeof import("../record/index.js").default} - Backend scope owner.
|
|
94
|
+
*/
|
|
95
|
+
function backendScopeModelClass(scopeDescriptor) {
|
|
96
|
+
const modelClass = scopeDescriptor.modelClass
|
|
97
|
+
|
|
98
|
+
if (!("canonicalRecordMetadataModelClass" in modelClass)) {
|
|
99
|
+
throw new Error("A frontend-model scope cannot be applied to a database record query")
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// The runtime member check above narrows the shared frontend/backend descriptor boundary.
|
|
103
|
+
return /** @type {typeof import("../record/index.js").default} */ (modelClass)
|
|
104
|
+
}
|
|
105
|
+
|
|
90
106
|
/**
|
|
91
107
|
* Deep-copies a preload select map (keyed by model name with attribute arrays)
|
|
92
108
|
* so a cloned query's selections can be mutated without affecting the original.
|
|
@@ -490,6 +506,15 @@ export default class VelociousDatabaseQueryModelClassQuery extends DatabaseQuery
|
|
|
490
506
|
return this.modelClass
|
|
491
507
|
}
|
|
492
508
|
|
|
509
|
+
/**
|
|
510
|
+
* Binds a relationship target to this query's physical database generation.
|
|
511
|
+
* @param {typeof import("../record/index.js").default} modelClass - Canonical relationship target.
|
|
512
|
+
* @returns {typeof import("../record/index.js").default} - Query-bound relationship target.
|
|
513
|
+
*/
|
|
514
|
+
bindModelClass(modelClass) {
|
|
515
|
+
return this.getModelClass().bindRecordMetadataModelClass(modelClass)
|
|
516
|
+
}
|
|
517
|
+
|
|
493
518
|
/**
|
|
494
519
|
* Runs get join base path.
|
|
495
520
|
* @returns {string[]} - The join base path.
|
|
@@ -563,7 +588,7 @@ export default class VelociousDatabaseQueryModelClassQuery extends DatabaseQuery
|
|
|
563
588
|
throw new Error(`No target model class for ${modelClass.name}#${relationshipName}`)
|
|
564
589
|
}
|
|
565
590
|
|
|
566
|
-
modelClass = targetModelClass
|
|
591
|
+
modelClass = this.bindModelClass(targetModelClass)
|
|
567
592
|
}
|
|
568
593
|
|
|
569
594
|
return modelClass
|
|
@@ -642,7 +667,9 @@ export default class VelociousDatabaseQueryModelClassQuery extends DatabaseQuery
|
|
|
642
667
|
throw new Error("scope() expects a descriptor returned by defineScope(...).scope(...)")
|
|
643
668
|
}
|
|
644
669
|
|
|
645
|
-
|
|
670
|
+
const scopeModelClass = backendScopeModelClass(scopeDescriptor)
|
|
671
|
+
|
|
672
|
+
if (scopeModelClass.canonicalRecordMetadataModelClass() !== this.getModelClass().canonicalRecordMetadataModelClass()) {
|
|
646
673
|
throw new Error(`Cannot apply ${scopeDescriptor.modelClass.name} scope to ${this.getModelClass().name} query`)
|
|
647
674
|
}
|
|
648
675
|
|
|
@@ -671,7 +698,9 @@ export default class VelociousDatabaseQueryModelClassQuery extends DatabaseQuery
|
|
|
671
698
|
const fullJoinPath = this.getJoinBasePath().concat(joinPath)
|
|
672
699
|
const targetModelClass = this._resolveModelClassForJoinPath(fullJoinPath)
|
|
673
700
|
|
|
674
|
-
|
|
701
|
+
const scopeModelClass = backendScopeModelClass(scopeDescriptor)
|
|
702
|
+
|
|
703
|
+
if (scopeModelClass.canonicalRecordMetadataModelClass() !== targetModelClass.canonicalRecordMetadataModelClass()) {
|
|
675
704
|
throw new Error(`Cannot apply ${scopeDescriptor.modelClass.name} scope to join path ${fullJoinPath.join(".")} (${targetModelClass.name})`)
|
|
676
705
|
}
|
|
677
706
|
|
|
@@ -1418,11 +1447,12 @@ function splitWhereHash({hash, modelClass}) {
|
|
|
1418
1447
|
|
|
1419
1448
|
if (isNested) {
|
|
1420
1449
|
if (relationship) {
|
|
1421
|
-
const
|
|
1422
|
-
if (!
|
|
1450
|
+
const rawTargetModelClass = relationship.getTargetModelClass()
|
|
1451
|
+
if (!rawTargetModelClass) {
|
|
1423
1452
|
fallbackHash[key] = value
|
|
1424
1453
|
continue
|
|
1425
1454
|
}
|
|
1455
|
+
const targetModelClass = modelClass.bindRecordMetadataModelClass(rawTargetModelClass)
|
|
1426
1456
|
const nestedResult = splitWhereHash({hash: value, modelClass: targetModelClass})
|
|
1427
1457
|
const nestedResolvedKeys = Object.keys(nestedResult.resolvedHash)
|
|
1428
1458
|
const nestedFallbackKeys = Object.keys(nestedResult.fallbackHash)
|
|
@@ -1476,8 +1506,9 @@ function buildJoinObjectFromWhereHash({hash, modelClass}) {
|
|
|
1476
1506
|
if (!relationship) continue
|
|
1477
1507
|
|
|
1478
1508
|
if (isPlainObject(value)) {
|
|
1479
|
-
const
|
|
1480
|
-
if (!
|
|
1509
|
+
const rawTargetModelClass = relationship.getTargetModelClass()
|
|
1510
|
+
if (!rawTargetModelClass) continue
|
|
1511
|
+
const targetModelClass = modelClass.bindRecordMetadataModelClass(rawTargetModelClass)
|
|
1481
1512
|
const nestedJoinObject = buildJoinObjectFromWhereHash({hash: value, modelClass: targetModelClass})
|
|
1482
1513
|
|
|
1483
1514
|
joinObject[key] = Object.keys(nestedJoinObject).length > 0 ? nestedJoinObject : true
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import ensureModelClassInitialized from "./ensure-model-class-initialized.js"
|
|
4
4
|
import PreloaderSelection from "./selection.js"
|
|
5
|
-
import preloadQueryForModel from "./query-for-model.js"
|
|
5
|
+
import preloadQueryForModel, { bindPreloadModelClass } from "./query-for-model.js"
|
|
6
6
|
import restArgsError from "../../../utils/rest-args-error.js"
|
|
7
7
|
|
|
8
8
|
export default class VelociousDatabaseQueryPreloaderBelongsTo {
|
|
@@ -22,7 +22,8 @@ export default class VelociousDatabaseQueryPreloaderBelongsTo {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
async run() {
|
|
25
|
-
const
|
|
25
|
+
const sourceModelClass = this.models[0].getModelClass()
|
|
26
|
+
const foreignKey = this.relationship.getForeignKeyForModelClasses({modelClass: sourceModelClass, targetModelClass: sourceModelClass})
|
|
26
27
|
const primaryKey = this.relationship.getPrimaryKey()
|
|
27
28
|
const relationshipName = this.relationship.getRelationshipName()
|
|
28
29
|
|
|
@@ -30,9 +31,11 @@ export default class VelociousDatabaseQueryPreloaderBelongsTo {
|
|
|
30
31
|
return await this._runPolymorphic({foreignKey, primaryKey, relationshipName})
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
const
|
|
34
|
+
const rawTargetModelClass = this.relationship.getTargetModelClass()
|
|
34
35
|
|
|
35
|
-
if (!
|
|
36
|
+
if (!rawTargetModelClass) throw new Error("No target model class could be gotten from relationship")
|
|
37
|
+
|
|
38
|
+
const targetModelClass = bindPreloadModelClass(this.models, rawTargetModelClass)
|
|
36
39
|
|
|
37
40
|
/**
|
|
38
41
|
* Satisfied targets.
|
|
@@ -155,7 +158,7 @@ export default class VelociousDatabaseQueryPreloaderBelongsTo {
|
|
|
155
158
|
for (const model of this.models) {
|
|
156
159
|
const targetType = /** @type {string | undefined} */ (model.readColumn(typeColumn))
|
|
157
160
|
const instanceRelationship = model.getRelationshipByName(relationshipName)
|
|
158
|
-
const targetModelClass = targetType ? configuration.getModelClass(targetType) : undefined
|
|
161
|
+
const targetModelClass = targetType ? bindPreloadModelClass(this.models, configuration.getModelClass(targetType)) : undefined
|
|
159
162
|
|
|
160
163
|
if (targetModelClass && this.selection.isSatisfied({instanceRelationship, targetModelClass, mappingColumns: [primaryKey]})) {
|
|
161
164
|
const loaded = /** @type {import("../../record/index.js").default | undefined} */ (instanceRelationship.getLoadedOrUndefined())
|
|
@@ -203,7 +206,7 @@ export default class VelociousDatabaseQueryPreloaderBelongsTo {
|
|
|
203
206
|
const targetModels = []
|
|
204
207
|
|
|
205
208
|
for (const targetType in foreignKeyValuesByType) {
|
|
206
|
-
const targetModelClass = configuration.getModelClass(targetType)
|
|
209
|
+
const targetModelClass = bindPreloadModelClass(this.models, configuration.getModelClass(targetType))
|
|
207
210
|
|
|
208
211
|
await ensureModelClassInitialized(targetModelClass, configuration, this.models[0])
|
|
209
212
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import ensureModelClassInitialized from "./ensure-model-class-initialized.js"
|
|
4
4
|
import PreloaderSelection from "./selection.js"
|
|
5
|
-
import preloadQueryForModel from "./query-for-model.js"
|
|
5
|
+
import preloadQueryForModel, { bindPreloadModelClass } from "./query-for-model.js"
|
|
6
6
|
import restArgsError from "../../../utils/rest-args-error.js"
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -18,19 +18,23 @@ export function hasManyThroughTargetForeignKey(relationship, throughModelClass,
|
|
|
18
18
|
// (e.g. a default plus an alternate), so picking the first match would otherwise be ambiguous.
|
|
19
19
|
const explicitForeignKey = relationship.getExplicitForeignKey()
|
|
20
20
|
|
|
21
|
-
if (explicitForeignKey)
|
|
21
|
+
if (explicitForeignKey) {
|
|
22
|
+
return targetModelClass.getAttributeNameToColumnNameMap()[explicitForeignKey] || explicitForeignKey
|
|
23
|
+
}
|
|
22
24
|
|
|
23
25
|
for (const targetRelationship of targetModelClass.getRelationships()) {
|
|
24
26
|
if (targetRelationship.getType() != "belongsTo") continue
|
|
25
27
|
|
|
26
28
|
const relationshipTargetModelClass = targetRelationship.getTargetModelClass()
|
|
27
29
|
|
|
28
|
-
if (relationshipTargetModelClass
|
|
29
|
-
|
|
30
|
+
if (!relationshipTargetModelClass) continue
|
|
31
|
+
|
|
32
|
+
if (relationshipTargetModelClass.canonicalRecordMetadataModelClass() === throughModelClass.canonicalRecordMetadataModelClass()) {
|
|
33
|
+
return targetRelationship.getForeignKeyForModelClasses({modelClass: targetModelClass, targetModelClass: throughModelClass})
|
|
30
34
|
}
|
|
31
35
|
}
|
|
32
36
|
|
|
33
|
-
return relationship.
|
|
37
|
+
return relationship.getForeignKeyForModelClasses({modelClass: throughModelClass, targetModelClass})
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
export default class VelociousDatabaseQueryPreloaderHasMany {
|
|
@@ -107,15 +111,19 @@ export default class VelociousDatabaseQueryPreloaderHasMany {
|
|
|
107
111
|
}
|
|
108
112
|
|
|
109
113
|
const throughRelationshipName = /** @type {string} */ (this.relationship.through)
|
|
110
|
-
const parentModelClass = this.
|
|
114
|
+
const parentModelClass = this.models[0].getModelClass()
|
|
111
115
|
const throughRelationship = parentModelClass.getRelationshipByName(throughRelationshipName)
|
|
112
|
-
const
|
|
116
|
+
const rawThroughModelClass = throughRelationship.getTargetModelClass()
|
|
117
|
+
|
|
118
|
+
if (!rawThroughModelClass) throw new Error(`Through relationship ${throughRelationshipName} has no target model class`)
|
|
113
119
|
|
|
114
|
-
|
|
120
|
+
const throughModelClass = bindPreloadModelClass(this.models, rawThroughModelClass)
|
|
115
121
|
|
|
116
|
-
const
|
|
122
|
+
const rawTargetModelClass = this.relationship.getTargetModelClass()
|
|
117
123
|
|
|
118
|
-
if (!
|
|
124
|
+
if (!rawTargetModelClass) throw new Error("No target model class could be gotten from relationship")
|
|
125
|
+
|
|
126
|
+
const targetModelClass = bindPreloadModelClass(this.models, rawTargetModelClass)
|
|
119
127
|
|
|
120
128
|
const targetForeignKey = hasManyThroughTargetForeignKey(this.relationship, throughModelClass, targetModelClass)
|
|
121
129
|
const {modelsToLoad, satisfiedTargets} = this._partition(targetModelClass, [targetForeignKey])
|
|
@@ -127,7 +135,7 @@ export default class VelociousDatabaseQueryPreloaderHasMany {
|
|
|
127
135
|
await ensureModelClassInitialized(throughModelClass, configuration, modelsToLoad[0])
|
|
128
136
|
await ensureModelClassInitialized(targetModelClass, configuration, modelsToLoad[0])
|
|
129
137
|
|
|
130
|
-
const throughForeignKey = throughRelationship.
|
|
138
|
+
const throughForeignKey = throughRelationship.getForeignKeyForModelClasses({modelClass: parentModelClass, targetModelClass: throughModelClass})
|
|
131
139
|
|
|
132
140
|
/**
|
|
133
141
|
* Models primary key values.
|
|
@@ -260,16 +268,19 @@ export default class VelociousDatabaseQueryPreloaderHasMany {
|
|
|
260
268
|
* @returns {Promise<import("../../record/index.js").default[]>} - Loaded target models.
|
|
261
269
|
*/
|
|
262
270
|
async _runDirect() {
|
|
263
|
-
const foreignKey = this.relationship.getForeignKey()
|
|
264
271
|
const primaryKey = this.relationship.getPrimaryKey()
|
|
265
272
|
|
|
266
273
|
if (!primaryKey) {
|
|
267
274
|
throw new Error(`${this.relationship.getModelClass().name}#${this.relationship.getRelationshipName()} doesn't have a primary key`)
|
|
268
275
|
}
|
|
269
276
|
|
|
270
|
-
const
|
|
277
|
+
const rawTargetModelClass = this.relationship.getTargetModelClass()
|
|
278
|
+
|
|
279
|
+
if (!rawTargetModelClass) throw new Error("No target model class could be gotten from relationship")
|
|
271
280
|
|
|
272
|
-
|
|
281
|
+
const sourceModelClass = this.models[0].getModelClass()
|
|
282
|
+
const targetModelClass = bindPreloadModelClass(this.models, rawTargetModelClass)
|
|
283
|
+
const foreignKey = this.relationship.getForeignKeyForModelClasses({modelClass: sourceModelClass, targetModelClass})
|
|
273
284
|
|
|
274
285
|
const {modelsToLoad, satisfiedTargets} = this._partition(targetModelClass, [foreignKey])
|
|
275
286
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import ensureModelClassInitialized from "./ensure-model-class-initialized.js"
|
|
4
4
|
import PreloaderSelection from "./selection.js"
|
|
5
|
-
import preloadQueryForModel from "./query-for-model.js"
|
|
5
|
+
import preloadQueryForModel, { bindPreloadModelClass } from "./query-for-model.js"
|
|
6
6
|
import restArgsError from "../../../utils/rest-args-error.js"
|
|
7
7
|
|
|
8
8
|
export default class VelociousDatabaseQueryPreloaderHasOne {
|
|
@@ -32,13 +32,16 @@ export default class VelociousDatabaseQueryPreloaderHasOne {
|
|
|
32
32
|
* @type {Record<number | string, Array<import("../../record/index.js").default>>} */
|
|
33
33
|
const modelsByPrimaryKeyValue = {}
|
|
34
34
|
|
|
35
|
-
const foreignKey = this.relationship.getForeignKey()
|
|
36
35
|
const primaryKey = this.relationship.getPrimaryKey()
|
|
37
36
|
const relationshipName = this.relationship.getRelationshipName()
|
|
38
37
|
|
|
39
|
-
const
|
|
38
|
+
const rawTargetModelClass = this.relationship.getTargetModelClass()
|
|
40
39
|
|
|
41
|
-
if (!
|
|
40
|
+
if (!rawTargetModelClass) throw new Error("No target model class could be gotten from relationship")
|
|
41
|
+
|
|
42
|
+
const sourceModelClass = this.models[0].getModelClass()
|
|
43
|
+
const targetModelClass = bindPreloadModelClass(this.models, rawTargetModelClass)
|
|
44
|
+
const foreignKey = this.relationship.getForeignKeyForModelClasses({modelClass: sourceModelClass, targetModelClass})
|
|
42
45
|
|
|
43
46
|
/**
|
|
44
47
|
* Preload collections.
|
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Binds a preload target to the source records' physical database generation.
|
|
5
|
+
* @template {typeof import("../../record/index.js").default} MC
|
|
6
|
+
* @param {import("../../record/index.js").default[]} models - Source records.
|
|
7
|
+
* @param {MC} ModelClass - Target model class.
|
|
8
|
+
* @returns {MC} - Generation-bound target model class.
|
|
9
|
+
*/
|
|
10
|
+
export function bindPreloadModelClass(models, ModelClass) {
|
|
11
|
+
const sourceModel = models[0]
|
|
12
|
+
|
|
13
|
+
if (!sourceModel) throw new Error("Cannot bind a preload model class without source records")
|
|
14
|
+
|
|
15
|
+
return /** @type {MC} */ (sourceModel.getModelClass().bindRecordMetadataModelClass(ModelClass))
|
|
16
|
+
}
|
|
17
|
+
|
|
3
18
|
/**
|
|
4
19
|
* Builds a target query preserving the explicit database operation owned by
|
|
5
20
|
* the source records.
|
|
@@ -13,5 +28,5 @@ export default function preloadQueryForModel(models, ModelClass) {
|
|
|
13
28
|
|
|
14
29
|
if (!sourceModel) throw new Error("Cannot build a preload query without source records")
|
|
15
30
|
|
|
16
|
-
return sourceModel.queryForModel(ModelClass)
|
|
31
|
+
return sourceModel.queryForModel(bindPreloadModelClass(models, ModelClass))
|
|
17
32
|
}
|
|
@@ -88,7 +88,7 @@ export default class VelociousDatabaseQueryPreloader {
|
|
|
88
88
|
static async preload(models, queryOrSpec, {force = false} = {}) {
|
|
89
89
|
if (models.length == 0) return
|
|
90
90
|
|
|
91
|
-
const modelClass =
|
|
91
|
+
const modelClass = models[0].getModelClass()
|
|
92
92
|
const isQuery = Boolean(queryOrSpec) && typeof queryOrSpec == "object" && "_preload" in queryOrSpec
|
|
93
93
|
// Reuse the query builder's preload/select normalization for raw specs
|
|
94
94
|
// instead of duplicating it here.
|
|
@@ -176,15 +176,17 @@ export default class VelociousDatabaseQueryPreloader {
|
|
|
176
176
|
|
|
177
177
|
if (models.length == 0) continue
|
|
178
178
|
|
|
179
|
-
const targetModelClass = configuration.getModelClass(className)
|
|
179
|
+
const targetModelClass = this.modelClass.bindRecordMetadataModelClass(configuration.getModelClass(className))
|
|
180
180
|
const preloader = new VelociousDatabaseQueryPreloader({modelClass: targetModelClass, models, preload: normalizedPreload, selection: this.selection})
|
|
181
181
|
|
|
182
182
|
await preloader.run()
|
|
183
183
|
}
|
|
184
184
|
} else {
|
|
185
|
-
const
|
|
185
|
+
const rawTargetModelClass = relationship.getTargetModelClass()
|
|
186
186
|
|
|
187
|
-
if (!
|
|
187
|
+
if (!rawTargetModelClass) throw new Error("No target model class could be gotten from relationship")
|
|
188
|
+
|
|
189
|
+
const targetModelClass = this.modelClass.bindRecordMetadataModelClass(rawTargetModelClass)
|
|
188
190
|
|
|
189
191
|
const preloader = new VelociousDatabaseQueryPreloader({modelClass: targetModelClass, models: targetModels, preload: normalizedPreload, selection: this.selection})
|
|
190
192
|
|
|
@@ -153,13 +153,13 @@ function resolveTargetModelClass(rootModelClass, chain) {
|
|
|
153
153
|
|
|
154
154
|
for (const segment of chain) {
|
|
155
155
|
const relationship = modelClass.getRelationshipByName(segment)
|
|
156
|
-
const
|
|
156
|
+
const rawTarget = relationship.getTargetModelClass()
|
|
157
157
|
|
|
158
|
-
if (!
|
|
158
|
+
if (!rawTarget) {
|
|
159
159
|
throw new Error(`queryData: could not resolve target model for ${modelClass.name}#${segment}`)
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
modelClass =
|
|
162
|
+
modelClass = modelClass.bindRecordMetadataModelClass(rawTarget)
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
return modelClass
|
|
@@ -403,9 +403,11 @@ export default class VelociousDatabaseQueryWhereModelClassHash extends WhereBase
|
|
|
403
403
|
if (relationship && tuples) {
|
|
404
404
|
if (index > 0) sql += " AND "
|
|
405
405
|
|
|
406
|
-
const
|
|
406
|
+
const rawTargetModelClass = relationship.getTargetModelClass()
|
|
407
407
|
|
|
408
|
-
if (!
|
|
408
|
+
if (!rawTargetModelClass) throw new Error(`Relationship "${whereKey}" for ${modelClass.name} has no target model class`)
|
|
409
|
+
|
|
410
|
+
const targetModelClass = modelClass.bindRecordMetadataModelClass(rawTargetModelClass)
|
|
409
411
|
|
|
410
412
|
const nestedPath = path.concat([whereKey])
|
|
411
413
|
const nestedTableName = modelQuery.getTableReferenceForJoin(...nestedPath)
|
|
@@ -431,9 +433,11 @@ export default class VelociousDatabaseQueryWhereModelClassHash extends WhereBase
|
|
|
431
433
|
throw new Error(`Unknown relationship "${whereKey}" for ${modelClass.name}`)
|
|
432
434
|
}
|
|
433
435
|
|
|
434
|
-
const
|
|
436
|
+
const rawTargetModelClass = relationship.getTargetModelClass()
|
|
437
|
+
|
|
438
|
+
if (!rawTargetModelClass) throw new Error(`Relationship "${whereKey}" for ${modelClass.name} has no target model class`)
|
|
435
439
|
|
|
436
|
-
|
|
440
|
+
const targetModelClass = modelClass.bindRecordMetadataModelClass(rawTargetModelClass)
|
|
437
441
|
|
|
438
442
|
const nestedHash = /** @type {WhereHash} */ (whereValue)
|
|
439
443
|
const nestedPath = path.concat([whereKey])
|