Compare commits

..

2 Commits

Author SHA1 Message Date
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
5 changed files with 96 additions and 32 deletions

View File

@@ -1,5 +1,13 @@
# Changelog # Changelog
## 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.11", "version": "7.0.12",
"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.12',
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

@@ -93,33 +93,62 @@ export function Collection(dbArg: SmartdataDb | TDelayed<SmartdataDb>) {
// 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 for ${constructor.name}: metadata.saveableProperties = ${metadata?.saveableProperties?.length ?? 'undefined'}`);
if (metadata) { if (metadata) {
const proto = decoratedClass.prototype; const proto = decoratedClass.prototype;
const origProto = constructor.prototype;
// Initialize globalSaveableProperties // Initialize globalSaveableProperties on BOTH prototypes
if (metadata.globalSaveableProperties && !proto.globalSaveableProperties) { if (metadata.globalSaveableProperties) {
if (!proto.globalSaveableProperties) {
proto.globalSaveableProperties = [...metadata.globalSaveableProperties]; proto.globalSaveableProperties = [...metadata.globalSaveableProperties];
} }
if (!origProto.globalSaveableProperties) {
origProto.globalSaveableProperties = [...metadata.globalSaveableProperties];
}
}
// Initialize saveableProperties // Initialize saveableProperties on BOTH prototypes (closure fix)
if (metadata.saveableProperties && !proto.saveableProperties) { if (metadata.saveableProperties) {
if (!proto.saveableProperties) {
proto.saveableProperties = [...metadata.saveableProperties]; proto.saveableProperties = [...metadata.saveableProperties];
} }
// Also set on original constructor's prototype for closure references
if (!origProto.saveableProperties) {
origProto.saveableProperties = [...metadata.saveableProperties];
logger.log('debug', `Collection decorator: set saveableProperties on original prototype (${origProto.saveableProperties.length} props)`);
}
}
// Initialize uniqueIndexes // Initialize uniqueIndexes on BOTH prototypes
if (metadata.uniqueIndexes && !proto.uniqueIndexes) { if (metadata.uniqueIndexes) {
if (!proto.uniqueIndexes) {
proto.uniqueIndexes = [...metadata.uniqueIndexes]; proto.uniqueIndexes = [...metadata.uniqueIndexes];
} }
if (!origProto.uniqueIndexes) {
// Initialize regularIndexes origProto.uniqueIndexes = [...metadata.uniqueIndexes];
if (metadata.regularIndexes && !proto.regularIndexes) { }
proto.regularIndexes = [...metadata.regularIndexes];
} }
// Initialize searchableFields on constructor (not prototype) // Initialize regularIndexes on BOTH prototypes
if (metadata.searchableFields && !Array.isArray((decoratedClass as any).searchableFields)) { if (metadata.regularIndexes) {
if (!proto.regularIndexes) {
proto.regularIndexes = [...metadata.regularIndexes];
}
if (!origProto.regularIndexes) {
origProto.regularIndexes = [...metadata.regularIndexes];
}
}
// Initialize searchableFields on BOTH constructors
if (metadata.searchableFields) {
if (!Array.isArray((decoratedClass as any).searchableFields)) {
(decoratedClass as any).searchableFields = [...metadata.searchableFields]; (decoratedClass as any).searchableFields = [...metadata.searchableFields];
} }
if (!Array.isArray((constructor as any).searchableFields)) {
(constructor as any).searchableFields = [...metadata.searchableFields];
}
}
// Initialize _svDbOptions from metadata // Initialize _svDbOptions from metadata
if (metadata._svDbOptions && !originalConstructor._svDbOptions) { if (metadata._svDbOptions && !originalConstructor._svDbOptions) {
@@ -244,31 +273,57 @@ export function managed<TManager extends IManager>(managerArg?: TManager | TDela
const metadata = context.metadata as any; const metadata = context.metadata as any;
if (metadata) { if (metadata) {
const proto = decoratedClass.prototype; const proto = decoratedClass.prototype;
const origProto = constructor.prototype;
// Initialize globalSaveableProperties // Initialize globalSaveableProperties on BOTH prototypes
if (metadata.globalSaveableProperties && !proto.globalSaveableProperties) { if (metadata.globalSaveableProperties) {
if (!proto.globalSaveableProperties) {
proto.globalSaveableProperties = [...metadata.globalSaveableProperties]; proto.globalSaveableProperties = [...metadata.globalSaveableProperties];
} }
if (!origProto.globalSaveableProperties) {
origProto.globalSaveableProperties = [...metadata.globalSaveableProperties];
}
}
// Initialize saveableProperties // Initialize saveableProperties on BOTH prototypes (closure fix)
if (metadata.saveableProperties && !proto.saveableProperties) { if (metadata.saveableProperties) {
if (!proto.saveableProperties) {
proto.saveableProperties = [...metadata.saveableProperties]; proto.saveableProperties = [...metadata.saveableProperties];
} }
if (!origProto.saveableProperties) {
origProto.saveableProperties = [...metadata.saveableProperties];
}
}
// Initialize uniqueIndexes // Initialize uniqueIndexes on BOTH prototypes
if (metadata.uniqueIndexes && !proto.uniqueIndexes) { if (metadata.uniqueIndexes) {
if (!proto.uniqueIndexes) {
proto.uniqueIndexes = [...metadata.uniqueIndexes]; proto.uniqueIndexes = [...metadata.uniqueIndexes];
} }
if (!origProto.uniqueIndexes) {
// Initialize regularIndexes origProto.uniqueIndexes = [...metadata.uniqueIndexes];
if (metadata.regularIndexes && !proto.regularIndexes) { }
proto.regularIndexes = [...metadata.regularIndexes];
} }
// Initialize searchableFields on constructor (not prototype) // Initialize regularIndexes on BOTH prototypes
if (metadata.searchableFields && !Array.isArray((decoratedClass as any).searchableFields)) { if (metadata.regularIndexes) {
if (!proto.regularIndexes) {
proto.regularIndexes = [...metadata.regularIndexes];
}
if (!origProto.regularIndexes) {
origProto.regularIndexes = [...metadata.regularIndexes];
}
}
// Initialize searchableFields on BOTH constructors
if (metadata.searchableFields) {
if (!Array.isArray((decoratedClass as any).searchableFields)) {
(decoratedClass as any).searchableFields = [...metadata.searchableFields]; (decoratedClass as any).searchableFields = [...metadata.searchableFields];
} }
if (!Array.isArray((constructor as any).searchableFields)) {
(constructor as any).searchableFields = [...metadata.searchableFields];
}
}
// Initialize _svDbOptions from metadata // Initialize _svDbOptions from metadata
if (metadata._svDbOptions && !originalConstructor._svDbOptions) { if (metadata._svDbOptions && !originalConstructor._svDbOptions) {

View File

@@ -951,6 +951,7 @@ export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends
const globalProps = this.globalSaveableProperties || []; const globalProps = this.globalSaveableProperties || [];
const specificProps = this.saveableProperties || []; const specificProps = this.saveableProperties || [];
const saveableProperties = [...globalProps, ...specificProps]; const saveableProperties = [...globalProps, ...specificProps];
logger.log('debug', `createSavableObject: globalProps=${globalProps.length}, specificProps=${specificProps.length}, total=${saveableProperties.length}`);
// apply custom serialization if configured // apply custom serialization if configured
const optionsMap = (this.constructor as any)._svDbOptions || {}; const optionsMap = (this.constructor as any)._svDbOptions || {};
for (const propertyNameString of saveableProperties) { for (const propertyNameString of saveableProperties) {