From 3a850924cae2086ea4a45196d7898ab62cbc05f7 Mon Sep 17 00:00:00 2001 From: Infuseting Date: Tue, 13 Oct 2026 20:26:21 +0200 Subject: [PATCH 1/6] feat(template): initialize Roblox TypeScript tooling --- .gitignore | 11 + .husky/pre-commit | 1 + .npmrc | 2 + .prettierignore | 1 + .prettierrc.json | 4 + .vscode/settings.json | 20 + assets/animations/.gitkeep | 0 assets/models/.gitkeep | 0 assets/sfx/.gitkeep | 0 assets/textures/.gitkeep | 0 assets/ui/.gitkeep | 0 assets/vfx/.gitkeep | 0 default.project.json | 31 + eslint.config.mjs | 14 + flamework.build | 8 + mise.toml | 4 + package.json | 72 + pnpm-lock.yaml | 2650 ++++++++++++++++++++++++ pnpm-workspace.yaml | 12 + renovate.json | 20 + rokit.toml | 10 + src/client/main.client.ts | 5 + src/server/main.server.ts | 6 + src/server/services/startup-service.ts | 8 + src/shared/template-name.ts | 1 + tsconfig.json | 27 + 26 files changed, 2907 insertions(+) create mode 100644 .gitignore create mode 100644 .husky/pre-commit create mode 100644 .npmrc create mode 100644 .prettierignore create mode 100644 .prettierrc.json create mode 100644 .vscode/settings.json create mode 100644 assets/animations/.gitkeep create mode 100644 assets/models/.gitkeep create mode 100644 assets/sfx/.gitkeep create mode 100644 assets/textures/.gitkeep create mode 100644 assets/ui/.gitkeep create mode 100644 assets/vfx/.gitkeep create mode 100644 default.project.json create mode 100644 eslint.config.mjs create mode 100644 flamework.build create mode 100644 mise.toml create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 renovate.json create mode 100644 rokit.toml create mode 100644 src/client/main.client.ts create mode 100644 src/server/main.server.ts create mode 100644 src/server/services/startup-service.ts create mode 100644 src/shared/template-name.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..356e64b --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +node_modules/ +out/ +include/ +build.rbxlx +game.rbxl +.rbxts-build/ +*.rbxl.lock +*.rbxlx.lock +.env +.env.* +!.env.example diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..009b3f8 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +pnpm lint diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..8e5a554 --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +node-linker=hoisted +shamefully-hoist=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..7091420 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +assets/**/*.gltf diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 0000000..c4b4336 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,4 @@ +{ + "printWidth": 100, + "trailingComma": "all" +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..11ef447 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,20 @@ +{ + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/.jj": true, + "**/.DS_Store": true, + "**/Thumbs.db": true, + "**/node_modules": true, + "**/out": true, + "**/include": true, + "**/build.rbxlx": true, + "**/game.rbxl": true, + "**/.rbxts-build": true, + "**/*.rbxl.lock": true, + "**/*.rbxlx.lock": true, + "**/.env": true, + "**/.env.*": true + } +} diff --git a/assets/animations/.gitkeep b/assets/animations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/assets/models/.gitkeep b/assets/models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/assets/sfx/.gitkeep b/assets/sfx/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/assets/textures/.gitkeep b/assets/textures/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/assets/ui/.gitkeep b/assets/ui/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/assets/vfx/.gitkeep b/assets/vfx/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/default.project.json b/default.project.json new file mode 100644 index 0000000..6565585 --- /dev/null +++ b/default.project.json @@ -0,0 +1,31 @@ +{ + "name": "CrownStudioTemplate", + "globIgnorePaths": ["**/package.json", "**/tsconfig.json"], + "tree": { + "$className": "DataModel", + "ServerScriptService": { + "$className": "ServerScriptService", + "TS": { "$path": "out/server" } + }, + "ReplicatedStorage": { + "$className": "ReplicatedStorage", + "rbxts_include": { + "$path": "include", + "node_modules": { + "$className": "Folder", + "@rbxts": { "$path": "node_modules/@rbxts" }, + "@rbxts-js": { "$path": "node_modules/@rbxts-js" }, + "@flamework": { "$path": "node_modules/@flamework" } + } + }, + "TS": { "$path": "out/shared" } + }, + "StarterPlayer": { + "$className": "StarterPlayer", + "StarterPlayerScripts": { + "$className": "StarterPlayerScripts", + "TS": { "$path": "out/client" } + } + } + } +} diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..af2d8a0 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,14 @@ +import js from "@eslint/js"; +import prettier from "eslint-config-prettier"; +import tseslint from "typescript-eslint"; + +export default tseslint.config( + { ignores: ["node_modules/**", "out/**", "include/**", "build.rbxlx"] }, + js.configs.recommended, + ...tseslint.configs.recommended, + { + files: ["src/**/*.ts", "src/**/*.tsx"], + rules: { "no-undef": "off" }, + }, + prettier, +); diff --git a/flamework.build b/flamework.build new file mode 100644 index 0000000..3a63735 --- /dev/null +++ b/flamework.build @@ -0,0 +1,8 @@ +{ + "version": 1, + "flameworkVersion": "1.3.2", + "identifiers": { + "crownstudio-roblox-ts-template:out/server/services/startup-service@StartupService": "server/services/startup-service@StartupService" + }, + "metadata": {} +} \ No newline at end of file diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..fcdc061 --- /dev/null +++ b/mise.toml @@ -0,0 +1,4 @@ +[tools] +node = "24.16.0" +pnpm = "11.21.0" +uv = "0.13.0" diff --git a/package.json b/package.json new file mode 100644 index 0000000..53098e3 --- /dev/null +++ b/package.json @@ -0,0 +1,72 @@ +{ + "name": "crownstudio-roblox-ts-template", + "version": "0.1.0", + "private": true, + "packageManager": "pnpm@11.21.0", + "engines": { + "node": ">=24" + }, + "scripts": { + "compile": "rbxtsc", + "watch": "rbxts-build watch", + "build": "pnpm compile && pnpm build:rojo", + "build:rojo": "rbxts-build build", + "start": "rbxts-build start", + "open": "rbxts-build open", + "stop": "rbxts-build stop", + "serve": "rojo serve default.project.json", + "lint": "eslint .", + "format:check": "prettier --check .", + "format": "prettier --write .", + "check": "pnpm lint && pnpm compile", + "prepare": "husky" + }, + "rbxts-build": { + "names": { + "build": "build:rojo" + }, + "rojoBuildArgs": [ + "default.project.json", + "--output", + "game.rbxl" + ] + }, + "dependencies": { + "@flamework/components": "1.3.2", + "@flamework/core": "1.3.2", + "@flamework/networking": "1.3.2", + "@rbxts-js/roblox-lua-promise": "3.5.0-ts.2", + "@rbxts/cmdr": "1.12.1", + "@rbxts/gamejoy": "1.1.4", + "@rbxts/lapis": "0.3.8", + "@rbxts/log": "0.6.3", + "@rbxts/net": "3.0.10", + "@rbxts/object-cache": "1.4.5", + "@rbxts/react": "17.3.13-ts.1", + "@rbxts/react-roblox": "17.3.13-ts.1", + "@rbxts/reflex": "4.3.1", + "@rbxts/ripple": "0.10.2", + "@rbxts/services": "1.6.0", + "@rbxts/sift": "0.0.11", + "@rbxts/sound-manager": "2.3.4", + "@rbxts/t": "3.2.1", + "@rbxts/timer": "0.0.9", + "@rbxts/trove": "1.3.0", + "@rbxts/tween": "1.0.5", + "@rbxts/ui-labs": "2.4.2" + }, + "devDependencies": { + "@eslint/js": "10.0.1", + "@rbxts/compiler-types": "3.0.0-types.0", + "@rbxts/types": "1.0.957", + "eslint": "10.12.0", + "eslint-config-prettier": "10.1.8", + "husky": "9.1.7", + "prettier": "3.9.10", + "rbxts-build": "2.6.0", + "rbxts-transformer-flamework": "1.3.2", + "roblox-ts": "3.0.0", + "typescript": "5.5.3", + "typescript-eslint": "8.71.2" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..6f9b96c --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2650 @@ +lockfileVersion: "9.0" + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: + dependencies: + "@flamework/components": + specifier: 1.3.2 + version: 1.3.2(@flamework/core@1.3.2) + "@flamework/core": + specifier: 1.3.2 + version: 1.3.2 + "@flamework/networking": + specifier: 1.3.2 + version: 1.3.2(@flamework/core@1.3.2) + "@rbxts-js/roblox-lua-promise": + specifier: 3.5.0-ts.2 + version: 3.5.0-ts.2 + "@rbxts/cmdr": + specifier: 1.12.1 + version: 1.12.1 + "@rbxts/gamejoy": + specifier: 1.1.4 + version: 1.1.4 + "@rbxts/lapis": + specifier: 0.3.8 + version: 0.3.8(@rbxts/t@3.2.1) + "@rbxts/log": + specifier: 0.6.3 + version: 0.6.3 + "@rbxts/net": + specifier: 3.0.10 + version: 3.0.10 + "@rbxts/object-cache": + specifier: 1.4.5 + version: 1.4.5 + "@rbxts/react": + specifier: 17.3.13-ts.1 + version: 17.3.13-ts.1 + "@rbxts/react-roblox": + specifier: 17.3.13-ts.1 + version: 17.3.13-ts.1(@rbxts/react@17.3.13-ts.1) + "@rbxts/reflex": + specifier: 4.3.1 + version: 4.3.1 + "@rbxts/ripple": + specifier: 0.10.2 + version: 0.10.2 + "@rbxts/services": + specifier: 1.6.0 + version: 1.6.0 + "@rbxts/sift": + specifier: 0.0.11 + version: 0.0.11 + "@rbxts/sound-manager": + specifier: 2.3.4 + version: 2.3.4 + "@rbxts/t": + specifier: 3.2.1 + version: 3.2.1 + "@rbxts/timer": + specifier: 0.0.9 + version: 0.0.9 + "@rbxts/trove": + specifier: 1.3.0 + version: 1.3.0 + "@rbxts/tween": + specifier: 1.0.5 + version: 1.0.5 + "@rbxts/ui-labs": + specifier: 2.4.2 + version: 2.4.2 + devDependencies: + "@eslint/js": + specifier: 10.0.1 + version: 10.0.1(eslint@10.12.0(supports-color@7.2.0)) + "@rbxts/compiler-types": + specifier: 3.0.0-types.0 + version: 3.0.0-types.0 + "@rbxts/types": + specifier: 1.0.957 + version: 1.0.957 + eslint: + specifier: 10.12.0 + version: 10.12.0(supports-color@7.2.0) + eslint-config-prettier: + specifier: 10.1.8 + version: 10.1.8(eslint@10.12.0(supports-color@7.2.0)) + husky: + specifier: 9.1.7 + version: 9.1.7 + prettier: + specifier: 3.9.10 + version: 3.9.10 + rbxts-build: + specifier: 2.6.0 + version: 2.6.0 + rbxts-transformer-flamework: + specifier: 1.3.2 + version: 1.3.2(typescript@5.5.3) + roblox-ts: + specifier: 3.0.0 + version: 3.0.0 + typescript: + specifier: 5.5.3 + version: 5.5.3 + typescript-eslint: + specifier: 8.71.2 + version: 8.71.2(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3) + +packages: + "@cacheable/memory@2.2.0": + resolution: + { + integrity: sha512-CTLKqLItRCEixEAewD3/j9DB3/o96gpTPD4eJ1v+DGOlxZRZncRQkGYqqnAGCscYd6RNeXfGeiuCphsPtqyIfQ==, + } + + "@cacheable/utils@2.5.0": + resolution: + { + integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==, + } + + "@eslint-community/eslint-utils@4.10.1": + resolution: + { + integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + "@eslint-community/regexpp@4.12.2": + resolution: + { + integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==, + } + engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } + + "@eslint/config-array@0.23.5": + resolution: + { + integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + "@eslint/config-helpers@0.7.0": + resolution: + { + integrity: sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + "@eslint/core@1.2.1": + resolution: + { + integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + "@eslint/js@10.0.1": + resolution: + { + integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + peerDependencies: + eslint: ^10.0.0 + peerDependenciesMeta: + eslint: + optional: true + + "@eslint/object-schema@3.0.5": + resolution: + { + integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + "@eslint/plugin-kit@0.7.3": + resolution: + { + integrity: sha512-IkO+/KEUvwbVpiURZg+P7zF74z5Jxe0UgJxVni+RtoHQ6IZieXaO02kmadomap/q+l6bc/jdPGGqTjhuZnuz1Q==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + "@flamework/components@1.3.2": + resolution: + { + integrity: sha512-u9a8vwrCiXpG2LXihogsEb5DzDOSTJwxBON6XdQyQmobm0lPOQzRjtwvWRB83wMLtJPjzIs6T2L1Ce00yXw+WA==, + } + peerDependencies: + "@flamework/core": "*" + + "@flamework/core@1.3.2": + resolution: + { + integrity: sha512-4/tY5plivbvr4vvwRtxzuym7Nl5eDHM3RAR2nZHuuNz31h2S2CIrCoGHjkod0I6qPpfs4eUw2TiIrvVN8iC8JQ==, + } + + "@flamework/networking@1.3.2": + resolution: + { + integrity: sha512-ImIOe3PDiUjYZBElZOWCDtPmDXwxG0z8syerKRYWHkTWHc9cchRdh6213xoguRDRhmZZ0MVJsIVZ/pCiNq5IYg==, + } + peerDependencies: + "@flamework/core": "*" + + "@humanfs/core@0.19.2": + resolution: + { + integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==, + } + engines: { node: ">=18.18.0" } + + "@humanfs/node@0.16.8": + resolution: + { + integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==, + } + engines: { node: ">=18.18.0" } + + "@humanfs/types@0.15.0": + resolution: + { + integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==, + } + engines: { node: ">=18.18.0" } + + "@humanwhocodes/module-importer@1.0.1": + resolution: + { + integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, + } + engines: { node: ">=12.22" } + + "@humanwhocodes/retry@0.4.3": + resolution: + { + integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==, + } + engines: { node: ">=18.18" } + + "@keyv/bigmap@1.3.1": + resolution: + { + integrity: sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==, + } + engines: { node: ">= 18" } + peerDependencies: + keyv: ^5.6.0 + + "@keyv/serialize@1.1.1": + resolution: + { + integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==, + } + + "@rbxts-js/boolean@1.2.3-ts.1": + resolution: + { + integrity: sha512-IlYFG9uatXQKXOLR1mdptVeEcxudwBCierO+Vu6VZRRaI+/+JDxUClm+QFZyce/kitwnDTmtNvhqVAUtFvCU0Q==, + } + + "@rbxts-js/collections@1.2.3-ts.1": + resolution: + { + integrity: sha512-RYT73QyEdsV+DCQMtyzT2bZ1P12j2b+JSHVdb1x6xrDq/KdVEkk/QAzW+o22vPrLXbHa6/jA5bmVlgVcOOrIjA==, + } + + "@rbxts-js/console@1.2.3-ts.1": + resolution: + { + integrity: sha512-qacyJE7bYf4n9448F7mn9e8Y5qljZb7QkSmsmnbQT2HA9Mecvg3gqc9RwCIdVkBV6o1AGVAHbatNQ3a39zPjaw==, + } + + "@rbxts-js/es7-types@1.2.3-ts.1": + resolution: + { + integrity: sha512-Z7NitBQp6tyzsr5eKlSX54rmopKXpzGu33lYikjF37llrnGFBpnRcZgCJwHKYciloRmjUtnR/UohUNwbWyG9Zg==, + } + + "@rbxts-js/instance-of@1.2.3-ts.1": + resolution: + { + integrity: sha512-3fKvDPvxHuEZALNBP2XdbXfzjEfbgsXAheCm1Jf5kC2r+HgszW+yqg4zY6TjW0pcsJM2JjH+fM52oomSNu3LLQ==, + } + + "@rbxts-js/luau-polyfill@1.2.3-ts.1": + resolution: + { + integrity: sha512-2asgvWrOhf311rNgEM/fxMO3+xOVI63Cz1K1PobyZVtDdd87ZJ6RGiZSFl1EAeU77SZc8280toJDVs9YSwxWnA==, + } + + "@rbxts-js/math@1.2.3-ts.1": + resolution: + { + integrity: sha512-mF7IMJJyxYE02cRNWJ1wsq4pPaAEXKX+CbGlmI0G3QBgWVHj+Oi02IR6pssyUyE6Y2BKvgvP3dcJuTO51OX25A==, + } + + "@rbxts-js/number@1.2.3-ts.1": + resolution: + { + integrity: sha512-tPdt2s7fEBSxImVFBUvuSMtBKIvaZUCtlOjBl58Y0+GKB15VPXFIRW/5biVruXPnv9d4sDE3mh986ZxxN4UQ5w==, + } + + "@rbxts-js/react-globals@17.3.13-ts.2": + resolution: + { + integrity: sha512-mw9GlbLUJm0Z1aXrkm3ptAUK6nhuP0SxtnpyZUsYw/EceKyNOFLxuFHo7rHwH4xy4RmcwgpAuMwn6EDq0WFwtA==, + } + + "@rbxts-js/react-reconciler@17.3.13-ts.2": + resolution: + { + integrity: sha512-QTI0g/89WD7gyoyRb+YZj94B6ftfkCuLdlcx+8nXQjv+eVa+q61xgKqVYGdzsKbo/44v4HrZGERN2JyXksfb+w==, + } + + "@rbxts-js/react-roblox@17.3.13-ts.2": + resolution: + { + integrity: sha512-nvLh2zW0PV7k2UMMOm6YRpNdwiDaGBXOupEtt9Lu1YxZWh7EQvNZEEfWHRr8mLi9E6+ujgcsYD8jyr1CUUnHkQ==, + } + + "@rbxts-js/react@17.3.13-ts.2": + resolution: + { + integrity: sha512-GPHfux4iNhaE5LbNL/3IvF5ajE3bosNKA8iOmsB5WPpOY/P+sdTI/XHwZhDa00wS415GnqZ5rn4YCugDrWCEIg==, + } + + "@rbxts-js/roblox-lua-promise@3.5.0-ts.2": + resolution: + { + integrity: sha512-H8fxKVJkRnM60yTZ/riO4X43GZD/MViFm+ftCzzAALkAhE4vSaBDNv9E7IUjZZoWi21bG6vZ8mGIiYfavaljvw==, + } + + "@rbxts-js/safe-flags@17.3.13-ts.2": + resolution: + { + integrity: sha512-hWLSTvN75U8JeviFg32LMSjZjVunJ7py0aDGNcybXIcKtbqC7k0vh0MEITSsdQsNUlcbpWiCKCkcLMSbfg+nXg==, + } + + "@rbxts-js/scheduler@17.3.13-ts.2": + resolution: + { + integrity: sha512-5leODce1rBNwHvnXtZ9a+wUZUo7zKBxIHFd7aEc9VmD/PMxFoRVGbhnrP46ijEDz60h94ALmXpWR5bf3pB/vKg==, + } + + "@rbxts-js/shared@17.3.13-ts.2": + resolution: + { + integrity: sha512-9q4xJkA9BMa7oAWyO6YzwlMIelfcn7eB+oSvVkBacrsJDaOjTF24VqYjhIs2cKoKoVDNm0Q84gW6GXYREa4iJA==, + } + + "@rbxts-js/string@1.2.3-ts.1": + resolution: + { + integrity: sha512-Je/WKgZbXF/sAHEsQPuns5n4rWzux327zjr0KRf9Gy62ieUzl2FRZZHjVYce3xdMtHif2JstIf/J3p513TUtRg==, + } + + "@rbxts-js/symbol-luau@1.0.0-ts.1": + resolution: + { + integrity: sha512-TQS/ImDCK5KPGvbmyScH5EZS0ELSCDvZEFR3W+ItPlsLl+bDRSI/0uaKtJ7rbwXgdGwBDKM1QXFPjqe+KOphIA==, + } + + "@rbxts-js/timers@1.2.3-ts.1": + resolution: + { + integrity: sha512-HKP4ymFX3QLWbR/2Gy4LXwvkhjC9OzbwZ5bNunxu+M8Ocbi1cjRduiZK/y5ogLr9cNIfSoxs6e1DQ3soc8/hwA==, + } + + "@rbxts/bin@1.4.0": + resolution: + { + integrity: sha512-uNg623aZK951W1YFL8Q/BuFWK747ZCbsjKp8smvTJpWuGPXJKZiY1/q/z1rcO4cTmgsEVBqvnsxCooVt7aKQhg==, + } + + "@rbxts/cmdr@1.12.1": + resolution: + { + integrity: sha512-1I/Wnz4hY5fv6Eup7RZ2xGwog4X28dk5zUKYGc/Uo0ENfhwg344c2yf8Sn4CkimLOE2jpLYHq0p8IJ1i1AQg6g==, + } + + "@rbxts/compiler-types@3.0.0-types.0": + resolution: + { + integrity: sha512-VGOHJPoL7+56NTatMGqQj3K7xWuzEV+aP4QD5vZiHu+bcff3kiTmtoadaF6NkJrmwfFAvbsd4Dg764ZjWNceag==, + } + + "@rbxts/cubic-bezier@1.1.0": + resolution: + { + integrity: sha512-JzGODd8HpjbmYXZYLkPoSUUbBqrO2NWv+iUaMMugHGS8cEeayVTbRlstvXlRO8IGatisxhGd17mraregBmXJ0A==, + } + + "@rbxts/dumpster@1.0.8": + resolution: + { + integrity: sha512-mkitEvfA166+EkRPF51vji9ZkAJEbqtwvrMAtPhP6EJy8IKAgh5QseHu5Zh1d5n459ufy1qY+5XHjGi2fQCqJg==, + } + + "@rbxts/easing-functions@1.1.0": + resolution: + { + integrity: sha512-Lt1tKqnDjXLI8mIE/m/aM4AfmaDAOvVGjDRAC8MLoQwadu4+cczv/BDc+2H4vKApWz0GtIeEb4CB/3iejgFw5A==, + } + peerDependencies: + "@rbxts/cubic-bezier": ^1.0.2 + + "@rbxts/gamejoy@1.1.4": + resolution: + { + integrity: sha512-vzjIg9PpRfFDwu6fC/3pUf3tf7eNG8hzuYntDhZQr1Icz3JJqGroiu4koL42uLSd9mezR3tWNtYCGH3kuWSO1w==, + } + + "@rbxts/lapis@0.3.8": + resolution: + { + integrity: sha512-SQTVdSp47Rh7nzuI/ArUsGssz2+KmciPlue4Qlj3i9iO2p3Xgf1ZsgfhvMemPih/kFdjSH47E8wHGA4dY/Y+AA==, + } + peerDependencies: + "@rbxts/t": "*" + + "@rbxts/lerp-functions@1.0.2": + resolution: + { + integrity: sha512-ZNcy/VTyAmCX8Pgc/w4ij6Tf7Ez4e3S44s91cdgQBt347Xkxxu1G2phI/CLJvIXRWNA2hRz8898KBYqq1kXKfA==, + } + + "@rbxts/log@0.6.3": + resolution: + { + integrity: sha512-YZpDvjL7yif9aYuNAkuM9vnegcwQmOwX0CfvGfWvrzCpmARY4Ey2pTMhoEgxKo36HcdPPi3aLxmcuvn0NHrPPg==, + } + + "@rbxts/maid@1.1.0": + resolution: + { + integrity: sha512-bVWXZ0p2M3OJzPzvN5fY0T4s37ezUMY7EX31Xspp7Ds4C/K9yE4MHMRXjtlNvsYVPmoc5tdhAbpZY02Veix5lg==, + } + + "@rbxts/message-templates@0.3.2": + resolution: + { + integrity: sha512-79onYskH3pgrBT73Zs+biQ31vAVvupKQaxGNWGjyGsxwNhO2YaN/qkut0bvOshaGa+ZzqAXApRVrN8ifIMPiMQ==, + } + + "@rbxts/net@3.0.10": + resolution: + { + integrity: sha512-R+0tKn4/+nmHIVbYgfZsuGq1jxYU5AcKuhyOlSYyzzODTIyJ2QhkyFGjvPSAvEhD+CzeTGKciQt4oy25CWI1oA==, + } + + "@rbxts/object-cache@1.4.5": + resolution: + { + integrity: sha512-PjPfljNRU5ksWOEuh5IFPx14t3Df5D9H6c27pTmTZ+tzGvnYsMvyfsmZal+wxRgKR0rxI8jZ0L0sZSc6LmB9LQ==, + } + + "@rbxts/object-utils@1.0.4": + resolution: + { + integrity: sha512-dLLhf022ipV+9i910sOE7kl9losKHoon0WgeerHqVMQA5EYsLUsVT2AxhJuhk8MiDn5oJ2GiFofE/LadY9TpJQ==, + } + + "@rbxts/react-roblox@17.3.13-ts.1": + resolution: + { + integrity: sha512-mcl6pcZLFe023ikBIn4BdJDRWENqoaNe1rsTzdQlcXrlxqbqQdbjYZ8D1sHl/KGlUertwDl2qJutLf3TBq3FIQ==, + } + peerDependencies: + "@rbxts/react": ^17.3.13-ts.1 + + "@rbxts/react@17.3.13-ts.1": + resolution: + { + integrity: sha512-4QVdsx25DUAHfurjEWW9534qWj8ZE4zDvVLv8hK71eeT2adwyqDwxSBQ7tVHVwDSp73MXcT8HGnMVHon26sxMA==, + } + + "@rbxts/reflex@4.3.1": + resolution: + { + integrity: sha512-S5MbEpHKeRh9NWhwGJ789WbUsmlAlpQziWaNK4+kz1EpaaQ7uHQMZAuN1DYl4+QkqmOSuPlj04COwmArBGWH1Q==, + } + + "@rbxts/ripple@0.10.2": + resolution: + { + integrity: sha512-IakL24BomX5BX0CZSMjDSZdCUNwRbTA6GP+H62qgXQymql3h9MLjit4Jsaz9+NLrKAZfNKSfajMREr/fJkNEdw==, + } + + "@rbxts/rust-classes@0.11.2": + resolution: + { + integrity: sha512-aYbpYxtu1Gn9/r4tqA2EvH1WGHAUtzarTlztZiuKYDMATXHu/aFjJZvCLdFc+w3KVPCRPg8lhJexwsEAfZtRVg==, + } + + "@rbxts/services@1.6.0": + resolution: + { + integrity: sha512-YH02E1/tGHeMXB0Mam8qPdAZa2fjQWdylEscSL4Zn0RIaQeByYImA2qUsXM3nBdhcyKHDwltzSznDKyCGfFEWA==, + } + + "@rbxts/sift@0.0.11": + resolution: + { + integrity: sha512-JT1WTLWKkoBdDaokPz/hEjC64kessP/MPe0OW2rHGtkpQkgnnvOx4wQXzqQs1XMwciyKazjhJZjoV83X3dJplA==, + } + + "@rbxts/signal@1.1.1": + resolution: + { + integrity: sha512-WX+ONE+ld4pG9PvRkR8OgDld9NpaV1RfXyUIw+Q2oXP/5rehkYzvt20NWtrLAP3NhMc5inYInLd+hnufey6nFw==, + } + + "@rbxts/signals-tooling@1.0.8": + resolution: + { + integrity: sha512-BJ5uiFqoKMw7F5QfK5k21CXg/RoFiikh1PfEgoobbEbgWRxPbNT4QCWDASixxcNjBA0Hfph1hb7CUc61p+u6KQ==, + } + + "@rbxts/sound-manager@2.3.4": + resolution: + { + integrity: sha512-8meMwfecVuT9JbPFam9OcPnurlebTkzC9NuYop+zuGXtGGqH+lknPhJggR4d4jR7RR+i5cYr9dc9gJerN5iojg==, + } + + "@rbxts/t@2.2.1": + resolution: + { + integrity: sha512-MoFLHRQdm1tpAviAVxgkQj/ktxtnGBund2HXQj5xRqgYUWuC3kYuLkctuxXiJHUd1o1JzfU/HvVHtZfpqp61tg==, + } + + "@rbxts/t@3.2.1": + resolution: + { + integrity: sha512-/fnf2QSx+TI3HSJ6QW2ikvGQk0mlx/63UXo60pLAKGqbxXiAuTJh88bpKpHw6aCzfFLJAwrVIcgCUOokLMPcag==, + } + + "@rbxts/testez@0.4.2-ts.0": + resolution: + { + integrity: sha512-8Q+OG9ddTD2P3aARCuRKpPqUBvuifgSnHvQMZ6jBMqUzxhIysnb0l4c3vnnaQnvdyZ1OW9tKxcdEHMGTb67uOw==, + } + + "@rbxts/timer@0.0.9": + resolution: + { + integrity: sha512-XeX68qpRjNxvDy5efcWI0jGKM4zRFyV6EhQHSmuBbWZDQl1pD2F7h95lSZK53ai2Z2mP1l94UQYY93WMODUcIQ==, + } + + "@rbxts/trove@1.3.0": + resolution: + { + integrity: sha512-Ibekh0xr2G0WTWlUhbIaybWZvKF0C4flaBJBhJOuyS/sp6qyyn9l3pWWbSI4zrIExuOIswwKQ7O1BJj2bIJYCA==, + } + + "@rbxts/tween@1.0.5": + resolution: + { + integrity: sha512-FbkogRa2+DZKdUdr+HV5IjHAzttqacvLEWfhwtqQ53vofXIby1i03vq6cCjv94315mCTOcRaR2/vIj1l95Aftw==, + } + + "@rbxts/types@1.0.957": + resolution: + { + integrity: sha512-nqJMT4blRms/Bi1gC/T4x0SVOgr9nZWWz0f7f+Zx1d4ez4QwOWFG3FfKkz1HQCC0SmhKhXJSoZaPpTsweuikaQ==, + } + + "@rbxts/ui-labs@2.4.2": + resolution: + { + integrity: sha512-9cyzDYN4mM7KSupZpbHRCUWCfSHzy8LCqa9Czys2jaKYUCn8oV4j5AE+5jTrHD/8RcltP5EvCSFIFwB0N/NfxA==, + } + + "@roblox-ts/luau-ast@2.0.0": + resolution: + { + integrity: sha512-cmMi093IdwBOLVxwuordhM8AmtbyTIyRpsTbB0D/JauidW4SXsQRQowSwWjHo4QP0DRJBXvOIlxtqEQi50uNzQ==, + } + + "@roblox-ts/path-translator@1.1.0": + resolution: + { + integrity: sha512-D0akTmnNYqBw+ZIek5JxocT3BjmbgGOuOy0x1nIIxHBPNLGCpzseToY8jyYs/0mlvnN2xnSP/k8Tv+jvGOQSwQ==, + } + + "@roblox-ts/rojo-resolver@1.1.0": + resolution: + { + integrity: sha512-QmvVryu1EeME+3QUoG5j/gHGJoJUaffCgZ92mhlG7cJSd1uyhgpY4CNWriZAwZJYkTlzd5Htkpn+18yDFbOFXA==, + } + + "@roblox-ts/rojo-resolver@1.3.0": + resolution: + { + integrity: sha512-LY9UenpWid0UI7OuM0pyvG9cV7QBRyfEUT0hi+1932fBWF2407OhOBjMfRCeRkyDC/Lw0ZMx107HK3gf3LLeFQ==, + } + + "@types/esrecurse@4.3.1": + resolution: + { + integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==, + } + + "@types/estree@1.0.9": + resolution: + { + integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==, + } + + "@types/json-schema@7.0.15": + resolution: + { + integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==, + } + + "@typescript-eslint/eslint-plugin@8.71.2": + resolution: + { + integrity: sha512-K72/pvT7qpm8q3k8G5u93NCSN1RcLxX5h7Gw0bQpbMXviBHeY6Nx1WEwmk9wP+eKTSQ8ecxvcgt/ZX/rB938xQ==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + "@typescript-eslint/parser": ^8.71.2 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + + "@typescript-eslint/parser@8.71.2": + resolution: + { + integrity: sha512-aTtmZjp/x3gsGHkDs04/1I8kNogBJkpQaeGaN0PfiG43NzDEmW2n+OP5im9wfsvyX0yP1UZWBrAGtDBfH9jEVw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + + "@typescript-eslint/project-service@8.71.2": + resolution: + { + integrity: sha512-Pd68GsLVh5ZXlcEccFPdhSOvtHZ/2WU0XsA8zmXOS3F158lVlcTLhfQ6qvtL1RzbMie0Kp8xtbVwAg4nejKPHw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + + "@typescript-eslint/scope-manager@8.71.2": + resolution: + { + integrity: sha512-YF4wgg5iUX5Obywoac27B56FhozMNFhdJSbh11H5GBAwmBtfyRhmgTQqW9tRNNmHDvsZ2CX6UGzoz+YfYn2Leg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@typescript-eslint/tsconfig-utils@8.71.2": + resolution: + { + integrity: sha512-jz49TAWokm/7yWD9/o837nYax9DyALxIPOImdImvVzo6canBCZYzin8zJyqNR5k1j9K1DE0bVvr0XYB+99uAdg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + + "@typescript-eslint/type-utils@8.71.2": + resolution: + { + integrity: sha512-cC8UaalCViJkb3rH/xLIvidEwoFRtMZMUqDjjJMGEKaTFwYRHYBcoczlEGHuvkHEO8svRDrgZ0yQgOArFmwang==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + + "@typescript-eslint/types@8.71.2": + resolution: + { + integrity: sha512-y5Ukl3oVp7e/VGxqIoEXoeb/Roj71PAJ4WG/N/RGj+eeDvKRLM15kGr9MijLbN0i03S52zxu31Z98E5gWChJKw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + "@typescript-eslint/typescript-estree@8.71.2": + resolution: + { + integrity: sha512-DgNOEsl4pdYzwSL+N/Cl33tX1Hlw3FvId73LnKXfq7jnrF2viOEn7X+P7RBkkSWCVm92gnWd28GIC5vJuLREeA==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + typescript: ">=4.8.4 <6.1.0" + + "@typescript-eslint/utils@8.71.2": + resolution: + { + integrity: sha512-RyKa1G1M9rKHDhrk+HJtiF1MvLD5Ux2JW3TSkzw+02pRALdpJb+QzG1Y5em+LvCgzGwgg8H7mBRFKljNz7ImPw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + + "@typescript-eslint/visitor-keys@8.71.2": + resolution: + { + integrity: sha512-zozlN0UA32bSG2zkKAgQyRYSbEISVoyU6vvkKYo2aSny74D8Jh4e+POkJ6Lk0dlezZi5x7TtD+88Xb06IB8nhg==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + + acorn-jsx@5.3.2: + resolution: + { + integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, + } + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.19.0: + resolution: + { + integrity: sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==, + } + engines: { node: ">=0.4.0" } + hasBin: true + + ajv@6.15.0: + resolution: + { + integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==, + } + + ajv@8.20.0: + resolution: + { + integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==, + } + + ansi-regex@5.0.1: + resolution: + { + integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, + } + engines: { node: ">=8" } + + ansi-styles@4.3.0: + resolution: + { + integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, + } + engines: { node: ">=8" } + + anymatch@3.1.3: + resolution: + { + integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==, + } + engines: { node: ">= 8" } + + balanced-match@1.0.2: + resolution: + { + integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, + } + + balanced-match@4.0.4: + resolution: + { + integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==, + } + engines: { node: 18 || 20 || >=22 } + + binary-extensions@2.3.0: + resolution: + { + integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==, + } + engines: { node: ">=8" } + + brace-expansion@2.1.7: + resolution: + { + integrity: sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==, + } + + brace-expansion@5.0.12: + resolution: + { + integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==, + } + engines: { node: 20 || >=22 } + + braces@3.0.3: + resolution: + { + integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==, + } + engines: { node: ">=8" } + + cacheable@2.5.0: + resolution: + { + integrity: sha512-60cyAOytib/OzBw1JNSoSV/boK1AtHryDIjvVBk7XbN4ugfkM3+Sry7fEjNgPMGgOjuaZPAp8ruZ0Cxafwyq9g==, + } + + chalk@4.1.2: + resolution: + { + integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, + } + engines: { node: ">=10" } + + chokidar@3.6.0: + resolution: + { + integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==, + } + engines: { node: ">= 8.10.0" } + + cliui@8.0.1: + resolution: + { + integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==, + } + engines: { node: ">=12" } + + color-convert@2.0.1: + resolution: + { + integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, + } + engines: { node: ">=7.0.0" } + + color-name@1.1.4: + resolution: + { + integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, + } + + cross-spawn@7.0.6: + resolution: + { + integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==, + } + engines: { node: ">= 8" } + + debug@4.4.3: + resolution: + { + integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==, + } + engines: { node: ">=6.0" } + peerDependencies: + supports-color: "*" + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: + { + integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, + } + + emoji-regex@8.0.0: + resolution: + { + integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, + } + + es-errors@1.3.0: + resolution: + { + integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==, + } + engines: { node: ">= 0.4" } + + escalade@3.2.0: + resolution: + { + integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, + } + engines: { node: ">=6" } + + escape-string-regexp@4.0.0: + resolution: + { + integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, + } + engines: { node: ">=10" } + + eslint-config-prettier@10.1.8: + resolution: + { + integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==, + } + hasBin: true + peerDependencies: + eslint: ">=7.0.0" + + eslint-scope@9.1.2: + resolution: + { + integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + eslint-visitor-keys@3.4.3: + resolution: + { + integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, + } + engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + + eslint-visitor-keys@5.0.1: + resolution: + { + integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + eslint@10.12.0: + resolution: + { + integrity: sha512-npHjrHb2o4ehH3mE+YuxABR+0gyb0aVWCIolgJDArwCiHEIsonBm3ZKjel5b0U5+a+MbEvIyRCGCzD3eKKK2NA==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + hasBin: true + peerDependencies: + jiti: "*" + peerDependenciesMeta: + jiti: + optional: true + + espree@11.2.0: + resolution: + { + integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==, + } + engines: { node: ^20.19.0 || ^22.13.0 || >=24 } + + esquery@1.7.0: + resolution: + { + integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==, + } + engines: { node: ">=0.10" } + + esrecurse@4.3.0: + resolution: + { + integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, + } + engines: { node: ">=4.0" } + + estraverse@5.3.0: + resolution: + { + integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, + } + engines: { node: ">=4.0" } + + esutils@2.0.3: + resolution: + { + integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, + } + engines: { node: ">=0.10.0" } + + fast-deep-equal@3.1.3: + resolution: + { + integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, + } + + fast-json-stable-stringify@2.1.0: + resolution: + { + integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, + } + + fast-levenshtein@2.0.6: + resolution: + { + integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, + } + + fast-uri@3.1.8: + resolution: + { + integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==, + } + + fdir@6.5.0: + resolution: + { + integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, + } + engines: { node: ">=12.0.0" } + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@11.1.5: + resolution: + { + integrity: sha512-+PFTHITI08JIGhnNpGNI8T8inUpgZfk3GNEqfT9R2zZV2iFXg3CvqzSl/uEhs7TSGujYRELEANyDvS8Fj7+S7Q==, + } + + fill-range@7.1.1: + resolution: + { + integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==, + } + engines: { node: ">=8" } + + find-up@5.0.0: + resolution: + { + integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, + } + engines: { node: ">=10" } + + flat-cache@6.1.23: + resolution: + { + integrity: sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==, + } + + flatted@3.4.4: + resolution: + { + integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==, + } + + fs-extra@10.1.0: + resolution: + { + integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==, + } + engines: { node: ">=12" } + + fs-extra@11.4.1: + resolution: + { + integrity: sha512-KYAb4c9BJQI6QqGKthV68OHe0badztdXJWKo0WtBA9IuCFPTKvE5ZdUBglP833aMjhaSPNO4A5j/EkzZtGlKjA==, + } + engines: { node: ">=14.14" } + + fs.realpath@1.0.0: + resolution: + { + integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==, + } + + fsevents@2.3.3: + resolution: + { + integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + os: [darwin] + + function-bind@1.1.2: + resolution: + { + integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==, + } + + get-caller-file@2.0.5: + resolution: + { + integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==, + } + engines: { node: 6.* || 8.* || >= 10.* } + + glob-parent@5.1.2: + resolution: + { + integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, + } + engines: { node: ">= 6" } + + glob-parent@6.0.2: + resolution: + { + integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, + } + engines: { node: ">=10.13.0" } + + glob@9.3.5: + resolution: + { + integrity: sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==, + } + engines: { node: ">=16 || 14 >=14.17" } + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + graceful-fs@4.2.11: + resolution: + { + integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, + } + + has-flag@4.0.0: + resolution: + { + integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, + } + engines: { node: ">=8" } + + hashery@1.5.1: + resolution: + { + integrity: sha512-iZyKG96/JwPz1N55vj2Ie2vXbhu440zfUfJvSwEqEbeLluk7NnapfGqa7LH0mOsnDxTF85Mx8/dyR6HfqcbmbQ==, + } + engines: { node: ">=20" } + + hashids@2.3.0: + resolution: + { + integrity: sha512-ljM73TE/avEhNnazxaj0Dw3BbEUuLC5yYCQ9RSkSUcT4ZSU6ZebdKCIBJ+xT/DnSYW36E9k82GH1Q6MydSIosQ==, + } + + hasown@2.0.4: + resolution: + { + integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==, + } + engines: { node: ">= 0.4" } + + hookified@1.15.1: + resolution: + { + integrity: sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==, + } + + hookified@2.2.0: + resolution: + { + integrity: sha512-p/LgFzRN5FeoD3DLS6bkUapeye6E4SI6yJs6KetENd18S+FBthqYq2amJUWpt5z0EQwwHemidjY5OqJGEKm5uA==, + } + + hosted-git-info@4.1.0: + resolution: + { + integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==, + } + engines: { node: ">=10" } + + husky@9.1.7: + resolution: + { + integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==, + } + engines: { node: ">=18" } + hasBin: true + + ignore@5.3.2: + resolution: + { + integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==, + } + engines: { node: ">= 4" } + + ignore@7.0.12: + resolution: + { + integrity: sha512-/8UvqAPU9DGTI9k4mxtf49U37Isfwr8Uts96+SBHIkFxPJnHS0Ew4f00sM4Scd8V8EjM0jUNtVDdv1kPdt35lg==, + } + engines: { node: ">= 4" } + + imurmurhash@0.1.4: + resolution: + { + integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, + } + engines: { node: ">=0.8.19" } + + is-binary-path@2.1.0: + resolution: + { + integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==, + } + engines: { node: ">=8" } + + is-core-module@2.17.0: + resolution: + { + integrity: sha512-J/vG0zBCbIKOQFfufSwyXdMrsohyJIUNkrnmo6WZGzoM7tr/lsbfW5b2BvisL6zsyMzK9UxV9L6c7AoFbyXHOA==, + } + engines: { node: ">= 0.4" } + + is-extglob@2.1.1: + resolution: + { + integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, + } + engines: { node: ">=0.10.0" } + + is-fullwidth-code-point@3.0.0: + resolution: + { + integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, + } + engines: { node: ">=8" } + + is-glob@4.0.3: + resolution: + { + integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, + } + engines: { node: ">=0.10.0" } + + is-number@7.0.0: + resolution: + { + integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, + } + engines: { node: ">=0.12.0" } + + isexe@2.0.0: + resolution: + { + integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, + } + + json-schema-traverse@0.4.1: + resolution: + { + integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, + } + + json-schema-traverse@1.0.0: + resolution: + { + integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==, + } + + json-stable-stringify-without-jsonify@1.0.1: + resolution: + { + integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, + } + + jsonfile@6.2.1: + resolution: + { + integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==, + } + + keyv@5.6.0: + resolution: + { + integrity: sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==, + } + + kleur@4.1.5: + resolution: + { + integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==, + } + engines: { node: ">=6" } + + levn@0.4.1: + resolution: + { + integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, + } + engines: { node: ">= 0.8.0" } + + locate-path@6.0.0: + resolution: + { + integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, + } + engines: { node: ">=10" } + + lru-cache@10.4.3: + resolution: + { + integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==, + } + + lru-cache@6.0.0: + resolution: + { + integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==, + } + engines: { node: ">=10" } + + minimatch@10.2.6: + resolution: + { + integrity: sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==, + } + engines: { node: 18 || 20 || >=22 } + + minimatch@8.0.7: + resolution: + { + integrity: sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==, + } + engines: { node: ">=16 || 14 >=14.17" } + + minipass@4.2.8: + resolution: + { + integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==, + } + engines: { node: ">=8" } + + minipass@7.1.3: + resolution: + { + integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==, + } + engines: { node: ">=16 || 14 >=14.17" } + + ms@2.1.3: + resolution: + { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, + } + + natural-compare@1.4.0: + resolution: + { + integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, + } + + normalize-package-data@3.0.3: + resolution: + { + integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==, + } + engines: { node: ">=10" } + + normalize-path@3.0.0: + resolution: + { + integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==, + } + engines: { node: ">=0.10.0" } + + optionator@0.9.4: + resolution: + { + integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==, + } + engines: { node: ">= 0.8.0" } + + p-limit@3.1.0: + resolution: + { + integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, + } + engines: { node: ">=10" } + + p-locate@5.0.0: + resolution: + { + integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, + } + engines: { node: ">=10" } + + path-exists@4.0.0: + resolution: + { + integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, + } + engines: { node: ">=8" } + + path-key@3.1.1: + resolution: + { + integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, + } + engines: { node: ">=8" } + + path-parse@1.0.7: + resolution: + { + integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, + } + + path-scurry@1.11.1: + resolution: + { + integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==, + } + engines: { node: ">=16 || 14 >=14.18" } + + picomatch@2.3.2: + resolution: + { + integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==, + } + engines: { node: ">=8.6" } + + picomatch@4.0.7: + resolution: + { + integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==, + } + engines: { node: ">=12" } + + prelude-ls@1.2.1: + resolution: + { + integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, + } + engines: { node: ">= 0.8.0" } + + prettier@3.9.10: + resolution: + { + integrity: sha512-skGHTH1Yb9QvaTC4hWnRTzEYtCpApF0UMBPz+t/0ZPzP8ihefo/btKd+QA7pUE0fEwA1SbS/NmhyEcstdXOIdA==, + } + engines: { node: ">=14" } + hasBin: true + + punycode@2.3.1: + resolution: + { + integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==, + } + engines: { node: ">=6" } + + qified@0.10.1: + resolution: + { + integrity: sha512-+Owyggi9IxT1ePKGafcI87ubSmxol6smwJ+RAHDQlx9+9cPwFWDiKFFCPuWhr9ignlGpZ9vDQLw67N4dcTVFEA==, + } + engines: { node: ">=20" } + + rbxts-build@2.6.0: + resolution: + { + integrity: sha512-YTS5P6Xs4AQu6AV3YPBBPqccBwKq5kSmiA1TGdE/O/Tikk58opqUy+xvInRP18qwdoBuH0vJVN+h8n6utA+cJA==, + } + hasBin: true + + rbxts-transformer-flamework@1.3.2: + resolution: + { + integrity: sha512-FKAT7+jAmoa2/MZNqZnjZfpSKypNO1+IzRGdHQAjGsKzH8J58Zcbb0fCA/Dydwb4LiPVVYlfgoItrxOCWRh3rg==, + } + peerDependencies: + typescript: "*" + + readdirp@3.6.0: + resolution: + { + integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==, + } + engines: { node: ">=8.10.0" } + + require-directory@2.1.1: + resolution: + { + integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==, + } + engines: { node: ">=0.10.0" } + + require-from-string@2.0.2: + resolution: + { + integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, + } + engines: { node: ">=0.10.0" } + + resolve@1.22.13: + resolution: + { + integrity: sha512-Dj/cW9zBV2aRj6XzCxuGIs3ESZKv02jixne3q6jFZYZ3/Tg09gHHviSMDL4kObh9pqSahdRHEnDoaP2gn8YLhA==, + } + engines: { node: ">= 0.4" } + hasBin: true + + roblox-ts@3.0.0: + resolution: + { + integrity: sha512-hwAC2frIFlLJOtHd6F+5opMEhBgfAMK9z5l1mP+bykLBbMO5cn1q5lIwhhXbeh9Pq07rlhF8uGHlmeRLPd/3AA==, + } + hasBin: true + + semver@7.8.5: + resolution: + { + integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==, + } + engines: { node: ">=10" } + hasBin: true + + shebang-command@2.0.0: + resolution: + { + integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, + } + engines: { node: ">=8" } + + shebang-regex@3.0.0: + resolution: + { + integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, + } + engines: { node: ">=8" } + + spdx-correct@3.2.0: + resolution: + { + integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==, + } + + spdx-exceptions@2.5.0: + resolution: + { + integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==, + } + + spdx-expression-parse@3.0.1: + resolution: + { + integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==, + } + + spdx-license-ids@3.0.24: + resolution: + { + integrity: sha512-cLS9TtWkIQFyLkJ3/5aFQAOHOSKTlOs/7WDut/XPSdjom1fJhUdkepeJAKXa9Y05+FabHd0sti+Et559vDtkpQ==, + } + + string-width@4.2.3: + resolution: + { + integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, + } + engines: { node: ">=8" } + + strip-ansi@6.0.1: + resolution: + { + integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, + } + engines: { node: ">=8" } + + supports-color@7.2.0: + resolution: + { + integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, + } + engines: { node: ">=8" } + + supports-preserve-symlinks-flag@1.0.0: + resolution: + { + integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, + } + engines: { node: ">= 0.4" } + + tinyglobby@0.2.17: + resolution: + { + integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==, + } + engines: { node: ">=12.0.0" } + + to-regex-range@5.0.1: + resolution: + { + integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, + } + engines: { node: ">=8.0" } + + ts-api-utils@2.5.0: + resolution: + { + integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==, + } + engines: { node: ">=18.12" } + peerDependencies: + typescript: ">=4.8.4" + + type-check@0.4.0: + resolution: + { + integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, + } + engines: { node: ">= 0.8.0" } + + typescript-eslint@8.71.2: + resolution: + { + integrity: sha512-Rz4effbMy2g9wnvOJZG6SbVavX313Lap1vNEbk1RUochzickvzP8P4HB+bkNg5e50eVz1hQGjh9K1omI1xB4Bw==, + } + engines: { node: ^18.18.0 || ^20.9.0 || >=21.1.0 } + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: ">=4.8.4 <6.1.0" + + typescript@5.5.3: + resolution: + { + integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==, + } + engines: { node: ">=14.17" } + hasBin: true + + universalify@2.0.1: + resolution: + { + integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==, + } + engines: { node: ">= 10.0.0" } + + uri-js@4.4.1: + resolution: + { + integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, + } + + uuid@8.3.2: + resolution: + { + integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==, + } + deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + hasBin: true + + validate-npm-package-license@3.0.4: + resolution: + { + integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==, + } + + which@2.0.2: + resolution: + { + integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, + } + engines: { node: ">= 8" } + hasBin: true + + word-wrap@1.2.5: + resolution: + { + integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==, + } + engines: { node: ">=0.10.0" } + + wrap-ansi@7.0.0: + resolution: + { + integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==, + } + engines: { node: ">=10" } + + y18n@5.0.8: + resolution: + { + integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==, + } + engines: { node: ">=10" } + + yallist@4.0.0: + resolution: + { + integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==, + } + + yargs-parser@21.1.1: + resolution: + { + integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==, + } + engines: { node: ">=12" } + + yargs@17.7.3: + resolution: + { + integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==, + } + engines: { node: ">=12" } + + yocto-queue@0.1.0: + resolution: + { + integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, + } + engines: { node: ">=10" } + + zod@3.25.76: + resolution: + { + integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==, + } + +snapshots: + "@cacheable/memory@2.2.0": + dependencies: + "@cacheable/utils": 2.5.0 + "@keyv/bigmap": 1.3.1(keyv@5.6.0) + hookified: 1.15.1 + keyv: 5.6.0 + + "@cacheable/utils@2.5.0": + dependencies: + hashery: 1.5.1 + keyv: 5.6.0 + + "@eslint-community/eslint-utils@4.10.1(eslint@10.12.0(supports-color@7.2.0))": + dependencies: + eslint: 10.12.0(supports-color@7.2.0) + eslint-visitor-keys: 3.4.3 + + "@eslint-community/regexpp@4.12.2": {} + + "@eslint/config-array@0.23.5(supports-color@7.2.0)": + dependencies: + "@eslint/object-schema": 3.0.5 + debug: 4.4.3(supports-color@7.2.0) + minimatch: 10.2.6 + transitivePeerDependencies: + - supports-color + + "@eslint/config-helpers@0.7.0": + dependencies: + "@eslint/core": 1.2.1 + + "@eslint/core@1.2.1": + dependencies: + "@types/json-schema": 7.0.15 + + "@eslint/js@10.0.1(eslint@10.12.0(supports-color@7.2.0))": + optionalDependencies: + eslint: 10.12.0(supports-color@7.2.0) + + "@eslint/object-schema@3.0.5": {} + + "@eslint/plugin-kit@0.7.3": + dependencies: + "@eslint/core": 1.2.1 + levn: 0.4.1 + + "@flamework/components@1.3.2(@flamework/core@1.3.2)": + dependencies: + "@flamework/core": 1.3.2 + "@rbxts/maid": 1.1.0 + "@rbxts/services": 1.6.0 + "@rbxts/signal": 1.1.1 + "@rbxts/t": 2.2.1 + + "@flamework/core@1.3.2": + dependencies: + "@rbxts/maid": 1.1.0 + "@rbxts/object-utils": 1.0.4 + "@rbxts/services": 1.6.0 + "@rbxts/signal": 1.1.1 + "@rbxts/t": 3.2.1 + + "@flamework/networking@1.3.2(@flamework/core@1.3.2)": + dependencies: + "@flamework/core": 1.3.2 + "@rbxts/object-utils": 1.0.4 + "@rbxts/services": 1.6.0 + "@rbxts/signal": 1.1.1 + "@rbxts/t": 3.2.1 + + "@humanfs/core@0.19.2": + dependencies: + "@humanfs/types": 0.15.0 + + "@humanfs/node@0.16.8": + dependencies: + "@humanfs/core": 0.19.2 + "@humanfs/types": 0.15.0 + "@humanwhocodes/retry": 0.4.3 + + "@humanfs/types@0.15.0": {} + + "@humanwhocodes/module-importer@1.0.1": {} + + "@humanwhocodes/retry@0.4.3": {} + + "@keyv/bigmap@1.3.1(keyv@5.6.0)": + dependencies: + hashery: 1.5.1 + hookified: 1.15.1 + keyv: 5.6.0 + + "@keyv/serialize@1.1.1": {} + + "@rbxts-js/boolean@1.2.3-ts.1": + dependencies: + "@rbxts-js/collections": 1.2.3-ts.1 + + "@rbxts-js/collections@1.2.3-ts.1": + dependencies: + "@rbxts-js/es7-types": 1.2.3-ts.1 + "@rbxts-js/instance-of": 1.2.3-ts.1 + + "@rbxts-js/console@1.2.3-ts.1": + dependencies: + "@rbxts-js/collections": 1.2.3-ts.1 + + "@rbxts-js/es7-types@1.2.3-ts.1": {} + + "@rbxts-js/instance-of@1.2.3-ts.1": {} + + "@rbxts-js/luau-polyfill@1.2.3-ts.1": + dependencies: + "@rbxts-js/boolean": 1.2.3-ts.1 + "@rbxts-js/collections": 1.2.3-ts.1 + "@rbxts-js/console": 1.2.3-ts.1 + "@rbxts-js/es7-types": 1.2.3-ts.1 + "@rbxts-js/instance-of": 1.2.3-ts.1 + "@rbxts-js/math": 1.2.3-ts.1 + "@rbxts-js/number": 1.2.3-ts.1 + "@rbxts-js/string": 1.2.3-ts.1 + "@rbxts-js/symbol-luau": 1.0.0-ts.1 + "@rbxts-js/timers": 1.2.3-ts.1 + + "@rbxts-js/math@1.2.3-ts.1": {} + + "@rbxts-js/number@1.2.3-ts.1": {} + + "@rbxts-js/react-globals@17.3.13-ts.2": + dependencies: + "@rbxts-js/safe-flags": 17.3.13-ts.2 + + "@rbxts-js/react-reconciler@17.3.13-ts.2": + dependencies: + "@rbxts-js/luau-polyfill": 1.2.3-ts.1 + "@rbxts-js/react": 17.3.13-ts.2 + "@rbxts-js/react-globals": 17.3.13-ts.2 + "@rbxts-js/roblox-lua-promise": 3.5.0-ts.2 + "@rbxts-js/safe-flags": 17.3.13-ts.2 + "@rbxts-js/scheduler": 17.3.13-ts.2 + "@rbxts-js/shared": 17.3.13-ts.2 + + "@rbxts-js/react-roblox@17.3.13-ts.2": + dependencies: + "@rbxts-js/luau-polyfill": 1.2.3-ts.1 + "@rbxts-js/react": 17.3.13-ts.2 + "@rbxts-js/react-globals": 17.3.13-ts.2 + "@rbxts-js/react-reconciler": 17.3.13-ts.2 + "@rbxts-js/scheduler": 17.3.13-ts.2 + "@rbxts-js/shared": 17.3.13-ts.2 + + "@rbxts-js/react@17.3.13-ts.2": + dependencies: + "@rbxts-js/luau-polyfill": 1.2.3-ts.1 + "@rbxts-js/react-globals": 17.3.13-ts.2 + "@rbxts-js/shared": 17.3.13-ts.2 + + "@rbxts-js/roblox-lua-promise@3.5.0-ts.2": {} + + "@rbxts-js/safe-flags@17.3.13-ts.2": {} + + "@rbxts-js/scheduler@17.3.13-ts.2": + dependencies: + "@rbxts-js/luau-polyfill": 1.2.3-ts.1 + "@rbxts-js/react-globals": 17.3.13-ts.2 + "@rbxts-js/safe-flags": 17.3.13-ts.2 + "@rbxts-js/shared": 17.3.13-ts.2 + + "@rbxts-js/shared@17.3.13-ts.2": + dependencies: + "@rbxts-js/luau-polyfill": 1.2.3-ts.1 + "@rbxts-js/react-globals": 17.3.13-ts.2 + "@rbxts-js/safe-flags": 17.3.13-ts.2 + + "@rbxts-js/string@1.2.3-ts.1": + dependencies: + "@rbxts-js/es7-types": 1.2.3-ts.1 + "@rbxts-js/number": 1.2.3-ts.1 + + "@rbxts-js/symbol-luau@1.0.0-ts.1": {} + + "@rbxts-js/timers@1.2.3-ts.1": + dependencies: + "@rbxts-js/collections": 1.2.3-ts.1 + + "@rbxts/bin@1.4.0": {} + + "@rbxts/cmdr@1.12.1": {} + + "@rbxts/compiler-types@3.0.0-types.0": {} + + "@rbxts/cubic-bezier@1.1.0": {} + + "@rbxts/dumpster@1.0.8": {} + + "@rbxts/easing-functions@1.1.0(@rbxts/cubic-bezier@1.1.0)": + dependencies: + "@rbxts/cubic-bezier": 1.1.0 + + "@rbxts/gamejoy@1.1.4": + dependencies: + "@rbxts/bin": 1.4.0 + "@rbxts/rust-classes": 0.11.2 + "@rbxts/services": 1.6.0 + "@rbxts/signal": 1.1.1 + "@rbxts/t": 3.2.1 + + "@rbxts/lapis@0.3.8(@rbxts/t@3.2.1)": + dependencies: + "@rbxts/t": 3.2.1 + + "@rbxts/lerp-functions@1.0.2": + dependencies: + "@rbxts/types": 1.0.957 + + "@rbxts/log@0.6.3": + dependencies: + "@rbxts/message-templates": 0.3.2 + + "@rbxts/maid@1.1.0": {} + + "@rbxts/message-templates@0.3.2": {} + + "@rbxts/net@3.0.10": {} + + "@rbxts/object-cache@1.4.5": {} + + "@rbxts/object-utils@1.0.4": {} + + "@rbxts/react-roblox@17.3.13-ts.1(@rbxts/react@17.3.13-ts.1)": + dependencies: + "@rbxts-js/react-roblox": 17.3.13-ts.2 + "@rbxts/react": 17.3.13-ts.1 + + "@rbxts/react@17.3.13-ts.1": + dependencies: + "@rbxts-js/react": 17.3.13-ts.2 + + "@rbxts/reflex@4.3.1": {} + + "@rbxts/ripple@0.10.2": {} + + "@rbxts/rust-classes@0.11.2": {} + + "@rbxts/services@1.6.0": {} + + "@rbxts/sift@0.0.11": {} + + "@rbxts/signal@1.1.1": {} + + "@rbxts/signals-tooling@1.0.8": + dependencies: + "@rbxts/dumpster": 1.0.8 + "@rbxts/services": 1.6.0 + + "@rbxts/sound-manager@2.3.4": + dependencies: + "@rbxts/testez": 0.4.2-ts.0 + + "@rbxts/t@2.2.1": {} + + "@rbxts/t@3.2.1": {} + + "@rbxts/testez@0.4.2-ts.0": {} + + "@rbxts/timer@0.0.9": + dependencies: + "@rbxts/dumpster": 1.0.8 + "@rbxts/services": 1.6.0 + "@rbxts/signals-tooling": 1.0.8 + + "@rbxts/trove@1.3.0": {} + + "@rbxts/tween@1.0.5": + dependencies: + "@rbxts/cubic-bezier": 1.1.0 + "@rbxts/easing-functions": 1.1.0(@rbxts/cubic-bezier@1.1.0) + "@rbxts/lerp-functions": 1.0.2 + + "@rbxts/types@1.0.957": {} + + "@rbxts/ui-labs@2.4.2": {} + + "@roblox-ts/luau-ast@2.0.0": {} + + "@roblox-ts/path-translator@1.1.0": + dependencies: + ajv: 8.20.0 + fs-extra: 11.4.1 + + "@roblox-ts/rojo-resolver@1.1.0": + dependencies: + ajv: 8.20.0 + fs-extra: 11.4.1 + + "@roblox-ts/rojo-resolver@1.3.0": + dependencies: + ajv: 8.20.0 + fs-extra: 11.4.1 + + "@types/esrecurse@4.3.1": {} + + "@types/estree@1.0.9": {} + + "@types/json-schema@7.0.15": {} + + "@typescript-eslint/eslint-plugin@8.71.2(@typescript-eslint/parser@8.71.2(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3))(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3)": + dependencies: + "@eslint-community/regexpp": 4.12.2 + "@typescript-eslint/parser": 8.71.2(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3) + "@typescript-eslint/scope-manager": 8.71.2 + "@typescript-eslint/type-utils": 8.71.2(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3) + "@typescript-eslint/utils": 8.71.2(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3) + "@typescript-eslint/visitor-keys": 8.71.2 + eslint: 10.12.0(supports-color@7.2.0) + ignore: 7.0.12 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.5.3) + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/parser@8.71.2(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3)": + dependencies: + "@typescript-eslint/scope-manager": 8.71.2 + "@typescript-eslint/types": 8.71.2 + "@typescript-eslint/typescript-estree": 8.71.2(supports-color@7.2.0)(typescript@5.5.3) + "@typescript-eslint/visitor-keys": 8.71.2 + debug: 4.4.3(supports-color@7.2.0) + eslint: 10.12.0(supports-color@7.2.0) + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/project-service@8.71.2(supports-color@7.2.0)(typescript@5.5.3)": + dependencies: + "@typescript-eslint/tsconfig-utils": 8.71.2(typescript@5.5.3) + "@typescript-eslint/types": 8.71.2 + debug: 4.4.3(supports-color@7.2.0) + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/scope-manager@8.71.2": + dependencies: + "@typescript-eslint/types": 8.71.2 + "@typescript-eslint/visitor-keys": 8.71.2 + + "@typescript-eslint/tsconfig-utils@8.71.2(typescript@5.5.3)": + dependencies: + typescript: 5.5.3 + + "@typescript-eslint/type-utils@8.71.2(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3)": + dependencies: + "@typescript-eslint/types": 8.71.2 + "@typescript-eslint/typescript-estree": 8.71.2(supports-color@7.2.0)(typescript@5.5.3) + "@typescript-eslint/utils": 8.71.2(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3) + debug: 4.4.3(supports-color@7.2.0) + eslint: 10.12.0(supports-color@7.2.0) + ts-api-utils: 2.5.0(typescript@5.5.3) + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/types@8.71.2": {} + + "@typescript-eslint/typescript-estree@8.71.2(supports-color@7.2.0)(typescript@5.5.3)": + dependencies: + "@typescript-eslint/project-service": 8.71.2(supports-color@7.2.0)(typescript@5.5.3) + "@typescript-eslint/tsconfig-utils": 8.71.2(typescript@5.5.3) + "@typescript-eslint/types": 8.71.2 + "@typescript-eslint/visitor-keys": 8.71.2 + debug: 4.4.3(supports-color@7.2.0) + minimatch: 10.2.6 + semver: 7.8.5 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.5.3) + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/utils@8.71.2(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3)": + dependencies: + "@eslint-community/eslint-utils": 4.10.1(eslint@10.12.0(supports-color@7.2.0)) + "@typescript-eslint/scope-manager": 8.71.2 + "@typescript-eslint/types": 8.71.2 + "@typescript-eslint/typescript-estree": 8.71.2(supports-color@7.2.0)(typescript@5.5.3) + eslint: 10.12.0(supports-color@7.2.0) + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + "@typescript-eslint/visitor-keys@8.71.2": + dependencies: + "@typescript-eslint/types": 8.71.2 + eslint-visitor-keys: 5.0.1 + + acorn-jsx@5.3.2(acorn@8.19.0): + dependencies: + acorn: 8.19.0 + + acorn@8.19.0: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.8 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.2 + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + binary-extensions@2.3.0: {} + + brace-expansion@2.1.7: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@5.0.12: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + cacheable@2.5.0: + dependencies: + "@cacheable/memory": 2.2.0 + "@cacheable/utils": 2.5.0 + hookified: 1.15.1 + keyv: 5.6.0 + qified: 0.10.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + debug@4.4.3(supports-color@7.2.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 7.2.0 + + deep-is@0.1.4: {} + + emoji-regex@8.0.0: {} + + es-errors@1.3.0: {} + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-prettier@10.1.8(eslint@10.12.0(supports-color@7.2.0)): + dependencies: + eslint: 10.12.0(supports-color@7.2.0) + + eslint-scope@9.1.2: + dependencies: + "@types/esrecurse": 4.3.1 + "@types/estree": 1.0.9 + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@10.12.0(supports-color@7.2.0): + dependencies: + "@eslint-community/eslint-utils": 4.10.1(eslint@10.12.0(supports-color@7.2.0)) + "@eslint-community/regexpp": 4.12.2 + "@eslint/config-array": 0.23.5(supports-color@7.2.0) + "@eslint/config-helpers": 0.7.0 + "@eslint/core": 1.2.1 + "@eslint/plugin-kit": 0.7.3 + "@humanfs/node": 0.16.8 + "@humanwhocodes/module-importer": 1.0.1 + "@humanwhocodes/retry": 0.4.3 + "@types/estree": 1.0.9 + ajv: 6.15.0 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@7.2.0) + escape-string-regexp: 4.0.0 + eslint-scope: 9.1.2 + eslint-visitor-keys: 5.0.1 + espree: 11.2.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 11.1.5 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + minimatch: 10.2.6 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@11.2.0: + dependencies: + acorn: 8.19.0 + acorn-jsx: 5.3.2(acorn@8.19.0) + eslint-visitor-keys: 5.0.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-uri@3.1.8: {} + + fdir@6.5.0(picomatch@4.0.7): + optionalDependencies: + picomatch: 4.0.7 + + file-entry-cache@11.1.5: + dependencies: + flat-cache: 6.1.23 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@6.1.23: + dependencies: + cacheable: 2.5.0 + flatted: 3.4.4 + hookified: 1.15.1 + + flatted@3.4.4: {} + + fs-extra@10.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fs-extra@11.4.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.1 + universalify: 2.0.1 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + get-caller-file@2.0.5: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@9.3.5: + dependencies: + fs.realpath: 1.0.0 + minimatch: 8.0.7 + minipass: 4.2.8 + path-scurry: 1.11.1 + + graceful-fs@4.2.11: {} + + has-flag@4.0.0: {} + + hashery@1.5.1: + dependencies: + hookified: 1.15.1 + + hashids@2.3.0: {} + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + hookified@1.15.1: {} + + hookified@2.2.0: {} + + hosted-git-info@4.1.0: + dependencies: + lru-cache: 6.0.0 + + husky@9.1.7: {} + + ignore@5.3.2: {} + + ignore@7.0.12: {} + + imurmurhash@0.1.4: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.17.0: + dependencies: + hasown: 2.0.4 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + isexe@2.0.0: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + jsonfile@6.2.1: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + keyv@5.6.0: + dependencies: + "@keyv/serialize": 1.1.1 + + kleur@4.1.5: {} + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lru-cache@10.4.3: {} + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + minimatch@10.2.6: + dependencies: + brace-expansion: 5.0.12 + + minimatch@8.0.7: + dependencies: + brace-expansion: 2.1.7 + + minipass@4.2.8: {} + + minipass@7.1.3: {} + + ms@2.1.3: {} + + natural-compare@1.4.0: {} + + normalize-package-data@3.0.3: + dependencies: + hosted-git-info: 4.1.0 + is-core-module: 2.17.0 + semver: 7.8.5 + validate-npm-package-license: 3.0.4 + + normalize-path@3.0.0: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.3 + + picomatch@2.3.2: {} + + picomatch@4.0.7: {} + + prelude-ls@1.2.1: {} + + prettier@3.9.10: {} + + punycode@2.3.1: {} + + qified@0.10.1: + dependencies: + hookified: 2.2.0 + + rbxts-build@2.6.0: + dependencies: + kleur: 4.1.5 + yargs: 17.7.3 + zod: 3.25.76 + + rbxts-transformer-flamework@1.3.2(typescript@5.5.3): + dependencies: + "@roblox-ts/rojo-resolver": 1.3.0 + ajv: 8.20.0 + chalk: 4.1.2 + fs-extra: 10.1.0 + glob: 9.3.5 + hashids: 2.3.0 + normalize-package-data: 3.0.3 + typescript: 5.5.3 + uuid: 8.3.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.2 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resolve@1.22.13: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.17.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + roblox-ts@3.0.0: + dependencies: + "@roblox-ts/luau-ast": 2.0.0 + "@roblox-ts/path-translator": 1.1.0 + "@roblox-ts/rojo-resolver": 1.1.0 + chokidar: 3.6.0 + fs-extra: 11.4.1 + kleur: 4.1.5 + resolve: 1.22.13 + typescript: 5.5.3 + yargs: 17.7.3 + + semver@7.8.5: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.24 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.24 + + spdx-license-ids@3.0.24: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.7) + picomatch: 4.0.7 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.5.0(typescript@5.5.3): + dependencies: + typescript: 5.5.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typescript-eslint@8.71.2(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3): + dependencies: + "@typescript-eslint/eslint-plugin": 8.71.2(@typescript-eslint/parser@8.71.2(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3))(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3) + "@typescript-eslint/parser": 8.71.2(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3) + "@typescript-eslint/typescript-estree": 8.71.2(supports-color@7.2.0)(typescript@5.5.3) + "@typescript-eslint/utils": 8.71.2(eslint@10.12.0(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3) + eslint: 10.12.0(supports-color@7.2.0) + typescript: 5.5.3 + transitivePeerDependencies: + - supports-color + + typescript@5.5.3: {} + + universalify@2.0.1: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + uuid@8.3.2: {} + + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + y18n@5.0.8: {} + + yallist@4.0.0: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.3: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + zod@3.25.76: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..595dfc7 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,12 @@ +minimumReleaseAgeExclude: + - "@typescript-eslint/eslint-plugin@8.71.2" + - "@typescript-eslint/parser@8.71.2" + - "@typescript-eslint/project-service@8.71.2" + - "@typescript-eslint/scope-manager@8.71.2" + - "@typescript-eslint/tsconfig-utils@8.71.2" + - "@typescript-eslint/type-utils@8.71.2" + - "@typescript-eslint/types@8.71.2" + - "@typescript-eslint/typescript-estree@8.71.2" + - "@typescript-eslint/utils@8.71.2" + - "@typescript-eslint/visitor-keys@8.71.2" + - typescript-eslint@8.71.2 diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..75fe32c --- /dev/null +++ b/renovate.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended"], + "rangeStrategy": "pin", + "packageRules": [ + { + "matchPackageNames": [ + "@flamework/core", + "@flamework/components", + "@flamework/networking", + "rbxts-transformer-flamework" + ], + "groupName": "Flamework" + }, + { + "matchPackageNames": ["roblox-ts", "@rbxts/compiler-types"], + "groupName": "roblox-ts compiler" + } + ] +} diff --git a/rokit.toml b/rokit.toml new file mode 100644 index 0000000..cc646f4 --- /dev/null +++ b/rokit.toml @@ -0,0 +1,10 @@ +# This file lists tools managed by Rokit, a toolchain manager for Roblox projects. +# For more information, see https://github.com/rojo-rbx/rokit + +# New tools can be added by running `rokit add ` in a terminal. + +[tools] +rojo = "rojo-rbx/rojo@7.7.1" +asphalt = "jackTabsCode/asphalt@2.0.2" +darklua = "seaofvoices/darklua@0.19.0" +mantle = "blake-mealey/mantle@0.11.18" diff --git a/src/client/main.client.ts b/src/client/main.client.ts new file mode 100644 index 0000000..5017629 --- /dev/null +++ b/src/client/main.client.ts @@ -0,0 +1,5 @@ +import { Flamework } from "@flamework/core"; +import { TEMPLATE_NAME } from "shared/template-name"; + +Flamework.ignite(); +print(`${TEMPLATE_NAME} ready (client)`); diff --git a/src/server/main.server.ts b/src/server/main.server.ts new file mode 100644 index 0000000..6e21d2b --- /dev/null +++ b/src/server/main.server.ts @@ -0,0 +1,6 @@ +import { Flamework } from "@flamework/core"; +import { TEMPLATE_NAME } from "shared/template-name"; + +Flamework.addPaths("src/server/services"); +Flamework.ignite(); +print(`${TEMPLATE_NAME} ready (server)`); diff --git a/src/server/services/startup-service.ts b/src/server/services/startup-service.ts new file mode 100644 index 0000000..2cc86db --- /dev/null +++ b/src/server/services/startup-service.ts @@ -0,0 +1,8 @@ +import { OnStart, Service } from "@flamework/core"; + +@Service() +export class StartupService implements OnStart { + public onStart(): void { + print("CrownStudio template ready (server)"); + } +} diff --git a/src/shared/template-name.ts b/src/shared/template-name.ts new file mode 100644 index 0000000..480e787 --- /dev/null +++ b/src/shared/template-name.ts @@ -0,0 +1 @@ +export const TEMPLATE_NAME = "CrownStudio template"; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..bbf5a03 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "allowSyntheticDefaultImports": true, + "downlevelIteration": true, + "experimentalDecorators": true, + "forceConsistentCasingInFileNames": true, + "incremental": true, + "jsx": "react", + "jsxFactory": "React.createElement", + "jsxFragmentFactory": "React.Fragment", + "module": "commonjs", + "moduleDetection": "force", + "moduleResolution": "Node", + "noLib": true, + "outDir": "out", + "baseUrl": "src", + "resolveJsonModule": true, + "rootDir": "src", + "strict": true, + "target": "ESNext", + "tsBuildInfoFile": "out/tsconfig.tsbuildinfo", + "typeRoots": ["node_modules/@rbxts", "node_modules/@flamework"], + "types": ["types", "compiler-types"], + "plugins": [{ "transform": "rbxts-transformer-flamework" }] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"] +} From 5a7f646a8b909b9951837877abec10ebe0b9ab3c Mon Sep 17 00:00:00 2001 From: Infuseting Date: Tue, 13 Oct 2026 20:26:40 +0200 Subject: [PATCH 2/6] chore(mcp): configure Blender and Roblox Studio clients --- .agents/mcp_config.json | 16 ++++++++++++++++ .codex/config.toml | 7 +++++++ .cursor/mcp.json | 16 ++++++++++++++++ .mcp.json | 16 ++++++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 .agents/mcp_config.json create mode 100644 .codex/config.toml create mode 100644 .cursor/mcp.json create mode 100644 .mcp.json diff --git a/.agents/mcp_config.json b/.agents/mcp_config.json new file mode 100644 index 0000000..e426abc --- /dev/null +++ b/.agents/mcp_config.json @@ -0,0 +1,16 @@ +{ + "mcpServers": { + "roblox_studio": { + "command": "cmd.exe", + "args": ["/c", "%LOCALAPPDATA%\\Roblox\\mcp.bat"] + }, + "blender": { + "command": "uvx", + "args": [ + "--from", + "git+https://projects.blender.org/lab/blender_mcp.git@v1.0.3#subdirectory=mcp", + "blender-mcp" + ] + } + } +} diff --git a/.codex/config.toml b/.codex/config.toml new file mode 100644 index 0000000..7b68f1d --- /dev/null +++ b/.codex/config.toml @@ -0,0 +1,7 @@ +[mcp_servers.roblox_studio] +command = "cmd.exe" +args = ["/c", "%LOCALAPPDATA%\\Roblox\\mcp.bat"] + +[mcp_servers.blender] +command = "uvx" +args = ["--from", "git+https://projects.blender.org/lab/blender_mcp.git@v1.0.3#subdirectory=mcp", "blender-mcp"] diff --git a/.cursor/mcp.json b/.cursor/mcp.json new file mode 100644 index 0000000..e426abc --- /dev/null +++ b/.cursor/mcp.json @@ -0,0 +1,16 @@ +{ + "mcpServers": { + "roblox_studio": { + "command": "cmd.exe", + "args": ["/c", "%LOCALAPPDATA%\\Roblox\\mcp.bat"] + }, + "blender": { + "command": "uvx", + "args": [ + "--from", + "git+https://projects.blender.org/lab/blender_mcp.git@v1.0.3#subdirectory=mcp", + "blender-mcp" + ] + } + } +} diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..e426abc --- /dev/null +++ b/.mcp.json @@ -0,0 +1,16 @@ +{ + "mcpServers": { + "roblox_studio": { + "command": "cmd.exe", + "args": ["/c", "%LOCALAPPDATA%\\Roblox\\mcp.bat"] + }, + "blender": { + "command": "uvx", + "args": [ + "--from", + "git+https://projects.blender.org/lab/blender_mcp.git@v1.0.3#subdirectory=mcp", + "blender-mcp" + ] + } + } +} From c0b6256a44fdcd135c320aa6515a75f83c846bd0 Mon Sep 17 00:00:00 2001 From: Infuseting Date: Tue, 13 Oct 2026 20:27:01 +0200 Subject: [PATCH 3/6] docs: document template setup and dependencies --- readme.md | 116 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 41 deletions(-) diff --git a/readme.md b/readme.md index 61273b6..789ddf5 100644 --- a/readme.md +++ b/readme.md @@ -1,48 +1,82 @@ -# Roblox Libs +# CrownStudio Roblox TS Template -@flamework/core -@flamework/components -@flamework/networking -@rbxts/ripple -@rbxts/react-roblox -@rbxts/services -@rbxts/react -@rbxts/reflex -@rbxts/lapis -@rbxts/trove -@rbxts/log -@csqrl/sift -@rbxts/t -@rbxts/cmdr -@rbxts/sound -@rbxts/gamejoy -@rbxts/tween -@rbxts/part-cache -@rbxts/timer -@rbxts/cross-server -@rbxts/promise -@rbxts/ui-labs +Template Roblox TypeScript avec Flamework, Rojo et une sélection de bibliothèques prêtes à importer. Les versions exactes sont dans [`package.json`](package.json), [`pnpm-lock.yaml`](pnpm-lock.yaml), [`rokit.toml`](rokit.toml) et [`mise.toml`](mise.toml). Pour un agent, ces fichiers et les statuts ci-dessous sont la source de vérité : une référence dans ce README ne signifie pas qu'une fonction de jeu est déjà implémentée. -# Roblox Tools +## Démarrage -Asphalt -Darklua -Mantle -Mise -Rojo -rbxts-build -roblox-ts +Prérequis : [Roblox Studio](https://create.roblox.com/docs/studio/setup), [mise](https://mise.jdx.dev/getting-started.html) et [Rokit](https://github.com/rojo-rbx/rokit#readme). Sous Windows, rouvrir le terminal après leur installation pour actualiser `PATH`. -# Development Tools +```powershell +mise install +rokit install +pnpm install --frozen-lockfile +pnpm check +pnpm build +pnpm serve +``` -ESLint config -ESLint -Husky -Renovate -pnpm +`mise` épingle Node.js 24.16.0, pnpm 11.21.0 et uv 0.13.0. `rokit` installe Rojo 7.7.1, Asphalt 2.0.2, darklua 0.19.0 et Mantle 0.11.18. `pnpm build` compile le TypeScript et crée `game.rbxl` avec Rojo. Ouvrir ce fichier dans Studio, ou utiliser `pnpm start` pour le flux [`rbxts-build`](https://github.com/osyrisrblx/rbxts-build#readme). `pnpm watch` lance la compilation et le serveur Rojo en continu. Le projet Rojo est [`default.project.json`](default.project.json) et les entrées client/serveur sont sous [`src`](src/). -# MCP +Autres commandes : `pnpm lint`, `pnpm format:check` et `pnpm format`. [Husky](https://typicode.github.io/husky/) installe un hook local `pre-commit` qui exécute `pnpm lint`. [Renovate](https://docs.renovatebot.com/) lit [`renovate.json`](renovate.json) une fois son application activée sur le dépôt distant. -https://www.blender.org/lab/mcp-server/ -https://create.roblox.com/docs/studio/mcp -https://create.roblox.com/docs/fr-fr/cloud/reference/features/creator-store +## Bibliothèques installées + +Chaque paquet de cette section est une dépendance directe. Les liens mènent à la documentation du projet ou à son README actuel ; les versions exécutables sont épinglées dans le lockfile. Les paquets ne sont pas automatiquement utilisés par le code de démarrage. + +- `@flamework/core` ([documentation](https://flamework.fireboltofdeath.dev/docs/introduction/)) — services, contrôleurs et injection ; le service de démarrage en donne un exemple. +- `@flamework/components` ([documentation](https://flamework.fireboltofdeath.dev/docs/additional-modules/components/creating-a-component/)) — composants d'instances. +- `@flamework/networking` ([documentation](https://flamework.fireboltofdeath.dev/docs/additional-modules/networking/introduction/)) — réseau typé. +- `@rbxts/react` ([documentation React Lua](https://github.com/jsdotlua/react-lua/blob/main/docs/api-reference/react.md), [liaison roblox-ts](https://github.com/littensy/rbxts-react#readme)) — interface React. +- `@rbxts/react-roblox` ([documentation ReactRoblox](https://github.com/jsdotlua/react-lua/blob/main/docs/api-reference/react-roblox.md), [liaison roblox-ts](https://github.com/littensy/rbxts-react#readme)) — rendu React dans Roblox. +- `@rbxts/reflex` ([documentation](https://littensy.github.io/reflex/docs/)) — état applicatif. +- `@rbxts/ripple` ([README et API](https://github.com/littensy/ripple#readme)) — animations. +- `@rbxts/services` ([README](https://github.com/roblox-ts/services#readme)) — accès typé aux services Roblox. +- `@rbxts/lapis` ([documentation](https://github.com/nezuo/lapis/tree/master/docs)) — persistance de données. +- `@rbxts/trove` ([README et API](https://github.com/Sleitnick/rbxts-trove#readme)) — nettoyage des ressources. +- `@rbxts/log` ([README](https://github.com/roblox-aurora/rbx-log#readme)) — journalisation. +- `@rbxts/sift` ([documentation](https://cxmeel.github.io/sift/)) — collections immuables ; l'ancien libellé `@csqrl/sift` n'est pas le nom npm utilisé ici. +- `@rbxts/t` ([README et API](https://github.com/osyrisrblx/t#readme)) — validation de valeurs à l'exécution. +- `@rbxts/cmdr` ([documentation](https://eryn.io/Cmdr/), [types roblox-ts](https://github.com/sasial-dev/Cmdr#readme)) — commandes de développement. +- `@rbxts/gamejoy` ([README](https://github.com/Rimuy/GameJoy#readme)) — contrôles de jeu. +- `@rbxts/tween` ([README et API](https://github.com/Validark/Roblox-TS-Libraries/blob/master/tween/README.md)) — interpolations. +- `@rbxts/timer` ([README](https://github.com/Bytebit-Org/roblox-Timer#readme)) — temporisation. +- `@rbxts/ui-labs` ([documentation](https://ui-labs.luau.page/docs/getstarted.html)) — prévisualisation d'interface. +- `@rbxts/sound-manager` ([documentation du projet](https://github.com/xelnae-r/Sound-Manager#readme)) — gestion des sons ; le README de la version 2.3.4 est dans `node_modules/@rbxts/sound-manager/README.md`. +- `@rbxts/object-cache` ([documentation amont](https://github.com/Pyseph/ObjectCache#readme)) — cache de pièces/objets ; le README de la version 1.4.5 est dans `node_modules/@rbxts/object-cache/README.md`. +- `@rbxts/net` ([README](https://github.com/roblox-aurora/rbx-net#readme)) — contient `ExperienceBroadcastEvent` pour les messages entre serveurs. +- `@rbxts-js/roblox-lua-promise` ([README](https://github.com/evaera/roblox-lua-promise#readme)) — implémentation de promesses ; les types globaux `Promise` sont fournis par `@rbxts/compiler-types`. + +## Outils initialisés + +- [roblox-ts](https://roblox-ts.com/docs/) : `roblox-ts`, `@rbxts/types`, `@rbxts/compiler-types` et le transformer Flamework sont configurés dans [`tsconfig.json`](tsconfig.json). +- [Rojo](https://rojo.space/docs/) : manifeste [`default.project.json`](default.project.json), `pnpm build` et `pnpm serve`. +- [rbxts-build](https://github.com/osyrisrblx/rbxts-build#readme) : commandes `pnpm start`, `pnpm watch`, `pnpm open`, `pnpm stop` et construction `game.rbxl`. +- [Asphalt](https://github.com/jackTabsCode/asphalt#readme) : binaire épinglé avec Rokit ; aucun asset à synchroniser dans ce template vierge. +- [darklua](https://darklua.com/docs/) : binaire épinglé avec Rokit ; aucune transformation de Lua requise pour le démarrage. +- [Mantle](https://mantledeploy.vercel.app/) : binaire épinglé avec Rokit ; le déploiement exige une expérience Roblox et des identifiants propres au projet. Son [dépôt](https://github.com/blake-mealey/mantle#readme) indique qu'il n'est plus maintenu. +- [mise](https://mise.jdx.dev/) et [pnpm](https://pnpm.io/) : versions du runtime et gestion reproductible des dépendances. +- [ESLint](https://eslint.org/docs/latest/) : configuration flat dans [`eslint.config.mjs`](eslint.config.mjs), avec [typescript-eslint](https://typescript-eslint.io/) et [Prettier](https://prettier.io/docs/). +- [Husky](https://typicode.github.io/husky/) : vérification Git locale. +- [Renovate](https://docs.renovatebot.com/) : règles de mise à jour des dépendances ; l'activation du bot sur l'hébergeur reste à faire. + +## MCP : Codex, Claude, Cursor et Antigravity + +Les deux serveurs sont définis dans les fichiers de projet suivants. Chaque client lit son propre fichier, et les processus sont lancés à la demande : + +| Client | Configuration dans ce dépôt | Référence du client | +| ----------- | ---------------------------------------------------- | ------------------------------------------------------------------ | +| Codex | [`.codex/config.toml`](.codex/config.toml) | [MCP Codex](https://learn.chatgpt.com/docs/extend/mcp?surface=cli) | +| Claude Code | [`.mcp.json`](.mcp.json) | [MCP Claude Code](https://code.claude.com/docs/en/mcp) | +| Cursor | [`.cursor/mcp.json`](.cursor/mcp.json) | [MCP Cursor](https://prod.cursor.com/help/customization/mcp) | +| Antigravity | [`.agents/mcp_config.json`](.agents/mcp_config.json) | [MCP Antigravity](https://www.antigravity.google/docs/mcp) | + +- **Roblox Studio MCP** ([documentation officielle](https://create.roblox.com/docs/studio/mcp)) : les configurations lancent `%LOCALAPPDATA%\Roblox\mcp.bat` via `cmd.exe`. Ce lanceur est fourni par Studio sous Windows. Démarrer Studio, activer son serveur MCP dans les paramètres Assistant/MCP, puis redémarrer le client d'agent si nécessaire. Le fichier `mcp.bat` est présent sur la machine d'initialisation ; une connexion à une session Studio ouverte reste à vérifier. +- **Blender MCP** ([documentation officielle](https://www.blender.org/lab/mcp-server/)) : les configurations lancent `blender-mcp` depuis le dépôt officiel Blender Lab, épinglé sur `v1.0.3`, au moyen de `uvx`. Blender 5.1+ et l'extension MCP doivent être installés et actifs. Sur la machine d'initialisation, Blender 5.2.2 LTS et l'extension 1.0.3 ont été installés ; le serveur se lance et le pont de l'extension a été vu à l'écoute sur `localhost:9876`. Un appel MCP complet depuis un client reste à vérifier. Rouvrir les clients après installation de `uvx` pour qu'ils héritent du nouveau `PATH`. + +Le serveur Blender peut exécuter du code Python dans Blender : utiliser uniquement des projets de confiance. Les configurations n'incluent aucun secret. + +L'[API Cloud Creator Store](https://create.roblox.com/docs/cloud/reference/features/creator-store) est une API Roblox distincte des serveurs MCP ci-dessus. Elle n'est pas activée ici : son usage nécessite une clé et un cas d'utilisation précis. + +## Vérification du template + +Sur Windows, l'installation verrouillée, `pnpm check`, `pnpm format:check`, `pnpm build` et `pnpm start -- --no-watch` ont réussi. `pnpm build` produit `game.rbxl` et `pnpm start` a lancé Roblox Studio. `codex mcp list` reconnaît les deux serveurs. Un test de jeu dans Studio et un appel MCP complet restent à effectuer. From b8228a73beb7ae9db2b07ea4fc0b27580c098839 Mon Sep 17 00:00:00 2001 From: Infuseting Date: Tue, 13 Oct 2026 20:27:21 +0200 Subject: [PATCH 4/6] feat(skill): add local Blender asset workflow --- .agents/skills/blender-game-asset/SKILL.md | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .agents/skills/blender-game-asset/SKILL.md diff --git a/.agents/skills/blender-game-asset/SKILL.md b/.agents/skills/blender-game-asset/SKILL.md new file mode 100644 index 0000000..b667439 --- /dev/null +++ b/.agents/skills/blender-game-asset/SKILL.md @@ -0,0 +1,28 @@ +--- +name: blender-game-asset +description: Créer ou modifier avec Blender un modèle 3D texturé pour ce jeu Roblox et ranger les livrables dans assets/. À utiliser pour les armes, personnages, décors, objets et leurs textures ; les scripts de fabrication ne sont pas conservés. +--- + +# Asset 3D Blender pour Roblox + +Produire un asset utilisable dans le jeu, avec une source Blender modifiable, un export adapté à Roblox Studio et les textures nécessaires. Adapter le style, les dimensions et la complexité à la demande et aux assets déjà présents. + +## Rangement + +- Modèle et source : `assets/models//.blend` et `assets/models//.fbx`. Utiliser un nom court en kebab-case et garder les fichiers liés à un même modèle dans ce dossier. +- Textures finales : `assets/textures//`, par exemple `_albedo.png`. Garder aussi une copie empaquetée dans le `.blend` pour que la source reste autonome. +- Si la demande comprend des effets, sons, animations ou images d'interface, les ranger respectivement dans `assets/vfx/`, `assets/sfx/`, `assets/animations/` ou `assets/ui/`. Ne pas créer ces éléments par défaut pour un simple modèle 3D. +- Un export `.gltf` et son `.bin` peuvent accompagner le `.fbx` si utiles ; vérifier que leurs chemins de texture fonctionnent depuis le rangement final. Roblox Studio accepte `.fbx`, `.gltf` et `.obj` pour les maillages selon la [documentation de l'Importer](https://create.roblox.com/docs/studio/importer). + +## Création + +1. Examiner les références fournies, les assets voisins et la place prévue dans le jeu. Sans cahier des charges détaillé, choisir une échelle et un niveau de détail raisonnables pour Roblox et expliquer les choix importants. +2. Créer et texturer dans Blender. Utiliser le MCP Blender s'il est connecté et adapté à l'opération, ou l'exécutable Blender avec `bpy` pour une génération reproductible. Ne pas présenter le MCP comme utilisé si le travail est passé par la ligne de commande. +3. Si du code temporaire sert à construire le modèle, dessiner la texture, exporter ou valider, le créer dans un répertoire temporaire hors du dépôt. Ne placer aucun `generate.py`, script de texture, notebook ou autre code de fabrication dans `assets/` ou dans ce skill. Supprimer uniquement ces fichiers temporaires créés pour la tâche une fois les livrables vérifiés. Préserver tout script préexistant appartenant à l'utilisateur. +4. Conserver le `.blend`, l'export et les textures finales. Une image d'aperçu est utile pour la revue ; la ranger avec le modèle si elle est créée. Nettoyer seulement les sauvegardes et rendus intermédiaires issus de la tâche. + +## Vérification et livraison + +- Ouvrir ou réimporter l'export dans Blender ; vérifier géométrie, UV, orientation, échelle, matériau et présence effective des textures. Inspecter visuellement un rendu ou une capture du modèle final. +- Si Roblox Studio est disponible, tester l'import dans Studio. Un contrôle Blender seul ne prouve pas que l'asset est utilisable dans Studio ; indiquer clairement ce qui a été testé. Ne pas publier ou téléverser sur un compte Roblox sans demande explicite. +- Donner les liens vers la source, l'export, les textures et l'aperçu. Préciser si un MCP a été utilisé et confirmer que les scripts temporaires de fabrication n'ont pas été conservés. From fcdd2d7a501d40caae8df637ab6f8d04f0bae1a3 Mon Sep 17 00:00:00 2001 From: Infuseting Date: Tue, 13 Oct 2026 20:40:37 +0200 Subject: [PATCH 5/6] feat: append of features skilled --- .agents/skills/roblox-feature-docs/SKILL.md | 27 +++++++++++++++++++++ readme.md | 10 +++++--- 2 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 .agents/skills/roblox-feature-docs/SKILL.md diff --git a/.agents/skills/roblox-feature-docs/SKILL.md b/.agents/skills/roblox-feature-docs/SKILL.md new file mode 100644 index 0000000..17ea4fc --- /dev/null +++ b/.agents/skills/roblox-feature-docs/SKILL.md @@ -0,0 +1,27 @@ +--- +name: roblox-feature-docs +description: Guider toute nouvelle fonctionnalité de ce projet roblox-ts en comparant les bibliothèques installées, leurs documentations actuelles et les API Roblox avant l'implémentation. +--- + +# Fonctionnalités guidées par la documentation + +Pour chaque fonctionnalité, choisir la solution la plus adaptée au comportement demandé et aux versions réellement présentes dans ce dépôt. Le but est un code actuel, compatible et vérifié, sans ajouter une bibliothèque simplement parce qu'elle existe dans le template. + +## Chercher avant de choisir + +1. Comprendre le comportement attendu, les contraintes du jeu et l'emplacement client, serveur ou partagé. Examiner le code existant pour conserver ses conventions et éviter un second système pour le même rôle. +2. Consulter `package.json` et `pnpm-lock.yaml` pour les versions installées. Utiliser la section « Bibliothèques installées » du `readme.md` comme index de documentation, puis ouvrir seulement les sources pertinentes pour la fonctionnalité. Le README est un point d'entrée, pas une preuve que son lien décrit la version installée. +3. Vérifier les API et recommandations auprès des sources primaires actuelles : documentation officielle de Roblox pour le moteur, documentation, README, notes de version ou code du mainteneur pour une bibliothèque. Quand la documentation en ligne suit `main` ou la dernière version, chercher le tag correspondant à la version verrouillée et contrôler les déclarations/types dans `node_modules` avant d'écrire le code. Ne pas déduire une API du seul nom du paquet. + Si les sources en ligne sont indisponibles, utiliser les sources et types locaux et signaler que l'actualité de la recommandation n'a pas pu être vérifiée. +4. Comparer l'API Roblox native et les bibliothèques installées qui couvrent réellement le besoin. Pour des rôles qui se recoupent, comme le réseau (`@flamework/networking` et `@rbxts/net`) ou les interpolations (`@rbxts/ripple` et `@rbxts/tween`), choisir une approche cohérente avec le reste du projet et expliquer brièvement le critère décisif. Considérer compatibilité, maintenance, typage, coût d'intégration et comportement à l'exécution. Si rien d'installé ne convient, examiner une alternative maintenue avant d'ajouter une dépendance. + +## Implémenter avec la bonne version + +- Employer l'API confirmée pour la version installée. Si une meilleure approche exige une version plus récente, vérifier les changements et la compatibilité ; mettre à jour la dépendance et le lockfile lorsque cette évolution sert la fonctionnalité, puis valider la migration. Ne pas copier aveuglément un exemple de la dernière documentation dans un projet épinglé sur une version antérieure. +- Garder les responsabilités Roblox au bon endroit : décisions de jeu sensibles côté serveur, interface et effets locaux côté client, contrats partagés explicites. Valider les données qui traversent le réseau et libérer connexions, tâches et instances selon le cycle de vie de la fonctionnalité. +- Ajouter uniquement les dépendances nécessaires. Préférer les primitives du moteur ou les systèmes déjà adoptés quand ils répondent aussi bien au besoin. + +## Vérifier et rendre compte + +- Pour une modification de code, exécuter au minimum `pnpm check`, `pnpm format:check` et `pnpm build`. Ajouter des tests ciblés lorsqu'ils vérifient un comportement significatif ; tester aussi dans Roblox Studio quand le résultat dépend du moteur ou de l'expérience de jeu. +- Dans le compte rendu, nommer l'approche retenue, les alternatives réellement pertinentes, les versions et liens de documentation consultés, les vérifications effectuées et ce qui reste non vérifié. Distinguer une recommandation fondée sur la documentation d'un comportement observé en jeu. diff --git a/readme.md b/readme.md index 789ddf5..59d5d44 100644 --- a/readme.md +++ b/readme.md @@ -34,17 +34,19 @@ Chaque paquet de cette section est une dépendance directe. Les liens mènent à - `@rbxts/lapis` ([documentation](https://github.com/nezuo/lapis/tree/master/docs)) — persistance de données. - `@rbxts/trove` ([README et API](https://github.com/Sleitnick/rbxts-trove#readme)) — nettoyage des ressources. - `@rbxts/log` ([README](https://github.com/roblox-aurora/rbx-log#readme)) — journalisation. -- `@rbxts/sift` ([documentation](https://cxmeel.github.io/sift/)) — collections immuables ; l'ancien libellé `@csqrl/sift` n'est pas le nom npm utilisé ici. +- `@rbxts/sift` ([documentation](https://cxmeel.github.io/sift/)) — collections immuables - `@rbxts/t` ([README et API](https://github.com/osyrisrblx/t#readme)) — validation de valeurs à l'exécution. - `@rbxts/cmdr` ([documentation](https://eryn.io/Cmdr/), [types roblox-ts](https://github.com/sasial-dev/Cmdr#readme)) — commandes de développement. - `@rbxts/gamejoy` ([README](https://github.com/Rimuy/GameJoy#readme)) — contrôles de jeu. - `@rbxts/tween` ([README et API](https://github.com/Validark/Roblox-TS-Libraries/blob/master/tween/README.md)) — interpolations. - `@rbxts/timer` ([README](https://github.com/Bytebit-Org/roblox-Timer#readme)) — temporisation. - `@rbxts/ui-labs` ([documentation](https://ui-labs.luau.page/docs/getstarted.html)) — prévisualisation d'interface. -- `@rbxts/sound-manager` ([documentation du projet](https://github.com/xelnae-r/Sound-Manager#readme)) — gestion des sons ; le README de la version 2.3.4 est dans `node_modules/@rbxts/sound-manager/README.md`. -- `@rbxts/object-cache` ([documentation amont](https://github.com/Pyseph/ObjectCache#readme)) — cache de pièces/objets ; le README de la version 1.4.5 est dans `node_modules/@rbxts/object-cache/README.md`. +- `@rbxts/sound-manager` ([documentation du projet](https://github.com/xelnae-r/Sound-Manager#readme)) — gestion des sons +- `@rbxts/object-cache` ([documentation amont](https://github.com/Pyseph/ObjectCache#readme)) — cache de pièces/objets - `@rbxts/net` ([README](https://github.com/roblox-aurora/rbx-net#readme)) — contient `ExperienceBroadcastEvent` pour les messages entre serveurs. -- `@rbxts-js/roblox-lua-promise` ([README](https://github.com/evaera/roblox-lua-promise#readme)) — implémentation de promesses ; les types globaux `Promise` sont fournis par `@rbxts/compiler-types`. +- `@rbxts-js/roblox-lua-promise` ([README](https://github.com/evaera/roblox-lua-promise#readme)) — implémentation de promesses + +Pour choisir une approche lors d'une nouvelle fonctionnalité, le skill local [`roblox-feature-docs`](.agents/skills/roblox-feature-docs/SKILL.md) compare les bibliothèques pertinentes, leurs documentations et les API réellement disponibles dans les versions verrouillées. ## Outils initialisés From 76d200f0688341864fbf0fd14fdb842f04dcedff Mon Sep 17 00:00:00 2001 From: Infuseting Date: Wed, 14 Oct 2026 09:16:43 +0200 Subject: [PATCH 6/6] feat(template): initialize libraries and release v1.0.0 --- flamework.build | 4 +- package.json | 5 +- readme.md | 29 ++++----- scripts/cmdr-permissions.test.mjs | 61 +++++++++++++++++++ scripts/setup.ps1 | 73 +++++++++++++++++++++++ src/client/controllers/cmdr-controller.ts | 30 ++++++++++ src/client/main.client.ts | 7 ++- src/server/config/cmdr-permissions.ts | 27 +++++++++ src/server/main.server.ts | 6 +- src/server/services/cmdr-service.ts | 43 +++++++++++++ src/server/services/startup-service.ts | 3 +- src/shared/cmdr.ts | 2 + src/shared/logging.ts | 12 ++++ 13 files changed, 278 insertions(+), 24 deletions(-) create mode 100644 scripts/cmdr-permissions.test.mjs create mode 100644 scripts/setup.ps1 create mode 100644 src/client/controllers/cmdr-controller.ts create mode 100644 src/server/config/cmdr-permissions.ts create mode 100644 src/server/services/cmdr-service.ts create mode 100644 src/shared/cmdr.ts create mode 100644 src/shared/logging.ts diff --git a/flamework.build b/flamework.build index 3a63735..4e97ac4 100644 --- a/flamework.build +++ b/flamework.build @@ -2,7 +2,9 @@ "version": 1, "flameworkVersion": "1.3.2", "identifiers": { - "crownstudio-roblox-ts-template:out/server/services/startup-service@StartupService": "server/services/startup-service@StartupService" + "crownstudio-roblox-ts-template:out/server/services/startup-service@StartupService": "server/services/startup-service@StartupService", + "crownstudio-roblox-ts-template:out/client/controllers/cmdr-controller@CmdrController": "client/controllers/cmdr-controller@CmdrController", + "crownstudio-roblox-ts-template:out/server/services/cmdr-service@CmdrService": "server/services/cmdr-service@CmdrService" }, "metadata": {} } \ No newline at end of file diff --git a/package.json b/package.json index 53098e3..d6c8229 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "crownstudio-roblox-ts-template", - "version": "0.1.0", + "version": "1.0.0", "private": true, "packageManager": "pnpm@11.21.0", "engines": { @@ -18,7 +18,8 @@ "lint": "eslint .", "format:check": "prettier --check .", "format": "prettier --write .", - "check": "pnpm lint && pnpm compile", + "test:cmdr": "node --test scripts/cmdr-permissions.test.mjs", + "check": "pnpm lint && pnpm test:cmdr && pnpm compile", "prepare": "husky" }, "rbxts-build": { diff --git a/readme.md b/readme.md index 59d5d44..afa2184 100644 --- a/readme.md +++ b/readme.md @@ -4,24 +4,19 @@ Template Roblox TypeScript avec Flamework, Rojo et une sélection de bibliothèq ## Démarrage -Prérequis : [Roblox Studio](https://create.roblox.com/docs/studio/setup), [mise](https://mise.jdx.dev/getting-started.html) et [Rokit](https://github.com/rojo-rbx/rokit#readme). Sous Windows, rouvrir le terminal après leur installation pour actualiser `PATH`. +Sous Windows 10/11, lancer depuis le dépôt : ```powershell -mise install -rokit install -pnpm install --frozen-lockfile -pnpm check -pnpm build -pnpm serve +powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\setup.ps1 ``` -`mise` épingle Node.js 24.16.0, pnpm 11.21.0 et uv 0.13.0. `rokit` installe Rojo 7.7.1, Asphalt 2.0.2, darklua 0.19.0 et Mantle 0.11.18. `pnpm build` compile le TypeScript et crée `game.rbxl` avec Rojo. Ouvrir ce fichier dans Studio, ou utiliser `pnpm start` pour le flux [`rbxts-build`](https://github.com/osyrisrblx/rbxts-build#readme). `pnpm watch` lance la compilation et le serveur Rojo en continu. Le projet Rojo est [`default.project.json`](default.project.json) et les entrées client/serveur sont sous [`src`](src/). - -Autres commandes : `pnpm lint`, `pnpm format:check` et `pnpm format`. [Husky](https://typicode.github.io/husky/) installe un hook local `pre-commit` qui exécute `pnpm lint`. [Renovate](https://docs.renovatebot.com/) lit [`renovate.json`](renovate.json) une fois son application activée sur le dépôt distant. +Le [script d'installation](scripts/setup.ps1) nécessite WinGet (App Installer du Microsoft Store). Il installe Git, [mise](https://mise.jdx.dev/installing-mise.html), Rokit, Roblox Studio et Blender via WinGet, puis les versions définies dans `mise.toml` et `rokit.toml`, et les dépendances pnpm verrouillées. Il termine par `pnpm check` et `pnpm build` et s'arrête dès qu'une commande échoue. Windows peut demander une élévation pour certains installateurs. Une relance conserve les applications déjà installées ; elle ne les met pas à jour automatiquement. Les applications WinGet utilisent la version disponible dans son catalogue, les outils du projet restent épinglés. ## Bibliothèques installées -Chaque paquet de cette section est une dépendance directe. Les liens mènent à la documentation du projet ou à son README actuel ; les versions exécutables sont épinglées dans le lockfile. Les paquets ne sont pas automatiquement utilisés par le code de démarrage. +Chaque paquet de cette section est une dépendance directe. Les liens mènent à la documentation du projet ou à son README actuel ; les versions exécutables sont épinglées dans le lockfile. Les 22 bibliothèques ont été examinées : Flamework, son gestionnaire de composants, Cmdr et le logger sont branchés au démarrage ; les autres bibliothèques s'utilisent à l'import ou se configurent avec les données de leur fonctionnalité. + +**Console Cmdr** : en mode Play dans Studio, ouvrir avec **F2**. Les commandes intégrées sont enregistrées et protégées côté serveur. Hors Studio, seuls le propriétaire individuel de l'expérience et les UserIds renseignés dans [`cmdr-permissions.ts`](src/server/config/cmdr-permissions.ts) sont autorisés. Pour une expérience de groupe, renseigner les administrateurs explicitement. `pnpm test:cmdr` vérifie cette politique ; ce test fait aussi partie de `pnpm check`. La configuration client est dans [`cmdr-controller.ts`](src/client/controllers/cmdr-controller.ts). - `@flamework/core` ([documentation](https://flamework.fireboltofdeath.dev/docs/introduction/)) — services, contrôleurs et injection ; le service de démarrage en donne un exemple. - `@flamework/components` ([documentation](https://flamework.fireboltofdeath.dev/docs/additional-modules/components/creating-a-component/)) — composants d'instances. @@ -34,17 +29,17 @@ Chaque paquet de cette section est une dépendance directe. Les liens mènent à - `@rbxts/lapis` ([documentation](https://github.com/nezuo/lapis/tree/master/docs)) — persistance de données. - `@rbxts/trove` ([README et API](https://github.com/Sleitnick/rbxts-trove#readme)) — nettoyage des ressources. - `@rbxts/log` ([README](https://github.com/roblox-aurora/rbx-log#readme)) — journalisation. -- `@rbxts/sift` ([documentation](https://cxmeel.github.io/sift/)) — collections immuables +- `@rbxts/sift` ([documentation](https://cxmeel.github.io/sift/)) — collections immuables - `@rbxts/t` ([README et API](https://github.com/osyrisrblx/t#readme)) — validation de valeurs à l'exécution. - `@rbxts/cmdr` ([documentation](https://eryn.io/Cmdr/), [types roblox-ts](https://github.com/sasial-dev/Cmdr#readme)) — commandes de développement. - `@rbxts/gamejoy` ([README](https://github.com/Rimuy/GameJoy#readme)) — contrôles de jeu. - `@rbxts/tween` ([README et API](https://github.com/Validark/Roblox-TS-Libraries/blob/master/tween/README.md)) — interpolations. - `@rbxts/timer` ([README](https://github.com/Bytebit-Org/roblox-Timer#readme)) — temporisation. - `@rbxts/ui-labs` ([documentation](https://ui-labs.luau.page/docs/getstarted.html)) — prévisualisation d'interface. -- `@rbxts/sound-manager` ([documentation du projet](https://github.com/xelnae-r/Sound-Manager#readme)) — gestion des sons -- `@rbxts/object-cache` ([documentation amont](https://github.com/Pyseph/ObjectCache#readme)) — cache de pièces/objets +- `@rbxts/sound-manager` ([documentation du projet](https://github.com/xelnae-r/Sound-Manager#readme)) — gestion des sons +- `@rbxts/object-cache` ([documentation amont](https://github.com/Pyseph/ObjectCache#readme)) — cache de pièces/objets - `@rbxts/net` ([README](https://github.com/roblox-aurora/rbx-net#readme)) — contient `ExperienceBroadcastEvent` pour les messages entre serveurs. -- `@rbxts-js/roblox-lua-promise` ([README](https://github.com/evaera/roblox-lua-promise#readme)) — implémentation de promesses +- `@rbxts-js/roblox-lua-promise` ([README](https://github.com/evaera/roblox-lua-promise#readme)) — implémentation de promesses Pour choisir une approche lors d'une nouvelle fonctionnalité, le skill local [`roblox-feature-docs`](.agents/skills/roblox-feature-docs/SKILL.md) compare les bibliothèques pertinentes, leurs documentations et les API réellement disponibles dans les versions verrouillées. @@ -79,6 +74,4 @@ Le serveur Blender peut exécuter du code Python dans Blender : utiliser uniquem L'[API Cloud Creator Store](https://create.roblox.com/docs/cloud/reference/features/creator-store) est une API Roblox distincte des serveurs MCP ci-dessus. Elle n'est pas activée ici : son usage nécessite une clé et un cas d'utilisation précis. -## Vérification du template - -Sur Windows, l'installation verrouillée, `pnpm check`, `pnpm format:check`, `pnpm build` et `pnpm start -- --no-watch` ont réussi. `pnpm build` produit `game.rbxl` et `pnpm start` a lancé Roblox Studio. `codex mcp list` reconnaît les deux serveurs. Un test de jeu dans Studio et un appel MCP complet restent à effectuer. +Version du template : 1.0.0 diff --git a/scripts/cmdr-permissions.test.mjs b/scripts/cmdr-permissions.test.mjs new file mode 100644 index 0000000..08a405f --- /dev/null +++ b/scripts/cmdr-permissions.test.mjs @@ -0,0 +1,61 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import test from "node:test"; +import { URL } from "node:url"; +import { runInNewContext } from "node:vm"; +import ts from "typescript"; + +// Run the actual, engine-independent permission policy, not a duplicate of it. +const source = readFileSync( + new URL("../src/server/config/cmdr-permissions.ts", import.meta.url), + "utf8", +); +const compiled = ts.transpileModule(source, { + compilerOptions: { module: ts.ModuleKind.CommonJS }, +}).outputText; +const policyModule = { exports: {} }; +runInNewContext(compiled, { + exports: policyModule.exports, + module: policyModule, +}); +const { isCmdrAllowed, CMDR_PERMISSIONS } = policyModule.exports; + +test("published game denies ordinary players by default", () => { + assert.equal(isCmdrAllowed(42, false, 99), false); +}); + +test("Studio allows local testing without publishing or configuring an admin", () => { + assert.equal(isCmdrAllowed(-1, true, undefined), true); +}); + +test("experience owner is allowed in a user-owned published game", () => { + assert.equal(isCmdrAllowed(99, false, 99), true); +}); + +test("no user owner means no automatic permission in a group-owned game", () => { + assert.equal(isCmdrAllowed(99, false, undefined), false); +}); + +test("only explicitly listed users get allowlist access", () => { + const permissions = { + allowInStudio: false, + allowExperienceOwner: false, + adminUserIds: new Set([42]), + }; + assert.equal(isCmdrAllowed(42, false, undefined, permissions), true); + assert.equal(isCmdrAllowed(43, false, undefined, permissions), false); +}); + +test("Studio and owner exceptions can both be disabled", () => { + const permissions = { + allowInStudio: false, + allowExperienceOwner: false, + adminUserIds: new Set(), + }; + assert.equal(isCmdrAllowed(99, true, 99, permissions), false); + assert.equal(isCmdrAllowed(99, false, 99, permissions), false); +}); + +test("published default configuration contains no placeholder administrator", () => { + assert.equal(CMDR_PERMISSIONS.adminUserIds.size, 0); +}); diff --git a/scripts/setup.ps1 b/scripts/setup.ps1 new file mode 100644 index 0000000..723392a --- /dev/null +++ b/scripts/setup.ps1 @@ -0,0 +1,73 @@ +#Requires -Version 5.1 +[CmdletBinding()] +param( + [switch]$DryRun, + [switch]$SkipChecks +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version Latest +$projectRoot = Split-Path -Parent $PSScriptRoot + +function Invoke-Tool { + param([string]$Name, [string[]]$Arguments) + Write-Host "> $Name $($Arguments -join ' ')" + if ($DryRun) { return } + & $Name @Arguments + if ($LASTEXITCODE -ne 0) { + throw "$Name a echoue (code $LASTEXITCODE). Corriger l'erreur puis relancer le script." + } +} + +function Update-SessionPath { + $machinePath = [Environment]::GetEnvironmentVariable('Path', 'Machine') + $userPath = [Environment]::GetEnvironmentVariable('Path', 'User') + $env:Path = "$machinePath;$userPath;$env:Path;$env:USERPROFILE\.rokit\bin" +} + +function Install-Package { + param([string]$Id) + if (-not $DryRun) { + & winget list --id $Id --exact --source winget --accept-source-agreements --disable-interactivity + if ($LASTEXITCODE -eq 0) { + Write-Host "$Id est deja installe." + return + } + # APP_NOT_INSTALLED: other errors must not be mistaken for a missing package. + if ($LASTEXITCODE -ne -1978335212) { + throw "Impossible de verifier $Id avec winget (code $LASTEXITCODE)." + } + } + Invoke-Tool 'winget' @('install', '--id', $Id, '--exact', '--source', 'winget', '--silent', '--accept-package-agreements', '--accept-source-agreements', '--disable-interactivity') + if (-not $DryRun) { Update-SessionPath } +} + +Push-Location $projectRoot +try { + if (-not $DryRun -and -not (Get-Command winget -ErrorAction SilentlyContinue)) { + throw "Installer App Installer (winget) depuis le Microsoft Store, puis relancer ce script." + } + + foreach ($package in @('Git.Git', 'jdx.mise', 'Rojo.Rokit', 'Roblox.RobloxStudio', 'BlenderFoundation.Blender')) { + Install-Package $package + } + + Invoke-Tool 'mise' @('trust', 'mise.toml') + Invoke-Tool 'mise' @('install') + Invoke-Tool 'rokit' @('install') + Invoke-Tool 'mise' @('exec', '--', 'pnpm', 'install', '--frozen-lockfile') + if (-not $SkipChecks) { + Invoke-Tool 'mise' @('exec', '--', 'pnpm', 'check') + Invoke-Tool 'mise' @('exec', '--', 'pnpm', 'build') + } + + if ($DryRun) { + Write-Host 'Apercu termine : aucune installation effectuee.' + } else { + Write-Host 'Installation terminee. Rouvrir le terminal et les clients MCP.' + Write-Host 'Pour les commandes pnpm/uvx, utiliser mise exec -- ou activer mise dans le shell.' + Write-Host 'Activer le MCP dans Studio et installer/activer l extension MCP officielle dans Blender (voir README).' + } +} finally { + Pop-Location +} diff --git a/src/client/controllers/cmdr-controller.ts b/src/client/controllers/cmdr-controller.ts new file mode 100644 index 0000000..885ee02 --- /dev/null +++ b/src/client/controllers/cmdr-controller.ts @@ -0,0 +1,30 @@ +import { Controller, OnStart } from "@flamework/core"; +import { CmdrClient } from "@rbxts/cmdr"; +import { Players } from "@rbxts/services"; +import { Trove } from "@rbxts/trove"; +import { CMDR_ACCESS_ATTRIBUTE } from "shared/cmdr"; +import { TEMPLATE_NAME } from "shared/template-name"; + +@Controller() +export class CmdrController implements OnStart { + private readonly trove = new Trove(); + + public onStart(): void { + CmdrClient.SetActivationKeys([Enum.KeyCode.F2]); + CmdrClient.SetPlaceName(TEMPLATE_NAME); + CmdrClient.SetActivationUnlocksMouse(true); + this.trove.connect(Players.LocalPlayer.GetAttributeChangedSignal(CMDR_ACCESS_ATTRIBUTE), () => + this.updateAccess(), + ); + this.trove.attachToInstance(script); + this.updateAccess(); + } + + private updateAccess(): void { + const enabled = Players.LocalPlayer.GetAttribute(CMDR_ACCESS_ATTRIBUTE) === true; + CmdrClient.SetEnabled(enabled); + if (!enabled) { + CmdrClient.Hide(); + } + } +} diff --git a/src/client/main.client.ts b/src/client/main.client.ts index 5017629..877d9e6 100644 --- a/src/client/main.client.ts +++ b/src/client/main.client.ts @@ -1,5 +1,10 @@ import { Flamework } from "@flamework/core"; +import "@flamework/components"; +import Log from "@rbxts/log"; +import { initializeLogging } from "shared/logging"; import { TEMPLATE_NAME } from "shared/template-name"; +initializeLogging(); +Flamework.addPaths("src/client/controllers"); Flamework.ignite(); -print(`${TEMPLATE_NAME} ready (client)`); +Log.Info("{Template} ready (client)", TEMPLATE_NAME); diff --git a/src/server/config/cmdr-permissions.ts b/src/server/config/cmdr-permissions.ts new file mode 100644 index 0000000..08a6085 --- /dev/null +++ b/src/server/config/cmdr-permissions.ts @@ -0,0 +1,27 @@ +export interface CmdrPermissions { + readonly allowInStudio: boolean; + readonly allowExperienceOwner: boolean; + readonly adminUserIds: ReadonlySet; +} + +export const CMDR_PERMISSIONS: CmdrPermissions = { + allowInStudio: true, + allowExperienceOwner: true, + // Add individual Roblox UserIds here, especially for group-owned experiences. + adminUserIds: new Set(), +}; + +export function isCmdrAllowed( + userId: number, + isStudio: boolean, + experienceOwnerUserId: number | undefined, + permissions: CmdrPermissions = CMDR_PERMISSIONS, +): boolean { + return ( + (isStudio && permissions.allowInStudio) || + permissions.adminUserIds.has(userId) || + (permissions.allowExperienceOwner && + experienceOwnerUserId !== undefined && + userId === experienceOwnerUserId) + ); +} diff --git a/src/server/main.server.ts b/src/server/main.server.ts index 6e21d2b..fcdb2b7 100644 --- a/src/server/main.server.ts +++ b/src/server/main.server.ts @@ -1,6 +1,10 @@ import { Flamework } from "@flamework/core"; +import "@flamework/components"; +import Log from "@rbxts/log"; +import { initializeLogging } from "shared/logging"; import { TEMPLATE_NAME } from "shared/template-name"; +initializeLogging(); Flamework.addPaths("src/server/services"); Flamework.ignite(); -print(`${TEMPLATE_NAME} ready (server)`); +Log.Info("{Template} ready (server)", TEMPLATE_NAME); diff --git a/src/server/services/cmdr-service.ts b/src/server/services/cmdr-service.ts new file mode 100644 index 0000000..16177d6 --- /dev/null +++ b/src/server/services/cmdr-service.ts @@ -0,0 +1,43 @@ +import { OnStart, Service } from "@flamework/core"; +import { Cmdr } from "@rbxts/cmdr"; +import Log from "@rbxts/log"; +import { Players, RunService } from "@rbxts/services"; +import { Trove } from "@rbxts/trove"; +import { isCmdrAllowed } from "server/config/cmdr-permissions"; +import { CMDR_ACCESS_ATTRIBUTE } from "shared/cmdr"; + +@Service() +export class CmdrService implements OnStart { + private readonly trove = new Trove(); + + public onStart(): void { + // Register authorization before exposing the built-in commands. + Cmdr.Registry.RegisterHook("BeforeRun", (context) => { + if (!this.canUseConsole(context.Executor)) { + return "You do not have permission to run this command."; + } + }); + Cmdr.Registry.RegisterDefaultCommands(); + + this.trove.connect(Players.PlayerAdded, (player) => { + this.updateAccess(player); + }); + for (const player of Players.GetPlayers()) { + this.updateAccess(player); + } + game.BindToClose(() => this.trove.destroy()); + Log.Info("Cmdr ready with {CommandCount} commands", Cmdr.Registry.GetCommands().size()); + } + + private canUseConsole(player: Player): boolean { + return isCmdrAllowed( + player.UserId, + RunService.IsStudio(), + game.CreatorType === Enum.CreatorType.User ? game.CreatorId : undefined, + ); + } + + private updateAccess(player: Player): void { + player.SetAttribute(CMDR_ACCESS_ATTRIBUTE, this.canUseConsole(player)); + } +} diff --git a/src/server/services/startup-service.ts b/src/server/services/startup-service.ts index 2cc86db..a7c7164 100644 --- a/src/server/services/startup-service.ts +++ b/src/server/services/startup-service.ts @@ -1,8 +1,9 @@ import { OnStart, Service } from "@flamework/core"; +import Log from "@rbxts/log"; @Service() export class StartupService implements OnStart { public onStart(): void { - print("CrownStudio template ready (server)"); + Log.Debug("StartupService started"); } } diff --git a/src/shared/cmdr.ts b/src/shared/cmdr.ts new file mode 100644 index 0000000..6eb092a --- /dev/null +++ b/src/shared/cmdr.ts @@ -0,0 +1,2 @@ +// This attribute controls visibility only. Authorization is checked on the server. +export const CMDR_ACCESS_ATTRIBUTE = "CmdrEnabled"; diff --git a/src/shared/logging.ts b/src/shared/logging.ts new file mode 100644 index 0000000..05c59ff --- /dev/null +++ b/src/shared/logging.ts @@ -0,0 +1,12 @@ +import Log, { LogLevel } from "@rbxts/log"; +import { RunService } from "@rbxts/services"; + +export function initializeLogging(): void { + Log.SetLogger( + Log.Configure() + .SetMinLogLevel(RunService.IsStudio() ? LogLevel.Debugging : LogLevel.Information) + .EnrichWithProperty("Side", RunService.IsServer() ? "server" : "client") + .WriteTo(Log.RobloxOutput()) + .Create(), + ); +}