├── .eslintrc ├── .github └── FUNDING.yml ├── .gitignore ├── .prettierrc ├── .vscode ├── extensions.json └── settings.json ├── README.md ├── default.project.json ├── flamework.build ├── package-lock.json ├── package.json ├── src ├── constants.ts ├── createBinarySerializer.ts ├── dataType.ts ├── index.ts ├── metadata │ ├── index.ts │ ├── tuples.ts │ └── unions.ts ├── processSerializerData.ts └── serialization │ ├── createDeserializer.ts │ └── createSerializer.ts └── tsconfig.json /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "@typescript-eslint/parser", 3 | "parserOptions": { 4 | "jsx": true, 5 | "useJSXTextNode": true, 6 | "ecmaVersion": 2018, 7 | "sourceType": "module", 8 | "project": "./tsconfig.json" 9 | }, 10 | "ignorePatterns": [ 11 | "/out" 12 | ], 13 | "plugins": [ 14 | "@typescript-eslint", 15 | "roblox-ts", 16 | "prettier" 17 | ], 18 | "extends": [ 19 | "eslint:recommended", 20 | "plugin:@typescript-eslint/recommended", 21 | "plugin:roblox-ts/recommended", 22 | "plugin:prettier/recommended" 23 | ], 24 | "rules": { 25 | "prettier/prettier": "warn", 26 | "no-constant-condition": "off" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [Fireboltofdeath] 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /out 3 | /include 4 | *.tsbuildinfo 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 4, 4 | "trailingComma": "all", 5 | "useTabs": true 6 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "roblox-ts.vscode-roblox-ts", 4 | "dbaeumer.vscode-eslint" 5 | ] 6 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "files.eol": "\n", 4 | "[typescript]": { 5 | "editor.defaultFormatter": "dbaeumer.vscode-eslint", 6 | "editor.formatOnSave": true 7 | }, 8 | "[typescriptreact]": { 9 | "editor.defaultFormatter": "dbaeumer.vscode-eslint", 10 | "editor.formatOnSave": true 11 | }, 12 | "eslint.run": "onType", 13 | "eslint.format.enable": true 14 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flamework-binary-serializer 2 | This is a small and simple library that allows you to specify a small and optimized structure for binary data. 3 | 4 | This package is **not** an official Flamework package, but it does use Flamework to automatically generate a description given any arbitrary TS type. 5 | You should refer to the [Flamework documentation](https://flamework.fireboltofdeath.dev/) for installation steps. 6 | 7 | ## Demo 8 | 9 | Documentation is not planned, but here's an example of how to use the library. 10 | 11 | You should only call `createBinarySerializer` once, most likely as an export of a shared file. 12 | 13 | Serialization returns a buffer and a blobs array. The blobs array contains things that we leave Roblox to serialize (instances, `unknown` values, etc.) 14 | 15 | ```ts 16 | import { DataType, createBinarySerializer } from "@rbxts/flamework-binary-serializer"; 17 | 18 | export interface Data { 19 | optional?: boolean; 20 | f64: number; 21 | f32: DataType.f32; 22 | 23 | u8: DataType.u8; 24 | u16: DataType.u16; 25 | u32: DataType.u32; 26 | 27 | i8: DataType.i8; 28 | i16: DataType.i16; 29 | i32: DataType.i32; 30 | 31 | vector: Vector3; 32 | cframe: CFrame; 33 | color3: Color3; 34 | colorSequence: ColorSequence; 35 | numberSequence: NumberSequence; 36 | enum: Enum.KeyCode; 37 | 38 | boolean: boolean; 39 | string: string; 40 | array: number[]; 41 | 42 | // flamework-binary-serializer will optimize the `type` field into a single byte. 43 | // The rest of the object will serialize like a normal object, but without the `type` field. 44 | union: { type: "string"; value: string } | { type: "number"; value: number } | { type: "boolean"; value: boolean }; 45 | unionOfStrings: "a" | "b" | "c" | "d" | "e" | "a very large string that will not exist in the serialized output!"; 46 | unionOfPrimitives: 1 | 2 | "a" | "b" | true | undefined; 47 | 48 | tuple: [string, number, boolean]; 49 | tupleWithRest: [string, number, boolean, ...string[]]; 50 | 51 | // flamework-binary-serializer will use Roblox's serialization for types it does not recognize 52 | blob: Instance; 53 | unknown: unknown; 54 | 55 | map: Map; 56 | set: Set<{ type: "string"; value: string } | { type: "number"; value: number }>; 57 | 58 | packed: DataType.Packed; 59 | } 60 | 61 | // This interface will take up a byte and 8-10 bits. 62 | // 1 byte for the i8, 8 bits (1 byte) for 8 booleans, and 1-2 bits for the optional field. 63 | // Without packing, this interface would take up to 11 bytes. 64 | // 65 | // Packing also optimizes `optional` values by using a single bit for the presence of the value. 66 | // Packing recursively applies to the entire object, including things like arrays, other objects, etc. 67 | // 68 | // Packing additionally optimizes CFrames by optimizing out axis-aligned orientation or default positions. 69 | // This does add an extra bit of overhead per CFrame, but due to the size of an unoptimized CFrame, this is insignificant. 70 | // 71 | // Packing also optimizes discriminated and literal unions with two constituents by using a single bit for the discriminator. 72 | interface PackedObject { 73 | num: DataType.i8; 74 | a: boolean; 75 | b: boolean; 76 | c: boolean; 77 | d: boolean; 78 | e: boolean; 79 | f: boolean; 80 | g: boolean; 81 | h: boolean; 82 | 83 | // This only takes up to 2 bits to encode in a packed type! 84 | i?: boolean; 85 | } 86 | 87 | const testData: Data = { 88 | f64: 1552983.573, 89 | f32: 1552983.573, 90 | 91 | u8: 175, 92 | u16: 5892, 93 | u32: 850928, 94 | 95 | i8: 175, 96 | i16: 5892, 97 | i32: 850928, 98 | 99 | vector: new Vector3(4, 2, 0), 100 | cframe: new CFrame(6, 6, 6).mul(CFrame.fromOrientation(math.rad(15), math.rad(25), math.rad(35))), 101 | color3: new Color3(0.5258, 0.1919, 0.666), 102 | colorSequence: new ColorSequence([ 103 | new ColorSequenceKeypoint(0, new Color3(1, 0, 0)), 104 | new ColorSequenceKeypoint(0.5, new Color3(0, 1, 0)), 105 | new ColorSequenceKeypoint(1, new Color3(0, 0, 1)), 106 | ]), 107 | numberSequence: new NumberSequence([ 108 | new NumberSequenceKeypoint(0, 0.2), 109 | new NumberSequenceKeypoint(0.5, 1.75), 110 | new NumberSequenceKeypoint(1, 250), 111 | ]), 112 | enum: Enum.KeyCode.W, 113 | 114 | boolean: true, 115 | string: "hello i am a string!", 116 | array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 117 | 118 | union: { type: "string", value: "hey, I am a string!" }, 119 | unionOfStrings: "a very large string that will not exist in the serialized output!", 120 | unionOfPrimitives: true, 121 | 122 | tuple: ["tuple!", 15, true], 123 | tupleWithRest: ["tuple!!", 25, false, "various", "strings", "go", "here !"], 124 | 125 | blob: game.GetService("Workspace").Terrain, 126 | unknown: ["hey i can be any value, and I will serialize correctly!"], 127 | 128 | map: new Map(), 129 | set: new Set(), 130 | 131 | packed: { 132 | num: 35, 133 | a: true, 134 | b: false, 135 | c: true, 136 | d: false, 137 | e: true, 138 | f: false, 139 | g: true, 140 | h: false, 141 | }, 142 | }; 143 | 144 | testData.map.set(testData.blob, true); 145 | testData.set.add({ type: "string", value: "yo!" }); 146 | testData.set.add({ type: "number", value: 69420 }); 147 | 148 | const serializer = createBinarySerializer(); 149 | 150 | const serialized = serializer.serialize(testData); 151 | print("serializing", "blob:", serialized.blobs); 152 | print(buffer.tostring(serialized.buffer)); 153 | 154 | print("deserialized", serializer.deserialize(serialized.buffer, serialized.blobs)); 155 | print("original value", testData); 156 | ``` 157 | 158 | ## Serialization Tradeoffs 159 | 160 | ### CFrames 161 | 162 | CFrames must be orthonormalized to be sent through the binary serializer. If you have a non orthogonal CFrame, you should send the components and reconstruct it once received. 163 | 164 | ### Color3 165 | 166 | Color3 values are serialized as three u8 values, which means that if you are not creating a Color3 with values that are on the RGB spectrum there may be a loss of precision. It also means that you cannnot have infinity, NaN or other large values inside the Color3. 167 | -------------------------------------------------------------------------------- /default.project.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flamework-binary-serializer", 3 | "globIgnorePaths": [ 4 | "**/package.json", 5 | "**/tsconfig.json" 6 | ], 7 | "tree": { 8 | "$className": "Folder", 9 | "out": { 10 | "$path": "out" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /flamework.build: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "flameworkVersion": "1.1.1", 4 | "identifiers": {}, 5 | "metadata": {}, 6 | "identifierPrefix": "@rbxts/flamework-binary-serializer" 7 | } -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/flamework-binary-serializer", 3 | "version": "0.6.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@rbxts/flamework-binary-serializer", 9 | "version": "0.6.0", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "@flamework/core": "^1.1.1", 13 | "@rbxts/compiler-types": "^2.3.0-types.0", 14 | "@rbxts/types": "^1.0.754", 15 | "@typescript-eslint/eslint-plugin": "^7.0.1", 16 | "@typescript-eslint/parser": "^7.0.1", 17 | "eslint": "^8.56.0", 18 | "eslint-config-prettier": "^8.3.0", 19 | "eslint-plugin-prettier": "^3.4.0", 20 | "eslint-plugin-roblox-ts": "^0.0.36", 21 | "prettier": "^2.3.2", 22 | "rbxts-transformer-flamework": "^1.1.1", 23 | "roblox-ts": "^2.3.0", 24 | "typescript": "^5.2.2" 25 | }, 26 | "peerDependencies": { 27 | "@flamework/core": "*", 28 | "rbxts-transformer-flamework": "*" 29 | } 30 | }, 31 | "node_modules/@aashutoshrathi/word-wrap": { 32 | "version": "1.2.6", 33 | "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", 34 | "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", 35 | "dev": true, 36 | "engines": { 37 | "node": ">=0.10.0" 38 | } 39 | }, 40 | "node_modules/@eslint-community/eslint-utils": { 41 | "version": "4.4.0", 42 | "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", 43 | "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", 44 | "dev": true, 45 | "dependencies": { 46 | "eslint-visitor-keys": "^3.3.0" 47 | }, 48 | "engines": { 49 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 50 | }, 51 | "peerDependencies": { 52 | "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" 53 | } 54 | }, 55 | "node_modules/@eslint-community/regexpp": { 56 | "version": "4.10.0", 57 | "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", 58 | "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", 59 | "dev": true, 60 | "engines": { 61 | "node": "^12.0.0 || ^14.0.0 || >=16.0.0" 62 | } 63 | }, 64 | "node_modules/@eslint/eslintrc": { 65 | "version": "2.1.4", 66 | "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", 67 | "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", 68 | "dev": true, 69 | "dependencies": { 70 | "ajv": "^6.12.4", 71 | "debug": "^4.3.2", 72 | "espree": "^9.6.0", 73 | "globals": "^13.19.0", 74 | "ignore": "^5.2.0", 75 | "import-fresh": "^3.2.1", 76 | "js-yaml": "^4.1.0", 77 | "minimatch": "^3.1.2", 78 | "strip-json-comments": "^3.1.1" 79 | }, 80 | "engines": { 81 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 82 | }, 83 | "funding": { 84 | "url": "https://opencollective.com/eslint" 85 | } 86 | }, 87 | "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { 88 | "version": "1.1.11", 89 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 90 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 91 | "dev": true, 92 | "dependencies": { 93 | "balanced-match": "^1.0.0", 94 | "concat-map": "0.0.1" 95 | } 96 | }, 97 | "node_modules/@eslint/eslintrc/node_modules/minimatch": { 98 | "version": "3.1.2", 99 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 100 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 101 | "dev": true, 102 | "dependencies": { 103 | "brace-expansion": "^1.1.7" 104 | }, 105 | "engines": { 106 | "node": "*" 107 | } 108 | }, 109 | "node_modules/@eslint/js": { 110 | "version": "8.56.0", 111 | "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.56.0.tgz", 112 | "integrity": "sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==", 113 | "dev": true, 114 | "engines": { 115 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 116 | } 117 | }, 118 | "node_modules/@flamework/core": { 119 | "version": "1.1.1", 120 | "resolved": "https://registry.npmjs.org/@flamework/core/-/core-1.1.1.tgz", 121 | "integrity": "sha512-jlOubMWTkf4hPMjk+PBgGDEbw7Tj16woJ9vljCDfZpBC0bRCKhFqnqi8ua9Aqoz+o8zZqTOZakagcUp1DBGG2g==", 122 | "dev": true, 123 | "dependencies": { 124 | "@rbxts/maid": "^1.0.0-ts.1", 125 | "@rbxts/object-utils": "^1.0.4", 126 | "@rbxts/services": "^1.1.4", 127 | "@rbxts/signal": "^1.0.3", 128 | "@rbxts/t": "^3.1.0" 129 | } 130 | }, 131 | "node_modules/@humanwhocodes/config-array": { 132 | "version": "0.11.14", 133 | "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", 134 | "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", 135 | "dev": true, 136 | "dependencies": { 137 | "@humanwhocodes/object-schema": "^2.0.2", 138 | "debug": "^4.3.1", 139 | "minimatch": "^3.0.5" 140 | }, 141 | "engines": { 142 | "node": ">=10.10.0" 143 | } 144 | }, 145 | "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { 146 | "version": "1.1.11", 147 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 148 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 149 | "dev": true, 150 | "dependencies": { 151 | "balanced-match": "^1.0.0", 152 | "concat-map": "0.0.1" 153 | } 154 | }, 155 | "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { 156 | "version": "3.1.2", 157 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 158 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 159 | "dev": true, 160 | "dependencies": { 161 | "brace-expansion": "^1.1.7" 162 | }, 163 | "engines": { 164 | "node": "*" 165 | } 166 | }, 167 | "node_modules/@humanwhocodes/module-importer": { 168 | "version": "1.0.1", 169 | "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", 170 | "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", 171 | "dev": true, 172 | "engines": { 173 | "node": ">=12.22" 174 | }, 175 | "funding": { 176 | "type": "github", 177 | "url": "https://github.com/sponsors/nzakas" 178 | } 179 | }, 180 | "node_modules/@humanwhocodes/object-schema": { 181 | "version": "2.0.2", 182 | "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", 183 | "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", 184 | "dev": true 185 | }, 186 | "node_modules/@nodelib/fs.scandir": { 187 | "version": "2.1.5", 188 | "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", 189 | "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", 190 | "dev": true, 191 | "dependencies": { 192 | "@nodelib/fs.stat": "2.0.5", 193 | "run-parallel": "^1.1.9" 194 | }, 195 | "engines": { 196 | "node": ">= 8" 197 | } 198 | }, 199 | "node_modules/@nodelib/fs.stat": { 200 | "version": "2.0.5", 201 | "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", 202 | "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", 203 | "dev": true, 204 | "engines": { 205 | "node": ">= 8" 206 | } 207 | }, 208 | "node_modules/@nodelib/fs.walk": { 209 | "version": "1.2.8", 210 | "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", 211 | "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", 212 | "dev": true, 213 | "dependencies": { 214 | "@nodelib/fs.scandir": "2.1.5", 215 | "fastq": "^1.6.0" 216 | }, 217 | "engines": { 218 | "node": ">= 8" 219 | } 220 | }, 221 | "node_modules/@rbxts/compiler-types": { 222 | "version": "2.3.0-types.0", 223 | "resolved": "https://registry.npmjs.org/@rbxts/compiler-types/-/compiler-types-2.3.0-types.0.tgz", 224 | "integrity": "sha512-2kllUXLlK22OzSi3GmLT+/O70U9oB2iXp4rGFk5FLwc1pqf4W9EsInuJ0t7poLE2XnqnpW/ojff9CUNivI2+lw==", 225 | "dev": true 226 | }, 227 | "node_modules/@rbxts/maid": { 228 | "version": "1.1.0", 229 | "resolved": "https://registry.npmjs.org/@rbxts/maid/-/maid-1.1.0.tgz", 230 | "integrity": "sha512-bVWXZ0p2M3OJzPzvN5fY0T4s37ezUMY7EX31Xspp7Ds4C/K9yE4MHMRXjtlNvsYVPmoc5tdhAbpZY02Veix5lg==", 231 | "dev": true 232 | }, 233 | "node_modules/@rbxts/object-utils": { 234 | "version": "1.0.4", 235 | "resolved": "https://registry.npmjs.org/@rbxts/object-utils/-/object-utils-1.0.4.tgz", 236 | "integrity": "sha512-dLLhf022ipV+9i910sOE7kl9losKHoon0WgeerHqVMQA5EYsLUsVT2AxhJuhk8MiDn5oJ2GiFofE/LadY9TpJQ==", 237 | "dev": true 238 | }, 239 | "node_modules/@rbxts/services": { 240 | "version": "1.5.4", 241 | "resolved": "https://registry.npmjs.org/@rbxts/services/-/services-1.5.4.tgz", 242 | "integrity": "sha512-Klh+gRIT8zy3xRTX1YD6FZ9nAMzkzPLTHmciigt59PEF7PJXo31CF0IU0RegQ7VSMrz1iOnXwxbIwy3htcMfOA==", 243 | "dev": true 244 | }, 245 | "node_modules/@rbxts/signal": { 246 | "version": "1.1.1", 247 | "resolved": "https://registry.npmjs.org/@rbxts/signal/-/signal-1.1.1.tgz", 248 | "integrity": "sha512-WX+ONE+ld4pG9PvRkR8OgDld9NpaV1RfXyUIw+Q2oXP/5rehkYzvt20NWtrLAP3NhMc5inYInLd+hnufey6nFw==", 249 | "dev": true 250 | }, 251 | "node_modules/@rbxts/t": { 252 | "version": "3.1.1", 253 | "resolved": "https://registry.npmjs.org/@rbxts/t/-/t-3.1.1.tgz", 254 | "integrity": "sha512-r+IvHHGLt9ZM8+cJAs5Q7ZyGaSlsbkXTaeat85FxRmjjozykHLbQ24rY/5utJbp63a5uCy3Wyl1fGv/Gk/GnIw==", 255 | "dev": true 256 | }, 257 | "node_modules/@rbxts/types": { 258 | "version": "1.0.754", 259 | "resolved": "https://registry.npmjs.org/@rbxts/types/-/types-1.0.754.tgz", 260 | "integrity": "sha512-qwK2yN5OxOHon1y6xm1j3l1+PFW/m1xRRpXHw1p6SEOeZqap0GUTXwIczyiZwzOZNo2e4iHwOpRfCyGnzrNYoA==", 261 | "dev": true 262 | }, 263 | "node_modules/@roblox-ts/luau-ast": { 264 | "version": "1.0.11", 265 | "resolved": "https://registry.npmjs.org/@roblox-ts/luau-ast/-/luau-ast-1.0.11.tgz", 266 | "integrity": "sha512-+maoLYpqY0HK8ugLFHS3qz0phMyDaN3i21jjW75T2ZaqJg84heKDUo98iXClvnx3mUDhW10IxqH+cYJ2iftYhQ==", 267 | "dev": true 268 | }, 269 | "node_modules/@roblox-ts/path-translator": { 270 | "version": "1.0.0", 271 | "resolved": "https://registry.npmjs.org/@roblox-ts/path-translator/-/path-translator-1.0.0.tgz", 272 | "integrity": "sha512-Lp6qVUqjmXIrICy2KPKRiX8IkJ+lNqn6RqoUplLiTr+4JehIN+mJv0tTnE72XRyIfcx0VWl5nKrRwUuqcOj1yg==", 273 | "dev": true, 274 | "dependencies": { 275 | "ajv": "^8.12.0", 276 | "fs-extra": "^11.2.0" 277 | } 278 | }, 279 | "node_modules/@roblox-ts/path-translator/node_modules/ajv": { 280 | "version": "8.12.0", 281 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", 282 | "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", 283 | "dev": true, 284 | "dependencies": { 285 | "fast-deep-equal": "^3.1.1", 286 | "json-schema-traverse": "^1.0.0", 287 | "require-from-string": "^2.0.2", 288 | "uri-js": "^4.2.2" 289 | }, 290 | "funding": { 291 | "type": "github", 292 | "url": "https://github.com/sponsors/epoberezkin" 293 | } 294 | }, 295 | "node_modules/@roblox-ts/path-translator/node_modules/fs-extra": { 296 | "version": "11.2.0", 297 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", 298 | "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", 299 | "dev": true, 300 | "dependencies": { 301 | "graceful-fs": "^4.2.0", 302 | "jsonfile": "^6.0.1", 303 | "universalify": "^2.0.0" 304 | }, 305 | "engines": { 306 | "node": ">=14.14" 307 | } 308 | }, 309 | "node_modules/@roblox-ts/path-translator/node_modules/json-schema-traverse": { 310 | "version": "1.0.0", 311 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 312 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", 313 | "dev": true 314 | }, 315 | "node_modules/@roblox-ts/rojo-resolver": { 316 | "version": "1.0.6", 317 | "resolved": "https://registry.npmjs.org/@roblox-ts/rojo-resolver/-/rojo-resolver-1.0.6.tgz", 318 | "integrity": "sha512-+heTECMo6BdH3a3h4DCj+8kJvwKuxWqBevcW/m2BzQaVtmo1GtLa4V4bJCMvDuAMeEqYKQZUB7546nN2dcqqAA==", 319 | "dev": true, 320 | "dependencies": { 321 | "ajv": "^8.12.0", 322 | "fs-extra": "^11.1.1" 323 | } 324 | }, 325 | "node_modules/@roblox-ts/rojo-resolver/node_modules/ajv": { 326 | "version": "8.12.0", 327 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", 328 | "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", 329 | "dev": true, 330 | "dependencies": { 331 | "fast-deep-equal": "^3.1.1", 332 | "json-schema-traverse": "^1.0.0", 333 | "require-from-string": "^2.0.2", 334 | "uri-js": "^4.2.2" 335 | }, 336 | "funding": { 337 | "type": "github", 338 | "url": "https://github.com/sponsors/epoberezkin" 339 | } 340 | }, 341 | "node_modules/@roblox-ts/rojo-resolver/node_modules/fs-extra": { 342 | "version": "11.2.0", 343 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", 344 | "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", 345 | "dev": true, 346 | "dependencies": { 347 | "graceful-fs": "^4.2.0", 348 | "jsonfile": "^6.0.1", 349 | "universalify": "^2.0.0" 350 | }, 351 | "engines": { 352 | "node": ">=14.14" 353 | } 354 | }, 355 | "node_modules/@roblox-ts/rojo-resolver/node_modules/json-schema-traverse": { 356 | "version": "1.0.0", 357 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 358 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", 359 | "dev": true 360 | }, 361 | "node_modules/@types/json-schema": { 362 | "version": "7.0.15", 363 | "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", 364 | "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", 365 | "dev": true 366 | }, 367 | "node_modules/@types/node": { 368 | "version": "20.11.19", 369 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.19.tgz", 370 | "integrity": "sha512-7xMnVEcZFu0DikYjWOlRq7NTPETrm7teqUT2WkQjrTIkEgUyyGdWsj/Zg8bEJt5TNklzbPD1X3fqfsHw3SpapQ==", 371 | "dev": true, 372 | "dependencies": { 373 | "undici-types": "~5.26.4" 374 | } 375 | }, 376 | "node_modules/@types/semver": { 377 | "version": "7.5.7", 378 | "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.7.tgz", 379 | "integrity": "sha512-/wdoPq1QqkSj9/QOeKkFquEuPzQbHTWAMPH/PaUMB+JuR31lXhlWXRZ52IpfDYVlDOUBvX09uBrPwxGT1hjNBg==", 380 | "dev": true 381 | }, 382 | "node_modules/@typescript-eslint/eslint-plugin": { 383 | "version": "7.0.1", 384 | "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.0.1.tgz", 385 | "integrity": "sha512-OLvgeBv3vXlnnJGIAgCLYKjgMEU+wBGj07MQ/nxAaON+3mLzX7mJbhRYrVGiVvFiXtwFlkcBa/TtmglHy0UbzQ==", 386 | "dev": true, 387 | "dependencies": { 388 | "@eslint-community/regexpp": "^4.5.1", 389 | "@typescript-eslint/scope-manager": "7.0.1", 390 | "@typescript-eslint/type-utils": "7.0.1", 391 | "@typescript-eslint/utils": "7.0.1", 392 | "@typescript-eslint/visitor-keys": "7.0.1", 393 | "debug": "^4.3.4", 394 | "graphemer": "^1.4.0", 395 | "ignore": "^5.2.4", 396 | "natural-compare": "^1.4.0", 397 | "semver": "^7.5.4", 398 | "ts-api-utils": "^1.0.1" 399 | }, 400 | "engines": { 401 | "node": "^16.0.0 || >=18.0.0" 402 | }, 403 | "funding": { 404 | "type": "opencollective", 405 | "url": "https://opencollective.com/typescript-eslint" 406 | }, 407 | "peerDependencies": { 408 | "@typescript-eslint/parser": "^7.0.0", 409 | "eslint": "^8.56.0" 410 | }, 411 | "peerDependenciesMeta": { 412 | "typescript": { 413 | "optional": true 414 | } 415 | } 416 | }, 417 | "node_modules/@typescript-eslint/experimental-utils": { 418 | "version": "5.62.0", 419 | "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", 420 | "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", 421 | "dev": true, 422 | "dependencies": { 423 | "@typescript-eslint/utils": "5.62.0" 424 | }, 425 | "engines": { 426 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 427 | }, 428 | "funding": { 429 | "type": "opencollective", 430 | "url": "https://opencollective.com/typescript-eslint" 431 | }, 432 | "peerDependencies": { 433 | "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" 434 | } 435 | }, 436 | "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/scope-manager": { 437 | "version": "5.62.0", 438 | "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", 439 | "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", 440 | "dev": true, 441 | "dependencies": { 442 | "@typescript-eslint/types": "5.62.0", 443 | "@typescript-eslint/visitor-keys": "5.62.0" 444 | }, 445 | "engines": { 446 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 447 | }, 448 | "funding": { 449 | "type": "opencollective", 450 | "url": "https://opencollective.com/typescript-eslint" 451 | } 452 | }, 453 | "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/types": { 454 | "version": "5.62.0", 455 | "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", 456 | "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", 457 | "dev": true, 458 | "engines": { 459 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 460 | }, 461 | "funding": { 462 | "type": "opencollective", 463 | "url": "https://opencollective.com/typescript-eslint" 464 | } 465 | }, 466 | "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/typescript-estree": { 467 | "version": "5.62.0", 468 | "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", 469 | "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", 470 | "dev": true, 471 | "dependencies": { 472 | "@typescript-eslint/types": "5.62.0", 473 | "@typescript-eslint/visitor-keys": "5.62.0", 474 | "debug": "^4.3.4", 475 | "globby": "^11.1.0", 476 | "is-glob": "^4.0.3", 477 | "semver": "^7.3.7", 478 | "tsutils": "^3.21.0" 479 | }, 480 | "engines": { 481 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 482 | }, 483 | "funding": { 484 | "type": "opencollective", 485 | "url": "https://opencollective.com/typescript-eslint" 486 | }, 487 | "peerDependenciesMeta": { 488 | "typescript": { 489 | "optional": true 490 | } 491 | } 492 | }, 493 | "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/utils": { 494 | "version": "5.62.0", 495 | "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", 496 | "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", 497 | "dev": true, 498 | "dependencies": { 499 | "@eslint-community/eslint-utils": "^4.2.0", 500 | "@types/json-schema": "^7.0.9", 501 | "@types/semver": "^7.3.12", 502 | "@typescript-eslint/scope-manager": "5.62.0", 503 | "@typescript-eslint/types": "5.62.0", 504 | "@typescript-eslint/typescript-estree": "5.62.0", 505 | "eslint-scope": "^5.1.1", 506 | "semver": "^7.3.7" 507 | }, 508 | "engines": { 509 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 510 | }, 511 | "funding": { 512 | "type": "opencollective", 513 | "url": "https://opencollective.com/typescript-eslint" 514 | }, 515 | "peerDependencies": { 516 | "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" 517 | } 518 | }, 519 | "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/visitor-keys": { 520 | "version": "5.62.0", 521 | "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", 522 | "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", 523 | "dev": true, 524 | "dependencies": { 525 | "@typescript-eslint/types": "5.62.0", 526 | "eslint-visitor-keys": "^3.3.0" 527 | }, 528 | "engines": { 529 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 530 | }, 531 | "funding": { 532 | "type": "opencollective", 533 | "url": "https://opencollective.com/typescript-eslint" 534 | } 535 | }, 536 | "node_modules/@typescript-eslint/experimental-utils/node_modules/eslint-scope": { 537 | "version": "5.1.1", 538 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", 539 | "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", 540 | "dev": true, 541 | "dependencies": { 542 | "esrecurse": "^4.3.0", 543 | "estraverse": "^4.1.1" 544 | }, 545 | "engines": { 546 | "node": ">=8.0.0" 547 | } 548 | }, 549 | "node_modules/@typescript-eslint/experimental-utils/node_modules/estraverse": { 550 | "version": "4.3.0", 551 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", 552 | "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", 553 | "dev": true, 554 | "engines": { 555 | "node": ">=4.0" 556 | } 557 | }, 558 | "node_modules/@typescript-eslint/parser": { 559 | "version": "7.0.1", 560 | "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.0.1.tgz", 561 | "integrity": "sha512-8GcRRZNzaHxKzBPU3tKtFNing571/GwPBeCvmAUw0yBtfE2XVd0zFKJIMSWkHJcPQi0ekxjIts6L/rrZq5cxGQ==", 562 | "dev": true, 563 | "dependencies": { 564 | "@typescript-eslint/scope-manager": "7.0.1", 565 | "@typescript-eslint/types": "7.0.1", 566 | "@typescript-eslint/typescript-estree": "7.0.1", 567 | "@typescript-eslint/visitor-keys": "7.0.1", 568 | "debug": "^4.3.4" 569 | }, 570 | "engines": { 571 | "node": "^16.0.0 || >=18.0.0" 572 | }, 573 | "funding": { 574 | "type": "opencollective", 575 | "url": "https://opencollective.com/typescript-eslint" 576 | }, 577 | "peerDependencies": { 578 | "eslint": "^8.56.0" 579 | }, 580 | "peerDependenciesMeta": { 581 | "typescript": { 582 | "optional": true 583 | } 584 | } 585 | }, 586 | "node_modules/@typescript-eslint/scope-manager": { 587 | "version": "7.0.1", 588 | "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.0.1.tgz", 589 | "integrity": "sha512-v7/T7As10g3bcWOOPAcbnMDuvctHzCFYCG/8R4bK4iYzdFqsZTbXGln0cZNVcwQcwewsYU2BJLay8j0/4zOk4w==", 590 | "dev": true, 591 | "dependencies": { 592 | "@typescript-eslint/types": "7.0.1", 593 | "@typescript-eslint/visitor-keys": "7.0.1" 594 | }, 595 | "engines": { 596 | "node": "^16.0.0 || >=18.0.0" 597 | }, 598 | "funding": { 599 | "type": "opencollective", 600 | "url": "https://opencollective.com/typescript-eslint" 601 | } 602 | }, 603 | "node_modules/@typescript-eslint/type-utils": { 604 | "version": "7.0.1", 605 | "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.0.1.tgz", 606 | "integrity": "sha512-YtT9UcstTG5Yqy4xtLiClm1ZpM/pWVGFnkAa90UfdkkZsR1eP2mR/1jbHeYp8Ay1l1JHPyGvoUYR6o3On5Nhmw==", 607 | "dev": true, 608 | "dependencies": { 609 | "@typescript-eslint/typescript-estree": "7.0.1", 610 | "@typescript-eslint/utils": "7.0.1", 611 | "debug": "^4.3.4", 612 | "ts-api-utils": "^1.0.1" 613 | }, 614 | "engines": { 615 | "node": "^16.0.0 || >=18.0.0" 616 | }, 617 | "funding": { 618 | "type": "opencollective", 619 | "url": "https://opencollective.com/typescript-eslint" 620 | }, 621 | "peerDependencies": { 622 | "eslint": "^8.56.0" 623 | }, 624 | "peerDependenciesMeta": { 625 | "typescript": { 626 | "optional": true 627 | } 628 | } 629 | }, 630 | "node_modules/@typescript-eslint/types": { 631 | "version": "7.0.1", 632 | "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.0.1.tgz", 633 | "integrity": "sha512-uJDfmirz4FHib6ENju/7cz9SdMSkeVvJDK3VcMFvf/hAShg8C74FW+06MaQPODHfDJp/z/zHfgawIJRjlu0RLg==", 634 | "dev": true, 635 | "engines": { 636 | "node": "^16.0.0 || >=18.0.0" 637 | }, 638 | "funding": { 639 | "type": "opencollective", 640 | "url": "https://opencollective.com/typescript-eslint" 641 | } 642 | }, 643 | "node_modules/@typescript-eslint/typescript-estree": { 644 | "version": "7.0.1", 645 | "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.0.1.tgz", 646 | "integrity": "sha512-SO9wHb6ph0/FN5OJxH4MiPscGah5wjOd0RRpaLvuBv9g8565Fgu0uMySFEPqwPHiQU90yzJ2FjRYKGrAhS1xig==", 647 | "dev": true, 648 | "dependencies": { 649 | "@typescript-eslint/types": "7.0.1", 650 | "@typescript-eslint/visitor-keys": "7.0.1", 651 | "debug": "^4.3.4", 652 | "globby": "^11.1.0", 653 | "is-glob": "^4.0.3", 654 | "minimatch": "9.0.3", 655 | "semver": "^7.5.4", 656 | "ts-api-utils": "^1.0.1" 657 | }, 658 | "engines": { 659 | "node": "^16.0.0 || >=18.0.0" 660 | }, 661 | "funding": { 662 | "type": "opencollective", 663 | "url": "https://opencollective.com/typescript-eslint" 664 | }, 665 | "peerDependenciesMeta": { 666 | "typescript": { 667 | "optional": true 668 | } 669 | } 670 | }, 671 | "node_modules/@typescript-eslint/utils": { 672 | "version": "7.0.1", 673 | "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.0.1.tgz", 674 | "integrity": "sha512-oe4his30JgPbnv+9Vef1h48jm0S6ft4mNwi9wj7bX10joGn07QRfqIqFHoMiajrtoU88cIhXf8ahwgrcbNLgPA==", 675 | "dev": true, 676 | "dependencies": { 677 | "@eslint-community/eslint-utils": "^4.4.0", 678 | "@types/json-schema": "^7.0.12", 679 | "@types/semver": "^7.5.0", 680 | "@typescript-eslint/scope-manager": "7.0.1", 681 | "@typescript-eslint/types": "7.0.1", 682 | "@typescript-eslint/typescript-estree": "7.0.1", 683 | "semver": "^7.5.4" 684 | }, 685 | "engines": { 686 | "node": "^16.0.0 || >=18.0.0" 687 | }, 688 | "funding": { 689 | "type": "opencollective", 690 | "url": "https://opencollective.com/typescript-eslint" 691 | }, 692 | "peerDependencies": { 693 | "eslint": "^8.56.0" 694 | } 695 | }, 696 | "node_modules/@typescript-eslint/visitor-keys": { 697 | "version": "7.0.1", 698 | "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.0.1.tgz", 699 | "integrity": "sha512-hwAgrOyk++RTXrP4KzCg7zB2U0xt7RUU0ZdMSCsqF3eKUwkdXUMyTb0qdCuji7VIbcpG62kKTU9M1J1c9UpFBw==", 700 | "dev": true, 701 | "dependencies": { 702 | "@typescript-eslint/types": "7.0.1", 703 | "eslint-visitor-keys": "^3.4.1" 704 | }, 705 | "engines": { 706 | "node": "^16.0.0 || >=18.0.0" 707 | }, 708 | "funding": { 709 | "type": "opencollective", 710 | "url": "https://opencollective.com/typescript-eslint" 711 | } 712 | }, 713 | "node_modules/@ungap/structured-clone": { 714 | "version": "1.2.0", 715 | "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", 716 | "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", 717 | "dev": true 718 | }, 719 | "node_modules/acorn": { 720 | "version": "8.11.3", 721 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", 722 | "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", 723 | "dev": true, 724 | "bin": { 725 | "acorn": "bin/acorn" 726 | }, 727 | "engines": { 728 | "node": ">=0.4.0" 729 | } 730 | }, 731 | "node_modules/acorn-jsx": { 732 | "version": "5.3.2", 733 | "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", 734 | "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", 735 | "dev": true, 736 | "peerDependencies": { 737 | "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 738 | } 739 | }, 740 | "node_modules/ajv": { 741 | "version": "6.12.6", 742 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 743 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 744 | "dev": true, 745 | "dependencies": { 746 | "fast-deep-equal": "^3.1.1", 747 | "fast-json-stable-stringify": "^2.0.0", 748 | "json-schema-traverse": "^0.4.1", 749 | "uri-js": "^4.2.2" 750 | }, 751 | "funding": { 752 | "type": "github", 753 | "url": "https://github.com/sponsors/epoberezkin" 754 | } 755 | }, 756 | "node_modules/ansi-regex": { 757 | "version": "5.0.1", 758 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", 759 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", 760 | "dev": true, 761 | "engines": { 762 | "node": ">=8" 763 | } 764 | }, 765 | "node_modules/ansi-styles": { 766 | "version": "4.3.0", 767 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", 768 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", 769 | "dev": true, 770 | "dependencies": { 771 | "color-convert": "^2.0.1" 772 | }, 773 | "engines": { 774 | "node": ">=8" 775 | }, 776 | "funding": { 777 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 778 | } 779 | }, 780 | "node_modules/anymatch": { 781 | "version": "3.1.3", 782 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", 783 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", 784 | "dev": true, 785 | "dependencies": { 786 | "normalize-path": "^3.0.0", 787 | "picomatch": "^2.0.4" 788 | }, 789 | "engines": { 790 | "node": ">= 8" 791 | } 792 | }, 793 | "node_modules/argparse": { 794 | "version": "2.0.1", 795 | "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", 796 | "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", 797 | "dev": true 798 | }, 799 | "node_modules/array-union": { 800 | "version": "2.1.0", 801 | "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", 802 | "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", 803 | "dev": true, 804 | "engines": { 805 | "node": ">=8" 806 | } 807 | }, 808 | "node_modules/balanced-match": { 809 | "version": "1.0.2", 810 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 811 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 812 | "dev": true 813 | }, 814 | "node_modules/binary-extensions": { 815 | "version": "2.2.0", 816 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", 817 | "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", 818 | "dev": true, 819 | "engines": { 820 | "node": ">=8" 821 | } 822 | }, 823 | "node_modules/brace-expansion": { 824 | "version": "2.0.1", 825 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", 826 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", 827 | "dev": true, 828 | "dependencies": { 829 | "balanced-match": "^1.0.0" 830 | } 831 | }, 832 | "node_modules/braces": { 833 | "version": "3.0.2", 834 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", 835 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", 836 | "dev": true, 837 | "dependencies": { 838 | "fill-range": "^7.0.1" 839 | }, 840 | "engines": { 841 | "node": ">=8" 842 | } 843 | }, 844 | "node_modules/callsites": { 845 | "version": "3.1.0", 846 | "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", 847 | "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", 848 | "dev": true, 849 | "engines": { 850 | "node": ">=6" 851 | } 852 | }, 853 | "node_modules/chalk": { 854 | "version": "4.1.2", 855 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", 856 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", 857 | "dev": true, 858 | "dependencies": { 859 | "ansi-styles": "^4.1.0", 860 | "supports-color": "^7.1.0" 861 | }, 862 | "engines": { 863 | "node": ">=10" 864 | }, 865 | "funding": { 866 | "url": "https://github.com/chalk/chalk?sponsor=1" 867 | } 868 | }, 869 | "node_modules/chokidar": { 870 | "version": "3.6.0", 871 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", 872 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", 873 | "dev": true, 874 | "dependencies": { 875 | "anymatch": "~3.1.2", 876 | "braces": "~3.0.2", 877 | "glob-parent": "~5.1.2", 878 | "is-binary-path": "~2.1.0", 879 | "is-glob": "~4.0.1", 880 | "normalize-path": "~3.0.0", 881 | "readdirp": "~3.6.0" 882 | }, 883 | "engines": { 884 | "node": ">= 8.10.0" 885 | }, 886 | "funding": { 887 | "url": "https://paulmillr.com/funding/" 888 | }, 889 | "optionalDependencies": { 890 | "fsevents": "~2.3.2" 891 | } 892 | }, 893 | "node_modules/chokidar/node_modules/glob-parent": { 894 | "version": "5.1.2", 895 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 896 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 897 | "dev": true, 898 | "dependencies": { 899 | "is-glob": "^4.0.1" 900 | }, 901 | "engines": { 902 | "node": ">= 6" 903 | } 904 | }, 905 | "node_modules/cliui": { 906 | "version": "8.0.1", 907 | "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", 908 | "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", 909 | "dev": true, 910 | "dependencies": { 911 | "string-width": "^4.2.0", 912 | "strip-ansi": "^6.0.1", 913 | "wrap-ansi": "^7.0.0" 914 | }, 915 | "engines": { 916 | "node": ">=12" 917 | } 918 | }, 919 | "node_modules/color-convert": { 920 | "version": "2.0.1", 921 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 922 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 923 | "dev": true, 924 | "dependencies": { 925 | "color-name": "~1.1.4" 926 | }, 927 | "engines": { 928 | "node": ">=7.0.0" 929 | } 930 | }, 931 | "node_modules/color-name": { 932 | "version": "1.1.4", 933 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 934 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 935 | "dev": true 936 | }, 937 | "node_modules/concat-map": { 938 | "version": "0.0.1", 939 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 940 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 941 | "dev": true 942 | }, 943 | "node_modules/cross-spawn": { 944 | "version": "7.0.3", 945 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", 946 | "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", 947 | "dev": true, 948 | "dependencies": { 949 | "path-key": "^3.1.0", 950 | "shebang-command": "^2.0.0", 951 | "which": "^2.0.1" 952 | }, 953 | "engines": { 954 | "node": ">= 8" 955 | } 956 | }, 957 | "node_modules/debug": { 958 | "version": "4.3.4", 959 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 960 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 961 | "dev": true, 962 | "dependencies": { 963 | "ms": "2.1.2" 964 | }, 965 | "engines": { 966 | "node": ">=6.0" 967 | }, 968 | "peerDependenciesMeta": { 969 | "supports-color": { 970 | "optional": true 971 | } 972 | } 973 | }, 974 | "node_modules/deep-is": { 975 | "version": "0.1.4", 976 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", 977 | "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", 978 | "dev": true 979 | }, 980 | "node_modules/dir-glob": { 981 | "version": "3.0.1", 982 | "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", 983 | "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", 984 | "dev": true, 985 | "dependencies": { 986 | "path-type": "^4.0.0" 987 | }, 988 | "engines": { 989 | "node": ">=8" 990 | } 991 | }, 992 | "node_modules/doctrine": { 993 | "version": "3.0.0", 994 | "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 995 | "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", 996 | "dev": true, 997 | "dependencies": { 998 | "esutils": "^2.0.2" 999 | }, 1000 | "engines": { 1001 | "node": ">=6.0.0" 1002 | } 1003 | }, 1004 | "node_modules/emoji-regex": { 1005 | "version": "8.0.0", 1006 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", 1007 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", 1008 | "dev": true 1009 | }, 1010 | "node_modules/escalade": { 1011 | "version": "3.1.2", 1012 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", 1013 | "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", 1014 | "dev": true, 1015 | "engines": { 1016 | "node": ">=6" 1017 | } 1018 | }, 1019 | "node_modules/escape-string-regexp": { 1020 | "version": "4.0.0", 1021 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", 1022 | "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", 1023 | "dev": true, 1024 | "engines": { 1025 | "node": ">=10" 1026 | }, 1027 | "funding": { 1028 | "url": "https://github.com/sponsors/sindresorhus" 1029 | } 1030 | }, 1031 | "node_modules/eslint": { 1032 | "version": "8.56.0", 1033 | "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.56.0.tgz", 1034 | "integrity": "sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==", 1035 | "dev": true, 1036 | "dependencies": { 1037 | "@eslint-community/eslint-utils": "^4.2.0", 1038 | "@eslint-community/regexpp": "^4.6.1", 1039 | "@eslint/eslintrc": "^2.1.4", 1040 | "@eslint/js": "8.56.0", 1041 | "@humanwhocodes/config-array": "^0.11.13", 1042 | "@humanwhocodes/module-importer": "^1.0.1", 1043 | "@nodelib/fs.walk": "^1.2.8", 1044 | "@ungap/structured-clone": "^1.2.0", 1045 | "ajv": "^6.12.4", 1046 | "chalk": "^4.0.0", 1047 | "cross-spawn": "^7.0.2", 1048 | "debug": "^4.3.2", 1049 | "doctrine": "^3.0.0", 1050 | "escape-string-regexp": "^4.0.0", 1051 | "eslint-scope": "^7.2.2", 1052 | "eslint-visitor-keys": "^3.4.3", 1053 | "espree": "^9.6.1", 1054 | "esquery": "^1.4.2", 1055 | "esutils": "^2.0.2", 1056 | "fast-deep-equal": "^3.1.3", 1057 | "file-entry-cache": "^6.0.1", 1058 | "find-up": "^5.0.0", 1059 | "glob-parent": "^6.0.2", 1060 | "globals": "^13.19.0", 1061 | "graphemer": "^1.4.0", 1062 | "ignore": "^5.2.0", 1063 | "imurmurhash": "^0.1.4", 1064 | "is-glob": "^4.0.0", 1065 | "is-path-inside": "^3.0.3", 1066 | "js-yaml": "^4.1.0", 1067 | "json-stable-stringify-without-jsonify": "^1.0.1", 1068 | "levn": "^0.4.1", 1069 | "lodash.merge": "^4.6.2", 1070 | "minimatch": "^3.1.2", 1071 | "natural-compare": "^1.4.0", 1072 | "optionator": "^0.9.3", 1073 | "strip-ansi": "^6.0.1", 1074 | "text-table": "^0.2.0" 1075 | }, 1076 | "bin": { 1077 | "eslint": "bin/eslint.js" 1078 | }, 1079 | "engines": { 1080 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1081 | }, 1082 | "funding": { 1083 | "url": "https://opencollective.com/eslint" 1084 | } 1085 | }, 1086 | "node_modules/eslint-config-prettier": { 1087 | "version": "8.10.0", 1088 | "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", 1089 | "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", 1090 | "dev": true, 1091 | "bin": { 1092 | "eslint-config-prettier": "bin/cli.js" 1093 | }, 1094 | "peerDependencies": { 1095 | "eslint": ">=7.0.0" 1096 | } 1097 | }, 1098 | "node_modules/eslint-plugin-prettier": { 1099 | "version": "3.4.1", 1100 | "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz", 1101 | "integrity": "sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g==", 1102 | "dev": true, 1103 | "dependencies": { 1104 | "prettier-linter-helpers": "^1.0.0" 1105 | }, 1106 | "engines": { 1107 | "node": ">=6.0.0" 1108 | }, 1109 | "peerDependencies": { 1110 | "eslint": ">=5.0.0", 1111 | "prettier": ">=1.13.0" 1112 | }, 1113 | "peerDependenciesMeta": { 1114 | "eslint-config-prettier": { 1115 | "optional": true 1116 | } 1117 | } 1118 | }, 1119 | "node_modules/eslint-plugin-roblox-ts": { 1120 | "version": "0.0.36", 1121 | "resolved": "https://registry.npmjs.org/eslint-plugin-roblox-ts/-/eslint-plugin-roblox-ts-0.0.36.tgz", 1122 | "integrity": "sha512-vlbgfHY1heWSyDunVX3gIOamCy28KqrJlDkmuDOyYVBo8uLqdvbFkbWkdGxn59Vky1m6pFcvZGU6TObfmwm5Pg==", 1123 | "dev": true, 1124 | "dependencies": { 1125 | "@types/node": "^20.8.0", 1126 | "@typescript-eslint/experimental-utils": "^5.62.0", 1127 | "typescript": "^5.2.2" 1128 | }, 1129 | "engines": { 1130 | "node": ">=0.10.0" 1131 | } 1132 | }, 1133 | "node_modules/eslint-scope": { 1134 | "version": "7.2.2", 1135 | "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", 1136 | "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", 1137 | "dev": true, 1138 | "dependencies": { 1139 | "esrecurse": "^4.3.0", 1140 | "estraverse": "^5.2.0" 1141 | }, 1142 | "engines": { 1143 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1144 | }, 1145 | "funding": { 1146 | "url": "https://opencollective.com/eslint" 1147 | } 1148 | }, 1149 | "node_modules/eslint-visitor-keys": { 1150 | "version": "3.4.3", 1151 | "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", 1152 | "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", 1153 | "dev": true, 1154 | "engines": { 1155 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1156 | }, 1157 | "funding": { 1158 | "url": "https://opencollective.com/eslint" 1159 | } 1160 | }, 1161 | "node_modules/eslint/node_modules/brace-expansion": { 1162 | "version": "1.1.11", 1163 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1164 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1165 | "dev": true, 1166 | "dependencies": { 1167 | "balanced-match": "^1.0.0", 1168 | "concat-map": "0.0.1" 1169 | } 1170 | }, 1171 | "node_modules/eslint/node_modules/minimatch": { 1172 | "version": "3.1.2", 1173 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1174 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1175 | "dev": true, 1176 | "dependencies": { 1177 | "brace-expansion": "^1.1.7" 1178 | }, 1179 | "engines": { 1180 | "node": "*" 1181 | } 1182 | }, 1183 | "node_modules/espree": { 1184 | "version": "9.6.1", 1185 | "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", 1186 | "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", 1187 | "dev": true, 1188 | "dependencies": { 1189 | "acorn": "^8.9.0", 1190 | "acorn-jsx": "^5.3.2", 1191 | "eslint-visitor-keys": "^3.4.1" 1192 | }, 1193 | "engines": { 1194 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1195 | }, 1196 | "funding": { 1197 | "url": "https://opencollective.com/eslint" 1198 | } 1199 | }, 1200 | "node_modules/esquery": { 1201 | "version": "1.5.0", 1202 | "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", 1203 | "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", 1204 | "dev": true, 1205 | "dependencies": { 1206 | "estraverse": "^5.1.0" 1207 | }, 1208 | "engines": { 1209 | "node": ">=0.10" 1210 | } 1211 | }, 1212 | "node_modules/esrecurse": { 1213 | "version": "4.3.0", 1214 | "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", 1215 | "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", 1216 | "dev": true, 1217 | "dependencies": { 1218 | "estraverse": "^5.2.0" 1219 | }, 1220 | "engines": { 1221 | "node": ">=4.0" 1222 | } 1223 | }, 1224 | "node_modules/estraverse": { 1225 | "version": "5.3.0", 1226 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", 1227 | "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", 1228 | "dev": true, 1229 | "engines": { 1230 | "node": ">=4.0" 1231 | } 1232 | }, 1233 | "node_modules/esutils": { 1234 | "version": "2.0.3", 1235 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", 1236 | "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", 1237 | "dev": true, 1238 | "engines": { 1239 | "node": ">=0.10.0" 1240 | } 1241 | }, 1242 | "node_modules/fast-deep-equal": { 1243 | "version": "3.1.3", 1244 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 1245 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 1246 | "dev": true 1247 | }, 1248 | "node_modules/fast-diff": { 1249 | "version": "1.3.0", 1250 | "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", 1251 | "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", 1252 | "dev": true 1253 | }, 1254 | "node_modules/fast-glob": { 1255 | "version": "3.3.2", 1256 | "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", 1257 | "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", 1258 | "dev": true, 1259 | "dependencies": { 1260 | "@nodelib/fs.stat": "^2.0.2", 1261 | "@nodelib/fs.walk": "^1.2.3", 1262 | "glob-parent": "^5.1.2", 1263 | "merge2": "^1.3.0", 1264 | "micromatch": "^4.0.4" 1265 | }, 1266 | "engines": { 1267 | "node": ">=8.6.0" 1268 | } 1269 | }, 1270 | "node_modules/fast-glob/node_modules/glob-parent": { 1271 | "version": "5.1.2", 1272 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", 1273 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", 1274 | "dev": true, 1275 | "dependencies": { 1276 | "is-glob": "^4.0.1" 1277 | }, 1278 | "engines": { 1279 | "node": ">= 6" 1280 | } 1281 | }, 1282 | "node_modules/fast-json-stable-stringify": { 1283 | "version": "2.1.0", 1284 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 1285 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 1286 | "dev": true 1287 | }, 1288 | "node_modules/fast-levenshtein": { 1289 | "version": "2.0.6", 1290 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 1291 | "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", 1292 | "dev": true 1293 | }, 1294 | "node_modules/fastq": { 1295 | "version": "1.17.1", 1296 | "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", 1297 | "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", 1298 | "dev": true, 1299 | "dependencies": { 1300 | "reusify": "^1.0.4" 1301 | } 1302 | }, 1303 | "node_modules/file-entry-cache": { 1304 | "version": "6.0.1", 1305 | "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", 1306 | "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", 1307 | "dev": true, 1308 | "dependencies": { 1309 | "flat-cache": "^3.0.4" 1310 | }, 1311 | "engines": { 1312 | "node": "^10.12.0 || >=12.0.0" 1313 | } 1314 | }, 1315 | "node_modules/fill-range": { 1316 | "version": "7.0.1", 1317 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", 1318 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", 1319 | "dev": true, 1320 | "dependencies": { 1321 | "to-regex-range": "^5.0.1" 1322 | }, 1323 | "engines": { 1324 | "node": ">=8" 1325 | } 1326 | }, 1327 | "node_modules/find-up": { 1328 | "version": "5.0.0", 1329 | "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", 1330 | "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", 1331 | "dev": true, 1332 | "dependencies": { 1333 | "locate-path": "^6.0.0", 1334 | "path-exists": "^4.0.0" 1335 | }, 1336 | "engines": { 1337 | "node": ">=10" 1338 | }, 1339 | "funding": { 1340 | "url": "https://github.com/sponsors/sindresorhus" 1341 | } 1342 | }, 1343 | "node_modules/flat-cache": { 1344 | "version": "3.2.0", 1345 | "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", 1346 | "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", 1347 | "dev": true, 1348 | "dependencies": { 1349 | "flatted": "^3.2.9", 1350 | "keyv": "^4.5.3", 1351 | "rimraf": "^3.0.2" 1352 | }, 1353 | "engines": { 1354 | "node": "^10.12.0 || >=12.0.0" 1355 | } 1356 | }, 1357 | "node_modules/flatted": { 1358 | "version": "3.2.9", 1359 | "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", 1360 | "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", 1361 | "dev": true 1362 | }, 1363 | "node_modules/fs-extra": { 1364 | "version": "10.1.0", 1365 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", 1366 | "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", 1367 | "dev": true, 1368 | "dependencies": { 1369 | "graceful-fs": "^4.2.0", 1370 | "jsonfile": "^6.0.1", 1371 | "universalify": "^2.0.0" 1372 | }, 1373 | "engines": { 1374 | "node": ">=12" 1375 | } 1376 | }, 1377 | "node_modules/fs.realpath": { 1378 | "version": "1.0.0", 1379 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 1380 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 1381 | "dev": true 1382 | }, 1383 | "node_modules/fsevents": { 1384 | "version": "2.3.3", 1385 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 1386 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 1387 | "dev": true, 1388 | "hasInstallScript": true, 1389 | "optional": true, 1390 | "os": [ 1391 | "darwin" 1392 | ], 1393 | "engines": { 1394 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1395 | } 1396 | }, 1397 | "node_modules/function-bind": { 1398 | "version": "1.1.2", 1399 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 1400 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 1401 | "dev": true, 1402 | "funding": { 1403 | "url": "https://github.com/sponsors/ljharb" 1404 | } 1405 | }, 1406 | "node_modules/get-caller-file": { 1407 | "version": "2.0.5", 1408 | "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", 1409 | "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", 1410 | "dev": true, 1411 | "engines": { 1412 | "node": "6.* || 8.* || >= 10.*" 1413 | } 1414 | }, 1415 | "node_modules/glob": { 1416 | "version": "7.2.3", 1417 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 1418 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 1419 | "dev": true, 1420 | "dependencies": { 1421 | "fs.realpath": "^1.0.0", 1422 | "inflight": "^1.0.4", 1423 | "inherits": "2", 1424 | "minimatch": "^3.1.1", 1425 | "once": "^1.3.0", 1426 | "path-is-absolute": "^1.0.0" 1427 | }, 1428 | "engines": { 1429 | "node": "*" 1430 | }, 1431 | "funding": { 1432 | "url": "https://github.com/sponsors/isaacs" 1433 | } 1434 | }, 1435 | "node_modules/glob-parent": { 1436 | "version": "6.0.2", 1437 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", 1438 | "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", 1439 | "dev": true, 1440 | "dependencies": { 1441 | "is-glob": "^4.0.3" 1442 | }, 1443 | "engines": { 1444 | "node": ">=10.13.0" 1445 | } 1446 | }, 1447 | "node_modules/glob/node_modules/brace-expansion": { 1448 | "version": "1.1.11", 1449 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 1450 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 1451 | "dev": true, 1452 | "dependencies": { 1453 | "balanced-match": "^1.0.0", 1454 | "concat-map": "0.0.1" 1455 | } 1456 | }, 1457 | "node_modules/glob/node_modules/minimatch": { 1458 | "version": "3.1.2", 1459 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 1460 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 1461 | "dev": true, 1462 | "dependencies": { 1463 | "brace-expansion": "^1.1.7" 1464 | }, 1465 | "engines": { 1466 | "node": "*" 1467 | } 1468 | }, 1469 | "node_modules/globals": { 1470 | "version": "13.24.0", 1471 | "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", 1472 | "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", 1473 | "dev": true, 1474 | "dependencies": { 1475 | "type-fest": "^0.20.2" 1476 | }, 1477 | "engines": { 1478 | "node": ">=8" 1479 | }, 1480 | "funding": { 1481 | "url": "https://github.com/sponsors/sindresorhus" 1482 | } 1483 | }, 1484 | "node_modules/globby": { 1485 | "version": "11.1.0", 1486 | "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", 1487 | "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", 1488 | "dev": true, 1489 | "dependencies": { 1490 | "array-union": "^2.1.0", 1491 | "dir-glob": "^3.0.1", 1492 | "fast-glob": "^3.2.9", 1493 | "ignore": "^5.2.0", 1494 | "merge2": "^1.4.1", 1495 | "slash": "^3.0.0" 1496 | }, 1497 | "engines": { 1498 | "node": ">=10" 1499 | }, 1500 | "funding": { 1501 | "url": "https://github.com/sponsors/sindresorhus" 1502 | } 1503 | }, 1504 | "node_modules/graceful-fs": { 1505 | "version": "4.2.11", 1506 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 1507 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 1508 | "dev": true 1509 | }, 1510 | "node_modules/graphemer": { 1511 | "version": "1.4.0", 1512 | "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", 1513 | "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", 1514 | "dev": true 1515 | }, 1516 | "node_modules/has-flag": { 1517 | "version": "4.0.0", 1518 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", 1519 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", 1520 | "dev": true, 1521 | "engines": { 1522 | "node": ">=8" 1523 | } 1524 | }, 1525 | "node_modules/hashids": { 1526 | "version": "2.3.0", 1527 | "resolved": "https://registry.npmjs.org/hashids/-/hashids-2.3.0.tgz", 1528 | "integrity": "sha512-ljM73TE/avEhNnazxaj0Dw3BbEUuLC5yYCQ9RSkSUcT4ZSU6ZebdKCIBJ+xT/DnSYW36E9k82GH1Q6MydSIosQ==", 1529 | "dev": true 1530 | }, 1531 | "node_modules/hasown": { 1532 | "version": "2.0.1", 1533 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", 1534 | "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", 1535 | "dev": true, 1536 | "dependencies": { 1537 | "function-bind": "^1.1.2" 1538 | }, 1539 | "engines": { 1540 | "node": ">= 0.4" 1541 | } 1542 | }, 1543 | "node_modules/hosted-git-info": { 1544 | "version": "4.1.0", 1545 | "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", 1546 | "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", 1547 | "dev": true, 1548 | "dependencies": { 1549 | "lru-cache": "^6.0.0" 1550 | }, 1551 | "engines": { 1552 | "node": ">=10" 1553 | } 1554 | }, 1555 | "node_modules/ignore": { 1556 | "version": "5.3.1", 1557 | "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", 1558 | "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", 1559 | "dev": true, 1560 | "engines": { 1561 | "node": ">= 4" 1562 | } 1563 | }, 1564 | "node_modules/import-fresh": { 1565 | "version": "3.3.0", 1566 | "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", 1567 | "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", 1568 | "dev": true, 1569 | "dependencies": { 1570 | "parent-module": "^1.0.0", 1571 | "resolve-from": "^4.0.0" 1572 | }, 1573 | "engines": { 1574 | "node": ">=6" 1575 | }, 1576 | "funding": { 1577 | "url": "https://github.com/sponsors/sindresorhus" 1578 | } 1579 | }, 1580 | "node_modules/imurmurhash": { 1581 | "version": "0.1.4", 1582 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", 1583 | "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", 1584 | "dev": true, 1585 | "engines": { 1586 | "node": ">=0.8.19" 1587 | } 1588 | }, 1589 | "node_modules/inflight": { 1590 | "version": "1.0.6", 1591 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 1592 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 1593 | "dev": true, 1594 | "dependencies": { 1595 | "once": "^1.3.0", 1596 | "wrappy": "1" 1597 | } 1598 | }, 1599 | "node_modules/inherits": { 1600 | "version": "2.0.4", 1601 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1602 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1603 | "dev": true 1604 | }, 1605 | "node_modules/is-binary-path": { 1606 | "version": "2.1.0", 1607 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", 1608 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", 1609 | "dev": true, 1610 | "dependencies": { 1611 | "binary-extensions": "^2.0.0" 1612 | }, 1613 | "engines": { 1614 | "node": ">=8" 1615 | } 1616 | }, 1617 | "node_modules/is-core-module": { 1618 | "version": "2.13.1", 1619 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", 1620 | "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", 1621 | "dev": true, 1622 | "dependencies": { 1623 | "hasown": "^2.0.0" 1624 | }, 1625 | "funding": { 1626 | "url": "https://github.com/sponsors/ljharb" 1627 | } 1628 | }, 1629 | "node_modules/is-extglob": { 1630 | "version": "2.1.1", 1631 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", 1632 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", 1633 | "dev": true, 1634 | "engines": { 1635 | "node": ">=0.10.0" 1636 | } 1637 | }, 1638 | "node_modules/is-fullwidth-code-point": { 1639 | "version": "3.0.0", 1640 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", 1641 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", 1642 | "dev": true, 1643 | "engines": { 1644 | "node": ">=8" 1645 | } 1646 | }, 1647 | "node_modules/is-glob": { 1648 | "version": "4.0.3", 1649 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", 1650 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", 1651 | "dev": true, 1652 | "dependencies": { 1653 | "is-extglob": "^2.1.1" 1654 | }, 1655 | "engines": { 1656 | "node": ">=0.10.0" 1657 | } 1658 | }, 1659 | "node_modules/is-number": { 1660 | "version": "7.0.0", 1661 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", 1662 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", 1663 | "dev": true, 1664 | "engines": { 1665 | "node": ">=0.12.0" 1666 | } 1667 | }, 1668 | "node_modules/is-path-inside": { 1669 | "version": "3.0.3", 1670 | "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", 1671 | "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", 1672 | "dev": true, 1673 | "engines": { 1674 | "node": ">=8" 1675 | } 1676 | }, 1677 | "node_modules/isexe": { 1678 | "version": "2.0.0", 1679 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1680 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 1681 | "dev": true 1682 | }, 1683 | "node_modules/js-yaml": { 1684 | "version": "4.1.0", 1685 | "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", 1686 | "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", 1687 | "dev": true, 1688 | "dependencies": { 1689 | "argparse": "^2.0.1" 1690 | }, 1691 | "bin": { 1692 | "js-yaml": "bin/js-yaml.js" 1693 | } 1694 | }, 1695 | "node_modules/json-buffer": { 1696 | "version": "3.0.1", 1697 | "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", 1698 | "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", 1699 | "dev": true 1700 | }, 1701 | "node_modules/json-schema-traverse": { 1702 | "version": "0.4.1", 1703 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 1704 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 1705 | "dev": true 1706 | }, 1707 | "node_modules/json-stable-stringify-without-jsonify": { 1708 | "version": "1.0.1", 1709 | "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", 1710 | "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", 1711 | "dev": true 1712 | }, 1713 | "node_modules/jsonfile": { 1714 | "version": "6.1.0", 1715 | "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", 1716 | "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", 1717 | "dev": true, 1718 | "dependencies": { 1719 | "universalify": "^2.0.0" 1720 | }, 1721 | "optionalDependencies": { 1722 | "graceful-fs": "^4.1.6" 1723 | } 1724 | }, 1725 | "node_modules/keyv": { 1726 | "version": "4.5.4", 1727 | "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", 1728 | "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", 1729 | "dev": true, 1730 | "dependencies": { 1731 | "json-buffer": "3.0.1" 1732 | } 1733 | }, 1734 | "node_modules/kleur": { 1735 | "version": "4.1.5", 1736 | "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", 1737 | "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", 1738 | "dev": true, 1739 | "engines": { 1740 | "node": ">=6" 1741 | } 1742 | }, 1743 | "node_modules/levn": { 1744 | "version": "0.4.1", 1745 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", 1746 | "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", 1747 | "dev": true, 1748 | "dependencies": { 1749 | "prelude-ls": "^1.2.1", 1750 | "type-check": "~0.4.0" 1751 | }, 1752 | "engines": { 1753 | "node": ">= 0.8.0" 1754 | } 1755 | }, 1756 | "node_modules/locate-path": { 1757 | "version": "6.0.0", 1758 | "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", 1759 | "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", 1760 | "dev": true, 1761 | "dependencies": { 1762 | "p-locate": "^5.0.0" 1763 | }, 1764 | "engines": { 1765 | "node": ">=10" 1766 | }, 1767 | "funding": { 1768 | "url": "https://github.com/sponsors/sindresorhus" 1769 | } 1770 | }, 1771 | "node_modules/lodash.merge": { 1772 | "version": "4.6.2", 1773 | "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", 1774 | "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", 1775 | "dev": true 1776 | }, 1777 | "node_modules/lru-cache": { 1778 | "version": "6.0.0", 1779 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", 1780 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", 1781 | "dev": true, 1782 | "dependencies": { 1783 | "yallist": "^4.0.0" 1784 | }, 1785 | "engines": { 1786 | "node": ">=10" 1787 | } 1788 | }, 1789 | "node_modules/merge2": { 1790 | "version": "1.4.1", 1791 | "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", 1792 | "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", 1793 | "dev": true, 1794 | "engines": { 1795 | "node": ">= 8" 1796 | } 1797 | }, 1798 | "node_modules/micromatch": { 1799 | "version": "4.0.5", 1800 | "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", 1801 | "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", 1802 | "dev": true, 1803 | "dependencies": { 1804 | "braces": "^3.0.2", 1805 | "picomatch": "^2.3.1" 1806 | }, 1807 | "engines": { 1808 | "node": ">=8.6" 1809 | } 1810 | }, 1811 | "node_modules/minimatch": { 1812 | "version": "9.0.3", 1813 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", 1814 | "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", 1815 | "dev": true, 1816 | "dependencies": { 1817 | "brace-expansion": "^2.0.1" 1818 | }, 1819 | "engines": { 1820 | "node": ">=16 || 14 >=14.17" 1821 | }, 1822 | "funding": { 1823 | "url": "https://github.com/sponsors/isaacs" 1824 | } 1825 | }, 1826 | "node_modules/minipass": { 1827 | "version": "4.2.8", 1828 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", 1829 | "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", 1830 | "dev": true, 1831 | "engines": { 1832 | "node": ">=8" 1833 | } 1834 | }, 1835 | "node_modules/ms": { 1836 | "version": "2.1.2", 1837 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 1838 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", 1839 | "dev": true 1840 | }, 1841 | "node_modules/natural-compare": { 1842 | "version": "1.4.0", 1843 | "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", 1844 | "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", 1845 | "dev": true 1846 | }, 1847 | "node_modules/normalize-package-data": { 1848 | "version": "3.0.3", 1849 | "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", 1850 | "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", 1851 | "dev": true, 1852 | "dependencies": { 1853 | "hosted-git-info": "^4.0.1", 1854 | "is-core-module": "^2.5.0", 1855 | "semver": "^7.3.4", 1856 | "validate-npm-package-license": "^3.0.1" 1857 | }, 1858 | "engines": { 1859 | "node": ">=10" 1860 | } 1861 | }, 1862 | "node_modules/normalize-path": { 1863 | "version": "3.0.0", 1864 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", 1865 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", 1866 | "dev": true, 1867 | "engines": { 1868 | "node": ">=0.10.0" 1869 | } 1870 | }, 1871 | "node_modules/once": { 1872 | "version": "1.4.0", 1873 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1874 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1875 | "dev": true, 1876 | "dependencies": { 1877 | "wrappy": "1" 1878 | } 1879 | }, 1880 | "node_modules/optionator": { 1881 | "version": "0.9.3", 1882 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", 1883 | "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", 1884 | "dev": true, 1885 | "dependencies": { 1886 | "@aashutoshrathi/word-wrap": "^1.2.3", 1887 | "deep-is": "^0.1.3", 1888 | "fast-levenshtein": "^2.0.6", 1889 | "levn": "^0.4.1", 1890 | "prelude-ls": "^1.2.1", 1891 | "type-check": "^0.4.0" 1892 | }, 1893 | "engines": { 1894 | "node": ">= 0.8.0" 1895 | } 1896 | }, 1897 | "node_modules/p-limit": { 1898 | "version": "3.1.0", 1899 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", 1900 | "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", 1901 | "dev": true, 1902 | "dependencies": { 1903 | "yocto-queue": "^0.1.0" 1904 | }, 1905 | "engines": { 1906 | "node": ">=10" 1907 | }, 1908 | "funding": { 1909 | "url": "https://github.com/sponsors/sindresorhus" 1910 | } 1911 | }, 1912 | "node_modules/p-locate": { 1913 | "version": "5.0.0", 1914 | "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", 1915 | "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", 1916 | "dev": true, 1917 | "dependencies": { 1918 | "p-limit": "^3.0.2" 1919 | }, 1920 | "engines": { 1921 | "node": ">=10" 1922 | }, 1923 | "funding": { 1924 | "url": "https://github.com/sponsors/sindresorhus" 1925 | } 1926 | }, 1927 | "node_modules/parent-module": { 1928 | "version": "1.0.1", 1929 | "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", 1930 | "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", 1931 | "dev": true, 1932 | "dependencies": { 1933 | "callsites": "^3.0.0" 1934 | }, 1935 | "engines": { 1936 | "node": ">=6" 1937 | } 1938 | }, 1939 | "node_modules/path-exists": { 1940 | "version": "4.0.0", 1941 | "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", 1942 | "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", 1943 | "dev": true, 1944 | "engines": { 1945 | "node": ">=8" 1946 | } 1947 | }, 1948 | "node_modules/path-is-absolute": { 1949 | "version": "1.0.1", 1950 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 1951 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 1952 | "dev": true, 1953 | "engines": { 1954 | "node": ">=0.10.0" 1955 | } 1956 | }, 1957 | "node_modules/path-key": { 1958 | "version": "3.1.1", 1959 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1960 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1961 | "dev": true, 1962 | "engines": { 1963 | "node": ">=8" 1964 | } 1965 | }, 1966 | "node_modules/path-parse": { 1967 | "version": "1.0.7", 1968 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 1969 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 1970 | "dev": true 1971 | }, 1972 | "node_modules/path-scurry": { 1973 | "version": "1.10.1", 1974 | "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", 1975 | "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", 1976 | "dev": true, 1977 | "dependencies": { 1978 | "lru-cache": "^9.1.1 || ^10.0.0", 1979 | "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" 1980 | }, 1981 | "engines": { 1982 | "node": ">=16 || 14 >=14.17" 1983 | }, 1984 | "funding": { 1985 | "url": "https://github.com/sponsors/isaacs" 1986 | } 1987 | }, 1988 | "node_modules/path-scurry/node_modules/lru-cache": { 1989 | "version": "10.2.0", 1990 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", 1991 | "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", 1992 | "dev": true, 1993 | "engines": { 1994 | "node": "14 || >=16.14" 1995 | } 1996 | }, 1997 | "node_modules/path-scurry/node_modules/minipass": { 1998 | "version": "7.0.4", 1999 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", 2000 | "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", 2001 | "dev": true, 2002 | "engines": { 2003 | "node": ">=16 || 14 >=14.17" 2004 | } 2005 | }, 2006 | "node_modules/path-type": { 2007 | "version": "4.0.0", 2008 | "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", 2009 | "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", 2010 | "dev": true, 2011 | "engines": { 2012 | "node": ">=8" 2013 | } 2014 | }, 2015 | "node_modules/picomatch": { 2016 | "version": "2.3.1", 2017 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", 2018 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", 2019 | "dev": true, 2020 | "engines": { 2021 | "node": ">=8.6" 2022 | }, 2023 | "funding": { 2024 | "url": "https://github.com/sponsors/jonschlinkert" 2025 | } 2026 | }, 2027 | "node_modules/prelude-ls": { 2028 | "version": "1.2.1", 2029 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", 2030 | "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", 2031 | "dev": true, 2032 | "engines": { 2033 | "node": ">= 0.8.0" 2034 | } 2035 | }, 2036 | "node_modules/prettier": { 2037 | "version": "2.8.8", 2038 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", 2039 | "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", 2040 | "dev": true, 2041 | "bin": { 2042 | "prettier": "bin-prettier.js" 2043 | }, 2044 | "engines": { 2045 | "node": ">=10.13.0" 2046 | }, 2047 | "funding": { 2048 | "url": "https://github.com/prettier/prettier?sponsor=1" 2049 | } 2050 | }, 2051 | "node_modules/prettier-linter-helpers": { 2052 | "version": "1.0.0", 2053 | "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", 2054 | "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", 2055 | "dev": true, 2056 | "dependencies": { 2057 | "fast-diff": "^1.1.2" 2058 | }, 2059 | "engines": { 2060 | "node": ">=6.0.0" 2061 | } 2062 | }, 2063 | "node_modules/punycode": { 2064 | "version": "2.3.1", 2065 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 2066 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 2067 | "dev": true, 2068 | "engines": { 2069 | "node": ">=6" 2070 | } 2071 | }, 2072 | "node_modules/queue-microtask": { 2073 | "version": "1.2.3", 2074 | "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", 2075 | "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", 2076 | "dev": true, 2077 | "funding": [ 2078 | { 2079 | "type": "github", 2080 | "url": "https://github.com/sponsors/feross" 2081 | }, 2082 | { 2083 | "type": "patreon", 2084 | "url": "https://www.patreon.com/feross" 2085 | }, 2086 | { 2087 | "type": "consulting", 2088 | "url": "https://feross.org/support" 2089 | } 2090 | ] 2091 | }, 2092 | "node_modules/rbxts-transformer-flamework": { 2093 | "version": "1.1.1", 2094 | "resolved": "https://registry.npmjs.org/rbxts-transformer-flamework/-/rbxts-transformer-flamework-1.1.1.tgz", 2095 | "integrity": "sha512-+jzWfvjnYpkPlvXs+4eNc/JEZUY9YJ5SuEd2Lm3R5WlbLkpI5C4c9cI2EGS5Ciyfh4woTEWcWbtwnLcJjJxSEg==", 2096 | "dev": true, 2097 | "dependencies": { 2098 | "@roblox-ts/rojo-resolver": "^1.0.2", 2099 | "ajv": "^8.6.3", 2100 | "chalk": "^4.1.2", 2101 | "fs-extra": "^10.0.0", 2102 | "glob": "9.3", 2103 | "hashids": "^2.2.8", 2104 | "normalize-package-data": "^3.0.3", 2105 | "uuid": "^8.3.2" 2106 | }, 2107 | "peerDependencies": { 2108 | "typescript": "*" 2109 | } 2110 | }, 2111 | "node_modules/rbxts-transformer-flamework/node_modules/ajv": { 2112 | "version": "8.12.0", 2113 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", 2114 | "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", 2115 | "dev": true, 2116 | "dependencies": { 2117 | "fast-deep-equal": "^3.1.1", 2118 | "json-schema-traverse": "^1.0.0", 2119 | "require-from-string": "^2.0.2", 2120 | "uri-js": "^4.2.2" 2121 | }, 2122 | "funding": { 2123 | "type": "github", 2124 | "url": "https://github.com/sponsors/epoberezkin" 2125 | } 2126 | }, 2127 | "node_modules/rbxts-transformer-flamework/node_modules/glob": { 2128 | "version": "9.3.5", 2129 | "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", 2130 | "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", 2131 | "dev": true, 2132 | "dependencies": { 2133 | "fs.realpath": "^1.0.0", 2134 | "minimatch": "^8.0.2", 2135 | "minipass": "^4.2.4", 2136 | "path-scurry": "^1.6.1" 2137 | }, 2138 | "engines": { 2139 | "node": ">=16 || 14 >=14.17" 2140 | }, 2141 | "funding": { 2142 | "url": "https://github.com/sponsors/isaacs" 2143 | } 2144 | }, 2145 | "node_modules/rbxts-transformer-flamework/node_modules/json-schema-traverse": { 2146 | "version": "1.0.0", 2147 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", 2148 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", 2149 | "dev": true 2150 | }, 2151 | "node_modules/rbxts-transformer-flamework/node_modules/minimatch": { 2152 | "version": "8.0.4", 2153 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", 2154 | "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", 2155 | "dev": true, 2156 | "dependencies": { 2157 | "brace-expansion": "^2.0.1" 2158 | }, 2159 | "engines": { 2160 | "node": ">=16 || 14 >=14.17" 2161 | }, 2162 | "funding": { 2163 | "url": "https://github.com/sponsors/isaacs" 2164 | } 2165 | }, 2166 | "node_modules/readdirp": { 2167 | "version": "3.6.0", 2168 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", 2169 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", 2170 | "dev": true, 2171 | "dependencies": { 2172 | "picomatch": "^2.2.1" 2173 | }, 2174 | "engines": { 2175 | "node": ">=8.10.0" 2176 | } 2177 | }, 2178 | "node_modules/require-directory": { 2179 | "version": "2.1.1", 2180 | "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", 2181 | "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", 2182 | "dev": true, 2183 | "engines": { 2184 | "node": ">=0.10.0" 2185 | } 2186 | }, 2187 | "node_modules/require-from-string": { 2188 | "version": "2.0.2", 2189 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", 2190 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", 2191 | "dev": true, 2192 | "engines": { 2193 | "node": ">=0.10.0" 2194 | } 2195 | }, 2196 | "node_modules/resolve": { 2197 | "version": "1.22.8", 2198 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", 2199 | "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", 2200 | "dev": true, 2201 | "dependencies": { 2202 | "is-core-module": "^2.13.0", 2203 | "path-parse": "^1.0.7", 2204 | "supports-preserve-symlinks-flag": "^1.0.0" 2205 | }, 2206 | "bin": { 2207 | "resolve": "bin/resolve" 2208 | }, 2209 | "funding": { 2210 | "url": "https://github.com/sponsors/ljharb" 2211 | } 2212 | }, 2213 | "node_modules/resolve-from": { 2214 | "version": "4.0.0", 2215 | "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", 2216 | "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", 2217 | "dev": true, 2218 | "engines": { 2219 | "node": ">=4" 2220 | } 2221 | }, 2222 | "node_modules/reusify": { 2223 | "version": "1.0.4", 2224 | "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", 2225 | "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", 2226 | "dev": true, 2227 | "engines": { 2228 | "iojs": ">=1.0.0", 2229 | "node": ">=0.10.0" 2230 | } 2231 | }, 2232 | "node_modules/rimraf": { 2233 | "version": "3.0.2", 2234 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", 2235 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", 2236 | "dev": true, 2237 | "dependencies": { 2238 | "glob": "^7.1.3" 2239 | }, 2240 | "bin": { 2241 | "rimraf": "bin.js" 2242 | }, 2243 | "funding": { 2244 | "url": "https://github.com/sponsors/isaacs" 2245 | } 2246 | }, 2247 | "node_modules/roblox-ts": { 2248 | "version": "2.3.0", 2249 | "resolved": "https://registry.npmjs.org/roblox-ts/-/roblox-ts-2.3.0.tgz", 2250 | "integrity": "sha512-swz+3sxHcB1ww5iUkwxzPFqrbWYmjD9uDriLhta5MAShvRFW4Vdku/aBSU4KiLqtVWYvYo32G+5bXg1Pw2yvIA==", 2251 | "dev": true, 2252 | "dependencies": { 2253 | "@roblox-ts/luau-ast": "^1.0.11", 2254 | "@roblox-ts/path-translator": "^1.0.0", 2255 | "@roblox-ts/rojo-resolver": "^1.0.6", 2256 | "chokidar": "^3.6.0", 2257 | "fs-extra": "^11.2.0", 2258 | "kleur": "^4.1.5", 2259 | "resolve": "^1.22.6", 2260 | "typescript": "=5.2.2", 2261 | "yargs": "^17.7.2" 2262 | }, 2263 | "bin": { 2264 | "rbxtsc": "out/CLI/cli.js" 2265 | } 2266 | }, 2267 | "node_modules/roblox-ts/node_modules/fs-extra": { 2268 | "version": "11.2.0", 2269 | "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", 2270 | "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", 2271 | "dev": true, 2272 | "dependencies": { 2273 | "graceful-fs": "^4.2.0", 2274 | "jsonfile": "^6.0.1", 2275 | "universalify": "^2.0.0" 2276 | }, 2277 | "engines": { 2278 | "node": ">=14.14" 2279 | } 2280 | }, 2281 | "node_modules/run-parallel": { 2282 | "version": "1.2.0", 2283 | "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", 2284 | "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", 2285 | "dev": true, 2286 | "funding": [ 2287 | { 2288 | "type": "github", 2289 | "url": "https://github.com/sponsors/feross" 2290 | }, 2291 | { 2292 | "type": "patreon", 2293 | "url": "https://www.patreon.com/feross" 2294 | }, 2295 | { 2296 | "type": "consulting", 2297 | "url": "https://feross.org/support" 2298 | } 2299 | ], 2300 | "dependencies": { 2301 | "queue-microtask": "^1.2.2" 2302 | } 2303 | }, 2304 | "node_modules/semver": { 2305 | "version": "7.6.0", 2306 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", 2307 | "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", 2308 | "dev": true, 2309 | "dependencies": { 2310 | "lru-cache": "^6.0.0" 2311 | }, 2312 | "bin": { 2313 | "semver": "bin/semver.js" 2314 | }, 2315 | "engines": { 2316 | "node": ">=10" 2317 | } 2318 | }, 2319 | "node_modules/shebang-command": { 2320 | "version": "2.0.0", 2321 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 2322 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 2323 | "dev": true, 2324 | "dependencies": { 2325 | "shebang-regex": "^3.0.0" 2326 | }, 2327 | "engines": { 2328 | "node": ">=8" 2329 | } 2330 | }, 2331 | "node_modules/shebang-regex": { 2332 | "version": "3.0.0", 2333 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 2334 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 2335 | "dev": true, 2336 | "engines": { 2337 | "node": ">=8" 2338 | } 2339 | }, 2340 | "node_modules/slash": { 2341 | "version": "3.0.0", 2342 | "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", 2343 | "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", 2344 | "dev": true, 2345 | "engines": { 2346 | "node": ">=8" 2347 | } 2348 | }, 2349 | "node_modules/spdx-correct": { 2350 | "version": "3.2.0", 2351 | "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", 2352 | "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", 2353 | "dev": true, 2354 | "dependencies": { 2355 | "spdx-expression-parse": "^3.0.0", 2356 | "spdx-license-ids": "^3.0.0" 2357 | } 2358 | }, 2359 | "node_modules/spdx-exceptions": { 2360 | "version": "2.5.0", 2361 | "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", 2362 | "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", 2363 | "dev": true 2364 | }, 2365 | "node_modules/spdx-expression-parse": { 2366 | "version": "3.0.1", 2367 | "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", 2368 | "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", 2369 | "dev": true, 2370 | "dependencies": { 2371 | "spdx-exceptions": "^2.1.0", 2372 | "spdx-license-ids": "^3.0.0" 2373 | } 2374 | }, 2375 | "node_modules/spdx-license-ids": { 2376 | "version": "3.0.17", 2377 | "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.17.tgz", 2378 | "integrity": "sha512-sh8PWc/ftMqAAdFiBu6Fy6JUOYjqDJBJvIhpfDMyHrr0Rbp5liZqd4TjtQ/RgfLjKFZb+LMx5hpml5qOWy0qvg==", 2379 | "dev": true 2380 | }, 2381 | "node_modules/string-width": { 2382 | "version": "4.2.3", 2383 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", 2384 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", 2385 | "dev": true, 2386 | "dependencies": { 2387 | "emoji-regex": "^8.0.0", 2388 | "is-fullwidth-code-point": "^3.0.0", 2389 | "strip-ansi": "^6.0.1" 2390 | }, 2391 | "engines": { 2392 | "node": ">=8" 2393 | } 2394 | }, 2395 | "node_modules/strip-ansi": { 2396 | "version": "6.0.1", 2397 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", 2398 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", 2399 | "dev": true, 2400 | "dependencies": { 2401 | "ansi-regex": "^5.0.1" 2402 | }, 2403 | "engines": { 2404 | "node": ">=8" 2405 | } 2406 | }, 2407 | "node_modules/strip-json-comments": { 2408 | "version": "3.1.1", 2409 | "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", 2410 | "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", 2411 | "dev": true, 2412 | "engines": { 2413 | "node": ">=8" 2414 | }, 2415 | "funding": { 2416 | "url": "https://github.com/sponsors/sindresorhus" 2417 | } 2418 | }, 2419 | "node_modules/supports-color": { 2420 | "version": "7.2.0", 2421 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", 2422 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", 2423 | "dev": true, 2424 | "dependencies": { 2425 | "has-flag": "^4.0.0" 2426 | }, 2427 | "engines": { 2428 | "node": ">=8" 2429 | } 2430 | }, 2431 | "node_modules/supports-preserve-symlinks-flag": { 2432 | "version": "1.0.0", 2433 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 2434 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 2435 | "dev": true, 2436 | "engines": { 2437 | "node": ">= 0.4" 2438 | }, 2439 | "funding": { 2440 | "url": "https://github.com/sponsors/ljharb" 2441 | } 2442 | }, 2443 | "node_modules/text-table": { 2444 | "version": "0.2.0", 2445 | "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", 2446 | "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", 2447 | "dev": true 2448 | }, 2449 | "node_modules/to-regex-range": { 2450 | "version": "5.0.1", 2451 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", 2452 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", 2453 | "dev": true, 2454 | "dependencies": { 2455 | "is-number": "^7.0.0" 2456 | }, 2457 | "engines": { 2458 | "node": ">=8.0" 2459 | } 2460 | }, 2461 | "node_modules/ts-api-utils": { 2462 | "version": "1.2.1", 2463 | "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", 2464 | "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", 2465 | "dev": true, 2466 | "engines": { 2467 | "node": ">=16" 2468 | }, 2469 | "peerDependencies": { 2470 | "typescript": ">=4.2.0" 2471 | } 2472 | }, 2473 | "node_modules/tsutils": { 2474 | "version": "3.21.0", 2475 | "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", 2476 | "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", 2477 | "dev": true, 2478 | "dependencies": { 2479 | "tslib": "^1.8.1" 2480 | }, 2481 | "engines": { 2482 | "node": ">= 6" 2483 | }, 2484 | "peerDependencies": { 2485 | "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" 2486 | } 2487 | }, 2488 | "node_modules/tsutils/node_modules/tslib": { 2489 | "version": "1.14.1", 2490 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", 2491 | "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", 2492 | "dev": true 2493 | }, 2494 | "node_modules/type-check": { 2495 | "version": "0.4.0", 2496 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", 2497 | "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", 2498 | "dev": true, 2499 | "dependencies": { 2500 | "prelude-ls": "^1.2.1" 2501 | }, 2502 | "engines": { 2503 | "node": ">= 0.8.0" 2504 | } 2505 | }, 2506 | "node_modules/type-fest": { 2507 | "version": "0.20.2", 2508 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", 2509 | "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", 2510 | "dev": true, 2511 | "engines": { 2512 | "node": ">=10" 2513 | }, 2514 | "funding": { 2515 | "url": "https://github.com/sponsors/sindresorhus" 2516 | } 2517 | }, 2518 | "node_modules/typescript": { 2519 | "version": "5.2.2", 2520 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", 2521 | "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", 2522 | "dev": true, 2523 | "bin": { 2524 | "tsc": "bin/tsc", 2525 | "tsserver": "bin/tsserver" 2526 | }, 2527 | "engines": { 2528 | "node": ">=14.17" 2529 | } 2530 | }, 2531 | "node_modules/undici-types": { 2532 | "version": "5.26.5", 2533 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 2534 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", 2535 | "dev": true 2536 | }, 2537 | "node_modules/universalify": { 2538 | "version": "2.0.1", 2539 | "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", 2540 | "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", 2541 | "dev": true, 2542 | "engines": { 2543 | "node": ">= 10.0.0" 2544 | } 2545 | }, 2546 | "node_modules/uri-js": { 2547 | "version": "4.4.1", 2548 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 2549 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 2550 | "dev": true, 2551 | "dependencies": { 2552 | "punycode": "^2.1.0" 2553 | } 2554 | }, 2555 | "node_modules/uuid": { 2556 | "version": "8.3.2", 2557 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 2558 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", 2559 | "dev": true, 2560 | "bin": { 2561 | "uuid": "dist/bin/uuid" 2562 | } 2563 | }, 2564 | "node_modules/validate-npm-package-license": { 2565 | "version": "3.0.4", 2566 | "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", 2567 | "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", 2568 | "dev": true, 2569 | "dependencies": { 2570 | "spdx-correct": "^3.0.0", 2571 | "spdx-expression-parse": "^3.0.0" 2572 | } 2573 | }, 2574 | "node_modules/which": { 2575 | "version": "2.0.2", 2576 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 2577 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 2578 | "dev": true, 2579 | "dependencies": { 2580 | "isexe": "^2.0.0" 2581 | }, 2582 | "bin": { 2583 | "node-which": "bin/node-which" 2584 | }, 2585 | "engines": { 2586 | "node": ">= 8" 2587 | } 2588 | }, 2589 | "node_modules/wrap-ansi": { 2590 | "version": "7.0.0", 2591 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", 2592 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", 2593 | "dev": true, 2594 | "dependencies": { 2595 | "ansi-styles": "^4.0.0", 2596 | "string-width": "^4.1.0", 2597 | "strip-ansi": "^6.0.0" 2598 | }, 2599 | "engines": { 2600 | "node": ">=10" 2601 | }, 2602 | "funding": { 2603 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 2604 | } 2605 | }, 2606 | "node_modules/wrappy": { 2607 | "version": "1.0.2", 2608 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2609 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 2610 | "dev": true 2611 | }, 2612 | "node_modules/y18n": { 2613 | "version": "5.0.8", 2614 | "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", 2615 | "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", 2616 | "dev": true, 2617 | "engines": { 2618 | "node": ">=10" 2619 | } 2620 | }, 2621 | "node_modules/yallist": { 2622 | "version": "4.0.0", 2623 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", 2624 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", 2625 | "dev": true 2626 | }, 2627 | "node_modules/yargs": { 2628 | "version": "17.7.2", 2629 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", 2630 | "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", 2631 | "dev": true, 2632 | "dependencies": { 2633 | "cliui": "^8.0.1", 2634 | "escalade": "^3.1.1", 2635 | "get-caller-file": "^2.0.5", 2636 | "require-directory": "^2.1.1", 2637 | "string-width": "^4.2.3", 2638 | "y18n": "^5.0.5", 2639 | "yargs-parser": "^21.1.1" 2640 | }, 2641 | "engines": { 2642 | "node": ">=12" 2643 | } 2644 | }, 2645 | "node_modules/yargs-parser": { 2646 | "version": "21.1.1", 2647 | "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", 2648 | "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", 2649 | "dev": true, 2650 | "engines": { 2651 | "node": ">=12" 2652 | } 2653 | }, 2654 | "node_modules/yocto-queue": { 2655 | "version": "0.1.0", 2656 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", 2657 | "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", 2658 | "dev": true, 2659 | "engines": { 2660 | "node": ">=10" 2661 | }, 2662 | "funding": { 2663 | "url": "https://github.com/sponsors/sindresorhus" 2664 | } 2665 | } 2666 | } 2667 | } 2668 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@rbxts/flamework-binary-serializer", 3 | "version": "0.6.0", 4 | "description": "", 5 | "author": "Fireboltofdeath", 6 | "main": "out/init.lua", 7 | "homepage": "https://github.com/Fireboltofdeath/flamework-binary-serializer", 8 | "keywords": [], 9 | "license": "ISC", 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/Fireboltofdeath/flamework-binary-serializer.git" 13 | }, 14 | "publishConfig": { 15 | "access": "public" 16 | }, 17 | "files": [ 18 | "out", 19 | "flamework.build", 20 | "!**/*.tsbuildinfo" 21 | ], 22 | "scripts": { 23 | "build": "rbxtsc", 24 | "watch": "rbxtsc -w", 25 | "prepublishOnly": "npm run build" 26 | }, 27 | "types": "out/index.d.ts", 28 | "peerDependencies": { 29 | "@flamework/core": "*", 30 | "rbxts-transformer-flamework": "*" 31 | }, 32 | "devDependencies": { 33 | "@flamework/core": "^1.1.1", 34 | "@rbxts/compiler-types": "^2.3.0-types.0", 35 | "@rbxts/types": "^1.0.754", 36 | "@typescript-eslint/eslint-plugin": "^7.0.1", 37 | "@typescript-eslint/parser": "^7.0.1", 38 | "eslint": "^8.56.0", 39 | "eslint-config-prettier": "^8.3.0", 40 | "eslint-plugin-prettier": "^3.4.0", 41 | "eslint-plugin-roblox-ts": "^0.0.36", 42 | "prettier": "^2.3.2", 43 | "rbxts-transformer-flamework": "^1.1.1", 44 | "roblox-ts": "^2.3.0", 45 | "typescript": "^5.2.2" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | // This does not account for potential floating point drift but this is likely not an issue for axis aligned orientations. 2 | export const AXIS_ALIGNED_ORIENTATIONS = [ 3 | [0, 0, 0], 4 | [0, 180, 0], 5 | [90, 0, 0], 6 | [-90, -180, 0], 7 | [0, 180, 180], 8 | [0, 0, 180], 9 | [-90, 0, 0], 10 | [90, 180, 0], 11 | [0, 180, 90], 12 | [0, 0, -90], 13 | [0, 90, 90], 14 | [0, -90, -90], 15 | [0, 0, 90], 16 | [0, -180, -90], 17 | [0, -90, 90], 18 | [0, 90, -90], 19 | [-90, -90, 0], 20 | [90, 90, 0], 21 | [0, -90, 0], 22 | [0, 90, 0], 23 | [90, -90, 0], 24 | [-90, 90, 0], 25 | [0, 90, 180], 26 | [0, -90, -180], 27 | ].map(([x, y, z]) => CFrame.Angles(math.rad(x), math.rad(y), math.rad(z))); 28 | 29 | /** 30 | * Returns a consistently ordered array for a specific Enum. 31 | * 32 | * We can't send Enum values over the network as the values aren't always within the 8 bit limit, 33 | * so instead we send the EnumItem's position in the array returned here. 34 | */ 35 | export function getSortedEnumItems(enumObject: Enum) { 36 | const enumItems = enumObject.GetEnumItems(); 37 | enumItems.sort((a, b) => a.Value < b.Value); 38 | 39 | return enumItems; 40 | } 41 | -------------------------------------------------------------------------------- /src/createBinarySerializer.ts: -------------------------------------------------------------------------------- 1 | import { Modding } from "@flamework/core"; 2 | import type { SerializerMetadata } from "./metadata"; 3 | import { createSerializer } from "./serialization/createSerializer"; 4 | import { createDeserializer } from "./serialization/createDeserializer"; 5 | import { processSerializerData } from "./processSerializerData"; 6 | 7 | /** 8 | * A binary serializer. 9 | */ 10 | export interface Serializer { 11 | /** 12 | * Serializes the input into a buffer. 13 | * 14 | * Result includes a blobs array which is used for things that cannot be put into the buffer. 15 | * The blobs array can be sent across the network or otherwise stored and passed into the deserialize function. 16 | */ 17 | serialize: (value: T) => { buffer: buffer; blobs: defined[] }; 18 | 19 | /** 20 | * Deserializes the input back into `T`. 21 | * 22 | * The blobs array can be omitted, but if the buffer contains blob references then deserialization will error. 23 | */ 24 | deserialize: (input: buffer, inputBlobs?: defined[]) => T; 25 | } 26 | 27 | /** 28 | * Creates a binary serializer automatically from a given type. 29 | * 30 | * This generates the {@link SerializerMetadata} type, 31 | * which you can reuse in your own user macros to generate arbitrary serializers (e.g for a networking library.) 32 | * 33 | * Disclaimer: this serializer depends on the order of emit, but this isn't guaranteed to be stable across compiles. 34 | * This means two binary serializers for the type `T` may be incompatible, 35 | * if they're not created within the same TS file (and thereof might not be compiled at the same time.) 36 | * 37 | * @metadata macro 38 | */ 39 | export function createBinarySerializer(meta?: Modding.Many>): Serializer { 40 | const processed = processSerializerData(meta as never); 41 | return { 42 | serialize: createSerializer(processed), 43 | deserialize: createDeserializer(processed), 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /src/dataType.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This namespace includes additional types that can be used in the binary serializer. 3 | */ 4 | export namespace DataType { 5 | export type f32 = number & { _f32?: never }; 6 | export type f64 = number & { _f64?: never }; 7 | 8 | export type u8 = number & { _u8?: never }; 9 | export type u16 = number & { _u16?: never }; 10 | export type u32 = number & { _u32?: never }; 11 | 12 | export type i8 = number & { _i8?: never }; 13 | export type i16 = number & { _i16?: never }; 14 | export type i32 = number & { _i32?: never }; 15 | 16 | export type Packed = T & { _packed?: [T] }; 17 | } 18 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { createBinarySerializer, Serializer } from "./createBinarySerializer"; 2 | export { SerializerMetadata } from "./metadata"; 3 | export { DataType } from "./dataType"; 4 | -------------------------------------------------------------------------------- /src/metadata/index.ts: -------------------------------------------------------------------------------- 1 | import { FindDiscriminator, IsDiscriminableUnion, IsLiteralUnion, type IsUnion } from "./unions"; 2 | import { HasRest, RestType, SplitRest } from "./tuples"; 3 | 4 | type IsNumber = `_${K}` extends keyof T ? true : false; 5 | type HasNominal = T extends T ? (T extends `_nominal_${string}` ? true : never) : never; 6 | 7 | type GetEnumType = [T] extends [EnumItem] ? ExtractKeys : never; 8 | 9 | /** 10 | * Generates the metadata for arrays and tuples. 11 | */ 12 | type ArrayMetadata = [T] extends [{ length: number }] 13 | ? [ 14 | "tuple", 15 | SplitRest extends infer A ? { [k in keyof A]: SerializerMetadata } : never, 16 | HasRest extends true ? SerializerMetadata> : undefined, 17 | ] 18 | : ["array", SerializerMetadata]; 19 | 20 | /** 21 | * A shortcut for defining Roblox datatypes (which map directly to a simple type.) 22 | * 23 | * This may in the future be used to reduce the number of branches inside the serializer. 24 | */ 25 | interface DataTypes { 26 | cframe: CFrame; 27 | numbersequence: NumberSequence; 28 | colorsequence: ColorSequence; 29 | color3: Color3; 30 | } 31 | 32 | /** 33 | * This is the metadata expected by the `createSerializer` function. 34 | * 35 | * This can be used in your own user macros to generate serializers for arbitrary types, such as for a networking library. 36 | */ 37 | export type SerializerMetadata = IsLiteralUnion extends true 38 | ? ["literal", NonNullable[], true extends IsUnion ? (undefined extends T ? 1 : 0) : -1] 39 | : unknown extends T 40 | ? ["optional", ["blob"]] 41 | : ["_packed", T] extends [keyof T, { _packed?: [infer V] }] 42 | ? ["packed", SerializerMetadata] 43 | : undefined extends T 44 | ? ["optional", SerializerMetadata>] 45 | : IsNumber extends true 46 | ? ["f64"] 47 | : IsNumber extends true 48 | ? ["f32"] 49 | : IsNumber extends true 50 | ? ["u8"] 51 | : IsNumber extends true 52 | ? ["u16"] 53 | : IsNumber extends true 54 | ? ["u32"] 55 | : IsNumber extends true 56 | ? ["i8"] 57 | : IsNumber extends true 58 | ? ["i16"] 59 | : IsNumber extends true 60 | ? ["i32"] 61 | : [T] extends [boolean] 62 | ? ["boolean"] 63 | : [T] extends [number] 64 | ? ["f64"] 65 | : [T] extends [string] 66 | ? ["string"] 67 | : [T] extends [Vector3] 68 | ? ["vector"] 69 | : [T] extends [EnumItem] 70 | ? ["enum", GetEnumType] 71 | : [T] extends [DataTypes[keyof DataTypes]] 72 | ? [ExtractKeys] 73 | : [T] extends [unknown[]] 74 | ? ArrayMetadata 75 | : [T] extends [ReadonlyMap] 76 | ? ["map", SerializerMetadata, SerializerMetadata] 77 | : [T] extends [ReadonlySet] 78 | ? ["set", SerializerMetadata] 79 | : IsDiscriminableUnion extends true 80 | ? [ 81 | "union", 82 | FindDiscriminator, 83 | FindDiscriminator extends infer D 84 | ? (T extends T ? [T[D & keyof T], SerializerMetadata>] : never)[] 85 | : never, 86 | // This is the byte size length. This is annoying (and slow) to calculate in TS, so it's done at runtime. 87 | -1, 88 | ] 89 | : true extends HasNominal 90 | ? ["blob"] 91 | : T extends object 92 | ? [ 93 | "object_raw", 94 | { 95 | [k in keyof T]-?: [k, SerializerMetadata]; 96 | }[keyof T][], 97 | ] 98 | : ["blob"]; 99 | 100 | /** 101 | * This type is essentially a union of all possible values that `SerializerMetadata` can emit. 102 | * 103 | * This is necessary due to a TS bug that causes parts of the conditional types to get lost. 104 | * This may not be necessary when we upgrade to TS 5.4 due to improvements in how TypeScript treats conditional types in such cases. 105 | */ 106 | export type SerializerData = 107 | | ["f32"] 108 | | ["f64"] 109 | | ["u8"] 110 | | ["u16"] 111 | | ["u32"] 112 | | ["i8"] 113 | | ["i16"] 114 | | ["i32"] 115 | | ["boolean"] 116 | | ["string"] 117 | | ["vector"] 118 | | ["object", Array, object] 119 | | ["object_raw", [string, SerializerData][]] 120 | | ["packed", SerializerData] 121 | | ["union", string, [unknown, SerializerData][], number] 122 | | ["array", SerializerData] 123 | | ["tuple", SerializerData[], SerializerData | undefined] 124 | | ["map", SerializerData, SerializerData] 125 | | ["set", SerializerData] 126 | | ["optional", SerializerData] 127 | | ["literal", defined[], number] 128 | | ["blob"] 129 | | ["enum", string] 130 | | { [k in keyof DataTypes]: [k] }[keyof DataTypes]; 131 | -------------------------------------------------------------------------------- /src/metadata/tuples.ts: -------------------------------------------------------------------------------- 1 | type Length = T extends { length: infer N extends number } ? N : never; 2 | 3 | export type RestType = T[9e99]; 4 | export type HasRest = RestType extends undefined ? false : true; 5 | 6 | // This is a somewhat expensive type, so we only rebuild the tuple if we know `T` has a rest element. 7 | export type SplitRest = HasRest extends true 8 | ? [...Acc, ...RestType[]] extends T 9 | ? Acc 10 | : SplitRest]]> 11 | : T; 12 | -------------------------------------------------------------------------------- /src/metadata/unions.ts: -------------------------------------------------------------------------------- 1 | // This type allows us to detect types like `number & Marker` or `15 & Marker` correctly, 2 | // by mapping the object portion of `T` onto `V`. 3 | type Mask = T extends object ? V & Reconstruct : V; 4 | 5 | type IsLiteral = T extends undefined 6 | ? true 7 | : T extends string 8 | ? Mask extends T 9 | ? false 10 | : true 11 | : T extends number 12 | ? Mask extends T 13 | ? false 14 | : true 15 | : T extends boolean 16 | ? true 17 | : false; 18 | 19 | export type IsUnion = T extends T ? (U extends T ? never : true) : never; 20 | 21 | type NonUnionKeys = { [k in keyof T]: true extends IsUnion ? never : k }[keyof T]; 22 | type LiteralKeys = { [k in keyof T]: true extends IsLiteral ? k : never }[keyof T]; 23 | 24 | // This type finds all literal keys and excludes union keys. 25 | type DiscriminatorKeys = NonUnionKeys & LiteralKeys; 26 | 27 | // This type excludes discriminators that don't exist in every constituent. 28 | type FilterSharedDiscriminators = UnionToIntersection] : never>[never]; 29 | 30 | // This type excludes discriminators whose values are not unique between all constituents. 31 | type FilterUniqueDiscriminators = D extends D 32 | ? (T extends T ? (T[D] extends Exclude[D] ? unknown : D) : never) extends D 33 | ? D 34 | : never 35 | : never; 36 | 37 | export type FindDiscriminator = FilterUniqueDiscriminators>; 38 | 39 | export type IsDiscriminableUnion = true extends IsUnion 40 | ? FindDiscriminator extends never 41 | ? false 42 | : true 43 | : false; 44 | 45 | // This doesn't check whether T is a union, only that it is comprised of only literal values. 46 | // The literal metadata will check for non-unions itself to optimize single literal values into zero bytes. 47 | // We also exclude plain `boolean` here as that has a more efficient special case. 48 | export type IsLiteralUnion = [boolean, NonNullable] extends [NonNullable, boolean] 49 | ? false 50 | : (T extends T ? (T extends undefined ? true : IsLiteral extends true ? true : false) : never) extends true 51 | ? true 52 | : false; 53 | -------------------------------------------------------------------------------- /src/processSerializerData.ts: -------------------------------------------------------------------------------- 1 | import { getSortedEnumItems } from "./constants"; 2 | import type { SerializerData } from "./metadata"; 3 | 4 | const enum IterationFlags { 5 | Default = 0, 6 | SizeUnknown = 1 << 0, 7 | Packed = 1 << 1, 8 | } 9 | 10 | export interface ProcessedInfo { 11 | flags: IterationFlags; 12 | containsPacking: boolean; 13 | containsUnknownPacking: boolean; 14 | minimumPackedBits: number; 15 | minimumPackedBytes: number; 16 | sortedEnums: Record; 17 | } 18 | 19 | export interface ProcessedSerializerData extends ProcessedInfo { 20 | data: SerializerData; 21 | } 22 | 23 | function addPackedBit(info: ProcessedInfo) { 24 | if ((info.flags & IterationFlags.Packed) !== 0) { 25 | info.containsPacking = true; 26 | 27 | if ((info.flags & IterationFlags.SizeUnknown) !== 0) { 28 | info.containsUnknownPacking = true; 29 | } else { 30 | // We only keep track of guaranteed packing bits, which we can use for optimization. 31 | info.minimumPackedBits += 1; 32 | } 33 | } 34 | } 35 | 36 | /** 37 | * This runs an additional pass over the SerializerData to perform calculations not feasible type-wise: 38 | * 1. Optimize objects 39 | * 2. Calculate union sizes 40 | * 3. Calculate packing 41 | */ 42 | function iterateSerializerData(data: SerializerData, info: ProcessedInfo): SerializerData { 43 | const flags = info.flags; 44 | const kind = data[0]; 45 | 46 | if (kind === "object_raw") { 47 | // We transform objects as an array of tuples, but this is slow to iterate over. 48 | // We flatten the raw generated metadata into a single array, which can be iterated much quicker. 49 | // We also create a preallocated object that we can clone as we already know the structure ahead of time. 50 | const preallocation = new Set(); 51 | const transformed = new Array(); 52 | for (const [key, meta] of data[1]) { 53 | transformed.push(key, iterateSerializerData(meta, info)); 54 | preallocation.add(key); 55 | } 56 | 57 | data = ["object", transformed, preallocation]; 58 | } else if (kind === "optional") { 59 | addPackedBit(info); 60 | info.flags |= IterationFlags.SizeUnknown; 61 | 62 | data = [kind, iterateSerializerData(data[1], info)]; 63 | } else if (kind === "array" || kind === "set") { 64 | info.flags |= IterationFlags.SizeUnknown; 65 | 66 | data = [kind, iterateSerializerData(data[1], info)]; 67 | } else if (kind === "union") { 68 | // Whenever we only have two options, we can use a single bit. 69 | // We use a byte size of `-1` to indicate a packable union. 70 | const isPackable = (info.flags & IterationFlags.Packed) !== 0 && data[2].size() === 2; 71 | if (isPackable) { 72 | addPackedBit(info); 73 | } 74 | 75 | info.flags |= IterationFlags.SizeUnknown; 76 | 77 | data = [ 78 | kind, 79 | data[1], 80 | data[2].map(([key, data]): [unknown, SerializerData] => [key, iterateSerializerData(data, info)]), 81 | isPackable ? -1 : data[2].size() <= 256 ? 1 : 2, 82 | ]; 83 | } else if (kind === "map") { 84 | info.flags |= IterationFlags.SizeUnknown; 85 | 86 | data = [kind, iterateSerializerData(data[1], info), iterateSerializerData(data[2], info)]; 87 | } else if (kind === "tuple") { 88 | const fixedElements = data[1].map((v) => iterateSerializerData(v, info)); 89 | 90 | let restElement; 91 | if (data[2] !== undefined) { 92 | info.flags |= IterationFlags.SizeUnknown; 93 | 94 | restElement = iterateSerializerData(data[2], info); 95 | } 96 | 97 | data = [kind, fixedElements, restElement]; 98 | } else if (kind === "literal") { 99 | // Whenever we only have two options, we can use a single bit. 100 | // We exclude undefined using `data[2] === 0` as it complicates thing. 101 | if ((info.flags & IterationFlags.Packed) !== 0 && data[1].size() === 2 && data[2] === 0) { 102 | addPackedBit(info); 103 | 104 | // We use `-1` as the size to signify that this union can be packed, 105 | // as it's not a valid value otherwise. 106 | return [kind, data[1], -1]; 107 | } 108 | 109 | // Since `undefined` is not included in the size of `data[1]`, 110 | // we add the existing value of `data[3]` (which is 1 if undefined is in the union) to `data[1]` 111 | // to determine the final required size. 112 | // A size of -1 means this isn't a union. 113 | data = [kind, data[1], data[2] === -1 ? 0 : data[2] + data[1].size() <= 256 ? 1 : 2]; 114 | } else if (kind === "packed") { 115 | info.flags |= IterationFlags.Packed; 116 | 117 | data = [kind, iterateSerializerData(data[1], info)]; 118 | } else if (kind === "boolean") { 119 | addPackedBit(info); 120 | } else if (kind === "cframe") { 121 | addPackedBit(info); 122 | } else if (kind === "enum") { 123 | // Calculate the sorted enum items so that we can send a single byte for an enum. 124 | if (info.sortedEnums[data[1]] === undefined) { 125 | info.sortedEnums[data[1]] = getSortedEnumItems(Enum[data[1] as never]); 126 | } 127 | } 128 | 129 | info.flags = flags; 130 | 131 | return data; 132 | } 133 | 134 | function getMinimumPackedBytes(info: ProcessedSerializerData) { 135 | return math.max(0, math.ceil(info.minimumPackedBits / 8) - (info.containsUnknownPacking ? 1 : 0)); 136 | } 137 | 138 | export function processSerializerData(rawData: SerializerData): ProcessedSerializerData { 139 | const processedInfo: ProcessedSerializerData = { 140 | data: rawData, 141 | flags: IterationFlags.Default, 142 | containsPacking: false, 143 | containsUnknownPacking: false, 144 | minimumPackedBits: 0, 145 | minimumPackedBytes: 0, 146 | sortedEnums: {}, 147 | }; 148 | 149 | processedInfo.data = iterateSerializerData(rawData, processedInfo); 150 | processedInfo.minimumPackedBytes = getMinimumPackedBytes(processedInfo); 151 | return processedInfo; 152 | } 153 | -------------------------------------------------------------------------------- /src/serialization/createDeserializer.ts: -------------------------------------------------------------------------------- 1 | //!native 2 | //!optimize 2 3 | import { AXIS_ALIGNED_ORIENTATIONS } from "../constants"; 4 | import type { SerializerData } from "../metadata"; 5 | import type { ProcessedSerializerData } from "../processSerializerData"; 6 | 7 | export function createDeserializer(info: ProcessedSerializerData) { 8 | const bits = table.create(math.ceil(info.minimumPackedBits / 8) * 8); 9 | let bitIndex = 0; 10 | let buf!: buffer; 11 | let offset!: number; 12 | let blobs: defined[] | undefined; 13 | let blobIndex = 0; 14 | let packing = false; 15 | 16 | function deserialize(meta: SerializerData): unknown { 17 | const currentOffset = offset; 18 | const kind = meta[0]; 19 | if (kind === "f32") { 20 | offset += 4; 21 | return buffer.readf32(buf, currentOffset); 22 | } else if (kind === "f64") { 23 | offset += 8; 24 | return buffer.readf64(buf, currentOffset); 25 | } else if (kind === "u8") { 26 | offset += 1; 27 | return buffer.readu8(buf, currentOffset); 28 | } else if (kind === "u16") { 29 | offset += 2; 30 | return buffer.readu16(buf, currentOffset); 31 | } else if (kind === "u32") { 32 | offset += 4; 33 | return buffer.readu32(buf, currentOffset); 34 | } else if (kind === "i8") { 35 | offset += 1; 36 | return buffer.readi8(buf, currentOffset); 37 | } else if (kind === "i16") { 38 | offset += 2; 39 | return buffer.readi16(buf, currentOffset); 40 | } else if (kind === "i32") { 41 | offset += 4; 42 | return buffer.readi32(buf, currentOffset); 43 | } else if (kind === "boolean" && packing) { 44 | bitIndex++; 45 | return bits[bitIndex - 1]; 46 | } else if (kind === "boolean") { 47 | offset += 1; 48 | return buffer.readu8(buf, currentOffset) === 1; 49 | } else if (kind === "string") { 50 | const length = buffer.readu32(buf, currentOffset); 51 | offset += 4 + length; 52 | 53 | return buffer.readstring(buf, currentOffset + 4, length); 54 | } else if (kind === "vector") { 55 | offset += 12; 56 | 57 | return new Vector3( 58 | buffer.readf32(buf, currentOffset), 59 | buffer.readf32(buf, currentOffset + 4), 60 | buffer.readf32(buf, currentOffset + 8), 61 | ); 62 | } else if (kind === "object") { 63 | const elements = meta[1]; 64 | const obj = table.clone(meta[2]) as Map; 65 | for (const i of $range(1, elements.size(), 2)) { 66 | (obj as never as Record)[elements[i - 1] as string] = deserialize( 67 | elements[i] as SerializerData, 68 | ); 69 | } 70 | return obj; 71 | } else if (kind === "array") { 72 | const deserializer = meta[1]; 73 | const length = buffer.readu32(buf, currentOffset); 74 | const array = new Array(length); 75 | offset += 4; 76 | 77 | for (const i of $range(1, length)) { 78 | array.push(deserialize(deserializer)!); 79 | } 80 | 81 | return array; 82 | } else if (kind === "tuple") { 83 | const elements = meta[1]; 84 | const restDeserializer = meta[2]; 85 | 86 | let restLength = 0; 87 | if (restDeserializer) { 88 | offset += 4; 89 | restLength = buffer.readu32(buf, currentOffset); 90 | } 91 | 92 | const tuple = new Array(elements.size() + restLength); 93 | 94 | for (const element of elements) { 95 | tuple.push(deserialize(element) as defined); 96 | } 97 | 98 | if (restDeserializer) { 99 | for (const _ of $range(1, restLength)) { 100 | tuple.push(deserialize(restDeserializer) as defined); 101 | } 102 | } 103 | 104 | return tuple; 105 | } else if (kind === "map") { 106 | const keyDeserializer = meta[1]; 107 | const valueDeserializer = meta[2]; 108 | const length = buffer.readu32(buf, currentOffset); 109 | const map = new Map(); 110 | offset += 4; 111 | 112 | for (const i of $range(1, length)) { 113 | map.set(deserialize(keyDeserializer), deserialize(valueDeserializer)); 114 | } 115 | 116 | return map; 117 | } else if (kind === "set") { 118 | const valueDeserializer = meta[1]; 119 | const length = buffer.readu32(buf, currentOffset); 120 | const set = new Set(); 121 | offset += 4; 122 | 123 | for (const i of $range(1, length)) { 124 | set.add(deserialize(valueDeserializer)); 125 | } 126 | 127 | return set; 128 | } else if (kind === "optional" && packing) { 129 | bitIndex++; 130 | return bits[bitIndex - 1] ? deserialize(meta[1]) : undefined; 131 | } else if (kind === "optional") { 132 | offset += 1; 133 | return buffer.readu8(buf, currentOffset) === 1 ? deserialize(meta[1]) : undefined; 134 | } else if (kind === "union") { 135 | const byteSize = meta[3]; 136 | 137 | let tagIndex; 138 | if (byteSize === 1) { 139 | offset += 1; 140 | tagIndex = buffer.readu8(buf, currentOffset); 141 | } else if (byteSize === 2) { 142 | offset += 2; 143 | tagIndex = buffer.readu16(buf, currentOffset); 144 | } else { 145 | bitIndex++; 146 | tagIndex = bits[bitIndex - 1] ? 0 : 1; 147 | } 148 | 149 | const tag = meta[2][tagIndex]; 150 | const object = deserialize(tag[1]); 151 | (object as Record)[meta[1]] = tag[0]; 152 | 153 | return object; 154 | } else if (kind === "literal") { 155 | const literals = meta[1]; 156 | const byteSize = meta[2]; 157 | if (byteSize === 1) { 158 | offset += 1; 159 | return literals[buffer.readu8(buf, currentOffset)]; 160 | } else if (byteSize === 2) { 161 | offset += 2; 162 | return literals[buffer.readu16(buf, currentOffset)]; 163 | } else if (byteSize === -1) { 164 | bitIndex++; 165 | return bits[bitIndex - 1] ? literals[0] : literals[1]; 166 | } else { 167 | return literals[0]; 168 | } 169 | } else if (kind === "blob") { 170 | blobIndex++; 171 | return blobs![blobIndex - 1]; 172 | } else if (kind === "packed") { 173 | const innerType = meta[1]; 174 | const wasPacking = packing; 175 | packing = true; 176 | 177 | const value = deserialize(innerType); 178 | packing = wasPacking; 179 | 180 | return value; 181 | } else if (kind === "enum") { 182 | const index = buffer.readu8(buf, currentOffset); 183 | offset += 1; 184 | 185 | return info.sortedEnums[meta[1]][index]; 186 | } else if (kind === "cframe" && packing) { 187 | bitIndex++; 188 | 189 | // This is an unoptimized CFrame. 190 | if (!bits[bitIndex - 1]) { 191 | return deserializeCFrame(); 192 | } 193 | 194 | const packed = buffer.readu8(buf, currentOffset); 195 | offset += 1; 196 | 197 | const optimizedPosition = packed & 0x60; 198 | const optimizedRotation = packed & 0x1f; 199 | 200 | let position; 201 | if (optimizedPosition === 0x20) { 202 | position = Vector3.zero; 203 | } else if (optimizedPosition === 0x60) { 204 | position = Vector3.one; 205 | } else { 206 | position = new Vector3( 207 | buffer.readf32(buf, offset), 208 | buffer.readf32(buf, offset + 4), 209 | buffer.readf32(buf, offset + 8), 210 | ); 211 | 212 | offset += 12; 213 | } 214 | 215 | if (optimizedRotation !== 0x1f) { 216 | return AXIS_ALIGNED_ORIENTATIONS[optimizedRotation].add(position); 217 | } else { 218 | const axisRotation = new Vector3( 219 | buffer.readf32(buf, offset), 220 | buffer.readf32(buf, offset + 4), 221 | buffer.readf32(buf, offset + 8), 222 | ); 223 | 224 | offset += 12; 225 | 226 | if (axisRotation.Magnitude === 0) { 227 | return new CFrame(position); 228 | } 229 | 230 | return CFrame.fromAxisAngle(axisRotation.Unit, axisRotation.Magnitude).add(position); 231 | } 232 | } else if (kind === "cframe") { 233 | return deserializeCFrame(); 234 | } else if (kind === "colorsequence") { 235 | const keypointCount = buffer.readu8(buf, currentOffset); 236 | const keypoints = new Array(); 237 | offset += 1 + keypointCount * 7; 238 | 239 | for (const i of $range(1, keypointCount)) { 240 | const keypointOffset = currentOffset + 1 + 7 * (i - 1); 241 | const time = buffer.readf32(buf, keypointOffset); 242 | const value = Color3.fromRGB( 243 | buffer.readu8(buf, keypointOffset + 4), 244 | buffer.readu8(buf, keypointOffset + 5), 245 | buffer.readu8(buf, keypointOffset + 6), 246 | ); 247 | 248 | keypoints.push(new ColorSequenceKeypoint(time, value)); 249 | } 250 | 251 | return new ColorSequence(keypoints); 252 | } else if (kind === "numbersequence") { 253 | const keypointCount = buffer.readu8(buf, currentOffset); 254 | const keypoints = new Array(); 255 | offset += 1 + keypointCount * 8; 256 | 257 | for (const i of $range(1, keypointCount)) { 258 | const keypointOffset = currentOffset + 1 + 8 * (i - 1); 259 | const time = buffer.readf32(buf, keypointOffset); 260 | const value = buffer.readf32(buf, keypointOffset + 4); 261 | 262 | keypoints.push(new NumberSequenceKeypoint(time, value)); 263 | } 264 | 265 | return new NumberSequence(keypoints); 266 | } else if (kind === "color3") { 267 | offset += 3; 268 | 269 | return Color3.fromRGB( 270 | buffer.readu8(buf, currentOffset), 271 | buffer.readu8(buf, currentOffset + 1), 272 | buffer.readu8(buf, currentOffset + 2), 273 | ); 274 | } else { 275 | error(`unexpected kind: ${kind}`); 276 | } 277 | } 278 | 279 | function deserializeCFrame() { 280 | const currentOffset = offset; 281 | offset += 4 * 6; 282 | 283 | const position = new Vector3( 284 | buffer.readf32(buf, currentOffset), 285 | buffer.readf32(buf, currentOffset + 4), 286 | buffer.readf32(buf, currentOffset + 8), 287 | ); 288 | 289 | const rotation = new Vector3( 290 | buffer.readf32(buf, currentOffset + 12), 291 | buffer.readf32(buf, currentOffset + 16), 292 | buffer.readf32(buf, currentOffset + 20), 293 | ); 294 | 295 | return rotation.Magnitude === 0 296 | ? new CFrame(position) 297 | : CFrame.fromAxisAngle(rotation.Unit, rotation.Magnitude).add(position); 298 | } 299 | 300 | function readBits() { 301 | const guaranteedBytes = info.minimumPackedBytes; 302 | 303 | while (true) { 304 | const currentByte = buffer.readu8(buf, offset); 305 | const guaranteedByte = offset < guaranteedBytes; 306 | 307 | for (const bit of $range(guaranteedByte ? 0 : 1, 7)) { 308 | const value = (currentByte >>> bit) % 2 === 1; 309 | bits.push(value); 310 | } 311 | 312 | offset += 1; 313 | 314 | // Variable bit indicated the end. 315 | if (!guaranteedByte && currentByte % 2 === 0) { 316 | break; 317 | } 318 | 319 | // We only have guaranteed bits and we reached the end. 320 | if (!info.containsUnknownPacking && offset === guaranteedBytes) { 321 | break; 322 | } 323 | } 324 | } 325 | 326 | return (input: buffer, inputBlobs?: defined[]) => { 327 | blobs = inputBlobs; 328 | buf = input; 329 | offset = 0; 330 | blobIndex = 0; 331 | bitIndex = 0; 332 | 333 | if (info.containsPacking) { 334 | table.clear(bits); 335 | readBits(); 336 | } 337 | 338 | return deserialize(info.data) as T; 339 | }; 340 | } 341 | -------------------------------------------------------------------------------- /src/serialization/createSerializer.ts: -------------------------------------------------------------------------------- 1 | //!native 2 | //!optimize 2 3 | import { AXIS_ALIGNED_ORIENTATIONS } from "../constants"; 4 | import type { SerializerData } from "../metadata"; 5 | import type { ProcessedSerializerData } from "../processSerializerData"; 6 | 7 | export function createSerializer(info: ProcessedSerializerData) { 8 | const bits = table.create(info.minimumPackedBits); 9 | let currentSize = 2 ** 8; 10 | let buf = buffer.create(currentSize); 11 | let offset!: number; 12 | let blobs!: defined[]; 13 | let packing = false; 14 | 15 | function allocate(size: number) { 16 | offset += size; 17 | 18 | if (offset > currentSize) { 19 | const newSize = 2 ** math.ceil(math.log(offset) / math.log(2)); 20 | const oldBuffer = buf; 21 | 22 | currentSize = newSize; 23 | buf = buffer.create(newSize); 24 | buffer.copy(buf, 0, oldBuffer); 25 | } 26 | } 27 | 28 | function serialize(value: unknown, meta: SerializerData) { 29 | const currentOffset = offset; 30 | const kind = meta[0]; 31 | if (kind === "f32") { 32 | allocate(4); 33 | buffer.writef32(buf, currentOffset, value as number); 34 | } else if (kind === "f64") { 35 | allocate(8); 36 | buffer.writef64(buf, currentOffset, value as number); 37 | } else if (kind === "u8") { 38 | allocate(1); 39 | buffer.writeu8(buf, currentOffset, value as number); 40 | } else if (kind === "u16") { 41 | allocate(2); 42 | buffer.writeu16(buf, currentOffset, value as number); 43 | } else if (kind === "u32") { 44 | allocate(4); 45 | buffer.writeu32(buf, currentOffset, value as number); 46 | } else if (kind === "i8") { 47 | allocate(1); 48 | buffer.writei8(buf, currentOffset, value as number); 49 | } else if (kind === "i16") { 50 | allocate(2); 51 | buffer.writei16(buf, currentOffset, value as number); 52 | } else if (kind === "i32") { 53 | allocate(4); 54 | buffer.writei32(buf, currentOffset, value as number); 55 | } else if (kind === "boolean" && packing) { 56 | bits.push(value as boolean); 57 | } else if (kind === "boolean") { 58 | allocate(1); 59 | buffer.writeu8(buf, currentOffset, value === true ? 1 : 0); 60 | } else if (kind === "string") { 61 | const size = (value as string).size(); 62 | allocate(4 + size); 63 | buffer.writeu32(buf, currentOffset, size); 64 | buffer.writestring(buf, currentOffset + 4, value as string); 65 | } else if (kind === "vector") { 66 | allocate(12); 67 | buffer.writef32(buf, currentOffset, (value as Vector3).X); 68 | buffer.writef32(buf, currentOffset + 4, (value as Vector3).Y); 69 | buffer.writef32(buf, currentOffset + 8, (value as Vector3).Z); 70 | } else if (kind === "object") { 71 | const elements = meta[1]; 72 | for (const i of $range(1, elements.size(), 2)) { 73 | serialize((value as Record)[elements[i - 1] as string], elements[i] as SerializerData); 74 | } 75 | } else if (kind === "array") { 76 | const serializer = meta[1]; 77 | allocate(4); 78 | 79 | buffer.writeu32(buf, currentOffset, (value as unknown[]).size()); 80 | 81 | for (const element of value as unknown[]) { 82 | serialize(element, serializer); 83 | } 84 | } else if (kind === "tuple") { 85 | const elements = meta[1]; 86 | const restSerializer = meta[2]; 87 | const size = (value as unknown[]).size(); 88 | 89 | // We serialize the rest element length first so that the deserializer can allocate accordingly. 90 | if (restSerializer) { 91 | allocate(4); 92 | buffer.writeu32(buf, currentOffset, size - elements.size()); 93 | } 94 | 95 | for (const i of $range(1, size)) { 96 | const serializer = elements[i - 1] ?? restSerializer; 97 | if (serializer) { 98 | serialize((value as unknown[])[i - 1], serializer); 99 | } 100 | } 101 | } else if (kind === "map") { 102 | const keySerializer = meta[1]; 103 | const valueSerializer = meta[2]; 104 | allocate(4); 105 | 106 | let size = 0; 107 | for (const [elementIndex, elementValue] of value as Map) { 108 | size += 1; 109 | serialize(elementIndex, keySerializer); 110 | serialize(elementValue, valueSerializer); 111 | } 112 | 113 | // We already allocated this space before serializing the map, so this is safe. 114 | buffer.writeu32(buf, currentOffset, size); 115 | } else if (kind === "set") { 116 | // We could just generate `Map` for sets, but this is more efficient as it omits serializing a boolean per value. 117 | const valueSerializer = meta[1]; 118 | allocate(4); 119 | 120 | let size = 0; 121 | for (const elementValue of value as Set) { 122 | size += 1; 123 | serialize(elementValue, valueSerializer); 124 | } 125 | 126 | // We already allocated this space before serializing the set, so this is safe. 127 | buffer.writeu32(buf, currentOffset, size); 128 | } else if (kind === "optional" && packing) { 129 | if (value !== undefined) { 130 | bits.push(true); 131 | serialize(value, meta[1]); 132 | } else { 133 | bits.push(false); 134 | } 135 | } else if (kind === "optional") { 136 | allocate(1); 137 | if (value !== undefined) { 138 | buffer.writeu8(buf, currentOffset, 1); 139 | serialize(value, meta[1]); 140 | } else { 141 | buffer.writeu8(buf, currentOffset, 0); 142 | } 143 | } else if (kind === "union") { 144 | const tagName = meta[1]; 145 | const tagged = meta[2]; 146 | const byteSize = meta[3]; 147 | const objectTag = (value as Map).get(tagName); 148 | 149 | let tagIndex = 0; 150 | let tagMetadata!: SerializerData; 151 | for (const i of $range(1, tagged.size())) { 152 | const tagObject = tagged[i - 1]; 153 | if (tagObject[0] === objectTag) { 154 | tagIndex = i - 1; 155 | tagMetadata = tagObject[1]; 156 | break; 157 | } 158 | } 159 | 160 | if (byteSize === 1) { 161 | allocate(1); 162 | buffer.writeu8(buf, currentOffset, tagIndex); 163 | } else if (byteSize === 2) { 164 | allocate(2); 165 | buffer.writeu16(buf, currentOffset, tagIndex); 166 | } else if (byteSize === -1) { 167 | bits.push(tagIndex === 0); 168 | } 169 | 170 | serialize(value, tagMetadata); 171 | } else if (kind === "literal") { 172 | // We support `undefined` as a literal, but `indexOf` will actually return -1 173 | // This is fine, though, as -1 will serialize as the max integer which will be undefined on unions that do not exceed the size limit. 174 | const literals = meta[1]; 175 | const byteSize = meta[2]; 176 | if (byteSize === 1) { 177 | const index = literals.indexOf(value as defined); 178 | allocate(1); 179 | 180 | buffer.writeu8(buf, currentOffset, index); 181 | } else if (byteSize === 2) { 182 | const index = literals.indexOf(value as defined); 183 | allocate(2); 184 | 185 | buffer.writeu16(buf, currentOffset, index); 186 | } else if (byteSize === -1) { 187 | bits.push(value === literals[0]); 188 | } 189 | } else if (kind === "blob") { 190 | // Value will always be defined because if it isn't, it will be wrapped in `optional` 191 | blobs.push(value!); 192 | } else if (kind === "packed") { 193 | const innerType = meta[1]; 194 | const wasPacking = packing; 195 | packing = true; 196 | 197 | serialize(value, innerType); 198 | packing = wasPacking; 199 | } else if (kind === "enum") { 200 | const enumIndex = info.sortedEnums[meta[1]].indexOf(value as EnumItem); 201 | allocate(1); 202 | 203 | buffer.writeu8(buf, currentOffset, enumIndex); 204 | } else if (kind === "cframe" && packing) { 205 | // 1-5: Orientation, 6-7: Position, 8: unused 206 | let optimizedPosition = false; 207 | let optimizedRotation = false; 208 | let packed = 0; 209 | 210 | const cframe = value as CFrame; 211 | 212 | if (cframe.Position === Vector3.zero) { 213 | optimizedPosition = true; 214 | packed += 0x20; 215 | } else if (cframe.Position === Vector3.one) { 216 | optimizedPosition = true; 217 | packed += 0x20; 218 | packed += 0x40; 219 | } 220 | 221 | const specialCase = AXIS_ALIGNED_ORIENTATIONS.indexOf(cframe.Rotation); 222 | if (specialCase !== -1) { 223 | optimizedRotation = true; 224 | packed += specialCase; 225 | } else { 226 | packed += 0x1f; 227 | } 228 | 229 | const optimized = optimizedPosition || optimizedRotation; 230 | bits.push(optimized); 231 | 232 | allocate((optimized ? 1 : 0) + (optimizedPosition ? 0 : 12) + (optimizedRotation ? 0 : 12)); 233 | 234 | let newOffset = currentOffset; 235 | 236 | if (optimized) { 237 | buffer.writeu8(buf, newOffset, packed); 238 | newOffset += 1; 239 | } 240 | 241 | if (!optimizedPosition) { 242 | buffer.writef32(buf, newOffset, cframe.X); 243 | buffer.writef32(buf, newOffset + 4, cframe.Y); 244 | buffer.writef32(buf, newOffset + 8, cframe.Z); 245 | newOffset += 12; 246 | } 247 | 248 | if (!optimizedRotation) { 249 | const [axis, angle] = cframe.ToAxisAngle(); 250 | buffer.writef32(buf, newOffset, axis.X * angle); 251 | buffer.writef32(buf, newOffset + 4, axis.Y * angle); 252 | buffer.writef32(buf, newOffset + 8, axis.Z * angle); 253 | } 254 | } else if (kind === "cframe") { 255 | allocate(4 * 6); 256 | 257 | buffer.writef32(buf, currentOffset, (value as CFrame).X); 258 | buffer.writef32(buf, currentOffset + 4, (value as CFrame).Y); 259 | buffer.writef32(buf, currentOffset + 8, (value as CFrame).Z); 260 | 261 | const [axis, angle] = (value as CFrame).ToAxisAngle(); 262 | buffer.writef32(buf, currentOffset + 12, axis.X * angle); 263 | buffer.writef32(buf, currentOffset + 16, axis.Y * angle); 264 | buffer.writef32(buf, currentOffset + 20, axis.Z * angle); 265 | } else if (kind === "colorsequence") { 266 | const keypoints = (value as ColorSequence).Keypoints; 267 | const keypointCount = keypoints.size(); 268 | allocate(1 + keypointCount * 7); 269 | 270 | buffer.writeu8(buf, currentOffset, keypointCount); 271 | 272 | for (const i of $range(1, keypointCount)) { 273 | const keypointOffset = currentOffset + 1 + 7 * (i - 1); 274 | const keypoint = keypoints[i - 1]; 275 | buffer.writef32(buf, keypointOffset, keypoint.Time); 276 | buffer.writeu8(buf, keypointOffset + 4, keypoint.Value.R * 255); 277 | buffer.writeu8(buf, keypointOffset + 5, keypoint.Value.G * 255); 278 | buffer.writeu8(buf, keypointOffset + 6, keypoint.Value.B * 255); 279 | } 280 | } else if (kind === "numbersequence") { 281 | const keypoints = (value as NumberSequence).Keypoints; 282 | const keypointCount = keypoints.size(); 283 | allocate(1 + keypointCount * 8); 284 | 285 | buffer.writeu8(buf, currentOffset, keypointCount); 286 | 287 | for (const i of $range(1, keypointCount)) { 288 | const keypointOffset = currentOffset + 1 + 8 * (i - 1); 289 | const keypoint = keypoints[i - 1]; 290 | buffer.writef32(buf, keypointOffset, keypoint.Time); 291 | buffer.writef32(buf, keypointOffset + 4, keypoint.Value); 292 | } 293 | } else if (kind === "color3") { 294 | allocate(3); 295 | 296 | buffer.writeu8(buf, currentOffset, (value as Color3).R * 255); 297 | buffer.writeu8(buf, currentOffset + 1, (value as Color3).G * 255); 298 | buffer.writeu8(buf, currentOffset + 2, (value as Color3).B * 255); 299 | } else { 300 | error(`unexpected kind: ${kind}`); 301 | } 302 | } 303 | 304 | function writeBits(buf: buffer, offset: number, bitOffset: number, bytes: number, variable: boolean) { 305 | const bitSize = bits.size(); 306 | 307 | for (const byte of $range(0, bytes - 1)) { 308 | let currentByte = 0; 309 | 310 | for (const bit of $range(variable ? 1 : 0, math.min(7, bitSize - bitOffset))) { 311 | currentByte += (bits[bitOffset] ? 1 : 0) << bit; 312 | bitOffset += 1; 313 | } 314 | 315 | if (variable && byte !== bytes - 1) { 316 | currentByte += 1; 317 | } 318 | 319 | buffer.writeu8(buf, offset, currentByte); 320 | 321 | offset += 1; 322 | } 323 | } 324 | 325 | function calculatePackedBytes() { 326 | const minimumBytes = info.minimumPackedBytes; 327 | 328 | if (info.containsUnknownPacking) { 329 | const variableBytes = math.max(1, math.ceil((bits.size() - minimumBytes * 8) / 7)); 330 | const totalByteCount = minimumBytes + variableBytes; 331 | 332 | return $tuple(minimumBytes, variableBytes, totalByteCount); 333 | } 334 | 335 | return $tuple(minimumBytes, 0, minimumBytes); 336 | } 337 | 338 | return (value: T) => { 339 | offset = 0; 340 | blobs = []; 341 | table.clear(bits); 342 | serialize(value, info.data); 343 | 344 | if (info.containsPacking) { 345 | const [minimumBytes, variableBytes, totalBytes] = calculatePackedBytes(); 346 | const trim = buffer.create(offset + totalBytes); 347 | buffer.copy(trim, totalBytes, buf, 0, offset); 348 | 349 | if (minimumBytes > 0) { 350 | writeBits(trim, 0, 0, minimumBytes, false); 351 | } 352 | 353 | if (variableBytes > 0) { 354 | writeBits(trim, minimumBytes, minimumBytes * 8, variableBytes, true); 355 | } 356 | 357 | return { buffer: trim, blobs }; 358 | } else { 359 | const trim = buffer.create(offset); 360 | buffer.copy(trim, 0, buf, 0, offset); 361 | 362 | return { buffer: trim, blobs }; 363 | } 364 | }; 365 | } 366 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // required 4 | "allowSyntheticDefaultImports": true, 5 | "downlevelIteration": true, 6 | "jsx": "react", 7 | "jsxFactory": "Roact.createElement", 8 | "jsxFragmentFactory": "Roact.createFragment", 9 | "module": "commonjs", 10 | "moduleResolution": "Node", 11 | "noLib": true, 12 | "resolveJsonModule": true, 13 | "experimentalDecorators": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "moduleDetection": "force", 16 | "strict": true, 17 | "target": "ESNext", 18 | "typeRoots": ["node_modules/@rbxts"], 19 | 20 | // configurable 21 | "rootDir": "src", 22 | "outDir": "out", 23 | "incremental": true, 24 | "tsBuildInfoFile": "out/tsconfig.tsbuildinfo", 25 | "declaration": true, 26 | 27 | "plugins": [ 28 | { 29 | "transform": "rbxts-transformer-flamework" 30 | } 31 | ] 32 | } 33 | } 34 | --------------------------------------------------------------------------------