@serverkgg/bridge 0.0.8 → 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 +3 -3
- package/src/cli/validate.ts +2 -0
- package/src/context/bridgeContext.ts +17 -0
- package/src/context/index.ts +1 -0
- package/src/core/bridgeDownloadError.ts +10 -0
- package/src/core/bridgeHold.ts +12 -0
- package/src/core/bridgeKind.ts +3 -0
- package/src/core/bridgeProbeError.ts +16 -0
- package/src/core/bridgeSetup.ts +20 -0
- package/src/core/bridgeUserError.test.ts +23 -0
- package/src/core/bridgeUserError.ts +12 -0
- package/src/core/bridgeValue.test.ts +29 -0
- package/src/core/bridgeValue.ts +12 -0
- package/src/core/index.ts +5 -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/index.ts +1 -0
- package/src/manifest/manifest.test.ts +132 -0
- package/src/manifest/manifest.ts +21 -0
- package/src/manifest/presence.ts +56 -0
- package/src/manifest/primitives.ts +6 -0
- package/src/modules/bridge.ts +65 -2
- package/src/modules/bridgeDriver.ts +4 -0
- package/src/modules/bridgeModule.ts +32 -3
- package/src/panel/bridgeDetail.ts +49 -0
- package/src/panel/bridgeLayout.ts +1 -0
- package/src/panel/bridgePanel.ts +30 -0
- package/src/panel/bridgeUploadMode.ts +4 -0
- package/src/panel/index.ts +2 -0
- package/src/probes/a2s.test.ts +71 -0
- package/src/probes/a2s.ts +38 -11
- package/src/protocol/bridgeEgress.ts +8 -1
- package/src/protocol/bridgeEvents.ts +4 -0
- package/src/protocol/bridgeFrame.ts +33 -1
- package/src/protocol/bridgeLimits.ts +12 -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/bridgeActions.ts +7 -0
- package/src/runtime/bridgeConfigReader.ts +12 -8
- package/src/runtime/bridgeContext.ts +28 -2
- package/src/runtime/bridgeDriver.test.ts +748 -3
- package/src/runtime/bridgeDriver.ts +198 -4
- package/src/runtime/bridgeEgress.ts +3 -0
- package/src/runtime/bridgeEvents.test.ts +96 -0
- package/src/runtime/bridgeEvents.ts +11 -6
- package/src/runtime/bridgeFiles.test.ts +625 -15
- package/src/runtime/bridgeFiles.ts +152 -45
- package/src/runtime/bridgeInstall.test.ts +118 -0
- package/src/runtime/bridgeInstall.ts +55 -0
- package/src/runtime/bridgeNet.ts +32 -3
- package/src/runtime/bridgePending.test.ts +148 -0
- package/src/runtime/bridgePending.ts +43 -0
- package/src/runtime/bridgeRegistry.test.ts +172 -1
- package/src/runtime/bridgeRegistry.ts +40 -5
- package/src/runtime/bridgeSecrets.test.ts +121 -0
- package/src/runtime/bridgeSecrets.ts +43 -0
- package/src/runtime/bridgeServer.test.ts +598 -0
- package/src/runtime/bridgeServer.ts +42 -3
- 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 +55 -5
- package/src/runtime/bridgeZip.ts +179 -58
- package/src/runtime/serve.ts +115 -4
- package/src/utils/index.ts +1 -0
- package/src/utils/stripAnsi.ts +12 -0
- package/src/wire/panel.test.ts +259 -0
- package/src/wire/panel.ts +72 -0
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
BRIDGE_ID_PATTERN,
|
|
3
|
+
BRIDGE_MODULE_ID_PATTERN,
|
|
4
|
+
BridgeKind,
|
|
5
|
+
BridgeSetupStepKind,
|
|
6
|
+
type LocalizedText,
|
|
7
|
+
} from "../core";
|
|
8
|
+
import { GuideOpenTab } from "../guides";
|
|
2
9
|
import type { ManifestPort } from "../manifest";
|
|
3
10
|
import type { BridgeDriver, BridgePanelModule } from "../modules";
|
|
4
11
|
import {
|
|
@@ -8,6 +15,7 @@ import {
|
|
|
8
15
|
type BridgeSection,
|
|
9
16
|
type BridgeSectionAction,
|
|
10
17
|
type BridgeTab,
|
|
18
|
+
BridgeUploadMode,
|
|
11
19
|
type BridgeVisibleWhen,
|
|
12
20
|
} from "../panel";
|
|
13
21
|
import {
|
|
@@ -16,6 +24,7 @@ import {
|
|
|
16
24
|
BRIDGE_EVENTS_LIMIT,
|
|
17
25
|
BRIDGE_EXTENSION_LENGTH_LIMIT,
|
|
18
26
|
BRIDGE_FIELD_OPTIONS_LIMIT,
|
|
27
|
+
BRIDGE_FORM_CLEARS_LIMIT,
|
|
19
28
|
BRIDGE_FORM_FIELDS_LIMIT,
|
|
20
29
|
BRIDGE_ID_LENGTH_LIMIT,
|
|
21
30
|
BRIDGE_KEY_LENGTH_LIMIT,
|
|
@@ -26,6 +35,7 @@ import {
|
|
|
26
35
|
BRIDGE_PLACEHOLDER_LENGTH_LIMIT,
|
|
27
36
|
BRIDGE_ROW_ACTIONS_LIMIT,
|
|
28
37
|
BRIDGE_SECTION_ACTIONS_LIMIT,
|
|
38
|
+
BRIDGE_SETUP_STEPS_LIMIT,
|
|
29
39
|
BRIDGE_STAGING_LENGTH_LIMIT,
|
|
30
40
|
BRIDGE_TAB_SECTIONS_LIMIT,
|
|
31
41
|
BRIDGE_TABLE_COLUMNS_LIMIT,
|
|
@@ -35,13 +45,17 @@ import {
|
|
|
35
45
|
} from "../protocol";
|
|
36
46
|
import { actionsOf } from "./bridgeActions";
|
|
37
47
|
|
|
48
|
+
const UPLOAD_MODES = new Set<string>(Object.values(BridgeUploadMode));
|
|
49
|
+
|
|
38
50
|
const RESERVED_MODULE_IDS = new Set([
|
|
39
51
|
"announce",
|
|
40
52
|
"backup",
|
|
41
53
|
"events",
|
|
42
54
|
"install",
|
|
43
55
|
"lifecycle",
|
|
56
|
+
"pending",
|
|
44
57
|
"query",
|
|
58
|
+
"setup",
|
|
45
59
|
]);
|
|
46
60
|
|
|
47
61
|
export class BridgeDriverError extends Error {
|
|
@@ -259,6 +273,58 @@ const validateSection = (modules: Record<string, BridgePanelModule>, section: Br
|
|
|
259
273
|
countIn(section.fields.length, BRIDGE_FORM_FIELDS_LIMIT, "fields", where);
|
|
260
274
|
validateFields(modules, section.fields, where);
|
|
261
275
|
|
|
276
|
+
const clears = section.clears ?? [];
|
|
277
|
+
|
|
278
|
+
if (clears.length > 0 && section.target !== BridgeFormTarget.Variables) {
|
|
279
|
+
fail(`${where} clears variables but does not target variables`);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
countIn(clears.length, BRIDGE_FORM_CLEARS_LIMIT, "cleared variables", where);
|
|
283
|
+
|
|
284
|
+
const cleared = new Set<string>();
|
|
285
|
+
|
|
286
|
+
for (const clear of clears) {
|
|
287
|
+
if (cleared.has(clear.variable)) {
|
|
288
|
+
fail(`${where} clears "${clear.variable}" more than once`);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
cleared.add(clear.variable);
|
|
292
|
+
|
|
293
|
+
if (section.fields.some((field) => field.key === clear.variable)) {
|
|
294
|
+
fail(`${where} clears "${clear.variable}", which is one of its own fields`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
validateText(clear.warning, `clear "${clear.variable}" warning`, where);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
case BridgeLayout.Detail: {
|
|
304
|
+
const module = moduleOf(modules, section.module, where);
|
|
305
|
+
|
|
306
|
+
if (module.kind !== BridgeKind.Detail) {
|
|
307
|
+
fail(`${where} references "${section.module}", which is not a detail module`);
|
|
308
|
+
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
countIn((section.actions ?? []).length, BRIDGE_SECTION_ACTIONS_LIMIT, "actions", where);
|
|
313
|
+
validateActions(modules, section.actions ?? [], where);
|
|
314
|
+
|
|
315
|
+
for (const action of section.actions ?? []) {
|
|
316
|
+
if (action.download) {
|
|
317
|
+
fail(`${where} declares action "${action.id}" as a download, which a detail cannot serve`);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (!module.actions?.[action.id]) {
|
|
321
|
+
fail(`${where} declares action "${action.id}", which module "${section.module}" does not implement`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
validateText(section.confirmText, "confirm text", where);
|
|
326
|
+
validateText(section.related?.label, "related label", where);
|
|
327
|
+
|
|
262
328
|
return;
|
|
263
329
|
}
|
|
264
330
|
|
|
@@ -313,6 +379,10 @@ const validateSection = (modules: Record<string, BridgePanelModule>, section: Br
|
|
|
313
379
|
for (const extension of section.upload.extensions) {
|
|
314
380
|
lengthIn(extension, BRIDGE_EXTENSION_LENGTH_LIMIT, `upload extension "${extension}"`, where);
|
|
315
381
|
}
|
|
382
|
+
|
|
383
|
+
if (section.upload.mode !== undefined && !UPLOAD_MODES.has(section.upload.mode)) {
|
|
384
|
+
fail(`${where} declares an unknown upload mode "${section.upload.mode}"`);
|
|
385
|
+
}
|
|
316
386
|
}
|
|
317
387
|
}
|
|
318
388
|
|
|
@@ -346,6 +416,107 @@ const validateSection = (modules: Record<string, BridgePanelModule>, section: Br
|
|
|
346
416
|
}
|
|
347
417
|
};
|
|
348
418
|
|
|
419
|
+
const validatePending = (driver: BridgeDriver) => {
|
|
420
|
+
const pending = driver.pending;
|
|
421
|
+
|
|
422
|
+
if (!pending) {
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const where = "the pending module";
|
|
427
|
+
|
|
428
|
+
if (pending.kind !== BridgeKind.Pending) {
|
|
429
|
+
fail(`${where} declares kind "${pending.kind}", which is not "${BridgeKind.Pending}"`);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (pending.staging.length === 0) {
|
|
433
|
+
fail(`${where} declares no staging path`);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
lengthIn(pending.staging, BRIDGE_STAGING_LENGTH_LIMIT, "staging path", where);
|
|
437
|
+
|
|
438
|
+
if (pending.extensions.length === 0) {
|
|
439
|
+
fail(`${where} declares no extensions`);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
countIn(pending.extensions.length, BRIDGE_UPLOAD_EXTENSIONS_LIMIT, "extensions", where);
|
|
443
|
+
|
|
444
|
+
for (const extension of pending.extensions) {
|
|
445
|
+
lengthIn(extension, BRIDGE_EXTENSION_LENGTH_LIMIT, `extension "${extension}"`, where);
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
|
|
449
|
+
const formSectionOf = (driver: BridgeDriver, tabId: string, sectionId: string) => {
|
|
450
|
+
const tab = (driver.panel?.tabs ?? []).find((entry) => entry.id === tabId);
|
|
451
|
+
const section = (tab?.sections ?? []).find((entry) => entry.id === sectionId);
|
|
452
|
+
|
|
453
|
+
if (!section || section.layout !== BridgeLayout.Form) {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return section;
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
const validateSetup = (driver: BridgeDriver) => {
|
|
461
|
+
const setup = driver.setup;
|
|
462
|
+
|
|
463
|
+
if (!setup) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const where = "the setup module";
|
|
468
|
+
|
|
469
|
+
if (setup.kind !== BridgeKind.Setup) {
|
|
470
|
+
fail(`${where} declares kind "${setup.kind}", which is not "${BridgeKind.Setup}"`);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (setup.steps.length === 0) {
|
|
474
|
+
fail(`${where} declares no steps`);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
countIn(setup.steps.length, BRIDGE_SETUP_STEPS_LIMIT, "steps", where);
|
|
478
|
+
|
|
479
|
+
const ids = new Set<string>();
|
|
480
|
+
|
|
481
|
+
for (const step of setup.steps) {
|
|
482
|
+
if (ids.has(step.id)) {
|
|
483
|
+
fail(`${where} declares step "${step.id}" more than once`);
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
ids.add(step.id);
|
|
487
|
+
|
|
488
|
+
const at = `${where} step "${step.id}"`;
|
|
489
|
+
|
|
490
|
+
validateId(step.id, `${where} step`);
|
|
491
|
+
validateText(step.title, "title", at);
|
|
492
|
+
validateText(step.help, "help", at);
|
|
493
|
+
|
|
494
|
+
if (step.kind === BridgeSetupStepKind.Form) {
|
|
495
|
+
const section = formSectionOf(driver, step.tab, step.section);
|
|
496
|
+
|
|
497
|
+
if (!section) {
|
|
498
|
+
fail(`${at} names tab "${step.tab}" section "${step.section}", which is not a form section of this panel`);
|
|
499
|
+
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
for (const key of step.fields ?? []) {
|
|
504
|
+
if (!section.fields.some((field) => field.key === key)) {
|
|
505
|
+
fail(`${at} names field "${key}", which section "${step.section}" does not declare`);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (step.kind === BridgeSetupStepKind.Open) {
|
|
511
|
+
const target = step.target;
|
|
512
|
+
|
|
513
|
+
if (target.tab === GuideOpenTab.Panel && !(driver.panel?.tabs ?? []).some((tab) => tab.id === target.tabId)) {
|
|
514
|
+
fail(`${at} opens panel tab "${target.tabId}", which the panel does not declare`);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
|
|
349
520
|
const validateTab = (modules: Record<string, BridgePanelModule>, tab: BridgeTab) => {
|
|
350
521
|
const sections = new Set<string>();
|
|
351
522
|
|
|
@@ -382,6 +553,9 @@ export const validateDriver = (driver: BridgeDriver) => {
|
|
|
382
553
|
|
|
383
554
|
countIn(emitted.size, BRIDGE_EVENTS_LIMIT, "event names", "the driver");
|
|
384
555
|
|
|
556
|
+
validatePending(driver);
|
|
557
|
+
validateSetup(driver);
|
|
558
|
+
|
|
385
559
|
for (const [id, module] of Object.entries(modules)) {
|
|
386
560
|
validateModuleId(id, "module");
|
|
387
561
|
|
|
@@ -401,7 +575,7 @@ export const validateDriver = (driver: BridgeDriver) => {
|
|
|
401
575
|
}
|
|
402
576
|
}
|
|
403
577
|
|
|
404
|
-
if (module.kind === BridgeKind.Collection) {
|
|
578
|
+
if (module.kind === BridgeKind.Collection || module.kind === BridgeKind.Detail) {
|
|
405
579
|
for (const action of Object.keys(module.actions ?? {})) {
|
|
406
580
|
validateModuleId(action, `module "${id}" action`);
|
|
407
581
|
}
|
|
@@ -429,6 +603,26 @@ export const validateDriver = (driver: BridgeDriver) => {
|
|
|
429
603
|
|
|
430
604
|
validateTab(modules, tab);
|
|
431
605
|
}
|
|
606
|
+
|
|
607
|
+
const declared = declaredVariables(driver);
|
|
608
|
+
|
|
609
|
+
for (const tab of driver.panel.tabs) {
|
|
610
|
+
for (const section of tab.sections) {
|
|
611
|
+
const where = `tab "${tab.id}" section "${section.id}"`;
|
|
612
|
+
|
|
613
|
+
if (section.layout === BridgeLayout.Detail && section.related && !tabs.has(section.related.tab)) {
|
|
614
|
+
fail(`${where} relates to tab "${section.related.tab}", which the panel does not declare`);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (section.layout === BridgeLayout.Form) {
|
|
618
|
+
for (const clear of section.clears ?? []) {
|
|
619
|
+
if (!declared.has(clear.variable)) {
|
|
620
|
+
fail(`${where} clears "${clear.variable}", which no variables form, catalog or detail declares`);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
432
626
|
};
|
|
433
627
|
|
|
434
628
|
const declaredVariables = (driver: BridgeDriver) => {
|
|
@@ -442,7 +636,7 @@ const declaredVariables = (driver: BridgeDriver) => {
|
|
|
442
636
|
}
|
|
443
637
|
}
|
|
444
638
|
|
|
445
|
-
if (section.layout === BridgeLayout.Catalog && section.variable) {
|
|
639
|
+
if ((section.layout === BridgeLayout.Catalog || section.layout === BridgeLayout.Detail) && section.variable) {
|
|
446
640
|
declared.add(section.variable);
|
|
447
641
|
}
|
|
448
642
|
}
|
|
@@ -461,7 +655,7 @@ export const validatePortActivation = (ports: ManifestPort[], driver: BridgeDriv
|
|
|
461
655
|
}
|
|
462
656
|
|
|
463
657
|
fail(
|
|
464
|
-
`port "${port.key}" activeWhen[${index}] names "${rule.variable}", which no variables form or
|
|
658
|
+
`port "${port.key}" activeWhen[${index}] names "${rule.variable}", which no variables form, catalog or detail declares`,
|
|
465
659
|
);
|
|
466
660
|
}
|
|
467
661
|
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
BridgeEgressMethod,
|
|
8
8
|
type BridgeEgressRequest,
|
|
9
9
|
type BridgeEgressResponse,
|
|
10
|
+
type BridgeHttpMethod,
|
|
10
11
|
} from "../protocol";
|
|
11
12
|
|
|
12
13
|
const CONNECT_RETRY_MS = 10_000;
|
|
@@ -39,6 +40,8 @@ export class BridgeEgressCallError extends Error {
|
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
export interface BridgeEgressCallOptions {
|
|
43
|
+
httpMethod?: BridgeHttpMethod;
|
|
44
|
+
body?: string;
|
|
42
45
|
headers?: Record<string, string>;
|
|
43
46
|
timeoutMs?: number;
|
|
44
47
|
cache?: boolean;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { BridgeKind } from "../core";
|
|
6
|
+
import type { BridgeEventsModule } from "../modules";
|
|
7
|
+
import { BridgeEventName } from "../protocol";
|
|
8
|
+
import { createEventRing } from "./bridgeEvents";
|
|
9
|
+
import type { BridgeLeveledLogger } from "./bridgeLog";
|
|
10
|
+
import { createSupervisor } from "./bridgeSupervisor";
|
|
11
|
+
|
|
12
|
+
const RESET = "\u001B[m";
|
|
13
|
+
|
|
14
|
+
const PLAYER_JOINED = /^\[World\|(?<world>[^\]]+)\] Player '(?<player>[^']+)' joined world (?<target>\S+)$/;
|
|
15
|
+
|
|
16
|
+
const silent: BridgeLeveledLogger = Object.assign(() => {}, {
|
|
17
|
+
warn: () => {},
|
|
18
|
+
error: () => {},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const events: BridgeEventsModule = {
|
|
22
|
+
kind: BridgeKind.Events,
|
|
23
|
+
patterns: [
|
|
24
|
+
{
|
|
25
|
+
match: PLAYER_JOINED,
|
|
26
|
+
emit: BridgeEventName.PlayerJoined,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const emittedFor = async (line: string) => {
|
|
32
|
+
const dir = await mkdtemp(join(tmpdir(), "bridge-events-"));
|
|
33
|
+
const path = join(dir, "line.txt");
|
|
34
|
+
|
|
35
|
+
await Bun.write(path, `${line}\n`);
|
|
36
|
+
|
|
37
|
+
const supervisor = createSupervisor({
|
|
38
|
+
cwd: dir,
|
|
39
|
+
uid: process.getuid?.() ?? 0,
|
|
40
|
+
gid: process.getgid?.() ?? 0,
|
|
41
|
+
stopTimeoutSeconds: 1,
|
|
42
|
+
onLog: silent,
|
|
43
|
+
resolveCommand: async () => [
|
|
44
|
+
"bash",
|
|
45
|
+
"-c",
|
|
46
|
+
'cat "$0"; sleep 300',
|
|
47
|
+
path,
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const ring = createEventRing(events, supervisor);
|
|
52
|
+
|
|
53
|
+
await supervisor.start();
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
for (let waited = 0; waited < 5000; waited += 50) {
|
|
57
|
+
if (ring.drain(0).events.length > 0) {
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
await Bun.sleep(50);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return ring.drain(0).events;
|
|
65
|
+
} finally {
|
|
66
|
+
await supervisor.stop();
|
|
67
|
+
await rm(dir, {
|
|
68
|
+
force: true,
|
|
69
|
+
recursive: true,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const joined = {
|
|
75
|
+
seq: 1,
|
|
76
|
+
event: BridgeEventName.PlayerJoined,
|
|
77
|
+
payload: {
|
|
78
|
+
world: "default",
|
|
79
|
+
player: "Meslzy",
|
|
80
|
+
target: "default",
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
describe("matching a game line against the driver patterns", () => {
|
|
85
|
+
test("emits on the plain line", async () => {
|
|
86
|
+
expect(await emittedFor("[World|default] Player 'Meslzy' joined world default")).toEqual([
|
|
87
|
+
joined,
|
|
88
|
+
]);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("emits on the line hytale really prints, wrapped in ansi resets at both ends", async () => {
|
|
92
|
+
expect(await emittedFor(`${RESET}[World|default] Player 'Meslzy' joined world default${RESET}`)).toEqual([
|
|
93
|
+
joined,
|
|
94
|
+
]);
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { BridgeValues } from "../core";
|
|
2
2
|
import type { BridgeEventsModule } from "../modules";
|
|
3
3
|
import type { BridgeEvent } from "../protocol";
|
|
4
|
+
import { stripAnsi } from "../utils";
|
|
4
5
|
import type { BridgeSupervisor } from "./bridgeSupervisor";
|
|
5
6
|
|
|
6
7
|
const EVENT_BUFFER = 500;
|
|
@@ -26,22 +27,26 @@ export const createEventRing = (events: BridgeEventsModule | undefined, supervis
|
|
|
26
27
|
|
|
27
28
|
if (events) {
|
|
28
29
|
supervisor.subscribe((entry) => {
|
|
30
|
+
const line = stripAnsi(entry.line);
|
|
31
|
+
|
|
29
32
|
for (const pattern of events.patterns) {
|
|
30
33
|
if (pattern.stream && pattern.stream !== entry.stream) {
|
|
31
34
|
continue;
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
const match =
|
|
37
|
+
const match = line.match(pattern.match);
|
|
35
38
|
|
|
36
39
|
if (!match) {
|
|
37
40
|
continue;
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
const groups =
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const groups =
|
|
44
|
+
match.groups || pattern.payload
|
|
45
|
+
? {
|
|
46
|
+
...pattern.payload,
|
|
47
|
+
...match.groups,
|
|
48
|
+
}
|
|
49
|
+
: null;
|
|
45
50
|
|
|
46
51
|
push(pattern.emit, groups);
|
|
47
52
|
}
|