@fougere/calls 0.5.0-alpha.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.
- package/LICENSE +21 -0
- package/README.md +31 -0
- package/dist/CallRing.d.ts +45 -0
- package/dist/CallRing.d.ts.map +1 -0
- package/dist/CallRing.js +119 -0
- package/dist/CallRing.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +179 -0
- package/dist/index.js.map +1 -0
- package/dist/page.d.ts +23 -0
- package/dist/page.d.ts.map +1 -0
- package/dist/page.js +536 -0
- package/dist/page.js.map +1 -0
- package/dist/panel.d.ts +30 -0
- package/dist/panel.d.ts.map +1 -0
- package/dist/panel.js +102 -0
- package/dist/panel.js.map +1 -0
- package/dist/rings.d.ts +88 -0
- package/dist/rings.d.ts.map +1 -0
- package/dist/rings.js +112 -0
- package/dist/rings.js.map +1 -0
- package/package.json +58 -0
- package/src/CallRing.ts +119 -0
- package/src/index.ts +205 -0
- package/src/page.ts +535 -0
- package/src/panel.ts +123 -0
- package/src/rings.ts +158 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Fougere contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# @fougere/calls
|
|
2
|
+
> The dev panel — a bounded log of what this process dispatched
|
|
3
|
+
An optional extension. It **watches** rather than participates: it subscribes to
|
|
4
|
+
`app.observe`, which is passive and swallows an observer's own failure, and the ring
|
|
5
|
+
holds no reference to a body.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
extensions: [calls({ max: 500, panel: true })],
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
What it sees that a middleware cannot: a call refused **before** any handler — an unknown
|
|
12
|
+
route, an entity hosted elsewhere, a call arriving while the door drains — and the route
|
|
13
|
+
kind of every call, so a local execution and a hop to another process read the same way.
|
|
14
|
+
Beside the calls it keeps a bounded ring of log lines, of errors, and — when
|
|
15
|
+
`@fougere/adapter-sql` is installed — of the statements each call issued.
|
|
16
|
+
|
|
17
|
+
No port is opened unless `panel` says so. The ring is served as the `rpc.calls` operation,
|
|
18
|
+
and the reader is `fougere devtools` over `/_fougere/call`, like any other consumer. An app
|
|
19
|
+
that never installed this package answers
|
|
20
|
+
`Unknown rpc operation 'calls'. It serves discover.`
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
```bash
|
|
24
|
+
pnpm add -D @fougere/calls
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
Part of [Fougere](https://github.com/chok/fougere) — one schema, a gradient from
|
|
30
|
+
monolith to distributed, the same user code.
|
|
31
|
+
Reference documentation: [the site](https://fougere.dev/) (en/fr).
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { type CallPage, type CallRecord, type DispatchEvent } from '@fougere/core';
|
|
2
|
+
/**
|
|
3
|
+
* A bounded log of what this process dispatched.
|
|
4
|
+
*
|
|
5
|
+
* The five transitions of one call are folded into one record: `received` opens it,
|
|
6
|
+
* `resolved` names the route, `completed`/`failed` settle the verdict, `settled` closes
|
|
7
|
+
* the duration. They are matched by the identity of the `Call`, which is the same frozen
|
|
8
|
+
* object across all five.
|
|
9
|
+
*/
|
|
10
|
+
export declare class CallRing {
|
|
11
|
+
private readonly max;
|
|
12
|
+
private readonly frondOf;
|
|
13
|
+
private readonly held;
|
|
14
|
+
private readonly open;
|
|
15
|
+
private seq;
|
|
16
|
+
private lost;
|
|
17
|
+
private readonly watching;
|
|
18
|
+
constructor(max?: number, frondOf?: (entity: string) => string | undefined);
|
|
19
|
+
/**
|
|
20
|
+
* KNOWN LIMIT — a call this process ORIGINATES carries no trace here.
|
|
21
|
+
*
|
|
22
|
+
* `trace()` writes the traceparent inside the middleware chain, so inside
|
|
23
|
+
* `route.execute(call)`; the observer saw the `Call` at `received`, and that object is
|
|
24
|
+
* frozen. An ARRIVING call is unaffected: its traceparent is already on the invocation
|
|
25
|
+
* the transport handed over, which is why a hosted frond shows traces and its consumer
|
|
26
|
+
* does not.
|
|
27
|
+
*
|
|
28
|
+
* Closing it means `DispatchEvent` carrying the invocation as it ENDED rather than as it
|
|
29
|
+
* arrived — a core change, deliberately not smuggled in here. Until then the Traces view
|
|
30
|
+
* speaks for the side that receives.
|
|
31
|
+
*/
|
|
32
|
+
record(event: DispatchEvent): void;
|
|
33
|
+
private opened;
|
|
34
|
+
/**
|
|
35
|
+
* Be told when a record settles, and get the unsubscription back.
|
|
36
|
+
*
|
|
37
|
+
* A page that polls is a page that is always a little wrong; this is what lets a door
|
|
38
|
+
* push instead. Told at `settled` and not at `received`, because a record is only
|
|
39
|
+
* complete then — a reader would otherwise redraw the same line four times.
|
|
40
|
+
*/
|
|
41
|
+
watch(told: (record: CallRecord) => void): () => void;
|
|
42
|
+
/** Everything above `cursor`, and what was lost while the reader was away. */
|
|
43
|
+
since(cursor: number, inFlight?: number): CallPage;
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=CallRing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CallRing.d.ts","sourceRoot":"","sources":["../src/CallRing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,QAAQ,EAAE,KAAK,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAa/F;;;;;;;GAOG;AACH,qBAAa,QAAQ;IAQjB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAR1B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAoB;IACzC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAqC;IAC1D,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA2C;IAEpE,YACmB,GAAG,SAAM,EACT,OAAO,GAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,SAA2B,EAChF;IAEJ;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,CA0BjC;IAED,OAAO,CAAC,MAAM;IAsBd;;;;;;OAMG;IACH,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI,CAIpD;IAED,8EAA8E;IAC9E,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,QAAQ,CAO5C;CACF"}
|
package/dist/CallRing.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { RPC_ENTITY } from '@fougere/core';
|
|
2
|
+
/** The message and the code of a refusal, without the stack that carries a body. */
|
|
3
|
+
function refusalOf(error) {
|
|
4
|
+
if (error === null || error === undefined)
|
|
5
|
+
return { message: 'unknown refusal' };
|
|
6
|
+
const held = error;
|
|
7
|
+
return {
|
|
8
|
+
...(typeof held.code === 'string' ? { code: held.code } : {}),
|
|
9
|
+
message: typeof held.message === 'string' ? held.message : String(error),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* A bounded log of what this process dispatched.
|
|
14
|
+
*
|
|
15
|
+
* The five transitions of one call are folded into one record: `received` opens it,
|
|
16
|
+
* `resolved` names the route, `completed`/`failed` settle the verdict, `settled` closes
|
|
17
|
+
* the duration. They are matched by the identity of the `Call`, which is the same frozen
|
|
18
|
+
* object across all five.
|
|
19
|
+
*/
|
|
20
|
+
export class CallRing {
|
|
21
|
+
max;
|
|
22
|
+
frondOf;
|
|
23
|
+
held = [];
|
|
24
|
+
open = new WeakMap();
|
|
25
|
+
seq = 0;
|
|
26
|
+
lost = 0;
|
|
27
|
+
watching = new Set();
|
|
28
|
+
constructor(max = 500, frondOf = () => undefined) {
|
|
29
|
+
this.max = max;
|
|
30
|
+
this.frondOf = frondOf;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* KNOWN LIMIT — a call this process ORIGINATES carries no trace here.
|
|
34
|
+
*
|
|
35
|
+
* `trace()` writes the traceparent inside the middleware chain, so inside
|
|
36
|
+
* `route.execute(call)`; the observer saw the `Call` at `received`, and that object is
|
|
37
|
+
* frozen. An ARRIVING call is unaffected: its traceparent is already on the invocation
|
|
38
|
+
* the transport handed over, which is why a hosted frond shows traces and its consumer
|
|
39
|
+
* does not.
|
|
40
|
+
*
|
|
41
|
+
* Closing it means `DispatchEvent` carrying the invocation as it ENDED rather than as it
|
|
42
|
+
* arrived — a core change, deliberately not smuggled in here. Until then the Traces view
|
|
43
|
+
* speaks for the side that receives.
|
|
44
|
+
*/
|
|
45
|
+
record(event) {
|
|
46
|
+
// A reader reaches this ring through `rpc`, so recording that would make the panel
|
|
47
|
+
// watch itself — one reader, one line, forever.
|
|
48
|
+
if (event.call.address.entity === RPC_ENTITY)
|
|
49
|
+
return;
|
|
50
|
+
if (event.stage === 'received')
|
|
51
|
+
return this.opened(event);
|
|
52
|
+
const record = this.open.get(event.call);
|
|
53
|
+
if (!record)
|
|
54
|
+
return;
|
|
55
|
+
if (event.stage === 'resolved' && event.routeKind)
|
|
56
|
+
record.route = event.routeKind;
|
|
57
|
+
if (event.stage === 'completed')
|
|
58
|
+
record.verdict = 'ok';
|
|
59
|
+
if (event.stage === 'failed') {
|
|
60
|
+
record.verdict = 'failed';
|
|
61
|
+
record.refusal = refusalOf(event.error);
|
|
62
|
+
}
|
|
63
|
+
if (event.stage === 'settled') {
|
|
64
|
+
record.ms = Date.now() - record.startedAt;
|
|
65
|
+
if (event.routeKind)
|
|
66
|
+
record.route = event.routeKind;
|
|
67
|
+
this.open.delete(event.call);
|
|
68
|
+
for (const told of this.watching) {
|
|
69
|
+
// A watcher's own failure is not the dispatch's problem — the same rule
|
|
70
|
+
// `DispatchLifecycle` applies to an observer.
|
|
71
|
+
try {
|
|
72
|
+
told(record);
|
|
73
|
+
}
|
|
74
|
+
catch { /* observational */ }
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
opened(event) {
|
|
79
|
+
const { entity, operation, surface } = event.call.address;
|
|
80
|
+
const frond = this.frondOf(entity);
|
|
81
|
+
const { trace, caller } = event.call.invocation;
|
|
82
|
+
const record = {
|
|
83
|
+
seq: ++this.seq,
|
|
84
|
+
...(frond ? { frond } : {}),
|
|
85
|
+
entity,
|
|
86
|
+
operation,
|
|
87
|
+
...(surface !== undefined ? { surface } : {}),
|
|
88
|
+
...(trace ? { trace } : {}),
|
|
89
|
+
...(caller ? { caller } : {}),
|
|
90
|
+
startedAt: Date.now(),
|
|
91
|
+
verdict: 'running',
|
|
92
|
+
};
|
|
93
|
+
this.held.push(record);
|
|
94
|
+
this.open.set(event.call, record);
|
|
95
|
+
if (this.held.length > this.max)
|
|
96
|
+
this.lost += this.held.splice(0, this.held.length - this.max).length;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Be told when a record settles, and get the unsubscription back.
|
|
100
|
+
*
|
|
101
|
+
* A page that polls is a page that is always a little wrong; this is what lets a door
|
|
102
|
+
* push instead. Told at `settled` and not at `received`, because a record is only
|
|
103
|
+
* complete then — a reader would otherwise redraw the same line four times.
|
|
104
|
+
*/
|
|
105
|
+
watch(told) {
|
|
106
|
+
this.watching.add(told);
|
|
107
|
+
return () => this.watching.delete(told);
|
|
108
|
+
}
|
|
109
|
+
/** Everything above `cursor`, and what was lost while the reader was away. */
|
|
110
|
+
since(cursor, inFlight = 0) {
|
|
111
|
+
return {
|
|
112
|
+
calls: this.held.filter((record) => record.seq > cursor),
|
|
113
|
+
cursor: this.seq,
|
|
114
|
+
inFlight,
|
|
115
|
+
dropped: this.lost,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=CallRing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CallRing.js","sourceRoot":"","sources":["../src/CallRing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsD,MAAM,eAAe,CAAC;AAE/F,oFAAoF;AACpF,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,CAAC;IACjF,MAAM,IAAI,GAAG,KAA8C,CAAC;IAE5D,OAAO;QACL,GAAG,CAAC,OAAO,IAAI,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,OAAO,EAAE,OAAO,IAAI,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;KACzE,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,OAAO,QAAQ;IAQA,GAAG;IACH,OAAO;IART,IAAI,GAAiB,EAAE,CAAC;IACxB,IAAI,GAAG,IAAI,OAAO,EAAsB,CAAC;IAClD,GAAG,GAAG,CAAC,CAAC;IACR,IAAI,GAAG,CAAC,CAAC;IACA,QAAQ,GAAG,IAAI,GAAG,EAAgC,CAAC;IAEpE,YACmB,GAAG,GAAG,GAAG,EACT,OAAO,GAA2C,GAAG,EAAE,CAAC,SAAS;mBADjE,GAAG;uBACH,OAAO;IACvB,CAAC;IAEJ;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,KAAoB;QACzB,mFAAmF;QACnF,gDAAgD;QAChD,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,UAAU;YAAE,OAAO;QAErD,IAAI,KAAK,CAAC,KAAK,KAAK,UAAU;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAE1D,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM;YAAE,OAAO;QAEpB,IAAI,KAAK,CAAC,KAAK,KAAK,UAAU,IAAI,KAAK,CAAC,SAAS;YAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC;QAClF,IAAI,KAAK,CAAC,KAAK,KAAK,WAAW;YAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC;QACvD,IAAI,KAAK,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YAC7B,MAAM,CAAC,OAAO,GAAG,QAAQ,CAAC;YAC1B,MAAM,CAAC,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC1C,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC;YAC1C,IAAI,KAAK,CAAC,SAAS;gBAAE,MAAM,CAAC,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC;YACpD,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC7B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACjC,wEAAwE;gBACxE,8CAA8C;gBAC9C,IAAI,CAAC;oBAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAAC,CAAC;gBAAC,MAAM,CAAC,CAAC,mBAAmB,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;IACH,CAAC;IAEO,MAAM,CAAC,KAAoB;QACjC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC;QAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACnC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC;QAChD,MAAM,MAAM,GAAe;YACzB,GAAG,EAAE,EAAE,IAAI,CAAC,GAAG;YACf,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3B,MAAM;YACN,SAAS;YACT,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7C,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC7B,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,OAAO,EAAE,SAAS;SACnB,CAAC;QAEF,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAElC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG;YAAE,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC;IACxG,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,IAAkC;QACtC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAExB,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,8EAA8E;IAC9E,KAAK,CAAC,MAAc,EAAE,QAAQ,GAAG,CAAC;QAChC,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC;YACxD,MAAM,EAAE,IAAI,CAAC,GAAG;YAChB,QAAQ;YACR,OAAO,EAAE,IAAI,CAAC,IAAI;SACnB,CAAC;IACJ,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Extension } from '@fougere/core';
|
|
2
|
+
import { type PanelOptions } from './panel.js';
|
|
3
|
+
export type { CallPage, CallRecord } from '@fougere/core';
|
|
4
|
+
export { CallRing } from './CallRing.js';
|
|
5
|
+
export { LogRing, QueryRing, ErrorRing } from './rings.js';
|
|
6
|
+
export type { LogLine, QueryLine, ErrorGroup } from './rings.js';
|
|
7
|
+
export { servePanel, type PanelOptions } from './panel.js';
|
|
8
|
+
/** The rpc operation this extension answers under. */
|
|
9
|
+
export declare const CALLS_OP = "calls";
|
|
10
|
+
export interface CallsOptions {
|
|
11
|
+
/** How many calls the ring keeps. Beyond it, the oldest are dropped and counted. */
|
|
12
|
+
max?: number;
|
|
13
|
+
/**
|
|
14
|
+
* Serve the page too, on its own loopback port. `true` takes a free one.
|
|
15
|
+
*
|
|
16
|
+
* Node only — it opens an `http` server, which workerd has not. Without it the extension
|
|
17
|
+
* stays universal and `fougere devtools` is the reader.
|
|
18
|
+
*/
|
|
19
|
+
panel?: boolean | number | PanelOptions;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* What this process dispatched, kept in a bounded ring and served as an rpc operation.
|
|
23
|
+
*
|
|
24
|
+
* It watches rather than participates: `app.observe` is passive, `DispatchLifecycle`
|
|
25
|
+
* swallows an observer's own failure, and the ring holds no reference to a body. What it
|
|
26
|
+
* sees that a middleware cannot: a call refused BEFORE any handler — an unknown route, an
|
|
27
|
+
* entity hosted elsewhere, a call arriving while the door drains — and the route kind of
|
|
28
|
+
* every call, so a local execution and a hop to another process read the same way.
|
|
29
|
+
*
|
|
30
|
+
* The reader is `fougere devtools`, over `/_fougere/call` like any other consumer. No port
|
|
31
|
+
* is opened here, and an app that never installed this package answers
|
|
32
|
+
* `Unknown rpc operation 'calls'. It serves discover.`
|
|
33
|
+
*/
|
|
34
|
+
export declare function calls(options?: CallsOptions): Extension;
|
|
35
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmB,KAAK,SAAS,EAA0B,MAAM,eAAe,CAAC;AAGxF,OAAO,EAAc,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAE3D,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC3D,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,KAAK,YAAY,EAAE,MAAM,YAAY,CAAC;AAE3D,sDAAsD;AACtD,eAAO,MAAM,QAAQ,UAAU,CAAC;AAEhC,MAAM,WAAW,YAAY;IAC3B,oFAAoF;IACpF,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,YAAY,CAAC;CACzC;AAwGD;;;;;;;;;;;;GAYG;AACH,wBAAgB,KAAK,CAAC,OAAO,GAAE,YAAiB,GAAG,SAAS,CA+D3D"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { onLog } from '@fougere/core';
|
|
2
|
+
import { CallRing } from './CallRing.js';
|
|
3
|
+
import { ErrorRing, LogRing, QueryRing } from './rings.js';
|
|
4
|
+
import { servePanel } from './panel.js';
|
|
5
|
+
export { CallRing } from './CallRing.js';
|
|
6
|
+
export { LogRing, QueryRing, ErrorRing } from './rings.js';
|
|
7
|
+
export { servePanel } from './panel.js';
|
|
8
|
+
/** The rpc operation this extension answers under. */
|
|
9
|
+
export const CALLS_OP = 'calls';
|
|
10
|
+
/**
|
|
11
|
+
* What this process serves, read from the app itself.
|
|
12
|
+
*
|
|
13
|
+
* Not a second source: `operationsFor` is the same effective model the façade consumes, so
|
|
14
|
+
* the page shows what will actually answer — including the ops nobody has called yet, which
|
|
15
|
+
* is what makes an empty panel useful instead of blank.
|
|
16
|
+
*/
|
|
17
|
+
function servedModel(app) {
|
|
18
|
+
return {
|
|
19
|
+
fronds: app.fronds.map((frond) => ({
|
|
20
|
+
name: frond.name,
|
|
21
|
+
// What the config SAYS. What the runtime saw is in the ring, under `route` — and the
|
|
22
|
+
// two disagree exactly when something is misconfigured, which is the whole point of
|
|
23
|
+
// showing them side by side. `rpc.topology` calls a frond remote because it ANSWERED.
|
|
24
|
+
declared: app.remotes[frond.name] ? 'remote' : 'local',
|
|
25
|
+
at: app.remotes[frond.name] ? hostOf(app.remotes[frond.name]) : null,
|
|
26
|
+
entities: frond.entities.map((entity) => entity.name),
|
|
27
|
+
operations: frond.handlers.flatMap((handler) => {
|
|
28
|
+
const ops = app.operationsFor(handler.address);
|
|
29
|
+
return [...(ops?.values() ?? [])].map((op) => ({
|
|
30
|
+
id: op.id,
|
|
31
|
+
operation: op.operation,
|
|
32
|
+
kind: op.kind,
|
|
33
|
+
address: `${op.handler.address}.${op.implementation.method}`,
|
|
34
|
+
handler: op.handler.className,
|
|
35
|
+
description: op.description ?? null,
|
|
36
|
+
input: nameOf(op.input),
|
|
37
|
+
output: nameOf(op.output),
|
|
38
|
+
cardinality: op.cardinality ?? null,
|
|
39
|
+
parameters: op.parameters.map((one) => ({
|
|
40
|
+
name: one.name,
|
|
41
|
+
type: one.type,
|
|
42
|
+
optional: one.optional,
|
|
43
|
+
binding: one.binding.source.kind,
|
|
44
|
+
})),
|
|
45
|
+
surfaces: op.exposure.surfaces,
|
|
46
|
+
adapters: op.exposure.adapters,
|
|
47
|
+
placement: op.placement.runtime,
|
|
48
|
+
file: op.implementation.filePath,
|
|
49
|
+
}));
|
|
50
|
+
}),
|
|
51
|
+
})),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Host and port only — a declared address may carry credentials, and this answer leaves. */
|
|
55
|
+
function hostOf(address) {
|
|
56
|
+
try {
|
|
57
|
+
const url = new URL(address);
|
|
58
|
+
return `${url.protocol}//${url.host}`;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return address;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function nameOf(schema) {
|
|
65
|
+
const held = schema;
|
|
66
|
+
if (!held)
|
|
67
|
+
return null;
|
|
68
|
+
return typeof held.getName === 'function' ? held.getName() : held.name ?? null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* `address -> frond`, resolved once at boot: a call's address does not carry its frond.
|
|
72
|
+
*
|
|
73
|
+
* Indexed on HANDLERS and not on entities, because a frond may hold no entity at all —
|
|
74
|
+
* `demos/observability` has two — and then every one of its calls came back unnamed. The
|
|
75
|
+
* entities are indexed after, and only where a handler left the address free: an entity
|
|
76
|
+
* with no handler is served by `Crud`, which answers under the same address.
|
|
77
|
+
*/
|
|
78
|
+
function frondIndex(app) {
|
|
79
|
+
const byAddress = new Map();
|
|
80
|
+
for (const frond of app.fronds) {
|
|
81
|
+
for (const entity of frond.entities)
|
|
82
|
+
byAddress.set(entity.name, frond.name);
|
|
83
|
+
for (const handler of frond.handlers)
|
|
84
|
+
byAddress.set(handler.address, frond.name);
|
|
85
|
+
}
|
|
86
|
+
return (address) => byAddress.get(address);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Subscribe to SQL when there is SQL. Absence is an answer, not a blank tab.
|
|
90
|
+
*
|
|
91
|
+
* `logQueries` is installed in every Kysely this process builds, so the subscription is all
|
|
92
|
+
* that is missing — and it is taken here, in `up(app)`, like the other two.
|
|
93
|
+
*/
|
|
94
|
+
async function queriesFrom(queries) {
|
|
95
|
+
try {
|
|
96
|
+
const { onQuery } = await import('@fougere/adapter-sql');
|
|
97
|
+
return onQuery((event) => queries.record(event));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return () => { };
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function cursorOf(invocation) {
|
|
104
|
+
const body = invocation.body;
|
|
105
|
+
const since = Number(body?.since ?? 0);
|
|
106
|
+
return Number.isFinite(since) && since > 0 ? since : 0;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* What this process dispatched, kept in a bounded ring and served as an rpc operation.
|
|
110
|
+
*
|
|
111
|
+
* It watches rather than participates: `app.observe` is passive, `DispatchLifecycle`
|
|
112
|
+
* swallows an observer's own failure, and the ring holds no reference to a body. What it
|
|
113
|
+
* sees that a middleware cannot: a call refused BEFORE any handler — an unknown route, an
|
|
114
|
+
* entity hosted elsewhere, a call arriving while the door drains — and the route kind of
|
|
115
|
+
* every call, so a local execution and a hop to another process read the same way.
|
|
116
|
+
*
|
|
117
|
+
* The reader is `fougere devtools`, over `/_fougere/call` like any other consumer. No port
|
|
118
|
+
* is opened here, and an app that never installed this package answers
|
|
119
|
+
* `Unknown rpc operation 'calls'. It serves discover.`
|
|
120
|
+
*/
|
|
121
|
+
export function calls(options = {}) {
|
|
122
|
+
/**
|
|
123
|
+
* Per APP, not per extension. A host declares its extensions once, so the same instance
|
|
124
|
+
* goes up on the new app before the old one is released — one shared ring would mix two
|
|
125
|
+
* processes' worth of calls, and the older `down` would erase the newer subscription.
|
|
126
|
+
*/
|
|
127
|
+
const stopping = new WeakMap();
|
|
128
|
+
return {
|
|
129
|
+
name: 'calls',
|
|
130
|
+
async up(app) {
|
|
131
|
+
const ring = new CallRing(options.max ?? 500, frondIndex(app));
|
|
132
|
+
const logs = new LogRing();
|
|
133
|
+
const errors = new ErrorRing();
|
|
134
|
+
const queries = new QueryRing();
|
|
135
|
+
const undo = [
|
|
136
|
+
app.observe((event) => {
|
|
137
|
+
ring.record(event);
|
|
138
|
+
if (event.stage === 'failed')
|
|
139
|
+
errors.fromDispatch(event);
|
|
140
|
+
}),
|
|
141
|
+
onLog((line) => {
|
|
142
|
+
logs.record(line);
|
|
143
|
+
errors.fromLog(line);
|
|
144
|
+
}),
|
|
145
|
+
// `@fougere/adapter-sql` is optional — an app on another storage, or none, simply
|
|
146
|
+
// never resolves it, and the Queries tab says there is none rather than staying
|
|
147
|
+
// blank. Dynamic on purpose: this package declares no dependency on it.
|
|
148
|
+
await queriesFrom(queries),
|
|
149
|
+
];
|
|
150
|
+
const stop = () => { for (const one of undo)
|
|
151
|
+
one(); };
|
|
152
|
+
app.serveRpc(CALLS_OP, (invocation) => ring.since(cursorOf(invocation), app.inFlight()));
|
|
153
|
+
if (!options.panel) {
|
|
154
|
+
stopping.set(app, stop);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
const asked = typeof options.panel === 'object' ? options.panel : {};
|
|
158
|
+
const port = typeof options.panel === 'number' ? options.panel : asked.port;
|
|
159
|
+
const close = await servePanel(ring, {
|
|
160
|
+
...asked,
|
|
161
|
+
...(port !== undefined ? { port } : {}),
|
|
162
|
+
title: asked.title ?? app.fronds[0]?.name ?? 'fougere',
|
|
163
|
+
fronds: app.fronds.map((frond) => frond.name),
|
|
164
|
+
model: servedModel(app),
|
|
165
|
+
logs: (cursor) => logs.since(cursor),
|
|
166
|
+
errors: (cursor) => errors.since(cursor),
|
|
167
|
+
queries: (cursor) => queries.since(cursor),
|
|
168
|
+
inFlight: () => app.inFlight(),
|
|
169
|
+
announce: asked.announce ?? ((url) => console.log(` calls panel ${url}`)),
|
|
170
|
+
});
|
|
171
|
+
stopping.set(app, async () => { stop(); await close(); });
|
|
172
|
+
},
|
|
173
|
+
async down(app) {
|
|
174
|
+
await stopping.get(app)?.();
|
|
175
|
+
stopping.delete(app);
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/page.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The panel, as one file with no dependency.
|
|
3
|
+
*
|
|
4
|
+
* Inline on purpose: this package ships no framework and no build step, and a dev tool that
|
|
5
|
+
* needs `npm install` before it shows anything is a dev tool nobody opens. The page reads
|
|
6
|
+
* its own origin — the door that served it also serves its events — so there is no address
|
|
7
|
+
* to configure and no CORS to arrange.
|
|
8
|
+
*
|
|
9
|
+
* FOUR views, and the number is a decision. Six grew one tab at a time, each with its own
|
|
10
|
+
* empty state and its own caveat, and the result read as noise rather than as an answer.
|
|
11
|
+
* They fold along what a reader actually asks:
|
|
12
|
+
*
|
|
13
|
+
* Calls what happened, and did it hold — the screen that IS the argument
|
|
14
|
+
* Refused what did not, from two sources, since one of them is never a call
|
|
15
|
+
* Activity what the process did along the way — logs and statements are one stream,
|
|
16
|
+
* both chronological, both uncorrelated to a call; two tabs said that twice
|
|
17
|
+
* Served what it would answer if asked, filled before anything is called
|
|
18
|
+
*
|
|
19
|
+
* Traces lost its tab: it spoke for the receiving side only, and a trace already shows in a
|
|
20
|
+
* call's detail. A tab that half-delivers is worse than a field that says what it knows.
|
|
21
|
+
*/
|
|
22
|
+
export declare function page(title: string): string;
|
|
23
|
+
//# sourceMappingURL=page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page.d.ts","sourceRoot":"","sources":["../src/page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAigB1C"}
|