@serverkgg/bridge 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/package.json +3 -2
  2. package/rules/package.md +60 -0
  3. package/src/blog/articleFrontmatter.test.ts +10 -10
  4. package/src/blog/compile.test.ts +9 -9
  5. package/src/bot/botDeploy.test.ts +16 -16
  6. package/src/bot/botEnvironment.test.ts +8 -8
  7. package/src/bot/botIdentity.test.ts +12 -12
  8. package/src/bot/botPanel.test.ts +41 -11
  9. package/src/bot/botPanel.ts +13 -5
  10. package/src/bot/botState.test.ts +3 -3
  11. package/src/bot/botTerminal.test.ts +5 -5
  12. package/src/bot/botVariables.test.ts +8 -8
  13. package/src/bot/detect/botToken.test.ts +6 -6
  14. package/src/bot/detect/detect.test.ts +21 -21
  15. package/src/bot/detect/detectTable.test.ts +5 -5
  16. package/src/bot/detect/envExample.test.ts +2 -2
  17. package/src/bot/detect/envScan.test.ts +4 -4
  18. package/src/bot/detect/inferBotPlatform.test.ts +4 -4
  19. package/src/bot/detect/keys.test.ts +3 -3
  20. package/src/bot/detect/sensitiveKey.test.ts +5 -5
  21. package/src/bot/detect/serverkJson.test.ts +5 -5
  22. package/src/catalogs/curseforge.test.ts +35 -35
  23. package/src/catalogs/thunderstore.test.ts +9 -9
  24. package/src/cli/detectTable.test.ts +3 -3
  25. package/src/cli/link.test.ts +11 -11
  26. package/src/cli/main.ts +8 -0
  27. package/src/cli/rules.test.ts +198 -0
  28. package/src/cli/rules.ts +188 -0
  29. package/src/cli/validate.test.ts +123 -29
  30. package/src/cli/validate.ts +37 -1
  31. package/src/codecs/ini.test.ts +13 -13
  32. package/src/codecs/javaProperties.test.ts +11 -11
  33. package/src/codecs/sourceCfg.test.ts +14 -14
  34. package/src/codecs/xmlProperties.test.ts +10 -10
  35. package/src/context/bridgeContext.ts +6 -1
  36. package/src/core/bridgeAction.ts +2 -0
  37. package/src/core/bridgeActivity.ts +9 -0
  38. package/src/core/bridgeAvailability.ts +10 -0
  39. package/src/core/bridgePort.test.ts +6 -6
  40. package/src/core/bridgeUserError.test.ts +13 -3
  41. package/src/core/bridgeUserError.ts +6 -2
  42. package/src/core/bridgeValue.test.ts +5 -5
  43. package/src/core/describeDownloadFailure.test.ts +58 -0
  44. package/src/core/describeDownloadFailure.ts +69 -0
  45. package/src/core/index.ts +3 -0
  46. package/src/guides/markdown.test.ts +31 -31
  47. package/src/install/installStamp.test.ts +9 -9
  48. package/src/manifest/extension.ts +36 -0
  49. package/src/manifest/index.ts +1 -0
  50. package/src/manifest/manifest.test.ts +208 -90
  51. package/src/manifest/manifest.ts +34 -0
  52. package/src/modules/bridge.ts +8 -1
  53. package/src/modules/bridgeModule.ts +13 -1
  54. package/src/net/loopback.test.ts +10 -10
  55. package/src/panel/bridgeDetail.ts +2 -1
  56. package/src/presence/rosterSync.test.ts +7 -7
  57. package/src/probes/a2s.test.ts +5 -5
  58. package/src/probes/minecraftPing.test.ts +17 -17
  59. package/src/probes/raknetPing.test.ts +13 -13
  60. package/src/protocol/bridgeActivity.ts +54 -0
  61. package/src/protocol/bridgeEvents.ts +30 -0
  62. package/src/protocol/bridgeFrame.ts +10 -1
  63. package/src/protocol/index.ts +1 -0
  64. package/src/rcon/rconAccess.test.ts +20 -20
  65. package/src/rcon/rconAccess.ts +13 -13
  66. package/src/rcon/rconClient.test.ts +49 -49
  67. package/src/rcon/rconFrame.test.ts +14 -14
  68. package/src/rcon/rconIds.test.ts +4 -4
  69. package/src/rcon/rconWriter.test.ts +6 -6
  70. package/src/runtime/bridgeActions.ts +19 -2
  71. package/src/runtime/bridgeActivity.test.ts +318 -0
  72. package/src/runtime/bridgeActivity.ts +268 -0
  73. package/src/runtime/bridgeBackupLease.test.ts +9 -9
  74. package/src/runtime/bridgeConfigReader.test.ts +5 -5
  75. package/src/runtime/bridgeConsoleHandler.test.ts +8 -8
  76. package/src/runtime/bridgeContext.test.ts +11 -5
  77. package/src/runtime/bridgeContext.ts +8 -1
  78. package/src/runtime/bridgeDriver.test.ts +114 -93
  79. package/src/runtime/bridgeDriver.ts +10 -7
  80. package/src/runtime/bridgeEvents.test.ts +15 -15
  81. package/src/runtime/bridgeFiles.test.ts +54 -54
  82. package/src/runtime/bridgeInstall.test.ts +10 -10
  83. package/src/runtime/bridgeLiveConfig.test.ts +6 -6
  84. package/src/runtime/bridgeLog.ts +10 -0
  85. package/src/runtime/bridgePending.test.ts +6 -6
  86. package/src/runtime/bridgeRedaction.test.ts +16 -16
  87. package/src/runtime/bridgeRefusalError.ts +13 -0
  88. package/src/runtime/bridgeRegistry.test.ts +1077 -34
  89. package/src/runtime/bridgeRegistry.ts +464 -65
  90. package/src/runtime/bridgeScopedContext.ts +55 -0
  91. package/src/runtime/bridgeServer.test.ts +424 -24
  92. package/src/runtime/bridgeServer.ts +125 -35
  93. package/src/runtime/bridgeSetup.test.ts +27 -27
  94. package/src/runtime/bridgeSetup.ts +1 -1
  95. package/src/runtime/bridgeSetupHandlers.test.ts +80 -17
  96. package/src/runtime/bridgeSetupHandlers.ts +45 -10
  97. package/src/runtime/bridgeSignal.test.ts +7 -7
  98. package/src/runtime/bridgeSupervisor.test.ts +42 -42
  99. package/src/runtime/serve.ts +45 -3
  100. package/src/steam/avatars.test.ts +13 -13
  101. package/src/steam/steamApp.test.ts +8 -8
  102. package/src/steam/steamcmd.test.ts +44 -13
  103. package/src/steam/steamcmd.ts +27 -4
  104. package/src/steam/steamcmdNarrator.test.ts +4 -4
  105. package/src/steam/workshop.test.ts +5 -5
  106. package/src/utils/changedSettingKeys.test.ts +65 -0
  107. package/src/utils/changedSettingKeys.ts +6 -0
  108. package/src/utils/describeExecFailure.test.ts +34 -0
  109. package/src/utils/describeExecFailure.ts +9 -0
  110. package/src/utils/index.ts +3 -0
  111. package/src/utils/sameSettingValue.test.ts +35 -0
  112. package/src/utils/sameSettingValue.ts +40 -0
  113. package/src/utils/token.test.ts +7 -7
  114. package/src/wine/protonCommand.test.ts +5 -5
  115. package/src/wine/wineCommand.test.ts +6 -6
  116. package/src/wine/winePrefix.test.ts +6 -6
  117. package/src/wire/activity.test.ts +95 -0
  118. package/src/wire/activity.ts +41 -0
  119. package/src/wire/index.ts +1 -0
  120. package/src/wire/panel.test.ts +91 -25
  121. package/src/wire/panel.ts +11 -0
@@ -3,6 +3,7 @@ import {
3
3
  BRIDGE_MODULE_ID_PATTERN,
4
4
  BridgeKind,
5
5
  BridgeSetupStepKind,
6
+ isReadAction,
6
7
  type LocalizedText,
7
8
  } from "../core";
8
9
  import { GuideOpenTab } from "../guides";
@@ -801,15 +802,17 @@ export const validateDriver = (driver: BridgeDriver) => {
801
802
  countIn((module.dependsOn ?? []).length, BRIDGE_DEPENDS_ON_LIMIT, "dependencies", `module "${id}"`);
802
803
  }
803
804
 
804
- if (module.kind === BridgeKind.Actions) {
805
- for (const action of Object.keys(module.actions)) {
806
- validateModuleId(action, `module "${id}" action`);
807
- }
808
- }
809
-
810
- if (module.kind === BridgeKind.Collection || module.kind === BridgeKind.Detail) {
805
+ if (
806
+ module.kind === BridgeKind.Actions
807
+ || module.kind === BridgeKind.Collection
808
+ || module.kind === BridgeKind.Detail
809
+ ) {
811
810
  for (const action of Object.keys(module.actions ?? {})) {
812
811
  validateModuleId(action, `module "${id}" action`);
812
+
813
+ if (isReadAction(action)) {
814
+ fail(`module "${id}" declares action "${action}", which is reserved for reads — pick another id`);
815
+ }
813
816
  }
814
817
  }
815
818
  }
@@ -1,4 +1,4 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { describe, expect, it } from "bun:test";
2
2
  import { mkdtemp, rm } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
@@ -162,19 +162,19 @@ const joined = {
162
162
  };
163
163
 
164
164
  describe("matching a game line against the driver patterns", () => {
165
- test("emits on the plain line", async () => {
165
+ it("emits on the plain line", async () => {
166
166
  expect(await emittedFor("[World|default] Player 'Meslzy' joined world default")).toEqual([
167
167
  joined,
168
168
  ]);
169
169
  });
170
170
 
171
- test("emits on the line hytale really prints, wrapped in ansi resets at both ends", async () => {
171
+ it("emits on the line hytale really prints, wrapped in ansi resets at both ends", async () => {
172
172
  expect(await emittedFor(`${RESET}[World|default] Player 'Meslzy' joined world default${RESET}`)).toEqual([
173
173
  joined,
174
174
  ]);
175
175
  });
176
176
 
177
- test("holds a matched diagnostic line as evidence and spends it on the crash record", async () => {
177
+ it("holds a matched diagnostic line as evidence and spends it on the crash record", async () => {
178
178
  const { held, events: drained } = await crashAfter(
179
179
  "Exception in mod journeymap from journeymap-5.9.jar",
180
180
  crashRecord({
@@ -205,7 +205,7 @@ describe("matching a game line against the driver patterns", () => {
205
205
  });
206
206
 
207
207
  describe("the crash record the ring builds", () => {
208
- test("holds evidence out of the ring and lets every other event drain", () => {
208
+ it("holds evidence out of the ring and lets every other event drain", () => {
209
209
  const ring = idleRing();
210
210
 
211
211
  ring.emit(BridgeEventName.ModCrashed, {
@@ -221,7 +221,7 @@ describe("the crash record the ring builds", () => {
221
221
  ]);
222
222
  });
223
223
 
224
- test("prefers the oom kill over any evidence it holds", () => {
224
+ it("prefers the oom kill over any evidence it holds", () => {
225
225
  const ring = idleRing();
226
226
 
227
227
  ring.emit(BridgeEventName.ModCrashed, {
@@ -246,7 +246,7 @@ describe("the crash record the ring builds", () => {
246
246
  });
247
247
  });
248
248
 
249
- test("follows the evidence precedence when more than one name is held", () => {
249
+ it("follows the evidence precedence when more than one name is held", () => {
250
250
  const ring = idleRing();
251
251
 
252
252
  ring.emit(BridgeEventName.ModCrashed, {
@@ -271,7 +271,7 @@ describe("the crash record the ring builds", () => {
271
271
  });
272
272
  });
273
273
 
274
- test("lets the out of memory log pattern outrank the java version", () => {
274
+ it("lets the out of memory log pattern outrank the java version", () => {
275
275
  const ring = idleRing();
276
276
 
277
277
  ring.emit(BridgeEventName.WrongJavaVersion, {
@@ -290,7 +290,7 @@ describe("the crash record the ring builds", () => {
290
290
  });
291
291
  });
292
292
 
293
- test("never lets the evidence overwrite the record keys", () => {
293
+ it("never lets the evidence overwrite the record keys", () => {
294
294
  const ring = idleRing();
295
295
 
296
296
  ring.emit(BridgeEventName.ModCrashed, {
@@ -310,7 +310,7 @@ describe("the crash record the ring builds", () => {
310
310
  });
311
311
  });
312
312
 
313
- test("clears the evidence once it is spent", () => {
313
+ it("clears the evidence once it is spent", () => {
314
314
  const ring = idleRing();
315
315
 
316
316
  ring.emit(BridgeEventName.ModCrashed, {
@@ -332,7 +332,7 @@ describe("the crash record the ring builds", () => {
332
332
  });
333
333
  });
334
334
 
335
- test("carries the last line only when nothing explains the crash", () => {
335
+ it("carries the last line only when nothing explains the crash", () => {
336
336
  const ring = idleRing();
337
337
 
338
338
  ring.emit(BridgeEventName.ModCrashed, {
@@ -360,7 +360,7 @@ describe("the crash record the ring builds", () => {
360
360
  });
361
361
  });
362
362
 
363
- test("keeps listing the evidence names the driver declares", () => {
363
+ it("keeps listing the evidence names the driver declares", () => {
364
364
  expect(idleRing().names()).toEqual([
365
365
  BridgeEventName.PlayerJoined,
366
366
  BridgeEventName.ModCrashed,
@@ -433,7 +433,7 @@ const crashedEvent = async (ring: BridgeEventRing) => {
433
433
  };
434
434
 
435
435
  describe("the evidence a run may spend", () => {
436
- test("spends nothing printed before the run became stable on a running crash", async () => {
436
+ it("spends nothing printed before the run became stable on a running crash", async () => {
437
437
  const wiring = await wired(EVIDENCE_BEFORE_READY, READY_LINE);
438
438
 
439
439
  await wiring.supervisor.start();
@@ -452,7 +452,7 @@ describe("the evidence a run may spend", () => {
452
452
  }
453
453
  }, 20_000);
454
454
 
455
- test("spends what the run printed after it became stable", async () => {
455
+ it("spends what the run printed after it became stable", async () => {
456
456
  const wiring = await wired(EVIDENCE_AFTER_READY, READY_LINE);
457
457
 
458
458
  await wiring.supervisor.start();
@@ -470,7 +470,7 @@ describe("the evidence a run may spend", () => {
470
470
  }
471
471
  }, 20_000);
472
472
 
473
- test("drops what it holds when the stop was requested", async () => {
473
+ it("drops what it holds when the stop was requested", async () => {
474
474
  const wiring = await wired(EVIDENCE_THEN_IDLE);
475
475
 
476
476
  await wiring.supervisor.start();
@@ -1,4 +1,4 @@
1
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
1
+ import { afterEach, beforeEach, describe, expect, it } from "bun:test";
2
2
  import { mkdtemp, rm } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
4
  import { crc32, deflateRawSync } from "node:zlib";
@@ -205,7 +205,7 @@ describe("extract", () => {
205
205
  });
206
206
  });
207
207
 
208
- test("flattens paths, skips dotfiles and filters extensions by default", async () => {
208
+ it("flattens paths, skips dotfiles and filters extensions by default", async () => {
209
209
  const path = await archive([
210
210
  {
211
211
  name: "overrides/mods/alpha.jar",
@@ -234,7 +234,7 @@ describe("extract", () => {
234
234
  expect(await read("mods/alpha.jar")).toBe("alpha");
235
235
  });
236
236
 
237
- test("preflights duplicate paths, parent collisions and expansion before writing", async () => {
237
+ it("preflights duplicate paths, parent collisions and expansion before writing", async () => {
238
238
  for (const entries of [
239
239
  [
240
240
  {
@@ -285,7 +285,7 @@ describe("extract", () => {
285
285
  expect(await Bun.file(`${root}/staging/a.txt`).exists()).toBe(false);
286
286
  });
287
287
 
288
- test("allows a bounded per-entry runtime limit without bypassing archive limits", async () => {
288
+ it("allows a bounded per-entry runtime limit without bypassing archive limits", async () => {
289
289
  const path = await archive([
290
290
  {
291
291
  name: "runtime",
@@ -323,7 +323,7 @@ describe("extract", () => {
323
323
  expect(await read("staging/runtime")).toBe("12345678");
324
324
  });
325
325
 
326
- test("refuses unsafe unselected entries instead of accepting a partly safe archive", async () => {
326
+ it("refuses unsafe unselected entries instead of accepting a partly safe archive", async () => {
327
327
  const path = await archive([
328
328
  {
329
329
  name: "safe/a.txt",
@@ -343,7 +343,7 @@ describe("extract", () => {
343
343
  expect(await Bun.file(`${root}/staging/a.txt`).exists()).toBe(false);
344
344
  });
345
345
 
346
- test("flattens paths with no options at all", async () => {
346
+ it("flattens paths with no options at all", async () => {
347
347
  const path = await archive([
348
348
  {
349
349
  name: "nested/deep/alpha.jar",
@@ -363,7 +363,7 @@ describe("extract", () => {
363
363
  ]);
364
364
  });
365
365
 
366
- test("keeps relative paths and creates intermediate directories in tree mode", async () => {
366
+ it("keeps relative paths and creates intermediate directories in tree mode", async () => {
367
367
  const path = await archive([
368
368
  {
369
369
  name: "mods/alpha.jar",
@@ -388,7 +388,7 @@ describe("extract", () => {
388
388
  expect(await read("staging/config/deep/beta.json")).toBe("beta");
389
389
  });
390
390
 
391
- test("keeps the dotfile skip in tree mode", async () => {
391
+ it("keeps the dotfile skip in tree mode", async () => {
392
392
  const path = await archive([
393
393
  {
394
394
  name: "mods/alpha.jar",
@@ -413,7 +413,7 @@ describe("extract", () => {
413
413
  ]);
414
414
  });
415
415
 
416
- test("rejects absolute entries in tree mode", async () => {
416
+ it("rejects absolute entries in tree mode", async () => {
417
417
  const path = await archive([
418
418
  {
419
419
  name: "/etc/passwd",
@@ -428,7 +428,7 @@ describe("extract", () => {
428
428
  ).rejects.toThrow("absolute path");
429
429
  });
430
430
 
431
- test("rejects escaping entries in tree mode", async () => {
431
+ it("rejects escaping entries in tree mode", async () => {
432
432
  const path = await archive([
433
433
  {
434
434
  name: "overrides/../../escape.json",
@@ -443,7 +443,7 @@ describe("extract", () => {
443
443
  ).rejects.toThrow("is not allowed");
444
444
  });
445
445
 
446
- test("rejects symlink entries in tree mode", async () => {
446
+ it("rejects symlink entries in tree mode", async () => {
447
447
  const path = await archive([
448
448
  {
449
449
  name: "mods/link.jar",
@@ -459,7 +459,7 @@ describe("extract", () => {
459
459
  ).rejects.toThrow("symlink");
460
460
  });
461
461
 
462
- test("extracts only the selected prefix and strips it", async () => {
462
+ it("extracts only the selected prefix and strips it", async () => {
463
463
  const path = await archive([
464
464
  {
465
465
  name: "overrides/mods/alpha.jar",
@@ -486,7 +486,7 @@ describe("extract", () => {
486
486
  expect(await read("alpha.jar")).toBe("alpha");
487
487
  });
488
488
 
489
- test("composes select with tree", async () => {
489
+ it("composes select with tree", async () => {
490
490
  const path = await archive([
491
491
  {
492
492
  name: "overrides/mods/alpha.jar",
@@ -515,7 +515,7 @@ describe("extract", () => {
515
515
  expect(await read("config/deep/beta.json")).toBe("beta");
516
516
  });
517
517
 
518
- test("fails when nothing matches", async () => {
518
+ it("fails when nothing matches", async () => {
519
519
  const path = await archive([
520
520
  {
521
521
  name: "overrides/mods/alpha.jar",
@@ -531,7 +531,7 @@ describe("extract", () => {
531
531
  ).rejects.toThrow("contained no matching files");
532
532
  });
533
533
 
534
- test("takes every selected prefix in one pass and strips the one that matched", async () => {
534
+ it("takes every selected prefix in one pass and strips the one that matched", async () => {
535
535
  const path = await archive([
536
536
  {
537
537
  name: "overrides/mods/alpha.jar",
@@ -564,7 +564,7 @@ describe("extract", () => {
564
564
  expect(await read("config/beta.json")).toBe("beta");
565
565
  });
566
566
 
567
- test("keeps dotfiles in a tree when asked and never writes .git or .serverk", async () => {
567
+ it("keeps dotfiles in a tree when asked and never writes .git or .serverk", async () => {
568
568
  const path = await archive([
569
569
  {
570
570
  name: "bot/.env.example",
@@ -605,7 +605,7 @@ describe("extract", () => {
605
605
  expect(await files.exists(".serverk/deploy-requested")).toBe(false);
606
606
  });
607
607
 
608
- test("still skips dotfiles in a tree unless asked", async () => {
608
+ it("still skips dotfiles in a tree unless asked", async () => {
609
609
  const path = await archive([
610
610
  {
611
611
  name: ".env.example",
@@ -626,7 +626,7 @@ describe("extract", () => {
626
626
  ]);
627
627
  });
628
628
 
629
- test("takes a single named entry when the select carries no trailing slash", async () => {
629
+ it("takes a single named entry when the select carries no trailing slash", async () => {
630
630
  const path = await archive([
631
631
  {
632
632
  name: "modrinth.index.json",
@@ -652,7 +652,7 @@ describe("extract", () => {
652
652
  expect(await read("staging/modrinth.index.json")).toBe("{}");
653
653
  });
654
654
 
655
- test("drops excluded paths after the prefix is stripped", async () => {
655
+ it("drops excluded paths after the prefix is stripped", async () => {
656
656
  const path = await archive([
657
657
  {
658
658
  name: "overrides/mods/alpha.jar",
@@ -686,7 +686,7 @@ describe("extract", () => {
686
686
  expect(await Bun.file(`${root}/shaderpacks/heavy.zip`).exists()).toBe(false);
687
687
  });
688
688
 
689
- test("answers with nothing when a select matches nothing", async () => {
689
+ it("answers with nothing when a select matches nothing", async () => {
690
690
  const path = await archive([
691
691
  {
692
692
  name: "overrides/mods/alpha.jar",
@@ -704,7 +704,7 @@ describe("extract", () => {
704
704
  expect(written).toEqual([]);
705
705
  });
706
706
 
707
- test("inflates a deflated entry", async () => {
707
+ it("inflates a deflated entry", async () => {
708
708
  const path = await archive([
709
709
  {
710
710
  name: "mods/alpha.jar",
@@ -722,7 +722,7 @@ describe("extract", () => {
722
722
  expect(await read("mods/alpha.jar")).toBe("alpha the deflated");
723
723
  });
724
724
 
725
- test("rejects stored data corruption before writing the destination", async () => {
725
+ it("rejects stored data corruption before writing the destination", async () => {
726
726
  const data = buildZip([
727
727
  {
728
728
  name: "alpha.jar",
@@ -735,7 +735,7 @@ describe("extract", () => {
735
735
  expect(await Bun.file(`${root}/mods/alpha.jar`).exists()).toBe(false);
736
736
  });
737
737
 
738
- test("rejects deflated data with a valid size but incorrect checksum", async () => {
738
+ it("rejects deflated data with a valid size but incorrect checksum", async () => {
739
739
  const path = await archive([
740
740
  {
741
741
  name: "alpha.jar",
@@ -747,7 +747,7 @@ describe("extract", () => {
747
747
  expect(await Bun.file(`${root}/mods/alpha.jar`).exists()).toBe(false);
748
748
  });
749
749
 
750
- test("rejects local and central compression method disagreement", async () => {
750
+ it("rejects local and central compression method disagreement", async () => {
751
751
  const data = buildZip([
752
752
  {
753
753
  name: "alpha.jar",
@@ -759,7 +759,7 @@ describe("extract", () => {
759
759
  await expect(files.extract("corrupt.zip", "mods")).rejects.toThrow("method");
760
760
  });
761
761
 
762
- test("rejects local names that differ from the central directory", async () => {
762
+ it("rejects local names that differ from the central directory", async () => {
763
763
  const data = buildZip([
764
764
  {
765
765
  name: "alpha.jar",
@@ -771,7 +771,7 @@ describe("extract", () => {
771
771
  await expect(files.extract("corrupt.zip", "mods")).rejects.toThrow("local name");
772
772
  });
773
773
 
774
- test("rejects a local variable header that crosses into the central directory", async () => {
774
+ it("rejects a local variable header that crosses into the central directory", async () => {
775
775
  const data = buildZip([
776
776
  {
777
777
  name: "alpha.jar",
@@ -783,7 +783,7 @@ describe("extract", () => {
783
783
  await expect(files.extract("corrupt.zip", "mods")).rejects.toThrow("bounds");
784
784
  });
785
785
 
786
- test("rejects compressed data ranges that cross the central directory", async () => {
786
+ it("rejects compressed data ranges that cross the central directory", async () => {
787
787
  const data = buildZip([
788
788
  {
789
789
  name: "alpha.jar",
@@ -797,7 +797,7 @@ describe("extract", () => {
797
797
  await expect(files.extract("corrupt.zip", "mods")).rejects.toThrow("bounds");
798
798
  });
799
799
 
800
- test("accepts descriptor entries with zero local checksum and validates the central checksum", async () => {
800
+ it("accepts descriptor entries with zero local checksum and validates the central checksum", async () => {
801
801
  const path = await archive(
802
802
  [
803
803
  {
@@ -814,7 +814,7 @@ describe("extract", () => {
814
814
  expect(await read("mods/alpha.jar")).toBe("alpha");
815
815
  });
816
816
 
817
- test("rejects local checksum disagreement", async () => {
817
+ it("rejects local checksum disagreement", async () => {
818
818
  const data = buildZip([
819
819
  {
820
820
  name: "alpha.jar",
@@ -826,7 +826,7 @@ describe("extract", () => {
826
826
  await expect(files.extract("corrupt.zip", "mods")).rejects.toThrow("local checksum");
827
827
  });
828
828
 
829
- test("rejects local compressed-size disagreement", async () => {
829
+ it("rejects local compressed-size disagreement", async () => {
830
830
  const data = buildZip([
831
831
  {
832
832
  name: "alpha.jar",
@@ -838,7 +838,7 @@ describe("extract", () => {
838
838
  await expect(files.extract("corrupt.zip", "mods")).rejects.toThrow("local sizes");
839
839
  });
840
840
 
841
- test("reads a zip64 archive through its locator and extended field", async () => {
841
+ it("reads a zip64 archive through its locator and extended field", async () => {
842
842
  const path = await archive(
843
843
  [
844
844
  {
@@ -870,7 +870,7 @@ describe("extract", () => {
870
870
  expect(await read("config/beta.json")).toBe("beta");
871
871
  });
872
872
 
873
- test("takes a suffix select from anywhere in the archive", async () => {
873
+ it("takes a suffix select from anywhere in the archive", async () => {
874
874
  const path = await archive([
875
875
  {
876
876
  name: "BMC5 Server/mods/alpha.jar",
@@ -902,7 +902,7 @@ describe("extract", () => {
902
902
  expect(await read("staging/beta.jar")).toBe("beta");
903
903
  });
904
904
 
905
- test("keeps the suffix path under the destination in tree mode", async () => {
905
+ it("keeps the suffix path under the destination in tree mode", async () => {
906
906
  const path = await archive([
907
907
  {
908
908
  name: "BMC5 Server/mods/alpha.jar",
@@ -922,7 +922,7 @@ describe("extract", () => {
922
922
  ]);
923
923
  });
924
924
 
925
- test("never inflates an entry it did not select", async () => {
925
+ it("never inflates an entry it did not select", async () => {
926
926
  const entries: ArchiveEntry[] = [
927
927
  {
928
928
  name: "BMC5 Server/mods/alpha.jar",
@@ -956,7 +956,7 @@ describe("extract", () => {
956
956
  ).rejects.toThrow();
957
957
  });
958
958
 
959
- test("answers with nothing when every match is excluded", async () => {
959
+ it("answers with nothing when every match is excluded", async () => {
960
960
  const path = await archive([
961
961
  {
962
962
  name: "overrides/shaderpacks/heavy.zip",
@@ -997,7 +997,7 @@ describe("list", () => {
997
997
  recursive: true,
998
998
  });
999
999
  });
1000
- test("filters before the result cap and scopes metadata beyond a large runtime tree", async () => {
1000
+ it("filters before the result cap and scopes metadata beyond a large runtime tree", async () => {
1001
1001
  for (let start = 0; start < 4097; start += 64) {
1002
1002
  await Promise.all(
1003
1003
  Array.from(
@@ -1027,7 +1027,7 @@ describe("list", () => {
1027
1027
  ]);
1028
1028
  await expect(files.list("**/*")).rejects.toThrow("result limit");
1029
1029
  });
1030
- test("nonrecursive globs ignore unrelated deep trees and respect wildcard directory boundaries", async () => {
1030
+ it("nonrecursive globs ignore unrelated deep trees and respect wildcard directory boundaries", async () => {
1031
1031
  await files.write("server.jar", "jar");
1032
1032
  await files.write("server.properties", "settings");
1033
1033
  await files.write("mods/custom/plugin.jar", "plugin");
@@ -1048,7 +1048,7 @@ describe("list", () => {
1048
1048
  "server.jar",
1049
1049
  ]);
1050
1050
  });
1051
- test("prunes subtrees past the depth limit instead of failing the whole listing", async () => {
1051
+ it("prunes subtrees past the depth limit instead of failing the whole listing", async () => {
1052
1052
  const nested = Array.from(
1053
1053
  {
1054
1054
  length: 14,
@@ -1078,7 +1078,7 @@ describe("list", () => {
1078
1078
  "resource_packs/pack/pack_icon.png",
1079
1079
  ]);
1080
1080
  });
1081
- test("warns about what a pruned listing left out", async () => {
1081
+ it("warns about what a pruned listing left out", async () => {
1082
1082
  const lines: string[] = [];
1083
1083
  const noisy = createBridgeFiles({
1084
1084
  root,
@@ -1109,7 +1109,7 @@ describe("list", () => {
1109
1109
  "file listing skipped directories deeper than its depth limit",
1110
1110
  ]);
1111
1111
  });
1112
- test("refuses unsafe listing roots", async () => {
1112
+ it("refuses unsafe listing roots", async () => {
1113
1113
  await expect(
1114
1114
  files.list("**/*", {
1115
1115
  directory: "../outside",
@@ -1142,7 +1142,7 @@ describe("digest", () => {
1142
1142
  });
1143
1143
  });
1144
1144
 
1145
- test("answers with the known vectors for abc", async () => {
1145
+ it("answers with the known vectors for abc", async () => {
1146
1146
  await Bun.write(`${root}/mods/alpha.jar`, "abc");
1147
1147
 
1148
1148
  expect(await files.digest("mods/alpha.jar", "sha1")).toBe("a9993e364706816aba3e25717850c26c9cd0d89d");
@@ -1156,13 +1156,13 @@ describe("digest", () => {
1156
1156
  );
1157
1157
  });
1158
1158
 
1159
- test("answers with the known vector for an empty file", async () => {
1159
+ it("answers with the known vector for an empty file", async () => {
1160
1160
  await Bun.write(`${root}/empty.jar`, "");
1161
1161
 
1162
1162
  expect(await files.digest("empty.jar", "sha1")).toBe("da39a3ee5e6b4b0d3255bfef95601890afd80709");
1163
1163
  });
1164
1164
 
1165
- test("hashes a file larger than one read chunk", async () => {
1165
+ it("hashes a file larger than one read chunk", async () => {
1166
1166
  const content = "a".repeat(4 * 1024 * 1024 + 17);
1167
1167
 
1168
1168
  await Bun.write(`${root}/big.bin`, content);
@@ -1170,14 +1170,14 @@ describe("digest", () => {
1170
1170
  expect(await files.digest("big.bin", "sha256")).toBe(new Bun.CryptoHasher("sha256").update(content).digest("hex"));
1171
1171
  });
1172
1172
 
1173
- test("refuses a path that is not a readable file", async () => {
1173
+ it("refuses a path that is not a readable file", async () => {
1174
1174
  await files.ensure("mods");
1175
1175
 
1176
1176
  await expect(files.digest("mods", "sha1")).rejects.toThrow("is not a readable file");
1177
1177
  await expect(files.digest("mods/missing.jar", "sha1")).rejects.toThrow("is not a readable file");
1178
1178
  });
1179
1179
 
1180
- test("refuses a path outside the server directory", async () => {
1180
+ it("refuses a path outside the server directory", async () => {
1181
1181
  await expect(files.digest("../escape.jar", "sha1")).rejects.toThrow("is not allowed");
1182
1182
  });
1183
1183
  });
@@ -1243,7 +1243,7 @@ describe("download", () => {
1243
1243
  });
1244
1244
  });
1245
1245
 
1246
- test("keeps a file the source undersized when the checksum passes", async () => {
1246
+ it("keeps a file the source undersized when the checksum passes", async () => {
1247
1247
  const content = "a mod jar the index measured wrong";
1248
1248
 
1249
1249
  await filesServing(content).download("mods/alpha.jar", "https://cdn.example.com/alpha.jar", {
@@ -1257,7 +1257,7 @@ describe("download", () => {
1257
1257
  ]);
1258
1258
  });
1259
1259
 
1260
- test("refuses a file whose checksum does not match, however right its size", async () => {
1260
+ it("refuses a file whose checksum does not match, however right its size", async () => {
1261
1261
  const content = "a mod jar somebody swapped";
1262
1262
 
1263
1263
  await expect(
@@ -1270,7 +1270,7 @@ describe("download", () => {
1270
1270
  expect(await Bun.file(`${root}/mods/alpha.jar`).exists()).toBe(false);
1271
1271
  });
1272
1272
 
1273
- test("holds an unverified file to its declared size", async () => {
1273
+ it("holds an unverified file to its declared size", async () => {
1274
1274
  const content = "a mod jar with no checksum to trust";
1275
1275
 
1276
1276
  await expect(
@@ -1282,7 +1282,7 @@ describe("download", () => {
1282
1282
  expect(await Bun.file(`${root}/mods/alpha.jar`).exists()).toBe(false);
1283
1283
  });
1284
1284
 
1285
- test("holds an unverified file that stops short of its declared size", async () => {
1285
+ it("holds an unverified file that stops short of its declared size", async () => {
1286
1286
  const content = "a mod jar with no checksum to trust";
1287
1287
 
1288
1288
  await expect(
@@ -1292,7 +1292,7 @@ describe("download", () => {
1292
1292
  ).rejects.toThrow(`returned ${Buffer.byteLength(content)} bytes, expected ${Buffer.byteLength(content) + 2}`);
1293
1293
  });
1294
1294
 
1295
- test("carries the upstream status when the egress channel refuses", async () => {
1295
+ it("carries the upstream status when the egress channel refuses", async () => {
1296
1296
  const refusing: BridgeEgress = {
1297
1297
  fetch() {
1298
1298
  throw new Error("the egress channel is not part of this test");
@@ -1323,7 +1323,7 @@ describe("download", () => {
1323
1323
  expect(thrown instanceof BridgeDownloadError ? thrown.status : null).toBe(429);
1324
1324
  });
1325
1325
 
1326
- test("narrates a file it downloads unless the caller asks otherwise", async () => {
1326
+ it("narrates a file it downloads unless the caller asks otherwise", async () => {
1327
1327
  const content = "a server jar worth watching";
1328
1328
 
1329
1329
  await filesServing(content).download("server.jar", "https://cdn.example.com/server.jar", {
@@ -1337,7 +1337,7 @@ describe("download", () => {
1337
1337
  ]);
1338
1338
  });
1339
1339
 
1340
- test("says nothing about a quiet file", async () => {
1340
+ it("says nothing about a quiet file", async () => {
1341
1341
  const content = "a mod jar in a batch of hundreds";
1342
1342
 
1343
1343
  await filesServing(content).download("mods/alpha.jar", "https://cdn.example.com/alpha.jar", {
@@ -1350,7 +1350,7 @@ describe("download", () => {
1350
1350
  expect(messages).toEqual([]);
1351
1351
  });
1352
1352
 
1353
- test("still warns and still fails on a quiet file", async () => {
1353
+ it("still warns and still fails on a quiet file", async () => {
1354
1354
  const content = "a mod jar the index measured wrong";
1355
1355
 
1356
1356
  await filesServing(content).download("mods/alpha.jar", "https://cdn.example.com/alpha.jar", {
@@ -1373,7 +1373,7 @@ describe("download", () => {
1373
1373
  expect(messages).toEqual([]);
1374
1374
  });
1375
1375
 
1376
- test("keeps no warning when the declared size is right", async () => {
1376
+ it("keeps no warning when the declared size is right", async () => {
1377
1377
  const content = "a mod jar the index measured right";
1378
1378
 
1379
1379
  await filesServing(content).download("mods/alpha.jar", "https://cdn.example.com/alpha.jar", {