fix: accept string IDs at the Rust/TS boundary for BYODB BigInt#477
Merged
fix: accept string IDs at the Rust/TS boundary for BYODB BigInt#477
Conversation
devwdave
approved these changes
May 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
CockroachDB uses
BIGINTprimary keys that exceed JavaScript'sNumber.MAX_SAFE_INTEGER(e.g.
1172888091194032000). Our PostgreSQL/CockroachDB storage drivers were recently fixedto return these IDs as strings instead of casting them to
Number, which silently lostprecision.
However, the Rust N-API boundary hadn't been updated to match. When the TypeScript SDK passed
a stringified ID back through the
retrievepath, serde tried to deserialize the JSON string"1172888091194032000"into a stricti64field and panicked:[Error: invalid type: string "1", expected i64]
Solution
Widen the ID types at every layer of the stack — Rust structs, TypeScript interfaces, and the
index.d.tsdeclaration — so the engine acts as a universal pass-through that accepts bothinteger IDs (SQLite, MySQL) and string IDs (PostgreSQL, CockroachDB).
Rust (
core/bindings/node/src/)types.rs—NapiRecallObjectandNapiRecallSummary:id: i64→id: Either<i64, String>, consistent with the existing pattern already used byNapiEmbeddingRowandNapiCandidateFactRowentity_fact_id/fact_id:Option<i64>→Option<Either<i64, String>>#[derive(Serialize, Deserialize)]from both structs since they no longer go througha serde round-trip
engine.rs—retrievemethod:serde_json::to_value(&results)→serde_json::from_value(...)round-tripwith a direct
into_iter().map(...)that explicitly matchesFactId::Int(n) → Either::A(n)and
FactId::String(s) → Either::B(s)fact_id_from_jsonhelper to safely extractOption<Either<i64, String>>from theopaque
serde_json::Valuesummary blobsRankedFactto the storage importTypeScript (
memori-ts/src/)types/api.ts—RecallObject,NapiRecallRow,RecallSummary:id,entity_fact_id,fact_id,entityFactId,factIdwidened fromnumbertonumber | stringcore/engine.ts:as numbercasts ons.entityFactId/s.factIdtoas number | stringnative/index.d.ts(auto-generated, updated ahead of nextnapi build):NapiRecallObject.idandNapiRecallSummary.entityFactId/factIdwidened tonumber | stringutils/utils.ts:Map<number, RecallSummary[]>→Map<number | string, RecallSummary[]>in the cloud-pathsummary grouping function
Compatibility
Either::A(i64)/number. Nobehavior change.
Either::B(String)/stringwithout truncation or panic.
idacceptingnumber | stringis a non-breaking widening forall callers.