@serverkgg/bridge 0.1.8 → 0.1.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/rules/package.md +1 -0
- package/src/bot/botCauses.test.ts +88 -0
- package/src/bot/botCauses.ts +12 -0
- package/src/bot/botDeploy.test.ts +32 -0
- package/src/bot/botDeploy.ts +6 -0
- package/src/bot/botEnvironment.test.ts +218 -1
- package/src/bot/botEnvironment.ts +85 -3
- package/src/bot/botVariables.test.ts +32 -0
- package/src/bot/botVariables.ts +9 -0
- package/src/bot/detect/corpus/mruniquehacker-knightbot-md.json +35 -0
- package/src/bot/detect/detect.test.ts +22 -0
- package/src/bot/detect/keys.test.ts +50 -0
- package/src/bot/detect/keys.ts +45 -1
- package/src/cli/validate.test.ts +33 -3
- package/src/cli/validate.ts +13 -5
- package/src/guides/markdown.test.ts +118 -0
- package/src/guides/markdown.ts +24 -2
- package/src/manifest/manifest.test.ts +229 -0
- package/src/manifest/manifest.ts +109 -1
- package/src/protocol/bridgeCrash.ts +10 -0
- package/src/protocol/bridgeEvents.ts +2 -0
- package/src/runtime/bridgeDriver.test.ts +24 -0
- package/src/runtime/bridgeDriver.ts +6 -2
- package/src/runtime/bridgeEvents.test.ts +13 -0
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { describe, expect, it } from "bun:test";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import {
|
|
4
|
+
BOT_PORT_1,
|
|
5
|
+
BOT_PORT_1_KEY,
|
|
6
|
+
BOT_PORT_2,
|
|
7
|
+
BOT_PORT_2_KEY,
|
|
8
|
+
BOT_SWITCH_ON,
|
|
9
|
+
BOT_WEB,
|
|
10
|
+
BOT_WEB_PORT_KEY,
|
|
11
|
+
} from "../bot/detect";
|
|
3
12
|
import { PORT_BLOCK_MAX_COUNT, PortJoin, PortProtocol } from "../core";
|
|
4
13
|
import { GameCategory } from "./category";
|
|
5
14
|
import { ManifestExtensionKind, ManifestExtensionSource } from "./extension";
|
|
@@ -1766,6 +1775,146 @@ describe("port pairs and blocks", () => {
|
|
|
1766
1775
|
});
|
|
1767
1776
|
});
|
|
1768
1777
|
|
|
1778
|
+
describe("web ports", () => {
|
|
1779
|
+
const gamePort = () => {
|
|
1780
|
+
const [game] = base().container.ports;
|
|
1781
|
+
|
|
1782
|
+
if (!game) {
|
|
1783
|
+
throw new Error("fixture is missing its port");
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
return game;
|
|
1787
|
+
};
|
|
1788
|
+
|
|
1789
|
+
const webPort = (key = "web", min = 31_000) => ({
|
|
1790
|
+
key,
|
|
1791
|
+
containerPort: 8080,
|
|
1792
|
+
protocol: PortProtocol.Tcp,
|
|
1793
|
+
hostRange: {
|
|
1794
|
+
default: min,
|
|
1795
|
+
min,
|
|
1796
|
+
max: min + 99,
|
|
1797
|
+
},
|
|
1798
|
+
web: true,
|
|
1799
|
+
});
|
|
1800
|
+
|
|
1801
|
+
it("reads a port from a bundle published before the web flag as not the web port", () => {
|
|
1802
|
+
expect(manifestSchema.parse(base()).container.ports[0]?.web).toBe(false);
|
|
1803
|
+
});
|
|
1804
|
+
|
|
1805
|
+
it("keeps the one web port a package declares", () => {
|
|
1806
|
+
const manifest = manifestSchema.parse(
|
|
1807
|
+
withPorts([
|
|
1808
|
+
gamePort(),
|
|
1809
|
+
webPort(),
|
|
1810
|
+
]),
|
|
1811
|
+
);
|
|
1812
|
+
|
|
1813
|
+
expect(manifest.container.ports.map((port) => port.web)).toEqual([
|
|
1814
|
+
false,
|
|
1815
|
+
true,
|
|
1816
|
+
]);
|
|
1817
|
+
});
|
|
1818
|
+
|
|
1819
|
+
it("refuses a second web port", () => {
|
|
1820
|
+
expect(
|
|
1821
|
+
failure(
|
|
1822
|
+
withPorts([
|
|
1823
|
+
gamePort(),
|
|
1824
|
+
webPort(),
|
|
1825
|
+
webPort("panel", 31_100),
|
|
1826
|
+
]),
|
|
1827
|
+
),
|
|
1828
|
+
).toEqual([
|
|
1829
|
+
{
|
|
1830
|
+
path: "container.ports.2.web",
|
|
1831
|
+
message: 'port "web" is already the web port — a package declares at most one',
|
|
1832
|
+
},
|
|
1833
|
+
]);
|
|
1834
|
+
});
|
|
1835
|
+
|
|
1836
|
+
for (const protocol of [
|
|
1837
|
+
PortProtocol.Udp,
|
|
1838
|
+
PortProtocol.TcpUdp,
|
|
1839
|
+
]) {
|
|
1840
|
+
it(`refuses a web port carrying ${protocol}`, () => {
|
|
1841
|
+
expect(
|
|
1842
|
+
failure(
|
|
1843
|
+
withPorts([
|
|
1844
|
+
gamePort(),
|
|
1845
|
+
{
|
|
1846
|
+
...webPort(),
|
|
1847
|
+
protocol,
|
|
1848
|
+
},
|
|
1849
|
+
]),
|
|
1850
|
+
),
|
|
1851
|
+
).toEqual([
|
|
1852
|
+
{
|
|
1853
|
+
path: "container.ports.1.protocol",
|
|
1854
|
+
message: `a web port carries tcp only, not ${protocol}`,
|
|
1855
|
+
},
|
|
1856
|
+
]);
|
|
1857
|
+
});
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
it("refuses a web port that is a block", () => {
|
|
1861
|
+
expect(
|
|
1862
|
+
failure(
|
|
1863
|
+
withPorts([
|
|
1864
|
+
gamePort(),
|
|
1865
|
+
{
|
|
1866
|
+
...webPort(),
|
|
1867
|
+
count: 2,
|
|
1868
|
+
},
|
|
1869
|
+
]),
|
|
1870
|
+
),
|
|
1871
|
+
).toEqual([
|
|
1872
|
+
{
|
|
1873
|
+
path: "container.ports.1.count",
|
|
1874
|
+
message: "a web port is a single port, not a block of 2",
|
|
1875
|
+
},
|
|
1876
|
+
]);
|
|
1877
|
+
});
|
|
1878
|
+
|
|
1879
|
+
it("refuses srv, follows and mirror on a web port", () => {
|
|
1880
|
+
expect(
|
|
1881
|
+
failure(
|
|
1882
|
+
withPorts([
|
|
1883
|
+
gamePort(),
|
|
1884
|
+
{
|
|
1885
|
+
...webPort(),
|
|
1886
|
+
srv: {
|
|
1887
|
+
service: "_http",
|
|
1888
|
+
},
|
|
1889
|
+
follows: {
|
|
1890
|
+
key: "game",
|
|
1891
|
+
offset: 1,
|
|
1892
|
+
},
|
|
1893
|
+
mirror: true,
|
|
1894
|
+
},
|
|
1895
|
+
]),
|
|
1896
|
+
),
|
|
1897
|
+
).toEqual([
|
|
1898
|
+
{
|
|
1899
|
+
path: "container.ports.1.srv",
|
|
1900
|
+
message: "a web port is reached only by the server's name and takes no srv",
|
|
1901
|
+
},
|
|
1902
|
+
{
|
|
1903
|
+
path: "container.ports.1.follows",
|
|
1904
|
+
message: "a web port is reached only by the server's name and takes no follows",
|
|
1905
|
+
},
|
|
1906
|
+
{
|
|
1907
|
+
path: "container.ports.1.mirror",
|
|
1908
|
+
message: "a web port is reached only by the server's name and takes no mirror",
|
|
1909
|
+
},
|
|
1910
|
+
]);
|
|
1911
|
+
});
|
|
1912
|
+
|
|
1913
|
+
it("renders its description into the JSON schema", () => {
|
|
1914
|
+
expect(JSON.stringify(buildJsonSchema())).toContain("reached at the server's name over HTTPS");
|
|
1915
|
+
});
|
|
1916
|
+
});
|
|
1917
|
+
|
|
1769
1918
|
const botBase = () => ({
|
|
1770
1919
|
schemaVersion: MANIFEST_SCHEMA_VERSION,
|
|
1771
1920
|
kind: PackageKind.Bot,
|
|
@@ -1928,6 +2077,86 @@ describe("bot packages", () => {
|
|
|
1928
2077
|
expect(manifest.container.ports).toHaveLength(1);
|
|
1929
2078
|
});
|
|
1930
2079
|
|
|
2080
|
+
it("keep a web port and two raw ports the author turns on", () => {
|
|
2081
|
+
const switched = (variable: string) => [
|
|
2082
|
+
{
|
|
2083
|
+
variable,
|
|
2084
|
+
values: [
|
|
2085
|
+
BOT_SWITCH_ON,
|
|
2086
|
+
],
|
|
2087
|
+
},
|
|
2088
|
+
];
|
|
2089
|
+
|
|
2090
|
+
const manifest = botManifestSchema.parse({
|
|
2091
|
+
...botBase(),
|
|
2092
|
+
container: {
|
|
2093
|
+
...botBase().container,
|
|
2094
|
+
ports: [
|
|
2095
|
+
{
|
|
2096
|
+
key: BOT_WEB_PORT_KEY,
|
|
2097
|
+
containerPort: 8080,
|
|
2098
|
+
protocol: PortProtocol.Tcp,
|
|
2099
|
+
hostRange: {
|
|
2100
|
+
default: 31_000,
|
|
2101
|
+
min: 31_000,
|
|
2102
|
+
max: 31_999,
|
|
2103
|
+
},
|
|
2104
|
+
web: true,
|
|
2105
|
+
activeWhen: switched(BOT_WEB),
|
|
2106
|
+
},
|
|
2107
|
+
{
|
|
2108
|
+
key: BOT_PORT_1_KEY,
|
|
2109
|
+
containerPort: 29_000,
|
|
2110
|
+
protocol: PortProtocol.TcpUdp,
|
|
2111
|
+
hostRange: {
|
|
2112
|
+
default: 29_000,
|
|
2113
|
+
min: 29_000,
|
|
2114
|
+
max: 29_999,
|
|
2115
|
+
},
|
|
2116
|
+
mirror: true,
|
|
2117
|
+
activeWhen: switched(BOT_PORT_1),
|
|
2118
|
+
},
|
|
2119
|
+
{
|
|
2120
|
+
key: BOT_PORT_2_KEY,
|
|
2121
|
+
containerPort: 30_000,
|
|
2122
|
+
protocol: PortProtocol.TcpUdp,
|
|
2123
|
+
hostRange: {
|
|
2124
|
+
default: 30_000,
|
|
2125
|
+
min: 30_000,
|
|
2126
|
+
max: 30_999,
|
|
2127
|
+
},
|
|
2128
|
+
mirror: true,
|
|
2129
|
+
activeWhen: switched(BOT_PORT_2),
|
|
2130
|
+
},
|
|
2131
|
+
],
|
|
2132
|
+
},
|
|
2133
|
+
});
|
|
2134
|
+
|
|
2135
|
+
expect(
|
|
2136
|
+
manifest.container.ports.map((port) => ({
|
|
2137
|
+
key: port.key,
|
|
2138
|
+
web: port.web,
|
|
2139
|
+
variable: port.activeWhen[0]?.variable,
|
|
2140
|
+
})),
|
|
2141
|
+
).toEqual([
|
|
2142
|
+
{
|
|
2143
|
+
key: BOT_WEB_PORT_KEY,
|
|
2144
|
+
web: true,
|
|
2145
|
+
variable: BOT_WEB,
|
|
2146
|
+
},
|
|
2147
|
+
{
|
|
2148
|
+
key: BOT_PORT_1_KEY,
|
|
2149
|
+
web: false,
|
|
2150
|
+
variable: BOT_PORT_1,
|
|
2151
|
+
},
|
|
2152
|
+
{
|
|
2153
|
+
key: BOT_PORT_2_KEY,
|
|
2154
|
+
web: false,
|
|
2155
|
+
variable: BOT_PORT_2,
|
|
2156
|
+
},
|
|
2157
|
+
]);
|
|
2158
|
+
});
|
|
2159
|
+
|
|
1931
2160
|
it("reach the runtime reader like a game does", () => {
|
|
1932
2161
|
expect(manifestRuntimeOf(botManifestSchema.parse(botBase()))).toEqual({
|
|
1933
2162
|
...MANIFEST_RUNTIME_DEFAULTS,
|
package/src/manifest/manifest.ts
CHANGED
|
@@ -103,6 +103,10 @@ const manifestPortSchema = z.object({
|
|
|
103
103
|
description:
|
|
104
104
|
"Reserves this many contiguous host ports, bound one to one from containerPort upward. A block is infrastructure, never a player address: it takes no name, hint, join, srv or follows.",
|
|
105
105
|
}),
|
|
106
|
+
web: z.boolean().default(false).meta({
|
|
107
|
+
description:
|
|
108
|
+
"This port is the server's HTTP surface: bound inside the machine and reached at the server's name over HTTPS.",
|
|
109
|
+
}),
|
|
106
110
|
});
|
|
107
111
|
|
|
108
112
|
export type ManifestPort = z.infer<typeof manifestPortSchema>;
|
|
@@ -145,7 +149,8 @@ const gameContainerSchema = containerSchemaWith(z.array(manifestPortSchema).min(
|
|
|
145
149
|
|
|
146
150
|
const botContainerSchema = containerSchemaWith(
|
|
147
151
|
z.array(manifestPortSchema).max(MANIFEST_PORTS_LIMIT).default([]).meta({
|
|
148
|
-
description:
|
|
152
|
+
description:
|
|
153
|
+
"A bot listens on nothing until its author turns a port on. Gate every port with activeWhen on a variable, and mark the HTTP one web.",
|
|
149
154
|
}),
|
|
150
155
|
);
|
|
151
156
|
|
|
@@ -543,6 +548,108 @@ const portShapeIssues = (port: ManifestPort, index: number): CheckIssue[] => {
|
|
|
543
548
|
return issues;
|
|
544
549
|
};
|
|
545
550
|
|
|
551
|
+
const WEB_REFUSED_FIELDS = [
|
|
552
|
+
"srv",
|
|
553
|
+
"follows",
|
|
554
|
+
] as const;
|
|
555
|
+
|
|
556
|
+
const webPortIssues = (port: ManifestPort, index: number): CheckIssue[] => {
|
|
557
|
+
const issues: CheckIssue[] = [];
|
|
558
|
+
const path = [
|
|
559
|
+
"container",
|
|
560
|
+
"ports",
|
|
561
|
+
index,
|
|
562
|
+
];
|
|
563
|
+
|
|
564
|
+
if (port.protocol !== PortProtocol.Tcp) {
|
|
565
|
+
issues.push({
|
|
566
|
+
code: "custom",
|
|
567
|
+
message: `a web port carries ${PortProtocol.Tcp} only, not ${port.protocol}`,
|
|
568
|
+
input: port.protocol,
|
|
569
|
+
path: [
|
|
570
|
+
...path,
|
|
571
|
+
"protocol",
|
|
572
|
+
],
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (port.count !== 1) {
|
|
577
|
+
issues.push({
|
|
578
|
+
code: "custom",
|
|
579
|
+
message: `a web port is a single port, not a block of ${port.count}`,
|
|
580
|
+
input: port.count,
|
|
581
|
+
path: [
|
|
582
|
+
...path,
|
|
583
|
+
"count",
|
|
584
|
+
],
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
for (const field of WEB_REFUSED_FIELDS) {
|
|
589
|
+
if (port[field] === undefined) {
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
issues.push({
|
|
594
|
+
code: "custom",
|
|
595
|
+
message: `a web port is reached only by the server's name and takes no ${field}`,
|
|
596
|
+
input: port[field],
|
|
597
|
+
path: [
|
|
598
|
+
...path,
|
|
599
|
+
field,
|
|
600
|
+
],
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (port.mirror) {
|
|
605
|
+
issues.push({
|
|
606
|
+
code: "custom",
|
|
607
|
+
message: "a web port is reached only by the server's name and takes no mirror",
|
|
608
|
+
input: port.mirror,
|
|
609
|
+
path: [
|
|
610
|
+
...path,
|
|
611
|
+
"mirror",
|
|
612
|
+
],
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
return issues;
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
const webPortsIssues = (ports: ManifestPort[]): CheckIssue[] => {
|
|
620
|
+
const issues: CheckIssue[] = [];
|
|
621
|
+
|
|
622
|
+
let first: ManifestPort | null = null;
|
|
623
|
+
|
|
624
|
+
for (const [index, port] of ports.entries()) {
|
|
625
|
+
if (!port.web) {
|
|
626
|
+
continue;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
issues.push(...webPortIssues(port, index));
|
|
630
|
+
|
|
631
|
+
if (first === null) {
|
|
632
|
+
first = port;
|
|
633
|
+
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
issues.push({
|
|
638
|
+
code: "custom",
|
|
639
|
+
message: `port "${first.key}" is already the web port — a package declares at most one`,
|
|
640
|
+
input: port.web,
|
|
641
|
+
path: [
|
|
642
|
+
"container",
|
|
643
|
+
"ports",
|
|
644
|
+
index,
|
|
645
|
+
"web",
|
|
646
|
+
],
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
return issues;
|
|
651
|
+
};
|
|
652
|
+
|
|
546
653
|
const socketBindingIssues = (ports: ManifestPort[]): CheckIssue[] => {
|
|
547
654
|
const bindings = ports.reduce((total, port) => total + socketProtocolsOf(port.protocol).length, 0);
|
|
548
655
|
|
|
@@ -610,6 +717,7 @@ const portIssues = (ports: ManifestPort[]): CheckIssue[] => {
|
|
|
610
717
|
...ports.flatMap(portShapeIssues),
|
|
611
718
|
...socketBindingIssues(ports),
|
|
612
719
|
...followIssues(ports),
|
|
720
|
+
...webPortsIssues(ports),
|
|
613
721
|
];
|
|
614
722
|
};
|
|
615
723
|
|
|
@@ -17,6 +17,8 @@ export enum BridgeCrashCause {
|
|
|
17
17
|
FfmpegMissing = "ffmpeg-missing",
|
|
18
18
|
AudioopRemoved = "audioop-removed",
|
|
19
19
|
RateLimited = "rate-limited",
|
|
20
|
+
VariableMissing = "variable-missing",
|
|
21
|
+
WebhookActive = "webhook-active",
|
|
20
22
|
Unknown = "unknown",
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -75,6 +77,10 @@ export const BRIDGE_CRASH_EVIDENCE: readonly BridgeCrashEvidence[] = [
|
|
|
75
77
|
name: BridgeEventName.FfmpegMissing,
|
|
76
78
|
cause: BridgeCrashCause.FfmpegMissing,
|
|
77
79
|
},
|
|
80
|
+
{
|
|
81
|
+
name: BridgeEventName.VariableMissing,
|
|
82
|
+
cause: BridgeCrashCause.VariableMissing,
|
|
83
|
+
},
|
|
78
84
|
{
|
|
79
85
|
name: BridgeEventName.IntentsDisallowed,
|
|
80
86
|
cause: BridgeCrashCause.IntentsDisallowed,
|
|
@@ -83,6 +89,10 @@ export const BRIDGE_CRASH_EVIDENCE: readonly BridgeCrashEvidence[] = [
|
|
|
83
89
|
name: BridgeEventName.TokenInvalid,
|
|
84
90
|
cause: BridgeCrashCause.TokenInvalid,
|
|
85
91
|
},
|
|
92
|
+
{
|
|
93
|
+
name: BridgeEventName.WebhookActive,
|
|
94
|
+
cause: BridgeCrashCause.WebhookActive,
|
|
95
|
+
},
|
|
86
96
|
{
|
|
87
97
|
name: BridgeEventName.TokenInUse,
|
|
88
98
|
cause: BridgeCrashCause.TokenInUse,
|
|
@@ -47,6 +47,8 @@ export enum BridgeEventName {
|
|
|
47
47
|
TickLagging = "TickLagging",
|
|
48
48
|
TokenInUse = "TokenInUse",
|
|
49
49
|
TokenInvalid = "TokenInvalid",
|
|
50
|
+
VariableMissing = "VariableMissing",
|
|
51
|
+
WebhookActive = "WebhookActive",
|
|
50
52
|
WorldActivated = "WorldActivated",
|
|
51
53
|
WorldCloned = "WorldCloned",
|
|
52
54
|
WorldCorrupt = "WorldCorrupt",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import { BOT_PORT_SWITCHES, BOT_SWITCH_ON, BOT_WEB } from "../bot/detect";
|
|
2
3
|
import { BridgeKind, BridgeSetupStepKind, PortJoin, PortProtocol } from "../core";
|
|
3
4
|
import { GuideOpenTab } from "../guides";
|
|
4
5
|
import type { ManifestPort, ManifestPortActivation } from "../manifest";
|
|
@@ -242,6 +243,7 @@ const port = (activeWhen: ManifestPortActivation[]): ManifestPort => ({
|
|
|
242
243
|
activeWhen,
|
|
243
244
|
mirror: false,
|
|
244
245
|
count: 1,
|
|
246
|
+
web: false,
|
|
245
247
|
});
|
|
246
248
|
|
|
247
249
|
const driverWithTabs = (tabs: number, sections: number): BridgeDriver => ({
|
|
@@ -663,6 +665,7 @@ describe("validatePortActivation", () => {
|
|
|
663
665
|
]),
|
|
664
666
|
],
|
|
665
667
|
driverWithVariables(),
|
|
668
|
+
[],
|
|
666
669
|
),
|
|
667
670
|
).not.toThrow();
|
|
668
671
|
});
|
|
@@ -681,9 +684,29 @@ describe("validatePortActivation", () => {
|
|
|
681
684
|
]),
|
|
682
685
|
],
|
|
683
686
|
driverWithVariables(),
|
|
687
|
+
[],
|
|
684
688
|
),
|
|
685
689
|
).toThrow('port "bedrock" activeWhen[0] names "CROSSPLAY", which no variables form, catalog or detail declares');
|
|
686
690
|
});
|
|
691
|
+
|
|
692
|
+
it("accepts a variable the platform sets for the package's kind", () => {
|
|
693
|
+
expect(() =>
|
|
694
|
+
validatePortActivation(
|
|
695
|
+
[
|
|
696
|
+
port([
|
|
697
|
+
{
|
|
698
|
+
variable: BOT_WEB,
|
|
699
|
+
values: [
|
|
700
|
+
BOT_SWITCH_ON,
|
|
701
|
+
],
|
|
702
|
+
},
|
|
703
|
+
]),
|
|
704
|
+
],
|
|
705
|
+
driverWithVariables(),
|
|
706
|
+
Object.values(BOT_PORT_SWITCHES),
|
|
707
|
+
),
|
|
708
|
+
).not.toThrow();
|
|
709
|
+
});
|
|
687
710
|
});
|
|
688
711
|
|
|
689
712
|
const driverWithDetail = (): BridgeDriver => ({
|
|
@@ -874,6 +897,7 @@ describe("detail sections", () => {
|
|
|
874
897
|
]),
|
|
875
898
|
],
|
|
876
899
|
driverWithDetail(),
|
|
900
|
+
[],
|
|
877
901
|
),
|
|
878
902
|
).not.toThrow();
|
|
879
903
|
});
|
|
@@ -905,12 +905,16 @@ const declaredVariables = (driver: BridgeDriver) => {
|
|
|
905
905
|
return declared;
|
|
906
906
|
};
|
|
907
907
|
|
|
908
|
-
export const validatePortActivation = (
|
|
908
|
+
export const validatePortActivation = (
|
|
909
|
+
ports: ManifestPort[],
|
|
910
|
+
driver: BridgeDriver,
|
|
911
|
+
platformVariables: readonly string[],
|
|
912
|
+
): void => {
|
|
909
913
|
const declared = declaredVariables(driver);
|
|
910
914
|
|
|
911
915
|
for (const port of ports) {
|
|
912
916
|
for (const [index, rule] of port.activeWhen.entries()) {
|
|
913
|
-
if (declared.has(rule.variable)) {
|
|
917
|
+
if (declared.has(rule.variable) || platformVariables.includes(rule.variable)) {
|
|
914
918
|
continue;
|
|
915
919
|
}
|
|
916
920
|
|
|
@@ -271,6 +271,19 @@ describe("the crash record the ring builds", () => {
|
|
|
271
271
|
});
|
|
272
272
|
});
|
|
273
273
|
|
|
274
|
+
it("names an active webhook before the token in use when both are held", () => {
|
|
275
|
+
const ring = idleRing();
|
|
276
|
+
|
|
277
|
+
ring.emit(BridgeEventName.TokenInUse);
|
|
278
|
+
ring.emit(BridgeEventName.WebhookActive);
|
|
279
|
+
|
|
280
|
+
ring.crash(crashRecord());
|
|
281
|
+
|
|
282
|
+
expect(ring.drain(0).events.at(0)?.payload).toMatchObject({
|
|
283
|
+
cause: "webhook-active",
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
|
|
274
287
|
it("lets the out of memory log pattern outrank the java version", () => {
|
|
275
288
|
const ring = idleRing();
|
|
276
289
|
|