assai 2.0.0 → 2.1.1
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 +148 -148
- package/dist/src/__test/mock_get_collection.d.mts +2 -0
- package/dist/src/factories/create_mongo_collection.d.mts +55 -11
- package/dist/src/usecases/mongo/operation/aggregate.d.mts +16 -0
- package/dist/src/usecases/mongo/operation/bulk_write.d.mts +15 -0
- package/dist/src/usecases/mongo/operation/find_one_and_delete.d.mts +16 -0
- package/dist/src/usecases/mongo/operation/find_one_and_replace.d.mts +18 -0
- package/dist/src/usecases/mongo/operation/find_one_and_update.d.mts +18 -0
- package/dist/src/usecases/mongo/operation/index.d.mts +5 -0
- package/dist/src/usecases/mongo/transformers/id/rename_to_mongo_id.d.mts +3 -0
- package/dist/src/usecases/mongo/transformers/object_id/ids_into_strings.d.mts +5 -0
- package/dist/src/usecases/mongo/transformers/object_id/strings_into_id.d.mts +3 -0
- package/dist/src/usecases/mongo/transformers/timestamps.d.mts +5 -0
- package/index.mjs +2 -2
- package/jsconfig.json +17 -20
- package/jsconfig.prod.json +12 -12
- package/package.json +40 -39
- package/src/__test/manage_mock_database.mjs +24 -24
- package/src/__test/manage_mock_registry.mjs +54 -54
- package/src/__test/mock_get_collection.mjs +22 -20
- package/src/data/errors/operation_fail.mjs +9 -9
- package/src/data/errors/unsupported_error.mjs +9 -9
- package/src/data/interfaces/mongo_operator.mjs +2 -2
- package/src/data/interfaces/native_collection.mjs +27 -27
- package/src/factories/create_mock_collection.mjs +173 -173
- package/src/factories/create_mongo_collection.mjs +224 -181
- package/src/factories/index.mjs +1 -1
- package/src/types.ts +31 -31
- package/src/usecases/index.mjs +1 -1
- package/src/usecases/mongo/generate_new_id.mjs +4 -4
- package/src/usecases/mongo/index.mjs +3 -3
- package/src/usecases/mongo/mongo_client.mjs +36 -36
- package/src/usecases/mongo/operation/additional/delete_one_or_throw.mjs +17 -17
- package/src/usecases/mongo/operation/additional/index.mjs +1 -1
- package/src/usecases/mongo/operation/aggregate.mjs +21 -0
- package/src/usecases/mongo/operation/bulk_write.mjs +54 -0
- package/src/usecases/mongo/operation/count.mjs +16 -16
- package/src/usecases/mongo/operation/delete_many.mjs +15 -15
- package/src/usecases/mongo/operation/delete_one.mjs +16 -16
- package/src/usecases/mongo/operation/find.mjs +34 -34
- package/src/usecases/mongo/operation/find_one.mjs +32 -32
- package/src/usecases/mongo/operation/find_one_and_delete.mjs +23 -0
- package/src/usecases/mongo/operation/find_one_and_replace.mjs +24 -0
- package/src/usecases/mongo/operation/find_one_and_update.mjs +40 -0
- package/src/usecases/mongo/operation/index.mjs +15 -10
- package/src/usecases/mongo/operation/insert_many.mjs +39 -39
- package/src/usecases/mongo/operation/insert_one.mjs +32 -32
- package/src/usecases/mongo/operation/update_many.mjs +39 -39
- package/src/usecases/mongo/operation/update_one.mjs +40 -40
- package/src/usecases/mongo/transformers/id/index.mjs +4 -4
- package/src/usecases/mongo/transformers/id/rename_find_options.mjs +12 -12
- package/src/usecases/mongo/transformers/id/rename_to_dev_id.mjs +14 -13
- package/src/usecases/mongo/transformers/id/rename_to_mongo_id.mjs +34 -13
- package/src/usecases/mongo/transformers/index.mjs +2 -2
- package/src/usecases/mongo/transformers/input_transformer.mjs +32 -32
- package/src/usecases/mongo/transformers/object_id/ids_into_strings.mjs +47 -30
- package/src/usecases/mongo/transformers/object_id/index.mjs +3 -3
- package/src/usecases/mongo/transformers/object_id/strings_into_id.mjs +90 -44
- package/src/usecases/mongo/transformers/output_transformer.mjs +18 -18
- package/src/usecases/mongo/transformers/timestamps.mjs +32 -22
|
@@ -1,41 +1,41 @@
|
|
|
1
|
-
import { Collection } from 'mongodb'
|
|
2
|
-
import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @template {import('../../../types.js').MongoDocument} T
|
|
6
|
-
* @param {object} param
|
|
7
|
-
* @param {import('mongodb').Filter<T>} param.query
|
|
8
|
-
* @param {import('mongodb').UpdateFilter<T>} param.update
|
|
9
|
-
* @param {import('mongodb').UpdateOptions} [param.options]
|
|
10
|
-
* @param {() => Promise<Collection<T>>} param.getCollection
|
|
11
|
-
* @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [param.collectionOptions]
|
|
12
|
-
* @returns {Promise<boolean>}
|
|
13
|
-
*/
|
|
14
|
-
export async function updateOne({ query, update, options, collectionOptions, getCollection }) {
|
|
15
|
-
query = renameToMongoId(query)
|
|
16
|
-
stringsIntoId(query)
|
|
17
|
-
stringsIntoId(update)
|
|
18
|
-
const col = await getCollection()
|
|
19
|
-
|
|
20
|
-
const { timestamps } = collectionOptions ?? {}
|
|
21
|
-
const { updatedAt } = timestamps ?? {
|
|
22
|
-
updatedAt: 'generate'
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const { $set } = update
|
|
26
|
-
const hasUpdatedAt = $set != null && $set.updatedAt != null
|
|
27
|
-
if (!hasUpdatedAt && updatedAt === 'generate') {
|
|
28
|
-
if ($set != null) {
|
|
29
|
-
// @ts-ignore
|
|
30
|
-
$set.updatedAt = new Date()
|
|
31
|
-
} else {
|
|
32
|
-
// @ts-ignore
|
|
33
|
-
update.$set = {
|
|
34
|
-
updatedAt: new Date()
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const r = await col.updateOne(query, update, options)
|
|
40
|
-
return r.matchedCount > 0
|
|
1
|
+
import { Collection } from 'mongodb'
|
|
2
|
+
import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @template {import('../../../types.js').MongoDocument} T
|
|
6
|
+
* @param {object} param
|
|
7
|
+
* @param {import('mongodb').Filter<T>} param.query
|
|
8
|
+
* @param {import('mongodb').UpdateFilter<T>} param.update
|
|
9
|
+
* @param {import('mongodb').UpdateOptions} [param.options]
|
|
10
|
+
* @param {() => Promise<Collection<T>>} param.getCollection
|
|
11
|
+
* @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [param.collectionOptions]
|
|
12
|
+
* @returns {Promise<boolean>}
|
|
13
|
+
*/
|
|
14
|
+
export async function updateOne({ query, update, options, collectionOptions, getCollection }) {
|
|
15
|
+
query = renameToMongoId(query)
|
|
16
|
+
query = stringsIntoId(query)
|
|
17
|
+
update = stringsIntoId(update)
|
|
18
|
+
const col = await getCollection()
|
|
19
|
+
|
|
20
|
+
const { timestamps } = collectionOptions ?? {}
|
|
21
|
+
const { updatedAt } = timestamps ?? {
|
|
22
|
+
updatedAt: 'generate'
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const { $set } = update
|
|
26
|
+
const hasUpdatedAt = $set != null && $set.updatedAt != null
|
|
27
|
+
if (!hasUpdatedAt && updatedAt === 'generate') {
|
|
28
|
+
if ($set != null) {
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
$set.updatedAt = new Date()
|
|
31
|
+
} else {
|
|
32
|
+
// @ts-ignore
|
|
33
|
+
update.$set = {
|
|
34
|
+
updatedAt: new Date()
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const r = await col.updateOne(query, update, options)
|
|
40
|
+
return r.matchedCount > 0
|
|
41
41
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export * from './rename_find_options.mjs'
|
|
2
|
-
export * from './rename_to_dev_id.mjs'
|
|
3
|
-
export * from './rename_to_mongo_id.mjs'
|
|
4
|
-
|
|
1
|
+
export * from './rename_find_options.mjs'
|
|
2
|
+
export * from './rename_to_dev_id.mjs'
|
|
3
|
+
export * from './rename_to_mongo_id.mjs'
|
|
4
|
+
|
|
5
5
|
// Contains use cases related to the id/_id field in an object.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { renameToMongoId } from './index.mjs'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
*
|
|
5
|
-
* @param {import('../../../../types.js').FindOptions<any ,any>} options
|
|
6
|
-
*/
|
|
7
|
-
export function renameFindOptions(options = {}) {
|
|
8
|
-
return {
|
|
9
|
-
...options,
|
|
10
|
-
projection: renameToMongoId(options.projection),
|
|
11
|
-
sort: renameToMongoId(options.sort),
|
|
12
|
-
}
|
|
1
|
+
import { renameToMongoId } from './index.mjs'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
*
|
|
5
|
+
* @param {import('../../../../types.js').FindOptions<any ,any>} options
|
|
6
|
+
*/
|
|
7
|
+
export function renameFindOptions(options = {}) {
|
|
8
|
+
return {
|
|
9
|
+
...options,
|
|
10
|
+
projection: renameToMongoId(options.projection),
|
|
11
|
+
sort: renameToMongoId(options.sort),
|
|
12
|
+
}
|
|
13
13
|
}
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ```
|
|
3
|
-
* "_id" -> "id"
|
|
4
|
-
* ```
|
|
5
|
-
*/
|
|
6
|
-
export function renameToDevId(obj) {
|
|
7
|
-
if (!obj) return obj
|
|
8
|
-
let { _id, ...rest } = obj
|
|
9
|
-
if (
|
|
10
|
-
return
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
/**
|
|
2
|
+
* ```
|
|
3
|
+
* "_id" -> "id"
|
|
4
|
+
* ```
|
|
5
|
+
*/
|
|
6
|
+
export function renameToDevId(obj) {
|
|
7
|
+
if (!obj) return obj
|
|
8
|
+
let { _id, ...rest } = obj
|
|
9
|
+
if (_id === undefined) return obj
|
|
10
|
+
if (_id === null) return rest
|
|
11
|
+
return {
|
|
12
|
+
id: _id,
|
|
13
|
+
...rest
|
|
14
|
+
}
|
|
14
15
|
}
|
|
@@ -1,14 +1,35 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ```
|
|
3
|
-
* "id" -> "_id"
|
|
4
|
-
* ```
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
/**
|
|
2
|
+
* ```
|
|
3
|
+
* "id" -> "_id"
|
|
4
|
+
* ```
|
|
5
|
+
* Recursively renames all `id` keys to `_id` in objects and arrays.
|
|
6
|
+
* Does not mutate the original input.
|
|
7
|
+
* @param {*} obj
|
|
8
|
+
*/
|
|
9
|
+
export function renameToMongoId(obj) {
|
|
10
|
+
if (obj == null) return obj
|
|
11
|
+
if (typeof obj !== 'object') return obj
|
|
12
|
+
if (obj instanceof Date) return obj
|
|
13
|
+
|
|
14
|
+
if (Array.isArray(obj)) {
|
|
15
|
+
let hasChanges = false
|
|
16
|
+
const transformed = new Array(obj.length)
|
|
17
|
+
for (let i = 0; i < obj.length; i++) {
|
|
18
|
+
const item = obj[i]
|
|
19
|
+
const transformedItem = renameToMongoId(item)
|
|
20
|
+
transformed[i] = transformedItem
|
|
21
|
+
if (transformedItem !== item) hasChanges = true
|
|
22
|
+
}
|
|
23
|
+
return hasChanges ? transformed : obj
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let hasChanges = false
|
|
27
|
+
const transformed = {}
|
|
28
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
29
|
+
const newKey = key === 'id' ? '_id' : key
|
|
30
|
+
const newValue = renameToMongoId(value)
|
|
31
|
+
transformed[newKey] = newValue
|
|
32
|
+
if (newKey !== key || newValue !== value) hasChanges = true
|
|
33
|
+
}
|
|
34
|
+
return hasChanges ? transformed : obj
|
|
14
35
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from './id/index.mjs'
|
|
2
|
-
export * from './object_id/index.mjs'
|
|
1
|
+
export * from './id/index.mjs'
|
|
2
|
+
export * from './object_id/index.mjs'
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
import { renameToMongoId } from './id/rename_to_mongo_id.mjs'
|
|
2
|
-
import { stringsIntoId } from './object_id/strings_into_id.mjs'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @template {import('../../../types.js').MongoDocument} T
|
|
6
|
-
* @param {object} params
|
|
7
|
-
* @param {import('../../../types.js').Optional<T, 'id'>} params.document
|
|
8
|
-
* @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [params.collectionOptions]
|
|
9
|
-
*/
|
|
10
|
-
export function inputTransformer({
|
|
11
|
-
document, collectionOptions,
|
|
12
|
-
}) {
|
|
13
|
-
document = renameToMongoId(document)
|
|
14
|
-
stringsIntoId(document)
|
|
15
|
-
|
|
16
|
-
const { timestamps } = collectionOptions ?? {}
|
|
17
|
-
const { createdAt, updatedAt } = timestamps ?? {
|
|
18
|
-
createdAt: 'generate',
|
|
19
|
-
updatedAt: 'generate',
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (document.createdAt == null && createdAt === 'generate') {
|
|
23
|
-
// @ts-ignore
|
|
24
|
-
document.createdAt = new Date()
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (document.updatedAt == null && updatedAt === 'generate') {
|
|
28
|
-
// @ts-ignore
|
|
29
|
-
document.updatedAt = new Date()
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return document
|
|
1
|
+
import { renameToMongoId } from './id/rename_to_mongo_id.mjs'
|
|
2
|
+
import { stringsIntoId } from './object_id/strings_into_id.mjs'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @template {import('../../../types.js').MongoDocument} T
|
|
6
|
+
* @param {object} params
|
|
7
|
+
* @param {import('../../../types.js').Optional<T, 'id'>} params.document
|
|
8
|
+
* @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [params.collectionOptions]
|
|
9
|
+
*/
|
|
10
|
+
export function inputTransformer({
|
|
11
|
+
document, collectionOptions,
|
|
12
|
+
}) {
|
|
13
|
+
document = renameToMongoId(document)
|
|
14
|
+
document = stringsIntoId(document)
|
|
15
|
+
|
|
16
|
+
const { timestamps } = collectionOptions ?? {}
|
|
17
|
+
const { createdAt, updatedAt } = timestamps ?? {
|
|
18
|
+
createdAt: 'generate',
|
|
19
|
+
updatedAt: 'generate',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (document.createdAt == null && createdAt === 'generate') {
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
document.createdAt = new Date()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (document.updatedAt == null && updatedAt === 'generate') {
|
|
28
|
+
// @ts-ignore
|
|
29
|
+
document.updatedAt = new Date()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return document
|
|
33
33
|
}
|
|
@@ -1,30 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Convert the parameter from `ObjectId` into string whenever possible.
|
|
3
|
+
*
|
|
4
|
+
* If an object or array is given, this function will be applied recursively.
|
|
5
|
+
*
|
|
6
|
+
* Uses duck-typing (`_bsontype === 'ObjectId'`) instead of `instanceof` to
|
|
7
|
+
* safely identify ObjectId values across different instances of the `mongodb`
|
|
8
|
+
* / `bson` package (e.g. when the host project and this library resolve to
|
|
9
|
+
* separate copies of the package).
|
|
10
|
+
* @param {*} obj
|
|
11
|
+
*/
|
|
12
|
+
export function idsIntoString(obj) {
|
|
13
|
+
if (obj == null) return obj
|
|
14
|
+
if (typeof obj != 'object') return obj
|
|
15
|
+
if (isObjectId(obj)) return obj.toHexString()
|
|
16
|
+
if (Array.isArray(obj)) {
|
|
17
|
+
for (let i = 0; i < obj.length; i++) {
|
|
18
|
+
const item = obj[i]
|
|
19
|
+
obj[i] = idsIntoString(item)
|
|
20
|
+
}
|
|
21
|
+
return obj
|
|
22
|
+
}
|
|
23
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
24
|
+
if (isObjectId(value)) {
|
|
25
|
+
obj[key] = value.toHexString()
|
|
26
|
+
continue
|
|
27
|
+
}
|
|
28
|
+
if (value != null && typeof value == 'object') {
|
|
29
|
+
obj[key] = idsIntoString(value)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return obj
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Duck-type check for BSON ObjectId values.
|
|
37
|
+
*
|
|
38
|
+
* Using `_bsontype` instead of `instanceof ObjectId` avoids false negatives
|
|
39
|
+
* that occur when the host project and this package resolve to different copies
|
|
40
|
+
* of the `mongodb` / `bson` module, which makes `instanceof` return `false`
|
|
41
|
+
* even for legitimate ObjectId instances.
|
|
42
|
+
* @param {*} value
|
|
43
|
+
* @returns {boolean}
|
|
44
|
+
*/
|
|
45
|
+
function isObjectId(value) {
|
|
46
|
+
return value != null && typeof value == 'object' && value._bsontype === 'ObjectId'
|
|
47
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from './ids_into_strings.mjs'
|
|
2
|
-
export * from './strings_into_id.mjs'
|
|
3
|
-
|
|
1
|
+
export * from './ids_into_strings.mjs'
|
|
2
|
+
export * from './strings_into_id.mjs'
|
|
3
|
+
|
|
4
4
|
// Contains usecases related to the value ObjectId.
|
|
@@ -1,45 +1,91 @@
|
|
|
1
|
-
import { ObjectId } from 'mongodb'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Converts the input value into an `ObjectId`, doing a recursive internal search if possible.
|
|
5
|
-
*
|
|
6
|
-
* This function will do a recursive operation if the parameter is any of the following:
|
|
7
|
-
* - An object;
|
|
8
|
-
* - An array;
|
|
9
|
-
*
|
|
10
|
-
* If the parameter is a string, then it is converted to `ObjectId` if possible.
|
|
11
|
-
*
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
1
|
+
import { ObjectId } from 'mongodb'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Converts the input value into an `ObjectId`, doing a recursive internal search if possible.
|
|
5
|
+
*
|
|
6
|
+
* This function will do a recursive operation if the parameter is any of the following:
|
|
7
|
+
* - An object;
|
|
8
|
+
* - An array;
|
|
9
|
+
*
|
|
10
|
+
* If the parameter is a string, then it is converted to `ObjectId` if possible.
|
|
11
|
+
*
|
|
12
|
+
* This function does not mutate the original input. It preserves reference equality for
|
|
13
|
+
* objects and special types (like Date) that don't need transformation.
|
|
14
|
+
* @param {*} obj
|
|
15
|
+
*/
|
|
16
|
+
export function stringsIntoId(obj) {
|
|
17
|
+
if (obj == null) return obj
|
|
18
|
+
if (isObjectIdString(obj)) return new ObjectId(obj)
|
|
19
|
+
if (typeof obj != 'object') return obj
|
|
20
|
+
|
|
21
|
+
// Check if this object/array needs any transformation
|
|
22
|
+
const transformed = _transformRecursive(obj)
|
|
23
|
+
return transformed
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Recursively transforms ObjectId hex strings to ObjectId instances.
|
|
28
|
+
* Returns the original reference if no changes are needed, or a new object if changes are made.
|
|
29
|
+
* @param {*} obj
|
|
30
|
+
* @returns {*}
|
|
31
|
+
*/
|
|
32
|
+
function _transformRecursive(obj) {
|
|
33
|
+
if (obj == null || typeof obj != 'object') return obj
|
|
34
|
+
|
|
35
|
+
// Don't clone or transform special objects like Date
|
|
36
|
+
if (obj instanceof Date) return obj
|
|
37
|
+
|
|
38
|
+
if (Array.isArray(obj)) {
|
|
39
|
+
let hasChanges = false
|
|
40
|
+
const transformed = new Array(obj.length)
|
|
41
|
+
|
|
42
|
+
for (let i = 0; i < obj.length; i++) {
|
|
43
|
+
const item = obj[i]
|
|
44
|
+
if (isObjectIdString(item)) {
|
|
45
|
+
transformed[i] = new ObjectId(item)
|
|
46
|
+
hasChanges = true
|
|
47
|
+
} else if (item != null && typeof item == 'object') {
|
|
48
|
+
const transformedItem = _transformRecursive(item)
|
|
49
|
+
transformed[i] = transformedItem
|
|
50
|
+
if (transformedItem !== item) {
|
|
51
|
+
hasChanges = true
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
transformed[i] = item
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return hasChanges ? transformed : obj
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Handle plain objects
|
|
62
|
+
let hasChanges = false
|
|
63
|
+
/** @type {Record<string, any>} */
|
|
64
|
+
const transformed = {}
|
|
65
|
+
|
|
66
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
67
|
+
if (isObjectIdString(value)) {
|
|
68
|
+
transformed[key] = ObjectId.createFromHexString(value)
|
|
69
|
+
hasChanges = true
|
|
70
|
+
} else if (value != null && typeof value == 'object') {
|
|
71
|
+
const transformedValue = _transformRecursive(value)
|
|
72
|
+
transformed[key] = transformedValue
|
|
73
|
+
if (transformedValue !== value) {
|
|
74
|
+
hasChanges = true
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
transformed[key] = value
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return hasChanges ? transformed : obj
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
*
|
|
86
|
+
* @param {*} value
|
|
87
|
+
* @returns {value is string}
|
|
88
|
+
*/
|
|
89
|
+
function isObjectIdString(value) {
|
|
90
|
+
return typeof value == 'string' && ObjectId.isValid(value)
|
|
45
91
|
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import { renameToDevId } from './id/rename_to_dev_id.mjs'
|
|
2
|
-
import { idsIntoString } from './object_id/ids_into_strings.mjs'
|
|
3
|
-
import { timestampTransformer } from './timestamps.mjs'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @template {import('../../../types.js').MongoDocument} T
|
|
7
|
-
* @param {object} p
|
|
8
|
-
* @param {import('mongodb').WithId<T> | T} p.document
|
|
9
|
-
* @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [p.collectionOptions]
|
|
10
|
-
*/
|
|
11
|
-
export function outputTransformer({
|
|
12
|
-
document, collectionOptions,
|
|
13
|
-
}) {
|
|
14
|
-
if (collectionOptions) timestampTransformer(document, collectionOptions)
|
|
15
|
-
idsIntoString(document)
|
|
16
|
-
const transformedDoc = renameToDevId(document)
|
|
17
|
-
|
|
18
|
-
return transformedDoc
|
|
1
|
+
import { renameToDevId } from './id/rename_to_dev_id.mjs'
|
|
2
|
+
import { idsIntoString } from './object_id/ids_into_strings.mjs'
|
|
3
|
+
import { timestampTransformer } from './timestamps.mjs'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @template {import('../../../types.js').MongoDocument} T
|
|
7
|
+
* @param {object} p
|
|
8
|
+
* @param {import('mongodb').WithId<T> | T} p.document
|
|
9
|
+
* @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} [p.collectionOptions]
|
|
10
|
+
*/
|
|
11
|
+
export function outputTransformer({
|
|
12
|
+
document, collectionOptions,
|
|
13
|
+
}) {
|
|
14
|
+
if (collectionOptions) timestampTransformer(document, collectionOptions)
|
|
15
|
+
idsIntoString(document)
|
|
16
|
+
const transformedDoc = renameToDevId(document)
|
|
17
|
+
|
|
18
|
+
return transformedDoc
|
|
19
19
|
}
|
|
@@ -1,23 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if (doc.
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
import { ObjectId } from 'mongodb'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Transforms a document by adding or updating timestamp fields (createdAt and updatedAt).
|
|
5
|
+
* If createdAt is not set, it can be generated as a new Date or extracted from the document's ObjectId.
|
|
6
|
+
* If updatedAt is not set, it is generated as a new Date.
|
|
7
|
+
* The document is modified in place.
|
|
8
|
+
*
|
|
9
|
+
* @template {import('../../../types.js').MongoDocument} T
|
|
10
|
+
* @param {import('mongodb').WithId<T> | import('../../../types.js').MongoDocument} doc
|
|
11
|
+
* @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} options
|
|
12
|
+
*/
|
|
13
|
+
export function timestampTransformer(doc, options) {
|
|
14
|
+
const { timestamps } = options
|
|
15
|
+
const { createdAt, updatedAt } = timestamps ?? {
|
|
16
|
+
createdAt: 'generate',
|
|
17
|
+
updatedAt: 'generate',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (doc.createdAt == null) {
|
|
21
|
+
if (createdAt == 'fromId') {
|
|
22
|
+
const _id = doc._id
|
|
23
|
+
if (_id instanceof ObjectId) {
|
|
24
|
+
doc.createdAt = _id.getTimestamp()
|
|
25
|
+
}
|
|
26
|
+
} else if (createdAt == 'generate') {
|
|
27
|
+
doc.createdAt = new Date()
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (doc.updatedAt == null && updatedAt == 'generate') {
|
|
31
|
+
doc.updatedAt = new Date()
|
|
32
|
+
}
|
|
23
33
|
}
|