From 0b3e254065d0b62d1033d2f1c933fa8846864094 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Tue, 20 Feb 2024 22:10:42 +0100 Subject: [PATCH 01/25] upgrade to latest jscad modeling and io --- apps/jscad-web/package.json | 4 +- package-lock.json | 432 ++++++++++++++++++++++++++++++++++-- 2 files changed, 412 insertions(+), 24 deletions(-) diff --git a/apps/jscad-web/package.json b/apps/jscad-web/package.json index e3a6db7..97b85e8 100644 --- a/apps/jscad-web/package.json +++ b/apps/jscad-web/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@codemirror/lang-javascript": "^6.1.9", - "@jscad/io": "2.4.4", - "@jscad/modeling": "2.12.0", + "@jscad/io": "2.4.7", + "@jscad/modeling": "2.12.1", "@jscadui/format-jscad": "*", "@jscadui/format-threejs": "*", "@jscadui/fs-provider": "*", diff --git a/package-lock.json b/package-lock.json index 93cb891..107a243 100644 --- a/package-lock.json +++ b/package-lock.json @@ -155,8 +155,8 @@ "version": "0.0.0", "dependencies": { "@codemirror/lang-javascript": "^6.1.9", - "@jscad/io": "2.4.4", - "@jscad/modeling": "2.12.0", + "@jscad/io": "2.4.7", + "@jscad/modeling": "2.12.1", "@jscadui/format-jscad": "*", "@jscadui/format-threejs": "*", "@jscadui/fs-provider": "*", @@ -169,7 +169,6 @@ "@jscadui/scene": "*", "@jscadui/transform-babel": "*", "@jscadui/worker": "*", - "base64-arraybuffer": "^1.0.2", "codemirror": "^6.0.1", "fflate": "0.8.1", "gl-matrix": "^3.4.0", @@ -534,9 +533,206 @@ "node": ">=12" } }, + "apps/jscad-web/node_modules/@jscad/3mf-serializer": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@jscad/3mf-serializer/-/3mf-serializer-2.1.10.tgz", + "integrity": "sha512-jVmTQYi8xQarsrbZ66jlxcioNQGJbp9T9QQLTgfHC/XshaGuzGB3nOb4ELb+bGvNXljTYd0Eh0w0rPVAA3YaoQ==", + "dependencies": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1", + "fflate": "0.7.3", + "onml": "1.2.0" + } + }, + "apps/jscad-web/node_modules/@jscad/3mf-serializer/node_modules/fflate": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.3.tgz", + "integrity": "sha512-0Zz1jOzJWERhyhsimS54VTqOteCNwRtIlh8isdL0AXLo0g7xNTfTL7oWrkmCnPhZGocKIkWHBistBrrpoNH3aw==" + }, + "apps/jscad-web/node_modules/@jscad/amf-deserializer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@jscad/amf-deserializer/-/amf-deserializer-2.3.6.tgz", + "integrity": "sha512-V+Q/Kr1ga2oe+9fBY7k+MyUWUTX3JZccQKZMaxapEoySBy6VSar+hfaBWBb2SXmNKnvL/he8MGJGnpbcykLZ+g==", + "dependencies": { + "@jscad/modeling": "2.12.1", + "saxes": "5.0.1" + } + }, + "apps/jscad-web/node_modules/@jscad/amf-serializer": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@jscad/amf-serializer/-/amf-serializer-2.1.16.tgz", + "integrity": "sha512-+0aNeZUGKF0AsQb3v0Kdivl3Ln21cFlkyzoHVC1QF2f3Dg0TTNy89n63nrGCOS3Ly/2DPqfLJim26jXgyB+8/Q==", + "dependencies": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1", + "onml": "1.3.0" + } + }, + "apps/jscad-web/node_modules/@jscad/amf-serializer/node_modules/onml": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/onml/-/onml-1.3.0.tgz", + "integrity": "sha512-RhGUsC6Im2A5vAdIvxE3auRKTqrqUZQl/AYLn8+9lM3SO4da5bwhcI5TcM+hfQxNCSLLOVErsl9p0ZPjKKmz+g==", + "dependencies": { + "sax": "^1.2.1" + } + }, + "apps/jscad-web/node_modules/@jscad/dxf-deserializer": { + "version": "2.3.23", + "resolved": "https://registry.npmjs.org/@jscad/dxf-deserializer/-/dxf-deserializer-2.3.23.tgz", + "integrity": "sha512-veUS4mxkWAli5UihzAoz1rDuunoc5ff6SjmdKEtiYfyK25Hp2NcQ5EFedo/DE8SjCNIyEkIaJULfd3jC+XnckA==", + "dependencies": { + "@jscad/modeling": "2.12.1" + } + }, + "apps/jscad-web/node_modules/@jscad/dxf-serializer": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@jscad/dxf-serializer/-/dxf-serializer-2.1.16.tgz", + "integrity": "sha512-zS1E1vd/4i7S1ZG4vwZzlXr4qseMVnhnb6IzwgDJFth8a9Z/W2+VXR0TLLhYPYAcRKGHkGsCVpGcLHjigWFkDA==", + "dependencies": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1" + } + }, + "apps/jscad-web/node_modules/@jscad/io": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/@jscad/io/-/io-2.4.7.tgz", + "integrity": "sha512-ikt79RsjhD7Odm7pS5zS0ez7KF/QiDAVzsX1Wm65NOMlN/NEtDnVmEQCxtoOS5h+iZkmmtVsfaJYDOCvlbwxxQ==", + "dependencies": { + "@jscad/3mf-serializer": "2.1.10", + "@jscad/amf-deserializer": "2.3.6", + "@jscad/amf-serializer": "2.1.16", + "@jscad/array-utils": "2.1.4", + "@jscad/dxf-deserializer": "2.3.23", + "@jscad/dxf-serializer": "2.1.16", + "@jscad/io-utils": "2.0.26", + "@jscad/json-deserializer": "2.0.27", + "@jscad/json-serializer": "2.0.26", + "@jscad/modeling": "2.12.1", + "@jscad/obj-deserializer": "2.0.26", + "@jscad/obj-serializer": "2.1.16", + "@jscad/stl-deserializer": "2.1.23", + "@jscad/stl-serializer": "2.1.16", + "@jscad/svg-deserializer": "2.5.7", + "@jscad/svg-serializer": "2.3.14", + "@jscad/x3d-deserializer": "2.2.6", + "@jscad/x3d-serializer": "2.4.6" + } + }, + "apps/jscad-web/node_modules/@jscad/io-utils": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@jscad/io-utils/-/io-utils-2.0.26.tgz", + "integrity": "sha512-I4Egbw7c5yAZFJpaTDdTh9harNLghyU7R3a0s/4v2f2Rv5bZg6GsAFvPjFHl8e3gTZA2qdKhR2X/DTjN38em3g==" + }, + "apps/jscad-web/node_modules/@jscad/json-deserializer": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/@jscad/json-deserializer/-/json-deserializer-2.0.27.tgz", + "integrity": "sha512-tZuP1O5QiGtdZU0RlG/08Ftbf6OIp/8gTe+xkbzeq7gcd+WpWLcf19Fv9ka0lQKkUz7W9VSnNprP6PJqzA0LFg==", + "dependencies": { + "@jscad/array-utils": "2.1.4" + } + }, + "apps/jscad-web/node_modules/@jscad/json-serializer": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@jscad/json-serializer/-/json-serializer-2.0.26.tgz", + "integrity": "sha512-PoXg4nkiwvwDAZsAgzWK1VWe+tVZNBcf97H9EAskE882rQuW+ad7gDaBis3TLm78jjcPE+jgnTboifdEGR5HdQ==", + "dependencies": { + "@jscad/modeling": "2.12.1" + } + }, "apps/jscad-web/node_modules/@jscad/modeling": { - "version": "2.12.0", - "license": "MIT" + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/@jscad/modeling/-/modeling-2.12.1.tgz", + "integrity": "sha512-8DOOXmrMuzHgoCXkuBodBidY8K1NNRFQCsHuAiOVyZWoHql45+PG5KQYgZUj3MF2HmtsFD16pZOcMVsJjglhcQ==" + }, + "apps/jscad-web/node_modules/@jscad/obj-deserializer": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@jscad/obj-deserializer/-/obj-deserializer-2.0.26.tgz", + "integrity": "sha512-CORWS9spR3UT/cYxakVLdEGwl+CV5wEroZbgyJuOqQMpeDKJKnI8uRWMalyy/9jjdKMFt/DNG8cNKGZ/mN3SMw==", + "dependencies": { + "@jscad/modeling": "2.12.1" + } + }, + "apps/jscad-web/node_modules/@jscad/obj-serializer": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@jscad/obj-serializer/-/obj-serializer-2.1.16.tgz", + "integrity": "sha512-gRTTB2NY0S/99P83BhMj8SknAIPbhSUivoNuvdNKqR4PM6lp/6PFYYteutpQdmdIVbkRcSbZNQgQW0ZIqRue3w==", + "dependencies": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1" + } + }, + "apps/jscad-web/node_modules/@jscad/stl-deserializer": { + "version": "2.1.23", + "resolved": "https://registry.npmjs.org/@jscad/stl-deserializer/-/stl-deserializer-2.1.23.tgz", + "integrity": "sha512-Ttg3r78wgE+fi9PMa1Jil57v++B2ztpuB/PrkyU2x25FrgJyrXgyG5wtILN7dYEO81/ylR26TKVrYyMPofrWHA==", + "dependencies": { + "@jscad/io-utils": "2.0.26", + "@jscad/modeling": "2.12.1" + } + }, + "apps/jscad-web/node_modules/@jscad/stl-serializer": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@jscad/stl-serializer/-/stl-serializer-2.1.16.tgz", + "integrity": "sha512-d2GYMwHyfX7i5M3WRvfzF3IdQMrP9FYIKMi0Xa2GPysGGWHT3+cA2+9paAPOMzUNFDwWUObSV7Jr9mYDTHiKow==", + "dependencies": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1" + } + }, + "apps/jscad-web/node_modules/@jscad/svg-deserializer": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@jscad/svg-deserializer/-/svg-deserializer-2.5.7.tgz", + "integrity": "sha512-CbECgTkvjV6mwK94m8T5QE1D7Qz4tXHUNNL0WO3N30t842SrJkkJWKgBKhSdA0xru3URQ70f0x05vZ2yT+iEOQ==", + "dependencies": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1", + "saxes": "5.0.1" + } + }, + "apps/jscad-web/node_modules/@jscad/svg-serializer": { + "version": "2.3.14", + "resolved": "https://registry.npmjs.org/@jscad/svg-serializer/-/svg-serializer-2.3.14.tgz", + "integrity": "sha512-A2OsmaVunYQ4RnPbpqOPzT9aD88d7MlxhoGSJ981TNdyDEOs3O6Tpu6gdank66JPJ0dmSVEtDiHxdKPTN+Tneg==", + "dependencies": { + "@jscad/modeling": "2.12.1", + "onml": "1.3.0" + } + }, + "apps/jscad-web/node_modules/@jscad/svg-serializer/node_modules/onml": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/onml/-/onml-1.3.0.tgz", + "integrity": "sha512-RhGUsC6Im2A5vAdIvxE3auRKTqrqUZQl/AYLn8+9lM3SO4da5bwhcI5TcM+hfQxNCSLLOVErsl9p0ZPjKKmz+g==", + "dependencies": { + "sax": "^1.2.1" + } + }, + "apps/jscad-web/node_modules/@jscad/x3d-deserializer": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@jscad/x3d-deserializer/-/x3d-deserializer-2.2.6.tgz", + "integrity": "sha512-6emVP+AEW+BoIyEdPtklfzDM54pJCmzdV+D8WpL7GkxERmmZemsK1sqefRaFCa5gYAHum2AcNFrRndKrPwqPyQ==", + "dependencies": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1", + "saxes": "5.0.1" + } + }, + "apps/jscad-web/node_modules/@jscad/x3d-serializer": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@jscad/x3d-serializer/-/x3d-serializer-2.4.6.tgz", + "integrity": "sha512-6Kjf25c+sQEOR4Aq6Uo0PBET2s+AdVhTnCXppMWwD7ABIDycExKTVeHII80R9/PY6cPmMsmaooAQ1lPZMoObPw==", + "dependencies": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1", + "onml": "1.3.0" + } + }, + "apps/jscad-web/node_modules/@jscad/x3d-serializer/node_modules/onml": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/onml/-/onml-1.3.0.tgz", + "integrity": "sha512-RhGUsC6Im2A5vAdIvxE3auRKTqrqUZQl/AYLn8+9lM3SO4da5bwhcI5TcM+hfQxNCSLLOVErsl9p0ZPjKKmz+g==", + "dependencies": { + "sax": "^1.2.1" + } }, "apps/jscad-web/node_modules/esbuild": { "version": "0.17.19", @@ -4379,14 +4575,6 @@ "node": ">=0.10.0" } }, - "node_modules/base64-arraybuffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", - "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -20237,8 +20425,8 @@ "version": "file:apps/jscad-web", "requires": { "@codemirror/lang-javascript": "^6.1.9", - "@jscad/io": "2.4.4", - "@jscad/modeling": "2.12.0", + "@jscad/io": "2.4.7", + "@jscad/modeling": "2.12.1", "@jscadui/format-jscad": "*", "@jscadui/format-threejs": "*", "@jscadui/fs-provider": "*", @@ -20253,7 +20441,6 @@ "@jscadui/worker": "*", "@jsx6/build": "0.2.0", "@trivago/prettier-plugin-sort-imports": "~3.3.0", - "base64-arraybuffer": "^1.0.2", "codemirror": "^6.0.1", "esbuild": "^0.17.11", "fflate": "0.8.1", @@ -20415,8 +20602,214 @@ "dev": true, "optional": true }, + "@jscad/3mf-serializer": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@jscad/3mf-serializer/-/3mf-serializer-2.1.10.tgz", + "integrity": "sha512-jVmTQYi8xQarsrbZ66jlxcioNQGJbp9T9QQLTgfHC/XshaGuzGB3nOb4ELb+bGvNXljTYd0Eh0w0rPVAA3YaoQ==", + "requires": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1", + "fflate": "0.7.3", + "onml": "1.2.0" + }, + "dependencies": { + "fflate": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.3.tgz", + "integrity": "sha512-0Zz1jOzJWERhyhsimS54VTqOteCNwRtIlh8isdL0AXLo0g7xNTfTL7oWrkmCnPhZGocKIkWHBistBrrpoNH3aw==" + } + } + }, + "@jscad/amf-deserializer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@jscad/amf-deserializer/-/amf-deserializer-2.3.6.tgz", + "integrity": "sha512-V+Q/Kr1ga2oe+9fBY7k+MyUWUTX3JZccQKZMaxapEoySBy6VSar+hfaBWBb2SXmNKnvL/he8MGJGnpbcykLZ+g==", + "requires": { + "@jscad/modeling": "2.12.1", + "saxes": "5.0.1" + } + }, + "@jscad/amf-serializer": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@jscad/amf-serializer/-/amf-serializer-2.1.16.tgz", + "integrity": "sha512-+0aNeZUGKF0AsQb3v0Kdivl3Ln21cFlkyzoHVC1QF2f3Dg0TTNy89n63nrGCOS3Ly/2DPqfLJim26jXgyB+8/Q==", + "requires": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1", + "onml": "1.3.0" + }, + "dependencies": { + "onml": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/onml/-/onml-1.3.0.tgz", + "integrity": "sha512-RhGUsC6Im2A5vAdIvxE3auRKTqrqUZQl/AYLn8+9lM3SO4da5bwhcI5TcM+hfQxNCSLLOVErsl9p0ZPjKKmz+g==", + "requires": { + "sax": "^1.2.1" + } + } + } + }, + "@jscad/dxf-deserializer": { + "version": "2.3.23", + "resolved": "https://registry.npmjs.org/@jscad/dxf-deserializer/-/dxf-deserializer-2.3.23.tgz", + "integrity": "sha512-veUS4mxkWAli5UihzAoz1rDuunoc5ff6SjmdKEtiYfyK25Hp2NcQ5EFedo/DE8SjCNIyEkIaJULfd3jC+XnckA==", + "requires": { + "@jscad/modeling": "2.12.1" + } + }, + "@jscad/dxf-serializer": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@jscad/dxf-serializer/-/dxf-serializer-2.1.16.tgz", + "integrity": "sha512-zS1E1vd/4i7S1ZG4vwZzlXr4qseMVnhnb6IzwgDJFth8a9Z/W2+VXR0TLLhYPYAcRKGHkGsCVpGcLHjigWFkDA==", + "requires": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1" + } + }, + "@jscad/io": { + "version": "2.4.7", + "resolved": "https://registry.npmjs.org/@jscad/io/-/io-2.4.7.tgz", + "integrity": "sha512-ikt79RsjhD7Odm7pS5zS0ez7KF/QiDAVzsX1Wm65NOMlN/NEtDnVmEQCxtoOS5h+iZkmmtVsfaJYDOCvlbwxxQ==", + "requires": { + "@jscad/3mf-serializer": "2.1.10", + "@jscad/amf-deserializer": "2.3.6", + "@jscad/amf-serializer": "2.1.16", + "@jscad/array-utils": "2.1.4", + "@jscad/dxf-deserializer": "2.3.23", + "@jscad/dxf-serializer": "2.1.16", + "@jscad/io-utils": "2.0.26", + "@jscad/json-deserializer": "2.0.27", + "@jscad/json-serializer": "2.0.26", + "@jscad/modeling": "2.12.1", + "@jscad/obj-deserializer": "2.0.26", + "@jscad/obj-serializer": "2.1.16", + "@jscad/stl-deserializer": "2.1.23", + "@jscad/stl-serializer": "2.1.16", + "@jscad/svg-deserializer": "2.5.7", + "@jscad/svg-serializer": "2.3.14", + "@jscad/x3d-deserializer": "2.2.6", + "@jscad/x3d-serializer": "2.4.6" + } + }, + "@jscad/io-utils": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@jscad/io-utils/-/io-utils-2.0.26.tgz", + "integrity": "sha512-I4Egbw7c5yAZFJpaTDdTh9harNLghyU7R3a0s/4v2f2Rv5bZg6GsAFvPjFHl8e3gTZA2qdKhR2X/DTjN38em3g==" + }, + "@jscad/json-deserializer": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/@jscad/json-deserializer/-/json-deserializer-2.0.27.tgz", + "integrity": "sha512-tZuP1O5QiGtdZU0RlG/08Ftbf6OIp/8gTe+xkbzeq7gcd+WpWLcf19Fv9ka0lQKkUz7W9VSnNprP6PJqzA0LFg==", + "requires": { + "@jscad/array-utils": "2.1.4" + } + }, + "@jscad/json-serializer": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@jscad/json-serializer/-/json-serializer-2.0.26.tgz", + "integrity": "sha512-PoXg4nkiwvwDAZsAgzWK1VWe+tVZNBcf97H9EAskE882rQuW+ad7gDaBis3TLm78jjcPE+jgnTboifdEGR5HdQ==", + "requires": { + "@jscad/modeling": "2.12.1" + } + }, "@jscad/modeling": { - "version": "2.12.0" + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/@jscad/modeling/-/modeling-2.12.1.tgz", + "integrity": "sha512-8DOOXmrMuzHgoCXkuBodBidY8K1NNRFQCsHuAiOVyZWoHql45+PG5KQYgZUj3MF2HmtsFD16pZOcMVsJjglhcQ==" + }, + "@jscad/obj-deserializer": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/@jscad/obj-deserializer/-/obj-deserializer-2.0.26.tgz", + "integrity": "sha512-CORWS9spR3UT/cYxakVLdEGwl+CV5wEroZbgyJuOqQMpeDKJKnI8uRWMalyy/9jjdKMFt/DNG8cNKGZ/mN3SMw==", + "requires": { + "@jscad/modeling": "2.12.1" + } + }, + "@jscad/obj-serializer": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@jscad/obj-serializer/-/obj-serializer-2.1.16.tgz", + "integrity": "sha512-gRTTB2NY0S/99P83BhMj8SknAIPbhSUivoNuvdNKqR4PM6lp/6PFYYteutpQdmdIVbkRcSbZNQgQW0ZIqRue3w==", + "requires": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1" + } + }, + "@jscad/stl-deserializer": { + "version": "2.1.23", + "resolved": "https://registry.npmjs.org/@jscad/stl-deserializer/-/stl-deserializer-2.1.23.tgz", + "integrity": "sha512-Ttg3r78wgE+fi9PMa1Jil57v++B2ztpuB/PrkyU2x25FrgJyrXgyG5wtILN7dYEO81/ylR26TKVrYyMPofrWHA==", + "requires": { + "@jscad/io-utils": "2.0.26", + "@jscad/modeling": "2.12.1" + } + }, + "@jscad/stl-serializer": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@jscad/stl-serializer/-/stl-serializer-2.1.16.tgz", + "integrity": "sha512-d2GYMwHyfX7i5M3WRvfzF3IdQMrP9FYIKMi0Xa2GPysGGWHT3+cA2+9paAPOMzUNFDwWUObSV7Jr9mYDTHiKow==", + "requires": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1" + } + }, + "@jscad/svg-deserializer": { + "version": "2.5.7", + "resolved": "https://registry.npmjs.org/@jscad/svg-deserializer/-/svg-deserializer-2.5.7.tgz", + "integrity": "sha512-CbECgTkvjV6mwK94m8T5QE1D7Qz4tXHUNNL0WO3N30t842SrJkkJWKgBKhSdA0xru3URQ70f0x05vZ2yT+iEOQ==", + "requires": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1", + "saxes": "5.0.1" + } + }, + "@jscad/svg-serializer": { + "version": "2.3.14", + "resolved": "https://registry.npmjs.org/@jscad/svg-serializer/-/svg-serializer-2.3.14.tgz", + "integrity": "sha512-A2OsmaVunYQ4RnPbpqOPzT9aD88d7MlxhoGSJ981TNdyDEOs3O6Tpu6gdank66JPJ0dmSVEtDiHxdKPTN+Tneg==", + "requires": { + "@jscad/modeling": "2.12.1", + "onml": "1.3.0" + }, + "dependencies": { + "onml": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/onml/-/onml-1.3.0.tgz", + "integrity": "sha512-RhGUsC6Im2A5vAdIvxE3auRKTqrqUZQl/AYLn8+9lM3SO4da5bwhcI5TcM+hfQxNCSLLOVErsl9p0ZPjKKmz+g==", + "requires": { + "sax": "^1.2.1" + } + } + } + }, + "@jscad/x3d-deserializer": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@jscad/x3d-deserializer/-/x3d-deserializer-2.2.6.tgz", + "integrity": "sha512-6emVP+AEW+BoIyEdPtklfzDM54pJCmzdV+D8WpL7GkxERmmZemsK1sqefRaFCa5gYAHum2AcNFrRndKrPwqPyQ==", + "requires": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1", + "saxes": "5.0.1" + } + }, + "@jscad/x3d-serializer": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@jscad/x3d-serializer/-/x3d-serializer-2.4.6.tgz", + "integrity": "sha512-6Kjf25c+sQEOR4Aq6Uo0PBET2s+AdVhTnCXppMWwD7ABIDycExKTVeHII80R9/PY6cPmMsmaooAQ1lPZMoObPw==", + "requires": { + "@jscad/array-utils": "2.1.4", + "@jscad/modeling": "2.12.1", + "onml": "1.3.0" + }, + "dependencies": { + "onml": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/onml/-/onml-1.3.0.tgz", + "integrity": "sha512-RhGUsC6Im2A5vAdIvxE3auRKTqrqUZQl/AYLn8+9lM3SO4da5bwhcI5TcM+hfQxNCSLLOVErsl9p0ZPjKKmz+g==", + "requires": { + "sax": "^1.2.1" + } + } + } }, "esbuild": { "version": "0.17.19", @@ -22876,11 +23269,6 @@ } } }, - "base64-arraybuffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", - "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==" - }, "basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", From 4880dbb4fe647538b1a854ac25eea91177614fc1 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Tue, 20 Feb 2024 23:00:43 +0100 Subject: [PATCH 02/25] always rebuild bundles in build mode --- apps/jscad-web/build.js | 8 ++++---- apps/jscad-web/src_build/esbuildUtil.js | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/jscad-web/build.js b/apps/jscad-web/build.js index 2b6d1c3..5663a6c 100644 --- a/apps/jscad-web/build.js +++ b/apps/jscad-web/build.js @@ -35,10 +35,10 @@ if(!(dev & existsSync(outDir + "/docs"))){ } /**************************** BUILD JS that is static *************/ -await buildBundle(outDir + '/build', 'bundle.threejs.js', { globalName: 'THREE' }) -await buildBundle(outDir + '/build', 'bundle.jscad_modeling.js', { format: 'cjs' }) -await buildBundle(outDir + '/build', 'bundle.jscad_io.js', { format:'cjs' }) -await buildBundle(outDir + '/build', 'bundle.jscadui.transform-babel.js', { globalName: 'jscadui_transform_babel' }) +await buildBundle(outDir + '/build', 'bundle.threejs.js', { globalName: 'THREE', skipExisting: dev }) +await buildBundle(outDir + '/build', 'bundle.jscad_modeling.js', { format: 'cjs', skipExisting: dev }) +await buildBundle(outDir + '/build', 'bundle.jscad_io.js', { format:'cjs', skipExisting: dev }) +await buildBundle(outDir + '/build', 'bundle.jscadui.transform-babel.js', { globalName: 'jscadui_transform_babel', skipExisting: dev }) /**************************** BUILD JS THAT can change and watch if in dev mode *************/ await buildOne('src_bundle', outDir + '/build', 'bundle.worker.js', watch, { format: 'iife' }) diff --git a/apps/jscad-web/src_build/esbuildUtil.js b/apps/jscad-web/src_build/esbuildUtil.js index 350cb72..0da9e69 100644 --- a/apps/jscad-web/src_build/esbuildUtil.js +++ b/apps/jscad-web/src_build/esbuildUtil.js @@ -21,10 +21,10 @@ const bundleDef = { format: 'iife', } -export const buildBundle = (outDir, bundle, {srcDir='src_bundle', ...options})=>{ +export const buildBundle = (outDir, bundle, {srcDir='src_bundle', skipExisting = true, ...options})=>{ let file = `${srcDir}/${bundle}` let outfile = `${outDir}/${bundle}` - return runEsbuild(esbuild,{...bundleDef, ...options, skipExisting: true, entryPoints:[file], outfile}) + return runEsbuild(esbuild,{...bundleDef, ...options, skipExisting, entryPoints:[file], outfile}) } export const buildOneIfNeeded = (outDir, file, options={})=>{ From fbff309ef1000602543f34f7c27143af0d3ce8b9 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Wed, 28 Feb 2024 09:19:36 +0100 Subject: [PATCH 03/25] fix readme --- packages/require/README.md | 32 +------------------------------- 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/packages/require/README.md b/packages/require/README.md index 347e0e1..3bac3aa 100644 --- a/packages/require/README.md +++ b/packages/require/README.md @@ -1,31 +1 @@ -# fs-provider - -Provider that fills cache for `fs-service-worker` that you need in your main script to fill files data. - -Use case is for creating a virtual fodler taht can be accessed by javascript uring fetch or sync/async XMLHttpRequest. - -## temp caches - -This require wrapper will be used for running jscad scripts that might be whole project folders. In that case it can have local libraries in workspaces, and also will have multiple files. It is important to separate global cache (that caches possibly things like '@jscad/modeling' or some other library, versus modules or files from uploaded folder). When new folder is uploaded we should be able to purge only files cached from previous folder, and not others. - -Maybe this use case for temp caches will prove to be not so useful, so we also need to support full cache purging. - -# URL - useful to resolve relative paths - -Regardless if url is something `http://` or some fake prefix `fs:/` the `URL` class can be used to resolve paths relative to a file for us. We do not even need to cut the file name out, it will use the folder as basis like you would expect for html resources. - -```js -new URL('/index.js','fs:/bb/aaa/ccc/bla.js') -// .pathname: /index.js -new URL('./index.js','fs:/bb/aaa/ccc/bla.js') -// .pathname: /bb/aaa/ccc/index.js -new URL('../index.js','fs:/bb/aaa/ccc/bla.js') -// .pathname: /bb/aaa/index.js -``` - -Another also useful behaviour is that if url is not relative, secont param is ignored - -```js -new URL('http://google.com','fs:/bb/aaa/ccc/bla.js') -// http://google.com -``` +# require \ No newline at end of file From 8b720ab688047104534b104fbc3abcda62ea3241 Mon Sep 17 00:00:00 2001 From: Giulio Malventi Date: Fri, 8 Mar 2024 16:42:27 +0100 Subject: [PATCH 04/25] Adjust parameter triangle size and position (#98) Closed parameter group used a larger triangle shape ![image](https://github.com/hrgdavor/jscadui/assets/5012744/7d5081bc-14e4-4ac2-b62d-5b514052e7c1) --- apps/jscad-web/static/main.css | 3 ++- apps/vue3-jscad/src/App.vue | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/jscad-web/static/main.css b/apps/jscad-web/static/main.css index c65023f..ba6361e 100644 --- a/apps/jscad-web/static/main.css +++ b/apps/jscad-web/static/main.css @@ -515,10 +515,11 @@ p { #paramsDiv .form-line[type="group"]:before { content: "\25bc"; padding-right: 10px; + padding-top: 3px; cursor: pointer; } #paramsDiv .form-line[type="group"][closed="1"]:before { - content: "\25b6"; + content: "\25ba"; } #paramsDiv .form-line[type="group"] label { font-weight: 500; diff --git a/apps/vue3-jscad/src/App.vue b/apps/vue3-jscad/src/App.vue index 4cc2399..b50cb95 100644 --- a/apps/vue3-jscad/src/App.vue +++ b/apps/vue3-jscad/src/App.vue @@ -890,11 +890,12 @@ p { #paramsDiv .form-line[type="group"]:before { content: "\25bc"; padding-right: 10px; + padding-top: 3px; cursor: pointer; } #paramsDiv .form-line[type="group"][closed="1"]:before { - content: "\25b6"; + content: "\25ba"; } #paramsDiv .form-line[type="group"] label { From 43b25289f7ac91eeef687fbc6dfc418e3178eba2 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Mon, 11 Mar 2024 23:32:37 +0100 Subject: [PATCH 05/25] postMessage proxy --- apps/cardboard-cutter/main.js | 8 +- apps/engine-test/main.js | 14 +- apps/jscad-web/main.js | 111 ++++++------- apps/model-page/main.js | 8 +- packages/fs-serviceworker/fs-serviceworker.js | 2 +- packages/postmessage/README.md | 86 +++++++++- packages/postmessage/index.js | 154 +++++++++++------- packages/worker/worker.js | 52 +++++- 8 files changed, 288 insertions(+), 147 deletions(-) diff --git a/apps/cardboard-cutter/main.js b/apps/cardboard-cutter/main.js index 30533b5..8706ff5 100644 --- a/apps/cardboard-cutter/main.js +++ b/apps/cardboard-cutter/main.js @@ -111,7 +111,7 @@ function save(blob, filename) { } function exportModel(format) { - sendCmd('exportData', { format }).then(({ data }) => { + sendCmd('exportData', [{ format }]).then(({ data }) => { console.log('save', fileToRun + '.stl', data) save(new Blob([data], { type: 'text/plain' }), fileToRun + '.stl') }) @@ -123,12 +123,12 @@ const { sendCmd, sendNotify } = initMessaging(worker, handlers) const paramChangeCallback = params => { console.log('params', params) - sendCmd('runMain', { params }) + sendCmd('runMain', [{ params }]) } export const runScript = file => { fileToRun = file.replace(/.*\//, '').replace(/\..*/, '') - sendCmd('runScript', { url: file }).then(result => { + sendCmd('runScript', [{ url: file }]).then(result => { console.log('result', result) genParams({ target: byId('paramsDiv'), params: result.def || {}, callback: paramChangeCallback }) }) @@ -141,5 +141,5 @@ export const initEngine = async (THREE, elem, workerOptions) => { updateFromCtrl(ctrl) setTheme(theme) - await sendCmd('init', workerOptions) + await sendCmd('init', [workerOptions]) } diff --git a/apps/engine-test/main.js b/apps/engine-test/main.js index bf02090..f46ade6 100644 --- a/apps/engine-test/main.js +++ b/apps/engine-test/main.js @@ -122,11 +122,11 @@ document.body.ondrop = async ev => { if (!sw) await initFs() showDrop(false) - sendCmd('clearTempCache', {}) + sendCmd('clearTempCache', [{}]) const { alias, script } = await fileDropped(sw, files) projectName = sw.projectName if (alias.length) { - sendNotify('init', { alias }) + sendNotify('init', [{ alias }]) } runScript({ url: sw.fileToRun, base: sw.base }) } catch (error) { @@ -197,11 +197,11 @@ async function sendCmdAndSpin(method, params){ } } -sendCmdAndSpin('init', { +sendCmdAndSpin('init', [{ bundles: { '@jscad/modeling': toUrl('./build/bundle.jscad_modeling.js'), }, -}).then(()=>{ +}]).then(()=>{ runScript({script:`const { sphere, geodesicSphere } = require('@jscad/modeling').primitives const { translate, scale } = require('@jscad/modeling').transforms @@ -224,12 +224,12 @@ sendCmdAndSpin('init', { const paramChangeCallback = async params => { console.log('params changed', params) - let result = await sendCmdAndSpin('runMain', { params }) + let result = await sendCmdAndSpin('runMain', [{ params }]) handlers.entities(result) } const runScript = async ({script, url = './index.js', base, root}) => { - const result = await sendCmdAndSpin('runScript', { script, url, base, root }) + const result = await sendCmdAndSpin('runScript', [{ script, url, base, root }]) console.log('result', result) genParams({ target: byId('paramsDiv'), params: result.def || {}, callback: paramChangeCallback }) handlers.entities(result) @@ -240,7 +240,7 @@ async function initFs() { sw = await registerServiceWorker('bundle.fs-serviceworker.js?prefix=/swfs/') sw.defProjectName = 'jscad' sw.onfileschange = files => { - sendNotify('clearFileCache', { files }) + sendNotify('clearFileCache', [{ files }]) if (sw.fileToRun) runScript({ url: sw.fileToRun, base: sw.base }) } } diff --git a/apps/jscad-web/main.js b/apps/jscad-web/main.js index 944565a..5046912 100644 --- a/apps/jscad-web/main.js +++ b/apps/jscad-web/main.js @@ -10,7 +10,7 @@ import { import { Gizmo } from '@jscadui/html-gizmo' import { OrbitControl } from '@jscadui/orbit' import { genParams } from '@jscadui/params' -import { initMessaging } from '@jscadui/postmessage' +import { initMessaging, messageProxy } from '@jscadui/postmessage' import defaultCode from './examples/jscad.example.js' import * as editor from './src/editor.js' @@ -21,15 +21,17 @@ import * as remote from './src/remote.js' import { formatStacktrace } from './src/stacktrace.js' import { ViewState } from './src/viewState.js' import * as welcome from './src/welcome.js' -import { runMain } from '../../packages/worker/worker.js' export const byId = id => document.getElementById(id) + +/** @typedef {import('@jscadui/worker').JscadWorker} JscadWorker*/ + const appBase = document.baseURI let currentBase = appBase const toUrl = path => new URL(path, appBase).toString() const viewState = new ViewState() -viewState.onRequireReRender = ()=>paramChangeCallback(lastRunParams) +viewState.onRequireReRender = () => paramChangeCallback(lastRunParams) const gizmo = (window.gizmo = new Gizmo()) byId('overlay').parentNode.appendChild(gizmo) @@ -55,10 +57,10 @@ ctrl.oninput = state => updateFromCtrl(state) gizmo.oncam = ({ cam }) => ctrl.animateToCommonCamera(cam) let sw -async function resetFileRefs(){ +async function resetFileRefs() { editor.setFiles([]) saveMap = {} - if(sw){ + if (sw) { delete sw.fileToRun await clearFs(sw) } @@ -78,7 +80,7 @@ async function initFs() { }) sw.defProjectName = 'jscad' sw.onfileschange = files => { - sendNotify('clearFileCache', { files }) + workerApi.clearFileCache({ files }) editor.filesChanged(files) if (sw.fileToRun) runScript({ url: sw.fileToRun, base: sw.base }) } @@ -97,12 +99,12 @@ document.body.ondrop = async ev => { await resetFileRefs() if (!sw) await initFs() showDrop(false) - sendCmd('clearTempCache', {}) + workerApi.clearTempCache() saveMap = {} const { alias, script } = await fileDropped(sw, files) projectName = sw.projectName if (alias.length) { - sendNotify('init', { alias }) + workerApi.init({ alias }) } let url = sw.fileToRun runScript({ url, base: sw.base }) @@ -150,7 +152,7 @@ function save(blob, filename) { } const exportModel = async (format, extension) => { - const { data } = (await sendCmdAndSpin('exportData', { format })) || {} + const { data } = (await workerApi.exportData({ format })) || {} if (data) { save(new Blob([data], { type: 'text/plain' }), `${projectName}.${extension}`) console.log('save', `${projectName}.${extension}`, data) @@ -166,43 +168,45 @@ const handlers = { setError(undefined) }, } -const { sendCmd, sendNotify } = initMessaging(worker, handlers) + +/** @type {JscadWorker} */ +const workerApi = messageProxy(worker, handlers, { onJobCount: trackJobs }) const spinner = byId('spinner') -let jobs = 0 let firstJobTimer -async function sendCmdAndSpin(method, params) { - jobs++ + +function trackJobs(jobs) { if (jobs === 1) { // do not show spinner for fast renders firstJobTimer = setTimeout(() => { spinner.style.display = 'block' }, 300) } - try { - return await sendCmd(method, params) - } catch (error) { - setError(error) - throw error - } finally { - if (--jobs === 0) { - clearTimeout(firstJobTimer) - spinner.style.display = 'none' - } + if (jobs === 0) { + clearTimeout(firstJobTimer) + spinner.style.display = 'none' } } -sendCmdAndSpin('init', { - bundles: { - // local bundled alias for common libs. - '@jscad/modeling': toUrl('./build/bundle.jscad_modeling.js'), - '@jscad/io': toUrl('./build/bundle.jscad_io.js'), - }, -}).then(() => { - if (loadDefault) { - runScript({ script: defaultCode, smooth: viewState.smoothRender }) - } -}) +const runScript = async ({ script, url = './jscad.model.js', base = currentBase, root }) => { + currentBase = base + loadDefault = false // don't load default model if something else was loaded + const result = await workerApi.runScript({ script, url, base, root, smooth: viewState.smoothRender }) + genParams({ target: byId('paramsDiv'), params: result.def || {}, callback: paramChangeCallback }) + lastRunParams = result.params + handlers.entities(result) +} + +const bundles = { + // local bundled alias for common libs. + '@jscad/modeling': toUrl('./build/bundle.jscad_modeling.js'), + '@jscad/io': toUrl('./build/bundle.jscad_io.js'), +} + +await workerApi.init({ bundles }) +if (loadDefault) { + runScript({ script: defaultCode, smooth: viewState.smoothRender }) +} let working let lastParams @@ -216,23 +220,14 @@ const paramChangeCallback = async params => { } working = true let result - try{ - result = await sendCmdAndSpin('runMain', { params, smooth: viewState.smoothRender }) + try { + result = await workerApi.runMain({ params, smooth: viewState.smoothRender }) lastRunParams = params - } finally{ + } finally { working = false } - handlers.entities(result, {smooth: viewState.smoothRender}) - if(lastParams && lastParams != params) paramChangeCallback(lastParams) -} - -const runScript = async ({ script, url = './jscad.model.js', base = currentBase, root }) => { - currentBase = base - loadDefault = false // don't load default model if something else was loaded - const result = await sendCmdAndSpin('runScript', { script, url, base, root, smooth: viewState.smoothRender }) - genParams({ target: byId('paramsDiv'), params: result.def || {}, callback: paramChangeCallback }) - lastRunParams = result.params - handlers.entities(result) + handlers.entities(result, { smooth: viewState.smoothRender }) + if (lastParams && lastParams != params) paramChangeCallback(lastParams) } const loadExample = async (source, base = appBase) => { @@ -242,21 +237,19 @@ const loadExample = async (source, base = appBase) => { } // Initialize three engine -engine.init().then(viewer => { - viewState.setEngine(viewer) -}) +viewState.setEngine(await engine.init()) let saveMap = {} -setInterval(async ()=>{ - for(let p in saveMap){ +setInterval(async () => { + for (let p in saveMap) { let handle = saveMap[p] let file = await handle.getFile() - if(file.lastModified > handle.lastMod){ + if (file.lastModified > handle.lastMod) { handle.lastMod = file.lastModified editor.filesChanged([file]) } } -},500) +}, 500) editor.init( defaultCode, @@ -267,7 +260,7 @@ editor.init( // it is expected if multiple files require same file/module that first time it is loaded // but for others resolved module is returned // if not cleared by calling clearFileCache, require will not try to reload the file - await sendCmd('clearFileCache', { files: [path] }) + await workerApi.clearFileCache({ files: [path] }) if (sw.fileToRun) runScript({ url: sw.fileToRun, base: sw.base }) } else { runScript({ script }) @@ -277,7 +270,7 @@ editor.init( console.log('save file', path) let pathArr = path.split('/') let fileHandle = (await sw?.getFile(path))?.fileHandle - if(!fileHandle) fileHandle = saveMap[path] + if (!fileHandle) fileHandle = saveMap[path] if (!fileHandle) { const opts = { suggestedName: pathArr[pathArr.length - 1], @@ -296,10 +289,10 @@ editor.init( await writable.write(script) await writable.close() saveMap[path] = fileHandle - fileHandle.lastMod = Date.now()+500 + fileHandle.lastMod = Date.now() + 500 } }, - path=>sw?.getFile(path) + path => sw?.getFile(path), ) menu.init(loadExample) welcome.init() diff --git a/apps/model-page/main.js b/apps/model-page/main.js index 30533b5..8706ff5 100644 --- a/apps/model-page/main.js +++ b/apps/model-page/main.js @@ -111,7 +111,7 @@ function save(blob, filename) { } function exportModel(format) { - sendCmd('exportData', { format }).then(({ data }) => { + sendCmd('exportData', [{ format }]).then(({ data }) => { console.log('save', fileToRun + '.stl', data) save(new Blob([data], { type: 'text/plain' }), fileToRun + '.stl') }) @@ -123,12 +123,12 @@ const { sendCmd, sendNotify } = initMessaging(worker, handlers) const paramChangeCallback = params => { console.log('params', params) - sendCmd('runMain', { params }) + sendCmd('runMain', [{ params }]) } export const runScript = file => { fileToRun = file.replace(/.*\//, '').replace(/\..*/, '') - sendCmd('runScript', { url: file }).then(result => { + sendCmd('runScript', [{ url: file }]).then(result => { console.log('result', result) genParams({ target: byId('paramsDiv'), params: result.def || {}, callback: paramChangeCallback }) }) @@ -141,5 +141,5 @@ export const initEngine = async (THREE, elem, workerOptions) => { updateFromCtrl(ctrl) setTheme(theme) - await sendCmd('init', workerOptions) + await sendCmd('init', [workerOptions]) } diff --git a/packages/fs-serviceworker/fs-serviceworker.js b/packages/fs-serviceworker/fs-serviceworker.js index 8637a54..100a792 100644 --- a/packages/fs-serviceworker/fs-serviceworker.js +++ b/packages/fs-serviceworker/fs-serviceworker.js @@ -63,7 +63,7 @@ self.addEventListener('fetch', async event => { return (done = true) } - let resp = await clientWrapper.sendCmd('getFile', { path: path }) + let resp = await clientWrapper.sendCmd('getFile', [{ path: path }]) rCached = await clientWrapper.cache.match(fileReq) done = true resolve(rCached || new Response(path + ' not in cache', { status: rCached ? 200 : 404 })) diff --git a/packages/postmessage/README.md b/packages/postmessage/README.md index beb0925..ff31720 100644 --- a/packages/postmessage/README.md +++ b/packages/postmessage/README.md @@ -1,15 +1,89 @@ # postMessage utility -Allows for simpler usage of [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage) by defining a kind of RPC protocol that can send notifications or call methods. Calling methods is handled with Promises because the postMessage is async by definition. +Allows for simpler usage of [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage) by defining a RPC protocol that can send notifications or call methods. +Calling methods is handled with Promises because the postMessage is async by definition. -If you use this utility in your main thread and in the worker, it will (in my opinion) simplify +If you use this utility both in your main thread and in the worker you will get the most benefits. -## transferable +Consider these steps depending on the complexity + +1. **postMessage**: if you have only few messages pushing some data, you do not even need this +2. **RPC:** if you have messages, and some of them are response to a request(functionally) you should try formalizing a protocol and this RPC here can be a good starting point +3. **Proxy+methods:** as soon as you are considering RPC, I would recommend going the step further and formalize the communication with documented method names and parameter types (TS or JSDoc) and use the Proxy object to have nice code hints in your IDE. + +# RPC protocol + +The RPC protocol is inspired by JSONRPC, as I personally found it useful in multiple projects. + +**Request:** Structure the top level object like this: + + - `method` - name of the method called + - `params` - array of parameters + - `id` - id of the request. Optional, not used for one-way notifications. Used to match response with the original request. + +**Response:** Structure the top level object like this: + + - `response` - value returned by the method + - `error` - `{code,message,stack}` if method fails + - `id` - id of the request to match this response to + +# Proxy object and method definitions + +With modern JavaScript we can go few steps further and make communication with worker be as simple +as calling methods(all of them async ofc.). + +Here is partial sample of jsdoc definitions for jscad worker +```ts +/** +@typedef InitOptions +@prop {String} baseURI - to resolve inital relative path +@prop {Array} alias - +@prop {Array} bundles - bundle alias {name:path} + +@typedef ScriptResponse +@prop {Array} entities +@prop {number} mainTime - script run time +@prop {number} convertTime - tim converting script output to gl data -It is simple to send transferable objects when sending a message to the worker by adding a parameter. +@typedef JscadWorker +@prop {String} name +@prop {(options:InitOptions)=>Promise} init +@prop {(options:RunMainOptions)=>Promise} runMain +*/ +``` + +Manuall calling `init` worker method documented above, requires sending +`worker.postMessage({method:'init', params:[{bundles}]})` just to trigger the method. +Then you would also need to handle response, and wrap it all into a promise + +If you do not want to be fancy with typed code you can just use `initMessaging` and call worker methods using `sendCmd`. + +```js +const {sendCmd } = initMessaging(worker, handlers, { onJobCount: trackJobs }) +await sendCmd('init',[{ bundles }]) +const result = await sendCmd('runScript', [{ script}]) +// IDE does not know type of the result +``` + +You can then import and use the definition + +```ts +/** @typedef {import('@jscadui/worker').JscadWorker} JscadWorker*/ + +/** @type {JscadWorker} */ +const workerApi = messageProxy(worker, handlers, { onJobCount: trackJobs }) +await workerApi.init({ bundles }) +const result = await workerApi.runScript({ script}) +// result is now known to be ScriptResponse and you get autocomplete +``` + + + + +## transferable -It is however more tricky to support transferable for return values without complicating simple use -cases that do not need transferable. +It is simple to send transferable objects when sending a message to the worker by adding a parameter to `postMessage`. +It is however more tricky to support transferable for return values without complicating simple use cases that do not need transferable. If you have a method that can be called and it needs to return transferable then you must use object as a return value. When returning such object, include `__transferable` key in the return value. It will not be in the data at the receiving end but will be taken out and passed to postMessage as a transferable parameter. diff --git a/packages/postmessage/index.js b/packages/postmessage/index.js index 757ad59..9fcf95a 100644 --- a/packages/postmessage/index.js +++ b/packages/postmessage/index.js @@ -8,58 +8,27 @@ export const withTransferable = (params,trans)=>{ return params } -const messageHandler = (handlers, sendResponse, sendError) => { - return async (e) => { - const { method, params, id, error, stack } = e.data - if (id && method === RESPONSE) { - const p = reqMap.get(id) - - if (!p) return console.error(`req ${id} not found`) - reqMap.delete(id) - - const [resolve, reject] = p - if (error) { - // restore stacktrace - error.stack = stack - reject(error) - } else { - resolve(params) - } - - return - } - - const fn = handlers[method] - if (!fn) { - const msg = 'no handler for type: ' + method - console.error(msg, e) - throw new Error(msg) - } - try { - const out = await fn(params) - if (id) { - sendResponse(out, id) - } - } catch (error) { - console.error(`error executing command ${method}`, params, error) - sendError(error, id) - } - } -} - const fixTransfer = trans => (trans ? trans.map(a => a.buffer || a) : []) -const messageSender = _self => { - const sendResponse = (params, id) => { - let trans = params?.[TRANSFERABLE] +/** + * + * @param {*} _self reference to self of the main window (self) or reference to a worker + * @param {*} handlers - object where key if method name, and value ih handler + * @returns + */ +export const initMessaging = (_self, handlers, {onJobCount}={}) => { + // on service worker, postMessage is on the controller + const ___self = _self.postMessage ? _self : _self.controller + const sendResponse = (result, id) => { + let trans = result?.[TRANSFERABLE] if (trans) { - delete params[TRANSFERABLE] + delete result[TRANSFERABLE] } try { - _self.postMessage({ method: RESPONSE, params, id }, fixTransfer(trans)) + ___self.postMessage({ method: RESPONSE, params: result, id }, fixTransfer(trans)) } catch (error) { - console.error('failed to send ', params, trans) + console.error('failed to send ', result, trans) throw error } } @@ -68,7 +37,7 @@ const messageSender = _self => { try { // serialize stacktrace so it isn't lost in transit const stack = error.stack - _self.postMessage({ method: RESPONSE, error, stack, id }) + ___self.postMessage({ method: RESPONSE, error, stack, id }) } catch (error) { console.error('failed to send ', error) throw error @@ -82,8 +51,8 @@ const messageSender = _self => { * @param {object} params * @param {Array} trans */ - const sendNotify = (method, params = {}, trans = []) => { - _self.postMessage({ method, params }, fixTransfer(trans)) + const sendNotify = (method, params = [], trans = []) => { + ___self.postMessage({ method, params }, fixTransfer(trans)) } /** @@ -95,12 +64,13 @@ const messageSender = _self => { * @param {number?} timeout * @returns {Promise} resolves when response is received */ - const sendCmd = (method, params = {}, trans = [], timeout) => { + const sendCmd = (method, params = [], trans = [], timeout) => { const id = seq++ - _self.postMessage({ method, params, id }, fixTransfer(trans)) + ___self.postMessage({ method, params, id }, fixTransfer(trans)) const out = new Promise((resolve, reject) => { reqMap.set(id, [resolve, reject]) + onJobCount?.(reqMap.size) if (timeout) { setTimeout(() => { reject('timeout') @@ -110,21 +80,79 @@ const messageSender = _self => { return out } - return { sendCmd, sendNotify, sendResponse, sendError } + const listener = async (e) => { + const { method, params, id, error, stack } = e.data + if (id && method === RESPONSE) { + const p = reqMap.get(id) + + if (!p) return console.error(`req ${id} not found`,id, e.data, e) + reqMap.delete(id) + onJobCount?.(reqMap.size) + + const [resolve, reject] = p + if (error) { + // restore stacktrace + error.stack = stack + reject(error) + } else { + resolve(params) + } + + return + } + + const fn = handlers[method] + if (!fn) { + const msg = 'no handler for type: ' + method + console.error(msg, e) + throw new Error(msg) + } + try { + const out = await fn(...params) + if (id) { + sendResponse(out, id) + } + } catch (error) { + console.error(`error executing command ${method}`, params, error) + sendError(error, id) + } + } + + _self.addEventListener?.('message', listener) + + return { + sendCmd, + sendNotify, + sendResponse, + sendError, + listener, + self:_self, + getRpcJobCount:()=>reqMap.size + } } /** - * - * @param {*} _self reference to self of the main window (self) or reference to a worker - * @param {*} handlers - object where key if method name, and value ih handler - * @returns - */ -export const initMessaging = (_self, handlers) => { - // service worker has the postMessage on the .controller, it is not na same object as addEventListener - const out = messageSender(_self.postMessage ? _self : _self.controller) - out.listener = messageHandler(handlers, out.sendResponse, out.sendError) - out.self = _self - _self.addEventListener?.('message', out.listener) + * + * @param {*} _self + * @param {*} handlers + * @returns {object} +*/ +export const messageProxy = (_self, handlers, {sender, onJobCount}) => { + const { sendCmd, sendNotify, getRpcJobCount} = sender || initMessaging(_self, handlers,{onJobCount}) - return out + return new Proxy({ + getRpcJobCount + },{ + get(target, prop, receiver) { + if(prop in target) return target[prop] + if(prop.startsWith('on')){ + return target[prop] = function(...params){ + sendNotify(prop, params) + } + } + return target[prop] = function(...params){ + return sendCmd(prop, params) + } + }, + }) } diff --git a/packages/worker/worker.js b/packages/worker/worker.js index 3ae0319..6a3319a 100644 --- a/packages/worker/worker.js +++ b/packages/worker/worker.js @@ -7,6 +7,50 @@ import { combineParameterDefinitions, getParameterDefinitionsFromSource } from ' import { extractDefaults } from './src/extractDefaults.js' import { extractPathInfo, readAsArrayBuffer, readAsText } from '../fs-provider/fs-provider.js' +/** +@typedef Alias +@prop {String} name +@prop {String} path +*/ + +/** + @typedef RunScriptOptions + @prop {string} script - script source + @prop {string} url - script url/name + @prop {string} base - base url + @prop {string} base - root (do not allow paths below that root) + + @typedef ExportDataOptions + @prop {string} format + + @typedef ClearFileCacheOptions + @prop {Array} files + + @typedef RunMainOptions + @prop {Object} params + + @typedef InitOptions + @prop {String} baseURI - to resolve inital relative path + @prop {Array} alias - + @prop {Array} bundle - bundle alias {name:path} + + @typedef ScriptResponse + @prop {Array} entities + @prop {number} mainTime - script run time + @prop {number} convertTime - tim converting script output to gl data + + +@typedef JscadWorker +@prop {String} name +@prop {(options:InitOptions)=>Promise} init +@prop {(options:RunMainOptions)=>Promise} runMain +@prop {(options:RunScriptOptions)=>Promise} runScript +@prop {(options:ExportDataOptions)=>Promise} exportData +@prop {(options:ClearFileCacheOptions)=>Promise} clearFileCache +@prop {()=>Promise} clearTempCache + +*/ + let main self.JSCAD_WORKER_ENV = {} let transformFunc = x => x @@ -29,8 +73,8 @@ export const flatten = arr=>{ return out } -export const init = params => { - let { baseURI, alias = [], bundles = {} } = params +export const init = options => { + let { baseURI, alias = [], bundles = {} } = options if (baseURI) globalBase = baseURI if (bundles) Object.assign(requireCache.bundleAlias, bundles) @@ -39,7 +83,7 @@ export const init = params => { requireCache.alias[name] = path }) console.log('init alias', alias, 'bundles',bundles) - userInstances = params.userInstances + userInstances = options.userInstances } async function readFileFile(file, {bin=false}={}){ @@ -128,3 +172,5 @@ export const initWorker = (transform, exportData, _importData) => { client = initMessaging(self, handlers) } + + From d939e8f893b6f07f05826d61f2f545e81874840d Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Mon, 11 Mar 2024 23:36:37 +0100 Subject: [PATCH 06/25] typo --- packages/postmessage/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/postmessage/README.md b/packages/postmessage/README.md index ff31720..6bb7f30 100644 --- a/packages/postmessage/README.md +++ b/packages/postmessage/README.md @@ -40,6 +40,12 @@ Here is partial sample of jsdoc definitions for jscad worker @prop {Array} alias - @prop {Array} bundles - bundle alias {name:path} +@typedef RunScriptOptions +@prop {string} script - script source +@prop {string} url - script url/name +@prop {string} base - base url +@prop {string} base - root (do not allow paths below that root) + @typedef ScriptResponse @prop {Array} entities @prop {number} mainTime - script run time @@ -48,7 +54,7 @@ Here is partial sample of jsdoc definitions for jscad worker @typedef JscadWorker @prop {String} name @prop {(options:InitOptions)=>Promise} init -@prop {(options:RunMainOptions)=>Promise} runMain +@prop {(options:RunScriptOptions)=>Promise} runScript */ ``` From 955355405f83995d466244960858f9715e217856 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Mon, 11 Mar 2024 23:39:24 +0100 Subject: [PATCH 07/25] withTransferable --- packages/postmessage/README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/postmessage/README.md b/packages/postmessage/README.md index 6bb7f30..0c4892d 100644 --- a/packages/postmessage/README.md +++ b/packages/postmessage/README.md @@ -83,13 +83,15 @@ const result = await workerApi.runScript({ script}) // result is now known to be ScriptResponse and you get autocomplete ``` - - - ## transferable It is simple to send transferable objects when sending a message to the worker by adding a parameter to `postMessage`. It is however more tricky to support transferable for return values without complicating simple use cases that do not need transferable. If you have a method that can be called and it needs to return transferable then you must use object as a return value. -When returning such object, include `__transferable` key in the return value. It will not be in the data at the receiving end but will be taken out and passed to postMessage as a transferable parameter. +When returning such object in call to `withTransferable` before returning the value. +It will not be in the data at the receiving end, but will be taken out and passed to postMessage as the transferable parameter. + +```js + return withTransferable({ entities, mainTime }, transferable) +``` From 2a5a2b1b60df5bbeae5e0c04a7ab81f2ed6b4f52 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Mon, 11 Mar 2024 23:39:45 +0100 Subject: [PATCH 08/25] withTransferable example --- packages/postmessage/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/postmessage/README.md b/packages/postmessage/README.md index 0c4892d..48edcc9 100644 --- a/packages/postmessage/README.md +++ b/packages/postmessage/README.md @@ -92,6 +92,7 @@ If you have a method that can be called and it needs to return transferable then When returning such object in call to `withTransferable` before returning the value. It will not be in the data at the receiving end, but will be taken out and passed to postMessage as the transferable parameter. +Exmaple from jscad worker ```js return withTransferable({ entities, mainTime }, transferable) ``` From 6612194d345e29eccf9aafed243f860860f46196 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Tue, 12 Mar 2024 10:42:26 +0100 Subject: [PATCH 09/25] typescript example --- apps/engine-test/tsconfig.json | 1 + apps/jscad-web/tsconfig.json | 1 + packages/postmessage/README.md | 37 ++++++++++++++++++++++++-- packages/worker/worker.d.ts | 47 ++++++++++++++++++++++++++++++++++ packages/worker/worker.js | 8 +++--- 5 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 packages/worker/worker.d.ts diff --git a/apps/engine-test/tsconfig.json b/apps/engine-test/tsconfig.json index 4c9d069..9e48a45 100644 --- a/apps/engine-test/tsconfig.json +++ b/apps/engine-test/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "allowJs": true, + "skipLibCheck": true, "moduleResolution": "node16" }, "exclude": ["node_modules", "build_dev", "build", "dist"] diff --git a/apps/jscad-web/tsconfig.json b/apps/jscad-web/tsconfig.json index 9e23180..9c45611 100644 --- a/apps/jscad-web/tsconfig.json +++ b/apps/jscad-web/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "allowJs": true, + "skipLibCheck": true, "outDir": "build_dev", "moduleResolution": "node16" }, diff --git a/packages/postmessage/README.md b/packages/postmessage/README.md index 48edcc9..e0be8a9 100644 --- a/packages/postmessage/README.md +++ b/packages/postmessage/README.md @@ -38,13 +38,12 @@ Here is partial sample of jsdoc definitions for jscad worker @typedef InitOptions @prop {String} baseURI - to resolve inital relative path @prop {Array} alias - -@prop {Array} bundles - bundle alias {name:path} +@prop {Array} bundles - bundle alias {name:path} @typedef RunScriptOptions @prop {string} script - script source @prop {string} url - script url/name @prop {string} base - base url -@prop {string} base - root (do not allow paths below that root) @typedef ScriptResponse @prop {Array} entities @@ -58,6 +57,40 @@ Here is partial sample of jsdoc definitions for jscad worker */ ``` +For me, TypeScript is nicer to write declarations, but I still prefer [JSDoc](https://alexharri.com/blog/jsdoc-as-an-alternative-typescript-syntax) as it requires less tooling and also works in IDE. + +You can to it either way you prefer, so here is also typescript version of the sample: + +```ts +export interface InitOptions { + /** to resolve inital relative path */ + baseURI:String, + alias: Array, + /** bundle alias {name:path} */ + bundles: Array>, +} + +export interface RunScriptOptions { + /** script source */ + script: string, + /** script url/name */ + url: string, + /** base url */ + base: string, +} + +export interface ScriptResponse { + entities: Array, + /** script run time */ + mainTime: number, +} + +export interface JscadWorker { + async init(options:InitOptions):void, + async init(options:RunScriptOptions):ScriptResponse, +} +``` + Manuall calling `init` worker method documented above, requires sending `worker.postMessage({method:'init', params:[{bundles}]})` just to trigger the method. Then you would also need to handle response, and wrap it all into a promise diff --git a/packages/worker/worker.d.ts b/packages/worker/worker.d.ts new file mode 100644 index 0000000..8fa1d18 --- /dev/null +++ b/packages/worker/worker.d.ts @@ -0,0 +1,47 @@ +/** +@typedef InitOptions +@prop {String} baseURI - to resolve inital relative path +@prop {Array} alias - +@prop {Array} bundles - bundle alias {name:path} + +@typedef RunScriptOptions +@prop {string} script - script source +@prop {string} url - script url/name +@prop {string} base - base url + +@typedef ScriptResponse +@prop {Array} entities +@prop {number} mainTime - script run time + +@typedef JscadWorker +@prop {(options:InitOptions)=>Promise} init +@prop {(options:RunScriptOptions)=>Promise} runScript +*/ + +export interface InitOptions { + /** to resolve inital relative path */ + baseURI:String, + alias: Array, + /** bundle alias {name:path} */ + bundles: Array>, +} + +export interface RunScriptOptions { + /** script source */ + script: string, + /** script url/name */ + url: string, + /** base url */ + base: string, +} + +export interface ScriptResponse { + entities: Array, + /** script run time */ + mainTime: number, +} + +export interface JscadWorker { + async init(options:InitOptions):void, + async init(options:RunScriptOptions):ScriptResponse, +} diff --git a/packages/worker/worker.js b/packages/worker/worker.js index 6a3319a..7162f29 100644 --- a/packages/worker/worker.js +++ b/packages/worker/worker.js @@ -9,12 +9,10 @@ import { extractPathInfo, readAsArrayBuffer, readAsText } from '../fs-provider/f /** @typedef Alias -@prop {String} name -@prop {String} path -*/ + @prop {String} name + @prop {String} path -/** - @typedef RunScriptOptions +@typedef RunScriptOptions @prop {string} script - script source @prop {string} url - script url/name @prop {string} base - base url From 2f17fd190aed63d6dd9eb0305ba8d3bbf70390d9 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Tue, 12 Mar 2024 10:45:31 +0100 Subject: [PATCH 10/25] a notice --- packages/postmessage/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/postmessage/README.md b/packages/postmessage/README.md index e0be8a9..342a0f7 100644 --- a/packages/postmessage/README.md +++ b/packages/postmessage/README.md @@ -32,6 +32,9 @@ The RPC protocol is inspired by JSONRPC, as I personally found it useful in mult With modern JavaScript we can go few steps further and make communication with worker be as simple as calling methods(all of them async ofc.). +If you create a class that handles incomming rpc calls, NOTICE THAT unless all of your methods are actually async, you can not just use the declaration +of your class for the side that is takling to your class via postMessage. You must create an interface that has all the methods declared async. + Here is partial sample of jsdoc definitions for jscad worker ```ts /** From e97dc4d9f01be8d4ac50137c48264c4774fc7b76 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Tue, 12 Mar 2024 10:46:25 +0100 Subject: [PATCH 11/25] typo --- packages/postmessage/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/postmessage/README.md b/packages/postmessage/README.md index 342a0f7..370ef5d 100644 --- a/packages/postmessage/README.md +++ b/packages/postmessage/README.md @@ -62,7 +62,7 @@ Here is partial sample of jsdoc definitions for jscad worker For me, TypeScript is nicer to write declarations, but I still prefer [JSDoc](https://alexharri.com/blog/jsdoc-as-an-alternative-typescript-syntax) as it requires less tooling and also works in IDE. -You can to it either way you prefer, so here is also typescript version of the sample: +You can do it either way you prefer, so here is also typescript version of the sample: ```ts export interface InitOptions { From 2d90485a330bb4fead48df6736ca360ebd0db046 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Tue, 12 Mar 2024 12:49:06 +0100 Subject: [PATCH 12/25] readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f90eb7a..ae995db 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,8 @@ If you want to discuss jscad or jscadui you can also join us on discord: https:/ Most of the things are work in progres, but some parts are pretty ready to be used - [packages/html-gizmo](./packages/html-gizmo) - a gizmo to display current camera direction - +- [file-format/3mf-export](./file-format/3mf-export) - [![npm version](https://badge.fury.io/js/@jscadui%2F3mf-export.svg)](https://badge.fury.io/js/@jscadui%2F3mf-export) 3mf-export (also used by mynifold) +- # demo [jscad.app](https://jscad.app) is a nice demo and our attempt at making a an improved version of [openjscad.xyz](https://openjscad.xyz). From c930e00cebe1435386045f8cb6595a2f21513104 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Tue, 12 Mar 2024 12:57:10 +0100 Subject: [PATCH 13/25] badges, and postmessage v0.2.0 --- README.md | 5 +++-- file-format/3mf-export/README.md | 1 + packages/html-gizmo/README.md | 2 ++ packages/postmessage/README.md | 1 + packages/postmessage/cjs/index.js | 2 ++ packages/postmessage/cjs/index.js.map | 7 +++++++ packages/postmessage/esm/index.js | 2 ++ packages/postmessage/esm/index.js.map | 7 +++++++ packages/postmessage/package.json | 2 +- 9 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 packages/postmessage/cjs/index.js create mode 100644 packages/postmessage/cjs/index.js.map create mode 100644 packages/postmessage/esm/index.js create mode 100644 packages/postmessage/esm/index.js.map diff --git a/README.md b/README.md index ae995db..3e7ca5f 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,10 @@ If you want to discuss jscad or jscadui you can also join us on discord: https:/ Most of the things are work in progres, but some parts are pretty ready to be used -- [packages/html-gizmo](./packages/html-gizmo) - a gizmo to display current camera direction +- [packages/html-gizmo](./packages/html-gizmo) - [![npm version](https://badge.fury.io/js/@jscadui%2Fhtml-gizmo.svg)](https://badge.fury.io/js/@jscadui%2Fhtml-gizmo) a gizmo to display current camera direction - [file-format/3mf-export](./file-format/3mf-export) - [![npm version](https://badge.fury.io/js/@jscadui%2F3mf-export.svg)](https://badge.fury.io/js/@jscadui%2F3mf-export) 3mf-export (also used by mynifold) -- +- [packages/postmessage](./packages/postmessage) - [![npm version](https://badge.fury.io/js/@jscadui%2Fpostmessage.svg)](https://badge.fury.io/js/@jscadui%2Fpostmessage) postMessage quality of life improvement + # demo [jscad.app](https://jscad.app) is a nice demo and our attempt at making a an improved version of [openjscad.xyz](https://openjscad.xyz). diff --git a/file-format/3mf-export/README.md b/file-format/3mf-export/README.md index 18a4b3b..f094eab 100644 --- a/file-format/3mf-export/README.md +++ b/file-format/3mf-export/README.md @@ -1,4 +1,5 @@ # 3mf export MVP +[![npm version](https://badge.fury.io/js/@jscadui%2F3mf-export.svg)](https://badge.fury.io/js/@jscadui%2F3mf-export) This is a set of functions to generate 3mf content for exporting 3d models and optionally embeding a thumbnail. diff --git a/packages/html-gizmo/README.md b/packages/html-gizmo/README.md index 8bf3709..9684cad 100644 --- a/packages/html-gizmo/README.md +++ b/packages/html-gizmo/README.md @@ -1,5 +1,7 @@ # Cube camera gizmo Web Component +[![npm version](https://badge.fury.io/js/@jscadui%2Fhtml-gizmo.svg)](https://badge.fury.io/js/@jscadui%2Fhtml-gizmo) + | ![gizmo in action](docs/gizmo.gif) | Check the demo at: https://hrgdavor.github.io/jscadui/html-gizmo/ | | ---- | ---- | diff --git a/packages/postmessage/README.md b/packages/postmessage/README.md index 370ef5d..36ab3e7 100644 --- a/packages/postmessage/README.md +++ b/packages/postmessage/README.md @@ -1,4 +1,5 @@ # postMessage utility +[![npm version](https://badge.fury.io/js/@jscadui%2Fpostmessage.svg)](https://badge.fury.io/js/@jscadui%2Fpostmessage) Allows for simpler usage of [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage) by defining a RPC protocol that can send notifications or call methods. Calling methods is handled with Promises because the postMessage is async by definition. diff --git a/packages/postmessage/cjs/index.js b/packages/postmessage/cjs/index.js new file mode 100644 index 0000000..17fc127 --- /dev/null +++ b/packages/postmessage/cjs/index.js @@ -0,0 +1,2 @@ +var w=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var k=(e,t)=>{for(var d in t)w(e,d,{get:t[d],enumerable:!0})},T=(e,t,d,f)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of N(t))!P.call(e,a)&&a!==d&&w(e,a,{get:()=>t[a],enumerable:!(f=p(t,a))||f.enumerable});return e};var q=e=>T(w({},"__esModule",{value:!0}),e);var A={};k(A,{initMessaging:()=>_,messageProxy:()=>C,withTransferable:()=>z});module.exports=q(A);var v=1,m=new Map,E="__RESPONSE__",x=Symbol.for("__transferable__"),z=(e,t)=>(e[x]=t,e),M=e=>e?e.map(t=>t.buffer||t):[],_=(e,t,{onJobCount:d}={})=>{let f=e.postMessage?e:e.controller,a=(o,s)=>{let r=o?.[x];r&&delete o[x];try{f.postMessage({method:E,params:o,id:s},M(r))}catch(n){throw console.error("failed to send ",o,r),n}},g=(o,s)=>{try{let r=o.stack;f.postMessage({method:E,error:o,stack:r,id:s})}catch(r){throw console.error("failed to send ",r),r}},y=(o,s=[],r=[])=>{f.postMessage({method:o,params:s},M(r))},u=(o,s=[],r=[],n)=>{let l=v++;return f.postMessage({method:o,params:s,id:l},M(r)),new Promise((h,c)=>{m.set(l,[h,c]),d?.(m.size),n&&setTimeout(()=>{c("timeout")},n)})},i=async o=>{let{method:s,params:r,id:n,error:l,stack:R}=o.data;if(n&&s===E){let c=m.get(n);if(!c)return console.error(`req ${n} not found`,n,o.data,o);m.delete(n),d?.(m.size);let[S,b]=c;l?(l.stack=R,b(l)):S(r);return}let h=t[s];if(!h){let c="no handler for type: "+s;throw console.error(c,o),new Error(c)}try{let c=await h(...r);n&&a(c,n)}catch(c){console.error(`error executing command ${s}`,r,c),g(c,n)}};return e.addEventListener?.("message",i),{sendCmd:u,sendNotify:y,sendResponse:a,sendError:g,listener:i,self:e,getRpcJobCount:()=>m.size}},C=(e,t,{sender:d,onJobCount:f})=>{let{sendCmd:a,sendNotify:g,getRpcJobCount:y}=d||_(e,t,{onJobCount:f});return new Proxy({getRpcJobCount:y},{get(u,i,o){return i in u?u[i]:i.startsWith("on")?u[i]=function(...s){g(i,s)}:u[i]=function(...s){return a(i,s)}}})}; +//# sourceMappingURL=index.js.map diff --git a/packages/postmessage/cjs/index.js.map b/packages/postmessage/cjs/index.js.map new file mode 100644 index 0000000..b7b7274 --- /dev/null +++ b/packages/postmessage/cjs/index.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../index.js"], + "sourcesContent": ["let seq = 1\r\nlet reqMap = new Map()\r\nconst RESPONSE = '__RESPONSE__'\r\nconst TRANSFERABLE = Symbol.for('__transferable__')\r\n\r\nexport const withTransferable = (params,trans)=>{\r\n params[TRANSFERABLE] = trans\r\n return params\r\n}\r\n\r\nconst fixTransfer = trans => (trans ? trans.map(a => a.buffer || a) : [])\r\n\r\n/**\r\n *\r\n * @param {*} _self reference to self of the main window (self) or reference to a worker\r\n * @param {*} handlers - object where key if method name, and value ih handler\r\n * @returns\r\n */\r\nexport const initMessaging = (_self, handlers, {onJobCount}={}) => {\r\n // on service worker, postMessage is on the controller\r\n const ___self = _self.postMessage ? _self : _self.controller\r\n const sendResponse = (result, id) => {\r\n let trans = result?.[TRANSFERABLE]\r\n if (trans) {\r\n delete result[TRANSFERABLE]\r\n }\r\n try {\r\n ___self.postMessage({ method: RESPONSE, params: result, id }, fixTransfer(trans))\r\n \r\n } catch (error) {\r\n console.error('failed to send ', result, trans)\r\n throw error\r\n }\r\n }\r\n\r\n const sendError = (error, id) => {\r\n try {\r\n // serialize stacktrace so it isn't lost in transit\r\n const stack = error.stack\r\n ___self.postMessage({ method: RESPONSE, error, stack, id })\r\n } catch (error) {\r\n console.error('failed to send ', error)\r\n throw error\r\n }\r\n }\r\n\r\n /**\r\n * Send a message with no response\r\n *\r\n * @param {string} method\r\n * @param {object} params\r\n * @param {Array} trans\r\n */\r\n const sendNotify = (method, params = [], trans = []) => {\r\n ___self.postMessage({ method, params }, fixTransfer(trans))\r\n }\r\n\r\n /**\r\n * Send a message with response expected\r\n *\r\n * @param {string} method\r\n * @param {object} params\r\n * @param {Array} trans\r\n * @param {number?} timeout\r\n * @returns {Promise} resolves when response is received\r\n */\r\n const sendCmd = (method, params = [], trans = [], timeout) => {\r\n const id = seq++\r\n ___self.postMessage({ method, params, id }, fixTransfer(trans))\r\n\r\n const out = new Promise((resolve, reject) => {\r\n reqMap.set(id, [resolve, reject])\r\n onJobCount?.(reqMap.size)\r\n if (timeout) {\r\n setTimeout(() => {\r\n reject('timeout')\r\n }, timeout)\r\n }\r\n })\r\n return out\r\n }\r\n\r\n const listener = async (e) => {\r\n const { method, params, id, error, stack } = e.data\r\n if (id && method === RESPONSE) {\r\n const p = reqMap.get(id)\r\n\r\n if (!p) return console.error(`req ${id} not found`,id, e.data, e)\r\n reqMap.delete(id)\r\n onJobCount?.(reqMap.size)\r\n\r\n const [resolve, reject] = p\r\n if (error) {\r\n // restore stacktrace\r\n error.stack = stack\r\n reject(error)\r\n } else {\r\n resolve(params)\r\n }\r\n\r\n return\r\n }\r\n\r\n const fn = handlers[method]\r\n if (!fn) {\r\n const msg = 'no handler for type: ' + method\r\n console.error(msg, e)\r\n throw new Error(msg)\r\n }\r\n try {\r\n const out = await fn(...params)\r\n if (id) {\r\n sendResponse(out, id)\r\n }\r\n } catch (error) {\r\n console.error(`error executing command ${method}`, params, error)\r\n sendError(error, id)\r\n }\r\n }\r\n \r\n _self.addEventListener?.('message', listener)\r\n\r\n return { \r\n sendCmd, \r\n sendNotify, \r\n sendResponse, \r\n sendError, \r\n listener, \r\n self:_self, \r\n getRpcJobCount:()=>reqMap.size \r\n }\r\n}\r\n\r\n/**\r\n * \r\n * @param {*} _self \r\n * @param {*} handlers \r\n * @returns {object}\r\n*/\r\nexport const messageProxy = (_self, handlers, {sender, onJobCount}) => {\r\n const { sendCmd, sendNotify, getRpcJobCount} = sender || initMessaging(_self, handlers,{onJobCount})\r\n\r\n return new Proxy({\r\n getRpcJobCount\r\n },{\r\n get(target, prop, receiver) {\r\n if(prop in target) return target[prop]\r\n if(prop.startsWith('on')){\r\n return target[prop] = function(...params){\r\n sendNotify(prop, params)\r\n } \r\n }\r\n return target[prop] = function(...params){\r\n return sendCmd(prop, params)\r\n }\r\n },\r\n })\r\n}\r\n"], + "mappings": "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", + "names": ["postmessage_exports", "__export", "initMessaging", "messageProxy", "withTransferable", "__toCommonJS", "seq", "reqMap", "RESPONSE", "TRANSFERABLE", "params", "trans", "fixTransfer", "a", "_self", "handlers", "onJobCount", "___self", "sendResponse", "result", "id", "error", "sendError", "stack", "sendNotify", "method", "sendCmd", "timeout", "resolve", "reject", "listener", "e", "p", "fn", "msg", "out", "sender", "getRpcJobCount", "target", "prop", "receiver"] +} diff --git a/packages/postmessage/esm/index.js b/packages/postmessage/esm/index.js new file mode 100644 index 0000000..a3d9bb0 --- /dev/null +++ b/packages/postmessage/esm/index.js @@ -0,0 +1,2 @@ +var S=1,u=new Map,w="__RESPONSE__",M=Symbol.for("__transferable__"),p=(s,a)=>(s[M]=a,s),E=s=>s?s.map(a=>a.buffer||a):[],b=(s,a,{onJobCount:l}={})=>{let i=s.postMessage?s:s.controller,m=(e,t)=>{let o=e?.[M];o&&delete e[M];try{i.postMessage({method:w,params:e,id:t},E(o))}catch(r){throw console.error("failed to send ",e,o),r}},g=(e,t)=>{try{let o=e.stack;i.postMessage({method:w,error:e,stack:o,id:t})}catch(o){throw console.error("failed to send ",o),o}},y=(e,t=[],o=[])=>{i.postMessage({method:e,params:t},E(o))},d=(e,t=[],o=[],r)=>{let f=S++;return i.postMessage({method:e,params:t,id:f},E(o)),new Promise((h,n)=>{u.set(f,[h,n]),l?.(u.size),r&&setTimeout(()=>{n("timeout")},r)})},c=async e=>{let{method:t,params:o,id:r,error:f,stack:x}=e.data;if(r&&t===w){let n=u.get(r);if(!n)return console.error(`req ${r} not found`,r,e.data,e);u.delete(r),l?.(u.size);let[R,_]=n;f?(f.stack=x,_(f)):R(o);return}let h=a[t];if(!h){let n="no handler for type: "+t;throw console.error(n,e),new Error(n)}try{let n=await h(...o);r&&m(n,r)}catch(n){console.error(`error executing command ${t}`,o,n),g(n,r)}};return s.addEventListener?.("message",c),{sendCmd:d,sendNotify:y,sendResponse:m,sendError:g,listener:c,self:s,getRpcJobCount:()=>u.size}},N=(s,a,{sender:l,onJobCount:i})=>{let{sendCmd:m,sendNotify:g,getRpcJobCount:y}=l||b(s,a,{onJobCount:i});return new Proxy({getRpcJobCount:y},{get(d,c,e){return c in d?d[c]:c.startsWith("on")?d[c]=function(...t){g(c,t)}:d[c]=function(...t){return m(c,t)}}})};export{b as initMessaging,N as messageProxy,p as withTransferable}; +//# sourceMappingURL=index.js.map diff --git a/packages/postmessage/esm/index.js.map b/packages/postmessage/esm/index.js.map new file mode 100644 index 0000000..6ea897c --- /dev/null +++ b/packages/postmessage/esm/index.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../index.js"], + "sourcesContent": ["let seq = 1\r\nlet reqMap = new Map()\r\nconst RESPONSE = '__RESPONSE__'\r\nconst TRANSFERABLE = Symbol.for('__transferable__')\r\n\r\nexport const withTransferable = (params,trans)=>{\r\n params[TRANSFERABLE] = trans\r\n return params\r\n}\r\n\r\nconst fixTransfer = trans => (trans ? trans.map(a => a.buffer || a) : [])\r\n\r\n/**\r\n *\r\n * @param {*} _self reference to self of the main window (self) or reference to a worker\r\n * @param {*} handlers - object where key if method name, and value ih handler\r\n * @returns\r\n */\r\nexport const initMessaging = (_self, handlers, {onJobCount}={}) => {\r\n // on service worker, postMessage is on the controller\r\n const ___self = _self.postMessage ? _self : _self.controller\r\n const sendResponse = (result, id) => {\r\n let trans = result?.[TRANSFERABLE]\r\n if (trans) {\r\n delete result[TRANSFERABLE]\r\n }\r\n try {\r\n ___self.postMessage({ method: RESPONSE, params: result, id }, fixTransfer(trans))\r\n \r\n } catch (error) {\r\n console.error('failed to send ', result, trans)\r\n throw error\r\n }\r\n }\r\n\r\n const sendError = (error, id) => {\r\n try {\r\n // serialize stacktrace so it isn't lost in transit\r\n const stack = error.stack\r\n ___self.postMessage({ method: RESPONSE, error, stack, id })\r\n } catch (error) {\r\n console.error('failed to send ', error)\r\n throw error\r\n }\r\n }\r\n\r\n /**\r\n * Send a message with no response\r\n *\r\n * @param {string} method\r\n * @param {object} params\r\n * @param {Array} trans\r\n */\r\n const sendNotify = (method, params = [], trans = []) => {\r\n ___self.postMessage({ method, params }, fixTransfer(trans))\r\n }\r\n\r\n /**\r\n * Send a message with response expected\r\n *\r\n * @param {string} method\r\n * @param {object} params\r\n * @param {Array} trans\r\n * @param {number?} timeout\r\n * @returns {Promise} resolves when response is received\r\n */\r\n const sendCmd = (method, params = [], trans = [], timeout) => {\r\n const id = seq++\r\n ___self.postMessage({ method, params, id }, fixTransfer(trans))\r\n\r\n const out = new Promise((resolve, reject) => {\r\n reqMap.set(id, [resolve, reject])\r\n onJobCount?.(reqMap.size)\r\n if (timeout) {\r\n setTimeout(() => {\r\n reject('timeout')\r\n }, timeout)\r\n }\r\n })\r\n return out\r\n }\r\n\r\n const listener = async (e) => {\r\n const { method, params, id, error, stack } = e.data\r\n if (id && method === RESPONSE) {\r\n const p = reqMap.get(id)\r\n\r\n if (!p) return console.error(`req ${id} not found`,id, e.data, e)\r\n reqMap.delete(id)\r\n onJobCount?.(reqMap.size)\r\n\r\n const [resolve, reject] = p\r\n if (error) {\r\n // restore stacktrace\r\n error.stack = stack\r\n reject(error)\r\n } else {\r\n resolve(params)\r\n }\r\n\r\n return\r\n }\r\n\r\n const fn = handlers[method]\r\n if (!fn) {\r\n const msg = 'no handler for type: ' + method\r\n console.error(msg, e)\r\n throw new Error(msg)\r\n }\r\n try {\r\n const out = await fn(...params)\r\n if (id) {\r\n sendResponse(out, id)\r\n }\r\n } catch (error) {\r\n console.error(`error executing command ${method}`, params, error)\r\n sendError(error, id)\r\n }\r\n }\r\n \r\n _self.addEventListener?.('message', listener)\r\n\r\n return { \r\n sendCmd, \r\n sendNotify, \r\n sendResponse, \r\n sendError, \r\n listener, \r\n self:_self, \r\n getRpcJobCount:()=>reqMap.size \r\n }\r\n}\r\n\r\n/**\r\n * \r\n * @param {*} _self \r\n * @param {*} handlers \r\n * @returns {object}\r\n*/\r\nexport const messageProxy = (_self, handlers, {sender, onJobCount}) => {\r\n const { sendCmd, sendNotify, getRpcJobCount} = sender || initMessaging(_self, handlers,{onJobCount})\r\n\r\n return new Proxy({\r\n getRpcJobCount\r\n },{\r\n get(target, prop, receiver) {\r\n if(prop in target) return target[prop]\r\n if(prop.startsWith('on')){\r\n return target[prop] = function(...params){\r\n sendNotify(prop, params)\r\n } \r\n }\r\n return target[prop] = function(...params){\r\n return sendCmd(prop, params)\r\n }\r\n },\r\n })\r\n}\r\n"], + "mappings": "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", + "names": ["seq", "reqMap", "RESPONSE", "TRANSFERABLE", "withTransferable", "params", "trans", "fixTransfer", "initMessaging", "_self", "handlers", "onJobCount", "___self", "sendResponse", "result", "id", "error", "sendError", "stack", "sendNotify", "method", "sendCmd", "timeout", "resolve", "reject", "listener", "p", "fn", "msg", "out", "messageProxy", "sender", "getRpcJobCount", "target", "prop", "receiver"] +} diff --git a/packages/postmessage/package.json b/packages/postmessage/package.json index f12857e..308e93f 100644 --- a/packages/postmessage/package.json +++ b/packages/postmessage/package.json @@ -2,7 +2,7 @@ "type": "module", "sideEffects": false, "name": "@jscadui/postmessage", - "version": "0.1.0", + "version": "0.2.0", "description": "postMessage utility for worker and main window", "keywords": ["postMessage","rpc","async","promise","worker"], "main": "index.js", From c2b24f3d79c9d4f3e64e23e92a273e2749330a34 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Tue, 12 Mar 2024 12:59:57 +0100 Subject: [PATCH 14/25] badges --- README.md | 6 +++--- file-format/3mf-export/README.md | 2 +- packages/html-gizmo/README.md | 2 +- packages/postmessage/README.md | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3e7ca5f..16eb231 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,9 @@ If you want to discuss jscad or jscadui you can also join us on discord: https:/ Most of the things are work in progres, but some parts are pretty ready to be used -- [packages/html-gizmo](./packages/html-gizmo) - [![npm version](https://badge.fury.io/js/@jscadui%2Fhtml-gizmo.svg)](https://badge.fury.io/js/@jscadui%2Fhtml-gizmo) a gizmo to display current camera direction -- [file-format/3mf-export](./file-format/3mf-export) - [![npm version](https://badge.fury.io/js/@jscadui%2F3mf-export.svg)](https://badge.fury.io/js/@jscadui%2F3mf-export) 3mf-export (also used by mynifold) -- [packages/postmessage](./packages/postmessage) - [![npm version](https://badge.fury.io/js/@jscadui%2Fpostmessage.svg)](https://badge.fury.io/js/@jscadui%2Fpostmessage) postMessage quality of life improvement +- [packages/html-gizmo](./packages/html-gizmo) - [![npm version](https://badge.fury.io/js/@jscadui%2Fhtml-gizmo.svg)](https://www.npmjs.com/package/@jscadui%2Fhtml-gizmo) a gizmo to display current camera direction +- [file-format/3mf-export](./file-format/3mf-export) - [![npm version](https://badge.fury.io/js/@jscadui%2F3mf-export.svg)](https://www.npmjs.com/package/@jscadui%2F3mf-export) 3mf-export (also used by mynifold) +- [packages/postmessage](./packages/postmessage) - [![npm version](https://badge.fury.io/js/@jscadui%2Fpostmessage.svg)](https://www.npmjs.com/package/@jscadui%2Fpostmessage) postMessage quality of life improvement # demo [jscad.app](https://jscad.app) is a nice demo and our attempt at making a an improved version of [openjscad.xyz](https://openjscad.xyz). diff --git a/file-format/3mf-export/README.md b/file-format/3mf-export/README.md index f094eab..95369be 100644 --- a/file-format/3mf-export/README.md +++ b/file-format/3mf-export/README.md @@ -1,5 +1,5 @@ # 3mf export MVP -[![npm version](https://badge.fury.io/js/@jscadui%2F3mf-export.svg)](https://badge.fury.io/js/@jscadui%2F3mf-export) +[![npm version](https://badge.fury.io/js/@jscadui%2F3mf-export.svg)](https://www.npmjs.com/package/@jscadui%2F3mf-export) This is a set of functions to generate 3mf content for exporting 3d models and optionally embeding a thumbnail. diff --git a/packages/html-gizmo/README.md b/packages/html-gizmo/README.md index 9684cad..905467b 100644 --- a/packages/html-gizmo/README.md +++ b/packages/html-gizmo/README.md @@ -1,6 +1,6 @@ # Cube camera gizmo Web Component -[![npm version](https://badge.fury.io/js/@jscadui%2Fhtml-gizmo.svg)](https://badge.fury.io/js/@jscadui%2Fhtml-gizmo) +[![npm version](https://badge.fury.io/js/@jscadui%2Fhtml-gizmo.svg)](https://www.npmjs.com/package/@jscadui%2Fhtml-gizmo) | ![gizmo in action](docs/gizmo.gif) | Check the demo at: https://hrgdavor.github.io/jscadui/html-gizmo/ | | ---- | ---- | diff --git a/packages/postmessage/README.md b/packages/postmessage/README.md index 36ab3e7..3884e27 100644 --- a/packages/postmessage/README.md +++ b/packages/postmessage/README.md @@ -1,5 +1,5 @@ # postMessage utility -[![npm version](https://badge.fury.io/js/@jscadui%2Fpostmessage.svg)](https://badge.fury.io/js/@jscadui%2Fpostmessage) +[![npm version](https://badge.fury.io/js/@jscadui%2Fpostmessage.svg)](https://www.npmjs.com/package/@jscadui%2Fpostmessage) Allows for simpler usage of [postMessage](https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage) by defining a RPC protocol that can send notifications or call methods. Calling methods is handled with Promises because the postMessage is async by definition. From 98c3aa54cc6c978411e5880c3d5c850cc9138c73 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Tue, 12 Mar 2024 13:37:56 +0100 Subject: [PATCH 15/25] . --- packages/postmessage/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/postmessage/README.md b/packages/postmessage/README.md index 3884e27..ddc701e 100644 --- a/packages/postmessage/README.md +++ b/packages/postmessage/README.md @@ -6,7 +6,7 @@ Calling methods is handled with Promises because the postMessage is async by def If you use this utility both in your main thread and in the worker you will get the most benefits. -Consider these steps depending on the complexity +Consider these steps depending on the complexity: 1. **postMessage**: if you have only few messages pushing some data, you do not even need this 2. **RPC:** if you have messages, and some of them are response to a request(functionally) you should try formalizing a protocol and this RPC here can be a good starting point From 5079ec14214416db2a49f0603628bfea7b33145d Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Tue, 12 Mar 2024 16:28:13 +0100 Subject: [PATCH 16/25] Update notes.md (#100) --- notes.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/notes.md b/notes.md index 0a07ee3..1d9a5f7 100644 --- a/notes.md +++ b/notes.md @@ -1,3 +1,9 @@ +# refresh badge after publishing + +```sh +curl -X PURGE BADGE_URL +``` + # libs source for easier integrationd with IDE Make `index.js` that exports all from `src` to have errors in console or `console.log` display fine lanem taht is not `index.js`. In case where I had multiple libs separated, and because they are simple, code was directly in `index.js`. This caused visibility issues because many trace lines had `index.js` as source and then you need to look closely at full path to find out what lib is causing it. From 322328ef4ad8e7a0ad62e29028113210f86b75b8 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Tue, 12 Mar 2024 18:00:19 +0100 Subject: [PATCH 17/25] update engine test to new worker --- apps/engine-test/main.js | 64 +++++++++++++++++++---------------- apps/vue3-jscad/package.json | 2 +- package-lock.json | 6 ++-- packages/postmessage/index.js | 2 +- packages/worker/worker.js | 2 +- 5 files changed, 41 insertions(+), 35 deletions(-) diff --git a/apps/engine-test/main.js b/apps/engine-test/main.js index f46ade6..8aacdb6 100644 --- a/apps/engine-test/main.js +++ b/apps/engine-test/main.js @@ -3,7 +3,7 @@ import { JscadToCommon } from '@jscadui/format-jscad' import { Gizmo } from '@jscadui/html-gizmo' import { OrbitControl, OrbitState, closerAngle, getCommonRotCombined } from '@jscadui/orbit' import { genParams } from '@jscadui/params' -import { initMessaging } from '@jscadui/postmessage' +import { initMessaging, messageProxy } from '@jscadui/postmessage' import { makeAxes, makeGrid } from '@jscadui/scene' import * as themes from '@jscadui/themes' @@ -16,6 +16,8 @@ import { availableEngines, availableEnginesList } from './src/availableEngines' import { CurrentUrl } from './src/currentUrl' import { EngineState } from './src/engineState' +/** @typedef {import('@jscadui/worker').JscadWorker} JscadWorker*/ + const theme = themes.light const { subtract } = booleans const { translate } = transforms @@ -122,7 +124,7 @@ document.body.ondrop = async ev => { if (!sw) await initFs() showDrop(false) - sendCmd('clearTempCache', [{}]) + workerApi.clearTempCache() const { alias, script } = await fileDropped(sw, files) projectName = sw.projectName if (alias.length) { @@ -167,14 +169,29 @@ function save(blob, filename) { } function exportModel(format) { - sendCmd('exportData', { format }).then(({ data }) => { + workerApi.exportData({ format }).then(({ data }) => { console.log('save', fileToRun + '.stl', data) save(new Blob([data], { type: 'text/plain' }), fileToRun + '.stl') }).catch(setError) } window.exportModel = exportModel +const paramChangeCallback = async params => { + console.log('params changed', params) + let result = await workerApi.runMain({ params }) + handlers.entities(result) +} + +const runScript = async ({script, url = './index.js', base, root}) => { + const result = await workerApi.runScript({ script, url, base, root }) + console.log('result', result) + genParams({ target: byId('paramsDiv'), params: result.def || {}, callback: paramChangeCallback }) + handlers.entities(result) +} + +/** @type {JscadWorker} */ const worker = new Worker('./build/bundle.worker.js') +const workerApi = messageProxy(worker, handlers, { onJobCount: trackJobs }) const handlers = { entities: ({ entities }) => { if (!(entities instanceof Array)) entities = [entities] @@ -182,27 +199,30 @@ const handlers = { setError(undefined) }, } -const { sendCmd, sendNotify } = initMessaging(worker, handlers) const spinner = byId('spinner') -async function sendCmdAndSpin(method, params){ - spinner.style.display = 'block' - try{ - return await sendCmd(method, params) - }catch(error){ - setError(error) - throw error - }finally{ +let firstJobTimer + +function trackJobs(jobs) { + if (jobs === 1) { + // do not show spinner for fast renders + firstJobTimer = setTimeout(() => { + spinner.style.display = 'block' + }, 300) + } + if (jobs === 0) { + clearTimeout(firstJobTimer) spinner.style.display = 'none' } } -sendCmdAndSpin('init', [{ +await workerApi.init({ bundles: { '@jscad/modeling': toUrl('./build/bundle.jscad_modeling.js'), }, -}]).then(()=>{ - runScript({script:`const { sphere, geodesicSphere } = require('@jscad/modeling').primitives +}) + +runScript({script:`const { sphere, geodesicSphere } = require('@jscad/modeling').primitives const { translate, scale } = require('@jscad/modeling').transforms const main = () => [ @@ -220,20 +240,6 @@ sendCmdAndSpin('init', [{ module.exports = { main }` }) -}) - -const paramChangeCallback = async params => { - console.log('params changed', params) - let result = await sendCmdAndSpin('runMain', [{ params }]) - handlers.entities(result) -} - -const runScript = async ({script, url = './index.js', base, root}) => { - const result = await sendCmdAndSpin('runScript', [{ script, url, base, root }]) - console.log('result', result) - genParams({ target: byId('paramsDiv'), params: result.def || {}, callback: paramChangeCallback }) - handlers.entities(result) -} let sw async function initFs() { diff --git a/apps/vue3-jscad/package.json b/apps/vue3-jscad/package.json index b881156..c0a83a1 100644 --- a/apps/vue3-jscad/package.json +++ b/apps/vue3-jscad/package.json @@ -13,7 +13,7 @@ "@jscadui/html-gizmo": "^0.1.0", "@jscadui/orbit": "^0.1.0", "@jscadui/params": "^0.1.0", - "@jscadui/postmessage": "^0.1.0", + "@jscadui/postmessage": "^0.2.0", "vue": "^3.3.11" }, "devDependencies": { diff --git a/package-lock.json b/package-lock.json index 107a243..f89684e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -847,7 +847,7 @@ "@jscadui/html-gizmo": "^0.1.0", "@jscadui/orbit": "^0.1.0", "@jscadui/params": "^0.1.0", - "@jscadui/postmessage": "^0.1.0", + "@jscadui/postmessage": "^0.2.0", "vue": "^3.3.11" }, "devDependencies": { @@ -15536,7 +15536,7 @@ }, "packages/postmessage": { "name": "@jscadui/postmessage", - "version": "0.1.0", + "version": "0.2.0", "license": "MIT", "devDependencies": { "@trivago/prettier-plugin-sort-imports": "~3.3.0", @@ -28165,7 +28165,7 @@ "@jscadui/html-gizmo": "^0.1.0", "@jscadui/orbit": "^0.1.0", "@jscadui/params": "^0.1.0", - "@jscadui/postmessage": "^0.1.0", + "@jscadui/postmessage": "^0.2.0", "@vitejs/plugin-vue": "^4.5.2", "sass": "^1.69.6", "vite": "^5.0.10", diff --git a/packages/postmessage/index.js b/packages/postmessage/index.js index 9fcf95a..6b042b7 100644 --- a/packages/postmessage/index.js +++ b/packages/postmessage/index.js @@ -137,7 +137,7 @@ export const initMessaging = (_self, handlers, {onJobCount}={}) => { * @param {*} handlers * @returns {object} */ -export const messageProxy = (_self, handlers, {sender, onJobCount}) => { +export const messageProxy = (_self, handlers, {sender, onJobCount}={}) => { const { sendCmd, sendNotify, getRpcJobCount} = sender || initMessaging(_self, handlers,{onJobCount}) return new Proxy({ diff --git a/packages/worker/worker.js b/packages/worker/worker.js index 7162f29..27a1c9a 100644 --- a/packages/worker/worker.js +++ b/packages/worker/worker.js @@ -120,7 +120,7 @@ export async function runMain({ params } = {}) { const importReg = /import(?:(?:(?:[ \n\t]+([^ *\n\t\{\},]+)[ \n\t]*(?:,|[ \n\t]+))?([ \n\t]*\{(?:[ \n\t]*[^ \n\t"'\{\}]+[ \n\t]*,?)+\})?[ \n\t]*)|[ \n\t]*\*[ \n\t]*as[ \n\t]+([^ \n\t\{\}]+)[ \n\t]+)from[ \n\t]*(?:['"])([^'"\n]+)(['"])/ const exportReg = /export.*from/ -const runScript = async ({ script, url, base=globalBase, root=base }) => { +const runScript = async ({ script, url='jscad.js', base=globalBase, root=base }) => { console.log('run script with base:', base) if(!script) script = readFileWeb(resolveUrl(url, base, root).url) From 22ed872748066a563fd17c7310b031abcabcf2a8 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Tue, 12 Mar 2024 18:01:35 +0100 Subject: [PATCH 18/25] update engine test to new worker --- apps/engine-test/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/engine-test/main.js b/apps/engine-test/main.js index 8aacdb6..0edc827 100644 --- a/apps/engine-test/main.js +++ b/apps/engine-test/main.js @@ -128,7 +128,7 @@ document.body.ondrop = async ev => { const { alias, script } = await fileDropped(sw, files) projectName = sw.projectName if (alias.length) { - sendNotify('init', [{ alias }]) + workerApi.init({ alias }) } runScript({ url: sw.fileToRun, base: sw.base }) } catch (error) { @@ -246,7 +246,7 @@ async function initFs() { sw = await registerServiceWorker('bundle.fs-serviceworker.js?prefix=/swfs/') sw.defProjectName = 'jscad' sw.onfileschange = files => { - sendNotify('clearFileCache', [{ files }]) + workerApi.clearFileCache({ files }) if (sw.fileToRun) runScript({ url: sw.fileToRun, base: sw.base }) } } From c45bbaf71665a740cafebe27b8474c34fba75bb0 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Thu, 14 Mar 2024 09:13:03 +0100 Subject: [PATCH 19/25] default run start without docs --- apps/jscad-web/build.js | 6 +++--- apps/jscad-web/package.json | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/jscad-web/build.js b/apps/jscad-web/build.js index 5663a6c..38e1635 100644 --- a/apps/jscad-web/build.js +++ b/apps/jscad-web/build.js @@ -7,12 +7,12 @@ import {serve} from './serve.js' import { buildBundle, buildOne } from './src_build/esbuildUtil.js' // *************** read parameters ********************** -const { dev, port = 5120, serve:serveBuild=false } = parseArgs() +const { dev, port = 5120, serve:serveBuild=false, skipDocs=false } = parseArgs() const watch = dev const outDir = dev ? 'build_dev' : 'build' const docsDir = 'jscad/docs' // if docs dir does not exist, then clone jscad and run `npm run docs` to generate it -if (!existsSync(docsDir)) { +if (!skipDocs &&!existsSync(docsDir)) { console.log('generating docs') if (!existsSync('jscad')) { // TODO: faster to fetch https://github.com/jscad/OpenJSCAD.org/archive/refs/heads/master.zip @@ -29,7 +29,7 @@ mkdirSync(outDir, { recursive: true }) copyTask('static', outDir, { include: [], exclude: [], watch, filters: [] }) copyTask('examples', outDir+'/examples', { include: [], exclude: [], watch, filters: [] }) //in dev mode dont try to sync docs, just copy the first time -if(!(dev & existsSync(outDir + "/docs"))){ +if(!skipDocs && !(dev & existsSync(outDir + "/docs"))){ // this task is heavy copyTask(docsDir, outDir + "/docs", { include: [], exclude: [], watch:false, filters: [] }) } diff --git a/apps/jscad-web/package.json b/apps/jscad-web/package.json index 97b85e8..08af3e7 100644 --- a/apps/jscad-web/package.json +++ b/apps/jscad-web/package.json @@ -3,7 +3,8 @@ "version": "0.0.0", "type": "module", "scripts": { - "start": "node build.js --dev", + "start": "node build.js --dev --skipDocs", + "start:full": "node build.js --dev", "build": "node build.js", "serve": "node build.js --serve", "test": "ava" From 2c29742b0e4e5058d8eb90e4763bc9b9851f9ded Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Thu, 14 Mar 2024 09:16:09 +0100 Subject: [PATCH 20/25] Update README.md --- apps/jscad-web/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/jscad-web/README.md b/apps/jscad-web/README.md index 59ee669..7aaced3 100644 --- a/apps/jscad-web/README.md +++ b/apps/jscad-web/README.md @@ -20,6 +20,15 @@ To start the local development server, go to the `apps/jscad-web` directory and npm start ``` +this will start the dev server without generating jscad docs, which is like ok 99% of time. + +to start dev server that also has docs run + +``` +npm run start:full +``` + + ## Deployment To start the production server run: From 4b9d6972237ecc151cb55e73e762e55342859adb Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Thu, 14 Mar 2024 15:26:55 +0100 Subject: [PATCH 21/25] reload on package.json change, start docs on editing --- apps/jscad-web/main.js | 40 ++++++++++++++++-------- apps/jscad-web/online.editor.md | 48 +++++++++++++++++++++++++++++ apps/jscad-web/src/editor.js | 3 ++ apps/jscad-web/static/index.html | 1 + apps/jscad-web/static/main.css | 5 ++- packages/fs-provider/fs-provider.js | 10 +++--- 6 files changed, 90 insertions(+), 17 deletions(-) create mode 100644 apps/jscad-web/online.editor.md diff --git a/apps/jscad-web/main.js b/apps/jscad-web/main.js index 5046912..7fffe64 100644 --- a/apps/jscad-web/main.js +++ b/apps/jscad-web/main.js @@ -2,6 +2,7 @@ import { addToCache, clearFs, extractEntries, + analyzeProject, fileDropped, getFile, getFileContent, @@ -80,8 +81,13 @@ async function initFs() { }) sw.defProjectName = 'jscad' sw.onfileschange = files => { - workerApi.clearFileCache({ files }) - editor.filesChanged(files) + console.log('files', files) + if(files.includes('/package.json')){ + reloadProject() + }else{ + workerApi.clearFileCache({ files }) + editor.filesChanged(files) + } if (sw.fileToRun) runScript({ url: sw.fileToRun, base: sw.base }) } sw.getFile = path => getFile(path, sw) @@ -100,22 +106,30 @@ document.body.ondrop = async ev => { if (!sw) await initFs() showDrop(false) workerApi.clearTempCache() - saveMap = {} - const { alias, script } = await fileDropped(sw, files) - projectName = sw.projectName - if (alias.length) { - workerApi.init({ alias }) - } - let url = sw.fileToRun - runScript({ url, base: sw.base }) - editor.setSource(script, url) - editor.setFiles(sw.filesToCheck) + + await fileDropped(sw, files) + + reloadProject() + } catch (error) { setError(error) console.error(error) } } +async function reloadProject(){ + saveMap = {} + const { alias, script } = await analyzeProject(sw) + projectName = sw.projectName + if (alias.length) { + workerApi.init({ alias }) + } + let url = sw.fileToRun + runScript({ url, base: sw.base }) + editor.setSource(script, url) + editor.setFiles(sw.filesToCheck) +} + document.body.ondragover = ev => { ev.preventDefault() showDrop(true) @@ -176,7 +190,9 @@ const spinner = byId('spinner') let firstJobTimer function trackJobs(jobs) { + console.log('jobs', jobs) if (jobs === 1) { + clearTimeout(firstJobTimer) // do not show spinner for fast renders firstJobTimer = setTimeout(() => { spinner.style.display = 'block' diff --git a/apps/jscad-web/online.editor.md b/apps/jscad-web/online.editor.md new file mode 100644 index 0000000..8ca970b --- /dev/null +++ b/apps/jscad-web/online.editor.md @@ -0,0 +1,48 @@ + +## Easy: edit online + + +## Advanced: edit in your fav code editor + +Any text or code editor will work for this (vi, vim, neovim, notepad++, VSCodium , VSCode, Atom, SublimeText...). + +- create a file `someScript.js` +- copy exmaple code form jscad.app +- save the file +- drag&drop the file to jscad.app browser window +- put your editor and jscad.app side by side +- edit the file, save the changes, jscad.app will automatically re-run the script + +A chromium based browser is required, as other browser do not allow JS to see changes to the file. + +## Enthusiast: a multifile project + +## Expert: project with package.json and workspaces for internal libs + + +Bonus: if you change `main` in package.json, project will be reloaded +and that file will be the new starting point for the project without you needing +D&D again. + +https://github.com/hrgdavor/jscadui/assets/2480762/63821c2d-3b2c-45cb-816a-b99f2f0e24fe + + +better option is to download a nice editor that can syntax highlight JS code +edit with the said editor (you may try VScode if it works well on your computer) +save the fil on yor drive, and keep the openjscad window open, then drag and drop the file there +it will render the script, and check for changes, so you continue editing it the text editor on your computer +as soon as you save it will pick-up the changes +... another simpler option that was made available recently on https://jscad.app/ is to copy the script text from openjscad + +paste there, and then just use CTRL+S it will aks the first time permission to save the file, and further saving will save the file, and render changes +jscad.app is not official yet, but works rather well +I personally prefer to use VSCode and drag/drop my file onto jscad.app to see it rendered there, and export in the end for 3d printing + + +any editor +drag and dropping file to openjscad or jscad.app will cause it to be given to the browser with permission to read it. +You must use Chrome for jscad to be able to check few times a second to see if it changed +Only Chrome can get change info and read new content ... Firefox plays dumb and will indefinitely give the initial file version, even after it chanes on your drive + + + diff --git a/apps/jscad-web/src/editor.js b/apps/jscad-web/src/editor.js index 2d09e31..3e636ba 100644 --- a/apps/jscad-web/src/editor.js +++ b/apps/jscad-web/src/editor.js @@ -70,6 +70,9 @@ export const init = (defaultCode, fn, _saveFn, _getFileFn) => { document.getElementById('editor-hint').addEventListener('click', () => { compile(view.state.doc.toString(), currentFile) }) + document.getElementById('editor-hint2').addEventListener('click', () => { + save(view.state.doc.toString(), currentFile) + }) // Setup file selector editorFile.addEventListener('click', () => { diff --git a/apps/jscad-web/static/index.html b/apps/jscad-web/static/index.html index 1aa9471..4b73274 100644 --- a/apps/jscad-web/static/index.html +++ b/apps/jscad-web/static/index.html @@ -70,6 +70,7 @@

JSCAD

Shift-enter to update + CTRL+S to save and update
diff --git a/apps/jscad-web/static/main.css b/apps/jscad-web/static/main.css index ba6361e..1da5c5f 100644 --- a/apps/jscad-web/static/main.css +++ b/apps/jscad-web/static/main.css @@ -190,7 +190,7 @@ p { height: calc(100vh - 60px); flex: 1; } -#editor-hint { +#editor-hint, #editor-hint2 { position: absolute; bottom: 10px; right: calc(min(10px, 100% - 180px)); /* push right when editor is small */ @@ -199,6 +199,9 @@ p { font-size: 10pt; user-select: none; } +#editor-hint { + bottom: 30px; +} .dark #editor-hint { color: #dddddd88; } diff --git a/packages/fs-provider/fs-provider.js b/packages/fs-provider/fs-provider.js index 296a1c2..0592e77 100644 --- a/packages/fs-provider/fs-provider.js +++ b/packages/fs-provider/fs-provider.js @@ -225,13 +225,12 @@ export const checkFiles = sw => { export async function fileDropped(sw, files) { sw.filesToCheck.length = 0 sw.fileToRun = 'index.js' - let folderName clearFs(sw) let rootFiles = [] if (files.length === 1) { const file = files[0] if (file.isDirectory) { - folderName = file.name + sw.folderName = file.name file.fsDir = '/' rootFiles = await readDir(file) } else { @@ -243,16 +242,17 @@ export async function fileDropped(sw, files) { } rootFiles = rootFiles.map(e => fileToFsEntry(e, '/')) sw.roots.push(rootFiles) +} +export async function analyzeProject(sw){ const alias = await getWorkspaceAliases(sw) - let time = Date.now() const preLoad = ['/' + sw.fileToRun, '/package.json'] const loaded = await addPreLoadAll(sw, preLoad, true) sw.projectName = sw.defProjectName if (sw.fileToRun !== 'index.js') sw.projectName = sw.fileToRun.replace(/\.js$/, '') - if (folderName) sw.projectName = folderName + if (sw.folderName) sw.projectName = sw.folderName let script = '' if (sw.fileToRun) { @@ -266,6 +266,7 @@ export async function fileDropped(sw, files) { } } return { alias, script } + } /** @@ -280,6 +281,7 @@ const getWorkspaceAliases = async sw => { let pkgFile = await findFileInRoots(sw.roots, 'package.json') if (pkgFile) { try { + sw.filesToCheck.push(pkgFile) const pack = JSON.parse(await readAsText(pkgFile)) if (pack.main) sw.fileToRun = pack.main if (pack.workspaces) From aca2807fd6a3eab17bd3c43bd9e79050f2b3a829 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Thu, 14 Mar 2024 20:53:36 +0100 Subject: [PATCH 22/25] catch error --- apps/jscad-web/main.js | 12 ++++++++---- apps/jscad-web/online.editor.md | 3 ++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/jscad-web/main.js b/apps/jscad-web/main.js index 7fffe64..9417416 100644 --- a/apps/jscad-web/main.js +++ b/apps/jscad-web/main.js @@ -207,10 +207,14 @@ function trackJobs(jobs) { const runScript = async ({ script, url = './jscad.model.js', base = currentBase, root }) => { currentBase = base loadDefault = false // don't load default model if something else was loaded - const result = await workerApi.runScript({ script, url, base, root, smooth: viewState.smoothRender }) - genParams({ target: byId('paramsDiv'), params: result.def || {}, callback: paramChangeCallback }) - lastRunParams = result.params - handlers.entities(result) + try{ + const result = await workerApi.runScript({ script, url, base, root, smooth: viewState.smoothRender }) + genParams({ target: byId('paramsDiv'), params: result.def || {}, callback: paramChangeCallback }) + lastRunParams = result.params + handlers.entities(result) + }catch(err){ + setError(err) + } } const bundles = { diff --git a/apps/jscad-web/online.editor.md b/apps/jscad-web/online.editor.md index 8ca970b..f3e6b7c 100644 --- a/apps/jscad-web/online.editor.md +++ b/apps/jscad-web/online.editor.md @@ -14,6 +14,7 @@ Any text or code editor will work for this (vi, vim, neovim, notepad++, VSCodium - edit the file, save the changes, jscad.app will automatically re-run the script A chromium based browser is required, as other browser do not allow JS to see changes to the file. +Firefox plays dumb and will indefinitely give the initial file version, even after it changes on your drive. ## Enthusiast: a multifile project @@ -42,7 +43,7 @@ I personally prefer to use VSCode and drag/drop my file onto jscad.app to see it any editor drag and dropping file to openjscad or jscad.app will cause it to be given to the browser with permission to read it. You must use Chrome for jscad to be able to check few times a second to see if it changed -Only Chrome can get change info and read new content ... Firefox plays dumb and will indefinitely give the initial file version, even after it chanes on your drive +Only Chrome can get change info and read new content ... From 0c42a94bd3bdb37da90321c7fbdf88f4bcb2be91 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Thu, 14 Mar 2024 22:10:22 +0100 Subject: [PATCH 23/25] err handling p1 --- apps/jscad-web/main.js | 1 - packages/postmessage/index.js | 13 +++++++++---- packages/require/src/require.js | 1 + 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/apps/jscad-web/main.js b/apps/jscad-web/main.js index 9417416..a547db8 100644 --- a/apps/jscad-web/main.js +++ b/apps/jscad-web/main.js @@ -190,7 +190,6 @@ const spinner = byId('spinner') let firstJobTimer function trackJobs(jobs) { - console.log('jobs', jobs) if (jobs === 1) { clearTimeout(firstJobTimer) // do not show spinner for fast renders diff --git a/packages/postmessage/index.js b/packages/postmessage/index.js index 6b042b7..b5e178e 100644 --- a/packages/postmessage/index.js +++ b/packages/postmessage/index.js @@ -37,7 +37,7 @@ export const initMessaging = (_self, handlers, {onJobCount}={}) => { try { // serialize stacktrace so it isn't lost in transit const stack = error.stack - ___self.postMessage({ method: RESPONSE, error, stack, id }) + ___self.postMessage({ method: RESPONSE, error: {message: error.message, name:error.name, stack}, id }) } catch (error) { console.error('failed to send ', error) throw error @@ -81,7 +81,8 @@ export const initMessaging = (_self, handlers, {onJobCount}={}) => { } const listener = async (e) => { - const { method, params, id, error, stack } = e.data + const { method, params, id, error } = e.data + console.log('error', error) if (id && method === RESPONSE) { const p = reqMap.get(id) @@ -92,8 +93,11 @@ export const initMessaging = (_self, handlers, {onJobCount}={}) => { const [resolve, reject] = p if (error) { // restore stacktrace - error.stack = stack - reject(error) + // if(typeof error === 'string') + const _error = new Error(error.message) + _error.stack = error.stack + _error.name = error.name + reject(_error) } else { resolve(params) } @@ -113,6 +117,7 @@ export const initMessaging = (_self, handlers, {onJobCount}={}) => { sendResponse(out, id) } } catch (error) { + console.log('ERR', error) console.error(`error executing command ${method}`, params, error) sendError(error, id) } diff --git a/packages/require/src/require.js b/packages/require/src/require.js index 1c45f1c..5967aa3 100644 --- a/packages/require/src/require.js +++ b/packages/require/src/require.js @@ -122,6 +122,7 @@ const requireModule = (id, url, source, _require) => { const exports = {} const module = { id, uri: url, exports, source } // according to node.js modules //module.require = _require + source += '\n//# sourceURL=' + url runModule(_require, exports, module, source) return module } catch (err) { From 46134be414e54ab6403dafd627c4c88188e5afb5 Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Thu, 14 Mar 2024 22:15:10 +0100 Subject: [PATCH 24/25] Update stacktrace.js --- apps/jscad-web/src/stacktrace.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/apps/jscad-web/src/stacktrace.js b/apps/jscad-web/src/stacktrace.js index a406701..5c4be76 100644 --- a/apps/jscad-web/src/stacktrace.js +++ b/apps/jscad-web/src/stacktrace.js @@ -7,21 +7,20 @@ */ export const formatStacktrace = (error) => { // error.stack is not standard but works on chrome and firefox - if (!error.stack) return error.toString() + const stack = error.stack + if (!stack) return error.toString() // chrome stacktrace (script error, syntax error): - // at main (eval at (eval at (require.js:24:69)), :13:3) - // at eval (eval at (require.js:24:69), :3:12) - // firefox stacktrace: - // main@http://localhost:5120/build/bundle.worker.js line 14 > eval line 1 > eval:13:3 - // @http://localhost:5120/build/bundle.worker.js line 14 > eval:1:37 - const cleaned = error.stack + // ReferenceError: gggggg is not defined + // at causeErr (./jscad.model.js:51:3) + // at main (./jscad.model.js:46:27) + // at ve (http://localhost:5120/build/bundle.worker.js:28:2964) + // at Pt (http://localhost:5120/build/bundle.worker.js:28:3731) + // at async http://localhost:5120/build/bundle.worker.js:14:3218 + const cleaned = stack .split('\n') - .filter(line => line.includes('eval')) - .map(line => line.replace(/eval at \(.*?\), /, '')) // chrome - .map(line => line.replace(/^@/, '@')) // firefox - .map(line => line.replace(/@http.*?bundle.worker.js.* > eval:/, ' ')) // firefox - .map(line => line.replace(/^\s*(at )?/, ' at ')) // indent + .filter(line => !line.includes('bundle.worker.js')) - return [error.message, ...cleaned].join('\n') -} + if(!stack.includes(error.message)) cleaned.unshift(error.message) + return cleaned.join('\n') +} \ No newline at end of file From e92fda462aad7683e3cec9be98f33267375adf8d Mon Sep 17 00:00:00 2001 From: Davor Hrg Date: Thu, 14 Mar 2024 22:40:40 +0100 Subject: [PATCH 25/25] fixed err reporting --- apps/jscad-web/main.js | 1 - packages/postmessage/index.js | 2 -- packages/require/src/require.js | 2 +- packages/worker/worker.js | 11 ++++++++++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/apps/jscad-web/main.js b/apps/jscad-web/main.js index a547db8..e7c9a04 100644 --- a/apps/jscad-web/main.js +++ b/apps/jscad-web/main.js @@ -144,7 +144,6 @@ document.body.ondragleave = document.body.ondragend = ev => { const setError = error => { const errorBar = byId('error-bar') if (error) { - console.error(error) const name = (error.name || 'Error') + ': ' byId('error-name').innerText = name const message = formatStacktrace(error) diff --git a/packages/postmessage/index.js b/packages/postmessage/index.js index b5e178e..045145c 100644 --- a/packages/postmessage/index.js +++ b/packages/postmessage/index.js @@ -82,7 +82,6 @@ export const initMessaging = (_self, handlers, {onJobCount}={}) => { const listener = async (e) => { const { method, params, id, error } = e.data - console.log('error', error) if (id && method === RESPONSE) { const p = reqMap.get(id) @@ -117,7 +116,6 @@ export const initMessaging = (_self, handlers, {onJobCount}={}) => { sendResponse(out, id) } } catch (error) { - console.log('ERR', error) console.error(`error executing command ${method}`, params, error) sendError(error, id) } diff --git a/packages/require/src/require.js b/packages/require/src/require.js index 5967aa3..2ea587d 100644 --- a/packages/require/src/require.js +++ b/packages/require/src/require.js @@ -126,7 +126,7 @@ const requireModule = (id, url, source, _require) => { runModule(_require, exports, module, source) return module } catch (err) { - err.message = `failed loading module ${id}\n ${err}` + err.message += ` / failed loading module ${id}` throw err } } diff --git a/packages/worker/worker.js b/packages/worker/worker.js index 27a1c9a..7304b32 100644 --- a/packages/worker/worker.js +++ b/packages/worker/worker.js @@ -127,7 +127,16 @@ const runScript = async ({ script, url='jscad.js', base=globalBase, root=base }) const shouldTransform = url.endsWith('.ts') || script.includes('import') && (importReg.test(script) || exportReg.test(script)) let def = [] - const scriptModule = require({url,script}, shouldTransform ? transformFunc : undefined, readFileWeb, base, root, importData) + let scriptModule + try{ + scriptModule = require({url,script}, shouldTransform ? transformFunc : undefined, readFileWeb, base, root, importData) + }catch(e){ + // with syntax error in browser we do not get nice stack trace + // we then try to parse the script to let transform function generate nice error with nice trace + if(e.name === 'SyntaxError') transformFunc(script, url) + // if error is not SyntaxError or if transform func does not find sysntax err (very unlikely) + throw e + } const fromSource = getParameterDefinitionsFromSource(script) def = combineParameterDefinitions(fromSource, await scriptModule.getParameterDefinitions?.()) main = scriptModule.main