@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
@@ -1,9 +1,9 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { describe, expect, it } from "bun:test";
2
2
  import { tmpdir } from "node:os";
3
3
  import type { BridgeContext } from "../context";
4
4
  import { BridgeKind, BridgeSetupPromptKind, BridgeSetupStepKind, BridgeSetupStepState, BridgeUserError } from "../core";
5
5
  import type { BridgeDriver, BridgeSetupModule } from "../modules";
6
- import type { BridgeConfig, BridgeSetupRuntime } from "../protocol";
6
+ import { type BridgeConfig, BridgeEventName, type BridgeSetupRuntime } from "../protocol";
7
7
  import { createContext } from "./bridgeContext";
8
8
  import type { BridgeLeveledLogger } from "./bridgeLog";
9
9
  import { log } from "./bridgeLog";
@@ -51,6 +51,9 @@ const contextOf = (): BridgeContext => {
51
51
  onSetup() {
52
52
  return;
53
53
  },
54
+ onActivity() {
55
+ return;
56
+ },
54
57
  async onRestart() {
55
58
  return;
56
59
  },
@@ -137,7 +140,7 @@ const setupWith = (submit?: BridgeSetupModule["submit"]): BridgeSetupModule => (
137
140
  });
138
141
 
139
142
  describe("the setup handlers", () => {
140
- test("answer unsupported for a driver that declares no setup", async () => {
143
+ it("answer unsupported for a driver that declares no setup", async () => {
141
144
  const { logger } = recorder();
142
145
  const handlers = createSetupHandlers(driverWith(), logger);
143
146
 
@@ -146,13 +149,13 @@ describe("the setup handlers", () => {
146
149
  runtime: null,
147
150
  });
148
151
 
149
- expect(await handlers.onSetupSubmit(contextOf(), "sign-in", "renew", {})).toEqual({
152
+ expect((await handlers.onSetupSubmit(contextOf(), "sign-in", "renew", {})).result).toEqual({
150
153
  supported: false,
151
154
  runtime: null,
152
155
  });
153
156
  });
154
157
 
155
- test("keep nothing for a driver that declares no setup, whatever it reports", () => {
158
+ it("keep nothing for a driver that declares no setup, whatever it reports", () => {
156
159
  const { logger, warnings } = recorder();
157
160
  const handlers = createSetupHandlers(driverWith(), logger);
158
161
 
@@ -162,7 +165,7 @@ describe("the setup handlers", () => {
162
165
  expect(warnings).toEqual([]);
163
166
  });
164
167
 
165
- test("answer the normalized runtime the driver reported", () => {
168
+ it("answer the normalized runtime the driver reported", () => {
166
169
  const { logger, warnings } = recorder();
167
170
  const handlers = createSetupHandlers(driverWith(setupWith()), logger);
168
171
 
@@ -203,17 +206,17 @@ describe("the setup handlers", () => {
203
206
  ]);
204
207
  });
205
208
 
206
- test("answer unsupported for a setup that exports no submit", async () => {
209
+ it("answer unsupported for a setup that exports no submit", async () => {
207
210
  const { logger } = recorder();
208
211
  const handlers = createSetupHandlers(driverWith(setupWith()), logger);
209
212
 
210
- expect(await handlers.onSetupSubmit(contextOf(), "sign-in", "renew", {})).toEqual({
213
+ expect((await handlers.onSetupSubmit(contextOf(), "sign-in", "renew", {})).result).toEqual({
211
214
  supported: false,
212
215
  runtime: null,
213
216
  });
214
217
  });
215
218
 
216
- test("turn a user error into a failed prompt that keeps the previous actions", async () => {
219
+ it("turn a user error into a failed prompt that keeps the previous actions", async () => {
217
220
  const { logger, warnings } = recorder();
218
221
 
219
222
  const handlers = createSetupHandlers(
@@ -227,7 +230,7 @@ describe("the setup handlers", () => {
227
230
 
228
231
  handlers.onSetup(signIn());
229
232
 
230
- expect(await handlers.onSetupSubmit(contextOf(), "sign-in", "renew", {})).toEqual({
233
+ expect((await handlers.onSetupSubmit(contextOf(), "sign-in", "renew", {})).result).toEqual({
231
234
  supported: true,
232
235
  runtime: {
233
236
  steps: [
@@ -251,7 +254,66 @@ describe("the setup handlers", () => {
251
254
  expect(warnings.at(-1)?.message).toBe("setup submit rejected");
252
255
  });
253
256
 
254
- test("answer the runtime the driver reported while submitting", async () => {
257
+ it("add one rejected entry built from the user error", async () => {
258
+ const { logger } = recorder();
259
+
260
+ const handlers = createSetupHandlers(
261
+ driverWith(
262
+ setupWith(async () => {
263
+ throw new BridgeUserError({
264
+ ar: "الكود ما عاد ينفع",
265
+ en: "this code cannot be renewed any more",
266
+ });
267
+ }),
268
+ ),
269
+ logger,
270
+ );
271
+
272
+ handlers.onSetup(signIn());
273
+
274
+ const { activity } = await handlers.onSetupSubmit(contextOf(), "sign-in", "renew", {});
275
+
276
+ expect(activity).toEqual([
277
+ {
278
+ name: BridgeEventName.SetupRejected,
279
+ text: {
280
+ ar: "الكود ما عاد ينفع",
281
+ en: "this code cannot be renewed any more",
282
+ },
283
+ details: {
284
+ step: "sign-in",
285
+ action: "renew",
286
+ },
287
+ },
288
+ ]);
289
+ });
290
+
291
+ it("answer what the driver reported while submitting", async () => {
292
+ const { logger } = recorder();
293
+
294
+ const handlers = createSetupHandlers(
295
+ driverWith(
296
+ setupWith(async (context) => {
297
+ context.activity.report({
298
+ name: BridgeEventName.SetupCompleted,
299
+ text: text("signed in"),
300
+ });
301
+ }),
302
+ ),
303
+ logger,
304
+ );
305
+
306
+ const { activity } = await handlers.onSetupSubmit(contextOf(), "sign-in", "renew", {});
307
+
308
+ expect(activity).toEqual([
309
+ {
310
+ name: BridgeEventName.SetupCompleted,
311
+ text: text("signed in"),
312
+ },
313
+ ]);
314
+ });
315
+
316
+ it("answer the runtime the driver reported while submitting", async () => {
255
317
  const { logger } = recorder();
256
318
 
257
319
  const handlers = createSetupHandlers(
@@ -273,7 +335,7 @@ describe("the setup handlers", () => {
273
335
 
274
336
  handlers.onSetup(signIn());
275
337
 
276
- expect(await handlers.onSetupSubmit(contextOf(), "sign-in", "renew", {})).toEqual({
338
+ expect((await handlers.onSetupSubmit(contextOf(), "sign-in", "renew", {})).result).toEqual({
277
339
  supported: true,
278
340
  runtime: {
279
341
  steps: [
@@ -287,7 +349,7 @@ describe("the setup handlers", () => {
287
349
  });
288
350
  });
289
351
 
290
- test("let an error the driver cannot explain out", async () => {
352
+ it("let an error the driver cannot explain out", async () => {
291
353
  const { logger } = recorder();
292
354
 
293
355
  const handlers = createSetupHandlers(
@@ -310,7 +372,7 @@ describe("the setup handlers redaction", () => {
310
372
 
311
373
  const redact = (value: string) => value.split(SECRET).join("••••");
312
374
 
313
- test("blanks a secret value in the prompt a driver reports", () => {
375
+ it("blanks a secret value in the prompt a driver reports", () => {
314
376
  const { logger } = recorder();
315
377
  const handlers = createSetupHandlers(driverWith(setupWith()), logger, redact);
316
378
 
@@ -337,7 +399,7 @@ describe("the setup handlers redaction", () => {
337
399
  });
338
400
  });
339
401
 
340
- test("blanks a secret value in every text of a link prompt", () => {
402
+ it("blanks a secret value in every text of a link prompt", () => {
341
403
  const { logger } = recorder();
342
404
  const handlers = createSetupHandlers(driverWith(setupWith()), logger, redact);
343
405
  const reported = signIn();
@@ -367,7 +429,7 @@ describe("the setup handlers redaction", () => {
367
429
  });
368
430
  });
369
431
 
370
- test("blanks a secret value in the text of a rejected submit", async () => {
432
+ it("blanks a secret value in the text of a rejected submit", async () => {
371
433
  const { logger } = recorder();
372
434
  const handlers = createSetupHandlers(
373
435
  driverWith(
@@ -381,11 +443,12 @@ describe("the setup handlers redaction", () => {
381
443
 
382
444
  handlers.onSetup(signIn());
383
445
 
384
- const result = await handlers.onSetupSubmit(contextOf(), "sign-in", "renew", {});
446
+ const { result, activity } = await handlers.onSetupSubmit(contextOf(), "sign-in", "renew", {});
385
447
 
386
448
  expect(result.runtime?.prompt).toMatchObject({
387
449
  kind: BridgeSetupPromptKind.Failed,
388
450
  message: text("telegram refused ••••"),
389
451
  });
452
+ expect(JSON.stringify(activity)).not.toContain(SECRET);
390
453
  });
391
454
  });
@@ -1,15 +1,22 @@
1
1
  import type { BridgeContext } from "../context";
2
2
  import { BridgeUserError, type BridgeValues } from "../core";
3
3
  import type { BridgeDriver } from "../modules";
4
- import type { BridgeSetupResult, BridgeSetupRuntime } from "../protocol";
5
- import type { BridgeLeveledLogger } from "./bridgeLog";
6
- import { type BridgeRedact, redactLocalized, redactSetupRuntime } from "./bridgeRedaction";
4
+ import { BridgeEventName, type BridgeSetupResult, type BridgeSetupRuntime } from "../protocol";
5
+ import { type ActivityNormalizeOptions, type BridgeActivityAnswer, createActivityScope } from "./bridgeActivity";
6
+ import { type BridgeLeveledLogger, errorDetail } from "./bridgeLog";
7
+ import { type BridgeRedact, redactLocalized, redactSetupRuntime, secretFieldKeys } from "./bridgeRedaction";
8
+ import { scopedContext } from "./bridgeScopedContext";
7
9
  import { failedSetup, normalizeSetupRuntime, unsupportedSetup } from "./bridgeSetup";
8
10
 
9
11
  export interface BridgeSetupHandlers {
10
12
  onSetup(runtime: BridgeSetupRuntime | null): void;
11
13
  onSetupStatus(): BridgeSetupResult;
12
- onSetupSubmit(context: BridgeContext, step: string, action: string, args: BridgeValues): Promise<BridgeSetupResult>;
14
+ onSetupSubmit(
15
+ context: BridgeContext,
16
+ step: string,
17
+ action: string,
18
+ args: BridgeValues,
19
+ ): Promise<BridgeActivityAnswer<BridgeSetupResult>>;
13
20
  current(): BridgeSetupRuntime | null;
14
21
  }
15
22
 
@@ -22,6 +29,12 @@ export const createSetupHandlers = (
22
29
  ): BridgeSetupHandlers => {
23
30
  const declared: ReadonlySet<string> = new Set((driver.setup?.steps ?? []).map((step) => step.id));
24
31
 
32
+ const activity: ActivityNormalizeOptions = {
33
+ secretKeys: new Set(secretFieldKeys(driver)),
34
+ redact,
35
+ warn: log.warn,
36
+ };
37
+
25
38
  let current: BridgeSetupRuntime | null = null;
26
39
 
27
40
  return {
@@ -53,28 +66,50 @@ export const createSetupHandlers = (
53
66
  const module = driver.setup;
54
67
 
55
68
  if (!module?.submit) {
56
- return unsupportedSetup();
69
+ return {
70
+ result: unsupportedSetup(),
71
+ activity: [],
72
+ };
57
73
  }
58
74
 
75
+ const scope = createActivityScope(context.activity, activity);
76
+
59
77
  try {
60
- await module.submit(context, step, action, args);
78
+ await module.submit(scopedContext(context, scope.reporter), step, action, args);
61
79
  } catch (error) {
62
80
  if (!(error instanceof BridgeUserError)) {
81
+ scope.drain();
82
+
63
83
  throw error;
64
84
  }
65
85
 
66
86
  log.warn("setup submit rejected", {
67
87
  step,
68
88
  action,
69
- error: error.message,
89
+ error: errorDetail(error),
70
90
  });
71
91
 
72
- return failedSetup(current, redactLocalized(error.text, redact));
92
+ scope.reporter.report({
93
+ name: BridgeEventName.SetupRejected,
94
+ text: error.text,
95
+ details: {
96
+ step,
97
+ action,
98
+ },
99
+ });
100
+
101
+ return {
102
+ result: failedSetup(current, redactLocalized(error.text, redact)),
103
+ activity: scope.close(),
104
+ };
73
105
  }
74
106
 
75
107
  return {
76
- supported: true,
77
- runtime: current,
108
+ result: {
109
+ supported: true,
110
+ runtime: current,
111
+ },
112
+ activity: scope.close(),
78
113
  };
79
114
  },
80
115
  };
@@ -1,17 +1,17 @@
1
- import { describe, expect, test } from "bun:test";
1
+ import { describe, expect, it } from "bun:test";
2
2
  import { leadsGroup, readProcessGroup, signalGroup } from "./bridgeSignal";
3
3
 
4
4
  const MISSING_PID = 2 ** 22 - 1;
5
5
 
6
6
  describe("readProcessGroup", () => {
7
- test("reads the process group of this process", async () => {
7
+ it("reads the process group of this process", async () => {
8
8
  const group = await readProcessGroup(process.pid);
9
9
 
10
10
  expect(group).not.toBeNull();
11
11
  expect(group).toBeGreaterThan(0);
12
12
  });
13
13
 
14
- test("puts a detached child in a group of its own and an attached one in ours", async () => {
14
+ it("puts a detached child in a group of its own and an attached one in ours", async () => {
15
15
  const detached = Bun.spawn(
16
16
  [
17
17
  "sleep",
@@ -49,13 +49,13 @@ describe("readProcessGroup", () => {
49
49
  }
50
50
  });
51
51
 
52
- test("answers null for a pid that does not exist", async () => {
52
+ it("answers null for a pid that does not exist", async () => {
53
53
  expect(await readProcessGroup(MISSING_PID)).toBeNull();
54
54
  });
55
55
  });
56
56
 
57
57
  describe("leadsGroup", () => {
58
- test("only a pid equal to its own group leads it", () => {
58
+ it("only a pid equal to its own group leads it", () => {
59
59
  expect(leadsGroup(1234, 1234)).toBe(true);
60
60
  expect(leadsGroup(1234, 1200)).toBe(false);
61
61
  expect(leadsGroup(1234, null)).toBe(false);
@@ -63,7 +63,7 @@ describe("leadsGroup", () => {
63
63
  });
64
64
 
65
65
  describe("signalGroup", () => {
66
- test("signals every process sharing the leader's group", async () => {
66
+ it("signals every process sharing the leader's group", async () => {
67
67
  const leader = Bun.spawn(
68
68
  [
69
69
  "bash",
@@ -90,7 +90,7 @@ describe("signalGroup", () => {
90
90
  expect(() => process.kill(child, 0)).toThrow();
91
91
  }, 20_000);
92
92
 
93
- test("refuses to signal a group the pid does not lead", async () => {
93
+ it("refuses to signal a group the pid does not lead", async () => {
94
94
  const attached = Bun.spawn(
95
95
  [
96
96
  "sleep",