hackmud-script-manager 0.20.4-d9a4cf1 → 0.20.4-ec30de9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/hsm.js CHANGED
@@ -5,14 +5,14 @@ import { countHackmudCharacters } from "@samual/lib/countHackmudCharacters"
5
5
  import { writeFilePersistent } from "@samual/lib/writeFilePersistent"
6
6
  import { writeFile, readFile } from "fs/promises"
7
7
  import { homedir } from "os"
8
- import { extname, basename, resolve, dirname, relative } from "path"
8
+ import { resolve, extname, basename, dirname, relative } from "path"
9
9
  import { supportedExtensions } from "../constants.js"
10
10
  import { generateTypeDeclaration } from "../generateTypeDeclaration.js"
11
11
  import { pull } from "../pull.js"
12
12
  import { syncMacros } from "../syncMacros.js"
13
13
  import "@samual/lib/readDirectoryWithStats"
14
14
  import "@samual/lib/copyFilePersistent"
15
- const version = "0.20.4-d9a4cf1",
15
+ const version = "0.20.4-ec30de9",
16
16
  options = new Map(),
17
17
  commands = [],
18
18
  userColours = new Cache(user => {
@@ -36,10 +36,6 @@ for (const argument of process.argv.slice(2))
36
36
  if ("-" == argument[1]) options.set(key.slice(2), value)
37
37
  else for (const option of key.slice(1)) options.set(option, value)
38
38
  } else commands.push(argument)
39
- if ("v" == commands[0] || "version" == commands[0] || options.get("version") || options.get("v")) {
40
- console.log(version)
41
- process.exit()
42
- }
43
39
  const pushModule = import("../push.js"),
44
40
  processScriptModule = import("../processScript/index.js"),
45
41
  watchModule = import("../watch.js"),
@@ -57,153 +53,163 @@ const pushModule = import("../push.js"),
57
53
  colourS = chalk.rgb(122, 178, 244),
58
54
  colourV = chalk.rgb(255, 0, 236),
59
55
  colourW = chalk.rgb(255, 150, 224)
60
- if (options.get("help") || options.get("h")) {
56
+ if ("v" == commands[0] || "version" == commands[0] || popOption("version", "v")?.value) {
57
+ console.log(version)
58
+ process.exit()
59
+ }
60
+ if (popOption("help", "h")?.value) {
61
61
  logHelp()
62
62
  process.exit()
63
63
  }
64
64
  let autoExit = !0
65
65
  switch (commands[0]) {
66
66
  case "push":
67
+ case "dev":
68
+ case "watch":
69
+ case "golf":
70
+ case "minify":
67
71
  {
68
- const hackmudPath = getHackmudPath(),
69
- sourcePath = commands[1]
70
- if (!sourcePath) {
71
- logError("Must provide the directory to push from\n")
72
- logHelp()
73
- break
74
- }
75
- const scripts = commands.slice(2)
76
- if (scripts.length) {
77
- const invalidScript = scripts.find(
78
- script => !/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(script)
79
- )
80
- if (invalidScript) {
81
- logError(`Invalid script name: ${JSON.stringify(invalidScript)}\n`)
82
- logHelp()
83
- break
84
- }
85
- } else scripts.push("*.*")
86
- const optionsHasNoMinify = options.has("no-minify")
87
- if ((optionsHasNoMinify || options.has("skip-minify")) && options.has("mangle-names")) {
72
+ const noMinifyOption = popOption("no-minify", "skip-minify"),
73
+ mangleNamesOption = popOption("mangle-names"),
74
+ forceQuineCheatsOption = popOption("force-quine-cheats"),
75
+ noMinifyIncompatibleOption = mangleNamesOption || forceQuineCheatsOption
76
+ if (noMinifyOption && noMinifyIncompatibleOption) {
88
77
  logError(
89
- `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
78
+ `Options ${colourN(noMinifyOption.name)} and ${colourN(noMinifyIncompatibleOption.name)} are incompatible\n`
90
79
  )
91
80
  logHelp()
92
- break
81
+ process.exit(1)
93
82
  }
94
- const shouldSkipMinify = options.get("no-minify") || options.get("skip-minify")
95
- let shouldMinify
96
- if (null != shouldSkipMinify) {
97
- if ("boolean" != typeof shouldSkipMinify) {
83
+ noMinifyOption && assertOptionIsBoolean(noMinifyOption)
84
+ mangleNamesOption && assertOptionIsBoolean(mangleNamesOption)
85
+ forceQuineCheatsOption && assertOptionIsBoolean(forceQuineCheatsOption)
86
+ if ("golf" == commands[0] || "minify" == commands[0]) {
87
+ const watchOption = popOption("watch"),
88
+ target = commands[1]
89
+ if (!target) {
90
+ logError("Must provide target\n")
91
+ logHelp()
92
+ process.exit(1)
93
+ }
94
+ const fileExtension = extname(target)
95
+ if (!supportedExtensions.includes(fileExtension)) {
98
96
  logError(
99
- `The value for ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
97
+ `Unsupported file extension "${chalk.bold(fileExtension)}"\nSupported extensions are "${supportedExtensions.map(extension => chalk.bold(extension)).join('", "')}"`
100
98
  )
101
- logHelp()
102
- break
99
+ process.exit(1)
103
100
  }
104
- shouldMinify = !shouldSkipMinify
105
- }
106
- const shouldMangleNames = options.get("mangle-names")
107
- if (null != shouldMangleNames && "boolean" != typeof shouldMangleNames) {
108
- logError(
109
- `The value for ${colourN("--mangle-names")} must be ${colourV("true")} or ${colourV("false")}\n`
110
- )
111
- logHelp()
112
- break
113
- }
114
- const shouldforceQuineCheats = options.get("force-quine-cheats")
115
- if (null != shouldforceQuineCheats && "boolean" != typeof shouldforceQuineCheats) {
116
- logError(
117
- `The value for ${colourN("--force-quine-cheats")} must be ${colourV("true")} or ${colourV("false")}\n`
118
- )
119
- logHelp()
120
- break
121
- }
122
- const { push } = await pushModule
123
- ;(
124
- await push(sourcePath, hackmudPath, {
125
- scripts,
126
- onPush: info => logInfo(info, hackmudPath),
127
- minify: shouldMinify,
128
- mangleNames: shouldMangleNames,
129
- forceQuineCheats: shouldforceQuineCheats
130
- })
131
- ).length || logError("Could not find any scripts to push")
132
- }
133
- break
134
- case "dev":
135
- case "watch":
136
- {
137
- const hackmudPath = getHackmudPath(),
138
- sourcePath = commands[1]
139
- if (!sourcePath) {
140
- logError("Must provide the directory to watch\n")
141
- logHelp()
142
- break
143
- }
144
- const scripts = commands.slice(2)
145
- if (scripts.length) {
146
- const invalidScript = scripts.find(
147
- script => !/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(script)
148
- )
149
- if (invalidScript) {
150
- logError(`Invalid script name: ${JSON.stringify(invalidScript)}\n`)
101
+ const { processScript } = await processScriptModule,
102
+ fileBaseName = basename(target, fileExtension),
103
+ fileBaseNameEndsWithDotSrc = fileBaseName.endsWith(".src"),
104
+ scriptName = fileBaseNameEndsWithDotSrc ? fileBaseName.slice(0, -4) : fileBaseName,
105
+ scriptUser =
106
+ (
107
+ "scripts" == basename(resolve(target, "..")) &&
108
+ "hackmud" == basename(resolve(target, "../../.."))
109
+ ) ?
110
+ basename(resolve(target, "../.."))
111
+ : void 0
112
+ let outputPath =
113
+ commands[2] ||
114
+ resolve(
115
+ dirname(target),
116
+ fileBaseNameEndsWithDotSrc ? scriptName + ".js"
117
+ : ".js" == fileExtension ? fileBaseName + ".min.js"
118
+ : fileBaseName + ".js"
119
+ )
120
+ const golfFile = () =>
121
+ readFile(target, { encoding: "utf8" }).then(async source => {
122
+ const timeStart = performance.now(),
123
+ { script, warnings } = await processScript(source, {
124
+ minify: noMinifyOption && !noMinifyOption.value,
125
+ scriptUser,
126
+ scriptName,
127
+ filePath: target,
128
+ mangleNames: mangleNamesOption?.value,
129
+ forceQuineCheats: forceQuineCheatsOption?.value
130
+ }),
131
+ timeTook = performance.now() - timeStart
132
+ for (const { message, line } of warnings)
133
+ log(`Warning "${chalk.bold(message)}" on line ${chalk.bold(line + "")}`)
134
+ await writeFilePersistent(outputPath, script)
135
+ .catch(error => {
136
+ if (!commands[2] || "EISDIR" != error.code) throw error
137
+ outputPath = resolve(outputPath, basename(target, fileExtension) + ".js")
138
+ return writeFilePersistent(outputPath, script)
139
+ })
140
+ .then(() =>
141
+ log(
142
+ `Wrote ${chalk.bold(countHackmudCharacters(script))} chars to ${chalk.bold(relative(".", outputPath))} | took ${Math.round(100 * timeTook) / 100}ms`
143
+ )
144
+ )
145
+ })
146
+ if (watchOption) {
147
+ const { watch: watchFile } = await chokidarModule
148
+ watchFile(target, { awaitWriteFinish: { stabilityThreshold: 100 } })
149
+ .on("ready", () => log("Watching " + target))
150
+ .on("change", golfFile)
151
+ autoExit = !1
152
+ } else await golfFile()
153
+ } else {
154
+ const hackmudPath = getHackmudPath(),
155
+ sourcePath = commands[1]
156
+ if (!sourcePath) {
157
+ logError(`Must provide the directory to ${"push" == commands[0] ? "push from" : "watch"}\n`)
151
158
  logHelp()
152
- break
159
+ process.exit(1)
153
160
  }
154
- } else scripts.push("*.*")
155
- const optionsHasNoMinify = options.has("no-minify")
156
- if ((optionsHasNoMinify || options.has("skip-minify")) && options.has("mangle-names")) {
157
- logError(
158
- `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
159
- )
160
- logHelp()
161
- break
162
- }
163
- const shouldSkipMinify = options.get("no-minify") || options.get("skip-minify")
164
- let shouldMinify
165
- if (null != shouldSkipMinify) {
166
- if ("boolean" != typeof shouldSkipMinify) {
167
- logError(
168
- `The value for ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} must be ${colourV("true")} or ${colourV("false")}\n`
161
+ const scripts = commands.slice(2)
162
+ if (scripts.length) {
163
+ const invalidScript = scripts.find(
164
+ script => !/^(?:[a-z_][a-z\d_]{0,24}|\*)\.(?:[a-z_][a-z\d_]{0,24}|\*)$/.test(script)
169
165
  )
170
- logHelp()
171
- break
166
+ if (invalidScript) {
167
+ logError(`Invalid script name: ${JSON.stringify(invalidScript)}\n`)
168
+ logHelp()
169
+ process.exit(1)
170
+ }
171
+ } else scripts.push("*.*")
172
+ if ("push" == commands[0]) {
173
+ const { push, MissingSourceFolderError, MissingHackmudFolderError, NoUsersError } =
174
+ await pushModule,
175
+ infos = await push(sourcePath, hackmudPath, {
176
+ scripts,
177
+ onPush: info => logInfo(info, hackmudPath),
178
+ minify: noMinifyOption && !noMinifyOption.value,
179
+ mangleNames: mangleNamesOption?.value,
180
+ forceQuineCheats: forceQuineCheatsOption?.value
181
+ })
182
+ if (infos instanceof Error) {
183
+ logError(infos.message)
184
+ if (infos instanceof MissingSourceFolderError || infos instanceof NoUsersError) {
185
+ console.log()
186
+ logHelp()
187
+ } else
188
+ infos instanceof MissingHackmudFolderError &&
189
+ log(
190
+ `If this is not where your hackmud folder is, you can specify it with the\n${colourN("--hackmud-path")}=${colourB("<path>")} option or ${colourN("HSM_HACKMUD_PATH")} environment variable`
191
+ )
192
+ } else infos.length || logError("Could not find any scripts to push")
193
+ } else {
194
+ const typeDeclarationPathOption = popOption(
195
+ "type-declaration-path",
196
+ "type-declaration",
197
+ "dts",
198
+ "gen-types"
199
+ ),
200
+ { watch } = await watchModule
201
+ watch(sourcePath, hackmudPath, {
202
+ scripts,
203
+ onPush: info => logInfo(info, hackmudPath),
204
+ typeDeclarationPath: typeDeclarationPathOption?.value.toString(),
205
+ minify: noMinifyOption && !noMinifyOption.value,
206
+ mangleNames: mangleNamesOption?.value,
207
+ onReady: () => log("Watching"),
208
+ forceQuineCheats: forceQuineCheatsOption?.value
209
+ })
210
+ autoExit = !1
172
211
  }
173
- shouldMinify = !shouldSkipMinify
174
212
  }
175
- const shouldMangleNames = options.get("mangle-names")
176
- if (null != shouldMangleNames && "boolean" != typeof shouldMangleNames) {
177
- logError(
178
- `The value for ${colourN("--mangle-names")} must be ${colourV("true")} or ${colourV("false")}\n`
179
- )
180
- logHelp()
181
- break
182
- }
183
- const shouldforceQuineCheats = options.get("force-quine-cheats")
184
- if (null != shouldforceQuineCheats && "boolean" != typeof shouldforceQuineCheats) {
185
- logError(
186
- `The value for ${colourN("--force-quine-cheats")} must be ${colourV("true")} or ${colourV("false")}\n`
187
- )
188
- logHelp()
189
- break
190
- }
191
- const { watch } = await watchModule
192
- watch(sourcePath, hackmudPath, {
193
- scripts,
194
- onPush: info => logInfo(info, hackmudPath),
195
- typeDeclarationPath: (
196
- options.get("type-declaration-path") ||
197
- options.get("type-declaration") ||
198
- options.get("dts") ||
199
- options.get("gen-types")
200
- )?.toString(),
201
- minify: shouldMinify,
202
- mangleNames: shouldMangleNames,
203
- onReady: () => log("Watching"),
204
- forceQuineCheats: shouldforceQuineCheats
205
- })
206
- autoExit = !1
207
213
  }
208
214
  break
209
215
  case "pull":
@@ -213,7 +219,7 @@ switch (commands[0]) {
213
219
  if (!script) {
214
220
  logError("Must provide the script to pull\n")
215
221
  logHelp()
216
- break
222
+ process.exit(1)
217
223
  }
218
224
  const sourcePath = commands[2] || "."
219
225
  await pull(sourcePath, hackmudPath, script).catch(error => {
@@ -234,18 +240,19 @@ switch (commands[0]) {
234
240
  case "gen-dts":
235
241
  case "gen-types":
236
242
  {
237
- const target = commands[1]
243
+ const hackmudPath = getHackmudPath(),
244
+ target = commands[1]
238
245
  if (!target) {
239
246
  logError("Must provide target directory\n")
240
247
  logHelp()
241
- break
248
+ process.exit(1)
242
249
  }
243
250
  const sourcePath = resolve(target),
244
251
  outputPath = commands[2] || "./player.d.ts",
245
- typeDeclaration = await generateTypeDeclaration(sourcePath, getHackmudPath())
252
+ typeDeclaration = await generateTypeDeclaration(sourcePath, hackmudPath)
246
253
  let typeDeclarationPath = resolve(outputPath)
247
254
  await writeFile(typeDeclarationPath, typeDeclaration).catch(error => {
248
- assert(error instanceof Error, "src/bin/hsm.ts:327:35")
255
+ assert(error instanceof Error, "src/bin/hsm.ts:321:35")
249
256
  if ("EISDIR" != error.code) throw error
250
257
  typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
251
258
  return writeFile(typeDeclarationPath, typeDeclaration)
@@ -257,99 +264,6 @@ switch (commands[0]) {
257
264
  case "h":
258
265
  logHelp()
259
266
  break
260
- case "golf":
261
- case "minify":
262
- {
263
- const target = commands[1]
264
- if (!target) {
265
- logError("Must provide target\n")
266
- logHelp()
267
- break
268
- }
269
- const fileExtension = extname(target)
270
- if (!supportedExtensions.includes(fileExtension)) {
271
- logError(
272
- `Unsupported file extension "${chalk.bold(fileExtension)}"\nSupported extensions are "${supportedExtensions.map(extension => chalk.bold(extension)).join('", "')}"`
273
- )
274
- break
275
- }
276
- const { processScript } = await processScriptModule,
277
- fileBaseName = basename(target, fileExtension),
278
- fileBaseNameEndsWithDotSrc = fileBaseName.endsWith(".src"),
279
- scriptName = fileBaseNameEndsWithDotSrc ? fileBaseName.slice(0, -4) : fileBaseName,
280
- scriptUser =
281
- "scripts" == basename(resolve(target, "..")) && "hackmud" == basename(resolve(target, "../../..")) ?
282
- basename(resolve(target, "../.."))
283
- : void 0,
284
- optionsHasNoMinify = options.has("no-minify")
285
- if ((optionsHasNoMinify || options.has("skip-minify")) && options.has("mangle-names")) {
286
- logError(
287
- `Options ${colourN("--mangle-names")} and ${colourN(optionsHasNoMinify ? "--no-minify" : "--skip-minify")} are incompatible\n`
288
- )
289
- logHelp()
290
- break
291
- }
292
- const mangleNames_ = options.get("mangle-names")
293
- if (null != mangleNames_ && "boolean" != typeof mangleNames_) {
294
- logError(
295
- `The value for ${colourN("--mangle-names")} must be ${colourV("true")} or ${colourV("false")}\n`
296
- )
297
- logHelp()
298
- break
299
- }
300
- const mangleNames = mangleNames_,
301
- forceQuineCheats_ = options.get("force-quine-cheats")
302
- if (null != forceQuineCheats_ && "boolean" != typeof forceQuineCheats_) {
303
- logError(
304
- `the value for ${colourN("--force-quine-cheats")} must be ${colourV("true")} or ${colourV("false")}\n`
305
- )
306
- logHelp()
307
- break
308
- }
309
- const forceQuineCheats = forceQuineCheats_
310
- let outputPath =
311
- commands[2] ||
312
- resolve(
313
- dirname(target),
314
- fileBaseNameEndsWithDotSrc ? scriptName + ".js"
315
- : ".js" == fileExtension ? fileBaseName + ".min.js"
316
- : fileBaseName + ".js"
317
- )
318
- const golfFile = () =>
319
- readFile(target, { encoding: "utf8" }).then(async source => {
320
- const timeStart = performance.now(),
321
- { script, warnings } = await processScript(source, {
322
- minify: !(options.get("no-minify") || options.get("skip-minify")),
323
- scriptUser,
324
- scriptName,
325
- filePath: target,
326
- mangleNames,
327
- forceQuineCheats
328
- }),
329
- timeTook = performance.now() - timeStart
330
- for (const { message, line } of warnings)
331
- log(`Warning "${chalk.bold(message)}" on line ${chalk.bold(line + "")}`)
332
- await writeFilePersistent(outputPath, script)
333
- .catch(error => {
334
- if (!commands[2] || "EISDIR" != error.code) throw error
335
- outputPath = resolve(outputPath, basename(target, fileExtension) + ".js")
336
- return writeFilePersistent(outputPath, script)
337
- })
338
- .then(() =>
339
- log(
340
- `Wrote ${chalk.bold(countHackmudCharacters(script))} chars to ${chalk.bold(relative(".", outputPath))} | took ${Math.round(100 * timeTook) / 100}ms`
341
- )
342
- )
343
- })
344
- if (options.get("watch")) {
345
- const { watch: watchFile } = await chokidarModule
346
- watchFile(target, { awaitWriteFinish: { stabilityThreshold: 100 } })
347
- .on("ready", () => log("Watching " + target))
348
- .on("change", golfFile)
349
- autoExit = !1
350
- } else await golfFile()
351
- }
352
- break
353
267
  default:
354
268
  commands[0] && logError(`Unknown command: ${colourL(commands[0])}\n`)
355
269
  logHelp()
@@ -423,7 +337,7 @@ function logError(message) {
423
337
  process.exitCode = 1
424
338
  }
425
339
  function getHackmudPath() {
426
- const hackmudPathOption = options.get("hackmud-path")
340
+ const hackmudPathOption = popOption("hackmud-path")
427
341
  if (null != hackmudPathOption && "string" != typeof hackmudPathOption) {
428
342
  logError(`Option ${colourN("--hackmud-path")} must be a string, got ${colourV(hackmudPathOption)}\n`)
429
343
  logHelp()
@@ -435,3 +349,26 @@ function getHackmudPath() {
435
349
  ("win32" == process.platform ? resolve(process.env.APPDATA, "hackmud") : resolve(homedir(), ".config/hackmud"))
436
350
  )
437
351
  }
352
+ function assertOptionIsBoolean(option) {
353
+ if ("boolean" != typeof option.value) {
354
+ logError(`The value for ${colourN(option.name)} must be ${colourV("true")} or ${colourV("false")}\n`)
355
+ logHelp()
356
+ process.exit(1)
357
+ }
358
+ }
359
+ function popOption(...names) {
360
+ const presentOptionNames = names.filter(name => options.has(name))
361
+ if (!presentOptionNames.length) return
362
+ const presentOptionNamesWithDashDash = presentOptionNames.map(name =>
363
+ colourN(`-${1 == name.length ? "" : "-"}${name}`)
364
+ )
365
+ if (presentOptionNames.length > 1) {
366
+ logError(
367
+ `The options ${presentOptionNamesWithDashDash.join(", ")} are aliases for each other. Please only specify one`
368
+ )
369
+ process.exit(1)
370
+ }
371
+ const value = options.get(presentOptionNames[0])
372
+ options.delete(presentOptionNames[0])
373
+ return { name: presentOptionNamesWithDashDash[0], value }
374
+ }
package/env.d.ts CHANGED
@@ -1,18 +1,14 @@
1
- type Replace<T, R> = Omit<T, Extract<keyof R, keyof T>> & R
1
+ type Replace<A, B> = Omit<A, keyof B> & B
2
2
  type ScriptSuccess<T = object> = { ok: true } & T
3
3
  type ScriptFailure = { ok: false, msg?: string }
4
4
  type ScriptResponse<T = object> = ScriptSuccess<T> | ScriptFailure
5
5
  type ErrorScripts = Record<string, () => ScriptFailure>
6
6
 
7
- type AllOptional<T> = {
8
- [K in keyof T]-?: {} extends Pick<T, K> ? true : false
9
- }[keyof T]
7
+ type AllOptional<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? true : false }[keyof T]
10
8
 
11
9
  type Scriptor<Args = unknown, Ret = unknown> = {
12
- name: string,
13
- call: AllOptional<Args> extends true
14
- ? (args?: Args) => Ret
15
- : (args: Args) => Ret
10
+ name: string
11
+ call: AllOptional<Args> extends true ? (args?: Args) => Ret : (args: Args) => Ret
16
12
  }
17
13
 
18
14
  type Subscripts = Record<string, Record<string, (...args: any) => any>> & {
@@ -41,7 +37,7 @@ type UpgradeRarityString = "`0noob`" | "`1kiddie`" | "`2h4x0r`" | "`3h4rdc0r3`"
41
37
  type UpgradeRarityNumber = 0 | 1 | 2 | 3 | 4 | 5;
42
38
  type UpgradeRarity = UpgradeRarityString | UpgradeRarityNumber;
43
39
 
44
- type UpgradeCore = {
40
+ type UpgradeBase = {
45
41
  name: string
46
42
  type: "lock" | "script_space" | "chat" | "script" | "tool" | "bot_brain" | "glam"
47
43
  up_class?: -1 | 0 | 1 | 2 | 3
@@ -53,9 +49,9 @@ type UpgradeCore = {
53
49
  description: string
54
50
  }
55
51
 
56
- type Upgrade = UpgradeCore & Record<string, null | boolean | number | string>
52
+ type Upgrade = UpgradeBase & Record<string, null | boolean | number | string>
57
53
 
58
- type CLIUpgrade = Omit<UpgradeCore, `rarity`> & {
54
+ type CliUpgrade = Omit<UpgradeBase, `rarity`> & {
59
55
  [x: string]: null | boolean | number | string
60
56
  rarity: UpgradeRarityString
61
57
  }
@@ -155,7 +151,16 @@ type Fullsec = Subscripts & PlayerFullsec & {
155
151
  market: {
156
152
  /** **FULLSEC** */ browse: {
157
153
  (args:
158
- Partial<{ seller: string, listed_before: number, listed_after: number, cost: number | string } & Omit<CLIUpgrade, "rarity">>
154
+ Partial<{
155
+ seller: string | MongoQuerySelector<string>,
156
+ listed_before: number | MongoQuerySelector<number>,
157
+ listed_after: number,
158
+ cost: number | MongoQuerySelector<number> | string,
159
+ rarity: UpgradeRarityNumber | MongoQuerySelector<UpgradeRarityNumber>,
160
+ name: string | MongoQuerySelector<string>
161
+ } & Omit<{
162
+ [k in keyof CliUpgrade]: CliUpgrade[k] | MongoQuerySelector<CliUpgrade[k]>
163
+ }, "rarity">>
159
164
  ): { i: string, name: string, rarity: Upgrade["rarity"], cost: number }[] | ScriptFailure
160
165
 
161
166
  <I extends string>(args: { i: I }): {
@@ -357,7 +362,7 @@ type Fullsec = Subscripts & PlayerFullsec & {
357
362
  * const arr = [ 1, 2, 2, 3, 2 ]
358
363
  *
359
364
  * $D(uniq(arr)) // [ 1, 2, 3, 2 ] */
360
- uniq: (array: T[]) => T[]
365
+ uniq: <T>(array: T[]) => T[]
361
366
 
362
367
  /** Sorts an array of numbers or number-coercible strings in descending order. */
363
368
  u_sort_num_arr_desc: <T>(array: T[]) => T[]
@@ -424,17 +429,17 @@ type Fullsec = Subscripts & PlayerFullsec & {
424
429
  upgrades_of_owner: {
425
430
  <F extends Partial<Upgrade & { loaded: boolean }> = object>(args?: { filter?: F, full?: false }): (
426
431
  Omit<
427
- Pick<UpgradeCore, "tier" | "rarity" | "name" | "type" | "i" | "loaded">,
432
+ Pick<UpgradeBase, "tier" | "rarity" | "name" | "type" | "i" | "loaded">,
428
433
  keyof F
429
434
  > & Pick<F, "tier" | "rarity" | "name" | "type" | "i" | "loaded">
430
435
  )[] | ScriptFailure
431
436
 
432
437
  <F extends Partial<Upgrade & { loaded: boolean }> = object>(args: { filter?: F, full: true }): (
433
- Omit<UpgradeCore, keyof F> & F & Record<string, null | boolean | number | string>
438
+ Omit<UpgradeBase, keyof F> & F & Record<string, null | boolean | number | string>
434
439
  )[] | ScriptFailure
435
440
 
436
441
  <I extends number>(args: { i: I }): (
437
- Omit<UpgradeCore, "i"> & { [x: string]: null | boolean | number | string, i: I }
442
+ Omit<UpgradeBase, "i"> & { [x: string]: null | boolean | number | string, i: I }
438
443
  ) | ScriptFailure
439
444
  }
440
445
 
@@ -511,7 +516,7 @@ type Highsec = Fullsec & PlayerHighsec & {
511
516
  /** **HIGHSEC** */
512
517
  upgrades: {
513
518
  <I extends number>(args: { i: I }): (
514
- Omit<UpgradeCore, "i"> & { [x: string]: null | boolean | number | string, i: I }
519
+ Omit<UpgradeBase, "i"> & { [x: string]: null | boolean | number | string, i: I }
515
520
  ) | ScriptFailure
516
521
 
517
522
  <F extends Partial<Upgrade & { loaded: boolean }> = object>(args?: {
@@ -519,20 +524,20 @@ type Highsec = Fullsec & PlayerHighsec & {
519
524
  is_script?: true
520
525
  full?: false
521
526
  }): (
522
- Omit<Pick<UpgradeCore, "tier" | "rarity" | "name" | "type" | "i" | "loaded">, keyof F> & F &
527
+ Omit<Pick<UpgradeBase, "tier" | "rarity" | "name" | "type" | "i" | "loaded">, keyof F> & F &
523
528
  Record<string, null | boolean | number | string>
524
529
  )[] | ScriptFailure
525
530
 
526
531
  <F extends Partial<Upgrade & { loaded: boolean }> = object>(args?:
527
532
  { filter?: F, is_script?: true, full: true }
528
- ): (Omit<UpgradeCore, keyof F> & F & Record<string, null | boolean | number | string>)[] | ScriptFailure
533
+ ): (Omit<UpgradeBase, keyof F> & F & Record<string, null | boolean | number | string>)[] | ScriptFailure
529
534
 
530
535
  (args?: { filter?: Partial<Upgrade & { loaded: boolean }>, is_script: false, full?: false }):
531
536
  { msg: string, upgrades: string[] } | ScriptFailure
532
537
 
533
538
  <F extends Partial<Upgrade & { loaded: boolean }> = object>(
534
539
  args?: { filter?: F, is_script: false, full: true }
535
- ): (Omit<UpgradeCore, keyof F | `rarity`> & F & {
540
+ ): (Omit<UpgradeBase, keyof F | `rarity`> & F & {
536
541
  [x: string]: null | boolean | number | string
537
542
  rarity: UpgradeRarityString
538
543
  })[] | ScriptFailure
@@ -714,10 +719,50 @@ type Nullsec = Lowsec & PlayerNullsec & {
714
719
  }
715
720
  }
716
721
 
722
+ type MongoTypeString = "minKey" | "double" | "string" | "object" | "array" | "binData" | "undefined" | "objectId" |
723
+ "bool" | "date" | "null" | "regex" | "dbPointer" | "javascript" | "symbol" | "int" | "timestamp" | "long" | "decimal" | "maxKey";
724
+ type MongoTypeNumber = -1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 127;
725
+
717
726
  type MongoValue = string | number | boolean | Date | MongoValue[] | { [key: string]: MongoValue } | null
718
727
 
719
728
  type MongoCommandValue = string | number | boolean | Date | MongoCommandValue[] | { [key: string]: MongoCommandValue } |
720
- null | undefined
729
+ null | undefined
730
+
731
+ /**
732
+ * Currently unused
733
+ */
734
+ type MongoLogicalSelectors<T extends MongoValue = MongoValue> = {
735
+ $not: T | MongoComparisonSelectors<T> | MongoLogicalSelectors<T>
736
+ $nor: T[]
737
+ $or: T[]
738
+ $and: T[]
739
+ }
740
+
741
+ type MongoArraySelectors<T extends Array<MongoValue> = Array<MongoValue>> = {
742
+ $all: T
743
+ $elemMatch: T
744
+ $size: number
745
+ }
746
+
747
+ type MongoComparisonSelectors<T extends MongoValue = MongoValue> = {
748
+ $eq: T
749
+ $gt: T
750
+ $gte: T
751
+ $in: T[]
752
+ $lt: T
753
+ $lte: T
754
+ $ne: T
755
+ $nin: T[]
756
+ }
757
+
758
+ type MongoElementSelectors = {
759
+ $exists: boolean
760
+ $type: MongoTypeNumber | MongoTypeString
761
+ }
762
+
763
+ type MongoQuerySelector<T extends MongoValue = MongoValue> = Partial<T extends MongoValue[] ?
764
+ (MongoArraySelectors<T> & MongoElementSelectors & MongoComparisonSelectors<T>) :
765
+ (MongoElementSelectors & MongoComparisonSelectors<T>)>
721
766
 
722
767
  type Query = { [key: string]: MongoValue | Query } & { _id?: Id, $in?: MongoValue[] }
723
768
  type Projection = Record<string, boolean | 0 | 1>
@@ -766,7 +811,7 @@ type Cursor = {
766
811
  ObjectId: () => any
767
812
  }
768
813
 
769
- type CLIContext = {
814
+ type CliContext = {
770
815
  /** The name of the user who is calling the script. */ caller: string
771
816
  /** The name of this script. */ this_script: string
772
817
  /** The number of columns in the caller’s terminal. */ cols: number
@@ -774,17 +819,23 @@ type CLIContext = {
774
819
 
775
820
  /** The name of the script that directly called this script, or null if called on the command line or as a
776
821
  * scriptor. */ calling_script: null
822
+ is_scriptor?: undefined
823
+ is_brain?: undefined
777
824
  }
778
825
 
779
- type SubscriptContext = Replace<CLIContext, {
826
+ type SubscriptContext = Replace<CliContext, {
780
827
  /** The name of the script that directly called this script, or null if called on the command line or as a scriptor.
781
828
  */
782
829
  calling_script: string
783
830
  }>
784
831
 
785
- type ScriptorContext = CLIContext & { /** Whether the script is being run as a scriptor. */ is_scriptor: true }
786
- type BrainContext = CLIContext & { /** Whether the script is being run via a bot brain. */ is_brain: true }
787
- type Context = CLIContext | SubscriptContext | ScriptorContext | BrainContext
832
+ type ScriptorContext =
833
+ Replace<CliContext, { /** Whether the script is being run as a scriptor. */ is_scriptor: true }>
834
+
835
+ type BrainContext =
836
+ Replace<CliContext, { /** Whether the script is being run via a bot brain. */ is_brain: true }>
837
+
838
+ type Context = CliContext | SubscriptContext | ScriptorContext | BrainContext
788
839
 
789
840
  /** Subscript space that can call FULLSEC scripts. */ declare const $fs: Fullsec
790
841
 
@@ -827,6 +878,8 @@ declare const $0s: typeof $ns
827
878
  * } */
828
879
  declare const $s: Nullsec
829
880
 
881
+ type ObjectId = { $oid: string }
882
+
830
883
  declare const $db: {
831
884
  /** Insert a document or documents into a collection.
832
885
  * @param documents A document or array of documents to insert into the collection. */
@@ -920,6 +973,8 @@ declare const $db: {
920
973
  signature: { hash: "Undefined Conversion", keyId: "Undefined Conversion" }
921
974
  }
922
975
  }
976
+
977
+ ObjectId: () => ObjectId
923
978
  }
924
979
 
925
980
  /** Debug Log.
@@ -956,7 +1011,7 @@ declare const $FMCL: undefined | true
956
1011
  * @example
957
1012
  * if (!$G.dbCache)
958
1013
  * $G.dbCache = $db.f({ whatever: true }).first() */
959
- declare const $G: any
1014
+ declare const $G: Record<string | symbol, any>
960
1015
 
961
1016
  /** This contains a JS timestamp (not Date) set immediately before your code begins running.
962
1017
  * @example
@@ -1005,3 +1060,14 @@ declare const _FULL_SCRIPT_NAME: string
1005
1060
  *
1006
1061
  * In rare cases where it's not known at build time, it's `-1`. */
1007
1062
  declare const _SECLEVEL: -1 | 0 | 1 | 2 | 3 | 4
1063
+
1064
+ type DeepFreeze<T> = { readonly [P in keyof T]: DeepFreeze<T[P]> }
1065
+
1066
+ /** Recursively
1067
+ * [`Object.freeze()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)
1068
+ * an object and its properties' objects and its properties' objects and so on.
1069
+ *
1070
+ * [Official Hackmud Wiki](https://wiki.hackmud.com/scripting/extensions/deep_freeze) */
1071
+ declare const DEEP_FREEZE: <T>(value: T) => DeepFreeze<T>
1072
+
1073
+ declare const _RUN_ID: string
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hackmud-script-manager",
3
- "version": "0.20.4-d9a4cf1",
3
+ "version": "0.20.4-ec30de9",
4
4
  "description": "Script manager for game hackmud, with minification, TypeScript support, and player script type definition generation.",
5
5
  "keywords": [
6
6
  "api",
@@ -323,7 +323,7 @@ async function processScript(
323
323
  trailingComma: "none"
324
324
  })
325
325
  }
326
- code = postprocess(code, seclevel, uniqueId)
326
+ code = postprocess(code, uniqueId)
327
327
  if (includesIllegalString(code))
328
328
  throw Error(
329
329
  'you found a weird edge case where I wasn\'t able to replace illegal strings like "SC$", please report thx'
@@ -43,15 +43,6 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
43
43
  )
44
44
  }
45
45
  }
46
- const hashGReferencePaths = getReferencePathsToGlobal(`$${uniqueId}$GLOBAL$`, program)
47
- if (hashGReferencePaths.length > 3) {
48
- for (const path of hashGReferencePaths) path.replaceWith(t.identifier(`_${uniqueId}_G_`))
49
- mainFunctionPath.node.body.body.unshift(
50
- t.variableDeclaration("let", [
51
- t.variableDeclarator(t.identifier(`_${uniqueId}_G_`), t.identifier(`$${uniqueId}$GLOBAL$`))
52
- ])
53
- )
54
- }
55
46
  const jsonValues = []
56
47
  let scriptBeforeJSONValueReplacement,
57
48
  comment,
@@ -61,7 +52,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
61
52
  traverse(fileBeforeJSONValueReplacement, {
62
53
  MemberExpression({ node: memberExpression }) {
63
54
  if (!memberExpression.computed) {
64
- assert("Identifier" == memberExpression.property.type, "src/processScript/minify.ts:127:60")
55
+ assert("Identifier" == memberExpression.property.type, "src/processScript/minify.ts:115:60")
65
56
  if ("prototype" == memberExpression.property.name) {
66
57
  memberExpression.computed = !0
67
58
  memberExpression.property = t.identifier(`_${uniqueId}_PROTOTYPE_PROPERTY_`)
@@ -136,7 +127,8 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
136
127
  const promises = []
137
128
  traverse(file, {
138
129
  FunctionDeclaration(path) {
139
- path.traverse({
130
+ const body = path.get("body")
131
+ body.traverse({
140
132
  Function(path) {
141
133
  "CallExpression" != path.parent.type && "callee" != path.parentKey && path.skip()
142
134
  },
@@ -152,7 +144,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
152
144
  path.replaceWith(t.identifier(`_${uniqueId}_JSON_VALUE_${jsonValues.push(o) - 1}_`))
153
145
  }
154
146
  })
155
- path.traverse({
147
+ body.traverse({
156
148
  TemplateLiteral(path) {
157
149
  if ("TaggedTemplateExpression" == path.parent.type) return
158
150
  const templateLiteral = path.node
@@ -172,7 +164,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
172
164
  },
173
165
  MemberExpression({ node: memberExpression }) {
174
166
  if (!memberExpression.computed) {
175
- assert("Identifier" == memberExpression.property.type, "src/processScript/minify.ts:259:62")
167
+ assert("Identifier" == memberExpression.property.type, "src/processScript/minify.ts:249:62")
176
168
  if (!(memberExpression.property.name.length < 3)) {
177
169
  memberExpression.computed = !0
178
170
  memberExpression.property = t.stringLiteral(memberExpression.property.name)
@@ -246,7 +238,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
246
238
  })
247
239
  await Promise.all(promises)
248
240
  const functionDeclaration = file.program.body[0]
249
- assert("FunctionDeclaration" == functionDeclaration.type, "src/processScript/minify.ts:364:61")
241
+ assert("FunctionDeclaration" == functionDeclaration.type, "src/processScript/minify.ts:354:61")
250
242
  if (jsonValues.length) {
251
243
  hasComment = !0
252
244
  if (1 == jsonValues.length)
@@ -258,7 +250,10 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
258
250
  t.memberExpression(
259
251
  t.taggedTemplateExpression(
260
252
  t.memberExpression(
261
- t.callExpression(t.identifier(`$${uniqueId}$SUBSCRIPT$scripts$quine$`), []),
253
+ t.callExpression(
254
+ t.identifier(`$${uniqueId}$4$SUBSCRIPT$scripts$quine$`),
255
+ []
256
+ ),
262
257
  t.identifier("split")
263
258
  ),
264
259
  t.templateLiteral([t.templateElement({ raw: "\t", cooked: "\t" }, !0)], [])
@@ -282,7 +277,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
282
277
  t.memberExpression(
283
278
  t.taggedTemplateExpression(
284
279
  t.memberExpression(
285
- t.callExpression(t.identifier(`$${uniqueId}$SUBSCRIPT$scripts$quine$`), []),
280
+ t.callExpression(t.identifier(`$${uniqueId}$4$SUBSCRIPT$scripts$quine$`), []),
286
281
  t.identifier("split")
287
282
  ),
288
283
  t.templateLiteral([t.templateElement({ raw: "\t", cooked: "\t" }, !0)], [])
@@ -307,7 +302,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
307
302
  t.memberExpression(
308
303
  t.taggedTemplateExpression(
309
304
  t.memberExpression(
310
- t.callExpression(t.identifier(`$${uniqueId}$SUBSCRIPT$scripts$quine$`), []),
305
+ t.callExpression(t.identifier(`$${uniqueId}$4$SUBSCRIPT$scripts$quine$`), []),
311
306
  t.identifier("split")
312
307
  ),
313
308
  t.templateLiteral([t.templateElement({ raw: "\t", cooked: "\t" }, !0)], [])
@@ -363,7 +358,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
363
358
  )
364
359
  }
365
360
  if (1 == forceQuineCheats) return code
366
- assert(scriptBeforeJSONValueReplacement, "src/processScript/minify.ts:495:43")
361
+ assert(scriptBeforeJSONValueReplacement, "src/processScript/minify.ts:485:43")
367
362
  return (
368
363
  countHackmudCharacters(scriptBeforeJSONValueReplacement) <=
369
364
  countHackmudCharacters(code) + Number(hasComment)
@@ -379,7 +374,7 @@ function parseObjectExpression(node, o) {
379
374
  "Identifier" == property.key.type ||
380
375
  "NumericLiteral" == property.key.type ||
381
376
  "StringLiteral" == property.key.type,
382
- "src/processScript/minify.ts:517:4"
377
+ "src/processScript/minify.ts:507:4"
383
378
  )
384
379
  if ("ArrayExpression" == property.value.type) {
385
380
  const childArray = []
@@ -1 +1 @@
1
- export declare const postprocess: (code: string, seclevel: number, uniqueId: string) => string;
1
+ export declare const postprocess: (code: string, uniqueId: string) => string;
@@ -1,12 +1,12 @@
1
- const postprocess = (code, seclevel, uniqueId) =>
1
+ const postprocess = (code, uniqueId) =>
2
2
  code
3
- .replace(/^function\s*\w+\(/, "function(")
3
+ .replace(/^function\s*[\w$]+\(/, "function(")
4
4
  .replace(RegExp(`\\$${uniqueId}\\$\\\\(?:\\\\)?\\$SC_DOLLAR\\$`, "g"), "S\\C$")
5
5
  .replace(RegExp(`\\$${uniqueId}\\$\\\\(?:\\\\)?\\$DB_DOLLAR\\$`, "g"), "D\\B$")
6
6
  .replace(RegExp(`\\$${uniqueId}\\$\\\\(?:\\\\)?\\$D\\$`, "g"), "_\\_D_S")
7
7
  .replace(RegExp(`\\$${uniqueId}\\$\\\\(?:\\\\)?\\$FMCL\\$`, "g"), "_\\_FMCL_")
8
8
  .replace(RegExp(`\\$${uniqueId}\\$\\\\(?:\\\\)?\\$G\\$`, "g"), "_\\_G_")
9
- .replace(RegExp(`\\$${uniqueId}\\$SUBSCRIPT\\$(\\w+)\\$(\\w+)\\$`, "g"), `#${"nlmhf"[seclevel]}s.$1.$2`)
9
+ .replace(RegExp(`\\$${uniqueId}\\$(\\d)\\$SUBSCRIPT\\$(\\w+)\\$(\\w+)\\$`, "g"), "#$1s.$2.$3")
10
10
  .replace(RegExp(`\\$${uniqueId}\\$DEBUG\\$`, "g"), "#D")
11
11
  .replace(RegExp(`\\$${uniqueId}\\$FMCL\\$`, "g"), "#FMCL")
12
12
  .replace(RegExp(`\\$${uniqueId}\\$GLOBAL\\$`, "g"), "#G")
@@ -101,29 +101,29 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
101
101
  functionDotPrototypeIsReferencedMultipleTimes = !0
102
102
  }
103
103
  }
104
- const neededSubscriptLets = new Set()
104
+ const neededSubscriptLets = new Map()
105
105
  let detectedSeclevel = 4
106
106
  for (const fakeSubscriptObjectName of ["$fs", "$4s", "$s"])
107
- program.scope.hasGlobal(fakeSubscriptObjectName) && processFakeSubscriptObject(fakeSubscriptObjectName)
107
+ program.scope.hasGlobal(fakeSubscriptObjectName) && processFakeSubscriptObject(fakeSubscriptObjectName, 4)
108
108
  for (const fakeSubscriptObjectName of ["$hs", "$3s"])
109
109
  if (program.scope.hasGlobal(fakeSubscriptObjectName)) {
110
110
  detectedSeclevel = 3
111
- processFakeSubscriptObject(fakeSubscriptObjectName)
111
+ processFakeSubscriptObject(fakeSubscriptObjectName, 3)
112
112
  }
113
113
  for (const fakeSubscriptObjectName of ["$ms", "$2s"])
114
114
  if (program.scope.hasGlobal(fakeSubscriptObjectName)) {
115
115
  detectedSeclevel = 2
116
- processFakeSubscriptObject(fakeSubscriptObjectName)
116
+ processFakeSubscriptObject(fakeSubscriptObjectName, 2)
117
117
  }
118
118
  for (const fakeSubscriptObjectName of ["$ls", "$1s"])
119
119
  if (program.scope.hasGlobal(fakeSubscriptObjectName)) {
120
120
  detectedSeclevel = 1
121
- processFakeSubscriptObject(fakeSubscriptObjectName)
121
+ processFakeSubscriptObject(fakeSubscriptObjectName, 1)
122
122
  }
123
123
  for (const fakeSubscriptObjectName of ["$ns", "$0s"])
124
124
  if (program.scope.hasGlobal(fakeSubscriptObjectName)) {
125
125
  detectedSeclevel = 0
126
- processFakeSubscriptObject(fakeSubscriptObjectName)
126
+ processFakeSubscriptObject(fakeSubscriptObjectName, 0)
127
127
  }
128
128
  seclevel = Math.min(seclevel, detectedSeclevel)
129
129
  const neededDbMethodLets = new Set()
@@ -157,9 +157,10 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
157
157
  if (program.scope.hasGlobal("$FMCL"))
158
158
  for (const referencePath of getReferencePathsToGlobal("$FMCL", program))
159
159
  referencePath.replaceWith(t.identifier(`$${uniqueId}$FMCL$`))
160
- if (program.scope.hasGlobal("$G"))
160
+ let needG = program.scope.hasGlobal("$G")
161
+ if (needG)
161
162
  for (const referencePath of getReferencePathsToGlobal("$G", program))
162
- referencePath.replaceWith(t.identifier(`$${uniqueId}$GLOBAL$`))
163
+ referencePath.replaceWith(t.identifier(`_${uniqueId}_G_`))
163
164
  if (program.scope.hasGlobal("_SECLEVEL"))
164
165
  for (const referencePath of getReferencePathsToGlobal("_SECLEVEL", program))
165
166
  referencePath.replaceWith(t.numericLiteral(seclevel))
@@ -167,7 +168,7 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
167
168
  if (program.scope.hasGlobal("Object"))
168
169
  for (const referencePath of getReferencePathsToGlobal("Object", program))
169
170
  if ("MemberExpression" == referencePath.parent.type && !referencePath.parent.computed) {
170
- assert("Identifier" == referencePath.parent.property.type, "src/processScript/transform.ts:241:64")
171
+ assert("Identifier" == referencePath.parent.property.type, "src/processScript/transform.ts:243:64")
171
172
  if ("getPrototypeOf" == referencePath.parent.property.name) {
172
173
  referencePath.parentPath.replaceWith(t.identifier(`_${uniqueId}_GET_PROTOTYPE_OF_`))
173
174
  needGetPrototypeOf = !0
@@ -177,7 +178,7 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
177
178
  if (program.scope.hasGlobal("console"))
178
179
  for (const referencePath of getReferencePathsToGlobal("console", program))
179
180
  if ("MemberExpression" == referencePath.parent.type && !referencePath.parent.computed) {
180
- assert("Identifier" == referencePath.parent.property.type, "src/processScript/transform.ts:256:64")
181
+ assert("Identifier" == referencePath.parent.property.type, "src/processScript/transform.ts:258:64")
181
182
  referencePath.parentPath.replaceWith(
182
183
  t.identifier(`_${uniqueId}_CONSOLE_METHOD_${referencePath.parent.property.name}_`)
183
184
  )
@@ -185,13 +186,13 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
185
186
  }
186
187
  const lastStatement = program.node.body.at(-1)
187
188
  let exportDefaultName
188
- assert(lastStatement, "src/processScript/transform.ts:270:27 program is empty")
189
+ assert(lastStatement, "src/processScript/transform.ts:272:27 program is empty")
189
190
  if ("ExportNamedDeclaration" == lastStatement.type) {
190
191
  program.node.body.pop()
191
192
  for (const specifier of lastStatement.specifiers) {
192
193
  assert(
193
194
  "ExportSpecifier" == specifier.type,
194
- `src/processScript/transform.ts:276:51 ${specifier.type} is currently unsupported`
195
+ `src/processScript/transform.ts:278:51 ${specifier.type} is currently unsupported`
195
196
  )
196
197
  const exportedName =
197
198
  "Identifier" == specifier.exported.type ? specifier.exported.name : specifier.exported.value
@@ -314,11 +315,11 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
314
315
  let hoistedGlobalBlockFunctions = 0
315
316
  for (const [globalBlockIndex, globalBlockStatement] of [...globalBlock.body.entries()].reverse())
316
317
  if ("VariableDeclaration" == globalBlockStatement.type) {
317
- assert(1 == globalBlockStatement.declarations.length, "src/processScript/transform.ts:410:59")
318
+ assert(1 == globalBlockStatement.declarations.length, "src/processScript/transform.ts:412:59")
318
319
  const declarator = globalBlockStatement.declarations[0]
319
320
  assert(
320
321
  "Identifier" == declarator.id.type,
321
- `src/processScript/transform.ts:414:51 declarator.id.type was "${declarator.id.type}"`
322
+ `src/processScript/transform.ts:416:51 declarator.id.type was "${declarator.id.type}"`
322
323
  )
323
324
  program.scope.crawl()
324
325
  if (program.scope.hasGlobal(declarator.id.name)) {
@@ -333,15 +334,16 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
333
334
  Object.keys(program.scope.globals).some(global => globalBlockVariables.has(global))
334
335
  ) {
335
336
  const binding = program.scope.getBinding(declarator.id.name)
336
- assert(binding, "src/processScript/transform.ts:433:23")
337
+ assert(binding, "src/processScript/transform.ts:435:23")
337
338
  for (const referencePath of binding.referencePaths) {
338
- assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:436:56")
339
+ assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:438:56")
339
340
  referencePath.replaceWith(
340
341
  t.memberExpression(
341
- t.identifier(`$${uniqueId}$GLOBAL$`),
342
+ t.identifier(`_${uniqueId}_G_`),
342
343
  t.identifier(referencePath.node.name)
343
344
  )
344
345
  )
346
+ needG = !0
345
347
  }
346
348
  for (const referencePath of binding.constantViolations)
347
349
  if ("AssignmentExpression" == referencePath.node.type)
@@ -350,12 +352,13 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
350
352
  clearObject(node)
351
353
  Object.assign(
352
354
  node,
353
- t.memberExpression(t.identifier(`$${uniqueId}$GLOBAL$`), t.identifier(name))
355
+ t.memberExpression(t.identifier(`_${uniqueId}_G_`), t.identifier(name))
354
356
  )
357
+ needG = !0
355
358
  }
356
359
  globalBlockPath.remove()
357
360
  globalBlockStatementPath.remove()
358
- declarator.init &&
361
+ if (declarator.init) {
359
362
  globalBlock.body.splice(
360
363
  globalBlockIndex,
361
364
  0,
@@ -363,13 +366,15 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
363
366
  t.assignmentExpression(
364
367
  "=",
365
368
  t.memberExpression(
366
- t.identifier(`$${uniqueId}$GLOBAL$`),
369
+ t.identifier(`_${uniqueId}_G_`),
367
370
  t.identifier(declarator.id.name)
368
371
  ),
369
372
  declarator.init
370
373
  )
371
374
  )
372
375
  )
376
+ needG = !0
377
+ }
373
378
  } else {
374
379
  globalBlockPath.remove()
375
380
  globalBlockStatementPath.remove()
@@ -379,22 +384,20 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
379
384
  } else globalBlockVariables.add(declarator.id.name)
380
385
  } else if ("ClassDeclaration" == globalBlockStatement.type) {
381
386
  program.scope.crawl()
382
- assert(globalBlockStatement.id, "src/processScript/transform.ts:487:37")
387
+ assert(globalBlockStatement.id, "src/processScript/transform.ts:495:37")
383
388
  if (program.scope.hasGlobal(globalBlockStatement.id.name)) {
384
389
  globalBlock.body.splice(globalBlockIndex, 1)
385
390
  const [globalBlockPath] = program.unshiftContainer("body", globalBlock),
386
391
  [globalBlockStatementPath] = program.unshiftContainer("body", globalBlockStatement)
387
392
  program.scope.crawl()
388
393
  const binding = program.scope.getBinding(globalBlockStatement.id.name)
389
- assert(binding, "src/processScript/transform.ts:499:22")
394
+ assert(binding, "src/processScript/transform.ts:507:22")
390
395
  for (const referencePath of binding.referencePaths) {
391
- assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:502:55")
396
+ assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:510:55")
392
397
  referencePath.replaceWith(
393
- t.memberExpression(
394
- t.identifier(`$${uniqueId}$GLOBAL$`),
395
- t.identifier(referencePath.node.name)
396
- )
398
+ t.memberExpression(t.identifier(`_${uniqueId}_G_`), t.identifier(referencePath.node.name))
397
399
  )
400
+ needG = !0
398
401
  }
399
402
  globalBlockPath.remove()
400
403
  globalBlockStatementPath.remove()
@@ -405,7 +408,7 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
405
408
  t.assignmentExpression(
406
409
  "=",
407
410
  t.memberExpression(
408
- t.identifier(`$${uniqueId}$GLOBAL$`),
411
+ t.identifier(`_${uniqueId}_G_`),
409
412
  t.identifier(globalBlockStatement.id.name)
410
413
  ),
411
414
  t.classExpression(
@@ -417,6 +420,7 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
417
420
  )
418
421
  )
419
422
  )
423
+ needG = !0
420
424
  }
421
425
  }
422
426
  if (program.scope.hasGlobal("_EXPORTS"))
@@ -522,12 +526,12 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
522
526
  mainFunction.body.body.unshift(
523
527
  t.variableDeclaration(
524
528
  "let",
525
- [...neededSubscriptLets].map(name =>
529
+ [...neededSubscriptLets].map(([name, seclevel]) =>
526
530
  t.variableDeclarator(
527
531
  t.identifier(`_${uniqueId}_SUBSCRIPT_${name}_`),
528
532
  t.arrowFunctionExpression(
529
533
  [t.restElement(t.identifier("args"))],
530
- t.callExpression(t.identifier(`$${uniqueId}$SUBSCRIPT$${name}$`), [
534
+ t.callExpression(t.identifier(`$${uniqueId}$${seclevel}$SUBSCRIPT$${name}$`), [
531
535
  t.spreadElement(t.identifier("args"))
532
536
  ])
533
537
  )
@@ -535,6 +539,12 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
535
539
  )
536
540
  )
537
541
  )
542
+ needG &&
543
+ mainFunction.body.body.unshift(
544
+ t.variableDeclaration("let", [
545
+ t.variableDeclarator(t.identifier(`_${uniqueId}_G_`), t.identifier(`$${uniqueId}$GLOBAL$`))
546
+ ])
547
+ )
538
548
  traverse(file, {
539
549
  BlockStatement({ node: blockStatement }) {
540
550
  for (const [index, functionDeclaration] of blockStatement.body.entries())
@@ -555,7 +565,7 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
555
565
  }
556
566
  },
557
567
  ClassBody({ node: classBody, scope, parent }) {
558
- assert(t.isClass(parent), "src/processScript/transform.ts:669:30")
568
+ assert(t.isClass(parent), "src/processScript/transform.ts:688:30")
559
569
  let thisIsReferenced = !1
560
570
  for (const classMethod of classBody.body) {
561
571
  if ("ClassMethod" != classMethod.type) continue
@@ -661,36 +671,37 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
661
671
  t.identifier("prototype")
662
672
  )
663
673
  }
664
- function processFakeSubscriptObject(fakeSubscriptObjectName) {
674
+ function processFakeSubscriptObject(fakeSubscriptObjectName, seclevel) {
665
675
  for (const referencePath of getReferencePathsToGlobal(fakeSubscriptObjectName, program)) {
666
- assert("MemberExpression" == referencePath.parent.type, "src/processScript/transform.ts:785:60")
676
+ assert("MemberExpression" == referencePath.parent.type, "src/processScript/transform.ts:804:60")
667
677
  assert("Identifier" == referencePath.parent.property.type)
668
678
  assert(
669
679
  "MemberExpression" == referencePath.parentPath.parentPath?.node.type,
670
- "src/processScript/transform.ts:787:81"
680
+ "src/processScript/transform.ts:806:81"
671
681
  )
672
682
  assert(
673
683
  "Identifier" == referencePath.parentPath.parentPath.node.property.type,
674
- "src/processScript/transform.ts:788:83"
684
+ "src/processScript/transform.ts:807:83"
675
685
  )
676
686
  assert(
677
687
  /^[_a-z][\d_a-z]{0,24}$/.test(referencePath.parent.property.name),
678
- `src/processScript/transform.ts:792:8 invalid user "${referencePath.parent.property.name}" in subscript`
688
+ `src/processScript/transform.ts:811:8 invalid user "${referencePath.parent.property.name}" in subscript`
679
689
  )
680
690
  assert(
681
691
  /^[_a-z][\d_a-z]{0,24}$/.test(referencePath.parentPath.parentPath.node.property.name),
682
- `src/processScript/transform.ts:797:8 invalid script name "${referencePath.parentPath.parentPath.node.property.name}" in subscript`
692
+ `src/processScript/transform.ts:816:8 invalid script name "${referencePath.parentPath.parentPath.node.property.name}" in subscript`
683
693
  )
684
694
  if ("CallExpression" == referencePath.parentPath.parentPath.parentPath?.type)
685
695
  referencePath.parentPath.parentPath.replaceWith(
686
696
  t.identifier(
687
- `$${uniqueId}$SUBSCRIPT$${referencePath.parent.property.name}$${referencePath.parentPath.parentPath.node.property.name}$`
697
+ `$${uniqueId}$${seclevel}$SUBSCRIPT$${referencePath.parent.property.name}$${referencePath.parentPath.parentPath.node.property.name}$`
688
698
  )
689
699
  )
690
700
  else {
691
701
  const name = `${referencePath.parent.property.name}$${referencePath.parentPath.parentPath.node.property.name}`
692
702
  referencePath.parentPath.parentPath.replaceWith(t.identifier(`_${uniqueId}_SUBSCRIPT_${name}_`))
693
- neededSubscriptLets.add(name)
703
+ const maxSecLevel = Math.max(neededSubscriptLets.get(name) || 0, seclevel)
704
+ neededSubscriptLets.set(name, maxSecLevel)
694
705
  }
695
706
  }
696
707
  }
package/push.d.ts CHANGED
@@ -18,6 +18,14 @@ export type PushOptions = LaxPartial<{
18
18
  */
19
19
  forceQuineCheats: boolean;
20
20
  }>;
21
+ export declare class MissingSourceFolderError extends Error {
22
+ }
23
+ export declare class MissingHackmudFolderError extends Error {
24
+ }
25
+ export declare class NoUsersError extends Error {
26
+ }
27
+ export declare class NoScriptsError extends Error {
28
+ }
21
29
  /** Push scripts from a source directory to the hackmud directory.
22
30
  *
23
31
  * Pushes files directly in the source folder to all users
@@ -25,4 +33,4 @@ export type PushOptions = LaxPartial<{
25
33
  * @param hackmudPath directory created by hackmud containing user data including scripts
26
34
  * @param options {@link PushOptions details}
27
35
  * @returns array of info on pushed scripts */
28
- export declare function push(sourcePath: string, hackmudPath: string, { scripts, onPush, minify, mangleNames, forceQuineCheats }?: PushOptions): Promise<Info[]>;
36
+ export declare function push(sourcePath: string, hackmudPath: string, { scripts, onPush, minify, mangleNames, forceQuineCheats }?: PushOptions): Promise<MissingSourceFolderError | MissingHackmudFolderError | NoUsersError | NoScriptsError | Info[]>;
package/push.js CHANGED
@@ -42,32 +42,54 @@ import "./processScript/preprocess.js"
42
42
  import "import-meta-resolve"
43
43
  import "./processScript/transform.js"
44
44
  import "@samual/lib/clearObject"
45
+ class MissingSourceFolderError extends Error {}
46
+ Object.defineProperty(MissingSourceFolderError.prototype, "name", { value: "MissingSourceFolderError" })
47
+ class MissingHackmudFolderError extends Error {}
48
+ Object.defineProperty(MissingHackmudFolderError.prototype, "name", { value: "MissingHackmudFolderError" })
49
+ class NoUsersError extends Error {}
50
+ Object.defineProperty(NoUsersError.prototype, "name", { value: "NoUsersError" })
51
+ class NoScriptsError extends Error {}
52
+ Object.defineProperty(NoScriptsError.prototype, "name", { value: "NoScriptsError" })
45
53
  async function push(
46
54
  sourcePath,
47
55
  hackmudPath,
48
56
  { scripts = ["*.*"], onPush = () => {}, minify = !0, mangleNames = !1, forceQuineCheats } = {}
49
57
  ) {
50
58
  const [sourceFolder, hackmudFolder] = await Promise.all([
51
- readDirectoryWithStats(sourcePath),
52
- readDirectoryWithStats(hackmudPath)
53
- ]),
54
- sourceFolderFolders = sourceFolder.filter(({ stats }) => stats.isDirectory()),
59
+ readDirectoryWithStats(sourcePath).catch(error => {
60
+ if (error && "ENOENT" == error.code)
61
+ return new MissingSourceFolderError("There is no folder at " + sourcePath)
62
+ throw error
63
+ }),
64
+ readDirectoryWithStats(hackmudPath).catch(error => {
65
+ if (error && "ENOENT" == error.code)
66
+ return new MissingHackmudFolderError("There is no folder at " + hackmudPath)
67
+ throw error
68
+ })
69
+ ])
70
+ if (sourceFolder instanceof Error) return sourceFolder
71
+ if (hackmudFolder instanceof Error) return hackmudFolder
72
+ const sourceFolderFolders = sourceFolder.filter(({ stats }) => stats.isDirectory()),
55
73
  allUsers = new Set([
56
74
  ...scripts
57
- .map(scriptName => ensure(scriptName.split(".")[0], "src/push.ts:52:65"))
75
+ .map(scriptName => ensure(scriptName.split(".")[0], "src/push.ts:82:65"))
58
76
  .filter(name => "*" != name),
59
77
  ...sourceFolderFolders.map(({ name }) => name),
60
78
  ...hackmudFolder.filter(({ stats }) => stats.isDirectory()).map(({ name }) => name),
61
79
  ...hackmudFolder
62
80
  .filter(({ stats, name }) => stats.isFile() && name.endsWith(".key"))
63
81
  .map(({ name }) => name.slice(0, -4))
64
- ]),
65
- usersToScriptsToPush = new Cache(_user => new Map()),
82
+ ])
83
+ if (!allUsers.size)
84
+ return new NoUsersError(
85
+ "Could not find any users. Either provide the names of your users or log into a user in hackmud"
86
+ )
87
+ const usersToScriptsToPush = new Cache(_user => new Map()),
66
88
  scriptNamesToUsers = new Cache(_scriptName => new Set())
67
89
  for (const script of scripts) {
68
90
  const [user, scriptName] = script.split(".")
69
- assert(user, "src/push.ts:69:16")
70
- assert(scriptName, "src/push.ts:70:22")
91
+ assert(user, "src/push.ts:105:16")
92
+ assert(scriptName, "src/push.ts:106:22")
71
93
  "*" == user ? scriptNamesToUsers.set(scriptName, allUsers) : scriptNamesToUsers.get(scriptName).add(user)
72
94
  }
73
95
  const sourceFolderFiles = sourceFolder.filter(({ stats }) => stats.isFile()),
@@ -98,7 +120,7 @@ async function push(
98
120
  for (const [scriptName, users] of scriptNamesToUsers)
99
121
  for (const user of users)
100
122
  if (!usersToScriptsToPush.get(user).has(scriptName))
101
- throw Error(`Could not find script ${user}.${scriptName} to push`)
123
+ return new NoScriptsError(`Could not find script ${user}.${scriptName} to push`)
102
124
  const pathsToUsers = new Cache(_path => new Set())
103
125
  for (const [user, scriptsToPush] of usersToScriptsToPush)
104
126
  for (const path of scriptsToPush.values()) pathsToUsers.get(path).add(user)
@@ -135,4 +157,4 @@ async function push(
135
157
  )
136
158
  return allInfo
137
159
  }
138
- export { push }
160
+ export { MissingHackmudFolderError, MissingSourceFolderError, NoScriptsError, NoUsersError, push }