@serverkgg/bridge 0.0.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.
Files changed (73) hide show
  1. package/LICENSE.md +22 -0
  2. package/README.md +52 -0
  3. package/package.json +26 -0
  4. package/src/cli/compile.ts +75 -0
  5. package/src/cli/directorySource.ts +48 -0
  6. package/src/cli/main.ts +44 -0
  7. package/src/cli/validate.ts +94 -0
  8. package/src/codecs/index.ts +3 -0
  9. package/src/codecs/json.ts +42 -0
  10. package/src/codecs/properties.ts +75 -0
  11. package/src/codecs/ueIni.ts +235 -0
  12. package/src/context/bridgeContext.ts +120 -0
  13. package/src/context/index.ts +1 -0
  14. package/src/core/bridgeAction.ts +13 -0
  15. package/src/core/bridgeId.ts +3 -0
  16. package/src/core/bridgeKind.ts +11 -0
  17. package/src/core/bridgePort.ts +4 -0
  18. package/src/core/bridgeRuntime.ts +13 -0
  19. package/src/core/bridgeSecretError.ts +10 -0
  20. package/src/core/bridgeStream.ts +4 -0
  21. package/src/core/bridgeValue.ts +19 -0
  22. package/src/core/index.ts +8 -0
  23. package/src/main.ts +5 -0
  24. package/src/manifest/assets.ts +102 -0
  25. package/src/manifest/category.ts +10 -0
  26. package/src/manifest/checks.ts +50 -0
  27. package/src/manifest/dimensions.ts +106 -0
  28. package/src/manifest/guide.ts +36 -0
  29. package/src/manifest/image.ts +1 -0
  30. package/src/manifest/index.ts +9 -0
  31. package/src/manifest/localized.ts +8 -0
  32. package/src/manifest/manifest.ts +170 -0
  33. package/src/manifest/source.ts +89 -0
  34. package/src/modules/bridge.ts +53 -0
  35. package/src/modules/bridgeDriver.ts +19 -0
  36. package/src/modules/bridgeModule.ts +114 -0
  37. package/src/modules/index.ts +3 -0
  38. package/src/panel/bridgeConfirm.ts +5 -0
  39. package/src/panel/bridgeControl.ts +7 -0
  40. package/src/panel/bridgeFormTarget.ts +4 -0
  41. package/src/panel/bridgeIcon.ts +17 -0
  42. package/src/panel/bridgeLayout.ts +6 -0
  43. package/src/panel/bridgePanel.ts +107 -0
  44. package/src/panel/index.ts +6 -0
  45. package/src/probes/a2s.ts +170 -0
  46. package/src/probes/index.ts +1 -0
  47. package/src/protocol/bridgeConfig.ts +13 -0
  48. package/src/protocol/bridgeFrame.ts +91 -0
  49. package/src/protocol/bridgeLine.ts +40 -0
  50. package/src/protocol/bridgeManifest.ts +19 -0
  51. package/src/protocol/bridgeVersion.ts +11 -0
  52. package/src/protocol/index.ts +7 -0
  53. package/src/runtime/bridgeCodec.ts +78 -0
  54. package/src/runtime/bridgeConfigReader.ts +119 -0
  55. package/src/runtime/bridgeContext.ts +96 -0
  56. package/src/runtime/bridgeDriver.ts +179 -0
  57. package/src/runtime/bridgeEvents.ts +71 -0
  58. package/src/runtime/bridgeExec.ts +143 -0
  59. package/src/runtime/bridgeFiles.ts +412 -0
  60. package/src/runtime/bridgeLog.ts +15 -0
  61. package/src/runtime/bridgeNet.ts +59 -0
  62. package/src/runtime/bridgePath.ts +132 -0
  63. package/src/runtime/bridgePrivilege.ts +99 -0
  64. package/src/runtime/bridgeProbe.ts +14 -0
  65. package/src/runtime/bridgeRegistry.ts +311 -0
  66. package/src/runtime/bridgeServer.ts +233 -0
  67. package/src/runtime/bridgeState.ts +40 -0
  68. package/src/runtime/bridgeSupervisor.ts +498 -0
  69. package/src/runtime/bridgeZip.ts +122 -0
  70. package/src/runtime/index.ts +18 -0
  71. package/src/runtime/serve.ts +294 -0
  72. package/src/utils/glob.ts +62 -0
  73. package/src/utils/index.ts +1 -0
@@ -0,0 +1,106 @@
1
+ export const ASSET_MAX_EDGE = 4096;
2
+
3
+ export interface AssetDimensions {
4
+ width: number;
5
+ height: number;
6
+ }
7
+
8
+ const readUint16 = (bytes: Uint8Array, offset: number) => {
9
+ const a = bytes[offset];
10
+ const b = bytes[offset + 1];
11
+
12
+ if (a === undefined || b === undefined) {
13
+ return null;
14
+ }
15
+
16
+ return (b << 8) + a;
17
+ };
18
+
19
+ const readUint24 = (bytes: Uint8Array, offset: number) => {
20
+ const a = bytes[offset];
21
+ const b = bytes[offset + 1];
22
+ const c = bytes[offset + 2];
23
+
24
+ if (a === undefined || b === undefined || c === undefined) {
25
+ return null;
26
+ }
27
+
28
+ return (c << 16) + (b << 8) + a;
29
+ };
30
+
31
+ const readUint32 = (bytes: Uint8Array, offset: number) => {
32
+ const a = bytes[offset];
33
+ const b = bytes[offset + 1];
34
+ const c = bytes[offset + 2];
35
+ const d = bytes[offset + 3];
36
+
37
+ if (a === undefined || b === undefined || c === undefined || d === undefined) {
38
+ return null;
39
+ }
40
+
41
+ return a + (b << 8) + (c << 16) + d * 0x1_00_00_00;
42
+ };
43
+
44
+ const lossyDimensions = (bytes: Uint8Array): AssetDimensions | null => {
45
+ const width = readUint16(bytes, 26);
46
+ const height = readUint16(bytes, 28);
47
+
48
+ if (width === null || height === null) {
49
+ return null;
50
+ }
51
+
52
+ return {
53
+ width: width & 0x3f_ff,
54
+ height: height & 0x3f_ff,
55
+ };
56
+ };
57
+
58
+ const losslessDimensions = (bytes: Uint8Array): AssetDimensions | null => {
59
+ const packed = readUint32(bytes, 21);
60
+
61
+ if (packed === null) {
62
+ return null;
63
+ }
64
+
65
+ return {
66
+ width: (packed & 0x3f_ff) + 1,
67
+ height: ((packed >>> 14) & 0x3f_ff) + 1,
68
+ };
69
+ };
70
+
71
+ const extendedDimensions = (bytes: Uint8Array): AssetDimensions | null => {
72
+ const width = readUint24(bytes, 24);
73
+ const height = readUint24(bytes, 27);
74
+
75
+ if (width === null || height === null) {
76
+ return null;
77
+ }
78
+
79
+ return {
80
+ width: width + 1,
81
+ height: height + 1,
82
+ };
83
+ };
84
+
85
+ const READERS: Record<string, (bytes: Uint8Array) => AssetDimensions | null> = {
86
+ "VP8 ": lossyDimensions,
87
+ VP8L: losslessDimensions,
88
+ VP8X: extendedDimensions,
89
+ };
90
+
91
+ export const readDimensions = (bytes: Uint8Array): AssetDimensions | null => {
92
+ const format = new TextDecoder().decode(bytes.subarray(12, 16));
93
+ const reader = READERS[format];
94
+
95
+ if (!reader) {
96
+ return null;
97
+ }
98
+
99
+ const dimensions = reader(bytes);
100
+
101
+ if (!dimensions || dimensions.width <= 0 || dimensions.height <= 0) {
102
+ return null;
103
+ }
104
+
105
+ return dimensions;
106
+ };
@@ -0,0 +1,36 @@
1
+ import { z } from "zod";
2
+ import { localizedTextSchema } from "./localized";
3
+
4
+ export const zGuideId = z
5
+ .string()
6
+ .min(1)
7
+ .max(64)
8
+ .regex(/^[a-z0-9][a-z0-9-]*$/);
9
+
10
+ export const zGuideTag = z
11
+ .string()
12
+ .min(1)
13
+ .max(32)
14
+ .regex(/^[a-z0-9][a-z0-9._-]*$/);
15
+
16
+ export const zYoutubeId = z.string().regex(/^[A-Za-z0-9_-]{11}$/);
17
+
18
+ const zMarkdownPath = z
19
+ .string()
20
+ .min(1)
21
+ .max(256)
22
+ .regex(/^[a-zA-Z0-9][a-zA-Z0-9/_-]*\.md$/);
23
+
24
+ export const guideEntrySchema = z.object({
25
+ id: zGuideId,
26
+ title: localizedTextSchema,
27
+ summary: localizedTextSchema,
28
+ tags: z.array(zGuideTag).max(8).default([]),
29
+ video: zYoutubeId.optional(),
30
+ body: z.object({
31
+ ar: zMarkdownPath,
32
+ en: zMarkdownPath,
33
+ }),
34
+ });
35
+
36
+ export type GuideEntry = z.infer<typeof guideEntrySchema>;
@@ -0,0 +1 @@
1
+ export const IMAGE_NAMESPACE = "serverkgg";
@@ -0,0 +1,9 @@
1
+ export * from "./assets";
2
+ export * from "./category";
3
+ export * from "./checks";
4
+ export * from "./dimensions";
5
+ export * from "./guide";
6
+ export * from "./image";
7
+ export * from "./localized";
8
+ export * from "./manifest";
9
+ export * from "./source";
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+
3
+ export const localizedTextSchema = z.object({
4
+ ar: z.string().min(1),
5
+ en: z.string().min(1),
6
+ });
7
+
8
+ export type LocalizedText = z.infer<typeof localizedTextSchema>;
@@ -0,0 +1,170 @@
1
+ import { z } from "zod";
2
+ import { PortProtocol } from "../core";
3
+ import { ASSET_EXTENSION } from "./assets";
4
+ import { GameCategory } from "./category";
5
+ import { guideEntrySchema } from "./guide";
6
+ import { IMAGE_NAMESPACE } from "./image";
7
+ import { localizedTextSchema } from "./localized";
8
+
9
+ export const MANIFEST_API_VERSION = "serverk.gg/v1";
10
+
11
+ export const BACKUP_SETTLE_SECONDS_DEFAULT = 5;
12
+
13
+ const zRelativePath = z
14
+ .string()
15
+ .min(1)
16
+ .max(256)
17
+ .regex(/^[a-zA-Z0-9][a-zA-Z0-9/_-]*\.[a-z0-9]+$/);
18
+
19
+ const zAssetPath = zRelativePath.refine((value) => {
20
+ return value.endsWith(`.${ASSET_EXTENSION}`);
21
+ });
22
+
23
+ export const MANIFEST_HOST_PORT_MIN = 1024;
24
+
25
+ export const MANIFEST_HOST_PORT_SPAN = 1000;
26
+
27
+ export const manifestPortHostSchema = z
28
+ .object({
29
+ default: z.int().min(MANIFEST_HOST_PORT_MIN).max(65_535),
30
+ min: z.int().min(MANIFEST_HOST_PORT_MIN).max(65_535),
31
+ max: z.int().min(MANIFEST_HOST_PORT_MIN).max(65_535),
32
+ })
33
+ .refine((host) => host.min <= host.max)
34
+ .refine((host) => host.min <= host.default && host.default <= host.max)
35
+ .refine((host) => host.max - host.min + 1 <= MANIFEST_HOST_PORT_SPAN);
36
+
37
+ export type ManifestPortHost = z.infer<typeof manifestPortHostSchema>;
38
+
39
+ export const manifestPortSchema = z.object({
40
+ key: z
41
+ .string()
42
+ .min(1)
43
+ .max(32)
44
+ .regex(/^[a-z][a-z0-9-]*$/),
45
+ port: z.int().min(1).max(65_535),
46
+ protocol: z.enum(PortProtocol).default(PortProtocol.Tcp),
47
+ host: manifestPortHostSchema,
48
+ });
49
+
50
+ export type ManifestPort = z.infer<typeof manifestPortSchema>;
51
+
52
+ const zImageReference = z
53
+ .string()
54
+ .min(1)
55
+ .max(256)
56
+ .regex(new RegExp(`^${IMAGE_NAMESPACE}/[a-z0-9][a-z0-9-]*$`));
57
+
58
+ export const manifestRuntimeSchema = z.object({
59
+ image: zImageReference,
60
+ ports: z.array(manifestPortSchema).min(1).max(8),
61
+ });
62
+
63
+ export const manifestMetadataSchema = z.object({
64
+ id: z
65
+ .string()
66
+ .min(1)
67
+ .max(64)
68
+ .regex(/^[a-z0-9][a-z0-9-]*$/),
69
+ name: localizedTextSchema,
70
+ description: localizedTextSchema,
71
+ tagline: localizedTextSchema,
72
+ category: z.enum(GameCategory),
73
+ logo: zAssetPath,
74
+ banner: zAssetPath,
75
+ });
76
+
77
+ export const manifestResourcesSchema = z
78
+ .object({
79
+ minMemoryMb: z.int().min(128).max(1_048_576),
80
+ recommendedMemoryMb: z.int().min(128).max(1_048_576),
81
+ minDiskMb: z.int().min(512),
82
+ playersPerGb: z.int().min(1).max(1024).optional(),
83
+ maxPlayers: z.int().min(1).max(100_000).optional(),
84
+ })
85
+ .refine((resources) => resources.recommendedMemoryMb >= resources.minMemoryMb);
86
+
87
+ const zRelativeGlob = z
88
+ .string()
89
+ .min(1)
90
+ .max(256)
91
+ .regex(/^[^/\\][^\\]*$/)
92
+ .refine((value) => !value.split("/").includes(".."));
93
+
94
+ export const manifestQuiesceSchema = z.object({
95
+ before: z.string().max(256).optional(),
96
+ flush: z.string().max(256).optional(),
97
+ after: z.string().max(256).optional(),
98
+ await: z.string().max(256).optional(),
99
+ settleSeconds: z.int().min(0).max(120).default(BACKUP_SETTLE_SECONDS_DEFAULT),
100
+ });
101
+
102
+ export const manifestBackupSchema = z.object({
103
+ include: z.array(zRelativeGlob).max(32).default([]),
104
+ exclude: z.array(zRelativeGlob).max(64).default([]),
105
+ quiesce: manifestQuiesceSchema.optional(),
106
+ });
107
+
108
+ export const manifestResetSchema = z.object({
109
+ keep: z.array(zRelativeGlob).max(32).default([]),
110
+ });
111
+
112
+ const zEnvKey = z
113
+ .string()
114
+ .min(2)
115
+ .max(32)
116
+ .regex(/^[A-Z][A-Z0-9_]*$/);
117
+
118
+ export const manifestSecretSchema = z.object({
119
+ key: zEnvKey,
120
+ description: localizedTextSchema,
121
+ required: z.boolean().default(true),
122
+ });
123
+
124
+ export type ManifestSecret = z.infer<typeof manifestSecretSchema>;
125
+
126
+ export const manifestFilesSchema = z.object({
127
+ hidden: z
128
+ .array(
129
+ z
130
+ .string()
131
+ .min(1)
132
+ .max(128)
133
+ .regex(/^[^/\\][^\\]*$/)
134
+ .refine((value) => !value.split("/").includes("..")),
135
+ )
136
+ .max(64)
137
+ .default([]),
138
+ });
139
+
140
+ export const manifestSchema = z.object({
141
+ apiVersion: z.literal(MANIFEST_API_VERSION),
142
+ kind: z.literal("GamePackage"),
143
+ metadata: manifestMetadataSchema,
144
+ resources: manifestResourcesSchema,
145
+ runtime: manifestRuntimeSchema,
146
+ guides: z.array(guideEntrySchema).max(64).default([]),
147
+ backup: manifestBackupSchema.optional(),
148
+ reset: manifestResetSchema.optional(),
149
+ secrets: z.array(manifestSecretSchema).max(16).default([]),
150
+ files: manifestFilesSchema.default({
151
+ hidden: [],
152
+ }),
153
+ });
154
+
155
+ export type Manifest = z.infer<typeof manifestSchema>;
156
+
157
+ export const guidePackageSchema = z.object({
158
+ apiVersion: z.literal(MANIFEST_API_VERSION),
159
+ kind: z.literal("GuidePackage"),
160
+ metadata: z.object({
161
+ id: z
162
+ .string()
163
+ .min(1)
164
+ .max(64)
165
+ .regex(/^[a-z0-9][a-z0-9-]*$/),
166
+ }),
167
+ guides: z.array(guideEntrySchema).min(1).max(64),
168
+ });
169
+
170
+ export type GuidePackage = z.infer<typeof guidePackageSchema>;
@@ -0,0 +1,89 @@
1
+ import { verifyAsset } from "./assets";
2
+ import { checkManifest } from "./checks";
3
+ import { type Manifest, manifestSchema } from "./manifest";
4
+
5
+ export const MANIFEST_FILE = "serverk.yml";
6
+
7
+ export interface PackageSource {
8
+ list(): string[];
9
+ read(path: string): Uint8Array | null;
10
+ }
11
+
12
+ const readText = (source: PackageSource, path: string) => {
13
+ const bytes = source.read(path);
14
+
15
+ if (!bytes) {
16
+ throw new Error(`missing ${path}`);
17
+ }
18
+
19
+ return new TextDecoder().decode(bytes);
20
+ };
21
+
22
+ export const readYaml = (source: PackageSource, path: string) => {
23
+ return Bun.YAML.parse(readText(source, path));
24
+ };
25
+
26
+ const readAsset = (source: PackageSource, asset: string) => {
27
+ const bytes = source.read(asset);
28
+
29
+ if (!bytes) {
30
+ throw new Error(`missing asset ${asset}`);
31
+ }
32
+
33
+ const { mediaType, dimensions } = verifyAsset(asset, bytes);
34
+
35
+ return {
36
+ path: asset,
37
+ bytes,
38
+ mediaType,
39
+ dimensions,
40
+ };
41
+ };
42
+
43
+ export const readAssets = (source: PackageSource, manifest: Manifest) => {
44
+ return {
45
+ logo: readAsset(source, manifest.metadata.logo),
46
+ banner: readAsset(source, manifest.metadata.banner),
47
+ };
48
+ };
49
+
50
+ export const declaredFiles = (manifest: Manifest, guideFiles: string[] = []) => {
51
+ return [
52
+ MANIFEST_FILE,
53
+ manifest.metadata.logo,
54
+ manifest.metadata.banner,
55
+ ...guideFiles,
56
+ ];
57
+ };
58
+
59
+ export const computeBundleDigest = (source: PackageSource, paths: string[]) => {
60
+ const entries = [
61
+ ...new Set(paths),
62
+ ].sort();
63
+ const bundleHasher = new Bun.CryptoHasher("sha256");
64
+
65
+ for (const entry of entries) {
66
+ const bytes = source.read(entry);
67
+
68
+ if (!bytes) {
69
+ continue;
70
+ }
71
+
72
+ const fileHasher = new Bun.CryptoHasher("sha256");
73
+
74
+ fileHasher.update(bytes);
75
+ bundleHasher.update(`${entry}:${fileHasher.digest("hex")}\n`);
76
+ }
77
+
78
+ return `sha256:${bundleHasher.digest("hex")}`;
79
+ };
80
+
81
+ export const readPackage = (source: PackageSource) => {
82
+ const manifest: Manifest = manifestSchema.parse(readYaml(source, MANIFEST_FILE));
83
+
84
+ checkManifest(manifest);
85
+
86
+ return {
87
+ manifest,
88
+ };
89
+ };
@@ -0,0 +1,53 @@
1
+ import type { BridgeContext, BridgeExecResult, BridgeFileEntry, BridgeReply } from "../context";
2
+ import type { BridgeOption, BridgeRow, BridgeValue, BridgeValues, LocalizedText } from "../core";
3
+ import type { BridgeField, BridgePanel, BridgeSection, BridgeTab } from "../panel";
4
+ import type { BridgeDriver } from "./bridgeDriver";
5
+ import type {
6
+ BridgeBackupModule,
7
+ BridgeCatalogEntry,
8
+ BridgeCatalogHit,
9
+ BridgeCatalogModule,
10
+ BridgeCatalogPage,
11
+ BridgeCollectionModule,
12
+ BridgeEventPattern,
13
+ BridgeEventsModule,
14
+ BridgeInstallModule,
15
+ BridgeLifecycleModule,
16
+ BridgeOptionsModule,
17
+ BridgeQueryModule,
18
+ BridgeRowAction,
19
+ BridgeSample,
20
+ BridgeSettingsModule,
21
+ } from "./bridgeModule";
22
+
23
+ export namespace Bridge {
24
+ export type Backup = BridgeBackupModule;
25
+ export type Catalog = BridgeCatalogModule;
26
+ export type CatalogEntry = BridgeCatalogEntry;
27
+ export type CatalogHit = BridgeCatalogHit;
28
+ export type CatalogPage = BridgeCatalogPage;
29
+ export type Collection<Row extends BridgeRow = BridgeRow> = BridgeCollectionModule<Row>;
30
+ export type Context = BridgeContext;
31
+ export type Driver = BridgeDriver;
32
+ export type EventPattern = BridgeEventPattern;
33
+ export type Events = BridgeEventsModule;
34
+ export type ExecResult = BridgeExecResult;
35
+ export type Field = BridgeField;
36
+ export type FileEntry = BridgeFileEntry;
37
+ export type Install = BridgeInstallModule;
38
+ export type Lifecycle = BridgeLifecycleModule;
39
+ export type Option = BridgeOption;
40
+ export type Options = BridgeOptionsModule;
41
+ export type Panel = BridgePanel;
42
+ export type Query = BridgeQueryModule;
43
+ export type Reply = BridgeReply;
44
+ export type Row = BridgeRow;
45
+ export type RowAction<Row extends BridgeRow = BridgeRow> = BridgeRowAction<Row>;
46
+ export type Sample = BridgeSample;
47
+ export type Section = BridgeSection;
48
+ export type Settings = BridgeSettingsModule;
49
+ export type Tab = BridgeTab;
50
+ export type Text = LocalizedText;
51
+ export type Value = BridgeValue;
52
+ export type Values = BridgeValues;
53
+ }
@@ -0,0 +1,19 @@
1
+ import type { BridgePanel } from "../panel";
2
+ import type {
3
+ BridgeBackupModule,
4
+ BridgeEventsModule,
5
+ BridgeInstallModule,
6
+ BridgeLifecycleModule,
7
+ BridgePanelModule,
8
+ BridgeQueryModule,
9
+ } from "./bridgeModule";
10
+
11
+ export interface BridgeDriver {
12
+ install: BridgeInstallModule;
13
+ lifecycle: BridgeLifecycleModule;
14
+ events?: BridgeEventsModule;
15
+ query?: BridgeQueryModule;
16
+ backup?: BridgeBackupModule;
17
+ panel?: BridgePanel;
18
+ modules?: Record<string, BridgePanelModule>;
19
+ }
@@ -0,0 +1,114 @@
1
+ import type { BridgeContext } from "../context";
2
+ import type { BridgeKind, BridgeOption, BridgeRow, BridgeStream, BridgeValues } from "../core";
3
+
4
+ export interface BridgeInstallModule {
5
+ kind: BridgeKind.Install;
6
+ run(context: BridgeContext): Promise<void>;
7
+ }
8
+
9
+ export interface BridgeLifecycleModule {
10
+ kind: BridgeKind.Lifecycle;
11
+ ready?: RegExp;
12
+ stopTimeoutSeconds?: number;
13
+ command(context: BridgeContext): Promise<string[]>;
14
+ stop?(context: BridgeContext): Promise<void>;
15
+ onReady?(context: BridgeContext): Promise<void>;
16
+ }
17
+
18
+ export interface BridgeBackupModule {
19
+ kind: BridgeKind.Backup;
20
+ settleSeconds?: number;
21
+ quiesce?(context: BridgeContext): Promise<void>;
22
+ release?(context: BridgeContext): Promise<void>;
23
+ }
24
+
25
+ export interface BridgeSample {
26
+ online: number | null;
27
+ max: number | null;
28
+ }
29
+
30
+ export interface BridgeQueryModule {
31
+ kind: BridgeKind.Query;
32
+ refreshSeconds?: number;
33
+ sample(context: BridgeContext): Promise<BridgeSample>;
34
+ }
35
+
36
+ export interface BridgeEventPattern {
37
+ match: RegExp;
38
+ emit: string;
39
+ stream?: BridgeStream;
40
+ }
41
+
42
+ export interface BridgeEventsModule {
43
+ kind: BridgeKind.Events;
44
+ patterns: BridgeEventPattern[];
45
+ }
46
+
47
+ export interface BridgeOptionsModule {
48
+ kind: BridgeKind.Options;
49
+ dependsOn?: string[];
50
+ ttlSeconds?: number;
51
+ list(context: BridgeContext): Promise<BridgeOption[]>;
52
+ }
53
+
54
+ export interface BridgeSettingsModule {
55
+ kind: BridgeKind.Settings;
56
+ read(context: BridgeContext): Promise<BridgeValues>;
57
+ write?(context: BridgeContext, values: BridgeValues): Promise<void>;
58
+ }
59
+
60
+ export type BridgeRowAction<Row extends BridgeRow = BridgeRow> = (context: BridgeContext, row: Row) => Promise<void>;
61
+
62
+ export interface BridgeCollectionModule<Row extends BridgeRow = BridgeRow> {
63
+ kind: BridgeKind.Collection;
64
+ requiresRunning?: boolean;
65
+ refreshSeconds?: number;
66
+ list(context: BridgeContext): Promise<Row[]>;
67
+ add?(context: BridgeContext, input: string): Promise<void>;
68
+ actions?: Record<string, BridgeRowAction<Row>>;
69
+ }
70
+
71
+ export interface BridgeCatalogHit {
72
+ id: string;
73
+ title: string;
74
+ description: string;
75
+ icon: string | null;
76
+ downloads: number;
77
+ pageUrl?: string | null;
78
+ }
79
+
80
+ export interface BridgeCatalogPage {
81
+ hits: BridgeCatalogHit[];
82
+ total: number;
83
+ }
84
+
85
+ export interface BridgeCatalogEntry {
86
+ id: string;
87
+ path: string;
88
+ title: string;
89
+ version: string | null;
90
+ sizeBytes: number;
91
+ }
92
+
93
+ export interface BridgeCatalogModule {
94
+ kind: BridgeKind.Catalog;
95
+ pageSize?: number;
96
+ search(context: BridgeContext, query: string, page: number): Promise<BridgeCatalogPage>;
97
+ installed(context: BridgeContext): Promise<BridgeCatalogEntry[]>;
98
+ install(context: BridgeContext, id: string): Promise<BridgeCatalogEntry>;
99
+ remove(context: BridgeContext, id: string): Promise<void>;
100
+ }
101
+
102
+ export type BridgePanelModule =
103
+ | BridgeCatalogModule
104
+ | BridgeCollectionModule
105
+ | BridgeOptionsModule
106
+ | BridgeSettingsModule;
107
+
108
+ export type BridgeModule =
109
+ | BridgeBackupModule
110
+ | BridgeEventsModule
111
+ | BridgeInstallModule
112
+ | BridgeLifecycleModule
113
+ | BridgePanelModule
114
+ | BridgeQueryModule;
@@ -0,0 +1,3 @@
1
+ export * from "./bridge";
2
+ export * from "./bridgeDriver";
3
+ export * from "./bridgeModule";
@@ -0,0 +1,5 @@
1
+ export enum BridgeConfirm {
2
+ None = "none",
3
+ Normal = "normal",
4
+ Strong = "strong",
5
+ }
@@ -0,0 +1,7 @@
1
+ export enum BridgeControl {
2
+ Boolean = "boolean",
3
+ Number = "number",
4
+ Select = "select",
5
+ Slider = "slider",
6
+ Text = "text",
7
+ }
@@ -0,0 +1,4 @@
1
+ export enum BridgeFormTarget {
2
+ Settings = "settings",
3
+ Variables = "variables",
4
+ }
@@ -0,0 +1,17 @@
1
+ export enum BridgeIcon {
2
+ Box = "box",
3
+ Chart = "chart",
4
+ Command = "command",
5
+ Download = "download",
6
+ Gamepad = "gamepad",
7
+ Globe = "globe",
8
+ Map = "map",
9
+ Puzzle = "puzzle",
10
+ Rocket = "rocket",
11
+ Settings = "settings",
12
+ Shield = "shield",
13
+ Sword = "sword",
14
+ Tag = "tag",
15
+ Users = "users",
16
+ Wrench = "wrench",
17
+ }
@@ -0,0 +1,6 @@
1
+ export enum BridgeLayout {
2
+ Cards = "cards",
3
+ Catalog = "catalog",
4
+ Form = "form",
5
+ Table = "table",
6
+ }