Git Product home page Git Product logo

brr's People

Contributors

armael avatar bclement-ocp avatar dboris avatar dbuenzli avatar dinosaure avatar emiletrotignon avatar hannesm avatar hhugo avatar joprice avatar mooreryan avatar v-gb avatar voodoos avatar vouillon avatar zapashcanon avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

brr's Issues

`Fut.to_promise` doesn't handle rejected futures correctly

Given a Fut.t whose underlying promise is rejected, Fut.to_promise creates a promise that never resolves, which then prevents whatever surrounding code from cleaning up and displaying said error.

Example :

let test str to_promise =
  ignore (
      Jv.Promise.then'
        (to_promise
           ~ok:Fun.id
           (Fut.bind
              (Fut.return "qwe")
              failwith))
        (fun x -> Brr.Console.log [str; "ok"; x]; Jv.Promise.resolve Jv.undefined)
        (fun x -> Brr.Console.log [str; "error"; x]; Jv.Promise.resolve Jv.undefined))

let () = test "Fut.to_promise" Fut.to_promise
(* Bad. prints neither "ok", nor "error".  Instead you get a console warning saying "uncaught in promise: ..." *)

let fut_to_promise
    : ok:('a -> Jv.t) -> 'a Fut.or_error -> Jv.Promise.t
  = fun ~ok f ->
  Jv.Promise.bind (Jv.get (Obj.magic f) "fut")
    (function
     | Ok v -> Jv.Promise.resolve (ok v)
     | Error e -> Jv.Promise.reject e)

let () = test "custom Fut.to_promise" fut_to_promise
(* Good. prints in the "error" branch *)

The implementation of the module says it's an invariant that the promise of a future is never rejected, but the invariant isn't maintained in the bind function (and map). Not sure if that's an implementation or documentation bug. I think it'd be more useful to change the implementation as above, rather than changing the documentation.

Illegal invocation using alert function

Hey, so I get a kind of weird error. When calling this function

let alert msg =
  match Jv.find Jv.global "alert" with
  | None -> prerr_endline "no alert"
  | Some alert -> ignore @@ Jv.apply alert Jv.[| of_string msg |]

I get an illegal invocation error.

However, this one is fine...almost the same, except for a print function stuck in there. (No idea why the print function helps...)

let alert msg =
  match Jv.find Jv.global "alert" with
  | None -> prerr_endline "no alert"
  | Some alert ->
      (* Needs this print to work. *)
      prerr_endline "yay!";
      ignore @@ Jv.apply alert Jv.[| of_string msg |]

This also works fine.

let alert msg =
  match Jv.find Jv.global "window" with
  | None -> prerr_endline "no window"
  | Some window -> ignore @@ Jv.call window "alert" Jv.[| of_string msg |]

All of them work fine in dev mode, but when in release mode, the error happens.

I'm using js_of_ocaml 4.0.0 and brr 0.0.3.

Have you run in to this sort of thing, or am I just doing something wrong?

Add namespace support

Hi,

I use Brr.El.v to draw some SVG images. However when I try to embed them directly in my HTML they do not show, because the root svg-tag is missing the 'http://www.w3.org/2000/svg' namespace.

Would you accept a change to add namespaces? And what would the best solution be: changing the type At.t from type t = name * Jstr.t to type t = ns Option.t name * Jstr.t? Or even

type t =
  | Attr name * Jstr.t
  | NSAttr ns * name * Jstr.t

Best regards,
--Martin

More FFI discipline

For upcoming effect support

  1. Replace as much as possible the external primitives in Jv and Jstr by corresponding calls to Jsoo_runtime.Js. Not essential, but in general safer and more robust to changes. This is now tracked by #45.

  2. Review the callback stuff in brr we need to use the callback stuff provided in Jsoo_runtime.Js. This is essential and not done at the moment.

  3. Review and update the cookbook bits on callbacks and OCaml function export to JavaScript.

Question: Unbound module issues for `todomvc.ml` in ocaml-lsp (merlin)

I'm trying to get ocaml-lsp to work on todomvc.ml however ocaml-lsp (merlin I guess) is complaining about unbound modules Brr etc. I guess I have to compile some stuff for ocaml-lsp to start picking things up.

I tried ocamlbuild -use-ocamlfind test/todomvc.byte -- this command works but that does not help with merlin issues.

The .merlin has test/** but it does not get picked up by ocaml-lsp.

Parentheses are not percent encoded

I'd like to report what I think is a bug in the URL (percent) encoding of Uri.with_uri

Consider the following minimal example:

open Brr
let () =
  let location = Window.location G.window in
  let msg =
    Jstr.to_string
    @@ Uri.to_jstr
    @@ Result.get_ok
    @@ Uri.with_uri ~path:(Jstr.v "/some/path/that/includes/?(parens)") location in
  El.set_children (Document.body G.document) El.[txt' msg]

The output of this is:

file:///some/path/that/includes/%3F(parens)

when I think it should be

file:///some/path/that/includes/%3F%28parens%29

Namespace and the svg story.

@schutm provided a nice module to generate SVG contents. The only drawback is that one has to use Obj.magic to get said content inserted into a HTML page. This is because there is currently no way to create values of type El.t with a namespace specification, as required to create SVG elements.

After seeing #17 and the arguments for not including it, how about adding a El.v_ns function? That way, it would be possible to have a brr_svg or a brr_mathml library that do not force Obj.magic on their users, with no risk of performance regression.

javascript record

Greetings, I'm in the process of writing a binding and the webidl has defined a record : typedef record<DOMString, boolean> MyName - giving, in plain javascript : #{ "a": true, "b": false } etc - is there any simple way of providing the construct with Jv?

return type of target_to_jv

The name of the function target_to_jv makes me think it would return Jv.t, but it returns 'a. I was surprised to find my code compiling without needing to apply my own type coercion to a custom target type. Is this intentional?

error on console evaluation

I'm getting this error when I hit enter to submit a line for evaluation in the console:

ocaml_console.js:1788 Uncaught TypeError: s.toUtf16 is not a function
    at caml_jsstring_of_string (ocaml_console.js:1788)
    at caml_js_object (ocaml_console.js:2473)
    at set$1 (ocaml_console.js:3826)
    at caml_call2 (ocaml_console.js:2791)
    at HTMLTextAreaElement.<anonymous> (ocaml_console.js:4151)

I do see the definition of MlBytes.prototype.toUtf16 in ocaml_console.js.

Jstr.concat produce wrong result

I have something strange with Jstr.concat:

this code:

    let message = Jstr.(concat [ v "A"; v "B"; v "C"]) in
    Console.(log [message]);

produce this output in the console : AB,C. The expected result is ABC (without coma).

I have taken a look in the code, and except the call to external caml_list_of_js_array I do not see anything wrong. The coma in the second place seems indicate a wrong parameter in the call, but I cannot see where this definition comes from.

The parseInt FFI example is slightly confusing

Hello!

I found the FFI example documented here slightly confusing: https://erratique.ch/software/brr/doc/ffi_cookbook.html#fun

To my knowledge, parseInt won't ever throw, but rather return NaN:

$ node
Welcome to Node.js v16.20.0.
Type ".help" for more information.
> parseInt("wat")
NaN

See what I've done below:

BEFORE

(* NOTE: matching on the exception variant will never actually occur in this example. Or will it?? *)
let parse_int' = Jv.get Jv.global "parseInt"
let parse_int ?radix s =
  let r = Jv.of_option ~none:Jv.undefined Jv.of_int radix in
  match Jv.apply parse_int' Jv.[| of_jstr s; r |] with
  | exception Jv.Error e -> Error e
  | v -> Ok (Jv.to_int v)

AFTER

let parse_int' ?radix s : (int, Jv.Error.t) result =
  let r = Jv.of_option ~none:Jv.undefined Jv.of_int radix in
  match Jv.apply (Jv.get Jv.global "parseInt") Jv.[| of_jstr s; r |] with
  | v -> (
      let v' = Jv.to_float v in
      match Float.is_nan v' with
      | true -> Error (Jv.Error.v (Jstr.v "Reject NaN!"))
      | false -> Ok (int_of_float v'))
;;

Maybe it'd be worth it to emphasize that we can deal with 2 types of functions in JS: ones that fail, and ones that silently do the wrong thing and handle them accordingly?

Here's an example of a builtin function that trows:

> decodeURI('%')
Uncaught URIError: URI malformed
    at decodeURI (<anonymous>)

What do you think?

Base64 decoding

I just spent some time banging my head against the wall due to the following behavior:

module B64 = Base64
open Brr

let () =
  let encoded = "AAAAAAAAACAAAAAcP9s2eg+QlrxAWRMzMzMzMwAAAAAAAAAAAAAAAA==" in
  let dec_js = Jstr.to_string @@ Result.get_ok @@ Base64.decode (Jstr.of_string encoded) in
  let dec_caml = B64.decode_exn encoded in
  Printf.eprintf "got      : %s\n" (String.escaped dec_js);
  Printf.eprintf "expected : %s\n" (String.escaped dec_caml);
  ()
got     : \000\000\000\000\000\000\000 \000\000\000\028?\195\1556z\015\194\144\194\150\194\188@Y\01933333\000\000\000\000\000\000\000\000\000\000\000\000
expected: \000\000\000\000\000\000\000 \000\000\000\028?\2196z\015\144\150\188@Y\01933333\000\000\000\000\000\000\000\000\000\000\000\000

where Base64 is the ocaml implementation of Base64 (from the opam library of the same name).
Now, coming back to the documentation of Brr.Base64.decode, I can see that it says that the string returned is a binary string, which I assume means it is wrong to call Jstr.to_string on it? But then I couldn't find what to do instead.

parentNode property ?

Hello, I'm opening the issue because I did not found how to I can declare/access parentNode the property.

The type of the property should be Brr.El.t Brr.El.Prop.t, but there is no constructor with such signature (only the basic types are provided). Maybe is this causing cyclic references between El and El.Prop modules ? Is there an easy way to access to such properties ?

clip

Is there any chance the clip signature could be updated

from val clip : ?fill_rule:Fill_rule.t -> t -> Path.t -> unit

to val clip : ?fill_rule:Fill_rule.t -> ?path:Path.t -> t -> unit

in order to comply with https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip?

-> I have a scenario where I don't know, but wish to retain, the previous clipping region

OCaml console improvements

A few things that could be improved in the OCaml console

  • Error reporting highlighting (note that #use does a good job at this as per built-in toplevel support)
  • WYSWYG history management
  • Completion (upstream ?)
  • Toplevel printer story (this should likely be solved upstream)
  • A worfklow for app specific toplevel init ?
  • See if we can extend #use to act over URLs.

`Uri`: `Params.of_jstr` can't correctly operate on the output of `Uri.query` or `Uri.fragment`

As pointed out in #47 (comment), URLs and URL encoding is a zoo, so apologies in advance if I'm misunderstanding something here.

Background: the Brr.Uri API claims (emphasis added):

Params.t values represent key-value parameters stored in strings as "k0=v0&k1=v1...". They can be constructed from any Jstr.t. In particular it means they can be used with an URI fragment or query.

However, the output of Uri.query (or Uri.fragment) has already been percent-decoded. Using Params.of_jstr then percent-decodes a second time. That results in the following behaviour:

let double_decode x =
  Jstr.append (Jstr.v "https://example.com?x=") x
  |> Uri.of_jstr |> Result.get_ok
  |> Uri.query |> Uri.Params.of_jstr
  |> Uri.Params.find (Jstr.v "x") |> Option.get

let _ = [
  double_decode (Jstr.v "abc")   (* "abc", as expected *) ;
  double_decode (Jstr.v "%25")   (* "%", as expected *)   ;
  double_decode (Jstr.v "%2525") (* "%" !!! *)            ;
]

For comparison, by using JavaScript directly:

const no_double_decode = x => new URL(`https://example.com?x=${x}`).searchParams.get("x");

[
  no_double_decode("abc"),  // "abc"
  no_double_decode("%25"),  // "%"
  no_double_decode("%2525") // "%25"
];

Taking a URL, grabbing its query parameter string, and using that to make a Params is directly encouraged by the API, but the last case here is wrong with Brr.

I don't see any way to get the correct behaviour either. Some things that don't work:

  • Pass the raw query string to Params.of_jstr. This would work, but the API doesn't expose the raw query string.
  • Re-encode the output of Uri.query. Uri.query uses Uri.decode to percent-decode the result, which means it leaves some %-escapes in the output. Re-encoding would however escape the % symbol in those escapes, rather than only percent-encoding the the characters that were percent-decoded (in other words, encode is not the inverse of decode).

The problem here is not that Params.of_jstr is wrong, but rather that Brr.Uri is trying to be overly helpful by returning percent-decoded strings. By https://url.spec.whatwg.org/#concept-urlencoded-parser parsing the query string means splitting on &, then on =, then by https://url.spec.whatwg.org/#percent-decode percent-decoding the resulting key/value pairs. But that needs to performed on the raw URL, not the decoded URL.

Realistically, this isn't the only case where the semantics of a URI component can no longer be fully captured after percent-decoding, so if I'm understanding correctly, adding raw (non-decoded) accessors to Uri is inevitable. That aside, having a Uri.parameters : Uri.t -> Params.t directly would also be nice.

Would Ctypes-esque function creation for JavaScript FFI be possible?

This is a question and possibly a feature request because I don't know if it's actually possible to do. I tried looking into it myself and got nowhere so I'm posting it here in case someone smarter than me can figure out if the idea's viable or not.

The idea was to create some kind of FFI function creation method for Brr similar to how Ctypes allows you to programmatically define foreign functions entirely within OCaml. To explain, with Ctypes you can define function signatures in a form like string @-> int @-> returning void and end up with a callable function with the appropriate number of arguments and correct function signature, handling the conversion of OCaml types for each argument as needed.

If possible, something like that could be useful in Brr as a cleaner alternative to writing wrapper functions that do things like Jv.call js_fun [| Jv.of_string "foo"; Jv.of_int 42; Jv.of_string "bar" |] by hand. Instead, you'd do something like my_fun = fn js_fun string @-> int @-> string and get a curried function that has the appropriate conversions in place, allowing you to then write my_fun "foo" 42 "bar".

This may be impossible to implement; I tried looking into how Ctypes works to see if it could be applied to Brr, but it uses powerful dark magics to do what it does, and I got completely lost trying to make sense of it. I really wanted to make it happen because the way Ctypes works is amazing; Melange and Bucklescript make it easy to do something similar by using external to bind JS functions, so I thought it'd be nice to give Brr something similar by using a Ctypes-esque solution, but figuring out how Ctypes does its magic is beyond me.

note and mutable data structures

I'm interested in measuring the performance of the todomvc example versus implementations in other languages. My first thought when looking over the code was whether the use of immutable data structures (ocaml's list) would become an issue, especially when removing an element at an arbitrary index. I saw this before when running the todomvc benchmark against a version written using bucklescript-tea. Is it possible for Note to work with mutable data, or maybe the approach would be to use a persistent data structure with better asymptotics?

How to export a value?

I am writing a VScode extension using the Brr library.

I don't have a minimal reproducible example but here is my snippet:

 let activate =
    Jv.callback ~arity:1 (fun context ->
        Console.log "Congratulations, your extension for kalkulu is now active!";
        let callback () = Vscode.Window.show "Hello world!" in
        let disposable =
          Vscode.Command.register ~command:"extension.helloWorld" ~callback
        in
        Vscode.Extension_context.subscribe context disposable)
  in
  (* Js.export "activate" @@ activate *)
  Jv.set Jv.global "activate" activate

If I uncomment the Js.export line, it works, but the Jv.set does not.

I am not sure to understand what is going on. I observe the compiled JS is a little bit different. In particular Js.export uses "jsoo_exports` value.

I have tried to read the FFI cookbook, but I did not find an explanation. Is this expected? Why?

Update developer tool console to manifest v3

We don't do it yet, because in Firefox it still seems to be behind flags. In Chrome an error is reported but it's not fatal. Here's a patch.

diff --git a/src/console/manifest.json b/src/console/manifest.json
index 33823fc..456eb29 100644
--- a/src/console/manifest.json
+++ b/src/console/manifest.json
@@ -1,12 +1,13 @@
 {
-  "manifest_version": 2,
+  "manifest_version": 3,
   "name": "OCaml console",
-  "version": "0.0.2",
+  "version": "0.0.4",
   "author": "The brr programmers",
   "description":
   "OCaml console developer tools panel. Connects to an OCaml toplevel (REPL) provided by the inspected web page.",
   "homepage_url": "https://erratique.ch/software/brr",
   "icons": { "128": "ocaml.png" },
-  "permissions": ["<all_urls>", "storage", "management"],
+  "permissions": ["storage", "management"],
+  "host_permissions": ["<all_urls>"],
   "devtools_page": "devtools.html"
 }

Rely more on `Jsoo_runtime` rather than `external`

Quite a few of the external definitions in the Jv and Jstr modules could be replaced by definitions of the new Jsoo_runtimemodule. We should do that, it's safer.

We just need to make sure this doesn't introduce any performance regression since some of those are declared as external in the .mli files aswell (I never remember if it has an impact).

In default dune builds, the brr library raises an exception in web workers

I think without aggressive dead code elimination (which is the case with --profile=dev dune builds) there is something that accesses the global document object, and this causes an exception when the code is run in a web worker context.

With --profile=release things seem to be OK.

Maybe worth some comment in the documentation, though it would be really nice if things worked smoothly in dev mode also.

Incorrect blob creation API

The function to create blobs from Jstr.t seems to be incorrect:

brr/src/brr.ml

Line 753 in 3d857a6

let of_jstr ?(init = Jv.undefined) s = Jv.new' blob [| Jv.of_jstr s; init |]

According to https://w3c.github.io/FileAPI/#blob-section, the constructor for a blob is:

  constructor(optional sequence<BlobPart> blobParts,
              optional BlobPropertyBag options = {});

i.e the first argument should be a sequence.

When trying to create a blob, I get a browser type error when using the function provided by Brr.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.