@design.estate/wcctools 6.1.0 → 7.0.0

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 (112) hide show
  1. package/changelog.md +41 -0
  2. package/dist_shell/bundle.js +405 -368
  3. package/dist_shell/bundle.js.map +1 -1
  4. package/dist_shell/bundle.js.third-party-notices.json +52 -0
  5. package/dist_shell/bundle.js.third-party-notices.txt +107 -1
  6. package/dist_shell/index.html +1 -1
  7. package/dist_ts/00_commitinfo_data.js +1 -1
  8. package/dist_ts/capture/classes.capturebrowser.d.ts +65 -0
  9. package/dist_ts/capture/classes.capturebrowser.js +251 -0
  10. package/dist_ts/capture/classes.captureservice.d.ts +39 -0
  11. package/dist_ts/capture/classes.captureservice.js +119 -0
  12. package/dist_ts/capture/classes.screencast.d.ts +47 -0
  13. package/dist_ts/capture/classes.screencast.js +127 -0
  14. package/dist_ts/capture/errors.d.ts +14 -0
  15. package/dist_ts/capture/errors.js +23 -0
  16. package/dist_ts/capture/images.d.ts +3 -0
  17. package/dist_ts/capture/images.js +58 -0
  18. package/dist_ts/capture/index.d.ts +4 -0
  19. package/dist_ts/capture/index.js +5 -0
  20. package/dist_ts/capture/interaction.d.ts +11 -0
  21. package/dist_ts/capture/interaction.js +138 -0
  22. package/dist_ts/capture/navigationguard.d.ts +44 -0
  23. package/dist_ts/capture/navigationguard.js +129 -0
  24. package/dist_ts/capture/previewpage.d.ts +29 -0
  25. package/dist_ts/capture/previewpage.js +96 -0
  26. package/dist_ts/capture/request.d.ts +69 -0
  27. package/dist_ts/capture/request.js +239 -0
  28. package/dist_ts/classes.bundlestatus.d.ts +36 -0
  29. package/dist_ts/classes.bundlestatus.js +110 -0
  30. package/dist_ts/classes.devapi.d.ts +3 -0
  31. package/dist_ts/classes.devapi.js +8 -1
  32. package/dist_ts/classes.devserver.d.ts +65 -14
  33. package/dist_ts/classes.devserver.js +157 -52
  34. package/dist_ts/classes.hostpolicy.d.ts +63 -0
  35. package/dist_ts/classes.hostpolicy.js +171 -0
  36. package/dist_ts/cli.js +28 -3
  37. package/dist_ts/cli.screenshot.d.ts +6 -0
  38. package/dist_ts/cli.screenshot.js +89 -0
  39. package/dist_ts/index.d.ts +0 -2
  40. package/dist_ts/index.js +1 -3
  41. package/dist_ts/plugins.d.ts +6 -2
  42. package/dist_ts/plugins.js +8 -3
  43. package/dist_ts_interfaces/capture.d.ts +151 -0
  44. package/dist_ts_interfaces/capture.js +2 -0
  45. package/dist_ts_interfaces/index.d.ts +1 -0
  46. package/dist_ts_interfaces/index.js +2 -1
  47. package/dist_ts_interfaces/requests.d.ts +59 -0
  48. package/dist_ts_shell/bundlestatus.d.ts +15 -0
  49. package/dist_ts_shell/bundlestatus.js +64 -0
  50. package/dist_ts_shell/elements/wcc-contextmenu.d.ts +3 -0
  51. package/dist_ts_shell/elements/wcc-contextmenu.js +18 -3
  52. package/dist_ts_shell/elements/wcc-preview-frame.d.ts +6 -0
  53. package/dist_ts_shell/elements/wcc-preview-frame.js +50 -2
  54. package/dist_ts_shell/elements/wcc-recording-panel.d.ts +9 -0
  55. package/dist_ts_shell/elements/wcc-recording-panel.js +12 -1
  56. package/dist_ts_shell/elements/wcc-shell.d.ts +15 -0
  57. package/dist_ts_shell/elements/wcc-shell.js +45 -3
  58. package/dist_ts_shell/plugins.d.ts +2 -1
  59. package/dist_ts_shell/plugins.js +3 -2
  60. package/dist_ts_shell/services/framesampler.service.d.ts +23 -0
  61. package/dist_ts_shell/services/framesampler.service.js +101 -0
  62. package/dist_ts_web/00_commitinfo_data.js +1 -1
  63. package/package.json +6 -2
  64. package/readme.md +108 -6
  65. package/ts/00_commitinfo_data.ts +1 -1
  66. package/ts/capture/classes.capturebrowser.ts +290 -0
  67. package/ts/capture/classes.captureservice.ts +152 -0
  68. package/ts/capture/classes.screencast.ts +152 -0
  69. package/ts/capture/errors.ts +25 -0
  70. package/ts/capture/images.ts +61 -0
  71. package/ts/capture/index.ts +4 -0
  72. package/ts/capture/interaction.ts +146 -0
  73. package/ts/capture/navigationguard.ts +147 -0
  74. package/ts/capture/previewpage.ts +130 -0
  75. package/ts/capture/request.ts +304 -0
  76. package/ts/classes.bundlestatus.ts +123 -0
  77. package/ts/classes.devapi.ts +9 -0
  78. package/ts/classes.devserver.ts +172 -50
  79. package/ts/classes.hostpolicy.ts +201 -0
  80. package/ts/cli.screenshot.ts +95 -0
  81. package/ts/cli.ts +28 -2
  82. package/ts/index.ts +0 -2
  83. package/ts/plugins.ts +9 -2
  84. package/ts_interfaces/bridge.ts +95 -0
  85. package/ts_interfaces/capture.ts +144 -0
  86. package/ts_interfaces/catalog.ts +37 -0
  87. package/ts_interfaces/index.ts +5 -0
  88. package/ts_interfaces/plugins.ts +3 -0
  89. package/ts_interfaces/requests.ts +104 -0
  90. package/ts_interfaces/standard.ts +103 -0
  91. package/ts_shared/index.ts +1 -0
  92. package/ts_shared/plugins.ts +3 -0
  93. package/ts_shared/previewroute.ts +58 -0
  94. package/ts_shell/bundlestatus.ts +68 -0
  95. package/ts_shell/elements/wcc-contextmenu.ts +306 -0
  96. package/ts_shell/elements/wcc-preview-frame.ts +167 -0
  97. package/ts_shell/elements/wcc-properties.ts +998 -0
  98. package/ts_shell/elements/wcc-record-button.ts +108 -0
  99. package/ts_shell/elements/wcc-recording-panel.ts +1017 -0
  100. package/ts_shell/elements/wcc-shell.ts +485 -0
  101. package/ts_shell/elements/wcc-sidebar.ts +1419 -0
  102. package/ts_shell/index.html +21 -0
  103. package/ts_shell/index.ts +4 -0
  104. package/ts_shell/plugins.ts +12 -0
  105. package/ts_shell/previewconnection.ts +103 -0
  106. package/ts_shell/services/framesampler.service.ts +112 -0
  107. package/ts_shell/services/recorder.service.ts +451 -0
  108. package/ts_shell/types/dom-mediacapture-stub/index.d.ts +12 -0
  109. package/ts_shell/types/dom-mediacapture-stub/package.json +6 -0
  110. package/ts_shell/types/dom-webcodecs-stub/index.d.ts +2 -0
  111. package/ts_shell/types/dom-webcodecs-stub/package.json +6 -0
  112. package/ts_web/00_commitinfo_data.ts +1 -1
@@ -0,0 +1,110 @@
1
+ import * as plugins from './plugins.js';
2
+ /** The longest time a status request waits for a change before it answers the unchanged status. */
3
+ export const bundleStatusWaitLimitMs = 25_000;
4
+ /** What a status request answers while the board is closed: the server is not serving. */
5
+ export const bundleStatusClosedMessage = 'wcctools: the dev server is not serving the bundle status.';
6
+ /**
7
+ * The status of the catalog bundles, as tswatch's bundle events report it, for the shell's
8
+ * `getBundleStatus` requests. Every event raises the revision and answers the requests waiting for
9
+ * a change. While closed (the server is not serving), requests are refused with a typed error, so
10
+ * a client backs off as it does for any failed request.
11
+ */
12
+ export class WccBundleStatusBoard {
13
+ bundles = new Map();
14
+ revision = 0;
15
+ isOpen = false;
16
+ /** Settles one waiting request: answers the current snapshot, or refuses; each removes itself. */
17
+ waiters = new Set();
18
+ /** Starts a run of the server: the bundles of an earlier run are forgotten. */
19
+ open() {
20
+ this.bundles.clear();
21
+ this.isOpen = true;
22
+ this.changed();
23
+ }
24
+ /** Ends the run: every waiting request is refused now, later ones at once. */
25
+ close() {
26
+ this.isOpen = false;
27
+ const refusal = this.createClosedError();
28
+ for (const settle of [...this.waiters]) {
29
+ settle(refusal);
30
+ }
31
+ }
32
+ /** The number of requests waiting for a change. */
33
+ getWaiterCount() {
34
+ return this.waiters.size;
35
+ }
36
+ record(eventArg) {
37
+ const previous = this.bundles.get(eventArg.name);
38
+ const status = { name: eventArg.name, state: eventArg.type };
39
+ switch (eventArg.type) {
40
+ case 'started':
41
+ // The latest completed run stays reported while the next one runs
42
+ if (previous?.durationMs !== undefined) {
43
+ status.durationMs = previous.durationMs;
44
+ }
45
+ if (previous?.errorMessage !== undefined) {
46
+ status.errorMessage = previous.errorMessage;
47
+ }
48
+ break;
49
+ case 'finished':
50
+ status.durationMs = eventArg.durationMs;
51
+ break;
52
+ case 'failed':
53
+ status.durationMs = eventArg.durationMs;
54
+ status.errorMessage = eventArg.errorMessage;
55
+ break;
56
+ }
57
+ this.bundles.set(eventArg.name, status);
58
+ this.changed();
59
+ }
60
+ getSnapshot() {
61
+ return {
62
+ revision: this.revision,
63
+ bundles: [...this.bundles.values()].map((statusArg) => ({ ...statusArg })),
64
+ };
65
+ }
66
+ /**
67
+ * The snapshot once it differs from `afterRevisionArg`, or after the wait limit; at once without
68
+ * a revision or for a revision that is no longer current. Refused while closed. When
69
+ * `abortSignalArg` aborts (the client went away), the wait ends at once and releases its timer;
70
+ * it answers the current snapshot, which the transport drops.
71
+ */
72
+ waitForChange(afterRevisionArg, abortSignalArg) {
73
+ if (!this.isOpen) {
74
+ return Promise.reject(this.createClosedError());
75
+ }
76
+ if (afterRevisionArg === undefined || afterRevisionArg !== this.revision || abortSignalArg?.aborted) {
77
+ return Promise.resolve(this.getSnapshot());
78
+ }
79
+ return new Promise((resolve, reject) => {
80
+ const settle = (refusalArg) => {
81
+ clearTimeout(waitLimit);
82
+ abortSignalArg?.removeEventListener('abort', onAbort);
83
+ this.waiters.delete(settle);
84
+ if (refusalArg) {
85
+ reject(refusalArg);
86
+ }
87
+ else {
88
+ resolve(this.getSnapshot());
89
+ }
90
+ };
91
+ const onAbort = () => settle();
92
+ const waitLimit = setTimeout(() => settle(), bundleStatusWaitLimitMs);
93
+ abortSignalArg?.addEventListener('abort', onAbort, { once: true });
94
+ this.waiters.add(settle);
95
+ });
96
+ }
97
+ changed() {
98
+ this.revision++;
99
+ this.answerWaiters();
100
+ }
101
+ answerWaiters() {
102
+ for (const settle of [...this.waiters]) {
103
+ settle();
104
+ }
105
+ }
106
+ createClosedError() {
107
+ return new plugins.typedrequest.TypedResponseError(bundleStatusClosedMessage);
108
+ }
109
+ }
110
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5idW5kbGVzdGF0dXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9jbGFzc2VzLmJ1bmRsZXN0YXR1cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUV4QyxtR0FBbUc7QUFDbkcsTUFBTSxDQUFDLE1BQU0sdUJBQXVCLEdBQUcsTUFBTSxDQUFDO0FBRTlDLDBGQUEwRjtBQUMxRixNQUFNLENBQUMsTUFBTSx5QkFBeUIsR0FBRyw0REFBNEQsQ0FBQztBQUV0Rzs7Ozs7R0FLRztBQUNILE1BQU0sT0FBTyxvQkFBb0I7SUFDZCxPQUFPLEdBQUcsSUFBSSxHQUFHLEVBQStDLENBQUM7SUFDMUUsUUFBUSxHQUFHLENBQUMsQ0FBQztJQUNiLE1BQU0sR0FBRyxLQUFLLENBQUM7SUFDdkIsa0dBQWtHO0lBQ2pGLE9BQU8sR0FBRyxJQUFJLEdBQUcsRUFBZ0MsQ0FBQztJQUVuRSwrRUFBK0U7SUFDeEUsSUFBSTtRQUNULElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUM7UUFDbkIsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ2pCLENBQUM7SUFFRCw4RUFBOEU7SUFDdkUsS0FBSztRQUNWLElBQUksQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDO1FBQ3BCLE1BQU0sT0FBTyxHQUFHLElBQUksQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO1FBQ3pDLEtBQUssTUFBTSxNQUFNLElBQUksQ0FBQyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDO1lBQ3ZDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUNsQixDQUFDO0lBQ0gsQ0FBQztJQUVELG1EQUFtRDtJQUM1QyxjQUFjO1FBQ25CLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7SUFDM0IsQ0FBQztJQUVN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
@@ -1,10 +1,13 @@
1
1
  import * as plugins from './plugins.js';
2
+ import { WccBundleStatusBoard } from './classes.bundlestatus.js';
2
3
  /**
3
4
  * The typed API the shell reaches on `/wcctools/typedrequest`. Later features register their
4
5
  * handlers on `typedrouter`.
5
6
  */
6
7
  export declare class WccDevApi {
7
8
  readonly typedrouter: plugins.typedrequest.TypedRouter<import("@api.global/typedrequest-interfaces").ITypedRequest>;
9
+ /** The catalog bundles' status, fed with tswatch's bundle events while the server runs. */
10
+ readonly bundleStatus: WccBundleStatusBoard;
8
11
  private serverInfo;
9
12
  constructor();
10
13
  setServerInfo(infoArg: plugins.interfaces.IWccDevServerInfo): void;
@@ -1,10 +1,13 @@
1
1
  import * as plugins from './plugins.js';
2
+ import { WccBundleStatusBoard } from './classes.bundlestatus.js';
2
3
  /**
3
4
  * The typed API the shell reaches on `/wcctools/typedrequest`. Later features register their
4
5
  * handlers on `typedrouter`.
5
6
  */
6
7
  export class WccDevApi {
7
8
  typedrouter = new plugins.typedrequest.TypedRouter();
9
+ /** The catalog bundles' status, fed with tswatch's bundle events while the server runs. */
10
+ bundleStatus = new WccBundleStatusBoard();
8
11
  serverInfo = null;
9
12
  constructor() {
10
13
  this.typedrouter.addTypedHandler(new plugins.typedrequest.TypedHandler('getDevServerInfo', async () => {
@@ -13,9 +16,13 @@ export class WccDevApi {
13
16
  }
14
17
  return { ...this.serverInfo };
15
18
  }));
19
+ this.typedrouter.addTypedHandler(new plugins.typedrequest.TypedHandler('getBundleStatus', async (requestArg, typedToolsArg) => {
20
+ // A client that goes away ends its wait, so no waiter or timer outlives the request
21
+ return this.bundleStatus.waitForChange(requestArg.afterRevision, typedToolsArg?.abortSignal);
22
+ }));
16
23
  }
17
24
  setServerInfo(infoArg) {
18
25
  this.serverInfo = { ...infoArg };
19
26
  }
20
27
  }
21
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5kZXZhcGkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9jbGFzc2VzLmRldmFwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUV4Qzs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sU0FBUztJQUNKLFdBQVcsR0FBRyxJQUFJLE9BQU8sQ0FBQyxZQUFZLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDN0QsVUFBVSxHQUFnRCxJQUFJLENBQUM7SUFFdkU7UUFDRSxJQUFJLENBQUMsV0FBVyxDQUFDLGVBQWUsQ0FDOUIsSUFBSSxPQUFPLENBQUMsWUFBWSxDQUFDLFlBQVksQ0FBMkMsa0JBQWtCLEVBQUUsS0FBSyxJQUFJLEVBQUU7WUFDN0csSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztnQkFDckIsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQ0FBaUMsQ0FBQyxDQUFDO1lBQ3JELENBQUM7WUFDRCxPQUFPLEVBQUUsR0FBRyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7UUFDaEMsQ0FBQyxDQUFDLENBQ0gsQ0FBQztJQUNKLENBQUM7SUFFTSxhQUFhLENBQUMsT0FBNkM7UUFDaEUsSUFBSSxDQUFDLFVBQVUsR0FBRyxFQUFFLEdBQUcsT0FBTyxFQUFFLENBQUM7SUFDbkMsQ0FBQztDQUNGIn0=
28
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5kZXZhcGkuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9jbGFzc2VzLmRldmFwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUVqRTs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sU0FBUztJQUNKLFdBQVcsR0FBRyxJQUFJLE9BQU8sQ0FBQyxZQUFZLENBQUMsV0FBVyxFQUFFLENBQUM7SUFDckUsMkZBQTJGO0lBQzNFLFlBQVksR0FBRyxJQUFJLG9CQUFvQixFQUFFLENBQUM7SUFDbEQsVUFBVSxHQUFnRCxJQUFJLENBQUM7SUFFdkU7UUFDRSxJQUFJLENBQUMsV0FBVyxDQUFDLGVBQWUsQ0FDOUIsSUFBSSxPQUFPLENBQUMsWUFBWSxDQUFDLFlBQVksQ0FBMkMsa0JBQWtCLEVBQUUsS0FBSyxJQUFJLEVBQUU7WUFDN0csSUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztnQkFDckIsTUFBTSxJQUFJLEtBQUssQ0FBQyxpQ0FBaUMsQ0FBQyxDQUFDO1lBQ3JELENBQUM7WUFDRCxPQUFPLEVBQUUsR0FBRyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7UUFDaEMsQ0FBQyxDQUFDLENBQ0gsQ0FBQztRQUNGLElBQUksQ0FBQyxXQUFXLENBQUMsZUFBZSxDQUM5QixJQUFJLE9BQU8sQ0FBQyxZQUFZLENBQUMsWUFBWSxDQUEwQyxpQkFBaUIsRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLGFBQWEsRUFBRSxFQUFFO1lBQ3BJLG9GQUFvRjtZQUNwRixPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsYUFBYSxDQUFDLFVBQVUsQ0FBQyxhQUFhLEVBQUUsYUFBYSxFQUFFLFdBQVcsQ0FBQyxDQUFDO1FBQy9GLENBQUMsQ0FBQyxDQUNILENBQUM7SUFDSixDQUFDO0lBRU0sYUFBYSxDQUFDLE9BQTZDO1FBQ2hFLElBQUksQ0FBQyxVQUFVLEdBQUcsRUFBRSxHQUFHLE9BQU8sRUFBRSxDQUFDO0lBQ25DLENBQUM7Q0FDRiJ9
@@ -1,42 +1,93 @@
1
1
  import { WccDevApi } from './classes.devapi.js';
2
+ import { WccHostPolicy } from './classes.hostpolicy.js';
3
+ import { WccCaptureService } from './capture/index.js';
2
4
  export interface IWccDevServerOptions {
3
5
  /** Port to listen on; overrides the tswatch configuration. 0 picks a free port. */
4
6
  port?: number;
7
+ /**
8
+ * The project directory: its tswatch configuration, package.json and catalog are served, and
9
+ * tswatch bundles and watches in it (default: `process.cwd()` when the server is constructed).
10
+ */
11
+ cwd?: string;
12
+ /**
13
+ * The interface address to listen on (default `127.0.0.1`, this machine only). `0.0.0.0` or
14
+ * `::` listen on every interface; the machine's interface addresses are then allowed hosts.
15
+ */
16
+ host?: string;
17
+ /** Further hostnames or addresses the server answers, besides localhost and the listen address. */
18
+ allowedHosts?: readonly string[];
5
19
  }
6
20
  export interface IWccDevServerAddress {
7
21
  port: number;
8
22
  /** The shell's address. */
9
23
  url: string;
24
+ /** The interface address the server listens on. */
25
+ host: string;
26
+ /** The hostnames the server answers; requests for any other Host are refused. */
27
+ allowedHosts: string[];
10
28
  }
11
29
  /**
12
- * `wcctools dev`: bundles the catalog of the current project with tswatch, as its
13
- * `@git.zone/tswatch` configuration (or the `element` preset) describes, and serves on one port:
30
+ * `wcctools dev`: bundles the catalog of a project with tswatch, as its `@git.zone/tswatch`
31
+ * configuration (or the `element` preset) describes, and serves on one port:
14
32
  *
15
33
  * - the shell on `/wcctools/` (`/` redirects there) and its routes `/wcctools-route/...`,
16
- * - the typed API on `/wcctools/typedrequest`,
34
+ * - the typed API on `/wcctools/typedrequest`, including the bundles' status,
17
35
  * - the catalog's preview document and bundle from the tswatch serve directory on every other
18
- * path, with live reload that reloads only the preview document.
36
+ * path, with live reload that reloads only the preview document,
37
+ * - captures of single demos in a headless browser (`capture`, also on the typed API).
38
+ *
39
+ * It listens on loopback unless told otherwise and admits requests as its `WccHostPolicy`
40
+ * describes: only the hosts it serves, and state-changing requests and WebSockets only from its
41
+ * own origin.
19
42
  *
20
- * tswatch resolves its paths against the process's working directory, so the server serves the
21
- * project the process runs in.
43
+ * The server owns no process lifecycle: while it runs, a process shutdown through smartexit's
44
+ * `ProcessLifecycle` stops it. The entry point installs that lifecycle with
45
+ * `getShutdownTimeoutMs()`.
22
46
  */
23
47
  export declare class WccDevServer {
24
48
  private readonly options;
25
49
  /** The typed API on `/wcctools/typedrequest`; register further handlers before `start()`. */
26
50
  readonly api: WccDevApi;
27
- private tsWatch;
51
+ /** Screenshots and interaction captures of the served catalog; available while the server runs. */
52
+ readonly capture: WccCaptureService;
53
+ /** Who may talk to the server: the hosts it answers and the origin of its requests. */
54
+ readonly hostPolicy: WccHostPolicy;
55
+ /** The project directory everything resolves against. */
56
+ readonly cwd: string;
57
+ /** The project's tswatch configuration, or the `element` preset; read once, on construction. */
58
+ private readonly config;
59
+ /** Bundling and watching stay tswatch's; its own server stays off. */
60
+ private readonly tsWatch;
28
61
  private typedServer;
29
- private readonly smartExit;
62
+ /** Stops the server on a process shutdown; registered by start(), released by stop(). */
63
+ private smartExit;
64
+ private unsubscribeBundleEvents;
65
+ private running;
66
+ private starting;
67
+ private stopping;
30
68
  constructor(options?: IWccDevServerOptions);
69
+ /**
70
+ * The shutdown deadline the server needs, tswatch's: the entry point installs smartexit's
71
+ * `ProcessLifecycle` with it, so every watcher command keeps its full grace period.
72
+ */
73
+ getShutdownTimeoutMs(): number;
74
+ /**
75
+ * Bundles the catalog, starts the watchers and serves. Refused while the server runs or stops;
76
+ * the server starts again after stop().
77
+ */
31
78
  start(): Promise<IWccDevServerAddress>;
32
- /** Stops the server, then tswatch's bundling and watching. */
79
+ /**
80
+ * Stops the server and tswatch's bundling and watching, and releases everything start()
81
+ * registered. A stop during start() waits for the start to settle first; concurrent and
82
+ * repeated calls share one stop.
83
+ */
33
84
  stop(): Promise<void>;
85
+ private release;
86
+ private startServing;
87
+ private getElementPreset;
34
88
  /**
35
- * The shell: the prebuilt shell bundle and the typed API.
36
- *
37
- * Seam for side-effecting APIs (assistant, capture, standards fixes): before any handler with
38
- * side effects is registered here, add a `websocketAdmission`/`requestAdmission` that accepts
39
- * only the shell's own origin, and keep surface mode's strict authority validation.
89
+ * The shell: the prebuilt shell bundle and the typed API, whose requests (captures among them)
90
+ * are admitted only from the server's own origin.
40
91
  */
41
92
  private createShellSurface;
42
93
  /**
@@ -2,6 +2,8 @@ import * as plugins from './plugins.js';
2
2
  import * as paths from './paths.js';
3
3
  import { commitinfo } from './00_commitinfo_data.js';
4
4
  import { WccDevApi } from './classes.devapi.js';
5
+ import { WccHostPolicy, defaultListenHost, formatUrlHost } from './classes.hostpolicy.js';
6
+ import { WccCaptureService } from './capture/index.js';
5
7
  import { loadShellContent, shellPathPrefix } from './shellcontent.js';
6
8
  /** tswatch's default port and serve directory, used when the configuration names none. */
7
9
  const defaultPort = 3002;
@@ -11,83 +13,182 @@ const securityHeaders = {
11
13
  crossOriginOpenerPolicy: 'same-origin',
12
14
  crossOriginEmbedderPolicy: 'require-corp',
13
15
  };
16
+ /** Every surface takes every hostname: the server-wide Host admission refuses the ones it does not serve. */
14
17
  const acceptAnyHostname = () => true;
15
18
  /**
16
- * `wcctools dev`: bundles the catalog of the current project with tswatch, as its
17
- * `@git.zone/tswatch` configuration (or the `element` preset) describes, and serves on one port:
19
+ * `wcctools dev`: bundles the catalog of a project with tswatch, as its `@git.zone/tswatch`
20
+ * configuration (or the `element` preset) describes, and serves on one port:
18
21
  *
19
22
  * - the shell on `/wcctools/` (`/` redirects there) and its routes `/wcctools-route/...`,
20
- * - the typed API on `/wcctools/typedrequest`,
23
+ * - the typed API on `/wcctools/typedrequest`, including the bundles' status,
21
24
  * - the catalog's preview document and bundle from the tswatch serve directory on every other
22
- * path, with live reload that reloads only the preview document.
25
+ * path, with live reload that reloads only the preview document,
26
+ * - captures of single demos in a headless browser (`capture`, also on the typed API).
23
27
  *
24
- * tswatch resolves its paths against the process's working directory, so the server serves the
25
- * project the process runs in.
28
+ * It listens on loopback unless told otherwise and admits requests as its `WccHostPolicy`
29
+ * describes: only the hosts it serves, and state-changing requests and WebSockets only from its
30
+ * own origin.
31
+ *
32
+ * The server owns no process lifecycle: while it runs, a process shutdown through smartexit's
33
+ * `ProcessLifecycle` stops it. The entry point installs that lifecycle with
34
+ * `getShutdownTimeoutMs()`.
26
35
  */
27
36
  export class WccDevServer {
28
37
  options;
29
38
  /** The typed API on `/wcctools/typedrequest`; register further handlers before `start()`. */
30
39
  api = new WccDevApi();
31
- tsWatch = null;
40
+ /** Screenshots and interaction captures of the served catalog; available while the server runs. */
41
+ capture;
42
+ /** Who may talk to the server: the hosts it answers and the origin of its requests. */
43
+ hostPolicy;
44
+ /** The project directory everything resolves against. */
45
+ cwd;
46
+ /** The project's tswatch configuration, or the `element` preset; read once, on construction. */
47
+ config;
48
+ /** Bundling and watching stay tswatch's; its own server stays off. */
49
+ tsWatch;
32
50
  typedServer = null;
33
- smartExit = new plugins.smartexit.SmartExit({ silent: true });
51
+ /** Stops the server on a process shutdown; registered by start(), released by stop(). */
52
+ smartExit = null;
53
+ unsubscribeBundleEvents = null;
54
+ running = false;
55
+ starting = null;
56
+ stopping = null;
34
57
  constructor(options = {}) {
35
58
  this.options = options;
36
- this.smartExit.addCleanupFunction(() => this.stop());
59
+ this.cwd = plugins.path.resolve(options.cwd ?? process.cwd());
60
+ const configHandler = new plugins.tswatch.ConfigHandler(this.cwd);
61
+ this.config = configHandler.loadConfig() ?? this.getElementPreset(configHandler);
62
+ this.tsWatch = new plugins.tswatch.TsWatch({ ...this.config, server: { enabled: false } }, { cwd: this.cwd });
63
+ this.hostPolicy = new WccHostPolicy({ listenHost: options.host ?? defaultListenHost, allowedHosts: options.allowedHosts });
64
+ this.capture = new WccCaptureService(this.api.bundleStatus);
65
+ this.capture.addTypedHandlers(this.api.typedrouter);
37
66
  }
67
+ /**
68
+ * The shutdown deadline the server needs, tswatch's: the entry point installs smartexit's
69
+ * `ProcessLifecycle` with it, so every watcher command keeps its full grace period.
70
+ */
71
+ getShutdownTimeoutMs() {
72
+ return this.tsWatch.getShutdownTimeoutMs();
73
+ }
74
+ /**
75
+ * Bundles the catalog, starts the watchers and serves. Refused while the server runs or stops;
76
+ * the server starts again after stop().
77
+ */
38
78
  async start() {
39
- if (this.typedServer) {
40
- throw new Error('wcctools: the dev server is already started.');
79
+ if (this.running || this.stopping) {
80
+ throw new Error('wcctools: the dev server is already started or still stopping.');
81
+ }
82
+ this.running = true;
83
+ // A process shutdown stops the server from here on, also while it is still starting
84
+ const smartExit = new plugins.smartexit.SmartExit({ silent: true });
85
+ smartExit.addCleanupFunction(() => this.stop());
86
+ this.smartExit = smartExit;
87
+ const starting = this.startServing();
88
+ this.starting = starting;
89
+ try {
90
+ const address = await starting;
91
+ if (this.stopping) {
92
+ throw new Error('wcctools: the dev server was stopped while starting.');
93
+ }
94
+ return address;
95
+ }
96
+ catch (error) {
97
+ // A start that failed halfway releases what it opened
98
+ await this.stop();
99
+ throw error;
100
+ }
101
+ finally {
102
+ if (this.starting === starting) {
103
+ this.starting = null;
104
+ }
105
+ }
106
+ }
107
+ /**
108
+ * Stops the server and tswatch's bundling and watching, and releases everything start()
109
+ * registered. A stop during start() waits for the start to settle first; concurrent and
110
+ * repeated calls share one stop.
111
+ */
112
+ stop() {
113
+ this.stopping ??= this.release().finally(() => {
114
+ this.stopping = null;
115
+ });
116
+ return this.stopping;
117
+ }
118
+ async release() {
119
+ await this.starting?.catch(() => undefined);
120
+ const smartExit = this.smartExit;
121
+ const typedServer = this.typedServer;
122
+ const unsubscribeBundleEvents = this.unsubscribeBundleEvents;
123
+ this.smartExit = null;
124
+ this.typedServer = null;
125
+ this.unsubscribeBundleEvents = null;
126
+ smartExit?.deregister();
127
+ // Status requests waiting for a change are refused now, so none holds the server's stop, and
128
+ // requests until the server stops listening are refused too: the shell backs off
129
+ this.api.bundleStatus.close();
130
+ try {
131
+ // Captures end and the capture browser closes before the server stops answering its pages
132
+ await this.capture.close();
133
+ await typedServer?.stop();
134
+ }
135
+ finally {
136
+ this.hostPolicy.setPort(null);
137
+ try {
138
+ await this.tsWatch.stop();
139
+ }
140
+ finally {
141
+ unsubscribeBundleEvents?.();
142
+ this.running = false;
143
+ }
41
144
  }
42
- const cwd = process.cwd();
43
- const configHandler = new plugins.tswatch.ConfigHandler(cwd);
44
- const config = configHandler.loadConfig() ?? configHandler.getPreset('element');
45
- const serveDir = plugins.path.resolve(cwd, config.server?.serveDir ?? defaultServeDir);
46
- const port = this.options.port ?? config.server?.port ?? defaultPort;
145
+ }
146
+ async startServing() {
147
+ const serveDir = plugins.path.resolve(this.cwd, this.config.server?.serveDir ?? defaultServeDir);
148
+ const port = this.options.port ?? this.config.server?.port ?? defaultPort;
47
149
  // The preview surface watches the serve directory, which must exist before the first bundle
48
150
  await plugins.fs.mkdir(serveDir, { recursive: true });
49
151
  this.api.setServerInfo({
50
152
  wcctoolsVersion: commitinfo.version,
51
- projectName: await this.readProjectName(cwd),
153
+ projectName: await this.readProjectName(),
52
154
  });
155
+ const surfaces = [await this.createShellSurface(), this.createPreviewSurface(serveDir)];
53
156
  const typedServer = new plugins.typedserver.TypedServer({
54
157
  cors: false,
55
158
  port,
56
- surfaces: [
57
- await this.createShellSurface(),
58
- this.createPreviewSurface(serveDir),
59
- ],
159
+ listenHostname: this.hostPolicy.listenHost,
160
+ // Requests and WebSocket upgrades for a Host the server does not serve are refused
161
+ requestAdmission: this.hostPolicy.admitHost,
162
+ websocketAdmission: this.hostPolicy.admitHost,
163
+ surfaces,
60
164
  });
61
165
  this.typedServer = typedServer;
62
- // Bundling and watching stay tswatch's; its own server stays off
63
- this.tsWatch = new plugins.tswatch.TsWatch({ ...config, server: { enabled: false } });
64
- try {
65
- await this.tsWatch.start();
66
- await typedServer.start();
67
- }
68
- catch (error) {
69
- await this.stop();
70
- throw error;
71
- }
166
+ this.api.bundleStatus.open();
167
+ const bundleStatus = this.api.bundleStatus;
168
+ this.unsubscribeBundleEvents = this.tsWatch.onBundleEvent((eventArg) => bundleStatus.record(eventArg));
169
+ // The initial bundles run here; a failed one is reported in the bundle status
170
+ await this.tsWatch.start();
171
+ await typedServer.start();
72
172
  const boundPort = typedServer.listeningPort ?? port;
73
- return { port: boundPort, url: `http://localhost:${boundPort}${shellPathPrefix}/` };
173
+ this.hostPolicy.setPort(boundPort);
174
+ this.capture.open(`http://${formatUrlHost(this.hostPolicy.getLocalHost())}:${boundPort}`);
175
+ return {
176
+ port: boundPort,
177
+ url: `http://${formatUrlHost(this.hostPolicy.getDisplayHost())}:${boundPort}${shellPathPrefix}/`,
178
+ host: this.hostPolicy.listenHost,
179
+ allowedHosts: [...this.hostPolicy.allowedHostnames],
180
+ };
74
181
  }
75
- /** Stops the server, then tswatch's bundling and watching. */
76
- async stop() {
77
- const typedServer = this.typedServer;
78
- const tsWatch = this.tsWatch;
79
- this.typedServer = null;
80
- this.tsWatch = null;
81
- this.smartExit.deregister();
82
- await typedServer?.stop();
83
- await tsWatch?.stop();
182
+ getElementPreset(configHandlerArg) {
183
+ const preset = configHandlerArg.getPreset('element');
184
+ if (!preset) {
185
+ throw new Error('wcctools: tswatch provides no `element` preset.');
186
+ }
187
+ return preset;
84
188
  }
85
189
  /**
86
- * The shell: the prebuilt shell bundle and the typed API.
87
- *
88
- * Seam for side-effecting APIs (assistant, capture, standards fixes): before any handler with
89
- * side effects is registered here, add a `websocketAdmission`/`requestAdmission` that accepts
90
- * only the shell's own origin, and keep surface mode's strict authority validation.
190
+ * The shell: the prebuilt shell bundle and the typed API, whose requests (captures among them)
191
+ * are admitted only from the server's own origin.
91
192
  */
92
193
  async createShellSurface() {
93
194
  return {
@@ -100,6 +201,8 @@ export class WccDevServer {
100
201
  spaFallback: true,
101
202
  httpTypedRouter: this.api.typedrouter,
102
203
  typedRequestPath: `${shellPathPrefix}/typedrequest`,
204
+ requestAdmission: this.hostPolicy.admitRequestOrigin,
205
+ websocketAdmission: this.hostPolicy.admitWebsocketOrigin,
103
206
  noCache: true,
104
207
  securityHeaders,
105
208
  };
@@ -122,8 +225,10 @@ export class WccDevServer {
122
225
  watch: true,
123
226
  // Lets the reload client get pushed changes instead of polling
124
227
  websocketTypedRouter: new plugins.typedrequest.TypedRouter(),
125
- // As tswatch serves catalogs
126
- cors: true,
228
+ requestAdmission: this.hostPolicy.admitRequestOrigin,
229
+ websocketAdmission: this.hostPolicy.admitWebsocketOrigin,
230
+ // Shell and preview share one origin, so no other origin may read the catalog
231
+ cors: false,
127
232
  httpHandler: async (contextArg) => {
128
233
  if (contextArg.path === '/' && (contextArg.method === 'GET' || contextArg.method === 'HEAD')) {
129
234
  return new Response(null, { status: 302, headers: { Location: `${shellPathPrefix}/` } });
@@ -134,9 +239,9 @@ export class WccDevServer {
134
239
  securityHeaders,
135
240
  };
136
241
  }
137
- async readProjectName(cwdArg) {
138
- const packageJson = JSON.parse(await plugins.fs.readFile(plugins.path.join(cwdArg, 'package.json'), 'utf8'));
139
- return packageJson.name ?? plugins.path.basename(cwdArg);
242
+ async readProjectName() {
243
+ const packageJson = JSON.parse(await plugins.fs.readFile(plugins.path.join(this.cwd, 'package.json'), 'utf8'));
244
+ return packageJson.name ?? plugins.path.basename(this.cwd);
140
245
  }
141
246
  }
142
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5kZXZzZXJ2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9jbGFzc2VzLmRldnNlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEtBQUssS0FBSyxNQUFNLFlBQVksQ0FBQztBQUNwQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDckQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxlQUFlLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQztBQWF0RSwwRkFBMEY7QUFDMUYsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDO0FBQ3pCLE1BQU0sZUFBZSxHQUFHLGVBQWUsQ0FBQztBQUV4Qyw4RkFBOEY7QUFDOUYsTUFBTSxlQUFlLEdBQXlDO0lBQzVELHVCQUF1QixFQUFFLGFBQWE7SUFDdEMseUJBQXlCLEVBQUUsY0FBYztDQUMxQyxDQUFDO0FBRUYsTUFBTSxpQkFBaUIsR0FBRyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUM7QUFFckM7Ozs7Ozs7Ozs7O0dBV0c7QUFDSCxNQUFNLE9BQU8sWUFBWTtJQU9NO0lBTjdCLDZGQUE2RjtJQUM3RSxHQUFHLEdBQUcsSUFBSSxTQUFTLEVBQUUsQ0FBQztJQUM5QixPQUFPLEdBQW1DLElBQUksQ0FBQztJQUMvQyxXQUFXLEdBQTJDLElBQUksQ0FBQztJQUNsRCxTQUFTLEdBQUcsSUFBSSxPQUFPLENBQUMsU0FBUyxDQUFDLFNBQVMsQ0FBQyxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBRS9FLFlBQTZCLFVBQWdDLEVBQUU7UUFBbEMsWUFBTyxHQUFQLE9BQU8sQ0FBMkI7UUFDN0QsSUFBSSxDQUFDLFNBQVMsQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMs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
247
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5kZXZzZXJ2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9jbGFzc2VzLmRldnNlcnZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUN4QyxPQUFPLEtBQUssS0FBSyxNQUFNLFlBQVksQ0FBQztBQUNwQyxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDckQsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxhQUFhLEVBQUUsaUJBQWlCLEVBQUUsYUFBYSxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDMUYsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFDdkQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLGVBQWUsRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBNkJ0RSwwRkFBMEY7QUFDMUYsTUFBTSxXQUFXLEdBQUcsSUFBSSxDQUFDO0FBQ3pCLE1BQU0sZUFBZSxHQUFHLGVBQWUsQ0FBQztBQUV4Qyw4RkFBOEY7QUFDOUYsTUFBTSxlQUFlLEdBQXlDO0lBQzVELHVCQUF1QixFQUFFLGFBQWE7SUFDdEMseUJBQXlCLEVBQUUsY0FBYztDQUMxQyxDQUFDO0FBRUYsNkdBQTZHO0FBQzdHLE1BQU0saUJBQWlCLEdBQUcsR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDO0FBRXJDOzs7Ozs7Ozs7Ozs7Ozs7OztHQWlCRztBQUNILE1BQU0sT0FBTyxZQUFZO0lBcUJNO0lBcEI3Qiw2RkFBNkY7SUFDN0UsR0FBRyxHQUFHLElBQUksU0FBUyxFQUFFLENBQUM7SUFDdEMsbUdBQW1HO0lBQ25GLE9BQU8sQ0FBb0I7SUFDM0MsdUZBQXVGO0lBQ3ZFLFVBQVUsQ0FBZ0I7SUFDMUMseURBQXlEO0lBQ3pDLEdBQUcsQ0FBUztJQUM1QixnR0FBZ0c7SUFDL0UsTUFBTSxDQUFpQztJ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
@@ -0,0 +1,63 @@
1
+ import * as plugins from './plugins.js';
2
+ type TRequestContext = plugins.typedserver.IRequestContext;
3
+ type TAdmissionResult = plugins.typedserver.TRequestAdmissionResult;
4
+ /** The interface address `wcctools dev` listens on unless told otherwise. */
5
+ export declare const defaultListenHost = "127.0.0.1";
6
+ /** Canonical form of a hostname: lower case, IPv6 without brackets, no trailing dot. */
7
+ export declare const normalizeHostname: (hostnameArg: string) => string;
8
+ /** Whether the address is the wildcard address of all interfaces. */
9
+ export declare const isWildcardHost: (hostArg: string) => boolean;
10
+ /** Whether a peer address is a loopback address (IPv4, IPv6 or IPv4-mapped IPv6). */
11
+ export declare const isLoopbackAddress: (addressArg: string) => boolean;
12
+ /** A hostname as it appears in a URL: IPv6 addresses in brackets. */
13
+ export declare const formatUrlHost: (hostnameArg: string) => string;
14
+ /** An invalid `--host` or `--allowed-host` value. */
15
+ export declare class WccHostPolicyError extends Error {
16
+ constructor(messageArg: string);
17
+ }
18
+ export interface IWccHostPolicyOptions {
19
+ /** The interface address the server listens on. */
20
+ listenHost: string;
21
+ /** Further hostnames or addresses the server answers (`--allowed-host`). */
22
+ allowedHosts?: readonly string[];
23
+ }
24
+ /**
25
+ * Who may talk to a `wcctools dev` server. The server listens on loopback unless told otherwise
26
+ * and answers only the hostnames it serves: the loopback names, the address it listens on, the
27
+ * machine's interface addresses when it listens on all of them, and the hosts allowed explicitly.
28
+ * Every other Host is refused, which defeats DNS rebinding. Requests that can change state
29
+ * (any method but GET, HEAD and OPTIONS) and WebSocket connections are further admitted only from
30
+ * the server's own origin, so other web pages cannot drive the API; a request without an Origin
31
+ * is admitted only from a loopback peer (local tools, such as the CLI and tests).
32
+ */
33
+ export declare class WccHostPolicy {
34
+ /** The canonical hostnames the server answers. */
35
+ readonly allowedHostnames: ReadonlySet<string>;
36
+ readonly listenHost: string;
37
+ private port;
38
+ constructor(optionsArg: IWccHostPolicyOptions);
39
+ /** A hostname or address, as a URL would carry it; anything else is refused. */
40
+ private static parseHost;
41
+ /** Every address of the machine's network interfaces. */
42
+ private static getInterfaceAddresses;
43
+ /** The port the server listens on, once it does: origins are compared against it. */
44
+ setPort(portArg: number | null): void;
45
+ /**
46
+ * The host the server's own tools reach it on: the loopback address when it listens on all
47
+ * interfaces or on loopback, otherwise the address it listens on.
48
+ */
49
+ getLocalHost(): string;
50
+ /** The host shown to the user: `localhost` for a loopback or all-interface server. */
51
+ getDisplayHost(): string;
52
+ /** Server-wide HTTP and WebSocket admission: the Host must be one the server answers. */
53
+ readonly admitHost: (contextArg: TRequestContext) => TAdmissionResult;
54
+ /** Surface HTTP admission: a request that can change state must come from the server's origin. */
55
+ readonly admitRequestOrigin: (contextArg: TRequestContext) => TAdmissionResult;
56
+ /** Surface WebSocket admission: a connection must come from the server's origin. */
57
+ readonly admitWebsocketOrigin: (contextArg: TRequestContext) => TAdmissionResult;
58
+ /** Whether an Origin header names this server: http, an allowed host and the server's port. */
59
+ isOwnOrigin(originArg: string): boolean;
60
+ private admitOrigin;
61
+ private refuse;
62
+ }
63
+ export {};