
How do I convert a exn to JSError.t?
Doesn’t look like there’s a built-in conversion for that in particular, but I think the reject argument for Promise.make is polymorphic, so it should be able to take anything in. Can you take a snapshot of the code without the popover?
Ended up with some custom promise bindings, I now have:
@send
external thenResolve: (promise<'data>, 'data => unit) => promise<'data> = "then"
@send
external catchJSError: (promise<'data>, JsError.t => unit) => promise<'data> = "catch"
@send
external finally: (promise<'data>, unit => unit) => unit = "finally"
let withKaplayContext = (testFn: Context.t => promise<unit>): promise<unit> => {
let k = Context.kaplay(
~initOptions={
width: 160,
height: 160,
global: false,
background: "#000000",
scale: 1.,
crisp: true,
},
)
// Load assets ...
Promise.make((resolve, reject) => {
// https://v4000.kaplayjs.com/docs/api/ctx/onError/
k->Context.onError((error: JsError.t) => {
k->Context.quit
reject(error)
})
k->Context.onLoad(() => {
testFn(k)
->thenResolve(resolve)
->catchJSError(reject)
->finally(
() => {
k->Context.quit
},
)
->ignore
})
})
}
Ended up going with some custom bindings.
I guess one question would be if Context.onError does take a JSError.t?
@tsnobip what do you think?
@nojaf, well the issue comes from the fact there’s no silver bullets when it comes to know if a given variable is a JS error! So you can either be defensive with it and use any kind of knowledge about what you expect to catch to test if it indeed has the shape you’d like, or just trust the system and state that it’s indeed a JS error as you did!
Unfortunately there’s no easy answer, testing if the variable is of instance Error doesn’t always work for example.
Maybe JSExn.t would work? That has a built-in conversion from exn, at least.
Yeah the other solution would be to make onError accept JsExn.t.
Ah, a third option.
The underlying problem is that, within a Promise.make call, reject needs to be called on items of the same type. We can’t cast exn to JsError.t, but we can convert JsError.t to JSExn.t:
k->Context.onError((error: JsError.t) => {
k->Context.quit
reject(JsError.toJsExn(error))
})
k->Context.onLoad(() => {
testFn(k)
->thenResolve(resolve)
->catchJSError(err => reject(JsError.toJsExn(err)))
->finally(
() => {
k->Context.quit
},
)
->ignore
})
Wrapping the errors into exceptions like this would then let us mix in exn via JsExn.fromException
So, withKaplayContext is used in a vitest unit test.
So, I want to avoid exn all together really, it is not the relevant thing to have here.
// https://v4000.kaplayjs.com/docs/api/ctx/onError/
onError(action: (err: Error) => void): KEventController
uses Error
And for a rejecting promise in a vitest test, it might come from toHaveBeenCalled
which is all an actual Error object.
Are we not missing something in the StdLib like:
let tryConvert: (exn) => JsError.t = %raw`function (exn) { return Error.isError(exn) ? exn : undefined;`
I agree it would be cool to have bindings for Error.isError, but the support is quite limited, it was rolled out from TC39 in May of this year only and is still not supported by Safari and only by Node 24 if I’m not mistaken, might be a bit early stage to depend on it for now, what do you think @nojaf?
Ah yes, it does seem a bit too experimental.