@lossless.org/client 1.7.0 → 1.9.0

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 (51) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/nosqldb/classes.atomicdelete.d.ts +1 -0
  3. package/dist_ts/nosqldb/classes.atomicdelete.js +10 -7
  4. package/dist_ts/nosqldb/classes.atomicfindoneandupdate.d.ts +2 -0
  5. package/dist_ts/nosqldb/classes.atomicfindoneandupdate.js +9 -5
  6. package/dist_ts/nosqldb/classes.atomicupdate.d.ts +2 -0
  7. package/dist_ts/nosqldb/classes.atomicupdate.js +13 -8
  8. package/dist_ts/nosqldb/classes.collection.d.ts +13 -4
  9. package/dist_ts/nosqldb/classes.collection.js +57 -64
  10. package/dist_ts/nosqldb/classes.db.d.ts +1 -1
  11. package/dist_ts/nosqldb/classes.doc.d.ts +60 -3
  12. package/dist_ts/nosqldb/classes.doc.js +68 -26
  13. package/dist_ts/nosqldb/classes.exactpersistence.d.ts +36 -7
  14. package/dist_ts/nosqldb/classes.exactpersistence.js +92 -81
  15. package/dist_ts/nosqldb/classes.operationdeadline.d.ts +61 -0
  16. package/dist_ts/nosqldb/classes.operationdeadline.js +149 -0
  17. package/dist_ts/nosqldb/classes.persistence.d.ts +16 -1
  18. package/dist_ts/nosqldb/classes.persistence.js +20 -1
  19. package/dist_ts/nosqldb/classes.session.d.ts +73 -2
  20. package/dist_ts/nosqldb/classes.session.js +164 -38
  21. package/dist_ts/objectstorage/classes.bucket.d.ts +8 -3
  22. package/dist_ts/objectstorage/classes.bucket.js +83 -131
  23. package/dist_ts/objectstorage/classes.directory.js +17 -28
  24. package/dist_ts/objectstorage/classes.smartbucket.d.ts +13 -6
  25. package/dist_ts/objectstorage/classes.smartbucket.js +18 -12
  26. package/dist_ts/objectstorage/classes.watcher.js +1 -2
  27. package/dist_ts/objectstorage/interfaces.d.ts +8 -0
  28. package/dist_ts/objectstorage/internal.bucketrequests.d.ts +14 -0
  29. package/dist_ts/objectstorage/internal.bucketrequests.js +53 -0
  30. package/dist_ts/objectstorage/internal.exactupload.capability.d.ts +12 -0
  31. package/dist_ts/objectstorage/internal.exactupload.capability.js +46 -17
  32. package/package.json +2 -2
  33. package/readme.md +14 -7
  34. package/ts/00_commitinfo_data.ts +1 -1
  35. package/ts/nosqldb/classes.atomicdelete.ts +10 -6
  36. package/ts/nosqldb/classes.atomicfindoneandupdate.ts +10 -4
  37. package/ts/nosqldb/classes.atomicupdate.ts +14 -7
  38. package/ts/nosqldb/classes.collection.ts +82 -73
  39. package/ts/nosqldb/classes.db.ts +1 -1
  40. package/ts/nosqldb/classes.doc.ts +162 -33
  41. package/ts/nosqldb/classes.exactpersistence.ts +131 -93
  42. package/ts/nosqldb/classes.operationdeadline.ts +179 -0
  43. package/ts/nosqldb/classes.persistence.ts +36 -1
  44. package/ts/nosqldb/classes.session.ts +221 -39
  45. package/ts/objectstorage/classes.bucket.ts +111 -158
  46. package/ts/objectstorage/classes.directory.ts +18 -27
  47. package/ts/objectstorage/classes.smartbucket.ts +21 -11
  48. package/ts/objectstorage/classes.watcher.ts +0 -1
  49. package/ts/objectstorage/interfaces.ts +9 -0
  50. package/ts/objectstorage/internal.bucketrequests.ts +70 -0
  51. package/ts/objectstorage/internal.exactupload.capability.ts +105 -24
package/readme.md CHANGED
@@ -102,18 +102,23 @@ Family imports preserve the established SmartData and SmartBucket names and cons
102
102
  | Migration-owned indexes | `toleratedIndexNames` on the model — named undeclared indexes are observed, never created, dropped or verified, and no longer make the topology divergent |
103
103
  | Partial index filters in inspection | `inspectNamespace()`, `inspectCollectionTopology()` and `getIndexInfo()` report a partial index's `partialFilterExpression` exactly as the backend lists it, as a frozen snapshot present only on a partial index |
104
104
  | Key-value store readiness | `EasyStore.ensureInitialized()` — installs the store's declared index at startup, outside any transaction window, so a first `readKey()`/`writeKey()` needs no index DDL |
105
+ | Client deadlines | `timeoutMS` (1..120000) on every ordinary model read and write, instance `save()`/`saveIf()`/`delete()` included, on every exact call, and `session.withTransaction(callback, { timeoutMS })` for a whole transaction, retries and commit included; expiry rejects with `SmartdataPersistenceError` code `timeout`, and an exact write interrupted in flight with `ambiguous_write` |
106
+ | Cancellation | `signal` on every exact call, `atomicDeleteMany()` and `session.withTransaction(callback, { signal })`, which interrupts the session's operation in flight, rolls the attempt back and never commits after an abort |
105
107
 
106
108
  `docs/source/smartdata/readme.md` documents each one with its refusals.
107
109
 
108
110
  | `@lossless.org/client/objectstorage` addition | API |
109
111
  | --- | --- |
110
112
  | Listing with size and ETag | `bucket.listObjectEntriesPage({ prefix?, limit, startAfter?, signal? })` — one keyset page of `{ key, size, etag, lastModified? }`; `bucket.listAllObjectEntries(prefix?, { pageSize?, signal? })` iterates every page |
113
+ | Bucket lookup for bucket-scoped credentials | `smartBucket.getBucketByName(name, { signal? })` and `smartBucket.bucketExists(name, { signal? })` send one HeadBucket; `createBucket(name, { signal? })` and `removeBucket(name, { signal? })` take the same options |
114
+
115
+ A bucket lookup sends one HeadBucket for the named bucket, never ListBuckets, so a credential scoped to its bucket can open it without the account-wide list permission, and a lookup does not depend on which buckets a listing returns. A missing bucket (404) keeps its answer: `getBucketByName()` rejects with `Bucket '<name>' not found.` and `bucketExists()` resolves `false`. A bucket the credentials may not access (403) rejects both with an error that names the denial and keeps the provider error as `cause`; S3 does not say whether such a bucket exists. Any other provider failure is rethrown unchanged. `signal` aborts the in-flight request and rejects with the signal's reason. Lookups, creations and removals run as lifecycle-managed operations: `SmartBucket.close()` aborts and awaits them, and the storage client cannot be replaced while one is active.
111
116
 
112
117
  `listObjectEntriesPage()` pages exactly like `listObjectKeysPage()`: `limit` is 1 to 1000, `startAfter` must be a non-empty key under the prefix, entries arrive in strict UTF-8 byte order after it, and `nextStartAfter` is present only while the provider reports more. Keyset pages stay valid when earlier keys are deleted between requests. Every field comes from the one ListObjectsV2 response, so no HEAD is sent per object. `size` is the listed `Size`, the byte length HEAD reports as `ContentLength`. `etag` is the listed `ETag` with its surrounding double quotes removed; HEAD returns the same entity tag, quoted. An entity tag identifies an object version and is not a content digest: AWS S3 derives it from the content MD5 only for single-part uploads without SSE-KMS or SSE-C, and a multipart tag ends in `-<parts>`. `lastModified` is present when the provider sends `LastModified`. Both values describe the object when the page was listed. A page with a missing or non-integer size, a missing or malformed ETag, or any key-page violation is refused rather than returned partially. `signal` aborts the in-flight request and rejects with the signal's reason; the iterator issues one request per page and none while the caller holds an entry, so breaking out ends the listing.
113
118
 
114
119
  ### Document APIs that need a MongoDB backend
115
120
 
116
- The document model runs on both NoSQLDB and MongoDB, but four APIs do not mean the same thing on both. The NoSQLDB engine is qualified at 8.0.2 and 10.6.0 (see [Verification](#verification)); the two releases part on the grouped-totals and guarded-`$expr` rows below — 10.6.0 groups composite keys exactly and evaluates the guard, 8.0.2 does neither — and on whether a migration can create a partial index at all.
121
+ The document model runs on both NoSQLDB and MongoDB, but several APIs do not mean the same thing on both. The NoSQLDB engine is qualified at 8.0.2 and 10.7.0 (see [Verification](#verification)); the two releases part on the grouped-totals and guarded-`$expr` rows below — 10.7.0 groups composite keys exactly and evaluates the guard, 8.0.2 does neither — on whether a migration can create a partial index or a collection validator at all, and on when a transaction learns of a write conflict.
117
122
 
118
123
  `init()` asks the connected engine what it is with one `buildInfo` command and records the answer on the database as `engineIdentity` (`{ product, version }`). NoSQLDB answers with its own `nosqldb` identity document from 10.3.0 onward; a MongoDB server answers `buildInfo` without one; an engine that refuses the command — NoSQLDB 8.0.2 answers `CommandNotFound` — stays `{ product: 'unknown', version: null }`. The identity is what states `capabilities` on a plain `SmartdataDb`, and an unidentified engine states none at all, so every capability-gated call refuses. `NoSqlConnection` keeps stating the capabilities of its declared backend from construction on.
119
124
 
@@ -121,12 +126,14 @@ The document model runs on both NoSQLDB and MongoDB, but four APIs do not mean t
121
126
  | --- | --- | --- |
122
127
  | `getGroupedTotals()` / `getGroupedTotalsPage()` with one `groupBy` field | exact, including `sums` | exact |
123
128
  | the same with **two** `groupBy` fields | exact, `sums` included, on an engine that identifies itself as NoSQLDB 10.5.0 or newer: that release evaluates the composite group key per document. Below it the call is refused with `unsupported_operation` naming both fields and the single-field form, because the engine leaves the composite group key unresolved and answers with a single row whose group values are the literal field paths (`'$owner'`) and whose count is the whole filtered collection; the refusal is the guard against that wrong number, not a fix for it | exact |
124
- | guarded `$expr` atomic filters (`$add`, `$lte`, `$ifNull`) | exact on 10.6.0: the engine evaluates the client's fail-closed compilation (`$cond`, `$let`, `$isNumber`, `$type`), so the guard admits within the limit, refuses beyond it and never matches a stored value that is not a finite number. 8.0.2 rejects the compilation with `invalid argument: $cond` and leaves the document untouched | exact |
129
+ | guarded `$expr` atomic filters (`$add`, `$lte`, `$ifNull`) | exact on 10.6.0 and newer: the engine evaluates the client's fail-closed compilation (`$cond`, `$let`, `$isNumber`, `$type`), so the guard admits within the limit, refuses beyond it and never matches a stored value that is not a finite number. 8.0.2 rejects the compilation with `invalid argument: $cond` and leaves the document untouched | exact |
125
130
  | `watch()` change streams | unsupported. `NoSqlConnection` refuses with `unsupported_capability`, and so does a plain `SmartdataDb` whose engine identified itself; against an engine that answers no identity the refusal instead arrives from the engine as `CommandNotFound` once the change stream is read | supported on replica sets |
126
- | model-declared partial indexes (`partialFilterExpression`) | not expressible: the index-option type rejects it and the runtime refuses it with `invalid_configuration`. A client decision, not an engine limit on every release — 10.6.0 creates partial indexes, 8.0.2 refuses the option with `InvalidOptions` (72) — because one model declaration has to hold on every qualified backend. A migration may create one on 10.6.0; `toleratedIndexNames` accepts it and inspection reports its filter | not expressible, but a migration may own one; inspection reports its filter |
131
+ | model-declared partial indexes (`partialFilterExpression`) | not expressible: the index-option type rejects it and the runtime refuses it with `invalid_configuration`. A client decision, not an engine limit on every release — 10.6.0 and newer create partial indexes, 8.0.2 refuses the option with `InvalidOptions` (72) — because one model declaration has to hold on every qualified backend. A migration may create one on 10.7.0; `toleratedIndexNames` accepts it and inspection reports its filter | not expressible, but a migration may own one; inspection reports its filter |
132
+ | collection validators (`validator`, `validationLevel`, `validationAction`) | no client API; a migration owns a validator through the driver. 10.7.0 creates the collection, lists the options exactly as given and enforces them on every model write: a violating `insert()` or `atomicUpdate()` rejects with the engine's `DocumentValidationFailure` (121) and its `errInfo`, which the client passes through unchanged rather than mapping to a `SmartdataPersistenceError` code, and stores nothing. Models initialize on a validated collection like on any other. 8.0.2 refuses the option with `InvalidOptions` (72) and creates no collection, so a validator is never silently dropped | enforced; not exercised by the client's MongoDB suite |
133
+ | write conflicts between transactions, and writes outside a transaction to a document a transaction holds | 10.7.0 (since 10.6.1) detects a conflict at the statement, as MongoDB does: the first writer wins, and the later transaction's statement rejects with the engine's `WriteConflict` (112) labelled `TransientTransactionError`, which the client passes through so `session.withTransaction()` re-runs that callback. A write outside a transaction waits until the transaction holding its document ends, then applies on top; the wait is bounded by the write's own `timeoutMS` and by the engine's fixed 60-second transaction lifetime, after which a waiting writer aborts the holder. Awaiting such a write inside the holding transaction's callback therefore stalls that transaction until one of the two expires. 8.0.2 detects the conflict at commit: the first transaction to commit wins, a write outside a transaction applies at once, and the transaction that wrote first re-runs. Both writes land on either engine; a callback must be safe to re-run and must not swallow the statement error | first writer wins at the statement and outside writes wait, MongoDB's documented rule; not exercised by the client's MongoDB suite |
127
134
  | aggregation-pipeline (array) updates | refused by the client with `invalid_argument` before any backend is contacted. A client-owned refusal, not an engine limit: both qualified engines apply a pipeline update themselves | same client refusal |
128
135
 
129
- `test/nosqldb/smartdb/test.engine-capability-boundary.node.ts` asserts the identity, grouped-totals, guarded-`$expr`, partial-index and pipeline-update rows against both qualified engines, so a boundary that moves shows up as a failing suite rather than as a changed number in a consumer. The two halves of the grouped-totals and guarded-`$expr` rows are separate tests, each bound to the engine that answers it, and the engine's own composite-key answer stays under assertion through the raw `aggregateGroupedTotals()` boundary — one literal row on 8.0.2, the two exact pairs on 10.6.0. The same boundary pins the rest of the engine's `$group` stage: a non-document accumulator is refused with `TypeMismatch` (14) on 10.6.0 while 8.0.2 answers it, and a `$cond` inside a composite key is evaluated per document on 10.6.0 while 8.0.2 answers its single literal row. It also pins whether a migration can create a partial index — 10.6.0 creates it and lists the filter, 8.0.2 refuses with 72. The `watch()` row is the contract refusal `ts/nosqldb/classes.collection.ts` states; the engine suite opens no change stream.
136
+ `test/nosqldb/smartdb/test.engine-capability-boundary.node.ts` asserts the identity, grouped-totals, guarded-`$expr`, partial-index, collection-validator, write-conflict and pipeline-update rows against both qualified engines, so a boundary that moves shows up as a failing suite rather than as a changed number in a consumer. The two halves of the grouped-totals, guarded-`$expr` and write-conflict rows are separate tests, each bound to the engine that answers it, and the engine's own composite-key answer stays under assertion through the raw `aggregateGroupedTotals()` boundary — one literal row on 8.0.2, the two exact pairs on 10.7.0. The same boundary pins the rest of the engine's `$group` stage: a non-document accumulator is refused with `TypeMismatch` (14) on 10.7.0 while 8.0.2 answers it, and a `$cond` inside a composite key is evaluated per document on 10.7.0 while 8.0.2 answers its single literal row. It also pins whether a migration can create a partial index — 10.7.0 creates it and lists the filter, 8.0.2 refuses with 72 — and a collection validator — 10.7.0 creates and enforces it against model inserts and updates, 8.0.2 refuses with 72 — and which writer a conflict sends back: on 10.7.0 the later transaction fails at its statement and re-runs while a write outside a transaction waits for the holder's commit, on 8.0.2 the later transaction commits first and the holder re-runs. The `watch()` row is the contract refusal `ts/nosqldb/classes.collection.ts` states; the engine suite opens no change stream.
130
137
 
131
138
  ## Relational SQL
132
139
 
@@ -171,7 +178,7 @@ New SQL methods accept `signal` and a total `timeoutMs` (30 seconds by default,
171
178
 
172
179
  `LosslessClientError` provides a safe outer message, a code, optional `backendCode`, `retryability`, `outcome` and acknowledged-row evidence. `cause` preserves the original error and can contain statement values; do not log it indiscriminately. The taxonomy includes invalid arguments, unsupported capability, a missing optional driver, authentication, conflict, timeout, cancellation, result limits, backend failure, partial writes and unknown write outcomes. There are no automatic write retries. A transient hint never establishes that retrying an ambiguous write is safe.
173
180
 
174
- Migrated document and object APIs retain their existing typed errors and exact-operation evidence. Their established per-operation timeout/ownership contracts continue to apply. `capabilities` distinguishes `available`, `unsupported` and `unknown`; MongoDB transaction/change-stream availability still depends on deployment topology. NoSQLDB change streams are explicitly unsupported. `capabilities.compositeGrouping` is the one member with its own vocabulary — `exact` or `unsupported` — and states whether two-field grouped totals are evaluated exactly; MongoDB and NoSQLDB 10.5.0 and newer are `exact`, every earlier or unidentified engine and every relational or object connection is `unsupported`. On a plain `SmartdataDb` the whole statement is `undefined` until `init()` identifies the engine, and an absent statement means "not available". S3 exact-operation capability tests remain explicit and require owned disposable resources.
181
+ Migrated document and object APIs retain their existing typed errors and exact-operation evidence. Their established per-operation timeout/ownership contracts continue to apply. Ordinary document calls and every exact call take a client-side `timeoutMS` that the driver enforces from server selection to the last reply, and `session.withTransaction(callback, { timeoutMS })` bounds a whole transaction; an expired deadline rejects with `SmartdataPersistenceError` code `timeout`, and a write or commit that expired has an unknown outcome, which an exact write reports as `ambiguous_write`. The same exact calls, `atomicDeleteMany()` and `withTransaction()` take an `AbortSignal`; an aborted transaction is rolled back and never committed, while a commit already in flight runs to its own outcome. `docs/source/smartdata/readme.md` describes both, including the two driver cleanups that run on a fresh deadline of the same length after the deadline expired — the closing abort of a transaction whose callback stalled, and the `killCursors` after a stalled `getMore` of a read that needs more than one batch — so either settles within about twice `timeoutMS`. `capabilities` distinguishes `available`, `unsupported` and `unknown`; MongoDB transaction/change-stream availability still depends on deployment topology. NoSQLDB change streams are explicitly unsupported. `capabilities.compositeGrouping` is the one member with its own vocabulary — `exact` or `unsupported` — and states whether two-field grouped totals are evaluated exactly; MongoDB and NoSQLDB 10.5.0 and newer are `exact`, every earlier or unidentified engine and every relational or object connection is `unsupported`. On a plain `SmartdataDb` the whole statement is `undefined` until `init()` identifies the engine, and an absent statement means "not available". S3 exact-operation capability tests remain explicit and require owned disposable resources.
175
182
 
176
183
  ## Migrating existing applications
177
184
 
@@ -206,14 +213,14 @@ Qualified engines:
206
213
 
207
214
  | Component | Qualified versions |
208
215
  | --- | --- |
209
- | NoSQLDB engine | **8.0.2 and 10.6.0** — the same `test/nosqldb/smartdb/` suite runs against both, through the single engine seam in `test/nosqldb/helpers/smartdb.ts`; `NOSQLDB_ENGINE=qualified` selects 10.6.0 |
216
+ | NoSQLDB engine | **8.0.2 and 10.7.0** — the same `test/nosqldb/smartdb/` suite runs against both, through the single engine seam in `test/nosqldb/helpers/smartdb.ts`; `NOSQLDB_ENGINE=qualified` selects 10.7.0 |
210
217
  | MongoDB | 8.0.26 replica sets |
211
218
  | ObjectStorage | 10.3.1 |
212
219
  | SQLDB | 0.2.3 |
213
220
  | MariaDB | 11.8 |
214
221
  | ClickHouse | 25.8 |
215
222
 
216
- Both engine versions run the whole document suite. They part on asserted boundaries — 10.6.0 groups composite keys exactly and answers two-field grouped totals, evaluates guarded `$expr` filters and creates migration-owned partial indexes, 8.0.2 does none of these and the client refuses the two-field form there — which [Document APIs that need a MongoDB backend](#document-apis-that-need-a-mongodb-backend) states row by row. Text search, the Lucene adapter and the broad read-filter surface are exercised against MongoDB only. `pnpm run test:minio` uses a digest-pinned MinIO image: `sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e`. That MinIO version enforces the tested conditional uploads but does not enforce conditional deletion; its exact-purge capability is correctly unavailable. ObjectStorage passes both live probes. AWS S3 has not been qualified against a live account in this migration; no universal S3 exact-operation guarantee is inferred from the SDK.
223
+ Both engine versions run the whole document suite. They part on asserted boundaries — 10.7.0 groups composite keys exactly and answers two-field grouped totals, evaluates guarded `$expr` filters, creates migration-owned partial indexes and collection validators and reports a transaction write conflict at the statement, 8.0.2 does none of these, reports the conflict at commit, and the client refuses the two-field form there — which [Document APIs that need a MongoDB backend](#document-apis-that-need-a-mongodb-backend) states row by row. Text search, the Lucene adapter and the broad read-filter surface are exercised against MongoDB only. `pnpm run test:minio` uses a digest-pinned MinIO image: `sha256:14cea493d9a34af32f524e538b8346cf79f3321eff8e708c1e2960462bd8936e`. That MinIO version enforces the tested conditional uploads but does not enforce conditional deletion; its exact-purge capability is correctly unavailable. ObjectStorage passes both live probes. AWS S3 has not been qualified against a live account in this migration; no universal S3 exact-operation guarantee is inferred from the SDK.
217
224
 
218
225
  ## License and Legal Information
219
226
 
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@lossless.org/client',
6
- version: '1.7.0',
6
+ version: '1.9.0',
7
7
  description: 'One typed client for NoSQLDB, MongoDB, SQLDB, MariaDB, ClickHouse and S3 object storage.'
8
8
  }
@@ -27,14 +27,15 @@ export const executeAtomicDelete = async <T>(
27
27
  ordinaryWrite: true,
28
28
  prepared: collectionArg.isInitializedForCurrentDatabase(),
29
29
  collectionName: collectionArg.collectionName,
30
+ timeoutMS: optsArg?.timeoutMS,
30
31
  },
31
- async (rawSessionArg) => {
32
- await collectionArg.init();
32
+ async (rawSessionArg, deadlineArg) => {
33
+ await deadlineArg.join(collectionArg.init());
33
34
  return collectionArg.mongoDbCollection.deleteOne(
34
35
  filterArg,
35
36
  {
36
37
  session: rawSessionArg,
37
- timeoutMS: optsArg?.timeoutMS,
38
+ ...deadlineArg.commandOptions(),
38
39
  },
39
40
  );
40
41
  },
@@ -55,6 +56,7 @@ export const executeAtomicDeleteMany = async <T>(
55
56
  optsArg?: {
56
57
  session?: TSmartdataOrdinarySession;
57
58
  timeoutMS?: number;
59
+ signal?: AbortSignal;
58
60
  },
59
61
  ): Promise<plugins.mongodb.DeleteResult> => {
60
62
  return runWithOrdinarySmartdataSession(
@@ -64,14 +66,16 @@ export const executeAtomicDeleteMany = async <T>(
64
66
  ordinaryWrite: true,
65
67
  prepared: collectionArg.isInitializedForCurrentDatabase(),
66
68
  collectionName: collectionArg.collectionName,
69
+ timeoutMS: optsArg?.timeoutMS,
70
+ signal: optsArg?.signal,
67
71
  },
68
- async (rawSessionArg) => {
69
- await collectionArg.init();
72
+ async (rawSessionArg, deadlineArg) => {
73
+ await deadlineArg.join(collectionArg.init());
70
74
  return collectionArg.mongoDbCollection.deleteMany(
71
75
  filterArg,
72
76
  {
73
77
  session: rawSessionArg,
74
- timeoutMS: optsArg?.timeoutMS,
78
+ ...deadlineArg.commandOptions(),
75
79
  },
76
80
  );
77
81
  },
@@ -95,6 +95,7 @@ export const executeAtomicFindOneAndUpdate = async <T>(
95
95
  upsert: boolean;
96
96
  sort?: Record<string, 1 | -1>;
97
97
  session?: TSmartdataOrdinarySession;
98
+ timeoutMS?: number;
98
99
  },
99
100
  ): Promise<TAtomicFindOneAndUpdateExecutionResult> => {
100
101
  const policy = collectionArg.getBoundModelSchema()?.ordinaryPersistence;
@@ -109,13 +110,14 @@ export const executeAtomicFindOneAndUpdate = async <T>(
109
110
  ordinaryWrite: true,
110
111
  prepared: collectionArg.isInitializedForCurrentDatabase(),
111
112
  collectionName: collectionArg.collectionName,
113
+ timeoutMS: optionsArg.timeoutMS,
112
114
  },
113
- async (rawSessionArg) => {
115
+ async (rawSessionArg, deadlineArg) => {
114
116
  if (policy && !rawSessionArg?.inTransaction()) {
115
117
  throw new SmartdataPersistenceError('unsupported_operation',
116
118
  'Validated ordinary updates require an active owned transaction.');
117
119
  }
118
- await collectionArg.init();
120
+ await deadlineArg.join(collectionArg.init());
119
121
  try {
120
122
  const result = await collectionArg.mongoDbCollection.findOneAndUpdate(
121
123
  filterArg,
@@ -126,6 +128,7 @@ export const executeAtomicFindOneAndUpdate = async <T>(
126
128
  upsert: optionsArg.upsert,
127
129
  ...(optionsArg.sort ? { sort: optionsArg.sort } : {}),
128
130
  session: rawSessionArg,
131
+ ...deadlineArg.commandOptions(),
129
132
  },
130
133
  );
131
134
  try {
@@ -170,6 +173,7 @@ export const executeAtomicFindOneAndReplace = async <T>(
170
173
  optionsArg: {
171
174
  returnDocument: 'before' | 'after';
172
175
  session?: TSmartdataOrdinarySession;
176
+ timeoutMS?: number;
173
177
  },
174
178
  ): Promise<TAtomicFindOneAndReplaceExecutionResult> => {
175
179
  if (collectionArg.getBoundModelSchema()?.ordinaryPersistence) {
@@ -185,9 +189,10 @@ export const executeAtomicFindOneAndReplace = async <T>(
185
189
  ordinaryWrite: true,
186
190
  prepared: collectionArg.isInitializedForCurrentDatabase(),
187
191
  collectionName: collectionArg.collectionName,
192
+ timeoutMS: optionsArg.timeoutMS,
188
193
  },
189
- async (rawSessionArg) => {
190
- await collectionArg.init();
194
+ async (rawSessionArg, deadlineArg) => {
195
+ await deadlineArg.join(collectionArg.init());
191
196
  try {
192
197
  const result = await collectionArg.mongoDbCollection.findOneAndReplace(
193
198
  filterArg,
@@ -197,6 +202,7 @@ export const executeAtomicFindOneAndReplace = async <T>(
197
202
  returnDocument: optionsArg.returnDocument,
198
203
  upsert: false,
199
204
  session: rawSessionArg,
205
+ ...deadlineArg.commandOptions(),
200
206
  },
201
207
  );
202
208
  const classified = classifyResult(result, {
@@ -20,6 +20,7 @@ export const executeAtomicUpdate = async <T>(
20
20
  optsArg?: {
21
21
  upsert?: boolean;
22
22
  session?: TSmartdataOrdinarySession;
23
+ timeoutMS?: number;
23
24
  },
24
25
  ): Promise<plugins.mongodb.UpdateResult> => {
25
26
  return runWithOrdinarySmartdataSession(
@@ -29,9 +30,10 @@ export const executeAtomicUpdate = async <T>(
29
30
  ordinaryWrite: true,
30
31
  prepared: collectionArg.isInitializedForCurrentDatabase(),
31
32
  collectionName: collectionArg.collectionName,
33
+ timeoutMS: optsArg?.timeoutMS,
32
34
  },
33
- async (rawSessionArg) => {
34
- await collectionArg.init();
35
+ async (rawSessionArg, deadlineArg) => {
36
+ await deadlineArg.join(collectionArg.init());
35
37
  try {
36
38
  return await collectionArg.mongoDbCollection.updateOne(
37
39
  filterArg,
@@ -39,6 +41,7 @@ export const executeAtomicUpdate = async <T>(
39
41
  {
40
42
  upsert: optsArg?.upsert === true,
41
43
  session: rawSessionArg,
44
+ ...deadlineArg.commandOptions(),
42
45
  },
43
46
  );
44
47
  } catch (errorArg) {
@@ -83,9 +86,10 @@ export const executeAtomicUpsertMany = async <T>(
83
86
  ordinaryWrite: true,
84
87
  prepared: collectionArg.isInitializedForCurrentDatabase(),
85
88
  collectionName: collectionArg.collectionName,
89
+ timeoutMS: optsArg?.timeoutMS,
86
90
  },
87
- async (rawSessionArg) => {
88
- await collectionArg.init();
91
+ async (rawSessionArg, deadlineArg) => {
92
+ await deadlineArg.join(collectionArg.init());
89
93
  try {
90
94
  const result = await collectionArg.mongoDbCollection.bulkWrite(
91
95
  operationsArg.map((operationArg) => ({
@@ -98,7 +102,7 @@ export const executeAtomicUpsertMany = async <T>(
98
102
  {
99
103
  ordered: false,
100
104
  session: rawSessionArg,
101
- timeoutMS: optsArg?.timeoutMS,
105
+ ...deadlineArg.commandOptions(),
102
106
  },
103
107
  );
104
108
  return {
@@ -132,6 +136,7 @@ export const executeAtomicUpdateMany = async <T>(
132
136
  updateArg: plugins.mongodb.UpdateFilter<plugins.mongodb.Document>,
133
137
  optsArg?: {
134
138
  session?: TSmartdataOrdinarySession;
139
+ timeoutMS?: number;
135
140
  },
136
141
  ): Promise<plugins.mongodb.UpdateResult> => {
137
142
  return runWithOrdinarySmartdataSession(
@@ -141,15 +146,17 @@ export const executeAtomicUpdateMany = async <T>(
141
146
  ordinaryWrite: true,
142
147
  prepared: collectionArg.isInitializedForCurrentDatabase(),
143
148
  collectionName: collectionArg.collectionName,
149
+ timeoutMS: optsArg?.timeoutMS,
144
150
  },
145
- async (rawSessionArg) => {
146
- await collectionArg.init();
151
+ async (rawSessionArg, deadlineArg) => {
152
+ await deadlineArg.join(collectionArg.init());
147
153
  try {
148
154
  return await collectionArg.mongoDbCollection.updateMany(
149
155
  filterArg,
150
156
  updateArg,
151
157
  {
152
158
  session: rawSessionArg,
159
+ ...deadlineArg.commandOptions(),
153
160
  },
154
161
  );
155
162
  } catch (errorArg) {