@serverkgg/bridge 0.1.13 → 0.1.15
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 +3 -0
- package/src/catalogs/curseforge.test.ts +46 -0
- package/src/catalogs/curseforge.ts +20 -0
- package/src/context/bridgeContext.ts +1 -0
- package/src/manifest/extension.ts +1 -0
- package/src/manifest/manifest.test.ts +18 -0
- package/src/modules/bridge.ts +13 -2
- package/src/modules/bridgeModule.ts +20 -4
- package/src/panel/bridgePanel.ts +2 -0
- package/src/protocol/bridgeCatalog.ts +7 -0
- package/src/protocol/bridgeCatalogSource.ts +9 -0
- package/src/protocol/bridgeConfig.ts +1 -0
- package/src/protocol/bridgeFrame.ts +1 -0
- package/src/protocol/bridgeLimits.ts +6 -0
- package/src/protocol/bridgeManifest.ts +2 -0
- package/src/protocol/index.ts +1 -0
- package/src/runtime/bridgeActions.ts +9 -2
- package/src/runtime/bridgeConfigReader.test.ts +31 -0
- package/src/runtime/bridgeConfigReader.ts +1 -0
- package/src/runtime/bridgeContext.test.ts +53 -0
- package/src/runtime/bridgeContext.ts +3 -0
- package/src/runtime/bridgeDriver.test.ts +347 -1
- package/src/runtime/bridgeDriver.ts +130 -18
- package/src/runtime/bridgeLiveConfig.test.ts +137 -1
- package/src/runtime/bridgeLiveConfig.ts +46 -2
- package/src/runtime/bridgeRegistry.test.ts +220 -0
- package/src/runtime/bridgeRegistry.ts +27 -1
- package/src/runtime/bridgeSample.test.ts +63 -0
- package/src/runtime/bridgeSample.ts +22 -0
- package/src/runtime/serve.ts +5 -12
- package/src/steam/workshop.test.ts +19 -1
- package/src/steam/workshop.ts +12 -0
- package/src/wire/panel.test.ts +177 -0
- package/src/wire/panel.ts +16 -0
package/src/steam/workshop.ts
CHANGED
|
@@ -6,6 +6,10 @@ export const STEAM_REMOTE_STORAGE = "https://api.steampowered.com/ISteamRemoteSt
|
|
|
6
6
|
|
|
7
7
|
export const WORKSHOP_CONTENT_DIRECTORY = "steamapps/workshop/content";
|
|
8
8
|
|
|
9
|
+
const WORKSHOP_ARCHIVE_DIRECTORY = ".serverk/snapshots";
|
|
10
|
+
|
|
11
|
+
const WORKSHOP_ITEM_PAGE = "https://steamcommunity.com/sharedfiles/filedetails/?id=";
|
|
12
|
+
|
|
9
13
|
const FORM_CONTENT_TYPE = "application/x-www-form-urlencoded";
|
|
10
14
|
|
|
11
15
|
const DETAILS_CACHE_SECONDS = 600;
|
|
@@ -63,6 +67,14 @@ export const workshopContentPath = (
|
|
|
63
67
|
return `${directory}/${workshopAppId}/${id}`;
|
|
64
68
|
};
|
|
65
69
|
|
|
70
|
+
export const workshopArchivePath = (appId: number, itemId: string) => {
|
|
71
|
+
return `${WORKSHOP_ARCHIVE_DIRECTORY}/workshop-${appId}-${itemId}.zip`;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export const workshopItemPageUrl = (itemId: string) => {
|
|
75
|
+
return `${WORKSHOP_ITEM_PAGE}${itemId}`;
|
|
76
|
+
};
|
|
77
|
+
|
|
66
78
|
export const workshopFormBody = (countKey: string, ids: string[]) => {
|
|
67
79
|
const form = new URLSearchParams();
|
|
68
80
|
|
package/src/wire/panel.test.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { GuideOpenTab } from "@serverkgg/bridge/guides";
|
|
|
3
3
|
import {
|
|
4
4
|
BridgeAccess,
|
|
5
5
|
BridgeAction,
|
|
6
|
+
type BridgeCatalogSource,
|
|
7
|
+
BridgeCatalogSourceKind,
|
|
6
8
|
BridgeConfirm,
|
|
7
9
|
BridgeControl,
|
|
8
10
|
type BridgeField,
|
|
@@ -11,6 +13,7 @@ import {
|
|
|
11
13
|
BridgeLayout,
|
|
12
14
|
type BridgeManifest,
|
|
13
15
|
BridgePlace,
|
|
16
|
+
type BridgeSectionUpload,
|
|
14
17
|
type BridgeSetupManifest,
|
|
15
18
|
BridgeSetupStepKind,
|
|
16
19
|
type BridgeTerminalManifest,
|
|
@@ -460,6 +463,143 @@ describe("the upload mirror", () => {
|
|
|
460
463
|
});
|
|
461
464
|
});
|
|
462
465
|
|
|
466
|
+
const MODS_UPLOAD: BridgeSectionUpload = {
|
|
467
|
+
label: text("Upload a mod"),
|
|
468
|
+
extensions: [
|
|
469
|
+
"zip",
|
|
470
|
+
],
|
|
471
|
+
staging: ".serverk-staging/mods-upload",
|
|
472
|
+
mode: BridgeUploadMode.Archive,
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
const catalogManifest = (source?: BridgeCatalogSource, upload?: BridgeSectionUpload): BridgeManifest => ({
|
|
476
|
+
version: "1.0.0",
|
|
477
|
+
panel: {
|
|
478
|
+
tabs: [
|
|
479
|
+
{
|
|
480
|
+
id: "mods",
|
|
481
|
+
title: text("Mods"),
|
|
482
|
+
sections: [
|
|
483
|
+
{
|
|
484
|
+
layout: BridgeLayout.Catalog,
|
|
485
|
+
id: "mods",
|
|
486
|
+
module: "mods",
|
|
487
|
+
...(upload
|
|
488
|
+
? {
|
|
489
|
+
upload,
|
|
490
|
+
}
|
|
491
|
+
: {}),
|
|
492
|
+
},
|
|
493
|
+
],
|
|
494
|
+
},
|
|
495
|
+
],
|
|
496
|
+
},
|
|
497
|
+
terminal: null,
|
|
498
|
+
setup: null,
|
|
499
|
+
modules: [
|
|
500
|
+
{
|
|
501
|
+
id: "mods",
|
|
502
|
+
kind: BridgeKind.Catalog,
|
|
503
|
+
requiresRunning: false,
|
|
504
|
+
refreshSeconds: null,
|
|
505
|
+
pageSize: null,
|
|
506
|
+
dependsOn: [],
|
|
507
|
+
actions: [
|
|
508
|
+
BridgeAction.Preview,
|
|
509
|
+
BridgeAction.Install,
|
|
510
|
+
BridgeAction.Installed,
|
|
511
|
+
BridgeAction.Remove,
|
|
512
|
+
BridgeAction.Search,
|
|
513
|
+
BridgeAction.Add,
|
|
514
|
+
],
|
|
515
|
+
...(source
|
|
516
|
+
? {
|
|
517
|
+
source,
|
|
518
|
+
}
|
|
519
|
+
: {}),
|
|
520
|
+
},
|
|
521
|
+
],
|
|
522
|
+
events: [],
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
const workshopSource = (overrides: Partial<BridgeCatalogSource> = {}): BridgeCatalogSource => ({
|
|
526
|
+
kind: BridgeCatalogSourceKind.Workshop,
|
|
527
|
+
appId: 1_623_730,
|
|
528
|
+
provided: [
|
|
529
|
+
"UE4SS",
|
|
530
|
+
"PalSchema",
|
|
531
|
+
],
|
|
532
|
+
...overrides,
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
describe("the catalog mirror", () => {
|
|
536
|
+
it("carries a sourced catalog and its section's upload through unchanged", () => {
|
|
537
|
+
const source = catalogManifest(workshopSource(), MODS_UPLOAD);
|
|
538
|
+
|
|
539
|
+
expect(bridgeManifestSchema.parse(source)).toEqual(source);
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
it("takes a source that names nothing the image provides", () => {
|
|
543
|
+
expect(
|
|
544
|
+
bridgeManifestSchema.safeParse(
|
|
545
|
+
catalogManifest({
|
|
546
|
+
kind: BridgeCatalogSourceKind.Workshop,
|
|
547
|
+
appId: 1_623_730,
|
|
548
|
+
}),
|
|
549
|
+
).success,
|
|
550
|
+
).toBe(true);
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
it("reads a catalog from an older bridge that carries no source and no upload", () => {
|
|
554
|
+
const parsed = bridgeManifestSchema.parse(catalogManifest());
|
|
555
|
+
|
|
556
|
+
expect(parsed.modules[0]?.source).toBeUndefined();
|
|
557
|
+
expect(parsed.panel?.tabs[0]?.sections[0]).toEqual({
|
|
558
|
+
layout: BridgeLayout.Catalog,
|
|
559
|
+
id: "mods",
|
|
560
|
+
module: "mods",
|
|
561
|
+
});
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
it("refuses a source the platform does not serve", () => {
|
|
565
|
+
const source = catalogManifest(
|
|
566
|
+
workshopSource({
|
|
567
|
+
kind: "nexus" as BridgeCatalogSourceKind,
|
|
568
|
+
}),
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
expect(bridgeManifestSchema.safeParse(source).success).toBe(false);
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it("refuses an app id that is not a positive whole number", () => {
|
|
575
|
+
for (const appId of [
|
|
576
|
+
0,
|
|
577
|
+
-1,
|
|
578
|
+
1.5,
|
|
579
|
+
]) {
|
|
580
|
+
const source = catalogManifest(
|
|
581
|
+
workshopSource({
|
|
582
|
+
appId,
|
|
583
|
+
}),
|
|
584
|
+
);
|
|
585
|
+
|
|
586
|
+
expect(bridgeManifestSchema.safeParse(source).success).toBe(false);
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
it("refuses an empty provided name", () => {
|
|
591
|
+
const source = catalogManifest(
|
|
592
|
+
workshopSource({
|
|
593
|
+
provided: [
|
|
594
|
+
"",
|
|
595
|
+
],
|
|
596
|
+
}),
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
expect(bridgeManifestSchema.safeParse(source).success).toBe(false);
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
|
|
463
603
|
const fieldManifest = (field: BridgeField): BridgeManifest => ({
|
|
464
604
|
version: "1.0.0",
|
|
465
605
|
panel: {
|
|
@@ -521,6 +661,43 @@ describe("secret fields", () => {
|
|
|
521
661
|
});
|
|
522
662
|
});
|
|
523
663
|
|
|
664
|
+
describe("field defaults", () => {
|
|
665
|
+
it("carries a field's default through unchanged", () => {
|
|
666
|
+
const source = fieldManifest({
|
|
667
|
+
key: "WINDOWS_STEAM",
|
|
668
|
+
control: BridgeControl.Boolean,
|
|
669
|
+
label: text("Use Steam"),
|
|
670
|
+
default: "true",
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
expect(bridgeManifestSchema.parse(source)).toEqual(source);
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
it("reads a field from an older bridge that declares no default", () => {
|
|
677
|
+
const parsed = bridgeManifestSchema.parse(
|
|
678
|
+
fieldManifest({
|
|
679
|
+
key: "WINDOWS_STEAM",
|
|
680
|
+
control: BridgeControl.Boolean,
|
|
681
|
+
label: text("Use Steam"),
|
|
682
|
+
}),
|
|
683
|
+
);
|
|
684
|
+
const [section] = parsed.panel?.tabs[0]?.sections ?? [];
|
|
685
|
+
|
|
686
|
+
expect(section?.layout === BridgeLayout.Form ? section.fields[0]?.default : null).toBeUndefined();
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
it("refuses a default longer than the platform takes", () => {
|
|
690
|
+
const source = fieldManifest({
|
|
691
|
+
key: "MOTD",
|
|
692
|
+
control: BridgeControl.Text,
|
|
693
|
+
label: text("Message"),
|
|
694
|
+
default: "a".repeat(257),
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
expect(bridgeManifestSchema.safeParse(source).success).toBe(false);
|
|
698
|
+
});
|
|
699
|
+
});
|
|
700
|
+
|
|
524
701
|
const setupManifest = (setup: BridgeSetupManifest | null): BridgeManifest => ({
|
|
525
702
|
version: "1.0.0",
|
|
526
703
|
panel: null,
|
package/src/wire/panel.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { guideOpenTargetSchema } from "@serverkgg/bridge/guides";
|
|
2
2
|
import {
|
|
3
|
+
BRIDGE_CATALOG_PROVIDED_LIMIT,
|
|
4
|
+
BRIDGE_DEFAULT_LENGTH_LIMIT,
|
|
3
5
|
BRIDGE_ID_PATTERN,
|
|
4
6
|
BRIDGE_MODULE_ID_PATTERN,
|
|
5
7
|
BRIDGE_PATTERN_LENGTH_LIMIT,
|
|
8
|
+
BRIDGE_PROVIDED_NAME_LENGTH_LIMIT,
|
|
6
9
|
BridgeAccess,
|
|
10
|
+
BridgeCatalogSourceKind,
|
|
7
11
|
BridgeConfirm,
|
|
8
12
|
BridgeControl,
|
|
9
13
|
BridgeFormTarget,
|
|
@@ -120,6 +124,7 @@ export const bridgeFieldSchema = z.object({
|
|
|
120
124
|
help: panelTextSchema.optional(),
|
|
121
125
|
warning: panelTextSchema.optional(),
|
|
122
126
|
placeholder: z.string().max(256).optional(),
|
|
127
|
+
default: z.string().max(BRIDGE_DEFAULT_LENGTH_LIMIT).optional(),
|
|
123
128
|
min: z.number().optional(),
|
|
124
129
|
max: z.number().optional(),
|
|
125
130
|
step: z.number().optional(),
|
|
@@ -263,6 +268,7 @@ export const bridgeCatalogSectionSchema = z.object({
|
|
|
263
268
|
variable: zEnvKey.optional(),
|
|
264
269
|
confirm: z.enum(BridgeConfirm).optional(),
|
|
265
270
|
confirmText: panelTextSchema.optional(),
|
|
271
|
+
upload: bridgeSectionUploadSchema.optional(),
|
|
266
272
|
empty: panelTextSchema.optional(),
|
|
267
273
|
visibleWhen: bridgeVisibleWhenSchema.optional(),
|
|
268
274
|
access: z.enum(BridgeAccess).optional(),
|
|
@@ -352,6 +358,15 @@ export const bridgeSetupManifestSchema: z.ZodType<BridgeSetupManifest> = z.objec
|
|
|
352
358
|
steps: z.array(bridgeSetupStepSchema).min(1).max(SETUP_STEPS_LIMIT),
|
|
353
359
|
});
|
|
354
360
|
|
|
361
|
+
export const bridgeCatalogSourceSchema = z.object({
|
|
362
|
+
kind: z.enum(BridgeCatalogSourceKind),
|
|
363
|
+
appId: z.int().positive(),
|
|
364
|
+
provided: z
|
|
365
|
+
.array(z.string().min(1).max(BRIDGE_PROVIDED_NAME_LENGTH_LIMIT))
|
|
366
|
+
.max(BRIDGE_CATALOG_PROVIDED_LIMIT)
|
|
367
|
+
.optional(),
|
|
368
|
+
});
|
|
369
|
+
|
|
355
370
|
export const bridgeModuleManifestSchema = z.object({
|
|
356
371
|
id: zBridgeModuleId,
|
|
357
372
|
kind: z.enum(BridgeKind),
|
|
@@ -361,6 +376,7 @@ export const bridgeModuleManifestSchema = z.object({
|
|
|
361
376
|
dependsOn: z.array(zKey).max(8),
|
|
362
377
|
actions: z.array(z.string().min(1).max(64)).max(16),
|
|
363
378
|
protectedActions: z.array(z.string().min(1).max(64)).max(16).optional(),
|
|
379
|
+
source: bridgeCatalogSourceSchema.optional(),
|
|
364
380
|
});
|
|
365
381
|
|
|
366
382
|
export const bridgeManifestSchema: z.ZodType<BridgeManifest> = z.object({
|