velocious 1.0.568 → 1.0.570
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 +30 -0
- package/build/configuration.js +36 -0
- package/build/database/drivers/base.js +244 -93
- package/build/database/drivers/mssql/index.js +10 -7
- package/build/database/drivers/mysql/index.js +10 -5
- package/build/database/drivers/pgsql/index.js +4 -4
- package/build/database/drivers/sqlite/base.js +2 -2
- package/build/database/drivers/sqlite/connection-sql-js.js +62 -2
- package/build/database/drivers/sqlite/index.web.js +74 -0
- package/build/database/operation-connection.js +121 -0
- package/build/database/operation-lease.js +47 -0
- package/build/database/operation.js +150 -0
- package/build/database/pool/base.js +26 -1
- package/build/database/pool/single-multi-use.js +49 -0
- package/build/database/query/model-class-query.js +38 -7
- package/build/database/query/preloader/belongs-to.js +3 -2
- package/build/database/query/preloader/has-many.js +4 -3
- package/build/database/query/preloader/has-one.js +2 -1
- package/build/database/query/preloader/query-for-model.js +17 -0
- package/build/database/query/query-data.js +12 -3
- package/build/database/query/with-count.js +5 -3
- package/build/database/record/acts-as-list.js +11 -8
- package/build/database/record/attachments/store.js +15 -11
- package/build/database/record/auditing.js +12 -9
- package/build/database/record/counter-cache-magnitude.js +5 -3
- package/build/database/record/index.js +95 -14
- package/build/database/record/instance-relationships/belongs-to.js +4 -2
- package/build/database/record/instance-relationships/has-many.js +7 -5
- package/build/database/record/instance-relationships/has-one.js +4 -2
- package/build/database/record/validators/uniqueness.js +3 -2
- package/build/frontend-models/websocket-publishers.js +2 -2
- package/build/src/configuration.d.ts +12 -0
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +35 -1
- package/build/src/database/drivers/base.d.ts +96 -23
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +230 -90
- package/build/src/database/drivers/mssql/index.d.ts +4 -1
- package/build/src/database/drivers/mssql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/index.js +11 -8
- package/build/src/database/drivers/mysql/index.d.ts +2 -1
- package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/index.js +10 -6
- package/build/src/database/drivers/pgsql/index.d.ts +2 -1
- package/build/src/database/drivers/pgsql/index.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/index.js +5 -5
- package/build/src/database/drivers/sqlite/base.d.ts +1 -1
- package/build/src/database/drivers/sqlite/base.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/base.js +3 -3
- package/build/src/database/drivers/sqlite/connection-sql-js.d.ts +20 -0
- package/build/src/database/drivers/sqlite/connection-sql-js.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/connection-sql-js.js +56 -3
- package/build/src/database/drivers/sqlite/index.web.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/index.web.js +64 -1
- package/build/src/database/operation-connection.d.ts +67 -0
- package/build/src/database/operation-connection.d.ts.map +1 -0
- package/build/src/database/operation-connection.js +102 -0
- package/build/src/database/operation-lease.d.ts +27 -0
- package/build/src/database/operation-lease.d.ts.map +1 -0
- package/build/src/database/operation-lease.js +44 -0
- package/build/src/database/operation.d.ts +85 -0
- package/build/src/database/operation.d.ts.map +1 -0
- package/build/src/database/operation.js +132 -0
- package/build/src/database/pool/base.d.ts +14 -0
- package/build/src/database/pool/base.d.ts.map +1 -1
- package/build/src/database/pool/base.js +24 -2
- package/build/src/database/pool/single-multi-use.d.ts +1 -0
- package/build/src/database/pool/single-multi-use.d.ts.map +1 -1
- package/build/src/database/pool/single-multi-use.js +49 -1
- package/build/src/database/query/model-class-query.d.ts +14 -0
- package/build/src/database/query/model-class-query.d.ts.map +1 -1
- package/build/src/database/query/model-class-query.js +32 -8
- package/build/src/database/query/preloader/belongs-to.d.ts.map +1 -1
- package/build/src/database/query/preloader/belongs-to.js +4 -3
- package/build/src/database/query/preloader/has-many.d.ts.map +1 -1
- package/build/src/database/query/preloader/has-many.js +5 -4
- package/build/src/database/query/preloader/has-one.d.ts.map +1 -1
- package/build/src/database/query/preloader/has-one.js +3 -2
- package/build/src/database/query/preloader/query-for-model.d.ts +10 -0
- package/build/src/database/query/preloader/query-for-model.d.ts.map +1 -0
- package/build/src/database/query/preloader/query-for-model.js +16 -0
- package/build/src/database/query/query-data.d.ts.map +1 -1
- package/build/src/database/query/query-data.js +13 -4
- package/build/src/database/query/with-count.d.ts.map +1 -1
- package/build/src/database/query/with-count.js +6 -4
- package/build/src/database/record/acts-as-list.js +12 -9
- package/build/src/database/record/attachments/attachment-record.d.ts +9 -0
- package/build/src/database/record/attachments/attachment-record.d.ts.map +1 -1
- package/build/src/database/record/attachments/store.d.ts +4 -2
- package/build/src/database/record/attachments/store.d.ts.map +1 -1
- package/build/src/database/record/attachments/store.js +16 -12
- package/build/src/database/record/auditing.d.ts.map +1 -1
- package/build/src/database/record/auditing.js +13 -10
- package/build/src/database/record/counter-cache-magnitude.js +6 -4
- package/build/src/database/record/index.d.ts +42 -2
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +87 -15
- package/build/src/database/record/instance-relationships/belongs-to.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/belongs-to.js +4 -3
- package/build/src/database/record/instance-relationships/has-many.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/has-many.js +7 -5
- package/build/src/database/record/instance-relationships/has-one.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/has-one.js +4 -3
- package/build/src/database/record/validators/uniqueness.d.ts.map +1 -1
- package/build/src/database/record/validators/uniqueness.js +4 -3
- package/build/src/frontend-models/websocket-publishers.js +3 -3
- package/build/src/sync/server-sequence-allocator.d.ts +16 -4
- package/build/src/sync/server-sequence-allocator.d.ts.map +1 -1
- package/build/src/sync/server-sequence-allocator.js +37 -11
- package/build/src/sync/sync-client.d.ts.map +1 -1
- package/build/src/sync/sync-client.js +7 -3
- package/build/src/sync/sync-publisher.d.ts +2 -1
- package/build/src/sync/sync-publisher.d.ts.map +1 -1
- package/build/src/sync/sync-publisher.js +11 -6
- package/build/sync/server-sequence-allocator.js +43 -10
- package/build/sync/sync-client.js +6 -2
- package/build/sync/sync-publisher.js +10 -5
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/configuration.js +36 -0
- package/src/database/drivers/base.js +244 -93
- package/src/database/drivers/mssql/index.js +10 -7
- package/src/database/drivers/mysql/index.js +10 -5
- package/src/database/drivers/pgsql/index.js +4 -4
- package/src/database/drivers/sqlite/base.js +2 -2
- package/src/database/drivers/sqlite/connection-sql-js.js +62 -2
- package/src/database/drivers/sqlite/index.web.js +74 -0
- package/src/database/operation-connection.js +121 -0
- package/src/database/operation-lease.js +47 -0
- package/src/database/operation.js +150 -0
- package/src/database/pool/base.js +26 -1
- package/src/database/pool/single-multi-use.js +49 -0
- package/src/database/query/model-class-query.js +38 -7
- package/src/database/query/preloader/belongs-to.js +3 -2
- package/src/database/query/preloader/has-many.js +4 -3
- package/src/database/query/preloader/has-one.js +2 -1
- package/src/database/query/preloader/query-for-model.js +17 -0
- package/src/database/query/query-data.js +12 -3
- package/src/database/query/with-count.js +5 -3
- package/src/database/record/acts-as-list.js +11 -8
- package/src/database/record/attachments/store.js +15 -11
- package/src/database/record/auditing.js +12 -9
- package/src/database/record/counter-cache-magnitude.js +5 -3
- package/src/database/record/index.js +95 -14
- package/src/database/record/instance-relationships/belongs-to.js +4 -2
- package/src/database/record/instance-relationships/has-many.js +7 -5
- package/src/database/record/instance-relationships/has-one.js +4 -2
- package/src/database/record/validators/uniqueness.js +3 -2
- package/src/frontend-models/websocket-publishers.js +2 -2
- package/src/sync/server-sequence-allocator.js +43 -10
- package/src/sync/sync-client.js +6 -2
- package/src/sync/sync-publisher.js +10 -5
|
@@ -444,6 +444,11 @@ class VelociousDatabaseRecord {
|
|
|
444
444
|
* @type {import("../drivers/base.js").default | undefined} */
|
|
445
445
|
__connection = undefined
|
|
446
446
|
|
|
447
|
+
/**
|
|
448
|
+
* Explicit operation owning this record's database work.
|
|
449
|
+
* @type {import("../operation.js").default | undefined} */
|
|
450
|
+
_databaseOperation = undefined
|
|
451
|
+
|
|
447
452
|
/**
|
|
448
453
|
* Instance relationships.
|
|
449
454
|
* @type {Record<string, import("./instance-relationships/base.js").default>} */
|
|
@@ -863,9 +868,10 @@ class VelociousDatabaseRecord {
|
|
|
863
868
|
* single UPDATE ... SET col = (SELECT COUNT(*)) so concurrent
|
|
864
869
|
* creates/destroys cannot race into a stale count.
|
|
865
870
|
* @param {number | string | null} parentId - Parent primary-key value.
|
|
871
|
+
* @param {VelociousDatabaseRecord} record - Child record owning the connection.
|
|
866
872
|
* @returns {Promise<void>} - Resolves when the counter cache has been synced.
|
|
867
873
|
*/
|
|
868
|
-
async function syncCounter(parentId) {
|
|
874
|
+
async function syncCounter(parentId, record) {
|
|
869
875
|
if (!parentId) return
|
|
870
876
|
|
|
871
877
|
const relationship = ChildModel.getRelationshipByName(relationshipName)
|
|
@@ -880,7 +886,9 @@ class VelociousDatabaseRecord {
|
|
|
880
886
|
const parentTable = ParentModel.tableName()
|
|
881
887
|
const childTable = ChildModel.tableName()
|
|
882
888
|
const pkColumn = inflection.underscore(primaryKey)
|
|
883
|
-
const connection =
|
|
889
|
+
const connection = record
|
|
890
|
+
.queryForModel(ParentModel)
|
|
891
|
+
.driver
|
|
884
892
|
const quoted = connection.quote(parentId)
|
|
885
893
|
|
|
886
894
|
const sql = `UPDATE ${connection.quoteTable(parentTable)} SET ${connection.quoteColumn(counterColumn)} = (SELECT COUNT(*) FROM ${connection.quoteTable(childTable)} WHERE ${connection.quoteColumn(fk)} = ${quoted}) WHERE ${connection.quoteColumn(pkColumn)} = ${quoted}`
|
|
@@ -901,11 +909,11 @@ class VelociousDatabaseRecord {
|
|
|
901
909
|
}
|
|
902
910
|
|
|
903
911
|
ChildModel.afterCreate(async (record) => {
|
|
904
|
-
await syncCounter(readFkAttribute(record))
|
|
912
|
+
await syncCounter(readFkAttribute(record), record)
|
|
905
913
|
})
|
|
906
914
|
|
|
907
915
|
ChildModel.afterDestroy(async (record) => {
|
|
908
|
-
await syncCounter(readFkAttribute(record))
|
|
916
|
+
await syncCounter(readFkAttribute(record), record)
|
|
909
917
|
})
|
|
910
918
|
|
|
911
919
|
ChildModel.beforeSave(async (record) => {
|
|
@@ -932,8 +940,8 @@ class VelociousDatabaseRecord {
|
|
|
932
940
|
|
|
933
941
|
if (previousParentId !== undefined) {
|
|
934
942
|
delete model[prevKey]
|
|
935
|
-
await syncCounter(previousParentId)
|
|
936
|
-
await syncCounter(readFkAttribute(model))
|
|
943
|
+
await syncCounter(previousParentId, record)
|
|
944
|
+
await syncCounter(readFkAttribute(model), record)
|
|
937
945
|
}
|
|
938
946
|
})
|
|
939
947
|
}
|
|
@@ -1452,15 +1460,16 @@ class VelociousDatabaseRecord {
|
|
|
1452
1460
|
* Runs initialize record.
|
|
1453
1461
|
* @param {object} args - Options object.
|
|
1454
1462
|
* @param {import("../../configuration.js").default} args.configuration - Configuration instance.
|
|
1463
|
+
* @param {import("../drivers/base.js").default} [args.connection] - Explicit metadata connection.
|
|
1455
1464
|
* @returns {Promise<void>} - Resolves when complete.
|
|
1456
1465
|
*/
|
|
1457
|
-
static async initializeRecord({configuration, ...restArgs}) {
|
|
1466
|
+
static async initializeRecord({configuration, connection: explicitConnection, ...restArgs}) {
|
|
1458
1467
|
restArgsError(restArgs)
|
|
1459
1468
|
|
|
1460
1469
|
if (!configuration) throw new Error(`No configuration given for ${this.name}`)
|
|
1461
1470
|
|
|
1462
1471
|
this.registerRecordClass({configuration})
|
|
1463
|
-
const connection = this.connection({enforceTenantDatabaseScope: false})
|
|
1472
|
+
const connection = explicitConnection || this.connection({enforceTenantDatabaseScope: false})
|
|
1464
1473
|
|
|
1465
1474
|
this._databaseType = connection.getType()
|
|
1466
1475
|
|
|
@@ -2427,7 +2436,7 @@ class VelociousDatabaseRecord {
|
|
|
2427
2436
|
await this._runLifecycleCallbacks("beforeValidation")
|
|
2428
2437
|
await this._runValidations()
|
|
2429
2438
|
|
|
2430
|
-
|
|
2439
|
+
const saveInTransaction = async () => {
|
|
2431
2440
|
await this._runLifecycleCallbacks("beforeSave")
|
|
2432
2441
|
|
|
2433
2442
|
// If any belongs-to-relationships was saved, then updated-at should still be set on this record.
|
|
@@ -2454,7 +2463,13 @@ class VelociousDatabaseRecord {
|
|
|
2454
2463
|
await this._autoSaveAttachments()
|
|
2455
2464
|
await this._runLifecycleCallbacks("afterSave")
|
|
2456
2465
|
await this._emitRecordChangeAfterCommit(isNewRecord ? "create" : "update")
|
|
2457
|
-
}
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
if (this._databaseOperation) {
|
|
2469
|
+
await this._databaseOperation.transaction(saveInTransaction)
|
|
2470
|
+
} else {
|
|
2471
|
+
await this.getModelClass().transaction(saveInTransaction)
|
|
2472
|
+
}
|
|
2458
2473
|
})
|
|
2459
2474
|
|
|
2460
2475
|
this._assignedAttributeNames = undefined
|
|
@@ -2481,6 +2496,7 @@ class VelociousDatabaseRecord {
|
|
|
2481
2496
|
if (model) {
|
|
2482
2497
|
if (model instanceof VelociousDatabaseRecord) {
|
|
2483
2498
|
if (model.isChanged()) {
|
|
2499
|
+
this.bindRelatedRecord(model)
|
|
2484
2500
|
await model.save()
|
|
2485
2501
|
|
|
2486
2502
|
const foreignKey = this._relationshipForeignKeyAttribute(instanceRelationship)
|
|
@@ -2539,6 +2555,7 @@ class VelociousDatabaseRecord {
|
|
|
2539
2555
|
|
|
2540
2556
|
if (loaded) {
|
|
2541
2557
|
for (const model of loaded) {
|
|
2558
|
+
this.bindRelatedRecord(model)
|
|
2542
2559
|
const foreignKey = model._relationshipForeignKeyAttribute(instanceRelationship)
|
|
2543
2560
|
|
|
2544
2561
|
model.setAttribute(foreignKey, this.id())
|
|
@@ -2592,6 +2609,7 @@ class VelociousDatabaseRecord {
|
|
|
2592
2609
|
}
|
|
2593
2610
|
|
|
2594
2611
|
for (const model of loaded) {
|
|
2612
|
+
this.bindRelatedRecord(model)
|
|
2595
2613
|
const foreignKey = model._relationshipForeignKeyAttribute(instanceRelationship)
|
|
2596
2614
|
|
|
2597
2615
|
model.setAttribute(foreignKey, this.id())
|
|
@@ -3021,15 +3039,18 @@ class VelociousDatabaseRecord {
|
|
|
3021
3039
|
* Runs new query.
|
|
3022
3040
|
* @template {typeof VelociousDatabaseRecord} MC
|
|
3023
3041
|
* @this {MC}
|
|
3042
|
+
* @param {{driver?: import("../drivers/base.js").default | (() => import("../drivers/base.js").default), operation?: import("../operation.js").default}} [args] - Explicit query ownership.
|
|
3024
3043
|
* @returns {ModelClassQuery<MC>} - The new query.
|
|
3025
3044
|
*/
|
|
3026
|
-
static _newQuery() {
|
|
3045
|
+
static _newQuery({driver = () => this.connection(), operation, ...restArgs} = {}) {
|
|
3046
|
+
restArgsError(restArgs)
|
|
3027
3047
|
this._assertHasBeenInitialized()
|
|
3028
3048
|
const handler = new Handler()
|
|
3029
3049
|
const query = new ModelClassQuery({
|
|
3030
|
-
driver
|
|
3050
|
+
driver,
|
|
3031
3051
|
handler,
|
|
3032
|
-
modelClass: this
|
|
3052
|
+
modelClass: this,
|
|
3053
|
+
operation
|
|
3033
3054
|
})
|
|
3034
3055
|
|
|
3035
3056
|
return query.from(new FromTable(this.tableName()))
|
|
@@ -3416,6 +3437,53 @@ class VelociousDatabaseRecord {
|
|
|
3416
3437
|
}
|
|
3417
3438
|
}
|
|
3418
3439
|
|
|
3440
|
+
/**
|
|
3441
|
+
* Binds future query, lifecycle, relationship, and persistence work to an operation.
|
|
3442
|
+
* @param {import("../operation.js").default} operation - Owning operation.
|
|
3443
|
+
* @returns {this} - Bound record.
|
|
3444
|
+
*/
|
|
3445
|
+
bindDatabaseOperation(operation) {
|
|
3446
|
+
if (this._databaseOperation && this._databaseOperation !== operation) {
|
|
3447
|
+
throw new Error("Record is already bound to another database operation")
|
|
3448
|
+
}
|
|
3449
|
+
|
|
3450
|
+
this._databaseOperation = operation
|
|
3451
|
+
|
|
3452
|
+
return this
|
|
3453
|
+
}
|
|
3454
|
+
|
|
3455
|
+
/**
|
|
3456
|
+
* Returns the explicit operation owning this record, if any.
|
|
3457
|
+
* @returns {import("../operation.js").default | undefined} - Owning operation.
|
|
3458
|
+
*/
|
|
3459
|
+
databaseOperation() {
|
|
3460
|
+
return this._databaseOperation
|
|
3461
|
+
}
|
|
3462
|
+
|
|
3463
|
+
/**
|
|
3464
|
+
* Binds a related record to the same operation as this record.
|
|
3465
|
+
* @template {VelociousDatabaseRecord} Model
|
|
3466
|
+
* @param {Model} record - Related record.
|
|
3467
|
+
* @returns {Model} - Related record.
|
|
3468
|
+
*/
|
|
3469
|
+
bindRelatedRecord(record) {
|
|
3470
|
+
if (this._databaseOperation) this._databaseOperation.bindRecord(record)
|
|
3471
|
+
|
|
3472
|
+
return record
|
|
3473
|
+
}
|
|
3474
|
+
|
|
3475
|
+
/**
|
|
3476
|
+
* Builds a model query preserving this record's operation ownership.
|
|
3477
|
+
* @template {typeof VelociousDatabaseRecord} MC
|
|
3478
|
+
* @param {MC} ModelClass - Target model class.
|
|
3479
|
+
* @returns {ModelClassQuery<MC>} - Target query.
|
|
3480
|
+
*/
|
|
3481
|
+
queryForModel(ModelClass) {
|
|
3482
|
+
if (this._databaseOperation) return this._databaseOperation.forModel(ModelClass)
|
|
3483
|
+
|
|
3484
|
+
return ModelClass._newQuery()
|
|
3485
|
+
}
|
|
3486
|
+
|
|
3419
3487
|
/**
|
|
3420
3488
|
* Runs load existing record.
|
|
3421
3489
|
* @param {object} attributes - Attributes.
|
|
@@ -3473,11 +3541,20 @@ class VelociousDatabaseRecord {
|
|
|
3473
3541
|
* @returns {import("../drivers/base.js").default} - The connection.
|
|
3474
3542
|
*/
|
|
3475
3543
|
_connection() {
|
|
3544
|
+
if (this._databaseOperation) return this._databaseOperation.connection()
|
|
3476
3545
|
if (this.__connection) return this.__connection
|
|
3477
3546
|
|
|
3478
3547
|
return this.getModelClass().connection()
|
|
3479
3548
|
}
|
|
3480
3549
|
|
|
3550
|
+
/**
|
|
3551
|
+
* Returns the connection that owns this record's database work.
|
|
3552
|
+
* @returns {import("../drivers/base.js").default} - Connection.
|
|
3553
|
+
*/
|
|
3554
|
+
connection() {
|
|
3555
|
+
return this._connection()
|
|
3556
|
+
}
|
|
3557
|
+
|
|
3481
3558
|
/**
|
|
3482
3559
|
* Counts dependent records for a `dependent: "restrict"` relationship.
|
|
3483
3560
|
* @param {RestrictInstanceRelationship} instanceRelationship - Relationship instance to count.
|
|
@@ -4374,7 +4451,11 @@ class VelociousDatabaseRecord {
|
|
|
4374
4451
|
|
|
4375
4452
|
whereObject[primaryKey] = id
|
|
4376
4453
|
|
|
4377
|
-
const query = /** @type {import("../query/model-class-query.js").default<MC>} */ (
|
|
4454
|
+
const query = /** @type {import("../query/model-class-query.js").default<MC>} */ (
|
|
4455
|
+
this
|
|
4456
|
+
.queryForModel(this.getModelClass())
|
|
4457
|
+
.where(whereObject)
|
|
4458
|
+
)
|
|
4378
4459
|
const reloadedModel = await query.first()
|
|
4379
4460
|
|
|
4380
4461
|
if (!reloadedModel) throw new Error(`${this.constructor.name}#${id} couldn't be reloaded - record didn't exist`)
|
|
@@ -27,7 +27,9 @@ export default class VelociousDatabaseRecordBelongsToInstanceRelationship extend
|
|
|
27
27
|
|
|
28
28
|
if (!TargetModelClass) throw new Error("Can't build a new record without a target model")
|
|
29
29
|
|
|
30
|
-
const newInstance =
|
|
30
|
+
const newInstance = this.getModel().bindRelatedRecord(
|
|
31
|
+
/** @type {InstanceType<TMC>} */ (new TargetModelClass(data))
|
|
32
|
+
)
|
|
31
33
|
|
|
32
34
|
this._loaded = newInstance
|
|
33
35
|
|
|
@@ -85,7 +87,7 @@ export default class VelociousDatabaseRecordBelongsToInstanceRelationship extend
|
|
|
85
87
|
|
|
86
88
|
whereArgs[primaryKey] = foreignModelID
|
|
87
89
|
|
|
88
|
-
const query = this.applyScope(TargetModelClass.where(whereArgs))
|
|
90
|
+
const query = this.applyScope(this.getModel().queryForModel(TargetModelClass).where(whereArgs))
|
|
89
91
|
|
|
90
92
|
const foreignModel = await query.first()
|
|
91
93
|
|
|
@@ -29,8 +29,9 @@ export default class VelociousDatabaseRecordHasManyInstanceRelationship extends
|
|
|
29
29
|
|
|
30
30
|
if (!targetModelClass) throw new Error("Can't build a new record without a taget model class")
|
|
31
31
|
|
|
32
|
-
const newInstance =
|
|
33
|
-
|
|
32
|
+
const newInstance = this.getModel().bindRelatedRecord(
|
|
33
|
+
/** @type {InstanceType<TMC>} */ (new targetModelClass(data))
|
|
34
|
+
)
|
|
34
35
|
|
|
35
36
|
// Add it to the loaded models of this relationship
|
|
36
37
|
if (this._loaded === undefined) {
|
|
@@ -196,13 +197,14 @@ export default class VelociousDatabaseRecordHasManyInstanceRelationship extends
|
|
|
196
197
|
)
|
|
197
198
|
const targetTable = TargetModelClass.tableName()
|
|
198
199
|
const throughTable = throughModelClass.tableName()
|
|
199
|
-
const
|
|
200
|
+
const baseQuery = this.getModel().queryForModel(TargetModelClass)
|
|
201
|
+
const driver = baseQuery.driver
|
|
200
202
|
const parentPrimaryKey = this.getPrimaryKey()
|
|
201
203
|
const parentId = /** @type {string | number} */ (this.getModel().readColumn(parentPrimaryKey))
|
|
202
204
|
const joinSql = `LEFT JOIN ${driver.quoteTable(throughTable)} ON ${driver.quoteTable(throughTable)}.${driver.quoteColumn(throughPrimaryKey)} = ${driver.quoteTable(targetTable)}.${driver.quoteColumn(targetForeignKey)}`
|
|
203
205
|
const whereSql = `${driver.quoteTable(throughTable)}.${driver.quoteColumn(throughForeignKey)} = ${driver.options().quote(parentId)}`
|
|
204
206
|
|
|
205
|
-
const query =
|
|
207
|
+
const query = baseQuery.joins(joinSql).where(whereSql)
|
|
206
208
|
|
|
207
209
|
return this.applyScope(query)
|
|
208
210
|
}
|
|
@@ -224,7 +226,7 @@ export default class VelociousDatabaseRecordHasManyInstanceRelationship extends
|
|
|
224
226
|
whereArgs[typeColumn] = this.getModel().getModelClass().getModelName()
|
|
225
227
|
}
|
|
226
228
|
|
|
227
|
-
const query = TargetModelClass.where(whereArgs)
|
|
229
|
+
const query = this.getModel().queryForModel(TargetModelClass).where(whereArgs)
|
|
228
230
|
|
|
229
231
|
return this.applyScope(query)
|
|
230
232
|
}
|
|
@@ -32,7 +32,9 @@ export default class VelociousDatabaseRecordHasOneInstanceRelationship extends B
|
|
|
32
32
|
|
|
33
33
|
if (!TargetModelClass) throw new Error("Can't build a new record without a target model class")
|
|
34
34
|
|
|
35
|
-
const newInstance =
|
|
35
|
+
const newInstance = this.getModel().bindRelatedRecord(
|
|
36
|
+
/** @type {InstanceType<TMC>} */ (new TargetModelClass(data))
|
|
37
|
+
)
|
|
36
38
|
|
|
37
39
|
this._loaded = newInstance
|
|
38
40
|
|
|
@@ -64,7 +66,7 @@ export default class VelociousDatabaseRecordHasOneInstanceRelationship extends B
|
|
|
64
66
|
|
|
65
67
|
whereArgs[foreignKey] = primaryModelID
|
|
66
68
|
|
|
67
|
-
let query = TargetModelClass.where(whereArgs)
|
|
69
|
+
let query = this.getModel().queryForModel(TargetModelClass).where(whereArgs)
|
|
68
70
|
|
|
69
71
|
query = this.applyScope(query)
|
|
70
72
|
|
|
@@ -15,7 +15,7 @@ export default class VelociousDatabaseRecordValidatorsUniqueness extends Base {
|
|
|
15
15
|
async validate({model, attributeName}) {
|
|
16
16
|
const modelClass = /** @type {typeof import("../index.js").default} */ (model.constructor)
|
|
17
17
|
|
|
18
|
-
const connection =
|
|
18
|
+
const connection = model.connection()
|
|
19
19
|
const tableName = modelClass._getTable().getName()
|
|
20
20
|
const attributeValue = /** @type {string | number} */ (model.readAttribute(attributeName))
|
|
21
21
|
const attributeNameUnderscore = inflection.underscore(attributeName)
|
|
@@ -49,7 +49,8 @@ export default class VelociousDatabaseRecordValidatorsUniqueness extends Base {
|
|
|
49
49
|
whereArgs[scopeUnderscore] = /** @type {string | number} */ (scopeValue)
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
let existingRecordQuery =
|
|
52
|
+
let existingRecordQuery = model
|
|
53
|
+
.queryForModel(modelClass)
|
|
53
54
|
.select(modelClass.primaryKey())
|
|
54
55
|
.where(whereArgs)
|
|
55
56
|
|
|
@@ -145,7 +145,7 @@ export async function ensureFrontendModelWebsocketPublishersRegistered(configura
|
|
|
145
145
|
|
|
146
146
|
if (action !== "create" && action !== "update") return
|
|
147
147
|
|
|
148
|
-
void model.
|
|
148
|
+
void model.connection().afterCommit(async () => {
|
|
149
149
|
broadcastFrontendModelEvent(model._getConfiguration(), modelName, {
|
|
150
150
|
action,
|
|
151
151
|
id: model.id(),
|
|
@@ -156,7 +156,7 @@ export async function ensureFrontendModelWebsocketPublishersRegistered(configura
|
|
|
156
156
|
})
|
|
157
157
|
|
|
158
158
|
modelClass.afterDestroy((model) => {
|
|
159
|
-
void model.
|
|
159
|
+
void model.connection().afterCommit(async () => {
|
|
160
160
|
broadcastFrontendModelEvent(model._getConfiguration(), modelName, {
|
|
161
161
|
action: "destroy",
|
|
162
162
|
id: model.id()
|
|
@@ -49,23 +49,52 @@ export default class ServerSequenceAllocator {
|
|
|
49
49
|
* parallel `next()` calls - including calls from other allocator instances
|
|
50
50
|
* sharing the same table and connection - cannot interleave their insert
|
|
51
51
|
* and last-insert-id reads and hand out duplicate sequences.
|
|
52
|
+
* @param {{connection?: import("../database/drivers/base.js").default}} [options] - Explicit record-owned connection.
|
|
52
53
|
* @returns {Promise<number>} Next sequence value.
|
|
53
54
|
*/
|
|
54
|
-
async next() {
|
|
55
|
+
async next({connection} = {}) {
|
|
55
56
|
const queueKey = `${this.databaseIdentifier}::${this.tableName}`
|
|
56
57
|
const previousAllocation = allocationQueues.get(queueKey) ?? Promise.resolve()
|
|
57
|
-
const allocation = previousAllocation.then(() => this._allocateNext())
|
|
58
|
+
const allocation = previousAllocation.then(() => this._allocateNext(connection))
|
|
58
59
|
|
|
59
60
|
allocationQueues.set(queueKey, allocation.then(() => undefined, () => undefined))
|
|
60
61
|
|
|
61
62
|
return await allocation
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Allocates for a record while preserving allocator routing and operation ownership.
|
|
67
|
+
* @param {import("../database/record/index.js").default} record - Record receiving the sequence.
|
|
68
|
+
* @returns {Promise<number>} Next sequence value.
|
|
69
|
+
*/
|
|
70
|
+
async _nextForRecord(record) {
|
|
71
|
+
const operation = record.databaseOperation()
|
|
72
|
+
|
|
73
|
+
if (!operation) return await this.next()
|
|
74
|
+
|
|
75
|
+
const ModelClass = record.getModelClass()
|
|
76
|
+
|
|
77
|
+
if (this._getConfiguration() !== ModelClass._getConfiguration()) {
|
|
78
|
+
throw new Error("Server sequence allocator belongs to another Velocious configuration")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const modelDatabaseIdentifier = ModelClass.getDatabaseIdentifier()
|
|
82
|
+
|
|
83
|
+
if (this.databaseIdentifier !== modelDatabaseIdentifier) {
|
|
84
|
+
throw new Error(`Server sequence allocator uses database ${JSON.stringify(this.databaseIdentifier)}, not operation model database ${JSON.stringify(modelDatabaseIdentifier)}`)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const operationScope = operation.forModel(ModelClass)
|
|
88
|
+
|
|
89
|
+
return await this.next({connection: operationScope.driver})
|
|
90
|
+
}
|
|
91
|
+
|
|
64
92
|
/**
|
|
65
93
|
* Ensures the backing table exists.
|
|
94
|
+
* @param {import("../database/drivers/base.js").default} [connection] - Explicit record-owned connection.
|
|
66
95
|
* @returns {Promise<void>} Resolves when ready.
|
|
67
96
|
*/
|
|
68
|
-
async ensureReady() {
|
|
97
|
+
async ensureReady(connection) {
|
|
69
98
|
if (this._isReady) return
|
|
70
99
|
|
|
71
100
|
if (this._usesMemoryStorage()) {
|
|
@@ -85,7 +114,7 @@ export default class ServerSequenceAllocator {
|
|
|
85
114
|
// cache readiness when the table was not created inside an active
|
|
86
115
|
// transaction; otherwise the next allocation re-verifies the table.
|
|
87
116
|
if (!created || !db.insideTransaction()) this._isReady = true
|
|
88
|
-
})
|
|
117
|
+
}, connection)
|
|
89
118
|
|
|
90
119
|
try {
|
|
91
120
|
await this._readyPromise
|
|
@@ -96,10 +125,11 @@ export default class ServerSequenceAllocator {
|
|
|
96
125
|
|
|
97
126
|
/**
|
|
98
127
|
* Allocates one sequence value after queueing.
|
|
128
|
+
* @param {import("../database/drivers/base.js").default} [connection] - Explicit record-owned connection.
|
|
99
129
|
* @returns {Promise<number>} Allocated sequence value.
|
|
100
130
|
*/
|
|
101
|
-
async _allocateNext() {
|
|
102
|
-
await this.ensureReady()
|
|
131
|
+
async _allocateNext(connection) {
|
|
132
|
+
await this.ensureReady(connection)
|
|
103
133
|
|
|
104
134
|
if (this._usesMemoryStorage()) {
|
|
105
135
|
return ++this._memorySequence
|
|
@@ -123,7 +153,7 @@ export default class ServerSequenceAllocator {
|
|
|
123
153
|
if (insertedId !== undefined && insertedId !== null) return Number(insertedId)
|
|
124
154
|
|
|
125
155
|
return Number(await db.lastInsertID())
|
|
126
|
-
})
|
|
156
|
+
}, connection)
|
|
127
157
|
}
|
|
128
158
|
|
|
129
159
|
/**
|
|
@@ -158,9 +188,12 @@ export default class ServerSequenceAllocator {
|
|
|
158
188
|
* Runs a callback with a database connection.
|
|
159
189
|
* @template Result
|
|
160
190
|
* @param {(db: import("../database/drivers/base.js").default) => Promise<Result>} callback - Database callback.
|
|
191
|
+
* @param {import("../database/drivers/base.js").default} [connection] - Explicit record-owned connection.
|
|
161
192
|
* @returns {Promise<Result>} Callback result.
|
|
162
193
|
*/
|
|
163
|
-
async _withDb(callback) {
|
|
194
|
+
async _withDb(callback, connection) {
|
|
195
|
+
if (connection) return await callback(connection)
|
|
196
|
+
|
|
164
197
|
return await this._getConfiguration().ensureConnections({databaseIdentifiers: [this.databaseIdentifier], name: "Server sequence allocator"}, async (dbs) => {
|
|
165
198
|
const db = dbs[this.databaseIdentifier]
|
|
166
199
|
|
|
@@ -225,11 +258,11 @@ export function withServerSequence(ModelClass, {allocator, column = "serverSeque
|
|
|
225
258
|
if (typeof prototype[advanceMethodName] != "function") {
|
|
226
259
|
/**
|
|
227
260
|
* Assigns the next server-side sequence.
|
|
228
|
-
* @this {Record<string, ?>}
|
|
261
|
+
* @this {import("../database/record/index.js").default & Record<string, ?>}
|
|
229
262
|
* @returns {Promise<void>}
|
|
230
263
|
*/
|
|
231
264
|
prototype[advanceMethodName] = async function advanceServerSequenceThroughAllocator() {
|
|
232
|
-
this[setterMethodName](await allocator.
|
|
265
|
+
this[setterMethodName](await allocator._nextForRecord(this))
|
|
233
266
|
}
|
|
234
267
|
}
|
|
235
268
|
|
package/src/sync/sync-client.js
CHANGED
|
@@ -220,13 +220,17 @@ export default class SyncClient {
|
|
|
220
220
|
resource: record
|
|
221
221
|
})
|
|
222
222
|
const syncType = this.defaultSyncType({operation, record, resourceConfig})
|
|
223
|
+
const databaseOperation = record.databaseOperation()
|
|
224
|
+
const operationScope = databaseOperation
|
|
225
|
+
? databaseOperation.forModel(this.config.syncModel)
|
|
226
|
+
: this.config.syncModel
|
|
223
227
|
|
|
224
|
-
await
|
|
228
|
+
await record.connection().afterCommit(async () => {
|
|
225
229
|
try {
|
|
226
230
|
await SyncApiClient.queueLocalSync({
|
|
227
231
|
data,
|
|
228
232
|
resource: record,
|
|
229
|
-
syncModel:
|
|
233
|
+
syncModel: operationScope,
|
|
230
234
|
syncType
|
|
231
235
|
})
|
|
232
236
|
} catch (error) {
|
|
@@ -203,10 +203,14 @@ export default class SyncPublisher {
|
|
|
203
203
|
sync_type: syncType,
|
|
204
204
|
...scopeValues.columns
|
|
205
205
|
}
|
|
206
|
+
const databaseOperation = record.databaseOperation()
|
|
207
|
+
const operationScope = databaseOperation
|
|
208
|
+
? databaseOperation.forModel(this.config.syncModel)
|
|
209
|
+
: this.config.syncModel
|
|
206
210
|
|
|
207
|
-
await
|
|
211
|
+
await record.connection().afterCommit(async () => {
|
|
208
212
|
try {
|
|
209
|
-
const syncRow = await this.upsertPublishedSyncRow(attributes)
|
|
213
|
+
const syncRow = await this.upsertPublishedSyncRow(attributes, operationScope)
|
|
210
214
|
|
|
211
215
|
await this.broadcaster()({
|
|
212
216
|
body: {
|
|
@@ -273,10 +277,11 @@ export default class SyncPublisher {
|
|
|
273
277
|
* change back to), so repeated server changes to one resource reuse and
|
|
274
278
|
* re-sequence one feed row.
|
|
275
279
|
* @param {Record<string, ?>} attributes - Snapshotted sync row attributes.
|
|
280
|
+
* @param {?} syncModel - Operation-bound or static Sync model interface.
|
|
276
281
|
* @returns {Promise<?>} Upserted sync row.
|
|
277
282
|
*/
|
|
278
|
-
async upsertPublishedSyncRow(attributes) {
|
|
279
|
-
const existingSync = await
|
|
283
|
+
async upsertPublishedSyncRow(attributes, syncModel = this.config.syncModel) {
|
|
284
|
+
const existingSync = await syncModel
|
|
280
285
|
.where({
|
|
281
286
|
[this.config.actorForeignKeyColumn]: null,
|
|
282
287
|
resource_id: attributes.resource_id,
|
|
@@ -284,7 +289,7 @@ export default class SyncPublisher {
|
|
|
284
289
|
})
|
|
285
290
|
.first()
|
|
286
291
|
|
|
287
|
-
return await upsertSyncRow({attributes, existingSync, syncModel
|
|
292
|
+
return await upsertSyncRow({attributes, existingSync, syncModel})
|
|
288
293
|
}
|
|
289
294
|
|
|
290
295
|
/**
|