Code generation/serialization without DOM

78 views
Skip to first unread message

CMan

unread,
Jul 22, 2025, 2:05:58 PMJul 22
to Blockly
Hi all,

For security reasons, I can't generate code on the client, and must generate it on the server. I've been trying to accomplish this by sending the serialized workspace as JSON from the client to the server, to allow it to be de-serialized on the server. From there, the server would run javascriptGenerator.workspaceToCode. However, when de-serializing the workspace on the server, DOM errors are thrown, even though the workspace is headless. 

Is this use case supported? Am I doing something wrong or am I missing something? Any and all help is appreciated. I can provide additional details if need be.

Cloudflare worker handler for codegen:
//Handler script for generating code from Blockly Workspace
import * as Blockly from 'blockly';
import { javascriptGenerator } from 'blockly/javascript';

export function generateCodeFromWorkspace(workspace) {
    if (!workspace) {
        throw new Error('Missing workspace');
    }
    let tempWorkspace;
    try {
        // Accept both JSON string and object
        const json = typeof workspace === 'string' ? JSON.parse(workspace) : workspace;
        tempWorkspace = new Blockly.Workspace();
        Blockly.serialization.workspaces.load(json, tempWorkspace);
    } catch (e) {
        //This is the part where DOM erros are thrown :(
        throw new Error('Failed to load workspace: ' + e.message);
    }
    try {
        const code = javascriptGenerator.workspaceToCode(tempWorkspace);
        return code;
    } catch (e) {
        throw new Error('Code generation failed: ' + e.message);
    }
}

Maribeth Moffatt

unread,
Jul 22, 2025, 3:17:33 PMJul 22
to Blockly
This is a supported use case, so what you're describing generally should work.

What are the errors you're getting? And can you post a sample of the workspace json?

CMan

unread,
Jul 22, 2025, 8:17:21 PMJul 22
to Blockly
I'm getting the error:Failed to load workspace: Cannot read properties of undefined (reading 'createElementNS') Failed to load workspace: Cannot read properties of undefined (readingFailed to load workspace: Cannot read properties of undefined (reading 'createElementNS') Failed to load workspace: Cannot read properties of undefined (reading 'createElementNS') Failed to load workspace: Cannot read properties of undefined (reading 'createElementNS') Failed to load workspace: Cannot read properties of undefined (reading 'createElementNS') Failed to load workspace: Cannot read properties of undefined (reading 'createElementNS') Failed to load workspace: Cannot read properties of undefined (reading 'createElementNS') Failed to load workspace: Cannot read properties of undefined (reading 'createElementNS') Failed to load workspace: Cannot read properties of undefined (reading 'createElementNS') Failed to load workspace: Cannot read properties of undefined (reading 'createElementNS')Failed to load workspace: Cannot read properties of undefined (reading 'createElementNS'). "Failed to load workspace" is a part of my error message. Here's the JSON on one of the requests:
Failed to load workspace: Cannot read properties of undefined (reading 'createElementNS'){
  "blocks": {
    "languageVersion": 0,
    "blocks": [
      {
        "type": "controls_if",
        "id": "[fb013R.j@IsIPVvj4WX",
        "x": 251,
        "y": 296,
        "inputs": {
          "IF0": {
            "block": {
              "type": "logic_compare",
              "id": "1(erB.:Ho@GYf2$d9I5b",
              "fields": {
                "OP": "EQ"
              },
              "inputs": {
                "A": {
                  "block": {
                    "type": "math_number",
                    "id": "rhkj[J8K}N8wu^XH.MEP",
                    "fields": {
                      "NUM": 123
                    }
                  }
                },
                "B": {
                  "block": {
                    "type": "math_number",
                    "id": "tXs]q1{92*-2C75vG*zl",
                    "fields": {
                      "NUM": 123
                    }
                  }
                }
              }
            }
          }
        }
      }
    ]
  }
}Failed to load workspace: Cannot read properties of undefined (reading 'createElementNSFailed to load workspace: Cannot read properties of undefined (reading 'crea

CMan

unread,
Jul 23, 2025, 11:42:31 AMJul 23
to Blockly
Not sure why the error pasted about a million times, apologies.

Maribeth Moffatt

unread,
Jul 23, 2025, 3:34:46 PMJul 23
to Blockly
Can you provide the stack trace for the error, please? You can get it by throwing `e` instead of swallowing the error and throwing your own.

Mark Friedman

unread,
Jul 23, 2025, 4:10:28 PMJul 23
to blo...@googlegroups.com
FWIW, I took CMan's exact code and workspace JSON and ran it using node and it worked just fine.  So there might be something else going on in his environment or the context in which his code is running that might be an issue.  

CMan, Any chance that you can provide us with the context in which the code you show is running?  Maybe a GitHub repo or gist link, a CodePen link, or something like that.

The following was my code:

import * as Blockly from 'blockly';
import {javascriptGenerator} from 'blockly/javascript';

function generateCodeFromWorkspace(workspace) {
  if (!workspace) {
    throw new Error('Missing workspace');
  }
  let tempWorkspace;
  try {
    // Accept both JSON string and object
    const json = typeof workspace === 'string' ? JSON.parse(workspace) : workspace;
    tempWorkspace = new Blockly.Workspace();
    Blockly.serialization.workspaces.load(json, tempWorkspace);
  } catch (e) {
    //This is the part where DOM erros are thrown :(
    throw new Error('Failed to load workspace: ' + e.message);
  }
  try {
    const code = javascriptGenerator.workspaceToCode(tempWorkspace);
    return code;
  } catch (e) {
    throw new Error('Code generation failed: ' + e.message);
  }
}

const workspaceObj = {
const code = generateCodeFromWorkspace(workspaceObj);

console.log(code);


-Mark


--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/blockly/a65e37a7-1947-4658-99ee-50a4cc642199n%40googlegroups.com.

CMan

unread,
Jul 24, 2025, 11:31:02 AMJul 24
to Blockly
Maribeth-here's all of the error details, including the stack
{
  "source": {
    "message": "Cannot read properties of undefined (reading 'createElementNS')",
    "exception": {
      "stack": "    at $.createElement$$module$build$src$core$utils$xml (index.js:706:58)\n    at blockToDom$$module$build$src$core$xml (index.js:2187:21)\n    at blockToDomWithXY$$module$build$src$core$xml (index.js:2166:13)\n    at new BlockCreate$$module$build$src$core$events$events_block_create (index.js:8133:71)\n    at appendInternal$$module$build$src$core$serialization$blocks (index.js:1314:101)\n    at append$$module$build$src$core$serialization$blocks (index.js:1294:13)\n    at BlockSerializer$$module$build$src$core$serialization$blocks.load (index.js:6678:30)\n    at Object.load$$module$build$src$core$serialization$workspaces [as load] (index.js:3381:34)\n    at generateCodeFromWorkspace (index.js:25777:30)\n    at Object.fetch (index.js:25868:20)",
      "name": "TypeError",
      "message": "Cannot read properties of undefined (reading 'createElementNS')",
      "timestamp": 1753319571855
    },
    "$cloudflare": {
      "$metadata": {
        "id": "01K0WZV0CF3S3CVJ9RT25KYWFE",
        "type": "cf-worker",
        "error": "Cannot read properties of undefined (reading 'createElementNS')",
        "messagePattern": "Cannot read properties of undefined (reading 'createElementNS')"
      }
    }
  },
  "dataset": "",
  "timestamp": "2025-07-24T01:12:51.855Z",
  "$workers": {
    "event": {
      "request": {
        "url": "https://api.snapbot.top/commands/codegen",
        "method": "POST",
        "path": "/commands/codegen"
      }
    },
    "outcome": "exception",
    "scriptName": "snapbot-core",
    "eventType": "fetch",
    "executionModel": "stateless",
    "scriptVersion": {
      "id": "493d937c-7aa0-4941-b635-10106f7d37f5"
    },
    "truncated": false,
    "requestId": "963f8d3bff3ae14b"
  },
  "$metadata": {
    "id": "01K0WZV0CF3S3CVJ9RT25KYWFE",
    "requestId": "963f8d3bff3ae14b",
    "trigger": "POST /commands/codegen",
    "service": "snapbot-core",
    "level": "error",
    "statusCode": 500,
    "error": "Cannot read properties of undefined (reading 'createElementNS')",
    "message": "Cannot read properties of undefined (reading 'createElementNS')",
    "url": redacted,
    "account": redacted,
    "provider": "cloudflare",
    "type": "cf-worker",
    "fingerprint":  redacted ,
    "origin": "fetch",
    "messageTemplate": "Cannot read properties of undefined (reading 'createElementNS')",
    "now": null
  },
  "links": []

CMan

unread,
Jul 24, 2025, 11:31:03 AMJul 24
to Blockly
Mark,
Presently my code is running on a Cloudflare worker. Your code on a CF worker resulted in the same error unfortunately. Cloudflare workers do not support DOM APIs.
I'm not sure what other code would be relevant, but here's my truncated index.js file which handles the network request and calls the exported function from the code I presented earlier in this thread:
//import all of the required modules;
import { generateCodeFromWorkspace } from './code-gen/code-gen-handle.js';

export default {
    async fetch(request, env, ctx) {
        if (url.pathname === '/commands/codegen' && request.method === 'POST') {
            const ws = await request.json();
            const code = generateCodeFromWorkspace(ws);
            return new Response(code, { status: 200, headers: { 'Access-Control-Allow-Origin': '*' } });
        }
        return new Response('Unknown URL', { status: 404 });
    },
};
Best,
-CMan

Maribeth Moffatt

unread,
Jul 24, 2025, 12:53:02 PMJul 24
to Blockly
Thanks for the additional information.

When you deserialize into a workspace, we fire block create events, which then serializes a block into XML, which requires `document.createElementNS` call which is failing on your worker. Typically this works in node because blockly requires jsdom which supplies the document object for this case. I don't know exactly how your worker is set up but from some cursory research it seems like using jsdom isn't feasible. There are an increasing number of ways to run javascript code in environments that don't support dom, so it's probably worth our team rethinking this approach to events and XML. I've filed an issue for this, but it's not something we'll be able to look at in the immediate future.

Can you try calling `Blockly.Events.disable()` before you deserialize the workspace? That should prevent the event that calls the xml serializer from being created in the first place and hopefully prevents this. Blockly does rely on the events internally in a number of places, but I think since all you're doing with this workspace is converting it to code, it should be fine.

Best,
Maribeth

CMan

unread,
Jul 24, 2025, 4:01:13 PMJul 24
to Blockly
Thank you for the detailed explanation. Calling Blockly.Events.disable() before deserializing fixes my issue entirely, and my code runs successfully as intended on my worker.

Thanks again,
-CMan
Reply all
Reply to author
Forward
0 new messages