init: the basics

This commit is contained in:
Erica Marigold 2024-03-27 16:38:52 +05:30
commit 997ba997fe
No known key found for this signature in database
GPG key ID: 2768CC0C23D245D1
10 changed files with 3842 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
out/
node_modules/

17
lune_require_patch.diff Normal file
View file

@ -0,0 +1,17 @@
--- roblox.d.ts 2024-03-27 14:21:02.418167012 +0530
+++ node_modules/@rbxts/types/include/roblox.d.ts 2024-03-27 14:22:57.588164607 +0530
@@ -2635,10 +2635,10 @@
* The number reflects the current heap consumption from the operating system perspective, which fluctuates over time as garbage collector frees objects.
*/
declare function gcinfo(): number;
-/** Runs the supplied ModuleScript if it has not been run already, and returns what the ModuleScript returned (in both cases).
-
-If the ModuleScript the user wants to use has been uploaded to Roblox (with the instances name being MainModule), it can be loaded by using the require function on the asset ID of the ModuleScript, though only on the server. */
-declare function require(moduleScript: ModuleScript | number): unknown;
+/**
+ * Requires a module with a path relative to the current module's path.
+*/
+declare function require<T>(path: string | number): T;
/** Runs the specified callback function in a separate thread, without yielding the current thread.
The function will be executed the next time Robloxs Task Scheduler runs an update cycle. This delay will take at least 29 milliseconds but can arbitrarily take longer, depending on the target framerate and various throttling conditions. */
declare function spawn(callback: DelayedCallback): void;

29
package.json Normal file
View file

@ -0,0 +1,29 @@
{
"name": "@rbxts/wg-translate-js",
"version": "1.0.0",
"description": "",
"main": "out/init.lua",
"scripts": {
"build": "rbxtsc",
"watch": "rbxtsc -w",
"postinstall": "patch node_modules/@rbxts/types/include/roblox.d.ts < lune_require_patch.diff",
"prepublishOnly": "pnpm run build"
},
"keywords": [],
"author": "",
"license": "ISC",
"types": "out/index.d.ts",
"files": [
"out",
"!**/*.tsbuildinfo"
],
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@rbxts/compiler-types": "2.3.0-types.0",
"@rbxts/types": "^1.0.765",
"roblox-ts": "^2.3.0",
"typescript": "^5.4.3"
}
}

382
pnpm-lock.yaml Normal file
View file

@ -0,0 +1,382 @@
lockfileVersion: '6.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
devDependencies:
'@rbxts/compiler-types':
specifier: 2.3.0-types.0
version: 2.3.0-types.0
'@rbxts/types':
specifier: ^1.0.765
version: 1.0.765
roblox-ts:
specifier: ^2.3.0
version: 2.3.0
typescript:
specifier: ^5.4.3
version: 5.4.3
packages:
/@rbxts/compiler-types@2.3.0-types.0:
resolution: {integrity: sha512-2kllUXLlK22OzSi3GmLT+/O70U9oB2iXp4rGFk5FLwc1pqf4W9EsInuJ0t7poLE2XnqnpW/ojff9CUNivI2+lw==}
dev: true
/@rbxts/types@1.0.765:
resolution: {integrity: sha512-cueZb3n1Dmu/tAxEnYd4qaIUYs8M4dyJTKaHzlWlcl23Ln3QIRNxy7vN+oWvfkgN1icZHc4up7dCBm9PVH38BA==}
dev: true
/@roblox-ts/luau-ast@1.0.11:
resolution: {integrity: sha512-+maoLYpqY0HK8ugLFHS3qz0phMyDaN3i21jjW75T2ZaqJg84heKDUo98iXClvnx3mUDhW10IxqH+cYJ2iftYhQ==}
dev: true
/@roblox-ts/path-translator@1.0.0:
resolution: {integrity: sha512-Lp6qVUqjmXIrICy2KPKRiX8IkJ+lNqn6RqoUplLiTr+4JehIN+mJv0tTnE72XRyIfcx0VWl5nKrRwUuqcOj1yg==}
dependencies:
ajv: 8.12.0
fs-extra: 11.2.0
dev: true
/@roblox-ts/rojo-resolver@1.0.6:
resolution: {integrity: sha512-+heTECMo6BdH3a3h4DCj+8kJvwKuxWqBevcW/m2BzQaVtmo1GtLa4V4bJCMvDuAMeEqYKQZUB7546nN2dcqqAA==}
dependencies:
ajv: 8.12.0
fs-extra: 11.2.0
dev: true
/ajv@8.12.0:
resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==}
dependencies:
fast-deep-equal: 3.1.3
json-schema-traverse: 1.0.0
require-from-string: 2.0.2
uri-js: 4.4.1
dev: true
/ansi-regex@5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
dev: true
/ansi-styles@4.3.0:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
dependencies:
color-convert: 2.0.1
dev: true
/anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
dependencies:
normalize-path: 3.0.0
picomatch: 2.3.1
dev: true
/binary-extensions@2.3.0:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
dev: true
/braces@3.0.2:
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
engines: {node: '>=8'}
dependencies:
fill-range: 7.0.1
dev: true
/chokidar@3.6.0:
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'}
dependencies:
anymatch: 3.1.3
braces: 3.0.2
glob-parent: 5.1.2
is-binary-path: 2.1.0
is-glob: 4.0.3
normalize-path: 3.0.0
readdirp: 3.6.0
optionalDependencies:
fsevents: 2.3.3
dev: true
/cliui@8.0.1:
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
engines: {node: '>=12'}
dependencies:
string-width: 4.2.3
strip-ansi: 6.0.1
wrap-ansi: 7.0.0
dev: true
/color-convert@2.0.1:
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
engines: {node: '>=7.0.0'}
dependencies:
color-name: 1.1.4
dev: true
/color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
dev: true
/emoji-regex@8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
dev: true
/escalade@3.1.2:
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
engines: {node: '>=6'}
dev: true
/fast-deep-equal@3.1.3:
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
dev: true
/fill-range@7.0.1:
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
engines: {node: '>=8'}
dependencies:
to-regex-range: 5.0.1
dev: true
/fs-extra@11.2.0:
resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==}
engines: {node: '>=14.14'}
dependencies:
graceful-fs: 4.2.11
jsonfile: 6.1.0
universalify: 2.0.1
dev: true
/fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
requiresBuild: true
dev: true
optional: true
/function-bind@1.1.2:
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
dev: true
/get-caller-file@2.0.5:
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
engines: {node: 6.* || 8.* || >= 10.*}
dev: true
/glob-parent@5.1.2:
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
engines: {node: '>= 6'}
dependencies:
is-glob: 4.0.3
dev: true
/graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
dev: true
/hasown@2.0.2:
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
engines: {node: '>= 0.4'}
dependencies:
function-bind: 1.1.2
dev: true
/is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
dependencies:
binary-extensions: 2.3.0
dev: true
/is-core-module@2.13.1:
resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
dependencies:
hasown: 2.0.2
dev: true
/is-extglob@2.1.1:
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
engines: {node: '>=0.10.0'}
dev: true
/is-fullwidth-code-point@3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
dev: true
/is-glob@4.0.3:
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
engines: {node: '>=0.10.0'}
dependencies:
is-extglob: 2.1.1
dev: true
/is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
dev: true
/json-schema-traverse@1.0.0:
resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
dev: true
/jsonfile@6.1.0:
resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
dependencies:
universalify: 2.0.1
optionalDependencies:
graceful-fs: 4.2.11
dev: true
/kleur@4.1.5:
resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
engines: {node: '>=6'}
dev: true
/normalize-path@3.0.0:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
dev: true
/path-parse@1.0.7:
resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
dev: true
/picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
dev: true
/punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
dev: true
/readdirp@3.6.0:
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
engines: {node: '>=8.10.0'}
dependencies:
picomatch: 2.3.1
dev: true
/require-directory@2.1.1:
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
engines: {node: '>=0.10.0'}
dev: true
/require-from-string@2.0.2:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
dev: true
/resolve@1.22.8:
resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
hasBin: true
dependencies:
is-core-module: 2.13.1
path-parse: 1.0.7
supports-preserve-symlinks-flag: 1.0.0
dev: true
/roblox-ts@2.3.0:
resolution: {integrity: sha512-swz+3sxHcB1ww5iUkwxzPFqrbWYmjD9uDriLhta5MAShvRFW4Vdku/aBSU4KiLqtVWYvYo32G+5bXg1Pw2yvIA==}
hasBin: true
dependencies:
'@roblox-ts/luau-ast': 1.0.11
'@roblox-ts/path-translator': 1.0.0
'@roblox-ts/rojo-resolver': 1.0.6
chokidar: 3.6.0
fs-extra: 11.2.0
kleur: 4.1.5
resolve: 1.22.8
typescript: 5.2.2
yargs: 17.7.2
dev: true
/string-width@4.2.3:
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
engines: {node: '>=8'}
dependencies:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
dev: true
/strip-ansi@6.0.1:
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
engines: {node: '>=8'}
dependencies:
ansi-regex: 5.0.1
dev: true
/supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
dev: true
/to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
dependencies:
is-number: 7.0.0
dev: true
/typescript@5.2.2:
resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
engines: {node: '>=14.17'}
hasBin: true
dev: true
/typescript@5.4.3:
resolution: {integrity: sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==}
engines: {node: '>=14.17'}
hasBin: true
dev: true
/universalify@2.0.1:
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
engines: {node: '>= 10.0.0'}
dev: true
/uri-js@4.4.1:
resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
dependencies:
punycode: 2.3.1
dev: true
/wrap-ansi@7.0.0:
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
engines: {node: '>=10'}
dependencies:
ansi-styles: 4.3.0
string-width: 4.2.3
strip-ansi: 6.0.1
dev: true
/y18n@5.0.8:
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
engines: {node: '>=10'}
dev: true
/yargs-parser@21.1.1:
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
engines: {node: '>=12'}
dev: true
/yargs@17.7.2:
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
engines: {node: '>=12'}
dependencies:
cliui: 8.0.1
escalade: 3.1.2
get-caller-file: 2.0.5
require-directory: 2.1.1
string-width: 4.2.3
y18n: 5.0.8
yargs-parser: 21.1.1
dev: true

2939
roblox.d.ts vendored Normal file

File diff suppressed because it is too large Load diff

117
src/base64.ts Normal file
View file

@ -0,0 +1,117 @@
const { slice } = require<{
slice: <T extends defined>(arr: T[], start: number, stop?: number) => T[];
}>("util");
function stringToBytes(str: string) {
let result = [];
for (let i = 0; i < str.size(); i++) {
result.push(string.byte(str, i + 1)[0]);
}
return result;
}
// Adapted from https://github.com/un-ts/ab64/blob/main/src/ponyfill.ts#L24
const _atob = (asc: string) => {
const b64CharList =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
const b64Chars = string.split(b64CharList, "");
const b64Table = b64Chars.reduce<Record<string, number>>(
(acc, char, index) => {
acc[char] = index;
return acc;
},
{}
);
const fromCharCode = string.char;
asc = string.gsub(asc, "%s+", "")[0];
asc += string.char(...slice(stringToBytes("=="), 2 - (asc.size() & 3)));
let u24: number;
let binary = "";
let r1: number;
let r2: number;
for (let i = 0; i < asc.size(); ) {
u24 =
(b64Table[string.byte(asc, i++)[0]] << 18) |
(b64Table[string.byte(asc, i++)[0]] << 12) |
((r1 = b64Table[string.byte(asc, i++)[0]]) << 6) |
(r2 = b64Table[string.byte(asc, i++)[0]]);
binary +=
r1 === 64
? fromCharCode((u24 >> 16) & 255)
: r2 === 64
? fromCharCode((u24 >> 16) & 255, (u24 >> 8) & 255)
: fromCharCode((u24 >> 16) & 255, (u24 >> 8) & 255, u24 & 255);
}
return binary;
};
// Adapted from https://gist.github.com/jonleighton/958841
export function atob(buf: number[]): string {
let base64 = "";
let encodings =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let byteLength = buf.size();
let byteRemainder = byteLength % 3;
let mainLength = byteLength - byteRemainder;
let a: number, b: number, c: number, d: number;
let chunk;
// Main loop deals with bytes in chunks of 3
for (let i = 0; i < mainLength; i = i + 3) {
// Combine the three bytes into a single integer
chunk = (buf[i] << 16) | (buf[i + 1] << 8) | buf[i + 2];
// Use bitmasks to extract 6-bit segments from the triplet
a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
d = chunk & 63; // 63 = 2^6 - 1
// Convert the raw binary segments to the appropriate ASCII encoding
base64 +=
string.char(string.byte(encodings, a + 1)[0]) +
string.char(string.byte(encodings, b + 1)[0]) +
string.char(string.byte(encodings, c + 1)[0]) +
string.char(string.byte(encodings, d + 1)[0]);
}
// Deal with the remaining bytes and padding
if (byteRemainder === 1) {
chunk = buf[mainLength];
a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
// Set the 4 least significant bits to zero
b = (chunk & 3) << 4; // 3 = 2^2 - 1
base64 +=
string.byte(encodings, a)[0] + string.byte(encodings, b)[0] + "==";
} else if (byteRemainder === 2) {
chunk = (buf[mainLength] << 8) | buf[mainLength + 1];
a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4
// Set the 2 least significant bits to zero
c = (chunk & 15) << 2; // 15 = 2^4 - 1
base64 +=
string.char(string.byte(encodings, a + 1)[0]) +
string.char(string.byte(encodings, b + 1)[0]) +
string.char(string.byte(encodings, c + 1)[0]) +
"=";
}
return base64;
}

26
src/index.ts Normal file
View file

@ -0,0 +1,26 @@
const { generatePrivateKey, generatePublicKey } = require<{
generatePrivateKey: () => number[];
generatePublicKey: (privateKey: number[]) => number[];
}>("wg");
const { atob } = require<{ atob: (buf: number[]) => string }>("base64");
export interface Keypair {
publicKey: string;
privateKey: string;
}
export interface Wireguard {
generateKeypair(): Keypair;
}
export const wireguard: Wireguard = {
generateKeypair: function () {
const privateKey = generatePrivateKey();
const publicKey = generatePublicKey(privateKey);
return {
publicKey: atob(publicKey),
privateKey: atob(privateKey),
};
},
};

21
src/util.ts Normal file
View file

@ -0,0 +1,21 @@
export function slice<T extends defined>(arr: T[], start: number, stop?: number): T[] {
const length = arr.size();
if (start < 0) {
start = math.max(length + start, 0);
}
if (stop === undefined) {
stop = length;
} else if (stop < 0) {
stop = math.max(length + stop, 0);
}
const result: T[] = [];
for (let i = start; i < stop; i++) {
result.push(arr[i]);
}
return result;
}

282
src/wg.ts Normal file
View file

@ -0,0 +1,282 @@
const { atob } = require<{ atob: (buf: number[]) => string }>("base64");
function gf(init?: number[]): number[] {
const r = new Array<number>(16, 0);
if (init) {
for (let i = 0; i < init.size(); ++i) {
r[i] = init[i];
}
}
return r;
}
function pack(o: number[], n: number[]) {
let b: number,
m = gf(),
t = gf();
for (let i = 0; i < 16; ++i) {
t[i] = n[i];
}
carry(t);
carry(t);
carry(t);
for (let j = 0; j < 2; ++j) {
m[0] = t[0] - 0xffed;
for (let i = 1; i < 15; ++i) {
m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
m[i - 1] &= 0xffff;
}
m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
b = (m[15] >> 16) & 1;
m[14] &= 0xffff;
cswap(t, m, 1 - b);
}
for (let i = 0; i < 16; ++i) {
o[2 * i] = t[i] & 0xff;
o[2 * i + 1] = t[i] >> 8;
}
}
function carry(o: number[]) {
let c: number;
for (let i = 0; i < 16; ++i) {
o[(i + 1) % 16] += (i < 15 ? 1 : 38) * math.floor(o[i] / 65536);
o[i] &= 0xffff;
}
}
function cswap(p: number[], q: number[], b: number) {
let t: number,
c = ~(b - 1);
for (let i = 0; i < 16; ++i) {
t = c & (p[i] ^ q[i]);
p[i] ^= t;
q[i] ^= t;
}
}
function add(o: number[], a: number[], b: number[]) {
for (let i = 0; i < 16; ++i) {
o[i] = (a[i] + b[i]) | 0;
}
}
function subtract(o: number[], a: number[], b: number[]) {
for (let i = 0; i < 16; ++i) {
o[i] = (a[i] - b[i]) | 0;
}
}
function multmod(o: number[], a: number[], b: number[]) {
const t = new Array<number>(31, 0);
for (let i = 0; i < 16; ++i) {
for (let j = 0; j < 16; ++j) {
t[i + j] += a[i] * b[j];
}
}
for (let i = 0; i < 15; ++i) {
t[i] += 38 * t[i + 16];
}
for (let i = 0; i < 16; ++i) {
o[i] = t[i];
}
carry(o);
carry(o);
}
function invert(o: number[], i: number[]) {
const c = gf();
for (let a = 0; a < 16; ++a) {
c[a] = i[a];
}
for (let a = 253; a >= 0; --a) {
multmod(c, c, c);
if (a !== 2 && a !== 4) {
multmod(c, c, i);
}
}
for (let a = 0; a < 16; ++a) {
o[a] = c[a];
}
}
let Base64 = {
_keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
encode: function (e: string) {
let t = "";
let n: number, r: number, i: number, s, o, u, a;
let f = 0;
e = Base64._utf8_encode(e);
while (f < e.size()) {
n = string.byte(e, f++)[0];
r = string.byte(e, f++)[0];
i = string.byte(e, f++)[0];
s = n >> 2;
o = ((n & 3) << 4) | (r >> 4);
u = ((r & 15) << 2) | (i >> 6);
a = i & 63;
if (r !== r) {
u = a = 64;
} else if (i !== i) {
a = 64;
}
t =
t +
string.byte(this._keyStr, s)[0] +
string.byte(this._keyStr, o)[0] +
string.byte(this._keyStr, u)[0] +
string.byte(this._keyStr, a)[0];
}
return t;
},
decode: function (e: string) {
let t = "";
let n, r, i;
let s: number, o: number, u: number, a: number;
let f = 0;
e = string.gsub(e, "[^A-Za-z0-9%+%/%=]", "")[0];
while (f < e.size()) {
s = string.find(
this._keyStr,
string.char(string.byte(e, f++)[0])
)[0] as number;
o = string.find(
this._keyStr,
string.char(string.byte(e, f++)[0])
)[0] as number;
u = string.find(
this._keyStr,
string.char(string.byte(e, f++)[0])
)[0] as number;
a = string.find(
this._keyStr,
string.char(string.byte(e, f++)[0])
)[0] as number;
n = (s << 2) | (o >> 4);
r = ((o & 15) << 4) | (u >> 2);
i = ((u & 3) << 6) | a;
t = t + string.char(n);
if (u !== 64) {
t = t + string.char(r);
}
if (a !== 64) {
t = t + string.char(i);
}
}
t = Base64._utf8_decode(t);
return t;
},
_utf8_encode: function (e: string) {
e = string.gsub(e, "\r\n", "\n")[0];
let t = "";
for (let n = 0; n < e.size(); n++) {
let r = string.byte(e, n)[0];
if (r < 128) {
t += string.char(r);
} else if (r > 127 && r < 2048) {
t += string.char((r >> 6) | 192);
t += string.char((r & 63) | 128);
} else {
t += string.char((r >> 12) | 224);
t += string.char(((r >> 6) & 63) | 128);
t += string.char((r & 63) | 128);
}
}
return t;
},
_utf8_decode: function (e: string) {
let t = "";
let n = 0;
let r = 0;
let c1 = 0;
let c2 = 0;
let c3 = 0;
while (n < e.size()) {
r = string.byte(e, n)[0];
if (r < 128) {
t += string.char(r);
n++;
} else if (r > 191 && r < 224) {
c2 = string.byte(e, n + 1)[0];
t += string.char(((r & 31) << 6) | (c2 & 63));
n += 2;
} else {
c2 = string.byte(e, n + 1)[0];
c3 = string.byte(e, n + 2)[0];
t += string.char(((r & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
n += 3;
}
}
return t;
},
};
function clamp(z: number[]) {
z[31] = (z[31] & 127) | 64;
z[0] &= 248;
}
export function generatePublicKey(privateKey: number[]): number[] {
let r: number,
z = new Array<number>(32, 0);
let a = gf([1]),
b = gf([9]),
c = gf(),
d = gf([1]),
e = gf(),
f = gf(),
_121665 = gf([0xdb41, 1]),
_9 = gf([9]);
for (let i = 0; i < 32; ++i) {
z[i] = privateKey[i];
}
clamp(z);
for (let i = 254; i >= 0; --i) {
r = (z[i >>> 3] >>> (i & 7)) & 1;
cswap(a, b, r);
cswap(c, d, r);
add(e, a, c);
subtract(a, a, c);
add(c, b, d);
subtract(b, b, d);
multmod(d, e, e);
multmod(f, a, a);
multmod(a, c, a);
multmod(c, b, e);
add(e, a, c);
subtract(a, a, c);
multmod(b, a, a);
subtract(c, d, f);
multmod(a, c, _121665);
add(a, a, d);
multmod(c, c, a);
multmod(a, d, f);
multmod(d, b, _9);
multmod(b, e, e);
cswap(a, b, r);
cswap(c, d, r);
}
invert(c, c);
multmod(a, a, c);
pack(z, a);
return z;
}
function generatePresharedKey(): number[] {
let privateKey = new Array(32, 0).map(() => {
return math.floor(math.random() * 256);
});
return privateKey;
}
export function generatePrivateKey(): number[] {
const privateKey = generatePresharedKey();
clamp(privateKey);
return privateKey;
}

27
tsconfig.json Normal file
View file

@ -0,0 +1,27 @@
{
"compilerOptions": {
// required
"allowSyntheticDefaultImports": true,
"downlevelIteration": true,
"jsx": "react",
"jsxFactory": "Roact.createElement",
"jsxFragmentFactory": "Roact.createFragment",
"module": "commonjs",
"moduleResolution": "Node",
"noLib": true,
"resolveJsonModule": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"moduleDetection": "force",
"strict": true,
"target": "ESNext",
"typeRoots": ["node_modules/@rbxts"],
// configurable
"rootDir": "src",
"outDir": "out",
"incremental": true,
"tsBuildInfoFile": "out/tsconfig.tsbuildinfo",
"declaration": true
}
}