forked from Crown-Studio/roblox-ts-template
feat(deploy): replace mantle with bedrock and local state
This commit is contained in:
parent
4d6f8b57b2
commit
b9c15be3d5
|
|
@ -0,0 +1,2 @@
|
|||
ASPHALT_API_KEY=
|
||||
BEDROCK_API_KEY=
|
||||
|
|
@ -9,3 +9,4 @@ game.rbxl
|
|||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
.bedrock/state/
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
assets/**/*.gltf
|
||||
.bedrock/state/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"codegen": {
|
||||
"enabled": false
|
||||
},
|
||||
"environments": {
|
||||
"production": {
|
||||
"universe": {
|
||||
"universeId": "REPLACE_WITH_UNIVERSE_ID"
|
||||
},
|
||||
"places": {
|
||||
"start": {
|
||||
"placeId": "REPLACE_WITH_PLACE_ID"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"places": {
|
||||
"start": {
|
||||
"filePath": "game.rbxl"
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
"backend": "local",
|
||||
"directory": ".bedrock/state"
|
||||
},
|
||||
"plugins": ["./scripts/bedrock-local-state.mjs"]
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import prettier from "eslint-config-prettier";
|
|||
import tseslint from "typescript-eslint";
|
||||
|
||||
export default tseslint.config(
|
||||
{ ignores: ["node_modules/**", "out/**", "include/**", "build.rbxlx"] },
|
||||
{ ignores: ["node_modules/**", "out/**", "include/**", "build.rbxlx", ".bedrock/state/**"] },
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
"private": true,
|
||||
"packageManager": "pnpm@11.21.0",
|
||||
"engines": {
|
||||
"node": ">=24"
|
||||
"node": ">=24.12.0"
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "rbxtsc",
|
||||
|
|
@ -15,6 +15,9 @@
|
|||
"open": "rbxts-build open",
|
||||
"stop": "rbxts-build stop",
|
||||
"serve": "rojo serve default.project.json",
|
||||
"bedrock": "node --env-file-if-exists=.env node_modules/@bedrock-rbx/core/dist/cli/run.mjs",
|
||||
"deploy:diff": "pnpm bedrock diff --env production",
|
||||
"deploy": "pnpm build && pnpm bedrock deploy --env production",
|
||||
"lint": "eslint .",
|
||||
"format:check": "prettier --check .",
|
||||
"format": "prettier --write .",
|
||||
|
|
@ -58,9 +61,11 @@
|
|||
"@rbxts/ui-labs": "2.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@bedrock-rbx/core": "0.3.4",
|
||||
"@eslint/js": "10.0.1",
|
||||
"@rbxts/compiler-types": "3.0.0-types.0",
|
||||
"@rbxts/types": "1.0.957",
|
||||
"arktype": "2.2.0",
|
||||
"eslint": "10.12.0",
|
||||
"eslint-config-prettier": "10.1.8",
|
||||
"husky": "9.1.7",
|
||||
|
|
|
|||
377
pnpm-lock.yaml
377
pnpm-lock.yaml
|
|
@ -74,21 +74,27 @@ importers:
|
|||
specifier: 2.4.2
|
||||
version: 2.4.2
|
||||
devDependencies:
|
||||
"@bedrock-rbx/core":
|
||||
specifier: 0.3.4
|
||||
version: 0.3.4
|
||||
"@eslint/js":
|
||||
specifier: 10.0.1
|
||||
version: 10.0.1(eslint@10.12.0(supports-color@7.2.0))
|
||||
version: 10.0.1(eslint@10.12.0(jiti@2.7.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
|
||||
arktype:
|
||||
specifier: 2.2.0
|
||||
version: 2.2.0
|
||||
eslint:
|
||||
specifier: 10.12.0
|
||||
version: 10.12.0(supports-color@7.2.0)
|
||||
version: 10.12.0(jiti@2.7.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))
|
||||
version: 10.1.8(eslint@10.12.0(jiti@2.7.0)(supports-color@7.2.0))
|
||||
husky:
|
||||
specifier: 9.1.7
|
||||
version: 9.1.7
|
||||
|
|
@ -109,9 +115,36 @@ importers:
|
|||
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)
|
||||
version: 8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3)
|
||||
|
||||
packages:
|
||||
"@ark/schema@0.56.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA==,
|
||||
}
|
||||
|
||||
"@ark/util@0.56.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-BghfRC8b9pNs3vBoDJhcta0/c1J1rsoS1+HgVUreMFPdhz/CRAKReAu57YEllNaSy98rWAdY1gE+gFup7OXpgA==,
|
||||
}
|
||||
|
||||
"@bedrock-rbx/core@0.3.4":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-itlF1cSIvAZXbrFjxZOj1kT31D32ue79/Oz3aAcZdOcNVWHNEQ1seOl1WLyQcfVx2IYzwxnYRMfXB8OH93bieQ==,
|
||||
}
|
||||
engines: { bun: ">=1.3.0", node: ">=24.12.0" }
|
||||
hasBin: true
|
||||
|
||||
"@bedrock-rbx/ocale@0.3.4":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-tbhbx7K4fXSC/v8ByWL9oCdHq3WRT1zXuTNs/3OvkXNZfe0t1qYFns/A9ZELdv96JXsGgCBRqcpzSEZzrM3sVQ==,
|
||||
}
|
||||
engines: { bun: ">=1.3.0", node: ">=24.12.0" }
|
||||
|
||||
"@cacheable/memory@2.2.0":
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -124,6 +157,18 @@ packages:
|
|||
integrity: sha512-buipgOVDkkPXNR5+xBpDw7Zk2n1EvU7qBJCNUcL7rhQ//kfpOXPAvQ511Os0vpLYJ1pZnvudNytkQt2hst3wqA==,
|
||||
}
|
||||
|
||||
"@clack/core@1.2.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-qfxof/3T3t9DPU/Rj3OmcFyZInceqj/NVtO9rwIuJqCUgh32gwPjpFQQp/ben07qKlhpwq7GzfWpST4qdJ5Drg==,
|
||||
}
|
||||
|
||||
"@clack/prompts@1.2.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w==,
|
||||
}
|
||||
|
||||
"@eslint-community/eslint-utils@4.10.1":
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -757,6 +802,18 @@ packages:
|
|||
}
|
||||
engines: { node: ">= 8" }
|
||||
|
||||
arkregex@0.0.5:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw==,
|
||||
}
|
||||
|
||||
arktype@2.2.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-t54MZ7ti5BhOEvzEkgKnWvqj+UbDfWig+DHr5I34xatymPusKLS0lQpNJd8M6DzmIto2QGszHfNKoFIT8tMCZQ==,
|
||||
}
|
||||
|
||||
balanced-match@1.0.2:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -797,6 +854,17 @@ packages:
|
|||
}
|
||||
engines: { node: ">=8" }
|
||||
|
||||
c12@3.3.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==,
|
||||
}
|
||||
peerDependencies:
|
||||
magicast: "*"
|
||||
peerDependenciesMeta:
|
||||
magicast:
|
||||
optional: true
|
||||
|
||||
cacheable@2.5.0:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -817,6 +885,13 @@ packages:
|
|||
}
|
||||
engines: { node: ">= 8.10.0" }
|
||||
|
||||
chokidar@5.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==,
|
||||
}
|
||||
engines: { node: ">= 20.19.0" }
|
||||
|
||||
cliui@8.0.1:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -837,6 +912,18 @@ packages:
|
|||
integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==,
|
||||
}
|
||||
|
||||
confbox@0.2.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==,
|
||||
}
|
||||
|
||||
confbox@0.3.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-cKUSoKa8YxFZZSmraVi7onONx3amu77ngK3kGpsYHDH7drPwCRkQE1RYMPlLRrMtnciRj274XNRxcHxnKmDSnA==,
|
||||
}
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -862,6 +949,25 @@ packages:
|
|||
integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==,
|
||||
}
|
||||
|
||||
defu@6.1.7:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==,
|
||||
}
|
||||
|
||||
destr@2.0.5:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==,
|
||||
}
|
||||
|
||||
dotenv@17.4.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==,
|
||||
}
|
||||
engines: { node: ">=12" }
|
||||
|
||||
emoji-regex@8.0.0:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -967,6 +1073,12 @@ packages:
|
|||
}
|
||||
engines: { node: ">=0.10.0" }
|
||||
|
||||
exsolve@1.1.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-kuzL9zherLQoqDeQaS55AiiIg1kb4KYXaSusTtgZTPlvKP2gGCI82LNmBNh5RjDVUXIcmgx+/Mx7vPwRzBbhJA==,
|
||||
}
|
||||
|
||||
fast-deep-equal@3.1.3:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -985,12 +1097,30 @@ packages:
|
|||
integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==,
|
||||
}
|
||||
|
||||
fast-string-truncated-width@1.2.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Q9acT/+Uu3GwGj+5w/zsGuQjh9O1TyywhIwAxHudtWrgF09nHOPrvTLhQevPbttcxjr/SNN7mJmfOw/B1bXgow==,
|
||||
}
|
||||
|
||||
fast-string-width@1.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-O3fwIVIH5gKB38QNbdg+3760ZmGz0SZMgvwJbA1b2TGXceKE6A2cOlfogh1iw8lr049zPyd7YADHy+B7U4W9bQ==,
|
||||
}
|
||||
|
||||
fast-uri@3.1.8:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==,
|
||||
}
|
||||
|
||||
fast-wrap-ansi@0.1.6:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-HlUwET7a5gqjURj70D5jl7aC3Zmy4weA1SHUfM0JFI0Ptq987NH2TwbBFLoERhfwk+E+eaq4EK3jXoT+R3yp3w==,
|
||||
}
|
||||
|
||||
fdir@6.5.0:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -1076,6 +1206,13 @@ packages:
|
|||
}
|
||||
engines: { node: 6.* || 8.* || >= 10.* }
|
||||
|
||||
giget@3.3.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-r+mvuDjrjMpsdw46Kmeydb8bdHm7wOKw8wNBtTndkjbPjgAp5oUJUxRE76wZFknxIPokfWvep2qSXK37aXE6zg==,
|
||||
}
|
||||
hasBin: true
|
||||
|
||||
glob-parent@5.1.2:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -1227,6 +1364,13 @@ packages:
|
|||
integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==,
|
||||
}
|
||||
|
||||
jiti@2.7.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==,
|
||||
}
|
||||
hasBin: true
|
||||
|
||||
json-schema-traverse@0.4.1:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -1319,6 +1463,13 @@ packages:
|
|||
}
|
||||
engines: { node: ">=16 || 14 >=14.17" }
|
||||
|
||||
mri@1.2.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==,
|
||||
}
|
||||
engines: { node: ">=4" }
|
||||
|
||||
ms@2.1.3:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -1345,6 +1496,12 @@ packages:
|
|||
}
|
||||
engines: { node: ">=0.10.0" }
|
||||
|
||||
ohash@2.0.12:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==,
|
||||
}
|
||||
|
||||
optionator@0.9.4:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -1393,6 +1550,18 @@ packages:
|
|||
}
|
||||
engines: { node: ">=16 || 14 >=14.18" }
|
||||
|
||||
pathe@2.0.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==,
|
||||
}
|
||||
|
||||
perfect-debounce@2.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==,
|
||||
}
|
||||
|
||||
picomatch@2.3.2:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -1407,6 +1576,12 @@ packages:
|
|||
}
|
||||
engines: { node: ">=12" }
|
||||
|
||||
pkg-types@2.3.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-j/lCFdcppV0JxWpCEITdbDltBxPP6cHT+yNJ6Go2OgoSA9518X847X9z0p6LtA4Nc16+eQzCZjRrWanTGvHJ5w==,
|
||||
}
|
||||
|
||||
prelude-ls@1.2.1:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -1451,6 +1626,12 @@ packages:
|
|||
peerDependencies:
|
||||
typescript: "*"
|
||||
|
||||
rc9@3.1.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ufjkNVzbRHKcCOmTahZkmVsyc3W+MSk3jY03m+a7tGHkIsdVMG9l10/3HvFbWkkKzY5VFp3pkRsIo/UYgmFL7Q==,
|
||||
}
|
||||
|
||||
readdirp@3.6.0:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -1458,6 +1639,13 @@ packages:
|
|||
}
|
||||
engines: { node: ">=8.10.0" }
|
||||
|
||||
readdirp@5.1.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==,
|
||||
}
|
||||
engines: { node: ">= 20.19.0" }
|
||||
|
||||
require-directory@2.1.1:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -1487,6 +1675,13 @@ packages:
|
|||
}
|
||||
hasBin: true
|
||||
|
||||
sade@1.8.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==,
|
||||
}
|
||||
engines: { node: ">=6" }
|
||||
|
||||
semver@7.8.5:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -1509,6 +1704,12 @@ packages:
|
|||
}
|
||||
engines: { node: ">=8" }
|
||||
|
||||
sisteransi@1.0.5:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==,
|
||||
}
|
||||
|
||||
spdx-correct@3.2.0:
|
||||
resolution:
|
||||
{
|
||||
|
|
@ -1699,6 +1900,26 @@ packages:
|
|||
}
|
||||
|
||||
snapshots:
|
||||
"@ark/schema@0.56.0":
|
||||
dependencies:
|
||||
"@ark/util": 0.56.0
|
||||
|
||||
"@ark/util@0.56.0": {}
|
||||
|
||||
"@bedrock-rbx/core@0.3.4":
|
||||
dependencies:
|
||||
"@bedrock-rbx/ocale": 0.3.4
|
||||
"@clack/prompts": 1.2.0
|
||||
arktype: 2.2.0
|
||||
c12: 3.3.4
|
||||
confbox: 0.2.4
|
||||
defu: 6.1.7
|
||||
sade: 1.8.1
|
||||
transitivePeerDependencies:
|
||||
- magicast
|
||||
|
||||
"@bedrock-rbx/ocale@0.3.4": {}
|
||||
|
||||
"@cacheable/memory@2.2.0":
|
||||
dependencies:
|
||||
"@cacheable/utils": 2.5.0
|
||||
|
|
@ -1711,9 +1932,21 @@ snapshots:
|
|||
hashery: 1.5.1
|
||||
keyv: 5.6.0
|
||||
|
||||
"@eslint-community/eslint-utils@4.10.1(eslint@10.12.0(supports-color@7.2.0))":
|
||||
"@clack/core@1.2.0":
|
||||
dependencies:
|
||||
eslint: 10.12.0(supports-color@7.2.0)
|
||||
fast-wrap-ansi: 0.1.6
|
||||
sisteransi: 1.0.5
|
||||
|
||||
"@clack/prompts@1.2.0":
|
||||
dependencies:
|
||||
"@clack/core": 1.2.0
|
||||
fast-string-width: 1.1.0
|
||||
fast-wrap-ansi: 0.1.6
|
||||
sisteransi: 1.0.5
|
||||
|
||||
"@eslint-community/eslint-utils@4.10.1(eslint@10.12.0(jiti@2.7.0)(supports-color@7.2.0))":
|
||||
dependencies:
|
||||
eslint: 10.12.0(jiti@2.7.0)(supports-color@7.2.0)
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
"@eslint-community/regexpp@4.12.2": {}
|
||||
|
|
@ -1734,9 +1967,9 @@ snapshots:
|
|||
dependencies:
|
||||
"@types/json-schema": 7.0.15
|
||||
|
||||
"@eslint/js@10.0.1(eslint@10.12.0(supports-color@7.2.0))":
|
||||
"@eslint/js@10.0.1(eslint@10.12.0(jiti@2.7.0)(supports-color@7.2.0))":
|
||||
optionalDependencies:
|
||||
eslint: 10.12.0(supports-color@7.2.0)
|
||||
eslint: 10.12.0(jiti@2.7.0)(supports-color@7.2.0)
|
||||
|
||||
"@eslint/object-schema@3.0.5": {}
|
||||
|
||||
|
|
@ -2005,15 +2238,15 @@ snapshots:
|
|||
|
||||
"@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)":
|
||||
"@typescript-eslint/eslint-plugin@8.71.2(@typescript-eslint/parser@8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3))(eslint@10.12.0(jiti@2.7.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/parser": 8.71.2(eslint@10.12.0(jiti@2.7.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/type-utils": 8.71.2(eslint@10.12.0(jiti@2.7.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(jiti@2.7.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)
|
||||
eslint: 10.12.0(jiti@2.7.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)
|
||||
|
|
@ -2021,14 +2254,14 @@ snapshots:
|
|||
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)":
|
||||
"@typescript-eslint/parser@8.71.2(eslint@10.12.0(jiti@2.7.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)
|
||||
eslint: 10.12.0(jiti@2.7.0)(supports-color@7.2.0)
|
||||
typescript: 5.5.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -2051,13 +2284,13 @@ snapshots:
|
|||
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)":
|
||||
"@typescript-eslint/type-utils@8.71.2(eslint@10.12.0(jiti@2.7.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)
|
||||
"@typescript-eslint/utils": 8.71.2(eslint@10.12.0(jiti@2.7.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)
|
||||
eslint: 10.12.0(jiti@2.7.0)(supports-color@7.2.0)
|
||||
ts-api-utils: 2.5.0(typescript@5.5.3)
|
||||
typescript: 5.5.3
|
||||
transitivePeerDependencies:
|
||||
|
|
@ -2080,13 +2313,13 @@ snapshots:
|
|||
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)":
|
||||
"@typescript-eslint/utils@8.71.2(eslint@10.12.0(jiti@2.7.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))
|
||||
"@eslint-community/eslint-utils": 4.10.1(eslint@10.12.0(jiti@2.7.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)
|
||||
eslint: 10.12.0(jiti@2.7.0)(supports-color@7.2.0)
|
||||
typescript: 5.5.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
@ -2127,6 +2360,16 @@ snapshots:
|
|||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.2
|
||||
|
||||
arkregex@0.0.5:
|
||||
dependencies:
|
||||
"@ark/util": 0.56.0
|
||||
|
||||
arktype@2.2.0:
|
||||
dependencies:
|
||||
"@ark/schema": 0.56.0
|
||||
"@ark/util": 0.56.0
|
||||
arkregex: 0.0.5
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
balanced-match@4.0.4: {}
|
||||
|
|
@ -2145,6 +2388,21 @@ snapshots:
|
|||
dependencies:
|
||||
fill-range: 7.1.1
|
||||
|
||||
c12@3.3.4:
|
||||
dependencies:
|
||||
chokidar: 5.0.0
|
||||
confbox: 0.2.4
|
||||
defu: 6.1.7
|
||||
dotenv: 17.4.2
|
||||
exsolve: 1.1.3
|
||||
giget: 3.3.1
|
||||
jiti: 2.7.0
|
||||
ohash: 2.0.12
|
||||
pathe: 2.0.3
|
||||
perfect-debounce: 2.1.0
|
||||
pkg-types: 2.3.3
|
||||
rc9: 3.1.0
|
||||
|
||||
cacheable@2.5.0:
|
||||
dependencies:
|
||||
"@cacheable/memory": 2.2.0
|
||||
|
|
@ -2170,6 +2428,10 @@ snapshots:
|
|||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
chokidar@5.0.0:
|
||||
dependencies:
|
||||
readdirp: 5.1.1
|
||||
|
||||
cliui@8.0.1:
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
|
|
@ -2182,6 +2444,10 @@ snapshots:
|
|||
|
||||
color-name@1.1.4: {}
|
||||
|
||||
confbox@0.2.4: {}
|
||||
|
||||
confbox@0.3.1: {}
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
dependencies:
|
||||
path-key: 3.1.1
|
||||
|
|
@ -2196,6 +2462,12 @@ snapshots:
|
|||
|
||||
deep-is@0.1.4: {}
|
||||
|
||||
defu@6.1.7: {}
|
||||
|
||||
destr@2.0.5: {}
|
||||
|
||||
dotenv@17.4.2: {}
|
||||
|
||||
emoji-regex@8.0.0: {}
|
||||
|
||||
es-errors@1.3.0: {}
|
||||
|
|
@ -2204,9 +2476,9 @@ snapshots:
|
|||
|
||||
escape-string-regexp@4.0.0: {}
|
||||
|
||||
eslint-config-prettier@10.1.8(eslint@10.12.0(supports-color@7.2.0)):
|
||||
eslint-config-prettier@10.1.8(eslint@10.12.0(jiti@2.7.0)(supports-color@7.2.0)):
|
||||
dependencies:
|
||||
eslint: 10.12.0(supports-color@7.2.0)
|
||||
eslint: 10.12.0(jiti@2.7.0)(supports-color@7.2.0)
|
||||
|
||||
eslint-scope@9.1.2:
|
||||
dependencies:
|
||||
|
|
@ -2219,9 +2491,9 @@ snapshots:
|
|||
|
||||
eslint-visitor-keys@5.0.1: {}
|
||||
|
||||
eslint@10.12.0(supports-color@7.2.0):
|
||||
eslint@10.12.0(jiti@2.7.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/eslint-utils": 4.10.1(eslint@10.12.0(jiti@2.7.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
|
||||
|
|
@ -2251,6 +2523,8 @@ snapshots:
|
|||
minimatch: 10.2.6
|
||||
natural-compare: 1.4.0
|
||||
optionator: 0.9.4
|
||||
optionalDependencies:
|
||||
jiti: 2.7.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -2272,14 +2546,26 @@ snapshots:
|
|||
|
||||
esutils@2.0.3: {}
|
||||
|
||||
exsolve@1.1.3: {}
|
||||
|
||||
fast-deep-equal@3.1.3: {}
|
||||
|
||||
fast-json-stable-stringify@2.1.0: {}
|
||||
|
||||
fast-levenshtein@2.0.6: {}
|
||||
|
||||
fast-string-truncated-width@1.2.1: {}
|
||||
|
||||
fast-string-width@1.1.0:
|
||||
dependencies:
|
||||
fast-string-truncated-width: 1.2.1
|
||||
|
||||
fast-uri@3.1.8: {}
|
||||
|
||||
fast-wrap-ansi@0.1.6:
|
||||
dependencies:
|
||||
fast-string-width: 1.1.0
|
||||
|
||||
fdir@6.5.0(picomatch@4.0.7):
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.7
|
||||
|
|
@ -2326,6 +2612,8 @@ snapshots:
|
|||
|
||||
get-caller-file@2.0.5: {}
|
||||
|
||||
giget@3.3.1: {}
|
||||
|
||||
glob-parent@5.1.2:
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
|
|
@ -2391,6 +2679,8 @@ snapshots:
|
|||
|
||||
isexe@2.0.0: {}
|
||||
|
||||
jiti@2.7.0: {}
|
||||
|
||||
json-schema-traverse@0.4.1: {}
|
||||
|
||||
json-schema-traverse@1.0.0: {}
|
||||
|
|
@ -2436,6 +2726,8 @@ snapshots:
|
|||
|
||||
minipass@7.1.3: {}
|
||||
|
||||
mri@1.2.0: {}
|
||||
|
||||
ms@2.1.3: {}
|
||||
|
||||
natural-compare@1.4.0: {}
|
||||
|
|
@ -2449,6 +2741,8 @@ snapshots:
|
|||
|
||||
normalize-path@3.0.0: {}
|
||||
|
||||
ohash@2.0.12: {}
|
||||
|
||||
optionator@0.9.4:
|
||||
dependencies:
|
||||
deep-is: 0.1.4
|
||||
|
|
@ -2477,10 +2771,20 @@ snapshots:
|
|||
lru-cache: 10.4.3
|
||||
minipass: 7.1.3
|
||||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
perfect-debounce@2.1.0: {}
|
||||
|
||||
picomatch@2.3.2: {}
|
||||
|
||||
picomatch@4.0.7: {}
|
||||
|
||||
pkg-types@2.3.3:
|
||||
dependencies:
|
||||
confbox: 0.3.1
|
||||
exsolve: 1.1.3
|
||||
pathe: 2.0.3
|
||||
|
||||
prelude-ls@1.2.1: {}
|
||||
|
||||
prettier@3.9.10: {}
|
||||
|
|
@ -2509,10 +2813,17 @@ snapshots:
|
|||
typescript: 5.5.3
|
||||
uuid: 8.3.2
|
||||
|
||||
rc9@3.1.0:
|
||||
dependencies:
|
||||
defu: 6.1.7
|
||||
destr: 2.0.5
|
||||
|
||||
readdirp@3.6.0:
|
||||
dependencies:
|
||||
picomatch: 2.3.2
|
||||
|
||||
readdirp@5.1.1: {}
|
||||
|
||||
require-directory@2.1.1: {}
|
||||
|
||||
require-from-string@2.0.2: {}
|
||||
|
|
@ -2536,6 +2847,10 @@ snapshots:
|
|||
typescript: 5.5.3
|
||||
yargs: 17.7.3
|
||||
|
||||
sade@1.8.1:
|
||||
dependencies:
|
||||
mri: 1.2.0
|
||||
|
||||
semver@7.8.5: {}
|
||||
|
||||
shebang-command@2.0.0:
|
||||
|
|
@ -2544,6 +2859,8 @@ snapshots:
|
|||
|
||||
shebang-regex@3.0.0: {}
|
||||
|
||||
sisteransi@1.0.5: {}
|
||||
|
||||
spdx-correct@3.2.0:
|
||||
dependencies:
|
||||
spdx-expression-parse: 3.0.1
|
||||
|
|
@ -2591,13 +2908,13 @@ snapshots:
|
|||
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):
|
||||
typescript-eslint@8.71.2(eslint@10.12.0(jiti@2.7.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/eslint-plugin": 8.71.2(@typescript-eslint/parser@8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3))(eslint@10.12.0(jiti@2.7.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(jiti@2.7.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-eslint/utils": 8.71.2(eslint@10.12.0(jiti@2.7.0)(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.5.3)
|
||||
eslint: 10.12.0(jiti@2.7.0)(supports-color@7.2.0)
|
||||
typescript: 5.5.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
|
|
|||
23
readme.md
23
readme.md
|
|
@ -25,6 +25,7 @@ $env:Path = "$($paths -join ';');$env:Path"
|
|||
pnpm --version
|
||||
rokit --version
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
### Configuration complète dans Ubuntu WSL2
|
||||
|
|
@ -85,12 +86,32 @@ Pour choisir une approche lors d'une nouvelle fonctionnalité, le skill local [`
|
|||
- [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.
|
||||
- [Bedrock](https://github.com/christopher-buss/bedrock#readme) : CLI `@bedrock-rbx/core@0.3.4` épinglée avec pnpm ; les setups Windows et WSL l’installent et vérifient son démarrage. Voir la configuration de déploiement ci-dessous.
|
||||
- [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.
|
||||
|
||||
## Déploiement avec Bedrock
|
||||
|
||||
Bedrock est installé par pnpm, avec Node.js 24.12 minimum (la version de `mise.toml` convient). Après une mise à jour du template, lancer `rokit install` puis `pnpm install --frozen-lockfile` et `pnpm bedrock --version`. Les scripts de setup réalisent aussi ces étapes. La version 0.3.4 est antérieure à la version 1.0 ; sa configuration peut évoluer lors des mises à jour. Voir le [README du paquet](https://github.com/christopher-buss/bedrock/tree/v0.3.4/packages/bedrock#readme).
|
||||
|
||||
1. Créer ou choisir l’expérience Roblox cible.
|
||||
2. Copier [`bedrock.config.example.json`](bedrock.config.example.json) vers `bedrock.config.json`, puis remplacer `REPLACE_WITH_UNIVERSE_ID` et `REPLACE_WITH_PLACE_ID`. Le fichier publie `game.rbxl` dans l’environnement `production`. La génération de code est désactivée pour conserver le build roblox-ts existant.
|
||||
3. Copier [`.env.example`](.env.example) vers `.env` et renseigner `BEDROCK_API_KEY` (clé Roblox Open Cloud autorisée sur l’expérience cible). Adapter les permissions Open Cloud aux ressources gérées selon la [documentation Bedrock](https://github.com/christopher-buss/bedrock#readme). Les commandes ci-dessous chargent `.env` automatiquement ; les variables déjà exportées dans le terminal ont priorité. Ne jamais commiter les jetons.
|
||||
|
||||
```bash
|
||||
pnpm build
|
||||
pnpm deploy:diff
|
||||
pnpm deploy
|
||||
```
|
||||
|
||||
Le [plugin local](scripts/bedrock-local-state.mjs) utilise le [contrat officiel de Bedrock 0.3.4](https://github.com/christopher-buss/bedrock/blob/v0.3.4/apps/website/docs/bedrock/guide/plugins.md). Il conserve l’état dans `.bedrock/state/production.json` (chemin relatif à la racine du dépôt), sans Gist, compte de stockage supplémentaire ou jeton GitHub. Ces fichiers sont ignorés par Git, ESLint et Prettier. Sauvegarder ce dossier après un déploiement et le conserver lors d’un changement de machine : il relie les ressources du projet aux IDs Roblox. Un état corrompu provoque une erreur et ne devient jamais un état vide.
|
||||
|
||||
Un verrou local empêche deux déploiements simultanés sur le même environnement. Si un processus interrompu laisse un verrou, arrêter tous les déploiements concernés avant de lancer `pnpm bedrock state unlock --env production`. Ne pas partager ce dossier entre plusieurs machines ni le synchroniser pendant un déploiement. La publication contacte toujours Roblox et nécessite `BEDROCK_API_KEY`.
|
||||
|
||||
`pnpm deploy:diff` prévisualise les opérations sans les appliquer ; `game.rbxl` doit déjà exister. `pnpm deploy` reconstruit le jeu puis applique les changements à l’expérience configurée. Le setup ne déploie rien. La configuration réelle `bedrock.config.json` peut être versionnée : les IDs ne sont pas des secrets. Aucun état de déploiement antérieur n’est présent dans ce template.
|
||||
|
||||
## 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 :
|
||||
|
|
|
|||
|
|
@ -7,4 +7,3 @@
|
|||
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"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,204 @@
|
|||
import { randomUUID } from "node:crypto";
|
||||
import {
|
||||
closeSync,
|
||||
existsSync,
|
||||
fsyncSync,
|
||||
mkdirSync,
|
||||
openSync,
|
||||
readFileSync,
|
||||
renameSync,
|
||||
rmSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { hostname } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import process from "node:process";
|
||||
import { parseStateContents, serializeStateFile, validateEnvironmentName } from "@bedrock-rbx/core";
|
||||
import { type } from "arktype";
|
||||
|
||||
const ok = (data) => ({ success: true, data });
|
||||
const fail = (reason) => ({ success: false, err: { reason } });
|
||||
|
||||
function environmentFile(directory, environment) {
|
||||
const valid = validateEnvironmentName(environment);
|
||||
if (!valid.success) throw new Error(valid.err.reason);
|
||||
if (/^(con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(environment)) {
|
||||
throw new Error("Environment name is reserved on Windows.");
|
||||
}
|
||||
return join(directory, `${environment}.json`);
|
||||
}
|
||||
|
||||
function stateFailure(file, error) {
|
||||
return {
|
||||
success: false,
|
||||
err: {
|
||||
kind: error.code === "EACCES" || error.code === "EPERM" ? "stateAccessDenied" : "stateError",
|
||||
file,
|
||||
reason: error.message,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** Create a filesystem adapter without creating files during read-only commands. */
|
||||
export function createLocalStatePorts(directory) {
|
||||
directory = resolve(directory);
|
||||
const lockPath = (environment) => `${environmentFile(directory, environment)}.lock`;
|
||||
|
||||
const statePort = {
|
||||
async read(environment) {
|
||||
let file = directory;
|
||||
try {
|
||||
file = environmentFile(directory, environment);
|
||||
let raw;
|
||||
try {
|
||||
raw = readFileSync(file, "utf8");
|
||||
} catch (error) {
|
||||
if (error.code === "ENOENT") return ok({});
|
||||
throw error;
|
||||
}
|
||||
const parsed = parseStateContents(raw, file);
|
||||
if (!parsed.success) return parsed;
|
||||
if (parsed.data.environment !== environment) {
|
||||
throw new Error(`State belongs to "${parsed.data.environment}", not "${environment}".`);
|
||||
}
|
||||
return ok({ state: parsed.data });
|
||||
} catch (error) {
|
||||
return stateFailure(file, error);
|
||||
}
|
||||
},
|
||||
|
||||
async write(state) {
|
||||
let file = directory;
|
||||
let temporary;
|
||||
try {
|
||||
file = environmentFile(directory, state.environment);
|
||||
const raw = serializeStateFile(state);
|
||||
const parsed = parseStateContents(raw, file);
|
||||
if (!parsed.success) return parsed;
|
||||
mkdirSync(directory, { recursive: true });
|
||||
temporary = `${file}.${randomUUID()}.tmp`;
|
||||
const handle = openSync(temporary, "wx", 0o600);
|
||||
try {
|
||||
writeFileSync(handle, `${raw}\n`, "utf8");
|
||||
fsyncSync(handle);
|
||||
} finally {
|
||||
closeSync(handle);
|
||||
}
|
||||
// Rename on the same filesystem keeps readers from seeing partial JSON.
|
||||
renameSync(temporary, file);
|
||||
return ok(undefined);
|
||||
} catch (error) {
|
||||
return stateFailure(file, error);
|
||||
} finally {
|
||||
if (temporary) {
|
||||
try {
|
||||
rmSync(temporary, { force: true });
|
||||
} catch {
|
||||
// Keep the original failure if the filesystem also refuses cleanup.
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const stateLockPort = {
|
||||
async acquire(environment, options = {}) {
|
||||
let lock;
|
||||
let created = false;
|
||||
try {
|
||||
lock = lockPath(environment);
|
||||
mkdirSync(directory, { recursive: true });
|
||||
// Directory creation is exclusive across independent CLI processes.
|
||||
mkdirSync(lock);
|
||||
created = true;
|
||||
const token = randomUUID();
|
||||
const holding = {
|
||||
token,
|
||||
owner: `${hostname()}:${process.pid}`,
|
||||
since: new Date().toISOString(),
|
||||
operation: options.operation,
|
||||
};
|
||||
writeFileSync(join(lock, "owner.json"), JSON.stringify(holding), { mode: 0o600 });
|
||||
return ok({
|
||||
async release() {
|
||||
try {
|
||||
const current = JSON.parse(readFileSync(join(lock, "owner.json"), "utf8"));
|
||||
if (current.token !== token)
|
||||
return fail("The local state lock is held by another run.");
|
||||
rmSync(lock, { recursive: true });
|
||||
return ok(undefined);
|
||||
} catch (error) {
|
||||
return fail(`Cannot release local state lock: ${error.message}`);
|
||||
}
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
if (created) {
|
||||
try {
|
||||
rmSync(lock, { recursive: true, force: true });
|
||||
} catch {
|
||||
return fail(
|
||||
`Cannot initialize or remove local state lock at ${lock}: ${error.message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
return fail(
|
||||
error.code === "EEXIST"
|
||||
? `Local state is locked for "${environment}". Stop the other deploy before using bedrock state unlock --env ${environment}.`
|
||||
: `Cannot acquire local state lock: ${error.message}`,
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
async inspect(environment) {
|
||||
try {
|
||||
const lock = lockPath(environment);
|
||||
try {
|
||||
return ok(JSON.parse(readFileSync(join(lock, "owner.json"), "utf8")));
|
||||
} catch (error) {
|
||||
if (error.code === "ENOENT") {
|
||||
return ok(existsSync(lock) ? { owner: "Unknown local run" } : undefined);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
} catch (error) {
|
||||
return fail(`Cannot inspect local state lock: ${error.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
async forceRelease(environment) {
|
||||
try {
|
||||
const lock = lockPath(environment);
|
||||
if (!existsSync(lock)) return ok(undefined);
|
||||
const holding = await stateLockPort.inspect(environment);
|
||||
rmSync(lock, { recursive: true });
|
||||
return ok(holding.success ? holding.data : { owner: "Unknown local run" });
|
||||
} catch (error) {
|
||||
return fail(`Cannot unlock local state: ${error.message}`);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
return { statePort, stateLockPort };
|
||||
}
|
||||
|
||||
export default {
|
||||
name: "crownstudio-local-state",
|
||||
stateBackends: [
|
||||
{
|
||||
name: "local",
|
||||
schema: type({ directory: "string > 0" }),
|
||||
createPort: ({ stateConfig }) => ok(createLocalStatePorts(stateConfig.directory).statePort),
|
||||
createLockPort: ({ stateConfig }) =>
|
||||
ok(createLocalStatePorts(stateConfig.directory).stateLockPort),
|
||||
migratePrompts: [
|
||||
{
|
||||
key: "directory",
|
||||
label: "Local state directory?",
|
||||
placeholder: ".bedrock/state",
|
||||
validationMessage: "A local state directory is required.",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
@ -54,8 +54,10 @@ try {
|
|||
|
||||
Invoke-Tool 'mise' @('trust', 'mise.toml')
|
||||
Invoke-Tool 'mise' @('install')
|
||||
Invoke-Tool 'rokit' @('trust', 'rojo-rbx/rojo', 'jackTabsCode/asphalt', 'seaofvoices/darklua')
|
||||
Invoke-Tool 'rokit' @('install')
|
||||
Invoke-Tool 'mise' @('exec', '--', 'pnpm', 'install', '--frozen-lockfile')
|
||||
Invoke-Tool 'mise' @('exec', '--', 'pnpm', 'bedrock', '--version')
|
||||
if (-not $SkipChecks) {
|
||||
Invoke-Tool 'mise' @('exec', '--', 'pnpm', 'check')
|
||||
Invoke-Tool 'mise' @('exec', '--', 'pnpm', 'build')
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ if [[ ! -x "$rokit_bin" ]]; then
|
|||
(cd -- "$temporary_dir" && bash "${temporary_dir}/rokit-install.sh")
|
||||
fi
|
||||
[[ -x "$rokit_bin" ]] || fail "Rokit was not installed at ${rokit_bin}."
|
||||
"$rokit_bin" trust rojo-rbx/rojo jackTabsCode/asphalt seaofvoices/darklua blake-mealey/mantle
|
||||
"$rokit_bin" trust rojo-rbx/rojo jackTabsCode/asphalt seaofvoices/darklua
|
||||
"$rokit_bin" install
|
||||
|
||||
if [[ ! -d "$blender_dir" ]]; then
|
||||
|
|
@ -129,6 +129,7 @@ fi
|
|||
"$mise_bin" exec -- uv --version
|
||||
"$rokit_bin" --version
|
||||
"$mise_bin" exec -- pnpm install --frozen-lockfile
|
||||
"$mise_bin" exec -- pnpm bedrock --version
|
||||
if [[ "$skip_checks" == false ]]; then
|
||||
"$mise_bin" exec -- pnpm check
|
||||
"$mise_bin" exec -- pnpm build
|
||||
|
|
|
|||
|
|
@ -0,0 +1,193 @@
|
|||
import assert from "node:assert/strict";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import process from "node:process";
|
||||
import test from "node:test";
|
||||
import { fileURLToPath, pathToFileURL, URL } from "node:url";
|
||||
import { buildStateBackend, loadProjectAsync } from "@bedrock-rbx/core";
|
||||
import { createLocalStatePorts } from "../scripts/bedrock-local-state.mjs";
|
||||
|
||||
const pluginUrl = new URL("../scripts/bedrock-local-state.mjs", import.meta.url);
|
||||
const cli = fileURLToPath(
|
||||
new URL("../node_modules/@bedrock-rbx/core/dist/cli/run.mjs", import.meta.url),
|
||||
);
|
||||
const example = JSON.parse(
|
||||
readFileSync(new URL("../bedrock.config.example.json", import.meta.url)),
|
||||
);
|
||||
const state = (environment = "production") => ({ version: 1, environment, resources: [] });
|
||||
|
||||
function fixture(t) {
|
||||
const root = mkdtempSync(join(tmpdir(), "bedrock-local-state-"));
|
||||
t.after(() => rmSync(root, { recursive: true, force: true }));
|
||||
const directory = join(root, "state");
|
||||
return { root, directory, ...createLocalStatePorts(directory) };
|
||||
}
|
||||
|
||||
test("first reads are empty without creating state directories", async (t) => {
|
||||
const { statePort, stateLockPort, directory } = fixture(t);
|
||||
assert.deepEqual(await statePort.read("production"), { success: true, data: {} });
|
||||
assert.deepEqual(await stateLockPort.inspect("production"), { success: true, data: undefined });
|
||||
assert.equal(existsSync(directory), false);
|
||||
});
|
||||
|
||||
test("state persists across adapter instances with Bedrock bookkeeping and environment isolation", async (t) => {
|
||||
const { directory, statePort } = fixture(t);
|
||||
const snapshot = { ...state(), pendingRebuild: new Set(["start"]), codegenHash: "a".repeat(64) };
|
||||
assert.equal((await statePort.write(snapshot)).success, true);
|
||||
const fresh = createLocalStatePorts(directory).statePort;
|
||||
assert.deepEqual((await fresh.read("production")).data.state, snapshot);
|
||||
assert.equal((await fresh.write(state("staging"))).success, true);
|
||||
assert.deepEqual((await fresh.read("production")).data.state, snapshot);
|
||||
assert.deepEqual((await fresh.read("staging")).data.state, state("staging"));
|
||||
assert.deepEqual(JSON.parse(readFileSync(join(directory, "production.json"))).$bedrock, {
|
||||
version: 1,
|
||||
pendingRebuild: ["start"],
|
||||
codegenHash: "a".repeat(64),
|
||||
});
|
||||
assert.deepEqual(readdirSync(directory).sort(), ["production.json", "staging.json"]);
|
||||
});
|
||||
|
||||
test("atomic replacement leaves valid state and no temporary files", async (t) => {
|
||||
const { directory, statePort } = fixture(t);
|
||||
await statePort.write(state());
|
||||
for (let index = 0; index < 5; index++) {
|
||||
assert.equal(
|
||||
(await statePort.write({ ...state(), codegenHash: String(index).repeat(64) })).success,
|
||||
true,
|
||||
);
|
||||
assert.equal(
|
||||
(await statePort.read("production")).data.state.codegenHash,
|
||||
String(index).repeat(64),
|
||||
);
|
||||
}
|
||||
assert.deepEqual(readdirSync(directory), ["production.json"]);
|
||||
});
|
||||
|
||||
test("corrupt, unsupported and mismatched state never becomes a first deployment", async (t) => {
|
||||
const { statePort, directory } = fixture(t);
|
||||
await statePort.write(state());
|
||||
const file = join(directory, "production.json");
|
||||
for (const raw of [
|
||||
"{broken",
|
||||
JSON.stringify({ $bedrock: { version: 999 }, environment: "production", resources: [] }),
|
||||
JSON.stringify({ $bedrock: { version: 1 }, environment: "staging", resources: [] }),
|
||||
]) {
|
||||
writeFileSync(file, raw);
|
||||
const result = await statePort.read("production");
|
||||
assert.equal(result.success, false);
|
||||
assert.equal(result.err.kind, "stateError");
|
||||
assert.equal(readFileSync(file, "utf8"), raw);
|
||||
}
|
||||
});
|
||||
|
||||
test("invalid state cannot replace the last valid snapshot", async (t) => {
|
||||
const { statePort, directory } = fixture(t);
|
||||
await statePort.write(state());
|
||||
const before = readFileSync(join(directory, "production.json"), "utf8");
|
||||
assert.equal((await statePort.write({ ...state(), version: 999 })).success, false);
|
||||
assert.equal(readFileSync(join(directory, "production.json"), "utf8"), before);
|
||||
});
|
||||
|
||||
test("unsafe environment names cannot escape the state directory", async (t) => {
|
||||
const { statePort, stateLockPort, directory } = fixture(t);
|
||||
for (const environment of ["../outside", "..", "a/b", "a\\b", "CON", ""]) {
|
||||
assert.equal((await statePort.read(environment)).success, false);
|
||||
assert.equal((await statePort.write(state(environment))).success, false);
|
||||
assert.equal((await stateLockPort.acquire(environment)).success, false);
|
||||
}
|
||||
assert.equal(existsSync(directory), false);
|
||||
});
|
||||
|
||||
test("deployment locks exclude independent Node processes and release normally", async (t) => {
|
||||
const { directory, stateLockPort } = fixture(t);
|
||||
const first = await stateLockPort.acquire("production", { operation: "deploy" });
|
||||
assert.equal(first.success, true);
|
||||
assert.equal((await stateLockPort.inspect("production")).data.operation, "deploy");
|
||||
const probe = () =>
|
||||
spawnSync(
|
||||
process.execPath,
|
||||
[
|
||||
"--input-type=module",
|
||||
"-e",
|
||||
`
|
||||
import { createLocalStatePorts } from ${JSON.stringify(pluginUrl.href)};
|
||||
const lock = await createLocalStatePorts(${JSON.stringify(directory)}).stateLockPort.acquire("production");
|
||||
if (!lock.success) process.exit(7);
|
||||
await lock.data.release();
|
||||
`,
|
||||
],
|
||||
{ encoding: "utf8" },
|
||||
);
|
||||
assert.equal(probe().status, 7);
|
||||
assert.equal((await first.data.release()).success, true);
|
||||
const next = probe();
|
||||
assert.equal(next.status, 0, next.stderr);
|
||||
assert.equal((await stateLockPort.inspect("production")).data, undefined);
|
||||
});
|
||||
|
||||
test("explicit unlock recovers an abandoned lock without allowing its old holder to release a new one", async (t) => {
|
||||
const { directory, stateLockPort } = fixture(t);
|
||||
const old = await stateLockPort.acquire("production");
|
||||
const fresh = createLocalStatePorts(directory).stateLockPort;
|
||||
assert.equal((await fresh.forceRelease("production")).success, true);
|
||||
const next = await fresh.acquire("production");
|
||||
assert.equal(next.success, true);
|
||||
assert.equal((await old.data.release()).success, false);
|
||||
assert.ok((await fresh.inspect("production")).data);
|
||||
assert.equal((await next.data.release()).success, true);
|
||||
});
|
||||
|
||||
test("the real Bedrock loader and CLI diff work without GitHub credentials or network calls", async (t) => {
|
||||
const { root, directory } = fixture(t);
|
||||
const config = globalThis.structuredClone(example);
|
||||
config.plugins = [pluginUrl.href];
|
||||
config.environments.production.universe.universeId = "123456789";
|
||||
config.environments.production.places.start.placeId = "987654321";
|
||||
config.places.start.filePath = join(root, "game.rbxl");
|
||||
config.state.directory = directory;
|
||||
const configFile = join(root, "bedrock.config.json");
|
||||
writeFileSync(configFile, JSON.stringify(config));
|
||||
writeFileSync(config.places.start.filePath, "local test artifact");
|
||||
const loaded = await loadProjectAsync({ cwd: root });
|
||||
assert.equal(loaded.success, true, JSON.stringify(loaded));
|
||||
const built = buildStateBackend({
|
||||
stateConfig: loaded.data.config.state,
|
||||
plugins: loaded.data.plugins,
|
||||
getEnv: () => undefined,
|
||||
});
|
||||
assert.equal(built.success, true, JSON.stringify(built));
|
||||
assert.equal(built.data.locking, "exclusive");
|
||||
await built.data.statePort.write(state());
|
||||
assert.deepEqual((await built.data.statePort.read("production")).data.state, state());
|
||||
rmSync(directory, { recursive: true });
|
||||
const guard = join(root, "offline.mjs");
|
||||
writeFileSync(
|
||||
guard,
|
||||
'globalThis.fetch = () => { throw new Error("Unexpected network request"); };',
|
||||
);
|
||||
const env = { ...process.env };
|
||||
delete env.BEDROCK_GITHUB_TOKEN;
|
||||
delete env.BEDROCK_API_KEY;
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
[
|
||||
"--import",
|
||||
pathToFileURL(guard).href,
|
||||
cli,
|
||||
"diff",
|
||||
"--config",
|
||||
configFile,
|
||||
"--env",
|
||||
"production",
|
||||
],
|
||||
{
|
||||
cwd: root,
|
||||
env,
|
||||
encoding: "utf8",
|
||||
},
|
||||
);
|
||||
assert.equal(result.status, 0, result.stdout + result.stderr);
|
||||
assert.equal(existsSync(directory), false);
|
||||
});
|
||||
Loading…
Reference in New Issue