assai 2.1.2 → 2.1.4

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.
@@ -1,2 +1,3 @@
1
1
  export * from "./ids_into_strings.mjs";
2
2
  export * from "./strings_into_id.mjs";
3
+ export * from "./transform_options.mjs";
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Applies `stringsIntoId` to the data-bearing fields of an operation's
3
+ * `options`, leaving metadata and special objects untouched.
4
+ *
5
+ * Does not mutate the caller's `options`: a shallow copy is created only when a
6
+ * field actually changes, otherwise the original reference is returned.
7
+ *
8
+ * @template {Record<string, any> | undefined} T
9
+ * @param {T} options
10
+ * @returns {T}
11
+ */
12
+ export function transformOptions<T extends Record<string, any> | undefined>(options: T): T;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assai",
3
- "version": "2.1.2",
3
+ "version": "2.1.4",
4
4
  "repository": {
5
5
  "url": "https://github.com/TimeLord2010/assai",
6
6
  "type": "git"
@@ -1,5 +1,5 @@
1
1
  import { Collection } from 'mongodb'
2
- import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
2
+ import { renameToMongoId, stringsIntoId, transformOptions } from '../transformers/index.mjs'
3
3
  import { outputTransformer } from '../transformers/output_transformer.mjs'
4
4
 
5
5
  /**
@@ -15,6 +15,7 @@ export async function aggregate({ getCollection, pipeline, options, collectionOp
15
15
  const finalPipeline = stringsIntoId(renameToMongoId(pipeline))
16
16
 
17
17
  const col = await getCollection()
18
+ options = transformOptions(options)
18
19
  const docs = await col.aggregate(finalPipeline, options).toArray()
19
20
  // @ts-ignore
20
21
  return docs.map((doc) => outputTransformer({ document: doc, collectionOptions }))
@@ -1,5 +1,5 @@
1
1
  import { Collection } from 'mongodb'
2
- import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
2
+ import { renameToMongoId, stringsIntoId, transformOptions } from '../transformers/index.mjs'
3
3
  import { inputTransformer } from '../transformers/input_transformer.mjs'
4
4
 
5
5
  /**
@@ -23,10 +23,17 @@ export async function bulkWrite({ getCollection, operations, options, collection
23
23
  o.updateOne.filter = renameToMongoId(o.updateOne.filter)
24
24
  o.updateOne.filter = stringsIntoId(o.updateOne.filter)
25
25
  o.updateOne.update = stringsIntoId(o.updateOne.update)
26
+ // `o.updateOne` is a bulk operation descriptor, not an `options` object,
27
+ // but it carries `arrayFilters` at the same top level that transformOptions
28
+ // looks for. `filter`/`update`/`upsert` aren't data-bearing keys, so they
29
+ // are left untouched — this reuses the same conversion without duplicating it.
30
+ o.updateOne = transformOptions(o.updateOne)
26
31
  } else if (o.updateMany != null) {
27
32
  o.updateMany.filter = renameToMongoId(o.updateMany.filter)
28
33
  o.updateMany.filter = stringsIntoId(o.updateMany.filter)
29
34
  o.updateMany.update = stringsIntoId(o.updateMany.update)
35
+ // See the note above: same reuse of transformOptions on the operation descriptor.
36
+ o.updateMany = transformOptions(o.updateMany)
30
37
  } else if (o.deleteOne != null) {
31
38
  o.deleteOne.filter = renameToMongoId(o.deleteOne.filter)
32
39
  o.deleteOne.filter = stringsIntoId(o.deleteOne.filter)
@@ -2,7 +2,8 @@ import { Collection } from 'mongodb'
2
2
  import {
3
3
  renameFindOptions,
4
4
  renameToMongoId,
5
- stringsIntoId
5
+ stringsIntoId,
6
+ transformOptions
6
7
  } from '../transformers/index.mjs'
7
8
  import { outputTransformer } from '../transformers/output_transformer.mjs'
8
9
 
@@ -19,6 +20,7 @@ import { outputTransformer } from '../transformers/output_transformer.mjs'
19
20
  export async function find({ getCollection, query, options, collectionOptions }) {
20
21
  query = renameToMongoId(query)
21
22
  options = renameFindOptions(options)
23
+ options = transformOptions(options)
22
24
 
23
25
  query = stringsIntoId(query)
24
26
 
@@ -1,5 +1,5 @@
1
1
  import { Collection } from 'mongodb'
2
- import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
2
+ import { renameToMongoId, stringsIntoId, transformOptions } from '../transformers/index.mjs'
3
3
  import { outputTransformer } from '../transformers/output_transformer.mjs'
4
4
 
5
5
  /**
@@ -16,6 +16,8 @@ export async function findOneAndDelete({ query, options, collectionOptions, getC
16
16
  query = stringsIntoId(query)
17
17
  const col = await getCollection()
18
18
 
19
+ options = transformOptions(options)
20
+
19
21
  const doc = await col.findOneAndDelete(query, options ?? {})
20
22
  if (doc == null) return null
21
23
 
@@ -1,5 +1,5 @@
1
1
  import { Collection } from 'mongodb'
2
- import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
2
+ import { renameToMongoId, stringsIntoId, transformOptions } from '../transformers/index.mjs'
3
3
  import { outputTransformer } from '../transformers/output_transformer.mjs'
4
4
 
5
5
  /**
@@ -17,6 +17,8 @@ export async function findOneAndReplace({ query, replacement, options, collectio
17
17
  query = stringsIntoId(query)
18
18
  const col = await getCollection()
19
19
 
20
+ options = transformOptions(options)
21
+
20
22
  const doc = await col.findOneAndReplace(query, replacement, options ?? {})
21
23
  if (doc == null) return null
22
24
 
@@ -1,5 +1,5 @@
1
1
  import { Collection } from 'mongodb'
2
- import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
2
+ import { renameToMongoId, stringsIntoId, transformOptions } from '../transformers/index.mjs'
3
3
  import { outputTransformer } from '../transformers/output_transformer.mjs'
4
4
 
5
5
  /**
@@ -33,6 +33,8 @@ export async function findOneAndUpdate({ query, update, options, collectionOptio
33
33
  }
34
34
  }
35
35
 
36
+ options = transformOptions(options)
37
+
36
38
  const doc = await col.findOneAndUpdate(query, update, options ?? {})
37
39
  if (doc == null) return null
38
40
 
@@ -1,5 +1,6 @@
1
1
  import { Collection, ObjectId } from 'mongodb'
2
2
  import { inputTransformer } from '../transformers/input_transformer.mjs'
3
+ import { outputTransformer } from '../transformers/output_transformer.mjs'
3
4
 
4
5
  /**
5
6
  * @template {import('../../../types.js').MongoDocument} T
@@ -30,11 +31,16 @@ export async function insertMany({ docs, collectionOptions, getCollection }) {
30
31
  let { insertedIds } = result
31
32
  const indexes = Object.keys(insertedIds)
32
33
  for (const index of indexes) {
34
+ // @ts-ignore
33
35
  const id = insertedIds[index]
34
36
  if (id instanceof ObjectId) {
37
+ // @ts-ignore
35
38
  docs[index].id = id.toHexString()
36
39
  }
37
40
  }
38
- // @ts-ignore
39
- return docs
41
+ return docs.map(doc => outputTransformer({
42
+ // @ts-ignore
43
+ document: doc,
44
+ collectionOptions
45
+ }))
40
46
  }
@@ -1,5 +1,6 @@
1
1
  import { Collection, ObjectId } from 'mongodb'
2
2
  import { inputTransformer } from '../transformers/input_transformer.mjs'
3
+ import { outputTransformer } from '../transformers/output_transformer.mjs'
3
4
 
4
5
  /**
5
6
  * @template {import('../../../types.js').MongoDocument} T
@@ -26,8 +27,12 @@ export async function insertOne({ doc, collectionOptions, getCollection }) {
26
27
  id = id.toHexString()
27
28
  }
28
29
 
29
- delete doc._id
30
-
31
30
  // @ts-ignore
32
- return { id, ...doc }
31
+ doc.id = id
32
+
33
+ return outputTransformer({
34
+ // @ts-ignore
35
+ document: doc,
36
+ collectionOptions,
37
+ })
33
38
  }
@@ -1,5 +1,5 @@
1
1
  import { Collection } from 'mongodb'
2
- import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
2
+ import { renameToMongoId, stringsIntoId, transformOptions } from '../transformers/index.mjs'
3
3
 
4
4
  /**
5
5
  * @template {import('../../../types.js').MongoDocument} T
@@ -34,6 +34,8 @@ export async function updateMany({ query, update, options, collectionOptions, ge
34
34
  }
35
35
  }
36
36
 
37
+ options = transformOptions(options)
38
+
37
39
  const col = await getCollection()
38
40
  const r = await col.updateMany(query, update, options)
39
41
  return r
@@ -1,5 +1,5 @@
1
1
  import { Collection } from 'mongodb'
2
- import { renameToMongoId, stringsIntoId } from '../transformers/index.mjs'
2
+ import { renameToMongoId, stringsIntoId, transformOptions } from '../transformers/index.mjs'
3
3
 
4
4
  /**
5
5
  * @template {import('../../../types.js').MongoDocument} T
@@ -36,6 +36,8 @@ export async function updateOne({ query, update, options, collectionOptions, get
36
36
  }
37
37
  }
38
38
 
39
+ options = transformOptions(options)
40
+
39
41
  const r = await col.updateOne(query, update, options)
40
42
  return r.matchedCount > 0
41
43
  }
@@ -1,2 +1,2 @@
1
1
  export * from './id/index.mjs'
2
- export * from './object_id/index.mjs'
2
+ export * from './object_id/index.mjs'
@@ -1,4 +1,5 @@
1
1
  export * from './ids_into_strings.mjs'
2
2
  export * from './strings_into_id.mjs'
3
+ export * from './transform_options.mjs'
3
4
 
4
5
  // Contains usecases related to the value ObjectId.
@@ -0,0 +1,52 @@
1
+ import { stringsIntoId } from './strings_into_id.mjs'
2
+
3
+ /**
4
+ * Option fields that carry actual data values (documents/queries that may
5
+ * contain ObjectId hex-strings), as opposed to metadata like sort directions,
6
+ * index names or write concerns. These are the only fields that need the same
7
+ * hex-string → `ObjectId` conversion applied to `query` and `update`.
8
+ *
9
+ * - `arrayFilters`: filters referencing filtered positional operators.
10
+ * - `let`: variables accessible via `$$var` in `$expr`/pipeline updates.
11
+ * - `min`/`max`: inclusive/exclusive index bounds (find only).
12
+ * - `projection`: normally inclusion/exclusion flags, but may embed a query
13
+ * through operators like `$elemMatch`.
14
+ *
15
+ * Fields such as `session` (`ClientSession`), `signal` (`AbortSignal`),
16
+ * `readPreference` and `writeConcern` are deliberately left out: `stringsIntoId`
17
+ * only preserves `Date`, so recursing into those class instances would deep-clone
18
+ * them into plain objects and break them. Scoping to data-bearing keys keeps the
19
+ * conversion safe.
20
+ */
21
+ const DATA_BEARING_KEYS = ['arrayFilters', 'let', 'min', 'max', 'projection']
22
+
23
+ /**
24
+ * Applies `stringsIntoId` to the data-bearing fields of an operation's
25
+ * `options`, leaving metadata and special objects untouched.
26
+ *
27
+ * Does not mutate the caller's `options`: a shallow copy is created only when a
28
+ * field actually changes, otherwise the original reference is returned.
29
+ *
30
+ * @template {Record<string, any> | undefined} T
31
+ * @param {T} options
32
+ * @returns {T}
33
+ */
34
+ export function transformOptions(options) {
35
+ if (options == null) return options
36
+
37
+ let result = options
38
+ for (const key of DATA_BEARING_KEYS) {
39
+ const value = options[key]
40
+ if (value == null) continue
41
+
42
+ const transformed = stringsIntoId(value)
43
+ if (transformed !== value) {
44
+ if (result === options) {
45
+ result = { ...options }
46
+ }
47
+ result[key] = transformed
48
+ }
49
+ }
50
+
51
+ return result
52
+ }