monsqlize 2.0.3 → 2.0.5
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 +11 -5
- package/README.md +33 -3
- package/changelogs/v2.0.4.md +61 -0
- package/changelogs/v2.0.5.md +33 -0
- package/dist/cjs/index.cjs +263 -62
- package/dist/esm/index.mjs +263 -62
- package/dist/types/model.d.ts +104 -0
- package/dist/types/monsqlize.d.ts +9 -1
- package/dist/types/runtime.d.ts +2 -1
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
-
> Summary index — current release details are packaged in [changelogs/v2.0.
|
|
4
|
-
> **Last updated**: 2026-06-
|
|
3
|
+
> Summary index — current release details are packaged in [changelogs/v2.0.5.md](./changelogs/v2.0.5.md); historical details live in the repository changelog archive.
|
|
4
|
+
> **Last updated**: 2026-06-13
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
| Version | Date | Summary | Details |
|
|
11
11
|
|---------|------|---------|---------|
|
|
12
|
+
| [v2.0.5](./changelogs/v2.0.5.md) | 2026-06-13 | Patch: Model schema adapter delegates DSL type authority to `schema-dsl@2.0.10`, removing the duplicated monSQLize allowlist while preserving legacy aliases and business literals | [View](./changelogs/v2.0.5.md) |
|
|
13
|
+
| [v2.0.4](./changelogs/v2.0.4.md) | 2026-06-12 | Patch: production-safe Model index rollout controls, `schema-dsl@2.0.9`, capability-index wording cleanup, and documentation home refinements | [View](./changelogs/v2.0.4.md) |
|
|
12
14
|
| [v2.0.3](./changelogs/v2.0.3.md) | 2026-06-11 | Patch: v1 compatibility fixes, public stats APIs, standalone docs-site link safety, bilingual docs consistency, and release preflight alignment | [View](./changelogs/v2.0.3.md) |
|
|
13
15
|
| [v2.0.2](./changelogs/v2.0.2.md) | 2026-06-09 | Patch: direct runtime, optional and development dependencies pinned to exact versions for deterministic consumer installs | [View](./changelogs/v2.0.2.md) |
|
|
14
16
|
| [v2.0.1](./changelogs/v2.0.1.md) | 2026-06-03 | Patch: model collection/pool compatibility, automatic-index task dedupe, runtime cache/pool v1 smooth-upgrade fixes, and current docs/types alignment | [View](./changelogs/v2.0.1.md) |
|
|
@@ -446,7 +448,9 @@ const result = await msq.collection('orders').insertOne(dataFromMongoose);
|
|
|
446
448
|
changelogs/
|
|
447
449
|
├── README.md # 变更文档说明
|
|
448
450
|
├── TEMPLATE.md # 变更文档模板
|
|
449
|
-
├── v2.0.
|
|
451
|
+
├── v2.0.5.md # 当前发布详细变更
|
|
452
|
+
├── v2.0.4.md # v2.0.4 详细变更
|
|
453
|
+
├── v2.0.3.md # v2.0.3 详细变更
|
|
450
454
|
├── v2.0.2.md # v2.0.2 详细变更
|
|
451
455
|
├── v2.0.1.md # v2.0.1 详细变更
|
|
452
456
|
├── v2.0.0.md # v2 TypeScript 重写发布详细变更
|
|
@@ -500,7 +504,9 @@ changelogs/
|
|
|
500
504
|
|
|
501
505
|
## 相关文档
|
|
502
506
|
|
|
503
|
-
- [changelogs/v2.0.
|
|
507
|
+
- [changelogs/v2.0.5.md](./changelogs/v2.0.5.md) - 当前发布详细变更文档
|
|
508
|
+
- [changelogs/v2.0.4.md](./changelogs/v2.0.4.md) - v2.0.4 详细变更文档
|
|
509
|
+
- [changelogs/v2.0.3.md](./changelogs/v2.0.3.md) - v2.0.3 详细变更文档
|
|
504
510
|
- [changelogs/v2.0.2.md](./changelogs/v2.0.2.md) - v2.0.2 详细变更文档
|
|
505
511
|
- [changelogs/v2.0.1.md](./changelogs/v2.0.1.md) - v2.0.1 详细变更文档
|
|
506
512
|
- [README.md](./README.md) - 项目说明
|
|
@@ -508,5 +514,5 @@ changelogs/
|
|
|
508
514
|
|
|
509
515
|
---
|
|
510
516
|
|
|
511
|
-
**最后更新**: 2026-06-
|
|
517
|
+
**最后更新**: 2026-06-13
|
|
512
518
|
|
package/README.md
CHANGED
|
@@ -7,6 +7,8 @@ TypeScript-native MongoDB ODM and enhancement layer with v1-compatible APIs, mul
|
|
|
7
7
|
[](https://www.mongodb.com/)
|
|
8
8
|
[](https://nodejs.org/)
|
|
9
9
|
|
|
10
|
+
Documentation: [English](https://vextjs.github.io/monSQLize/) · [简体中文](https://vextjs.github.io/monSQLize/zh/)
|
|
11
|
+
|
|
10
12
|
```bash
|
|
11
13
|
npm install monsqlize
|
|
12
14
|
```
|
|
@@ -37,7 +39,7 @@ monSQLize keeps the MongoDB driver mental model while adding the production feat
|
|
|
37
39
|
|
|
38
40
|
- Drop-in collection helpers that preserve MongoDB-style CRUD, aggregation, indexes, transactions, and Change Streams.
|
|
39
41
|
- Smart caching through `cache-hub`, including local memory caching, optional Redis-backed L2 caching, automatic invalidation, and function-level caching.
|
|
40
|
-
- A lightweight Model layer with `schema-dsl` validation, hooks, relations, populate, custom methods, timestamps, soft delete, and
|
|
42
|
+
- A lightweight Model layer with `schema-dsl` validation, hooks, relations, populate, custom methods, timestamps, soft delete, optimistic locking, and production-safe index preflight.
|
|
41
43
|
- Multi-connection-pool support, pool health checks, pool-scoped collections/models, and fallback strategies.
|
|
42
44
|
- Business locks and distributed locks for multi-instance deployments.
|
|
43
45
|
- Saga orchestration for multi-step business workflows.
|
|
@@ -251,6 +253,27 @@ module.exports = {
|
|
|
251
253
|
|
|
252
254
|
Relative model paths are resolved from `process.cwd()`. In production services, prefer absolute paths such as `path.join(__dirname, 'models')`.
|
|
253
255
|
|
|
256
|
+
### Production Model Index Rollout
|
|
257
|
+
|
|
258
|
+
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
|
+
|
|
260
|
+
```js
|
|
261
|
+
const db = new MonSQLize({
|
|
262
|
+
type: 'mongodb',
|
|
263
|
+
databaseName: 'mydb',
|
|
264
|
+
config: { uri: 'mongodb://localhost:27017' },
|
|
265
|
+
autoIndex: false
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const plan = await db.ensureModelIndexes({ models: ['users'], dryRun: true });
|
|
269
|
+
|
|
270
|
+
if (plan.totals.conflicts === 0) {
|
|
271
|
+
await db.ensureModelIndexes({ models: ['users'], throwOnError: true });
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
`ensureModelIndexes()` creates only missing indexes. It does not drop, rename, or rebuild conflicting indexes.
|
|
276
|
+
|
|
254
277
|
### Populate
|
|
255
278
|
|
|
256
279
|
```js
|
|
@@ -443,6 +466,7 @@ See the current support and verification documents:
|
|
|
443
466
|
|
|
444
467
|
Current TypeScript documentation and examples are the source of truth for the v2 package:
|
|
445
468
|
|
|
469
|
+
- Complete docs: [English](https://vextjs.github.io/monSQLize/) · [简体中文](https://vextjs.github.io/monSQLize/zh/)
|
|
446
470
|
- `docs/en/**` - default English documentation.
|
|
447
471
|
- `docs/zh/**` - Simplified Chinese documentation.
|
|
448
472
|
- `docs/en/recipes.md` / `docs/zh/recipes.md` - shortest copy-ready paths for common setup scenarios.
|
|
@@ -494,7 +518,7 @@ npm run test:real-env:private
|
|
|
494
518
|
|
|
495
519
|
## Release Status
|
|
496
520
|
|
|
497
|
-
The current published release is `v2.0.
|
|
521
|
+
The current published release is `v2.0.4`.
|
|
498
522
|
|
|
499
523
|
Key release-readiness points:
|
|
500
524
|
|
|
@@ -502,11 +526,17 @@ Key release-readiness points:
|
|
|
502
526
|
- Package exports are consolidated under `dist/cjs`, `dist/esm`, and `dist/types`.
|
|
503
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`.
|
|
504
528
|
- v1 smooth-upgrade compatibility has been validated against the target workspace consumers.
|
|
505
|
-
- `schema-dsl` follows the npm `latest` TypeScript line `schema-dsl@2.0.
|
|
529
|
+
- `schema-dsl` follows the npm `latest` TypeScript line `schema-dsl@2.0.9`; deprecated `2.3.x` mistake releases are intentionally excluded.
|
|
506
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.
|
|
507
531
|
|
|
508
532
|
## Roadmap
|
|
509
533
|
|
|
534
|
+
### v2.0.4
|
|
535
|
+
|
|
536
|
+
- Production-safe Model index rollout controls with `autoIndex`, dry-run preflight, conflict reporting, and explicit `ensureIndexes()` / `ensureModelIndexes()` APIs.
|
|
537
|
+
- `schema-dsl` updated to `2.0.9`, with transitive `cache-hub` aligned to `2.2.4`.
|
|
538
|
+
- User-facing capability and verification documentation wording cleaned up, plus documentation home experience refinements.
|
|
539
|
+
|
|
510
540
|
### v2.0.3
|
|
511
541
|
|
|
512
542
|
- v1 compatibility patch for documented `findPage({ cache })` behavior.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# v2.0.4 — 2026-06-12
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
v2.0.4 is a production-safety and documentation-quality patch. It keeps the public API backward compatible while adding explicit Model index rollout controls, updating the schema validation dependency, and refining user-facing documentation language.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Runtime Fixes
|
|
10
|
+
|
|
11
|
+
- Added production-safe Model index controls:
|
|
12
|
+
- runtime-level and model-level `autoIndex` options.
|
|
13
|
+
- explicit `ModelInstance.ensureIndexes()` and `MonSQLize.ensureModelIndexes()` APIs.
|
|
14
|
+
- dry-run index preflight with `existing`, `missing`, `conflicts`, and `failed` classification.
|
|
15
|
+
- missing-index creation without dropping, renaming, or rebuilding conflicting indexes.
|
|
16
|
+
- `model-index-error` events for observable automatic-index creation failures.
|
|
17
|
+
- Kept automatic Model indexing enabled by default for backward compatibility.
|
|
18
|
+
- Moved runtime Model index aggregation into the Model runtime helper layer so the strict source-size gate remains clean.
|
|
19
|
+
|
|
20
|
+
## Dependencies
|
|
21
|
+
|
|
22
|
+
- Updated `schema-dsl` from `2.0.8` to npm `latest` `2.0.9`.
|
|
23
|
+
- Confirmed `schema-dsl@2.0.9` keeps the required `dsl()` and `validate()` runtime paths compatible with monSQLize.
|
|
24
|
+
- Confirmed the `schema-dsl` transitive `cache-hub` dependency now resolves to `2.2.4`, matching the root direct dependency.
|
|
25
|
+
|
|
26
|
+
## Documentation
|
|
27
|
+
|
|
28
|
+
- Documented the recommended production index rollout flow:
|
|
29
|
+
- set `autoIndex: false` in production services.
|
|
30
|
+
- run `ensureIndexes({ dryRun: true })` / `ensureModelIndexes({ dryRun: true })`.
|
|
31
|
+
- create only missing indexes during a low-traffic maintenance window.
|
|
32
|
+
- monitor TTL and index-build behavior.
|
|
33
|
+
- Corrected create-index documentation to distinguish local `INVALID_ARGUMENT` validation from raw MongoDB driver/server index conflicts.
|
|
34
|
+
- Reworded capability-index and verification-entry documentation to use user-facing availability and verification language instead of internal migration status terms.
|
|
35
|
+
- Refined the documentation home experience with the VextJS-style footer, aligned hero/feature layout, and updated hero data-flow visual.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Compatibility
|
|
40
|
+
|
|
41
|
+
- SemVer: patch release.
|
|
42
|
+
- Breaking changes: none.
|
|
43
|
+
- Existing v2 imports remain under `dist/cjs`, `dist/esm`, and `dist/types`.
|
|
44
|
+
- Existing default automatic Model indexing behavior remains enabled unless users opt into the new production-safe controls.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Validation
|
|
49
|
+
|
|
50
|
+
- `npm run lint`
|
|
51
|
+
- `npm run check:docs-examples`
|
|
52
|
+
- `npm run type-check`
|
|
53
|
+
- targeted Model/index unit and integration tests
|
|
54
|
+
- `npm run test:examples`
|
|
55
|
+
- `npm --prefix website run build`
|
|
56
|
+
- `npm run verify:fast`
|
|
57
|
+
- `npm run test:server-matrix`
|
|
58
|
+
- `npm run test:audit`
|
|
59
|
+
- `git diff --check`
|
|
60
|
+
|
|
61
|
+
Full release validation is handled by `npm run release:preflight`, `npm pack --dry-run`, `npm publish --dry-run`, and pack install smoke before publishing.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# v2.0.5 — 2026-06-13
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
v2.0.5 is a compatibility patch for Model schema validation. It removes the duplicated schema-dsl base-type allowlist from monSQLize and delegates DSL type decisions to schema-dsl, keeping schema-dsl as the single validation DSL authority.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Runtime Fixes
|
|
10
|
+
|
|
11
|
+
- Removed the monSQLize-side schema-dsl base-type allowlist from the Model schema adapter.
|
|
12
|
+
- Delegated schema compilation and unknown-type handling to schema-dsl diagnostics instead of maintaining a second type table.
|
|
13
|
+
- Preserved compatibility for schema-dsl-owned aliases such as `int`, `mixed`, `buffer` and `objectid`.
|
|
14
|
+
- Preserved existing literal business DSL strings such as `datetime` and `admin_login!` through schema-dsl fallback semantics.
|
|
15
|
+
|
|
16
|
+
## Dependencies
|
|
17
|
+
|
|
18
|
+
- Targets `schema-dsl@2.0.10`, which contains the shared legacy DSL aliases and structured diagnostics used by this patch.
|
|
19
|
+
- Keeps the root `cache-hub@2.2.4` dependency unchanged.
|
|
20
|
+
|
|
21
|
+
## Compatibility
|
|
22
|
+
|
|
23
|
+
- SemVer: patch release.
|
|
24
|
+
- Breaking changes: none.
|
|
25
|
+
- Existing v2 Model schema declarations continue to validate through the same public Model API.
|
|
26
|
+
- Consumer projects do not need to change source code for the checked schema alias and business-literal paths.
|
|
27
|
+
|
|
28
|
+
## Validation
|
|
29
|
+
|
|
30
|
+
- `npm run type-check`
|
|
31
|
+
- targeted Model schema validation tests
|
|
32
|
+
- targeted pure-function and branch coverage tests
|
|
33
|
+
- local schema-dsl@2.0.10 compatibility smoke
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -320,49 +320,8 @@ try {
|
|
|
320
320
|
_schemaValidateFn = mod.validate;
|
|
321
321
|
} catch {
|
|
322
322
|
}
|
|
323
|
-
var KNOWN_SCHEMA_BASE_TYPES = /* @__PURE__ */ new Set([
|
|
324
|
-
"string",
|
|
325
|
-
"number",
|
|
326
|
-
"boolean",
|
|
327
|
-
"integer",
|
|
328
|
-
"float",
|
|
329
|
-
"int",
|
|
330
|
-
"double",
|
|
331
|
-
"decimal",
|
|
332
|
-
"date",
|
|
333
|
-
"objectid",
|
|
334
|
-
"uuid",
|
|
335
|
-
"email",
|
|
336
|
-
"url",
|
|
337
|
-
"buffer",
|
|
338
|
-
"binary",
|
|
339
|
-
"object",
|
|
340
|
-
"array",
|
|
341
|
-
"any",
|
|
342
|
-
"mixed",
|
|
343
|
-
"null"
|
|
344
|
-
]);
|
|
345
|
-
function _extractBaseType(typeStr) {
|
|
346
|
-
const m = typeStr.match(/^[a-zA-Z_]+/);
|
|
347
|
-
return m ? m[0].toLowerCase() : "";
|
|
348
|
-
}
|
|
349
323
|
function _makeValidatingDslFn(realDsl) {
|
|
350
324
|
const validating = function validatingDsl(fields) {
|
|
351
|
-
if (fields && typeof fields === "object") {
|
|
352
|
-
for (const [field, spec] of Object.entries(fields)) {
|
|
353
|
-
if (typeof spec === "string") {
|
|
354
|
-
if (spec.includes("|")) {
|
|
355
|
-
continue;
|
|
356
|
-
}
|
|
357
|
-
const base = _extractBaseType(spec);
|
|
358
|
-
if (base && !KNOWN_SCHEMA_BASE_TYPES.has(base)) {
|
|
359
|
-
throw new TypeError(
|
|
360
|
-
`[schema] Invalid type "${base}" in field "${field}". Known types: ${[...KNOWN_SCHEMA_BASE_TYPES].join(", ")}.`
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
325
|
return realDsl(fields);
|
|
367
326
|
};
|
|
368
327
|
return validating;
|
|
@@ -997,6 +956,75 @@ function stableIndexStringify(value) {
|
|
|
997
956
|
}
|
|
998
957
|
return JSON.stringify(value) ?? "undefined";
|
|
999
958
|
}
|
|
959
|
+
function getIndexOptionName(options) {
|
|
960
|
+
return typeof options.name === "string" && options.name.length > 0 ? options.name : void 0;
|
|
961
|
+
}
|
|
962
|
+
function summarizeIndexError(error) {
|
|
963
|
+
if (error instanceof Error) {
|
|
964
|
+
const record = error;
|
|
965
|
+
return {
|
|
966
|
+
name: error.name,
|
|
967
|
+
message: error.message,
|
|
968
|
+
code: record.code ?? record.codeName
|
|
969
|
+
};
|
|
970
|
+
}
|
|
971
|
+
return {
|
|
972
|
+
message: String(error)
|
|
973
|
+
};
|
|
974
|
+
}
|
|
975
|
+
function isRecord(value) {
|
|
976
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
977
|
+
}
|
|
978
|
+
function getExistingIndexKey(index) {
|
|
979
|
+
return index.key;
|
|
980
|
+
}
|
|
981
|
+
function declaredOptionEntries(options) {
|
|
982
|
+
return Object.entries(options).filter(([name, value]) => {
|
|
983
|
+
if (value === void 0) return false;
|
|
984
|
+
if (name === "background") return false;
|
|
985
|
+
return true;
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
function indexOptionsMatch(existing, declared) {
|
|
989
|
+
if (stableIndexStringify(getExistingIndexKey(existing)) !== stableIndexStringify(declared.key)) {
|
|
990
|
+
return false;
|
|
991
|
+
}
|
|
992
|
+
for (const [name, value] of declaredOptionEntries(declared.options)) {
|
|
993
|
+
const existingValue = existing[name];
|
|
994
|
+
if (value === false && existingValue === void 0) {
|
|
995
|
+
continue;
|
|
996
|
+
}
|
|
997
|
+
if (stableIndexStringify(existingValue) !== stableIndexStringify(value)) {
|
|
998
|
+
return false;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return true;
|
|
1002
|
+
}
|
|
1003
|
+
function findExistingIndexByName(existingIndexes, name) {
|
|
1004
|
+
if (!name) return void 0;
|
|
1005
|
+
return existingIndexes.find((index) => index.name === name);
|
|
1006
|
+
}
|
|
1007
|
+
function findExistingIndexByKey(existingIndexes, key) {
|
|
1008
|
+
const fingerprint = stableIndexStringify(key);
|
|
1009
|
+
return existingIndexes.find((index) => stableIndexStringify(getExistingIndexKey(index)) === fingerprint);
|
|
1010
|
+
}
|
|
1011
|
+
function createIndexEnsureError(message, result, cause) {
|
|
1012
|
+
return createError(ErrorCodes.MONGODB_ERROR, message, [result], cause);
|
|
1013
|
+
}
|
|
1014
|
+
function resolveModelAutoIndexOptions(definition, runtimeAutoIndex) {
|
|
1015
|
+
const modelAutoIndex = toCompatDefinition(definition).options?.autoIndex;
|
|
1016
|
+
const value = modelAutoIndex ?? runtimeAutoIndex;
|
|
1017
|
+
if (value === false) {
|
|
1018
|
+
return { enabled: false, emitEvents: true };
|
|
1019
|
+
}
|
|
1020
|
+
if (value && typeof value === "object") {
|
|
1021
|
+
return {
|
|
1022
|
+
enabled: value.enabled !== false,
|
|
1023
|
+
emitEvents: value.emitEvents !== false
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
return { enabled: true, emitEvents: true };
|
|
1027
|
+
}
|
|
1000
1028
|
function getIndexTaskRegistry(runtime) {
|
|
1001
1029
|
if (!runtime) {
|
|
1002
1030
|
return fallbackModelIndexTasks;
|
|
@@ -1024,6 +1052,20 @@ function resolveIndexTaskScope(collection, options) {
|
|
|
1024
1052
|
};
|
|
1025
1053
|
}
|
|
1026
1054
|
}
|
|
1055
|
+
function toIndexNamespace(scope) {
|
|
1056
|
+
return {
|
|
1057
|
+
db: scope.dbName,
|
|
1058
|
+
collection: scope.collectionName,
|
|
1059
|
+
poolName: scope.poolName
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
function emitIndexFailure(runtime, payload, emitEvents) {
|
|
1063
|
+
if (!emitEvents) {
|
|
1064
|
+
return;
|
|
1065
|
+
}
|
|
1066
|
+
const emitter = runtime;
|
|
1067
|
+
emitter?.emit?.("model-index-error", payload);
|
|
1068
|
+
}
|
|
1027
1069
|
function warnIndexFailure(runtime, taskKey, error) {
|
|
1028
1070
|
const logger = runtime;
|
|
1029
1071
|
logger?.logger?.warn?.("[MonSQLize] model index creation failed", {
|
|
@@ -1031,9 +1073,10 @@ function warnIndexFailure(runtime, taskKey, error) {
|
|
|
1031
1073
|
error: error instanceof Error ? error.message : String(error)
|
|
1032
1074
|
});
|
|
1033
1075
|
}
|
|
1034
|
-
function scheduleIndexTask(collection,
|
|
1076
|
+
function scheduleIndexTask(collection, declaredIndex, emitEvents, options) {
|
|
1035
1077
|
const scope = resolveIndexTaskScope(collection, options);
|
|
1036
|
-
const
|
|
1078
|
+
const { key, options: indexOptions } = declaredIndex;
|
|
1079
|
+
const indexFingerprint = declaredIndex.fingerprint;
|
|
1037
1080
|
const taskKey = `${scope.poolName}:${scope.dbName}:${scope.collectionName}:${indexFingerprint}`;
|
|
1038
1081
|
const registry = getIndexTaskRegistry(options?.runtime);
|
|
1039
1082
|
const existing = registry.get(taskKey);
|
|
@@ -1051,6 +1094,14 @@ function scheduleIndexTask(collection, key, indexOptions, options) {
|
|
|
1051
1094
|
task.status = "failed";
|
|
1052
1095
|
task.error = error;
|
|
1053
1096
|
warnIndexFailure(options?.runtime, taskKey, error);
|
|
1097
|
+
emitIndexFailure(options?.runtime, {
|
|
1098
|
+
namespace: scope,
|
|
1099
|
+
taskKey,
|
|
1100
|
+
source: declaredIndex.source,
|
|
1101
|
+
key,
|
|
1102
|
+
options: indexOptions,
|
|
1103
|
+
error: summarizeIndexError(error)
|
|
1104
|
+
}, emitEvents);
|
|
1054
1105
|
resolve();
|
|
1055
1106
|
});
|
|
1056
1107
|
});
|
|
@@ -1167,6 +1218,134 @@ function resolveModelHooksFactory(definition) {
|
|
|
1167
1218
|
const hooks = toCompatDefinition(definition).hooks;
|
|
1168
1219
|
return typeof hooks === "function" ? hooks : null;
|
|
1169
1220
|
}
|
|
1221
|
+
function collectModelIndexDefinitions(definition, softDeleteConfig) {
|
|
1222
|
+
const declared = [];
|
|
1223
|
+
if (softDeleteConfig?.enabled && softDeleteConfig.type === "timestamp" && softDeleteConfig.ttl) {
|
|
1224
|
+
const key = { [softDeleteConfig.field]: 1 };
|
|
1225
|
+
const options = { expireAfterSeconds: softDeleteConfig.ttl };
|
|
1226
|
+
declared.push({
|
|
1227
|
+
source: "softDelete",
|
|
1228
|
+
key,
|
|
1229
|
+
options,
|
|
1230
|
+
name: getIndexOptionName(options),
|
|
1231
|
+
fingerprint: stableIndexStringify({ key, options })
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
const indexes = toCompatDefinition(definition).indexes;
|
|
1235
|
+
if (!Array.isArray(indexes) || indexes.length === 0) {
|
|
1236
|
+
return declared;
|
|
1237
|
+
}
|
|
1238
|
+
for (const indexSpec of indexes) {
|
|
1239
|
+
if (!isRecord(indexSpec) || !indexSpec.key) {
|
|
1240
|
+
continue;
|
|
1241
|
+
}
|
|
1242
|
+
const { key, ...indexOptions } = indexSpec;
|
|
1243
|
+
declared.push({
|
|
1244
|
+
source: "definition",
|
|
1245
|
+
key,
|
|
1246
|
+
options: indexOptions,
|
|
1247
|
+
name: getIndexOptionName(indexOptions),
|
|
1248
|
+
fingerprint: stableIndexStringify({ key, options: indexOptions })
|
|
1249
|
+
});
|
|
1250
|
+
}
|
|
1251
|
+
return declared;
|
|
1252
|
+
}
|
|
1253
|
+
async function ensureModelIndexesForCollection(collection, definition, softDeleteConfig, options = {}) {
|
|
1254
|
+
const namespace = toIndexNamespace(resolveIndexTaskScope(collection, options));
|
|
1255
|
+
const declared = collectModelIndexDefinitions(definition, softDeleteConfig);
|
|
1256
|
+
const existingIndexes = await collection.listIndexes();
|
|
1257
|
+
const existing = [];
|
|
1258
|
+
const missing = [];
|
|
1259
|
+
const conflicts = [];
|
|
1260
|
+
for (const declaredIndex of declared) {
|
|
1261
|
+
const existingByName = findExistingIndexByName(existingIndexes, declaredIndex.name);
|
|
1262
|
+
if (existingByName) {
|
|
1263
|
+
if (indexOptionsMatch(existingByName, declaredIndex)) {
|
|
1264
|
+
existing.push({ declared: declaredIndex, existing: existingByName });
|
|
1265
|
+
} else {
|
|
1266
|
+
conflicts.push({
|
|
1267
|
+
declared: declaredIndex,
|
|
1268
|
+
existing: existingByName,
|
|
1269
|
+
reason: "name-conflict"
|
|
1270
|
+
});
|
|
1271
|
+
}
|
|
1272
|
+
continue;
|
|
1273
|
+
}
|
|
1274
|
+
const existingByKey = findExistingIndexByKey(existingIndexes, declaredIndex.key);
|
|
1275
|
+
if (existingByKey) {
|
|
1276
|
+
if (indexOptionsMatch(existingByKey, declaredIndex)) {
|
|
1277
|
+
existing.push({ declared: declaredIndex, existing: existingByKey });
|
|
1278
|
+
} else {
|
|
1279
|
+
conflicts.push({
|
|
1280
|
+
declared: declaredIndex,
|
|
1281
|
+
existing: existingByKey,
|
|
1282
|
+
reason: "options-conflict"
|
|
1283
|
+
});
|
|
1284
|
+
}
|
|
1285
|
+
continue;
|
|
1286
|
+
}
|
|
1287
|
+
missing.push(declaredIndex);
|
|
1288
|
+
}
|
|
1289
|
+
const result = {
|
|
1290
|
+
dryRun: options.dryRun === true,
|
|
1291
|
+
namespace,
|
|
1292
|
+
declared,
|
|
1293
|
+
existing,
|
|
1294
|
+
missing,
|
|
1295
|
+
created: [],
|
|
1296
|
+
conflicts,
|
|
1297
|
+
failed: [],
|
|
1298
|
+
skipped: options.dryRun === true ? missing.map((declaredIndex) => ({ declared: declaredIndex, reason: "dry-run" })) : conflicts.map((conflict) => ({ declared: conflict.declared, reason: conflict.reason }))
|
|
1299
|
+
};
|
|
1300
|
+
if (conflicts.length > 0 && options.throwOnError) {
|
|
1301
|
+
throw createIndexEnsureError("Model index conflicts detected.", result);
|
|
1302
|
+
}
|
|
1303
|
+
if (options.dryRun === true) {
|
|
1304
|
+
return result;
|
|
1305
|
+
}
|
|
1306
|
+
for (const declaredIndex of missing) {
|
|
1307
|
+
try {
|
|
1308
|
+
const createdName = await collection.createIndex(declaredIndex.key, declaredIndex.options);
|
|
1309
|
+
result.created.push({
|
|
1310
|
+
declared: declaredIndex,
|
|
1311
|
+
name: typeof createdName === "string" ? createdName : void 0,
|
|
1312
|
+
result: createdName
|
|
1313
|
+
});
|
|
1314
|
+
} catch (error) {
|
|
1315
|
+
result.failed.push({
|
|
1316
|
+
declared: declaredIndex,
|
|
1317
|
+
error: summarizeIndexError(error)
|
|
1318
|
+
});
|
|
1319
|
+
if (options.throwOnError) {
|
|
1320
|
+
throw createIndexEnsureError(
|
|
1321
|
+
"Model index creation failed.",
|
|
1322
|
+
result,
|
|
1323
|
+
error instanceof Error ? error : void 0
|
|
1324
|
+
);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
return result;
|
|
1329
|
+
}
|
|
1330
|
+
function summarizeModelIndexEnsureResults(results) {
|
|
1331
|
+
return results.reduce((totals, result) => ({
|
|
1332
|
+
declared: totals.declared + result.declared.length,
|
|
1333
|
+
existing: totals.existing + result.existing.length,
|
|
1334
|
+
missing: totals.missing + result.missing.length,
|
|
1335
|
+
created: totals.created + result.created.length,
|
|
1336
|
+
conflicts: totals.conflicts + result.conflicts.length,
|
|
1337
|
+
failed: totals.failed + result.failed.length,
|
|
1338
|
+
skipped: totals.skipped + result.skipped.length
|
|
1339
|
+
}), {
|
|
1340
|
+
declared: 0,
|
|
1341
|
+
existing: 0,
|
|
1342
|
+
missing: 0,
|
|
1343
|
+
created: 0,
|
|
1344
|
+
conflicts: 0,
|
|
1345
|
+
failed: 0,
|
|
1346
|
+
skipped: 0
|
|
1347
|
+
});
|
|
1348
|
+
}
|
|
1170
1349
|
function initializeModelV1Methods(target, definition) {
|
|
1171
1350
|
const methods = toCompatDefinition(definition).methods;
|
|
1172
1351
|
if (typeof methods !== "function") {
|
|
@@ -1191,25 +1370,12 @@ function initializeModelV1Methods(target, definition) {
|
|
|
1191
1370
|
}
|
|
1192
1371
|
}
|
|
1193
1372
|
function scheduleModelIndexes(collection, definition, softDeleteConfig, options) {
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
scheduleIndexTask(
|
|
1197
|
-
collection,
|
|
1198
|
-
{ [softDeleteIndex.field]: 1 },
|
|
1199
|
-
{ expireAfterSeconds: softDeleteIndex.ttl },
|
|
1200
|
-
options
|
|
1201
|
-
);
|
|
1202
|
-
}
|
|
1203
|
-
const indexes = toCompatDefinition(definition).indexes;
|
|
1204
|
-
if (!Array.isArray(indexes) || indexes.length === 0) {
|
|
1373
|
+
const autoIndex = resolveModelAutoIndexOptions(definition, options?.autoIndex);
|
|
1374
|
+
if (!autoIndex.enabled) {
|
|
1205
1375
|
return;
|
|
1206
1376
|
}
|
|
1207
|
-
for (const
|
|
1208
|
-
|
|
1209
|
-
continue;
|
|
1210
|
-
}
|
|
1211
|
-
const { key, ...indexOptions } = indexSpec;
|
|
1212
|
-
scheduleIndexTask(collection, key, indexOptions, options);
|
|
1377
|
+
for (const declaredIndex of collectModelIndexDefinitions(definition, softDeleteConfig)) {
|
|
1378
|
+
scheduleIndexTask(collection, declaredIndex, autoIndex.emitEvents, options);
|
|
1213
1379
|
}
|
|
1214
1380
|
}
|
|
1215
1381
|
|
|
@@ -1777,7 +1943,8 @@ var ModelInstance = class {
|
|
|
1777
1943
|
runtime: this.runtime,
|
|
1778
1944
|
dbName: options.dbName,
|
|
1779
1945
|
poolName: options.poolName,
|
|
1780
|
-
collectionName: options.collectionName
|
|
1946
|
+
collectionName: options.collectionName,
|
|
1947
|
+
autoIndex: this.runtime.options?.autoIndex
|
|
1781
1948
|
});
|
|
1782
1949
|
this._v1InstanceMethods = initializeModelV1Methods(this, options.definition);
|
|
1783
1950
|
}
|
|
@@ -1981,6 +2148,15 @@ var ModelInstance = class {
|
|
|
1981
2148
|
listIndexes() {
|
|
1982
2149
|
return this.collection.listIndexes();
|
|
1983
2150
|
}
|
|
2151
|
+
ensureIndexes(options = {}) {
|
|
2152
|
+
return ensureModelIndexesForCollection(this.collection, this.definition, this._softDeleteConfig, {
|
|
2153
|
+
...options,
|
|
2154
|
+
runtime: this.runtime,
|
|
2155
|
+
dbName: this.dbName,
|
|
2156
|
+
poolName: this.poolName,
|
|
2157
|
+
collectionName: this.collectionName
|
|
2158
|
+
});
|
|
2159
|
+
}
|
|
1984
2160
|
dropIndex(name) {
|
|
1985
2161
|
return this.collection.dropIndex(name);
|
|
1986
2162
|
}
|
|
@@ -8079,6 +8255,26 @@ function createRuntimeModelInstance(host, name, scope) {
|
|
|
8079
8255
|
});
|
|
8080
8256
|
return instance;
|
|
8081
8257
|
}
|
|
8258
|
+
async function ensureRuntimeModelIndexes(host, options = {}) {
|
|
8259
|
+
const modelNames = options.models ?? Model.list();
|
|
8260
|
+
const models = [];
|
|
8261
|
+
for (const name of modelNames) {
|
|
8262
|
+
const model = host.scopedModel(name, {
|
|
8263
|
+
database: options.database,
|
|
8264
|
+
pool: options.pool
|
|
8265
|
+
});
|
|
8266
|
+
const result = await model.ensureIndexes({
|
|
8267
|
+
dryRun: options.dryRun,
|
|
8268
|
+
throwOnError: options.throwOnError
|
|
8269
|
+
});
|
|
8270
|
+
models.push({ name, result });
|
|
8271
|
+
}
|
|
8272
|
+
return {
|
|
8273
|
+
dryRun: options.dryRun === true,
|
|
8274
|
+
models,
|
|
8275
|
+
totals: summarizeModelIndexEnsureResults(models.map((item) => item.result))
|
|
8276
|
+
};
|
|
8277
|
+
}
|
|
8082
8278
|
|
|
8083
8279
|
// src/entry/runtime-core-hosts.ts
|
|
8084
8280
|
function resolveAdapterCache(state) {
|
|
@@ -10787,7 +10983,8 @@ var MonSQLizeRuntime = class {
|
|
|
10787
10983
|
namespace: d.namespace,
|
|
10788
10984
|
log: d.log,
|
|
10789
10985
|
countQueue: this.options.countQueue,
|
|
10790
|
-
models: this.options.models
|
|
10986
|
+
models: this.options.models,
|
|
10987
|
+
autoIndex: this.options.autoIndex
|
|
10791
10988
|
};
|
|
10792
10989
|
}
|
|
10793
10990
|
async close() {
|
|
@@ -10984,6 +11181,10 @@ var MonSQLizeRuntime = class {
|
|
|
10984
11181
|
cache.set(name, instance);
|
|
10985
11182
|
return instance;
|
|
10986
11183
|
}
|
|
11184
|
+
async ensureModelIndexes(options = {}) {
|
|
11185
|
+
this.ensureConnected();
|
|
11186
|
+
return ensureRuntimeModelIndexes(this, options);
|
|
11187
|
+
}
|
|
10987
11188
|
// Capability delegation ----------------------------------------------------
|
|
10988
11189
|
async startSession(options = {}) {
|
|
10989
11190
|
this.ensureConnected();
|
package/dist/esm/index.mjs
CHANGED
|
@@ -303,49 +303,8 @@ try {
|
|
|
303
303
|
_schemaValidateFn = mod.validate;
|
|
304
304
|
} catch {
|
|
305
305
|
}
|
|
306
|
-
var KNOWN_SCHEMA_BASE_TYPES = /* @__PURE__ */ new Set([
|
|
307
|
-
"string",
|
|
308
|
-
"number",
|
|
309
|
-
"boolean",
|
|
310
|
-
"integer",
|
|
311
|
-
"float",
|
|
312
|
-
"int",
|
|
313
|
-
"double",
|
|
314
|
-
"decimal",
|
|
315
|
-
"date",
|
|
316
|
-
"objectid",
|
|
317
|
-
"uuid",
|
|
318
|
-
"email",
|
|
319
|
-
"url",
|
|
320
|
-
"buffer",
|
|
321
|
-
"binary",
|
|
322
|
-
"object",
|
|
323
|
-
"array",
|
|
324
|
-
"any",
|
|
325
|
-
"mixed",
|
|
326
|
-
"null"
|
|
327
|
-
]);
|
|
328
|
-
function _extractBaseType(typeStr) {
|
|
329
|
-
const m = typeStr.match(/^[a-zA-Z_]+/);
|
|
330
|
-
return m ? m[0].toLowerCase() : "";
|
|
331
|
-
}
|
|
332
306
|
function _makeValidatingDslFn(realDsl) {
|
|
333
307
|
const validating = function validatingDsl(fields) {
|
|
334
|
-
if (fields && typeof fields === "object") {
|
|
335
|
-
for (const [field, spec] of Object.entries(fields)) {
|
|
336
|
-
if (typeof spec === "string") {
|
|
337
|
-
if (spec.includes("|")) {
|
|
338
|
-
continue;
|
|
339
|
-
}
|
|
340
|
-
const base = _extractBaseType(spec);
|
|
341
|
-
if (base && !KNOWN_SCHEMA_BASE_TYPES.has(base)) {
|
|
342
|
-
throw new TypeError(
|
|
343
|
-
`[schema] Invalid type "${base}" in field "${field}". Known types: ${[...KNOWN_SCHEMA_BASE_TYPES].join(", ")}.`
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
308
|
return realDsl(fields);
|
|
350
309
|
};
|
|
351
310
|
return validating;
|
|
@@ -980,6 +939,75 @@ function stableIndexStringify(value) {
|
|
|
980
939
|
}
|
|
981
940
|
return JSON.stringify(value) ?? "undefined";
|
|
982
941
|
}
|
|
942
|
+
function getIndexOptionName(options) {
|
|
943
|
+
return typeof options.name === "string" && options.name.length > 0 ? options.name : void 0;
|
|
944
|
+
}
|
|
945
|
+
function summarizeIndexError(error) {
|
|
946
|
+
if (error instanceof Error) {
|
|
947
|
+
const record = error;
|
|
948
|
+
return {
|
|
949
|
+
name: error.name,
|
|
950
|
+
message: error.message,
|
|
951
|
+
code: record.code ?? record.codeName
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
return {
|
|
955
|
+
message: String(error)
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
function isRecord(value) {
|
|
959
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
960
|
+
}
|
|
961
|
+
function getExistingIndexKey(index) {
|
|
962
|
+
return index.key;
|
|
963
|
+
}
|
|
964
|
+
function declaredOptionEntries(options) {
|
|
965
|
+
return Object.entries(options).filter(([name, value]) => {
|
|
966
|
+
if (value === void 0) return false;
|
|
967
|
+
if (name === "background") return false;
|
|
968
|
+
return true;
|
|
969
|
+
});
|
|
970
|
+
}
|
|
971
|
+
function indexOptionsMatch(existing, declared) {
|
|
972
|
+
if (stableIndexStringify(getExistingIndexKey(existing)) !== stableIndexStringify(declared.key)) {
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
for (const [name, value] of declaredOptionEntries(declared.options)) {
|
|
976
|
+
const existingValue = existing[name];
|
|
977
|
+
if (value === false && existingValue === void 0) {
|
|
978
|
+
continue;
|
|
979
|
+
}
|
|
980
|
+
if (stableIndexStringify(existingValue) !== stableIndexStringify(value)) {
|
|
981
|
+
return false;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
return true;
|
|
985
|
+
}
|
|
986
|
+
function findExistingIndexByName(existingIndexes, name) {
|
|
987
|
+
if (!name) return void 0;
|
|
988
|
+
return existingIndexes.find((index) => index.name === name);
|
|
989
|
+
}
|
|
990
|
+
function findExistingIndexByKey(existingIndexes, key) {
|
|
991
|
+
const fingerprint = stableIndexStringify(key);
|
|
992
|
+
return existingIndexes.find((index) => stableIndexStringify(getExistingIndexKey(index)) === fingerprint);
|
|
993
|
+
}
|
|
994
|
+
function createIndexEnsureError(message, result, cause) {
|
|
995
|
+
return createError(ErrorCodes.MONGODB_ERROR, message, [result], cause);
|
|
996
|
+
}
|
|
997
|
+
function resolveModelAutoIndexOptions(definition, runtimeAutoIndex) {
|
|
998
|
+
const modelAutoIndex = toCompatDefinition(definition).options?.autoIndex;
|
|
999
|
+
const value = modelAutoIndex ?? runtimeAutoIndex;
|
|
1000
|
+
if (value === false) {
|
|
1001
|
+
return { enabled: false, emitEvents: true };
|
|
1002
|
+
}
|
|
1003
|
+
if (value && typeof value === "object") {
|
|
1004
|
+
return {
|
|
1005
|
+
enabled: value.enabled !== false,
|
|
1006
|
+
emitEvents: value.emitEvents !== false
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
return { enabled: true, emitEvents: true };
|
|
1010
|
+
}
|
|
983
1011
|
function getIndexTaskRegistry(runtime) {
|
|
984
1012
|
if (!runtime) {
|
|
985
1013
|
return fallbackModelIndexTasks;
|
|
@@ -1007,6 +1035,20 @@ function resolveIndexTaskScope(collection, options) {
|
|
|
1007
1035
|
};
|
|
1008
1036
|
}
|
|
1009
1037
|
}
|
|
1038
|
+
function toIndexNamespace(scope) {
|
|
1039
|
+
return {
|
|
1040
|
+
db: scope.dbName,
|
|
1041
|
+
collection: scope.collectionName,
|
|
1042
|
+
poolName: scope.poolName
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
function emitIndexFailure(runtime, payload, emitEvents) {
|
|
1046
|
+
if (!emitEvents) {
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
const emitter = runtime;
|
|
1050
|
+
emitter?.emit?.("model-index-error", payload);
|
|
1051
|
+
}
|
|
1010
1052
|
function warnIndexFailure(runtime, taskKey, error) {
|
|
1011
1053
|
const logger = runtime;
|
|
1012
1054
|
logger?.logger?.warn?.("[MonSQLize] model index creation failed", {
|
|
@@ -1014,9 +1056,10 @@ function warnIndexFailure(runtime, taskKey, error) {
|
|
|
1014
1056
|
error: error instanceof Error ? error.message : String(error)
|
|
1015
1057
|
});
|
|
1016
1058
|
}
|
|
1017
|
-
function scheduleIndexTask(collection,
|
|
1059
|
+
function scheduleIndexTask(collection, declaredIndex, emitEvents, options) {
|
|
1018
1060
|
const scope = resolveIndexTaskScope(collection, options);
|
|
1019
|
-
const
|
|
1061
|
+
const { key, options: indexOptions } = declaredIndex;
|
|
1062
|
+
const indexFingerprint = declaredIndex.fingerprint;
|
|
1020
1063
|
const taskKey = `${scope.poolName}:${scope.dbName}:${scope.collectionName}:${indexFingerprint}`;
|
|
1021
1064
|
const registry = getIndexTaskRegistry(options?.runtime);
|
|
1022
1065
|
const existing = registry.get(taskKey);
|
|
@@ -1034,6 +1077,14 @@ function scheduleIndexTask(collection, key, indexOptions, options) {
|
|
|
1034
1077
|
task.status = "failed";
|
|
1035
1078
|
task.error = error;
|
|
1036
1079
|
warnIndexFailure(options?.runtime, taskKey, error);
|
|
1080
|
+
emitIndexFailure(options?.runtime, {
|
|
1081
|
+
namespace: scope,
|
|
1082
|
+
taskKey,
|
|
1083
|
+
source: declaredIndex.source,
|
|
1084
|
+
key,
|
|
1085
|
+
options: indexOptions,
|
|
1086
|
+
error: summarizeIndexError(error)
|
|
1087
|
+
}, emitEvents);
|
|
1037
1088
|
resolve();
|
|
1038
1089
|
});
|
|
1039
1090
|
});
|
|
@@ -1150,6 +1201,134 @@ function resolveModelHooksFactory(definition) {
|
|
|
1150
1201
|
const hooks = toCompatDefinition(definition).hooks;
|
|
1151
1202
|
return typeof hooks === "function" ? hooks : null;
|
|
1152
1203
|
}
|
|
1204
|
+
function collectModelIndexDefinitions(definition, softDeleteConfig) {
|
|
1205
|
+
const declared = [];
|
|
1206
|
+
if (softDeleteConfig?.enabled && softDeleteConfig.type === "timestamp" && softDeleteConfig.ttl) {
|
|
1207
|
+
const key = { [softDeleteConfig.field]: 1 };
|
|
1208
|
+
const options = { expireAfterSeconds: softDeleteConfig.ttl };
|
|
1209
|
+
declared.push({
|
|
1210
|
+
source: "softDelete",
|
|
1211
|
+
key,
|
|
1212
|
+
options,
|
|
1213
|
+
name: getIndexOptionName(options),
|
|
1214
|
+
fingerprint: stableIndexStringify({ key, options })
|
|
1215
|
+
});
|
|
1216
|
+
}
|
|
1217
|
+
const indexes = toCompatDefinition(definition).indexes;
|
|
1218
|
+
if (!Array.isArray(indexes) || indexes.length === 0) {
|
|
1219
|
+
return declared;
|
|
1220
|
+
}
|
|
1221
|
+
for (const indexSpec of indexes) {
|
|
1222
|
+
if (!isRecord(indexSpec) || !indexSpec.key) {
|
|
1223
|
+
continue;
|
|
1224
|
+
}
|
|
1225
|
+
const { key, ...indexOptions } = indexSpec;
|
|
1226
|
+
declared.push({
|
|
1227
|
+
source: "definition",
|
|
1228
|
+
key,
|
|
1229
|
+
options: indexOptions,
|
|
1230
|
+
name: getIndexOptionName(indexOptions),
|
|
1231
|
+
fingerprint: stableIndexStringify({ key, options: indexOptions })
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
return declared;
|
|
1235
|
+
}
|
|
1236
|
+
async function ensureModelIndexesForCollection(collection, definition, softDeleteConfig, options = {}) {
|
|
1237
|
+
const namespace = toIndexNamespace(resolveIndexTaskScope(collection, options));
|
|
1238
|
+
const declared = collectModelIndexDefinitions(definition, softDeleteConfig);
|
|
1239
|
+
const existingIndexes = await collection.listIndexes();
|
|
1240
|
+
const existing = [];
|
|
1241
|
+
const missing = [];
|
|
1242
|
+
const conflicts = [];
|
|
1243
|
+
for (const declaredIndex of declared) {
|
|
1244
|
+
const existingByName = findExistingIndexByName(existingIndexes, declaredIndex.name);
|
|
1245
|
+
if (existingByName) {
|
|
1246
|
+
if (indexOptionsMatch(existingByName, declaredIndex)) {
|
|
1247
|
+
existing.push({ declared: declaredIndex, existing: existingByName });
|
|
1248
|
+
} else {
|
|
1249
|
+
conflicts.push({
|
|
1250
|
+
declared: declaredIndex,
|
|
1251
|
+
existing: existingByName,
|
|
1252
|
+
reason: "name-conflict"
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
continue;
|
|
1256
|
+
}
|
|
1257
|
+
const existingByKey = findExistingIndexByKey(existingIndexes, declaredIndex.key);
|
|
1258
|
+
if (existingByKey) {
|
|
1259
|
+
if (indexOptionsMatch(existingByKey, declaredIndex)) {
|
|
1260
|
+
existing.push({ declared: declaredIndex, existing: existingByKey });
|
|
1261
|
+
} else {
|
|
1262
|
+
conflicts.push({
|
|
1263
|
+
declared: declaredIndex,
|
|
1264
|
+
existing: existingByKey,
|
|
1265
|
+
reason: "options-conflict"
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
continue;
|
|
1269
|
+
}
|
|
1270
|
+
missing.push(declaredIndex);
|
|
1271
|
+
}
|
|
1272
|
+
const result = {
|
|
1273
|
+
dryRun: options.dryRun === true,
|
|
1274
|
+
namespace,
|
|
1275
|
+
declared,
|
|
1276
|
+
existing,
|
|
1277
|
+
missing,
|
|
1278
|
+
created: [],
|
|
1279
|
+
conflicts,
|
|
1280
|
+
failed: [],
|
|
1281
|
+
skipped: options.dryRun === true ? missing.map((declaredIndex) => ({ declared: declaredIndex, reason: "dry-run" })) : conflicts.map((conflict) => ({ declared: conflict.declared, reason: conflict.reason }))
|
|
1282
|
+
};
|
|
1283
|
+
if (conflicts.length > 0 && options.throwOnError) {
|
|
1284
|
+
throw createIndexEnsureError("Model index conflicts detected.", result);
|
|
1285
|
+
}
|
|
1286
|
+
if (options.dryRun === true) {
|
|
1287
|
+
return result;
|
|
1288
|
+
}
|
|
1289
|
+
for (const declaredIndex of missing) {
|
|
1290
|
+
try {
|
|
1291
|
+
const createdName = await collection.createIndex(declaredIndex.key, declaredIndex.options);
|
|
1292
|
+
result.created.push({
|
|
1293
|
+
declared: declaredIndex,
|
|
1294
|
+
name: typeof createdName === "string" ? createdName : void 0,
|
|
1295
|
+
result: createdName
|
|
1296
|
+
});
|
|
1297
|
+
} catch (error) {
|
|
1298
|
+
result.failed.push({
|
|
1299
|
+
declared: declaredIndex,
|
|
1300
|
+
error: summarizeIndexError(error)
|
|
1301
|
+
});
|
|
1302
|
+
if (options.throwOnError) {
|
|
1303
|
+
throw createIndexEnsureError(
|
|
1304
|
+
"Model index creation failed.",
|
|
1305
|
+
result,
|
|
1306
|
+
error instanceof Error ? error : void 0
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
return result;
|
|
1312
|
+
}
|
|
1313
|
+
function summarizeModelIndexEnsureResults(results) {
|
|
1314
|
+
return results.reduce((totals, result) => ({
|
|
1315
|
+
declared: totals.declared + result.declared.length,
|
|
1316
|
+
existing: totals.existing + result.existing.length,
|
|
1317
|
+
missing: totals.missing + result.missing.length,
|
|
1318
|
+
created: totals.created + result.created.length,
|
|
1319
|
+
conflicts: totals.conflicts + result.conflicts.length,
|
|
1320
|
+
failed: totals.failed + result.failed.length,
|
|
1321
|
+
skipped: totals.skipped + result.skipped.length
|
|
1322
|
+
}), {
|
|
1323
|
+
declared: 0,
|
|
1324
|
+
existing: 0,
|
|
1325
|
+
missing: 0,
|
|
1326
|
+
created: 0,
|
|
1327
|
+
conflicts: 0,
|
|
1328
|
+
failed: 0,
|
|
1329
|
+
skipped: 0
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1153
1332
|
function initializeModelV1Methods(target, definition) {
|
|
1154
1333
|
const methods = toCompatDefinition(definition).methods;
|
|
1155
1334
|
if (typeof methods !== "function") {
|
|
@@ -1174,25 +1353,12 @@ function initializeModelV1Methods(target, definition) {
|
|
|
1174
1353
|
}
|
|
1175
1354
|
}
|
|
1176
1355
|
function scheduleModelIndexes(collection, definition, softDeleteConfig, options) {
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
scheduleIndexTask(
|
|
1180
|
-
collection,
|
|
1181
|
-
{ [softDeleteIndex.field]: 1 },
|
|
1182
|
-
{ expireAfterSeconds: softDeleteIndex.ttl },
|
|
1183
|
-
options
|
|
1184
|
-
);
|
|
1185
|
-
}
|
|
1186
|
-
const indexes = toCompatDefinition(definition).indexes;
|
|
1187
|
-
if (!Array.isArray(indexes) || indexes.length === 0) {
|
|
1356
|
+
const autoIndex = resolveModelAutoIndexOptions(definition, options?.autoIndex);
|
|
1357
|
+
if (!autoIndex.enabled) {
|
|
1188
1358
|
return;
|
|
1189
1359
|
}
|
|
1190
|
-
for (const
|
|
1191
|
-
|
|
1192
|
-
continue;
|
|
1193
|
-
}
|
|
1194
|
-
const { key, ...indexOptions } = indexSpec;
|
|
1195
|
-
scheduleIndexTask(collection, key, indexOptions, options);
|
|
1360
|
+
for (const declaredIndex of collectModelIndexDefinitions(definition, softDeleteConfig)) {
|
|
1361
|
+
scheduleIndexTask(collection, declaredIndex, autoIndex.emitEvents, options);
|
|
1196
1362
|
}
|
|
1197
1363
|
}
|
|
1198
1364
|
|
|
@@ -1760,7 +1926,8 @@ var ModelInstance = class {
|
|
|
1760
1926
|
runtime: this.runtime,
|
|
1761
1927
|
dbName: options.dbName,
|
|
1762
1928
|
poolName: options.poolName,
|
|
1763
|
-
collectionName: options.collectionName
|
|
1929
|
+
collectionName: options.collectionName,
|
|
1930
|
+
autoIndex: this.runtime.options?.autoIndex
|
|
1764
1931
|
});
|
|
1765
1932
|
this._v1InstanceMethods = initializeModelV1Methods(this, options.definition);
|
|
1766
1933
|
}
|
|
@@ -1964,6 +2131,15 @@ var ModelInstance = class {
|
|
|
1964
2131
|
listIndexes() {
|
|
1965
2132
|
return this.collection.listIndexes();
|
|
1966
2133
|
}
|
|
2134
|
+
ensureIndexes(options = {}) {
|
|
2135
|
+
return ensureModelIndexesForCollection(this.collection, this.definition, this._softDeleteConfig, {
|
|
2136
|
+
...options,
|
|
2137
|
+
runtime: this.runtime,
|
|
2138
|
+
dbName: this.dbName,
|
|
2139
|
+
poolName: this.poolName,
|
|
2140
|
+
collectionName: this.collectionName
|
|
2141
|
+
});
|
|
2142
|
+
}
|
|
1967
2143
|
dropIndex(name) {
|
|
1968
2144
|
return this.collection.dropIndex(name);
|
|
1969
2145
|
}
|
|
@@ -8062,6 +8238,26 @@ function createRuntimeModelInstance(host, name, scope) {
|
|
|
8062
8238
|
});
|
|
8063
8239
|
return instance;
|
|
8064
8240
|
}
|
|
8241
|
+
async function ensureRuntimeModelIndexes(host, options = {}) {
|
|
8242
|
+
const modelNames = options.models ?? Model.list();
|
|
8243
|
+
const models = [];
|
|
8244
|
+
for (const name of modelNames) {
|
|
8245
|
+
const model = host.scopedModel(name, {
|
|
8246
|
+
database: options.database,
|
|
8247
|
+
pool: options.pool
|
|
8248
|
+
});
|
|
8249
|
+
const result = await model.ensureIndexes({
|
|
8250
|
+
dryRun: options.dryRun,
|
|
8251
|
+
throwOnError: options.throwOnError
|
|
8252
|
+
});
|
|
8253
|
+
models.push({ name, result });
|
|
8254
|
+
}
|
|
8255
|
+
return {
|
|
8256
|
+
dryRun: options.dryRun === true,
|
|
8257
|
+
models,
|
|
8258
|
+
totals: summarizeModelIndexEnsureResults(models.map((item) => item.result))
|
|
8259
|
+
};
|
|
8260
|
+
}
|
|
8065
8261
|
|
|
8066
8262
|
// src/entry/runtime-core-hosts.ts
|
|
8067
8263
|
function resolveAdapterCache(state) {
|
|
@@ -10773,7 +10969,8 @@ var MonSQLizeRuntime = class {
|
|
|
10773
10969
|
namespace: d.namespace,
|
|
10774
10970
|
log: d.log,
|
|
10775
10971
|
countQueue: this.options.countQueue,
|
|
10776
|
-
models: this.options.models
|
|
10972
|
+
models: this.options.models,
|
|
10973
|
+
autoIndex: this.options.autoIndex
|
|
10777
10974
|
};
|
|
10778
10975
|
}
|
|
10779
10976
|
async close() {
|
|
@@ -10970,6 +11167,10 @@ var MonSQLizeRuntime = class {
|
|
|
10970
11167
|
cache.set(name, instance);
|
|
10971
11168
|
return instance;
|
|
10972
11169
|
}
|
|
11170
|
+
async ensureModelIndexes(options = {}) {
|
|
11171
|
+
this.ensureConnected();
|
|
11172
|
+
return ensureRuntimeModelIndexes(this, options);
|
|
11173
|
+
}
|
|
10973
11174
|
// Capability delegation ----------------------------------------------------
|
|
10974
11175
|
async startSession(options = {}) {
|
|
10975
11176
|
this.ensureConnected();
|
package/dist/types/model.d.ts
CHANGED
|
@@ -62,6 +62,104 @@ export interface VirtualConfig {
|
|
|
62
62
|
set?: (this: Record<string, unknown>, value: unknown) => void;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
export type ModelAutoIndexOptions = boolean | {
|
|
66
|
+
/** Enable automatic model index creation. Defaults to true for backward compatibility. */
|
|
67
|
+
enabled?: boolean;
|
|
68
|
+
/** Emit `model-index-error` when automatic index creation fails. Defaults to true. */
|
|
69
|
+
emitEvents?: boolean;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export type ModelIndexSource = 'definition' | 'softDelete';
|
|
73
|
+
|
|
74
|
+
export interface ModelDeclaredIndex {
|
|
75
|
+
source: ModelIndexSource;
|
|
76
|
+
key: unknown;
|
|
77
|
+
options: Record<string, unknown>;
|
|
78
|
+
name?: string;
|
|
79
|
+
fingerprint: string;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface ModelIndexNamespace {
|
|
83
|
+
db: string;
|
|
84
|
+
collection: string;
|
|
85
|
+
poolName: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface ModelIndexErrorSummary {
|
|
89
|
+
name?: string;
|
|
90
|
+
message: string;
|
|
91
|
+
code?: unknown;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface ModelIndexEnsureExisting {
|
|
95
|
+
declared: ModelDeclaredIndex;
|
|
96
|
+
existing: Record<string, unknown>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface ModelIndexConflict {
|
|
100
|
+
declared: ModelDeclaredIndex;
|
|
101
|
+
existing?: Record<string, unknown>;
|
|
102
|
+
reason: string;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface ModelIndexCreated {
|
|
106
|
+
declared: ModelDeclaredIndex;
|
|
107
|
+
name?: string;
|
|
108
|
+
result?: unknown;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export interface ModelIndexFailure {
|
|
112
|
+
declared: ModelDeclaredIndex;
|
|
113
|
+
error: ModelIndexErrorSummary;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface ModelIndexSkipped {
|
|
117
|
+
declared: ModelDeclaredIndex;
|
|
118
|
+
reason: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface ModelEnsureIndexesOptions {
|
|
122
|
+
/** Return the index diff without creating missing indexes. */
|
|
123
|
+
dryRun?: boolean;
|
|
124
|
+
/** Throw a MonSQLize `MONGODB_ERROR` when conflicts or creation failures are found. */
|
|
125
|
+
throwOnError?: boolean;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface ModelEnsureAllIndexesOptions extends ModelEnsureIndexesOptions {
|
|
129
|
+
/** Limit the operation to specific registered model names. Defaults to all models. */
|
|
130
|
+
models?: string[];
|
|
131
|
+
/** Optional database scope for models without their own connection override. */
|
|
132
|
+
database?: string;
|
|
133
|
+
/** Optional pool scope for models without their own connection override. */
|
|
134
|
+
pool?: string;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface ModelIndexEnsureResult {
|
|
138
|
+
dryRun: boolean;
|
|
139
|
+
namespace: ModelIndexNamespace;
|
|
140
|
+
declared: ModelDeclaredIndex[];
|
|
141
|
+
existing: ModelIndexEnsureExisting[];
|
|
142
|
+
missing: ModelDeclaredIndex[];
|
|
143
|
+
created: ModelIndexCreated[];
|
|
144
|
+
conflicts: ModelIndexConflict[];
|
|
145
|
+
failed: ModelIndexFailure[];
|
|
146
|
+
skipped: ModelIndexSkipped[];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export interface ModelIndexEnsureSummary {
|
|
150
|
+
dryRun: boolean;
|
|
151
|
+
models: Array<{ name: string; result: ModelIndexEnsureResult }>;
|
|
152
|
+
totals: {
|
|
153
|
+
declared: number;
|
|
154
|
+
existing: number;
|
|
155
|
+
missing: number;
|
|
156
|
+
created: number;
|
|
157
|
+
conflicts: number;
|
|
158
|
+
failed: number;
|
|
159
|
+
skipped: number;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
65
163
|
/** v1 hooks factory format */
|
|
66
164
|
export type V1HooksFactory<TDocument = Record<string, unknown>> = (
|
|
67
165
|
model: ModelInstance<TDocument>,
|
|
@@ -95,6 +193,7 @@ export type V1MethodsFactory<TDocument = Record<string, unknown>> = (
|
|
|
95
193
|
export interface ModelDefinitionOptions {
|
|
96
194
|
timestamps?: boolean | { createdAt?: string | boolean; updatedAt?: string | boolean };
|
|
97
195
|
validate?: boolean;
|
|
196
|
+
autoIndex?: ModelAutoIndexOptions;
|
|
98
197
|
softDelete?: boolean | {
|
|
99
198
|
enabled?: boolean;
|
|
100
199
|
field?: string;
|
|
@@ -443,6 +542,11 @@ export interface ModelInstance<TDocument = any> {
|
|
|
443
542
|
createIndexes(specs: Array<{ key: unknown; } & Record<string, unknown>>): Promise<string[]>;
|
|
444
543
|
/** Lists all existing index definitions on the collection. */
|
|
445
544
|
listIndexes(): Promise<Record<string, unknown>[]>;
|
|
545
|
+
/**
|
|
546
|
+
* Compares declared model indexes with the database and optionally creates missing indexes.
|
|
547
|
+
* Does not drop, rename, or rebuild conflicting indexes.
|
|
548
|
+
*/
|
|
549
|
+
ensureIndexes(options?: ModelEnsureIndexesOptions): Promise<ModelIndexEnsureResult>;
|
|
446
550
|
/**
|
|
447
551
|
* Drops the specified index by name.
|
|
448
552
|
* @param name Index name.
|
|
@@ -33,7 +33,7 @@ export interface SSHConfig {
|
|
|
33
33
|
|
|
34
34
|
import type { Collection, DbAccessor, HealthView } from './collection';
|
|
35
35
|
import type { Lock, LockOptions, LockStats } from './lock';
|
|
36
|
-
import type { ModelInstance } from './model';
|
|
36
|
+
import type { ModelAutoIndexOptions, ModelEnsureAllIndexesOptions, ModelIndexEnsureSummary, ModelInstance } from './model';
|
|
37
37
|
import type { MongoConnectConfig } from './mongodb';
|
|
38
38
|
import type { ConnectionPoolManagerOptions, PoolConfig, PoolHealthStatus, PoolStats, PoolStrategy } from './pool';
|
|
39
39
|
import type {
|
|
@@ -176,6 +176,8 @@ export interface MonSQLizeOptions {
|
|
|
176
176
|
countQueue?: boolean | { enabled?: boolean; concurrency?: number; maxQueueSize?: number; timeout?: number; };
|
|
177
177
|
/** Model definitions to auto-register on connect. Accepts a file path (string) or an object with { path, pattern?, recursive? }. @since v1.3.0 */
|
|
178
178
|
models?: string | { path: string; pattern?: string; recursive?: boolean; };
|
|
179
|
+
/** Global automatic model index creation control. Defaults to true for backward compatibility. */
|
|
180
|
+
autoIndex?: ModelAutoIndexOptions;
|
|
179
181
|
/** Auto-invalidate cache on write operations. @since v1.3.0 */
|
|
180
182
|
cacheAutoInvalidate?: boolean;
|
|
181
183
|
}
|
|
@@ -256,6 +258,11 @@ export interface MonSQLizeInstance {
|
|
|
256
258
|
*/
|
|
257
259
|
model<TDocument = any>(name: string): ModelInstance<TDocument>;
|
|
258
260
|
model(name: string): ModelInstance<any>;
|
|
261
|
+
/**
|
|
262
|
+
* Ensures declared indexes for registered models.
|
|
263
|
+
* Use `dryRun: true` for production preflight; execution only creates missing indexes.
|
|
264
|
+
*/
|
|
265
|
+
ensureModelIndexes(options?: ModelEnsureAllIndexesOptions): Promise<ModelIndexEnsureSummary>;
|
|
259
266
|
/**
|
|
260
267
|
* Start a MongoDB transaction session.
|
|
261
268
|
* @param options Optional transaction options.
|
|
@@ -442,6 +449,7 @@ export default class MonSQLize implements MonSQLizeInstance {
|
|
|
442
449
|
scopedModel(name: string, options?: { database?: string; pool?: string; }): ModelInstance<any>;
|
|
443
450
|
model<TDocument = any>(name: string): ModelInstance<TDocument>;
|
|
444
451
|
model(name: string): ModelInstance<any>;
|
|
452
|
+
ensureModelIndexes(options?: ModelEnsureAllIndexesOptions): Promise<ModelIndexEnsureSummary>;
|
|
445
453
|
startSession(options?: TransactionOptions): Promise<Transaction>;
|
|
446
454
|
withTransaction<T>(callback: (transaction: Transaction) => Promise<T>, options?: TransactionOptions): Promise<T>;
|
|
447
455
|
withLock<T>(key: string, callback: () => Promise<T>, options?: LockOptions): Promise<T>;
|
package/dist/types/runtime.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BookmarkClearResult, BookmarkListResult, BookmarkPrewarmResult, DeleteBatchResult, DeleteResult, IncrementOneResult, IndexCreateResult, InsertBatchResult, InsertManyResult, UpdateBatchResult, UpdateResult } from './collection';
|
|
2
2
|
import type { LoggerLike, ExpressionFunction, ExpressionObject } from './base';
|
|
3
|
-
import type { ModelDefinition, ModelInstance as ModelInstanceContract, RegisteredModel, RelationConfig } from './model';
|
|
3
|
+
import type { ModelDefinition, ModelEnsureIndexesOptions, ModelIndexEnsureResult, ModelInstance as ModelInstanceContract, RegisteredModel, RelationConfig } from './model';
|
|
4
4
|
import type { LockOptions, LockStats } from './lock';
|
|
5
5
|
import type { ConnectionPoolManagerOptions, FallbackStrategy, PoolConfig, PoolHealthStatus, PoolRole, PoolStats, PoolStrategy } from './pool';
|
|
6
6
|
import type { SagaDefinition, SagaOrchestratorOptions, SagaResult, SagaStats, SagaStep } from './saga';
|
|
@@ -335,6 +335,7 @@ export declare class ModelInstance<TDocument = any> implements ModelInstanceCont
|
|
|
335
335
|
createIndex(keys: unknown, options?: unknown): Promise<IndexCreateResult>;
|
|
336
336
|
createIndexes(specs: Array<{ key: unknown; } & Record<string, unknown>>): Promise<string[]>;
|
|
337
337
|
listIndexes(): Promise<Record<string, unknown>[]>;
|
|
338
|
+
ensureIndexes(options?: ModelEnsureIndexesOptions): Promise<ModelIndexEnsureResult>;
|
|
338
339
|
dropIndex(name: string): Promise<unknown>;
|
|
339
340
|
dropIndexes(): Promise<unknown>;
|
|
340
341
|
prewarmBookmarks(keyDims?: unknown, pages?: number[]): Promise<BookmarkPrewarmResult>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "monsqlize",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "TypeScript-native MongoDB ODM with multi-level caching (cache-hub), distributed locks, Saga orchestration, unified expression system (122 operators), connection pool management, ChangeStream sync, slow-query logging, and full v1 API compatibility",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./dist/cjs/index.cjs",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"dist/**/*.cjs",
|
|
19
19
|
"dist/**/*.mjs",
|
|
20
20
|
"dist/**/*.d.ts",
|
|
21
|
+
"changelogs/v2.0.5.md",
|
|
22
|
+
"changelogs/v2.0.4.md",
|
|
21
23
|
"changelogs/v2.0.3.md",
|
|
22
24
|
"changelogs/v2.0.2.md",
|
|
23
25
|
"changelogs/v2.0.1.md",
|
|
@@ -115,7 +117,7 @@
|
|
|
115
117
|
"cache-hub": "2.2.4",
|
|
116
118
|
"ioredis": "5.8.2",
|
|
117
119
|
"mongodb": "6.21.0",
|
|
118
|
-
"schema-dsl": "2.0.
|
|
120
|
+
"schema-dsl": "2.0.10",
|
|
119
121
|
"ssh2": "1.17.0"
|
|
120
122
|
}
|
|
121
123
|
}
|