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.
Files changed (60) hide show
  1. package/README.md +148 -148
  2. package/dist/src/__test/mock_get_collection.d.mts +2 -0
  3. package/dist/src/factories/create_mongo_collection.d.mts +55 -11
  4. package/dist/src/usecases/mongo/operation/aggregate.d.mts +16 -0
  5. package/dist/src/usecases/mongo/operation/bulk_write.d.mts +15 -0
  6. package/dist/src/usecases/mongo/operation/find_one_and_delete.d.mts +16 -0
  7. package/dist/src/usecases/mongo/operation/find_one_and_replace.d.mts +18 -0
  8. package/dist/src/usecases/mongo/operation/find_one_and_update.d.mts +18 -0
  9. package/dist/src/usecases/mongo/operation/index.d.mts +5 -0
  10. package/dist/src/usecases/mongo/transformers/id/rename_to_mongo_id.d.mts +3 -0
  11. package/dist/src/usecases/mongo/transformers/object_id/ids_into_strings.d.mts +5 -0
  12. package/dist/src/usecases/mongo/transformers/object_id/strings_into_id.d.mts +3 -0
  13. package/dist/src/usecases/mongo/transformers/timestamps.d.mts +5 -0
  14. package/index.mjs +2 -2
  15. package/jsconfig.json +17 -20
  16. package/jsconfig.prod.json +12 -12
  17. package/package.json +40 -39
  18. package/src/__test/manage_mock_database.mjs +24 -24
  19. package/src/__test/manage_mock_registry.mjs +54 -54
  20. package/src/__test/mock_get_collection.mjs +22 -20
  21. package/src/data/errors/operation_fail.mjs +9 -9
  22. package/src/data/errors/unsupported_error.mjs +9 -9
  23. package/src/data/interfaces/mongo_operator.mjs +2 -2
  24. package/src/data/interfaces/native_collection.mjs +27 -27
  25. package/src/factories/create_mock_collection.mjs +173 -173
  26. package/src/factories/create_mongo_collection.mjs +224 -181
  27. package/src/factories/index.mjs +1 -1
  28. package/src/types.ts +31 -31
  29. package/src/usecases/index.mjs +1 -1
  30. package/src/usecases/mongo/generate_new_id.mjs +4 -4
  31. package/src/usecases/mongo/index.mjs +3 -3
  32. package/src/usecases/mongo/mongo_client.mjs +36 -36
  33. package/src/usecases/mongo/operation/additional/delete_one_or_throw.mjs +17 -17
  34. package/src/usecases/mongo/operation/additional/index.mjs +1 -1
  35. package/src/usecases/mongo/operation/aggregate.mjs +21 -0
  36. package/src/usecases/mongo/operation/bulk_write.mjs +54 -0
  37. package/src/usecases/mongo/operation/count.mjs +16 -16
  38. package/src/usecases/mongo/operation/delete_many.mjs +15 -15
  39. package/src/usecases/mongo/operation/delete_one.mjs +16 -16
  40. package/src/usecases/mongo/operation/find.mjs +34 -34
  41. package/src/usecases/mongo/operation/find_one.mjs +32 -32
  42. package/src/usecases/mongo/operation/find_one_and_delete.mjs +23 -0
  43. package/src/usecases/mongo/operation/find_one_and_replace.mjs +24 -0
  44. package/src/usecases/mongo/operation/find_one_and_update.mjs +40 -0
  45. package/src/usecases/mongo/operation/index.mjs +15 -10
  46. package/src/usecases/mongo/operation/insert_many.mjs +39 -39
  47. package/src/usecases/mongo/operation/insert_one.mjs +32 -32
  48. package/src/usecases/mongo/operation/update_many.mjs +39 -39
  49. package/src/usecases/mongo/operation/update_one.mjs +40 -40
  50. package/src/usecases/mongo/transformers/id/index.mjs +4 -4
  51. package/src/usecases/mongo/transformers/id/rename_find_options.mjs +12 -12
  52. package/src/usecases/mongo/transformers/id/rename_to_dev_id.mjs +14 -13
  53. package/src/usecases/mongo/transformers/id/rename_to_mongo_id.mjs +34 -13
  54. package/src/usecases/mongo/transformers/index.mjs +2 -2
  55. package/src/usecases/mongo/transformers/input_transformer.mjs +32 -32
  56. package/src/usecases/mongo/transformers/object_id/ids_into_strings.mjs +47 -30
  57. package/src/usecases/mongo/transformers/object_id/index.mjs +3 -3
  58. package/src/usecases/mongo/transformers/object_id/strings_into_id.mjs +90 -44
  59. package/src/usecases/mongo/transformers/output_transformer.mjs +18 -18
  60. 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 (!_id) return obj
10
- return {
11
- id: _id,
12
- ...rest
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
- export function renameToMongoId(obj) {
7
- if (!obj) return obj
8
- let { id, ...rest } = obj
9
- if (!id) return obj
10
- return {
11
- _id: id,
12
- ...rest
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
- import { ObjectId } from 'mongodb'
2
-
3
- /**
4
- * Convert the parameter from `ObjectId` into string whenever possible.
5
- *
6
- * If an object or array is given, this function will be applied recursively.
7
- * @param {*} obj
8
- */
9
- export function idsIntoString(obj) {
10
- if (obj == null) return obj
11
- if (typeof obj != 'object') return obj
12
- if (obj instanceof ObjectId) return obj.toHexString()
13
- if (Array.isArray(obj)) {
14
- for (let i = 0; i < obj.length; i++) {
15
- const item = obj[i]
16
- obj[i] = idsIntoString(item)
17
- }
18
- return obj
19
- }
20
- for (const [key, value] of Object.entries(obj)) {
21
- if (value instanceof ObjectId) {
22
- obj[key] = value.toHexString()
23
- continue
24
- }
25
- if (value != null && typeof value == 'object') {
26
- obj[key] = idsIntoString(value)
27
- }
28
- }
29
- return obj
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
- * @param {*} obj
12
- */
13
- export function stringsIntoId(obj) {
14
- if (obj == null) return obj
15
- if (isObjectIdString(obj)) return new ObjectId(obj)
16
- if (typeof obj != 'object') return obj
17
- if (Array.isArray(obj)) {
18
- // We clone the array to prevent the original array reference to change.
19
- obj = [...obj]
20
-
21
- for (let i = 0; i < obj.length; i++) {
22
- const item = obj[i]
23
- obj[i] = stringsIntoId(item)
24
- }
25
- return obj
26
- }
27
- for (const [key, value] of Object.entries(obj)) {
28
- if (isObjectIdString(value)) {
29
- obj[key] = ObjectId.createFromHexString(value)
30
- }
31
- if (value != null && typeof value == 'object') {
32
- obj[key] = stringsIntoId(value)
33
- }
34
- }
35
- return obj
36
- }
37
-
38
- /**
39
- *
40
- * @param {*} value
41
- * @returns {value is string}
42
- */
43
- function isObjectIdString(value) {
44
- return typeof value == 'string' && ObjectId.isValid(value)
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
- * @template {import('../../../types.js').MongoDocument} T
3
- * @param {import('mongodb').WithId<T> | import('../../../types.js').MongoDocument} doc
4
- * @param {import('../../../factories/create_mongo_collection.mjs').IcreateCollectionOptions<T>} options
5
- */
6
- export function timestampTransformer(doc, options) {
7
- const { timestamps } = options
8
- const { createdAt, updatedAt } = timestamps ?? {
9
- createdAt: 'generate',
10
- updatedAt: 'generate',
11
- }
12
-
13
- if (doc.createdAt == null) {
14
- if (createdAt == 'fromId') {
15
- doc.createdAt = doc._id.getTimestamp()
16
- } else if (createdAt == 'generate') {
17
- doc.createdAt = new Date()
18
- }
19
- }
20
- if (doc.updatedAt == null && updatedAt == 'generate') {
21
- doc.updatedAt = new Date()
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
  }