@getfamiliar/shared 0.1.2
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/build/AgentRun.d.ts +187 -0
- package/build/AgentRun.js +1 -0
- package/build/AgentRunBus.d.ts +147 -0
- package/build/AgentRunBus.js +362 -0
- package/build/Calendar.d.ts +450 -0
- package/build/Calendar.js +37 -0
- package/build/ChatMessage.d.ts +53 -0
- package/build/ChatMessage.js +11 -0
- package/build/ChatMessageBus.d.ts +110 -0
- package/build/ChatMessageBus.js +257 -0
- package/build/Config.d.ts +83 -0
- package/build/Config.js +1 -0
- package/build/ContainerTool.d.ts +34 -0
- package/build/ContainerTool.js +1 -0
- package/build/CronExpression.d.ts +53 -0
- package/build/CronExpression.js +77 -0
- package/build/Event.d.ts +280 -0
- package/build/Event.js +14 -0
- package/build/EventBus.d.ts +198 -0
- package/build/EventBus.js +482 -0
- package/build/HandlerCatalog.d.ts +75 -0
- package/build/HandlerCatalog.js +165 -0
- package/build/InferenceEvent.d.ts +30 -0
- package/build/InferenceEvent.js +1 -0
- package/build/InferenceEventBus.d.ts +39 -0
- package/build/InferenceEventBus.js +80 -0
- package/build/Mail.d.ts +250 -0
- package/build/Mail.js +73 -0
- package/build/MailStyleTemplate.d.ts +71 -0
- package/build/MailStyleTemplate.js +47 -0
- package/build/ModelMetaData.d.ts +63 -0
- package/build/ModelMetaData.js +12 -0
- package/build/PathGlob.d.ts +20 -0
- package/build/PathGlob.js +33 -0
- package/build/Plugin.d.ts +709 -0
- package/build/Plugin.js +29 -0
- package/build/PostgresConnection.d.ts +75 -0
- package/build/PostgresConnection.js +162 -0
- package/build/ScheduledSubagent.d.ts +49 -0
- package/build/ScheduledSubagent.js +1 -0
- package/build/ScheduledSubagentBus.d.ts +94 -0
- package/build/ScheduledSubagentBus.js +187 -0
- package/build/Schema.d.ts +84 -0
- package/build/Schema.js +570 -0
- package/build/StepResult.d.ts +103 -0
- package/build/StepResult.js +1 -0
- package/build/StepResultBus.d.ts +64 -0
- package/build/StepResultBus.js +169 -0
- package/build/Timezone.d.ts +58 -0
- package/build/Timezone.js +84 -0
- package/build/ToolCall.d.ts +25 -0
- package/build/ToolCall.js +1 -0
- package/build/ToolCallBus.d.ts +65 -0
- package/build/ToolCallBus.js +110 -0
- package/build/ToolLevel.d.ts +20 -0
- package/build/ToolLevel.js +2 -0
- package/build/ToolRunner.d.ts +118 -0
- package/build/ToolRunner.js +192 -0
- package/build/ToolsExpressionUtils.d.ts +80 -0
- package/build/ToolsExpressionUtils.js +102 -0
- package/build/WorkspaceFile.d.ts +104 -0
- package/build/WorkspaceFile.js +7 -0
- package/build/estimateTokens.d.ts +16 -0
- package/build/estimateTokens.js +18 -0
- package/build/index.d.ts +37 -0
- package/build/index.js +26 -0
- package/build/logging/Logger.d.ts +66 -0
- package/build/logging/Logger.js +90 -0
- package/build/markdownHtml.d.ts +14 -0
- package/build/markdownHtml.js +21 -0
- package/build/markdownTerminal.d.ts +14 -0
- package/build/markdownTerminal.js +376 -0
- package/build/testing/MockAgentRunBus.d.ts +52 -0
- package/build/testing/MockAgentRunBus.js +175 -0
- package/build/testing/MockBusStore.d.ts +22 -0
- package/build/testing/MockBusStore.js +22 -0
- package/build/testing/MockEventBus.d.ts +20 -0
- package/build/testing/MockEventBus.js +57 -0
- package/build/testing/MockLogger.d.ts +52 -0
- package/build/testing/MockLogger.js +65 -0
- package/build/testing/index.d.ts +4 -0
- package/build/testing/index.js +4 -0
- package/package.json +63 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AgentRunPatch, AgentRunRow, NewAgentRun } from "../AgentRun.js";
|
|
2
|
+
import type { MockBusStore } from "./MockBusStore.js";
|
|
3
|
+
/**
|
|
4
|
+
* Listener for {@link MockAgentRunBus.subscribe}. Fires synchronously
|
|
5
|
+
* after every state-affecting write so test code can advance the
|
|
6
|
+
* Scheduler in lock-step with mutations.
|
|
7
|
+
*/
|
|
8
|
+
export type MockAgentRunBusListener = (row: AgentRunRow) => void;
|
|
9
|
+
/**
|
|
10
|
+
* In-memory {@link AgentRunBus} replacement for unit tests. Stores
|
|
11
|
+
* agentruns in a {@link MockBusStore} so it can recompute the parent
|
|
12
|
+
* event's terminal state when a row settles — the same coupling the
|
|
13
|
+
* production schema's `EVENT_TERMINAL_UPDATE_SQL` enforces.
|
|
14
|
+
*
|
|
15
|
+
* Listeners (subscribed via {@link subscribe}) fire synchronously
|
|
16
|
+
* after every state-affecting write, replacing postgres NOTIFY for
|
|
17
|
+
* test purposes.
|
|
18
|
+
*
|
|
19
|
+
* The class is intentionally not typed as `implements AgentRunBus`:
|
|
20
|
+
* the production class carries private postgres fields that block
|
|
21
|
+
* structural compat. The Scheduler (and any other consumer wanting
|
|
22
|
+
* test-friendliness) should accept a structural subset of the bus —
|
|
23
|
+
* e.g. `Pick<AgentRunBus, 'add' | 'update' | …>` — which this class
|
|
24
|
+
* satisfies by shape.
|
|
25
|
+
*/
|
|
26
|
+
export declare class MockAgentRunBus {
|
|
27
|
+
private readonly store;
|
|
28
|
+
private readonly listeners;
|
|
29
|
+
constructor(store: MockBusStore);
|
|
30
|
+
/** No-op — kept for shape compatibility with the production bus. */
|
|
31
|
+
installSchema(): Promise<void>;
|
|
32
|
+
add(run: NewAgentRun): Promise<AgentRunRow>;
|
|
33
|
+
update(id: string, patch: AgentRunPatch): Promise<void>;
|
|
34
|
+
settle(id: string, toState: "done" | "failed", outcome?: {
|
|
35
|
+
readonly result?: unknown;
|
|
36
|
+
readonly resultText?: string | null;
|
|
37
|
+
readonly error?: string | null;
|
|
38
|
+
}): Promise<void>;
|
|
39
|
+
getById(id: string): Promise<AgentRunRow | undefined>;
|
|
40
|
+
listEligible(): Promise<readonly AgentRunRow[]>;
|
|
41
|
+
areAllStartedChildrenSettled(parentId: string): Promise<boolean>;
|
|
42
|
+
listSince(since: Date): Promise<AgentRunRow[]>;
|
|
43
|
+
listByEventId(eventId: string): Promise<readonly AgentRunRow[]>;
|
|
44
|
+
postpone(id: string, runAfter: Date, errorText: string | null): Promise<void>;
|
|
45
|
+
/**
|
|
46
|
+
* Test-only: subscribe to row-change notifications. Replaces
|
|
47
|
+
* postgres NOTIFY for test code. Returns an unsubscriber.
|
|
48
|
+
*/
|
|
49
|
+
subscribe(listener: MockAgentRunBusListener): () => void;
|
|
50
|
+
private fire;
|
|
51
|
+
private recomputeEventTerminal;
|
|
52
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory {@link AgentRunBus} replacement for unit tests. Stores
|
|
3
|
+
* agentruns in a {@link MockBusStore} so it can recompute the parent
|
|
4
|
+
* event's terminal state when a row settles — the same coupling the
|
|
5
|
+
* production schema's `EVENT_TERMINAL_UPDATE_SQL` enforces.
|
|
6
|
+
*
|
|
7
|
+
* Listeners (subscribed via {@link subscribe}) fire synchronously
|
|
8
|
+
* after every state-affecting write, replacing postgres NOTIFY for
|
|
9
|
+
* test purposes.
|
|
10
|
+
*
|
|
11
|
+
* The class is intentionally not typed as `implements AgentRunBus`:
|
|
12
|
+
* the production class carries private postgres fields that block
|
|
13
|
+
* structural compat. The Scheduler (and any other consumer wanting
|
|
14
|
+
* test-friendliness) should accept a structural subset of the bus —
|
|
15
|
+
* e.g. `Pick<AgentRunBus, 'add' | 'update' | …>` — which this class
|
|
16
|
+
* satisfies by shape.
|
|
17
|
+
*/
|
|
18
|
+
export class MockAgentRunBus {
|
|
19
|
+
store;
|
|
20
|
+
listeners = [];
|
|
21
|
+
constructor(store) {
|
|
22
|
+
this.store = store;
|
|
23
|
+
}
|
|
24
|
+
/** No-op — kept for shape compatibility with the production bus. */
|
|
25
|
+
async installSchema() {
|
|
26
|
+
// intentionally empty
|
|
27
|
+
}
|
|
28
|
+
async add(run) {
|
|
29
|
+
const id = String(this.store.nextAgentrunId++);
|
|
30
|
+
const now = new Date();
|
|
31
|
+
const row = {
|
|
32
|
+
id,
|
|
33
|
+
eventId: run.eventId,
|
|
34
|
+
parentAgentrunId: run.parentAgentrunId ?? null,
|
|
35
|
+
topic: run.topic,
|
|
36
|
+
handler: run.handler,
|
|
37
|
+
priority: run.priority ?? 50,
|
|
38
|
+
state: run.state ?? "pending",
|
|
39
|
+
prompt: run.prompt ?? null,
|
|
40
|
+
payload: run.payload ?? {},
|
|
41
|
+
result: null,
|
|
42
|
+
resultText: null,
|
|
43
|
+
error: null,
|
|
44
|
+
privileged: run.privileged ?? false,
|
|
45
|
+
calltype: (run.calltype ?? null),
|
|
46
|
+
retryCount: 0,
|
|
47
|
+
notBefore: null,
|
|
48
|
+
model: null,
|
|
49
|
+
systemPrompt: null,
|
|
50
|
+
initialMessages: null,
|
|
51
|
+
createdAt: now,
|
|
52
|
+
updatedAt: now,
|
|
53
|
+
};
|
|
54
|
+
this.store.agentruns.set(id, row);
|
|
55
|
+
this.fire(row);
|
|
56
|
+
return row;
|
|
57
|
+
}
|
|
58
|
+
async update(id, patch) {
|
|
59
|
+
const row = this.store.agentruns.get(id);
|
|
60
|
+
if (!row) {
|
|
61
|
+
throw new Error(`Agentrun ${id} not found`);
|
|
62
|
+
}
|
|
63
|
+
const next = {
|
|
64
|
+
...row,
|
|
65
|
+
state: patch.state ?? row.state,
|
|
66
|
+
payload: patch.payload === undefined ? row.payload : patch.payload,
|
|
67
|
+
result: patch.result === undefined ? row.result : patch.result,
|
|
68
|
+
resultText: patch.resultText === undefined ? row.resultText : patch.resultText,
|
|
69
|
+
error: patch.error === undefined ? row.error : patch.error,
|
|
70
|
+
priority: patch.priority ?? row.priority,
|
|
71
|
+
model: patch.model === undefined ? row.model : patch.model,
|
|
72
|
+
systemPrompt: patch.systemPrompt === undefined ? row.systemPrompt : patch.systemPrompt,
|
|
73
|
+
initialMessages: patch.initialMessages === undefined ? row.initialMessages : patch.initialMessages,
|
|
74
|
+
calltype: patch.calltype === undefined ? row.calltype : patch.calltype,
|
|
75
|
+
updatedAt: new Date(),
|
|
76
|
+
};
|
|
77
|
+
this.store.agentruns.set(id, next);
|
|
78
|
+
this.fire(next);
|
|
79
|
+
}
|
|
80
|
+
async settle(id, toState, outcome = {}) {
|
|
81
|
+
const row = this.store.agentruns.get(id);
|
|
82
|
+
if (!row) {
|
|
83
|
+
throw new Error(`Agentrun ${id} not found`);
|
|
84
|
+
}
|
|
85
|
+
const next = {
|
|
86
|
+
...row,
|
|
87
|
+
state: toState,
|
|
88
|
+
result: outcome.result === undefined ? row.result : outcome.result,
|
|
89
|
+
resultText: outcome.resultText === undefined ? row.resultText : outcome.resultText,
|
|
90
|
+
error: outcome.error === undefined ? row.error : outcome.error,
|
|
91
|
+
updatedAt: new Date(),
|
|
92
|
+
};
|
|
93
|
+
this.store.agentruns.set(id, next);
|
|
94
|
+
// Mirror EVENT_TERMINAL_UPDATE_SQL: the event's terminal state
|
|
95
|
+
// mirrors the root agentrun's terminal state. Settling a
|
|
96
|
+
// non-root agentrun is a no-op for the event.
|
|
97
|
+
this.recomputeEventTerminal(row, toState);
|
|
98
|
+
this.fire(next);
|
|
99
|
+
}
|
|
100
|
+
async getById(id) {
|
|
101
|
+
return this.store.agentruns.get(id);
|
|
102
|
+
}
|
|
103
|
+
async listEligible() {
|
|
104
|
+
const now = Date.now();
|
|
105
|
+
return [...this.store.agentruns.values()]
|
|
106
|
+
.filter((r) => r.state === "pending" && (r.notBefore === null || r.notBefore.getTime() <= now))
|
|
107
|
+
.sort((a, b) => b.priority - a.priority || Number(a.id) - Number(b.id));
|
|
108
|
+
}
|
|
109
|
+
async areAllStartedChildrenSettled(parentId) {
|
|
110
|
+
for (const row of this.store.agentruns.values()) {
|
|
111
|
+
if (row.parentAgentrunId === parentId &&
|
|
112
|
+
row.calltype === "started" &&
|
|
113
|
+
row.state !== "done" &&
|
|
114
|
+
row.state !== "failed") {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
async listSince(since) {
|
|
121
|
+
return [...this.store.agentruns.values()]
|
|
122
|
+
.filter((r) => r.updatedAt.getTime() >= since.getTime())
|
|
123
|
+
.sort((a, b) => a.updatedAt.getTime() - b.updatedAt.getTime() || Number(a.id) - Number(b.id));
|
|
124
|
+
}
|
|
125
|
+
async listByEventId(eventId) {
|
|
126
|
+
return [...this.store.agentruns.values()]
|
|
127
|
+
.filter((r) => r.eventId === eventId)
|
|
128
|
+
.sort((a, b) => Number(a.id) - Number(b.id));
|
|
129
|
+
}
|
|
130
|
+
async postpone(id, runAfter, errorText) {
|
|
131
|
+
const row = this.store.agentruns.get(id);
|
|
132
|
+
if (!row) {
|
|
133
|
+
throw new Error(`Agentrun ${id} not found`);
|
|
134
|
+
}
|
|
135
|
+
const next = {
|
|
136
|
+
...row,
|
|
137
|
+
state: "pending",
|
|
138
|
+
retryCount: row.retryCount + 1,
|
|
139
|
+
notBefore: runAfter,
|
|
140
|
+
error: errorText,
|
|
141
|
+
updatedAt: new Date(),
|
|
142
|
+
};
|
|
143
|
+
this.store.agentruns.set(id, next);
|
|
144
|
+
this.fire(next);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Test-only: subscribe to row-change notifications. Replaces
|
|
148
|
+
* postgres NOTIFY for test code. Returns an unsubscriber.
|
|
149
|
+
*/
|
|
150
|
+
subscribe(listener) {
|
|
151
|
+
this.listeners.push(listener);
|
|
152
|
+
return () => {
|
|
153
|
+
const idx = this.listeners.indexOf(listener);
|
|
154
|
+
if (idx >= 0) {
|
|
155
|
+
this.listeners.splice(idx, 1);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
fire(row) {
|
|
160
|
+
for (const l of this.listeners) {
|
|
161
|
+
l(row);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
recomputeEventTerminal(settledRow, settledState) {
|
|
165
|
+
if (settledRow.parentAgentrunId !== null) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
const event = this.store.events.get(settledRow.eventId);
|
|
169
|
+
if (!event || event.state !== "running") {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const next = { ...event, state: settledState, updatedAt: new Date() };
|
|
173
|
+
this.store.events.set(settledRow.eventId, next);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AgentRunRow } from "../AgentRun.js";
|
|
2
|
+
import type { EventRow } from "../Event.js";
|
|
3
|
+
/**
|
|
4
|
+
* Shared in-memory backing store for {@link MockAgentRunBus} and
|
|
5
|
+
* {@link MockEventBus}. Lets `settle()` on the agentrun bus recompute
|
|
6
|
+
* the parent event's terminal state — the cross-table coupling the
|
|
7
|
+
* production schema enforces via {@link EVENT_TERMINAL_UPDATE_SQL} —
|
|
8
|
+
* without separate processes or a real postgres.
|
|
9
|
+
*
|
|
10
|
+
* Construct one store per test and pass it into both bus mocks.
|
|
11
|
+
* `nextEventId` / `nextAgentrunId` mirror postgres's `bigserial`,
|
|
12
|
+
* yielding deterministic ids `"1"`, `"2"`, … in the order rows are
|
|
13
|
+
* inserted.
|
|
14
|
+
*/
|
|
15
|
+
export declare class MockBusStore {
|
|
16
|
+
readonly events: Map<string, EventRow>;
|
|
17
|
+
readonly agentruns: Map<string, AgentRunRow>;
|
|
18
|
+
nextEventId: number;
|
|
19
|
+
nextAgentrunId: number;
|
|
20
|
+
/** All agentruns whose parent_agentrun_id matches `parentId`. */
|
|
21
|
+
childrenOf(parentId: string): AgentRunRow[];
|
|
22
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared in-memory backing store for {@link MockAgentRunBus} and
|
|
3
|
+
* {@link MockEventBus}. Lets `settle()` on the agentrun bus recompute
|
|
4
|
+
* the parent event's terminal state — the cross-table coupling the
|
|
5
|
+
* production schema enforces via {@link EVENT_TERMINAL_UPDATE_SQL} —
|
|
6
|
+
* without separate processes or a real postgres.
|
|
7
|
+
*
|
|
8
|
+
* Construct one store per test and pass it into both bus mocks.
|
|
9
|
+
* `nextEventId` / `nextAgentrunId` mirror postgres's `bigserial`,
|
|
10
|
+
* yielding deterministic ids `"1"`, `"2"`, … in the order rows are
|
|
11
|
+
* inserted.
|
|
12
|
+
*/
|
|
13
|
+
export class MockBusStore {
|
|
14
|
+
events = new Map();
|
|
15
|
+
agentruns = new Map();
|
|
16
|
+
nextEventId = 1;
|
|
17
|
+
nextAgentrunId = 1;
|
|
18
|
+
/** All agentruns whose parent_agentrun_id matches `parentId`. */
|
|
19
|
+
childrenOf(parentId) {
|
|
20
|
+
return [...this.agentruns.values()].filter((r) => r.parentAgentrunId === parentId);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { EventPatch, EventRow, NewEvent } from "../Event.js";
|
|
2
|
+
import type { MockBusStore } from "./MockBusStore.js";
|
|
3
|
+
/**
|
|
4
|
+
* In-memory {@link EventBus} replacement for unit tests. The Scheduler
|
|
5
|
+
* tests need only a tiny slice of the surface — `getById`, occasional
|
|
6
|
+
* `update`, and an `add` helper to seed events. Anything beyond that
|
|
7
|
+
* is not needed yet and intentionally absent.
|
|
8
|
+
*
|
|
9
|
+
* Stored in a shared {@link MockBusStore} so `MockAgentRunBus.settle`
|
|
10
|
+
* can recompute the parent event's terminal state on the same data.
|
|
11
|
+
*/
|
|
12
|
+
export declare class MockEventBus {
|
|
13
|
+
private readonly store;
|
|
14
|
+
constructor(store: MockBusStore);
|
|
15
|
+
/** No-op — kept for shape compatibility with the production bus. */
|
|
16
|
+
installSchema(): Promise<void>;
|
|
17
|
+
add(event: NewEvent): Promise<EventRow>;
|
|
18
|
+
getById(id: string): Promise<EventRow | undefined>;
|
|
19
|
+
update(id: string, patch: EventPatch): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory {@link EventBus} replacement for unit tests. The Scheduler
|
|
3
|
+
* tests need only a tiny slice of the surface — `getById`, occasional
|
|
4
|
+
* `update`, and an `add` helper to seed events. Anything beyond that
|
|
5
|
+
* is not needed yet and intentionally absent.
|
|
6
|
+
*
|
|
7
|
+
* Stored in a shared {@link MockBusStore} so `MockAgentRunBus.settle`
|
|
8
|
+
* can recompute the parent event's terminal state on the same data.
|
|
9
|
+
*/
|
|
10
|
+
export class MockEventBus {
|
|
11
|
+
store;
|
|
12
|
+
constructor(store) {
|
|
13
|
+
this.store = store;
|
|
14
|
+
}
|
|
15
|
+
/** No-op — kept for shape compatibility with the production bus. */
|
|
16
|
+
async installSchema() {
|
|
17
|
+
// intentionally empty
|
|
18
|
+
}
|
|
19
|
+
async add(event) {
|
|
20
|
+
const id = String(this.store.nextEventId++);
|
|
21
|
+
const now = new Date();
|
|
22
|
+
const row = {
|
|
23
|
+
id,
|
|
24
|
+
topic: event.topic,
|
|
25
|
+
priority: event.priority ?? 50,
|
|
26
|
+
state: "pending",
|
|
27
|
+
payload: event.payload ?? {},
|
|
28
|
+
idempotencyKey: event.idempotencyKey ?? null,
|
|
29
|
+
isChat: event.isChat === true,
|
|
30
|
+
preferredChatChannelId: event.preferredChatChannelId ?? null,
|
|
31
|
+
prompt: event.prompt ?? null,
|
|
32
|
+
startHandler: event.startHandler ?? null,
|
|
33
|
+
privileged: event.privileged === true,
|
|
34
|
+
outputChatOnFailure: event.outputChatOnFailure === true,
|
|
35
|
+
createdAt: now,
|
|
36
|
+
updatedAt: now,
|
|
37
|
+
};
|
|
38
|
+
this.store.events.set(id, row);
|
|
39
|
+
return row;
|
|
40
|
+
}
|
|
41
|
+
async getById(id) {
|
|
42
|
+
return this.store.events.get(id);
|
|
43
|
+
}
|
|
44
|
+
async update(id, patch) {
|
|
45
|
+
const row = this.store.events.get(id);
|
|
46
|
+
if (!row) {
|
|
47
|
+
throw new Error(`Event ${id} not found`);
|
|
48
|
+
}
|
|
49
|
+
const next = {
|
|
50
|
+
...row,
|
|
51
|
+
state: patch.state ?? row.state,
|
|
52
|
+
payload: patch.payload === undefined ? row.payload : patch.payload,
|
|
53
|
+
updatedAt: new Date(),
|
|
54
|
+
};
|
|
55
|
+
this.store.events.set(id, next);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { Logger, LogLevel } from "../logging/Logger.js";
|
|
2
|
+
/**
|
|
3
|
+
* One captured log call. `obj` is whatever the caller passed as the
|
|
4
|
+
* structured payload (an object or `undefined` for plain-message
|
|
5
|
+
* variants); `msg` is the rendered message string.
|
|
6
|
+
*/
|
|
7
|
+
export interface MockLogEntry {
|
|
8
|
+
readonly level: LogLevel;
|
|
9
|
+
readonly msg: string;
|
|
10
|
+
readonly obj: object | undefined;
|
|
11
|
+
/**
|
|
12
|
+
* Accumulated child-logger bindings. A root logger has no bindings;
|
|
13
|
+
* a child via `.child({ component: 'x' })` adds `{ component: 'x' }`.
|
|
14
|
+
*/
|
|
15
|
+
readonly bindings: ReadonlyArray<Record<string, unknown>>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* In-memory {@link Logger} that records every call into a flat
|
|
19
|
+
* {@link entries} array, suitable for assertions in unit tests.
|
|
20
|
+
*
|
|
21
|
+
* Children share the same backing entries array and accumulate
|
|
22
|
+
* bindings — so `logger.child({ a: 1 }).child({ b: 2 }).info('hi')`
|
|
23
|
+
* records `{ bindings: [{a:1}, {b:2}], msg: 'hi', ... }`.
|
|
24
|
+
*
|
|
25
|
+
* Lives in `shared/` because every package that depends on
|
|
26
|
+
* `@getfamiliar/shared` already has the `Logger` interface visible,
|
|
27
|
+
* and the same mock works for host- and container-side tests.
|
|
28
|
+
*/
|
|
29
|
+
export declare class MockLogger implements Logger {
|
|
30
|
+
/** Every call observed by this logger (and its children). */
|
|
31
|
+
readonly entries: MockLogEntry[];
|
|
32
|
+
private readonly bindings;
|
|
33
|
+
constructor(bindings?: ReadonlyArray<Record<string, unknown>>);
|
|
34
|
+
debug(obj: object, msg?: string): void;
|
|
35
|
+
debug(msg: string): void;
|
|
36
|
+
info(obj: object, msg?: string): void;
|
|
37
|
+
info(msg: string): void;
|
|
38
|
+
warn(obj: object, msg?: string): void;
|
|
39
|
+
warn(msg: string): void;
|
|
40
|
+
error(obj: object, msg?: string): void;
|
|
41
|
+
error(msg: string): void;
|
|
42
|
+
child(bindings: Record<string, unknown>): Logger;
|
|
43
|
+
/**
|
|
44
|
+
* Convenience query: does an entry at `level` with a message
|
|
45
|
+
* matching `match` exist? `match` is either a substring or a
|
|
46
|
+
* RegExp.
|
|
47
|
+
*/
|
|
48
|
+
hasEntry(level: LogLevel, match: string | RegExp): boolean;
|
|
49
|
+
/** Drop all recorded entries. Useful between assertions. */
|
|
50
|
+
clear(): void;
|
|
51
|
+
private record;
|
|
52
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory {@link Logger} that records every call into a flat
|
|
3
|
+
* {@link entries} array, suitable for assertions in unit tests.
|
|
4
|
+
*
|
|
5
|
+
* Children share the same backing entries array and accumulate
|
|
6
|
+
* bindings — so `logger.child({ a: 1 }).child({ b: 2 }).info('hi')`
|
|
7
|
+
* records `{ bindings: [{a:1}, {b:2}], msg: 'hi', ... }`.
|
|
8
|
+
*
|
|
9
|
+
* Lives in `shared/` because every package that depends on
|
|
10
|
+
* `@getfamiliar/shared` already has the `Logger` interface visible,
|
|
11
|
+
* and the same mock works for host- and container-side tests.
|
|
12
|
+
*/
|
|
13
|
+
export class MockLogger {
|
|
14
|
+
/** Every call observed by this logger (and its children). */
|
|
15
|
+
entries = [];
|
|
16
|
+
bindings;
|
|
17
|
+
constructor(bindings = []) {
|
|
18
|
+
this.bindings = bindings;
|
|
19
|
+
}
|
|
20
|
+
debug(a, b) {
|
|
21
|
+
this.record("debug", a, b);
|
|
22
|
+
}
|
|
23
|
+
info(a, b) {
|
|
24
|
+
this.record("info", a, b);
|
|
25
|
+
}
|
|
26
|
+
warn(a, b) {
|
|
27
|
+
this.record("warn", a, b);
|
|
28
|
+
}
|
|
29
|
+
error(a, b) {
|
|
30
|
+
this.record("error", a, b);
|
|
31
|
+
}
|
|
32
|
+
child(bindings) {
|
|
33
|
+
const child = new MockLogger([...this.bindings, bindings]);
|
|
34
|
+
// Share the entries array so parent observations see child
|
|
35
|
+
// writes too — matches `pino.child` semantics from the test's
|
|
36
|
+
// POV.
|
|
37
|
+
child.entries = this.entries;
|
|
38
|
+
return child;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Convenience query: does an entry at `level` with a message
|
|
42
|
+
* matching `match` exist? `match` is either a substring or a
|
|
43
|
+
* RegExp.
|
|
44
|
+
*/
|
|
45
|
+
hasEntry(level, match) {
|
|
46
|
+
return this.entries.some((e) => e.level === level &&
|
|
47
|
+
(typeof match === "string" ? e.msg.includes(match) : match.test(e.msg)));
|
|
48
|
+
}
|
|
49
|
+
/** Drop all recorded entries. Useful between assertions. */
|
|
50
|
+
clear() {
|
|
51
|
+
this.entries.length = 0;
|
|
52
|
+
}
|
|
53
|
+
record(level, a, b) {
|
|
54
|
+
if (typeof a === "string") {
|
|
55
|
+
this.entries.push({ level, msg: a, obj: undefined, bindings: this.bindings });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
this.entries.push({
|
|
59
|
+
level,
|
|
60
|
+
msg: b ?? "",
|
|
61
|
+
obj: a,
|
|
62
|
+
bindings: this.bindings,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@getfamiliar/shared",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.2",
|
|
5
|
+
"description": "Familiar — shared types and bus-state client between host and container.",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"main": "build/index.js",
|
|
10
|
+
"types": "build/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./build/index.d.ts",
|
|
14
|
+
"default": "./build/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./testing": {
|
|
17
|
+
"types": "./build/testing/index.d.ts",
|
|
18
|
+
"default": "./build/testing/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"build"
|
|
23
|
+
],
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=24"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "tsc",
|
|
29
|
+
"format": "biome format --write --config-path .. src/",
|
|
30
|
+
"format:check": "biome format --config-path .. src/",
|
|
31
|
+
"lint": "biome lint --config-path .. src/",
|
|
32
|
+
"check": "biome check --config-path .. src/",
|
|
33
|
+
"test": "tsx --test 'src/**/*.test.ts'"
|
|
34
|
+
},
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
38
|
+
"chalk": "^5.4.1",
|
|
39
|
+
"citty": "^0.1.6",
|
|
40
|
+
"cli-highlight": "^2.1.11",
|
|
41
|
+
"cli-table3": "^0.6.5",
|
|
42
|
+
"croner": "^10.0.1",
|
|
43
|
+
"friendly-node-cron": "^1.5.0",
|
|
44
|
+
"luxon": "^3.7.2",
|
|
45
|
+
"marked": "^15.0.12",
|
|
46
|
+
"pg": "^8.13.1",
|
|
47
|
+
"pino": "^9.14.0",
|
|
48
|
+
"supports-hyperlinks": "^3.2.0",
|
|
49
|
+
"wrap-ansi": "^9.0.2"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@biomejs/biome": "^2.4.12",
|
|
53
|
+
"@types/luxon": "^3.7.1",
|
|
54
|
+
"@types/node": "^25.6.0",
|
|
55
|
+
"@types/pg": "^8.11.10",
|
|
56
|
+
"typescript": "^6.0.3"
|
|
57
|
+
},
|
|
58
|
+
"repository": {
|
|
59
|
+
"type": "git",
|
|
60
|
+
"url": "git+https://github.com/getfamiliar/familiar.git",
|
|
61
|
+
"directory": "shared"
|
|
62
|
+
}
|
|
63
|
+
}
|