monsqlize 2.0.6 → 3.1.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 +12 -5
- package/MIGRATION.md +127 -0
- package/README.md +194 -202
- package/SECURITY.md +21 -0
- package/changelogs/README.md +16 -4
- package/changelogs/v2.0.7.md +63 -0
- package/changelogs/v3.0.0.md +99 -0
- package/changelogs/v3.1.0-rc.0.md +23 -0
- package/changelogs/v3.1.0.md +24 -0
- package/dist/cjs/cli/data-task.cjs +380 -0
- package/dist/cjs/index.cjs +12261 -5497
- package/dist/cjs/transaction/Transaction.cjs +151 -19
- package/dist/cjs/transaction/TransactionManager.cjs +152 -20
- package/dist/esm/index.mjs +12265 -5500
- package/dist/types/collection.d.mts +92 -13
- package/dist/types/collection.d.ts +92 -13
- package/dist/types/data-tasks.d.mts +221 -0
- package/dist/types/data-tasks.d.ts +221 -0
- package/dist/types/index.d.mts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/lock.d.mts +7 -4
- package/dist/types/lock.d.ts +7 -4
- package/dist/types/model.d.mts +46 -14
- package/dist/types/model.d.ts +46 -14
- package/dist/types/monsqlize.d.mts +130 -16
- package/dist/types/monsqlize.d.ts +130 -16
- package/dist/types/runtime.d.mts +14 -1
- package/dist/types/runtime.d.ts +14 -1
- package/dist/types/saga.d.mts +11 -6
- package/dist/types/saga.d.ts +11 -6
- package/dist/types/sync.d.mts +47 -1
- package/dist/types/sync.d.ts +47 -1
- package/dist/types/transaction.d.mts +8 -0
- package/dist/types/transaction.d.ts +8 -0
- package/licenses/production-dependencies.json +11 -0
- package/package.json +45 -16
package/README.md
CHANGED
|
@@ -1,52 +1,73 @@
|
|
|
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)
|
|
7
|
-
[](https://www.mongodb.com/)
|
|
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.
|
|
16
|
+
|
|
17
|
+
This source tree contains the stable `3.1.0` release candidate, pinned to verified registry `schema-dsl@3.0.0`. The v3 package remains subject to the full release gate before its immutable tag and npm publication.
|
|
11
18
|
|
|
12
19
|
```bash
|
|
13
20
|
npm install monsqlize
|
|
14
21
|
```
|
|
15
22
|
|
|
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)
|
|
23
|
+
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
24
|
|
|
36
25
|
## Why monSQLize
|
|
37
26
|
|
|
38
|
-
monSQLize
|
|
27
|
+
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
28
|
|
|
40
|
-
-
|
|
41
|
-
- Smart caching through `cache-hub`, including local memory caching, optional Redis-backed L2 caching, automatic invalidation
|
|
29
|
+
- Database-native adapter APIs: the current stable adapter preserves MongoDB-style CRUD, aggregation, indexes, transactions, and Change Streams.
|
|
30
|
+
- Smart database caching through `cache-hub`, including local memory caching, optional Redis-backed L2 caching, and automatic invalidation.
|
|
42
31
|
- A lightweight Model layer with `schema-dsl` validation, hooks, relations, populate, custom methods, timestamps, soft delete, optimistic locking, and production-safe index preflight.
|
|
43
32
|
- 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
33
|
- Change Stream sync helpers with resume token storage.
|
|
47
34
|
- Slow-query logging and query diagnostics.
|
|
48
35
|
- CommonJS, ESM, and TypeScript declaration outputs from `dist/**`.
|
|
49
36
|
|
|
37
|
+
## Adapter Status
|
|
38
|
+
|
|
39
|
+
| Adapter | Current status | Public entry |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| MongoDB | Stable and fully implemented | `type: 'mongodb'`, `collection()`, `db()`, `use()`, `pool()` |
|
|
42
|
+
| MySQL | Planned / in development | Not part of the current npm runtime yet |
|
|
43
|
+
| PostgreSQL | Planned / in development | Not part of the current npm runtime yet |
|
|
44
|
+
|
|
45
|
+
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.
|
|
46
|
+
|
|
47
|
+
## Runtime Consistency Contract
|
|
48
|
+
|
|
49
|
+
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 current v3 runtime; use explicit business coordination, idempotency/fencing, or cache bypassing for cross-instance strict flows.
|
|
50
|
+
|
|
51
|
+
## Write Path Policy
|
|
52
|
+
|
|
53
|
+
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`.
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
const msq = new MonSQLize({
|
|
57
|
+
type: 'mongodb',
|
|
58
|
+
databaseName: 'app',
|
|
59
|
+
config: { uri: 'mongodb://localhost:27017' },
|
|
60
|
+
writePathPolicy: {
|
|
61
|
+
default: 'model-only',
|
|
62
|
+
namespaces: {
|
|
63
|
+
'app.audit_logs': 'allow-both'
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`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).
|
|
70
|
+
|
|
50
71
|
## When to Use It
|
|
51
72
|
|
|
52
73
|
monSQLize is a good fit when you need:
|
|
@@ -54,8 +75,9 @@ monSQLize is a good fit when you need:
|
|
|
54
75
|
| Scenario | Benefit |
|
|
55
76
|
|---|---|
|
|
56
77
|
| High-concurrency reads | Cache hot data and reduce repeated database work. |
|
|
57
|
-
| MongoDB API compatibility | Keep familiar
|
|
58
|
-
|
|
|
78
|
+
| MongoDB API compatibility today | Keep familiar MongoDB syntax while adding higher-level runtime helpers. |
|
|
79
|
+
| A future multi-database runtime boundary | Prepare for MySQL/PostgreSQL adapters without turning SQL into fake MongoDB syntax. |
|
|
80
|
+
| Multi-instance services | Use Redis invalidation and pool health checks to keep instances coordinated. |
|
|
59
81
|
| Transaction-heavy flows | Use `withTransaction()` and transaction-aware helpers instead of hand-rolled lifecycle code. |
|
|
60
82
|
| Model-level ergonomics | Add schema validation, hooks, populate, and custom methods only where needed. |
|
|
61
83
|
| Smooth upgrade from v1 | Keep legacy application source stable while adopting the TypeScript rewrite. |
|
|
@@ -68,37 +90,32 @@ monSQLize is usually not the best first choice for pure write-heavy workloads, e
|
|
|
68
90
|
npm install monsqlize
|
|
69
91
|
```
|
|
70
92
|
|
|
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.
|
|
93
|
+
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
94
|
|
|
80
95
|
## Quick Start
|
|
81
96
|
|
|
97
|
+
The current stable quick start uses the MongoDB adapter.
|
|
98
|
+
|
|
82
99
|
### CommonJS
|
|
83
100
|
|
|
84
101
|
```js
|
|
85
102
|
const MonSQLize = require('monsqlize');
|
|
86
103
|
|
|
87
|
-
const
|
|
104
|
+
const msq = new MonSQLize({
|
|
88
105
|
type: 'mongodb',
|
|
89
106
|
databaseName: 'mydb',
|
|
90
107
|
config: {
|
|
91
108
|
uri: 'mongodb://localhost:27017'
|
|
92
109
|
},
|
|
93
110
|
cache: {
|
|
94
|
-
|
|
95
|
-
|
|
111
|
+
maxEntries: 10000,
|
|
112
|
+
defaultTtl: 60_000
|
|
96
113
|
}
|
|
97
114
|
});
|
|
98
115
|
|
|
99
|
-
await
|
|
116
|
+
await msq.connect();
|
|
100
117
|
|
|
101
|
-
const users =
|
|
118
|
+
const users = msq.collection('users');
|
|
102
119
|
|
|
103
120
|
await users.insertOne({
|
|
104
121
|
username: 'john',
|
|
@@ -107,23 +124,25 @@ await users.insertOne({
|
|
|
107
124
|
});
|
|
108
125
|
|
|
109
126
|
const user = await users.findOne({ email: 'john@example.com' });
|
|
110
|
-
const
|
|
127
|
+
const sameUser = await users.findOne({ _id: '507f1f77bcf86cd799439011' });
|
|
111
128
|
|
|
112
129
|
await users.updateOne(
|
|
113
130
|
{ email: 'john@example.com' },
|
|
114
131
|
{ $set: { lastLoginAt: new Date() } }
|
|
115
132
|
);
|
|
116
133
|
|
|
117
|
-
await
|
|
134
|
+
await msq.close();
|
|
118
135
|
```
|
|
119
136
|
|
|
137
|
+
Next, use the [Basic Usage guide](https://vextjs.github.io/monSQLize/basic-operations) for common CRUD, pagination, read cache, and Model entry-point examples.
|
|
138
|
+
|
|
120
139
|
### ESM and TypeScript
|
|
121
140
|
|
|
122
141
|
```ts
|
|
123
142
|
import MonSQLize from 'monsqlize';
|
|
124
143
|
import type { Collection } from 'monsqlize';
|
|
125
144
|
|
|
126
|
-
const
|
|
145
|
+
const msq = new MonSQLize({
|
|
127
146
|
type: 'mongodb',
|
|
128
147
|
databaseName: 'mydb',
|
|
129
148
|
config: {
|
|
@@ -131,12 +150,12 @@ const db = new MonSQLize({
|
|
|
131
150
|
}
|
|
132
151
|
});
|
|
133
152
|
|
|
134
|
-
await
|
|
153
|
+
await msq.connect();
|
|
135
154
|
|
|
136
|
-
const users: Collection =
|
|
155
|
+
const users: Collection = msq.collection('users');
|
|
137
156
|
const activeUsers = await users.find({ status: 'active' }).toArray();
|
|
138
157
|
|
|
139
|
-
await
|
|
158
|
+
await msq.close();
|
|
140
159
|
```
|
|
141
160
|
|
|
142
161
|
Published entry points:
|
|
@@ -147,13 +166,13 @@ Published entry points:
|
|
|
147
166
|
| ESM | `dist/esm/index.mjs` |
|
|
148
167
|
| Types | `dist/types/index.d.ts` |
|
|
149
168
|
|
|
150
|
-
The package root exports only the public package contract. Deep imports into historical `lib/**` files are not part of the
|
|
169
|
+
The package root exports only the public package contract. Deep imports into historical `lib/**` files are not part of the v3 publishing surface.
|
|
151
170
|
|
|
152
171
|
## Model Layer
|
|
153
172
|
|
|
154
173
|
The Model layer is optional. Use it when you want schema validation, hooks, relations, populate, custom methods, timestamps, soft delete, or optimistic locking.
|
|
155
174
|
|
|
156
|
-
`schema-dsl`
|
|
175
|
+
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
176
|
|
|
158
177
|
### Manual Model Registration
|
|
159
178
|
|
|
@@ -162,7 +181,7 @@ const MonSQLize = require('monsqlize');
|
|
|
162
181
|
const { Model } = MonSQLize;
|
|
163
182
|
|
|
164
183
|
Model.define('users', {
|
|
165
|
-
schema: (
|
|
184
|
+
schema: (s) => s({
|
|
166
185
|
username: 'string:3-32!',
|
|
167
186
|
email: 'email!',
|
|
168
187
|
password: 'string:6-!',
|
|
@@ -198,15 +217,15 @@ Model.define('users', {
|
|
|
198
217
|
})
|
|
199
218
|
});
|
|
200
219
|
|
|
201
|
-
const
|
|
220
|
+
const msq = new MonSQLize({
|
|
202
221
|
type: 'mongodb',
|
|
203
222
|
databaseName: 'mydb',
|
|
204
223
|
config: { uri: 'mongodb://localhost:27017' }
|
|
205
224
|
});
|
|
206
225
|
|
|
207
|
-
await
|
|
226
|
+
await msq.connect();
|
|
208
227
|
|
|
209
|
-
const User =
|
|
228
|
+
const User = msq.model('users');
|
|
210
229
|
const user = await User.insertOne({
|
|
211
230
|
username: 'john',
|
|
212
231
|
email: 'john@example.com',
|
|
@@ -215,29 +234,60 @@ const user = await User.insertOne({
|
|
|
215
234
|
});
|
|
216
235
|
```
|
|
217
236
|
|
|
237
|
+
When a service needs runtime-local custom types, messages, locale, or an already-owned schema-dsl runtime, configure `schemaDsl` on the MonSQLize instance:
|
|
238
|
+
|
|
239
|
+
```js
|
|
240
|
+
const { createRuntime } = require('schema-dsl/runtime');
|
|
241
|
+
|
|
242
|
+
const schemaRuntime = createRuntime({
|
|
243
|
+
types: {
|
|
244
|
+
tenantId: { type: 'string', pattern: '^tenant_[a-z0-9]+$' }
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
Model.define('tenantUsers', {
|
|
249
|
+
schema: (s) => s({
|
|
250
|
+
tenantId: 'tenantId!',
|
|
251
|
+
email: 'email!'
|
|
252
|
+
})
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
const msq = new MonSQLize({
|
|
256
|
+
type: 'mongodb',
|
|
257
|
+
databaseName: 'mydb',
|
|
258
|
+
config: { uri: 'mongodb://localhost:27017' },
|
|
259
|
+
schemaDsl: { runtime: schemaRuntime }
|
|
260
|
+
});
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
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.
|
|
264
|
+
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.
|
|
265
|
+
|
|
266
|
+
For `insertOne`, `insertMany`, `insertBatch`, `replaceOne`, `findOneAndReplace`, and hydrated document `save()`, successful schema validation now forwards schema-dsl's normalized `data` into the actual write. Public errors remain `{ field, message }`, mapped from canonical upstream `path/message`. Update operators and pipelines are intentionally outside this complete-document normalization contract.
|
|
267
|
+
|
|
218
268
|
### Automatic Model Loading
|
|
219
269
|
|
|
220
270
|
```js
|
|
221
271
|
const path = require('path');
|
|
222
272
|
const MonSQLize = require('monsqlize');
|
|
223
273
|
|
|
224
|
-
const
|
|
274
|
+
const msq = new MonSQLize({
|
|
225
275
|
type: 'mongodb',
|
|
226
276
|
databaseName: 'mydb',
|
|
227
277
|
config: { uri: 'mongodb://localhost:27017' },
|
|
228
278
|
models: path.join(__dirname, 'models')
|
|
229
279
|
});
|
|
230
280
|
|
|
231
|
-
await
|
|
281
|
+
await msq.connect();
|
|
232
282
|
|
|
233
|
-
const User =
|
|
283
|
+
const User = msq.model('users');
|
|
234
284
|
```
|
|
235
285
|
|
|
236
286
|
```js
|
|
237
287
|
// models/user.model.js
|
|
238
288
|
module.exports = {
|
|
239
289
|
name: 'users',
|
|
240
|
-
schema: (
|
|
290
|
+
schema: (s) => s({
|
|
241
291
|
username: 'string:3-32!',
|
|
242
292
|
email: 'email!'
|
|
243
293
|
}),
|
|
@@ -258,27 +308,56 @@ Relative model paths are resolved from `process.cwd()`. In production services,
|
|
|
258
308
|
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
309
|
|
|
260
310
|
```js
|
|
261
|
-
const
|
|
311
|
+
const msq = new MonSQLize({
|
|
262
312
|
type: 'mongodb',
|
|
263
313
|
databaseName: 'mydb',
|
|
264
314
|
config: { uri: 'mongodb://localhost:27017' },
|
|
265
315
|
autoIndex: false
|
|
266
316
|
});
|
|
267
317
|
|
|
268
|
-
const plan = await
|
|
318
|
+
const plan = await msq.ensureModelIndexes({ models: ['users'], dryRun: true });
|
|
269
319
|
|
|
270
320
|
if (plan.totals.conflicts === 0) {
|
|
271
|
-
await
|
|
321
|
+
await msq.ensureModelIndexes({ models: ['users'], throwOnError: true });
|
|
272
322
|
}
|
|
273
323
|
```
|
|
274
324
|
|
|
275
325
|
`ensureModelIndexes()` creates only missing indexes. It does not drop, rename, or rebuild conflicting indexes.
|
|
276
326
|
|
|
327
|
+
### Versioned Writes
|
|
328
|
+
|
|
329
|
+
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:
|
|
330
|
+
|
|
331
|
+
```js
|
|
332
|
+
await User.updateOne(
|
|
333
|
+
{ _id: userId },
|
|
334
|
+
{ $set: { status: 'active' } }
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
await User.replaceOne(
|
|
338
|
+
{ _id: userId },
|
|
339
|
+
nextUser,
|
|
340
|
+
{ expectedVersion: user.version }
|
|
341
|
+
);
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
Successful `save()`, `updateOne()`, `replaceOne()`, `findOneAndUpdate()`, and `findOneAndReplace()` advance the version. Stale writes throw a `WRITE_CONFLICT` error.
|
|
345
|
+
|
|
346
|
+
For `updateMany()` and `updateBatch()`, choose the batch version behavior explicitly when needed:
|
|
347
|
+
|
|
348
|
+
```js
|
|
349
|
+
await User.updateMany({ status: 'pending' }, { $set: { status: 'active' } }, {
|
|
350
|
+
versionMode: 'strict' // 'counter' | 'strict' | 'off'
|
|
351
|
+
});
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
`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.
|
|
355
|
+
|
|
277
356
|
### Populate
|
|
278
357
|
|
|
279
358
|
```js
|
|
280
359
|
Model.define('posts', {
|
|
281
|
-
schema: (
|
|
360
|
+
schema: (s) => s({
|
|
282
361
|
title: 'string:1-200!',
|
|
283
362
|
content: 'string!',
|
|
284
363
|
userId: 'objectId!'
|
|
@@ -296,12 +375,20 @@ const userWithPosts = await User.findOne({ username: 'john' })
|
|
|
296
375
|
|
|
297
376
|
Populate is supported by `find()`, `findOne()`, `findByIds()`, `findOneById()`, `findAndCount()`, and `findPage()`.
|
|
298
377
|
|
|
378
|
+
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.
|
|
379
|
+
|
|
380
|
+
### Soft Delete Visibility
|
|
381
|
+
|
|
382
|
+
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.
|
|
383
|
+
|
|
384
|
+
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.
|
|
385
|
+
|
|
299
386
|
## Caching and Performance
|
|
300
387
|
|
|
301
|
-
monSQLize can cache collection queries and
|
|
388
|
+
monSQLize can cache collection queries and coordinate local/Redis-backed invalidation for database runtime usage.
|
|
302
389
|
|
|
303
390
|
```js
|
|
304
|
-
const users =
|
|
391
|
+
const users = msq.collection('users');
|
|
305
392
|
|
|
306
393
|
const hotUser = await users.findOne(
|
|
307
394
|
{ email: 'john@example.com' },
|
|
@@ -309,51 +396,36 @@ const hotUser = await users.findOne(
|
|
|
309
396
|
);
|
|
310
397
|
```
|
|
311
398
|
|
|
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
399
|
Cache capabilities include:
|
|
330
400
|
|
|
331
401
|
- In-memory L1 cache.
|
|
332
402
|
- Optional Redis-backed L2 cache.
|
|
333
403
|
- Automatic invalidation after writes.
|
|
334
|
-
-
|
|
335
|
-
- In-flight request deduplication.
|
|
336
|
-
- Namespaces, TTLs, statistics, and conditional caching.
|
|
404
|
+
- Cache namespace, TTL, and distributed invalidation controls.
|
|
337
405
|
|
|
338
406
|
## Advanced Capabilities
|
|
339
407
|
|
|
340
408
|
### Transactions
|
|
341
409
|
|
|
342
410
|
```js
|
|
343
|
-
await
|
|
344
|
-
await
|
|
345
|
-
await
|
|
411
|
+
await msq.withTransaction(async (tx) => {
|
|
412
|
+
await msq.collection('orders').insertOne({ userId, status: 'pending' }, { session: tx.session });
|
|
413
|
+
await msq.collection('users').updateOne(
|
|
346
414
|
{ _id: userId },
|
|
347
415
|
{ $inc: { orderCount: 1 } },
|
|
348
|
-
{ session }
|
|
416
|
+
{ session: tx.session }
|
|
349
417
|
);
|
|
350
418
|
});
|
|
351
419
|
```
|
|
352
420
|
|
|
421
|
+
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.
|
|
422
|
+
|
|
423
|
+
Transaction cache invalidations are recorded during the transaction and flushed only after a successful commit. If the transaction aborts, pending invalidations are discarded.
|
|
424
|
+
|
|
353
425
|
### Connection Pools
|
|
354
426
|
|
|
355
427
|
```js
|
|
356
|
-
const
|
|
428
|
+
const msq = new MonSQLize({
|
|
357
429
|
type: 'mongodb',
|
|
358
430
|
databaseName: 'main',
|
|
359
431
|
config: { uri: 'mongodb://primary:27017' },
|
|
@@ -362,24 +434,13 @@ const db = new MonSQLize({
|
|
|
362
434
|
]
|
|
363
435
|
});
|
|
364
436
|
|
|
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
|
-
});
|
|
437
|
+
const reports = msq.pool('analytics').collection('reports');
|
|
377
438
|
```
|
|
378
439
|
|
|
379
440
|
### Change Streams
|
|
380
441
|
|
|
381
442
|
```js
|
|
382
|
-
const watcher =
|
|
443
|
+
const watcher = msq.collection('orders').watch([
|
|
383
444
|
{ $match: { 'fullDocument.status': 'pending' } }
|
|
384
445
|
]);
|
|
385
446
|
|
|
@@ -388,25 +449,6 @@ watcher.on('change', (change) => {
|
|
|
388
449
|
});
|
|
389
450
|
```
|
|
390
451
|
|
|
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
452
|
## Migration from the MongoDB Driver
|
|
411
453
|
|
|
412
454
|
The smallest migration is usually to replace only initialization:
|
|
@@ -421,15 +463,15 @@ const nativeUsers = nativeClient.db('mydb').collection('users');
|
|
|
421
463
|
```js
|
|
422
464
|
const MonSQLize = require('monsqlize');
|
|
423
465
|
|
|
424
|
-
const
|
|
466
|
+
const msq = new MonSQLize({
|
|
425
467
|
type: 'mongodb',
|
|
426
468
|
databaseName: 'mydb',
|
|
427
469
|
config: { uri: 'mongodb://localhost:27017' },
|
|
428
470
|
cache: { enabled: true }
|
|
429
471
|
});
|
|
430
472
|
|
|
431
|
-
await
|
|
432
|
-
const users =
|
|
473
|
+
await msq.connect();
|
|
474
|
+
const users = msq.collection('users');
|
|
433
475
|
```
|
|
434
476
|
|
|
435
477
|
MongoDB-style collection calls can remain unchanged in most cases:
|
|
@@ -439,15 +481,15 @@ const user = await users.findOne({ email });
|
|
|
439
481
|
const list = await users.find({ status: 'active' }).toArray();
|
|
440
482
|
```
|
|
441
483
|
|
|
442
|
-
The
|
|
484
|
+
The v3 line has been validated against the workspace consumers `chat`, `payment`, `user`, `admin`, `search`, `vext`, and `permission-core` without requiring business-source changes in those projects.
|
|
443
485
|
|
|
444
486
|
## Compatibility
|
|
445
487
|
|
|
446
488
|
| Surface | Current Support |
|
|
447
489
|
|---|---|
|
|
448
490
|
| Node.js | `>=18.0.0`; CI covers Node 18 / 20 / 22. |
|
|
449
|
-
| MongoDB driver | `mongodb@6.21.0` runtime baseline; driver 7.
|
|
450
|
-
| MongoDB server | Memory-server based
|
|
491
|
+
| MongoDB driver | `mongodb@6.21.0` runtime baseline; driver 7.5.0 has additional compatibility coverage. |
|
|
492
|
+
| MongoDB server | Memory-server based 7.x / 8.x validation is covered by the strict project test matrix. |
|
|
451
493
|
| Module systems | CommonJS and ESM are both validated. |
|
|
452
494
|
| TypeScript | Public declarations are published from `dist/types/index.d.ts`. |
|
|
453
495
|
| Package license | Apache-2.0. |
|
|
@@ -456,25 +498,27 @@ See the current support and verification documents:
|
|
|
456
498
|
|
|
457
499
|
- [English documentation](https://github.com/vextjs/monSQLize/blob/main/docs/en/README.md)
|
|
458
500
|
- [Chinese documentation](https://github.com/vextjs/monSQLize/blob/main/docs/zh/README.md)
|
|
459
|
-
- [English
|
|
501
|
+
- [English common scenarios](https://github.com/vextjs/monSQLize/blob/main/docs/en/recipes.md)
|
|
460
502
|
- [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
503
|
- [test/compatibility/README.md](https://github.com/vextjs/monSQLize/blob/main/test/compatibility/README.md)
|
|
463
504
|
- [test/validation/VERIFICATION-PROGRESS.md](https://github.com/vextjs/monSQLize/blob/main/test/validation/VERIFICATION-PROGRESS.md)
|
|
464
505
|
|
|
465
506
|
## Documentation
|
|
466
507
|
|
|
467
|
-
Current TypeScript documentation and examples are the source of truth for the
|
|
508
|
+
Current TypeScript documentation and examples are the source of truth for the v3 package:
|
|
468
509
|
|
|
469
510
|
- Complete docs: [English](https://vextjs.github.io/monSQLize/) · [简体中文](https://vextjs.github.io/monSQLize/zh/)
|
|
470
|
-
-
|
|
471
|
-
-
|
|
472
|
-
-
|
|
473
|
-
-
|
|
474
|
-
-
|
|
475
|
-
-
|
|
476
|
-
|
|
477
|
-
|
|
511
|
+
- Constructor configuration: [English](https://vextjs.github.io/monSQLize/configuration) · [简体中文](https://vextjs.github.io/monSQLize/zh/configuration)
|
|
512
|
+
- Production rollout: [English](https://vextjs.github.io/monSQLize/production-rollout) · [简体中文](https://vextjs.github.io/monSQLize/zh/production-rollout)
|
|
513
|
+
- Production data migration: [English](https://vextjs.github.io/monSQLize/production-data-migration) · [简体中文](https://vextjs.github.io/monSQLize/zh/production-data-migration)
|
|
514
|
+
- Data tasks API: [English](https://vextjs.github.io/monSQLize/data-tasks) · [简体中文](https://vextjs.github.io/monSQLize/zh/data-tasks)
|
|
515
|
+
- Common scenarios: [English](https://vextjs.github.io/monSQLize/recipes) · [简体中文](https://vextjs.github.io/monSQLize/zh/recipes)
|
|
516
|
+
- Example source: [examples index](https://github.com/vextjs/monSQLize/blob/main/examples/README.md)
|
|
517
|
+
- Documentation source: [docs/en](https://github.com/vextjs/monSQLize/tree/main/docs/en) · [docs/zh](https://github.com/vextjs/monSQLize/tree/main/docs/zh)
|
|
518
|
+
- Compatibility checks: [test/compatibility](https://github.com/vextjs/monSQLize/tree/main/test/compatibility)
|
|
519
|
+
- Verification mapping: [test/validation](https://github.com/vextjs/monSQLize/tree/main/test/validation)
|
|
520
|
+
|
|
521
|
+
Historical v1 and v2 assets are useful for tracing old behavior, but they are not the current publishing surface for v3.
|
|
478
522
|
|
|
479
523
|
## Development
|
|
480
524
|
|
|
@@ -495,9 +539,9 @@ npm run verify:full
|
|
|
495
539
|
npm run release:preflight
|
|
496
540
|
```
|
|
497
541
|
|
|
498
|
-
|
|
542
|
+
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
543
|
|
|
500
|
-
`npm run release:publish` runs the
|
|
544
|
+
`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
545
|
|
|
502
546
|
Optional commands:
|
|
503
547
|
|
|
@@ -507,82 +551,30 @@ npm run test:examples
|
|
|
507
551
|
npm run test:coverage
|
|
508
552
|
npm run test:audit
|
|
509
553
|
npm run test:server-matrix
|
|
554
|
+
npm run test:data-tasks:integration
|
|
555
|
+
npm run test:data-task-cli
|
|
556
|
+
npm run test:pack-install
|
|
510
557
|
npm run test:real-env:private
|
|
511
558
|
```
|
|
512
559
|
|
|
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.
|
|
560
|
+
`check:docs-examples` verifies the bilingual documentation matrix, runnable-example runner parity, shared-example targets, doc-check targets, and user-facing path text.
|
|
516
561
|
|
|
517
|
-
`test:
|
|
562
|
+
`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.
|
|
518
563
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
The current release train targets `v2.0.6`.
|
|
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.11`; 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.
|
|
564
|
+
`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
565
|
|
|
532
566
|
## Roadmap
|
|
533
567
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
-
|
|
537
|
-
-
|
|
538
|
-
- Restored v1-compatible root option type exports.
|
|
539
|
-
|
|
540
|
-
### v2.0.4
|
|
541
|
-
|
|
542
|
-
- Production-safe Model index rollout controls with `autoIndex`, dry-run preflight, conflict reporting, and explicit `ensureIndexes()` / `ensureModelIndexes()` APIs.
|
|
543
|
-
- `schema-dsl` updated to `2.0.9`, with transitive `cache-hub` aligned to `2.2.4`.
|
|
544
|
-
- User-facing capability and verification documentation wording cleaned up, plus documentation home experience refinements.
|
|
545
|
-
|
|
546
|
-
### v2.0.3
|
|
547
|
-
|
|
548
|
-
- v1 compatibility patch for documented `findPage({ cache })` behavior.
|
|
549
|
-
- Public transaction and distributed cache invalidator statistics APIs.
|
|
550
|
-
- Standalone documentation-site link safety and bilingual docs consistency fixes.
|
|
551
|
-
- Release preflight alignment for bilingual docs paths and production dependency audit.
|
|
552
|
-
|
|
553
|
-
### v2.0.2
|
|
554
|
-
|
|
555
|
-
- Deterministic dependency metadata patch.
|
|
556
|
-
- `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.
|
|
557
|
-
|
|
558
|
-
### v2.0.1
|
|
559
|
-
|
|
560
|
-
- v1 smooth-upgrade compatibility patch for Model actual collection names, scoped pools/databases, automatic-index dedupe, and cache/pool option aliases.
|
|
561
|
-
- Documentation and public types aligned with the current runtime behavior.
|
|
562
|
-
|
|
563
|
-
### v2.0.0
|
|
564
|
-
|
|
565
|
-
- TypeScript-native runtime and declarations.
|
|
566
|
-
- v1 smooth-upgrade compatibility bridge.
|
|
567
|
-
- Multi-level cache and function-cache support through `cache-hub`.
|
|
568
|
-
- Transactions, business locks, distributed locks, Saga orchestration, connection pools, Change Stream sync, and slow-query logging.
|
|
569
|
-
- Model layer with `schema-dsl` validation, relations, populate, hooks, and custom methods.
|
|
570
|
-
|
|
571
|
-
### v2.x
|
|
572
|
-
|
|
568
|
+
- MongoDB remains the stable adapter and the current production runtime.
|
|
569
|
+
- MySQL and PostgreSQL adapters will be introduced as database-native adapters under the same production runtime contract.
|
|
570
|
+
- Adapter status will move from planned to alpha/stable only after runtime support, public types, examples, and verification coverage are present.
|
|
571
|
+
- The project does not currently promise production-ready "one query syntax automatically adapts to every database" behavior.
|
|
573
572
|
- Query analyzer improvements.
|
|
574
573
|
- Automatic index suggestions.
|
|
575
|
-
-
|
|
574
|
+
- Additional data-task observability and large-scope operational probes.
|
|
576
575
|
- GraphQL integration experiments.
|
|
577
576
|
- More real-environment validation coverage.
|
|
578
577
|
|
|
579
|
-
### v3.0+
|
|
580
|
-
|
|
581
|
-
- Unified API experiments for MySQL.
|
|
582
|
-
- Unified API experiments for PostgreSQL.
|
|
583
|
-
- Broader ORM capabilities.
|
|
584
|
-
- Cross-database sync middleware.
|
|
585
|
-
|
|
586
578
|
## License
|
|
587
579
|
|
|
588
580
|
monSQLize is released under the [Apache License 2.0](./LICENSE).
|