velocious 1.0.567 → 1.0.569
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 +49 -1
- package/build/configuration.js +36 -0
- package/build/database/drivers/base-table.js +4 -3
- package/build/database/drivers/base.js +195 -54
- package/build/database/drivers/index-metadata.js +28 -0
- package/build/database/drivers/mssql/column.js +2 -34
- package/build/database/drivers/mssql/columns-index.js +37 -0
- package/build/database/drivers/mssql/index.js +10 -7
- package/build/database/drivers/mssql/table.js +55 -11
- package/build/database/drivers/mysql/index.js +10 -5
- package/build/database/drivers/mysql/sql/alter-table.js +13 -0
- package/build/database/drivers/pgsql/column.js +2 -27
- package/build/database/drivers/pgsql/columns-index.js +37 -0
- package/build/database/drivers/pgsql/index.js +4 -4
- package/build/database/drivers/pgsql/table.js +61 -10
- 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/drivers/sqlite/sql/alter-table.js +18 -0
- package/build/database/migration/index.js +92 -3
- package/build/database/operation-connection.js +110 -0
- package/build/database/operation-lease.js +47 -0
- package/build/database/operation.js +140 -0
- package/build/database/pool/base.js +26 -1
- package/build/database/pool/single-multi-use.js +49 -0
- package/build/database/query/alter-table-base.js +20 -1
- 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/normalize-input.js +38 -10
- package/build/database/record/attachments/storage-drivers/filesystem.js +33 -2
- package/build/database/record/attachments/storage-drivers/native.js +17 -2
- package/build/database/record/attachments/storage-drivers/s3.js +27 -7
- package/build/database/record/attachments/store.js +173 -53
- 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/database/table-data/table-foreign-key.js +9 -1
- package/build/environment-handlers/base.js +1 -1
- package/build/environment-handlers/node/attachment-path-source.js +144 -0
- package/build/environment-handlers/node.js +28 -3
- 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-table.d.ts.map +1 -1
- package/build/src/database/drivers/base-table.js +4 -3
- package/build/src/database/drivers/base.d.ts +73 -20
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +181 -54
- package/build/src/database/drivers/index-metadata.d.ts +41 -0
- package/build/src/database/drivers/index-metadata.d.ts.map +1 -0
- package/build/src/database/drivers/index-metadata.js +26 -0
- package/build/src/database/drivers/mssql/column.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/column.js +3 -31
- package/build/src/database/drivers/mssql/columns-index.d.ts +41 -0
- package/build/src/database/drivers/mssql/columns-index.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/columns-index.js +35 -1
- 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/mssql/table.d.ts +42 -0
- package/build/src/database/drivers/mssql/table.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/table.js +49 -12
- 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/mysql/sql/alter-table.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/sql/alter-table.js +12 -1
- package/build/src/database/drivers/pgsql/column.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/column.js +3 -24
- package/build/src/database/drivers/pgsql/columns-index.d.ts +41 -0
- package/build/src/database/drivers/pgsql/columns-index.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/columns-index.js +35 -1
- 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/pgsql/table.d.ts +42 -0
- package/build/src/database/drivers/pgsql/table.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/table.js +54 -11
- 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/drivers/sqlite/sql/alter-table.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/sql/alter-table.js +15 -1
- package/build/src/database/migration/index.d.ts +35 -1
- package/build/src/database/migration/index.d.ts.map +1 -1
- package/build/src/database/migration/index.js +78 -4
- package/build/src/database/operation-connection.d.ts +61 -0
- package/build/src/database/operation-connection.d.ts.map +1 -0
- package/build/src/database/operation-connection.js +93 -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 +77 -0
- package/build/src/database/operation.d.ts.map +1 -0
- package/build/src/database/operation.js +123 -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/alter-table-base.d.ts +6 -0
- package/build/src/database/query/alter-table-base.d.ts.map +1 -1
- package/build/src/database/query/alter-table-base.js +18 -2
- 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/normalize-input.d.ts +33 -4
- package/build/src/database/record/attachments/normalize-input.d.ts.map +1 -1
- package/build/src/database/record/attachments/normalize-input.js +39 -11
- package/build/src/database/record/attachments/storage-drivers/filesystem.d.ts +2 -5
- package/build/src/database/record/attachments/storage-drivers/filesystem.d.ts.map +1 -1
- package/build/src/database/record/attachments/storage-drivers/filesystem.js +28 -3
- package/build/src/database/record/attachments/storage-drivers/native.d.ts +2 -6
- package/build/src/database/record/attachments/storage-drivers/native.d.ts.map +1 -1
- package/build/src/database/record/attachments/storage-drivers/native.js +14 -3
- package/build/src/database/record/attachments/storage-drivers/s3.d.ts +2 -6
- package/build/src/database/record/attachments/storage-drivers/s3.d.ts.map +1 -1
- package/build/src/database/record/attachments/storage-drivers/s3.js +29 -8
- package/build/src/database/record/attachments/store.d.ts +31 -2
- package/build/src/database/record/attachments/store.d.ts.map +1 -1
- package/build/src/database/record/attachments/store.js +158 -54
- 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/database/table-data/table-foreign-key.d.ts +9 -1
- package/build/src/database/table-data/table-foreign-key.d.ts.map +1 -1
- package/build/src/database/table-data/table-foreign-key.js +9 -2
- package/build/src/environment-handlers/base.d.ts +2 -5
- package/build/src/environment-handlers/base.d.ts.map +1 -1
- package/build/src/environment-handlers/base.js +2 -2
- package/build/src/environment-handlers/node/attachment-path-source.d.ts +64 -0
- package/build/src/environment-handlers/node/attachment-path-source.d.ts.map +1 -0
- package/build/src/environment-handlers/node/attachment-path-source.js +116 -0
- package/build/src/environment-handlers/node.d.ts +12 -0
- package/build/src/environment-handlers/node.d.ts.map +1 -1
- package/build/src/environment-handlers/node.js +24 -4
- 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-table.js +4 -3
- package/src/database/drivers/base.js +195 -54
- package/src/database/drivers/index-metadata.js +28 -0
- package/src/database/drivers/mssql/column.js +2 -34
- package/src/database/drivers/mssql/columns-index.js +37 -0
- package/src/database/drivers/mssql/index.js +10 -7
- package/src/database/drivers/mssql/table.js +55 -11
- package/src/database/drivers/mysql/index.js +10 -5
- package/src/database/drivers/mysql/sql/alter-table.js +13 -0
- package/src/database/drivers/pgsql/column.js +2 -27
- package/src/database/drivers/pgsql/columns-index.js +37 -0
- package/src/database/drivers/pgsql/index.js +4 -4
- package/src/database/drivers/pgsql/table.js +61 -10
- 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/drivers/sqlite/sql/alter-table.js +18 -0
- package/src/database/migration/index.js +92 -3
- package/src/database/operation-connection.js +110 -0
- package/src/database/operation-lease.js +47 -0
- package/src/database/operation.js +140 -0
- package/src/database/pool/base.js +26 -1
- package/src/database/pool/single-multi-use.js +49 -0
- package/src/database/query/alter-table-base.js +20 -1
- 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/normalize-input.js +38 -10
- package/src/database/record/attachments/storage-drivers/filesystem.js +33 -2
- package/src/database/record/attachments/storage-drivers/native.js +17 -2
- package/src/database/record/attachments/storage-drivers/s3.js +27 -7
- package/src/database/record/attachments/store.js +173 -53
- 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/database/table-data/table-foreign-key.js +9 -1
- package/src/environment-handlers/base.js +1 -1
- package/src/environment-handlers/node/attachment-path-source.js +144 -0
- package/src/environment-handlers/node.js +28 -3
- 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
|
@@ -24,7 +24,7 @@ import WhereParser from "../query-parser/where-parser.js"
|
|
|
24
24
|
/**
|
|
25
25
|
* Defines this typedef.
|
|
26
26
|
* @template {typeof import("../record/index.js").default} [MC=typeof import("../record/index.js").default]
|
|
27
|
-
* @typedef {import("./index.js").QueryArgsType & {modelClass: MC, joinBasePath?: string[], joinTracker?: import("./join-tracker.js").default, forceQualifyBaseTable?: boolean, withCount?: import("./with-count.js").WithCountEntry[], queryData?: import("./query-data.js").QueryDataEntry[]}} ModelClassQueryArgsType
|
|
27
|
+
* @typedef {import("./index.js").QueryArgsType & {modelClass: MC, joinBasePath?: string[], joinTracker?: import("./join-tracker.js").default, forceQualifyBaseTable?: boolean, withCount?: import("./with-count.js").WithCountEntry[], queryData?: import("./query-data.js").QueryDataEntry[], operation?: import("../operation.js").default}} ModelClassQueryArgsType
|
|
28
28
|
*/
|
|
29
29
|
/**
|
|
30
30
|
* Runs unquote sql identifier.
|
|
@@ -200,6 +200,7 @@ export default class VelociousDatabaseQueryModelClassQuery extends DatabaseQuery
|
|
|
200
200
|
this._joinBasePath = args.joinBasePath || []
|
|
201
201
|
this._joinTracker = args.joinTracker || new JoinTracker({modelClass: this.modelClass})
|
|
202
202
|
this._forceQualifyBaseTable = Boolean(args.forceQualifyBaseTable)
|
|
203
|
+
this._operation = args.operation
|
|
203
204
|
|
|
204
205
|
/**
|
|
205
206
|
* Narrows the runtime value to the documented type.
|
|
@@ -240,7 +241,8 @@ export default class VelociousDatabaseQueryModelClassQuery extends DatabaseQuery
|
|
|
240
241
|
joinTracker: this._joinTracker.clone(),
|
|
241
242
|
forceQualifyBaseTable: this._forceQualifyBaseTable,
|
|
242
243
|
withCount: [...this._withCount],
|
|
243
|
-
queryData: [...this._queryData]
|
|
244
|
+
queryData: [...this._queryData],
|
|
245
|
+
operation: this._operation
|
|
244
246
|
}))
|
|
245
247
|
|
|
246
248
|
// @ts-expect-error
|
|
@@ -723,7 +725,11 @@ export default class VelociousDatabaseQueryModelClassQuery extends DatabaseQuery
|
|
|
723
725
|
* @returns {VelociousDatabaseQueryModelClassQuery<MC>} - The scoped join query.
|
|
724
726
|
*/
|
|
725
727
|
buildJoinScopeQuery(targetModelClass, joinPath) {
|
|
726
|
-
const scopedQuery = /** @type {VelociousDatabaseQueryModelClassQuery<MC>} */ (
|
|
728
|
+
const scopedQuery = /** @type {VelociousDatabaseQueryModelClassQuery<MC>} */ (
|
|
729
|
+
this._operation
|
|
730
|
+
? this._operation.forModel(targetModelClass)
|
|
731
|
+
: targetModelClass._newQuery()
|
|
732
|
+
)
|
|
727
733
|
|
|
728
734
|
scopedQuery._joinTracker = this._joinTracker
|
|
729
735
|
scopedQuery._joinBasePath = joinPath
|
|
@@ -864,8 +870,7 @@ export default class VelociousDatabaseQueryModelClassQuery extends DatabaseQuery
|
|
|
864
870
|
|
|
865
871
|
if (record) return record
|
|
866
872
|
|
|
867
|
-
const
|
|
868
|
-
const newRecord = /** @type {InstanceType<MC>} */ (new ModelClass(conditions))
|
|
873
|
+
const newRecord = this.build(conditions)
|
|
869
874
|
|
|
870
875
|
if (callback) {
|
|
871
876
|
callback(newRecord)
|
|
@@ -874,6 +879,33 @@ export default class VelociousDatabaseQueryModelClassQuery extends DatabaseQuery
|
|
|
874
879
|
return newRecord
|
|
875
880
|
}
|
|
876
881
|
|
|
882
|
+
/**
|
|
883
|
+
* Builds a record owned by the query's operation, when present.
|
|
884
|
+
* @param {Record<string, ?>} [attributes] - Initial attributes.
|
|
885
|
+
* @returns {InstanceType<MC>} - Built record.
|
|
886
|
+
*/
|
|
887
|
+
build(attributes = {}) {
|
|
888
|
+
const ModelClass = this.getModelClass()
|
|
889
|
+
const record = /** @type {InstanceType<MC>} */ (new ModelClass(attributes))
|
|
890
|
+
|
|
891
|
+
if (this._operation) this._operation.bindRecord(record)
|
|
892
|
+
|
|
893
|
+
return record
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Creates a record owned by the query's operation, when present.
|
|
898
|
+
* @param {Record<string, ?>} [attributes] - Initial attributes.
|
|
899
|
+
* @returns {Promise<InstanceType<MC>>} - Created record.
|
|
900
|
+
*/
|
|
901
|
+
async create(attributes = {}) {
|
|
902
|
+
const record = this.build(attributes)
|
|
903
|
+
|
|
904
|
+
await record.save()
|
|
905
|
+
|
|
906
|
+
return record
|
|
907
|
+
}
|
|
908
|
+
|
|
877
909
|
/**
|
|
878
910
|
* Runs first.
|
|
879
911
|
* @returns {Promise<InstanceType<MC> | null>} - Resolves with the first.
|
|
@@ -917,8 +949,7 @@ export default class VelociousDatabaseQueryModelClassQuery extends DatabaseQuery
|
|
|
917
949
|
const results = await this.results()
|
|
918
950
|
|
|
919
951
|
for (const result of results) {
|
|
920
|
-
const
|
|
921
|
-
const model = /** @type {InstanceType<MC>} */ (new ModelClass())
|
|
952
|
+
const model = this.build()
|
|
922
953
|
|
|
923
954
|
model.loadExistingRecord(result)
|
|
924
955
|
models.push(model)
|
|
@@ -2,6 +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
6
|
import restArgsError from "../../../utils/rest-args-error.js"
|
|
6
7
|
|
|
7
8
|
export default class VelociousDatabaseQueryPreloaderBelongsTo {
|
|
@@ -94,7 +95,7 @@ export default class VelociousDatabaseQueryPreloaderBelongsTo {
|
|
|
94
95
|
whereArgs[primaryKey] = [...foreignKeyValues]
|
|
95
96
|
|
|
96
97
|
// Load target models to be preloaded on the given models
|
|
97
|
-
let query = targetModelClass.where(whereArgs)
|
|
98
|
+
let query = preloadQueryForModel(modelsToLoad, targetModelClass).where(whereArgs)
|
|
98
99
|
|
|
99
100
|
query = this.relationship.applyScope(query)
|
|
100
101
|
query = this.selection.applyToQuery({query, targetModelClass, mappingColumns: [primaryKey]})
|
|
@@ -211,7 +212,7 @@ export default class VelociousDatabaseQueryPreloaderBelongsTo {
|
|
|
211
212
|
|
|
212
213
|
whereArgs[primaryKey] = [...foreignKeyValuesByType[targetType]]
|
|
213
214
|
|
|
214
|
-
let query = targetModelClass.where(whereArgs)
|
|
215
|
+
let query = preloadQueryForModel(this.models, targetModelClass).where(whereArgs)
|
|
215
216
|
|
|
216
217
|
query = this.relationship.applyScope(query)
|
|
217
218
|
query = this.selection.applyToQuery({query, targetModelClass, mappingColumns: [primaryKey]})
|
|
@@ -2,6 +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
6
|
import restArgsError from "../../../utils/rest-args-error.js"
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -155,7 +156,7 @@ export default class VelociousDatabaseQueryPreloaderHasMany {
|
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
// Step 1: Query the through table to build parent→target ID mapping
|
|
158
|
-
const throughModels = await throughModelClass
|
|
159
|
+
const throughModels = await preloadQueryForModel(modelsToLoad, throughModelClass)
|
|
159
160
|
.where({[throughForeignKey]: [...modelsPrimaryKeyValues]})
|
|
160
161
|
.toArray()
|
|
161
162
|
|
|
@@ -186,7 +187,7 @@ export default class VelociousDatabaseQueryPreloaderHasMany {
|
|
|
186
187
|
let targetModels = []
|
|
187
188
|
|
|
188
189
|
if (allTargetIds.size > 0) {
|
|
189
|
-
let query = targetModelClass.where({[targetForeignKey]: [...allTargetIds]})
|
|
190
|
+
let query = preloadQueryForModel(modelsToLoad, targetModelClass).where({[targetForeignKey]: [...allTargetIds]})
|
|
190
191
|
|
|
191
192
|
query = this.relationship.applyScope(query)
|
|
192
193
|
query = this.selection.applyToQuery({query, targetModelClass, mappingColumns: [targetForeignKey]})
|
|
@@ -299,7 +300,7 @@ export default class VelociousDatabaseQueryPreloaderHasMany {
|
|
|
299
300
|
|
|
300
301
|
await ensureModelClassInitialized(targetModelClass, this.relationship.getConfiguration())
|
|
301
302
|
|
|
302
|
-
let query = targetModelClass.where(whereArgs)
|
|
303
|
+
let query = preloadQueryForModel(modelsToLoad, targetModelClass).where(whereArgs)
|
|
303
304
|
|
|
304
305
|
query = this.relationship.applyScope(query)
|
|
305
306
|
query = this.selection.applyToQuery({query, targetModelClass, mappingColumns: [foreignKey]})
|
|
@@ -2,6 +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
6
|
import restArgsError from "../../../utils/rest-args-error.js"
|
|
6
7
|
|
|
7
8
|
export default class VelociousDatabaseQueryPreloaderHasOne {
|
|
@@ -87,7 +88,7 @@ export default class VelociousDatabaseQueryPreloaderHasOne {
|
|
|
87
88
|
await ensureModelClassInitialized(targetModelClass, this.relationship.getConfiguration())
|
|
88
89
|
|
|
89
90
|
// Load target models to be preloaded on the given models
|
|
90
|
-
let query = targetModelClass.where(whereArgs)
|
|
91
|
+
let query = preloadQueryForModel(this.models, targetModelClass).where(whereArgs)
|
|
91
92
|
|
|
92
93
|
query = this.relationship.applyScope(query)
|
|
93
94
|
query = this.selection.applyToQuery({query, targetModelClass, mappingColumns: [foreignKey]})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Builds a target query preserving the explicit database operation owned by
|
|
5
|
+
* the source records.
|
|
6
|
+
* @template {typeof import("../../record/index.js").default} MC
|
|
7
|
+
* @param {import("../../record/index.js").default[]} models - Source records.
|
|
8
|
+
* @param {MC} ModelClass - Target model class.
|
|
9
|
+
* @returns {import("../model-class-query.js").default<MC>} - Target query.
|
|
10
|
+
*/
|
|
11
|
+
export default function preloadQueryForModel(models, ModelClass) {
|
|
12
|
+
const sourceModel = models[0]
|
|
13
|
+
|
|
14
|
+
if (!sourceModel) throw new Error("Cannot build a preload query without source records")
|
|
15
|
+
|
|
16
|
+
return sourceModel.queryForModel(ModelClass)
|
|
17
|
+
}
|
|
@@ -190,7 +190,15 @@ export async function runQueryData({rootModelClass, rootModels, entries}) {
|
|
|
190
190
|
|
|
191
191
|
const primaryKey = rootModelClass.primaryKey()
|
|
192
192
|
const rootIds = rootModels.map((model) => /** @type {string | number} */ (model.readColumn(primaryKey)))
|
|
193
|
-
const
|
|
193
|
+
const sourceModel = rootModels[0]
|
|
194
|
+
const preparedEntries = entries.map((entry, entryIndex) => prepareEntry({
|
|
195
|
+
entry,
|
|
196
|
+
entryIndex,
|
|
197
|
+
primaryKey,
|
|
198
|
+
rootIds,
|
|
199
|
+
rootModelClass,
|
|
200
|
+
sourceModel
|
|
201
|
+
}))
|
|
194
202
|
/**
|
|
195
203
|
* Compatible query groups.
|
|
196
204
|
* @type {Array<{aliases: Set<string>, query: import("./model-class-query.js").default, signature: string}>} */
|
|
@@ -231,9 +239,10 @@ export async function runQueryData({rootModelClass, rootModels, entries}) {
|
|
|
231
239
|
* @param {string} args.primaryKey - Root model primary key column.
|
|
232
240
|
* @param {Array<string | number>} args.rootIds - Root primary-key values.
|
|
233
241
|
* @param {typeof import("../record/index.js").default} args.rootModelClass - Root model class.
|
|
242
|
+
* @param {import("../record/index.js").default} args.sourceModel - Loaded operation owner.
|
|
234
243
|
* @returns {{aliases: string[], query: import("./model-class-query.js").default, signature: string}} - Prepared entry.
|
|
235
244
|
*/
|
|
236
|
-
function prepareEntry({entry, entryIndex, primaryKey, rootIds, rootModelClass}) {
|
|
245
|
+
function prepareEntry({entry, entryIndex, primaryKey, rootIds, rootModelClass, sourceModel}) {
|
|
237
246
|
const targetModelClass = resolveTargetModelClass(rootModelClass, entry.chain)
|
|
238
247
|
const fn = targetModelClass.getQueryDataByName(entry.fnName)
|
|
239
248
|
|
|
@@ -242,7 +251,7 @@ function prepareEntry({entry, entryIndex, primaryKey, rootIds, rootModelClass})
|
|
|
242
251
|
`Declare it with ${targetModelClass.name}.queryData(${JSON.stringify(entry.fnName)}, ({query, tableName}) => query.select(...))`)
|
|
243
252
|
}
|
|
244
253
|
|
|
245
|
-
const query =
|
|
254
|
+
const query = sourceModel.queryForModel(rootModelClass)
|
|
246
255
|
|
|
247
256
|
// Empty out any defaults the query factory added — queryData runs
|
|
248
257
|
// a bare aggregate, not a full model load.
|
|
@@ -111,10 +111,11 @@ export async function runWithCount({models, modelClass, entries}) {
|
|
|
111
111
|
|
|
112
112
|
const primaryKey = modelClass.primaryKey()
|
|
113
113
|
const parentIds = models.map((model) => /** @type {string | number} */ (model.readColumn(primaryKey)))
|
|
114
|
+
const sourceModel = models[0]
|
|
114
115
|
const queryGroups = new Map()
|
|
115
116
|
|
|
116
117
|
for (const entry of entries) {
|
|
117
|
-
const countQuery = queryForEntry({entry, modelClass, parentIds})
|
|
118
|
+
const countQuery = queryForEntry({entry, modelClass, parentIds, sourceModel})
|
|
118
119
|
const sql = countQuery.toSql()
|
|
119
120
|
const existingGroup = queryGroups.get(sql)
|
|
120
121
|
|
|
@@ -138,9 +139,10 @@ export async function runWithCount({models, modelClass, entries}) {
|
|
|
138
139
|
* @param {WithCountEntry} args.entry - Entry being evaluated.
|
|
139
140
|
* @param {typeof import("../record/index.js").default} args.modelClass - Parent model class.
|
|
140
141
|
* @param {Array<string | number>} args.parentIds - Primary keys of the loaded parents.
|
|
142
|
+
* @param {import("../record/index.js").default} args.sourceModel - Loaded operation owner.
|
|
141
143
|
* @returns {import("./model-class-query.js").default} - Prepared count query.
|
|
142
144
|
*/
|
|
143
|
-
function queryForEntry({entry, modelClass, parentIds}) {
|
|
145
|
+
function queryForEntry({entry, modelClass, parentIds, sourceModel}) {
|
|
144
146
|
const relationship = modelClass.getRelationshipByName(entry.relationshipName)
|
|
145
147
|
|
|
146
148
|
if (!relationship) {
|
|
@@ -172,7 +174,7 @@ function queryForEntry({entry, modelClass, parentIds}) {
|
|
|
172
174
|
Object.assign(whereConditions, entry.where)
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
const baseQuery =
|
|
177
|
+
const baseQuery = sourceModel.queryForModel(targetModelClass)
|
|
176
178
|
baseQuery._forceQualifyBaseTable = true
|
|
177
179
|
baseQuery.where(whereConditions)
|
|
178
180
|
|
|
@@ -173,7 +173,7 @@ function assertPositivePosition({position, positionColumn, persisted = false}) {
|
|
|
173
173
|
*/
|
|
174
174
|
async function placeMovedRecord({record, positionColumn, scope, scopeValue, position}) {
|
|
175
175
|
const modelClass = /** @type {typeof import("./index.js").default} */ (record.constructor)
|
|
176
|
-
const connection =
|
|
176
|
+
const connection = record.connection()
|
|
177
177
|
const tableSql = connection.quoteTable(modelClass._getTable().getName())
|
|
178
178
|
const scopeCol = modelClass.getColumnNameForAttributeName(scope)
|
|
179
179
|
const posCol = modelClass.getColumnNameForAttributeName(positionColumn)
|
|
@@ -224,7 +224,7 @@ function clearBelongsToChangeForScope(record) {
|
|
|
224
224
|
*/
|
|
225
225
|
async function shiftPositionsUp({record, positionColumn, scope, fromPosition, toPosition, scopeValue, excludeRecordId}) {
|
|
226
226
|
const modelClass = /** @type {typeof import("./index.js").default} */ (record.constructor)
|
|
227
|
-
const connection =
|
|
227
|
+
const connection = record.connection()
|
|
228
228
|
const tableName = modelClass._getTable().getName()
|
|
229
229
|
const resolvedScopeValue = scopeValue != null ? scopeValue : resolveScopeValue(record, scope)
|
|
230
230
|
const scopeColumnName = modelClass.getColumnNameForAttributeName(scope)
|
|
@@ -239,7 +239,8 @@ async function shiftPositionsUp({record, positionColumn, scope, fromPosition, to
|
|
|
239
239
|
const quotedScope = connection.quote(resolvedScopeValue)
|
|
240
240
|
|
|
241
241
|
// Load rows in descending order so we bump the highest first
|
|
242
|
-
let query =
|
|
242
|
+
let query = record
|
|
243
|
+
.queryForModel(modelClass)
|
|
243
244
|
.select(modelClass.primaryKey())
|
|
244
245
|
.select(positionColumn)
|
|
245
246
|
.where({[scopeColumnName]: resolvedScopeValue})
|
|
@@ -289,7 +290,7 @@ async function shiftPositionsUp({record, positionColumn, scope, fromPosition, to
|
|
|
289
290
|
*/
|
|
290
291
|
async function shiftPositionsDown({record, positionColumn, scope, fromPosition, toPosition, scopeValue}) {
|
|
291
292
|
const modelClass = /** @type {typeof import("./index.js").default} */ (record.constructor)
|
|
292
|
-
const connection =
|
|
293
|
+
const connection = record.connection()
|
|
293
294
|
const tableName = modelClass._getTable().getName()
|
|
294
295
|
const resolvedScopeValue = scopeValue != null ? scopeValue : resolveScopeValue(record, scope)
|
|
295
296
|
const scopeColumnName = modelClass.getColumnNameForAttributeName(scope)
|
|
@@ -304,7 +305,8 @@ async function shiftPositionsDown({record, positionColumn, scope, fromPosition,
|
|
|
304
305
|
const quotedScope = connection.quote(resolvedScopeValue)
|
|
305
306
|
|
|
306
307
|
// Load rows in ascending order so we shift the lowest gap first
|
|
307
|
-
let query =
|
|
308
|
+
let query = record
|
|
309
|
+
.queryForModel(modelClass)
|
|
308
310
|
.select(modelClass.primaryKey())
|
|
309
311
|
.select(positionColumn)
|
|
310
312
|
.where({[scopeColumnName]: resolvedScopeValue})
|
|
@@ -345,7 +347,7 @@ async function shiftPositionsDown({record, positionColumn, scope, fromPosition,
|
|
|
345
347
|
*/
|
|
346
348
|
async function highestPositionInScope({record, positionColumn, scope, scopeValue}) {
|
|
347
349
|
const modelClass = /** @type {typeof import("./index.js").default} */ (record.constructor)
|
|
348
|
-
const connection =
|
|
350
|
+
const connection = record.connection()
|
|
349
351
|
const scopeColumnName = modelClass.getColumnNameForAttributeName(scope)
|
|
350
352
|
const positionColumnName = modelClass.getColumnNameForAttributeName(positionColumn)
|
|
351
353
|
const positionColumnSql = connection.quoteColumn(positionColumnName)
|
|
@@ -353,7 +355,8 @@ async function highestPositionInScope({record, positionColumn, scope, scopeValue
|
|
|
353
355
|
|
|
354
356
|
if (resolvedScopeValue == null) return 0
|
|
355
357
|
|
|
356
|
-
const rows = await
|
|
358
|
+
const rows = await record
|
|
359
|
+
.queryForModel(modelClass)
|
|
357
360
|
.select(positionColumn)
|
|
358
361
|
.where({[scopeColumnName]: resolvedScopeValue})
|
|
359
362
|
.order(`${positionColumnSql} DESC`)
|
|
@@ -415,7 +418,7 @@ function resolveScopeValue(record, scope) {
|
|
|
415
418
|
*/
|
|
416
419
|
async function moveOutOfWay({record, positionColumn, scope, scopeValue}) {
|
|
417
420
|
const modelClass = /** @type {typeof import("./index.js").default} */ (record.constructor)
|
|
418
|
-
const connection =
|
|
421
|
+
const connection = record.connection()
|
|
419
422
|
const tableName = modelClass._getTable().getName()
|
|
420
423
|
const resolvedScopeValue = scopeValue != null ? scopeValue : resolveScopeValue(record, scope)
|
|
421
424
|
|
|
@@ -2,14 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
import UploadedFile from "../../../http-server/client/uploaded-file/uploaded-file.js"
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* AttachmentPathSource type.
|
|
7
|
+
* @typedef {object} AttachmentPathSource
|
|
8
|
+
* @property {number} byteSize - Opened file snapshot size.
|
|
9
|
+
* @property {string} filePath - Validated source path for metadata only.
|
|
10
|
+
* @property {() => Promise<import("node:stream").Readable>} createReadStream - Creates a bounded snapshot stream.
|
|
11
|
+
* @property {() => Promise<Buffer>} readBuffer - Reads snapshot bytes for compatibility callers.
|
|
12
|
+
* @property {() => Promise<void>} close - Closes the owned source.
|
|
13
|
+
*/
|
|
5
14
|
/**
|
|
6
15
|
* NormalizedAttachmentInput type.
|
|
7
16
|
* @typedef {object} NormalizedAttachmentInput
|
|
8
17
|
* @property {number} byteSize - File size in bytes.
|
|
9
|
-
* @property {Buffer} contentBuffer - Raw content bytes.
|
|
10
|
-
* @property {string} contentBase64 - Base64 encoded content.
|
|
18
|
+
* @property {Buffer | null} contentBuffer - Raw in-memory content bytes.
|
|
19
|
+
* @property {string | null} contentBase64 - Base64 encoded in-memory content.
|
|
11
20
|
* @property {string | null} contentType - Content type.
|
|
12
21
|
* @property {string} filename - Filename.
|
|
22
|
+
* @property {AttachmentPathSource | null} pathSource - Environment-owned opened path source.
|
|
13
23
|
*/
|
|
14
24
|
/**
|
|
15
25
|
* Runs base name.
|
|
@@ -120,8 +130,16 @@ export default async function normalizeRecordAttachmentInput(input, args = {}) {
|
|
|
120
130
|
const environmentHandler = args.environmentHandler
|
|
121
131
|
/**
|
|
122
132
|
* Defines buffer.
|
|
123
|
-
* @type {Buffer} */
|
|
124
|
-
let buffer
|
|
133
|
+
* @type {Buffer | null} */
|
|
134
|
+
let buffer = null
|
|
135
|
+
/**
|
|
136
|
+
* Defines byte size.
|
|
137
|
+
* @type {number | null} */
|
|
138
|
+
let byteSize = null
|
|
139
|
+
/**
|
|
140
|
+
* Defines path source.
|
|
141
|
+
* @type {AttachmentPathSource | null} */
|
|
142
|
+
let pathSource = null
|
|
125
143
|
/**
|
|
126
144
|
* Content type.
|
|
127
145
|
* @type {string | null} */
|
|
@@ -147,13 +165,15 @@ export default async function normalizeRecordAttachmentInput(input, args = {}) {
|
|
|
147
165
|
const allowedPathPrefixes = Array.isArray(args.allowedPathPrefixes)
|
|
148
166
|
? args.allowedPathPrefixes.filter((entry) => typeof entry === "string" && entry.length > 0)
|
|
149
167
|
: []
|
|
150
|
-
|
|
168
|
+
pathSource = await environmentHandler.resolveAttachmentInputPath({
|
|
151
169
|
allowedPathPrefixes,
|
|
152
170
|
inputPath: input.path
|
|
153
171
|
})
|
|
154
172
|
|
|
155
|
-
|
|
156
|
-
filename = typeof input.filename === "string" && input.filename.length > 0
|
|
173
|
+
byteSize = pathSource.byteSize
|
|
174
|
+
filename = typeof input.filename === "string" && input.filename.length > 0
|
|
175
|
+
? input.filename
|
|
176
|
+
: baseName(pathSource.filePath)
|
|
157
177
|
contentType = typeof input.contentType === "string" && input.contentType.length > 0 ? input.contentType : null
|
|
158
178
|
} else if (isPlainObject(input) && typeof input.contentBase64 === "string") {
|
|
159
179
|
buffer = Buffer.from(input.contentBase64, "base64")
|
|
@@ -184,11 +204,19 @@ export default async function normalizeRecordAttachmentInput(input, args = {}) {
|
|
|
184
204
|
? baseName(filename)
|
|
185
205
|
: ""
|
|
186
206
|
|
|
207
|
+
if (!buffer && !pathSource) {
|
|
208
|
+
throw new Error("Attachment input normalization produced no content")
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (buffer) byteSize = buffer.length
|
|
212
|
+
if (byteSize === null) throw new Error("Attachment input normalization produced no byte size")
|
|
213
|
+
|
|
187
214
|
return {
|
|
188
|
-
byteSize
|
|
215
|
+
byteSize,
|
|
189
216
|
contentBuffer: buffer,
|
|
190
|
-
contentBase64: buffer.toString("base64"),
|
|
217
|
+
contentBase64: buffer ? buffer.toString("base64") : null,
|
|
191
218
|
contentType,
|
|
192
|
-
filename: normalizedFilename || defaultFilename
|
|
219
|
+
filename: normalizedFilename || defaultFilename,
|
|
220
|
+
pathSource
|
|
193
221
|
}
|
|
194
222
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
|
+
import { createWriteStream } from "node:fs"
|
|
3
4
|
import fs from "fs/promises"
|
|
4
5
|
import path from "path"
|
|
6
|
+
import { pipeline } from "node:stream/promises"
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Runs normalize base url.
|
|
@@ -52,16 +54,45 @@ export default class FilesystemAttachmentStorageDriver {
|
|
|
52
54
|
* Runs write.
|
|
53
55
|
* @param {object} args - Options.
|
|
54
56
|
* @param {string} args.attachmentId - Attachment id.
|
|
55
|
-
* @param {
|
|
57
|
+
* @param {import("../normalize-input.js").NormalizedAttachmentInput} args.input - Normalized attachment input.
|
|
56
58
|
* @returns {Promise<{storageKey: string}>} - Storage key result.
|
|
57
59
|
*/
|
|
58
60
|
async write({attachmentId, input}) {
|
|
59
61
|
const normalizedFilename = path.basename(input.filename || "attachment.bin")
|
|
60
62
|
const storageKey = `${attachmentId}-${normalizedFilename}`
|
|
61
63
|
const filePath = path.resolve(this.directory(), storageKey)
|
|
64
|
+
const temporaryFilePath = `${filePath}.tmp`
|
|
62
65
|
|
|
63
66
|
await fs.mkdir(path.dirname(filePath), {recursive: true})
|
|
64
|
-
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
if (input.pathSource) {
|
|
70
|
+
await pipeline(
|
|
71
|
+
await input.pathSource.createReadStream(),
|
|
72
|
+
createWriteStream(temporaryFilePath)
|
|
73
|
+
)
|
|
74
|
+
} else if (input.contentBuffer) {
|
|
75
|
+
await fs.writeFile(temporaryFilePath, input.contentBuffer)
|
|
76
|
+
} else {
|
|
77
|
+
throw new Error("Filesystem attachment input has no content")
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
await fs.rename(temporaryFilePath, filePath)
|
|
81
|
+
} catch (error) {
|
|
82
|
+
try {
|
|
83
|
+
await fs.unlink(temporaryFilePath)
|
|
84
|
+
} catch (cleanupError) {
|
|
85
|
+
if (!(cleanupError instanceof Error) || !("code" in cleanupError) || cleanupError.code !== "ENOENT") {
|
|
86
|
+
throw new AggregateError(
|
|
87
|
+
[error, cleanupError],
|
|
88
|
+
`Filesystem attachment write and partial-file cleanup both failed for ${storageKey}`,
|
|
89
|
+
{cause: cleanupError}
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
throw error
|
|
95
|
+
}
|
|
65
96
|
|
|
66
97
|
return {storageKey}
|
|
67
98
|
}
|
|
@@ -47,7 +47,7 @@ export default class NativeAttachmentStorageDriver {
|
|
|
47
47
|
* Runs write.
|
|
48
48
|
* @param {object} args - Write args.
|
|
49
49
|
* @param {string} args.attachmentId - Attachment id.
|
|
50
|
-
* @param {
|
|
50
|
+
* @param {import("../normalize-input.js").NormalizedAttachmentInput} args.input - Normalized attachment input.
|
|
51
51
|
* @param {import("../../index.js").default} args.model - Model instance.
|
|
52
52
|
* @param {string} args.name - Attachment name.
|
|
53
53
|
* @returns {Promise<{storageKey: string}>} - Storage key result.
|
|
@@ -57,10 +57,25 @@ export default class NativeAttachmentStorageDriver {
|
|
|
57
57
|
throwMissingMethod({driverName: this.name, methodName: "write"})
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
let contentBase64 = input.contentBase64
|
|
61
|
+
|
|
62
|
+
if (input.pathSource) {
|
|
63
|
+
// Native write callbacks have always received Base64. Path input is
|
|
64
|
+
// intentionally buffered here, after driver selection, to preserve that
|
|
65
|
+
// public callback contract without forcing filesystem/S3 to buffer.
|
|
66
|
+
const contentBuffer = await input.pathSource.readBuffer()
|
|
67
|
+
|
|
68
|
+
contentBase64 = contentBuffer.toString("base64")
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (contentBase64 === null) {
|
|
72
|
+
throw new Error(`Attachment storage driver "${this.name}" input has no content`)
|
|
73
|
+
}
|
|
74
|
+
|
|
60
75
|
const result = await this.options.write({
|
|
61
76
|
attachmentId,
|
|
62
77
|
attachmentName: name,
|
|
63
|
-
contentBase64
|
|
78
|
+
contentBase64,
|
|
64
79
|
contentType: input.contentType,
|
|
65
80
|
filename: input.filename,
|
|
66
81
|
model
|
|
@@ -167,20 +167,40 @@ export default class S3AttachmentStorageDriver {
|
|
|
167
167
|
* Runs write.
|
|
168
168
|
* @param {object} args - Write args.
|
|
169
169
|
* @param {string} args.attachmentId - Attachment id.
|
|
170
|
-
* @param {
|
|
170
|
+
* @param {import("../normalize-input.js").NormalizedAttachmentInput} args.input - Normalized attachment input.
|
|
171
171
|
* @returns {Promise<{storageKey: string}>} - Storage key.
|
|
172
172
|
*/
|
|
173
173
|
async write({attachmentId, input}) {
|
|
174
174
|
const {PutObjectCommand} = await this.s3Runtime()
|
|
175
175
|
const client = await this.client()
|
|
176
176
|
const storageKey = `${attachmentId}-${input.filename}`
|
|
177
|
+
/**
|
|
178
|
+
* S3 request body.
|
|
179
|
+
* @type {Buffer | import("node:stream").Readable | null} */
|
|
180
|
+
let body = input.contentBuffer
|
|
181
|
+
/**
|
|
182
|
+
* Path input stream.
|
|
183
|
+
* @type {import("node:stream").Readable | null} */
|
|
184
|
+
let pathInputStream = null
|
|
185
|
+
|
|
186
|
+
if (input.pathSource) {
|
|
187
|
+
pathInputStream = await input.pathSource.createReadStream()
|
|
188
|
+
body = pathInputStream
|
|
189
|
+
}
|
|
177
190
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
191
|
+
if (!body) throw new Error("S3 attachment input has no content")
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
await client.send(new PutObjectCommand({
|
|
195
|
+
Body: body,
|
|
196
|
+
Bucket: this.bucket(),
|
|
197
|
+
ContentLength: input.byteSize,
|
|
198
|
+
ContentType: input.contentType || undefined,
|
|
199
|
+
Key: storageKey
|
|
200
|
+
}))
|
|
201
|
+
} finally {
|
|
202
|
+
if (pathInputStream) pathInputStream.destroy()
|
|
203
|
+
}
|
|
184
204
|
|
|
185
205
|
return {storageKey}
|
|
186
206
|
}
|