Compare commits

..

6 Commits

Author SHA1 Message Date
49b121aa5b v7.0.13
Some checks failed
Default (tags) / security (push) Successful in 41s
Default (tags) / test (push) Failing after 49s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-28 11:40:52 +00:00
514d3dbd29 fix(classes.doc): Remove noisy debug logging from decorators and serialization logic 2025-11-28 11:40:52 +00:00
2b7316dc46 v7.0.12
Some checks failed
Default (tags) / security (push) Successful in 54s
Default (tags) / test (push) Failing after 1m16s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-28 11:27:36 +00:00
11a1345891 fix(collection): Ensure TC39 decorator metadata is initialized on both original and decorated constructors/prototypes and add debug logging 2025-11-28 11:27:36 +00:00
2fe3a72eaf 7.0.11
Some checks failed
Default (tags) / security (push) Successful in 56s
Default (tags) / test (push) Failing after 1m18s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-11-28 11:19:45 +00:00
fb7e82557b chore: Remove debug logging from Collection decorator 2025-11-28 11:19:44 +00:00
5 changed files with 82 additions and 141 deletions

View File

@@ -1,5 +1,21 @@
# Changelog # Changelog
## 2025-11-28 - 7.0.13 - fix(classes.doc)
Remove noisy debug logging from decorators and serialization logic
- Removed debug logger calls from globalSvDb decorator initialization
- Removed debug logger calls from svDb decorator initialization and svDb options handling
- Removed debug logger calls from unI and index decorator initializers
- Removed debug logging in createSavableObject to reduce console noise; no functional changes
## 2025-11-28 - 7.0.12 - fix(collection)
Ensure TC39 decorator metadata is initialized on both original and decorated constructors/prototypes and add debug logging
- Initialize metadata-driven prototype properties (globalSaveableProperties, saveableProperties, uniqueIndexes, regularIndexes) on both the decorated class prototype and the original constructor prototype to avoid closure/compatibility issues
- Initialize searchableFields on both the decorated constructor and the original constructor so text-index creation and searches see the fields correctly
- Forward and initialize _svDbOptions from decorator metadata onto the original constructor to preserve custom serialization options
- Add debug logging in the Collection decorator and in createSavableObject to surface metadata and saveable-property counts for easier troubleshooting
## 2025-11-28 - 7.0.9 - fix(classes.collection) ## 2025-11-28 - 7.0.9 - fix(classes.collection)
Fix closure bug in Collection decorator by defining collection getter on original constructor and prototype Fix closure bug in Collection decorator by defining collection getter on original constructor and prototype

View File

@@ -1,6 +1,6 @@
{ {
"name": "@push.rocks/smartdata", "name": "@push.rocks/smartdata",
"version": "7.0.10", "version": "7.0.13",
"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.",
"exports": { "exports": {

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@push.rocks/smartdata', name: '@push.rocks/smartdata',
version: '7.0.9', version: '7.0.13',
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.'
} }

View File

@@ -31,9 +31,7 @@ export function Collection(dbArg: SmartdataDb | TDelayed<SmartdataDb>) {
throw new Error('Collection can only decorate classes'); throw new Error('Collection can only decorate classes');
} }
// Capture original constructor for _svDbOptions forwarding const constructor = value as { new (...args: any[]): any } & { className?: string };
const originalConstructor = value as any;
const constructor = value as { new (...args: any[]): any };
const getCollection = () => { const getCollection = () => {
if (!(dbArg instanceof SmartdataDb)) { if (!(dbArg instanceof SmartdataDb)) {
@@ -42,97 +40,61 @@ export function Collection(dbArg: SmartdataDb | TDelayed<SmartdataDb>) {
const coll = collectionFactory.getCollection(constructor.name, dbArg); const coll = collectionFactory.getCollection(constructor.name, dbArg);
// Attach document constructor for searchableFields lookup // Attach document constructor for searchableFields lookup
if (coll && !(coll as any).docCtor) { if (coll && !(coll as any).docCtor) {
(coll as any).docCtor = decoratedClass; (coll as any).docCtor = constructor;
} }
return coll; return coll;
}; };
const decoratedClass = class extends constructor { // Add static className property directly on the constructor
public static className = constructor.name; (constructor as any).className = constructor.name;
public static get collection() {
return getCollection();
}
public get collection() {
return getCollection();
}
};
// Ensure instance getter works in Deno by defining it on the prototype // Define collection getter on constructor (static access)
Object.defineProperty(decoratedClass.prototype, 'collection', {
get: getCollection,
enumerable: false,
configurable: true
});
// Closure fix: When class methods reference the class name (e.g., `User.collection`),
// they get the original constructor via closure, not the decorated class.
// Define collection getter on the original constructor.
Object.defineProperty(constructor, 'collection', { Object.defineProperty(constructor, 'collection', {
get: getCollection, get: getCollection,
enumerable: false, enumerable: false,
configurable: true configurable: true
}); });
// Define collection getter on prototype (instance access)
Object.defineProperty(constructor.prototype, 'collection', { Object.defineProperty(constructor.prototype, 'collection', {
get: getCollection, get: getCollection,
enumerable: false, enumerable: false,
configurable: true 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
});
// Initialize prototype properties from context.metadata (TC39 decorator metadata) // Initialize prototype properties from context.metadata (TC39 decorator metadata)
// This ensures prototype properties are available before any instance is created // This ensures prototype properties are available before any instance is created
const metadata = context.metadata as any; const metadata = context.metadata as any;
logger.log('debug', `Collection decorator: metadata keys = ${metadata ? Object.keys(metadata).join(', ') : 'null'}`);
logger.log('debug', `Collection decorator: saveableProperties in metadata = ${metadata?.saveableProperties?.length ?? 0}`);
logger.log('debug', `Collection decorator: globalSaveableProperties in metadata = ${metadata?.globalSaveableProperties?.length ?? 0}`);
if (metadata) { if (metadata) {
const proto = decoratedClass.prototype; const proto = constructor.prototype;
// Initialize globalSaveableProperties
if (metadata.globalSaveableProperties && !proto.globalSaveableProperties) { if (metadata.globalSaveableProperties && !proto.globalSaveableProperties) {
proto.globalSaveableProperties = [...metadata.globalSaveableProperties]; proto.globalSaveableProperties = [...metadata.globalSaveableProperties];
logger.log('debug', `Collection decorator: initialized globalSaveableProperties with ${proto.globalSaveableProperties.length} properties`);
} }
// Initialize saveableProperties
if (metadata.saveableProperties && !proto.saveableProperties) { if (metadata.saveableProperties && !proto.saveableProperties) {
proto.saveableProperties = [...metadata.saveableProperties]; proto.saveableProperties = [...metadata.saveableProperties];
logger.log('debug', `Collection decorator: initialized saveableProperties with ${proto.saveableProperties.length} properties`);
} }
// Initialize uniqueIndexes
if (metadata.uniqueIndexes && !proto.uniqueIndexes) { if (metadata.uniqueIndexes && !proto.uniqueIndexes) {
proto.uniqueIndexes = [...metadata.uniqueIndexes]; proto.uniqueIndexes = [...metadata.uniqueIndexes];
} }
// Initialize regularIndexes
if (metadata.regularIndexes && !proto.regularIndexes) { if (metadata.regularIndexes && !proto.regularIndexes) {
proto.regularIndexes = [...metadata.regularIndexes]; proto.regularIndexes = [...metadata.regularIndexes];
} }
// Initialize searchableFields on constructor (not prototype) if (metadata.searchableFields && !Array.isArray((constructor as any).searchableFields)) {
if (metadata.searchableFields && !Array.isArray((decoratedClass as any).searchableFields)) { (constructor as any).searchableFields = [...metadata.searchableFields];
(decoratedClass as any).searchableFields = [...metadata.searchableFields];
} }
// Initialize _svDbOptions from metadata if (metadata._svDbOptions && !(constructor as any)._svDbOptions) {
if (metadata._svDbOptions && !originalConstructor._svDbOptions) { (constructor as any)._svDbOptions = { ...metadata._svDbOptions };
originalConstructor._svDbOptions = { ...metadata._svDbOptions };
} }
} }
return decoratedClass as any; // Return the ORIGINAL constructor (no class replacement)
return constructor as any;
}; };
} }
@@ -155,133 +117,108 @@ export function managed<TManager extends IManager>(managerArg?: TManager | TDela
throw new Error('managed can only decorate classes'); throw new Error('managed can only decorate classes');
} }
const constructor = value as { new (...args: any[]): any }; const constructor = value as { new (...args: any[]): any } & { className?: string };
const decoratedClass = class extends constructor { // Add static className property directly on the constructor
public static className = constructor.name; (constructor as any).className = constructor.name;
public static get collection() {
let dbArg: SmartdataDb;
if (!managerArg) {
dbArg = this.prototype.defaultManager.db;
} else if (managerArg['db']) {
dbArg = (managerArg as TManager).db;
} else {
dbArg = (managerArg as TDelayed<TManager>)().db;
}
return collectionFactory.getCollection(constructor.name, dbArg);
}
public get collection() {
let dbArg: SmartdataDb;
if (!managerArg) {
//console.log(this.defaultManager.db);
//process.exit(0)
dbArg = this.defaultManager.db;
} else if (managerArg['db']) {
dbArg = (managerArg as TManager).db;
} else {
dbArg = (managerArg as TDelayed<TManager>)().db;
}
return collectionFactory.getCollection(constructor.name, dbArg);
}
public static get manager() {
let manager: TManager;
if (!managerArg) {
manager = this.prototype.defaultManager;
} else if (managerArg['db']) {
manager = managerArg as TManager;
} else {
manager = (managerArg as TDelayed<TManager>)();
}
return manager;
}
public get manager() {
let manager: TManager;
if (!managerArg) {
manager = this.defaultManager;
} else if (managerArg['db']) {
manager = managerArg as TManager;
} else {
manager = (managerArg as TDelayed<TManager>)();
}
return manager;
}
};
// Closure fix: When class methods reference the class name (e.g., `User.collection`), // Define collection getter (static)
// they get the original constructor via closure, not the decorated class.
// Define collection/manager getters on the original constructor.
const getCollectionStatic = function(this: any) {
let dbArg: SmartdataDb;
if (!managerArg) {
dbArg = this.prototype.defaultManager.db;
} else if (managerArg['db']) {
dbArg = (managerArg as TManager).db;
} else {
dbArg = (managerArg as TDelayed<TManager>)().db;
}
return collectionFactory.getCollection(constructor.name, dbArg);
};
const getCollectionInstance = function(this: any) {
let dbArg: SmartdataDb;
if (!managerArg) {
dbArg = this.defaultManager.db;
} else if (managerArg['db']) {
dbArg = (managerArg as TManager).db;
} else {
dbArg = (managerArg as TDelayed<TManager>)().db;
}
return collectionFactory.getCollection(constructor.name, dbArg);
};
Object.defineProperty(constructor, 'collection', { Object.defineProperty(constructor, 'collection', {
get: getCollectionStatic, get: function(this: any) {
let dbArg: SmartdataDb;
if (!managerArg) {
dbArg = this.prototype.defaultManager.db;
} else if (managerArg['db']) {
dbArg = (managerArg as TManager).db;
} else {
dbArg = (managerArg as TDelayed<TManager>)().db;
}
return collectionFactory.getCollection(constructor.name, dbArg);
},
enumerable: false, enumerable: false,
configurable: true configurable: true
}); });
// Define collection getter (instance)
Object.defineProperty(constructor.prototype, 'collection', { Object.defineProperty(constructor.prototype, 'collection', {
get: getCollectionInstance, get: function(this: any) {
let dbArg: SmartdataDb;
if (!managerArg) {
dbArg = this.defaultManager.db;
} else if (managerArg['db']) {
dbArg = (managerArg as TManager).db;
} else {
dbArg = (managerArg as TDelayed<TManager>)().db;
}
return collectionFactory.getCollection(constructor.name, dbArg);
},
enumerable: false,
configurable: true
});
// Define manager getter (static)
Object.defineProperty(constructor, 'manager', {
get: function(this: any) {
if (!managerArg) {
return this.prototype.defaultManager;
} else if (managerArg['db']) {
return managerArg as TManager;
} else {
return (managerArg as TDelayed<TManager>)();
}
},
enumerable: false,
configurable: true
});
// Define manager getter (instance)
Object.defineProperty(constructor.prototype, 'manager', {
get: function(this: any) {
if (!managerArg) {
return this.defaultManager;
} else if (managerArg['db']) {
return managerArg as TManager;
} else {
return (managerArg as TDelayed<TManager>)();
}
},
enumerable: false, enumerable: false,
configurable: true configurable: true
}); });
// Initialize prototype properties from context.metadata (TC39 decorator metadata) // Initialize prototype properties from context.metadata (TC39 decorator metadata)
// This ensures prototype properties are available before any instance is created // This ensures prototype properties are available before any instance is created
const originalConstructor = value as any;
const metadata = context.metadata as any; const metadata = context.metadata as any;
if (metadata) { if (metadata) {
const proto = decoratedClass.prototype; const proto = constructor.prototype;
// Initialize globalSaveableProperties
if (metadata.globalSaveableProperties && !proto.globalSaveableProperties) { if (metadata.globalSaveableProperties && !proto.globalSaveableProperties) {
proto.globalSaveableProperties = [...metadata.globalSaveableProperties]; proto.globalSaveableProperties = [...metadata.globalSaveableProperties];
} }
// Initialize saveableProperties
if (metadata.saveableProperties && !proto.saveableProperties) { if (metadata.saveableProperties && !proto.saveableProperties) {
proto.saveableProperties = [...metadata.saveableProperties]; proto.saveableProperties = [...metadata.saveableProperties];
} }
// Initialize uniqueIndexes
if (metadata.uniqueIndexes && !proto.uniqueIndexes) { if (metadata.uniqueIndexes && !proto.uniqueIndexes) {
proto.uniqueIndexes = [...metadata.uniqueIndexes]; proto.uniqueIndexes = [...metadata.uniqueIndexes];
} }
// Initialize regularIndexes
if (metadata.regularIndexes && !proto.regularIndexes) { if (metadata.regularIndexes && !proto.regularIndexes) {
proto.regularIndexes = [...metadata.regularIndexes]; proto.regularIndexes = [...metadata.regularIndexes];
} }
// Initialize searchableFields on constructor (not prototype) if (metadata.searchableFields && !Array.isArray((constructor as any).searchableFields)) {
if (metadata.searchableFields && !Array.isArray((decoratedClass as any).searchableFields)) { (constructor as any).searchableFields = [...metadata.searchableFields];
(decoratedClass as any).searchableFields = [...metadata.searchableFields];
} }
// Initialize _svDbOptions from metadata if (metadata._svDbOptions && !(constructor as any)._svDbOptions) {
if (metadata._svDbOptions && !originalConstructor._svDbOptions) { (constructor as any)._svDbOptions = { ...metadata._svDbOptions };
originalConstructor._svDbOptions = { ...metadata._svDbOptions };
} }
} }
return decoratedClass as any; // Return the ORIGINAL constructor (no class replacement)
return constructor as any;
}; };
} }

View File

@@ -51,8 +51,6 @@ export function globalSvDb() {
} }
metadata.globalSaveableProperties.push(String(context.name)); metadata.globalSaveableProperties.push(String(context.name));
logger.log('debug', `called globalSvDb() on metadata for property ${String(context.name)}`);
// Use addInitializer to ensure prototype arrays are set up once // Use addInitializer to ensure prototype arrays are set up once
context.addInitializer(function(this: any) { context.addInitializer(function(this: any) {
const proto = this.constructor.prototype; const proto = this.constructor.prototype;
@@ -61,7 +59,6 @@ export function globalSvDb() {
if (metadata && metadata.globalSaveableProperties && !proto.globalSaveableProperties) { if (metadata && metadata.globalSaveableProperties && !proto.globalSaveableProperties) {
// Initialize prototype array from metadata (runs once per class) // Initialize prototype array from metadata (runs once per class)
proto.globalSaveableProperties = [...metadata.globalSaveableProperties]; proto.globalSaveableProperties = [...metadata.globalSaveableProperties];
logger.log('debug', `initialized globalSaveableProperties with ${proto.globalSaveableProperties.length} properties`);
} }
}); });
}; };
@@ -103,8 +100,6 @@ export function svDb(options?: SvDbOptions) {
metadata._svDbOptions[propName] = options; metadata._svDbOptions[propName] = options;
} }
logger.log('debug', `called svDb() on metadata for property ${propName}`);
// Use addInitializer to ensure prototype arrays are set up once // Use addInitializer to ensure prototype arrays are set up once
context.addInitializer(function(this: any) { context.addInitializer(function(this: any) {
const proto = this.constructor.prototype; const proto = this.constructor.prototype;
@@ -114,7 +109,6 @@ export function svDb(options?: SvDbOptions) {
if (metadata && metadata.saveableProperties && !proto.saveableProperties) { if (metadata && metadata.saveableProperties && !proto.saveableProperties) {
// Initialize prototype array from metadata (runs once per class) // Initialize prototype array from metadata (runs once per class)
proto.saveableProperties = [...metadata.saveableProperties]; proto.saveableProperties = [...metadata.saveableProperties];
logger.log('debug', `initialized saveableProperties with ${proto.saveableProperties.length} properties`);
} }
// Initialize svDbOptions from metadata // Initialize svDbOptions from metadata
@@ -187,8 +181,6 @@ export function unI() {
metadata.saveableProperties.push(propName); metadata.saveableProperties.push(propName);
} }
logger.log('debug', `called unI on metadata for property ${propName}`);
// Use addInitializer to ensure prototype arrays are set up once // Use addInitializer to ensure prototype arrays are set up once
context.addInitializer(function(this: any) { context.addInitializer(function(this: any) {
const proto = this.constructor.prototype; const proto = this.constructor.prototype;
@@ -196,7 +188,6 @@ export function unI() {
if (metadata && metadata.uniqueIndexes && !proto.uniqueIndexes) { if (metadata && metadata.uniqueIndexes && !proto.uniqueIndexes) {
proto.uniqueIndexes = [...metadata.uniqueIndexes]; proto.uniqueIndexes = [...metadata.uniqueIndexes];
logger.log('debug', `initialized uniqueIndexes with ${proto.uniqueIndexes.length} properties`);
} }
if (metadata && metadata.saveableProperties && !proto.saveableProperties) { if (metadata && metadata.saveableProperties && !proto.saveableProperties) {
@@ -246,8 +237,6 @@ export function index(options?: IIndexOptions) {
metadata.saveableProperties.push(propName); metadata.saveableProperties.push(propName);
} }
logger.log('debug', `called index() on metadata for property ${propName}`);
// Use addInitializer to ensure prototype arrays are set up once // Use addInitializer to ensure prototype arrays are set up once
context.addInitializer(function(this: any) { context.addInitializer(function(this: any) {
const proto = this.constructor.prototype; const proto = this.constructor.prototype;
@@ -255,7 +244,6 @@ export function index(options?: IIndexOptions) {
if (metadata && metadata.regularIndexes && !proto.regularIndexes) { if (metadata && metadata.regularIndexes && !proto.regularIndexes) {
proto.regularIndexes = [...metadata.regularIndexes]; proto.regularIndexes = [...metadata.regularIndexes];
logger.log('debug', `initialized regularIndexes with ${proto.regularIndexes.length} indexes`);
} }
if (metadata && metadata.saveableProperties && !proto.saveableProperties) { if (metadata && metadata.saveableProperties && !proto.saveableProperties) {