Implement basic fs component with web implementation

Implement server side object creation
implicitly include core Component types
remove @sveltejs/kit dependency
This commit is contained in:
DH
2025-08-29 19:38:41 +03:00
parent 524538fbdd
commit b0da307183
27 changed files with 904 additions and 1098 deletions

View File

@@ -396,9 +396,9 @@ tryFetchPs3Game(const std::filesystem::directory_entry &entry) {
}
struct ExplorerDescriber : ExplorerDescriberInterface {
ExplorerDescribeResponse
describe(const ExplorerDescribeRequest &request) override {
ExplorerDescribeResponse result;
ExplorerDescriberDescribeResponse
describe(const ExplorerDescriberDescribeRequest &request) override {
ExplorerDescriberDescribeResponse result;
unsigned index = -1;
for (auto &uri : request.uris) {

814
package-lock.json generated
View File

@@ -4360,18 +4360,6 @@
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/remapping": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
@@ -4543,13 +4531,6 @@
"node": ">=14"
}
},
"node_modules/@polka/url": {
"version": "1.0.0-next.29",
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
"integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
"dev": true,
"license": "MIT"
},
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
@@ -5054,306 +5035,6 @@
"url": "https://github.com/SpacingBat3/ReForged?sponsor=1"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.47.1.tgz",
"integrity": "sha512-lTahKRJip0knffA/GTNFJMrToD+CM+JJ+Qt5kjzBK/sFQ0EWqfKW3AYQSlZXN98tX0lx66083U9JYIMioMMK7g==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"peer": true
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.47.1.tgz",
"integrity": "sha512-uqxkb3RJLzlBbh/bbNQ4r7YpSZnjgMgyoEOY7Fy6GCbelkDSAzeiogxMG9TfLsBbqmGsdDObo3mzGqa8hps4MA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"peer": true
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.47.1.tgz",
"integrity": "sha512-tV6reObmxBDS4DDyLzTDIpymthNlxrLBGAoQx6m2a7eifSNEZdkXQl1PE4ZjCkEDPVgNXSzND/k9AQ3mC4IOEQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"peer": true
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.47.1.tgz",
"integrity": "sha512-XuJRPTnMk1lwsSnS3vYyVMu4x/+WIw1MMSiqj5C4j3QOWsMzbJEK90zG+SWV1h0B1ABGCQ0UZUjti+TQK35uHQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"peer": true
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.47.1.tgz",
"integrity": "sha512-79BAm8Ag/tmJ5asCqgOXsb3WY28Rdd5Lxj8ONiQzWzy9LvWORd5qVuOnjlqiWWZJw+dWewEktZb5yiM1DLLaHw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"peer": true
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.47.1.tgz",
"integrity": "sha512-OQ2/ZDGzdOOlyfqBiip0ZX/jVFekzYrGtUsqAfLDbWy0jh1PUU18+jYp8UMpqhly5ltEqotc2miLngf9FPSWIA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.47.1.tgz",
"integrity": "sha512-HZZBXJL1udxlCVvoVadstgiU26seKkHbbAMLg7680gAcMnRNP9SAwTMVet02ANA94kXEI2VhBnXs4e5nf7KG2A==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.47.1.tgz",
"integrity": "sha512-sZ5p2I9UA7T950JmuZ3pgdKA6+RTBr+0FpK427ExW0t7n+QwYOcmDTK/aRlzoBrWyTpJNlS3kacgSlSTUg6P/Q==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.47.1.tgz",
"integrity": "sha512-3hBFoqPyU89Dyf1mQRXCdpc6qC6At3LV6jbbIOZd72jcx7xNk3aAp+EjzAtN6sDlmHFzsDJN5yeUySvorWeRXA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.47.1.tgz",
"integrity": "sha512-49J4FnMHfGodJWPw73Ve+/hsPjZgcXQGkmqBGZFvltzBKRS+cvMiWNLadOMXKGnYRhs1ToTGM0sItKISoSGUNA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.47.1.tgz",
"integrity": "sha512-4yYU8p7AneEpQkRX03pbpLmE21z5JNys16F1BZBZg5fP9rIlb0TkeQjn5du5w4agConCCEoYIG57sNxjryHEGg==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.47.1.tgz",
"integrity": "sha512-fAiq+J28l2YMWgC39jz/zPi2jqc0y3GSRo1yyxlBHt6UN0yYgnegHSRPa3pnHS5amT/efXQrm0ug5+aNEu9UuQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.47.1.tgz",
"integrity": "sha512-daoT0PMENNdjVYYU9xec30Y2prb1AbEIbb64sqkcQcSaR0zYuKkoPuhIztfxuqN82KYCKKrj+tQe4Gi7OSm1ow==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.47.1.tgz",
"integrity": "sha512-JNyXaAhWtdzfXu5pUcHAuNwGQKevR+6z/poYQKVW+pLaYOj9G1meYc57/1Xv2u4uTxfu9qEWmNTjv/H/EpAisw==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.47.1.tgz",
"integrity": "sha512-U/CHbqKSwEQyZXjCpY43/GLYcTVKEXeRHw0rMBJP7fP3x6WpYG4LTJWR3ic6TeYKX6ZK7mrhltP4ppolyVhLVQ==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.47.1.tgz",
"integrity": "sha512-uTLEakjxOTElfeZIGWkC34u2auLHB1AYS6wBjPGI00bWdxdLcCzK5awjs25YXpqB9lS8S0vbO0t9ZcBeNibA7g==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.47.1.tgz",
"integrity": "sha512-Ft+d/9DXs30BK7CHCTX11FtQGHUdpNDLJW0HHLign4lgMgBcPFN3NkdIXhC5r9iwsMwYreBBc4Rho5ieOmKNVQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"peer": true
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.47.1.tgz",
"integrity": "sha512-N9X5WqGYzZnjGAFsKSfYFtAShYjwOmFJoWbLg3dYixZOZqU7hdMq+/xyS14zKLhFhZDhP9VfkzQnsdk0ZDS9IA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.47.1.tgz",
"integrity": "sha512-O+KcfeCORZADEY8oQJk4HK8wtEOCRE4MdOkb8qGZQNun3jzmj2nmhV/B/ZaaZOkPmJyvm/gW9n0gsB4eRa1eiQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.47.1.tgz",
"integrity": "sha512-CpKnYa8eHthJa3c+C38v/E+/KZyF1Jdh2Cz3DyKZqEWYgrM1IHFArXNWvBLPQCKUEsAqqKX27tTqVEFbDNUcOA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"peer": true
},
"node_modules/@rtsao/scc": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
@@ -5405,104 +5086,6 @@
"dev": true,
"license": "ISC"
},
"node_modules/@standard-schema/spec": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz",
"integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==",
"dev": true,
"license": "MIT"
},
"node_modules/@sveltejs/acorn-typescript": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.5.tgz",
"integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==",
"dev": true,
"license": "MIT",
"peerDependencies": {
"acorn": "^8.9.0"
}
},
"node_modules/@sveltejs/kit": {
"version": "2.36.1",
"resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.36.1.tgz",
"integrity": "sha512-dldNCtSIpaGxQMEfHaUxSPH/k3uU28pTZwtKzfkn8fqpOjWufKlMBeIL7FJ/s93dOrhEq41zaQYkXh+XTgEgVw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@sveltejs/acorn-typescript": "^1.0.5",
"@types/cookie": "^0.6.0",
"acorn": "^8.14.1",
"cookie": "^0.6.0",
"devalue": "^5.1.0",
"esm-env": "^1.2.2",
"kleur": "^4.1.5",
"magic-string": "^0.30.5",
"mrmime": "^2.0.0",
"sade": "^1.8.1",
"set-cookie-parser": "^2.6.0",
"sirv": "^3.0.0"
},
"bin": {
"svelte-kit": "svelte-kit.js"
},
"engines": {
"node": ">=18.13"
},
"peerDependencies": {
"@opentelemetry/api": "^1.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0",
"svelte": "^4.0.0 || ^5.0.0-next.0",
"vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0"
},
"peerDependenciesMeta": {
"@opentelemetry/api": {
"optional": true
}
}
},
"node_modules/@sveltejs/vite-plugin-svelte": {
"version": "6.1.3",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.1.3.tgz",
"integrity": "sha512-3pppgIeIZs6nrQLazzKcdnTJ2IWiui/UucEPXKyFG35TKaHQrfkWBnv6hyJcLxFuR90t+LaoecrqTs8rJKWfSQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@sveltejs/vite-plugin-svelte-inspector": "^5.0.0",
"debug": "^4.4.1",
"deepmerge": "^4.3.1",
"kleur": "^4.1.5",
"magic-string": "^0.30.17",
"vitefu": "^1.1.1"
},
"engines": {
"node": "^20.19 || ^22.12 || >=24"
},
"peerDependencies": {
"svelte": "^5.0.0",
"vite": "^6.3.0 || ^7.0.0"
}
},
"node_modules/@sveltejs/vite-plugin-svelte-inspector": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-5.0.1.tgz",
"integrity": "sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"debug": "^4.4.1"
},
"engines": {
"node": "^20.19 || ^22.12 || >=24"
},
"peerDependencies": {
"@sveltejs/vite-plugin-svelte": "^6.0.0-next.0",
"svelte": "^5.0.0",
"vite": "^6.3.0 || ^7.0.0"
}
},
"node_modules/@szmarczak/http-timer": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
@@ -5591,13 +5174,6 @@
"@types/responselike": "^1.0.0"
}
},
"node_modules/@types/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/estree": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
@@ -6523,17 +6099,6 @@
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
"license": "Python-2.0"
},
"node_modules/aria-query": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
"integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": ">= 0.4"
}
},
"node_modules/array-buffer-byte-length": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
@@ -6752,17 +6317,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/axobject-query": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
"integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": ">= 0.4"
}
},
"node_modules/babel-jest": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
@@ -7650,17 +7204,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6"
}
},
"node_modules/color": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
@@ -7825,16 +7368,6 @@
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"license": "MIT"
},
"node_modules/cookie": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
"integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/core-js-compat": {
"version": "3.45.1",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.45.1.tgz",
@@ -8206,13 +7739,6 @@
"license": "MIT",
"optional": true
},
"node_modules/devalue": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz",
"integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==",
"dev": true,
"license": "MIT"
},
"node_modules/dir-compare": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz",
@@ -9219,13 +8745,6 @@
"node": "*"
}
},
"node_modules/esm-env": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz",
"integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==",
"dev": true,
"license": "MIT"
},
"node_modules/espree": {
"version": "10.4.0",
"resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
@@ -9283,17 +8802,6 @@
"node": ">=0.10"
}
},
"node_modules/esrap": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/esrap/-/esrap-2.1.0.tgz",
"integrity": "sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15"
}
},
"node_modules/esrecurse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
@@ -11414,17 +10922,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-reference": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
"integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "^1.0.6"
}
},
"node_modules/is-regex": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
@@ -11999,16 +11496,6 @@
"json-buffer": "3.0.1"
}
},
"node_modules/kleur": {
"version": "4.1.5",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
"integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/lan-network": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/lan-network/-/lan-network-0.1.7.tgz",
@@ -12390,14 +11877,6 @@
"node": ">=0.10.0"
}
},
"node_modules/locate-character": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
"integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
"dev": true,
"license": "MIT",
"peer": true
},
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -12539,16 +12018,6 @@
"yallist": "^3.0.2"
}
},
"node_modules/magic-string": {
"version": "0.30.18",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.18.tgz",
"integrity": "sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/make-fetch-happen": {
"version": "10.2.1",
"resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz",
@@ -13367,26 +12836,6 @@
"integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==",
"license": "MIT"
},
"node_modules/mri": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
"integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
}
},
"node_modules/mrmime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
"integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -14257,36 +13706,6 @@
"node": ">= 0.4"
}
},
"node_modules/postcss": {
"version": "8.5.6",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
"integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"nanoid": "^3.3.11",
"picocolors": "^1.1.1",
"source-map-js": "^1.2.1"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/postcss-value-parser": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
@@ -15524,47 +14943,6 @@
"node": ">=8.0"
}
},
"node_modules/rollup": {
"version": "4.47.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.47.1.tgz",
"integrity": "sha512-iasGAQoZ5dWDzULEUX3jiW0oB1qyFOepSyDyoU6S/OhVlDIwj5knI5QBa5RRQ0sK7OE0v+8VIi2JuV+G+3tfNg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.47.1",
"@rollup/rollup-android-arm64": "4.47.1",
"@rollup/rollup-darwin-arm64": "4.47.1",
"@rollup/rollup-darwin-x64": "4.47.1",
"@rollup/rollup-freebsd-arm64": "4.47.1",
"@rollup/rollup-freebsd-x64": "4.47.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.47.1",
"@rollup/rollup-linux-arm-musleabihf": "4.47.1",
"@rollup/rollup-linux-arm64-gnu": "4.47.1",
"@rollup/rollup-linux-arm64-musl": "4.47.1",
"@rollup/rollup-linux-loongarch64-gnu": "4.47.1",
"@rollup/rollup-linux-ppc64-gnu": "4.47.1",
"@rollup/rollup-linux-riscv64-gnu": "4.47.1",
"@rollup/rollup-linux-riscv64-musl": "4.47.1",
"@rollup/rollup-linux-s390x-gnu": "4.47.1",
"@rollup/rollup-linux-x64-gnu": "4.47.1",
"@rollup/rollup-linux-x64-musl": "4.47.1",
"@rollup/rollup-win32-arm64-msvc": "4.47.1",
"@rollup/rollup-win32-ia32-msvc": "4.47.1",
"@rollup/rollup-win32-x64-msvc": "4.47.1",
"fsevents": "~2.3.2"
}
},
"node_modules/rpcsx-ui-kit": {
"resolved": "rpcsx-ui-kit",
"link": true
@@ -15593,19 +14971,6 @@
"queue-microtask": "^1.2.2"
}
},
"node_modules/sade": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz",
"integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==",
"dev": true,
"license": "MIT",
"dependencies": {
"mri": "^1.1.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/safe-array-concat": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
@@ -15969,13 +15334,6 @@
"integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==",
"license": "MIT"
},
"node_modules/set-cookie-parser": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
"dev": true,
"license": "MIT"
},
"node_modules/set-function-length": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
@@ -16211,21 +15569,6 @@
"integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
"license": "MIT"
},
"node_modules/sirv": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.1.tgz",
"integrity": "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==",
"dev": true,
"license": "MIT",
"dependencies": {
"@polka/url": "^1.0.0-next.24",
"mrmime": "^2.0.0",
"totalist": "^3.0.0"
},
"engines": {
"node": ">=18"
}
},
"node_modules/sisteransi": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
@@ -16943,33 +16286,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/svelte": {
"version": "5.38.2",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-5.38.2.tgz",
"integrity": "sha512-iAcp/oFAWauVSGILdD67n7DiwgLHXZzWZIdzl7araRxu72jUr7PFAo2Iie7gXt0IbnlYvhxCb9GT3ZJUquO3PA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@jridgewell/remapping": "^2.3.4",
"@jridgewell/sourcemap-codec": "^1.5.0",
"@sveltejs/acorn-typescript": "^1.0.5",
"@types/estree": "^1.0.5",
"acorn": "^8.12.1",
"aria-query": "^5.3.1",
"axobject-query": "^4.1.0",
"clsx": "^2.1.1",
"esm-env": "^1.2.1",
"esrap": "^2.1.0",
"is-reference": "^3.0.3",
"locate-character": "^3.0.0",
"magic-string": "^0.30.11",
"zimmerframe": "^1.1.2"
},
"engines": {
"node": ">=18"
}
},
"node_modules/tar": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
@@ -17222,16 +16538,6 @@
"node": ">=0.6"
}
},
"node_modules/totalist": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
"integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
@@ -17767,117 +17073,6 @@
"node": ">= 0.8"
}
},
"node_modules/vite": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.3.tgz",
"integrity": "sha512-OOUi5zjkDxYrKhTV3V7iKsoS37VUM7v40+HuwEmcrsf11Cdx9y3DIr2Px6liIcZFwt3XSRpQvFpL3WVy7ApkGw==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
"picomatch": "^4.0.3",
"postcss": "^8.5.6",
"rollup": "^4.43.0",
"tinyglobby": "^0.2.14"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^20.19.0 || >=22.12.0",
"jiti": ">=1.21.0",
"less": "^4.0.0",
"lightningcss": "^1.21.0",
"sass": "^1.70.0",
"sass-embedded": "^1.70.0",
"stylus": ">=0.54.8",
"sugarss": "^5.0.0",
"terser": "^5.16.0",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"jiti": {
"optional": true
},
"less": {
"optional": true
},
"lightningcss": {
"optional": true
},
"sass": {
"optional": true
},
"sass-embedded": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
},
"tsx": {
"optional": true
},
"yaml": {
"optional": true
}
}
},
"node_modules/vite/node_modules/picomatch": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/jonschlinkert"
}
},
"node_modules/vitefu": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz",
"integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==",
"dev": true,
"license": "MIT",
"peer": true,
"workspaces": [
"tests/deps/*",
"tests/projects/*",
"tests/projects/workspace/packages/*"
],
"peerDependencies": {
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0"
},
"peerDependenciesMeta": {
"vite": {
"optional": true
}
}
},
"node_modules/vlq": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz",
@@ -18386,14 +17581,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/zimmerframe": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz",
"integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==",
"dev": true,
"license": "MIT",
"peer": true
},
"rpcsx-ui-kit": {
"version": "0.1.0",
"license": "GPL-3.0-only",
@@ -18408,7 +17595,6 @@
"rpcsx-ui-kit": "build/main.js"
},
"devDependencies": {
"@sveltejs/kit": "^2.31.0",
"@types/yargs": "^17.0.33",
"@typescript-eslint/eslint-plugin": "^8.39.1",
"@typescript-eslint/parser": "^8.39.1",

View File

@@ -21,7 +21,6 @@
"yargs": "^18.0.0"
},
"devDependencies": {
"@sveltejs/kit": "^2.31.0",
"@types/yargs": "^17.0.33",
"@typescript-eslint/eslint-plugin": "^8.39.1",
"@typescript-eslint/parser": "^8.39.1",

View File

@@ -120,7 +120,7 @@ ${components.map(x => `#include <${this.getPrefix("/")}/${x.manifest.name}.hpp>`
};
class CppTypesGenerator implements ContributionGenerator {
generatedTypes: Record<string, string> = {};
body = "";
includes = new Set<string>();
constructor(private namespace: string, private libPath: string) {
}
@@ -129,13 +129,12 @@ class CppTypesGenerator implements ContributionGenerator {
let result = '#pragma once\n\n';
result += [...this.includes].map(x => `#include <${x}>`).join("\n");
result += `\n\nnamespace ${this.namespace} {\n`;
result += Object.values(this.generatedTypes).join("\n");
result += this.body;
return result + `\n} // namespace ${this.namespace}\n`;
}
generateType(component: string, type: object, name: string) {
const labelName = generateComponentLabelName(component, name, true);
const typeName = labelName;
if (typeof type != 'object') {
throw `${type}: must be object`;
@@ -148,36 +147,32 @@ class CppTypesGenerator implements ContributionGenerator {
throw `${name}: type must be string value`;
}
if (!(typeName in this.generatedTypes)) {
let paramsType = "";
if (type.type === "object") {
if (!("params" in type)) {
throw `${type}: params must be present`;
}
if ((typeof type.params != 'object') || !type.params) {
throw `${type.params}: must be object`;
}
paramsType += `struct ${labelName} {\n${this.generateObjectBody(component, type.params)}};\n\n`;
paramsType += this.generateObjectSerializer(labelName, type.params);
paramsType += this.generateObjectDeserializer(labelName, type.params);
} else if (type.type === "enum") {
if (!("enumerators" in type)) {
throw `${type}: enumerators must be present`;
}
if ((typeof type.enumerators != 'object') || !type.enumerators) {
throw `${type.enumerators}: must be object`;
}
paramsType += `enum class ${labelName} {\n${this.generateEnumBody(type.enumerators)}};\n`;
let paramsType = "";
if (type.type === "object") {
if (!("params" in type)) {
throw `${type}: params must be present`;
}
this.generatedTypes[typeName] = paramsType;
} else {
throw new Error(`${name}: type ${typeName} already declared`);
if ((typeof type.params != 'object') || !type.params) {
throw `${type.params}: must be object`;
}
paramsType += `struct ${labelName} {\n${this.generateObjectBody(component, type.params)}};\n\n`;
paramsType += this.generateObjectSerializer(labelName, type.params);
paramsType += this.generateObjectDeserializer(labelName, type.params);
} else if (type.type === "enum") {
if (!("enumerators" in type)) {
throw `${type}: enumerators must be present`;
}
if ((typeof type.enumerators != 'object') || !type.enumerators) {
throw `${type.enumerators}: must be object`;
}
paramsType += `enum class ${labelName} {\n${this.generateEnumBody(type.enumerators)}};\n`;
}
this.body += paramsType;
}
generateEnumBody(enumerators: object) {
@@ -228,35 +223,50 @@ class CppTypesGenerator implements ContributionGenerator {
const requestTypeName = `${labelName}Request`;
const responseTypeName = `${labelName}Response`;
if (!(requestTypeName in this.generatedTypes)) {
{
let paramsType = '';
paramsType += `struct ${requestTypeName} {\n`;
if ("params" in method && method.params && typeof method.params == "object") {
paramsType += this.generateObjectBody(component, method.params);
if ("params" in method && method.params) {
if (typeof method.params == "object") {
paramsType += `struct ${requestTypeName} {\n`;
paramsType += this.generateObjectBody(component, method.params);
paramsType += "};\n";
paramsType += this.generateObjectSerializer(requestTypeName, method.params);
paramsType += this.generateObjectDeserializer(requestTypeName, method.params);
} else if (typeof method.params == "string") {
paramsType += `using ${requestTypeName} = ${this.getTypeName(component, method.params)}\n`;
}
} else {
paramsType += `struct ${requestTypeName} {};\n`;
paramsType += this.generateObjectSerializer(requestTypeName, {});
paramsType += this.generateObjectDeserializer(requestTypeName, {});
}
paramsType += "};\n";
paramsType += this.generateObjectSerializer(requestTypeName, "params" in method ? method.params ?? {} : {});
paramsType += this.generateObjectDeserializer(requestTypeName, "params" in method ? method.params ?? {} : {});
this.generatedTypes[requestTypeName] = paramsType;
} else {
throw new Error(`${name}: type ${requestTypeName} already declared`);
this.body += paramsType;
}
if (!(responseTypeName in this.generatedTypes)) {
let responseType = `struct ${responseTypeName} {\n`;
if ("returns" in method && method.returns && typeof method.returns == "object") {
responseType += this.generateObjectBody(component, method.returns);
}
responseType += "};\n";
responseType += this.generateObjectSerializer(responseTypeName, "returns" in method ? method.returns ?? {} : {});
responseType += this.generateObjectDeserializer(responseTypeName, "returns" in method ? method.returns ?? {} : {});
{
let responseType = '';
this.generatedTypes[responseTypeName] = responseType;
} else {
throw new Error(`${name}: type ${responseTypeName} already declared`);
if ("returns" in method && method.returns) {
if (typeof method.returns == "object") {
responseType += `struct ${responseTypeName} {\n`;
responseType += this.generateObjectBody(component, method.returns);
responseType += "};\n";
responseType += this.generateObjectSerializer(responseTypeName, method.returns);
responseType += this.generateObjectDeserializer(responseTypeName, method.returns);
} else if (typeof method.returns == "string") {
responseType += `using ${responseTypeName} = ${this.getTypeName(component, method.returns)}\n`;
}
} else {
responseType += `struct ${responseTypeName} {};\n`;
responseType += this.generateObjectSerializer(responseTypeName, {});
responseType += this.generateObjectDeserializer(responseTypeName, {});
}
this.body += responseType;
}
this.generatedTypes[labelName] = `struct ${labelName} {
this.body += `struct ${labelName} {
using Request = ${requestTypeName};
using Response = ${responseTypeName};
};
@@ -267,20 +277,28 @@ class CppTypesGenerator implements ContributionGenerator {
const labelName = generateComponentLabelName(component, name, true);
const requestTypeName = `${labelName}Request`;
if (!(requestTypeName in this.generatedTypes)) {
let paramsType = `struct ${requestTypeName} {\n`;
if ("params" in notification && notification.params && typeof notification.params == "object") {
paramsType += this.generateObjectBody(component, notification.params);
{
let paramsType = '';
if ("params" in notification && notification.params) {
if (typeof notification.params == "object") {
paramsType += `struct ${requestTypeName} {\n`;
paramsType += this.generateObjectBody(component, notification.params);
paramsType += "};\n";
paramsType += this.generateObjectSerializer(requestTypeName, notification.params);
paramsType += this.generateObjectDeserializer(requestTypeName, notification.params);
} else if (typeof notification.params == "string") {
paramsType += `using ${requestTypeName} = ${this.getTypeName(component, notification.params)}\n`;
}
} else {
paramsType += `struct ${requestTypeName} {};\n`;
paramsType += this.generateObjectSerializer(requestTypeName, {});
paramsType += this.generateObjectDeserializer(requestTypeName, {});
}
paramsType += "};\n";
paramsType += this.generateObjectSerializer(requestTypeName, "params" in notification ? notification.params ?? {} : {});
paramsType += this.generateObjectDeserializer(requestTypeName, "params" in notification ? notification.params ?? {} : {});
this.generatedTypes[requestTypeName] = paramsType;
} else {
throw new Error(`${name}: type ${requestTypeName} already declared`);
this.body += paramsType;
}
this.generatedTypes[labelName] = `struct ${labelName} {
this.body += `struct ${labelName} {
using Request = ${requestTypeName};
};
`
@@ -288,14 +306,14 @@ class CppTypesGenerator implements ContributionGenerator {
generateInterface(component: string, iface: object, name: string) {
const labelName = generateComponentLabelName(component, name + "-interface", true);
this.generatedTypes[labelName] = `struct ${labelName} {
this.body += `struct ${labelName} {
static constexpr auto kInterfaceId = "${component}/${name}";
using InterfaceType = ${labelName};
virtual ~${labelName}() = default;
${"methods" in iface ? iface.methods && Object.keys(iface.methods).map(method => {
const methodTypeLabel = generateComponentLabelName(component, method, true);
const methodTypeLabel = generateComponentLabelName(component, `${name}-${method}`, true);
if ("params" in (iface.methods as any)[method]) {
return ` virtual ${methodTypeLabel}Response ${generateLabelName(method, false)}(const ${methodTypeLabel}Request &request) = 0;`
} else {
@@ -303,7 +321,7 @@ ${"methods" in iface ? iface.methods && Object.keys(iface.methods).map(method =>
}
}).join("\n") : ""}
${"notifications" in iface ? iface.notifications && Object.keys(iface.notifications).map(notification => {
const methodTypeLabel = generateComponentLabelName(component, notification, true);
const methodTypeLabel = generateComponentLabelName(component, `${name}-${notification}`, true);
if ("params" in (iface.notifications as any)[notification]) {
return ` virtual void ${generateLabelName(notification, false)}(const ${methodTypeLabel}Request &request) = 0;`
} else {
@@ -353,22 +371,19 @@ ${"notifications" in iface ? iface.notifications && Object.keys(iface.notificati
const labelName = generateComponentLabelName(component, name, true);
const typeName = `${labelName}Event`;
if (!(typeName in this.generatedTypes)) {
if (typeof event == 'object') {
let paramsType = `struct ${typeName} {\n`;
paramsType += this.generateObjectBody(component, event);
paramsType += "\n};\n"
paramsType += this.generateObjectSerializer(typeName, event);
paramsType += this.generateObjectDeserializer(typeName, event);
this.generatedTypes[typeName] = paramsType;
} else if (typeof event == 'string') {
this.generatedTypes[typeName] = `using ${typeName} = ${this.getTypeName(component, event)};\n`;
} else {
throw new Error(`${name}: must be object or string`);
}
if (typeof event == 'object') {
let paramsType = `struct ${typeName} {\n`;
paramsType += this.generateObjectBody(component, event);
paramsType += "};\n"
paramsType += this.generateObjectSerializer(typeName, event);
paramsType += this.generateObjectDeserializer(typeName, event);
this.body += paramsType;
} else if (typeof event == 'string') {
this.body += `using ${typeName} = ${this.getTypeName(component, event)};\n`;
} else {
throw new Error(`${name}: type ${typeName} already declared`);
throw new Error(`${name}: must be object or string`);
}
}
addInclude(include: string) {
@@ -381,7 +396,8 @@ ${"notifications" in iface ? iface.notifications && Object.keys(iface.notificati
this.addInclude("string")
return "std::string";
case "number":
return "int";
this.addInclude("cstdint")
return "std::int64_t";
case "void":
return "void";
case "boolean":

View File

@@ -60,7 +60,7 @@ export type ContributionGenerator = {
generateView?(component: string, path: string, name: string): void | Promise<void>;
generateSetting?(component: string, schema: object, name: string): void | Promise<void>;
generateInterface?(component: string, iface: object, name: string): void | Promise<void>;
toString(): string;
toString(component: string): string;
};
const componentManifestName = "component.json";
@@ -640,10 +640,16 @@ class TypesGenerator implements ContributionGenerator {
if (!(requestTypeName in this.generatedTypes)) {
let paramsType = `type ${requestTypeName} = `;
if ("params" in method && method.params && typeof method.params == "object") {
paramsType += "{\n";
paramsType += this.generateObjectBody(component, method.params);
paramsType += "};\n";
if ("params" in method && method.params) {
if (typeof method.params == "object") {
paramsType += "{\n";
paramsType += this.generateObjectBody(component, method.params);
paramsType += "};\n";
} else if (typeof method.params == "string") {
paramsType += `${this.getTypeName(component, method.params)};\n`;
} else {
throw new Error(`${name}: params must be object or string`);
}
} else {
paramsType += "undefined;\n";
}
@@ -654,10 +660,16 @@ class TypesGenerator implements ContributionGenerator {
if (!(responseTypeName in this.generatedTypes)) {
let responseType = `type ${responseTypeName} = `;
if ("returns" in method && method.returns && typeof method.returns == "object") {
responseType += "{\n";
responseType += this.generateObjectBody(component, method.returns);
responseType += "}\n";
if ("returns" in method && method.returns) {
if (typeof method.returns == "object") {
responseType += "{\n";
responseType += this.generateObjectBody(component, method.returns);
responseType += "};\n";
} else if (typeof method.returns == "string") {
responseType += `${this.getTypeName(component, method.returns)};\n`;
} else {
throw new Error(`${name}: returns must be object or string`);
}
} else {
responseType += "void;\n";
}
@@ -673,10 +685,16 @@ class TypesGenerator implements ContributionGenerator {
if (!(requestTypeName in this.generatedTypes)) {
let paramsType = `type ${requestTypeName} = `;
if ("params" in notification && notification.params && typeof notification.params == "object") {
paramsType += "{\n";
paramsType += this.generateObjectBody(component, notification.params);
paramsType += "};\n";
if ("params" in notification && notification.params) {
if (typeof notification.params == "object") {
paramsType += "{\n";
paramsType += this.generateObjectBody(component, notification.params);
paramsType += "};\n";
} else if (typeof notification.params == "string") {
paramsType += `${this.getTypeName(component, notification.params)};\n`;
} else {
throw new Error(`${name}: params must be object or string`);
}
} else {
paramsType += "undefined;\n";
}
@@ -686,6 +704,29 @@ class TypesGenerator implements ContributionGenerator {
}
}
generateInterface(component: string, iface: object, name: string) {
const uLabel = generateLabelName(name, true);
this.generatedTypes[`${uLabel}Interface`] = `
type ${uLabel}Interface = {
${"methods" in iface ? iface.methods && Object.keys(iface.methods).map(method => {
const methodTypeLabel = generateComponentLabelName(component, `${name}-${method}`, true);
if ("params" in (iface.methods as any)[method]) {
return ` ${generateLabelName(method, false)}(caller: Component, request: ${methodTypeLabel}Request): ${methodTypeLabel}Response | Promise<${methodTypeLabel}Response>`;
} else {
return ` ${generateLabelName(method, false)}(caller: Component): ${methodTypeLabel}Response | Promise<${methodTypeLabel}Response>`;
}
}).join("\n") : ""}
${"notifications" in iface ? iface.notifications && Object.keys(iface.notifications).map(notification => {
const methodTypeLabel = generateComponentLabelName(component, `${name}-${notification}`, true);
if ("params" in (iface.notifications as any)[notification]) {
return ` ${generateLabelName(notification, false)}(caller: Component, request: ${methodTypeLabel}Request): void | Promise<void>;`
} else {
return ` ${generateLabelName(notification, false)}(caller: Component): void | Promise<void>;`;
}
}).join("\n") : ""}
};`
}
generateEvent(component: string, event: object, name: string) {
const labelName = generateComponentLabelName(component, name, true);
const typeName = `${labelName}Event`;
@@ -702,7 +743,7 @@ class TypesGenerator implements ContributionGenerator {
}
this.generatedTypes[typeName] = paramsType;
} else if (typeof event == 'string') {
this.generatedTypes[typeName] = `export type ${typeName} = ${this.getTypeName(component, event)};\n`;
this.generatedTypes[typeName] = `type ${typeName} = ${this.getTypeName(component, event)};\n`;
} else {
throw new Error(`${name}: must be object or string`);
}
@@ -780,7 +821,6 @@ class TypesGenerator implements ContributionGenerator {
}
class ServerPublicApiGenerator implements ContributionGenerator {
toString(): string {
return `${generatedHeader}
import { thisComponent } from "$/component-info";
@@ -801,17 +841,121 @@ export function onEvent(caller: ComponentInstance, event: string, listener: (par
}
};
function generateInterfaceContribution(component: string, iface: object, name: string) {
const uLabel = generateLabelName(name, true);
return `
export class ${uLabel}ComponentObject implements ComponentObject {
typeId = "${component}/${name}";
constructor(public impl: ${uLabel}Interface, public name: string) {}
call(caller: Component, method: string, params: JsonObject | undefined): Promise<JsonObject | void> | JsonObject | void {
void caller, params;
switch (method) {
${"methods" in iface ? iface.methods && Object.keys(iface.methods).map(method => {
if ("params" in (iface.methods as any)[method]) {
return ` case "${method}": return this.impl.${generateLabelName(method, false)}(caller, params as any);\n`
} else {
return ` case "${method}": return this.impl.${generateLabelName(method, false)}(caller);\n`
}
}).join("\n") : ""}
default:
throw createError(ErrorCode.MethodNotFound);
}
}
notify(caller: Component, method: string, params: JsonObject | undefined): void | Promise<void> {
void caller, params;
switch (method) {
${"notifications" in iface ? iface.notifications && Object.keys(iface.notifications).map(notification => {
if ("params" in (iface.notifications as any)[notification]) {
return ` case "${notification}": return this.impl.${generateLabelName(notification, false)}(caller, params as any);\n`
} else {
return ` case "${notification}": return this.impl.${generateLabelName(notification, false)}(caller);\n`
}
}).join("\n") : ""}
default:
throw createError(ErrorCode.MethodNotFound);
}
}
dispose(): void | Promise<void> {
if ("dispose" in this.impl && typeof this.impl.dispose == "function") {
return this.impl.dispose();
}
}
}
export class ${uLabel} {
constructor(private id: number) {}
${"methods" in iface ? iface.methods && Object.keys(iface.methods).map(method => {
const methodTypeLabel = generateComponentLabelName(component, `${name}-${method}`, true);
if ("params" in (iface.methods as any)[method]) {
return ` async ${generateLabelName(method, false)}(request: ${methodTypeLabel}Request): Promise<${methodTypeLabel}Response> {
return (await ${component == "core" ? "" : "core."}objectCall({ object: this.id, method: "${method}", params: request})).result as any;
}
`
} else {
return ` async ${generateLabelName(method, false)}(): Promise<${methodTypeLabel}Response> {
return (await ${component == "core" ? "" : "core."}objectCall({ object: this.id, method: "${method}", params: {}})).result as any;
}
`
}
}).join("\n") : ""}
${"notifications" in iface ? iface.notifications && Object.keys(iface.notifications).map(notification => {
const methodTypeLabel = generateComponentLabelName(component, `${name}-${notification}`, true);
if ("params" in (iface.notifications as any)[notification]) {
return ` async ${generateLabelName(notification, false)}(request: ${methodTypeLabel}Request) {
return ${component == "core" ? "" : "core."}objectNotify({ object: this.id, notification: "${notification}", params: request});
}
`
} else {
return ` async ${generateLabelName(notification, false)}() {
return ${component == "core" ? "" : "core."}objectNotify({ object: this.id, notification: "${notification}", params: {}});
}
`
}
}).join("\n") : ""}
destroy() {
return ${component == "core" ? "" : "core."}objectDestroy({ object: this.id });
}
async getName() {
return (await ${component == "core" ? "" : "core."}objectGetName({ object: this.id })).name;
}
getId() {
return this.id;
}
};
export async function get${uLabel}Objects() {
return (await ${component == "core" ? "" : "core."}objectGetList({ interface: "${component}/${name}" })).objects.map(id => new ${uLabel}(id));
}
export async function find${uLabel}Object(name: string) {
return new ${uLabel}((await ${component == "core" ? "" : "core."}objectFind({ interfaceName: "${component}/${name}", objectName: name })).object);
}
export function to${uLabel}(handle: number): ${uLabel} {
return new ${uLabel}(handle);
}
`;
}
class ServerComponentApiGenerator implements ContributionGenerator {
private body = '';
private viewBody = '';
private externalComponent?: string;
private interfaceBody = '';
generateMethod(component: string, method: object, name: string) {
if ("virtual" in method && method.virtual === true) {
return;
}
this.externalComponent ??= component;
const label = generateComponentLabelName(component, name, false);
const uLabel = generateComponentLabelName(component, name, true);
this.body += `export async function ${label}(params: ${uLabel}Request): Promise<${uLabel}Response> {
@@ -825,7 +969,6 @@ class ServerComponentApiGenerator implements ContributionGenerator {
return;
}
this.externalComponent ??= component;
const label = generateComponentLabelName(component, name, false);
const uLabel = generateComponentLabelName(component, name, true);
this.body += `export async function ${label}(params: ${uLabel}Request) {
@@ -834,8 +977,34 @@ class ServerComponentApiGenerator implements ContributionGenerator {
`;
}
generateInterface(component: string, iface: object, name: string) {
const uLabel = generateLabelName(name, true);
this.interfaceBody += generateInterfaceContribution(component, iface, name);
this.interfaceBody += `
export async function create${uLabel}Object<Impl extends ${uLabel}Interface, Params extends any[]>(name: string, Constructor: new (id: number, ...args: Params) => Impl, ...args: Params) {
const id = (await ${component == "core" ? "" : "core."}objectCreate({ interface: "${component}/${name}", name })).object;
const impl = new Constructor(id, ...args);
self.registerObject(id, new ${uLabel}ComponentObject(impl, name));
return impl;
}
export function on${uLabel}Created(handler: (object: ${uLabel}) => Promise<void> | void) {
return ${component == "core" ? "" : "core."}onObjectCreated((params) => {
if (params.interface == "${component}/${name}") {
handler(new ${uLabel}(params.object));
}
});
}
export function onAny${uLabel}Created(handler: () => Promise<void> | void) {
return ${component == "core" ? "" : "core."}onObjectCreated((params) => {
if (params.interface == "${component}/${name}") {
handler();
}
});
}
`;
}
generateEvent(component: string, event: object, name: string) {
this.externalComponent ??= component;
const label = generateComponentLabelName(component, name, true);
if (Object.keys(event).length == 0) {
this.body += `export function on${label}(handler: () => Promise<void> | void) {
@@ -850,7 +1019,7 @@ class ServerComponentApiGenerator implements ContributionGenerator {
`;
}
generateView(_component: string, _path: string, name: string) {
generateView(component: string, _path: string, name: string) {
this.viewBody += `
export function push${name}View(target: Window, params: ${name}Props) {
return target.pushView("${name}", params);
@@ -862,18 +1031,30 @@ export function set${name}View(target: Window, params: ${name}Props) {
`;
}
toString(): string {
if (this.body.length === 0 || !this.externalComponent) {
return `${generatedHeader}export { };\n`;
}
toString(component: string): string {
return `${generatedHeader}
import { thisComponent } from "$/component-info";
import * as ${generateLabelName(this.externalComponent, false)} from '$${this.externalComponent}/api';
${this.viewBody && "import { Window } from '$core/Window';"}
${this.body && `import { thisComponent } from "$/component-info";` || ""}
import * as ${generateLabelName(component, false)} from '$${component}/api';
${this.viewBody && "import { Window } from '$core/Window';" || ""}
${(this.interfaceBody && component != "core") && "import * as core from '$core'" || ""}
${this.interfaceBody && "import * as self from './api';" || ""}
import { createError } from '$core/Error';
type ComponentObject = {
typeId: string;
name: string;
impl: object;
call(caller: Component, method: string, params: JsonObject | undefined): Promise<JsonObject | void> | JsonObject | void;
notify(caller: Component, method: string, params: JsonObject | undefined): void | Promise<void>;
dispose(): void | Promise<void>;
};
${this.body}
${this.viewBody}
${this.interfaceBody}
export { };
`;
}
};
@@ -969,7 +1150,7 @@ export async function ${label}(params: ${uLabel}Request) {
this.notifyBody += ` case "${name}": return notify${uLabel}(caller, params as ${uLabel}Request);\n`;
}
generateView(component: string, _path: string, name: string) {
generateView(_component: string, _path: string, name: string) {
this.viewBody += `
export function push${name}View(target: Window, params: ${name}Props) {
return target.pushView("${name}", params);
@@ -994,64 +1175,22 @@ export function set${name}View(target: Window, params: ${name}Props) {
generateInterface(component: string, iface: object, name: string) {
const uLabel = generateLabelName(name, true);
this.interfaceBody += generateInterfaceContribution(component, iface, name);
this.interfaceBody += `
export class ${uLabel}Interface {
constructor(private id: number) {}
${"methods" in iface ? iface.methods && Object.keys(iface.methods).map(method => {
const methodTypeLabel = generateComponentLabelName(component, method, true);
if ("params" in (iface.methods as any)[method]) {
return ` async ${generateLabelName(method, false)}(request: ${methodTypeLabel}Request): Promise<${methodTypeLabel}Response> {
return (await core.objectCall({ object: this.id, method: "${method}", params: request})).result as ${methodTypeLabel}Response;
}
`
} else {
return ` async ${generateLabelName(method, false)}(): Promise<${methodTypeLabel}Response> {
return (await core.objectCall({ object: this.id, method: "${method}", params: {}})).result as ${methodTypeLabel}Response;
}
`
}
}).join("\n") : ""}
${"notifications" in iface ? iface.notifications && Object.keys(iface.notifications).map(notification => {
const methodTypeLabel = generateComponentLabelName(component, notification, true);
if ("params" in (iface.notifications as any)[notification]) {
return ` async ${generateLabelName(notification, false)}(request: ${methodTypeLabel}Request) {
return core.objectNotify({ object: this.id, notification: "${notification}", params: request});
}
`
} else {
return ` async ${generateLabelName(notification, false)}() {
return core.objectNotify({ object: this.id, notification: "${notification}", params: {}});
}
`
}
}).join("\n") : ""}
destroy() {
return core.objectDestroy({ object: this.id });
}
async getName() {
return (await core.objectGetName({ object: this.id })).name;
}
getId() {
return this.id;
}
};
export async function get${uLabel}Objects() {
return (await core.objectGetList({ interface: "${component}/${name}" })).objects.map(id => new ${uLabel}Interface(id));
export async function create${uLabel}Object<Impl extends ${uLabel}Interface, Params extends any[]>(name: string, Constructor: new (id: number, ...args: Params) => Impl, ...args: Params) {
const id = (await ${component == "core" ? "" : "core."}objectCreate({ interface: "${component}/${name}", name })).object;
const impl = new Constructor(id, ...args);
objects[id] = new ${uLabel}ComponentObject(impl, name);
return impl;
}
export async function find${uLabel}Object(name: string) {
return new ${uLabel}Interface((await core.objectFind({ interfaceName: "${component}/${name}", objectName: name })).object);
}
export async function create${uLabel}Object(name: string) {
return new ${uLabel}Interface((await core.objectCreate({ interface: "${component}/${name}", name })).object);
}
export function on${uLabel}Created(handler: (object: ${uLabel}Interface) => Promise<void> | void) {
`;
if (component != 'core') {
this.interfaceBody += `
export function on${uLabel}Created(handler: (object: ${uLabel}) => Promise<void> | void) {
return core.onObjectCreated((params) => {
if (params.interface == "${component}/${name}") {
handler(new ${uLabel}Interface(params.object));
handler(new ${uLabel}(params.object));
}
});
}
@@ -1063,34 +1202,14 @@ export function onAny${uLabel}Created(handler: () => Promise<void> | void) {
});
}
`;
}
}
toString(): string {
if (this.body.length === 0) {
return `${generatedHeader}
import { createError } from "$core/Error";
import { Component } from "$core/Component";
import * as core from "$core";
${this.viewBody && "import { Window } from '$core/Window';"}
export async function call(_caller: Component, _method: string, _params: JsonObject | undefined): Promise<JsonObject | void> {
throw createError(ErrorCode.MethodNotFound);
}
export async function notify(_caller: Component, _method: string, _params: JsonObject | undefined) {
throw createError(ErrorCode.MethodNotFound);
}
export const settings = {${this.settingBody}};
${this.viewBody}
`;
}
return `${generatedHeader}
${this.callBody.length > 0 || this.notifyBody.length > 0 ? 'import * as impl from "$/main";' : ""}
import { createError } from "$core/Error";
import { Component } from "$core/Component";
import { thisComponent } from "$/component-info";
import * as core from "$core";
${this.viewBody && "import { Window } from '$core/Window';"}
@@ -1098,13 +1217,53 @@ export { thisComponent } from "$/component-info";
${this.body}
type ComponentObject = {
typeId: string;
name: string;
impl: object;
call(caller: Component, method: string, params: JsonObject | undefined): Promise<JsonObject | void> | JsonObject | void;
notify(caller: Component, method: string, params: JsonObject | undefined): void | Promise<void>;
dispose(): void | Promise<void>;
};
const objects: Record<number, ComponentObject> = {};
export async function destroyObject(object: number) {
await core.objectDestroy({ object });
const instance = objects[object];
if (instance) {
instance.dispose();
delete objects[object];
}
}
export function registerObject(id: number, object: ComponentObject) {
objects[id] = object;
}
export function ownObjects() {
return Object.values(objects);
}
export async function call(caller: Component, method: string, params: JsonObject | undefined): Promise<JsonObject | void> {
void caller, params;
switch (method) {
${this.callBody}
default:
throw createError(ErrorCode.MethodNotFound);
case "$/object/call":
if (params && typeof params.method == "string" && typeof params.object == 'number') {
if (params.object in objects) {
return objects[params.object].call(caller, params.method,
"params" in params ? params.params as any : undefined
);
}
throw createError(ErrorCode.InvalidParams);
}
throw createError(ErrorCode.InvalidRequest);
default:
throw createError(ErrorCode.MethodNotFound);
}
}
@@ -1113,8 +1272,21 @@ export async function notify(caller: Component, method: string, params: JsonObje
switch (method) {
${this.notifyBody}
default:
throw createError(ErrorCode.MethodNotFound);
case "$/object/notify":
if (params && typeof params.notification == "string" && typeof params.object == 'number') {
if (params.object in objects) {
return objects[params.object].notify(caller, params.notification,
"params" in params ? params.params as any : undefined
);
}
throw createError(ErrorCode.InvalidParams);
}
throw createError(ErrorCode.InvalidRequest);
default:
throw createError(ErrorCode.MethodNotFound);
}
}
@@ -1331,7 +1503,7 @@ export async function generateContributions<Params extends any[], RT extends Con
object.virtual = true;
await gen(component.manifest.name, object, method);
await gen(component.manifest.name, object, `${name}-${method}`);
}));
}
@@ -1354,7 +1526,7 @@ export async function generateContributions<Params extends any[], RT extends Con
object.virtual = true;
await gen(component.manifest.name, object, notification);
await gen(component.manifest.name, object, `${name}-${notification}`);
}));
}
@@ -1496,7 +1668,7 @@ export class TsServerGenerator implements ProjectGenerator {
if (generatedFile) {
try {
generatedFile.content = (await generateContributions(sourceComponent, Generator, ...params)).toString();
generatedFile.content = (await generateContributions(sourceComponent, Generator, ...params)).toString(sourceComponent.manifest.name);
} catch (e) {
throw Error(`${sourceComponent.manifest.name}: ${e}`);
}
@@ -1511,7 +1683,7 @@ export class TsServerGenerator implements ProjectGenerator {
if (generatedFile) {
try {
generatedFile.content = (await generateContributions(project.component, Generator, ...params)).toString();
generatedFile.content = (await generateContributions(project.component, Generator, ...params)).toString(project.component.manifest.name);
} catch (e) {
throw Error(`${project.component.manifest.name}: ${e}`);
}
@@ -1603,7 +1775,6 @@ export function thisComponent() {
componentFile.content = `${generatedHeader}
import * as api from '$';
import { IComponentImpl, registerComponent } from '$core/ComponentInstance';
import { ComponentContext, Component } from '$core/Component';
import { manifest } from '$/component-info';
export { thisComponent } from '$/component-info';

View File

@@ -7,9 +7,6 @@
},
{
"name": "explorer"
},
{
"name": "github"
}
]
}

25
rpcsx-ui/src/core/lib/Component.d.ts vendored Normal file
View File

@@ -0,0 +1,25 @@
import { IDisposable } from "./Disposable";
type Key<K, T> = T extends [never] ? string | symbol : K | keyof T;
type Listener<K, T, F> = T extends [never] ? F : (
K extends keyof T ? (
T[K] extends unknown[] ? (...args: T[K]) => void : never
)
: never
);
declare global {
export type ComponentContext = {
manage(...objects: (IDisposable | (() => void))[]): void;
subscribe<K, T extends Record<keyof T, any[]> | [never] = [never]>(emitter: NodeJS.EventEmitter<T>, channel: Key<K, T>, listener: Listener<K, T, (...args: any[]) => void>): void;
};
export type ComponentId = string;
export type Component = {
getId(): ComponentId;
onClose(listener: () => void | Promise<void>): IDisposable;
sendEvent(event: string, params?: any): void;
};
}

View File

@@ -1,24 +0,0 @@
import { IDisposable } from "./Disposable";
type Key<K, T> = T extends [never] ? string | symbol : K | keyof T;
type Listener<K, T, F> = T extends [never] ? F : (
K extends keyof T ? (
T[K] extends unknown[] ? (...args: T[K]) => void : never
)
: never
);
export type ComponentContext = {
manage(...objects: (IDisposable | (() => void))[]): void;
subscribe<K, T extends Record<keyof T, any[]> | [never] = [never]>(emitter: NodeJS.EventEmitter<T>, channel: Key<K, T>, listener: Listener<K, T, (...args: any[]) => void>): void;
};
export type ComponentId = string;
export type Component = {
getId(): ComponentId;
onClose(listener: () => void | Promise<void>): IDisposable;
sendEvent(event: string, params?: any): void;
};

View File

@@ -0,0 +1,11 @@
export function join(...a: string[]) {
return a.join("/");
}
export function toURI(path: string) {
if (path.startsWith("/")) {
return "file://" + path;
} else {
return "file:///" + path;
}
}

View File

@@ -1,6 +1,5 @@
import { ComponentInstance } from "./ComponentInstance";
import { Disposable } from "$/Disposable";
import { Component } from "lib/Component";
import { ipcMain } from "electron";
import { createError } from "lib/Error";

View File

@@ -1,5 +1,4 @@
import * as Event from "$/Event";
import { ComponentContext, ComponentId, Component } from "$/Component";
import { Disposable, IDisposable } from "$/Disposable";
import { isJsonObject } from '$/Json';
import { createError } from "$/Error";
@@ -23,6 +22,7 @@ export type IComponentImpl = IDisposable & {
deactivate(context: ComponentContext): void | Promise<void>;
call?(caller: Component, method: string, params: JsonObject | undefined): Promise<JsonObject | void>;
notify?(caller: Component, notification: string, params: JsonObject | undefined): Promise<void>;
getPid?(): number;
}
const activateEvent = "activate";
@@ -154,6 +154,10 @@ export class ComponentInstance implements ComponentContext {
}
}
getPid() {
return this.impl.getPid ? this.impl.getPid() : 0;
}
subscribe<K, T extends Record<keyof T, any[]> | [never] = [never]>(emitter: NodeJS.EventEmitter<T>, channel: Key<K, T>, listener: Listener<K, T, (...args: any[]) => void>) {
emitter.on(channel, listener);
const disposable = Disposable.Create(() => { emitter.off(channel, listener); });

View File

@@ -1,7 +1,6 @@
import { Process } from './Launcher';
import packageJson from '../../../../package.json' with { type: "json" };
import { Component, ComponentContext } from '$core/Component.js';
import { findComponent, getComponentId, registerComponent, uninitializeComponent, IComponentImpl } from './ComponentInstance';
import { createError } from 'lib/Error';
@@ -72,6 +71,10 @@ export class Extension implements IComponentImpl {
registerComponent(this.componentManifest, this);
}
getPid() {
return this.extensionProcess.getPid();
}
private debugLog(message: string) {
const date = new Date().toLocaleString("en-US");
for (const line of message.split('\n')) {

View File

@@ -11,6 +11,7 @@ export type Process = {
on: (event: 'close' | 'exit', listener: (...args: any[]) => void) => void;
once: (event: 'close' | 'exit', listener: (...args: any[]) => void) => void;
off: (event: 'close' | 'exit', listener: (...args: any[]) => void) => void;
getPid(): number;
}
export type LaunchParams = {

View File

@@ -1,4 +1,3 @@
import { Component, ComponentId } from "lib/Component";
import { createError } from "lib/Error";
import { ComponentInstance, findComponentById } from "./ComponentInstance";
import * as self from '$';

View File

@@ -1,5 +1,4 @@
import * as self from '$';
import { Component } from '$core/Component';
import { createError } from '$core/Error';
import { ComponentInstance, findComponentById, getActivatedComponentList, getComponentList, initializeComponent } from './ComponentInstance';
import * as instance from './ComponentInstance';

View File

@@ -4,18 +4,20 @@ import { Target } from "./Target";
import { fork, spawn } from "child_process";
import { Duplex } from "stream";
import { EventEmitter } from "events";
import * as locations from '$/locations';
const nativeLauncher: Launcher = {
launch: async (path: string, args: string[], params: LaunchParams) => {
const newProcess = spawn(path, args, {
argv0: path,
cwd: dirname(path),
cwd: locations.rootPath,
signal: params.signal,
stdio: 'pipe'
});
newProcess.stdout.setEncoding('utf8');
newProcess.stderr.setEncoding('utf8');
const pid = newProcess.pid ?? 0;
const result: Process = {
stdin: newProcess.stdin,
@@ -33,7 +35,8 @@ const nativeLauncher: Launcher = {
},
off: (event: string, handler: (...args: any[]) => void) => {
newProcess.off(event, handler);
}
},
getPid: () => pid
};
return result;
@@ -50,6 +53,7 @@ const nodeLauncher: Launcher = {
newProcess.stdout!.setEncoding('utf8');
newProcess.stderr!.setEncoding('utf8');
const pid = newProcess.pid ?? 0;
const result: Process = {
stdin: newProcess.stdin!,
@@ -67,7 +71,8 @@ const nodeLauncher: Launcher = {
},
off: (event: string, handler: (...args: any[]) => void) => {
newProcess.off(event, handler);
}
},
getPid: () => pid
};
return result;
@@ -106,7 +111,10 @@ const inlineLauncher: Launcher = {
},
off: (event: string, handler: (...args: any[]) => void) => {
eventEmitter.off(event, handler);
}
},
getPid() {
return 0;
},
};
return result;

View File

@@ -1,4 +1,3 @@
import { Component, ComponentContext } from "$core/Component";
import * as self from "$";
import * as progress from "$progress";
import { IDisposable } from "$core/Disposable";

View File

@@ -1,4 +1,3 @@
import { Component, ComponentContext } from "$core/Component";
import { createError } from "$core/Error";
import { ExplorerComponent } from "./Component";

View File

@@ -6,7 +6,10 @@
"name": "settings"
},
{
"name": "app"
"name": "explorer"
},
{
"name": "fs"
}
]
}

View File

@@ -1,2 +1,44 @@
export function activateLocalExtensions(_list: Set<string>) {
import * as core from "$core";
import * as locations from "$core/locations";
import * as fs from '$fs';
import * as path from '$core/path';
export async function activateLocalExtensions(list: Set<string>) {
try {
const rootPath = locations.localExtensionsPath;
for (const entry of (await fs.fsReadDir({ uri: path.toURI(rootPath) })).items) {
if (entry.type != FsDirEntryType.Directory) {
continue;
}
try {
await fs.fsStat({ uri: path.toURI(path.join(rootPath, entry.name, "extension.json")) });
} catch {
continue;
}
try {
await core.extensionLoad({ id: entry.name });
} catch (e) {
console.error(`failed to load local extension ${entry.name}`, e);
continue;
}
try {
await core.componentActivate({ id: entry.name });
} catch (e) {
console.error(`failed to activate extension ${entry.name}`, e);
try {
await core.extensionUnload({ id: entry.name });
} catch (e) {
console.error(`failed to unload extension ${entry.name}`, e);
}
continue;
}
list.add(entry.name);
}
} catch (e) {
console.error(`failed to load local extensions`, e);
}
}

View File

@@ -1,43 +0,0 @@
import * as core from "$core";
import * as locations from "$core/locations";
import fs from 'fs/promises';
import path from 'path';
export async function activateLocalExtensions(list: Set<string>) {
try {
for (const entry of await fs.readdir(locations.localExtensionsPath, { withFileTypes: true, encoding: 'utf-8' })) {
if (!entry.isDirectory()) {
continue;
}
try {
await fs.stat(path.join(path.join(entry.parentPath, entry.name, "extension.json")));
} catch {
continue;
}
try {
await core.extensionLoad({ id: entry.name });
} catch (e) {
console.error(`failed to load local extension ${entry.name}`, e);
continue;
}
try {
await core.componentActivate({ id: entry.name });
} catch (e) {
console.error(`failed to activate extension ${entry.name}`, e);
try {
await core.extensionUnload({ id: entry.name });
} catch (e) {
console.error(`failed to unload extension ${entry.name}`, e);
}
continue;
}
list.add(entry.name);
}
} catch (e) {
console.error(`failed to load local extensions`, e);
}
}

View File

@@ -0,0 +1,198 @@
{
"name": "fs",
"version": "0.1.0",
"contributions": {
"types": {
"dir-entry-type": {
"type": "enum",
"enumerators": {
"file": 0,
"directory": 1,
"block-device": 2,
"character-device": 2,
"symbolic-link": 3,
"fifo": 4,
"socket": 5,
"other": 6
}
},
"dir-entry": {
"type": "object",
"params": {
"type": {
"type": "dir-entry-type"
},
"name": {
"type": "string"
}
}
},
"file-stat": {
"type": "object",
"params": {
"type": {
"type": "dir-entry-type"
},
"size": {
"type": "number"
}
}
}
},
"interfaces": {
"file": {
"methods": {
"read": {
"params": {
"offset": {
"type": "number"
},
"size": {
"type": "number"
}
},
"returns": {
"data": {
"type": "array",
"item-type": "number"
}
}
},
"write": {
"params": {
"offset": {
"type": "number"
},
"data": {
"type": "array",
"item-type": "number"
}
},
"returns": {
"written": {
"type": "number"
}
}
},
"close": {}
}
},
"file-system": {
"methods": {
"open": {
"params": {
"uri": {
"type": "string"
}
},
"returns": {
"file": {
"type": "number"
}
}
},
"read-to-string": {
"params": {
"uri": {
"type": "string"
}
},
"returns": {
"data": {
"type": "string"
}
}
},
"read-dir": {
"params": {
"uri": {
"type": "string"
}
},
"returns": {
"items": {
"type": "array",
"item-type": "dir-entry"
}
}
},
"stat": {
"params": {
"uri": {
"type": "string"
}
},
"returns": {
"item": {
"type": "file-stat"
}
}
}
}
}
},
"methods": {
"open": {
"handler": "handleOpen",
"params": {
"uri": {
"type": "string"
},
"flags": {
"type": "string",
"optional": true
},
"mode": {
"type": "number",
"optional": true
}
},
"returns": {
"file": {
"type": "number"
}
}
},
"read-to-string": {
"handler": "handleReadToString",
"params": {
"uri": {
"type": "string"
}
},
"returns": {
"data": {
"type": "string"
}
}
},
"read-dir": {
"handler": "handleReadDir",
"params": {
"uri": {
"type": "string"
}
},
"returns": {
"items": {
"type": "array",
"item-type": "dir-entry"
}
}
},
"stat": {
"handler": "handleStat",
"params": {
"uri": {
"type": "string"
}
},
"returns": {
"item": {
"type": "file-stat"
}
}
}
}
}
}

View File

@@ -0,0 +1,25 @@
import { createError } from "$core/Error";
export async function initialize() {
throw createError(ErrorCode.InternalError, "not implemented");
}
export async function uninitialize() {
throw createError(ErrorCode.InternalError, "not implemented");
}
export async function open(_caller: Component, _request: FsOpenRequest): Promise<FsOpenResponse> {
throw createError(ErrorCode.InternalError, "not implemented");
}
export async function readToString(_caller: Component, _request: FsReadToStringRequest): Promise<FsReadToStringResponse> {
throw createError(ErrorCode.InternalError, "not implemented");
}
export async function readDir(_caller: Component, _request: FsReadDirRequest): Promise<FsReadDirResponse> {
throw createError(ErrorCode.InternalError, "not implemented");
}
export async function stat(_caller: Component, _request: FsStatRequest): Promise<FsStatResponse> {
throw createError(ErrorCode.InternalError, "not implemented");
}

View File

@@ -0,0 +1,168 @@
import * as self from '$';
import * as core from '$core';
import { createError } from "$core/Error";
import fs from 'fs/promises';
class NativeFile implements FileInterface {
constructor(private id: number, private handle: fs.FileHandle) { }
async close(_caller: Component) {
core.objectDestroy({
object: this.id
});
await this.handle.close();
}
async read(_caller: Component, request: FsFileReadRequest): Promise<FsFileReadResponse> {
const buffer = new Uint8Array(request.size);
const result = await this.handle.read(buffer, 0, request.size, request.offset);
return {
data: Array.from(result.buffer)
};
}
async write(_caller: Component, request: FsFileWriteRequest): Promise<FsFileWriteResponse> {
const result = await this.handle.write(Uint8Array.from(request.data), {
position: request.offset
});
return {
written: result.bytesWritten
};
}
getId() {
return this.id;
}
}
type WithFileType = {
isFile(): boolean;
isDirectory(): boolean;
isBlockDevice(): boolean;
isCharacterDevice(): boolean;
isSymbolicLink(): boolean;
isFIFO(): boolean;
isSocket(): boolean;
};
function toFileType(fileType: WithFileType) {
if (fileType.isFile()) {
return FsDirEntryType.File;
}
if (fileType.isDirectory()) {
return FsDirEntryType.Directory;
}
if (fileType.isBlockDevice()) {
return FsDirEntryType.BlockDevice;
}
if (fileType.isCharacterDevice()) {
return FsDirEntryType.CharacterDevice;
}
if (fileType.isSocket()) {
return FsDirEntryType.Socket;
}
if (fileType.isSymbolicLink()) {
return FsDirEntryType.SymbolicLink;
}
if (fileType.isFIFO()) {
return FsDirEntryType.Fifo;
}
return FsDirEntryType.Other;
}
class NativeFileSystem implements FileSystemInterface {
async open(_caller: Component, request: FsFileSystemOpenRequest): Promise<FsFileSystemOpenResponse> {
const filePath = new URL(request.uri).pathname;
try {
const descriptor = await fs.open(filePath, "rb");
await self.createFileObject(request.uri, NativeFile, descriptor);
} catch (e) { }
throw createError(ErrorCode.InvalidParams);
}
async readToString(_caller: Component, request: FsFileSystemReadToStringRequest): Promise<FsFileSystemReadToStringResponse> {
const filePath = new URL(request.uri).pathname;
try {
return {
data: await fs.readFile(filePath, { encoding: "utf8" })
};
} catch (e) { }
throw createError(ErrorCode.InvalidParams);
}
async readDir(_caller: Component, request: FsFileSystemReadDirRequest): Promise<FsFileSystemReadDirResponse> {
const path = new URL(request.uri).pathname;
const result = await fs.readdir(path, { withFileTypes: true });
return {
items: result.map(item => {
const result: FsDirEntry = {
name: item.name,
type: toFileType(item)
};
return result;
})
};
}
async stat(_caller: Component, request: FsFileSystemStatRequest): Promise<FsFileSystemStatResponse> {
const path = new URL(request.uri).pathname;
const result = await fs.stat(path);
return {
item: {
size: result.size,
type: toFileType(result)
}
};
}
}
export async function initialize() {
await self.createFileSystemObject("file:", NativeFileSystem);
}
export async function uninitialize() {
await Promise.all(self.ownObjects().map(object => object.dispose()));
}
export async function open(_caller: Component, request: FsOpenRequest): Promise<FsOpenResponse> {
const protocol = new URL(request.uri).protocol;
const object = await self.findFileSystemObject(protocol);
return await object.open(request);
}
export async function readToString(_caller: Component, request: FsReadToStringRequest): Promise<FsReadToStringResponse> {
const protocol = new URL(request.uri).protocol;
const object = await self.findFileSystemObject(protocol);
return await object.readToString(request);
}
export async function readDir(_caller: Component, request: FsReadDirRequest): Promise<FsReadDirResponse> {
const protocol = new URL(request.uri).protocol;
const object = await self.findFileSystemObject(protocol);
return await object.readDir(request);
}
export async function stat(_caller: Component, request: FsStatRequest): Promise<FsStatResponse> {
const protocol = new URL(request.uri).protocol;
const object = await self.findFileSystemObject(protocol);
return await object.stat(request);
}

View File

@@ -0,0 +1,24 @@
import * as fs from './fs';
export async function activate() {
await fs.initialize();
}
export async function deactivate() {
await fs.uninitialize();
}
export async function handleOpen(caller: Component, request: FsOpenRequest) {
return fs.open(caller, request);
}
export async function handleReadToString(caller: Component, request: FsReadToStringRequest) {
return fs.readToString(caller, request);
}
export async function handleReadDir(caller: Component, request: FsReadDirRequest) {
return fs.readDir(caller, request);
}
export async function handleStat(caller: Component, request: FsStatRequest) {
return fs.stat(caller, request);
}

View File

@@ -1,4 +1,3 @@
import { Component } from '$core/Component';
import * as github from './github';
export async function handleReleasesLatest(_caller: Component, params: GithubReleasesLatestRequest): Promise<GithubReleasesLatestResponse> {

View File

@@ -1,5 +1,3 @@
// import { ipcMain } from 'electron';
import { Component } from '$core/Component';
import { createError } from '$core/Error';
import * as api from '$';