From d90c5175a658cd99cd8c2a1acb2ad56b3327c641 Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 19:58:20 -0400 Subject: [PATCH 01/24] build: add electron-vite devDependency at workspace root Refs #144 --- package-lock.json | 568 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 3 + 2 files changed, 568 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index d569e97..fe61ce6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,9 @@ "app/packages/lambda/*", "scripts" ], + "devDependencies": { + "electron-vite": "^5.0.0" + }, "engines": { "node": ">=20" } @@ -1560,7 +1563,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", "dev": true, "license": "MIT", "engines": { @@ -1617,6 +1622,22 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz", + "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-transform-react-jsx-self": { "version": "7.27.1", "dev": true, @@ -3880,7 +3901,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@rollup/rollup-linux-x64-musl": { "version": "4.60.3", @@ -3892,7 +3914,8 @@ "optional": true, "os": [ "linux" - ] + ], + "peer": true }, "node_modules/@sindresorhus/is": { "version": "4.6.0", @@ -6925,6 +6948,520 @@ "dev": true, "license": "ISC" }, + "node_modules/electron-vite": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/electron-vite/-/electron-vite-5.0.0.tgz", + "integrity": "sha512-OHp/vjdlubNlhNkPkL/+3JD34ii5ov7M0GpuXEVdQeqdQ3ulvVR7Dg/rNBLfS5XPIFwgoBLDf9sjjrL+CuDyRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.4", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "cac": "^6.7.14", + "esbuild": "^0.25.11", + "magic-string": "^0.30.19", + "picocolors": "^1.1.1" + }, + "bin": { + "electron-vite": "bin/electron-vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@swc/core": "^1.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + } + } + }, + "node_modules/electron-vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/electron-vite/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, "node_modules/electron/node_modules/@types/node": { "version": "22.19.19", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz", @@ -9389,6 +9926,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -9408,6 +9946,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -12985,6 +13524,7 @@ "os": [ "aix" ], + "peer": true, "engines": { "node": ">=12" } @@ -13002,6 +13542,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -13019,6 +13560,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -13036,6 +13578,7 @@ "os": [ "android" ], + "peer": true, "engines": { "node": ">=12" } @@ -13053,6 +13596,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=12" } @@ -13070,6 +13614,7 @@ "os": [ "darwin" ], + "peer": true, "engines": { "node": ">=12" } @@ -13087,6 +13632,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -13104,6 +13650,7 @@ "os": [ "freebsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -13121,6 +13668,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -13138,6 +13686,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -13155,6 +13704,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -13172,6 +13722,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -13189,6 +13740,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -13206,6 +13758,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -13223,6 +13776,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -13240,6 +13794,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -13255,6 +13810,7 @@ "os": [ "linux" ], + "peer": true, "engines": { "node": ">=12" } @@ -13272,6 +13828,7 @@ "os": [ "netbsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -13289,6 +13846,7 @@ "os": [ "openbsd" ], + "peer": true, "engines": { "node": ">=12" } @@ -13306,6 +13864,7 @@ "os": [ "sunos" ], + "peer": true, "engines": { "node": ">=12" } @@ -13323,6 +13882,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -13340,6 +13900,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } @@ -13357,6 +13918,7 @@ "os": [ "win32" ], + "peer": true, "engines": { "node": ">=12" } diff --git a/package.json b/package.json index cc1d07b..90f1003 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,9 @@ "engines": { "node": ">=20" }, + "devDependencies": { + "electron-vite": "^5.0.0" + }, "scripts": { "app:dev": "npm run dev -w game-server-manager", "app:build": "npm run build -w game-server-manager", From 9019311a5341d89c6ceef6e60c9071d410c8230c Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 20:02:21 -0400 Subject: [PATCH 02/24] feat(desktop-main): add Electron app entry with BrowserWindow and IPC bootstrap Refs #144 --- .../desktop-main/src/electron-entry.ts | 49 +++++++++++++++++++ app/packages/desktop-main/src/main.test.ts | 9 ++-- app/packages/desktop-main/src/main.ts | 10 ++-- 3 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 app/packages/desktop-main/src/electron-entry.ts diff --git a/app/packages/desktop-main/src/electron-entry.ts b/app/packages/desktop-main/src/electron-entry.ts new file mode 100644 index 0000000..01937b6 --- /dev/null +++ b/app/packages/desktop-main/src/electron-entry.ts @@ -0,0 +1,49 @@ +import { app, BrowserWindow } from 'electron'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { bootstrap } from './main.js'; + +// electron-vite injects __dirname for main-process entries, but we also +// compute it explicitly via import.meta.url so the file is valid plain ESM. +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +/** + * Creates the main application window with the preload script wired in and + * loads either the dev server URL or the production renderer bundle. + */ +function createWindow(): void { + const win = new BrowserWindow({ + width: 1200, + height: 800, + webPreferences: { + // electron-vite outputs the preload bundle to out/preload/index.js by + // default. __dirname here resolves to out/main, so we go one level up. + preload: path.join(__dirname, '../preload/index.js'), + contextIsolation: true, + sandbox: true, + }, + }); + + if (process.env.ELECTRON_RENDERER_URL) { + void win.loadURL(process.env.ELECTRON_RENDERER_URL); + } else { + void win.loadFile(path.join(__dirname, '../renderer/index.html')); + } +} + +app.whenReady().then(() => { + void bootstrap(); + createWindow(); + + // On macOS re-create the window when the dock icon is clicked and there are + // no other windows open (standard macOS behaviour). + app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) createWindow(); + }); +}); + +// Quit the app when all windows are closed, except on macOS where the app and +// its menu bar conventionally stay active until the user explicitly quits. +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') app.quit(); +}); diff --git a/app/packages/desktop-main/src/main.test.ts b/app/packages/desktop-main/src/main.test.ts index 594e05d..9110a9d 100644 --- a/app/packages/desktop-main/src/main.test.ts +++ b/app/packages/desktop-main/src/main.test.ts @@ -88,7 +88,8 @@ describe('main bootstrap', () => { * all stubs remain active. */ vi.resetModules(); - await import('./main.js'); + const { bootstrap } = await import('./main.js'); + await bootstrap(); // Flush the event loop so the async bootstrap chain fully resolves. await new Promise((resolve) => setTimeout(resolve, 0)); @@ -116,7 +117,8 @@ describe('main bootstrap', () => { it('should call applyFixPath during bootstrap', async () => { vi.resetModules(); - await import('./main.js'); + const { bootstrap } = await import('./main.js'); + await bootstrap(); // Flush the event loop so the async bootstrap chain fully resolves. await new Promise((resolve) => setTimeout(resolve, 0)); @@ -127,7 +129,8 @@ describe('main bootstrap', () => { it('should resolve userData path and initialise the file logger before bootstrap', async () => { vi.resetModules(); - await import('./main.js'); + const { bootstrap } = await import('./main.js'); + await bootstrap(); // Flush the event loop so the async bootstrap chain fully resolves. await new Promise((resolve) => setTimeout(resolve, 0)); diff --git a/app/packages/desktop-main/src/main.ts b/app/packages/desktop-main/src/main.ts index 052aba0..d6c7ed6 100644 --- a/app/packages/desktop-main/src/main.ts +++ b/app/packages/desktop-main/src/main.ts @@ -23,12 +23,16 @@ if (!process.versions['electron']) { const { app } = await import('electron') as unknown as { app: { getPath(name: string): string } }; createLogger(path.join(app.getPath('userData'), 'logs')); -async function bootstrap(): Promise { +/** + * Bootstraps the NestJS IPC microservice. + * + * Called from `electron-entry.ts` after `app.whenReady()` so that + * `ipcMain` is available before the transport is initialised. + */ +export async function bootstrap(): Promise { const app = await NestFactory.createMicroservice(AppModule, { strategy: new ElectronIPCTransport(), }); await app.listen(); } - -void bootstrap(); From 56c44ca017f6743b0648660e8da59d968d05093b Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 20:14:04 -0400 Subject: [PATCH 03/24] test(desktop-main): add electron-entry unit tests for BrowserWindow and IPC bootstrap Refs #144 --- .../desktop-main/src/electron-entry.test.ts | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 app/packages/desktop-main/src/electron-entry.test.ts diff --git a/app/packages/desktop-main/src/electron-entry.test.ts b/app/packages/desktop-main/src/electron-entry.test.ts new file mode 100644 index 0000000..daf82c6 --- /dev/null +++ b/app/packages/desktop-main/src/electron-entry.test.ts @@ -0,0 +1,224 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; + +/* + * Spy variables must be hoisted before vi.mock() factories run, because + * vi.mock() calls are lifted to the top of the compiled output above regular + * declarations. + */ +const { + mockLoadURL, + mockLoadFile, + mockQuit, + mockOn, + mockWhenReady, + MockBrowserWindow, + mockGetAllWindows, + bootstrapMock, + whenReadyCallbacks, + onCallbacks, +} = vi.hoisted(() => { + const mockLoadURL = vi.fn().mockResolvedValue(undefined); + const mockLoadFile = vi.fn().mockResolvedValue(undefined); + const mockQuit = vi.fn(); + const mockGetAllWindows = vi.fn().mockReturnValue([]); + + /** + * Collects every callback passed to `app.whenReady().then(cb)`. + * Tests can fire them on demand by calling `whenReadyCallbacks[n]()`. + */ + const whenReadyCallbacks: Array<() => void> = []; + + /** + * Collects every callback registered via `app.on(event, cb)` keyed by + * event name, so tests can trigger lifecycle events synchronously. + */ + const onCallbacks: Record void> = {}; + + const mockOn = vi.fn((event: string, cb: () => void) => { + onCallbacks[event] = cb; + }); + + /** + * Returns a thenable that stores the `.then()` callback instead of + * resolving it, giving tests full control over when the ready handler fires. + */ + const mockWhenReady = vi.fn(() => ({ + then: (cb: () => void) => { + whenReadyCallbacks.push(cb); + return { then: vi.fn() }; + }, + })); + + /** Spy BrowserWindow constructor whose instances expose controlled load fns. */ + const MockBrowserWindow = vi.fn().mockImplementation(() => ({ + loadURL: mockLoadURL, + loadFile: mockLoadFile, + })); + + /** `BrowserWindow.getAllWindows()` static method used by the activate handler. */ + MockBrowserWindow.getAllWindows = mockGetAllWindows; + + /** Spy for `bootstrap` imported from `./main.js`. */ + const bootstrapMock = vi.fn().mockResolvedValue(undefined); + + return { + mockLoadURL, + mockLoadFile, + mockQuit, + mockOn, + mockWhenReady, + MockBrowserWindow, + mockGetAllWindows, + bootstrapMock, + whenReadyCallbacks, + onCallbacks, + }; +}); + +vi.mock('electron', () => ({ + app: { + whenReady: mockWhenReady, + on: mockOn, + quit: mockQuit, + }, + BrowserWindow: MockBrowserWindow, +})); + +vi.mock('./main.js', () => ({ + bootstrap: bootstrapMock, +})); + +/** Flush the micro-task / timer queue so async chains fully settle. */ +async function flushPromises(): Promise { + await new Promise((resolve) => setTimeout(resolve, 0)); +} + +describe('electron-entry', () => { + beforeEach(() => { + mockLoadURL.mockResolvedValue(undefined); + mockLoadFile.mockResolvedValue(undefined); + mockQuit.mockImplementation(() => undefined); + bootstrapMock.mockResolvedValue(undefined); + mockGetAllWindows.mockReturnValue([]); + + // Re-apply the BrowserWindow constructor implementation in case clearMocks + // cleared it between tests (clearMocks resets call history and return value + // queues; mockImplementation persists, but we re-set to be defensive). + MockBrowserWindow.mockImplementation(() => ({ + loadURL: mockLoadURL, + loadFile: mockLoadFile, + })); + + // Re-apply mockOn and mockWhenReady implementations so callback capturing + // works correctly after clearMocks resets the call history. + mockOn.mockImplementation((event: string, cb: () => void) => { + onCallbacks[event] = cb; + }); + mockWhenReady.mockImplementation(() => ({ + then: (cb: () => void) => { + whenReadyCallbacks.push(cb); + return { then: vi.fn() }; + }, + })); + + // Reset the callback queues so each test starts clean. + whenReadyCallbacks.length = 0; + for (const key of Object.keys(onCallbacks)) { + delete onCallbacks[key]; + } + }); + + afterEach(() => { + vi.unstubAllEnvs(); + }); + + it('should call bootstrap() inside the app.whenReady() callback', async () => { + vi.resetModules(); + delete process.env['ELECTRON_RENDERER_URL']; + + await import('./electron-entry.js'); + await flushPromises(); + + // Fire the whenReady callback that the module registered at import time. + expect(whenReadyCallbacks).toHaveLength(1); + whenReadyCallbacks[0]!(); + await flushPromises(); + + expect(bootstrapMock).toHaveBeenCalledOnce(); + }); + + it('should call win.loadURL() with the dev server URL when ELECTRON_RENDERER_URL is set', async () => { + vi.resetModules(); + process.env['ELECTRON_RENDERER_URL'] = 'http://localhost:5173'; + + await import('./electron-entry.js'); + await flushPromises(); + + expect(whenReadyCallbacks).toHaveLength(1); + whenReadyCallbacks[0]!(); + await flushPromises(); + + expect(mockLoadURL).toHaveBeenCalledOnce(); + expect(mockLoadURL).toHaveBeenCalledWith('http://localhost:5173'); + expect(mockLoadFile).not.toHaveBeenCalled(); + }); + + it('should call win.loadFile() with the production renderer path when ELECTRON_RENDERER_URL is not set', async () => { + vi.resetModules(); + delete process.env['ELECTRON_RENDERER_URL']; + + await import('./electron-entry.js'); + await flushPromises(); + + expect(whenReadyCallbacks).toHaveLength(1); + whenReadyCallbacks[0]!(); + await flushPromises(); + + expect(mockLoadFile).toHaveBeenCalledOnce(); + expect(mockLoadURL).not.toHaveBeenCalled(); + + // The path must end with the standard electron-vite renderer bundle location. + const calledPath = mockLoadFile.mock.calls[0]?.[0] as string; + expect(calledPath).toMatch(/renderer[/\\]index\.html$/); + }); + + it('should call app.quit() on window-all-closed for non-macOS platforms', async () => { + vi.resetModules(); + delete process.env['ELECTRON_RENDERER_URL']; + + await import('./electron-entry.js'); + await flushPromises(); + + const handler = onCallbacks['window-all-closed']; + expect(handler).toBeDefined(); + + const originalPlatform = process.platform; + Object.defineProperty(process, 'platform', { value: 'linux', configurable: true }); + + handler!(); + + expect(mockQuit).toHaveBeenCalledOnce(); + + Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true }); + }); + + it('should NOT call app.quit() on window-all-closed on macOS', async () => { + vi.resetModules(); + delete process.env['ELECTRON_RENDERER_URL']; + + await import('./electron-entry.js'); + await flushPromises(); + + const handler = onCallbacks['window-all-closed']; + expect(handler).toBeDefined(); + + const originalPlatform = process.platform; + Object.defineProperty(process, 'platform', { value: 'darwin', configurable: true }); + + handler!(); + + expect(mockQuit).not.toHaveBeenCalled(); + + Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true }); + }); +}); From 51b108d1a63fdab47ee129033c9257bd7f59a5cd Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 20:16:36 -0400 Subject: [PATCH 04/24] build: add electron.vite.config.ts with main/preload/renderer pipelines Ignore **/out/** in ESLint to exclude build artifacts. Refs #144 --- app/eslint.config.js | 1 + electron.vite.config.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 electron.vite.config.ts diff --git a/app/eslint.config.js b/app/eslint.config.js index be61b18..98bafb5 100644 --- a/app/eslint.config.js +++ b/app/eslint.config.js @@ -10,6 +10,7 @@ export default tseslint.config( { ignores: [ '**/dist/**', + '**/out/**', '**/node_modules/**', '**/*.d.ts', 'packages/web/vite.config.ts', diff --git a/electron.vite.config.ts b/electron.vite.config.ts new file mode 100644 index 0000000..d10db96 --- /dev/null +++ b/electron.vite.config.ts @@ -0,0 +1,40 @@ +import { defineConfig, externalizeDepsPlugin } from 'electron-vite'; +import react from '@vitejs/plugin-react'; +import tailwindcss from '@tailwindcss/vite'; +import { fileURLToPath } from 'node:url'; + +export default defineConfig({ + main: { + plugins: [externalizeDepsPlugin()], + build: { + rollupOptions: { + input: 'app/packages/desktop-main/src/electron-entry.ts', + output: { + format: 'es', + }, + }, + }, + }, + preload: { + plugins: [externalizeDepsPlugin()], + build: { + rollupOptions: { + input: 'app/packages/desktop-preload/src/preload.ts', + }, + }, + }, + renderer: { + root: 'app/packages/web', + plugins: [react(), tailwindcss()], + resolve: { + alias: { + '@': fileURLToPath(new URL('app/packages/web/src', import.meta.url)), + }, + }, + build: { + rollupOptions: { + input: 'app/packages/web/index.html', + }, + }, + }, +}); From 7f6d4949ae0aee0b3d88996f4eee4ca13a880638 Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 20:18:40 -0400 Subject: [PATCH 05/24] build: wire desktop:dev and desktop:build scripts; drop Epic A stubs Refs #144 --- app/package.json | 4 ++-- app/packages/desktop-main/package.json | 4 ++-- package.json | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/package.json b/app/package.json index 1cff71a..cf5b0b1 100644 --- a/app/package.json +++ b/app/package.json @@ -5,11 +5,11 @@ "type": "module", "scripts": { "predev": "node scripts/embed-tfstate.mjs", - "dev": "echo 'app:dev requires the Electron launcher from Epic A (#136). Run npm run dev -w @hyveon/web for the web-only dev server.' && exit 1", + "dev": "electron-vite dev", "prebuild": "node scripts/embed-tfstate.mjs", "build": "npm run build -w @hyveon/shared && npm run build -w @hyveon/desktop-main && npm run build -w @hyveon/web", "build:lambdas": "npm run build -w @hyveon/shared && npm run build -w @hyveon/lambda-interactions -w @hyveon/lambda-followup -w @hyveon/lambda-update-dns -w @hyveon/lambda-watchdog -w @hyveon/lambda-efs-seeder", - "start": "echo 'app:start requires the Electron launcher from Epic A (#136).' && exit 1", + "start": "electron-vite build", "test": "vitest run", "test:coverage": "vitest run --coverage", "test:watch": "vitest", diff --git a/app/packages/desktop-main/package.json b/app/packages/desktop-main/package.json index 2ec8c49..875a2b2 100644 --- a/app/packages/desktop-main/package.json +++ b/app/packages/desktop-main/package.json @@ -5,9 +5,9 @@ "type": "module", "main": "./dist/main.js", "scripts": { - "dev": "echo 'desktop-main now runs as an Electron IPC microservice. The Electron launcher is wired in Epic A (#136).' && exit 1", + "dev": "electron-vite dev", "build": "tsc -b", - "start": "echo 'desktop-main now runs as an Electron IPC microservice. The Electron launcher is wired in Epic A (#136).' && exit 1" + "start": "electron-vite build" }, "dependencies": { "@aws-sdk/client-cloudwatch-logs": "^3.600.0", diff --git a/package.json b/package.json index 90f1003..33e5866 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,8 @@ "app:lint:fix": "npm run lint:fix -w game-server-manager", "app:test:e2e": "npm run test:e2e -w @hyveon/web", "app:test:integration": "node app/scripts/embed-tfstate.mjs && npm run build -w @hyveon/desktop-main && npm run test:integration -w @hyveon/web", - "scripts:init-parent": "npm run init-parent -w @hyveon/scripts" + "scripts:init-parent": "npm run init-parent -w @hyveon/scripts", + "desktop:dev": "electron-vite dev", + "desktop:build": "electron-vite build" } } From 16536dceadfca6a7cb5b062a6608f8009c170c1e Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 20:21:15 -0400 Subject: [PATCH 06/24] docs: document desktop:dev and desktop:build in CLAUDE.md Refs #144 --- CLAUDE.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CLAUDE.md b/CLAUDE.md index 6d45e22..0d5dc44 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,6 +20,11 @@ npm run app:dev # Production build (shared → server → web) npm run app:build && npm run app:start # http://localhost:3001 +# Electron desktop app — electron-vite drives three pipelines (main/preload/renderer) +# configured in electron.vite.config.ts; outputs land in out/main, out/preload, out/renderer +npm run desktop:dev # electron-vite dev: HMR on renderer saves, auto-restarts main+preload +npm run desktop:build # electron-vite build: produces out/main, out/preload, out/renderer + # Build all Lambda bundles (required before `terraform apply`) npm run app:build:lambdas From fedaceee9664f8d78bd66bfbc25d29e537ab1881 Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 20:27:41 -0400 Subject: [PATCH 07/24] =?UTF-8?q?fix(desktop-main):=20Glenn=20=E2=80=94=20?= =?UTF-8?q?surface=20bootstrap()=20rejection=20instead=20of=20swallowing?= =?UTF-8?q?=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #144 --- .../desktop-main/src/electron-entry.test.ts | 17 ++++++++++++++++ .../desktop-main/src/electron-entry.ts | 20 ++++++++++++------- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/app/packages/desktop-main/src/electron-entry.test.ts b/app/packages/desktop-main/src/electron-entry.test.ts index daf82c6..d651f49 100644 --- a/app/packages/desktop-main/src/electron-entry.test.ts +++ b/app/packages/desktop-main/src/electron-entry.test.ts @@ -221,4 +221,21 @@ describe('electron-entry', () => { Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true }); }); + + it('should call app.quit() and not open a window when bootstrap() rejects', async () => { + vi.resetModules(); + delete process.env['ELECTRON_RENDERER_URL']; + + bootstrapMock.mockRejectedValueOnce(new Error('IPC init failure')); + + await import('./electron-entry.js'); + await flushPromises(); + + expect(whenReadyCallbacks).toHaveLength(1); + whenReadyCallbacks[0]!(); + await flushPromises(); + + expect(mockQuit).toHaveBeenCalledOnce(); + expect(MockBrowserWindow).not.toHaveBeenCalled(); + }); }); diff --git a/app/packages/desktop-main/src/electron-entry.ts b/app/packages/desktop-main/src/electron-entry.ts index 01937b6..e2836ff 100644 --- a/app/packages/desktop-main/src/electron-entry.ts +++ b/app/packages/desktop-main/src/electron-entry.ts @@ -32,14 +32,20 @@ function createWindow(): void { } app.whenReady().then(() => { - void bootstrap(); - createWindow(); + bootstrap() + .then(() => { + createWindow(); - // On macOS re-create the window when the dock icon is clicked and there are - // no other windows open (standard macOS behaviour). - app.on('activate', () => { - if (BrowserWindow.getAllWindows().length === 0) createWindow(); - }); + // On macOS re-create the window when the dock icon is clicked and there + // are no other windows open (standard macOS behaviour). + app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) createWindow(); + }); + }) + .catch((err: unknown) => { + console.error('[desktop-main] NestJS IPC bootstrap failed — quitting:', err); + app.quit(); + }); }); // Quit the app when all windows are closed, except on macOS where the app and From 211b8f830609bdf680c3224567aacbeb33751921 Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 20:27:41 -0400 Subject: [PATCH 08/24] =?UTF-8?q?fix(desktop-main):=20Haise=20=E2=80=94=20?= =?UTF-8?q?correct=20test=20descriptions=20to=20reflect=20module-init=20ti?= =?UTF-8?q?ming?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #144 --- app/packages/desktop-main/src/main.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/packages/desktop-main/src/main.test.ts b/app/packages/desktop-main/src/main.test.ts index 9110a9d..f4f5e35 100644 --- a/app/packages/desktop-main/src/main.test.ts +++ b/app/packages/desktop-main/src/main.test.ts @@ -115,7 +115,7 @@ describe('main bootstrap', () => { expect(fakeApp.listen).toHaveBeenCalledOnce(); }); - it('should call applyFixPath during bootstrap', async () => { + it('should call applyFixPath on module initialisation', async () => { vi.resetModules(); const { bootstrap } = await import('./main.js'); await bootstrap(); @@ -127,7 +127,7 @@ describe('main bootstrap', () => { expect(applyFixPathMock).toHaveBeenCalledTimes(1); }); - it('should resolve userData path and initialise the file logger before bootstrap', async () => { + it('should resolve userData path and initialise the file logger on module initialisation', async () => { vi.resetModules(); const { bootstrap } = await import('./main.js'); await bootstrap(); From e02f782b003c1baaad04130a4ad98882b46f1d48 Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 20:53:51 -0400 Subject: [PATCH 09/24] fix: regenerate package-lock.json with Node 24/npm 11 to include cross-platform optional packages npm 10 on Linux omitted optional platform packages for darwin/win32/android from the lockfile. npm 11 (Node 24, used by CI) requires them to be present for npm ci. Refs #144 --- package-lock.json | 579 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 551 insertions(+), 28 deletions(-) diff --git a/package-lock.json b/package-lock.json index fe61ce6..bee19b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3891,18 +3891,288 @@ "dev": true, "license": "MIT" }, - "node_modules/@rollup/rollup-linux-x64-gnu": { + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.3.tgz", + "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.3.tgz", + "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.3.tgz", + "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.3.tgz", + "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.3.tgz", + "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.3.tgz", + "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.3.tgz", + "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, "os": [ "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.3.tgz", + "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==", + "cpu": [ + "arm" ], - "peer": true + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.3.tgz", + "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.3.tgz", + "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.3.tgz", + "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.3.tgz", + "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.3.tgz", + "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.3.tgz", + "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.3.tgz", + "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.3.tgz", + "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.3.tgz", + "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.3", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] }, "node_modules/@rollup/rollup-linux-x64-musl": { "version": "4.60.3", @@ -3914,8 +4184,91 @@ "optional": true, "os": [ "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.3.tgz", + "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==", + "cpu": [ + "x64" ], - "peer": true + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.3.tgz", + "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.3.tgz", + "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.3.tgz", + "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.3.tgz", + "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.3.tgz", + "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] }, "node_modules/@sindresorhus/is": { "version": "4.6.0", @@ -9915,6 +10268,159 @@ "lightningcss-win32-x64-msvc": "1.32.0" } }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lightningcss-linux-x64-gnu": { "version": "1.32.0", "cpu": [ @@ -9926,7 +10432,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">= 12.0.0" }, @@ -9946,7 +10451,48 @@ "os": [ "linux" ], - "peer": true, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { "node": ">= 12.0.0" }, @@ -13524,7 +14070,6 @@ "os": [ "aix" ], - "peer": true, "engines": { "node": ">=12" } @@ -13542,7 +14087,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=12" } @@ -13560,7 +14104,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=12" } @@ -13578,7 +14121,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=12" } @@ -13596,7 +14138,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=12" } @@ -13614,7 +14155,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=12" } @@ -13632,7 +14172,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=12" } @@ -13650,7 +14189,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=12" } @@ -13668,7 +14206,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -13686,7 +14223,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -13704,7 +14240,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -13722,7 +14257,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -13740,7 +14274,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -13758,7 +14291,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -13776,7 +14308,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -13794,7 +14325,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -13810,7 +14340,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=12" } @@ -13828,7 +14357,6 @@ "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=12" } @@ -13846,7 +14374,6 @@ "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=12" } @@ -13864,7 +14391,6 @@ "os": [ "sunos" ], - "peer": true, "engines": { "node": ">=12" } @@ -13882,7 +14408,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=12" } @@ -13900,7 +14425,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=12" } @@ -13918,7 +14442,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=12" } From 49fdc3ea89ea9a3a662bea0d152be93c0ff8567e Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 21:08:30 -0400 Subject: [PATCH 10/24] fix: tighten engines.node to match electron-vite@5 requirement electron-vite@5.0.0 requires ^20.19.0 || >=22.12.0. The previous >=20 allowed versions where electron-vite would refuse to run. Refs #144 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 33e5866..8f0e3a8 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts" ], "engines": { - "node": ">=20" + "node": "^20.19.0 || >=22.12.0" }, "devDependencies": { "electron-vite": "^5.0.0" From 1648270bb90641db1218546e7392ccaf3f64a4e1 Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 21:10:50 -0400 Subject: [PATCH 11/24] ci: cache Playwright browsers in e2e and integration workflows Without caching, every run re-downloads ~280MB of Chromium binaries via npx playwright install, which was causing hangs on slow runners. Refs #144 --- .github/workflows/e2e.yml | 6 ++++++ .github/workflows/integration.yml | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index f967348..0b538d0 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -22,6 +22,12 @@ jobs: - run: npm ci + - name: Cache Playwright browsers + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: playwright-chromium-${{ runner.os }}-${{ hashFiles('app/packages/web/package-lock.json', 'package-lock.json') }} + - name: Install Playwright browsers run: npx playwright install chromium --with-deps working-directory: app/packages/web diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index fcf2118..30fe23c 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -22,6 +22,12 @@ jobs: - run: npm ci + - name: Cache Playwright browsers + uses: actions/cache@v4 + with: + path: ~/.cache/ms-playwright + key: playwright-chromium-${{ runner.os }}-${{ hashFiles('app/packages/web/package-lock.json', 'package-lock.json') }} + - name: Install Playwright browsers run: npx playwright install chromium --with-deps working-directory: app/packages/web From 917b6450dd8f2bc17a9b28e2ca8141ef5172e0e6 Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 21:11:27 -0400 Subject: [PATCH 12/24] =?UTF-8?q?chore:=20drop=20Node=2020=20support=20?= =?UTF-8?q?=E2=80=94=20require=20>=3D22.12.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Node 20 EOL was April 2026. CI already runs on Node 24. electron-vite requires ^20.19.0 || >=22.12.0 so Node 22.12+ covers it cleanly. Refs #144 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8f0e3a8..169bd63 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "scripts" ], "engines": { - "node": "^20.19.0 || >=22.12.0" + "node": ">=22.12.0" }, "devDependencies": { "electron-vite": "^5.0.0" From 2c8fc4055642d715ed6a3267cc6899fe38f4c808 Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 21:14:09 -0400 Subject: [PATCH 13/24] chore: remove commented-out Claude Actions workflows Both files were fully commented out (no-ops) but still appeared as failed check runs on every PR push. Removing them cleans up the CI status view. Refs #144 --- .github/workflows/claude-code-review.yml | 44 --------------------- .github/workflows/claude.yml | 50 ------------------------ 2 files changed, 94 deletions(-) delete mode 100644 .github/workflows/claude-code-review.yml delete mode 100644 .github/workflows/claude.yml diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml deleted file mode 100644 index 1ecba69..0000000 --- a/.github/workflows/claude-code-review.yml +++ /dev/null @@ -1,44 +0,0 @@ -# name: Claude Code Review - -# on: -# pull_request: -# types: [opened, synchronize, ready_for_review, reopened] -# # Optional: Only run on specific file changes -# # paths: -# # - "src/**/*.ts" -# # - "src/**/*.tsx" -# # - "src/**/*.js" -# # - "src/**/*.jsx" - -# jobs: -# claude-review: -# # Optional: Filter by PR author -# # if: | -# # github.event.pull_request.user.login == 'external-contributor' || -# # github.event.pull_request.user.login == 'new-developer' || -# # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' - -# runs-on: ubuntu-latest -# permissions: -# contents: read -# pull-requests: read -# issues: read -# id-token: write - -# steps: -# - name: Checkout repository -# uses: actions/checkout@v4 -# with: -# fetch-depth: 1 - -# - name: Run Claude Code Review -# id: claude-review -# uses: anthropics/claude-code-action@v1 -# with: -# claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} -# plugin_marketplaces: 'https://github.com/anthropics/claude-code.git' -# plugins: 'code-review@claude-code-plugins' -# prompt: '/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}' -# # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md -# # or https://code.claude.com/docs/en/cli-reference for available options - diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml deleted file mode 100644 index 812b1d5..0000000 --- a/.github/workflows/claude.yml +++ /dev/null @@ -1,50 +0,0 @@ -# name: Claude Code - -# on: -# issue_comment: -# types: [created] -# pull_request_review_comment: -# types: [created] -# issues: -# types: [opened, assigned] -# pull_request_review: -# types: [submitted] - -# jobs: -# claude: -# if: | -# (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || -# (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || -# (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || -# (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) -# runs-on: ubuntu-latest -# permissions: -# contents: read -# pull-requests: read -# issues: read -# id-token: write -# actions: read # Required for Claude to read CI results on PRs -# steps: -# - name: Checkout repository -# uses: actions/checkout@v4 -# with: -# fetch-depth: 1 - -# - name: Run Claude Code -# id: claude -# uses: anthropics/claude-code-action@v1 -# with: -# claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} - -# # This is an optional setting that allows Claude to read CI results on PRs -# additional_permissions: | -# actions: read - -# # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. -# # prompt: 'Update the pull request description to include a summary of changes.' - -# # Optional: Add claude_args to customize behavior and configuration -# # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md -# # or https://code.claude.com/docs/en/cli-reference for available options -# # claude_args: '--allowed-tools Bash(gh pr *)' - From 27b038edf0171c675513b4d9b1e3cf2f091b1ad1 Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 21:24:49 -0400 Subject: [PATCH 14/24] ci: split Playwright install into deps + binary steps with timeouts --with-deps in a single step was hanging silently. Split into: - install-deps (DEBIAN_FRONTEND=noninteractive, 5min timeout) - install binary (5min timeout) Added job-level timeout-minutes: 20 so hung jobs fail fast. Refs #144 --- .github/workflows/e2e.yml | 11 +++++++++-- .github/workflows/integration.yml | 11 +++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 0b538d0..d392834 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -11,6 +11,7 @@ permissions: jobs: e2e: runs-on: ubuntu-latest + timeout-minutes: 20 steps: - uses: actions/checkout@v5 @@ -28,9 +29,15 @@ jobs: path: ~/.cache/ms-playwright key: playwright-chromium-${{ runner.os }}-${{ hashFiles('app/packages/web/package-lock.json', 'package-lock.json') }} - - name: Install Playwright browsers - run: npx playwright install chromium --with-deps + - name: Install Playwright system dependencies + run: DEBIAN_FRONTEND=noninteractive npx playwright install-deps chromium working-directory: app/packages/web + timeout-minutes: 5 + + - name: Install Playwright browser binaries + run: npx playwright install chromium + working-directory: app/packages/web + timeout-minutes: 5 - run: npm run app:test:e2e diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 30fe23c..9ffac0b 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -11,6 +11,7 @@ permissions: jobs: integration: runs-on: ubuntu-latest + timeout-minutes: 20 steps: - uses: actions/checkout@v5 @@ -28,9 +29,15 @@ jobs: path: ~/.cache/ms-playwright key: playwright-chromium-${{ runner.os }}-${{ hashFiles('app/packages/web/package-lock.json', 'package-lock.json') }} - - name: Install Playwright browsers - run: npx playwright install chromium --with-deps + - name: Install Playwright system dependencies + run: DEBIAN_FRONTEND=noninteractive npx playwright install-deps chromium working-directory: app/packages/web + timeout-minutes: 5 + + - name: Install Playwright browser binaries + run: npx playwright install chromium + working-directory: app/packages/web + timeout-minutes: 5 - run: npm run app:test:integration From a3698caf8a893cea8e8c8077ba04982cf73f096b Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 21:32:48 -0400 Subject: [PATCH 15/24] ci: use system Chrome instead of downloading Chromium in CI npx playwright install chromium hangs after download (extraction phase never exits). ubuntu-latest ships google-chrome-stable; use channel: 'chrome' in CI so Playwright picks it up with no download. Drops the browser binary install step from both e2e and integration workflows. Keeps install-deps for system library requirements. Refs #144 --- .github/workflows/e2e.yml | 12 ------------ .github/workflows/integration.yml | 12 ------------ app/packages/web/playwright.config.ts | 9 ++++++++- app/packages/web/playwright.integration.config.ts | 9 ++++++++- 4 files changed, 16 insertions(+), 26 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index d392834..798829a 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -23,21 +23,9 @@ jobs: - run: npm ci - - name: Cache Playwright browsers - uses: actions/cache@v4 - with: - path: ~/.cache/ms-playwright - key: playwright-chromium-${{ runner.os }}-${{ hashFiles('app/packages/web/package-lock.json', 'package-lock.json') }} - - name: Install Playwright system dependencies run: DEBIAN_FRONTEND=noninteractive npx playwright install-deps chromium working-directory: app/packages/web - timeout-minutes: 5 - - - name: Install Playwright browser binaries - run: npx playwright install chromium - working-directory: app/packages/web - timeout-minutes: 5 - run: npm run app:test:e2e diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 9ffac0b..711445d 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -23,21 +23,9 @@ jobs: - run: npm ci - - name: Cache Playwright browsers - uses: actions/cache@v4 - with: - path: ~/.cache/ms-playwright - key: playwright-chromium-${{ runner.os }}-${{ hashFiles('app/packages/web/package-lock.json', 'package-lock.json') }} - - name: Install Playwright system dependencies run: DEBIAN_FRONTEND=noninteractive npx playwright install-deps chromium working-directory: app/packages/web - timeout-minutes: 5 - - - name: Install Playwright browser binaries - run: npx playwright install chromium - working-directory: app/packages/web - timeout-minutes: 5 - run: npm run app:test:integration diff --git a/app/packages/web/playwright.config.ts b/app/packages/web/playwright.config.ts index a1f8b9d..f6d2d6f 100644 --- a/app/packages/web/playwright.config.ts +++ b/app/packages/web/playwright.config.ts @@ -15,7 +15,14 @@ export default defineConfig({ video: 'retain-on-failure', }, projects: [ - { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + // In CI use the pre-installed system Chrome to avoid downloading Chromium + ...(process.env.CI ? { channel: 'chrome' } : {}), + }, + }, ], webServer: { command: 'npm run build && npm run preview', diff --git a/app/packages/web/playwright.integration.config.ts b/app/packages/web/playwright.integration.config.ts index 112a850..023c23f 100644 --- a/app/packages/web/playwright.integration.config.ts +++ b/app/packages/web/playwright.integration.config.ts @@ -22,7 +22,14 @@ export default defineConfig({ video: 'retain-on-failure', }, projects: [ - { name: 'chromium', use: { ...devices['Desktop Chrome'] } }, + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + // In CI use the pre-installed system Chrome to avoid downloading Chromium + ...(process.env.CI ? { channel: 'chrome' } : {}), + }, + }, ], webServer: [ { From ba3b1d95ddacb8ccf63eff103d917e5261ad3c9e Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 21:33:26 -0400 Subject: [PATCH 16/24] ci: add timeout-minutes: 5 to install-deps step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensures apt-get step can't hang silently — the job-level 20min cap remains as the overall ceiling. Refs #144 --- .github/workflows/e2e.yml | 1 + .github/workflows/integration.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 798829a..ab159a3 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -26,6 +26,7 @@ jobs: - name: Install Playwright system dependencies run: DEBIAN_FRONTEND=noninteractive npx playwright install-deps chromium working-directory: app/packages/web + timeout-minutes: 5 - run: npm run app:test:e2e diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 711445d..402a516 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -26,6 +26,7 @@ jobs: - name: Install Playwright system dependencies run: DEBIAN_FRONTEND=noninteractive npx playwright install-deps chromium working-directory: app/packages/web + timeout-minutes: 5 - run: npm run app:test:integration From 93314d4431467f2bc1b42b4c21b6a9f857a4f0bd Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 21:37:26 -0400 Subject: [PATCH 17/24] ci: install Playwright ffmpeg for video capture on test failure video: 'retain-on-failure' requires ffmpeg. Previously it was pulled in by 'npx playwright install chromium --with-deps' but we switched to system Chrome. Install ffmpeg explicitly (3min timeout). Refs #144 --- .github/workflows/e2e.yml | 5 +++++ .github/workflows/integration.yml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index ab159a3..056fd2c 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -28,6 +28,11 @@ jobs: working-directory: app/packages/web timeout-minutes: 5 + - name: Install Playwright ffmpeg + run: npx playwright install ffmpeg + working-directory: app/packages/web + timeout-minutes: 3 + - run: npm run app:test:e2e - uses: actions/upload-artifact@v4 diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 402a516..e76035e 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -28,6 +28,11 @@ jobs: working-directory: app/packages/web timeout-minutes: 5 + - name: Install Playwright ffmpeg + run: npx playwright install ffmpeg + working-directory: app/packages/web + timeout-minutes: 3 + - run: npm run app:test:integration - uses: actions/upload-artifact@v4 From b132b30faed6d93b507b276f06765a562e6d643e Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 21:42:11 -0400 Subject: [PATCH 18/24] ci: disable video recording in CI; remove ffmpeg install step MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit npx playwright install hangs post-download on ubuntu-latest regardless of binary size. Video is not needed in CI — traces retain-on-failure are sufficient for debugging. Locally video still records on failure. Refs #144 --- .github/workflows/e2e.yml | 4 ---- .github/workflows/integration.yml | 4 ---- app/packages/web/playwright.config.ts | 3 ++- app/packages/web/playwright.integration.config.ts | 3 ++- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 056fd2c..02d625b 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -28,10 +28,6 @@ jobs: working-directory: app/packages/web timeout-minutes: 5 - - name: Install Playwright ffmpeg - run: npx playwright install ffmpeg - working-directory: app/packages/web - timeout-minutes: 3 - run: npm run app:test:e2e diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index e76035e..9c35992 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -28,10 +28,6 @@ jobs: working-directory: app/packages/web timeout-minutes: 5 - - name: Install Playwright ffmpeg - run: npx playwright install ffmpeg - working-directory: app/packages/web - timeout-minutes: 3 - run: npm run app:test:integration diff --git a/app/packages/web/playwright.config.ts b/app/packages/web/playwright.config.ts index f6d2d6f..fcb32cc 100644 --- a/app/packages/web/playwright.config.ts +++ b/app/packages/web/playwright.config.ts @@ -12,7 +12,8 @@ export default defineConfig({ use: { baseURL: 'http://localhost:4173', trace: 'retain-on-failure', - video: 'retain-on-failure', + // Video requires ffmpeg which hangs on install in CI; traces are sufficient + video: process.env.CI ? 'off' : 'retain-on-failure', }, projects: [ { diff --git a/app/packages/web/playwright.integration.config.ts b/app/packages/web/playwright.integration.config.ts index 023c23f..d3f254d 100644 --- a/app/packages/web/playwright.integration.config.ts +++ b/app/packages/web/playwright.integration.config.ts @@ -19,7 +19,8 @@ export default defineConfig({ use: { baseURL: 'http://localhost:4174', trace: 'retain-on-failure', - video: 'retain-on-failure', + // Video requires ffmpeg which hangs on install in CI; traces are sufficient + video: process.env.CI ? 'off' : 'retain-on-failure', }, projects: [ { From 81f61267ed267cdafd92cb07014c5b752dad5aae Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 21:49:07 -0400 Subject: [PATCH 19/24] fix(desktop-main): lazy-load electron-store to avoid requiring electron binary in tests electron-store v11 imports electron at the top of its index.js, so any require('electron-store') fails in plain-Node environments where the electron binary hasn't been downloaded (e.g. integration test CI). Switch the import to `import type` (type-only, no runtime effect) and use createRequire inside createStore(), which is only called when readIsElectron() returns true. Plain-Node paths continue to use the in-memory Map fallback without ever loading electron-store. Refs #144 --- .../desktop-main/src/services/ElectronStoreService.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/app/packages/desktop-main/src/services/ElectronStoreService.ts b/app/packages/desktop-main/src/services/ElectronStoreService.ts index 77c16e5..3c194d0 100644 --- a/app/packages/desktop-main/src/services/ElectronStoreService.ts +++ b/app/packages/desktop-main/src/services/ElectronStoreService.ts @@ -1,6 +1,9 @@ +import { createRequire } from 'node:module'; import { Injectable } from '@nestjs/common'; -import Store from 'electron-store'; +import type Store from 'electron-store'; import { logger } from '../logger.js'; + +const _require = createRequire(import.meta.url); import { SafeStorageService } from './SafeStorageService.js'; /** @@ -147,7 +150,10 @@ export class ElectronStoreService { * directory. */ protected createStore(): Store { - return new Store({ name: 'electron-store' }); + // Lazy-require so electron-store (which imports electron at the top level) + // is only loaded inside a real Electron process, never in plain-Node tests. + const StoreClass = (_require('electron-store') as { default: typeof Store }).default; + return new StoreClass({ name: 'electron-store' }); } /** From 18b774d9373d44a284e617c2eeb4f8061dce4342 Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Sun, 31 May 2026 22:14:21 -0400 Subject: [PATCH 20/24] =?UTF-8?q?fix(build):=20address=20Copilot=20review?= =?UTF-8?q?=20=E2=80=94=20correct=20electron-vite=20script=20paths=20and?= =?UTF-8?q?=20stale=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - electron.vite.config.ts: renderer input was 'app/packages/web/index.html' which double-prefixed the root; change to 'index.html' (resolved relative to root) - app/package.json: add --config ../electron.vite.config.ts to dev; fix start to launch the built app instead of re-building it - app/packages/desktop-main/package.json: same --config fix at correct relative depth - CLAUDE.md: remove stale 'Nest on 3001 / http://localhost:3001' description; document that app:dev now launches the Electron desktop app Refs #144 Co-Authored-By: copilot-pull-request-reviewer (via PR comment) --- CLAUDE.md | 9 +++++---- app/package.json | 4 ++-- app/packages/desktop-main/package.json | 4 ++-- electron.vite.config.ts | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 0d5dc44..22b81ad 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,11 +14,12 @@ The repo uses a single **npm-workspaces** tree rooted at the repo root. Workspac # Install all workspaces in one go (run from repo root) npm install -# Run the dev servers (Nest on 3001, Vite on 5173 with /api proxy) -npm run app:dev +# Launch the Electron desktop app in dev mode (HMR on renderer saves, auto-restarts main+preload) +npm run app:dev # delegates to desktop:dev — equivalent to: npm run desktop:dev -# Production build (shared → server → web) -npm run app:build && npm run app:start # http://localhost:3001 +# Production build then launch +npm run app:build # compiles shared → desktop-main → web TypeScript +npm run app:start # starts the built Electron app # Electron desktop app — electron-vite drives three pipelines (main/preload/renderer) # configured in electron.vite.config.ts; outputs land in out/main, out/preload, out/renderer diff --git a/app/package.json b/app/package.json index cf5b0b1..02c7292 100644 --- a/app/package.json +++ b/app/package.json @@ -5,11 +5,11 @@ "type": "module", "scripts": { "predev": "node scripts/embed-tfstate.mjs", - "dev": "electron-vite dev", + "dev": "electron-vite dev --config ../electron.vite.config.ts", "prebuild": "node scripts/embed-tfstate.mjs", "build": "npm run build -w @hyveon/shared && npm run build -w @hyveon/desktop-main && npm run build -w @hyveon/web", "build:lambdas": "npm run build -w @hyveon/shared && npm run build -w @hyveon/lambda-interactions -w @hyveon/lambda-followup -w @hyveon/lambda-update-dns -w @hyveon/lambda-watchdog -w @hyveon/lambda-efs-seeder", - "start": "electron-vite build", + "start": "electron ../out/main/index.js", "test": "vitest run", "test:coverage": "vitest run --coverage", "test:watch": "vitest", diff --git a/app/packages/desktop-main/package.json b/app/packages/desktop-main/package.json index 875a2b2..d6dce2a 100644 --- a/app/packages/desktop-main/package.json +++ b/app/packages/desktop-main/package.json @@ -5,9 +5,9 @@ "type": "module", "main": "./dist/main.js", "scripts": { - "dev": "electron-vite dev", + "dev": "electron-vite dev --config ../../../electron.vite.config.ts", "build": "tsc -b", - "start": "electron-vite build" + "start": "electron ../../../out/main/index.js" }, "dependencies": { "@aws-sdk/client-cloudwatch-logs": "^3.600.0", diff --git a/electron.vite.config.ts b/electron.vite.config.ts index d10db96..6b644f0 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -33,7 +33,7 @@ export default defineConfig({ }, build: { rollupOptions: { - input: 'app/packages/web/index.html', + input: 'index.html', }, }, }, From 850bdb6a2fbbfd6ea0c636358604c3aefb21b5bd Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Tue, 2 Jun 2026 22:24:40 -0400 Subject: [PATCH 21/24] fix(desktop-main): use module-level import type for electron-store cast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit typeof Store is invalid when Store is an import-type binding — TypeScript treats it as a value reference even in type positions. Replace the cast with typeof import('electron-store') which queries the module type directly. Refs #144 Co-Authored-By: copilot-pull-request-reviewer (via PR comment) --- app/packages/desktop-main/src/services/ElectronStoreService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/packages/desktop-main/src/services/ElectronStoreService.ts b/app/packages/desktop-main/src/services/ElectronStoreService.ts index 3c194d0..3aeb840 100644 --- a/app/packages/desktop-main/src/services/ElectronStoreService.ts +++ b/app/packages/desktop-main/src/services/ElectronStoreService.ts @@ -152,7 +152,7 @@ export class ElectronStoreService { protected createStore(): Store { // Lazy-require so electron-store (which imports electron at the top level) // is only loaded inside a real Electron process, never in plain-Node tests. - const StoreClass = (_require('electron-store') as { default: typeof Store }).default; + const StoreClass = (_require('electron-store') as typeof import('electron-store')).default; return new StoreClass({ name: 'electron-store' }); } From d2d8ca34d14e69d2a554794d5833eefaad435aaa Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Tue, 2 Jun 2026 22:38:31 -0400 Subject: [PATCH 22/24] fix(desktop-main): handle loadURL/loadFile rejection in createWindow void-ing the promise dropped renderer-load failures silently. Chain a .catch() that logs and calls app.quit(), consistent with the bootstrap failure path. Adds a unit test for the new rejection branch. Refs #144 Co-Authored-By: copilot-pull-request-reviewer (via PR comment) --- .../desktop-main/src/electron-entry.test.ts | 16 ++++++++++++++++ app/packages/desktop-main/src/electron-entry.ts | 13 ++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/app/packages/desktop-main/src/electron-entry.test.ts b/app/packages/desktop-main/src/electron-entry.test.ts index d651f49..a222338 100644 --- a/app/packages/desktop-main/src/electron-entry.test.ts +++ b/app/packages/desktop-main/src/electron-entry.test.ts @@ -222,6 +222,22 @@ describe('electron-entry', () => { Object.defineProperty(process, 'platform', { value: originalPlatform, configurable: true }); }); + it('should call app.quit() when the renderer fails to load', async () => { + vi.resetModules(); + delete process.env['ELECTRON_RENDERER_URL']; + + mockLoadFile.mockRejectedValueOnce(new Error('renderer bundle missing')); + + await import('./electron-entry.js'); + await flushPromises(); + + expect(whenReadyCallbacks).toHaveLength(1); + whenReadyCallbacks[0]!(); + await flushPromises(); + + expect(mockQuit).toHaveBeenCalledOnce(); + }); + it('should call app.quit() and not open a window when bootstrap() rejects', async () => { vi.resetModules(); delete process.env['ELECTRON_RENDERER_URL']; diff --git a/app/packages/desktop-main/src/electron-entry.ts b/app/packages/desktop-main/src/electron-entry.ts index e2836ff..019563f 100644 --- a/app/packages/desktop-main/src/electron-entry.ts +++ b/app/packages/desktop-main/src/electron-entry.ts @@ -24,11 +24,14 @@ function createWindow(): void { }, }); - if (process.env.ELECTRON_RENDERER_URL) { - void win.loadURL(process.env.ELECTRON_RENDERER_URL); - } else { - void win.loadFile(path.join(__dirname, '../renderer/index.html')); - } + const load = process.env.ELECTRON_RENDERER_URL + ? win.loadURL(process.env.ELECTRON_RENDERER_URL) + : win.loadFile(path.join(__dirname, '../renderer/index.html')); + + load.catch((err: unknown) => { + console.error('[desktop-main] Renderer failed to load — quitting:', err); + app.quit(); + }); } app.whenReady().then(() => { From 6d72d3ba439e3c647813b3d844740b0ed5709309 Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Tue, 2 Jun 2026 22:47:28 -0400 Subject: [PATCH 23/24] fix(build): use dynamic import for electron-store; anchor config paths to import.meta.url MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit electron-store@11 is ESM-only — require() throws ERR_REQUIRE_ESM at runtime. Replace createRequire with a module-level conditional dynamic import guarded by process.versions.electron; plain-Node tests never trigger the import. All entrypoint/root paths in electron.vite.config.ts were relative strings resolved against cwd, breaking workspace invocations with --config. Anchor every path via fileURLToPath(new URL(p, import.meta.url)) so the config is cwd-independent. Refs #144 Co-Authored-By: copilot-pull-request-reviewer (via PR comment) --- .../src/services/ElectronStoreService.ts | 17 +++++++++++------ electron.vite.config.ts | 13 ++++++++----- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/app/packages/desktop-main/src/services/ElectronStoreService.ts b/app/packages/desktop-main/src/services/ElectronStoreService.ts index 3aeb840..68c06a9 100644 --- a/app/packages/desktop-main/src/services/ElectronStoreService.ts +++ b/app/packages/desktop-main/src/services/ElectronStoreService.ts @@ -1,11 +1,16 @@ -import { createRequire } from 'node:module'; import { Injectable } from '@nestjs/common'; import type Store from 'electron-store'; import { logger } from '../logger.js'; - -const _require = createRequire(import.meta.url); import { SafeStorageService } from './SafeStorageService.js'; +// electron-store@11 is ESM-only — require() would throw ERR_REQUIRE_ESM. +// Load via dynamic import, but only inside Electron; in plain-Node test +// environments process.versions.electron is undefined so this stays undefined +// and createStore() (which is only called inside Electron) is never reached. +const ElectronStoreModule = process.versions['electron'] + ? await import('electron-store') + : undefined; + /** * Typed schema for the application's persistent electron-store. * @@ -150,9 +155,9 @@ export class ElectronStoreService { * directory. */ protected createStore(): Store { - // Lazy-require so electron-store (which imports electron at the top level) - // is only loaded inside a real Electron process, never in plain-Node tests. - const StoreClass = (_require('electron-store') as typeof import('electron-store')).default; + // ElectronStoreModule is always defined here: the constructor calls + // createStore() only when readIsElectron() is true. + const StoreClass = ElectronStoreModule!.default; return new StoreClass({ name: 'electron-store' }); } diff --git a/electron.vite.config.ts b/electron.vite.config.ts index 6b644f0..0302835 100644 --- a/electron.vite.config.ts +++ b/electron.vite.config.ts @@ -3,12 +3,15 @@ import react from '@vitejs/plugin-react'; import tailwindcss from '@tailwindcss/vite'; import { fileURLToPath } from 'node:url'; +/** Resolve a path relative to this config file, regardless of cwd. */ +const r = (p: string) => fileURLToPath(new URL(p, import.meta.url)); + export default defineConfig({ main: { plugins: [externalizeDepsPlugin()], build: { rollupOptions: { - input: 'app/packages/desktop-main/src/electron-entry.ts', + input: r('app/packages/desktop-main/src/electron-entry.ts'), output: { format: 'es', }, @@ -19,21 +22,21 @@ export default defineConfig({ plugins: [externalizeDepsPlugin()], build: { rollupOptions: { - input: 'app/packages/desktop-preload/src/preload.ts', + input: r('app/packages/desktop-preload/src/preload.ts'), }, }, }, renderer: { - root: 'app/packages/web', + root: r('app/packages/web'), plugins: [react(), tailwindcss()], resolve: { alias: { - '@': fileURLToPath(new URL('app/packages/web/src', import.meta.url)), + '@': r('app/packages/web/src'), }, }, build: { rollupOptions: { - input: 'index.html', + input: r('app/packages/web/index.html'), }, }, }, From 0414342775fbea2a75382a46dc17029ea6d08f3d Mon Sep 17 00:00:00 2001 From: CoderCoco Date: Tue, 2 Jun 2026 23:15:03 -0400 Subject: [PATCH 24/24] docs(CLAUDE.md): clarify that desktop:build produces the Electron bundle npm run app:build compiles TypeScript workspaces but does not run electron-vite, so out/main/index.js is never produced. app:start (electron out/main/index.js) therefore fails unless desktop:build ran first. Document the correct three-step production flow. Refs #144 Co-Authored-By: copilot-pull-request-reviewer (via PR comment) --- CLAUDE.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 22b81ad..b20fb93 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,8 +18,9 @@ npm install npm run app:dev # delegates to desktop:dev — equivalent to: npm run desktop:dev # Production build then launch -npm run app:build # compiles shared → desktop-main → web TypeScript -npm run app:start # starts the built Electron app +npm run app:build # compiles shared → desktop-main → web TypeScript (not the Electron bundle) +npm run desktop:build # electron-vite build → produces out/main, out/preload, out/renderer +npm run app:start # starts the built Electron app (requires desktop:build to have run first) # Electron desktop app — electron-vite drives three pipelines (main/preload/renderer) # configured in electron.vite.config.ts; outputs land in out/main, out/preload, out/renderer