fix(build): update build and test tooling configuration, migrate project config to .smartconfig.json, and align TypeScript typings

This commit is contained in:
2026-03-24 19:44:49 +00:00
parent 27c1500db5
commit 03431535d7
29 changed files with 2210 additions and 1669 deletions

View File

@@ -7,7 +7,7 @@ on:
env:
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@code.foss.global/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}

View File

@@ -7,7 +7,7 @@ on:
env:
IMAGE: code.foss.global/host.today/ht-docker-node:npmci
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@/${{gitea.repository}}.git
NPMCI_COMPUTED_REPOURL: https://${{gitea.repository_owner}}:${{secrets.GITEA_TOKEN}}@code.foss.global/${{gitea.repository}}.git
NPMCI_TOKEN_NPM: ${{secrets.NPMCI_TOKEN_NPM}}
NPMCI_TOKEN_NPM2: ${{secrets.NPMCI_TOKEN_NPM2}}
NPMCI_GIT_GITHUBTOKEN: ${{secrets.NPMCI_GIT_GITHUBTOKEN}}

10
.gitignore vendored
View File

@@ -16,4 +16,12 @@ node_modules/
dist/
dist_*/
#------# custom
# rust
rust/target/
dist_rust/
# AI
.claude/
.serena/
#------# custom

View File

@@ -1,7 +1,7 @@
{
"json.schemas": [
{
"fileMatch": ["/npmextra.json"],
"fileMatch": ["/.smartconfig.json"],
"schema": {
"type": "object",
"properties": {
@@ -22,6 +22,5 @@
}
}
}
],
"deno.enable": false
]
}

View File

@@ -1,6 +1,16 @@
# Changelog
## 2026-03-24 - 7.1.1 - fix(build)
update build and test tooling configuration, migrate project config to .smartconfig.json, and align TypeScript typings
- Switch the build script to tsbuild tsfolders and upgrade core build/test dependencies including @git.zone/tsbuild, @git.zone/tstest, and @git.zone/tsrun.
- Replace npmextra.json with .smartconfig.json and update package packaging to include the new config file.
- Update test files to import tapbundle from @git.zone/tstest/tapbundle and remove the standalone @push.rocks/tapbundle dependency.
- Adjust TypeScript configuration and source typings for stricter compatibility, including node types and definite assignment/nullability fixes.
- Fix Gitea workflow repository URLs for code.foss.global and expand .gitignore for generated Rust and local tooling directories.
## 2026-02-26 - 7.1.0 - feat(config)
normalize npmextra.json to namespaced keys and add CI/release configuration
- Replaced legacy keys (npmdocker, npmci, gitzone, tsdoc) with namespaced package keys (@git.zone/cli, @git.zone/tsdoc, @git.zone/tsdocker, @ship.zone/szci).
@@ -10,6 +20,7 @@ normalize npmextra.json to namespaced keys and add CI/release configuration
- Removed old top-level entries to consolidate tooling configuration under scoped keys.
## 2026-02-26 - 7.0.16 - fix(mongodb)
set default socketTimeoutMS to 30000ms in MongoClient options to prevent hung operations from holding connections
- Adds socketTimeoutMS: 30000 to MongoClient clientOptions in ts/classes.db.ts
@@ -17,20 +28,23 @@ set default socketTimeoutMS to 30000ms in MongoClient options to prevent hung op
- Non-breaking change (defaults only)
## 2025-12-01 - 7.0.15 - fix(classes.doc)
Avoid emitting instance fields for collection and manager to preserve decorator-defined prototype getters
- ts/classes.doc.ts: changed instance properties `collection` and `manager` to `declare` so TypeScript does not emit them as own properties — prevents ES2022 class fields from shadowing prototype getters created by @Collection and @managed decorators.
- readme.hints.md: added documentation explaining the ES2022 class fields issue and recommending use of `declare` for type-only instance properties; marks the fix as v7.0.15.
## 2025-11-28 - 7.0.14 - fix(classes.collection)
Centralize TC39 decorator metadata initialization and use context.metadata in class decorators
- Add initializeDecoratorMetadata helper to initialize prototype and constructor properties from TC39 decorator metadata
- Refactor Collection and managed decorators to call initializeDecoratorMetadata with context.metadata
- Remove direct reliance on constructor[Symbol.metadata] in class decorators to avoid read-only assignment issues
- Ensure consistent initialization of saveableProperties, globalSaveableProperties, uniqueIndexes, regularIndexes, searchableFields and _svDbOptions
- Ensure consistent initialization of saveableProperties, globalSaveableProperties, uniqueIndexes, regularIndexes, searchableFields and \_svDbOptions
## 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
@@ -39,20 +53,23 @@ Remove noisy debug logging from decorators and serialization logic
- 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
- 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)
Fix closure bug in Collection decorator by defining collection getter on original constructor and prototype
- Define the collection getter on the original constructor so class-level references (e.g. `User.collection`) resolve to the decorated collection instead of the original constructor's closure value.
- Also define the getter on the original constructor's prototype to ensure instance access works consistently across runtimes (Deno/Node).
## 2025-11-28 - 7.0.8 - fix(classes.collection)
Fix closure issue in managed decorator so Class.collection/instance.collection resolve correctly
- Resolve closure bug in the managed() decorator where class methods referencing Class.collection (or instance.collection) could receive the original constructor's captured value and thus the wrong collection/manager.
@@ -60,6 +77,7 @@ Fix closure issue in managed decorator so Class.collection/instance.collection r
- Getters are defined as non-enumerable and configurable to preserve compatibility with existing consumers.
## 2025-11-28 - 7.0.7 - fix(decorators)
Fix decorator metadata initialization and Lucene query transformation
- Ensure TC39 decorator metadata is used to initialize prototype properties so decorators work reliably across runtimes (context.metadata / Symbol.metadata shim imported early).
@@ -68,12 +86,14 @@ Fix decorator metadata initialization and Lucene query transformation
- Improve collection initialization to auto-create compound text indexes from searchableFields and ensure index creation is idempotent.
## 2025-11-28 - 7.0.6 - fix(classes.collection)
Guard against missing collection before attaching document constructor in Collection decorator
- Added a truthy check for `coll` before setting `(coll as any).docCtor` in the Collection decorator (ts/classes.collection.ts).
- Prevents a potential TypeError when `collectionFactory.getCollection` returns null/undefined during decorator initialization.
## 2025-11-28 - 7.0.5 - fix(package)
Add package exports entry and remove legacy main/typings fields
- Added an "exports" entry in package.json mapping "." to ./dist_ts/index.js to declare the package's ESM entrypoint.
@@ -81,6 +101,7 @@ Add package exports entry and remove legacy main/typings fields
- Improves Node/module resolution and modern bundler compatibility by using the package exports field.
## 2025-11-28 - 7.0.4 - fix(decorators)
Add Symbol.metadata polyfill and import it at entry to ensure decorator metadata is available
- Add ts/shim.ts: defines Symbol.metadata when missing (polyfill for TC39 Stage 3 decorator metadata).
@@ -88,11 +109,13 @@ Add Symbol.metadata polyfill and import it at entry to ensure decorator metadata
- Prevents runtime errors when decorators rely on Symbol.metadata and improves compatibility across runtimes/environments.
## 2025-11-28 - 7.0.3 - fix(build)
Bump devDependency @git.zone/tsbuild to ^3.1.2
- Updated @git.zone/tsbuild in devDependencies from ^3.1.1 to ^3.1.2
## 2025-11-28 - 7.0.2 - fix(collectionfactory)
Simplify CollectionFactory.getCollection: remove unnecessary IIFE and instantiate collection only when dbArg is SmartdataDb
- Remove redundant IIFE wrapper in getCollection for improved readability
@@ -100,18 +123,21 @@ Simplify CollectionFactory.getCollection: remove unnecessary IIFE and instantiat
- Avoid assigning undefined to the collections map by guarding instantiation and returning existing collection
## 2025-11-27 - 7.0.1 - fix(build)
Update build tooling and TypeScript compilation target
- Bump devDependency @git.zone/tsbuild from ^3.1.0 to ^3.1.1.
- Update tsconfig.json compiler target from ES2022 to ES2024 (affects emitted JS language level).
## 2025-11-27 - 7.0.0 - BREAKING CHANGE(mongodb)
Upgrade dependencies: bump mongodb to ^7.0.0 and @git.zone/tstest to ^3.1.3
- Bump 'mongodb' dependency from ^6.20.0 to ^7.0.0 — major version upgrade; may introduce breaking API changes and require code updates or verification against the new driver.
- Update devDependency '@git.zone/tstest' from ^2.8.1 to ^3.1.3 — test tooling updated.
## 2025-11-17 - 6.0.0 - BREAKING CHANGE(decorators)
Migrate to TC39 Stage 3 decorators and refactor decorator metadata handling; update class initialization, lucene adapter fixes and docs
- Switch all decorators to TC39 Stage 3 signatures and metadata usage (use context.metadata and context.addInitializer) — affects svDb, globalSvDb, searchable, unI, index, Collection and managed.
@@ -123,24 +149,27 @@ Migrate to TC39 Stage 3 decorators and refactor decorator metadata handling; upd
- Clean up project memory/config files related to the previous decorator approach and Deno configuration adjustments.
## 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.
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.
- 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
@@ -149,6 +178,7 @@ Update dependencies, tooling and watcher import; add .serena cache ignore
- Add .serena/.gitignore to ignore /cache
## 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
- Fix package.json test script: remove stray dot in tstest --verbose argument to ensure tests run correctly
@@ -159,6 +189,7 @@ Improve filter conversion: handle logical operators, merge operator objects, add
- Expand README filtering section with detailed examples for basic filtering, deep nested filters, comparison operators, array operations, logical and element operators, and advanced patterns
## 2025-08-18 - 5.16.3 - fix(docs)
Add local Claude settings and remove outdated codex.md
- Added .claude/settings.local.json to store local Claude/assistant permissions and configuration.
@@ -166,6 +197,7 @@ Add local Claude settings and remove outdated codex.md
- No runtime/library code changes; documentation/configuration-only update, bump patch version.
## 2025-08-18 - 5.16.2 - fix(readme)
Update README: clarify examples, expand search/cursor/docs and add local Claude settings
- Refined README wording and structure: clearer Quick Start, improved examples and developer-focused phrasing
@@ -174,6 +206,7 @@ Update README: clarify examples, expand search/cursor/docs and add local Claude
- Added .claude/settings.local.json to provide local assistant/CI permission configuration
## 2025-08-12 - 5.16.1 - fix(core)
Improve error handling and logging; enhance search query sanitization; update dependency versions and documentation
- Replaced console.log and console.warn with structured logger.log calls throughout the core modules
@@ -184,6 +217,7 @@ Improve error handling and logging; enhance search query sanitization; update de
- Updated README with improved instructions, feature highlights, and quick start sections
## 2025-04-25 - 5.16.0 - feat(watcher)
Enhance change stream watchers with buffering and EventEmitter support; update dependency versions
- Bumped smartmongo from ^2.0.11 to ^2.0.12 and smartrx from ^3.0.7 to ^3.0.10
@@ -192,6 +226,7 @@ Enhance change stream watchers with buffering and EventEmitter support; update d
- Modified SmartdataDbWatcher to extend EventEmitter and support event notifications
## 2025-04-24 - 5.15.1 - fix(cursor)
Improve cursor usage documentation and refactor getCursor API to support native cursor modifiers
- Updated examples in readme.md to demonstrate manual iteration using cursor.next() and proper cursor closing.
@@ -199,6 +234,7 @@ Improve cursor usage documentation and refactor getCursor API to support native
- Added new tests in test/test.cursor.ts to verify cursor operations, including limits, sorting, and skipping.
## 2025-04-24 - 5.15.0 - feat(svDb)
Enhance svDb decorator to support custom serialization and deserialization options
- Added an optional options parameter to the svDb decorator to accept serialize/deserialize functions
@@ -206,6 +242,7 @@ Enhance svDb decorator to support custom serialization and deserialization optio
- Updated createSavableObject to use custom serialization when available
## 2025-04-23 - 5.14.1 - fix(db operations)
Update transaction API to consistently pass optional session parameters across database operations
- Revised transaction support in readme to use startSession without await and showcased session usage in getInstance and save calls
@@ -214,14 +251,16 @@ Update transaction API to consistently pass optional session parameters across d
- Improved overall consistency of transactional APIs across the library
## 2025-04-23 - 5.14.0 - feat(doc)
Implement support for beforeSave, afterSave, beforeDelete, and afterDelete lifecycle hooks in document save and delete operations to allow custom logic execution during these critical moments.
- Calls beforeSave hook if defined before performing insert or update.
- Calls afterSave hook after a document is saved.
- Calls beforeDelete hook before deletion and afterDelete hook afterward.
- Ensures _updatedAt timestamp is refreshed during save operations.
- Ensures \_updatedAt timestamp is refreshed during save operations.
## 2025-04-22 - 5.13.1 - fix(search)
Improve search query parsing for implicit AND queries by preserving quoted substrings and better handling free terms, quoted phrases, and field:value tokens.
- Replace previous implicit AND logic with tokenization that preserves quoted substrings
@@ -229,6 +268,7 @@ Improve search query parsing for implicit AND queries by preserving quoted subst
- Ensure errors are thrown for non-searchable fields in field-specific queries
## 2025-04-22 - 5.13.0 - feat(search)
Improve search query handling and update documentation
- Added 'codex.md' providing a high-level project overview and detailed search API documentation.
@@ -237,12 +277,14 @@ Improve search query handling and update documentation
- Updated tests in test/test.search.ts to cover new combined query scenarios and ensure robust behavior.
## 2025-04-22 - 5.12.2 - fix(search)
Fix handling of quoted wildcard patterns in field-specific search queries and add tests for location-based wildcard phrase searches
- Strip surrounding quotes from wildcard patterns in field queries to correctly transform them to regex
- Introduce new tests in test/test.search.ts to validate exact quoted and unquoted wildcard searches on a location field
## 2025-04-22 - 5.12.1 - fix(search)
Improve implicit AND logic for mixed free term and field queries in search and enhance wildcard field handling.
- Updated regex for field:value parsing to capture full value with wildcards.
@@ -251,6 +293,7 @@ Improve implicit AND logic for mixed free term and field queries in search and e
- Extended tests to cover combined free term and wildcard field searches, including error cases.
## 2025-04-22 - 5.12.0 - feat(doc/search)
Enhance search functionality with filter and validate options for advanced query control
- Added 'filter' option to merge additional MongoDB query constraints in search
@@ -259,6 +302,7 @@ Enhance search functionality with filter and validate options for advanced query
- Updated tests to cover new search scenarios and fallback mechanisms
## 2025-04-22 - 5.11.4 - fix(search)
Implement implicit AND logic for mixed simple term and field:value queries in search
- Added a new branch to detect and handle search queries that mix field:value pairs with plain terms without explicit operators
@@ -266,6 +310,7 @@ Implement implicit AND logic for mixed simple term and field:value queries in se
- Ensures proper parsing and improved robustness of search filters
## 2025-04-22 - 5.11.3 - fix(lucene adapter and search tests)
Improve range query parsing in Lucene adapter and expand search test coverage
- Added a new 'testSearch' script in package.json to run search tests.
@@ -274,12 +319,14 @@ Improve range query parsing in Lucene adapter and expand search test coverage
- Fixed token validation in the parseRange method of the Lucene adapter to ensure proper error handling.
## 2025-04-21 - 5.11.2 - fix(readme)
Update readme to clarify usage of searchable fields retrieval
- Replaced getSearchableFields('Product') with Product.getSearchableFields()
- Updated documentation to reference the static method Class.getSearchableFields()
## 2025-04-21 - 5.11.1 - fix(doc)
Refactor searchable fields API and improve collection registration.
- Removed the standalone getSearchableFields utility in favor of a static method on document classes.
@@ -288,11 +335,13 @@ Refactor searchable fields API and improve collection registration.
- Added try/catch in test cleanup to gracefully handle dropDatabase errors.
## 2025-04-21 - 5.11.0 - feat(ts/classes.lucene.adapter)
Expose luceneWildcardToRegex method to allow external usage and enhance regex transformation capabilities.
- Changed luceneWildcardToRegex from private to public in ts/classes.lucene.adapter.ts.
## 2025-04-21 - 5.10.0 - feat(search)
Improve search functionality: update documentation, refine Lucene query transformation, and add advanced search tests
- Updated readme.md with detailed Lucenestyle search examples and use cases
@@ -301,6 +350,7 @@ Improve search functionality: update documentation, refine Lucene query transfor
- Added new advanced search tests covering boolean operators, grouping, quoted phrases, and wildcard queries
## 2025-04-18 - 5.9.2 - fix(documentation)
Update search API documentation to replace deprecated searchWithLucene examples with the unified search(query) API and clarify its behavior.
- Replaced 'searchWithLucene' examples with 'search(query)' in the README.
@@ -308,24 +358,28 @@ Update search API documentation to replace deprecated searchWithLucene examples
- Clarified guidelines for creating MongoDB text indexes on searchable fields for optimized search performance.
## 2025-04-18 - 5.9.1 - fix(search)
Refactor search tests to use unified search API and update text index type casting
- Replaced all calls from searchWithLucene with search in test/search tests
- Updated text index specification in the collection class to use proper type casting
## 2025-04-18 - 5.9.0 - feat(collections/search)
Improve text index creation and search fallback mechanisms in collections and document search methods
- Auto-create a compound text index on all searchable fields in SmartdataCollection with a one-time flag to prevent duplicate index creation.
- Refine the search method in SmartDataDbDoc to support exact field matches and safe regex fallback for non-Lucene queries.
## 2025-04-17 - 5.8.4 - fix(core)
Update commit metadata with no functional code changes
- Commit info and documentation refreshed
- No code or test changes detected in the diff
## 2025-04-17 - 5.8.3 - fix(readme)
Improve readme documentation on data models and connection management
- Clarify that data models use @Collection, @unI, @svDb, @index, and @searchable decorators
@@ -334,12 +388,14 @@ Improve readme documentation on data models and connection management
- Revise license section to reference the MIT License without including additional legal details
## 2025-04-14 - 5.8.2 - fix(classes.doc.ts)
Ensure collection initialization before creating a cursor in getCursorExtended
- Added 'await collection.init()' to guarantee that the MongoDB collection is initialized before using the cursor
- Prevents potential runtime errors when accessing collection.mongoDbCollection
## 2025-04-14 - 5.8.1 - fix(cursor, doc)
Add explicit return types and casts to SmartdataDbCursor methods and update getCursorExtended signature in SmartDataDbDoc.
- Specify Promise<T> as return type for next() in SmartdataDbCursor and cast return value to T.
@@ -347,12 +403,14 @@ Add explicit return types and casts to SmartdataDbCursor methods and update getC
- Update getCursorExtended to return Promise<SmartdataDbCursor<T>> for clearer type safety.
## 2025-04-14 - 5.8.0 - feat(cursor)
Add toArray method to SmartdataDbCursor to convert raw MongoDB documents into initialized class instances
- Introduced asynchronous toArray method in SmartdataDbCursor which retrieves all documents from the MongoDB cursor
- Maps each native document to a SmartDataDbDoc instance using createInstanceFromMongoDbNativeDoc for consistent API usage
## 2025-04-14 - 5.7.0 - feat(SmartDataDbDoc)
Add extended cursor method getCursorExtended for flexible cursor modifications
- Introduces getCursorExtended in classes.doc.ts to allow modifier functions for MongoDB cursors
@@ -360,6 +418,7 @@ Add extended cursor method getCursorExtended for flexible cursor modifications
- Enhances querying capabilities by enabling customized cursor transformations
## 2025-04-07 - 5.6.0 - feat(indexing)
Add support for regular index creation in documents and collections
- Implement new index decorator in classes.doc.ts to mark properties with regular indexing options
@@ -367,6 +426,7 @@ Add support for regular index creation in documents and collections
- Enhance document structure to store and utilize regular index configurations
## 2025-04-06 - 5.5.1 - fix(ci & formatting)
Minor fixes: update CI workflow image and npmci package references, adjust package.json and readme URLs, and apply consistent code formatting.
- Update image and repo URL in Gitea workflows from GitLab to code.foss.global
@@ -376,6 +436,7 @@ Minor fixes: update CI workflow image and npmci package references, adjust packa
- Minor update to .gitignore custom section label
## 2025-04-06 - 5.5.0 - feat(search)
Enhance search functionality with robust Lucene query transformation and reliable fallback mechanisms
- Improve Lucene adapter to properly structure $or queries for term, phrase, wildcard, and fuzzy search
@@ -383,15 +444,17 @@ Enhance search functionality with robust Lucene query transformation and reliabl
- Update readme and tests with extensive examples for @searchable fields and Lucene-based queries
## 2025-04-06 - 5.4.0 - feat(core)
Refactor file structure and update dependency versions
- Renamed files and modules from 'smartdata.classes.*' to 'classes.*' and adjusted corresponding import paths.
- Renamed files and modules from 'smartdata.classes._' to 'classes._' and adjusted corresponding import paths.
- Updated dependency versions: '@push.rocks/smartmongo' to ^2.0.11, '@tsclass/tsclass' to ^8.2.0, and 'mongodb' to ^6.15.0.
- Renamed dev dependency packages from '@gitzone/...' to '@git.zone/...' and updated '@push.rocks/tapbundle' and '@types/node'.
- Fixed YAML workflow command: replaced 'pnpm install -g @gitzone/tsdoc' with 'pnpm install -g @git.zone/tsdoc'.
- Added package manager configuration and pnpm-workspace.yaml for built dependencies.
## 2025-03-10 - 5.3.0 - feat(docs)
Enhance documentation with updated installation instructions and comprehensive usage examples covering advanced features such as deep queries, automatic indexing, and distributed coordination.
- Added pnpm installation command
@@ -401,11 +464,13 @@ Enhance documentation with updated installation instructions and comprehensive u
- Included detailed examples for transactions, deep object queries, and document lifecycle hooks
## 2025-02-03 - 5.2.12 - fix(documentation)
Remove license badge from README
- Removed the license badge from the README file, ensuring compliance with branding guidelines.
## 2025-02-03 - 5.2.11 - fix(documentation)
Updated project documentation for accuracy and added advanced feature details
- Added details for EasyStore, Distributed Coordination, and Real-time Data Watching features.
@@ -413,158 +478,188 @@ Updated project documentation for accuracy and added advanced feature details
- Re-organized advanced usage section to showcase additional features separately.
## 2024-09-05 - 5.2.10 - fix(smartdata.classes.doc)
Fix issue with array handling in convertFilterForMongoDb function
- Corrected the logic to properly handle array filters in the convertFilterForMongoDb function to avoid incorrect assignments.
## 2024-09-05 - 5.2.9 - fix(smartdata.classes.doc)
Fixed issue with convertFilterForMongoDb to handle array operators.
- Updated the convertFilterForMongoDb function in smartdata.classes.doc.ts to properly handle array operators like $in and $all.
## 2024-09-05 - 5.2.8 - fix(smartdata.classes.doc)
Fix key handling in convertFilterForMongoDb function
- Fixed an issue in convertFilterForMongoDb that allowed keys with dots which could cause errors.
## 2024-09-05 - 5.2.7 - fix(core)
Fixed issue with handling filter keys containing dots in smartdata.classes.doc.ts
- Fixed an error in the convertFilterForMongoDb function which previously threw an error when keys contained dots.
## 2024-06-18 - 5.2.6 - Chore
Maintenance Release
- Release version 5.2.6
## 2024-05-31 - 5.2.2 - Bug Fixes
Fixes and Maintenance
- Fixed issue where `_createdAt` and `_updatedAt` registered saveableProperties for all document types
## 2024-04-15 - 5.1.2 - New Feature
Enhancements and Bug Fixes
- Added static `.getCount({})` method to `SmartDataDbDoc`
- Changed fields `_createdAt` and `_updatedAt` to ISO format
## 2024-04-14 - 5.0.43 - New Feature
New Feature Addition
- Added default `_createdAt` and `_updatedAt` fields, fixes #1
## 2024-03-30 - 5.0.41 - Bug Fixes
Improvements and Fixes
- Improved `tsconfig.json` for ES Module use
## 2023-07-10 - 5.0.20 - Chore
Organizational Changes
- Switched to new org scheme
## 2023-07-21 - 5.0.21 to 5.0.26 - Fixes
Multiple Fix Releases
- Various core updates and bug fixes
## 2023-07-21 - 5.0.20 - Chore
Organizational Changes
- Switch to the new org scheme
## 2023-06-25 - 5.0.14 to 5.0.19 - Fixes
Multiple Fix Releases
- Various core updates and bug fixes
## 2022-05-17 - 5.0.0 - Major Update
Breaking Changes
- Switched to ESM
## 2022-05-18 - 5.0.2 - Bug Fixes
Bug Fixes
- The `watcher.changeSubject` now emits the correct type into observer functions
## 2022-05-17 - 5.0.1 - Chore
Testing Improvements
- Tests now use `@pushrocks/smartmongo` backed by `wiredTiger`
## 2022-05-17 to 2022-11-08 - 5.0.8 to 5.0.10
Multiple Fix Releases
- Various core updates and bug fixes
## 2021-11-12 - 4.0.17 to 4.0.20
Multiple Fix Releases
- Various core updates and bug fixes
## 2021-09-17 - 4.0.10 to 4.0.16
Multiple Fix Releases
- Various core updates and bug fixes
## 2021-06-09 - 4.0.1 to 4.0.9
Multiple Fix Releases
- Various core updates and bug fixes
## 2021-06-06 - 4.0.0 - Major Update
Major Release
- Maintenance and core updates
## 2021-05-17 - 3.1.56 - Chore
Maintenance Release
- Release version 3.1.56
## 2020-09-09 - 3.1.44 to 3.1.52
Multiple Fix Releases
- Various core updates and bug fixes
## 2020-06-12 - 3.1.26 to 3.1.28
Multiple Fix Releases
- Various core updates and bug fixes
## 2020-02-18 - 3.1.23 to 3.1.25
Multiple Fix Releases
- Various core updates and bug fixes
## 2019-09-11 - 3.1.20 to 3.1.22
Multiple Fix Releases
- Various core updates and bug fixes
## 2018-07-10 - 3.0.5 - New Feature
Added Feature
- Added custom unique indexes to `SmartdataDoc`
## 2018-07-08 - 3.0.1 - Chore
Dependencies Update
- Updated mongodb dependencies
## 2018-07-08 - 3.0.0 - Major Update
Refactor and Cleanup
- Cleaned project structure
## 2018-01-16 - 2.0.7 - Breaking Change
Big Changes
- Switched to `@pushrocks` scope and moved from `rethinkdb` to `mongodb`
## 2018-01-12 - 2.0.0 - Major Release
Core Updates
- Updated CI configurations

View File

@@ -10,7 +10,7 @@
"scripts": {
"test": "tstest test/ --verbose --logfile --timeout 120",
"testSearch": "tsx test/test.search.ts",
"build": "tsbuild --web --allowimplicitany",
"build": "tsbuild tsfolders",
"buildDocs": "tsdoc"
},
"repository": {
@@ -24,25 +24,24 @@
},
"homepage": "https://code.foss.global/push.rocks/smartdata#readme",
"dependencies": {
"@push.rocks/lik": "^6.2.2",
"@push.rocks/smartdelay": "^3.0.1",
"@push.rocks/smartlog": "^3.1.10",
"@push.rocks/smartmongo": "^2.0.14",
"@push.rocks/smartpromise": "^4.0.2",
"@push.rocks/lik": "^6.4.0",
"@push.rocks/smartdelay": "^3.0.5",
"@push.rocks/smartlog": "^3.2.1",
"@push.rocks/smartmongo": "^5.1.0",
"@push.rocks/smartpromise": "^4.2.3",
"@push.rocks/smartrx": "^3.0.10",
"@push.rocks/smartstring": "^4.1.0",
"@push.rocks/smarttime": "^4.0.6",
"@push.rocks/smartunique": "^3.0.8",
"@push.rocks/taskbuffer": "^3.4.0",
"@tsclass/tsclass": "^9.3.0",
"mongodb": "^7.0.0"
"@push.rocks/smarttime": "^4.2.3",
"@push.rocks/smartunique": "^3.0.9",
"@push.rocks/taskbuffer": "^8.0.2",
"@tsclass/tsclass": "^9.5.0",
"mongodb": "^7.1.1"
},
"devDependencies": {
"@git.zone/tsbuild": "^3.1.2",
"@git.zone/tsrun": "^2.0.0",
"@git.zone/tstest": "^3.1.3",
"@git.zone/tsbuild": "^4.4.0",
"@git.zone/tsrun": "^2.0.1",
"@git.zone/tstest": "^3.5.1",
"@push.rocks/qenv": "^6.1.3",
"@push.rocks/tapbundle": "^6.0.3",
"@types/node": "^22.15.2"
},
"files": [
@@ -54,7 +53,7 @@
"dist_ts_web/**/*",
"assets/**/*",
"cli.js",
"npmextra.json",
".smartconfig.json",
"readme.md"
],
"browserslist": [

3384
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +0,0 @@
onlyBuiltDependencies:
- esbuild
- mongodb-memory-server
- puppeteer

View File

@@ -3,9 +3,11 @@
## TC39 Decorator Migration (v6.0.0) - ✅ COMPLETED
### Final Status: All Tests Passing (157/157)
Migration successfully completed on 2025-11-17.
### What Changed:
- ✅ Removed `experimentalDecorators` from tsconfig.json
- ✅ Refactored all 7 decorators to TC39 Stage 3 syntax
- 5 property decorators: @globalSvDb, @svDb, @unI, @index, @searchable
@@ -14,13 +16,16 @@ Migration successfully completed on 2025-11-17.
- ✅ All tests passing across Node.js and Deno runtimes
### Critical Discovery: TC39 Metadata Access Pattern
**THE KEY INSIGHT**: In TC39 decorators, metadata is NOT accessed via `constructor[Symbol.metadata]`. Instead:
- **Field decorators**: Write to `context.metadata`
- **Class decorators**: Read from `context.metadata` (same shared object!)
- The `context.metadata` object is shared between all decorators on the same class
- Attempting to write to `constructor[Symbol.metadata]` throws: "Cannot assign to read only property"
### Implementation Pattern:
```typescript
// Field decorator - stores metadata
export function svDb() {
@@ -46,25 +51,30 @@ export function Collection(dbArg: SmartdataDb) {
```
### Runtime Compatibility:
-**Node.js v23.8.0**: Full TC39 support
-**Deno v2.5.4**: Full TC39 support
-**Bun v1.3.0**: No TC39 support (uses legacy decorators only)
- Removed "+bun" from test filenames to skip Bun tests
### Key Technical Notes:
1. **Metadata Initialization Timing**: Class decorators run AFTER field decorators, allowing them to read accumulated metadata and initialize prototypes before any instances are created
2. **Prototype vs Instance Properties**: Properties set on prototype are accessible via `this.propertyName` in instances
3. **TypeScript Lib Support**: TypeScript 5.9.3 includes built-in decorator types (no custom lib configuration needed)
4. **Interface Naming**: Used `ISmartdataDecoratorMetadata` extending `DecoratorMetadataObject` for type safety
### Files Modified:
- ts/classes.doc.ts (property decorators + metadata interface)
- ts/classes.collection.ts (class decorators + prototype initialization)
- tsconfig.json (removed experimentalDecorators flag)
- test/*.ts (renamed files to remove "+bun" suffix)
- test/\*.ts (renamed files to remove "+bun" suffix)
### Test Results:
All 157 tests passing across 10 test files:
- test.cursor.ts: 7/7
- test.deno.ts: 11/11 (queries working correctly!)
- test.search.advanced.ts: 41/41
@@ -73,6 +83,7 @@ All 157 tests passing across 10 test files:
- And 5 more test files
### Migration Learnings for Future Reference:
1. `context.metadata` is the ONLY way to share state between decorators
2. Class decorators must initialize prototypes from metadata immediately
3. `Symbol.metadata` on constructors is read-only (managed by runtime)
@@ -82,10 +93,13 @@ All 157 tests passing across 10 test files:
## ES2022 Class Fields & Prototype Getters - Fixed in v7.0.15
### Issue
ES2022 class fields (`useDefineForClassFields: true`) create own properties during construction that shadow prototype getters defined by decorators.
### Solution
Use `declare` keyword for instance properties that are accessed via prototype getters:
```typescript
// In SmartDataDbDoc (ts/classes.doc.ts):
declare public collection: SmartdataCollection<any>; // Type-only, no JS emitted
@@ -93,6 +107,7 @@ declare public manager: TManager; // Type-only, no JS emitted
```
### Key Insight
- `declare` tells TypeScript this is a type-only declaration
- No JavaScript code is emitted for `declare` properties
- Prototype getters defined by `@Collection` and `@managed` decorators are no longer shadowed

216
readme.md
View File

@@ -52,9 +52,9 @@ const db = new SmartdataDb({
mongoDbPass: 'password',
// Optional: Advanced connection pooling
maxPoolSize: 100, // Max connections in pool
maxIdleTimeMS: 300000, // Max idle time before connection close
serverSelectionTimeoutMS: 30000 // Connection timeout
maxPoolSize: 100, // Max connections in pool
maxIdleTimeMS: 300000, // Max idle time before connection close
serverSelectionTimeoutMS: 30000, // Connection timeout
});
// Initialize with automatic retry and health monitoring
@@ -77,25 +77,25 @@ import { ObjectId } from 'mongodb';
@Collection(() => db)
class User extends SmartDataDbDoc<User, User> {
@unI()
public id: string; // Unique index with automatic ID generation
public id: string; // Unique index with automatic ID generation
@svDb()
@searchable() // Enable Lucene-style searching
@searchable() // Enable Lucene-style searching
public username: string;
@svDb()
@searchable()
@index({ unique: false }) // Performance index
@index({ unique: false }) // Performance index
public email: string;
@svDb()
public status: 'active' | 'inactive' | 'pending'; // Full union type support
public status: 'active' | 'inactive' | 'pending'; // Full union type support
@svDb()
public organizationId: ObjectId; // Native MongoDB types
public organizationId: ObjectId; // Native MongoDB types
@svDb()
public profilePicture: Buffer; // Binary data support
public profilePicture: Buffer; // Binary data support
@svDb({
// Custom serialization for complex objects
@@ -105,7 +105,7 @@ class User extends SmartDataDbDoc<User, User> {
public preferences: Record<string, any>;
@svDb()
public tags: string[]; // Array support with operators
public tags: string[]; // Array support with operators
@svDb()
public createdAt: Date = new Date();
@@ -156,12 +156,12 @@ const john = await User.getInstances({ name: 'John Doe' });
// Multiple fields (implicit AND)
const activeAdults = await User.getInstances({
status: 'active',
age: { $gte: 18 }
age: { $gte: 18 },
});
// Union types work perfectly
const users = await User.getInstances({
status: { $in: ['active', 'pending'] } // TypeScript validates these values!
status: { $in: ['active', 'pending'] }, // TypeScript validates these values!
});
```
@@ -173,19 +173,19 @@ SmartData supports **both** nested object notation and dot notation for querying
// Nested object notation - natural TypeScript syntax
const users = await User.getInstances({
metadata: {
loginCount: { $gte: 5 }
}
loginCount: { $gte: 5 },
},
});
// Dot notation - MongoDB style
const sameUsers = await User.getInstances({
'metadata.loginCount': { $gte: 5 }
'metadata.loginCount': { $gte: 5 },
});
// POWERFUL: Combine both notations - operators are merged!
const filtered = await User.getInstances({
metadata: { loginCount: { $gte: 3 } }, // Object notation
'metadata.loginCount': { $lte: 10 } // Dot notation
metadata: { loginCount: { $gte: 3 } }, // Object notation
'metadata.loginCount': { $lte: 10 }, // Dot notation
// Result: metadata.loginCount between 3 and 10
});
@@ -195,10 +195,10 @@ const deepQuery = await User.getInstances({
settings: {
notifications: {
email: true,
frequency: { $in: ['daily', 'weekly'] }
}
}
}
frequency: { $in: ['daily', 'weekly'] },
},
},
},
});
// Mix styles for complex queries
@@ -206,11 +206,11 @@ const advanced = await User.getInstances({
// Object notation for structure
profile: {
age: { $gte: 21 },
verified: true
verified: true,
},
// Dot notation for specific overrides
'profile.settings.theme': 'dark',
'profile.lastSeen': { $gte: new Date('2024-01-01') }
'profile.lastSeen': { $gte: new Date('2024-01-01') },
});
```
@@ -219,17 +219,17 @@ const advanced = await User.getInstances({
```typescript
// Numeric comparisons with type checking
const adults = await User.getInstances({
age: { $gte: 18, $lt: 65 } // Type-safe numeric comparisons
age: { $gte: 18, $lt: 65 }, // Type-safe numeric comparisons
});
// Date comparisons
const recentUsers = await User.getInstances({
createdAt: { $gte: new Date('2024-01-01') }
createdAt: { $gte: new Date('2024-01-01') },
});
// Not equal
const nonAdmins = await User.getInstances({
role: { $ne: 'admin' }
role: { $ne: 'admin' },
});
```
@@ -238,23 +238,24 @@ const nonAdmins = await User.getInstances({
```typescript
// Array operations with full type safety
const experts = await User.getInstances({
tags: { $all: ['typescript', 'mongodb'] }, // Must have all tags
skills: { $size: 5 } // Exactly 5 skills
tags: { $all: ['typescript', 'mongodb'] }, // Must have all tags
skills: { $size: 5 }, // Exactly 5 skills
});
// Array element matching
const results = await Order.getInstances({
items: {
$elemMatch: { // Match array elements
$elemMatch: {
// Match array elements
product: 'laptop',
quantity: { $gte: 2 }
}
}
quantity: { $gte: 2 },
},
},
});
// Check if value exists in array field
const nodeUsers = await User.getInstances({
skills: { $in: ['nodejs'] } // Has nodejs in skills array
skills: { $in: ['nodejs'] }, // Has nodejs in skills array
});
```
@@ -266,24 +267,18 @@ const results = await Order.getInstances({
$and: [
{ status: { $in: ['pending', 'processing'] } },
{ 'items.price': { $gte: 100 } },
{ customer: { verified: true } }
]
{ customer: { verified: true } },
],
});
// $or operator
const urgentOrHighValue = await Order.getInstances({
$or: [
{ priority: 'urgent' },
{ totalAmount: { $gte: 1000 } }
]
$or: [{ priority: 'urgent' }, { totalAmount: { $gte: 1000 } }],
});
// $nor operator - none of the conditions
const excluded = await User.getInstances({
$nor: [
{ status: 'banned' },
{ role: 'guest' }
]
$nor: [{ status: 'banned' }, { role: 'guest' }],
});
// Combine logical operators
@@ -291,12 +286,9 @@ const complex = await Order.getInstances({
$and: [
{ status: 'active' },
{
$or: [
{ priority: 'high' },
{ value: { $gte: 1000 } }
]
}
]
$or: [{ priority: 'high' }, { value: { $gte: 1000 } }],
},
],
});
```
@@ -305,12 +297,12 @@ const complex = await Order.getInstances({
```typescript
// Check field existence
const withEmail = await User.getInstances({
email: { $exists: true }
email: { $exists: true },
});
// Check for null or missing nested fields
const noPreferences = await User.getInstances({
'profile.preferences': { $exists: false }
'profile.preferences': { $exists: false },
});
```
@@ -319,12 +311,12 @@ const noPreferences = await User.getInstances({
```typescript
// Regex patterns
const gmailUsers = await User.getInstances({
email: { $regex: '@gmail\\.com$', $options: 'i' }
email: { $regex: '@gmail\\.com$', $options: 'i' },
});
// Starts with pattern
const johnUsers = await User.getInstances({
name: { $regex: '^John' }
name: { $regex: '^John' },
});
```
@@ -349,22 +341,21 @@ const advancedQuery = await User.getInstances({
// Nested object with operators
profile: {
age: { $gte: 18, $lte: 65 },
verified: true
verified: true,
},
// Dot notation for deep paths
'settings.notifications.email': true,
'metadata.lastLogin': { $gte: new Date(Date.now() - 30*24*60*60*1000) },
'metadata.lastLogin': {
$gte: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000),
},
// Array operations
roles: { $in: ['admin', 'moderator'] },
tags: { $all: ['verified', 'premium'] },
// Logical grouping
$or: [
{ 'subscription.plan': 'premium' },
{ 'subscription.trial': true }
]
$or: [{ 'subscription.plan': 'premium' }, { 'subscription.trial': true }],
});
```
@@ -400,7 +391,7 @@ const exact = await Product.search('"MacBook Pro 16"');
// Combined with filters for powerful queries
const affordable = await Product.search('laptop', {
filter: { price: { $lte: 1500 } },
validate: async (p) => p.inStock === true
validate: async (p) => p.inStock === true,
});
```
@@ -427,7 +418,7 @@ const config = await db.createEasyStore<AppConfig>('app-config');
// Write with full type checking
await config.writeKey('features', {
darkMode: true,
notifications: false
notifications: false,
});
// Read with guaranteed types
@@ -437,7 +428,7 @@ const features = await config.readKey('features');
// Atomic updates
await config.updateKey('limits', (current) => ({
...current,
maxUsers: current.maxUsers + 100
maxUsers: current.maxUsers + 100,
}));
// Delete keys
@@ -454,11 +445,11 @@ React to database changes instantly with RxJS integration:
```typescript
// Watch for changes with automatic reconnection
const watcher = await User.watch(
{ status: 'active' }, // Filter which documents to watch
{ status: 'active' }, // Filter which documents to watch
{
fullDocument: 'updateLookup', // Get full document on updates
bufferTimeMs: 100 // Buffer changes for efficiency
}
fullDocument: 'updateLookup', // Get full document on updates
bufferTimeMs: 100, // Buffer changes for efficiency
},
);
// Subscribe to changes with RxJS
@@ -466,7 +457,7 @@ watcher.changeSubject.subscribe({
next: (change) => {
console.log('User changed:', change.fullDocument);
switch(change.operationType) {
switch (change.operationType) {
case 'insert':
console.log('New user created');
break;
@@ -478,7 +469,7 @@ watcher.changeSubject.subscribe({
break;
}
},
error: (err) => console.error('Watch error:', err)
error: (err) => console.error('Watch error:', err),
});
// Advanced: Watch with aggregation pipeline
@@ -487,9 +478,9 @@ const complexWatcher = await Order.watch(
{
pipeline: [
{ $match: { 'fullDocument.totalAmount': { $gte: 1000 } } },
{ $addFields: { isHighValue: true } }
]
}
{ $addFields: { isHighValue: true } },
],
},
);
// Clean up when done
@@ -522,7 +513,7 @@ const isLeader = coordinator.isLeader;
const result = await coordinator.fireDistributedTaskRequest({
taskName: 'process-payments',
taskExecutionTime: Date.now(),
requestResponseId: 'unique-id'
requestResponseId: 'unique-id',
});
// Graceful shutdown with leadership handoff
@@ -539,11 +530,12 @@ const cursor = await User.getCursor(
{ status: 'active' },
{
// Optional: Use MongoDB native cursor modifiers
modifier: (cursor) => cursor
.sort({ createdAt: -1 })
.limit(10000)
.project({ email: 1, username: 1 })
}
modifier: (cursor) =>
cursor
.sort({ createdAt: -1 })
.limit(10000)
.project({ email: 1, username: 1 }),
},
);
// Process one at a time
@@ -574,16 +566,13 @@ try {
// All operations in this block are atomic
const sender = await User.getInstance(
{ id: 'user-1' },
{ session } // Pass session to all operations
{ session }, // Pass session to all operations
);
sender.balance -= 100;
await sender.save({ session });
const receiver = await User.getInstance(
{ id: 'user-2' },
{ session }
);
const receiver = await User.getInstance({ id: 'user-2' }, { session });
receiver.balance += 100;
await receiver.save({ session });
@@ -609,28 +598,28 @@ class Document extends SmartDataDbDoc<Document, Document> {
@svDb({
// Encrypt sensitive data before storing
serialize: async (value) => await encrypt(value),
deserialize: async (value) => await decrypt(value)
deserialize: async (value) => await decrypt(value),
})
public sensitiveData: string;
@svDb({
// Compress large JSON objects
serialize: (value) => compress(JSON.stringify(value)),
deserialize: (value) => JSON.parse(decompress(value))
deserialize: (value) => JSON.parse(decompress(value)),
})
public largePayload: any;
@svDb({
// Store Sets as arrays
serialize: (set) => Array.from(set),
deserialize: (arr) => new Set(arr)
deserialize: (arr) => new Set(arr),
})
public tags: Set<string>;
@svDb({
// Handle custom date formats
serialize: (date) => date?.toISOString(),
deserialize: (str) => str ? new Date(str) : null
deserialize: (str) => (str ? new Date(str) : null),
})
public scheduledAt: Date | null;
}
@@ -651,8 +640,9 @@ class Order extends SmartDataDbDoc<Order, Order> {
// Called before saving (create or update)
async beforeSave() {
// Recalculate total
this.totalAmount = this.items.reduce((sum, item) =>
sum + (item.price * item.quantity), 0
this.totalAmount = this.items.reduce(
(sum, item) => sum + item.price * item.quantity,
0,
);
// Validate
@@ -698,34 +688,37 @@ class Order extends SmartDataDbDoc<Order, Order> {
```typescript
@Collection(() => db)
class HighPerformanceDoc extends SmartDataDbDoc<HighPerformanceDoc, HighPerformanceDoc> {
@unI() // Unique index
class HighPerformanceDoc extends SmartDataDbDoc<
HighPerformanceDoc,
HighPerformanceDoc
> {
@unI() // Unique index
public id: string;
@index() // Single field index
@index() // Single field index
public userId: string;
@index({ sparse: true }) // Sparse index for optional fields
@index({ sparse: true }) // Sparse index for optional fields
public deletedAt?: Date;
@index({
unique: false,
background: true, // Non-blocking index creation
expireAfterSeconds: 86400 // TTL index
background: true, // Non-blocking index creation
expireAfterSeconds: 86400, // TTL index
})
public sessionToken: string;
// Compound indexes for complex queries
static async createIndexes() {
await this.collection.createIndex(
{ userId: 1, createdAt: -1 }, // Compound index
{ name: 'user_activity_idx' }
{ userId: 1, createdAt: -1 }, // Compound index
{ name: 'user_activity_idx' },
);
// Text index for search
await this.collection.createIndex(
{ title: 'text', content: 'text' },
{ weights: { title: 10, content: 5 } }
{ weights: { title: 10, content: 5 } },
);
}
}
@@ -739,18 +732,18 @@ const db = new SmartdataDb({
mongoDbName: 'myapp',
// Connection pool optimization
maxPoolSize: 100, // Maximum connections
minPoolSize: 10, // Minimum connections to maintain
maxIdleTimeMS: 300000, // Close idle connections after 5 minutes
waitQueueTimeoutMS: 5000, // Max time to wait for available connection
maxPoolSize: 100, // Maximum connections
minPoolSize: 10, // Minimum connections to maintain
maxIdleTimeMS: 300000, // Close idle connections after 5 minutes
waitQueueTimeoutMS: 5000, // Max time to wait for available connection
// Server selection
serverSelectionTimeoutMS: 30000, // Timeout for selecting a server
heartbeatFrequencyMS: 10000, // How often to check server status
serverSelectionTimeoutMS: 30000, // Timeout for selecting a server
heartbeatFrequencyMS: 10000, // How often to check server status
// Socket settings
socketTimeoutMS: 360000, // Socket timeout (6 minutes)
family: 4, // Force IPv4
socketTimeoutMS: 360000, // Socket timeout (6 minutes)
family: 4, // Force IPv4
});
```
@@ -759,6 +752,7 @@ const db = new SmartdataDb({
### 1. Always Use TypeScript
SmartData is built for TypeScript. Using JavaScript means missing out on:
- Compile-time query validation
- IntelliSense for MongoDB operators
- Type-safe document updates
@@ -785,7 +779,7 @@ await session.withTransaction(async () => {
});
// ❌ Bad: Multiple operations without transactions
await debitAccount(fromAccount, amount); // What if this fails?
await debitAccount(fromAccount, amount); // What if this fails?
await creditAccount(toAccount, amount);
```
@@ -799,7 +793,7 @@ await cursor.forEach(async (doc) => {
});
// ❌ Bad: Loading everything into memory
const allDocs = await LargeCollection.getInstances({}); // Could OOM!
const allDocs = await LargeCollection.getInstances({}); // Could OOM!
```
### 5. Implement Proper Error Handling
@@ -819,7 +813,7 @@ try {
// ❌ Bad: Ignoring errors
const user = await User.getInstance({ id: userId });
await processUser(user); // What if user is null?
await processUser(user); // What if user is null?
```
## 🔧 Troubleshooting
@@ -854,17 +848,17 @@ Always clean up resources:
// Watchers
const watcher = await User.watch({});
// ... use watcher
await watcher.close(); // Always close!
await watcher.close(); // Always close!
// Cursors
const cursor = await User.getCursor({});
// ... use cursor
await cursor.close(); // Always close!
await cursor.close(); // Always close!
// Sessions
const session = db.startSession();
// ... use session
await session.endSession(); // Always end!
await session.endSession(); // Always end!
```
### Performance Issues

View File

@@ -1,4 +1,4 @@
import { tap, expect } from '@push.rocks/tapbundle';
import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as smartmongo from '@push.rocks/smartmongo';
import { smartunique } from '../ts/plugins.js';
import * as smartdata from '../ts/index.js';

View File

@@ -1,6 +1,6 @@
// TODO: Decorator support during testing for bun and deno in @git.zone/tstest
import { tap, expect } from '@push.rocks/tapbundle';
import { tap, expect } from '@git.zone/tstest/tapbundle';
import { Qenv } from '@push.rocks/qenv';
import * as smartmongo from '@push.rocks/smartmongo';
import { smartunique } from '../ts/plugins.js';

View File

@@ -1,4 +1,4 @@
import { tap, expect } from '@push.rocks/tapbundle';
import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as smartmongo from '@push.rocks/smartmongo';
import type * as taskbuffer from '@push.rocks/taskbuffer';

View File

@@ -1,4 +1,4 @@
import { tap, expect } from '@push.rocks/tapbundle';
import { tap, expect } from '@git.zone/tstest/tapbundle';
import { Qenv } from '@push.rocks/qenv';
import * as smartmongo from '@push.rocks/smartmongo';
import { smartunique } from '../ts/plugins.js';

View File

@@ -1,6 +1,6 @@
// TODO: Decorator support during testing for bun and deno in @git.zone/tstest
import { tap, expect } from '@push.rocks/tapbundle';
import { tap, expect } from '@git.zone/tstest/tapbundle';
import { Qenv } from '@push.rocks/qenv';
import * as smartmongo from '@push.rocks/smartmongo';
import { smartunique } from '../ts/plugins.js';

View File

@@ -1,4 +1,4 @@
import { tap, expect } from '@push.rocks/tapbundle';
import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as smartmongo from '@push.rocks/smartmongo';
import * as smartdata from '../ts/index.js';
import { searchable } from '../ts/classes.doc.js';

View File

@@ -1,4 +1,4 @@
import { tap, expect } from '@push.rocks/tapbundle';
import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as smartmongo from '@push.rocks/smartmongo';
import { smartunique } from '../ts/plugins.js';

View File

@@ -1,4 +1,4 @@
import { tap, expect } from '@push.rocks/tapbundle';
import { tap, expect } from '@git.zone/tstest/tapbundle';
import { Qenv } from '@push.rocks/qenv';
import * as smartmongo from '@push.rocks/smartmongo';
import { smartunique } from '../ts/plugins.js';

View File

@@ -1,4 +1,4 @@
import { tap, expect } from '@push.rocks/tapbundle';
import { tap, expect } from '@git.zone/tstest/tapbundle';
import { Qenv } from '@push.rocks/qenv';
import * as smartmongo from '@push.rocks/smartmongo';
import { smartunique } from '../ts/plugins.js';

View File

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

@@ -174,8 +174,8 @@ export class SmartdataCollection<T> {
/**
* the collection that is used
*/
public mongoDbCollection: plugins.mongodb.Collection;
public objectValidation: IDocValidationFunc<T> = null;
public mongoDbCollection!: plugins.mongodb.Collection;
public objectValidation: IDocValidationFunc<T> | null = null;
public collectionName: string;
public smartdataDb: SmartdataDb;
public uniqueIndexes: string[] = [];
@@ -330,7 +330,7 @@ export class SmartdataCollection<T> {
);
const smartdataWatcher = new SmartdataDbWatcher(
changeStream,
smartdataDbDocArg,
smartdataDbDocArg!,
{ bufferTimeMs },
);
await smartdataWatcher.readyDeferred.promise;
@@ -353,7 +353,7 @@ export class SmartdataCollection<T> {
this.createRegularIndexes(dbDocArg.regularIndexes);
}
const saveableObject = await dbDocArg.createSavableObject();
const saveableObject = await dbDocArg.createSavableObject() as any;
const result = await this.mongoDbCollection.insertOne(saveableObject, { session: opts?.session });
return result;
}
@@ -368,7 +368,7 @@ export class SmartdataCollection<T> {
await this.init();
await this.checkDoc(dbDocArg);
const identifiableObject = await dbDocArg.createIdentifiableObject();
const saveableObject = await dbDocArg.createSavableObject();
const saveableObject = await dbDocArg.createSavableObject() as any;
const updateableObject: any = {};
for (const key of Object.keys(saveableObject)) {
if (identifiableObject[key]) {

View File

@@ -12,8 +12,8 @@ export type TConnectionStatus = 'initial' | 'disconnected' | 'connected' | 'fail
export class SmartdataDb {
smartdataOptions: plugins.tsclass.database.IMongoDescriptor;
mongoDbClient: plugins.mongodb.MongoClient;
mongoDb: plugins.mongodb.Db;
mongoDbClient!: plugins.mongodb.MongoClient;
mongoDb!: plugins.mongodb.Db;
status: TConnectionStatus;
statusConnectedDeferred = plugins.smartpromise.defer();
smartdataCollectionMap = new plugins.lik.ObjectMap<SmartdataCollection<any>>();
@@ -51,8 +51,8 @@ export class SmartdataDb {
.replace('<user>', encodedUser)
.replace('<PASSWORD>', encodedPass)
.replace('<password>', encodedPass)
.replace('<DBNAME>', this.smartdataOptions.mongoDbName)
.replace('<dbname>', this.smartdataOptions.mongoDbName);
.replace('<DBNAME>', this.smartdataOptions.mongoDbName || '')
.replace('<dbname>', this.smartdataOptions.mongoDbName || '');
const clientOptions: plugins.mongodb.MongoClientOptions = {
maxPoolSize: (this.smartdataOptions as any).maxPoolSize ?? 100,
@@ -70,7 +70,7 @@ export class SmartdataDb {
} catch (error) {
this.status = 'disconnected';
this.statusConnectedDeferred.reject(error);
logger.log('error', `Failed to connect to database ${this.smartdataOptions.mongoDbName}: ${error.message}`);
logger.log('error', `Failed to connect to database ${this.smartdataOptions.mongoDbName}: ${(error as Error).message}`);
throw error;
}
}

View File

@@ -9,10 +9,10 @@ import { logger } from './logging.js';
export class DistributedClass extends SmartDataDbDoc<DistributedClass, DistributedClass> {
// INSTANCE
@unI()
public id: string;
public id!: string;
@svDb()
public data: {
public data!: {
status: 'initializing' | 'bidding' | 'settled' | 'stopped';
biddingShortcode?: string;
biddingStartTime?: number;
@@ -40,8 +40,8 @@ export class SmartdataDistributedCoordinator extends plugins.taskbuffer.distribu
public readyPromise: Promise<any>;
public db: SmartdataDb;
private asyncExecutionStack = new plugins.lik.AsyncExecutionStack();
public ownInstance: DistributedClass;
public distributedWatcher: SmartdataDbWatcher<DistributedClass>;
public ownInstance!: DistributedClass;
public distributedWatcher!: SmartdataDbWatcher<DistributedClass>;
constructor(dbArg: SmartdataDb) {
super();
@@ -163,8 +163,8 @@ export class SmartdataDistributedCoordinator extends plugins.taskbuffer.distribu
} else if (
(await DistributedClass.getInstances({})).find((instanceArg) => {
return instanceArg.data.status === 'bidding' &&
instanceArg.data.biddingStartTime <= Date.now() - 4000 &&
instanceArg.data.biddingStartTime >= Date.now() - 30000;
instanceArg.data.biddingStartTime! <= Date.now() - 4000 &&
instanceArg.data.biddingStartTime! >= Date.now() - 30000;
})
) {
logger.log('info', 'too late to the bidding party... waiting for next round.');
@@ -191,7 +191,7 @@ export class SmartdataDistributedCoordinator extends plugins.taskbuffer.distribu
logger.log('info', `found ${biddingInstances.length} bidding instances...`);
this.ownInstance.data.elected = true;
for (const biddingInstance of biddingInstances) {
if (biddingInstance.data.biddingShortcode < this.ownInstance.data.biddingShortcode) {
if (biddingInstance.data.biddingShortcode! < this.ownInstance.data.biddingShortcode!) {
this.ownInstance.data.elected = false;
}
}
@@ -270,7 +270,7 @@ export class SmartdataDistributedCoordinator extends plugins.taskbuffer.distribu
});
if (!result) {
logger.log('warn', 'no result found for task request...');
return null;
return null as any;
}
return result;
}

View File

@@ -482,7 +482,7 @@ export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends
convertFilterForMongoDb(filterArg),
{ session: opts?.session },
);
const returnArray = [];
const returnArray: T[] = [];
for (const foundDoc of foundDocs) {
const newInstance: T = (this as any).createInstanceFromMongoDbNativeDoc(foundDoc);
returnArray.push(newInstance);
@@ -510,7 +510,7 @@ export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends
const newInstance: T = (this as any).createInstanceFromMongoDbNativeDoc(foundDoc);
return newInstance;
} else {
return null;
return null as any;
}
}
@@ -833,12 +833,12 @@ export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends
/**
* name
*/
public name: string;
public name!: string;
/**
* primary id in the database
*/
public dbDocUniqueId: string;
public dbDocUniqueId!: string;
/**
* class constructor
@@ -898,7 +898,7 @@ export class SmartDataDbDoc<T extends TImplements, TImplements, TManager extends
* also store any referenced objects to DB
* better for data consistency
*/
public saveDeep(savedMapArg: plugins.lik.ObjectMap<SmartDataDbDoc<any, any>> = null) {
public saveDeep(savedMapArg?: plugins.lik.ObjectMap<SmartDataDbDoc<any, any>>) {
if (!savedMapArg) {
savedMapArg = new plugins.lik.ObjectMap<SmartDataDbDoc<any, any>>();
}

View File

@@ -15,19 +15,19 @@ export class EasyStore<T> {
@Collection(() => this.smartdataDbRef)
class SmartdataEasyStore extends SmartDataDbDoc<SmartdataEasyStore, SmartdataEasyStore> {
@unI()
public nameId: string;
public nameId!: string;
@svDb()
public ephemeral: {
public ephemeral!: {
activated: boolean;
timeout: number;
};
@svDb()
lastEdit: number;
lastEdit!: number;
@svDb()
public data: Partial<T>;
public data!: Partial<T>;
}
return SmartdataEasyStore;
})();
@@ -37,7 +37,7 @@ export class EasyStore<T> {
this.nameId = nameIdArg;
}
private easyStorePromise: Promise<InstanceType<typeof this.easyStoreClass>>;
private easyStorePromise!: Promise<InstanceType<typeof this.easyStoreClass>>;
private async getEasyStore(): Promise<InstanceType<typeof this.easyStoreClass>> {
if (this.easyStorePromise) {
return this.easyStorePromise;

View File

@@ -536,7 +536,7 @@ export class LuceneToMongoTransformer {
const searchTerm = rightQuery.$text.$search.replace(/"/g, '');
// Determine the fields to apply the negation to
const notConditions = [];
const notConditions: any[] = [];
for (const field in leftQuery) {
if (field !== '$or' && field !== '$and') {

View File

@@ -13,7 +13,7 @@ export class SmartdataDbWatcher<T = any> extends EventEmitter {
public readyDeferred = plugins.smartpromise.defer();
// INSTANCE
private changeStream: plugins.mongodb.ChangeStream<T>;
private changeStream: plugins.mongodb.ChangeStream<any>;
private rawSubject: plugins.smartrx.rxjs.Subject<T>;
/** Emits change documents (or arrays of documents if buffered) */
public changeSubject: any;
@@ -23,7 +23,7 @@ export class SmartdataDbWatcher<T = any> extends EventEmitter {
* @param opts.bufferTimeMs optional milliseconds to buffer events via RxJS
*/
constructor(
changeStreamArg: plugins.mongodb.ChangeStream<T>,
changeStreamArg: plugins.mongodb.ChangeStream<any>,
smartdataDbDocArg: typeof SmartDataDbDoc,
opts?: { bufferTimeMs?: number },
) {
@@ -37,14 +37,14 @@ export class SmartdataDbWatcher<T = any> extends EventEmitter {
}
this.changeStream = changeStreamArg;
this.changeStream.on('change', async (item: any) => {
let docInstance: T = null;
let docInstance: T | null = null;
if (item.fullDocument) {
docInstance = smartdataDbDocArg.createInstanceFromMongoDbNativeDoc(
item.fullDocument
) as any as T;
}
// Notify subscribers
this.rawSubject.next(docInstance);
this.rawSubject.next(docInstance as T);
this.emit('change', docInstance);
});
// Signal readiness after one tick

View File

@@ -5,10 +5,9 @@
"moduleResolution": "NodeNext",
"esModuleInterop": true,
"verbatimModuleSyntax": true,
"baseUrl": ".",
"paths": {}
"types": [
"node"
]
},
"exclude": [
"dist_*/**/*.d.ts"
]
}
"exclude": ["dist_*/**/*.d.ts"]
}