Every fix applied during the Phase 0 code audit, with what was wrong, what changed, and line references (before -> after).
- What was wrong:
findOne()built anewItemobject with populate/select applied, then returneditem(the raw document) instead ofnewItem. All projection and population was silently discarded. - What changed: Changed
return itemtoreturn newIteminfindOne(). - File:
src/collection.js: was line 222, now line 221.
- What was wrong: Multi-condition filters like
{ role: 'admin', age: 30 }only evaluated the first key and returned immediately.$in/$ninoperators were inverted (itemValue.includes(filterValue.$in)instead offilterValue.$in.includes(itemValue)). Nested key traversal crashed on null intermediates and skipped falsy values (0, "", false). - What changed: Complete rewrite of
matchesFilter(). All conditions now returnfalseon failure (AND semantics).$in/$nincorrected. Nested traversal usesreducewith null guard. Added support for RegExp as direct filter value. - File:
src/collection.js: was lines 375-438, now lines 369-413.
- What was wrong:
$incand$pushoperated on a localitemValuevariable (a copy), never writing the result back toitem[field]. TheupdatedAttimestamp was set inside the field loop (once per field instead of once per update).Object.assign(item, item)was a dead no-op. - What changed:
$incnow writes directly toitem[field].$pushoperates directly onitem[field]. Removed deadObject.assign(item, item). MovedupdatedAtand_index.set()outside the field loop. Also rewroteupdateOne()andupdateMany()to find raw documents directly rather than throughfindOne()/find()which return projected copies. - File:
src/collection.js: was lines 144-180, now lines 146-169.updateOnenow lines 84-112.updateManynow lines 122-138.
- What was wrong: A single
this.isSavingflag on the Skalex instance meant that if collection A was saving, collection B's save was silently skipped. The flag was also not reset on errors that escaped both try/catch blocks. - What changed: Removed database-level
isSaving. AddedisSaving: falseto each collection's data object increateCollection()andloadData().saveData()now checks/sets per-collectionisSaving. Usesfinallyblock to guarantee reset. - File:
src/index.js: removed old line 43 (this.isSaving). Per-collection flag at lines 112, 144.saveData()rewritten at lines 169-209.
- What was wrong:
saveData()calledJSON.stringify()on the data, thenwriteFile()calledJSON.stringify()again, producing a double-encoded file. On read,JSON.parse()returned a string instead of an object. - What changed: Removed
JSON.stringify(data)fromwriteFile(). Data is now written as-is (already stringified by caller for save, or plain text for exports). - File:
src/filesys.js: was line 92, now lines 91-95.
- What was wrong:
findOne()calledfindIndex()(full array scan), then iterated the array again. The_idMap index was never consulted. - What changed: Added fast-path: when
filter._idis present, lookup viathis._index.get(filter._id)(O(1)). Falls back to linear scan only when_idis not in the filter. - File:
src/collection.js: was lines 193-228, now lines 179-222.
- What was wrong: The nested key loop used
if (itemValue[nestedKey])which crashed on null/undefined and skipped falsy values. - What changed: Resolved by FIX-02 rewrite using
keys.reduce((obj, k) => (obj != null ? obj[k] : undefined), item). - File:
src/collection.js: covered inmatchesFilter()at lines 385-386.
- What was wrong:
$inuseditemValue.includes(filterValue.$in), which is backwards (item value is a scalar, not an array).$ninhad the same inversion. - What changed: Resolved by FIX-02 rewrite. Now
filterValue.$in.includes(itemValue)andfilterValue.$nin.includes(itemValue). - File:
src/collection.js: covered inmatchesFilter()at lines 400-401.
- What was wrong:
collection.jsimported Node'sfsandpathmodules and usedfs.writeFileSync()directly inexport(), bypassing the storage adapter abstraction. - What changed: Removed
require("fs"),require("path"), andrequire("./filesys")imports. Rewroteexport()to usethis.database.fs(the storage adapter) forcheckDir(),writeFile(), andjoin(). CSV export now properly quotes values containing commas. - File:
src/collection.js: removed lines 1-4.export()rewritten at lines 439-475.
- What was wrong: Each call to
db.useCollection('users')returned a newCollectionobject. State attached to one instance was invisible to the other. - What changed: Added
this._collectionInstances = {}cache in constructor.useCollection()now returns cached instance if one exists, or creates and caches a new one. Cache is cleared indisconnect(). - File:
src/index.js: constructor line 39.useCollection()at lines 85-100.disconnect()line 69.
- What was wrong:
this.dataandthis.indexwere direct references to the underlying store, allowing external code to bypass all validation and safety checks. - What changed: Constructor now stores
this._store = collectionData. Added_datagetter/setter and_indexgetter that access_store.dataand_store.index. All internal references updated fromthis.datatothis._dataandthis.indextothis._index. Public.dataand.indexare no longer exposed. - File:
src/collection.js: constructor at lines 13-17. Accessors at lines 19-21.
- What was wrong:
insertOne(),updateOne(), anddeleteOne()returned raw documents.updateMany()returned bare[]when no matches found. Mixed return shapes made the API unpredictable. - What changed: Single-document operations now return
{ data: document }. Multi-document operations consistently return{ docs: [...] }.updateManyreturns{ docs: [] }instead of bare[]. - File:
src/collection.js:insertOneline 45,updateOneline 108,deleteOneline 328,updateManyline 137.
- What was wrong:
insertOnedid not setupdatedAton new documents.applyUpdatesetupdatedAtinside the field loop, setting it once per field instead of once per update call. - What changed: Added
updatedAt: new Date()toinsertOneandinsertManydocument templates.applyUpdatenow setsupdatedAtonce, outside the field loop (was fixed as part of FIX-03). - File:
src/collection.js:insertOneline 34,insertManyline 59,applyUpdateline 165.
- What was wrong: Old type definitions referenced non-existent method
exportToCSV, had wrong constructor signature (dataDirectory: stringinstead of config object), and defined phantomsave()methods on return interfaces (InsertRecord,UpdateRecord,DeleteRecord). - What changed: Complete rewrite. New file defines correct interfaces:
SkalexConfig,FindOptions,FindResult,SingleResult,ManyResult,ExportOptions.Collectionclass is generic.Skalexconstructor accepts optionalSkalexConfig. All method signatures match the actual implementation. - File:
src/index.d.ts: entire file replaced (was 72 lines, now 69 lines).
- What was wrong: The class was named
fs, shadowing Node's built-infsmodule name. - What changed: Renamed class from
fstoFileSystem. Updated export and import inindex.js. - File:
src/filesys.jslines 5, 130.src/index.jslines 3, 18.
- What was wrong: Used
Math.floor(Math.random() * 9000000000)which is not cryptographically secure and has higher collision probability at high insert rates. - What changed: Now uses
crypto.randomBytes(8)(Node) with fallback tocrypto.getRandomValues(browser). Output is hex-encoded timestamp + random bytes, truncated to 24 characters. - File:
src/utils.js: was lines 5-17, now lines 5-19.
- What was wrong: All errors during file loading were caught and logged identically, whether the file didn't exist (expected) or was corrupt (unexpected). Corrupt data was silently lost.
- What changed:
ENOENTerrors are silently skipped (normal on first run). All other errors log aWARNINGmessage identifying the filename and error, making corrupt files visible. - File:
src/index.js: was lines 141-143, now lines 147-153.
- What was wrong: Both catch blocks logged the error but did not re-throw. Callers received
undefinedwhether the operation succeeded or failed. - What changed: Added
throw errorafter logging in bothexport()(collection.js) andsaveData()(index.js). - File:
src/collection.jsline 473.src/index.jsline 194.
- What was wrong: No
exportsmap inpackage.json.engineswas set to>=10.0.0despite requiring features from newer Node versions. - What changed: Added
module,exportsmap withrequire/import/defaultentries. Updatedenginesto>=18.0.0. - File:
package.json: addedmoduleandexportsfields, updatedengines.