@serverkgg/bridge 0.0.8 → 0.0.9

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 (48) hide show
  1. package/package.json +3 -3
  2. package/src/cli/validate.ts +1 -0
  3. package/src/context/bridgeContext.ts +10 -0
  4. package/src/context/index.ts +1 -0
  5. package/src/core/bridgeDownloadError.ts +10 -0
  6. package/src/core/bridgeHold.ts +12 -0
  7. package/src/core/bridgeKind.ts +2 -0
  8. package/src/core/bridgeUserError.test.ts +23 -0
  9. package/src/core/bridgeUserError.ts +12 -0
  10. package/src/core/index.ts +3 -0
  11. package/src/manifest/index.ts +1 -0
  12. package/src/manifest/manifest.test.ts +85 -0
  13. package/src/manifest/manifest.ts +21 -0
  14. package/src/manifest/presence.ts +48 -0
  15. package/src/manifest/primitives.ts +6 -0
  16. package/src/modules/bridge.ts +45 -2
  17. package/src/modules/bridgeDriver.ts +2 -0
  18. package/src/modules/bridgeModule.ts +25 -3
  19. package/src/panel/bridgeDetail.ts +49 -0
  20. package/src/panel/bridgeLayout.ts +1 -0
  21. package/src/panel/bridgePanel.ts +30 -0
  22. package/src/panel/bridgeUploadMode.ts +4 -0
  23. package/src/panel/index.ts +2 -0
  24. package/src/protocol/bridgeEgress.ts +8 -1
  25. package/src/protocol/bridgeEvents.ts +4 -0
  26. package/src/protocol/bridgeFrame.ts +28 -1
  27. package/src/protocol/bridgeLimits.ts +10 -0
  28. package/src/runtime/bridgeActions.ts +7 -0
  29. package/src/runtime/bridgeDriver.test.ts +477 -2
  30. package/src/runtime/bridgeDriver.ts +116 -3
  31. package/src/runtime/bridgeEgress.ts +3 -0
  32. package/src/runtime/bridgeEvents.ts +7 -5
  33. package/src/runtime/bridgeFiles.test.ts +625 -15
  34. package/src/runtime/bridgeFiles.ts +152 -45
  35. package/src/runtime/bridgeInstall.test.ts +118 -0
  36. package/src/runtime/bridgeInstall.ts +55 -0
  37. package/src/runtime/bridgeNet.ts +32 -3
  38. package/src/runtime/bridgePending.test.ts +148 -0
  39. package/src/runtime/bridgePending.ts +43 -0
  40. package/src/runtime/bridgeRegistry.test.ts +134 -0
  41. package/src/runtime/bridgeRegistry.ts +26 -4
  42. package/src/runtime/bridgeServer.test.ts +338 -0
  43. package/src/runtime/bridgeServer.ts +25 -3
  44. package/src/runtime/bridgeSupervisor.ts +18 -0
  45. package/src/runtime/bridgeZip.ts +179 -58
  46. package/src/runtime/serve.ts +64 -4
  47. package/src/wire/panel.test.ts +166 -0
  48. package/src/wire/panel.ts +32 -0
@@ -8,6 +8,7 @@ import {
8
8
  type BridgeSection,
9
9
  type BridgeSectionAction,
10
10
  type BridgeTab,
11
+ BridgeUploadMode,
11
12
  type BridgeVisibleWhen,
12
13
  } from "../panel";
13
14
  import {
@@ -16,6 +17,7 @@ import {
16
17
  BRIDGE_EVENTS_LIMIT,
17
18
  BRIDGE_EXTENSION_LENGTH_LIMIT,
18
19
  BRIDGE_FIELD_OPTIONS_LIMIT,
20
+ BRIDGE_FORM_CLEARS_LIMIT,
19
21
  BRIDGE_FORM_FIELDS_LIMIT,
20
22
  BRIDGE_ID_LENGTH_LIMIT,
21
23
  BRIDGE_KEY_LENGTH_LIMIT,
@@ -35,12 +37,15 @@ import {
35
37
  } from "../protocol";
36
38
  import { actionsOf } from "./bridgeActions";
37
39
 
40
+ const UPLOAD_MODES = new Set<string>(Object.values(BridgeUploadMode));
41
+
38
42
  const RESERVED_MODULE_IDS = new Set([
39
43
  "announce",
40
44
  "backup",
41
45
  "events",
42
46
  "install",
43
47
  "lifecycle",
48
+ "pending",
44
49
  "query",
45
50
  ]);
46
51
 
@@ -259,6 +264,58 @@ const validateSection = (modules: Record<string, BridgePanelModule>, section: Br
259
264
  countIn(section.fields.length, BRIDGE_FORM_FIELDS_LIMIT, "fields", where);
260
265
  validateFields(modules, section.fields, where);
261
266
 
267
+ const clears = section.clears ?? [];
268
+
269
+ if (clears.length > 0 && section.target !== BridgeFormTarget.Variables) {
270
+ fail(`${where} clears variables but does not target variables`);
271
+ }
272
+
273
+ countIn(clears.length, BRIDGE_FORM_CLEARS_LIMIT, "cleared variables", where);
274
+
275
+ const cleared = new Set<string>();
276
+
277
+ for (const clear of clears) {
278
+ if (cleared.has(clear.variable)) {
279
+ fail(`${where} clears "${clear.variable}" more than once`);
280
+ }
281
+
282
+ cleared.add(clear.variable);
283
+
284
+ if (section.fields.some((field) => field.key === clear.variable)) {
285
+ fail(`${where} clears "${clear.variable}", which is one of its own fields`);
286
+ }
287
+
288
+ validateText(clear.warning, `clear "${clear.variable}" warning`, where);
289
+ }
290
+
291
+ return;
292
+ }
293
+
294
+ case BridgeLayout.Detail: {
295
+ const module = moduleOf(modules, section.module, where);
296
+
297
+ if (module.kind !== BridgeKind.Detail) {
298
+ fail(`${where} references "${section.module}", which is not a detail module`);
299
+
300
+ return;
301
+ }
302
+
303
+ countIn((section.actions ?? []).length, BRIDGE_SECTION_ACTIONS_LIMIT, "actions", where);
304
+ validateActions(modules, section.actions ?? [], where);
305
+
306
+ for (const action of section.actions ?? []) {
307
+ if (action.download) {
308
+ fail(`${where} declares action "${action.id}" as a download, which a detail cannot serve`);
309
+ }
310
+
311
+ if (!module.actions?.[action.id]) {
312
+ fail(`${where} declares action "${action.id}", which module "${section.module}" does not implement`);
313
+ }
314
+ }
315
+
316
+ validateText(section.confirmText, "confirm text", where);
317
+ validateText(section.related?.label, "related label", where);
318
+
262
319
  return;
263
320
  }
264
321
 
@@ -313,6 +370,10 @@ const validateSection = (modules: Record<string, BridgePanelModule>, section: Br
313
370
  for (const extension of section.upload.extensions) {
314
371
  lengthIn(extension, BRIDGE_EXTENSION_LENGTH_LIMIT, `upload extension "${extension}"`, where);
315
372
  }
373
+
374
+ if (section.upload.mode !== undefined && !UPLOAD_MODES.has(section.upload.mode)) {
375
+ fail(`${where} declares an unknown upload mode "${section.upload.mode}"`);
376
+ }
316
377
  }
317
378
  }
318
379
 
@@ -346,6 +407,36 @@ const validateSection = (modules: Record<string, BridgePanelModule>, section: Br
346
407
  }
347
408
  };
348
409
 
410
+ const validatePending = (driver: BridgeDriver) => {
411
+ const pending = driver.pending;
412
+
413
+ if (!pending) {
414
+ return;
415
+ }
416
+
417
+ const where = "the pending module";
418
+
419
+ if (pending.kind !== BridgeKind.Pending) {
420
+ fail(`${where} declares kind "${pending.kind}", which is not "${BridgeKind.Pending}"`);
421
+ }
422
+
423
+ if (pending.staging.length === 0) {
424
+ fail(`${where} declares no staging path`);
425
+ }
426
+
427
+ lengthIn(pending.staging, BRIDGE_STAGING_LENGTH_LIMIT, "staging path", where);
428
+
429
+ if (pending.extensions.length === 0) {
430
+ fail(`${where} declares no extensions`);
431
+ }
432
+
433
+ countIn(pending.extensions.length, BRIDGE_UPLOAD_EXTENSIONS_LIMIT, "extensions", where);
434
+
435
+ for (const extension of pending.extensions) {
436
+ lengthIn(extension, BRIDGE_EXTENSION_LENGTH_LIMIT, `extension "${extension}"`, where);
437
+ }
438
+ };
439
+
349
440
  const validateTab = (modules: Record<string, BridgePanelModule>, tab: BridgeTab) => {
350
441
  const sections = new Set<string>();
351
442
 
@@ -382,6 +473,8 @@ export const validateDriver = (driver: BridgeDriver) => {
382
473
 
383
474
  countIn(emitted.size, BRIDGE_EVENTS_LIMIT, "event names", "the driver");
384
475
 
476
+ validatePending(driver);
477
+
385
478
  for (const [id, module] of Object.entries(modules)) {
386
479
  validateModuleId(id, "module");
387
480
 
@@ -401,7 +494,7 @@ export const validateDriver = (driver: BridgeDriver) => {
401
494
  }
402
495
  }
403
496
 
404
- if (module.kind === BridgeKind.Collection) {
497
+ if (module.kind === BridgeKind.Collection || module.kind === BridgeKind.Detail) {
405
498
  for (const action of Object.keys(module.actions ?? {})) {
406
499
  validateModuleId(action, `module "${id}" action`);
407
500
  }
@@ -429,6 +522,26 @@ export const validateDriver = (driver: BridgeDriver) => {
429
522
 
430
523
  validateTab(modules, tab);
431
524
  }
525
+
526
+ const declared = declaredVariables(driver);
527
+
528
+ for (const tab of driver.panel.tabs) {
529
+ for (const section of tab.sections) {
530
+ const where = `tab "${tab.id}" section "${section.id}"`;
531
+
532
+ if (section.layout === BridgeLayout.Detail && section.related && !tabs.has(section.related.tab)) {
533
+ fail(`${where} relates to tab "${section.related.tab}", which the panel does not declare`);
534
+ }
535
+
536
+ if (section.layout === BridgeLayout.Form) {
537
+ for (const clear of section.clears ?? []) {
538
+ if (!declared.has(clear.variable)) {
539
+ fail(`${where} clears "${clear.variable}", which no variables form, catalog or detail declares`);
540
+ }
541
+ }
542
+ }
543
+ }
544
+ }
432
545
  };
433
546
 
434
547
  const declaredVariables = (driver: BridgeDriver) => {
@@ -442,7 +555,7 @@ const declaredVariables = (driver: BridgeDriver) => {
442
555
  }
443
556
  }
444
557
 
445
- if (section.layout === BridgeLayout.Catalog && section.variable) {
558
+ if ((section.layout === BridgeLayout.Catalog || section.layout === BridgeLayout.Detail) && section.variable) {
446
559
  declared.add(section.variable);
447
560
  }
448
561
  }
@@ -461,7 +574,7 @@ export const validatePortActivation = (ports: ManifestPort[], driver: BridgeDriv
461
574
  }
462
575
 
463
576
  fail(
464
- `port "${port.key}" activeWhen[${index}] names "${rule.variable}", which no variables form or catalog declares`,
577
+ `port "${port.key}" activeWhen[${index}] names "${rule.variable}", which no variables form, catalog or detail declares`,
465
578
  );
466
579
  }
467
580
  }
@@ -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;
@@ -37,11 +37,13 @@ export const createEventRing = (events: BridgeEventsModule | undefined, supervis
37
37
  continue;
38
38
  }
39
39
 
40
- const groups = match.groups
41
- ? {
42
- ...match.groups,
43
- }
44
- : null;
40
+ const groups =
41
+ match.groups || pattern.payload
42
+ ? {
43
+ ...pattern.payload,
44
+ ...match.groups,
45
+ }
46
+ : null;
45
47
 
46
48
  push(pattern.emit, groups);
47
49
  }