Hi. im just wondering if this is a intended design or a bug. im building a sqlite library as a test project using napi-rs and rusqlite and for the most part it works fine on my end. there is this section of my code that uses ScopedGenerator to iterate rows and convert them into JS object. i've created another method in my RowIterator called toJSON so that i can integrate it with JSON.stringify according to MDN docs.
If the value has a toJSON() method, it's responsible to define what data will be serialized. Instead of the object being serialized, the value returned by the toJSON() method when called will be serialized. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
here is the code that i have implemented
RowIterator
#[napi(iterator)]
pub struct RowIterator<'a> {
pub(crate) rows: rusqlite::Rows<'a>,
}
#[napi]
impl<'a> ScopedGenerator<'a> for RowIterator<'a> {
type Next = ();
type Return = ();
type Yield = Unknown<'a>;
fn next(&mut self, env: &'a napi::Env, _value: Option<Self::Next>) -> Option<Self::Yield> {
let next_row = self.rows.next().ok().unwrap_or_default()?;
let mut value_map = HashMap::new();
let columns = next_row.as_ref().columns();
for column in &columns {
let raw_value = next_row.get_ref(column.name()).ok()?;
value_map.insert(column.name(), ValueRef(raw_value));
}
env.to_js_value(&value_map).ok()
}
}
#[napi]
impl RowIterator<'_> {
#[napi(js_name = "toJSON")]
pub fn to_json(&mut self, env: Env) -> napi::Result<Unknown<'_>> {
let mut rows = vec![];
while let Some(row) = self.rows.next().map_err(NodeRusqliteError::from)? {
let mut value_map = HashMap::new();
let columns = row.as_ref().columns();
for column in columns {
let raw_value = row
.get_ref(column.name())
.map_err(NodeRusqliteError::from)?;
let value = serde_json::to_value(ValueRef(raw_value))
.map_err(|err| rusqlite::Error::ToSqlConversionFailure(Box::new(err)))
.map_err(NodeRusqliteError::from)?;
value_map.insert(column.name().to_string(), value);
}
rows.push(value_map);
}
env.to_js_value(&rows)
}
}
when i run napi build (under bun run build:debug)
here is the generated typescript definition
binding.d.ts
/**
* This type extends JavaScript's `Iterator`, and so has the iterator helper
* methods. It may extend the upcoming TypeScript `Iterator` class in the future.
*
* @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Iterator#iterator_helper_methods
* @see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html#iterator-helper-methods
*/
export declare class RowIterator extends Iterator<unknown, void, void> {
toJSON(): unknown
next(value?: void): IteratorResult<unknown, void>
}
here is the code that i have as a basic test.
test.ts
import { Connection } from "../bindings/binding.js";
let conn = Connection.openInMemory();
conn.prepare("select ? as name",(statement) => {
const result = statement.query(["john doe"])
console.log("array result -> ", Array.from(result))
console.log("json.stringify result ->", JSON.stringify(result))
console.log("toJSON result -> ", result.toJSON())
})
and here is the output for bun runtime.
array result -> [
{
name: "john doe",
}
]
json.stringify result -> {}
4 |
5 | conn.prepare("select ? as name",(statement) => {
6 | const result = statement.query(["john doe"])
7 | console.log("array result -> ", Array.from(result))
8 | console.log("json.stringify result ->", JSON.stringify(result))
9 | console.log("toJSON result -> ", result.toJSON())
^
TypeError: result.toJSON is not a function. (In 'result.toJSON()', 'result.toJSON' is undefined)
at <anonymous> (/home/robeckk/opensource/node-rusqlite/examples/test.ts:9:43)
at /home/robeckk/opensource/node-rusqlite/examples/test.ts:5:6
at loadAndEvaluateModule (2:1)
Bun v1.3.11 (Linux x64)
and here is the output for nodejs
(node:184965) [MODULE_TYPELESS_PACKAGE_JSON] Warning: Module type of file:///home/robeckk/opensource/node-rusqlite/examples/test.ts is not specified and it doesn't parse as CommonJS.
Reparsing as ES module because module syntax was detected. This incurs a performance overhead.
To eliminate this warning, add "type": "module" to /home/robeckk/opensource/node-rusqlite/package.json.
(Use `node --trace-warnings ...` to show where the warning was created)
array result -> [ { name: 'john doe' } ]
json.stringify result -> {}
file:///home/robeckk/opensource/node-rusqlite/examples/test.ts:9
console.log("toJSON result -> ", result.toJSON())
^
TypeError: result.toJSON is not a function
at file:///home/robeckk/opensource/node-rusqlite/examples/test.ts:9:43
at file:///home/robeckk/opensource/node-rusqlite/examples/test.ts:5:6
at ModuleJob.run (node:internal/modules/esm/module_job:413:25)
at process.processTicksAndRejections (node:internal/process/task_queues:103:5)
at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:660:26)
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5)
Node.js v24.13.0
Hi. im just wondering if this is a intended design or a bug. im building a sqlite library as a test project using
napi-rsandrusqliteand for the most part it works fine on my end. there is this section of my code that uses ScopedGenerator to iterate rows and convert them into JS object. i've created another method in myRowIteratorcalledtoJSONso that i can integrate it withJSON.stringifyaccording to MDN docs.here is the code that i have implemented
RowIterator
when i run
napi build(under bun run build:debug)here is the generated typescript definition
binding.d.ts
here is the code that i have as a basic test.
test.ts
and here is the output for bun runtime.
and here is the output for nodejs