automerge / automerge-classic

A JSON-like data structure (a CRDT) that can be modified concurrently by different users, and merged again automatically.
http://automerge.org/
MIT License
14.75k stars 467 forks source link

Uncaught TypeError: Unsupported type of value: function #503

Closed michaelpalumbo closed 2 years ago

michaelpalumbo commented 2 years ago

Hi there. I'm getting a strange behavior when I pass variables into the automerge.change().

context.js:53 Uncaught TypeError: Unsupported type of value: function
    at Context.getValueDescription (context.js:53:13)
    at Context.setValue (context.js:302:32)
    at Context.createNestedObjects (context.js:268:33)
    at Context.setValue (context.js:299:19)
    at Context.createNestedObjects (context.js:268:33)
    at Context.setValue (context.js:299:19)
    at Context.createNestedObjects (context.js:268:33)
    at Context.setValue (context.js:299:19)
    at eval (context.js:342:33)
    at Context.applyAtPath (context.js:317:5)

My code is:

let automergeMsg = 'add node';
let nodeID = min_eec29508-1b05-4f71-8d1e-063fc73d1baa;
let newNode = {
        "genish": {
            "domain": "common",
            "box_expr": "generic",
            "category": "comparison",
            "inputs": {
                "value1": {
                    "label": "input value 1",
                    "optional": true,
                    "type": {
                        "name": "vector",
                        "params": [
                            "float"
                        ]
                    },
                    "digest": "input value 1",
                    "name": "value1",
                    "default": "0"
                },
                "value2": {
                    "label": "input value 2",
                    "optional": true,
                    "type": {
                        "name": "vector",
                        "params": [
                            "float"
                        ]
                    },
                    "digest": "input value 2",
                    "name": "value2",
                    "default": "0"
                }
            },
            "op": "min",
            "arguments": [
                "value1",
                "value2"
            ],
            "constructors": [
                {
                    "inlets": [
                        "value1"
                    ],
                    "arguments": [
                        "value2"
                    ]
                },
                {
                    "inlets": [
                        "value1",
                        "value2"
                    ],
                    "arguments": {}
                }
            ],
            "description": "The minimum of the inputs",
            "expr_type": "expr_type_coerce",
            "digest": "The minimum of the inputs",
            "seealso": [
                "clamp",
                "clip",
                "max"
            ],
            "outputs": [
                {
                    "name": "out1",
                    "label": "min(in1,in2)"
                }
            ],
            "stateful": false,
            "expr_outputs": "generic",
            "attributes": {},
            "aliases": [
                "minimum"
            ],
            "has_constant_expr": true
        },
        "threeProps": {
            "uuid": "eec29508-1b05-4f71-8d1e-063fc73d1baa",
            "id": 521,
            "op": "min",
            "position": {
                "x": 1.716728315827324,
                "y": -4.854380011130265,
                "z": 0.15000000596046448
            },
            "rotation": {
                "isEuler": true,
                "_x": 0,
                "_y": 0,
                "_z": 0,
                "_order": "XYZ"
            },
            "scale": {
                "x": 1,
                "y": 1,
                "z": 1
            }
        }
    }

//....
doc1 = Automerge.change(doc1, automergeMsg, doc => {
      doc.scene.nodes[nodeID] = newNode
})
ept commented 2 years ago

Hi @michaelpalumbo, the only problem I can find with this code is that the definition of nodeID should presumably be a string literal. If I add the missing quotation marks and initialise doc1 = Automerge.from({scene: {nodes: {}}}) then this code works fine for me and does not throw any exception. Could you please post a complete example that reproduces the exception?

michaelpalumbo commented 2 years ago

@ept ah I'm sorry that was a typo when I wrote the github issue, i pasted it in and forgot to include the quotation marks. yes, the nodeID is a string literal.

I'm using threeJS, and I'd like to store elements instanced to the scene graph in an automerge document. After some experimentation I learned that threeJS stores a function in the rotation object, and it seems that is what automerge is not agreeing with. For now, I can just filter out that function, as I only need the euler angles themselves in order to rebuild the scene on load or on another client.

ept commented 2 years ago

Okay, filtering out the function is probably the way to go. As far as I know there is no sensible way of serialising a JS function (even if you get its source, it wouldn't include the context in which it is defined, e.g. any other functions it may call) so I don't see any way of supporting functions within Automerge documents.