Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d254f58a05 | |||
| c5e7b6f982 | |||
| d30c9619c5 | |||
| 7344ae2db3 | |||
| 3b29a150a8 | |||
| 59186d84a9 |
1
.serena/.gitignore
vendored
Normal file
1
.serena/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/cache
|
||||||
Binary file not shown.
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -22,5 +22,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"deno.enable": false
|
||||||
}
|
}
|
||||||
|
|||||||
26
changelog.md
26
changelog.md
@@ -1,5 +1,31 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-11-17 - 5.16.7 - fix(classes.collection)
|
||||||
|
Improve Deno and TypeScript compatibility: Collection decorator _svDbOptions forwarding and config cleanup
|
||||||
|
|
||||||
|
- Collection decorator: capture original constructor and forward _svDbOptions to ensure property decorator options (serialize/deserialize) remain accessible in Deno environments.
|
||||||
|
- Collection decorator: keep instance getter defined on prototype for Deno compatibility (no behavior change, clarifies forwarding logic).
|
||||||
|
- Build/config: removed experimentalDecorators and useDefineForClassFields from deno.json and tsconfig.json to avoid Deno/TS build issues and rely on default compilation settings.
|
||||||
|
|
||||||
|
## 2025-11-17 - 5.16.6 - fix(classes)
|
||||||
|
Add Deno compatibility, prototype-safe decorators and safe collection accessor; bump a few deps
|
||||||
|
|
||||||
|
- Add deno.json to enable experimentalDecorators and target ES2022/DOM for Deno builds.
|
||||||
|
- Introduce getCollectionSafe() on SmartDataDbDoc and use it for save/update/delete/findOne to avoid runtime errors when instance 'collection' is not present.
|
||||||
|
- Change several instance properties (globalSaveableProperties, uniqueIndexes, regularIndexes, saveableProperties) to 'declare' so decorator-set prototype properties are not shadowed (Deno compatibility).
|
||||||
|
- Enhance @Collection decorator: capture original constructor/prototype for Deno, define prototype getter for collection on decorated class, attach docCtor for searchableFields, and forward _svDbOptions to the original constructor to preserve serializer metadata.
|
||||||
|
- Improve text/search index handling by relying on docCtor.searchableFields and guarding text index creation.
|
||||||
|
- Bump dependencies/devDependencies: @push.rocks/smartmongo -> ^2.0.14, @git.zone/tsbuild -> ^2.7.1, @git.zone/tstest -> ^2.8.1.
|
||||||
|
- These are non-breaking runtime compatibility and developer-experience fixes; intended as a patch release.
|
||||||
|
|
||||||
|
## 2025-11-16 - 5.16.5 - fix(watcher)
|
||||||
|
Update dependencies, tooling and watcher import; add .serena cache ignore
|
||||||
|
|
||||||
|
- Bump runtime dependencies: @push.rocks/smartlog 3.1.8 → 3.1.10, @push.rocks/smartstring 4.0.15 → 4.1.0, @push.rocks/taskbuffer 3.1.7 → 3.4.0, @tsclass/tsclass 9.2.0 → 9.3.0, mongodb 6.18.0 → 6.20.0
|
||||||
|
- Bump devDependencies: @git.zone/tsbuild 2.6.7 → 2.6.8, @git.zone/tsrun 1.2.44 → 1.6.2, @git.zone/tstest 2.3.5 → 2.6.2
|
||||||
|
- Switch EventEmitter import to node:events in ts/classes.watcher.ts to use the namespaced Node import
|
||||||
|
- Add .serena/.gitignore to ignore /cache
|
||||||
|
|
||||||
## 2025-08-18 - 5.16.4 - fix(classes.doc (convertFilterForMongoDb))
|
## 2025-08-18 - 5.16.4 - fix(classes.doc (convertFilterForMongoDb))
|
||||||
Improve filter conversion: handle logical operators, merge operator objects, add nested filter tests and docs, and fix test script
|
Improve filter conversion: handle logical operators, merge operator objects, add nested filter tests and docs, and fix test script
|
||||||
|
|
||||||
|
|||||||
10
deno.json
Normal file
10
deno.json
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": [
|
||||||
|
"ES2022",
|
||||||
|
"DOM"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nodeModulesDir": "auto",
|
||||||
|
"version": "5.16.7"
|
||||||
|
}
|
||||||
20
package.json
20
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@push.rocks/smartdata",
|
"name": "@push.rocks/smartdata",
|
||||||
"version": "5.16.4",
|
"version": "5.16.7",
|
||||||
"private": false,
|
"private": false,
|
||||||
"description": "An advanced library for NoSQL data organization and manipulation using TypeScript with support for MongoDB, data validation, collections, and custom data types.",
|
"description": "An advanced library for NoSQL data organization and manipulation using TypeScript with support for MongoDB, data validation, collections, and custom data types.",
|
||||||
"main": "dist_ts/index.js",
|
"main": "dist_ts/index.js",
|
||||||
@@ -25,21 +25,21 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@push.rocks/lik": "^6.2.2",
|
"@push.rocks/lik": "^6.2.2",
|
||||||
"@push.rocks/smartdelay": "^3.0.1",
|
"@push.rocks/smartdelay": "^3.0.1",
|
||||||
"@push.rocks/smartlog": "^3.1.8",
|
"@push.rocks/smartlog": "^3.1.10",
|
||||||
"@push.rocks/smartmongo": "^2.0.12",
|
"@push.rocks/smartmongo": "^2.0.14",
|
||||||
"@push.rocks/smartpromise": "^4.0.2",
|
"@push.rocks/smartpromise": "^4.0.2",
|
||||||
"@push.rocks/smartrx": "^3.0.10",
|
"@push.rocks/smartrx": "^3.0.10",
|
||||||
"@push.rocks/smartstring": "^4.0.15",
|
"@push.rocks/smartstring": "^4.1.0",
|
||||||
"@push.rocks/smarttime": "^4.0.6",
|
"@push.rocks/smarttime": "^4.0.6",
|
||||||
"@push.rocks/smartunique": "^3.0.8",
|
"@push.rocks/smartunique": "^3.0.8",
|
||||||
"@push.rocks/taskbuffer": "^3.1.7",
|
"@push.rocks/taskbuffer": "^3.4.0",
|
||||||
"@tsclass/tsclass": "^9.2.0",
|
"@tsclass/tsclass": "^9.3.0",
|
||||||
"mongodb": "^6.18.0"
|
"mongodb": "^6.20.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@git.zone/tsbuild": "^2.6.7",
|
"@git.zone/tsbuild": "^2.7.1",
|
||||||
"@git.zone/tsrun": "^1.2.44",
|
"@git.zone/tsrun": "^1.6.2",
|
||||||
"@git.zone/tstest": "^2.3.5",
|
"@git.zone/tstest": "^2.8.1",
|
||||||
"@push.rocks/qenv": "^6.1.3",
|
"@push.rocks/qenv": "^6.1.3",
|
||||||
"@push.rocks/tapbundle": "^6.0.3",
|
"@push.rocks/tapbundle": "^6.0.3",
|
||||||
"@types/node": "^22.15.2"
|
"@types/node": "^22.15.2"
|
||||||
|
|||||||
3942
pnpm-lock.yaml
generated
3942
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
255
test/test.deno.ts
Normal file
255
test/test.deno.ts
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
// TODO: Decorator support during testing for bun and deno in @git.zone/tstest
|
||||||
|
|
||||||
|
import { tap, expect } from '@push.rocks/tapbundle';
|
||||||
|
import { Qenv } from '@push.rocks/qenv';
|
||||||
|
import * as smartmongo from '@push.rocks/smartmongo';
|
||||||
|
import { smartunique } from '../ts/plugins.js';
|
||||||
|
|
||||||
|
import * as mongodb from 'mongodb';
|
||||||
|
|
||||||
|
const testQenv = new Qenv(process.cwd(), process.cwd() + '/.nogit/');
|
||||||
|
|
||||||
|
console.log(process.memoryUsage());
|
||||||
|
|
||||||
|
// the tested module
|
||||||
|
import * as smartdata from '../ts/index.js';
|
||||||
|
|
||||||
|
// =======================================
|
||||||
|
// Connecting to the database server
|
||||||
|
// =======================================
|
||||||
|
|
||||||
|
let smartmongoInstance: smartmongo.SmartMongo;
|
||||||
|
let testDb: smartdata.SmartdataDb;
|
||||||
|
|
||||||
|
const totalCars = 2000;
|
||||||
|
|
||||||
|
tap.test('should create a testinstance as database', async () => {
|
||||||
|
const databaseName = `test-smartdata-deno-${smartunique.shortId()}`;
|
||||||
|
testDb = new smartdata.SmartdataDb({
|
||||||
|
mongoDbUrl: await testQenv.getEnvVarOnDemand('MONGODB_URL'),
|
||||||
|
mongoDbName: databaseName,
|
||||||
|
});
|
||||||
|
await testDb.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
// =======================================
|
||||||
|
// The actual tests
|
||||||
|
// =======================================
|
||||||
|
|
||||||
|
// ------
|
||||||
|
// Collections
|
||||||
|
// ------
|
||||||
|
|
||||||
|
@smartdata.Collection(() => {
|
||||||
|
return testDb;
|
||||||
|
})
|
||||||
|
class Car extends smartdata.SmartDataDbDoc<Car, Car> {
|
||||||
|
@smartdata.unI()
|
||||||
|
public index: string = smartunique.shortId();
|
||||||
|
|
||||||
|
@smartdata.svDb()
|
||||||
|
public color: string;
|
||||||
|
|
||||||
|
@smartdata.svDb()
|
||||||
|
public brand: string;
|
||||||
|
|
||||||
|
@smartdata.svDb()
|
||||||
|
public testBuffer = Buffer.from('hello');
|
||||||
|
|
||||||
|
@smartdata.svDb()
|
||||||
|
deepData = {
|
||||||
|
sodeep: 'yes',
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(colorArg: string, brandArg: string) {
|
||||||
|
super();
|
||||||
|
this.color = colorArg;
|
||||||
|
this.brand = brandArg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tap.test('should create a new id', async () => {
|
||||||
|
const newid = await Car.getNewId();
|
||||||
|
console.log(newid);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should save the car to the db', async (toolsArg) => {
|
||||||
|
const myCar = new Car('red', 'Volvo');
|
||||||
|
console.log('Car.collection.smartdataDb:', (Car.collection as any).smartdataDb?.mongoDb?.databaseName);
|
||||||
|
console.log('Car.collection.collectionName:', (Car.collection as any).collectionName);
|
||||||
|
console.log('testDb.mongoDb.databaseName:', testDb.mongoDb.databaseName);
|
||||||
|
await myCar.save();
|
||||||
|
|
||||||
|
const myCar2 = new Car('red', 'Volvo');
|
||||||
|
await myCar2.save();
|
||||||
|
|
||||||
|
let counter = 0;
|
||||||
|
|
||||||
|
const gottenCarInstance = await Car.getInstance({});
|
||||||
|
console.log(gottenCarInstance.testBuffer instanceof mongodb.Binary);
|
||||||
|
process.memoryUsage();
|
||||||
|
do {
|
||||||
|
const myCar3 = new Car('red', 'Renault');
|
||||||
|
await myCar3.save();
|
||||||
|
counter++;
|
||||||
|
if (counter % 100 === 0) {
|
||||||
|
console.log(
|
||||||
|
`Filled database with ${counter} of ${totalCars} Cars and memory usage ${
|
||||||
|
process.memoryUsage().rss / 1e6
|
||||||
|
} MB`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} while (counter < totalCars);
|
||||||
|
console.log(process.memoryUsage());
|
||||||
|
|
||||||
|
// DEBUG: Check what's actually in the database
|
||||||
|
const savedCount = await Car.getCount({});
|
||||||
|
console.log('Total cars saved in DB:', savedCount);
|
||||||
|
const renaultCount = await Car.getCount({ brand: 'Renault' });
|
||||||
|
console.log('Renault cars in DB:', renaultCount);
|
||||||
|
|
||||||
|
// Check what's actually in the first saved car
|
||||||
|
const firstCar = await Car.getInstance({});
|
||||||
|
console.log('First car data:', JSON.stringify({
|
||||||
|
color: firstCar?.color,
|
||||||
|
brand: firstCar?.brand,
|
||||||
|
index: firstCar?.index
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('expect to get instance of Car with shallow match', async () => {
|
||||||
|
console.log('Before query - testDb.mongoDb.databaseName:', testDb.mongoDb.databaseName);
|
||||||
|
console.log('Before query - Car.collection.smartdataDb:', (Car.collection as any).smartdataDb?.mongoDb?.databaseName);
|
||||||
|
console.log('Before query - Car.collection.collectionName:', (Car.collection as any).collectionName);
|
||||||
|
|
||||||
|
const totalQueryCycles = totalCars / 2;
|
||||||
|
let counter = 0;
|
||||||
|
do {
|
||||||
|
const timeStart = Date.now();
|
||||||
|
const myCars = await Car.getInstances({
|
||||||
|
brand: 'Renault',
|
||||||
|
});
|
||||||
|
if (counter % 10 === 0) {
|
||||||
|
console.log(
|
||||||
|
`performed ${counter} of ${totalQueryCycles} total query cycles: took ${
|
||||||
|
Date.now() - timeStart
|
||||||
|
}ms to query a set of 2000 with memory footprint ${process.memoryUsage().rss / 1e6} MB`,
|
||||||
|
);
|
||||||
|
console.log('myCars.length:', myCars.length);
|
||||||
|
console.log('myCars[0]:', myCars[0]);
|
||||||
|
}
|
||||||
|
expect(myCars[0].deepData.sodeep).toEqual('yes');
|
||||||
|
expect(myCars[0].brand).toEqual('Renault');
|
||||||
|
counter++;
|
||||||
|
} while (counter < totalQueryCycles);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('expect to get instance of Car with deep match', async () => {
|
||||||
|
const totalQueryCycles = totalCars / 6;
|
||||||
|
let counter = 0;
|
||||||
|
do {
|
||||||
|
const timeStart = Date.now();
|
||||||
|
const myCars2 = await Car.getInstances({
|
||||||
|
deepData: {
|
||||||
|
sodeep: 'yes',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (counter % 10 === 0) {
|
||||||
|
console.log(
|
||||||
|
`performed ${counter} of ${totalQueryCycles} total query cycles: took ${
|
||||||
|
Date.now() - timeStart
|
||||||
|
}ms to deep query a set of 2000 with memory footprint ${process.memoryUsage().rss / 1e6} MB`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
expect(myCars2[0].deepData.sodeep).toEqual('yes');
|
||||||
|
expect(myCars2[0].brand).toEqual('Volvo');
|
||||||
|
counter++;
|
||||||
|
} while (counter < totalQueryCycles);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('expect to get instance of Car and update it', async () => {
|
||||||
|
const myCar = await Car.getInstance<Car>({
|
||||||
|
brand: 'Volvo',
|
||||||
|
});
|
||||||
|
expect(myCar.color).toEqual('red');
|
||||||
|
myCar.color = 'blue';
|
||||||
|
await myCar.save();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should be able to delete an instance of car', async () => {
|
||||||
|
const myCars = await Car.getInstances({
|
||||||
|
brand: 'Volvo',
|
||||||
|
color: 'blue',
|
||||||
|
});
|
||||||
|
console.log(myCars);
|
||||||
|
expect(myCars[0].color).toEqual('blue');
|
||||||
|
for (const myCar of myCars) {
|
||||||
|
await myCar.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
const myCar2 = await Car.getInstance<Car>({
|
||||||
|
brand: 'Volvo',
|
||||||
|
});
|
||||||
|
expect(myCar2.color).toEqual('red');
|
||||||
|
});
|
||||||
|
|
||||||
|
// tslint:disable-next-line: max-classes-per-file
|
||||||
|
@smartdata.Collection(() => {
|
||||||
|
return testDb;
|
||||||
|
})
|
||||||
|
class Truck extends smartdata.SmartDataDbDoc<Car, Car> {
|
||||||
|
@smartdata.unI()
|
||||||
|
public id: string = smartunique.shortId();
|
||||||
|
|
||||||
|
@smartdata.svDb()
|
||||||
|
public color: string;
|
||||||
|
|
||||||
|
@smartdata.svDb()
|
||||||
|
public brand: string;
|
||||||
|
|
||||||
|
constructor(colorArg: string, brandArg: string) {
|
||||||
|
super();
|
||||||
|
this.color = colorArg;
|
||||||
|
this.brand = brandArg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tap.test('should store a new Truck', async () => {
|
||||||
|
const truck = new Truck('blue', 'MAN');
|
||||||
|
await truck.save();
|
||||||
|
const myTruck2 = await Truck.getInstance({ color: 'blue' });
|
||||||
|
expect(myTruck2.color).toEqual('blue');
|
||||||
|
myTruck2.color = 'red';
|
||||||
|
await myTruck2.save();
|
||||||
|
const myTruck3 = await Truck.getInstance({ color: 'blue' });
|
||||||
|
expect(myTruck3).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should return a count', async () => {
|
||||||
|
const truckCount = await Truck.getCount();
|
||||||
|
expect(truckCount).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should use a cursor', async () => {
|
||||||
|
const cursor = await Car.getCursor({});
|
||||||
|
let counter = 0;
|
||||||
|
await cursor.forEach(async (carArg) => {
|
||||||
|
counter++;
|
||||||
|
counter % 50 === 0 ? console.log(`50 more of ${carArg.color}`) : null;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// =======================================
|
||||||
|
// close the database connection
|
||||||
|
// =======================================
|
||||||
|
tap.test('close', async () => {
|
||||||
|
if (smartmongoInstance) {
|
||||||
|
await smartmongoInstance.stopAndDumpToDir('./.nogit/dbdump/test.ts');
|
||||||
|
} else {
|
||||||
|
await testDb.mongoDb.dropDatabase();
|
||||||
|
await testDb.close();
|
||||||
|
}
|
||||||
|
setTimeout(() => process.exit(), 2000);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.start({ throwOnError: true });
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
// TODO: Decorator support during testing for bun and deno in @git.zone/tstest
|
||||||
|
|
||||||
import { tap, expect } from '@push.rocks/tapbundle';
|
import { tap, expect } from '@push.rocks/tapbundle';
|
||||||
import { Qenv } from '@push.rocks/qenv';
|
import { Qenv } from '@push.rocks/qenv';
|
||||||
import * as smartmongo from '@push.rocks/smartmongo';
|
import * as smartmongo from '@push.rocks/smartmongo';
|
||||||
@@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@push.rocks/smartdata',
|
name: '@push.rocks/smartdata',
|
||||||
version: '5.16.4',
|
version: '5.16.7',
|
||||||
description: 'An advanced library for NoSQL data organization and manipulation using TypeScript with support for MongoDB, data validation, collections, and custom data types.'
|
description: 'An advanced library for NoSQL data organization and manipulation using TypeScript with support for MongoDB, data validation, collections, and custom data types.'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,10 @@ const collectionFactory = new CollectionFactory();
|
|||||||
*/
|
*/
|
||||||
export function Collection(dbArg: SmartdataDb | TDelayed<SmartdataDb>) {
|
export function Collection(dbArg: SmartdataDb | TDelayed<SmartdataDb>) {
|
||||||
return function classDecorator<T extends { new (...args: any[]): {} }>(constructor: T) {
|
return function classDecorator<T extends { new (...args: any[]): {} }>(constructor: T) {
|
||||||
const decoratedClass = class extends constructor {
|
// Capture original constructor for _svDbOptions forwarding
|
||||||
public static className = constructor.name;
|
const originalConstructor = constructor as any;
|
||||||
public static get collection() {
|
|
||||||
|
const getCollection = () => {
|
||||||
if (!(dbArg instanceof SmartdataDb)) {
|
if (!(dbArg instanceof SmartdataDb)) {
|
||||||
dbArg = dbArg();
|
dbArg = dbArg();
|
||||||
}
|
}
|
||||||
@@ -39,18 +40,37 @@ export function Collection(dbArg: SmartdataDb | TDelayed<SmartdataDb>) {
|
|||||||
(coll as any).docCtor = decoratedClass;
|
(coll as any).docCtor = decoratedClass;
|
||||||
}
|
}
|
||||||
return coll;
|
return coll;
|
||||||
|
};
|
||||||
|
|
||||||
|
const decoratedClass = class extends constructor {
|
||||||
|
public static className = constructor.name;
|
||||||
|
public static get collection() {
|
||||||
|
return getCollection();
|
||||||
}
|
}
|
||||||
public get collection() {
|
public get collection() {
|
||||||
if (!(dbArg instanceof SmartdataDb)) {
|
return getCollection();
|
||||||
dbArg = dbArg();
|
|
||||||
}
|
|
||||||
const coll = collectionFactory.getCollection(constructor.name, dbArg);
|
|
||||||
if (!(coll as any).docCtor) {
|
|
||||||
(coll as any).docCtor = decoratedClass;
|
|
||||||
}
|
|
||||||
return coll;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Ensure instance getter works in Deno by defining it on the prototype
|
||||||
|
Object.defineProperty(decoratedClass.prototype, 'collection', {
|
||||||
|
get: getCollection,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// Deno compatibility note: Property decorators set properties on the prototype.
|
||||||
|
// Since we removed instance property declarations from SmartDataDbDoc,
|
||||||
|
// the decorator-set prototype properties are now accessible without shadowing.
|
||||||
|
// No manual forwarding needed - natural prototype inheritance works!
|
||||||
|
|
||||||
|
// Point to original constructor's _svDbOptions
|
||||||
|
Object.defineProperty(decoratedClass, '_svDbOptions', {
|
||||||
|
get() { return originalConstructor._svDbOptions; },
|
||||||
|
set(value) { originalConstructor._svDbOptions = value; },
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
|
||||||
return decoratedClass;
|
return decoratedClass;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,6 +339,13 @@ export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends
|
|||||||
public static manager;
|
public static manager;
|
||||||
public manager: TManager;
|
public manager: TManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to get collection with fallback to static for Deno compatibility
|
||||||
|
*/
|
||||||
|
private getCollectionSafe(): SmartdataCollection<any> {
|
||||||
|
return this.collection || (this.constructor as any).collection;
|
||||||
|
}
|
||||||
|
|
||||||
// STATIC
|
// STATIC
|
||||||
public static createInstanceFromMongoDbNativeDoc<T>(
|
public static createInstanceFromMongoDbNativeDoc<T>(
|
||||||
this: plugins.tsclass.typeFest.Class<T>,
|
this: plugins.tsclass.typeFest.Class<T>,
|
||||||
@@ -698,23 +705,28 @@ export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* an array of saveable properties of ALL doc
|
* an array of saveable properties of ALL doc
|
||||||
|
* Note: Set by decorators on prototype - NOT declared as instance property to avoid shadowing in Deno
|
||||||
|
* Declared with definite assignment assertion to satisfy TypeScript without creating instance property
|
||||||
*/
|
*/
|
||||||
public globalSaveableProperties: string[];
|
declare globalSaveableProperties: string[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* unique indexes
|
* unique indexes
|
||||||
|
* Note: Set by decorators on prototype - NOT declared as instance property to avoid shadowing in Deno
|
||||||
*/
|
*/
|
||||||
public uniqueIndexes: string[];
|
declare uniqueIndexes: string[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* regular indexes with their options
|
* regular indexes with their options
|
||||||
|
* Note: Set by decorators on prototype - NOT declared as instance property to avoid shadowing in Deno
|
||||||
*/
|
*/
|
||||||
public regularIndexes: Array<{field: string, options: IIndexOptions}> = [];
|
declare regularIndexes: Array<{field: string, options: IIndexOptions}>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* an array of saveable properties of a specific doc
|
* an array of saveable properties of a specific doc
|
||||||
|
* Note: Set by decorators on prototype - NOT declared as instance property to avoid shadowing in Deno
|
||||||
*/
|
*/
|
||||||
public saveableProperties: string[];
|
declare saveableProperties: string[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* name
|
* name
|
||||||
@@ -747,10 +759,10 @@ export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends
|
|||||||
// perform insert or update
|
// perform insert or update
|
||||||
switch (this.creationStatus) {
|
switch (this.creationStatus) {
|
||||||
case 'db':
|
case 'db':
|
||||||
dbResult = await this.collection.update(self, { session: opts?.session });
|
dbResult = await this.getCollectionSafe().update(self, { session: opts?.session });
|
||||||
break;
|
break;
|
||||||
case 'new':
|
case 'new':
|
||||||
dbResult = await this.collection.insert(self, { session: opts?.session });
|
dbResult = await this.getCollectionSafe().insert(self, { session: opts?.session });
|
||||||
this.creationStatus = 'db';
|
this.creationStatus = 'db';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -772,7 +784,7 @@ export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends
|
|||||||
await (this as any).beforeDelete();
|
await (this as any).beforeDelete();
|
||||||
}
|
}
|
||||||
// perform deletion
|
// perform deletion
|
||||||
const result = await this.collection.delete(this, { session: opts?.session });
|
const result = await this.getCollectionSafe().delete(this, { session: opts?.session });
|
||||||
// allow hook after delete
|
// allow hook after delete
|
||||||
if (typeof (this as any).afterDelete === 'function') {
|
if (typeof (this as any).afterDelete === 'function') {
|
||||||
await (this as any).afterDelete();
|
await (this as any).afterDelete();
|
||||||
@@ -802,7 +814,7 @@ export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends
|
|||||||
* updates an object from db
|
* updates an object from db
|
||||||
*/
|
*/
|
||||||
public async updateFromDb(): Promise<boolean> {
|
public async updateFromDb(): Promise<boolean> {
|
||||||
const mongoDbNativeDoc = await this.collection.findOne(await this.createIdentifiableObject());
|
const mongoDbNativeDoc = await this.getCollectionSafe().findOne(await this.createIdentifiableObject());
|
||||||
if (!mongoDbNativeDoc) {
|
if (!mongoDbNativeDoc) {
|
||||||
return false; // Document not found in database
|
return false; // Document not found in database
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { SmartDataDbDoc } from './classes.doc.js';
|
import { SmartDataDbDoc } from './classes.doc.js';
|
||||||
import * as plugins from './plugins.js';
|
import * as plugins from './plugins.js';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'node:events';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* a wrapper for the native mongodb cursor. Exposes better
|
* a wrapper for the native mongodb cursor. Exposes better
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"experimentalDecorators": true,
|
|
||||||
"useDefineForClassFields": false,
|
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"module": "NodeNext",
|
"module": "NodeNext",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
|
|||||||
Reference in New Issue
Block a user