hackmud-script-manager 0.20.4-550e28d → 0.20.4-698da0d

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -20,6 +20,10 @@ You can read about how HSM works [in my blog post](https://samual.uk/blog/js-cod
20
20
  > ```
21
21
  > You will need to run `Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser` in PowerShell as an administrator. For more information, see [Microsoft's page about Execution Policies](https://learn.microsoft.com/en-gb/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.4).
22
22
 
23
+ ![image](https://github.com/samualtnorman/hackmud-script-manager/assets/18307063/69a371fe-f8c8-43fe-b3c7-39f3735ce6fb)
24
+ ![image](https://github.com/samualtnorman/hackmud-script-manager/assets/18307063/08103f9e-74fa-4a56-a739-94858ba8c139)
25
+ ![image](https://github.com/samualtnorman/hackmud-script-manager/assets/18307063/25ccb86d-1fe3-4632-b703-ac47f5b32c9c)
26
+
23
27
  ## Features
24
28
  - Minification
25
29
  - This includes auto quine cheating.
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,10 +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-550e28d",
16
+ const formatOption = name => colourN(`-${1 == name.length ? "" : "-"}${name}`),
17
17
  options = new Map(),
18
18
  commands = [],
19
- userColours = new Cache(user => {
19
+ userColours = new AutoMap(user => {
20
20
  let hash = 0
21
21
  for (const char of user) hash += (hash >> 1) + hash + "xi1_8ratvsw9hlbgm02y5zpdcn7uekof463qj".indexOf(char) + 1
22
22
  return [colourJ, colourK, colourM, colourW, colourL, colourB][hash % 6](user)
@@ -62,7 +62,7 @@ process.version.startsWith("v21.") &&
62
62
  )
63
63
  )
64
64
  if ("v" == commands[0] || "version" == commands[0] || popOption("version", "v")?.value) {
65
- console.log(version)
65
+ console.log("0.20.4-698da0d")
66
66
  process.exit()
67
67
  }
68
68
  if (popOption("help", "h")?.value) {
@@ -83,7 +83,7 @@ switch (commands[0]) {
83
83
  noMinifyIncompatibleOption = mangleNamesOption || forceQuineCheatsOption
84
84
  if (noMinifyOption && noMinifyIncompatibleOption) {
85
85
  logError(
86
- `Options ${colourN(noMinifyOption.name)} and ${colourN(noMinifyIncompatibleOption.name)} are incompatible\n`
86
+ `Options ${formatOption(noMinifyOption.name)} and ${formatOption(noMinifyIncompatibleOption.name)} are incompatible\n`
87
87
  )
88
88
  logHelp()
89
89
  process.exit(1)
@@ -106,6 +106,7 @@ switch (commands[0]) {
106
106
  )
107
107
  process.exit(1)
108
108
  }
109
+ complainAboutUnrecognisedOptions()
109
110
  const { processScript } = await processScriptModule,
110
111
  fileBaseName = basename(target, fileExtension),
111
112
  fileBaseNameEndsWithDotSrc = fileBaseName.endsWith(".src"),
@@ -178,15 +179,15 @@ switch (commands[0]) {
178
179
  }
179
180
  } else scripts.push("*.*")
180
181
  if ("push" == commands[0]) {
181
- const { push, MissingSourceFolderError, MissingHackmudFolderError, NoUsersError } =
182
- await pushModule,
183
- infos = await push(sourcePath, hackmudPath, {
184
- scripts,
185
- onPush: info => logInfo(info, hackmudPath),
186
- minify: noMinifyOption && !noMinifyOption.value,
187
- mangleNames: mangleNamesOption?.value,
188
- forceQuineCheats: forceQuineCheatsOption?.value
189
- })
182
+ const { push, MissingSourceFolderError, MissingHackmudFolderError, NoUsersError } = await pushModule
183
+ complainAboutUnrecognisedOptions()
184
+ const infos = await push(sourcePath, hackmudPath, {
185
+ scripts,
186
+ onPush: info => logInfo(info, hackmudPath),
187
+ minify: noMinifyOption && !noMinifyOption.value,
188
+ mangleNames: mangleNamesOption?.value,
189
+ forceQuineCheats: forceQuineCheatsOption?.value
190
+ })
190
191
  if (infos instanceof Error) {
191
192
  logError(infos.message)
192
193
  if (infos instanceof MissingSourceFolderError || infos instanceof NoUsersError) {
@@ -200,12 +201,13 @@ switch (commands[0]) {
200
201
  } else infos.length || logError("Could not find any scripts to push")
201
202
  } else {
202
203
  const typeDeclarationPathOption = popOption(
203
- "type-declaration-path",
204
- "type-declaration",
205
- "dts",
206
- "gen-types"
207
- ),
208
- { watch } = await watchModule
204
+ "type-declaration-path",
205
+ "type-declaration",
206
+ "dts",
207
+ "gen-types"
208
+ )
209
+ complainAboutUnrecognisedOptions()
210
+ const { watch } = await watchModule
209
211
  watch(sourcePath, hackmudPath, {
210
212
  scripts,
211
213
  onPush: info => logInfo(info, hackmudPath),
@@ -229,6 +231,7 @@ switch (commands[0]) {
229
231
  logHelp()
230
232
  process.exit(1)
231
233
  }
234
+ complainAboutUnrecognisedOptions()
232
235
  const sourcePath = commands[2] || "."
233
236
  await pull(sourcePath, hackmudPath, script).catch(error => {
234
237
  console.error(error)
@@ -238,8 +241,9 @@ switch (commands[0]) {
238
241
  break
239
242
  case "sync-macros":
240
243
  {
241
- const hackmudPath = getHackmudPath(),
242
- { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
244
+ const hackmudPath = getHackmudPath()
245
+ complainAboutUnrecognisedOptions()
246
+ const { macrosSynced, usersSynced } = await syncMacros(hackmudPath)
243
247
  log(`Synced ${macrosSynced} macros to ${usersSynced} users`)
244
248
  }
245
249
  break
@@ -255,12 +259,13 @@ switch (commands[0]) {
255
259
  logHelp()
256
260
  process.exit(1)
257
261
  }
262
+ complainAboutUnrecognisedOptions()
258
263
  const sourcePath = resolve(target),
259
264
  outputPath = commands[2] || "./player.d.ts",
260
265
  typeDeclaration = await generateTypeDeclaration(sourcePath, hackmudPath)
261
266
  let typeDeclarationPath = resolve(outputPath)
262
267
  await writeFile(typeDeclarationPath, typeDeclaration).catch(error => {
263
- assert(error instanceof Error, "src/bin/hsm.ts:330:35")
268
+ assert(error instanceof Error, "src/bin/hsm.ts:343:35")
264
269
  if ("EISDIR" != error.code) throw error
265
270
  typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
266
271
  return writeFile(typeDeclarationPath, typeDeclaration)
@@ -281,7 +286,7 @@ function logHelp() {
281
286
  const pushCommandDescription = "Push scripts from a directory to hackmud user's scripts directories",
282
287
  forceQuineCheatsOptionDescription = `Force quine cheats on. Use ${colourN("--force-quine-cheats")}=${colourV("false")} to force off`,
283
288
  hackmudPathOption = `${colourN("--hackmud-path")}=${colourB("<path>")}\n Override hackmud path`
284
- console.log(colourN("Version") + colourS(": ") + colourV(version))
289
+ console.log(colourN("Version") + colourS(": ") + colourV("0.20.4-698da0d"))
285
290
  switch (commands[0]) {
286
291
  case "dev":
287
292
  case "watch":
@@ -359,7 +364,7 @@ function getHackmudPath() {
359
364
  }
360
365
  function assertOptionIsBoolean(option) {
361
366
  if ("boolean" != typeof option.value) {
362
- logError(`The value for ${colourN(option.name)} must be ${colourV("true")} or ${colourV("false")}\n`)
367
+ logError(`The value for ${formatOption(option.name)} must be ${colourV("true")} or ${colourV("false")}\n`)
363
368
  logHelp()
364
369
  process.exit(1)
365
370
  }
@@ -367,9 +372,7 @@ function assertOptionIsBoolean(option) {
367
372
  function popOption(...names) {
368
373
  const presentOptionNames = names.filter(name => options.has(name))
369
374
  if (!presentOptionNames.length) return
370
- const presentOptionNamesWithDashDash = presentOptionNames.map(name =>
371
- colourN(`-${1 == name.length ? "" : "-"}${name}`)
372
- )
375
+ const presentOptionNamesWithDashDash = presentOptionNames.map(formatOption)
373
376
  if (presentOptionNames.length > 1) {
374
377
  logError(
375
378
  `The options ${presentOptionNamesWithDashDash.join(", ")} are aliases for each other. Please only specify one`
@@ -380,3 +383,11 @@ function popOption(...names) {
380
383
  options.delete(presentOptionNames[0])
381
384
  return { name: presentOptionNamesWithDashDash[0], value }
382
385
  }
386
+ function complainAboutUnrecognisedOptions() {
387
+ if (options.size) {
388
+ logError(
389
+ `Unrecognised option${options.size > 1 ? "s" : ""}: ${[...options.keys()].map(formatOption).join(", ")}`
390
+ )
391
+ process.exit(1)
392
+ }
393
+ }
package/env.d.ts CHANGED
@@ -3,13 +3,7 @@ type ScriptSuccess<T = object> = { ok: true } & T
3
3
  type ScriptFailure = { ok: false, msg?: string }
4
4
  type ScriptResponse<T = object> = ScriptSuccess<T> | ScriptFailure
5
5
  type ErrorScripts = Record<string, () => ScriptFailure>
6
-
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
- }
6
+ type Scriptor<TArgs extends any[] = any[]> = { name: string, call: (...args: TArgs) => unknown }
13
7
 
14
8
  type Subscripts = Record<string, Record<string, (...args: any) => any>> & {
15
9
  accts: ErrorScripts
@@ -34,8 +28,8 @@ interface PlayerLowsec {}
34
28
  interface PlayerNullsec {}
35
29
 
36
30
  type UpgradeRarityString = "`0noob`" | "`1kiddie`" | "`2h4x0r`" | "`3h4rdc0r3`" | "`4|_|b3|2`" | "`531337`"
37
- type UpgradeRarityNumber = 0 | 1 | 2 | 3 | 4 | 5;
38
- type UpgradeRarity = UpgradeRarityString | UpgradeRarityNumber;
31
+ type UpgradeRarityNumber = 0 | 1 | 2 | 3 | 4 | 5
32
+ type UpgradeRarity = UpgradeRarityString | UpgradeRarityNumber
39
33
 
40
34
  type UpgradeBase = {
41
35
  name: string
@@ -51,10 +45,8 @@ type UpgradeBase = {
51
45
 
52
46
  type Upgrade = UpgradeBase & Record<string, null | boolean | number | string>
53
47
 
54
- type CliUpgrade = Omit<UpgradeBase, `rarity`> & {
55
- [x: string]: null | boolean | number | string
56
- rarity: UpgradeRarityString
57
- }
48
+ type CliUpgrade = Omit<UpgradeBase, `rarity`> &
49
+ { [k: string]: null | boolean | number | string, rarity: UpgradeRarityString }
58
50
 
59
51
  type UsersTopItem<R> = { rank: R, name: string, last_activity: string, balance: string }
60
52
  type CorpsTopItem<R> = { rank: R, name: string, worth: string }
@@ -132,21 +124,11 @@ type Fullsec = Subscripts & PlayerFullsec & {
132
124
  }
133
125
 
134
126
  escrow: {
135
- /** **FULLSEC** */ charge: (args: {
136
- cost: number | string
137
- is_unlim?: boolean
138
- }) => null | ScriptFailure
139
-
127
+ /** **FULLSEC** */ charge: (args: { cost: number | string, is_unlim?: boolean }) => null | ScriptFailure
140
128
  confirm: never
141
129
  }
142
130
 
143
- gui: {
144
- chats: never
145
- quiet: never
146
- size: never
147
- vfx: never
148
- vol: never
149
- }
131
+ gui: { chats: never, quiet: never, size: never, vfx: never, vol: never }
150
132
 
151
133
  market: {
152
134
  /** **FULLSEC** */ browse: {
@@ -465,7 +447,10 @@ type Highsec = Fullsec & PlayerHighsec & {
465
447
  /** **HIGHSEC**
466
448
  * @returns GC balance as number if `is_script` is true (default).
467
449
  * @returns GC balance as string if `is_script` is false. */
468
- balance: ((args?: { is_script?: true }) => number) & ((args: { is_script: false }) => string)
450
+ balance: {
451
+ (args?: { is_script?: true }): number
452
+ (args: { is_script: false }): string
453
+ }
469
454
 
470
455
  /** **HIGHSEC**
471
456
  * @returns Transaction history according to filter.
@@ -635,6 +620,9 @@ type Lowsec = Midsec & PlayerLowsec & {
635
620
  (args: { i: number | number[], to: string, memo?: string }): ScriptResponse
636
621
  (args: { sn: string | string[], to: string, memo?: string }): ScriptResponse
637
622
  }
623
+ /** **LOWSEC** */ expose_access_log: (args: { target: string }) => ScriptResponse
624
+ /** **LOWSEC** */ xfer_gc_from: (args: { target: string, amount: number | string }) => ScriptResponse
625
+ /** **LOWSEC** */ expose_balance: (args: { target: string }) => ScriptResponse
638
626
  }
639
627
  }
640
628
 
@@ -719,95 +707,146 @@ type Nullsec = Lowsec & PlayerNullsec & {
719
707
  }
720
708
  }
721
709
 
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
-
726
- type MongoValue = string | number | boolean | Date | MongoValue[] | { [key: string]: MongoValue } | null
727
-
728
- type MongoCommandValue = string | number | boolean | Date | MongoCommandValue[] | { [key: string]: MongoCommandValue } |
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[]
710
+ // database
711
+ type MongoPrimitive = null | boolean | number | Date | string
712
+ type MongoValue = MongoPrimitive | MongoValue[] | MongoObject
713
+ type MongoObject = { [k: string]: MongoValue, [k: `$${string}`]: never }
714
+ type MongoQueryValue = MongoPrimitive | MongoQueryValue[] | MongoQueryObject
715
+
716
+ type MongoQueryObject =
717
+ { [k: string]: MongoQueryValue, [k: `$${string}`]: MongoValue, $type?: keyof MongoTypeStringsToTypes | (string & {}) }
718
+
719
+ type MongoTypeStringsToTypes = {
720
+ double: number
721
+ string: string
722
+ object: MongoObject
723
+ array: MongoValue[]
724
+ objectId: ObjectId
725
+ bool: boolean
726
+ date: Date
727
+ null: null
728
+ int: number
729
+ long: number
739
730
  }
740
731
 
741
- type MongoArraySelectors<T extends Array<MongoValue> = Array<MongoValue>> = {
742
- $all: T
743
- $elemMatch: T
744
- $size: number
732
+ type MongoTypeString = keyof MongoTypeStringsToTypes
733
+ type MongoTypeNumber = -1 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 16 | 17 | 18 | 19 | 127
734
+ type MongoId = Exclude<MongoPrimitive, null> | MongoObject
735
+ type MongoQueryId = Exclude<MongoPrimitive, null> | MongoQueryObject
736
+ type MongoDocument = MongoObject & { _id?: MongoId }
737
+
738
+ type MongoQueryType<TQuery extends MongoQueryObject> = {
739
+ -readonly [K in keyof TQuery]:
740
+ TQuery[K] extends MongoPrimitive ?
741
+ TQuery[K]
742
+ : TQuery[K] extends { $type: infer TType } ?
743
+ TType extends keyof MongoTypeStringsToTypes ? MongoTypeStringsToTypes[TType] : unknown
744
+ : TQuery[K] extends { $in: (infer TIn)[] } ?
745
+ TIn
746
+ : keyof TQuery[K] extends `$${string}` ?
747
+ unknown
748
+ : TQuery[K] extends { [k: string]: any } ?
749
+ MongoQueryType<TQuery[K]>
750
+ : never
745
751
  }
746
752
 
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
- }
753
+ type MongoCommandValue = MongoPrimitive | MongoCommandValue[] | { [k: string]: MongoCommandValue }
754
+ type MongoArraySelectors<T extends MongoValue[] = MongoValue[]> = { $all: T, $elemMatch: T, $size: number }
757
755
 
758
- type MongoElementSelectors = {
759
- $exists: boolean
760
- $type: MongoTypeNumber | MongoTypeString
761
- }
756
+ type MongoComparisonSelectors<T extends MongoValue = MongoValue> =
757
+ { $eq: T, $gt: T, $gte: T, $in: T[], $lt: T, $lte: T, $ne: T, $nin: T[] }
758
+
759
+ type MongoElementSelectors = { $exists: boolean, $type: MongoTypeNumber | MongoTypeString }
762
760
 
763
- type MongoQuerySelector<T extends MongoValue = MongoValue> = Partial<T extends MongoValue[] ?
764
- (MongoArraySelectors<T> & MongoElementSelectors & MongoComparisonSelectors<T>) :
765
- (MongoElementSelectors & MongoComparisonSelectors<T>)>
761
+ type MongoQuerySelector<T extends MongoValue> = Partial<
762
+ T extends []
763
+ ? MongoArraySelectors<T> & MongoElementSelectors & MongoComparisonSelectors<T>
764
+ : MongoElementSelectors & MongoComparisonSelectors<T>
765
+ >
766
766
 
767
- type Query = { [key: string]: MongoValue | Query } & { _id?: Id, $in?: MongoValue[] }
767
+ type MongoQuery<T extends MongoObject> = { [K in keyof T]?: T[K] | MongoQuerySelector<T[K]> } & { _id?: MongoId }
768
768
  type Projection = Record<string, boolean | 0 | 1>
769
769
 
770
- type MongoCommand = MongoCommandValue & Partial<
771
- { $set: Record<string, MongoCommandValue>, $push: Record<string, MongoCommandValue>, $unset: Record<string, ""> }
772
- >
770
+ type MongoUpdateOperators<T extends MongoObject> = Partial<{
771
+ /* Universal operators */
772
+ $set: Partial<Record<string, MongoCommandValue> & T>
773
+ $setOnInsert: Partial<Record<string, MongoCommandValue> & T>
774
+ $unset: Partial<Record<string, ""> & T>
775
+
776
+ $rename: Partial<Record<string, string> & { [key in keyof T]: string }>
777
+
778
+ /* Date & number operators */
779
+ $inc: Record<string, number> &
780
+ { [K in keyof T as T[K] extends number | Date ? K : never]?: T[K] extends number ? number : Date }
781
+
782
+ $mul: Record<string, number> & { [K in keyof T as T[K] extends number ? K : never]?: number }
783
+ $min: Record<string, number> & { [K in keyof T as T[K] extends number ? K : never]?: number }
784
+ $max: Record<string, number> & { [K in keyof T as T[K] extends number ? K : never]?: number }
785
+
786
+ /* Array operators */
787
+ $pop: Record<string, -1 | 1> & { [K in keyof T as T[K] extends [] ? K : never]?: -1 | 1 }
788
+
789
+ $push: Record<string, MongoCommandValue> & {
790
+ [K in keyof T as T[K] extends [] ? K : never]?: (T[K] extends (infer U)[] ? U : never)
791
+ | MongoUpdateArrayOperatorModifiers<T[K]>
792
+ }
793
+
794
+ $addToSet: Partial<Record<string, MongoCommandValue> & {
795
+ [K in keyof T as T[K] extends [] ? K : never]: (T[K] extends (infer U)[] ? U : never)
796
+ | MongoUpdateArrayOperatorUniversalModifiers<T[K]>
797
+ }>
798
+
799
+ $pull: Partial<Record<string, MongoCommandValue> & {
800
+ [K in keyof T as T[K] extends [] ? K : never]: (T[K] extends (infer U)[] ? U : never)
801
+ | MongoQuerySelector<T[K]>
802
+ }>
803
+
804
+ $pullAll: Record<string, MongoCommandValue> & { [K in keyof T as T[K] extends [] ? K : never]?: T[K] }
805
+ }>
806
+
807
+ type MongoUpdateArrayOperatorUniversalModifiers<T> = { $each?: T extends [] ? T : T[] }
808
+
809
+ type MongoUpdateArrayOperatorModifiers<T> = MongoUpdateArrayOperatorUniversalModifiers<T> &
810
+ { $position?: number, $slice?: number, $sort?: 1 | -1 }
811
+
812
+ type MongoUpdateCommand<Schema extends MongoObject> = MongoUpdateOperators<Schema>
773
813
 
774
- type Id = string | number | boolean | Date | Record<string, MongoValue>
775
- type MongoDocument = { [key: string]: MongoValue, _id: Id }
776
814
  type SortOrder = { [key: string]: 1 | -1 | SortOrder }
777
815
 
778
- type Cursor = {
779
- /** Returns the first document that satisfies the query. */ first: () => MongoDocument | null
780
- /** Returns an array of documents that satisfy the query. */ array: () => MongoDocument[]
816
+ type Cursor<T> = {
817
+ /** Returns the first document that satisfies the query. */ first: () => T | null
818
+ /** Returns an array of documents that satisfy the query. */ array: () => T[]
781
819
  /** Returns the number of documents that match the query. */ count: () => number
782
820
 
783
821
  /** Returns the first document that satisfies the query. Also makes cursor unusable. */
784
- first_and_close: () => MongoDocument
822
+ first_and_close: () => T
785
823
 
786
824
  /** Returns an array of documents that satisfy the query. Also makes cursor unusable. */
787
- array_and_close: () => MongoDocument[]
825
+ array_and_close: () => T[]
788
826
 
789
827
  /** Returns the number of documents that match the query. Also makes cursor unusable. */
790
828
  count_and_close: () => number
791
829
 
792
830
  /** Run `callback` on each document that satisfied the query. */
793
- each: (callback: (document: MongoDocument) => void) => null
831
+ each: (callback: (document: T) => void) => null
794
832
 
795
833
  /** Returns a new cursor with documents sorted as specified.
796
834
  * A value of 1 sorts the property ascending, and -1 descending.
797
835
  * @param order The way the documents are to be sorted. */
798
- sort: (order?: SortOrder) => Cursor
836
+ sort: (order?: SortOrder) => Cursor<T>
799
837
 
800
838
  /** Returns a new cursor without the first number of documents.
801
839
  * @param count Number of documents to skip. */
802
- skip: (count: number) => Cursor
840
+ skip: (count: number) => Cursor<T>
803
841
 
804
842
  /** Returns a new cursor limited to a number of documents as specified.
805
843
  * @param count Number of documents. */
806
- limit: (count: number) => Cursor
844
+ limit: (count: number) => Cursor<T>
807
845
 
808
- /** @param key The key of the documents. */ distinct: ((key: string) => MongoValue[]) & ((key: "_id") => Id[])
846
+ /** @param key The key of the documents. */ distinct: { (key: string): MongoValue[], (key: "_id"): MongoId[] }
809
847
  /** Make cursor unusable. */ close: () => null
810
848
  NumberLong: (number: number) => number
849
+ // TODO what actually is the type here?
811
850
  ObjectId: () => any
812
851
  }
813
852
 
@@ -818,7 +857,9 @@ type CliContext = {
818
857
  /** The number of rows in the caller’s terminal. */ rows: number
819
858
 
820
859
  /** The name of the script that directly called this script, or null if called on the command line or as a
821
- * scriptor. */ calling_script: null
860
+ * scriptor. */
861
+ calling_script: null
862
+
822
863
  is_scriptor?: undefined
823
864
  is_brain?: undefined
824
865
  }
@@ -829,12 +870,8 @@ type SubscriptContext = Replace<CliContext, {
829
870
  calling_script: string
830
871
  }>
831
872
 
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
-
873
+ type ScriptorContext = Replace<CliContext, { /** Whether the script is being run as a scriptor. */ is_scriptor: true }>
874
+ type BrainContext = Replace<CliContext, { /** Whether the script is being run via a bot brain. */ is_brain: true }>
838
875
  type Context = CliContext | SubscriptContext | ScriptorContext | BrainContext
839
876
 
840
877
  /** Subscript space that can call FULLSEC scripts. */ declare const $fs: Fullsec
@@ -871,8 +908,8 @@ declare const $0s: typeof $ns
871
908
 
872
909
  /** Subscript space that can call any script. Uses seclevel provided in comment before script (defaults to NULLSEC)
873
910
  * @example
874
- * // @ seclevel MIDSEC
875
- * // remove the space betwen "@" and "s", there's only a space because otherwise vscode breaks
911
+ * // @​seclevel MIDSEC
912
+ * // note, do NOT copy paste the above line because there is a zero-width space inserted between "@" and "s"
876
913
  * export function script() {
877
914
  * $s.foo.bar() // will be converted to #ms.foo.bar()
878
915
  * } */
@@ -880,99 +917,65 @@ declare const $s: Nullsec
880
917
 
881
918
  type ObjectId = { $oid: string }
882
919
 
920
+ // _id is always returned unless _id: false is passed
921
+ // when anyField: true is given, other fields (except _id) are omitted
922
+
923
+ type MongoProject<TDocument, TProjection> =
924
+ true extends (1 extends TProjection[keyof TProjection] ? true : TProjection[keyof TProjection]) ?
925
+ (TProjection extends { _id: false | 0 } ? {} : { _id: TDocument extends { _id: infer TId } ? TId : MongoId }) &
926
+ {
927
+ [K in
928
+ keyof TDocument as K extends keyof TProjection ? TProjection[K] extends true | 1 ? K : never : never
929
+ ]: TDocument[K]
930
+ } &
931
+ {
932
+ -readonly [K in
933
+ keyof TProjection as TProjection[K] extends true | 1 ? K extends keyof TDocument ? never : K : never
934
+ ]?: MongoValue
935
+ }
936
+ : { [k: string]: MongoValue } & { [K in keyof TDocument as K extends keyof TProjection ? never : K]: TDocument[K] }
937
+
883
938
  declare const $db: {
884
939
  /** Insert a document or documents into a collection.
885
940
  * @param documents A document or array of documents to insert into the collection. */
886
- i: (documents: object | object[]) => {
887
- ok: 1
888
- n: number
889
- opTime: { ts: "Undefined Conversion", t: number }
890
- electionId: "Undefined Conversion"
891
- operationTime: "Undefined Conversion"
892
- $clusterTime: {
893
- clusterTime: "Undefined Conversion"
894
- signature: { hash: "Undefined Conversion", keyId: "Undefined Conversion" }
895
- }
896
- }
941
+ i: <T extends MongoDocument>(documents: (T & { _id?: MongoId }) | (T & { _id?: MongoId })[]) =>
942
+ { n: number, opTime: { t: number }, ok: 0 | 1 }[]
897
943
 
898
944
  /** Remove documents from a collection.
899
945
  * @param query Specifies deletion criteria using query operators. */
900
- r: (query: Query) => {
901
- ok: 0 | 1
902
- n: number
903
- opTime: { ts: "Undefined Conversion", t: number }
904
- electionId: "Undefined Conversion"
905
- operationTime: "Undefined Conversion"
906
- $clusterTime: {
907
- clusterTime: "Undefined Conversion"
908
- signature: { hash: "Undefined Conversion", keyId: "Undefined Conversion" }
909
- }
910
- }
946
+ r: <T extends MongoDocument>(query: MongoQuery<T>) => { n: number, opTime: { t: number }, ok: 0 | 1 }[]
911
947
 
912
948
  /** Find documents in a collection or view and returns a cursor to the selected documents.
913
949
  * @param query Specifies deletion criteria using query operators.
914
950
  * @param projection Specifies the fields to return in the documents that match the query filter. */
915
- f: (query?: Query, projection?: Projection) => Cursor
951
+ f: <
952
+ const TQuery extends MongoQueryObject & { _id?: MongoQueryId },
953
+ const TProjection extends { [k: string]: boolean | 0 | 1 } = {}
954
+ >(query: TQuery, projection?: TProjection) => Cursor<MongoProject<MongoQueryType<TQuery>, TProjection>>
916
955
 
917
- /** Update an existing documents in a collection.
956
+ /** Update existing documents in a collection.
918
957
  * @param query Specifies deletion criteria using query operators.
919
958
  * @param command The modifications to apply.
920
959
  * {@link https://docs.mongodb.com/manual/reference/method/db.collection.update/#parameters} */
921
- u: (query: Query | Query[], command: MongoCommand) => {
922
- ok: 0 | 1
923
- nModified: number
924
- n: number
925
- opTime: { ts: "Undefined Conversion", t: number }
926
- electionId: "Undefined Conversion"
927
- operationTime: "Undefined Conversion"
928
- $clusterTime: {
929
- clusterTime: "Undefined Conversion"
930
- signature: { hash: "Undefined Conversion", keyId: "Undefined Conversion" }
931
- }
932
- }
960
+ u: <T extends MongoDocument>(query: MongoQuery<T> | MongoQuery<T>[], command: MongoUpdateCommand<T>) =>
961
+ { n: number, opTime: { t: number }, ok: 0 | 1, nModified: number }[]
933
962
 
934
963
  /** Updates one document within the collection based on the filter.
935
964
  * @param query Specifies deletion criteria using query operators.
936
965
  * @param command The modifications to apply.
937
966
  * {@link https://docs.mongodb.com/manual/reference/method/db.collection.update/#parameters} */
938
- u1: (query: Query | Query[], command: MongoCommand) => {
939
- ok: 0 | 1
940
- nModified: number
941
- n: number
942
- opTime: {
943
- ts: "Undefined Conversion"
944
- t: number
945
- }
946
- electionId: "Undefined Conversion"
947
- operationTime: "Undefined Conversion"
948
- $clusterTime: {
949
- clusterTime: "Undefined Conversion"
950
- signature: {
951
- hash: "Undefined Conversion"
952
- keyId: "Undefined Conversion"
953
- }
954
- }
955
- }
967
+ u1: <T extends MongoDocument>(query: MongoQuery<T> | MongoQuery<T>[], command: MongoUpdateCommand<T>) =>
968
+ { n: number, ok: 0 | 1, opTime: { t: number }, nModified: number }[]
956
969
 
957
- /** Update or insert or insert document.
970
+ /** Update or insert document.
958
971
  * Same as Update, but if no documents match the query, one document will be inserted based on the properties in
959
972
  * both the query and the command.
960
973
  * The `$setOnInsert` operator is useful to set defaults.
961
974
  * @param query Specifies deletion criteria using query operators.
962
975
  * @param command The modifications to apply.
963
976
  * {@link https://docs.mongodb.com/manual/reference/method/db.collection.update/#parameters} */
964
- us: (query: Query | Query[], command: MongoCommand) => {
965
- ok: 0 | 1
966
- nModified: number
967
- n: number
968
- opTime: { ts: "Undefined Conversion", t: number }
969
- electionId: "Undefined Conversion"
970
- operationTime: "Undefined Conversion"
971
- $clusterTime: {
972
- clusterTime: "Undefined Conversion"
973
- signature: { hash: "Undefined Conversion", keyId: "Undefined Conversion" }
974
- }
975
- }
977
+ us: <T extends MongoDocument>(query: MongoQuery<T> | MongoQuery<T>[], command: MongoUpdateCommand<T>) =>
978
+ { n: number, ok: 0 | 1, opTime: { t: number }, nModified: number }[]
976
979
 
977
980
  ObjectId: () => ObjectId
978
981
  }
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-550e28d",
3
+ "version": "0.20.4-698da0d",
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": {
@@ -86,6 +86,6 @@
86
86
  "hsm": "bin/hsm.js"
87
87
  },
88
88
  "engines": {
89
- "node": "^18 || >=20"
89
+ "node": "^18 || ^20 || >=22"
90
90
  }
91
91
  }
@@ -204,6 +204,7 @@ async function processScript(
204
204
  const bundle = await rollup({
205
205
  input: filePathResolved,
206
206
  plugins: [
207
+ rollupPluginJSON({ preferConst: !0 }),
207
208
  {
208
209
  name: "hackmud-script-manager",
209
210
  async transform(code, id) {
@@ -227,8 +228,7 @@ async function processScript(
227
228
  },
228
229
  babel({ babelHelpers: "bundled", plugins, configFile: !1, extensions: supportedExtensions }),
229
230
  rollupPluginCommonJS(),
230
- rollupPluginNodeResolve({ extensions: supportedExtensions }),
231
- rollupPluginJSON()
231
+ rollupPluginNodeResolve({ extensions: supportedExtensions })
232
232
  ],
233
233
  treeshake: { moduleSideEffects: !1 }
234
234
  }),
@@ -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
@@ -629,39 +658,31 @@ function transform(file, sourceCode, { uniqueId = "00000000000", scriptUser, scr
629
658
  })
630
659
  return { file, seclevel }
631
660
  function createGetFunctionPrototypeNode() {
632
- for (const globalFunction of globalFunctionsUnder7Characters)
633
- if (!program.scope.hasOwnBinding(globalFunction))
634
- return t.memberExpression(
635
- t.memberExpression(t.identifier(globalFunction), t.identifier("constructor")),
636
- t.identifier("prototype")
637
- )
661
+ const name = globalFunctionsUnder7Characters.find(name => !program.scope.hasOwnBinding(name))
638
662
  return t.memberExpression(
639
- t.memberExpression(
640
- t.arrowFunctionExpression([t.identifier("_")], t.identifier("_")),
641
- t.identifier("constructor")
642
- ),
643
- t.identifier("prototype")
663
+ name ? t.identifier(name) : t.arrowFunctionExpression([t.identifier("_")], t.identifier("_")),
664
+ t.identifier("__proto__")
644
665
  )
645
666
  }
646
667
  function processFakeSubscriptObject(fakeSubscriptObjectName, seclevel) {
647
668
  for (const referencePath of getReferencePathsToGlobal(fakeSubscriptObjectName, program)) {
648
- assert("MemberExpression" == referencePath.parent.type, "src/processScript/transform.ts:774:60")
669
+ assert("MemberExpression" == referencePath.parent.type, "src/processScript/transform.ts:793:60")
649
670
  assert("Identifier" == referencePath.parent.property.type)
650
671
  assert(
651
672
  "MemberExpression" == referencePath.parentPath.parentPath?.node.type,
652
- "src/processScript/transform.ts:776:81"
673
+ "src/processScript/transform.ts:795:81"
653
674
  )
654
675
  assert(
655
676
  "Identifier" == referencePath.parentPath.parentPath.node.property.type,
656
- "src/processScript/transform.ts:777:83"
677
+ "src/processScript/transform.ts:796:83"
657
678
  )
658
679
  assert(
659
680
  /^[_a-z][\d_a-z]{0,24}$/.test(referencePath.parent.property.name),
660
- `src/processScript/transform.ts:781:8 invalid user "${referencePath.parent.property.name}" in subscript`
681
+ `src/processScript/transform.ts:800:8 invalid user "${referencePath.parent.property.name}" in subscript`
661
682
  )
662
683
  assert(
663
684
  /^[_a-z][\d_a-z]{0,24}$/.test(referencePath.parentPath.parentPath.node.property.name),
664
- `src/processScript/transform.ts:786: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`
665
686
  )
666
687
  if ("CallExpression" == referencePath.parentPath.parentPath.parentPath?.type)
667
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,10 +1,10 @@
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"
5
5
  import { writeFilePersistent } from "@samual/lib/writeFilePersistent"
6
6
  import { watch as watch$1 } from "chokidar"
7
- import { readFile, writeFile } from "fs/promises"
7
+ import { stat, readFile, writeFile } from "fs/promises"
8
8
  import { extname, basename, resolve } from "path"
9
9
  import { supportedExtensions } from "./constants.js"
10
10
  import { generateTypeDeclaration } from "./generateTypeDeclaration.js"
@@ -59,7 +59,8 @@ async function watch(
59
59
  } = {}
60
60
  ) {
61
61
  if (!scripts.length) throw Error("scripts option was an empty array")
62
- const scriptNamesToUsers = new Cache(_scriptName => new Set()),
62
+ if (!(await stat(sourceDirectory)).isDirectory()) throw Error("Target folder must be a folder")
63
+ const scriptNamesToUsers = new AutoMap(_scriptName => new Set()),
63
64
  wildScriptUsers = new Set(),
64
65
  wildUserScripts = new Set()
65
66
  let pushEverything = !1
@@ -91,7 +92,7 @@ async function watch(
91
92
  )
92
93
  )
93
94
  return
94
- const scriptNamesToUsersToSkip = new Cache(_scriptName => [])
95
+ const scriptNamesToUsersToSkip = new AutoMap(_scriptName => [])
95
96
  await Promise.all(
96
97
  (await readDirectoryWithStats(sourceDirectory)).map(async ({ stats, name, path }) => {
97
98
  if (stats.isDirectory())
@@ -136,7 +137,7 @@ async function watch(
136
137
  forceQuineCheats
137
138
  }))
138
139
  } catch (error) {
139
- assert(error instanceof Error, "src/watch.ts:141:36")
140
+ assert(error instanceof Error, "src/watch.ts:146:36")
140
141
  onPush?.({ path, users: [], characterCount: 0, error })
141
142
  return
142
143
  }
@@ -181,7 +182,7 @@ async function watch(
181
182
  forceQuineCheats
182
183
  }))
183
184
  } catch (error) {
184
- assert(error instanceof Error, "src/watch.ts:177:35")
185
+ assert(error instanceof Error, "src/watch.ts:182:35")
185
186
  onPush?.({ path, users: [], characterCount: 0, error })
186
187
  return
187
188
  }
@@ -196,7 +197,7 @@ async function watch(
196
197
  try {
197
198
  await writeFile(typeDeclarationPath, typeDeclaration)
198
199
  } catch (error) {
199
- assert(error instanceof Error, "src/watch.ts:210:35")
200
+ assert(error instanceof Error, "src/watch.ts:215:35")
200
201
  if ("EISDIR" != error.code) throw error
201
202
  typeDeclarationPath = resolve(typeDeclarationPath, "player.d.ts")
202
203
  await writeFile(typeDeclarationPath, typeDeclaration)