@nanobpm/nano-workforce 0.54.0 → 0.55.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/CHANGELOG.md +7 -0
- package/app/agentic/cockpit/index.ts +30 -0
- package/app/agentic/cockpit/supply-boot.test.ts +279 -0
- package/app/agentic/cockpit/supply-boot.ts +282 -0
- package/app/agentic/cockpit/supply-render.test.ts +76 -0
- package/app/agentic/cockpit/supply-render.ts +144 -0
- package/app/agentic/cockpit/supply-view.test.ts +76 -0
- package/app/agentic/cockpit/supply-view.ts +165 -0
- package/openapi.yaml +94 -0
- package/operations/getAgenticSupply.test.ts +153 -0
- package/operations/getAgenticSupply.ts +59 -0
- package/package.json +1 -1
- package/pages/cockpit/cockpit.css +145 -0
- package/pages/cockpit/embed.html +42 -0
- package/pages/cockpit/mount.js +331 -0
- package/pages/cockpit/standalone.html +46 -0
- package/pages/cockpit.page.json +46 -0
- package/pages/epic-detail.page.json +2 -1
- package/pages/epic.page.json +2 -1
- package/pages/home.page.json +4 -0
- package/test/agentic-cockpit-doubles.ts +136 -0
package/pages/epic.page.json
CHANGED
package/pages/home.page.json
CHANGED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
// Test-only doubles for the SUPPLY cockpit core (H5 / #148): an in-memory DOM that satisfies the
|
|
2
|
+
// renderer's structural {@link ElementLike} / {@link DocumentLike} types, and a fake relay socket that
|
|
3
|
+
// satisfies {@link RawSocket}. They let the supply renderer + boot layer be exercised on Node with no
|
|
4
|
+
// DOM library, no real WebSocket, and no `as` cast — the fakes are STRUCTURALLY the same subsets the
|
|
5
|
+
// browser's real `Document`/`Element`/`WebSocket` provide (mirrors the packaged cockpit's own fakes).
|
|
6
|
+
import { decodeFrame, encodeFrame, type Frame } from "@nanobpm/agentic/protocol";
|
|
7
|
+
import type { DocumentLike, ElementLike, RawSocket } from "@nanobpm/agentic/cockpit";
|
|
8
|
+
|
|
9
|
+
export class FakeElement implements ElementLike {
|
|
10
|
+
className = "";
|
|
11
|
+
textContent: string | null = null;
|
|
12
|
+
readonly tagName: string;
|
|
13
|
+
readonly attributes = new Map<string, string>();
|
|
14
|
+
readonly children: FakeElement[] = [];
|
|
15
|
+
readonly #listeners = new Map<string, Array<() => void>>();
|
|
16
|
+
|
|
17
|
+
constructor(tagName: string) {
|
|
18
|
+
this.tagName = tagName;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
setAttribute(name: string, value: string): void {
|
|
22
|
+
this.attributes.set(name, value);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
getAttribute(name: string): string | undefined {
|
|
26
|
+
return this.attributes.get(name);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
appendChild(child: ElementLike): ElementLike {
|
|
30
|
+
if (child instanceof FakeElement) this.children.push(child);
|
|
31
|
+
return child;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
replaceChildren(): void {
|
|
35
|
+
this.children.length = 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
addEventListener(type: string, handler: () => void): void {
|
|
39
|
+
const list = this.#listeners.get(type) ?? [];
|
|
40
|
+
list.push(handler);
|
|
41
|
+
this.#listeners.set(type, list);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Fire every listener registered for `type` (test driver for clicks). */
|
|
45
|
+
dispatch(type: string): void {
|
|
46
|
+
for (const handler of this.#listeners.get(type) ?? []) handler();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Depth-first walk of this element and its descendants. */
|
|
50
|
+
*walk(): IterableIterator<FakeElement> {
|
|
51
|
+
yield this;
|
|
52
|
+
for (const child of this.children) yield* child.walk();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Every descendant (and self) whose className contains `cls`. */
|
|
56
|
+
byClass(cls: string): FakeElement[] {
|
|
57
|
+
const out: FakeElement[] = [];
|
|
58
|
+
for (const node of this.walk()) {
|
|
59
|
+
if (node.className.split(/\s+/).includes(cls)) out.push(node);
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Every descendant (and self) with `data-<key>` equal to `value` (any value if omitted). */
|
|
65
|
+
byData(key: string, value?: string): FakeElement[] {
|
|
66
|
+
const attr = `data-${key}`;
|
|
67
|
+
const out: FakeElement[] = [];
|
|
68
|
+
for (const node of this.walk()) {
|
|
69
|
+
const got = node.attributes.get(attr);
|
|
70
|
+
if (got !== undefined && (value === undefined || got === value)) out.push(node);
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** The concatenated text content of this subtree (own text then children). */
|
|
76
|
+
text(): string {
|
|
77
|
+
let out = this.textContent ?? "";
|
|
78
|
+
for (const child of this.children) out += child.text();
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export class FakeDocument implements DocumentLike {
|
|
84
|
+
createElement(tagName: string): ElementLike {
|
|
85
|
+
return new FakeElement(tagName);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** A fake relay socket: records sent frames and lets a test drive open/close/deliver by hand. */
|
|
90
|
+
export class FakeSocket implements RawSocket {
|
|
91
|
+
readonly sent: Uint8Array[] = [];
|
|
92
|
+
closed = false;
|
|
93
|
+
#onMessage: ((bytes: Uint8Array) => void) | undefined;
|
|
94
|
+
#onOpen: (() => void) | undefined;
|
|
95
|
+
#onClose: (() => void) | undefined;
|
|
96
|
+
|
|
97
|
+
send(bytes: Uint8Array): void {
|
|
98
|
+
this.sent.push(bytes);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
close(): void {
|
|
102
|
+
this.closed = true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
onMessage(listener: (bytes: Uint8Array) => void): void {
|
|
106
|
+
this.#onMessage = listener;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
onOpen(listener: () => void): void {
|
|
110
|
+
this.#onOpen = listener;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
onClose(listener: () => void): void {
|
|
114
|
+
this.#onClose = listener;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Fire the open listener (a successful connect). */
|
|
118
|
+
fireOpen(): void {
|
|
119
|
+
this.#onOpen?.();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Fire the close listener (the socket dropped). */
|
|
123
|
+
fireClose(): void {
|
|
124
|
+
this.#onClose?.();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Deliver one inbound frame to the client. */
|
|
128
|
+
deliver(frame: Frame): void {
|
|
129
|
+
this.#onMessage?.(encodeFrame(frame));
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** The relay-family frames this socket sent, decoded. */
|
|
133
|
+
subscribeFrames(): Frame[] {
|
|
134
|
+
return this.sent.map(decodeFrame).filter((f) => f.family === "relay");
|
|
135
|
+
}
|
|
136
|
+
}
|