@toa.io/extensions.introspection 1.0.0-alpha.262
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/CHANGELOG.md +61 -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/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/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 +31 -0
- package/readme.md +141 -0
- package/schemas/annotation.cos.yaml +16 -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/contracts.d.ts +51 -0
- package/transpiled/contracts.js +53 -0
- package/transpiled/contracts.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/tsconfig.json +12 -0
- package/ui/dist/_app/env.js +1 -0
- package/ui/dist/_app/immutable/assets/0.fg2cP6bO.css +2 -0
- package/ui/dist/_app/immutable/assets/2.BghF56l_.css +1 -0
- package/ui/dist/_app/immutable/assets/inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-cyrillic-wght-normal.DqGufNeO.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-greek-ext-wght-normal.DlzME5K_.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-greek-wght-normal.CkhJZR-_.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-latin-ext-wght-normal.DO1Apj_S.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-latin-wght-normal.Dx4kXJAl.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-vietnamese-wght-normal.CBcvBZtf.woff2 +0 -0
- package/ui/dist/_app/immutable/chunks/Bjy-W4x2.js +81 -0
- package/ui/dist/_app/immutable/chunks/CIvRtvVA.js +1 -0
- package/ui/dist/_app/immutable/chunks/D1jNriZ8.js +1 -0
- package/ui/dist/_app/immutable/chunks/DINmginf.js +1 -0
- package/ui/dist/_app/immutable/chunks/DKZCAkrF.js +1 -0
- package/ui/dist/_app/immutable/chunks/DLG2JvyF.js +6 -0
- package/ui/dist/_app/immutable/chunks/DSi-FjwI.js +2 -0
- package/ui/dist/_app/immutable/chunks/DdoZuyU1.js +1 -0
- package/ui/dist/_app/immutable/chunks/O9IjE_TH.js +1 -0
- package/ui/dist/_app/immutable/chunks/PUjOYRcs.js +3 -0
- package/ui/dist/_app/immutable/chunks/QKD4Mgck.js +5 -0
- package/ui/dist/_app/immutable/chunks/Ux-r8C9U.js +1 -0
- package/ui/dist/_app/immutable/chunks/xihTtKlq.js +1 -0
- package/ui/dist/_app/immutable/entry/app.CTY34L35.js +2 -0
- package/ui/dist/_app/immutable/entry/start.Bq2Gn1Ax.js +1 -0
- package/ui/dist/_app/immutable/nodes/0.lOXtBRjQ.js +1 -0
- package/ui/dist/_app/immutable/nodes/1.r8ESZdgM.js +1 -0
- package/ui/dist/_app/immutable/nodes/2.DuVj0loz.js +8 -0
- package/ui/dist/_app/immutable/nodes/3.DhdkQ2Q1.js +1 -0
- package/ui/dist/_app/immutable/nodes/4.DUAEZdRr.js +1 -0
- package/ui/dist/_app/immutable/nodes/5.CIVn_xgq.js +1 -0
- package/ui/dist/_app/version.json +1 -0
- package/ui/dist/index.html +48 -0
- package/ui/dist/robots.txt +3 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Change Log
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
+
|
|
6
|
+
# [1.0.0-alpha.262](https://github.com/toa-io/toa/compare/v1.0.0-alpha.261...v1.0.0-alpha.262) (2026-08-28)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **introspection:** keep the collector off the critical path ([965ec77](https://github.com/toa-io/toa/commit/965ec77482d361a5a9a88579152158c7e1c79e66))
|
|
12
|
+
* **introspection:** meet a card across the edge the line touches ([2002de9](https://github.com/toa-io/toa/commit/2002de9bdc12400abc64d5234fa319339db4588f))
|
|
13
|
+
* **introspection:** return the whole map over HTTP ([053c0cd](https://github.com/toa-io/toa/commit/053c0cd5c22b1a80bceb23d2cda68ede33a0d1c0))
|
|
14
|
+
* **introspection:** serve a route that looks like a file ([b43bbb4](https://github.com/toa-io/toa/commit/b43bbb4a290819463c61bcba64357385c2423d25))
|
|
15
|
+
* **introspection:** show the lines an opened neighbour sends ([5fe8aed](https://github.com/toa-io/toa/commit/5fe8aed64c1f1e54f1c10e29b91b50c5cdc296ab))
|
|
16
|
+
* **introspection:** wait for the page before capturing it ([9e63af3](https://github.com/toa-io/toa/commit/9e63af34ee090a4f90dce9658b0d022f4acae50f))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
* feat(introspection)!: publish the UI ([81fe2cb](https://github.com/toa-io/toa/commit/81fe2cb4bafe91e74d3af8359327e3102e5b0e8a))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
|
|
24
|
+
* **introspection:** act on the only thing the filter left ([1a83709](https://github.com/toa-io/toa/commit/1a8370987b7d60c009f4d58f07005618899cdfd6))
|
|
25
|
+
* **introspection:** add the introspection extension ([a07ed86](https://github.com/toa-io/toa/commit/a07ed8651404258d544f3b6d8236e58cf4da09ac))
|
|
26
|
+
* **introspection:** animate a card opening on the map ([1d2180f](https://github.com/toa-io/toa/commit/1d2180f48fb9dc18d9fa0c2cc4eefa6f46626898))
|
|
27
|
+
* **introspection:** animate every collapsible from one place ([0b6a32b](https://github.com/toa-io/toa/commit/0b6a32b69128f2db2d3e449896614ff478f2775b))
|
|
28
|
+
* **introspection:** ask for the role rather than be refused it ([1ad01a7](https://github.com/toa-io/toa/commit/1ad01a7dd817ae37ed84e3a3b6b20b72049a4216))
|
|
29
|
+
* **introspection:** cache the map API ([f550c27](https://github.com/toa-io/toa/commit/f550c2780a80bbe2fdd131984c33d9848daa8c82))
|
|
30
|
+
* **introspection:** fold away a component's extensions ([de1bea6](https://github.com/toa-io/toa/commit/de1bea6329a1fd920cff6b5ad9a13eedfc527884))
|
|
31
|
+
* **introspection:** give a card's rows their own space ([3253639](https://github.com/toa-io/toa/commit/32536395b9d25e743c25b61deb9dbd5b79d966b6))
|
|
32
|
+
* **introspection:** give the map's two screens their own addresses ([90fd5af](https://github.com/toa-io/toa/commit/90fd5af39b8b6c38e22e05df46c70de533eb15c6))
|
|
33
|
+
* **introspection:** open a component on the map ([6382740](https://github.com/toa-io/toa/commit/63827403407fc5a305d95e9fde72ed5a99a1499a))
|
|
34
|
+
* **introspection:** open an operation onto what it takes ([f16a683](https://github.com/toa-io/toa/commit/f16a683175a89d3660c538768b24cd872b6d0b3d))
|
|
35
|
+
* **introspection:** publish the map as a page ([21b16b2](https://github.com/toa-io/toa/commit/21b16b2220a1b26796bdbedc85a0df2a88df31dd))
|
|
36
|
+
* **introspection:** say a component's shape in counts ([54aa771](https://github.com/toa-io/toa/commit/54aa771a51c257db759107d584d063916f0cd351))
|
|
37
|
+
* **introspection:** say how a silenced card comes back ([6e7674f](https://github.com/toa-io/toa/commit/6e7674f88137cfb780f3a7cac1fc67f191f96984))
|
|
38
|
+
* **introspection:** separate what the runtime provides ([289c7a3](https://github.com/toa-io/toa/commit/289c7a301dd0ab9c063a5b77b24745983ec79e29))
|
|
39
|
+
* **introspection:** show a schema as a shape ([7d9a595](https://github.com/toa-io/toa/commit/7d9a595d86f88846b21bb79aba82bb332b77fbf6))
|
|
40
|
+
* **introspection:** the map is the front page ([dbf1b5f](https://github.com/toa-io/toa/commit/dbf1b5fd6d262aa2aa952bbfe2a6be1e44af6302))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Performance Improvements
|
|
44
|
+
|
|
45
|
+
* **introspection:** build a card's details when it shows them ([c10f7c4](https://github.com/toa-io/toa/commit/c10f7c42e51cbef2d3767c36559d846f0e34653a))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
### BREAKING CHANGES
|
|
49
|
+
|
|
50
|
+
* the extension is predefined, so every Context now declares an
|
|
51
|
+
ingress and must say where it lands:
|
|
52
|
+
|
|
53
|
+
ingress:
|
|
54
|
+
hosts: [api.example.com]
|
|
55
|
+
|
|
56
|
+
An application that does not want the page says `introspection: { ui: false }`,
|
|
57
|
+
and one that wants no map at all says `introspection: false`. Failing the export
|
|
58
|
+
is deliberate: the alternative is quietly not publishing a page that was asked
|
|
59
|
+
for.
|
|
60
|
+
|
|
61
|
+
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
namespace: introspection
|
|
2
|
+
name: edges
|
|
3
|
+
|
|
4
|
+
# One document per edge of the map: a call one component made to another, with `src`
|
|
5
|
+
# saying what caused it — another operation, an event, or a service. What the
|
|
6
|
+
# application declares is described by `introspection.nodes`; an edge is what
|
|
7
|
+
# actually happened.
|
|
8
|
+
#
|
|
9
|
+
# Every replica of a component derives the same id for the same edge, so `merge` is
|
|
10
|
+
# an upsert and `_updated` shows when the edge was last observed.
|
|
11
|
+
|
|
12
|
+
entity:
|
|
13
|
+
associated: true
|
|
14
|
+
schema:
|
|
15
|
+
src: ~
|
|
16
|
+
dst: ~
|
|
17
|
+
sample: ~
|
|
18
|
+
|
|
19
|
+
operations:
|
|
20
|
+
# a mass transition: the scope is inferred from the algorithm's `objects` parameter
|
|
21
|
+
merge:
|
|
22
|
+
concurrency: retry
|
|
23
|
+
input:
|
|
24
|
+
type: object
|
|
25
|
+
properties:
|
|
26
|
+
edges:
|
|
27
|
+
type: object
|
|
28
|
+
required: [edges]
|
|
29
|
+
|
|
30
|
+
# the map must not describe itself
|
|
31
|
+
introspection: false
|
|
32
|
+
|
|
33
|
+
exposition:
|
|
34
|
+
/:
|
|
35
|
+
auth:role: system:introspection
|
|
36
|
+
|
|
37
|
+
# the map is the whole document, and its shape is what the application happens to declare
|
|
38
|
+
io:output: true
|
|
39
|
+
cache:control: max-age=60, public
|
|
40
|
+
# the map is read whole: a page of ten is of no use to anything that draws it
|
|
41
|
+
GET:
|
|
42
|
+
endpoint: enumerate
|
|
43
|
+
query:
|
|
44
|
+
limit:
|
|
45
|
+
value: 10000
|
|
46
|
+
range: [1, 10000]
|
|
47
|
+
/:id:
|
|
48
|
+
GET: observe
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface Source {
|
|
2
|
+
namespace?: string;
|
|
3
|
+
component?: string;
|
|
4
|
+
operation?: string;
|
|
5
|
+
event?: string;
|
|
6
|
+
service?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface Target {
|
|
9
|
+
namespace: string;
|
|
10
|
+
component: string;
|
|
11
|
+
operation: string;
|
|
12
|
+
}
|
|
13
|
+
export interface Sample {
|
|
14
|
+
at: number;
|
|
15
|
+
input?: unknown;
|
|
16
|
+
outcome: string;
|
|
17
|
+
}
|
|
18
|
+
export interface Observed {
|
|
19
|
+
src: Source;
|
|
20
|
+
dst: Target;
|
|
21
|
+
sample?: Sample;
|
|
22
|
+
}
|
|
23
|
+
export interface Edge extends Observed {
|
|
24
|
+
id: string;
|
|
25
|
+
}
|
|
26
|
+
export interface Input {
|
|
27
|
+
edges: Record<string, Observed>;
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../source/lib/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Edge, Input } from './lib/types';
|
|
2
|
+
/**
|
|
3
|
+
* Records the calls observed by a collector since its last flush.
|
|
4
|
+
*
|
|
5
|
+
* The scope is `objects`, so the caller passes every affected id in `query.ids`
|
|
6
|
+
* and the matching edge under `edges[id]`. Unknown edges are initialized by the
|
|
7
|
+
* runtime, since the entity is `associated`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function transition(input: Input, objects: Edge[]): Edge[];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.transition = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Records the calls observed by a collector since its last flush.
|
|
6
|
+
*
|
|
7
|
+
* The scope is `objects`, so the caller passes every affected id in `query.ids`
|
|
8
|
+
* and the matching edge under `edges[id]`. Unknown edges are initialized by the
|
|
9
|
+
* runtime, since the entity is `associated`.
|
|
10
|
+
*/
|
|
11
|
+
function transition(input, objects) {
|
|
12
|
+
for (const edge of objects) {
|
|
13
|
+
const observed = input.edges[edge.id];
|
|
14
|
+
if (observed === undefined)
|
|
15
|
+
continue;
|
|
16
|
+
edge.src = observed.src;
|
|
17
|
+
edge.dst = observed.dst;
|
|
18
|
+
// absent when sampling is off, and the last one wins otherwise
|
|
19
|
+
if (observed.sample !== undefined)
|
|
20
|
+
edge.sample = observed.sample;
|
|
21
|
+
}
|
|
22
|
+
return objects;
|
|
23
|
+
}
|
|
24
|
+
exports.transition = transition;
|
|
25
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../source/merge.ts"],"names":[],"mappings":";;;AAEA;;;;;;GAMG;AACH,SAAgB,UAAU,CAAE,KAAY,EAAE,OAAe;IACvD,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAErC,IAAI,QAAQ,KAAK,SAAS;YACxB,SAAQ;QAEV,IAAI,CAAC,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAA;QACvB,IAAI,CAAC,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAA;QAEvB,+DAA+D;QAC/D,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS;YAC/B,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAA;IACjC,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC;AAhBD,gCAgBC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface Source {
|
|
2
|
+
namespace?: string
|
|
3
|
+
component?: string
|
|
4
|
+
operation?: string
|
|
5
|
+
event?: string
|
|
6
|
+
service?: string
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface Target {
|
|
10
|
+
namespace: string
|
|
11
|
+
component: string
|
|
12
|
+
operation: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Sample {
|
|
16
|
+
at: number
|
|
17
|
+
input?: unknown
|
|
18
|
+
outcome: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface Observed {
|
|
22
|
+
src: Source
|
|
23
|
+
dst: Target
|
|
24
|
+
sample?: Sample
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface Edge extends Observed {
|
|
28
|
+
id: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface Input {
|
|
32
|
+
edges: Record<string, Observed>
|
|
33
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Edge, Input } from './lib/types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Records the calls observed by a collector since its last flush.
|
|
5
|
+
*
|
|
6
|
+
* The scope is `objects`, so the caller passes every affected id in `query.ids`
|
|
7
|
+
* and the matching edge under `edges[id]`. Unknown edges are initialized by the
|
|
8
|
+
* runtime, since the entity is `associated`.
|
|
9
|
+
*/
|
|
10
|
+
export function transition (input: Input, objects: Edge[]): Edge[] {
|
|
11
|
+
for (const edge of objects) {
|
|
12
|
+
const observed = input.edges[edge.id]
|
|
13
|
+
|
|
14
|
+
if (observed === undefined)
|
|
15
|
+
continue
|
|
16
|
+
|
|
17
|
+
edge.src = observed.src
|
|
18
|
+
edge.dst = observed.dst
|
|
19
|
+
|
|
20
|
+
// absent when sampling is off, and the last one wins otherwise
|
|
21
|
+
if (observed.sample !== undefined)
|
|
22
|
+
edge.sample = observed.sample
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return objects
|
|
26
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
namespace: introspection
|
|
2
|
+
name: nodes
|
|
3
|
+
|
|
4
|
+
# The static description of an application component: entity, operations, events, receivers.
|
|
5
|
+
# Every replica of a component announces the same id, thus `merge` is an upsert
|
|
6
|
+
# (`associated` initializes a missing object) and re-announcing refreshes `_updated`.
|
|
7
|
+
|
|
8
|
+
entity:
|
|
9
|
+
associated: true
|
|
10
|
+
schema:
|
|
11
|
+
namespace*: string
|
|
12
|
+
component*: string
|
|
13
|
+
version: string
|
|
14
|
+
entity: ~
|
|
15
|
+
operations: ~
|
|
16
|
+
events: ~
|
|
17
|
+
receivers: ~
|
|
18
|
+
extensions: ~
|
|
19
|
+
|
|
20
|
+
operations:
|
|
21
|
+
# a mass transition: the scope is inferred from the algorithm's `objects` parameter
|
|
22
|
+
merge:
|
|
23
|
+
concurrency: retry
|
|
24
|
+
input:
|
|
25
|
+
type: object
|
|
26
|
+
properties:
|
|
27
|
+
nodes:
|
|
28
|
+
type: object
|
|
29
|
+
required: [nodes]
|
|
30
|
+
|
|
31
|
+
# the map must not describe itself
|
|
32
|
+
introspection: false
|
|
33
|
+
|
|
34
|
+
exposition:
|
|
35
|
+
/:
|
|
36
|
+
auth:role: system:introspection
|
|
37
|
+
|
|
38
|
+
# the map is the whole document, and its shape is what the application happens to declare
|
|
39
|
+
io:output: true
|
|
40
|
+
cache:control: max-age=60, public
|
|
41
|
+
# the map is read whole: a page of ten is of no use to anything that draws it
|
|
42
|
+
GET:
|
|
43
|
+
endpoint: enumerate
|
|
44
|
+
query:
|
|
45
|
+
limit:
|
|
46
|
+
value: 1000
|
|
47
|
+
range: [1, 1000]
|
|
48
|
+
/:id:
|
|
49
|
+
GET: observe
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface Described {
|
|
2
|
+
namespace: string;
|
|
3
|
+
component: string;
|
|
4
|
+
version?: string;
|
|
5
|
+
entity?: unknown;
|
|
6
|
+
operations?: unknown;
|
|
7
|
+
events?: unknown;
|
|
8
|
+
receivers?: unknown;
|
|
9
|
+
extensions?: unknown;
|
|
10
|
+
}
|
|
11
|
+
export interface Node extends Described {
|
|
12
|
+
id: string;
|
|
13
|
+
}
|
|
14
|
+
export interface Input {
|
|
15
|
+
nodes: Record<string, Described>;
|
|
16
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../source/lib/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Input, Node } from './lib/types';
|
|
2
|
+
/**
|
|
3
|
+
* Records the components described by a collector since its last flush.
|
|
4
|
+
*
|
|
5
|
+
* The scope is `objects`, so the caller passes every affected id in `query.ids`
|
|
6
|
+
* and the matching description under `nodes[id]`. Unknown nodes are initialized
|
|
7
|
+
* by the runtime, since the entity is `associated`.
|
|
8
|
+
*/
|
|
9
|
+
export declare function transition(input: Input, objects: Node[]): Node[];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.transition = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Records the components described by a collector since its last flush.
|
|
6
|
+
*
|
|
7
|
+
* The scope is `objects`, so the caller passes every affected id in `query.ids`
|
|
8
|
+
* and the matching description under `nodes[id]`. Unknown nodes are initialized
|
|
9
|
+
* by the runtime, since the entity is `associated`.
|
|
10
|
+
*/
|
|
11
|
+
function transition(input, objects) {
|
|
12
|
+
for (const node of objects) {
|
|
13
|
+
const described = input.nodes[node.id];
|
|
14
|
+
if (described === undefined)
|
|
15
|
+
continue;
|
|
16
|
+
Object.assign(node, described);
|
|
17
|
+
}
|
|
18
|
+
return objects;
|
|
19
|
+
}
|
|
20
|
+
exports.transition = transition;
|
|
21
|
+
//# sourceMappingURL=merge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge.js","sourceRoot":"","sources":["../source/merge.ts"],"names":[],"mappings":";;;AAEA;;;;;;GAMG;AACH,SAAgB,UAAU,CAAE,KAAY,EAAE,OAAe;IACvD,KAAK,MAAM,IAAI,IAAI,OAAO,EAAE,CAAC;QAC3B,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAEtC,IAAI,SAAS,KAAK,SAAS;YACzB,SAAQ;QAEV,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;IAChC,CAAC;IAED,OAAO,OAAO,CAAA;AAChB,CAAC;AAXD,gCAWC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface Described {
|
|
2
|
+
namespace: string
|
|
3
|
+
component: string
|
|
4
|
+
version?: string
|
|
5
|
+
entity?: unknown
|
|
6
|
+
operations?: unknown
|
|
7
|
+
events?: unknown
|
|
8
|
+
receivers?: unknown
|
|
9
|
+
extensions?: unknown
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface Node extends Described {
|
|
13
|
+
id: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface Input {
|
|
17
|
+
nodes: Record<string, Described>
|
|
18
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Input, Node } from './lib/types'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Records the components described by a collector since its last flush.
|
|
5
|
+
*
|
|
6
|
+
* The scope is `objects`, so the caller passes every affected id in `query.ids`
|
|
7
|
+
* and the matching description under `nodes[id]`. Unknown nodes are initialized
|
|
8
|
+
* by the runtime, since the entity is `associated`.
|
|
9
|
+
*/
|
|
10
|
+
export function transition (input: Input, objects: Node[]): Node[] {
|
|
11
|
+
for (const node of objects) {
|
|
12
|
+
const described = input.nodes[node.id]
|
|
13
|
+
|
|
14
|
+
if (described === undefined)
|
|
15
|
+
continue
|
|
16
|
+
|
|
17
|
+
Object.assign(node, described)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return objects
|
|
21
|
+
}
|
package/cucumber.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
Feature: Call edges
|
|
2
|
+
|
|
3
|
+
An edge is recorded by the component being called. The caller only declares who
|
|
4
|
+
it is, on the request, so a caller that runs no collector of its own — a service,
|
|
5
|
+
the CLI — still appears on the map. A local call and a remote one are the same.
|
|
6
|
+
|
|
7
|
+
The origin is what tells a direct call apart from the delivery of an event. Which
|
|
8
|
+
events exist and who receives them is declared, so it is a node's business; an
|
|
9
|
+
edge is what actually happened.
|
|
10
|
+
|
|
11
|
+
Scenario: A remote call
|
|
12
|
+
When the `probe.source.relay` is called with:
|
|
13
|
+
"""yaml
|
|
14
|
+
input:
|
|
15
|
+
a: 2
|
|
16
|
+
b: 3
|
|
17
|
+
"""
|
|
18
|
+
Then the map contains an edge:
|
|
19
|
+
"""yaml
|
|
20
|
+
src:
|
|
21
|
+
namespace: probe
|
|
22
|
+
component: source
|
|
23
|
+
operation: relay
|
|
24
|
+
dst:
|
|
25
|
+
namespace: probe
|
|
26
|
+
component: target
|
|
27
|
+
operation: compute
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
Scenario: A call caused by an event
|
|
31
|
+
When the `probe.source.transit` is called with:
|
|
32
|
+
"""yaml
|
|
33
|
+
query:
|
|
34
|
+
id: c0ffee00c0ffee00c0ffee00c0ffee00
|
|
35
|
+
input:
|
|
36
|
+
value: 1
|
|
37
|
+
"""
|
|
38
|
+
Then the map contains an edge:
|
|
39
|
+
"""yaml
|
|
40
|
+
src:
|
|
41
|
+
namespace: probe
|
|
42
|
+
component: source
|
|
43
|
+
event: created
|
|
44
|
+
dst:
|
|
45
|
+
namespace: probe
|
|
46
|
+
component: target
|
|
47
|
+
operation: count
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
Scenario: A call from a service
|
|
51
|
+
When the `probe.target.compute` is called with:
|
|
52
|
+
"""yaml
|
|
53
|
+
input:
|
|
54
|
+
a: 1
|
|
55
|
+
b: 1
|
|
56
|
+
source:
|
|
57
|
+
service: cli
|
|
58
|
+
"""
|
|
59
|
+
Then the map contains an edge:
|
|
60
|
+
"""yaml
|
|
61
|
+
src:
|
|
62
|
+
service: cli
|
|
63
|
+
dst:
|
|
64
|
+
namespace: probe
|
|
65
|
+
component: target
|
|
66
|
+
operation: compute
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
Scenario: A call from an unidentified caller
|
|
70
|
+
When the `probe.target.compute` is called with:
|
|
71
|
+
"""yaml
|
|
72
|
+
input:
|
|
73
|
+
a: 1
|
|
74
|
+
b: 1
|
|
75
|
+
"""
|
|
76
|
+
Then the map contains an edge:
|
|
77
|
+
"""yaml
|
|
78
|
+
src:
|
|
79
|
+
service: unknown
|
|
80
|
+
dst:
|
|
81
|
+
namespace: probe
|
|
82
|
+
component: target
|
|
83
|
+
operation: compute
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
Scenario: An operation returning an error is still an edge
|
|
87
|
+
When the `probe.source.fail` is called with:
|
|
88
|
+
"""yaml
|
|
89
|
+
input: null
|
|
90
|
+
"""
|
|
91
|
+
Then the map contains an edge:
|
|
92
|
+
"""yaml
|
|
93
|
+
dst:
|
|
94
|
+
namespace: probe
|
|
95
|
+
component: source
|
|
96
|
+
operation: fail
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
Scenario: An operation throwing is still an edge
|
|
100
|
+
When the `probe.source.crash` is called with:
|
|
101
|
+
"""yaml
|
|
102
|
+
input: null
|
|
103
|
+
"""
|
|
104
|
+
Then the map contains an edge:
|
|
105
|
+
"""yaml
|
|
106
|
+
dst:
|
|
107
|
+
namespace: probe
|
|
108
|
+
component: source
|
|
109
|
+
operation: crash
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
Scenario: An opted out component produces no edges
|
|
113
|
+
When the `probe.quiet.ping` is called with:
|
|
114
|
+
"""yaml
|
|
115
|
+
input:
|
|
116
|
+
a: 1
|
|
117
|
+
"""
|
|
118
|
+
Then the map contains no edge:
|
|
119
|
+
"""yaml
|
|
120
|
+
dst:
|
|
121
|
+
namespace: probe
|
|
122
|
+
component: quiet
|
|
123
|
+
"""
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Feature: Component descriptions
|
|
2
|
+
|
|
3
|
+
The static half of the map: what each component is, taken from its manifest
|
|
4
|
+
when the composition starts. This is where the event graph lives — which events
|
|
5
|
+
a component publishes, and which event each of its receivers takes — since all
|
|
6
|
+
of it is declared, and none of it needs traffic to be true.
|
|
7
|
+
|
|
8
|
+
Scenario: Describing operations
|
|
9
|
+
Then the map contains a node:
|
|
10
|
+
"""yaml
|
|
11
|
+
namespace: probe
|
|
12
|
+
component: target
|
|
13
|
+
operations:
|
|
14
|
+
- endpoint: compute
|
|
15
|
+
type: computation
|
|
16
|
+
scope: none
|
|
17
|
+
input:
|
|
18
|
+
type: object
|
|
19
|
+
properties:
|
|
20
|
+
a: { type: number }
|
|
21
|
+
b: { type: number }
|
|
22
|
+
output:
|
|
23
|
+
type: number
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
Scenario: Describing the entity
|
|
27
|
+
Then the map contains a node:
|
|
28
|
+
"""yaml
|
|
29
|
+
namespace: probe
|
|
30
|
+
component: target
|
|
31
|
+
entity:
|
|
32
|
+
storage: "@toa.io/storages.null"
|
|
33
|
+
schema:
|
|
34
|
+
properties:
|
|
35
|
+
counted:
|
|
36
|
+
type: integer
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
Scenario: Describing events
|
|
40
|
+
Then the map contains a node:
|
|
41
|
+
"""yaml
|
|
42
|
+
namespace: probe
|
|
43
|
+
component: source
|
|
44
|
+
events:
|
|
45
|
+
- label: created
|
|
46
|
+
- label: updated
|
|
47
|
+
- label: deleted
|
|
48
|
+
- label: sync
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
Scenario: Describing receivers
|
|
52
|
+
Then the map contains a node:
|
|
53
|
+
"""yaml
|
|
54
|
+
namespace: probe
|
|
55
|
+
component: target
|
|
56
|
+
receivers:
|
|
57
|
+
- label: probe.source.created
|
|
58
|
+
source: probe.source
|
|
59
|
+
event: created
|
|
60
|
+
operation: count
|
|
61
|
+
conditioned: false
|
|
62
|
+
adaptive: false
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
Scenario: An opted out component is not described
|
|
66
|
+
Then the map contains no node:
|
|
67
|
+
"""yaml
|
|
68
|
+
namespace: probe
|
|
69
|
+
component: quiet
|
|
70
|
+
"""
|