@serverkgg/bridge 0.0.9 → 0.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/cli/validate.ts +1 -0
- package/src/context/bridgeContext.ts +8 -1
- package/src/core/bridgeKind.ts +1 -0
- package/src/core/bridgeProbeError.ts +16 -0
- package/src/core/bridgeSetup.ts +20 -0
- package/src/core/bridgeValue.test.ts +29 -0
- package/src/core/bridgeValue.ts +12 -0
- package/src/core/index.ts +2 -0
- package/src/guides/blocks.ts +8 -0
- package/src/guides/markdown.test.ts +18 -0
- package/src/guides/markdown.ts +7 -1
- package/src/manifest/manifest.test.ts +49 -2
- package/src/manifest/presence.ts +9 -1
- package/src/modules/bridge.ts +20 -0
- package/src/modules/bridgeDriver.ts +2 -0
- package/src/modules/bridgeModule.ts +7 -0
- package/src/panel/bridgeDetail.ts +5 -5
- package/src/probes/a2s.test.ts +71 -0
- package/src/probes/a2s.ts +38 -11
- package/src/protocol/bridgeFrame.ts +5 -0
- package/src/protocol/bridgeLimits.ts +2 -0
- package/src/protocol/bridgeManifest.ts +2 -0
- package/src/protocol/bridgeSetup.ts +97 -0
- package/src/protocol/index.ts +1 -0
- package/src/runtime/bridgeConfigReader.ts +12 -8
- package/src/runtime/bridgeContext.ts +28 -2
- package/src/runtime/bridgeDriver.test.ts +272 -2
- package/src/runtime/bridgeDriver.ts +82 -1
- package/src/runtime/bridgeEvents.test.ts +96 -0
- package/src/runtime/bridgeEvents.ts +4 -1
- package/src/runtime/bridgeRegistry.test.ts +38 -1
- package/src/runtime/bridgeRegistry.ts +14 -1
- package/src/runtime/bridgeSecrets.test.ts +121 -0
- package/src/runtime/bridgeSecrets.ts +43 -0
- package/src/runtime/bridgeServer.test.ts +262 -2
- package/src/runtime/bridgeServer.ts +17 -0
- package/src/runtime/bridgeSetup.test.ts +189 -0
- package/src/runtime/bridgeSetup.ts +63 -0
- package/src/runtime/bridgeSignal.test.ts +114 -0
- package/src/runtime/bridgeSignal.ts +31 -0
- package/src/runtime/bridgeSupervisor.test.ts +111 -0
- package/src/runtime/bridgeSupervisor.ts +37 -5
- package/src/runtime/serve.ts +52 -1
- package/src/utils/index.ts +1 -0
- package/src/utils/stripAnsi.ts +12 -0
- package/src/wire/panel.test.ts +93 -0
- package/src/wire/panel.ts +40 -0
package/src/runtime/serve.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { chmod, chown, mkdir, rm } from "node:fs/promises";
|
|
2
|
-
import { BridgeDesired, type BridgeInstallHold, BridgeRuntime, BridgeUserError } from "../core";
|
|
2
|
+
import { BridgeDesired, type BridgeInstallHold, BridgeRuntime, BridgeUserError, type LocalizedText } from "../core";
|
|
3
3
|
import type { BridgeDriver } from "../modules";
|
|
4
4
|
import {
|
|
5
5
|
BRIDGE_INSTALL_ERROR_LIMIT,
|
|
@@ -7,6 +7,8 @@ import {
|
|
|
7
7
|
type BridgeConfig,
|
|
8
8
|
type BridgePendingResult,
|
|
9
9
|
type BridgeSampleResult,
|
|
10
|
+
type BridgeSetupResult,
|
|
11
|
+
type BridgeSetupRuntime,
|
|
10
12
|
type BridgeStatus,
|
|
11
13
|
} from "../protocol";
|
|
12
14
|
import { configPath, readConfig, socketPath, statePath } from "./bridgeConfigReader";
|
|
@@ -18,6 +20,7 @@ import { errorText, log } from "./bridgeLog";
|
|
|
18
20
|
import { pendingResult, rejectedPending, unsupportedPending } from "./bridgePending";
|
|
19
21
|
import { createRegistry } from "./bridgeRegistry";
|
|
20
22
|
import { createServer } from "./bridgeServer";
|
|
23
|
+
import { failedSetup, setupSummaryOf, unsupportedSetup } from "./bridgeSetup";
|
|
21
24
|
import { readDesired, writeDesired } from "./bridgeState";
|
|
22
25
|
import { createSupervisor } from "./bridgeSupervisor";
|
|
23
26
|
|
|
@@ -67,8 +70,12 @@ export const serve = async (driver: BridgeDriver) => {
|
|
|
67
70
|
|
|
68
71
|
let installError: string | null = null;
|
|
69
72
|
|
|
73
|
+
let installErrorText: LocalizedText | null = null;
|
|
74
|
+
|
|
70
75
|
let installHold: BridgeInstallHold | null = null;
|
|
71
76
|
|
|
77
|
+
let setupRuntime: BridgeSetupRuntime | null = null;
|
|
78
|
+
|
|
72
79
|
const supervisor = createSupervisor({
|
|
73
80
|
cwd: config.cwd,
|
|
74
81
|
uid: config.gameUid,
|
|
@@ -98,6 +105,9 @@ export const serve = async (driver: BridgeDriver) => {
|
|
|
98
105
|
onEmit(event, payload) {
|
|
99
106
|
ring.emit(event, payload);
|
|
100
107
|
},
|
|
108
|
+
onSetup(runtime) {
|
|
109
|
+
setupRuntime = runtime;
|
|
110
|
+
},
|
|
101
111
|
});
|
|
102
112
|
|
|
103
113
|
const registry = createRegistry(driver, context, supervisor, ring);
|
|
@@ -113,7 +123,9 @@ export const serve = async (driver: BridgeDriver) => {
|
|
|
113
123
|
outOfMemory: supervisor.outOfMemory,
|
|
114
124
|
restarts: supervisor.restarts,
|
|
115
125
|
installError,
|
|
126
|
+
installErrorText,
|
|
116
127
|
installHold,
|
|
128
|
+
setup: setupSummaryOf(setupRuntime),
|
|
117
129
|
};
|
|
118
130
|
};
|
|
119
131
|
|
|
@@ -307,6 +319,44 @@ export const serve = async (driver: BridgeDriver) => {
|
|
|
307
319
|
|
|
308
320
|
return await pending();
|
|
309
321
|
},
|
|
322
|
+
onSetupStatus() {
|
|
323
|
+
if (!driver.setup) {
|
|
324
|
+
return unsupportedSetup();
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
supported: true,
|
|
329
|
+
runtime: setupRuntime,
|
|
330
|
+
};
|
|
331
|
+
},
|
|
332
|
+
async onSetupSubmit(step, action, args): Promise<BridgeSetupResult> {
|
|
333
|
+
const module = driver.setup;
|
|
334
|
+
|
|
335
|
+
if (!module?.submit) {
|
|
336
|
+
return unsupportedSetup();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
try {
|
|
340
|
+
await module.submit(context, step, action, args);
|
|
341
|
+
} catch (error) {
|
|
342
|
+
if (!(error instanceof BridgeUserError)) {
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
log.warn("setup submit rejected", {
|
|
347
|
+
step,
|
|
348
|
+
action,
|
|
349
|
+
error: error.message,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
return failedSetup(setupRuntime, error.text);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return {
|
|
356
|
+
supported: true,
|
|
357
|
+
runtime: setupRuntime,
|
|
358
|
+
};
|
|
359
|
+
},
|
|
310
360
|
});
|
|
311
361
|
|
|
312
362
|
await chmod(socketPath(config), SOCKET_MODE);
|
|
@@ -358,6 +408,7 @@ export const serve = async (driver: BridgeDriver) => {
|
|
|
358
408
|
log("install complete");
|
|
359
409
|
} catch (error) {
|
|
360
410
|
installError = errorText(error).slice(0, BRIDGE_INSTALL_ERROR_LIMIT);
|
|
411
|
+
installErrorText = error instanceof BridgeUserError ? error.text : null;
|
|
361
412
|
|
|
362
413
|
supervisor.setRuntime(BridgeRuntime.Crashed);
|
|
363
414
|
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const ANSI_PATTERN = new RegExp(
|
|
2
|
+
[
|
|
3
|
+
"\\u001B\\][^\\u0007\\u001B]*(?:\\u0007|\\u001B\\\\)",
|
|
4
|
+
"\\u001B\\[[0-9;?]*[ -/]*[@-~]",
|
|
5
|
+
"\\u001B[@-Z\\\\-_]",
|
|
6
|
+
].join("|"),
|
|
7
|
+
"g",
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
export const stripAnsi = (line: string): string => {
|
|
11
|
+
return line.replace(ANSI_PATTERN, "");
|
|
12
|
+
};
|
package/src/wire/panel.test.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { GuideOpenTab } from "@serverkgg/bridge/guides";
|
|
2
3
|
import {
|
|
3
4
|
BridgeAccess,
|
|
4
5
|
BridgeAction,
|
|
@@ -8,6 +9,8 @@ import {
|
|
|
8
9
|
BridgeKind,
|
|
9
10
|
BridgeLayout,
|
|
10
11
|
type BridgeManifest,
|
|
12
|
+
type BridgeSetupManifest,
|
|
13
|
+
BridgeSetupStepKind,
|
|
11
14
|
BridgeUploadMode,
|
|
12
15
|
} from "@serverkgg/bridge/protocol";
|
|
13
16
|
import { bridgeManifestSchema } from "./panel";
|
|
@@ -98,6 +101,7 @@ const manifest = (): BridgeManifest => ({
|
|
|
98
101
|
],
|
|
99
102
|
},
|
|
100
103
|
terminal: null,
|
|
104
|
+
setup: null,
|
|
101
105
|
modules: [
|
|
102
106
|
{
|
|
103
107
|
id: "broadcast",
|
|
@@ -159,6 +163,7 @@ const previewManifest = (): BridgeManifest => ({
|
|
|
159
163
|
],
|
|
160
164
|
},
|
|
161
165
|
terminal: null,
|
|
166
|
+
setup: null,
|
|
162
167
|
modules: [
|
|
163
168
|
{
|
|
164
169
|
id: "transition",
|
|
@@ -337,6 +342,7 @@ const detailManifest = (): BridgeManifest => ({
|
|
|
337
342
|
],
|
|
338
343
|
},
|
|
339
344
|
terminal: null,
|
|
345
|
+
setup: null,
|
|
340
346
|
modules: [
|
|
341
347
|
{
|
|
342
348
|
id: "modpackStatus",
|
|
@@ -412,6 +418,7 @@ const uploadManifest = (mode?: BridgeUploadMode): BridgeManifest => ({
|
|
|
412
418
|
],
|
|
413
419
|
},
|
|
414
420
|
terminal: null,
|
|
421
|
+
setup: null,
|
|
415
422
|
modules: [
|
|
416
423
|
{
|
|
417
424
|
id: "pending",
|
|
@@ -449,3 +456,89 @@ describe("the upload mirror", () => {
|
|
|
449
456
|
expect(bridgeManifestSchema.safeParse(source).success).toBe(false);
|
|
450
457
|
});
|
|
451
458
|
});
|
|
459
|
+
|
|
460
|
+
const setupManifest = (setup: BridgeSetupManifest | null): BridgeManifest => ({
|
|
461
|
+
version: "1.0.0",
|
|
462
|
+
panel: null,
|
|
463
|
+
terminal: null,
|
|
464
|
+
setup,
|
|
465
|
+
modules: [],
|
|
466
|
+
events: [],
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
describe("the setup mirror", () => {
|
|
470
|
+
test("carries every step kind through unchanged", () => {
|
|
471
|
+
const source = setupManifest({
|
|
472
|
+
steps: [
|
|
473
|
+
{
|
|
474
|
+
kind: BridgeSetupStepKind.Driver,
|
|
475
|
+
id: "sign-in",
|
|
476
|
+
title: text("Sign in"),
|
|
477
|
+
help: text("Sign in with your account"),
|
|
478
|
+
requiresRunning: true,
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
kind: BridgeSetupStepKind.Form,
|
|
482
|
+
id: "name",
|
|
483
|
+
title: text("Name your server"),
|
|
484
|
+
required: false,
|
|
485
|
+
tab: "settings",
|
|
486
|
+
section: "server",
|
|
487
|
+
fields: [
|
|
488
|
+
"SERVER_NAME",
|
|
489
|
+
],
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
kind: BridgeSetupStepKind.Open,
|
|
493
|
+
id: "invite",
|
|
494
|
+
title: text("Invite your friends"),
|
|
495
|
+
target: {
|
|
496
|
+
tab: GuideOpenTab.Access,
|
|
497
|
+
},
|
|
498
|
+
},
|
|
499
|
+
],
|
|
500
|
+
});
|
|
501
|
+
|
|
502
|
+
expect(bridgeManifestSchema.parse(source).setup).toEqual(source.setup);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
test("takes a manifest that declares no setup", () => {
|
|
506
|
+
expect(bridgeManifestSchema.parse(setupManifest(null)).setup).toBeNull();
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
test("defaults a manifest from an older bridge that carries no setup block to null", () => {
|
|
510
|
+
const older = setupManifest(null);
|
|
511
|
+
|
|
512
|
+
delete (older as Partial<BridgeManifest>).setup;
|
|
513
|
+
|
|
514
|
+
expect(bridgeManifestSchema.parse(older).setup).toBeNull();
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
test("refuses a step kind the platform does not know", () => {
|
|
518
|
+
const source = setupManifest({
|
|
519
|
+
steps: [
|
|
520
|
+
{
|
|
521
|
+
kind: "wizard" as BridgeSetupStepKind.Driver,
|
|
522
|
+
id: "sign-in",
|
|
523
|
+
title: text("Sign in"),
|
|
524
|
+
},
|
|
525
|
+
],
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
expect(bridgeManifestSchema.safeParse(source).success).toBe(false);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
test("refuses more steps than the platform takes", () => {
|
|
532
|
+
const source = setupManifest({
|
|
533
|
+
steps: Array.from({
|
|
534
|
+
length: 9,
|
|
535
|
+
}).map((_entry, index) => ({
|
|
536
|
+
kind: BridgeSetupStepKind.Driver as const,
|
|
537
|
+
id: `step-${index}`,
|
|
538
|
+
title: text(`Step ${index}`),
|
|
539
|
+
})),
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
expect(bridgeManifestSchema.safeParse(source).success).toBe(false);
|
|
543
|
+
});
|
|
544
|
+
});
|
package/src/wire/panel.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { guideOpenTargetSchema } from "@serverkgg/bridge/guides";
|
|
1
2
|
import {
|
|
2
3
|
BRIDGE_ID_PATTERN,
|
|
3
4
|
BRIDGE_MODULE_ID_PATTERN,
|
|
@@ -11,6 +12,9 @@ import {
|
|
|
11
12
|
type BridgeManifest,
|
|
12
13
|
type BridgePanel,
|
|
13
14
|
type BridgeSection,
|
|
15
|
+
type BridgeSetupManifest,
|
|
16
|
+
type BridgeSetupStep,
|
|
17
|
+
BridgeSetupStepKind,
|
|
14
18
|
BridgeStream,
|
|
15
19
|
type BridgeTerminalArg,
|
|
16
20
|
type BridgeTerminalCommand,
|
|
@@ -30,6 +34,10 @@ const SECTION_ACTIONS_LIMIT = 10;
|
|
|
30
34
|
|
|
31
35
|
const FORM_CLEARS_LIMIT = 8;
|
|
32
36
|
|
|
37
|
+
const SETUP_STEPS_LIMIT = 8;
|
|
38
|
+
|
|
39
|
+
const SETUP_STEP_FIELDS_LIMIT = 32;
|
|
40
|
+
|
|
33
41
|
const zKey = z
|
|
34
42
|
.string()
|
|
35
43
|
.min(1)
|
|
@@ -276,6 +284,37 @@ export const bridgeTerminalManifestSchema: z.ZodType<BridgeTerminalManifest> = z
|
|
|
276
284
|
rules: z.array(bridgeTerminalRuleSchema).max(64),
|
|
277
285
|
});
|
|
278
286
|
|
|
287
|
+
const setupStepBase = {
|
|
288
|
+
id: zBridgeId,
|
|
289
|
+
title: panelTextSchema,
|
|
290
|
+
help: panelTextSchema.optional(),
|
|
291
|
+
required: z.boolean().optional(),
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
export const bridgeSetupStepSchema: z.ZodType<BridgeSetupStep> = z.discriminatedUnion("kind", [
|
|
295
|
+
z.object({
|
|
296
|
+
...setupStepBase,
|
|
297
|
+
kind: z.literal(BridgeSetupStepKind.Driver),
|
|
298
|
+
requiresRunning: z.boolean().optional(),
|
|
299
|
+
}),
|
|
300
|
+
z.object({
|
|
301
|
+
...setupStepBase,
|
|
302
|
+
kind: z.literal(BridgeSetupStepKind.Form),
|
|
303
|
+
tab: zBridgeId,
|
|
304
|
+
section: zBridgeId,
|
|
305
|
+
fields: z.array(zKey).max(SETUP_STEP_FIELDS_LIMIT).optional(),
|
|
306
|
+
}),
|
|
307
|
+
z.object({
|
|
308
|
+
...setupStepBase,
|
|
309
|
+
kind: z.literal(BridgeSetupStepKind.Open),
|
|
310
|
+
target: guideOpenTargetSchema,
|
|
311
|
+
}),
|
|
312
|
+
]);
|
|
313
|
+
|
|
314
|
+
export const bridgeSetupManifestSchema: z.ZodType<BridgeSetupManifest> = z.object({
|
|
315
|
+
steps: z.array(bridgeSetupStepSchema).min(1).max(SETUP_STEPS_LIMIT),
|
|
316
|
+
});
|
|
317
|
+
|
|
279
318
|
export const bridgeModuleManifestSchema = z.object({
|
|
280
319
|
id: zBridgeModuleId,
|
|
281
320
|
kind: z.enum(BridgeKind),
|
|
@@ -290,6 +329,7 @@ export const bridgeManifestSchema: z.ZodType<BridgeManifest> = z.object({
|
|
|
290
329
|
version: z.string(),
|
|
291
330
|
panel: bridgePanelSchema.nullable(),
|
|
292
331
|
terminal: bridgeTerminalManifestSchema.nullable().default(null),
|
|
332
|
+
setup: bridgeSetupManifestSchema.nullable().default(null),
|
|
293
333
|
modules: z.array(bridgeModuleManifestSchema).max(32),
|
|
294
334
|
events: z.array(z.string().min(1).max(64)).max(32),
|
|
295
335
|
});
|