@syncbridge/common 1.1.4 → 2.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 (145) hide show
  1. package/app-factory.d.ts +29 -0
  2. package/{processor-factory.js → app-factory.js} +64 -44
  3. package/app-factory.js.map +1 -0
  4. package/classes/{processor-base.d.ts → app-base.d.ts} +3 -3
  5. package/classes/app-base.js +11 -0
  6. package/classes/app-base.js.map +1 -0
  7. package/classes/base-element.d.ts +3 -0
  8. package/classes/base-element.js +2 -0
  9. package/classes/base-element.js.map +1 -1
  10. package/classes/component-base.d.ts +6 -6
  11. package/classes/component-base.js +2 -2
  12. package/classes/component-base.js.map +1 -1
  13. package/classes/log-channel-writer.d.ts +32 -0
  14. package/classes/log-channel-writer.js +97 -0
  15. package/classes/log-channel-writer.js.map +1 -0
  16. package/classes/runnable.d.ts +6 -0
  17. package/classes/runnable.js +22 -0
  18. package/classes/runnable.js.map +1 -1
  19. package/constants.d.ts +1 -1
  20. package/constants.js +1 -1
  21. package/decorators/app-events.decorator.d.ts +5 -0
  22. package/decorators/{processor-events.decorator.js → app-events.decorator.js} +1 -1
  23. package/decorators/app-events.decorator.js.map +1 -0
  24. package/decorators/app.decorator.d.ts +29 -0
  25. package/decorators/{processor.decorator.js → app.decorator.js} +25 -19
  26. package/decorators/app.decorator.js.map +1 -0
  27. package/decorators/component.decorator.js +13 -5
  28. package/decorators/component.decorator.js.map +1 -1
  29. package/decorators/define-variable.decorator.d.ts +6 -1
  30. package/decorators/define-variable.decorator.js +5 -1
  31. package/decorators/define-variable.decorator.js.map +1 -1
  32. package/decorators/log-channel.decorator.d.ts +97 -0
  33. package/decorators/log-channel.decorator.js +34 -0
  34. package/decorators/log-channel.decorator.js.map +1 -0
  35. package/decorators/use-component.decorator.d.ts +2 -2
  36. package/decorators/use-component.decorator.js +1 -1
  37. package/decorators/use-component.decorator.js.map +1 -1
  38. package/decorators/use-variables.decorator.d.ts +2 -2
  39. package/host-link/host-identity.d.ts +53 -0
  40. package/host-link/host-identity.js +140 -0
  41. package/host-link/host-identity.js.map +1 -0
  42. package/host-link/host-link.d.ts +43 -0
  43. package/host-link/host-link.js +54 -0
  44. package/host-link/host-link.js.map +1 -0
  45. package/host-link/host-packages.d.ts +25 -0
  46. package/host-link/host-packages.js +10 -0
  47. package/host-link/host-packages.js.map +1 -0
  48. package/host-link/host-workers.d.ts +75 -0
  49. package/host-link/host-workers.js +29 -0
  50. package/host-link/host-workers.js.map +1 -0
  51. package/host-link/multiplexer.d.ts +115 -0
  52. package/host-link/multiplexer.js +289 -0
  53. package/host-link/multiplexer.js.map +1 -0
  54. package/host-link/sealed-box.d.ts +21 -0
  55. package/host-link/sealed-box.js +104 -0
  56. package/host-link/sealed-box.js.map +1 -0
  57. package/index.d.ts +16 -3
  58. package/index.js +16 -3
  59. package/index.js.map +1 -1
  60. package/models/enums/enum-source.enum.d.ts +11 -0
  61. package/models/enums/enum-source.enum.js +22 -0
  62. package/models/enums/enum-source.enum.js.map +1 -0
  63. package/models/enums/log-channel-kind.d.ts +7 -0
  64. package/models/enums/log-channel-kind.js +14 -0
  65. package/models/enums/log-channel-kind.js.map +1 -0
  66. package/models/enums/service-status.d.ts +5 -1
  67. package/models/enums/service-status.js +4 -0
  68. package/models/enums/service-status.js.map +1 -1
  69. package/models/enums/worker-permission.enum.d.ts +16 -0
  70. package/models/enums/worker-permission.enum.js +28 -0
  71. package/models/enums/worker-permission.enum.js.map +1 -0
  72. package/models/index.d.ts +6 -1
  73. package/models/index.js +6 -1
  74. package/models/index.js.map +1 -1
  75. package/models/metadata/app-metadata.d.ts +6 -0
  76. package/models/metadata/{processor-metadata.js → app-metadata.js} +7 -7
  77. package/models/metadata/app-metadata.js.map +1 -0
  78. package/models/metadata/element-component-metadata.d.ts +2 -0
  79. package/models/metadata/element-component-metadata.js +10 -2
  80. package/models/metadata/element-component-metadata.js.map +1 -1
  81. package/models/metadata/element-metadata.d.ts +8 -0
  82. package/models/metadata/element-metadata.js +40 -0
  83. package/models/metadata/element-metadata.js.map +1 -1
  84. package/models/metadata/log-channel-metadata.d.ts +23 -0
  85. package/models/metadata/log-channel-metadata.js +73 -0
  86. package/models/metadata/log-channel-metadata.js.map +1 -0
  87. package/models/metadata/plugin-metadata.d.ts +15 -0
  88. package/models/metadata/plugin-metadata.js +66 -0
  89. package/models/metadata/plugin-metadata.js.map +1 -0
  90. package/models/metadata/variable-metadata.d.ts +3 -0
  91. package/models/metadata/variable-metadata.js +15 -1
  92. package/models/metadata/variable-metadata.js.map +1 -1
  93. package/models/profile/log-options.d.ts +17 -0
  94. package/models/profile/log-options.js +58 -1
  95. package/models/profile/log-options.js.map +1 -1
  96. package/models/profile/profile.d.ts +2 -2
  97. package/models/profile/profile.js +9 -9
  98. package/models/profile/profile.js.map +1 -1
  99. package/package.json +2 -2
  100. package/registry/docs-sidebar.d.ts +59 -0
  101. package/registry/docs-sidebar.js +170 -0
  102. package/registry/docs-sidebar.js.map +1 -0
  103. package/registry/extension-package.d.ts +40 -13
  104. package/registry/extension-package.js +59 -18
  105. package/registry/extension-package.js.map +1 -1
  106. package/registry/extension-registry.d.ts +16 -16
  107. package/registry/extension-registry.js +59 -44
  108. package/registry/extension-registry.js.map +1 -1
  109. package/registry/module-catalog.d.ts +31 -0
  110. package/registry/module-catalog.js +89 -0
  111. package/registry/module-catalog.js.map +1 -0
  112. package/registry/package-docs.d.ts +98 -0
  113. package/registry/package-docs.js +160 -0
  114. package/registry/package-docs.js.map +1 -0
  115. package/registry/package-manifest.d.ts +54 -3
  116. package/registry/package-manifest.js +59 -1
  117. package/registry/package-manifest.js.map +1 -1
  118. package/utils/decode-profile.d.ts +18 -2
  119. package/utils/decode-profile.js +65 -9
  120. package/utils/decode-profile.js.map +1 -1
  121. package/utils/encrypt-helpers.d.ts +7 -0
  122. package/utils/encrypt-helpers.js +12 -5
  123. package/utils/encrypt-helpers.js.map +1 -1
  124. package/utils/materialize-metadata.d.ts +3 -3
  125. package/utils/materialize-metadata.js +16 -12
  126. package/utils/materialize-metadata.js.map +1 -1
  127. package/utils/normalize-metadata.d.ts +23 -4
  128. package/utils/normalize-metadata.js +52 -6
  129. package/utils/normalize-metadata.js.map +1 -1
  130. package/utils/type-guards.d.ts +2 -2
  131. package/utils/type-guards.js +4 -4
  132. package/utils/type-guards.js.map +1 -1
  133. package/utils/worker-permissions.d.ts +25 -0
  134. package/utils/worker-permissions.js +60 -0
  135. package/utils/worker-permissions.js.map +1 -0
  136. package/classes/processor-base.js +0 -11
  137. package/classes/processor-base.js.map +0 -1
  138. package/decorators/processor-events.decorator.d.ts +0 -5
  139. package/decorators/processor-events.decorator.js.map +0 -1
  140. package/decorators/processor.decorator.d.ts +0 -29
  141. package/decorators/processor.decorator.js.map +0 -1
  142. package/models/metadata/processor-metadata.d.ts +0 -6
  143. package/models/metadata/processor-metadata.js.map +0 -1
  144. package/processor-factory.d.ts +0 -26
  145. package/processor-factory.js.map +0 -1
@@ -0,0 +1,140 @@
1
+ import { createHash, createPrivateKey, createPublicKey, generateKeyPairSync, randomBytes, sign, verify, } from 'node:crypto';
2
+ /*
3
+ * A host's identity with SyncBridge Console: a key pair of its own, made on
4
+ * the host - its private key never leaves it. It is paired once, by a code
5
+ * Console gives (short-lived, taken once): the host sends its public key
6
+ * with the code. From then on it proves itself by tokens it signs, each
7
+ * short-lived: no secret travels, and a token seen is no key.
8
+ */
9
+ /** Where a host pairs with Console, by a code: on its API's port */
10
+ export const HOST_PAIR_PATH = '/hosts/pair';
11
+ /**
12
+ * The header a host's token for Console's APIs comes in: a bearer token
13
+ * is a user's, there
14
+ */
15
+ export const HOST_TOKEN_HEADER = 'x-sb-host-token';
16
+ /** The longest a token is taken for, by what it is for */
17
+ export const HOST_TOKEN_MAX_SECONDS = {
18
+ link: 60,
19
+ api: 15 * 60,
20
+ };
21
+ /** A token's first part: its kind and version */
22
+ const TOKEN_PREFIX = 'sbh1';
23
+ /** A key pair for a host: its public key told, its private key kept */
24
+ export function generateHostKeys() {
25
+ const { publicKey, privateKey } = generateKeyPairSync('ed25519');
26
+ return {
27
+ publicKey: publicKey
28
+ .export({ type: 'spki', format: 'der' })
29
+ .toString('base64'),
30
+ privateKey: privateKey.export({ type: 'pkcs8', format: 'pem' }).toString(),
31
+ };
32
+ }
33
+ /** A public key, as sent: an Ed25519 key's SPKI, base64; undefined if not */
34
+ export function readHostPublicKey(value) {
35
+ try {
36
+ const der = Buffer.from(String(value), 'base64');
37
+ const key = createPublicKey({ key: der, format: 'der', type: 'spki' });
38
+ return key.asymmetricKeyType === 'ed25519' ? der : undefined;
39
+ }
40
+ catch {
41
+ return;
42
+ }
43
+ }
44
+ /** A public key's fingerprint, to compare by eye: SHA256:<base64> */
45
+ export function hostKeyFingerprint(publicKey) {
46
+ return ('SHA256:' +
47
+ createHash('sha256')
48
+ .update(Buffer.from(publicKey, 'base64'))
49
+ .digest('base64')
50
+ .replace(/=+$/, ''));
51
+ }
52
+ /** A token of the host's, signed with its private key */
53
+ export function signHostToken(privateKey, input) {
54
+ const now = Math.floor(Date.now() / 1000);
55
+ const payload = {
56
+ h: input.hostId,
57
+ a: input.audience,
58
+ iat: now,
59
+ exp: now +
60
+ Math.min(input.seconds ?? HOST_TOKEN_MAX_SECONDS[input.audience], HOST_TOKEN_MAX_SECONDS[input.audience]),
61
+ n: randomBytes(12).toString('base64url'),
62
+ };
63
+ const body = `${TOKEN_PREFIX}.${Buffer.from(JSON.stringify(payload)).toString('base64url')}`;
64
+ const signature = sign(null, Buffer.from(body), createPrivateKey(privateKey));
65
+ return `${body}.${signature.toString('base64url')}`;
66
+ }
67
+ /** What a token says, not checked yet: whose it is, to find its key */
68
+ export function readHostToken(token) {
69
+ const [prefix, body] = String(token || '').split('.');
70
+ if (prefix !== TOKEN_PREFIX || !body)
71
+ return;
72
+ try {
73
+ const p = JSON.parse(Buffer.from(body, 'base64url').toString());
74
+ return typeof p?.h === 'string' &&
75
+ (p.a === 'link' || p.a === 'api') &&
76
+ Number.isInteger(p.iat) &&
77
+ Number.isInteger(p.exp) &&
78
+ typeof p.n === 'string'
79
+ ? p
80
+ : undefined;
81
+ }
82
+ catch {
83
+ return;
84
+ }
85
+ }
86
+ /**
87
+ * What a token says, if it is signed with the host's key, for this, now:
88
+ * not ended, not made later than now (a minute's clock skew taken), and
89
+ * not lasting longer than its kind may
90
+ */
91
+ export function verifyHostToken(token, publicKey, audience) {
92
+ const payload = readHostToken(token);
93
+ if (!payload || payload.a !== audience)
94
+ return;
95
+ const now = Math.floor(Date.now() / 1000);
96
+ if (payload.exp <= now ||
97
+ payload.iat > now + 60 ||
98
+ payload.exp - payload.iat > HOST_TOKEN_MAX_SECONDS[audience])
99
+ return;
100
+ const [prefix, body, signature] = token.split('.');
101
+ try {
102
+ const key = createPublicKey({
103
+ key: Buffer.from(publicKey, 'base64'),
104
+ format: 'der',
105
+ type: 'spki',
106
+ });
107
+ return verify(null, Buffer.from(`${prefix}.${body}`), key, Buffer.from(signature || '', 'base64url'))
108
+ ? payload
109
+ : undefined;
110
+ }
111
+ catch {
112
+ return;
113
+ }
114
+ }
115
+ /* A pairing code's letters: no 0/O, 1/I/L to mix up */
116
+ const CODE_LETTERS = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789';
117
+ /** A new pairing code: 8 letters, as XXXX-XXXX (40 bits) */
118
+ export function generatePairingCode() {
119
+ const bytes = randomBytes(8);
120
+ let code = '';
121
+ for (const b of bytes)
122
+ code += CODE_LETTERS[b % CODE_LETTERS.length];
123
+ return `${code.slice(0, 4)}-${code.slice(4)}`;
124
+ }
125
+ /**
126
+ * A pairing code as typed, taken as given: upper case, spaces and dashes
127
+ * left out; undefined if not a code
128
+ */
129
+ export function normalizePairingCode(input) {
130
+ const code = String(input || '')
131
+ .toUpperCase()
132
+ .replace(/[\s-]/g, '');
133
+ if (code.length !== 8)
134
+ return;
135
+ for (const c of code)
136
+ if (!CODE_LETTERS.includes(c))
137
+ return;
138
+ return `${code.slice(0, 4)}-${code.slice(4)}`;
139
+ }
140
+ //# sourceMappingURL=host-identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-identity.js","sourceRoot":"","sources":["../../src/host-link/host-identity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /** Where Console takes the hosts' links, on its API's port */
2
+ export declare const HOST_LINK_PATH = "/hosts/link";
3
+ /**
4
+ * The close code Console closes a link with when the host is not to
5
+ * connect again: its key changed, it was removed, or another host
6
+ * connected with the key. Any other close is a drop: it connects again.
7
+ */
8
+ export declare const HOST_LINK_CLOSED = 4001;
9
+ /** A worker as a host tells of it */
10
+ export interface HostWorker {
11
+ id: string;
12
+ name?: string;
13
+ status?: string;
14
+ }
15
+ /** A host's workers, counted: and those needing a look, by name */
16
+ export interface HostWorkerSummary {
17
+ total: number;
18
+ started: number;
19
+ stopped: number;
20
+ /** Unhealthy, invalid, or needing an init */
21
+ problem: number;
22
+ /** Starting, stopping, initializing */
23
+ other: number;
24
+ /** The workers with a problem, the first of them */
25
+ problems: HostWorker[];
26
+ }
27
+ /** What a host tells of itself: sent when it connects, and as it changes */
28
+ export interface HostStatusMessage {
29
+ type: 'status';
30
+ /** The SyncBridge version */
31
+ version: string;
32
+ /** When SyncBridge started, ISO */
33
+ startTime: string;
34
+ workers: HostWorkerSummary;
35
+ }
36
+ /** The problems named at most: the counts tell the rest */
37
+ export declare const MAX_NAMED_PROBLEMS = 10;
38
+ /** A host's workers, counted, as it tells Console */
39
+ export declare function summarizeWorkers(workers: Iterable<HostWorker>): HostWorkerSummary;
40
+ export type HostLinkMessage = HostStatusMessage;
41
+ export declare const CONSOLE_USER_HEADER = "x-sb-console-user";
42
+ export declare const CONSOLE_USER_NAME_HEADER = "x-sb-console-user-name";
43
+ export declare const CONSOLE_ROLE_HEADER = "x-sb-console-role";
@@ -0,0 +1,54 @@
1
+ /*
2
+ * The link of a SyncBridge server (a host) to SyncBridge Console: a
3
+ * WebSocket the host opens - so it may be behind any firewall - with its
4
+ * key (`Authorization: Bearer <key>`), its binary messages the frames of a
5
+ * Multiplexer. Console opens streams on it to the host's own HTTP
6
+ * server; the link's control messages are below.
7
+ */
8
+ /** Where Console takes the hosts' links, on its API's port */
9
+ export const HOST_LINK_PATH = '/hosts/link';
10
+ /**
11
+ * The close code Console closes a link with when the host is not to
12
+ * connect again: its key changed, it was removed, or another host
13
+ * connected with the key. Any other close is a drop: it connects again.
14
+ */
15
+ export const HOST_LINK_CLOSED = 4001;
16
+ /** The statuses of a worker needing a look */
17
+ const PROBLEM_STATUSES = new Set(['unhealthy', 'invalid', 'needs-init']);
18
+ /** The problems named at most: the counts tell the rest */
19
+ export const MAX_NAMED_PROBLEMS = 10;
20
+ /** A host's workers, counted, as it tells Console */
21
+ export function summarizeWorkers(workers) {
22
+ const summary = {
23
+ total: 0,
24
+ started: 0,
25
+ stopped: 0,
26
+ problem: 0,
27
+ other: 0,
28
+ problems: [],
29
+ };
30
+ for (const worker of workers) {
31
+ summary.total++;
32
+ if (worker.status === 'started')
33
+ summary.started++;
34
+ else if (worker.status === 'stopped')
35
+ summary.stopped++;
36
+ else if (PROBLEM_STATUSES.has(worker.status || '')) {
37
+ summary.problem++;
38
+ if (summary.problems.length < MAX_NAMED_PROBLEMS)
39
+ summary.problems.push(worker);
40
+ }
41
+ else
42
+ summary.other++;
43
+ }
44
+ return summary;
45
+ }
46
+ /*
47
+ * The headers a request Console passes on to the host carries: who it
48
+ * is for. The host takes them only from a stream of its link: what comes
49
+ * any other way is not Console.
50
+ */
51
+ export const CONSOLE_USER_HEADER = 'x-sb-console-user';
52
+ export const CONSOLE_USER_NAME_HEADER = 'x-sb-console-user-name';
53
+ export const CONSOLE_ROLE_HEADER = 'x-sb-console-role';
54
+ //# sourceMappingURL=host-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-link.js","sourceRoot":"","sources":["../../src/host-link/host-link.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,8DAA8D;AAC9D,MAAM,CAAC,MAAM,cAAc,GAAG,aAAa,CAAC;AAE5C;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAgCrC,8CAA8C;AAC9C,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC;AACzE,2DAA2D;AAC3D,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAErC,qDAAqD;AACrD,MAAM,UAAU,gBAAgB,CAC9B,OAA6B;IAE7B,MAAM,OAAO,GAAsB;QACjC,KAAK,EAAE,CAAC;QACR,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,CAAC;QACV,KAAK,EAAE,CAAC;QACR,QAAQ,EAAE,EAAE;KACb,CAAC;IACF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,OAAO,CAAC,KAAK,EAAE,CAAC;QAChB,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,CAAC,OAAO,EAAE,CAAC;aAC9C,IAAI,MAAM,CAAC,MAAM,KAAK,SAAS;YAAE,OAAO,CAAC,OAAO,EAAE,CAAC;aACnD,IAAI,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,EAAE,CAAC;YACnD,OAAO,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,kBAAkB;gBAC9C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,CAAC;;YAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAID;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,mBAAmB,CAAC;AACvD,MAAM,CAAC,MAAM,wBAAwB,GAAG,wBAAwB,CAAC;AACjE,MAAM,CAAC,MAAM,mBAAmB,GAAG,mBAAmB,CAAC"}
@@ -0,0 +1,25 @@
1
+ /** Where, under Console's API, a host's packages are planned */
2
+ export declare const HOST_PACKAGES_PATH = "host/packages";
3
+ /** What a host asks to install */
4
+ export interface HostPackagesResolveRequest {
5
+ /** "@org/name" */
6
+ name: string;
7
+ /** Its versions taken: "^2.0.0"; none, the newest */
8
+ range?: string;
9
+ /** What it has: each package's versions installed */
10
+ installed?: Record<string, string[]>;
11
+ }
12
+ /** A package of the plan, to be installed in its turn */
13
+ export interface HostPackagePlanItem {
14
+ name: string;
15
+ version: string;
16
+ size: number;
17
+ /** SHA-256 of its archive, hex: checked before it is installed */
18
+ sha256: string;
19
+ /** Where it is read, relative to Console's API; a few minutes good */
20
+ url: string;
21
+ }
22
+ /** What to install, in order: the packages needed first */
23
+ export interface HostPackagesResolveResponse {
24
+ items: HostPackagePlanItem[];
25
+ }
@@ -0,0 +1,10 @@
1
+ /*
2
+ * Packages a host installs from the registry: it asks Console for a plan -
3
+ * the version of what it wants, the registry packages it needs that it
4
+ * has not - each one it may download (public, or its workspace's), each
5
+ * with its size, its hash and a short-lived link to it. Asked by the host,
6
+ * with a token it signs (HOST_TOKEN_HEADER).
7
+ */
8
+ /** Where, under Console's API, a host's packages are planned */
9
+ export const HOST_PACKAGES_PATH = 'host/packages';
10
+ //# sourceMappingURL=host-packages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-packages.js","sourceRoot":"","sources":["../../src/host-link/host-packages.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,gEAAgE;AAChE,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC"}
@@ -0,0 +1,75 @@
1
+ /** Where, under Console's API, a host's workers are kept */
2
+ export declare const HOST_WORKERS_PATH = "host/workers";
3
+ /** A worker as a host tells of it: its id, its profile's hash */
4
+ export interface HostWorkerDigest {
5
+ id: string;
6
+ hash: string;
7
+ }
8
+ /** What a host tells of its workers: all of them, so those gone go */
9
+ export interface HostWorkersSyncRequest {
10
+ workers: HostWorkerDigest[];
11
+ }
12
+ /** What Console answers: whether it keeps them; if so, which to send */
13
+ export interface HostWorkersSyncResponse {
14
+ /** Its workspace keeps its hosts' workers: off, none are sent */
15
+ enabled: boolean;
16
+ /** Why not, when not */
17
+ reason?: string;
18
+ /** Those it has not, or not as they are: to be sent */
19
+ need: string[];
20
+ /** The key secret values are sealed for, on the way to Console */
21
+ sealKey?: string;
22
+ /**
23
+ * The host was reinstalled: the workers Console keeps are to be taken
24
+ * back (HOST_WORKERS_RESTORE_PATH). None is forgotten meanwhile.
25
+ */
26
+ restore?: boolean;
27
+ }
28
+ /** A worker's profile, as sent: its secret values sealed for Console */
29
+ export interface HostWorkerProfile {
30
+ id: string;
31
+ hash: string;
32
+ profile: Record<string, any>;
33
+ /** The version of its app's package it runs: taken again on restore */
34
+ packageVersion?: string;
35
+ }
36
+ /** The profiles sent at once: their secrets sealed for Console */
37
+ export interface HostWorkersPutRequest {
38
+ workers: HostWorkerProfile[];
39
+ }
40
+ /**
41
+ * A profile with each of its string values passed through `fn` - its
42
+ * secret values found by what they start with - the rest as they are
43
+ */
44
+ export declare function mapProfileStrings<T>(value: T, fn: (text: string) => string): T;
45
+ /** Where, under Console's API, a host reinstalled takes its workers back */
46
+ export declare const HOST_WORKERS_RESTORE_PATH = "host/workers/restore";
47
+ /** A worker to take back: its secret values sealed for the host */
48
+ export interface HostRestoreWorker {
49
+ id: string;
50
+ /** Its profile's hash, as Console keeps it: told back while it waits */
51
+ hash: string;
52
+ profile: Record<string, any>;
53
+ /** Its app's package, and the version of it it ran */
54
+ packageName?: string;
55
+ packageVersion?: string;
56
+ }
57
+ /** The workers a host reinstalled takes back */
58
+ export interface HostWorkersRestoreResponse {
59
+ workers: HostRestoreWorker[];
60
+ }
61
+ /**
62
+ * Where a worker taken back is: restored; waiting for its package - one
63
+ * not in the registry, or its workspace may not use - or failed
64
+ */
65
+ export type HostWorkerRestoreState = 'restored' | 'waiting' | 'failed';
66
+ /** What a host tells of the workers it takes back */
67
+ export interface HostWorkersRestoreStatusRequest {
68
+ workers: {
69
+ id: string;
70
+ state: HostWorkerRestoreState;
71
+ message?: string;
72
+ }[];
73
+ /** Each one tried: the reinstall is over, those waiting wait on the host */
74
+ done?: boolean;
75
+ }
@@ -0,0 +1,29 @@
1
+ /*
2
+ * A host's workers' configurations (profiles), kept by Console: a host
3
+ * reinstalled gets them back. The host tells Console what it has - each
4
+ * worker by a hash of its profile - and sends those Console asks for;
5
+ * Console keeps them, their secret values sealed on the way and kept
6
+ * enciphered. Asked by the host, with a token it signs (HOST_TOKEN_HEADER).
7
+ */
8
+ /** Where, under Console's API, a host's workers are kept */
9
+ export const HOST_WORKERS_PATH = 'host/workers';
10
+ /**
11
+ * A profile with each of its string values passed through `fn` - its
12
+ * secret values found by what they start with - the rest as they are
13
+ */
14
+ export function mapProfileStrings(value, fn) {
15
+ if (typeof value === 'string')
16
+ return fn(value);
17
+ if (Array.isArray(value))
18
+ return value.map(item => mapProfileStrings(item, fn));
19
+ if (value && typeof value === 'object') {
20
+ const out = {};
21
+ for (const [key, item] of Object.entries(value))
22
+ out[key] = mapProfileStrings(item, fn);
23
+ return out;
24
+ }
25
+ return value;
26
+ }
27
+ /** Where, under Console's API, a host reinstalled takes its workers back */
28
+ export const HOST_WORKERS_RESTORE_PATH = 'host/workers/restore';
29
+ //# sourceMappingURL=host-workers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-workers.js","sourceRoot":"","sources":["../../src/host-link/host-workers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,4DAA4D;AAC5D,MAAM,CAAC,MAAM,iBAAiB,GAAG,cAAc,CAAC;AA4ChD;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAQ,EACR,EAA4B;IAE5B,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC,KAAK,CAAM,CAAC;IACrD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACtB,OAAO,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAM,CAAC;IAC7D,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;YAC7C,GAAG,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACzC,OAAO,GAAQ,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,4EAA4E;AAC5E,MAAM,CAAC,MAAM,yBAAyB,GAAG,sBAAsB,CAAC"}
@@ -0,0 +1,115 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import { EventEmitter } from 'node:events';
3
+ import { Duplex } from 'node:stream';
4
+ declare const FrameType: {
5
+ /** Opens a stream; payload: its meta, JSON */
6
+ readonly OPEN: 1;
7
+ readonly DATA: 2;
8
+ /** Lets the peer send that many more bytes on the stream; payload: u32 */
9
+ readonly CREDIT: 3;
10
+ /** The sender writes no more on the stream */
11
+ readonly END: 4;
12
+ /** The stream is gone, both ways; payload: why, utf8 */
13
+ readonly RESET: 5;
14
+ /** A message of the link itself, on no stream (id 0); payload: JSON */
15
+ readonly CONTROL: 6;
16
+ };
17
+ type FrameType = (typeof FrameType)[keyof typeof FrameType];
18
+ export declare namespace Multiplexer {
19
+ interface Options {
20
+ /** Sends a frame over the link */
21
+ send: (frame: Buffer) => void;
22
+ /**
23
+ * The side it is: the streams one side opens have odd ids, the other's
24
+ * even, so both open without asking
25
+ */
26
+ side: 'console' | 'host';
27
+ }
28
+ interface Events {
29
+ /** The peer opened a stream */
30
+ stream: [stream: MuxStream, meta: Record<string, any>];
31
+ /** The peer sent a message of the link */
32
+ control: [message: Record<string, any>];
33
+ }
34
+ }
35
+ /**
36
+ * Streams over one link: each a Duplex, with its own flow control - a
37
+ * sender sends no more than its receiver has room for, so a slow stream
38
+ * holds back itself, not the link or the others.
39
+ */
40
+ export declare class Multiplexer extends EventEmitter<Multiplexer.Events> {
41
+ readonly options: Multiplexer.Options;
42
+ protected _streams: Map<number, MuxStream>;
43
+ protected _nextId: number;
44
+ protected _closed: boolean;
45
+ constructor(options: Multiplexer.Options);
46
+ get closed(): boolean;
47
+ /** Opens a stream to the peer, with what it is for */
48
+ open(meta?: Record<string, any>): MuxStream;
49
+ /** Sends a message of the link itself */
50
+ control(message: Record<string, any>): void;
51
+ /** Takes a frame the link received */
52
+ receive(frame: Buffer): void;
53
+ /** The link is gone: its streams too */
54
+ close(reason?: string): void;
55
+ /** @internal */
56
+ _send(type: FrameType, id: number, payload?: Buffer): void;
57
+ /** @internal */
58
+ _sendCredit(id: number, bytes: number): void;
59
+ /** @internal */
60
+ _sendData(id: number, data: Buffer): void;
61
+ /** @internal */
62
+ _sendEnd(id: number): void;
63
+ /** @internal */
64
+ _sendReset(id: number, reason: string): void;
65
+ /** @internal */
66
+ _forget(id: number): void;
67
+ }
68
+ /**
69
+ * A stream of a Multiplexer. It passes for a socket where a Node HTTP
70
+ * server or client takes one: it has what they call on a socket.
71
+ */
72
+ export declare class MuxStream extends Duplex {
73
+ protected _mux: Multiplexer;
74
+ readonly id: number;
75
+ /** Bytes it may send: what the peer has room for */
76
+ protected _credit: number;
77
+ /** Bytes taken in but not yet given back as credit: the reader is slow */
78
+ protected _owed: number;
79
+ /** A write waiting for credit */
80
+ protected _pending?: {
81
+ data: Buffer;
82
+ callback: (e?: Error) => void;
83
+ };
84
+ protected _ended: boolean;
85
+ protected _peerEnded: boolean;
86
+ protected _timeout?: NodeJS.Timeout;
87
+ protected _timeoutMs: number;
88
+ /** Where it comes from, for a server's request: the link */
89
+ readonly remoteAddress = "tunnel";
90
+ readonly remotePort = 0;
91
+ constructor(_mux: Multiplexer, id: number);
92
+ setTimeout(ms: number, callback?: () => void): this;
93
+ setNoDelay(): this;
94
+ setKeepAlive(): this;
95
+ ref(): this;
96
+ unref(): this;
97
+ address(): {};
98
+ /** @internal */
99
+ _receiveData(data: Buffer): void;
100
+ /** @internal */
101
+ _receiveCredit(bytes: number): void;
102
+ /** @internal */
103
+ _receiveEnd(): void;
104
+ /** @internal */
105
+ _receiveReset(reason: string): void;
106
+ _read(): void;
107
+ _write(chunk: Buffer, _encoding: BufferEncoding, callback: (e?: Error) => void): void;
108
+ _final(callback: (e?: Error) => void): void;
109
+ _destroy(error: Error | null, callback: (e: Error | null) => void): void;
110
+ /** Done both ways: it is let go */
111
+ protected _settle(): void;
112
+ /** Activity: the idle timeout starts over */
113
+ protected _touch(): void;
114
+ }
115
+ export {};