@sundaeswap/sprinkles 0.6.0 → 0.7.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/dist/cjs/Sprinkle/__tests__/action-integration.test.js +590 -0
- package/dist/cjs/Sprinkle/__tests__/action-integration.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/action-registry.test.js +193 -0
- package/dist/cjs/Sprinkle/__tests__/action-registry.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/action-runner.test.js +304 -0
- package/dist/cjs/Sprinkle/__tests__/action-runner.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/builtin-actions.test.js +1110 -0
- package/dist/cjs/Sprinkle/__tests__/builtin-actions.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/cli-adapter.test.js +722 -0
- package/dist/cjs/Sprinkle/__tests__/cli-adapter.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js +138 -0
- package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -1
- package/dist/cjs/Sprinkle/__tests__/mcp-adapter.test.js +713 -0
- package/dist/cjs/Sprinkle/__tests__/mcp-adapter.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/tui-helpers.test.js +334 -0
- package/dist/cjs/Sprinkle/__tests__/tui-helpers.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/wallet-transaction-actions.test.js +749 -0
- package/dist/cjs/Sprinkle/__tests__/wallet-transaction-actions.test.js.map +1 -0
- package/dist/cjs/Sprinkle/actions/builtin/blaze-helper.js +61 -0
- package/dist/cjs/Sprinkle/actions/builtin/blaze-helper.js.map +1 -0
- package/dist/cjs/Sprinkle/actions/builtin/index.js +117 -0
- package/dist/cjs/Sprinkle/actions/builtin/index.js.map +1 -0
- package/dist/cjs/Sprinkle/actions/builtin/profile-actions.js +202 -0
- package/dist/cjs/Sprinkle/actions/builtin/profile-actions.js.map +1 -0
- package/dist/cjs/Sprinkle/actions/builtin/settings-actions.js +87 -0
- package/dist/cjs/Sprinkle/actions/builtin/settings-actions.js.map +1 -0
- package/dist/cjs/Sprinkle/actions/builtin/transaction-actions.js +345 -0
- package/dist/cjs/Sprinkle/actions/builtin/transaction-actions.js.map +1 -0
- package/dist/cjs/Sprinkle/actions/builtin/wallet-actions.js +212 -0
- package/dist/cjs/Sprinkle/actions/builtin/wallet-actions.js.map +1 -0
- package/dist/cjs/Sprinkle/actions/cli-adapter.js +372 -0
- package/dist/cjs/Sprinkle/actions/cli-adapter.js.map +1 -0
- package/dist/cjs/Sprinkle/actions/index.js +127 -0
- package/dist/cjs/Sprinkle/actions/index.js.map +1 -0
- package/dist/cjs/Sprinkle/actions/mcp-adapter.js +415 -0
- package/dist/cjs/Sprinkle/actions/mcp-adapter.js.map +1 -0
- package/dist/cjs/Sprinkle/actions/registry.js +92 -0
- package/dist/cjs/Sprinkle/actions/registry.js.map +1 -0
- package/dist/cjs/Sprinkle/actions/runner.js +190 -0
- package/dist/cjs/Sprinkle/actions/runner.js.map +1 -0
- package/dist/cjs/Sprinkle/actions/tui-helpers.js +96 -0
- package/dist/cjs/Sprinkle/actions/tui-helpers.js.map +1 -0
- package/dist/cjs/Sprinkle/actions/types.js +68 -0
- package/dist/cjs/Sprinkle/actions/types.js.map +1 -0
- package/dist/cjs/Sprinkle/index.js +451 -4
- package/dist/cjs/Sprinkle/index.js.map +1 -1
- package/dist/cjs/Sprinkle/prompts.js +12 -7
- package/dist/cjs/Sprinkle/prompts.js.map +1 -1
- package/dist/cjs/Sprinkle/type-guards.js +7 -1
- package/dist/cjs/Sprinkle/type-guards.js.map +1 -1
- package/dist/esm/Sprinkle/__tests__/action-integration.test.js +588 -0
- package/dist/esm/Sprinkle/__tests__/action-integration.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/action-registry.test.js +192 -0
- package/dist/esm/Sprinkle/__tests__/action-registry.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/action-runner.test.js +302 -0
- package/dist/esm/Sprinkle/__tests__/action-runner.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/builtin-actions.test.js +1107 -0
- package/dist/esm/Sprinkle/__tests__/builtin-actions.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/cli-adapter.test.js +720 -0
- package/dist/esm/Sprinkle/__tests__/cli-adapter.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js +138 -0
- package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -1
- package/dist/esm/Sprinkle/__tests__/mcp-adapter.test.js +712 -0
- package/dist/esm/Sprinkle/__tests__/mcp-adapter.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/tui-helpers.test.js +332 -0
- package/dist/esm/Sprinkle/__tests__/tui-helpers.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/wallet-transaction-actions.test.js +747 -0
- package/dist/esm/Sprinkle/__tests__/wallet-transaction-actions.test.js.map +1 -0
- package/dist/esm/Sprinkle/actions/builtin/blaze-helper.js +55 -0
- package/dist/esm/Sprinkle/actions/builtin/blaze-helper.js.map +1 -0
- package/dist/esm/Sprinkle/actions/builtin/index.js +32 -0
- package/dist/esm/Sprinkle/actions/builtin/index.js.map +1 -0
- package/dist/esm/Sprinkle/actions/builtin/profile-actions.js +197 -0
- package/dist/esm/Sprinkle/actions/builtin/profile-actions.js.map +1 -0
- package/dist/esm/Sprinkle/actions/builtin/settings-actions.js +81 -0
- package/dist/esm/Sprinkle/actions/builtin/settings-actions.js.map +1 -0
- package/dist/esm/Sprinkle/actions/builtin/transaction-actions.js +340 -0
- package/dist/esm/Sprinkle/actions/builtin/transaction-actions.js.map +1 -0
- package/dist/esm/Sprinkle/actions/builtin/wallet-actions.js +207 -0
- package/dist/esm/Sprinkle/actions/builtin/wallet-actions.js.map +1 -0
- package/dist/esm/Sprinkle/actions/cli-adapter.js +361 -0
- package/dist/esm/Sprinkle/actions/cli-adapter.js.map +1 -0
- package/dist/esm/Sprinkle/actions/index.js +12 -0
- package/dist/esm/Sprinkle/actions/index.js.map +1 -0
- package/dist/esm/Sprinkle/actions/mcp-adapter.js +407 -0
- package/dist/esm/Sprinkle/actions/mcp-adapter.js.map +1 -0
- package/dist/esm/Sprinkle/actions/registry.js +85 -0
- package/dist/esm/Sprinkle/actions/registry.js.map +1 -0
- package/dist/esm/Sprinkle/actions/runner.js +182 -0
- package/dist/esm/Sprinkle/actions/runner.js.map +1 -0
- package/dist/esm/Sprinkle/actions/tui-helpers.js +91 -0
- package/dist/esm/Sprinkle/actions/tui-helpers.js.map +1 -0
- package/dist/esm/Sprinkle/actions/types.js +61 -0
- package/dist/esm/Sprinkle/actions/types.js.map +1 -0
- package/dist/esm/Sprinkle/index.js +299 -4
- package/dist/esm/Sprinkle/index.js.map +1 -1
- package/dist/esm/Sprinkle/prompts.js +12 -7
- package/dist/esm/Sprinkle/prompts.js.map +1 -1
- package/dist/esm/Sprinkle/type-guards.js +3 -0
- package/dist/esm/Sprinkle/type-guards.js.map +1 -1
- package/dist/types/Sprinkle/actions/builtin/blaze-helper.d.ts +39 -0
- package/dist/types/Sprinkle/actions/builtin/blaze-helper.d.ts.map +1 -0
- package/dist/types/Sprinkle/actions/builtin/index.d.ts +26 -0
- package/dist/types/Sprinkle/actions/builtin/index.d.ts.map +1 -0
- package/dist/types/Sprinkle/actions/builtin/profile-actions.d.ts +55 -0
- package/dist/types/Sprinkle/actions/builtin/profile-actions.d.ts.map +1 -0
- package/dist/types/Sprinkle/actions/builtin/settings-actions.d.ts +32 -0
- package/dist/types/Sprinkle/actions/builtin/settings-actions.d.ts.map +1 -0
- package/dist/types/Sprinkle/actions/builtin/transaction-actions.d.ts +70 -0
- package/dist/types/Sprinkle/actions/builtin/transaction-actions.d.ts.map +1 -0
- package/dist/types/Sprinkle/actions/builtin/wallet-actions.d.ts +50 -0
- package/dist/types/Sprinkle/actions/builtin/wallet-actions.d.ts.map +1 -0
- package/dist/types/Sprinkle/actions/cli-adapter.d.ts +104 -0
- package/dist/types/Sprinkle/actions/cli-adapter.d.ts.map +1 -0
- package/dist/types/Sprinkle/actions/index.d.ts +12 -0
- package/dist/types/Sprinkle/actions/index.d.ts.map +1 -0
- package/dist/types/Sprinkle/actions/mcp-adapter.d.ts +92 -0
- package/dist/types/Sprinkle/actions/mcp-adapter.d.ts.map +1 -0
- package/dist/types/Sprinkle/actions/registry.d.ts +42 -0
- package/dist/types/Sprinkle/actions/registry.d.ts.map +1 -0
- package/dist/types/Sprinkle/actions/runner.d.ts +45 -0
- package/dist/types/Sprinkle/actions/runner.d.ts.map +1 -0
- package/dist/types/Sprinkle/actions/tui-helpers.d.ts +53 -0
- package/dist/types/Sprinkle/actions/tui-helpers.d.ts.map +1 -0
- package/dist/types/Sprinkle/actions/types.d.ts +76 -0
- package/dist/types/Sprinkle/actions/types.d.ts.map +1 -0
- package/dist/types/Sprinkle/index.d.ts +81 -1
- package/dist/types/Sprinkle/index.d.ts.map +1 -1
- package/dist/types/Sprinkle/prompts.d.ts.map +1 -1
- package/dist/types/Sprinkle/type-guards.d.ts +4 -1
- package/dist/types/Sprinkle/type-guards.d.ts.map +1 -1
- package/dist/types/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +9 -2
- package/src/Sprinkle/__tests__/action-integration.test.ts +558 -0
- package/src/Sprinkle/__tests__/action-registry.test.ts +187 -0
- package/src/Sprinkle/__tests__/action-runner.test.ts +324 -0
- package/src/Sprinkle/__tests__/builtin-actions.test.ts +1022 -0
- package/src/Sprinkle/__tests__/cli-adapter.test.ts +715 -0
- package/src/Sprinkle/__tests__/fill-in-struct.test.ts +144 -0
- package/src/Sprinkle/__tests__/mcp-adapter.test.ts +718 -0
- package/src/Sprinkle/__tests__/tui-helpers.test.ts +325 -0
- package/src/Sprinkle/__tests__/wallet-transaction-actions.test.ts +695 -0
- package/src/Sprinkle/actions/builtin/blaze-helper.ts +89 -0
- package/src/Sprinkle/actions/builtin/index.ts +86 -0
- package/src/Sprinkle/actions/builtin/profile-actions.ts +229 -0
- package/src/Sprinkle/actions/builtin/settings-actions.ts +99 -0
- package/src/Sprinkle/actions/builtin/transaction-actions.ts +381 -0
- package/src/Sprinkle/actions/builtin/wallet-actions.ts +233 -0
- package/src/Sprinkle/actions/cli-adapter.ts +430 -0
- package/src/Sprinkle/actions/index.ts +32 -0
- package/src/Sprinkle/actions/mcp-adapter.ts +463 -0
- package/src/Sprinkle/actions/registry.ts +97 -0
- package/src/Sprinkle/actions/runner.ts +200 -0
- package/src/Sprinkle/actions/tui-helpers.ts +114 -0
- package/src/Sprinkle/actions/types.ts +91 -0
- package/src/Sprinkle/index.ts +395 -3
- package/src/Sprinkle/prompts.ts +118 -72
- package/src/Sprinkle/type-guards.ts +9 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { describe, expect, test, mock, beforeEach } from "bun:test";
|
|
2
|
+
import { Type } from "@sinclair/typebox";
|
|
3
|
+
import { promptAndExecute } from "../actions/tui-helpers.js";
|
|
4
|
+
import { UserCancelledError } from "../types.js";
|
|
5
|
+
import type { IAction } from "../actions/types.js";
|
|
6
|
+
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Fixtures
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
// Minimal action used throughout these tests
|
|
12
|
+
const greetAction: IAction<{ name: string }, { greeting: string }, any> = {
|
|
13
|
+
name: "greet",
|
|
14
|
+
description: "Greets someone",
|
|
15
|
+
inputSchema: Type.Object({ name: Type.String() }),
|
|
16
|
+
outputSchema: Type.Object({ greeting: Type.String() }),
|
|
17
|
+
execute: async (input: { name: string }) => ({
|
|
18
|
+
greeting: `Hello, ${input.name}!`,
|
|
19
|
+
}),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Build a minimal Sprinkle-shaped mock. promptAndExecute only calls:
|
|
23
|
+
// - sprinkle.FillInStruct(schema, defaults)
|
|
24
|
+
// - sprinkle.settings (read once for context)
|
|
25
|
+
// so we only need those two members.
|
|
26
|
+
function makeMockSprinkle(fillInStructImpl: (schema: any, defaults?: any) => Promise<any>) {
|
|
27
|
+
return {
|
|
28
|
+
FillInStruct: mock(fillInStructImpl),
|
|
29
|
+
settings: { apiUrl: "https://example.com" },
|
|
30
|
+
} as any;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
// describe: promptAndExecute – success path
|
|
35
|
+
// ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
describe("promptAndExecute – success path", () => {
|
|
38
|
+
test("returns success result when FillInStruct resolves", async () => {
|
|
39
|
+
const sprinkle = makeMockSprinkle(async () => ({ name: "Alice" }));
|
|
40
|
+
|
|
41
|
+
const result = await promptAndExecute(sprinkle, greetAction);
|
|
42
|
+
|
|
43
|
+
expect(result.success).toBe(true);
|
|
44
|
+
if (result.success) {
|
|
45
|
+
expect(result.data).toEqual({ greeting: "Hello, Alice!" });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test("passes the schema from the action to FillInStruct", async () => {
|
|
50
|
+
let capturedSchema: any;
|
|
51
|
+
const sprinkle = makeMockSprinkle(async (schema: any) => {
|
|
52
|
+
capturedSchema = schema;
|
|
53
|
+
return { name: "Bob" };
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
await promptAndExecute(sprinkle, greetAction);
|
|
57
|
+
|
|
58
|
+
expect(capturedSchema).toBe(greetAction.inputSchema);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("passes action execute function the validated input", async () => {
|
|
62
|
+
let capturedInput: any;
|
|
63
|
+
const action = {
|
|
64
|
+
...greetAction,
|
|
65
|
+
execute: async (input: { name: string }) => {
|
|
66
|
+
capturedInput = input;
|
|
67
|
+
return { greeting: `Hi, ${input.name}!` };
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
const sprinkle = makeMockSprinkle(async () => ({ name: "Charlie" }));
|
|
71
|
+
|
|
72
|
+
await promptAndExecute(sprinkle, action);
|
|
73
|
+
|
|
74
|
+
expect(capturedInput).toEqual({ name: "Charlie" });
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("provides the sprinkle instance via context to execute", async () => {
|
|
78
|
+
let capturedContext: any;
|
|
79
|
+
const action = {
|
|
80
|
+
...greetAction,
|
|
81
|
+
execute: async (_input: any, ctx: any) => {
|
|
82
|
+
capturedContext = ctx;
|
|
83
|
+
return { greeting: "hi" };
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
const sprinkle = makeMockSprinkle(async () => ({ name: "Dan" }));
|
|
87
|
+
|
|
88
|
+
await promptAndExecute(sprinkle, action);
|
|
89
|
+
|
|
90
|
+
// The context's sprinkle should refer to the same mock instance
|
|
91
|
+
expect(capturedContext).toBeDefined();
|
|
92
|
+
expect(capturedContext.sprinkle).toBe(sprinkle);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("provides sprinkle.settings via context to execute", async () => {
|
|
96
|
+
let capturedSettings: any;
|
|
97
|
+
const action = {
|
|
98
|
+
...greetAction,
|
|
99
|
+
execute: async (_input: any, ctx: any) => {
|
|
100
|
+
capturedSettings = ctx.settings;
|
|
101
|
+
return { greeting: "hi" };
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
const sprinkle = makeMockSprinkle(async () => ({ name: "Eve" }));
|
|
105
|
+
|
|
106
|
+
await promptAndExecute(sprinkle, action);
|
|
107
|
+
|
|
108
|
+
expect(capturedSettings).toEqual({ apiUrl: "https://example.com" });
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("returns failure when execute throws an Error", async () => {
|
|
112
|
+
const action = {
|
|
113
|
+
...greetAction,
|
|
114
|
+
execute: async () => {
|
|
115
|
+
throw new Error("execute failed");
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
const sprinkle = makeMockSprinkle(async () => ({ name: "Fail" }));
|
|
119
|
+
|
|
120
|
+
const result = await promptAndExecute(sprinkle, action);
|
|
121
|
+
|
|
122
|
+
expect(result.success).toBe(false);
|
|
123
|
+
if (!result.success) {
|
|
124
|
+
expect(result.error.code).toBe("EXECUTION_ERROR");
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
// describe: promptAndExecute – defaults parameter
|
|
131
|
+
// ---------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
describe("promptAndExecute – defaults parameter", () => {
|
|
134
|
+
test("passes defaults to FillInStruct when provided", async () => {
|
|
135
|
+
let capturedDefaults: any;
|
|
136
|
+
const sprinkle = makeMockSprinkle(async (_schema: any, defaults: any) => {
|
|
137
|
+
capturedDefaults = defaults;
|
|
138
|
+
return { name: "Frank" };
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await promptAndExecute(sprinkle, greetAction, { name: "Frank" });
|
|
142
|
+
|
|
143
|
+
expect(capturedDefaults).toEqual({ name: "Frank" });
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("passes undefined to FillInStruct when defaults are omitted", async () => {
|
|
147
|
+
let capturedDefaults: any = "sentinel";
|
|
148
|
+
const sprinkle = makeMockSprinkle(async (_schema: any, defaults: any) => {
|
|
149
|
+
capturedDefaults = defaults;
|
|
150
|
+
return { name: "Grace" };
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await promptAndExecute(sprinkle, greetAction);
|
|
154
|
+
|
|
155
|
+
expect(capturedDefaults).toBeUndefined();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test("passes partial defaults to FillInStruct", async () => {
|
|
159
|
+
// Action with multiple fields so we can supply only some defaults
|
|
160
|
+
const multiAction: IAction<{ name: string; count: number }, { result: string }, any> = {
|
|
161
|
+
name: "multi",
|
|
162
|
+
description: "Takes multiple fields",
|
|
163
|
+
inputSchema: Type.Object({ name: Type.String(), count: Type.Number() }),
|
|
164
|
+
outputSchema: Type.Object({ result: Type.String() }),
|
|
165
|
+
execute: async (input) => ({ result: `${input.name}:${input.count}` }),
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
let capturedDefaults: any;
|
|
169
|
+
const sprinkle = makeMockSprinkle(async (_schema: any, defaults: any) => {
|
|
170
|
+
capturedDefaults = defaults;
|
|
171
|
+
return { name: "Henry", count: 42 };
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
await promptAndExecute(sprinkle, multiAction, { name: "Henry" });
|
|
175
|
+
|
|
176
|
+
expect(capturedDefaults).toEqual({ name: "Henry" });
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// describe: promptAndExecute – UserCancelledError handling
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
|
|
184
|
+
describe("promptAndExecute – UserCancelledError handling", () => {
|
|
185
|
+
test("returns IActionFailure with code USER_CANCELLED when FillInStruct throws UserCancelledError", async () => {
|
|
186
|
+
const sprinkle = makeMockSprinkle(async () => {
|
|
187
|
+
throw new UserCancelledError("cancelled");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const result = await promptAndExecute(sprinkle, greetAction);
|
|
191
|
+
|
|
192
|
+
expect(result.success).toBe(false);
|
|
193
|
+
if (!result.success) {
|
|
194
|
+
expect(result.error.code).toBe("USER_CANCELLED");
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
test("does not throw when FillInStruct throws UserCancelledError", async () => {
|
|
199
|
+
const sprinkle = makeMockSprinkle(async () => {
|
|
200
|
+
throw new UserCancelledError();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Should resolve (not reject)
|
|
204
|
+
await expect(promptAndExecute(sprinkle, greetAction)).resolves.toBeDefined();
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
test("USER_CANCELLED failure has a descriptive message", async () => {
|
|
208
|
+
const sprinkle = makeMockSprinkle(async () => {
|
|
209
|
+
throw new UserCancelledError("user pressed escape");
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const result = await promptAndExecute(sprinkle, greetAction);
|
|
213
|
+
|
|
214
|
+
expect(result.success).toBe(false);
|
|
215
|
+
if (!result.success) {
|
|
216
|
+
expect(typeof result.error.message).toBe("string");
|
|
217
|
+
expect(result.error.message.length).toBeGreaterThan(0);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("USER_CANCELLED failure carries the original UserCancelledError as details", async () => {
|
|
222
|
+
const cancelled = new UserCancelledError("esc pressed");
|
|
223
|
+
const sprinkle = makeMockSprinkle(async () => {
|
|
224
|
+
throw cancelled;
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const result = await promptAndExecute(sprinkle, greetAction);
|
|
228
|
+
|
|
229
|
+
expect(result.success).toBe(false);
|
|
230
|
+
if (!result.success) {
|
|
231
|
+
expect(result.error.details).toBe(cancelled);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("does not call execute when FillInStruct throws UserCancelledError", async () => {
|
|
236
|
+
let executeCalled = false;
|
|
237
|
+
const action = {
|
|
238
|
+
...greetAction,
|
|
239
|
+
execute: async (_input: any) => {
|
|
240
|
+
executeCalled = true;
|
|
241
|
+
return { greeting: "hi" };
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
const sprinkle = makeMockSprinkle(async () => {
|
|
245
|
+
throw new UserCancelledError();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
await promptAndExecute(sprinkle, action);
|
|
249
|
+
|
|
250
|
+
expect(executeCalled).toBe(false);
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// ---------------------------------------------------------------------------
|
|
255
|
+
// describe: promptAndExecute – unexpected FillInStruct errors
|
|
256
|
+
// ---------------------------------------------------------------------------
|
|
257
|
+
|
|
258
|
+
describe("promptAndExecute – unexpected FillInStruct errors", () => {
|
|
259
|
+
test("returns IActionFailure with code PROMPT_ERROR when FillInStruct throws a non-UserCancelledError", async () => {
|
|
260
|
+
const sprinkle = makeMockSprinkle(async () => {
|
|
261
|
+
throw new Error("TTY not available");
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const result = await promptAndExecute(sprinkle, greetAction);
|
|
265
|
+
|
|
266
|
+
expect(result.success).toBe(false);
|
|
267
|
+
if (!result.success) {
|
|
268
|
+
expect(result.error.code).toBe("PROMPT_ERROR");
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
test("PROMPT_ERROR message includes the original error message", async () => {
|
|
273
|
+
const sprinkle = makeMockSprinkle(async () => {
|
|
274
|
+
throw new Error("underlying prompt failure");
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const result = await promptAndExecute(sprinkle, greetAction);
|
|
278
|
+
|
|
279
|
+
expect(result.success).toBe(false);
|
|
280
|
+
if (!result.success) {
|
|
281
|
+
expect(result.error.message).toContain("underlying prompt failure");
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("does not throw when FillInStruct throws an unexpected error", async () => {
|
|
286
|
+
const sprinkle = makeMockSprinkle(async () => {
|
|
287
|
+
throw new Error("unexpected");
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
await expect(promptAndExecute(sprinkle, greetAction)).resolves.toBeDefined();
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test("PROMPT_ERROR result includes non-Error thrown values in message", async () => {
|
|
294
|
+
const sprinkle = makeMockSprinkle(async () => {
|
|
295
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
296
|
+
throw "some string error";
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
const result = await promptAndExecute(sprinkle, greetAction);
|
|
300
|
+
|
|
301
|
+
expect(result.success).toBe(false);
|
|
302
|
+
if (!result.success) {
|
|
303
|
+
expect(result.error.code).toBe("PROMPT_ERROR");
|
|
304
|
+
expect(result.error.message).toContain("some string error");
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
// describe: promptAndExecute – input validation
|
|
311
|
+
// ---------------------------------------------------------------------------
|
|
312
|
+
|
|
313
|
+
describe("promptAndExecute – input validation", () => {
|
|
314
|
+
test("returns VALIDATION_ERROR when FillInStruct returns data that fails schema validation", async () => {
|
|
315
|
+
// FillInStruct returns a value that does not conform to the inputSchema
|
|
316
|
+
const sprinkle = makeMockSprinkle(async () => ({ name: 42 })); // name should be a string
|
|
317
|
+
|
|
318
|
+
const result = await promptAndExecute(sprinkle, greetAction);
|
|
319
|
+
|
|
320
|
+
expect(result.success).toBe(false);
|
|
321
|
+
if (!result.success) {
|
|
322
|
+
expect(result.error.code).toBe("VALIDATION_ERROR");
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
});
|