hackmud-script-manager 0.20.4-abe4703 → 0.20.4-b779329

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-abe4703",
15
+ const version = "0.20.4-b779329",
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
@@ -4,6 +4,13 @@ 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> = { [K in keyof T]-?: {} extends Pick<T, K> ? true : false }[keyof T]
8
+
9
+ type Scriptor<Args = unknown, Ret = unknown> = {
10
+ name: string
11
+ call: AllOptional<Args> extends true ? (args?: Args) => Ret : (args: Args) => Ret
12
+ }
13
+
7
14
  type Subscripts = Record<string, Record<string, (...args: any) => any>> & {
8
15
  accts: ErrorScripts
9
16
  autos: ErrorScripts
@@ -26,23 +33,27 @@ interface PlayerMidsec {}
26
33
  interface PlayerLowsec {}
27
34
  interface PlayerNullsec {}
28
35
 
29
- type UpgradeCore = {
36
+ type UpgradeRarityString = "`0noob`" | "`1kiddie`" | "`2h4x0r`" | "`3h4rdc0r3`" | "`4|_|b3|2`" | "`531337`"
37
+ type UpgradeRarityNumber = 0 | 1 | 2 | 3 | 4 | 5;
38
+ type UpgradeRarity = UpgradeRarityString | UpgradeRarityNumber;
39
+
40
+ type UpgradeBase = {
30
41
  name: string
31
42
  type: "lock" | "script_space" | "chat" | "script" | "tool" | "bot_brain" | "glam"
32
43
  up_class?: -1 | 0 | 1 | 2 | 3
33
44
  tier: 1 | 2 | 3 | 4
34
- rarity: 0 | 1 | 2 | 3 | 4 | 5
45
+ rarity: UpgradeRarityNumber
35
46
  i: number
36
47
  loaded: boolean
37
48
  sn: string
38
49
  description: string
39
50
  }
40
51
 
41
- type Upgrade = UpgradeCore & Record<string, null | boolean | number | string>
52
+ type Upgrade = UpgradeBase & Record<string, null | boolean | number | string>
42
53
 
43
- type CLIUpgrade = Omit<UpgradeCore, `rarity`> & {
54
+ type CliUpgrade = Omit<UpgradeBase, `rarity`> & {
44
55
  [x: string]: null | boolean | number | string
45
- rarity: "`0noob`" | "`1kiddie`" | "`2h4x0r`" | "`3h4rdc0r3`" | "`4|_|b3|2`" | "`531337`"
56
+ rarity: UpgradeRarityString
46
57
  }
47
58
 
48
59
  type UsersTopItem<R> = { rank: R, name: string, last_activity: string, balance: string }
@@ -140,7 +151,16 @@ type Fullsec = Subscripts & PlayerFullsec & {
140
151
  market: {
141
152
  /** **FULLSEC** */ browse: {
142
153
  (args:
143
- 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">>
144
164
  ): { i: string, name: string, rarity: Upgrade["rarity"], cost: number }[] | ScriptFailure
145
165
 
146
166
  <I extends string>(args: { i: I }): {
@@ -409,17 +429,17 @@ type Fullsec = Subscripts & PlayerFullsec & {
409
429
  upgrades_of_owner: {
410
430
  <F extends Partial<Upgrade & { loaded: boolean }> = object>(args?: { filter?: F, full?: false }): (
411
431
  Omit<
412
- Pick<UpgradeCore, "tier" | "rarity" | "name" | "type" | "i" | "loaded">,
432
+ Pick<UpgradeBase, "tier" | "rarity" | "name" | "type" | "i" | "loaded">,
413
433
  keyof F
414
434
  > & Pick<F, "tier" | "rarity" | "name" | "type" | "i" | "loaded">
415
435
  )[] | ScriptFailure
416
436
 
417
437
  <F extends Partial<Upgrade & { loaded: boolean }> = object>(args: { filter?: F, full: true }): (
418
- Omit<UpgradeCore, keyof F> & F & Record<string, null | boolean | number | string>
438
+ Omit<UpgradeBase, keyof F> & F & Record<string, null | boolean | number | string>
419
439
  )[] | ScriptFailure
420
440
 
421
441
  <I extends number>(args: { i: I }): (
422
- Omit<UpgradeCore, "i"> & { [x: string]: null | boolean | number | string, i: I }
442
+ Omit<UpgradeBase, "i"> & { [x: string]: null | boolean | number | string, i: I }
423
443
  ) | ScriptFailure
424
444
  }
425
445
 
@@ -496,7 +516,7 @@ type Highsec = Fullsec & PlayerHighsec & {
496
516
  /** **HIGHSEC** */
497
517
  upgrades: {
498
518
  <I extends number>(args: { i: I }): (
499
- Omit<UpgradeCore, "i"> & { [x: string]: null | boolean | number | string, i: I }
519
+ Omit<UpgradeBase, "i"> & { [x: string]: null | boolean | number | string, i: I }
500
520
  ) | ScriptFailure
501
521
 
502
522
  <F extends Partial<Upgrade & { loaded: boolean }> = object>(args?: {
@@ -504,22 +524,22 @@ type Highsec = Fullsec & PlayerHighsec & {
504
524
  is_script?: true
505
525
  full?: false
506
526
  }): (
507
- 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 &
508
528
  Record<string, null | boolean | number | string>
509
529
  )[] | ScriptFailure
510
530
 
511
531
  <F extends Partial<Upgrade & { loaded: boolean }> = object>(args?:
512
532
  { filter?: F, is_script?: true, full: true }
513
- ): (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
514
534
 
515
535
  (args?: { filter?: Partial<Upgrade & { loaded: boolean }>, is_script: false, full?: false }):
516
536
  { msg: string, upgrades: string[] } | ScriptFailure
517
537
 
518
538
  <F extends Partial<Upgrade & { loaded: boolean }> = object>(
519
539
  args?: { filter?: F, is_script: false, full: true }
520
- ): (Omit<UpgradeCore, keyof F | `rarity`> & F & {
540
+ ): (Omit<UpgradeBase, keyof F | `rarity`> & F & {
521
541
  [x: string]: null | boolean | number | string
522
- rarity: "`0noob`" | "`1kiddie`" | "`2h4x0r`" | "`3h4rdc0r3`" | "`4|_|b3|2`" | "`531337`"
542
+ rarity: UpgradeRarityString
523
543
  })[] | ScriptFailure
524
544
  }
525
545
  }
@@ -699,10 +719,50 @@ type Nullsec = Lowsec & PlayerNullsec & {
699
719
  }
700
720
  }
701
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
+
702
726
  type MongoValue = string | number | boolean | Date | MongoValue[] | { [key: string]: MongoValue } | null
703
727
 
704
728
  type MongoCommandValue = string | number | boolean | Date | MongoCommandValue[] | { [key: string]: MongoCommandValue } |
705
- 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>)>
706
766
 
707
767
  type Query = { [key: string]: MongoValue | Query } & { _id?: Id, $in?: MongoValue[] }
708
768
  type Projection = Record<string, boolean | 0 | 1>
@@ -751,7 +811,7 @@ type Cursor = {
751
811
  ObjectId: () => any
752
812
  }
753
813
 
754
- type CLIContext = {
814
+ type CliContext = {
755
815
  /** The name of the user who is calling the script. */ caller: string
756
816
  /** The name of this script. */ this_script: string
757
817
  /** The number of columns in the caller’s terminal. */ cols: number
@@ -759,17 +819,23 @@ type CLIContext = {
759
819
 
760
820
  /** The name of the script that directly called this script, or null if called on the command line or as a
761
821
  * scriptor. */ calling_script: null
822
+ is_scriptor?: undefined
823
+ is_brain?: undefined
762
824
  }
763
825
 
764
- type SubscriptContext = Replace<CLIContext, {
826
+ type SubscriptContext = Replace<CliContext, {
765
827
  /** The name of the script that directly called this script, or null if called on the command line or as a scriptor.
766
828
  */
767
829
  calling_script: string
768
830
  }>
769
831
 
770
- type ScriptorContext = CLIContext & { /** Whether the script is being run as a scriptor. */ is_scriptor: true }
771
- type BrainContext = CLIContext & { /** Whether the script is being run via a bot brain. */ is_brain: true }
772
- 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
773
839
 
774
840
  /** Subscript space that can call FULLSEC scripts. */ declare const $fs: Fullsec
775
841
 
@@ -812,6 +878,8 @@ declare const $0s: typeof $ns
812
878
  * } */
813
879
  declare const $s: Nullsec
814
880
 
881
+ type ObjectId = { $oid: string }
882
+
815
883
  declare const $db: {
816
884
  /** Insert a document or documents into a collection.
817
885
  * @param documents A document or array of documents to insert into the collection. */
@@ -905,6 +973,8 @@ declare const $db: {
905
973
  signature: { hash: "Undefined Conversion", keyId: "Undefined Conversion" }
906
974
  }
907
975
  }
976
+
977
+ ObjectId: () => ObjectId
908
978
  }
909
979
 
910
980
  /** Debug Log.
@@ -941,7 +1011,7 @@ declare const $FMCL: undefined | true
941
1011
  * @example
942
1012
  * if (!$G.dbCache)
943
1013
  * $G.dbCache = $db.f({ whatever: true }).first() */
944
- declare const $G: any
1014
+ declare const $G: Record<string | symbol, any>
945
1015
 
946
1016
  /** This contains a JS timestamp (not Date) set immediately before your code begins running.
947
1017
  * @example
@@ -990,3 +1060,14 @@ declare const _FULL_SCRIPT_NAME: string
990
1060
  *
991
1061
  * In rare cases where it's not known at build time, it's `-1`. */
992
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-abe4703",
3
+ "version": "0.20.4-b779329",
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'
@@ -259,7 +259,10 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
259
259
  t.memberExpression(
260
260
  t.taggedTemplateExpression(
261
261
  t.memberExpression(
262
- t.callExpression(t.identifier(`$${uniqueId}$SUBSCRIPT$scripts$quine$`), []),
262
+ t.callExpression(
263
+ t.identifier(`$${uniqueId}$4$SUBSCRIPT$scripts$quine$`),
264
+ []
265
+ ),
263
266
  t.identifier("split")
264
267
  ),
265
268
  t.templateLiteral([t.templateElement({ raw: "\t", cooked: "\t" }, !0)], [])
@@ -283,7 +286,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
283
286
  t.memberExpression(
284
287
  t.taggedTemplateExpression(
285
288
  t.memberExpression(
286
- t.callExpression(t.identifier(`$${uniqueId}$SUBSCRIPT$scripts$quine$`), []),
289
+ t.callExpression(t.identifier(`$${uniqueId}$4$SUBSCRIPT$scripts$quine$`), []),
287
290
  t.identifier("split")
288
291
  ),
289
292
  t.templateLiteral([t.templateElement({ raw: "\t", cooked: "\t" }, !0)], [])
@@ -308,7 +311,7 @@ async function minify(file, { uniqueId = "00000000000", mangleNames = !1, forceQ
308
311
  t.memberExpression(
309
312
  t.taggedTemplateExpression(
310
313
  t.memberExpression(
311
- t.callExpression(t.identifier(`$${uniqueId}$SUBSCRIPT$scripts$quine$`), []),
314
+ t.callExpression(t.identifier(`$${uniqueId}$4$SUBSCRIPT$scripts$quine$`), []),
312
315
  t.identifier("split")
313
316
  ),
314
317
  t.templateLiteral([t.templateElement({ raw: "\t", cooked: "\t" }, !0)], [])
@@ -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()
@@ -522,12 +522,12 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
522
522
  mainFunction.body.body.unshift(
523
523
  t.variableDeclaration(
524
524
  "let",
525
- [...neededSubscriptLets].map(name =>
525
+ [...neededSubscriptLets].map(([name, seclevel]) =>
526
526
  t.variableDeclarator(
527
527
  t.identifier(`_${uniqueId}_SUBSCRIPT_${name}_`),
528
528
  t.arrowFunctionExpression(
529
529
  [t.restElement(t.identifier("args"))],
530
- t.callExpression(t.identifier(`$${uniqueId}$SUBSCRIPT$${name}$`), [
530
+ t.callExpression(t.identifier(`$${uniqueId}$${seclevel}$SUBSCRIPT$${name}$`), [
531
531
  t.spreadElement(t.identifier("args"))
532
532
  ])
533
533
  )
@@ -661,7 +661,7 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
661
661
  t.identifier("prototype")
662
662
  )
663
663
  }
664
- function processFakeSubscriptObject(fakeSubscriptObjectName) {
664
+ function processFakeSubscriptObject(fakeSubscriptObjectName, seclevel) {
665
665
  for (const referencePath of getReferencePathsToGlobal(fakeSubscriptObjectName, program)) {
666
666
  assert("MemberExpression" == referencePath.parent.type, "src/processScript/transform.ts:785:60")
667
667
  assert("Identifier" == referencePath.parent.property.type)
@@ -684,13 +684,14 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
684
684
  if ("CallExpression" == referencePath.parentPath.parentPath.parentPath?.type)
685
685
  referencePath.parentPath.parentPath.replaceWith(
686
686
  t.identifier(
687
- `$${uniqueId}$SUBSCRIPT$${referencePath.parent.property.name}$${referencePath.parentPath.parentPath.node.property.name}$`
687
+ `$${uniqueId}$${seclevel}$SUBSCRIPT$${referencePath.parent.property.name}$${referencePath.parentPath.parentPath.node.property.name}$`
688
688
  )
689
689
  )
690
690
  else {
691
691
  const name = `${referencePath.parent.property.name}$${referencePath.parentPath.parentPath.node.property.name}`
692
692
  referencePath.parentPath.parentPath.replaceWith(t.identifier(`_${uniqueId}_SUBSCRIPT_${name}_`))
693
- neededSubscriptLets.add(name)
693
+ const maxSecLevel = Math.max(neededSubscriptLets.get(name) || 0, seclevel)
694
+ neededSubscriptLets.set(name, maxSecLevel)
694
695
  }
695
696
  }
696
697
  }
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,19 +42,37 @@ 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),
@@ -63,13 +81,15 @@ async function push(
63
81
  .map(({ name }) => name.slice(0, -4))
64
82
  ])
65
83
  if (!allUsers.size)
66
- throw Error("Could not find any users. Either provide the names of your users or log into a user in hackmud.")
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
+ )
67
87
  const usersToScriptsToPush = new Cache(_user => new Map()),
68
88
  scriptNamesToUsers = new Cache(_scriptName => new Set())
69
89
  for (const script of scripts) {
70
90
  const [user, scriptName] = script.split(".")
71
- assert(user, "src/push.ts:72:16")
72
- assert(scriptName, "src/push.ts:73:22")
91
+ assert(user, "src/push.ts:105:16")
92
+ assert(scriptName, "src/push.ts:106:22")
73
93
  "*" == user ? scriptNamesToUsers.set(scriptName, allUsers) : scriptNamesToUsers.get(scriptName).add(user)
74
94
  }
75
95
  const sourceFolderFiles = sourceFolder.filter(({ stats }) => stats.isFile()),
@@ -100,7 +120,7 @@ async function push(
100
120
  for (const [scriptName, users] of scriptNamesToUsers)
101
121
  for (const user of users)
102
122
  if (!usersToScriptsToPush.get(user).has(scriptName))
103
- throw Error(`Could not find script ${user}.${scriptName} to push`)
123
+ return new NoScriptsError(`Could not find script ${user}.${scriptName} to push`)
104
124
  const pathsToUsers = new Cache(_path => new Set())
105
125
  for (const [user, scriptsToPush] of usersToScriptsToPush)
106
126
  for (const path of scriptsToPush.values()) pathsToUsers.get(path).add(user)
@@ -137,4 +157,4 @@ async function push(
137
157
  )
138
158
  return allInfo
139
159
  }
140
- export { push }
160
+ export { MissingHackmudFolderError, MissingSourceFolderError, NoScriptsError, NoUsersError, push }