monsqlize 2.0.5 → 3.0.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.
- package/CHANGELOG.md +14 -5
- package/MIGRATION.md +105 -0
- package/README.md +183 -189
- package/SECURITY.md +21 -0
- package/changelogs/README.md +16 -4
- package/changelogs/v2.0.6.md +16 -0
- package/changelogs/v2.0.7.md +63 -0
- package/changelogs/v3.0.0.md +97 -0
- package/dist/cjs/cli/data-task.cjs +17768 -0
- package/dist/cjs/index.cjs +12166 -5505
- package/dist/cjs/transaction/Transaction.cjs +151 -19
- package/dist/cjs/transaction/TransactionManager.cjs +152 -20
- package/dist/esm/index.mjs +12171 -5509
- package/dist/types/base.d.mts +81 -0
- package/dist/types/collection.d.mts +1156 -0
- package/dist/types/collection.d.ts +138 -15
- package/dist/types/data-tasks.d.mts +221 -0
- package/dist/types/data-tasks.d.ts +221 -0
- package/dist/types/expression.d.mts +115 -0
- package/dist/types/index.d.mts +24 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/lock.d.mts +77 -0
- package/dist/types/lock.d.ts +7 -4
- package/dist/types/model.d.mts +666 -0
- package/dist/types/model.d.ts +46 -14
- package/dist/types/mongodb.d.mts +56 -0
- package/dist/types/monsqlize.d.mts +638 -0
- package/dist/types/monsqlize.d.ts +130 -16
- package/dist/types/pool.d.mts +84 -0
- package/dist/types/runtime.d.mts +400 -0
- package/dist/types/runtime.d.ts +18 -5
- package/dist/types/saga.d.mts +148 -0
- package/dist/types/saga.d.ts +11 -6
- package/dist/types/slow-query-log.d.mts +126 -0
- package/dist/types/sync.d.mts +149 -0
- package/dist/types/sync.d.ts +47 -1
- package/dist/types/transaction.d.mts +152 -0
- package/dist/types/transaction.d.ts +8 -0
- package/package.json +32 -14
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# monSQLize
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Database-native production data runtime layer for TypeScript services. monSQLize keeps database semantics explicit while adding shared runtime capabilities for caching, transactions, connection pools, models, synchronization, observability, and CommonJS / ESM / TypeScript declaration outputs.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/monsqlize)
|
|
6
6
|
[](https://www.apache.org/licenses/LICENSE-2.0)
|
|
@@ -8,45 +8,64 @@ TypeScript-native MongoDB ODM and enhancement layer with v1-compatible APIs, mul
|
|
|
8
8
|
[](https://nodejs.org/)
|
|
9
9
|
|
|
10
10
|
Documentation: [English](https://vextjs.github.io/monSQLize/) · [简体中文](https://vextjs.github.io/monSQLize/zh/)
|
|
11
|
+
Quick path: [Installation](https://vextjs.github.io/monSQLize/getting-started) · [Basic Usage](https://vextjs.github.io/monSQLize/basic-operations)
|
|
12
|
+
Configuration reference: [English](https://vextjs.github.io/monSQLize/configuration) · [简体中文](https://vextjs.github.io/monSQLize/zh/configuration)
|
|
13
|
+
Upgrade and security: [Migration Guide](./MIGRATION.md) · [Private Vulnerability Reporting](./SECURITY.md)
|
|
14
|
+
|
|
15
|
+
The npm `latest` dist-tag and GitHub Pages are the stable channel; `main` may contain the next release. Pages deployment accepts only a versioned Git tag whose package version is already present on npm, so unpublished APIs are not promoted as stable documentation.
|
|
11
16
|
|
|
12
17
|
```bash
|
|
13
18
|
npm install monsqlize
|
|
14
19
|
```
|
|
15
20
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
## Table of Contents
|
|
19
|
-
|
|
20
|
-
- [Why monSQLize](#why-monsqlize)
|
|
21
|
-
- [When to Use It](#when-to-use-it)
|
|
22
|
-
- [Installation](#installation)
|
|
23
|
-
- [Quick Start](#quick-start)
|
|
24
|
-
- [Model Layer](#model-layer)
|
|
25
|
-
- [Caching and Performance](#caching-and-performance)
|
|
26
|
-
- [Advanced Capabilities](#advanced-capabilities)
|
|
27
|
-
- [Migration from the MongoDB Driver](#migration-from-the-mongodb-driver)
|
|
28
|
-
- [Compatibility](#compatibility)
|
|
29
|
-
- [Documentation](#documentation)
|
|
30
|
-
- [Development](#development)
|
|
31
|
-
- [Release Status](#release-status)
|
|
32
|
-
- [Roadmap](#roadmap)
|
|
33
|
-
- [License](#license)
|
|
34
|
-
- [Support](#support)
|
|
21
|
+
MongoDB is the first complete adapter. MySQL and PostgreSQL adapters are planned as database-native runtime adapters, not as a transparent promise that every database already accepts the same query syntax.
|
|
35
22
|
|
|
36
23
|
## Why monSQLize
|
|
37
24
|
|
|
38
|
-
monSQLize
|
|
25
|
+
monSQLize is not an ORM and it is not just a CRUD wrapper. It is a production data runtime layer: the database driver remains visible, while the operational features teams usually build around the driver are provided in one runtime.
|
|
39
26
|
|
|
40
|
-
-
|
|
41
|
-
- Smart caching through `cache-hub`, including local memory caching, optional Redis-backed L2 caching, automatic invalidation
|
|
27
|
+
- Database-native adapter APIs: the current stable adapter preserves MongoDB-style CRUD, aggregation, indexes, transactions, and Change Streams.
|
|
28
|
+
- Smart database caching through `cache-hub`, including local memory caching, optional Redis-backed L2 caching, and automatic invalidation.
|
|
42
29
|
- A lightweight Model layer with `schema-dsl` validation, hooks, relations, populate, custom methods, timestamps, soft delete, optimistic locking, and production-safe index preflight.
|
|
43
30
|
- Multi-connection-pool support, pool health checks, pool-scoped collections/models, and fallback strategies.
|
|
44
|
-
- Business locks and distributed locks for multi-instance deployments.
|
|
45
|
-
- Saga orchestration for multi-step business workflows.
|
|
46
31
|
- Change Stream sync helpers with resume token storage.
|
|
47
32
|
- Slow-query logging and query diagnostics.
|
|
48
33
|
- CommonJS, ESM, and TypeScript declaration outputs from `dist/**`.
|
|
49
34
|
|
|
35
|
+
## Adapter Status
|
|
36
|
+
|
|
37
|
+
| Adapter | Current status | Public entry |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| MongoDB | Stable and fully implemented | `type: 'mongodb'`, `collection()`, `db()`, `use()`, `pool()` |
|
|
40
|
+
| MySQL | Planned / in development | Not part of the current npm runtime yet |
|
|
41
|
+
| PostgreSQL | Planned / in development | Not part of the current npm runtime yet |
|
|
42
|
+
|
|
43
|
+
The shared runtime direction is cache consistency, connection lifecycle, transaction helpers, model constraints, synchronization, and observability across adapters. Adapter-native query, transaction, and connection semantics will stay explicit.
|
|
44
|
+
|
|
45
|
+
## Runtime Consistency Contract
|
|
46
|
+
|
|
47
|
+
monSQLize coordinates cache, transactions, sync, and queues inside the runtime, but it does not provide a global strict-consistency kernel. MongoDB transactions keep MongoDB session ACID semantics. Query caching is opt-in, and write-side cache invalidation is explicit: use `cache.invalidate` for precise entries or `autoInvalidate` / `cache.autoInvalidate` for broad invalidation. Cache invalidation runs after successful writes or transaction commit and is best-effort; Redis/L2 cache and Pub/Sub invalidation provide eventual cross-instance coherence rather than atomic cache/DB commits. Change Stream sync is at-least-once; custom targets should be idempotent by change event `_id`, and `sync.idempotency` can record per-target replay markers when a durable store is provided. Transaction cache locks are process-local in the v2 runtime; use explicit business coordination, idempotency/fencing, or cache bypassing for cross-instance strict flows.
|
|
48
|
+
|
|
49
|
+
## Write Path Policy
|
|
50
|
+
|
|
51
|
+
By default, both `collection()` and `model()` may write. Applications that want all writes for selected namespaces to pass through Model defaults, hooks, timestamps, versioning, and soft-delete rules can enable `writePathPolicy`.
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const msq = new MonSQLize({
|
|
55
|
+
type: 'mongodb',
|
|
56
|
+
databaseName: 'app',
|
|
57
|
+
config: { uri: 'mongodb://localhost:27017' },
|
|
58
|
+
writePathPolicy: {
|
|
59
|
+
default: 'model-only',
|
|
60
|
+
namespaces: {
|
|
61
|
+
'app.audit_logs': 'allow-both'
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`model-only` blocks direct collection/db/legacy write surfaces by default, including raw access and `$out` / `$merge` aggregate writes, while Model write and Model management methods remain available. See [Write Path Policy](./docs/en/write-path-policy.md).
|
|
68
|
+
|
|
50
69
|
## When to Use It
|
|
51
70
|
|
|
52
71
|
monSQLize is a good fit when you need:
|
|
@@ -54,8 +73,9 @@ monSQLize is a good fit when you need:
|
|
|
54
73
|
| Scenario | Benefit |
|
|
55
74
|
|---|---|
|
|
56
75
|
| High-concurrency reads | Cache hot data and reduce repeated database work. |
|
|
57
|
-
| MongoDB API compatibility | Keep familiar
|
|
58
|
-
|
|
|
76
|
+
| MongoDB API compatibility today | Keep familiar MongoDB syntax while adding higher-level runtime helpers. |
|
|
77
|
+
| A future multi-database runtime boundary | Prepare for MySQL/PostgreSQL adapters without turning SQL into fake MongoDB syntax. |
|
|
78
|
+
| Multi-instance services | Use Redis invalidation and pool health checks to keep instances coordinated. |
|
|
59
79
|
| Transaction-heavy flows | Use `withTransaction()` and transaction-aware helpers instead of hand-rolled lifecycle code. |
|
|
60
80
|
| Model-level ergonomics | Add schema validation, hooks, populate, and custom methods only where needed. |
|
|
61
81
|
| Smooth upgrade from v1 | Keep legacy application source stable while adopting the TypeScript rewrite. |
|
|
@@ -68,37 +88,32 @@ monSQLize is usually not the best first choice for pure write-heavy workloads, e
|
|
|
68
88
|
npm install monsqlize
|
|
69
89
|
```
|
|
70
90
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
- `mongodb` - official MongoDB driver.
|
|
74
|
-
- `schema-dsl` - model schema validation runtime dependency.
|
|
75
|
-
- `cache-hub` - cache and function-cache foundation.
|
|
76
|
-
- `async-lock` - local concurrency lock support.
|
|
77
|
-
- `ioredis` - Redis-backed L2 cache, distributed invalidation, and distributed lock support.
|
|
78
|
-
- `ssh2` - SSH tunnel support for restricted/private network deployment.
|
|
91
|
+
Use Node.js 18 or newer and provide a MongoDB connection URI. Optional Redis, SSH tunnel, cache, Model, and sync features are configured only when your application enables them.
|
|
79
92
|
|
|
80
93
|
## Quick Start
|
|
81
94
|
|
|
95
|
+
The current stable quick start uses the MongoDB adapter.
|
|
96
|
+
|
|
82
97
|
### CommonJS
|
|
83
98
|
|
|
84
99
|
```js
|
|
85
100
|
const MonSQLize = require('monsqlize');
|
|
86
101
|
|
|
87
|
-
const
|
|
102
|
+
const msq = new MonSQLize({
|
|
88
103
|
type: 'mongodb',
|
|
89
104
|
databaseName: 'mydb',
|
|
90
105
|
config: {
|
|
91
106
|
uri: 'mongodb://localhost:27017'
|
|
92
107
|
},
|
|
93
108
|
cache: {
|
|
94
|
-
|
|
95
|
-
|
|
109
|
+
maxEntries: 10000,
|
|
110
|
+
defaultTtl: 60_000
|
|
96
111
|
}
|
|
97
112
|
});
|
|
98
113
|
|
|
99
|
-
await
|
|
114
|
+
await msq.connect();
|
|
100
115
|
|
|
101
|
-
const users =
|
|
116
|
+
const users = msq.collection('users');
|
|
102
117
|
|
|
103
118
|
await users.insertOne({
|
|
104
119
|
username: 'john',
|
|
@@ -107,23 +122,25 @@ await users.insertOne({
|
|
|
107
122
|
});
|
|
108
123
|
|
|
109
124
|
const user = await users.findOne({ email: 'john@example.com' });
|
|
110
|
-
const
|
|
125
|
+
const sameUser = await users.findOne({ _id: '507f1f77bcf86cd799439011' });
|
|
111
126
|
|
|
112
127
|
await users.updateOne(
|
|
113
128
|
{ email: 'john@example.com' },
|
|
114
129
|
{ $set: { lastLoginAt: new Date() } }
|
|
115
130
|
);
|
|
116
131
|
|
|
117
|
-
await
|
|
132
|
+
await msq.close();
|
|
118
133
|
```
|
|
119
134
|
|
|
135
|
+
Next, use the [Basic Usage guide](https://vextjs.github.io/monSQLize/basic-operations) for common CRUD, pagination, read cache, and Model entry-point examples.
|
|
136
|
+
|
|
120
137
|
### ESM and TypeScript
|
|
121
138
|
|
|
122
139
|
```ts
|
|
123
140
|
import MonSQLize from 'monsqlize';
|
|
124
141
|
import type { Collection } from 'monsqlize';
|
|
125
142
|
|
|
126
|
-
const
|
|
143
|
+
const msq = new MonSQLize({
|
|
127
144
|
type: 'mongodb',
|
|
128
145
|
databaseName: 'mydb',
|
|
129
146
|
config: {
|
|
@@ -131,12 +148,12 @@ const db = new MonSQLize({
|
|
|
131
148
|
}
|
|
132
149
|
});
|
|
133
150
|
|
|
134
|
-
await
|
|
151
|
+
await msq.connect();
|
|
135
152
|
|
|
136
|
-
const users: Collection =
|
|
153
|
+
const users: Collection = msq.collection('users');
|
|
137
154
|
const activeUsers = await users.find({ status: 'active' }).toArray();
|
|
138
155
|
|
|
139
|
-
await
|
|
156
|
+
await msq.close();
|
|
140
157
|
```
|
|
141
158
|
|
|
142
159
|
Published entry points:
|
|
@@ -153,7 +170,7 @@ The package root exports only the public package contract. Deep imports into his
|
|
|
153
170
|
|
|
154
171
|
The Model layer is optional. Use it when you want schema validation, hooks, relations, populate, custom methods, timestamps, soft delete, or optimistic locking.
|
|
155
172
|
|
|
156
|
-
`schema-dsl`
|
|
173
|
+
Models use the current `schema-dsl/runtime` path through monSQLize. monSQLize creates an isolated schema runtime for each `MonSQLize` instance, then compiles Model schema callbacks when a Model is bound to that runtime.
|
|
157
174
|
|
|
158
175
|
### Manual Model Registration
|
|
159
176
|
|
|
@@ -162,7 +179,7 @@ const MonSQLize = require('monsqlize');
|
|
|
162
179
|
const { Model } = MonSQLize;
|
|
163
180
|
|
|
164
181
|
Model.define('users', {
|
|
165
|
-
schema: (
|
|
182
|
+
schema: (s) => s({
|
|
166
183
|
username: 'string:3-32!',
|
|
167
184
|
email: 'email!',
|
|
168
185
|
password: 'string:6-!',
|
|
@@ -198,15 +215,15 @@ Model.define('users', {
|
|
|
198
215
|
})
|
|
199
216
|
});
|
|
200
217
|
|
|
201
|
-
const
|
|
218
|
+
const msq = new MonSQLize({
|
|
202
219
|
type: 'mongodb',
|
|
203
220
|
databaseName: 'mydb',
|
|
204
221
|
config: { uri: 'mongodb://localhost:27017' }
|
|
205
222
|
});
|
|
206
223
|
|
|
207
|
-
await
|
|
224
|
+
await msq.connect();
|
|
208
225
|
|
|
209
|
-
const User =
|
|
226
|
+
const User = msq.model('users');
|
|
210
227
|
const user = await User.insertOne({
|
|
211
228
|
username: 'john',
|
|
212
229
|
email: 'john@example.com',
|
|
@@ -215,29 +232,58 @@ const user = await User.insertOne({
|
|
|
215
232
|
});
|
|
216
233
|
```
|
|
217
234
|
|
|
235
|
+
When a service needs runtime-local custom types, messages, locale, or an already-owned schema-dsl runtime, configure `schemaDsl` on the MonSQLize instance:
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
const { createRuntime } = require('schema-dsl/runtime');
|
|
239
|
+
|
|
240
|
+
const schemaRuntime = createRuntime({
|
|
241
|
+
types: {
|
|
242
|
+
tenantId: { type: 'string', pattern: '^tenant_[a-z0-9]+$' }
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
Model.define('tenantUsers', {
|
|
247
|
+
schema: (s) => s({
|
|
248
|
+
tenantId: 'tenantId!',
|
|
249
|
+
email: 'email!'
|
|
250
|
+
})
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const msq = new MonSQLize({
|
|
254
|
+
type: 'mongodb',
|
|
255
|
+
databaseName: 'mydb',
|
|
256
|
+
config: { uri: 'mongodb://localhost:27017' },
|
|
257
|
+
schemaDsl: { runtime: schemaRuntime }
|
|
258
|
+
});
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
For monSQLize-only Model validation, no direct application import from `schema-dsl` is required. Direct application imports should use `schema-dsl/runtime` when they need to share the same isolated runtime state with monSQLize.
|
|
262
|
+
Use `schemaDsl: { extensions }` when monSQLize should own the runtime and register extension definitions. When the application owns the schema-dsl lifecycle, configure that runtime directly, including `runtime.registerExtensions([...])`, and inject it with `schemaDsl: { runtime }`. If the default `schema-dsl/runtime` entry cannot be resolved or does not expose the required runtime APIs, monSQLize throws `INVALID_CONFIG`; validation is disabled only when `schemaDsl: false` or `schemaDsl: { enabled: false }` is set explicitly.
|
|
263
|
+
|
|
218
264
|
### Automatic Model Loading
|
|
219
265
|
|
|
220
266
|
```js
|
|
221
267
|
const path = require('path');
|
|
222
268
|
const MonSQLize = require('monsqlize');
|
|
223
269
|
|
|
224
|
-
const
|
|
270
|
+
const msq = new MonSQLize({
|
|
225
271
|
type: 'mongodb',
|
|
226
272
|
databaseName: 'mydb',
|
|
227
273
|
config: { uri: 'mongodb://localhost:27017' },
|
|
228
274
|
models: path.join(__dirname, 'models')
|
|
229
275
|
});
|
|
230
276
|
|
|
231
|
-
await
|
|
277
|
+
await msq.connect();
|
|
232
278
|
|
|
233
|
-
const User =
|
|
279
|
+
const User = msq.model('users');
|
|
234
280
|
```
|
|
235
281
|
|
|
236
282
|
```js
|
|
237
283
|
// models/user.model.js
|
|
238
284
|
module.exports = {
|
|
239
285
|
name: 'users',
|
|
240
|
-
schema: (
|
|
286
|
+
schema: (s) => s({
|
|
241
287
|
username: 'string:3-32!',
|
|
242
288
|
email: 'email!'
|
|
243
289
|
}),
|
|
@@ -258,27 +304,56 @@ Relative model paths are resolved from `process.cwd()`. In production services,
|
|
|
258
304
|
Model-declared indexes are still created automatically by default for backward compatibility. Production services can turn off automatic indexing and run an explicit preflight before creating missing indexes:
|
|
259
305
|
|
|
260
306
|
```js
|
|
261
|
-
const
|
|
307
|
+
const msq = new MonSQLize({
|
|
262
308
|
type: 'mongodb',
|
|
263
309
|
databaseName: 'mydb',
|
|
264
310
|
config: { uri: 'mongodb://localhost:27017' },
|
|
265
311
|
autoIndex: false
|
|
266
312
|
});
|
|
267
313
|
|
|
268
|
-
const plan = await
|
|
314
|
+
const plan = await msq.ensureModelIndexes({ models: ['users'], dryRun: true });
|
|
269
315
|
|
|
270
316
|
if (plan.totals.conflicts === 0) {
|
|
271
|
-
await
|
|
317
|
+
await msq.ensureModelIndexes({ models: ['users'], throwOnError: true });
|
|
272
318
|
}
|
|
273
319
|
```
|
|
274
320
|
|
|
275
321
|
`ensureModelIndexes()` creates only missing indexes. It does not drop, rename, or rebuild conflicting indexes.
|
|
276
322
|
|
|
323
|
+
### Versioned Writes
|
|
324
|
+
|
|
325
|
+
Models with `options.version` use single-document optimistic concurrency control. When the filter contains a direct `_id`, monSQLize reads the current version automatically. You can still pass an explicit version in the filter or as `expectedVersion` when you want to control the expected version yourself:
|
|
326
|
+
|
|
327
|
+
```js
|
|
328
|
+
await User.updateOne(
|
|
329
|
+
{ _id: userId },
|
|
330
|
+
{ $set: { status: 'active' } }
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
await User.replaceOne(
|
|
334
|
+
{ _id: userId },
|
|
335
|
+
nextUser,
|
|
336
|
+
{ expectedVersion: user.version }
|
|
337
|
+
);
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Successful `save()`, `updateOne()`, `replaceOne()`, `findOneAndUpdate()`, and `findOneAndReplace()` advance the version. Stale writes throw a `WRITE_CONFLICT` error.
|
|
341
|
+
|
|
342
|
+
For `updateMany()` and `updateBatch()`, choose the batch version behavior explicitly when needed:
|
|
343
|
+
|
|
344
|
+
```js
|
|
345
|
+
await User.updateMany({ status: 'pending' }, { $set: { status: 'active' } }, {
|
|
346
|
+
versionMode: 'strict' // 'counter' | 'strict' | 'off'
|
|
347
|
+
});
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
`counter` is the default and only increments the version counter; it is not optimistic locking. `strict` pre-reads matching document IDs and versions, conditionally updates each document, and returns `conflictCount` / `conflictedIds` on the result object.
|
|
351
|
+
|
|
277
352
|
### Populate
|
|
278
353
|
|
|
279
354
|
```js
|
|
280
355
|
Model.define('posts', {
|
|
281
|
-
schema: (
|
|
356
|
+
schema: (s) => s({
|
|
282
357
|
title: 'string:1-200!',
|
|
283
358
|
content: 'string!',
|
|
284
359
|
userId: 'objectId!'
|
|
@@ -296,12 +371,20 @@ const userWithPosts = await User.findOne({ username: 'john' })
|
|
|
296
371
|
|
|
297
372
|
Populate is supported by `find()`, `findOne()`, `findByIds()`, `findOneById()`, `findAndCount()`, and `findPage()`.
|
|
298
373
|
|
|
374
|
+
For has-many relations, `skip` and `limit` are applied per parent document after related records are grouped. Nested populate has a default `maxDepth` of `5`; set `maxDepth` on a populate branch when a self-referencing relation needs a different cap.
|
|
375
|
+
|
|
376
|
+
### Soft Delete Visibility
|
|
377
|
+
|
|
378
|
+
When `softDelete` is enabled, standard model read paths hide deleted documents by default: `find()`, `findOne()`, `findOneById()`, `findByIds()`, `findPage()`, `findAndCount()`, `count()`, `distinct()`, `aggregate()`, `stream()`, and `explain()`. Pass `withDeleted: true` to include deleted documents or `onlyDeleted: true` to query only deleted documents.
|
|
379
|
+
|
|
380
|
+
For aggregation, monSQLize prepends a soft-delete `$match` stage before the user pipeline, or after `$geoNear` when that stage is first. Soft-delete filtering inside user-authored `$lookup` pipelines remains the application's responsibility.
|
|
381
|
+
|
|
299
382
|
## Caching and Performance
|
|
300
383
|
|
|
301
|
-
monSQLize can cache collection queries and
|
|
384
|
+
monSQLize can cache collection queries and coordinate local/Redis-backed invalidation for database runtime usage.
|
|
302
385
|
|
|
303
386
|
```js
|
|
304
|
-
const users =
|
|
387
|
+
const users = msq.collection('users');
|
|
305
388
|
|
|
306
389
|
const hotUser = await users.findOne(
|
|
307
390
|
{ email: 'john@example.com' },
|
|
@@ -309,51 +392,36 @@ const hotUser = await users.findOne(
|
|
|
309
392
|
);
|
|
310
393
|
```
|
|
311
394
|
|
|
312
|
-
```js
|
|
313
|
-
const { withCache } = require('monsqlize');
|
|
314
|
-
|
|
315
|
-
async function getUserProfile(userId) {
|
|
316
|
-
const user = await db.collection('users').findOneById(userId);
|
|
317
|
-
const orders = await db.collection('orders').find({ userId }).toArray();
|
|
318
|
-
return { user, orders };
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
const cachedGetUserProfile = withCache(getUserProfile, {
|
|
322
|
-
ttl: 300_000,
|
|
323
|
-
cache: db.getCache()
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
await cachedGetUserProfile('user-1');
|
|
327
|
-
```
|
|
328
|
-
|
|
329
395
|
Cache capabilities include:
|
|
330
396
|
|
|
331
397
|
- In-memory L1 cache.
|
|
332
398
|
- Optional Redis-backed L2 cache.
|
|
333
399
|
- Automatic invalidation after writes.
|
|
334
|
-
-
|
|
335
|
-
- In-flight request deduplication.
|
|
336
|
-
- Namespaces, TTLs, statistics, and conditional caching.
|
|
400
|
+
- Cache namespace, TTL, and distributed invalidation controls.
|
|
337
401
|
|
|
338
402
|
## Advanced Capabilities
|
|
339
403
|
|
|
340
404
|
### Transactions
|
|
341
405
|
|
|
342
406
|
```js
|
|
343
|
-
await
|
|
344
|
-
await
|
|
345
|
-
await
|
|
407
|
+
await msq.withTransaction(async (tx) => {
|
|
408
|
+
await msq.collection('orders').insertOne({ userId, status: 'pending' }, { session: tx.session });
|
|
409
|
+
await msq.collection('users').updateOne(
|
|
346
410
|
{ _id: userId },
|
|
347
411
|
{ $inc: { orderCount: 1 } },
|
|
348
|
-
{ session }
|
|
412
|
+
{ session: tx.session }
|
|
349
413
|
);
|
|
350
414
|
});
|
|
351
415
|
```
|
|
352
416
|
|
|
417
|
+
Read options such as `session`, `readConcern`, `readPreference`, `collation`, `hint`, `maxTimeMS`, and `allowDiskUse` are forwarded to the MongoDB driver. Query caches are bypassed while a `session` is present, so transaction reads use the driver snapshot instead of a shared cache entry.
|
|
418
|
+
|
|
419
|
+
Transaction cache invalidations are recorded during the transaction and flushed only after a successful commit. If the transaction aborts, pending invalidations are discarded.
|
|
420
|
+
|
|
353
421
|
### Connection Pools
|
|
354
422
|
|
|
355
423
|
```js
|
|
356
|
-
const
|
|
424
|
+
const msq = new MonSQLize({
|
|
357
425
|
type: 'mongodb',
|
|
358
426
|
databaseName: 'main',
|
|
359
427
|
config: { uri: 'mongodb://primary:27017' },
|
|
@@ -362,24 +430,13 @@ const db = new MonSQLize({
|
|
|
362
430
|
]
|
|
363
431
|
});
|
|
364
432
|
|
|
365
|
-
const reports =
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
### Distributed Locks
|
|
369
|
-
|
|
370
|
-
```js
|
|
371
|
-
await db.withLock('inventory:sku-1', async () => {
|
|
372
|
-
await db.collection('inventory').updateOne(
|
|
373
|
-
{ sku: 'sku-1' },
|
|
374
|
-
{ $inc: { stock: -1 } }
|
|
375
|
-
);
|
|
376
|
-
});
|
|
433
|
+
const reports = msq.pool('analytics').collection('reports');
|
|
377
434
|
```
|
|
378
435
|
|
|
379
436
|
### Change Streams
|
|
380
437
|
|
|
381
438
|
```js
|
|
382
|
-
const watcher =
|
|
439
|
+
const watcher = msq.collection('orders').watch([
|
|
383
440
|
{ $match: { 'fullDocument.status': 'pending' } }
|
|
384
441
|
]);
|
|
385
442
|
|
|
@@ -388,25 +445,6 @@ watcher.on('change', (change) => {
|
|
|
388
445
|
});
|
|
389
446
|
```
|
|
390
447
|
|
|
391
|
-
### Saga Orchestration
|
|
392
|
-
|
|
393
|
-
```js
|
|
394
|
-
db.defineSaga('checkout', [
|
|
395
|
-
{
|
|
396
|
-
name: 'reserveInventory',
|
|
397
|
-
execute: async (ctx) => reserveInventory(ctx),
|
|
398
|
-
compensate: async (ctx) => releaseInventory(ctx)
|
|
399
|
-
},
|
|
400
|
-
{
|
|
401
|
-
name: 'chargePayment',
|
|
402
|
-
execute: async (ctx) => chargePayment(ctx),
|
|
403
|
-
compensate: async (ctx) => refundPayment(ctx)
|
|
404
|
-
}
|
|
405
|
-
]);
|
|
406
|
-
|
|
407
|
-
await db.executeSaga('checkout', { orderId });
|
|
408
|
-
```
|
|
409
|
-
|
|
410
448
|
## Migration from the MongoDB Driver
|
|
411
449
|
|
|
412
450
|
The smallest migration is usually to replace only initialization:
|
|
@@ -421,15 +459,15 @@ const nativeUsers = nativeClient.db('mydb').collection('users');
|
|
|
421
459
|
```js
|
|
422
460
|
const MonSQLize = require('monsqlize');
|
|
423
461
|
|
|
424
|
-
const
|
|
462
|
+
const msq = new MonSQLize({
|
|
425
463
|
type: 'mongodb',
|
|
426
464
|
databaseName: 'mydb',
|
|
427
465
|
config: { uri: 'mongodb://localhost:27017' },
|
|
428
466
|
cache: { enabled: true }
|
|
429
467
|
});
|
|
430
468
|
|
|
431
|
-
await
|
|
432
|
-
const users =
|
|
469
|
+
await msq.connect();
|
|
470
|
+
const users = msq.collection('users');
|
|
433
471
|
```
|
|
434
472
|
|
|
435
473
|
MongoDB-style collection calls can remain unchanged in most cases:
|
|
@@ -446,7 +484,7 @@ The v2 line has been validated against the workspace consumers `chat`, `payment`
|
|
|
446
484
|
| Surface | Current Support |
|
|
447
485
|
|---|---|
|
|
448
486
|
| Node.js | `>=18.0.0`; CI covers Node 18 / 20 / 22. |
|
|
449
|
-
| MongoDB driver | `mongodb@6.21.0` runtime baseline; driver 7.
|
|
487
|
+
| MongoDB driver | `mongodb@6.21.0` runtime baseline; driver 7.5.0 has additional compatibility coverage. |
|
|
450
488
|
| MongoDB server | Memory-server based 6.x / 7.x validation is covered by the project test matrix. |
|
|
451
489
|
| Module systems | CommonJS and ESM are both validated. |
|
|
452
490
|
| TypeScript | Public declarations are published from `dist/types/index.d.ts`. |
|
|
@@ -456,9 +494,8 @@ See the current support and verification documents:
|
|
|
456
494
|
|
|
457
495
|
- [English documentation](https://github.com/vextjs/monSQLize/blob/main/docs/en/README.md)
|
|
458
496
|
- [Chinese documentation](https://github.com/vextjs/monSQLize/blob/main/docs/zh/README.md)
|
|
459
|
-
- [English
|
|
497
|
+
- [English common scenarios](https://github.com/vextjs/monSQLize/blob/main/docs/en/recipes.md)
|
|
460
498
|
- [Support matrix](https://github.com/vextjs/monSQLize/blob/main/docs/en/support-matrix.md)
|
|
461
|
-
- [Verification entry points](https://github.com/vextjs/monSQLize/blob/main/docs/en/verification-entrypoints.md)
|
|
462
499
|
- [test/compatibility/README.md](https://github.com/vextjs/monSQLize/blob/main/test/compatibility/README.md)
|
|
463
500
|
- [test/validation/VERIFICATION-PROGRESS.md](https://github.com/vextjs/monSQLize/blob/main/test/validation/VERIFICATION-PROGRESS.md)
|
|
464
501
|
|
|
@@ -467,12 +504,15 @@ See the current support and verification documents:
|
|
|
467
504
|
Current TypeScript documentation and examples are the source of truth for the v2 package:
|
|
468
505
|
|
|
469
506
|
- Complete docs: [English](https://vextjs.github.io/monSQLize/) · [简体中文](https://vextjs.github.io/monSQLize/zh/)
|
|
470
|
-
-
|
|
471
|
-
-
|
|
472
|
-
-
|
|
473
|
-
-
|
|
474
|
-
-
|
|
475
|
-
-
|
|
507
|
+
- Constructor configuration: [English](https://vextjs.github.io/monSQLize/configuration) · [简体中文](https://vextjs.github.io/monSQLize/zh/configuration)
|
|
508
|
+
- Production rollout: [English](https://vextjs.github.io/monSQLize/production-rollout) · [简体中文](https://vextjs.github.io/monSQLize/zh/production-rollout)
|
|
509
|
+
- Production data migration: [English](https://vextjs.github.io/monSQLize/production-data-migration) · [简体中文](https://vextjs.github.io/monSQLize/zh/production-data-migration)
|
|
510
|
+
- Data tasks API: [English](https://vextjs.github.io/monSQLize/data-tasks) · [简体中文](https://vextjs.github.io/monSQLize/zh/data-tasks)
|
|
511
|
+
- Common scenarios: [English](https://vextjs.github.io/monSQLize/recipes) · [简体中文](https://vextjs.github.io/monSQLize/zh/recipes)
|
|
512
|
+
- Example source: [examples index](https://github.com/vextjs/monSQLize/blob/main/examples/README.md)
|
|
513
|
+
- Documentation source: [docs/en](https://github.com/vextjs/monSQLize/tree/main/docs/en) · [docs/zh](https://github.com/vextjs/monSQLize/tree/main/docs/zh)
|
|
514
|
+
- Compatibility checks: [test/compatibility](https://github.com/vextjs/monSQLize/tree/main/test/compatibility)
|
|
515
|
+
- Verification mapping: [test/validation](https://github.com/vextjs/monSQLize/tree/main/test/validation)
|
|
476
516
|
|
|
477
517
|
Historical v1 assets are useful for tracing old behavior, but they are not the current publishing surface for v2.
|
|
478
518
|
|
|
@@ -495,9 +535,9 @@ npm run verify:full
|
|
|
495
535
|
npm run release:preflight
|
|
496
536
|
```
|
|
497
537
|
|
|
498
|
-
|
|
538
|
+
The package self-check command runs linting, documentation/type/size/runtime/compatibility/refactor guards, the default test suite, 90% source coverage, all runnable examples, the MongoDB server matrix, real dataTasks and CLI integration, production dependency audit, a temporary package-install smoke, and a final `npm pack --dry-run`.
|
|
499
539
|
|
|
500
|
-
`npm run release:publish` runs the
|
|
540
|
+
`npm run release:publish` runs the package self-check once and then calls `npm publish --ignore-scripts` so the final publish step does not repeat the full lifecycle check. Raw `npm publish` is still guarded by `prepublishOnly`.
|
|
501
541
|
|
|
502
542
|
Optional commands:
|
|
503
543
|
|
|
@@ -507,76 +547,30 @@ npm run test:examples
|
|
|
507
547
|
npm run test:coverage
|
|
508
548
|
npm run test:audit
|
|
509
549
|
npm run test:server-matrix
|
|
550
|
+
npm run test:data-tasks:integration
|
|
551
|
+
npm run test:data-task-cli
|
|
552
|
+
npm run test:pack-install
|
|
510
553
|
npm run test:real-env:private
|
|
511
554
|
```
|
|
512
555
|
|
|
513
|
-
`check:docs-examples` verifies the
|
|
514
|
-
|
|
515
|
-
`test:examples`, `test:server-matrix`, and `config.useMemoryServer` use a fixed `mongodb-memory-server` policy: MongoDB `7.0.14` by default, binaries cached under `.cache/mongodb-memory-server/binaries`, and temporary data paths created under `.cache/mongodb-memory-server/db` with forced cleanup for project-managed paths. Stale managed data paths whose owner PID is no longer alive are pruned before new memory-server launches. Override with `MONSQLIZE_MEMORY_MONGO_BINARY_VERSION`, `MONSQLIZE_REPLSET_BINARY_VERSION`, `MONGOMS_DOWNLOAD_DIR`, or `MONSQLIZE_MEMORY_SERVER_DB_DIR` when needed.
|
|
516
|
-
|
|
517
|
-
`test:coverage` is the independent 90% coverage governance gate for the published CJS runtime artifact. `test:audit` checks production dependencies against the npm registry. `test:real-env:private` is intentionally opt-in and expects private environment variables. Coverage and private real-environment checks are not part of the default CI or release gate.
|
|
556
|
+
`check:docs-examples` verifies the bilingual documentation matrix, runnable-example runner parity, shared-example targets, doc-check targets, and user-facing path text.
|
|
518
557
|
|
|
519
|
-
|
|
558
|
+
`test:examples`, `test:server-matrix`, and `config.useMemoryServer` use a fixed `mongodb-memory-server` policy: MongoDB `7.0.37` by default, binaries cached under `.cache/mongodb-memory-server/binaries`, and temporary data paths created under `.cache/mongodb-memory-server/db` with forced cleanup for project-managed paths. The release matrix strictly requires MongoDB `7.0.37` and `8.0.26`; a missing binary, failed probe, or failed combination exits nonzero. Stale managed data paths whose owner PID is no longer alive are pruned before new memory-server launches. Override with `MONSQLIZE_MEMORY_MONGO_BINARY_VERSION`, `MONSQLIZE_REPLSET_BINARY_VERSION`, `MONGOMS_DOWNLOAD_DIR`, or `MONSQLIZE_MEMORY_SERVER_DB_DIR` when needed.
|
|
520
559
|
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
Key release-readiness points:
|
|
524
|
-
|
|
525
|
-
- TypeScript rewrite completed for the current runtime and test entry points.
|
|
526
|
-
- Package exports are consolidated under `dist/cjs`, `dist/esm`, and `dist/types`.
|
|
527
|
-
- npm packages include the runtime bundles and declaration files only; source maps are disabled by default and can be generated locally with `MONSQLIZE_BUILD_SOURCEMAPS=1 npm run build`.
|
|
528
|
-
- v1 smooth-upgrade compatibility has been validated against the target workspace consumers.
|
|
529
|
-
- `schema-dsl` follows the npm `latest` TypeScript line `schema-dsl@2.0.9`; deprecated `2.3.x` mistake releases are intentionally excluded.
|
|
530
|
-
- GitHub Actions publishes to npm from `v*` tags after running `npm run release:preflight`; the publish step skips duplicate lifecycle scripts because the gate already ran in the same job.
|
|
560
|
+
`test:coverage` remains an independently runnable 90% governance command for the published CJS runtime plus source-level gap coverage resolved through sourcemaps, and the single-source release preflight requires it. Public CI runs that same preflight in its release-gate job. `test:audit` checks production dependencies against the npm registry. Only `test:real-env:private` remains opt-in because it expects private environment variables.
|
|
531
561
|
|
|
532
562
|
## Roadmap
|
|
533
563
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
-
|
|
537
|
-
-
|
|
538
|
-
- User-facing capability and verification documentation wording cleaned up, plus documentation home experience refinements.
|
|
539
|
-
|
|
540
|
-
### v2.0.3
|
|
541
|
-
|
|
542
|
-
- v1 compatibility patch for documented `findPage({ cache })` behavior.
|
|
543
|
-
- Public transaction and distributed cache invalidator statistics APIs.
|
|
544
|
-
- Standalone documentation-site link safety and bilingual docs consistency fixes.
|
|
545
|
-
- Release preflight alignment for bilingual docs paths and production dependency audit.
|
|
546
|
-
|
|
547
|
-
### v2.0.2
|
|
548
|
-
|
|
549
|
-
- Deterministic dependency metadata patch.
|
|
550
|
-
- `ioredis` and `ssh2` are installed with monSQLize by default, so Redis-backed cache/locks and SSH tunnels no longer require a separate dependency install step.
|
|
551
|
-
|
|
552
|
-
### v2.0.1
|
|
553
|
-
|
|
554
|
-
- v1 smooth-upgrade compatibility patch for Model actual collection names, scoped pools/databases, automatic-index dedupe, and cache/pool option aliases.
|
|
555
|
-
- Documentation and public types aligned with the current runtime behavior.
|
|
556
|
-
|
|
557
|
-
### v2.0.0
|
|
558
|
-
|
|
559
|
-
- TypeScript-native runtime and declarations.
|
|
560
|
-
- v1 smooth-upgrade compatibility bridge.
|
|
561
|
-
- Multi-level cache and function-cache support through `cache-hub`.
|
|
562
|
-
- Transactions, business locks, distributed locks, Saga orchestration, connection pools, Change Stream sync, and slow-query logging.
|
|
563
|
-
- Model layer with `schema-dsl` validation, relations, populate, hooks, and custom methods.
|
|
564
|
-
|
|
565
|
-
### v2.x
|
|
566
|
-
|
|
564
|
+
- MongoDB remains the stable adapter and the current production runtime.
|
|
565
|
+
- MySQL and PostgreSQL adapters will be introduced as database-native adapters under the same production runtime contract.
|
|
566
|
+
- Adapter status will move from planned to alpha/stable only after runtime support, public types, examples, and verification coverage are present.
|
|
567
|
+
- The project does not currently promise production-ready "one query syntax automatically adapts to every database" behavior.
|
|
567
568
|
- Query analyzer improvements.
|
|
568
569
|
- Automatic index suggestions.
|
|
569
|
-
-
|
|
570
|
+
- Additional data-task observability and large-scope operational probes.
|
|
570
571
|
- GraphQL integration experiments.
|
|
571
572
|
- More real-environment validation coverage.
|
|
572
573
|
|
|
573
|
-
### v3.0+
|
|
574
|
-
|
|
575
|
-
- Unified API experiments for MySQL.
|
|
576
|
-
- Unified API experiments for PostgreSQL.
|
|
577
|
-
- Broader ORM capabilities.
|
|
578
|
-
- Cross-database sync middleware.
|
|
579
|
-
|
|
580
574
|
## License
|
|
581
575
|
|
|
582
576
|
monSQLize is released under the [Apache License 2.0](./LICENSE).
|