hackmud-script-manager 0.20.4-9f460e8 → 0.20.4-a7d8309

Sign up to get free protection for your applications and to get access to all the features.
package/bin/hsm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { Cache } from "@samual/lib/Cache"
2
+ import { AutoMap } from "@samual/lib/AutoMap"
3
3
  import { assert } from "@samual/lib/assert"
4
4
  import { countHackmudCharacters } from "@samual/lib/countHackmudCharacters"
5
5
  import { writeFilePersistent } from "@samual/lib/writeFilePersistent"
@@ -13,11 +13,10 @@ import { syncMacros } from "../syncMacros.js"
13
13
  import "@samual/lib/readDirectoryWithStats"
14
14
  import "path/posix"
15
15
  import "@samual/lib/copyFilePersistent"
16
- const version = "0.20.4-9f460e8",
17
- formatOption = name => colourN(`-${1 == name.length ? "" : "-"}${name}`),
16
+ const formatOption = name => colourN(`-${1 == name.length ? "" : "-"}${name}`),
18
17
  options = new Map(),
19
18
  commands = [],
20
- userColours = new Cache(user => {
19
+ userColours = new AutoMap(user => {
21
20
  let hash = 0
22
21
  for (const char of user) hash += (hash >> 1) + hash + "xi1_8ratvsw9hlbgm02y5zpdcn7uekof463qj".indexOf(char) + 1
23
22
  return [colourJ, colourK, colourM, colourW, colourL, colourB][hash % 6](user)
@@ -59,11 +58,11 @@ const pushModule = import("../push.js"),
59
58
  process.version.startsWith("v21.") &&
60
59
  console.warn(
61
60
  colourF(
62
- "Warning: Support for Node.js 21 will be dropped in the next minor version of HSM\n You should update your version of Node.js\n https://nodejs.org/en/download/package-manager"
61
+ `Warning: Support for Node.js 21 will be dropped in the next minor version of HSM\n Your current version of Node.js is ${chalk.bold(process.version)}\n You should update your version of Node.js\n https://nodejs.org/en/download/package-manager\n`
63
62
  )
64
63
  )
65
64
  if ("v" == commands[0] || "version" == commands[0] || popOption("version", "v")?.value) {
66
- console.log(version)
65
+ console.log("0.20.4-a7d8309")
67
66
  process.exit()
68
67
  }
69
68
  if (popOption("help", "h")?.value) {
@@ -201,18 +200,27 @@ switch (commands[0]) {
201
200
  )
202
201
  } else infos.length || logError("Could not find any scripts to push")
203
202
  } else {
204
- const typeDeclarationPathOption = popOption(
203
+ const dtsPathOption = popOption(
204
+ "dts-path",
205
205
  "type-declaration-path",
206
206
  "type-declaration",
207
207
  "dts",
208
208
  "gen-types"
209
209
  )
210
+ dtsPathOption &&
211
+ "emit-dts" != dtsPathOption.name &&
212
+ "gen-dts" != dtsPathOption.name &&
213
+ console.warn(
214
+ colourF(
215
+ `Warning: ${colourN(dtsPathOption.name)} is being deprecated and will be removed in the\n next minor release of HSM\n You should switch to using its alias ${colourN("--dts-path")}\n`
216
+ )
217
+ )
210
218
  complainAboutUnrecognisedOptions()
211
219
  const { watch } = await watchModule
212
220
  watch(sourcePath, hackmudPath, {
213
221
  scripts,
214
222
  onPush: info => logInfo(info, hackmudPath),
215
- typeDeclarationPath: typeDeclarationPathOption?.value.toString(),
223
+ typeDeclarationPath: dtsPathOption?.value.toString(),
216
224
  minify: noMinifyOption && !noMinifyOption.value,
217
225
  mangleNames: mangleNamesOption?.value,
218
226
  onReady: () => log("Watching"),
@@ -252,7 +260,15 @@ switch (commands[0]) {
252
260
  case "gen-type-declaration":
253
261
  case "gen-dts":
254
262
  case "gen-types":
263
+ case "emit-dts":
255
264
  {
265
+ "emit-dts" != commands[0] &&
266
+ "gen-dts" != commands[0] &&
267
+ console.warn(
268
+ colourF(
269
+ `Warning: ${colourC("hsm")} ${colourL(commands[0])} is being deprecated and will be removed\n in the next minor release of HSM\n You should switch to using its alias ${colourC("hsm")} ${colourL("emit-dts")}\n`
270
+ )
271
+ )
256
272
  const hackmudPath = getHackmudPath(),
257
273
  target = commands[1]
258
274
  if (!target) {
@@ -266,7 +282,7 @@ switch (commands[0]) {
266
282
  typeDeclaration = await generateTypeDeclaration(sourcePath, hackmudPath)
267
283
  let typeDeclarationPath = resolve(outputPath)
268
284
  await writeFile(typeDeclarationPath, typeDeclaration).catch(error => {
269
- assert(error instanceof Error, "src/bin/hsm.ts:343:35")
285
+ assert(error instanceof Error, "src/bin/hsm.ts:361:35")
270
286
  if ("EISDIR" != error.code) throw error
271
287
  typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
272
288
  return writeFile(typeDeclarationPath, typeDeclaration)
@@ -275,7 +291,6 @@ switch (commands[0]) {
275
291
  }
276
292
  break
277
293
  case "help":
278
- case "h":
279
294
  logHelp()
280
295
  break
281
296
  default:
@@ -287,21 +302,20 @@ function logHelp() {
287
302
  const pushCommandDescription = "Push scripts from a directory to hackmud user's scripts directories",
288
303
  forceQuineCheatsOptionDescription = `Force quine cheats on. Use ${colourN("--force-quine-cheats")}=${colourV("false")} to force off`,
289
304
  hackmudPathOption = `${colourN("--hackmud-path")}=${colourB("<path>")}\n Override hackmud path`
290
- console.log(colourN("Version") + colourS(": ") + colourV(version))
291
305
  switch (commands[0]) {
292
306
  case "dev":
293
307
  case "watch":
294
308
  case "push":
295
309
  console.log(
296
310
  colourS(
297
- `\n${colourJ("push" == commands[0] ? pushCommandDescription : "Watch a directory and push a script when modified")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB('<directory> ["<script user>.<script name>"...]')}\n\n${colourA("Arguments:")}\n${colourB("<directory>")}\n The source directory containing your scripts\n${colourB("<script user>")}\n A user to push script(s) to. Can be set to wild card (${colourV("*")}) which will try\n and discover users to push to\n${colourB("<script name>")}\n Name of a script to push. Can be set to wild card (${colourV("*")}) to find all scripts\n\n${colourA("Options:")}\n${colourN("--no-minify")}\n Skip minification to produce a "readable" script\n${colourN("--mangle-names")}\n Reduce character count further but lose function names in error call stacks\n${colourN("--force-quine-cheats")}\n ${forceQuineCheatsOptionDescription}\n${hackmudPathOption}\n${"push" == commands[0] ? "" : `${colourN("--type-declaration-path")}=${colourB("<path>")}\n Path to generate a type declaration file for the scripts\n`}\n${colourA("Examples:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")}\n Pushes all scripts found in ${colourV("src")} folder to all users\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("foo")}${colourV(".")}${colourL("bar")}\n Pushes a script named ${colourL("bar")} found in ${colourV("src")} folder to user ${userColours.get("foo")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("foo")}${colourV(".")}${colourL("bar")} ${colourC("baz")}${colourV(".")}${colourL("qux")}\n Multiple can be specified\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("foo")}${colourV(".")}${colourL("*")}\n Pushes all scripts found in ${colourV("src")} folder to user ${userColours.get("foo")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("*")}${colourV(".")}${colourL("foo")}\n Pushes all scripts named ${colourL("foo")} found in ${colourV("src")} folder to all user\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("*")}${colourV(".")}${colourL("*")}\n Pushes all scripts found in ${colourV("src")} folder to all users`
311
+ `${colourJ("push" == commands[0] ? pushCommandDescription : "Watch a directory and push a script when modified")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB('<directory> ["<script user>.<script name>"...]')}\n\n${colourA("Arguments:")}\n${colourB("<directory>")}\n The source directory containing your scripts\n${colourB("<script user>")}\n A user to push script(s) to. Can be set to wild card (${colourV("*")}) which will try\n and discover users to push to\n${colourB("<script name>")}\n Name of a script to push. Can be set to wild card (${colourV("*")}) to find all scripts\n\n${colourA("Options:")}\n${colourN("--no-minify")}\n Skip minification to produce a "readable" script\n${colourN("--mangle-names")}\n Reduce character count further but lose function names in error call stacks\n${colourN("--force-quine-cheats")}\n ${forceQuineCheatsOptionDescription}\n${hackmudPathOption}\n${"push" == commands[0] ? "" : `${colourN("--dts-path")}=${colourB("<path>")}\n Path to generate a type declaration (.d.ts) file for the scripts\n`}\n${colourA("Examples:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")}\n Pushes all scripts found in ${colourV("src")} folder to all users\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("foo")}${colourV(".")}${colourL("bar")}\n Pushes a script named ${colourL("bar")} found in ${colourV("src")} folder to user ${userColours.get("foo")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("foo")}${colourV(".")}${colourL("bar")} ${colourC("baz")}${colourV(".")}${colourL("qux")}\n Multiple can be specified\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("foo")}${colourV(".")}${colourL("*")}\n Pushes all scripts found in ${colourV("src")} folder to user ${userColours.get("foo")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("*")}${colourV(".")}${colourL("foo")}\n Pushes all scripts named ${colourL("foo")} found in ${colourV("src")} folder to all user\n${colourC("hsm")} ${colourL(commands[0])} ${colourV("src")} ${colourC("*")}${colourV(".")}${colourL("*")}\n Pushes all scripts found in ${colourV("src")} folder to all users`
298
312
  )
299
313
  )
300
314
  break
301
315
  case "pull":
302
316
  console.log(
303
317
  colourS(
304
- `\n${colourJ("Pull a script a from a hackmud user's script directory")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<script user>")}${colourV(".")}${colourB("<script name>")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
318
+ `${colourJ("Pull a script a from a hackmud user's script directory")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<script user>")}${colourV(".")}${colourB("<script name>")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
305
319
  )
306
320
  )
307
321
  break
@@ -309,7 +323,7 @@ function logHelp() {
309
323
  case "golf":
310
324
  console.log(
311
325
  colourS(
312
- `\n${colourJ("Minify a script file on the spot")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<target> [output path]")}\n\n${colourA("Options:")}\n${colourN("--no-minify")}\n Skip minification to produce a "readable" script\n${colourN("--mangle-names")}\n Reduce character count further but lose function names in error call stacks\n${colourN("--force-quine-cheats")}\n ${forceQuineCheatsOptionDescription}\n${colourN("--watch")}\n Watch for changes`
326
+ `${colourJ("Minify a script file on the spot")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<target> [output path]")}\n\n${colourA("Options:")}\n${colourN("--no-minify")}\n Skip minification to produce a "readable" script\n${colourN("--mangle-names")}\n Reduce character count further but lose function names in error call stacks\n${colourN("--force-quine-cheats")}\n ${forceQuineCheatsOptionDescription}\n${colourN("--watch")}\n Watch for changes`
313
327
  )
314
328
  )
315
329
  break
@@ -317,6 +331,14 @@ function logHelp() {
317
331
  case "gen-type-declaration":
318
332
  case "gen-dts":
319
333
  case "gen-types":
334
+ case "emit-dts":
335
+ "emit-dts" != commands[0] &&
336
+ "gen-dts" != commands[0] &&
337
+ console.warn(
338
+ colourF(
339
+ `Warning: ${colourC("hsm")} ${colourL(commands[0])} is being deprecated and will be removed\n in the next minor release of HSM\n You should switch to using its alias ${colourC("hsm")} ${colourL("emit-dts")}\n`
340
+ )
341
+ )
320
342
  console.log(
321
343
  colourS(
322
344
  `${colourJ("Generate a type declaration file for a directory of scripts")}\n\n${colourA("Usage:")}\n${colourC("hsm")} ${colourL(commands[0])} ${colourB("<directory> [output path]")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
@@ -326,14 +348,14 @@ function logHelp() {
326
348
  case "sync-macros":
327
349
  console.log(
328
350
  colourS(
329
- `\n${colourJ("Sync macros across all hackmud users")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
351
+ `${colourJ("Sync macros across all hackmud users")}\n\n${colourA("Options:")}\n${hackmudPathOption}`
330
352
  )
331
353
  )
332
354
  break
333
355
  default:
334
356
  console.log(
335
357
  colourS(
336
- `\n${colourJ("Hackmud Script Manager")}\n\n${colourA("Commands:")}\n${colourL("push")}\n ${pushCommandDescription}\n${colourL("dev")}\n Watch a directory and push a script when modified\n${colourL("golf")}\n Minify a script file on the spot\n${colourL("gen-dts")}\n Generate a type declaration file for a directory of scripts\n${colourL("sync-macros")}\n Sync macros across all hackmud users\n${colourL("pull")}\n Pull a script a from a hackmud user's script directory`
358
+ `${colourJ("Hackmud Script Manager")}\n${colourN("Version") + colourS(": ") + colourV("0.20.4-a7d8309")}\n\n${colourA("Commands:")}\n${colourL("push")}\n ${pushCommandDescription}\n${colourL("dev")}\n Watch a directory and push a script when modified\n${colourL("minify")}\n Minify a script file on the spot\n${colourL("emit-dts")}\n Generate a type declaration file for a directory of scripts\n${colourL("sync-macros")}\n Sync macros across all hackmud users\n${colourL("pull")}\n Pull a script a from a hackmud user's script directory\n\n${colourA("Options:")}\n${colourN("--help")}\n Can be used on any command e.g. ${colourC("hsm")} ${colourL("push")} ${colourN("--help")} to show helpful information`
337
359
  )
338
360
  )
339
361
  }
@@ -351,7 +373,7 @@ function logError(message) {
351
373
  process.exitCode = 1
352
374
  }
353
375
  function getHackmudPath() {
354
- const hackmudPathOption = popOption("hackmud-path")
376
+ const hackmudPathOption = popOption("hackmud-path")?.value
355
377
  if (null != hackmudPathOption && "string" != typeof hackmudPathOption) {
356
378
  logError(`Option ${colourN("--hackmud-path")} must be a string, got ${colourV(hackmudPathOption)}\n`)
357
379
  logHelp()
package/env.d.ts CHANGED
@@ -1,9 +1,8 @@
1
1
  type Replace<A, B> = Omit<A, keyof B> & B
2
- type ScriptSuccess<T = object> = { ok: true } & T
2
+ type ScriptSuccess<T = unknown> = { ok: true } & T
3
3
  type ScriptFailure = { ok: false, msg?: string }
4
- type ScriptResponse<T = object> = ScriptSuccess<T> | ScriptFailure
4
+ type ScriptResponse<T = unknown> = ScriptSuccess<T> | ScriptFailure
5
5
  type ErrorScripts = Record<string, () => ScriptFailure>
6
- type Scriptor<TArgs extends any[] = any[]> = { name: string, call: (...args: TArgs) => unknown }
7
6
 
8
7
  type Subscripts = Record<string, Record<string, (...args: any) => any>> & {
9
8
  accts: ErrorScripts
@@ -28,8 +27,7 @@ interface PlayerLowsec {}
28
27
  interface PlayerNullsec {}
29
28
 
30
29
  type UpgradeRarityString = "`0noob`" | "`1kiddie`" | "`2h4x0r`" | "`3h4rdc0r3`" | "`4|_|b3|2`" | "`531337`"
31
- type UpgradeRarityNumber = 0 | 1 | 2 | 3 | 4 | 5;
32
- type UpgradeRarity = UpgradeRarityString | UpgradeRarityNumber;
30
+ type UpgradeRarityNumber = 0 | 1 | 2 | 3 | 4 | 5
33
31
 
34
32
  type UpgradeBase = {
35
33
  name: string
@@ -45,10 +43,8 @@ type UpgradeBase = {
45
43
 
46
44
  type Upgrade = UpgradeBase & Record<string, null | boolean | number | string>
47
45
 
48
- type CliUpgrade = Omit<UpgradeBase, `rarity`> & {
49
- [x: string]: null | boolean | number | string
50
- rarity: UpgradeRarityString
51
- }
46
+ type CliUpgrade = Omit<UpgradeBase, `rarity`> &
47
+ { [k: string]: null | boolean | number | string, rarity: UpgradeRarityString }
52
48
 
53
49
  type UsersTopItem<R> = { rank: R, name: string, last_activity: string, balance: string }
54
50
  type CorpsTopItem<R> = { rank: R, name: string, worth: string }
@@ -126,21 +122,11 @@ type Fullsec = Subscripts & PlayerFullsec & {
126
122
  }
127
123
 
128
124
  escrow: {
129
- /** **FULLSEC** */ charge: (args: {
130
- cost: number | string
131
- is_unlim?: boolean
132
- }) => null | ScriptFailure
133
-
125
+ /** **FULLSEC** */ charge: (args: { cost: number | string, is_unlim?: boolean }) => null | ScriptFailure
134
126
  confirm: never
135
127
  }
136
128
 
137
- gui: {
138
- chats: never
139
- quiet: never
140
- size: never
141
- vfx: never
142
- vol: never
143
- }
129
+ gui: { chats: never, quiet: never, size: never, vfx: never, vol: never }
144
130
 
145
131
  market: {
146
132
  /** **FULLSEC** */ browse: {
@@ -459,7 +445,10 @@ type Highsec = Fullsec & PlayerHighsec & {
459
445
  /** **HIGHSEC**
460
446
  * @returns GC balance as number if `is_script` is true (default).
461
447
  * @returns GC balance as string if `is_script` is false. */
462
- balance: ((args?: { is_script?: true }) => number) & ((args: { is_script: false }) => string)
448
+ balance: {
449
+ (args?: { is_script?: true }): number
450
+ (args: { is_script: false }): string
451
+ }
463
452
 
464
453
  /** **HIGHSEC**
465
454
  * @returns Transaction history according to filter.
@@ -716,95 +705,145 @@ type Nullsec = Lowsec & PlayerNullsec & {
716
705
  }
717
706
  }
718
707
 
719
- type MongoTypeString = "minKey" | "double" | "string" | "object" | "array" | "binData" | "undefined" | "objectId" |
720
- "bool" | "date" | "null" | "regex" | "dbPointer" | "javascript" | "symbol" | "int" | "timestamp" | "long" | "decimal" | "maxKey";
721
- type MongoTypeNumber = -1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 127;
708
+ // database
709
+ type MongoPrimitive = null | boolean | number | Date | string
710
+ type MongoValue = MongoPrimitive | MongoValue[] | MongoObject
711
+ type MongoObject = { [k: string]: MongoValue, [k: `$${string}`]: never }
712
+ type MongoQueryValue = MongoPrimitive | MongoQueryValue[] | MongoQueryObject
713
+
714
+ type MongoQueryObject =
715
+ { [k: string]: MongoQueryValue, [k: `$${string}`]: MongoValue, $type?: keyof MongoTypeStringsToTypes | (string & {}) }
716
+
717
+ type MongoTypeStringsToTypes = {
718
+ double: number
719
+ string: string
720
+ object: MongoObject
721
+ array: MongoValue[]
722
+ objectId: ObjectId
723
+ bool: boolean
724
+ date: Date
725
+ null: null
726
+ int: number
727
+ long: number
728
+ }
729
+
730
+ type MongoTypeString = keyof MongoTypeStringsToTypes
731
+ type MongoTypeNumber = -1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 127
732
+ type MongoId = Exclude<MongoPrimitive, null> | MongoObject
733
+ type MongoQueryId = Exclude<MongoPrimitive, null> | MongoQueryObject
734
+ type MongoDocument = MongoObject & { _id?: MongoId }
735
+
736
+ type MongoQueryType<TQuery extends MongoQueryObject> = {
737
+ -readonly [K in keyof TQuery]:
738
+ TQuery[K] extends MongoPrimitive ?
739
+ TQuery[K]
740
+ : TQuery[K] extends { $type: infer TType } ?
741
+ TType extends keyof MongoTypeStringsToTypes ? MongoTypeStringsToTypes[TType] : unknown
742
+ : TQuery[K] extends { $in: (infer TIn)[] } ?
743
+ TIn
744
+ : keyof TQuery[K] extends `$${string}` ?
745
+ unknown
746
+ : TQuery[K] extends { [k: string]: any } ?
747
+ MongoQueryType<TQuery[K]>
748
+ : never
749
+ }
722
750
 
723
- type MongoValue = string | number | boolean | Date | MongoValue[] | { [key: string]: MongoValue } | null
751
+ type MongoCommandValue = MongoPrimitive | MongoCommandValue[] | { [k: string]: MongoCommandValue }
752
+ type MongoArraySelectors<T extends MongoValue[] = MongoValue[]> = { $all: T, $elemMatch: T, $size: number }
724
753
 
725
- type MongoCommandValue = string | number | boolean | Date | MongoCommandValue[] | { [key: string]: MongoCommandValue } |
726
- null | undefined
754
+ type MongoComparisonSelectors<T extends MongoValue = MongoValue> =
755
+ { $eq: T, $gt: T, $gte: T, $in: T[], $lt: T, $lte: T, $ne: T, $nin: T[] }
727
756
 
728
- /**
729
- * Currently unused
730
- */
731
- type MongoLogicalSelectors<T extends MongoValue = MongoValue> = {
732
- $not: T | MongoComparisonSelectors<T> | MongoLogicalSelectors<T>
733
- $nor: T[]
734
- $or: T[]
735
- $and: T[]
736
- }
757
+ type MongoElementSelectors = { $exists: boolean, $type: MongoTypeNumber | MongoTypeString }
737
758
 
738
- type MongoArraySelectors<T extends Array<MongoValue> = Array<MongoValue>> = {
739
- $all: T
740
- $elemMatch: T
741
- $size: number
742
- }
759
+ type MongoQuerySelector<T extends MongoValue> = Partial<
760
+ T extends []
761
+ ? MongoArraySelectors<T> & MongoElementSelectors & MongoComparisonSelectors<T>
762
+ : MongoElementSelectors & MongoComparisonSelectors<T>
763
+ >
743
764
 
744
- type MongoComparisonSelectors<T extends MongoValue = MongoValue> = {
745
- $eq: T
746
- $gt: T
747
- $gte: T
748
- $in: T[]
749
- $lt: T
750
- $lte: T
751
- $ne: T
752
- $nin: T[]
753
- }
765
+ type MongoQuery<T extends MongoObject> = { [K in keyof T]?: T[K] | MongoQuerySelector<T[K]> } & { _id?: MongoId }
754
766
 
755
- type MongoElementSelectors = {
756
- $exists: boolean
757
- $type: MongoTypeNumber | MongoTypeString
758
- }
767
+ type MongoUpdateOperators<T extends MongoObject> = Partial<{
768
+ /* Universal operators */
769
+ $set: Partial<Record<string, MongoCommandValue> & T>
770
+ $setOnInsert: Partial<Record<string, MongoCommandValue> & T>
771
+ $unset: Partial<Record<string, ""> & T>
759
772
 
760
- type MongoQuerySelector<T extends MongoValue = MongoValue> = Partial<T extends MongoValue[] ?
761
- (MongoArraySelectors<T> & MongoElementSelectors & MongoComparisonSelectors<T>) :
762
- (MongoElementSelectors & MongoComparisonSelectors<T>)>
773
+ $rename: Partial<Record<string, string> & { [key in keyof T]: string }>
763
774
 
764
- type Query = { [key: string]: MongoValue | Query } & { _id?: Id, $in?: MongoValue[] }
765
- type Projection = Record<string, boolean | 0 | 1>
775
+ /* Date & number operators */
776
+ $inc: Record<string, number> &
777
+ { [K in keyof T as T[K] extends number | Date ? K : never]?: T[K] extends number ? number : Date }
766
778
 
767
- type MongoCommand = MongoCommandValue & Partial<
768
- { $set: Record<string, MongoCommandValue>, $push: Record<string, MongoCommandValue>, $unset: Record<string, ""> }
769
- >
779
+ $mul: Record<string, number> & { [K in keyof T as T[K] extends number ? K : never]?: number }
780
+ $min: Record<string, number> & { [K in keyof T as T[K] extends number ? K : never]?: number }
781
+ $max: Record<string, number> & { [K in keyof T as T[K] extends number ? K : never]?: number }
782
+
783
+ /* Array operators */
784
+ $pop: Record<string, -1 | 1> & { [K in keyof T as T[K] extends [] ? K : never]?: -1 | 1 }
785
+
786
+ $push: Record<string, MongoCommandValue> & {
787
+ [K in keyof T as T[K] extends [] ? K : never]?: (T[K] extends (infer U)[] ? U : never)
788
+ | MongoUpdateArrayOperatorModifiers<T[K]>
789
+ }
790
+
791
+ $addToSet: Partial<Record<string, MongoCommandValue> & {
792
+ [K in keyof T as T[K] extends [] ? K : never]: (T[K] extends (infer U)[] ? U : never)
793
+ | MongoUpdateArrayOperatorUniversalModifiers<T[K]>
794
+ }>
795
+
796
+ $pull: Partial<Record<string, MongoCommandValue> & {
797
+ [K in keyof T as T[K] extends [] ? K : never]: (T[K] extends (infer U)[] ? U : never)
798
+ | MongoQuerySelector<T[K]>
799
+ }>
800
+
801
+ $pullAll: Record<string, MongoCommandValue> & { [K in keyof T as T[K] extends [] ? K : never]?: T[K] }
802
+ }>
803
+
804
+ type MongoUpdateArrayOperatorUniversalModifiers<T> = { $each?: T extends [] ? T : T[] }
805
+
806
+ type MongoUpdateArrayOperatorModifiers<T> = MongoUpdateArrayOperatorUniversalModifiers<T> &
807
+ { $position?: number, $slice?: number, $sort?: 1 | -1 }
808
+
809
+ type MongoUpdateCommand<Schema extends MongoObject> = MongoUpdateOperators<Schema>
770
810
 
771
- type Id = string | number | boolean | Date | Record<string, MongoValue>
772
- type MongoDocument = { [key: string]: MongoValue, _id: Id }
773
811
  type SortOrder = { [key: string]: 1 | -1 | SortOrder }
774
812
 
775
- type Cursor = {
776
- /** Returns the first document that satisfies the query. */ first: () => MongoDocument | null
777
- /** Returns an array of documents that satisfy the query. */ array: () => MongoDocument[]
813
+ type Cursor<T> = {
814
+ /** Returns the first document that satisfies the query. */ first: () => T | null
815
+ /** Returns an array of documents that satisfy the query. */ array: () => T[]
778
816
  /** Returns the number of documents that match the query. */ count: () => number
779
817
 
780
818
  /** Returns the first document that satisfies the query. Also makes cursor unusable. */
781
- first_and_close: () => MongoDocument
819
+ first_and_close: () => T
782
820
 
783
821
  /** Returns an array of documents that satisfy the query. Also makes cursor unusable. */
784
- array_and_close: () => MongoDocument[]
822
+ array_and_close: () => T[]
785
823
 
786
824
  /** Returns the number of documents that match the query. Also makes cursor unusable. */
787
825
  count_and_close: () => number
788
826
 
789
827
  /** Run `callback` on each document that satisfied the query. */
790
- each: (callback: (document: MongoDocument) => void) => null
828
+ each: (callback: (document: T) => void) => null
791
829
 
792
830
  /** Returns a new cursor with documents sorted as specified.
793
831
  * A value of 1 sorts the property ascending, and -1 descending.
794
832
  * @param order The way the documents are to be sorted. */
795
- sort: (order?: SortOrder) => Cursor
833
+ sort: (order?: SortOrder) => Cursor<T>
796
834
 
797
835
  /** Returns a new cursor without the first number of documents.
798
836
  * @param count Number of documents to skip. */
799
- skip: (count: number) => Cursor
837
+ skip: (count: number) => Cursor<T>
800
838
 
801
839
  /** Returns a new cursor limited to a number of documents as specified.
802
840
  * @param count Number of documents. */
803
- limit: (count: number) => Cursor
841
+ limit: (count: number) => Cursor<T>
804
842
 
805
- /** @param key The key of the documents. */ distinct: ((key: string) => MongoValue[]) & ((key: "_id") => Id[])
843
+ /** @param key The key of the documents. */ distinct: { (key: string): MongoValue[], (key: "_id"): MongoId[] }
806
844
  /** Make cursor unusable. */ close: () => null
807
845
  NumberLong: (number: number) => number
846
+ // TODO what actually is the type here?
808
847
  ObjectId: () => any
809
848
  }
810
849
 
@@ -815,7 +854,9 @@ type CliContext = {
815
854
  /** The number of rows in the caller’s terminal. */ rows: number
816
855
 
817
856
  /** The name of the script that directly called this script, or null if called on the command line or as a
818
- * scriptor. */ calling_script: null
857
+ * scriptor. */
858
+ calling_script: null
859
+
819
860
  is_scriptor?: undefined
820
861
  is_brain?: undefined
821
862
  }
@@ -826,245 +867,212 @@ type SubscriptContext = Replace<CliContext, {
826
867
  calling_script: string
827
868
  }>
828
869
 
829
- type ScriptorContext =
830
- Replace<CliContext, { /** Whether the script is being run as a scriptor. */ is_scriptor: true }>
831
-
832
- type BrainContext =
833
- Replace<CliContext, { /** Whether the script is being run via a bot brain. */ is_brain: true }>
834
-
835
- type Context = CliContext | SubscriptContext | ScriptorContext | BrainContext
836
-
837
- /** Subscript space that can call FULLSEC scripts. */ declare const $fs: Fullsec
838
-
839
- /** Subscript space that can call HIGHSEC and above scripts. Makes your script HIGHSEC (overrides FULLSEC). */
840
- declare const $hs: Highsec
841
-
842
- /** Subscript space that can call MIDSEC and above scripts. Makes your script MIDSEC (overrides higher security levels).
843
- */
844
- declare const $ms: Midsec
845
-
846
- /** Subscript space that can call LOWSEC and above scripts. Makes your script LOWSEC (overrides higher security levels).
847
- */
848
- declare const $ls: Lowsec
849
-
850
- /** Subscript space that can call any script. Makes your script NULLSEC (overrides higher security levels). */
851
- declare const $ns: Nullsec
870
+ type ScriptorContext = Replace<CliContext, { /** Whether the script is being run as a scriptor. */ is_scriptor: true }>
871
+ type BrainContext = Replace<CliContext, { /** Whether the script is being run via a bot brain. */ is_brain: true }>
872
+
873
+ // _id is always returned unless _id: false is passed
874
+ // when anyField: true is given, other fields (except _id) are omitted
875
+
876
+ type MongoProject<TDocument, TProjection> =
877
+ true extends (1 extends TProjection[keyof TProjection] ? true : TProjection[keyof TProjection]) ?
878
+ (TProjection extends { _id: false | 0 } ? {} : { _id: TDocument extends { _id: infer TId } ? TId : MongoId }) &
879
+ {
880
+ [K in
881
+ keyof TDocument as K extends keyof TProjection ? TProjection[K] extends true | 1 ? K : never : never
882
+ ]: TDocument[K]
883
+ } &
884
+ {
885
+ -readonly [K in
886
+ keyof TProjection as TProjection[K] extends true | 1 ? K extends keyof TDocument ? never : K : never
887
+ ]?: MongoValue
888
+ }
889
+ : { [k: string]: MongoValue } & { [K in keyof TDocument as K extends keyof TProjection ? never : K]: TDocument[K] }
852
890
 
853
- /** Subscript space that can call FULLSEC scripts. */ declare const $4s: typeof $fs
891
+ type DeepFreeze<T> = { readonly [P in keyof T]: DeepFreeze<T[P]> }
854
892
 
855
- /** Subscript space that can call HIGHSEC and above scripts. Makes your script HIGHSEC (overrides FULLSEC). */
856
- declare const $3s: typeof $hs
893
+ declare global {
894
+ type Scriptor<TArgs extends any[] = any[]> = { name: string, call: (...args: TArgs) => unknown }
895
+ type Context = CliContext | SubscriptContext | ScriptorContext | BrainContext
896
+ type ObjectId = { $oid: string }
857
897
 
858
- /** Subscript space that can call MIDSEC and above scripts. Makes your script MIDSEC (overrides higher security levels).
859
- */
860
- declare const $2s: typeof $ms
898
+ /** Subscript space that can call FULLSEC scripts. */ const $fs: Fullsec
861
899
 
862
- /** Subscript space that can call LOWSEC and above scripts. Makes your script LOWSEC (overrides higher security levels).
863
- */
864
- declare const $1s: typeof $ls
900
+ /** Subscript space that can call HIGHSEC and above scripts. Makes your script HIGHSEC (overrides FULLSEC). */
901
+ const $hs: Highsec
865
902
 
866
- /** Subscript space that can call any script. Makes your script NULLSEC (overrides higher security levels). */
867
- declare const $0s: typeof $ns
903
+ /** Subscript space that can call MIDSEC and above scripts. Makes your script MIDSEC (overrides higher security levels).
904
+ */
905
+ const $ms: Midsec
868
906
 
869
- /** Subscript space that can call any script. Uses seclevel provided in comment before script (defaults to NULLSEC)
870
- * @example
871
- * // @ seclevel MIDSEC
872
- * // remove the space betwen "@" and "s", there's only a space because otherwise vscode breaks
873
- * export function script() {
874
- * $s.foo.bar() // will be converted to #ms.foo.bar()
875
- * } */
876
- declare const $s: Nullsec
907
+ /** Subscript space that can call LOWSEC and above scripts. Makes your script LOWSEC (overrides higher security levels).
908
+ */
909
+ const $ls: Lowsec
877
910
 
878
- type ObjectId = { $oid: string }
911
+ /** Subscript space that can call any script. Makes your script NULLSEC (overrides higher security levels). */
912
+ const $ns: Nullsec
879
913
 
880
- declare const $db: {
881
- /** Insert a document or documents into a collection.
882
- * @param documents A document or array of documents to insert into the collection. */
883
- i: (documents: object | object[]) => {
884
- ok: 1
885
- n: number
886
- opTime: { ts: "Undefined Conversion", t: number }
887
- electionId: "Undefined Conversion"
888
- operationTime: "Undefined Conversion"
889
- $clusterTime: {
890
- clusterTime: "Undefined Conversion"
891
- signature: { hash: "Undefined Conversion", keyId: "Undefined Conversion" }
892
- }
893
- }
914
+ /** Subscript space that can call FULLSEC scripts. */ const $4s: typeof $fs
894
915
 
895
- /** Remove documents from a collection.
896
- * @param query Specifies deletion criteria using query operators. */
897
- r: (query: Query) => {
898
- ok: 0 | 1
899
- n: number
900
- opTime: { ts: "Undefined Conversion", t: number }
901
- electionId: "Undefined Conversion"
902
- operationTime: "Undefined Conversion"
903
- $clusterTime: {
904
- clusterTime: "Undefined Conversion"
905
- signature: { hash: "Undefined Conversion", keyId: "Undefined Conversion" }
906
- }
907
- }
916
+ /** Subscript space that can call HIGHSEC and above scripts. Makes your script HIGHSEC (overrides FULLSEC). */
917
+ const $3s: typeof $hs
908
918
 
909
- /** Find documents in a collection or view and returns a cursor to the selected documents.
910
- * @param query Specifies deletion criteria using query operators.
911
- * @param projection Specifies the fields to return in the documents that match the query filter. */
912
- f: (query?: Query, projection?: Projection) => Cursor
913
-
914
- /** Update an existing documents in a collection.
915
- * @param query Specifies deletion criteria using query operators.
916
- * @param command The modifications to apply.
917
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.update/#parameters} */
918
- u: (query: Query | Query[], command: MongoCommand) => {
919
- ok: 0 | 1
920
- nModified: number
921
- n: number
922
- opTime: { ts: "Undefined Conversion", t: number }
923
- electionId: "Undefined Conversion"
924
- operationTime: "Undefined Conversion"
925
- $clusterTime: {
926
- clusterTime: "Undefined Conversion"
927
- signature: { hash: "Undefined Conversion", keyId: "Undefined Conversion" }
928
- }
929
- }
930
-
931
- /** Updates one document within the collection based on the filter.
932
- * @param query Specifies deletion criteria using query operators.
933
- * @param command The modifications to apply.
934
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.update/#parameters} */
935
- u1: (query: Query | Query[], command: MongoCommand) => {
936
- ok: 0 | 1
937
- nModified: number
938
- n: number
939
- opTime: {
940
- ts: "Undefined Conversion"
941
- t: number
942
- }
943
- electionId: "Undefined Conversion"
944
- operationTime: "Undefined Conversion"
945
- $clusterTime: {
946
- clusterTime: "Undefined Conversion"
947
- signature: {
948
- hash: "Undefined Conversion"
949
- keyId: "Undefined Conversion"
950
- }
951
- }
952
- }
919
+ /** Subscript space that can call MIDSEC and above scripts. Makes your script MIDSEC (overrides higher security levels).
920
+ */
921
+ const $2s: typeof $ms
953
922
 
954
- /** Update or insert or insert document.
955
- * Same as Update, but if no documents match the query, one document will be inserted based on the properties in
956
- * both the query and the command.
957
- * The `$setOnInsert` operator is useful to set defaults.
958
- * @param query Specifies deletion criteria using query operators.
959
- * @param command The modifications to apply.
960
- * {@link https://docs.mongodb.com/manual/reference/method/db.collection.update/#parameters} */
961
- us: (query: Query | Query[], command: MongoCommand) => {
962
- ok: 0 | 1
963
- nModified: number
964
- n: number
965
- opTime: { ts: "Undefined Conversion", t: number }
966
- electionId: "Undefined Conversion"
967
- operationTime: "Undefined Conversion"
968
- $clusterTime: {
969
- clusterTime: "Undefined Conversion"
970
- signature: { hash: "Undefined Conversion", keyId: "Undefined Conversion" }
971
- }
923
+ /** Subscript space that can call LOWSEC and above scripts. Makes your script LOWSEC (overrides higher security levels).
924
+ */
925
+ const $1s: typeof $ls
926
+
927
+ /** Subscript space that can call any script. Makes your script NULLSEC (overrides higher security levels). */
928
+ const $0s: typeof $ns
929
+
930
+ /** Subscript space that can call any script. Uses seclevel provided in comment before script (defaults to NULLSEC)
931
+ * @example
932
+ * // @​seclevel MIDSEC
933
+ * // note, do NOT copy paste the above line because there is a zero-width space inserted between "@" and "s"
934
+ * export function script() {
935
+ * $s.foo.bar() // will be converted to #ms.foo.bar()
936
+ * } */
937
+ const $s: Nullsec
938
+
939
+ const $db: {
940
+ /** Insert a document or documents into a collection.
941
+ * @param documents A document or array of documents to insert into the collection. */
942
+ i: <T extends MongoDocument>(documents: (T & { _id?: MongoId }) | (T & { _id?: MongoId })[]) =>
943
+ { n: number, opTime: { t: number }, ok: 0 | 1 }[]
944
+
945
+ /** Remove documents from a collection.
946
+ * @param query Specifies deletion criteria using query operators. */
947
+ r: <T extends MongoDocument>(query: MongoQuery<T>) => { n: number, opTime: { t: number }, ok: 0 | 1 }[]
948
+
949
+ /** Find documents in a collection or view and returns a cursor to the selected documents.
950
+ * @param query Specifies deletion criteria using query operators.
951
+ * @param projection Specifies the fields to return in the documents that match the query filter. */
952
+ f: <
953
+ const TQuery extends MongoQueryObject & { _id?: MongoQueryId },
954
+ const TProjection extends { [k: string]: boolean | 0 | 1 } = {}
955
+ >(query: TQuery, projection?: TProjection) => Cursor<MongoProject<MongoQueryType<TQuery>, TProjection>>
956
+
957
+ /** Update existing documents in a collection.
958
+ * @param query Specifies deletion criteria using query operators.
959
+ * @param command The modifications to apply.
960
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.update/#parameters} */
961
+ u: <T extends MongoDocument>(query: MongoQuery<T> | MongoQuery<T>[], command: MongoUpdateCommand<T>) =>
962
+ { n: number, opTime: { t: number }, ok: 0 | 1, nModified: number }[]
963
+
964
+ /** Updates one document within the collection based on the filter.
965
+ * @param query Specifies deletion criteria using query operators.
966
+ * @param command The modifications to apply.
967
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.update/#parameters} */
968
+ u1: <T extends MongoDocument>(query: MongoQuery<T> | MongoQuery<T>[], command: MongoUpdateCommand<T>) =>
969
+ { n: number, ok: 0 | 1, opTime: { t: number }, nModified: number }[]
970
+
971
+ /** Update or insert document.
972
+ * Same as Update, but if no documents match the query, one document will be inserted based on the properties in
973
+ * both the query and the command.
974
+ * The `$setOnInsert` operator is useful to set defaults.
975
+ * @param query Specifies deletion criteria using query operators.
976
+ * @param command The modifications to apply.
977
+ * {@link https://docs.mongodb.com/manual/reference/method/db.collection.update/#parameters} */
978
+ us: <T extends MongoDocument>(query: MongoQuery<T> | MongoQuery<T>[], command: MongoUpdateCommand<T>) =>
979
+ { n: number, ok: 0 | 1, opTime: { t: number }, nModified: number }[]
980
+
981
+ ObjectId: () => ObjectId
972
982
  }
973
983
 
974
- ObjectId: () => ObjectId
984
+ /** Debug Log.
985
+ *
986
+ * If `$D()` is called in a script you own, the `return` value of the top level script is suppressed and instead an
987
+ * array of every `$D()`’d entry is printed.
988
+ * This lets you use `$D()` like `console.log()`.
989
+ *
990
+ * `$D()` in scripts not owned by you are not shown but the `return` value always is.
991
+ *
992
+ * `$D()` returns the first argument so `$D("Hello, World!") evaluates to `"Hello, World!"` as if the `$D` text wasn't
993
+ * there.
994
+ *
995
+ * `$D()`’d items are returned even if the script times out or errors. */
996
+ function $D<T>(args: T): T
997
+
998
+ /** Function Multi-Call Lock.
999
+ *
1000
+ * This is used by escrow to ensure that it is only used once in script execution.
1001
+ *
1002
+ * The first time (per-script) `$FMCL` is encountered, it returns `undefined`, every other time it `return`s `true`.
1003
+ *
1004
+ * @example
1005
+ * if ($FMCL)
1006
+ * return { ok: false, msg: "This script can only be used once per script execution." }
1007
+ *
1008
+ * // all code here will only run once */
1009
+ const $FMCL: undefined | true
1010
+
1011
+ /** Per-script mutable "global" persistent object that is discarded at the end of top level script execution.
1012
+ *
1013
+ * `$G` persists between script calls until the end of the main script run making it useful for caching db entries when
1014
+ * your script is a subscript.
1015
+ * @example
1016
+ * if (!$G.dbCache)
1017
+ * $G.dbCache = $db.f({ whatever: true }).first() */
1018
+ const $G: Record<string | symbol, any>
1019
+
1020
+ /** This contains a JS timestamp (not Date) set immediately before your code begins running.
1021
+ * @example
1022
+ * $D(Date.now() - _START) // milliseconds left of run time
1023
+ */
1024
+ const _START: number
1025
+
1026
+ /** This contains a JS timestamp (not Date) set immediately before your code begins running.
1027
+ * @example
1028
+ * $D(Date.now() - _ST) // milliseconds left of run time */
1029
+ const _ST: typeof _START
1030
+
1031
+ /** JavaScript timestamp for the end of the script run (`_START + _TIMEOUT`). */ const _END: number
1032
+
1033
+ /** The number of milliseconds a script can run for. Normally `5000` though it has been known to change. */
1034
+ const _TIMEOUT: number
1035
+
1036
+ /** The number of milliseconds a script can run for. Normally `5000` though it has been known to change. */
1037
+ const _TO: typeof _TIMEOUT
1038
+
1039
+ /** The source code of this script as a string. */ const _SOURCE: string
1040
+ /** A unix timestamp of the date this script was built. */ const _BUILD_DATE: number
1041
+
1042
+ /** The user this script has been uploaded to.
1043
+ *
1044
+ * Shorter alternative to `context.this_script.split(".")[0].
1045
+ *
1046
+ * In rare cases where it's not known at build time, it's `"UNKNOWN"`. */
1047
+ const _SCRIPT_USER: string
1048
+
1049
+ /** The name of this script excluding the user and `.`.
1050
+ *
1051
+ * e.g. in the script `foo.bar`, `_SCRIPT_NAME` is `bar`.
1052
+ *
1053
+ * Shorter alternative to `context.this_script.split(".")[1].
1054
+ *
1055
+ * In rare cases where it's not known at build time, it's `"UNKNOWN"`. */
1056
+ const _SCRIPT_NAME: string
1057
+
1058
+ /** The full name of this script equivilent to `context.this_script` but should use less characters.
1059
+ *
1060
+ * In rare cases where it's not known at build time, it's `"UNKNOWN"`. */
1061
+ const _FULL_SCRIPT_NAME: string
1062
+
1063
+ /** The seclevel of this script as a number.
1064
+ *
1065
+ * In rare cases where it's not known at build time, it's `-1`. */
1066
+ const _SECLEVEL: -1 | 0 | 1 | 2 | 3 | 4
1067
+
1068
+ /** Recursively
1069
+ * [`Object.freeze()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)
1070
+ * an object and its properties' objects and its properties' objects and so on.
1071
+ *
1072
+ * [Official Hackmud Wiki](https://wiki.hackmud.com/scripting/extensions/deep_freeze) */
1073
+ const DEEP_FREEZE: <T>(value: T) => DeepFreeze<T>
1074
+
1075
+ const _RUN_ID: string
975
1076
  }
976
1077
 
977
- /** Debug Log.
978
- *
979
- * If `$D()` is called in a script you own, the `return` value of the top level script is suppressed and instead an
980
- * array of every `$D()`’d entry is printed.
981
- * This lets you use `$D()` like `console.log()`.
982
- *
983
- * `$D()` in scripts not owned by you are not shown but the `return` value always is.
984
- *
985
- * `$D()` returns the first argument so `$D("Hello, World!") evaluates to `"Hello, World!"` as if the `$D` text wasn't
986
- * there.
987
- *
988
- * `$D()`’d items are returned even if the script times out or errors. */
989
- declare function $D<T>(args: T): T
990
-
991
- /** Function Multi-Call Lock.
992
- *
993
- * This is used by escrow to ensure that it is only used once in script execution.
994
- *
995
- * The first time (per-script) `$FMCL` is encountered, it returns `undefined`, every other time it `return`s `true`.
996
- *
997
- * @example
998
- * if ($FMCL)
999
- * return { ok: false, msg: "This script can only be used once per script execution." }
1000
- *
1001
- * // all code here will only run once */
1002
- declare const $FMCL: undefined | true
1003
-
1004
- /** Per-script mutable "global" persistent object that is discarded at the end of top level script execution.
1005
- *
1006
- * `$G` persists between script calls until the end of the main script run making it useful for caching db entries when
1007
- * your script is a subscript.
1008
- * @example
1009
- * if (!$G.dbCache)
1010
- * $G.dbCache = $db.f({ whatever: true }).first() */
1011
- declare const $G: Record<string | symbol, any>
1012
-
1013
- /** This contains a JS timestamp (not Date) set immediately before your code begins running.
1014
- * @example
1015
- * $D(Date.now() - _START) // milliseconds left of run time
1016
- */
1017
- declare const _START: number
1018
-
1019
- /** This contains a JS timestamp (not Date) set immediately before your code begins running.
1020
- * @example
1021
- * $D(Date.now() - _ST) // milliseconds left of run time */
1022
- declare const _ST: typeof _START
1023
-
1024
- /** JavaScript timestamp for the end of the script run (`_START + _TIMEOUT`). */ declare const _END: number
1025
-
1026
- /** The number of milliseconds a script can run for. Normally `5000` though it has been known to change. */
1027
- declare const _TIMEOUT: number
1028
-
1029
- /** The number of milliseconds a script can run for. Normally `5000` though it has been known to change. */
1030
- declare const _TO: typeof _TIMEOUT
1031
-
1032
- /** The source code of this script as a string. */ declare const _SOURCE: string
1033
- /** A unix timestamp of the date this script was built. */ declare const _BUILD_DATE: number
1034
-
1035
- /** The user this script has been uploaded to.
1036
- *
1037
- * Shorter alternative to `context.this_script.split(".")[0].
1038
- *
1039
- * In rare cases where it's not known at build time, it's `"UNKNOWN"`. */
1040
- declare const _SCRIPT_USER: string
1041
-
1042
- /** The name of this script excluding the user and `.`.
1043
- *
1044
- * e.g. in the script `foo.bar`, `_SCRIPT_NAME` is `bar`.
1045
- *
1046
- * Shorter alternative to `context.this_script.split(".")[1].
1047
- *
1048
- * In rare cases where it's not known at build time, it's `"UNKNOWN"`. */
1049
- declare const _SCRIPT_NAME: string
1050
-
1051
- /** The full name of this script equivilent to `context.this_script` but should use less characters.
1052
- *
1053
- * In rare cases where it's not known at build time, it's `"UNKNOWN"`. */
1054
- declare const _FULL_SCRIPT_NAME: string
1055
-
1056
- /** The seclevel of this script as a number.
1057
- *
1058
- * In rare cases where it's not known at build time, it's `-1`. */
1059
- declare const _SECLEVEL: -1 | 0 | 1 | 2 | 3 | 4
1060
-
1061
- type DeepFreeze<T> = { readonly [P in keyof T]: DeepFreeze<T[P]> }
1062
-
1063
- /** Recursively
1064
- * [`Object.freeze()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze)
1065
- * an object and its properties' objects and its properties' objects and so on.
1066
- *
1067
- * [Official Hackmud Wiki](https://wiki.hackmud.com/scripting/extensions/deep_freeze) */
1068
- declare const DEEP_FREEZE: <T>(value: T) => DeepFreeze<T>
1069
-
1070
- declare const _RUN_ID: string
1078
+ export {}
package/index.js CHANGED
@@ -46,7 +46,7 @@ import "import-meta-resolve"
46
46
  import "./processScript/transform.js"
47
47
  import "@samual/lib/clearObject"
48
48
  import "@samual/lib/copyFilePersistent"
49
- import "@samual/lib/Cache"
49
+ import "@samual/lib/AutoMap"
50
50
  import "@samual/lib/writeFilePersistent"
51
51
  import "fs/promises"
52
52
  import "chokidar"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hackmud-script-manager",
3
- "version": "0.20.4-9f460e8",
3
+ "version": "0.20.4-a7d8309",
4
4
  "description": "Script manager for game hackmud, with minification, TypeScript support, and player script type definition generation.",
5
5
  "keywords": [
6
6
  "api",
@@ -31,50 +31,50 @@
31
31
  "url": "https://github.com/samualtnorman/hackmud-script-manager.git"
32
32
  },
33
33
  "dependencies": {
34
- "@babel/generator": "^7.24.4",
35
- "@babel/parser": "^7.24.4",
36
- "@babel/plugin-proposal-decorators": "^7.24.1",
37
- "@babel/plugin-proposal-destructuring-private": "^7.24.1",
38
- "@babel/plugin-proposal-do-expressions": "^7.24.1",
39
- "@babel/plugin-proposal-explicit-resource-management": "^7.24.1",
40
- "@babel/plugin-proposal-function-bind": "^7.24.1",
41
- "@babel/plugin-proposal-function-sent": "^7.24.1",
42
- "@babel/plugin-proposal-partial-application": "^7.24.1",
43
- "@babel/plugin-proposal-pipeline-operator": "^7.24.1",
44
- "@babel/plugin-proposal-record-and-tuple": "^7.24.1",
45
- "@babel/plugin-proposal-throw-expressions": "^7.24.1",
46
- "@babel/plugin-transform-class-properties": "^7.24.1",
47
- "@babel/plugin-transform-class-static-block": "^7.24.4",
48
- "@babel/plugin-transform-exponentiation-operator": "^7.24.1",
49
- "@babel/plugin-transform-json-strings": "^7.24.1",
50
- "@babel/plugin-transform-logical-assignment-operators": "^7.24.1",
51
- "@babel/plugin-transform-nullish-coalescing-operator": "^7.24.1",
52
- "@babel/plugin-transform-numeric-separator": "^7.24.1",
53
- "@babel/plugin-transform-object-rest-spread": "^7.24.1",
54
- "@babel/plugin-transform-optional-catch-binding": "^7.24.1",
55
- "@babel/plugin-transform-optional-chaining": "^7.24.1",
56
- "@babel/plugin-transform-private-property-in-object": "^7.24.1",
57
- "@babel/plugin-transform-typescript": "^7.24.4",
58
- "@babel/plugin-transform-unicode-sets-regex": "^7.24.1",
59
- "@babel/traverse": "^7.24.1",
60
- "@babel/types": "^7.24.0",
34
+ "@babel/generator": "^7.26.2",
35
+ "@babel/parser": "^7.26.2",
36
+ "@babel/plugin-proposal-decorators": "^7.25.9",
37
+ "@babel/plugin-proposal-destructuring-private": "^7.26.0",
38
+ "@babel/plugin-proposal-do-expressions": "^7.25.9",
39
+ "@babel/plugin-proposal-explicit-resource-management": "^7.25.9",
40
+ "@babel/plugin-proposal-function-bind": "^7.25.9",
41
+ "@babel/plugin-proposal-function-sent": "^7.25.9",
42
+ "@babel/plugin-proposal-partial-application": "^7.25.9",
43
+ "@babel/plugin-proposal-pipeline-operator": "^7.25.9",
44
+ "@babel/plugin-proposal-record-and-tuple": "^7.25.9",
45
+ "@babel/plugin-proposal-throw-expressions": "^7.25.9",
46
+ "@babel/plugin-transform-class-properties": "^7.25.9",
47
+ "@babel/plugin-transform-class-static-block": "^7.26.0",
48
+ "@babel/plugin-transform-exponentiation-operator": "^7.25.9",
49
+ "@babel/plugin-transform-json-strings": "^7.25.9",
50
+ "@babel/plugin-transform-logical-assignment-operators": "^7.25.9",
51
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.25.9",
52
+ "@babel/plugin-transform-numeric-separator": "^7.25.9",
53
+ "@babel/plugin-transform-object-rest-spread": "^7.25.9",
54
+ "@babel/plugin-transform-optional-catch-binding": "^7.25.9",
55
+ "@babel/plugin-transform-optional-chaining": "^7.25.9",
56
+ "@babel/plugin-transform-private-property-in-object": "^7.25.9",
57
+ "@babel/plugin-transform-typescript": "^7.25.9",
58
+ "@babel/plugin-transform-unicode-sets-regex": "^7.25.9",
59
+ "@babel/traverse": "^7.25.9",
60
+ "@babel/types": "^7.26.0",
61
61
  "@bloomberg/record-tuple-polyfill": "^0.0.4",
62
62
  "@rollup/plugin-babel": "^6.0.4",
63
- "@rollup/plugin-commonjs": "^25.0.7",
63
+ "@rollup/plugin-commonjs": "^28.0.1",
64
64
  "@rollup/plugin-json": "^6.1.0",
65
- "@rollup/plugin-node-resolve": "^15.2.3",
66
- "@samual/lib": "0.11.0",
67
- "acorn": "^8.11.3",
65
+ "@rollup/plugin-node-resolve": "^15.3.0",
66
+ "@samual/lib": "^0.13.0",
67
+ "acorn": "^8.14.0",
68
68
  "chalk": "^5.3.0",
69
- "chokidar": "^3.6.0",
70
- "import-meta-resolve": "^4.0.0",
71
- "prettier": "^3.2.5",
69
+ "chokidar": "^4.0.1",
70
+ "import-meta-resolve": "^4.1.0",
71
+ "prettier": "^3.3.3",
72
72
  "proxy-polyfill": "^0.3.2",
73
- "rollup": "^4.16.4",
74
- "terser": "^5.30.4"
73
+ "rollup": "^4.27.4",
74
+ "terser": "^5.36.0"
75
75
  },
76
76
  "peerDependencies": {
77
- "typescript": "5.4.5"
77
+ "typescript": "^5.4.5"
78
78
  },
79
79
  "type": "module",
80
80
  "exports": {
@@ -162,21 +162,25 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
162
162
  if (program.scope.hasGlobal("_SECLEVEL"))
163
163
  for (const referencePath of getReferencePathsToGlobal("_SECLEVEL", program))
164
164
  referencePath.replaceWith(t.numericLiteral(seclevel))
165
- let needGetPrototypeOf = !1
165
+ let needGetPrototypeOf = !1,
166
+ needHasOwn = !1
166
167
  if (program.scope.hasGlobal("Object"))
167
168
  for (const referencePath of getReferencePathsToGlobal("Object", program))
168
169
  if ("MemberExpression" == referencePath.parent.type && !referencePath.parent.computed) {
169
- assert("Identifier" == referencePath.parent.property.type, "src/processScript/transform.ts:241:64")
170
+ assert("Identifier" == referencePath.parent.property.type, "src/processScript/transform.ts:242:64")
170
171
  if ("getPrototypeOf" == referencePath.parent.property.name) {
171
172
  referencePath.parentPath.replaceWith(t.identifier(`_${uniqueId}_GET_PROTOTYPE_OF_`))
172
173
  needGetPrototypeOf = !0
174
+ } else if ("hasOwn" == referencePath.parent.property.name) {
175
+ referencePath.parentPath.replaceWith(t.identifier(`_${uniqueId}_HAS_OWN_`))
176
+ needHasOwn = !0
173
177
  }
174
178
  }
175
179
  const consoleMethodsReferenced = new Set()
176
180
  if (program.scope.hasGlobal("console"))
177
181
  for (const referencePath of getReferencePathsToGlobal("console", program))
178
182
  if ("MemberExpression" == referencePath.parent.type && !referencePath.parent.computed) {
179
- assert("Identifier" == referencePath.parent.property.type, "src/processScript/transform.ts:256:64")
183
+ assert("Identifier" == referencePath.parent.property.type, "src/processScript/transform.ts:260:64")
180
184
  referencePath.parentPath.replaceWith(
181
185
  t.identifier(`_${uniqueId}_CONSOLE_METHOD_${referencePath.parent.property.name}_`)
182
186
  )
@@ -184,13 +188,13 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
184
188
  }
185
189
  const lastStatement = program.node.body.at(-1)
186
190
  let exportDefaultName
187
- assert(lastStatement, "src/processScript/transform.ts:270:27 program is empty")
191
+ assert(lastStatement, "src/processScript/transform.ts:274:27 program is empty")
188
192
  if ("ExportNamedDeclaration" == lastStatement.type) {
189
193
  program.node.body.pop()
190
194
  for (const specifier of lastStatement.specifiers) {
191
195
  assert(
192
196
  "ExportSpecifier" == specifier.type,
193
- `src/processScript/transform.ts:276:51 ${specifier.type} is currently unsupported`
197
+ `src/processScript/transform.ts:280:51 ${specifier.type} is currently unsupported`
194
198
  )
195
199
  if (
196
200
  "default" !=
@@ -292,11 +296,11 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
292
296
  let hoistedGlobalBlockFunctions = 0
293
297
  for (const [globalBlockIndex, globalBlockStatement] of [...globalBlock.body.entries()].reverse())
294
298
  if ("VariableDeclaration" == globalBlockStatement.type) {
295
- assert(1 == globalBlockStatement.declarations.length, "src/processScript/transform.ts:390:59")
299
+ assert(1 == globalBlockStatement.declarations.length, "src/processScript/transform.ts:394:59")
296
300
  const declarator = globalBlockStatement.declarations[0]
297
301
  assert(
298
302
  "Identifier" == declarator.id.type,
299
- `src/processScript/transform.ts:394:51 declarator.id.type was "${declarator.id.type}"`
303
+ `src/processScript/transform.ts:398:51 declarator.id.type was "${declarator.id.type}"`
300
304
  )
301
305
  program.scope.crawl()
302
306
  if (program.scope.hasGlobal(declarator.id.name)) {
@@ -311,9 +315,9 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
311
315
  Object.keys(program.scope.globals).some(global => globalBlockVariables.has(global))
312
316
  ) {
313
317
  const binding = program.scope.getBinding(declarator.id.name)
314
- assert(binding, "src/processScript/transform.ts:413:23")
318
+ assert(binding, "src/processScript/transform.ts:417:23")
315
319
  for (const referencePath of binding.referencePaths) {
316
- assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:416:56")
320
+ assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:420:56")
317
321
  referencePath.replaceWith(
318
322
  t.memberExpression(
319
323
  t.identifier(`_${uniqueId}_G_`),
@@ -361,16 +365,16 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
361
365
  } else globalBlockVariables.add(declarator.id.name)
362
366
  } else if ("ClassDeclaration" == globalBlockStatement.type) {
363
367
  program.scope.crawl()
364
- assert(globalBlockStatement.id, "src/processScript/transform.ts:473:37")
368
+ assert(globalBlockStatement.id, "src/processScript/transform.ts:477:37")
365
369
  if (program.scope.hasGlobal(globalBlockStatement.id.name)) {
366
370
  globalBlock.body.splice(globalBlockIndex, 1)
367
371
  const [globalBlockPath] = program.unshiftContainer("body", globalBlock),
368
372
  [globalBlockStatementPath] = program.unshiftContainer("body", globalBlockStatement)
369
373
  program.scope.crawl()
370
374
  const binding = program.scope.getBinding(globalBlockStatement.id.name)
371
- assert(binding, "src/processScript/transform.ts:485:22")
375
+ assert(binding, "src/processScript/transform.ts:489:22")
372
376
  for (const referencePath of binding.referencePaths) {
373
- assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:488:55")
377
+ assert("Identifier" == referencePath.node.type, "src/processScript/transform.ts:492:55")
374
378
  referencePath.replaceWith(
375
379
  t.memberExpression(t.identifier(`_${uniqueId}_G_`), t.identifier(referencePath.node.name))
376
380
  )
@@ -448,6 +452,31 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
448
452
  )
449
453
  ])
450
454
  )
455
+ needHasOwn &&
456
+ mainFunction.body.body.unshift(
457
+ t.variableDeclaration("let", [
458
+ t.variableDeclarator(
459
+ t.identifier(`_${uniqueId}_HAS_OWN_`),
460
+ t.callExpression(
461
+ t.memberExpression(
462
+ t.memberExpression(
463
+ t.identifier(
464
+ globalFunctionsUnder7Characters.find(name => !program.scope.hasOwnBinding(name))
465
+ ),
466
+ t.identifier("call")
467
+ ),
468
+ t.identifier("bind")
469
+ ),
470
+ [
471
+ t.memberExpression(
472
+ t.memberExpression(t.identifier("Object"), t.identifier("prototype")),
473
+ t.identifier("hasOwnProperty")
474
+ )
475
+ ]
476
+ )
477
+ )
478
+ ])
479
+ )
451
480
  consoleMethodsReferenced.size &&
452
481
  mainFunction.body.body.unshift(
453
482
  t.variableDeclaration(
@@ -537,7 +566,7 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
537
566
  }
538
567
  },
539
568
  ClassBody({ node: classBody, scope, parent }) {
540
- assert(t.isClass(parent), "src/processScript/transform.ts:658:30")
569
+ assert(t.isClass(parent), "src/processScript/transform.ts:687:30")
541
570
  let thisIsReferenced = !1
542
571
  for (const classMethod of classBody.body) {
543
572
  if ("ClassMethod" != classMethod.type) continue
@@ -637,23 +666,23 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
637
666
  }
638
667
  function processFakeSubscriptObject(fakeSubscriptObjectName, seclevel) {
639
668
  for (const referencePath of getReferencePathsToGlobal(fakeSubscriptObjectName, program)) {
640
- assert("MemberExpression" == referencePath.parent.type, "src/processScript/transform.ts:764:60")
669
+ assert("MemberExpression" == referencePath.parent.type, "src/processScript/transform.ts:793:60")
641
670
  assert("Identifier" == referencePath.parent.property.type)
642
671
  assert(
643
672
  "MemberExpression" == referencePath.parentPath.parentPath?.node.type,
644
- "src/processScript/transform.ts:766:81"
673
+ "src/processScript/transform.ts:795:81"
645
674
  )
646
675
  assert(
647
676
  "Identifier" == referencePath.parentPath.parentPath.node.property.type,
648
- "src/processScript/transform.ts:767:83"
677
+ "src/processScript/transform.ts:796:83"
649
678
  )
650
679
  assert(
651
680
  /^[_a-z][\d_a-z]{0,24}$/.test(referencePath.parent.property.name),
652
- `src/processScript/transform.ts:771:8 invalid user "${referencePath.parent.property.name}" in subscript`
681
+ `src/processScript/transform.ts:800:8 invalid user "${referencePath.parent.property.name}" in subscript`
653
682
  )
654
683
  assert(
655
684
  /^[_a-z][\d_a-z]{0,24}$/.test(referencePath.parentPath.parentPath.node.property.name),
656
- `src/processScript/transform.ts:776:8 invalid script name "${referencePath.parentPath.parentPath.node.property.name}" in subscript`
685
+ `src/processScript/transform.ts:805:8 invalid script name "${referencePath.parentPath.parentPath.node.property.name}" in subscript`
657
686
  )
658
687
  if ("CallExpression" == referencePath.parentPath.parentPath.parentPath?.type)
659
688
  referencePath.parentPath.parentPath.replaceWith(
package/push.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Cache } from "@samual/lib/Cache"
1
+ import { AutoMap } from "@samual/lib/AutoMap"
2
2
  import { ensure, assert } from "@samual/lib/assert"
3
3
  import { countHackmudCharacters } from "@samual/lib/countHackmudCharacters"
4
4
  import { readDirectoryWithStats } from "@samual/lib/readDirectoryWithStats"
@@ -84,8 +84,8 @@ async function push(
84
84
  return new NoUsersError(
85
85
  "Could not find any users. Either provide the names of your users or log into a user in hackmud"
86
86
  )
87
- const usersToScriptsToPush = new Cache(_user => new Map()),
88
- scriptNamesToUsers = new Cache(_scriptName => new Set())
87
+ const usersToScriptsToPush = new AutoMap(_user => new Map()),
88
+ scriptNamesToUsers = new AutoMap(_scriptName => new Set())
89
89
  for (const script of scripts) {
90
90
  const [user, scriptName] = script.split(".")
91
91
  assert(user, "src/push.ts:105:16")
@@ -121,7 +121,7 @@ async function push(
121
121
  for (const user of users)
122
122
  if (!usersToScriptsToPush.get(user).has(scriptName))
123
123
  return new NoScriptsError(`Could not find script ${user}.${scriptName} to push`)
124
- const pathsToUsers = new Cache(_path => new Set())
124
+ const pathsToUsers = new AutoMap(_path => new Set())
125
125
  for (const [user, scriptsToPush] of usersToScriptsToPush)
126
126
  for (const path of scriptsToPush.values()) pathsToUsers.get(path).add(user)
127
127
  const allInfo = []
package/watch.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Cache } from "@samual/lib/Cache"
1
+ import { AutoMap } from "@samual/lib/AutoMap"
2
2
  import { assert } from "@samual/lib/assert"
3
3
  import { countHackmudCharacters } from "@samual/lib/countHackmudCharacters"
4
4
  import { readDirectoryWithStats } from "@samual/lib/readDirectoryWithStats"
@@ -60,7 +60,7 @@ async function watch(
60
60
  ) {
61
61
  if (!scripts.length) throw Error("scripts option was an empty array")
62
62
  if (!(await stat(sourceDirectory)).isDirectory()) throw Error("Target folder must be a folder")
63
- const scriptNamesToUsers = new Cache(_scriptName => new Set()),
63
+ const scriptNamesToUsers = new AutoMap(_scriptName => new Set()),
64
64
  wildScriptUsers = new Set(),
65
65
  wildUserScripts = new Set()
66
66
  let pushEverything = !1
@@ -92,7 +92,7 @@ async function watch(
92
92
  )
93
93
  )
94
94
  return
95
- const scriptNamesToUsersToSkip = new Cache(_scriptName => [])
95
+ const scriptNamesToUsersToSkip = new AutoMap(_scriptName => [])
96
96
  await Promise.all(
97
97
  (await readDirectoryWithStats(sourceDirectory)).map(async ({ stats, name, path }) => {
98
98
  if (stats.isDirectory())