@serverkgg/bridge 0.0.21 → 0.1.1
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 -1
- package/src/bot/botDeploy.test.ts +363 -0
- package/src/bot/botDeploy.ts +136 -0
- package/src/bot/botEnvironment.test.ts +189 -0
- package/src/bot/botEnvironment.ts +141 -0
- package/src/bot/botIdentity.test.ts +251 -0
- package/src/bot/botIdentity.ts +140 -0
- package/src/bot/botPanel.test.ts +216 -0
- package/src/bot/botPanel.ts +121 -0
- package/src/bot/botState.test.ts +45 -0
- package/src/bot/botState.ts +54 -0
- package/src/bot/botTerminal.test.ts +79 -0
- package/src/bot/botTerminal.ts +62 -0
- package/src/bot/botVariables.test.ts +193 -0
- package/src/bot/botVariables.ts +73 -0
- package/src/bot/detect/botFramework.ts +23 -0
- package/src/bot/detect/botFrameworks.ts +198 -0
- package/src/bot/detect/botInspectWarning.ts +15 -0
- package/src/bot/detect/botPackageManager.ts +6 -0
- package/src/bot/detect/botPlatform.ts +10 -0
- package/src/bot/detect/botRuntime.ts +15 -0
- package/src/bot/detect/botToken.test.ts +71 -0
- package/src/bot/detect/botToken.ts +126 -0
- package/src/bot/detect/detect.test.ts +543 -0
- package/src/bot/detect/detect.ts +842 -0
- package/src/bot/detect/detectResult.ts +40 -0
- package/src/bot/detect/detectTable.test.ts +72 -0
- package/src/bot/detect/detectTable.ts +166 -0
- package/src/bot/detect/envExample.test.ts +29 -0
- package/src/bot/detect/envExample.ts +48 -0
- package/src/bot/detect/envKey.ts +7 -0
- package/src/bot/detect/envScan.test.ts +39 -0
- package/src/bot/detect/envScan.ts +87 -0
- package/src/bot/detect/index.ts +17 -0
- package/src/bot/detect/inferBotPlatform.test.ts +60 -0
- package/src/bot/detect/inferBotPlatform.ts +28 -0
- package/src/bot/detect/keys.test.ts +28 -0
- package/src/bot/detect/keys.ts +64 -0
- package/src/bot/detect/sensitiveKey.test.ts +91 -0
- package/src/bot/detect/sensitiveKey.ts +44 -0
- package/src/bot/detect/serverkJson.test.ts +88 -0
- package/src/bot/detect/serverkJson.ts +174 -0
- package/src/bot/index.ts +8 -0
- package/src/cli/detectTable.test.ts +66 -0
- package/src/cli/detectTable.ts +43 -0
- package/src/cli/main.ts +12 -4
- package/src/cli/renderedFile.ts +35 -0
- package/src/cli/schema.ts +7 -26
- package/src/cli/validate.test.ts +210 -2
- package/src/cli/validate.ts +71 -7
- package/src/context/bridgeContext.ts +11 -0
- package/src/core/bridgePort.test.ts +45 -0
- package/src/core/bridgePort.ts +26 -0
- package/src/guides/compile.ts +2 -2
- package/src/manifest/index.ts +1 -0
- package/src/manifest/kind.ts +5 -0
- package/src/manifest/manifest.test.ts +630 -3
- package/src/manifest/manifest.ts +391 -87
- package/src/manifest/runtime.ts +2 -2
- package/src/manifest/source.ts +4 -4
- package/src/modules/bridgeLifecycleSubject.ts +8 -0
- package/src/modules/bridgeModule.ts +6 -0
- package/src/modules/bridgeStopSignal.ts +13 -0
- package/src/modules/index.ts +2 -0
- package/src/panel/bridgeDetail.ts +2 -0
- package/src/protocol/bridgeConfig.ts +2 -0
- package/src/runtime/bridgeConfigReader.test.ts +80 -0
- package/src/runtime/bridgeConfigReader.ts +20 -0
- package/src/runtime/bridgeContext.test.ts +241 -0
- package/src/runtime/bridgeContext.ts +35 -9
- package/src/runtime/bridgeDriver.test.ts +132 -1
- package/src/runtime/bridgeDriver.ts +54 -1
- package/src/runtime/bridgeFiles.test.ts +62 -0
- package/src/runtime/bridgeFiles.ts +12 -2
- package/src/runtime/bridgeLiveConfig.ts +18 -0
- package/src/runtime/bridgeRedaction.test.ts +410 -0
- package/src/runtime/bridgeRedaction.ts +214 -0
- package/src/runtime/bridgeRegistry.test.ts +3 -0
- package/src/runtime/bridgeRegistry.ts +15 -0
- package/src/runtime/bridgeServer.test.ts +130 -0
- package/src/runtime/bridgeServer.ts +24 -7
- package/src/runtime/bridgeSetupHandlers.test.ts +88 -0
- package/src/runtime/bridgeSetupHandlers.ts +16 -6
- package/src/runtime/bridgeSupervisor.test.ts +670 -2
- package/src/runtime/bridgeSupervisor.ts +219 -67
- package/src/runtime/serve.ts +47 -13
package/src/cli/validate.test.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
2
|
-
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test";
|
|
2
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { renderDetectTable } from "../bot/detect";
|
|
5
6
|
import { BridgeKind, BridgeSetupStepKind } from "../core";
|
|
6
7
|
import { GuideOpenTab } from "../guides";
|
|
8
|
+
import { MANIFEST_SCHEMA_VERSION, PackageKind } from "../manifest";
|
|
7
9
|
import type { BridgeDriver, BridgeSetupModule } from "../modules";
|
|
8
10
|
import {
|
|
9
11
|
BridgeControl,
|
|
@@ -394,3 +396,209 @@ describe("the placement report", () => {
|
|
|
394
396
|
).toEqual([]);
|
|
395
397
|
});
|
|
396
398
|
});
|
|
399
|
+
|
|
400
|
+
const webp = (width: number, height: number) => {
|
|
401
|
+
const bytes = new Uint8Array(30);
|
|
402
|
+
const view = new DataView(bytes.buffer);
|
|
403
|
+
const ascii = (offset: number, value: string) => {
|
|
404
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
405
|
+
bytes[offset + index] = value.charCodeAt(index);
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
|
|
409
|
+
ascii(0, "RIFF");
|
|
410
|
+
view.setUint32(4, bytes.byteLength - 8, true);
|
|
411
|
+
ascii(8, "WEBP");
|
|
412
|
+
ascii(12, "VP8X");
|
|
413
|
+
view.setUint32(16, 10, true);
|
|
414
|
+
view.setUint16(24, (width - 1) & 0xff_ff, true);
|
|
415
|
+
bytes[26] = ((width - 1) >> 16) & 0xff;
|
|
416
|
+
view.setUint16(27, (height - 1) & 0xff_ff, true);
|
|
417
|
+
bytes[29] = ((height - 1) >> 16) & 0xff;
|
|
418
|
+
|
|
419
|
+
return bytes;
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
const BOT_DRIVER = `export const driver = {
|
|
423
|
+
install: {
|
|
424
|
+
kind: "install",
|
|
425
|
+
run: async () => undefined,
|
|
426
|
+
},
|
|
427
|
+
lifecycle: {
|
|
428
|
+
kind: "lifecycle",
|
|
429
|
+
command: async () => ["node", "/serverk/launcher.mjs"],
|
|
430
|
+
environment: async () => ({}),
|
|
431
|
+
},
|
|
432
|
+
};
|
|
433
|
+
`;
|
|
434
|
+
|
|
435
|
+
const botManifest = (extra: Record<string, unknown> = {}) => ({
|
|
436
|
+
schemaVersion: MANIFEST_SCHEMA_VERSION,
|
|
437
|
+
kind: PackageKind.Bot,
|
|
438
|
+
metadata: {
|
|
439
|
+
id: "bot-node",
|
|
440
|
+
name: text("Node.js Bot"),
|
|
441
|
+
description: text("Run your own bot"),
|
|
442
|
+
tagline: text("Your code, always on"),
|
|
443
|
+
logo: "assets/logo.webp",
|
|
444
|
+
banner: "assets/banner.webp",
|
|
445
|
+
},
|
|
446
|
+
resources: {
|
|
447
|
+
minMemoryMb: 512,
|
|
448
|
+
recommendedMemoryMb: 1024,
|
|
449
|
+
minDiskMb: 2048,
|
|
450
|
+
},
|
|
451
|
+
container: {
|
|
452
|
+
image: "serverkgg/bot-node",
|
|
453
|
+
ports: [],
|
|
454
|
+
},
|
|
455
|
+
...extra,
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
const WEBHOOK_PORT = {
|
|
459
|
+
key: "webhook",
|
|
460
|
+
containerPort: 8080,
|
|
461
|
+
protocol: "tcp",
|
|
462
|
+
hostRange: {
|
|
463
|
+
default: 30_000,
|
|
464
|
+
min: 30_000,
|
|
465
|
+
max: 30_099,
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
describe("validating a bot package", () => {
|
|
470
|
+
let root = "";
|
|
471
|
+
let lines: string[] = [];
|
|
472
|
+
let restore = () => {};
|
|
473
|
+
|
|
474
|
+
const writeBot = (manifest: Record<string, unknown>, detectTable: string | null = renderDetectTable()) => {
|
|
475
|
+
mkdirSync(join(root, "assets"));
|
|
476
|
+
mkdirSync(join(root, "src"));
|
|
477
|
+
mkdirSync(join(root, "image"));
|
|
478
|
+
|
|
479
|
+
if (detectTable !== null) {
|
|
480
|
+
writeFileSync(join(root, "image/detect.json"), detectTable);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
writeFileSync(
|
|
484
|
+
join(root, "package.json"),
|
|
485
|
+
JSON.stringify({
|
|
486
|
+
name: "@serverkgg/bot-node",
|
|
487
|
+
dependencies: {
|
|
488
|
+
"@serverkgg/bridge": "0.1.0",
|
|
489
|
+
},
|
|
490
|
+
}),
|
|
491
|
+
);
|
|
492
|
+
writeFileSync(join(root, "serverk.yml"), JSON.stringify(manifest));
|
|
493
|
+
writeFileSync(join(root, "assets/logo.webp"), webp(256, 256));
|
|
494
|
+
writeFileSync(join(root, "assets/banner.webp"), webp(1280, 720));
|
|
495
|
+
writeFileSync(join(root, "src/driver.ts"), BOT_DRIVER);
|
|
496
|
+
};
|
|
497
|
+
|
|
498
|
+
const validate = () => {
|
|
499
|
+
return runValidate([
|
|
500
|
+
"--dir",
|
|
501
|
+
root,
|
|
502
|
+
]);
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
beforeEach(() => {
|
|
506
|
+
root = mkdtempSync(`${tmpdir()}/serverk-validate-bot-`);
|
|
507
|
+
lines = [];
|
|
508
|
+
|
|
509
|
+
const spy = spyOn(console, "log").mockImplementation((line: unknown) => {
|
|
510
|
+
lines.push(String(line));
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
restore = () => spy.mockRestore();
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
afterEach(() => {
|
|
517
|
+
restore();
|
|
518
|
+
rmSync(root, {
|
|
519
|
+
force: true,
|
|
520
|
+
recursive: true,
|
|
521
|
+
});
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
test("validates a bot with no ports and says which kind it read", async () => {
|
|
525
|
+
writeBot(botManifest());
|
|
526
|
+
|
|
527
|
+
await validate();
|
|
528
|
+
|
|
529
|
+
expect(lines).toContain("bot-node is valid");
|
|
530
|
+
expect(lines).toContain(
|
|
531
|
+
" manifest: BotPackage, 0 port(s), 0 conditional, 0 tcp+udp, 0 block(s), 0 secret(s), 0 guide(s)",
|
|
532
|
+
);
|
|
533
|
+
expect(lines).toContain(" environment: vault off");
|
|
534
|
+
expect(lines).toContain(" detect: image/detect.json matches this bridge");
|
|
535
|
+
expect(lines.some((line) => line.includes("warning"))).toBe(false);
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
test("says when a bot turns the environment vault on", async () => {
|
|
539
|
+
writeBot(
|
|
540
|
+
botManifest({
|
|
541
|
+
environment: {
|
|
542
|
+
vault: true,
|
|
543
|
+
},
|
|
544
|
+
}),
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
await validate();
|
|
548
|
+
|
|
549
|
+
expect(lines).toContain(" environment: vault on");
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
test("fails a bot whose detection table is missing", async () => {
|
|
553
|
+
writeBot(botManifest(), null);
|
|
554
|
+
|
|
555
|
+
await expect(validate()).rejects.toThrow(
|
|
556
|
+
'image/detect.json is missing — run "serverk-bridge detect-table" and commit it',
|
|
557
|
+
);
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
test("fails a bot whose detection table is stale", async () => {
|
|
561
|
+
writeBot(botManifest(), "{}\n");
|
|
562
|
+
|
|
563
|
+
await expect(validate()).rejects.toThrow(
|
|
564
|
+
'image/detect.json is out of date — run "serverk-bridge detect-table" and commit it',
|
|
565
|
+
);
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
test("warns about a port on a bot but still validates it", async () => {
|
|
569
|
+
writeBot(
|
|
570
|
+
botManifest({
|
|
571
|
+
container: {
|
|
572
|
+
image: "serverkgg/bot-node",
|
|
573
|
+
ports: [
|
|
574
|
+
WEBHOOK_PORT,
|
|
575
|
+
],
|
|
576
|
+
},
|
|
577
|
+
}),
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
await validate();
|
|
581
|
+
|
|
582
|
+
expect(lines).toContain("bot-node is valid");
|
|
583
|
+
expect(lines).toContain(
|
|
584
|
+
" warning: a bot package declares 1 port(s); bots need none — remove them unless a webhook needs a public port",
|
|
585
|
+
);
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
test("fails a bot that declares platform secrets", async () => {
|
|
589
|
+
writeBot(
|
|
590
|
+
botManifest({
|
|
591
|
+
secrets: [
|
|
592
|
+
{
|
|
593
|
+
key: "API_KEY",
|
|
594
|
+
description: text("A key"),
|
|
595
|
+
},
|
|
596
|
+
],
|
|
597
|
+
}),
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
await expect(validate()).rejects.toThrow(
|
|
601
|
+
"a bot package declares no platform secrets; per-bot values live in the environment vault",
|
|
602
|
+
);
|
|
603
|
+
});
|
|
604
|
+
});
|
package/src/cli/validate.ts
CHANGED
|
@@ -4,13 +4,18 @@ import { pathToFileURL } from "node:url";
|
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { type ArticlePack, compileArticles } from "../blog";
|
|
7
|
+
import { DETECT_TABLE_FILE, renderDetectTable } from "../bot/detect";
|
|
8
|
+
import { PortProtocol } from "../core";
|
|
7
9
|
import { GuideOpenTab, type GuideOpenTarget, type GuidePack, readGuidePack, readGuidePackage } from "../guides";
|
|
8
10
|
import {
|
|
9
11
|
declaredFiles,
|
|
10
12
|
type GuidePackage,
|
|
13
|
+
isBotManifest,
|
|
11
14
|
MANIFEST_FILE,
|
|
12
|
-
type
|
|
15
|
+
type Package,
|
|
16
|
+
PackageKind,
|
|
13
17
|
type PackageSource,
|
|
18
|
+
type RunnableManifest,
|
|
14
19
|
readAssets,
|
|
15
20
|
readPackageDocument,
|
|
16
21
|
} from "../manifest";
|
|
@@ -31,7 +36,7 @@ const HELP = `serverk-bridge validate [options]
|
|
|
31
36
|
|
|
32
37
|
options:
|
|
33
38
|
--dir <path> package directory (default: current directory)
|
|
34
|
-
--driver <path> driver module (default: <dir>/src/driver.ts),
|
|
39
|
+
--driver <path> driver module (default: <dir>/src/driver.ts), runnable packages only
|
|
35
40
|
--help show this help`;
|
|
36
41
|
|
|
37
42
|
const BRIDGE_PACKAGE = "@serverkgg/bridge";
|
|
@@ -317,7 +322,54 @@ export const setupReportLines = (setup: BridgeSetupModule | undefined): string[]
|
|
|
317
322
|
];
|
|
318
323
|
};
|
|
319
324
|
|
|
320
|
-
const
|
|
325
|
+
export const botPortLines = (manifest: RunnableManifest): string[] => {
|
|
326
|
+
const ports = manifest.container.ports.length;
|
|
327
|
+
|
|
328
|
+
if (!isBotManifest(manifest) || ports === 0) {
|
|
329
|
+
return [];
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return [
|
|
333
|
+
` warning: a bot package declares ${ports} port(s); bots need none — remove them unless a webhook needs a public port`,
|
|
334
|
+
];
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
export const botEnvironmentLines = (manifest: RunnableManifest): string[] => {
|
|
338
|
+
if (!isBotManifest(manifest)) {
|
|
339
|
+
return [];
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return [
|
|
343
|
+
` environment: vault ${manifest.environment.vault ? "on" : "off"}`,
|
|
344
|
+
];
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
export const botDetectLines = (source: PackageSource, manifest: RunnableManifest): string[] => {
|
|
348
|
+
if (!isBotManifest(manifest)) {
|
|
349
|
+
return [];
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const bytes = source.read(DETECT_TABLE_FILE);
|
|
353
|
+
|
|
354
|
+
if (bytes === null) {
|
|
355
|
+
throw new Error(`${DETECT_TABLE_FILE} is missing — run "serverk-bridge detect-table" and commit it`);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (new TextDecoder().decode(bytes) !== renderDetectTable()) {
|
|
359
|
+
throw new Error(`${DETECT_TABLE_FILE} is out of date — run "serverk-bridge detect-table" and commit it`);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return [
|
|
363
|
+
` detect: ${DETECT_TABLE_FILE} matches this bridge`,
|
|
364
|
+
];
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
const validateRunnablePackage = async (
|
|
368
|
+
source: PackageSource,
|
|
369
|
+
manifest: RunnableManifest,
|
|
370
|
+
dir: string,
|
|
371
|
+
driverArg?: string,
|
|
372
|
+
) => {
|
|
321
373
|
const assets = readAssets(source, manifest);
|
|
322
374
|
const guideFiles = manifest.guides.flatMap((guide) => [
|
|
323
375
|
guide.bodyPath.ar,
|
|
@@ -331,6 +383,7 @@ const validateGamePackage = async (source: PackageSource, manifest: Manifest, di
|
|
|
331
383
|
}
|
|
332
384
|
|
|
333
385
|
const pack = readGuidePack(source, manifest);
|
|
386
|
+
const detectLines = botDetectLines(source, manifest);
|
|
334
387
|
const driverPath = driverArg ? resolve(driverArg) : join(dir, "src/driver.ts");
|
|
335
388
|
|
|
336
389
|
if (!existsSync(driverPath)) {
|
|
@@ -348,11 +401,22 @@ const validateGamePackage = async (source: PackageSource, manifest: Manifest, di
|
|
|
348
401
|
validatePortActivation(manifest.container.ports, driver);
|
|
349
402
|
|
|
350
403
|
const conditionalPorts = manifest.container.ports.filter((port) => port.activeWhen.length > 0).length;
|
|
404
|
+
const pairedPorts = manifest.container.ports.filter((port) => port.protocol === PortProtocol.TcpUdp).length;
|
|
405
|
+
const blockPorts = manifest.container.ports.filter((port) => port.count > 1).length;
|
|
351
406
|
|
|
352
407
|
console.log(`${manifest.metadata.id} is valid`);
|
|
353
408
|
console.log(
|
|
354
|
-
` manifest: ${manifest.container.ports.length} port(s), ${conditionalPorts} conditional, ${manifest.secrets.length} secret(s), ${manifest.guides.length} guide(s)`,
|
|
409
|
+
` manifest: ${manifest.kind}, ${manifest.container.ports.length} port(s), ${conditionalPorts} conditional, ${pairedPorts} tcp+udp, ${blockPorts} block(s), ${(manifest.secrets ?? []).length} secret(s), ${manifest.guides.length} guide(s)`,
|
|
355
410
|
);
|
|
411
|
+
|
|
412
|
+
for (const line of [
|
|
413
|
+
...botPortLines(manifest),
|
|
414
|
+
...botEnvironmentLines(manifest),
|
|
415
|
+
...detectLines,
|
|
416
|
+
]) {
|
|
417
|
+
console.log(line);
|
|
418
|
+
}
|
|
419
|
+
|
|
356
420
|
console.log(
|
|
357
421
|
` assets: logo ${assets.logo.dimensions.width}x${assets.logo.dimensions.height}, banner ${assets.banner.dimensions.width}x${assets.banner.dimensions.height}`,
|
|
358
422
|
);
|
|
@@ -399,7 +463,7 @@ export const runValidate = async (args: string[]) => {
|
|
|
399
463
|
|
|
400
464
|
assertRegistryBridge(source);
|
|
401
465
|
|
|
402
|
-
let document:
|
|
466
|
+
let document: Package;
|
|
403
467
|
|
|
404
468
|
try {
|
|
405
469
|
document = readPackageDocument(source);
|
|
@@ -407,11 +471,11 @@ export const runValidate = async (args: string[]) => {
|
|
|
407
471
|
return invalidManifest(error);
|
|
408
472
|
}
|
|
409
473
|
|
|
410
|
-
if (document.kind ===
|
|
474
|
+
if (document.kind === PackageKind.Guide) {
|
|
411
475
|
validateGuidePackage(source, document);
|
|
412
476
|
|
|
413
477
|
return;
|
|
414
478
|
}
|
|
415
479
|
|
|
416
|
-
await
|
|
480
|
+
await validateRunnablePackage(source, document, dir, values.driver);
|
|
417
481
|
};
|
|
@@ -6,6 +6,12 @@ export interface BridgeServer {
|
|
|
6
6
|
memoryMb: number;
|
|
7
7
|
cpuPercent: number;
|
|
8
8
|
running: boolean;
|
|
9
|
+
readonly publicAddress: string | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface BridgePortBlock {
|
|
13
|
+
first: number;
|
|
14
|
+
count: number;
|
|
9
15
|
}
|
|
10
16
|
|
|
11
17
|
export interface BridgeFileEntry {
|
|
@@ -34,6 +40,7 @@ export interface BridgeExtractOptions {
|
|
|
34
40
|
select?: string;
|
|
35
41
|
selects?: string[];
|
|
36
42
|
exclude?: string[];
|
|
43
|
+
dotfiles?: boolean;
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
export interface BridgeListOptions {
|
|
@@ -191,8 +198,11 @@ export interface BridgeLog {
|
|
|
191
198
|
export interface BridgeContext {
|
|
192
199
|
readonly server: BridgeServer;
|
|
193
200
|
variable(key: string): string | null;
|
|
201
|
+
variables(): Readonly<Record<string, string>>;
|
|
194
202
|
secret(key: string): string | null;
|
|
203
|
+
secrets(): Readonly<Record<string, string>>;
|
|
195
204
|
port(key: string): number;
|
|
205
|
+
portBlock(key: string): BridgePortBlock;
|
|
196
206
|
files: BridgeFiles;
|
|
197
207
|
codec: BridgeCodecs;
|
|
198
208
|
net: BridgeNet;
|
|
@@ -203,5 +213,6 @@ export interface BridgeContext {
|
|
|
203
213
|
logs: BridgeLogs;
|
|
204
214
|
setup: BridgeSetupReporter;
|
|
205
215
|
emit(event: string, payload?: BridgeValues): void;
|
|
216
|
+
restart(): Promise<void>;
|
|
206
217
|
log: BridgeLog;
|
|
207
218
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { PortProtocol, portCountOf, SOCKET_PROTOCOLS, socketProtocolsOf } from "./bridgePort";
|
|
3
|
+
|
|
4
|
+
describe("socketProtocolsOf", () => {
|
|
5
|
+
test("keeps a single socket protocol as it is", () => {
|
|
6
|
+
expect(socketProtocolsOf(PortProtocol.Tcp)).toEqual([
|
|
7
|
+
PortProtocol.Tcp,
|
|
8
|
+
]);
|
|
9
|
+
expect(socketProtocolsOf(PortProtocol.Udp)).toEqual([
|
|
10
|
+
PortProtocol.Udp,
|
|
11
|
+
]);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("expands a pair into both socket protocols", () => {
|
|
15
|
+
expect(socketProtocolsOf(PortProtocol.TcpUdp)).toEqual([
|
|
16
|
+
PortProtocol.Tcp,
|
|
17
|
+
PortProtocol.Udp,
|
|
18
|
+
]);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("never hands out the shared list itself", () => {
|
|
22
|
+
const expanded = socketProtocolsOf(PortProtocol.TcpUdp);
|
|
23
|
+
|
|
24
|
+
expanded.pop();
|
|
25
|
+
|
|
26
|
+
expect(SOCKET_PROTOCOLS).toEqual([
|
|
27
|
+
PortProtocol.Tcp,
|
|
28
|
+
PortProtocol.Udp,
|
|
29
|
+
]);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("portCountOf", () => {
|
|
34
|
+
test("reads a port stored before blocks existed as a single port", () => {
|
|
35
|
+
expect(portCountOf({})).toBe(1);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("reads the count of a block", () => {
|
|
39
|
+
expect(
|
|
40
|
+
portCountOf({
|
|
41
|
+
count: 32,
|
|
42
|
+
}),
|
|
43
|
+
).toBe(32);
|
|
44
|
+
});
|
|
45
|
+
});
|
package/src/core/bridgePort.ts
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
export enum PortProtocol {
|
|
2
2
|
Tcp = "tcp",
|
|
3
3
|
Udp = "udp",
|
|
4
|
+
TcpUdp = "tcp+udp",
|
|
4
5
|
}
|
|
5
6
|
|
|
7
|
+
export const PORT_BLOCK_MAX_COUNT = 64;
|
|
8
|
+
|
|
9
|
+
export type SocketProtocol = PortProtocol.Tcp | PortProtocol.Udp;
|
|
10
|
+
|
|
11
|
+
export const SOCKET_PROTOCOLS = [
|
|
12
|
+
PortProtocol.Tcp,
|
|
13
|
+
PortProtocol.Udp,
|
|
14
|
+
] as const satisfies readonly SocketProtocol[];
|
|
15
|
+
|
|
16
|
+
export const socketProtocolsOf = (protocol: PortProtocol): SocketProtocol[] => {
|
|
17
|
+
if (protocol === PortProtocol.TcpUdp) {
|
|
18
|
+
return [
|
|
19
|
+
...SOCKET_PROTOCOLS,
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return [
|
|
24
|
+
protocol,
|
|
25
|
+
];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const portCountOf = (port: { count?: number | undefined }): number => {
|
|
29
|
+
return port.count ?? 1;
|
|
30
|
+
};
|
|
31
|
+
|
|
6
32
|
export enum PortJoin {
|
|
7
33
|
Combined = "combined",
|
|
8
34
|
Separate = "separate",
|
package/src/guides/compile.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { verifyAsset } from "../manifest/assets";
|
|
2
2
|
import type { GuideEntry, GuideSurface, GuideTag } from "../manifest/guide";
|
|
3
3
|
import type { LocalizedText } from "../manifest/localized";
|
|
4
|
-
import type { GuidePackage,
|
|
4
|
+
import type { GuidePackage, RunnableManifest } from "../manifest/manifest";
|
|
5
5
|
import type { PackageSource } from "../manifest/source";
|
|
6
6
|
import type { GuideBlock, GuideLeafBlock } from "./blocks";
|
|
7
7
|
import { type CompiledGuide, type CompiledGuideTag, compiledGuideSchema } from "./compiled";
|
|
@@ -140,7 +140,7 @@ export const compileGuides = (source: PackageSource, guideTags: GuideTag[], entr
|
|
|
140
140
|
};
|
|
141
141
|
};
|
|
142
142
|
|
|
143
|
-
export const readGuidePack = (source: PackageSource, manifest:
|
|
143
|
+
export const readGuidePack = (source: PackageSource, manifest: RunnableManifest): GuidePack | null => {
|
|
144
144
|
if (manifest.guides.length === 0) {
|
|
145
145
|
return null;
|
|
146
146
|
}
|
package/src/manifest/index.ts
CHANGED