From 5f3b898b42a53d3bb972402bd4c3d729dc4c5f00 Mon Sep 17 00:00:00 2001 From: Laurin Weger Date: Thu, 21 Aug 2025 17:47:19 +0200 Subject: [PATCH] set up mock shape server --- package-lock.json | 427 +++++++++++++++++++- package.json | 12 +- src/frontends/react/SvelteWrapper.tsx | 0 src/frontends/react/VueWrapper.tsx | 0 src/ng-mock/js-land/connector/applyDiff.ts | 6 + src/ng-mock/js-land/connector/connector.ts | 4 - src/ng-mock/js-land/connector/ngSignals.ts | 33 +- src/ng-mock/js-land/types.ts | 14 + src/ng-mock/tests/updatesWithWasm.test.ts | 23 ++ src/ng-mock/wasm-land/handleShapeRequest.ts | 50 +++ src/ng-mock/wasm-land/handleShapeUpdate.ts | 20 + src/ng-mock/wasm-land/shapeManager.ts | 10 + src/ng-mock/wasm-land/types.ts | 17 + 13 files changed, 597 insertions(+), 19 deletions(-) create mode 100644 src/frontends/react/SvelteWrapper.tsx create mode 100644 src/frontends/react/VueWrapper.tsx create mode 100644 src/ng-mock/js-land/connector/applyDiff.ts delete mode 100644 src/ng-mock/js-land/connector/connector.ts create mode 100644 src/ng-mock/js-land/types.ts create mode 100644 src/ng-mock/tests/updatesWithWasm.test.ts create mode 100644 src/ng-mock/wasm-land/handleShapeRequest.ts create mode 100644 src/ng-mock/wasm-land/handleShapeUpdate.ts create mode 100644 src/ng-mock/wasm-land/shapeManager.ts create mode 100644 src/ng-mock/wasm-land/types.ts diff --git a/package-lock.json b/package-lock.json index ef12806..1208640 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "@astrojs/react": "4.3.0", "@astrojs/svelte": "7.1.0", "@astrojs/vue": "^5.1.0", - "@picocss/pico": "2.1.1", "@types/react": "19.1.10", "@types/react-dom": "19.1.7", "astro": "5.13.2", @@ -24,7 +23,8 @@ "@types/node": "24.3.0", "@types/react": "19.1.10", "@types/react-dom": "19.1.7", - "vite": "7.1.3" + "vite": "7.1.3", + "vitest": "^3.2.4" } }, "node_modules/@ampproject/remapping": { @@ -1359,12 +1359,6 @@ "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", "license": "MIT" }, - "node_modules/@picocss/pico": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@picocss/pico/-/pico-2.1.1.tgz", - "integrity": "sha512-kIDugA7Ps4U+2BHxiNHmvgPIQDWPDU4IeU6TNRdvXQM1uZX+FibqDQT2xUOnnO2yq/LUHcwnGlu1hvf4KfXnMg==", - "license": "MIT" - }, "node_modules/@polka/url": { "version": "1.0.0-next.29", "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", @@ -1563,6 +1557,16 @@ "@babel/types": "^7.28.2" } }, + "node_modules/@types/chai": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.2.tgz", + "integrity": "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*" + } + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -1572,6 +1576,13 @@ "@types/ms": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "license": "MIT" @@ -1671,6 +1682,131 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/@vue/babel-helper-vue-transform-on": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz", @@ -1994,6 +2130,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/astro": { "version": "5.13.2", "resolved": "https://registry.npmjs.org/astro/-/astro-5.13.2.tgz", @@ -2324,6 +2470,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/camelcase": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", @@ -2366,6 +2522,23 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/chai": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.1.tgz", + "integrity": "sha512-48af6xm9gQK8rhIcOxWwdGzIervm8BVTin+yRp9HEvU20BtVZ2lBywlIJBzwaDtvo0FvjeL7QdCADoUoqIbV3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz", @@ -2408,6 +2581,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -2660,6 +2843,16 @@ "integrity": "sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==", "license": "MIT" }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -2945,6 +3138,16 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/expect-type": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", + "integrity": "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -3568,6 +3771,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/loupe": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz", + "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==", + "dev": true, + "license": "MIT" + }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -4738,6 +4948,16 @@ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "license": "MIT" }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/perfect-debounce": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", @@ -5276,6 +5496,13 @@ "@types/hast": "^3.0.4" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -5356,6 +5583,20 @@ "node": ">=0.10.0" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", + "dev": true, + "license": "MIT" + }, "node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", @@ -5414,6 +5655,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.0.0.tgz", + "integrity": "sha512-TcccoMhJOM3OebGhSBEmp3UZ2SfDMZUEBdRA/9ynfLi8yYajyWX3JiXArcJt4Umh4vISpspkQIY8ZZoCqjbviA==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/superjson": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", @@ -5469,6 +5730,13 @@ "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", "license": "MIT" }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyexec": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", @@ -5489,6 +5757,36 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.3.tgz", + "integrity": "sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/totalist": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", @@ -6046,6 +6344,29 @@ "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0" } }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/vite-plugin-vue-devtools": { "version": "7.7.7", "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-7.7.7.tgz", @@ -6137,6 +6458,79 @@ } } }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, "node_modules/vue": { "version": "3.5.19", "license": "MIT", @@ -6206,6 +6600,23 @@ "node": ">=4" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/widest-line": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", diff --git a/package.json b/package.json index e4ecaa4..f01e20a 100644 --- a/package.json +++ b/package.json @@ -8,15 +8,16 @@ "start": "astro dev", "build": "astro build", "preview": "astro preview", - "astro": "astro" + "astro": "astro", + "test": "vitest" }, "dependencies": { - "@types/react": "19.1.10", - "@types/react-dom": "19.1.7", - "astro": "5.13.2", "@astrojs/react": "4.3.0", "@astrojs/svelte": "7.1.0", "@astrojs/vue": "^5.1.0", + "@types/react": "19.1.10", + "@types/react-dom": "19.1.7", + "astro": "5.13.2", "react": "19.1.1", "react-dom": "19.1.1", "svelte": "5.38.2", @@ -26,6 +27,7 @@ "@types/node": "24.3.0", "@types/react": "19.1.10", "@types/react-dom": "19.1.7", - "vite": "7.1.3" + "vite": "7.1.3", + "vitest": "^3.2.4" } } diff --git a/src/frontends/react/SvelteWrapper.tsx b/src/frontends/react/SvelteWrapper.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/frontends/react/VueWrapper.tsx b/src/frontends/react/VueWrapper.tsx new file mode 100644 index 0000000..e69de29 diff --git a/src/ng-mock/js-land/connector/applyDiff.ts b/src/ng-mock/js-land/connector/applyDiff.ts new file mode 100644 index 0000000..4f49540 --- /dev/null +++ b/src/ng-mock/js-land/connector/applyDiff.ts @@ -0,0 +1,6 @@ +import type { Diff } from "../types"; + +/** Mock function to apply diffs. Just uses a copy of the diff as the new object. */ +export function applyDiff(currentState: object, diff: Diff): object { + return JSON.parse(JSON.stringify(diff)); +} diff --git a/src/ng-mock/js-land/connector/connector.ts b/src/ng-mock/js-land/connector/connector.ts deleted file mode 100644 index 6c9f53e..0000000 --- a/src/ng-mock/js-land/connector/connector.ts +++ /dev/null @@ -1,4 +0,0 @@ -// Requires functions -// - initializeConnection -// - sendUpdateToDb -// - onDbUpdate diff --git a/src/ng-mock/js-land/connector/ngSignals.ts b/src/ng-mock/js-land/connector/ngSignals.ts index 8e60e6f..8f437c0 100644 --- a/src/ng-mock/js-land/connector/ngSignals.ts +++ b/src/ng-mock/js-land/connector/ngSignals.ts @@ -1,2 +1,31 @@ -// Requires functions -// - createSignalForShape() +import handleShapeUpdate from "src/ng-mock/wasm-land/handleShapeUpdate"; +import type { Connection, Diff, Scope, Shape } from "../types"; +import handleShapeRequest from "src/ng-mock/wasm-land/handleShapeRequest"; +import { applyDiff } from "./applyDiff"; + +export async function createSignalObjectForShape(shape: Shape, scope?: Scope) { + const ret: { + state?: any; + connectionId?: Connection["id"]; + update: (diff: Diff) => Promise; + } = { + async update(diff) { + if (!ret.connectionId) + throw new Error("Connection not established yet for shape" + shape); + await handleShapeUpdate(ret.connectionId, diff); + }, + }; + + const onDbUpdate = (diff: Diff) => { + ret.state = applyDiff(ret.state || {}, diff); + }; + + await handleShapeRequest(shape, onDbUpdate).then( + ({ connectionId, shapeObject }) => { + ret.state = shapeObject; + ret.connectionId = connectionId; + } + ); + + return ret; +} diff --git a/src/ng-mock/js-land/types.ts b/src/ng-mock/js-land/types.ts new file mode 100644 index 0000000..accb677 --- /dev/null +++ b/src/ng-mock/js-land/types.ts @@ -0,0 +1,14 @@ +/** The shape of an object requested. */ +export type Shape = "Shape1" | "Shape2"; + +/** The Scope of a shape request */ +export type Scope = string | string[]; + +/** The diff format used to communicate updates between wasm-land and js-land. */ +export type Diff = object; + +/** A connection established between wasm-land and js-land for subscription of a shape. */ +export type Connection = { + id: string; + onUpdateFromWasm: (diff: Diff) => void; +}; diff --git a/src/ng-mock/tests/updatesWithWasm.test.ts b/src/ng-mock/tests/updatesWithWasm.test.ts new file mode 100644 index 0000000..65abdd9 --- /dev/null +++ b/src/ng-mock/tests/updatesWithWasm.test.ts @@ -0,0 +1,23 @@ +import { expect, test } from "vitest"; +import { createSignalObjectForShape } from "../js-land/connector/ngSignals"; + +test("shape object notification comes back to others", async () => { + const object1 = await createSignalObjectForShape("Shape1"); + const object2 = await createSignalObjectForShape("Shape1"); + + const object3 = await createSignalObjectForShape("Shape2"); + const object4 = await createSignalObjectForShape("Shape2"); + + // Update object 1 and expect object 2 to update as well. + await object1.update({ name: "Updated name from object1" }); + + expect(object2.state?.name).toBe("Updated name from object1"); + + // Expect object of different shape not to have changed. + expect(object3.state?.name).toBe("Niko's cat"); + + // Update object 4 and expect object 3 with same shape to have updated. + await object4.update({ name: "Updated name from object4" }); + + expect(object3.state?.name).toBe("Updated name from object4"); +}); diff --git a/src/ng-mock/wasm-land/handleShapeRequest.ts b/src/ng-mock/wasm-land/handleShapeRequest.ts new file mode 100644 index 0000000..2530e3a --- /dev/null +++ b/src/ng-mock/wasm-land/handleShapeRequest.ts @@ -0,0 +1,50 @@ +import { randomUUID } from "crypto"; +import * as shapeManager from "./shapeManager"; +import type { WasmConnection, Diff } from "./types"; +import type { Shape } from "../js-land/types"; + +const mockShapeObject1 = { + type: "Person", + name: "Bob", + address: { + street: "First street", + houseNumber: "15", + }, + hasChildren: true, + numberOfHouses: 0, +}; +const mockShapeObject2 = { + type: "Cat", + name: "Niko's cat", + age: 12, + numberOfHomes: 3, + address: { + street: "Niko's street", + compartment: 2, + }, +}; + +export default async function handleShapeRequest( + shape: Shape, + callback: (diff: Diff, connectionId: WasmConnection["id"]) => void +): Promise<{ + connectionId: string; + shapeObject: object; +}> { + const connection: WasmConnection = { + id: randomUUID(), + shape, + // Create a deep copy to prevent accidental by-reference changes. + state: JSON.parse( + JSON.stringify(shape === "Shape1" ? mockShapeObject1 : mockShapeObject2) + ), + callback, + }; + + shapeManager.connections.set(connection.id, connection); + + return { + connectionId: connection.id, + shapeObject: connection.state, + }; +} diff --git a/src/ng-mock/wasm-land/handleShapeUpdate.ts b/src/ng-mock/wasm-land/handleShapeUpdate.ts new file mode 100644 index 0000000..4caad93 --- /dev/null +++ b/src/ng-mock/wasm-land/handleShapeUpdate.ts @@ -0,0 +1,20 @@ +import * as shapeManager from "./shapeManager"; +import type { WasmConnection, Diff } from "./types"; + +export default async function handleShapeUpdate( + connectionId: WasmConnection["id"], + diff: Diff +) { + const connection = shapeManager.connections.get(connectionId); + if (!connection) throw new Error("No Connection found."); + + const newState = shapeManager.applyDiff(connection.state, diff); + connection.state = newState; + + shapeManager.connections.forEach((con) => { + if (con.shape == connection.shape) { + con.state = newState; + con.callback(diff, con.id); + } + }); +} diff --git a/src/ng-mock/wasm-land/shapeManager.ts b/src/ng-mock/wasm-land/shapeManager.ts new file mode 100644 index 0000000..5807530 --- /dev/null +++ b/src/ng-mock/wasm-land/shapeManager.ts @@ -0,0 +1,10 @@ +import type { Diff, ObjectState, WasmConnection } from "./types"; + +const connections: Map = new Map(); + +/** Mock function to apply diffs. Just uses a copy of the diff as the new object. */ +export function applyDiff(currentState: ObjectState, diff: Diff): ObjectState { + return JSON.parse(JSON.stringify(diff)); +} + +export { connections }; diff --git a/src/ng-mock/wasm-land/types.ts b/src/ng-mock/wasm-land/types.ts new file mode 100644 index 0000000..02673d6 --- /dev/null +++ b/src/ng-mock/wasm-land/types.ts @@ -0,0 +1,17 @@ +import type { Shape } from "../js-land/types"; + +/** The Scope of a shape request */ +export type Scope = string | string[]; + +/** The diff format used to communicate updates between wasm-land and js-land. */ +export type Diff = object; + +export type ObjectState = object; + +/** A connection established between wasm-land and js-land for subscription of a shape. */ +export type WasmConnection = { + id: string; + shape: Shape; + state: ObjectState; + callback: (diff: Diff, connectionId: WasmConnection["id"]) => void; +};