@toa.io/extensions.introspection 1.0.0-alpha.259
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/components/introspection.edges/manifest.toa.yaml +48 -0
- package/components/introspection.edges/operations/lib/types.d.ts +28 -0
- package/components/introspection.edges/operations/lib/types.js +3 -0
- package/components/introspection.edges/operations/lib/types.js.map +1 -0
- package/components/introspection.edges/operations/merge.d.ts +9 -0
- package/components/introspection.edges/operations/merge.js +25 -0
- package/components/introspection.edges/operations/merge.js.map +1 -0
- package/components/introspection.edges/operations/tsconfig.tsbuildinfo +1 -0
- package/components/introspection.edges/source/lib/types.ts +33 -0
- package/components/introspection.edges/source/merge.ts +26 -0
- package/components/introspection.edges/tsconfig.json +9 -0
- package/components/introspection.nodes/manifest.toa.yaml +49 -0
- package/components/introspection.nodes/operations/lib/types.d.ts +16 -0
- package/components/introspection.nodes/operations/lib/types.js +3 -0
- package/components/introspection.nodes/operations/lib/types.js.map +1 -0
- package/components/introspection.nodes/operations/merge.d.ts +9 -0
- package/components/introspection.nodes/operations/merge.js +21 -0
- package/components/introspection.nodes/operations/merge.js.map +1 -0
- package/components/introspection.nodes/operations/tsconfig.tsbuildinfo +1 -0
- package/components/introspection.nodes/source/lib/types.ts +18 -0
- package/components/introspection.nodes/source/merge.ts +21 -0
- package/components/introspection.nodes/tsconfig.json +9 -0
- package/cucumber.js +8 -0
- package/features/calls.feature +123 -0
- package/features/nodes.feature +70 -0
- package/features/samples.feature +75 -0
- package/features/shutdown.feature +37 -0
- package/features/site/_app/immutable/asset.js +1 -0
- package/features/site/index.html +6 -0
- package/features/steps/Map.ts +151 -0
- package/features/steps/UI.ts +88 -0
- package/features/steps/components/probe.discreet/manifest.toa.yaml +12 -0
- package/features/steps/components/probe.discreet/operations/ping.js +7 -0
- package/features/steps/components/probe.quiet/manifest.toa.yaml +11 -0
- package/features/steps/components/probe.quiet/operations/ping.js +7 -0
- package/features/steps/components/probe.source/manifest.toa.yaml +27 -0
- package/features/steps/components/probe.source/operations/crash.js +7 -0
- package/features/steps/components/probe.source/operations/fail.js +12 -0
- package/features/steps/components/probe.source/operations/relay.js +7 -0
- package/features/steps/components/probe.target/manifest.toa.yaml +24 -0
- package/features/steps/components/probe.target/operations/compute.js +7 -0
- package/features/steps/components/probe.target/operations/count.js +9 -0
- package/features/steps/config.ts +20 -0
- package/features/steps/tsconfig.json +9 -0
- package/features/ui.feature +85 -0
- package/package.json +32 -0
- package/readme.md +135 -0
- package/schemas/annotation.cos.yaml +21 -0
- package/schemas/declaration.cos.yaml +2 -0
- package/source/Composition.ts +61 -0
- package/source/Explorer.ts +16 -0
- package/source/Factory.ts +119 -0
- package/source/Reporter.ts +210 -0
- package/source/Tenant.ts +47 -0
- package/source/UI.ts +197 -0
- package/source/annotation.ts +83 -0
- package/source/const.ts +32 -0
- package/source/describe.ts +68 -0
- package/source/extension.ts +45 -0
- package/source/index.ts +7 -0
- package/source/keys.ts +0 -0
- package/source/manifest.ts +19 -0
- package/source/model.ts +82 -0
- package/source/sample.ts +49 -0
- package/source/schemas.ts +10 -0
- package/transpiled/Composition.d.ts +21 -0
- package/transpiled/Composition.js +48 -0
- package/transpiled/Composition.js.map +1 -0
- package/transpiled/Explorer.d.ts +9 -0
- package/transpiled/Explorer.js +19 -0
- package/transpiled/Explorer.js.map +1 -0
- package/transpiled/Factory.d.ts +22 -0
- package/transpiled/Factory.js +93 -0
- package/transpiled/Factory.js.map +1 -0
- package/transpiled/Reporter.d.ts +44 -0
- package/transpiled/Reporter.js +187 -0
- package/transpiled/Reporter.js.map +1 -0
- package/transpiled/Tenant.d.ts +18 -0
- package/transpiled/Tenant.js +40 -0
- package/transpiled/Tenant.js.map +1 -0
- package/transpiled/UI.d.ts +25 -0
- package/transpiled/UI.js +186 -0
- package/transpiled/UI.js.map +1 -0
- package/transpiled/annotation.d.ts +40 -0
- package/transpiled/annotation.js +46 -0
- package/transpiled/annotation.js.map +1 -0
- package/transpiled/const.d.ts +23 -0
- package/transpiled/const.js +27 -0
- package/transpiled/const.js.map +1 -0
- package/transpiled/describe.d.ts +9 -0
- package/transpiled/describe.js +64 -0
- package/transpiled/describe.js.map +1 -0
- package/transpiled/extension.d.ts +11 -0
- package/transpiled/extension.js +64 -0
- package/transpiled/extension.js.map +1 -0
- package/transpiled/index.d.ts +6 -0
- package/transpiled/index.js +25 -0
- package/transpiled/index.js.map +1 -0
- package/transpiled/keys.d.ts +9 -0
- package/transpiled/keys.js +0 -0
- package/transpiled/keys.js.map +1 -0
- package/transpiled/manifest.d.ts +9 -0
- package/transpiled/manifest.js +43 -0
- package/transpiled/manifest.js.map +1 -0
- package/transpiled/model.d.ts +71 -0
- package/transpiled/model.js +3 -0
- package/transpiled/model.js.map +1 -0
- package/transpiled/sample.d.ts +7 -0
- package/transpiled/sample.js +44 -0
- package/transpiled/sample.js.map +1 -0
- package/transpiled/schemas.d.ts +4 -0
- package/transpiled/schemas.js +13 -0
- package/transpiled/schemas.js.map +1 -0
- package/transpiled/tsconfig.tsbuildinfo +1 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Connector } from '@toa.io/core';
|
|
2
|
+
import type { Bootloader } from './Factory';
|
|
3
|
+
import type { Options } from './annotation';
|
|
4
|
+
import type { Edge, Node } from './model';
|
|
5
|
+
/**
|
|
6
|
+
* Buffers what a process observes and flushes it into the introspection
|
|
7
|
+
* components.
|
|
8
|
+
*
|
|
9
|
+
* Nothing here is on the critical path. Reaching the explorer is a discovery,
|
|
10
|
+
* which waits as long as it takes, so the collector never holds up a component
|
|
11
|
+
* starting, running or stopping: it buffers until the connection is there, and
|
|
12
|
+
* when it has to choose it gives up on the data rather than on the application.
|
|
13
|
+
*/
|
|
14
|
+
export declare class Reporter extends Connector {
|
|
15
|
+
private readonly boot;
|
|
16
|
+
private readonly options;
|
|
17
|
+
private readonly nodes;
|
|
18
|
+
private readonly edges;
|
|
19
|
+
/** Holds a remote only once it is connected and usable. */
|
|
20
|
+
private readonly remotes;
|
|
21
|
+
private timer;
|
|
22
|
+
private flushing;
|
|
23
|
+
private acquiring;
|
|
24
|
+
private dropped;
|
|
25
|
+
constructor(boot: Bootloader, options: Options);
|
|
26
|
+
/** The static description of a component. */
|
|
27
|
+
expose(node: Node): void;
|
|
28
|
+
/** A call between two components. */
|
|
29
|
+
observe(observed: Edge): void;
|
|
30
|
+
protected open(): Promise<void>;
|
|
31
|
+
protected close(): Promise<void>;
|
|
32
|
+
private ready;
|
|
33
|
+
private flush;
|
|
34
|
+
private discard;
|
|
35
|
+
private dispatch;
|
|
36
|
+
/**
|
|
37
|
+
* A mass transition: every affected object is acquired and committed at once,
|
|
38
|
+
* so a flush is one call per component whatever it carries.
|
|
39
|
+
*/
|
|
40
|
+
private merge;
|
|
41
|
+
/** Runs in the background: discovery waits for the explorer as long as it takes. */
|
|
42
|
+
private acquire;
|
|
43
|
+
private reach;
|
|
44
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.Reporter = void 0;
|
|
27
|
+
const core_1 = require("@toa.io/core");
|
|
28
|
+
const openspan_1 = require("openspan");
|
|
29
|
+
const const_1 = require("./const");
|
|
30
|
+
const keys = __importStar(require("./keys"));
|
|
31
|
+
/**
|
|
32
|
+
* Buffers what a process observes and flushes it into the introspection
|
|
33
|
+
* components.
|
|
34
|
+
*
|
|
35
|
+
* Nothing here is on the critical path. Reaching the explorer is a discovery,
|
|
36
|
+
* which waits as long as it takes, so the collector never holds up a component
|
|
37
|
+
* starting, running or stopping: it buffers until the connection is there, and
|
|
38
|
+
* when it has to choose it gives up on the data rather than on the application.
|
|
39
|
+
*/
|
|
40
|
+
class Reporter extends core_1.Connector {
|
|
41
|
+
boot;
|
|
42
|
+
options;
|
|
43
|
+
nodes = new Map();
|
|
44
|
+
edges = new Map();
|
|
45
|
+
/** Holds a remote only once it is connected and usable. */
|
|
46
|
+
remotes = {};
|
|
47
|
+
timer = null;
|
|
48
|
+
flushing = null;
|
|
49
|
+
acquiring = false;
|
|
50
|
+
dropped = 0;
|
|
51
|
+
constructor(boot, options) {
|
|
52
|
+
super();
|
|
53
|
+
this.boot = boot;
|
|
54
|
+
this.options = options;
|
|
55
|
+
}
|
|
56
|
+
/** The static description of a component. */
|
|
57
|
+
expose(node) {
|
|
58
|
+
this.nodes.set(keys.node(node.namespace, node.component), node);
|
|
59
|
+
void this.flush();
|
|
60
|
+
}
|
|
61
|
+
/** A call between two components. */
|
|
62
|
+
observe(observed) {
|
|
63
|
+
const id = keys.edge(observed.src, observed.dst);
|
|
64
|
+
const edge = this.edges.get(id);
|
|
65
|
+
if (edge === undefined) {
|
|
66
|
+
/*
|
|
67
|
+
* `source` arrives over the wire, so the number of distinct edges a process
|
|
68
|
+
* can hold has to be bounded regardless of what peers send — and of whether
|
|
69
|
+
* anyone is there to take them.
|
|
70
|
+
*/
|
|
71
|
+
if (this.edges.size >= const_1.MAX_EDGES) {
|
|
72
|
+
this.dropped++;
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
this.edges.set(id, observed);
|
|
76
|
+
}
|
|
77
|
+
else if (observed.sample !== undefined)
|
|
78
|
+
edge.sample = observed.sample;
|
|
79
|
+
if (this.edges.size >= this.options.threshold)
|
|
80
|
+
void this.flush();
|
|
81
|
+
}
|
|
82
|
+
async open() {
|
|
83
|
+
// deliberately not awaited: the explorer may not be there yet, or at all
|
|
84
|
+
this.acquire();
|
|
85
|
+
this.timer = setInterval(() => void this.flush(), this.options.interval * 1000);
|
|
86
|
+
this.timer.unref();
|
|
87
|
+
}
|
|
88
|
+
async close() {
|
|
89
|
+
if (this.timer !== null) {
|
|
90
|
+
clearInterval(this.timer);
|
|
91
|
+
this.timer = null;
|
|
92
|
+
}
|
|
93
|
+
await this.flushing;
|
|
94
|
+
if (!this.ready()) {
|
|
95
|
+
this.discard('the explorer was never reached');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// the remotes are still up: dependencies are disconnected after this returns
|
|
99
|
+
await this.dispatch().catch((error) => {
|
|
100
|
+
openspan_1.console.debug('Introspection final flush failed', { message: error.message });
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
ready() {
|
|
104
|
+
return const_1.NODES in this.remotes && const_1.EDGES in this.remotes;
|
|
105
|
+
}
|
|
106
|
+
async flush() {
|
|
107
|
+
// a dispatch in flight keeps observations buffered, they join the next batch
|
|
108
|
+
if (this.flushing !== null)
|
|
109
|
+
return;
|
|
110
|
+
if (this.nodes.size === 0 && this.edges.size === 0)
|
|
111
|
+
return;
|
|
112
|
+
if (!this.ready()) {
|
|
113
|
+
this.acquire();
|
|
114
|
+
if (this.edges.size >= const_1.MAX_EDGES)
|
|
115
|
+
this.discard('the explorer is not reachable');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
this.flushing = this.dispatch()
|
|
119
|
+
.catch((error) => {
|
|
120
|
+
openspan_1.console.debug('Introspection flush failed', { message: error.message });
|
|
121
|
+
})
|
|
122
|
+
.finally(() => {
|
|
123
|
+
this.flushing = null;
|
|
124
|
+
});
|
|
125
|
+
await this.flushing;
|
|
126
|
+
}
|
|
127
|
+
discard(reason) {
|
|
128
|
+
const nodes = this.nodes.size;
|
|
129
|
+
const edges = this.edges.size + this.dropped;
|
|
130
|
+
this.nodes.clear();
|
|
131
|
+
this.edges.clear();
|
|
132
|
+
this.dropped = 0;
|
|
133
|
+
if (nodes === 0 && edges === 0)
|
|
134
|
+
return;
|
|
135
|
+
openspan_1.console.warn(`Introspection data discarded, ${reason}`, { nodes, edges });
|
|
136
|
+
}
|
|
137
|
+
async dispatch() {
|
|
138
|
+
const nodes = [...this.nodes.entries()];
|
|
139
|
+
const edges = [...this.edges.entries()];
|
|
140
|
+
this.nodes.clear();
|
|
141
|
+
this.edges.clear();
|
|
142
|
+
if (this.dropped > 0) {
|
|
143
|
+
openspan_1.console.warn('Introspection edges dropped', { dropped: this.dropped, limit: const_1.MAX_EDGES });
|
|
144
|
+
this.dropped = 0;
|
|
145
|
+
}
|
|
146
|
+
await Promise.all([
|
|
147
|
+
this.merge(const_1.NODES, 'nodes', nodes),
|
|
148
|
+
this.merge(const_1.EDGES, 'edges', edges)
|
|
149
|
+
]);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* A mass transition: every affected object is acquired and committed at once,
|
|
153
|
+
* so a flush is one call per component whatever it carries.
|
|
154
|
+
*/
|
|
155
|
+
async merge(name, property, observed) {
|
|
156
|
+
if (observed.length === 0)
|
|
157
|
+
return;
|
|
158
|
+
const objects = {};
|
|
159
|
+
for (const [id, object] of observed)
|
|
160
|
+
objects[id] = object;
|
|
161
|
+
await this.remotes[name].invoke('merge', {
|
|
162
|
+
query: { ids: observed.map(([id]) => id) },
|
|
163
|
+
input: { [property]: objects },
|
|
164
|
+
task: true
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
/** Runs in the background: discovery waits for the explorer as long as it takes. */
|
|
168
|
+
acquire() {
|
|
169
|
+
if (this.acquiring)
|
|
170
|
+
return;
|
|
171
|
+
this.acquiring = true;
|
|
172
|
+
void this.reach().catch((error) => {
|
|
173
|
+
this.acquiring = false;
|
|
174
|
+
openspan_1.console.error('Introspection cannot reach its explorer', { message: error.message });
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
async reach() {
|
|
178
|
+
await Promise.all([const_1.NODES, const_1.EDGES].map(async (name) => {
|
|
179
|
+
const remote = await this.boot.remote(new core_1.Locator(name, const_1.NAMESPACE));
|
|
180
|
+
this.depends(remote);
|
|
181
|
+
await remote.connect();
|
|
182
|
+
this.remotes[name] = remote;
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
exports.Reporter = Reporter;
|
|
187
|
+
//# sourceMappingURL=Reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Reporter.js","sourceRoot":"","sources":["../source/Reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Connector } from '@toa.io/core';
|
|
2
|
+
import type { Reporter } from './Reporter';
|
|
3
|
+
import type { Node } from './model';
|
|
4
|
+
/**
|
|
5
|
+
* Announces the static description of a component.
|
|
6
|
+
*
|
|
7
|
+
* Delivery is guaranteed, so the repeat is not about reliability — it keeps
|
|
8
|
+
* `_updated` fresh, which is how a removed component fades off the map.
|
|
9
|
+
*/
|
|
10
|
+
export declare class Tenant extends Connector {
|
|
11
|
+
private readonly reporter;
|
|
12
|
+
private readonly node;
|
|
13
|
+
private stopped;
|
|
14
|
+
constructor(reporter: Reporter, node: Node);
|
|
15
|
+
protected open(): Promise<void>;
|
|
16
|
+
protected dispose(): void;
|
|
17
|
+
private announce;
|
|
18
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Tenant = void 0;
|
|
4
|
+
const promises_1 = require("node:timers/promises");
|
|
5
|
+
const core_1 = require("@toa.io/core");
|
|
6
|
+
const const_1 = require("./const");
|
|
7
|
+
/**
|
|
8
|
+
* Announces the static description of a component.
|
|
9
|
+
*
|
|
10
|
+
* Delivery is guaranteed, so the repeat is not about reliability — it keeps
|
|
11
|
+
* `_updated` fresh, which is how a removed component fades off the map.
|
|
12
|
+
*/
|
|
13
|
+
class Tenant extends core_1.Connector {
|
|
14
|
+
reporter;
|
|
15
|
+
node;
|
|
16
|
+
stopped = false;
|
|
17
|
+
constructor(reporter, node) {
|
|
18
|
+
super();
|
|
19
|
+
this.reporter = reporter;
|
|
20
|
+
this.node = node;
|
|
21
|
+
this.depends(reporter);
|
|
22
|
+
}
|
|
23
|
+
async open() {
|
|
24
|
+
this.reporter.expose(this.node);
|
|
25
|
+
void this.announce();
|
|
26
|
+
}
|
|
27
|
+
dispose() {
|
|
28
|
+
this.stopped = true;
|
|
29
|
+
}
|
|
30
|
+
async announce() {
|
|
31
|
+
while (!this.stopped) {
|
|
32
|
+
await (0, promises_1.setTimeout)(const_1.ANNOUNCE_INTERVAL, undefined, { ref: false });
|
|
33
|
+
if (this.stopped)
|
|
34
|
+
break;
|
|
35
|
+
this.reporter.expose(this.node);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.Tenant = Tenant;
|
|
40
|
+
//# sourceMappingURL=Tenant.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Tenant.js","sourceRoot":"","sources":["../source/Tenant.ts"],"names":[],"mappings":";;;AAAA,mDAAiD;AACjD,uCAAwC;AACxC,mCAA2C;AAI3C;;;;;GAKG;AACH,MAAa,MAAO,SAAQ,gBAAS;IAClB,QAAQ,CAAU;IAClB,IAAI,CAAM;IACnB,OAAO,GAAG,KAAK,CAAA;IAEvB,YAAoB,QAAkB,EAAE,IAAU;QAChD,KAAK,EAAE,CAAA;QAEP,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAEhB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IACxB,CAAC;IAEkB,KAAK,CAAC,IAAI;QAC3B,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAE/B,KAAK,IAAI,CAAC,QAAQ,EAAE,CAAA;IACtB,CAAC;IAEkB,OAAO;QACxB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAA;IACrB,CAAC;IAEO,KAAK,CAAC,QAAQ;QACpB,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YACrB,MAAM,IAAA,qBAAU,EAAC,yBAAiB,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,KAAK,EAAE,CAAC,CAAA;YAE9D,IAAI,IAAI,CAAC,OAAO;gBACd,MAAK;YAEP,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACjC,CAAC;IACH,CAAC;CACF;AAlCD,wBAkCC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Connector } from '@toa.io/core';
|
|
2
|
+
/**
|
|
3
|
+
* Serves the introspection UI: the directory `ui` builds, and nothing else.
|
|
4
|
+
*
|
|
5
|
+
* The page is a single-page application whose mount path is baked into the bundle,
|
|
6
|
+
* so this server routes relative to `UI_PATH` — which is also what the ingress
|
|
7
|
+
* forwards. `/introspection/*` is left alone: that is the components' own API.
|
|
8
|
+
*/
|
|
9
|
+
export declare class UI extends Connector {
|
|
10
|
+
private readonly server;
|
|
11
|
+
private readonly port;
|
|
12
|
+
private readonly root;
|
|
13
|
+
constructor(port: number, root?: string);
|
|
14
|
+
protected open(): Promise<void>;
|
|
15
|
+
protected close(): Promise<void>;
|
|
16
|
+
private listen;
|
|
17
|
+
private respond;
|
|
18
|
+
/**
|
|
19
|
+
* The file a request lands on, or `null` when nothing does. A path that exists is
|
|
20
|
+
* served as it is; anything else that could be a route falls back to the page,
|
|
21
|
+
* because the client router — not this server — knows what routes there are.
|
|
22
|
+
*/
|
|
23
|
+
private resolve;
|
|
24
|
+
private send;
|
|
25
|
+
}
|
package/transpiled/UI.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.UI = void 0;
|
|
27
|
+
const fs = __importStar(require("node:fs"));
|
|
28
|
+
const http = __importStar(require("node:http"));
|
|
29
|
+
const path = __importStar(require("node:path"));
|
|
30
|
+
const core_1 = require("@toa.io/core");
|
|
31
|
+
const openspan_1 = require("openspan");
|
|
32
|
+
const const_1 = require("./const");
|
|
33
|
+
/**
|
|
34
|
+
* Serves the introspection UI: the directory `ui` builds, and nothing else.
|
|
35
|
+
*
|
|
36
|
+
* The page is a single-page application whose mount path is baked into the bundle,
|
|
37
|
+
* so this server routes relative to `UI_PATH` — which is also what the ingress
|
|
38
|
+
* forwards. `/introspection/*` is left alone: that is the components' own API.
|
|
39
|
+
*/
|
|
40
|
+
class UI extends core_1.Connector {
|
|
41
|
+
server = http.createServer();
|
|
42
|
+
port;
|
|
43
|
+
root;
|
|
44
|
+
constructor(port, root = SITE) {
|
|
45
|
+
super();
|
|
46
|
+
this.port = port;
|
|
47
|
+
this.root = root;
|
|
48
|
+
this.server.on('request', (request, response) => {
|
|
49
|
+
this.listen(request, response);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async open() {
|
|
53
|
+
/*
|
|
54
|
+
* A taken port is a real error: uniqueness across services is settled at export
|
|
55
|
+
* time, so nothing here has to negotiate for one.
|
|
56
|
+
*/
|
|
57
|
+
await new Promise((resolve, reject) => {
|
|
58
|
+
const failed = (error) => {
|
|
59
|
+
this.server.off('listening', listening);
|
|
60
|
+
reject(error);
|
|
61
|
+
};
|
|
62
|
+
const listening = () => {
|
|
63
|
+
this.server.off('error', failed);
|
|
64
|
+
resolve();
|
|
65
|
+
};
|
|
66
|
+
this.server.once('error', failed);
|
|
67
|
+
this.server.once('listening', listening);
|
|
68
|
+
this.server.listen(this.port);
|
|
69
|
+
});
|
|
70
|
+
if (!isFile(path.join(this.root, 'index.html')))
|
|
71
|
+
openspan_1.console.warn('Introspection UI is not built', { root: this.root });
|
|
72
|
+
openspan_1.console.info('Introspection UI started', { port: this.port, path: const_1.UI_PATH + '/' });
|
|
73
|
+
}
|
|
74
|
+
async close() {
|
|
75
|
+
const closed = new Promise((resolve) => this.server.once('close', () => {
|
|
76
|
+
resolve();
|
|
77
|
+
}));
|
|
78
|
+
this.server.close();
|
|
79
|
+
this.server.closeAllConnections();
|
|
80
|
+
await closed;
|
|
81
|
+
}
|
|
82
|
+
listen(request, response) {
|
|
83
|
+
void this.respond(request, response).catch((error) => {
|
|
84
|
+
openspan_1.console.error('Introspection UI failure', { message: error.message });
|
|
85
|
+
if (!response.writableEnded)
|
|
86
|
+
response.writeHead(500).end();
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async respond(request, response) {
|
|
90
|
+
if (request.method !== 'GET' && request.method !== 'HEAD') {
|
|
91
|
+
response.writeHead(405, { allow: 'GET, HEAD' }).end();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const pathname = decode(request.url ?? '/');
|
|
95
|
+
if (pathname === null) {
|
|
96
|
+
response.writeHead(400).end();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
// the port is the explorer's alone, so the root is the way in
|
|
100
|
+
if (pathname === '/') {
|
|
101
|
+
response.writeHead(302, { location: const_1.UI_PATH + '/' }).end();
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const file = this.resolve(pathname);
|
|
105
|
+
if (file === null)
|
|
106
|
+
response.writeHead(404).end();
|
|
107
|
+
else
|
|
108
|
+
await this.send(file, request, response);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* The file a request lands on, or `null` when nothing does. A path that exists is
|
|
112
|
+
* served as it is; anything else that could be a route falls back to the page,
|
|
113
|
+
* because the client router — not this server — knows what routes there are.
|
|
114
|
+
*/
|
|
115
|
+
resolve(pathname) {
|
|
116
|
+
if (!pathname.startsWith(const_1.UI_PATH))
|
|
117
|
+
return null;
|
|
118
|
+
const relative = pathname.slice(const_1.UI_PATH.length);
|
|
119
|
+
if (relative !== '' && !relative.startsWith('/'))
|
|
120
|
+
return null;
|
|
121
|
+
const file = path.join(this.root, relative);
|
|
122
|
+
if (file !== this.root && !file.startsWith(this.root + path.sep))
|
|
123
|
+
return null;
|
|
124
|
+
if (isFile(file))
|
|
125
|
+
return file;
|
|
126
|
+
/*
|
|
127
|
+
* A missing asset is missing, but a route can look like one: `identity.passkeys` is a
|
|
128
|
+
* component, not a file with an extension. What this server would have served is what
|
|
129
|
+
* it knows how to serve, so anything else is a route and falls back to the page — as
|
|
130
|
+
* does anything ending in a slash, which is no name for a file at all.
|
|
131
|
+
*/
|
|
132
|
+
const asset = !relative.endsWith('/') && path.extname(relative) in TYPES;
|
|
133
|
+
return asset ? null : path.join(this.root, 'index.html');
|
|
134
|
+
}
|
|
135
|
+
async send(file, request, response) {
|
|
136
|
+
const stats = await fs.promises.stat(file).catch(() => null);
|
|
137
|
+
if (stats === null) {
|
|
138
|
+
response.writeHead(503, { 'content-type': 'text/plain' })
|
|
139
|
+
.end('The introspection UI is not built. Run `npm run build:ui`.\n');
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
response.writeHead(200, {
|
|
143
|
+
'content-type': TYPES[path.extname(file)] ?? 'application/octet-stream',
|
|
144
|
+
'content-length': stats.size,
|
|
145
|
+
'cache-control': file.startsWith(path.join(this.root, IMMUTABLE)) ? FOREVER : 'no-cache'
|
|
146
|
+
});
|
|
147
|
+
if (request.method === 'HEAD')
|
|
148
|
+
response.end();
|
|
149
|
+
else
|
|
150
|
+
fs.createReadStream(file).pipe(response);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
exports.UI = UI;
|
|
154
|
+
function decode(url) {
|
|
155
|
+
try {
|
|
156
|
+
return decodeURIComponent(url.split('?')[0]);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function isFile(file) {
|
|
163
|
+
return fs.existsSync(file) && fs.statSync(file).isFile();
|
|
164
|
+
}
|
|
165
|
+
/** Where `npm run build:ui` puts the page, from both `source` and `transpiled`. */
|
|
166
|
+
const SITE = path.resolve(__dirname, '..', 'ui', 'dist');
|
|
167
|
+
/** Assets under this prefix carry their build hash in the name. */
|
|
168
|
+
const IMMUTABLE = path.join('_app', 'immutable');
|
|
169
|
+
const FOREVER = 'public, max-age=31536000, immutable';
|
|
170
|
+
const TYPES = {
|
|
171
|
+
'.css': 'text/css; charset=utf-8',
|
|
172
|
+
'.html': 'text/html; charset=utf-8',
|
|
173
|
+
'.ico': 'image/x-icon',
|
|
174
|
+
'.jpg': 'image/jpeg',
|
|
175
|
+
'.js': 'text/javascript; charset=utf-8',
|
|
176
|
+
'.json': 'application/json; charset=utf-8',
|
|
177
|
+
'.map': 'application/json; charset=utf-8',
|
|
178
|
+
'.png': 'image/png',
|
|
179
|
+
'.svg': 'image/svg+xml',
|
|
180
|
+
'.txt': 'text/plain; charset=utf-8',
|
|
181
|
+
'.webmanifest': 'application/manifest+json',
|
|
182
|
+
'.webp': 'image/webp',
|
|
183
|
+
'.woff': 'font/woff',
|
|
184
|
+
'.woff2': 'font/woff2'
|
|
185
|
+
};
|
|
186
|
+
//# sourceMappingURL=UI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UI.js","sourceRoot":"","sources":["../source/UI.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Resources } from '@toa.io/operations';
|
|
2
|
+
/** `context.toa.yaml` */
|
|
3
|
+
export type Annotation = false | {
|
|
4
|
+
/** Capture real payloads. Off by default: this is production data. */
|
|
5
|
+
samples?: boolean;
|
|
6
|
+
/** Flush period, seconds. */
|
|
7
|
+
interval?: number;
|
|
8
|
+
/** Flush as soon as this many distinct edges are buffered. */
|
|
9
|
+
threshold?: number;
|
|
10
|
+
/** Publish the UI. On by default. */
|
|
11
|
+
ui?: boolean;
|
|
12
|
+
resources?: Resources;
|
|
13
|
+
};
|
|
14
|
+
/** `manifest.toa.yaml` */
|
|
15
|
+
export type Declaration = false | {
|
|
16
|
+
samples?: boolean;
|
|
17
|
+
};
|
|
18
|
+
/** What `deployment()` encodes into the environment. */
|
|
19
|
+
export interface Options {
|
|
20
|
+
samples: boolean;
|
|
21
|
+
interval: number;
|
|
22
|
+
threshold: number;
|
|
23
|
+
ui: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** The effective per-component decision. */
|
|
26
|
+
export interface Settings {
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
samples: boolean;
|
|
29
|
+
}
|
|
30
|
+
export declare const DISABLED: Settings;
|
|
31
|
+
export declare function options(annotation?: Annotation): Options;
|
|
32
|
+
/** Reads what `deployment()` has put into the environment. */
|
|
33
|
+
export declare function environment(): Options | null;
|
|
34
|
+
export declare function component(declaration: Declaration | null | undefined): Declaration;
|
|
35
|
+
/**
|
|
36
|
+
* Both levels must agree, and either can veto: the context is the environment
|
|
37
|
+
* ceiling, the manifest is the component's own call. A component handling
|
|
38
|
+
* personal data opts out for good, and no context flag overrides that.
|
|
39
|
+
*/
|
|
40
|
+
export declare function settings(namespace: string, declaration: Declaration, opts: Options | null): Settings;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.settings = exports.component = exports.environment = exports.options = exports.DISABLED = void 0;
|
|
4
|
+
const generic_1 = require("@toa.io/generic");
|
|
5
|
+
const const_1 = require("./const");
|
|
6
|
+
exports.DISABLED = { enabled: false, samples: false };
|
|
7
|
+
function options(annotation) {
|
|
8
|
+
const declaration = annotation === undefined || annotation === false ? {} : annotation;
|
|
9
|
+
return {
|
|
10
|
+
samples: declaration.samples === true,
|
|
11
|
+
interval: declaration.interval ?? const_1.DEFAULT_INTERVAL,
|
|
12
|
+
threshold: declaration.threshold ?? const_1.DEFAULT_THRESHOLD,
|
|
13
|
+
ui: declaration.ui !== false
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
exports.options = options;
|
|
17
|
+
/** Reads what `deployment()` has put into the environment. */
|
|
18
|
+
function environment() {
|
|
19
|
+
const value = process.env[const_1.ENV];
|
|
20
|
+
if (value === undefined)
|
|
21
|
+
return null;
|
|
22
|
+
return (0, generic_1.decode)(value);
|
|
23
|
+
}
|
|
24
|
+
exports.environment = environment;
|
|
25
|
+
function component(declaration) {
|
|
26
|
+
if (declaration === false)
|
|
27
|
+
return false;
|
|
28
|
+
// predefined extensions arrive as null for components that say nothing
|
|
29
|
+
if (declaration === null || declaration === undefined)
|
|
30
|
+
return {};
|
|
31
|
+
return declaration.samples === undefined ? {} : { samples: declaration.samples };
|
|
32
|
+
}
|
|
33
|
+
exports.component = component;
|
|
34
|
+
/**
|
|
35
|
+
* Both levels must agree, and either can veto: the context is the environment
|
|
36
|
+
* ceiling, the manifest is the component's own call. A component handling
|
|
37
|
+
* personal data opts out for good, and no context flag overrides that.
|
|
38
|
+
*/
|
|
39
|
+
function settings(namespace, declaration, opts) {
|
|
40
|
+
if (opts === null || declaration === false)
|
|
41
|
+
return exports.DISABLED;
|
|
42
|
+
const samples = opts.samples && declaration.samples !== false && !const_1.DENIED.has(namespace);
|
|
43
|
+
return { enabled: true, samples };
|
|
44
|
+
}
|
|
45
|
+
exports.settings = settings;
|
|
46
|
+
//# sourceMappingURL=annotation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"annotation.js","sourceRoot":"","sources":["../source/annotation.ts"],"names":[],"mappings":";;;AAAA,6CAAwC;AACxC,mCAA0E;AAmC7D,QAAA,QAAQ,GAAa,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAA;AAEpE,SAAgB,OAAO,CAAE,UAAuB;IAC9C,MAAM,WAAW,GAAG,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,CAAA;IAEtF,OAAO;QACL,OAAO,EAAE,WAAW,CAAC,OAAO,KAAK,IAAI;QACrC,QAAQ,EAAE,WAAW,CAAC,QAAQ,IAAI,wBAAgB;QAClD,SAAS,EAAE,WAAW,CAAC,SAAS,IAAI,yBAAiB;QACrD,EAAE,EAAE,WAAW,CAAC,EAAE,KAAK,KAAK;KAC7B,CAAA;AACH,CAAC;AATD,0BASC;AAED,8DAA8D;AAC9D,SAAgB,WAAW;IACzB,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,WAAG,CAAC,CAAA;IAE9B,IAAI,KAAK,KAAK,SAAS;QACrB,OAAO,IAAI,CAAA;IAEb,OAAO,IAAA,gBAAM,EAAU,KAAK,CAAC,CAAA;AAC/B,CAAC;AAPD,kCAOC;AAED,SAAgB,SAAS,CAAE,WAA2C;IACpE,IAAI,WAAW,KAAK,KAAK;QACvB,OAAO,KAAK,CAAA;IAEd,uEAAuE;IACvE,IAAI,WAAW,KAAK,IAAI,IAAI,WAAW,KAAK,SAAS;QACnD,OAAO,EAAE,CAAA;IAEX,OAAO,WAAW,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,OAAO,EAAE,CAAA;AAClF,CAAC;AATD,8BASC;AAED;;;;GAIG;AACH,SAAgB,QAAQ,CAAE,SAAiB,EAAE,WAAwB,EAAE,IAAoB;IACzF,IAAI,IAAI,KAAK,IAAI,IAAI,WAAW,KAAK,KAAK;QACxC,OAAO,gBAAQ,CAAA;IAEjB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,WAAW,CAAC,OAAO,KAAK,KAAK,IAAI,CAAC,cAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;IAEvF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;AACnC,CAAC;AAPD,4BAOC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const ID = "introspection";
|
|
2
|
+
export declare const NAMESPACE = "introspection";
|
|
3
|
+
export declare const ENV = "TOA_INTROSPECTION";
|
|
4
|
+
export declare const NODES = "nodes";
|
|
5
|
+
export declare const EDGES = "edges";
|
|
6
|
+
/** Where the UI is mounted; `/introspection/*` belongs to the components' own API. */
|
|
7
|
+
export declare const UI_PATH = "/.introspection";
|
|
8
|
+
export declare const UI_PORT = 8002;
|
|
9
|
+
export declare const DEFAULT_INTERVAL = 300;
|
|
10
|
+
export declare const DEFAULT_THRESHOLD = 1024;
|
|
11
|
+
/** How often a component re-announces its description, so that removed components fade out. */
|
|
12
|
+
export declare const ANNOUNCE_INTERVAL = 1800000;
|
|
13
|
+
/**
|
|
14
|
+
* `source` arrives over the wire, so the number of distinct edges a process
|
|
15
|
+
* can hold must be bounded regardless of what peers send.
|
|
16
|
+
*/
|
|
17
|
+
export declare const MAX_EDGES = 4096;
|
|
18
|
+
/** Never capture samples for these namespaces, whatever the annotation says. */
|
|
19
|
+
export declare const DENIED: Set<string>;
|
|
20
|
+
/** Keys never stored in a sample, even when sampling is on. */
|
|
21
|
+
export declare const REDACTED: RegExp;
|
|
22
|
+
/** Serialized size cap of a single sample. */
|
|
23
|
+
export declare const SAMPLE_LIMIT = 4096;
|