@sundaeswap/sprinkles 0.1.1

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.
Files changed (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +260 -0
  3. package/dist/cjs/Sprinkle/__tests__/bigint-reviver.test.js +40 -0
  4. package/dist/cjs/Sprinkle/__tests__/bigint-reviver.test.js.map +1 -0
  5. package/dist/cjs/Sprinkle/__tests__/encryption.test.js +267 -0
  6. package/dist/cjs/Sprinkle/__tests__/encryption.test.js.map +1 -0
  7. package/dist/cjs/Sprinkle/__tests__/enhancements.test.js +147 -0
  8. package/dist/cjs/Sprinkle/__tests__/enhancements.test.js.map +1 -0
  9. package/dist/cjs/Sprinkle/__tests__/extract-message.test.js +60 -0
  10. package/dist/cjs/Sprinkle/__tests__/extract-message.test.js.map +1 -0
  11. package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js +131 -0
  12. package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -0
  13. package/dist/cjs/Sprinkle/__tests__/schemas.test.js +184 -0
  14. package/dist/cjs/Sprinkle/__tests__/schemas.test.js.map +1 -0
  15. package/dist/cjs/Sprinkle/__tests__/settings-persistence.test.js +199 -0
  16. package/dist/cjs/Sprinkle/__tests__/settings-persistence.test.js.map +1 -0
  17. package/dist/cjs/Sprinkle/__tests__/show-menu.test.js +108 -0
  18. package/dist/cjs/Sprinkle/__tests__/show-menu.test.js.map +1 -0
  19. package/dist/cjs/Sprinkle/__tests__/test-helpers.js +16 -0
  20. package/dist/cjs/Sprinkle/__tests__/test-helpers.js.map +1 -0
  21. package/dist/cjs/Sprinkle/__tests__/tx-dialog.test.js +271 -0
  22. package/dist/cjs/Sprinkle/__tests__/tx-dialog.test.js.map +1 -0
  23. package/dist/cjs/Sprinkle/index.js +954 -0
  24. package/dist/cjs/Sprinkle/index.js.map +1 -0
  25. package/dist/cjs/index.js +17 -0
  26. package/dist/cjs/index.js.map +1 -0
  27. package/dist/cjs/package.json +1 -0
  28. package/dist/esm/Sprinkle/__tests__/bigint-reviver.test.js +38 -0
  29. package/dist/esm/Sprinkle/__tests__/bigint-reviver.test.js.map +1 -0
  30. package/dist/esm/Sprinkle/__tests__/encryption.test.js +264 -0
  31. package/dist/esm/Sprinkle/__tests__/encryption.test.js.map +1 -0
  32. package/dist/esm/Sprinkle/__tests__/enhancements.test.js +145 -0
  33. package/dist/esm/Sprinkle/__tests__/enhancements.test.js.map +1 -0
  34. package/dist/esm/Sprinkle/__tests__/extract-message.test.js +58 -0
  35. package/dist/esm/Sprinkle/__tests__/extract-message.test.js.map +1 -0
  36. package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js +130 -0
  37. package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -0
  38. package/dist/esm/Sprinkle/__tests__/schemas.test.js +182 -0
  39. package/dist/esm/Sprinkle/__tests__/schemas.test.js.map +1 -0
  40. package/dist/esm/Sprinkle/__tests__/settings-persistence.test.js +196 -0
  41. package/dist/esm/Sprinkle/__tests__/settings-persistence.test.js.map +1 -0
  42. package/dist/esm/Sprinkle/__tests__/show-menu.test.js +106 -0
  43. package/dist/esm/Sprinkle/__tests__/show-menu.test.js.map +1 -0
  44. package/dist/esm/Sprinkle/__tests__/test-helpers.js +10 -0
  45. package/dist/esm/Sprinkle/__tests__/test-helpers.js.map +1 -0
  46. package/dist/esm/Sprinkle/__tests__/tx-dialog.test.js +269 -0
  47. package/dist/esm/Sprinkle/__tests__/tx-dialog.test.js.map +1 -0
  48. package/dist/esm/Sprinkle/index.js +928 -0
  49. package/dist/esm/Sprinkle/index.js.map +1 -0
  50. package/dist/esm/index.js +2 -0
  51. package/dist/esm/index.js.map +1 -0
  52. package/dist/types/Sprinkle/index.d.ts +205 -0
  53. package/dist/types/Sprinkle/index.d.ts.map +1 -0
  54. package/dist/types/index.d.ts +2 -0
  55. package/dist/types/index.d.ts.map +1 -0
  56. package/dist/types/tsconfig.build.tsbuildinfo +1 -0
  57. package/package.json +85 -0
  58. package/src/Sprinkle/__tests__/bigint-reviver.test.ts +49 -0
  59. package/src/Sprinkle/__tests__/encryption.test.ts +266 -0
  60. package/src/Sprinkle/__tests__/enhancements.test.ts +154 -0
  61. package/src/Sprinkle/__tests__/extract-message.test.ts +60 -0
  62. package/src/Sprinkle/__tests__/fill-in-struct.test.ts +159 -0
  63. package/src/Sprinkle/__tests__/schemas.test.ts +215 -0
  64. package/src/Sprinkle/__tests__/settings-persistence.test.ts +181 -0
  65. package/src/Sprinkle/__tests__/show-menu.test.ts +123 -0
  66. package/src/Sprinkle/__tests__/test-helpers.ts +14 -0
  67. package/src/Sprinkle/__tests__/tx-dialog.test.ts +293 -0
  68. package/src/Sprinkle/index.ts +1215 -0
  69. package/src/index.ts +1 -0
@@ -0,0 +1,293 @@
1
+ import { describe, test, expect, mock, beforeEach } from "bun:test";
2
+ import { Type } from "@sinclair/typebox";
3
+
4
+ // Track select calls
5
+ let selectCalls: any[] = [];
6
+ let selectResponses: any[] = [];
7
+
8
+ // Mock @inquirer/prompts
9
+ mock.module("@inquirer/prompts", () => ({
10
+ input: mock(async () => "test"),
11
+ password: mock(async () => "secret"),
12
+ select: mock(async (opts: any) => {
13
+ selectCalls.push(opts);
14
+ const response = selectResponses.shift();
15
+ if (response === undefined) {
16
+ // Default: return -1 (Back/Exit)
17
+ return -1;
18
+ }
19
+ return response;
20
+ }),
21
+ search: mock(async () => "result"),
22
+ }));
23
+
24
+ // Mock clipboardy
25
+ let clipboardContent = "";
26
+ let clipboardShouldFail = false;
27
+ mock.module("clipboardy", () => ({
28
+ default: {
29
+ writeSync: (content: string) => {
30
+ if (clipboardShouldFail) throw new Error("clipboard not available");
31
+ clipboardContent = content;
32
+ },
33
+ },
34
+ }));
35
+
36
+ const { Sprinkle } = await import("../../index.js");
37
+
38
+ const TestSchema = Type.Object({
39
+ name: Type.String(),
40
+ });
41
+
42
+ describe("TxDialog (2.6)", () => {
43
+ beforeEach(() => {
44
+ selectCalls = [];
45
+ selectResponses = [];
46
+ clipboardContent = "";
47
+ clipboardShouldFail = false;
48
+ });
49
+
50
+ test("shows truncated CBOR by default", async () => {
51
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog");
52
+ const logs: string[] = [];
53
+ const origLog = console.log;
54
+ console.log = (...args: any[]) => logs.push(args.join(" "));
55
+
56
+ // Just exit immediately
57
+ selectResponses = [-1];
58
+
59
+ const mockTx = {
60
+ toCbor: () => "a".repeat(100),
61
+ body: () => ({
62
+ requiredSigners: () => null,
63
+ certs: () => null,
64
+ withdrawals: () => null,
65
+ }),
66
+ };
67
+
68
+ const mockBlaze = {
69
+ wallet: { getUsedAddresses: async () => [] },
70
+ };
71
+
72
+ await sprinkle.TxDialog(mockBlaze as any, mockTx as any);
73
+ console.log = origLog;
74
+
75
+ // Should show truncated CBOR (50 chars + ...)
76
+ const cborLog = logs.find(l => l.includes("Transaction CBOR:"));
77
+ expect(cborLog).toBeDefined();
78
+ expect(cborLog!.length).toBeLessThan(100);
79
+ expect(cborLog).toContain("...");
80
+ });
81
+
82
+ test("menu items for non-HotWallet include Expand, Copy, no Sign", async () => {
83
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog2");
84
+ const origLog = console.log;
85
+ console.log = () => {};
86
+
87
+ selectResponses = [-1];
88
+
89
+ const mockTx = {
90
+ toCbor: () => "deadbeef",
91
+ body: () => ({
92
+ requiredSigners: () => null,
93
+ certs: () => null,
94
+ withdrawals: () => null,
95
+ }),
96
+ };
97
+
98
+ const mockBlaze = {
99
+ wallet: { getUsedAddresses: async () => [] },
100
+ };
101
+
102
+ await sprinkle.TxDialog(mockBlaze as any, mockTx as any);
103
+ console.log = origLog;
104
+
105
+ // Check the select call had Expand and Copy but not Sign
106
+ const menuCall = selectCalls[0];
107
+ expect(menuCall).toBeDefined();
108
+ const choiceNames = menuCall.choices.map((c: any) => c.name);
109
+ expect(choiceNames).toContain("Expand CBOR");
110
+ expect(choiceNames).toContain("Copy CBOR to clipboard");
111
+ expect(choiceNames).not.toContain("Sign and submit transaction");
112
+ });
113
+
114
+ test("menu items for HotWallet include Sign", async () => {
115
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog3");
116
+ const origLog = console.log;
117
+ console.log = () => {};
118
+
119
+ // Need to import HotWallet to use instanceof
120
+ const { HotWallet } = await import("@blaze-cardano/sdk");
121
+
122
+ selectResponses = [-1];
123
+
124
+ const mockTx = {
125
+ toCbor: () => "deadbeef",
126
+ body: () => ({
127
+ requiredSigners: () => null,
128
+ certs: () => null,
129
+ withdrawals: () => null,
130
+ }),
131
+ };
132
+
133
+ // Create a mock that passes instanceof HotWallet check
134
+ // We need to use Object.create to set the prototype
135
+ const hotWalletProto = HotWallet.prototype;
136
+ const mockWallet = Object.create(hotWalletProto);
137
+ mockWallet.getUsedAddresses = async () => [];
138
+
139
+ const mockBlaze = {
140
+ wallet: mockWallet,
141
+ };
142
+
143
+ await sprinkle.TxDialog(mockBlaze as any, mockTx as any);
144
+ console.log = origLog;
145
+
146
+ const menuCall = selectCalls[0];
147
+ expect(menuCall).toBeDefined();
148
+ const choiceNames = menuCall.choices.map((c: any) => c.name);
149
+ expect(choiceNames).toContain("Sign and submit transaction");
150
+ });
151
+
152
+ test("copy CBOR to clipboard works", async () => {
153
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog4");
154
+ const origLog = console.log;
155
+ const logs: string[] = [];
156
+ console.log = (...args: any[]) => logs.push(args.join(" "));
157
+
158
+ // Select "Copy CBOR to clipboard" (index 1), then Back (-1)
159
+ selectResponses = [1, -1];
160
+
161
+ const txCbor = "deadbeefcafebabe";
162
+ const mockTx = {
163
+ toCbor: () => txCbor,
164
+ body: () => ({
165
+ requiredSigners: () => null,
166
+ certs: () => null,
167
+ withdrawals: () => null,
168
+ }),
169
+ };
170
+
171
+ const mockBlaze = {
172
+ wallet: { getUsedAddresses: async () => [] },
173
+ };
174
+
175
+ await sprinkle.TxDialog(mockBlaze as any, mockTx as any);
176
+ console.log = origLog;
177
+
178
+ expect(clipboardContent).toBe(txCbor);
179
+ expect(logs.some(l => l.includes("copied to clipboard"))).toBe(true);
180
+ });
181
+
182
+ test("clipboard failure falls back to expanded view", async () => {
183
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog5");
184
+ const origLog = console.log;
185
+ const logs: string[] = [];
186
+ console.log = (...args: any[]) => logs.push(args.join(" "));
187
+
188
+ clipboardShouldFail = true;
189
+
190
+ // Select "Copy CBOR to clipboard" (index 1),
191
+ // It will fail and call showDialog again with expanded=true
192
+ // Then Back (-1) from the expanded menu, then Back from outer
193
+ selectResponses = [1, -1, -1];
194
+
195
+ const txCbor = "a".repeat(100);
196
+ const mockTx = {
197
+ toCbor: () => txCbor,
198
+ body: () => ({
199
+ requiredSigners: () => null,
200
+ certs: () => null,
201
+ withdrawals: () => null,
202
+ }),
203
+ };
204
+
205
+ const mockBlaze = {
206
+ wallet: { getUsedAddresses: async () => [] },
207
+ };
208
+
209
+ await sprinkle.TxDialog(mockBlaze as any, mockTx as any);
210
+ console.log = origLog;
211
+
212
+ expect(logs.some(l => l.includes("Failed to copy"))).toBe(true);
213
+ // After failure it should show the full CBOR without truncation
214
+ expect(logs.some(l => l.includes(txCbor) && !l.includes("..."))).toBe(true);
215
+ });
216
+
217
+ test("expand CBOR shows full content", async () => {
218
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog6");
219
+ const origLog = console.log;
220
+ const logs: string[] = [];
221
+ console.log = (...args: any[]) => logs.push(args.join(" "));
222
+
223
+ // Select "Expand CBOR" (index 0), then Back (-1) from expanded, then Back from outer
224
+ selectResponses = [0, -1, -1];
225
+
226
+ const txCbor = "b".repeat(100);
227
+ const mockTx = {
228
+ toCbor: () => txCbor,
229
+ body: () => ({
230
+ requiredSigners: () => null,
231
+ certs: () => null,
232
+ withdrawals: () => null,
233
+ }),
234
+ };
235
+
236
+ const mockBlaze = {
237
+ wallet: { getUsedAddresses: async () => [] },
238
+ };
239
+
240
+ await sprinkle.TxDialog(mockBlaze as any, mockTx as any);
241
+ console.log = origLog;
242
+
243
+ // First call shows truncated, second shows full
244
+ const truncatedLog = logs.find(l => l.includes("...") && l.includes("Transaction CBOR:"));
245
+ const fullLog = logs.find(l => l.includes(txCbor) && !l.includes("..."));
246
+ expect(truncatedLog).toBeDefined();
247
+ expect(fullLog).toBeDefined();
248
+ });
249
+
250
+ test("beforeSign hook is called before signing", async () => {
251
+ const sprinkle = new Sprinkle(TestSchema, "/tmp/test-txdialog7");
252
+ const origLog = console.log;
253
+ const origWarn = console.warn;
254
+ console.log = () => {};
255
+ console.warn = () => {};
256
+
257
+ const { HotWallet } = await import("@blaze-cardano/sdk");
258
+
259
+ let beforeSignCalled = false;
260
+
261
+ // Select "Sign and submit" (index 2 - after Expand and Copy), then Back
262
+ selectResponses = [2, -1];
263
+
264
+ const mockTx = {
265
+ toCbor: () => "deadbeef",
266
+ body: () => ({
267
+ requiredSigners: () => null,
268
+ certs: () => null,
269
+ withdrawals: () => null,
270
+ }),
271
+ };
272
+
273
+ const hotWalletProto = HotWallet.prototype;
274
+ const mockWallet = Object.create(hotWalletProto);
275
+ mockWallet.getUsedAddresses = async () => [];
276
+
277
+ const mockBlaze = {
278
+ wallet: mockWallet,
279
+ signTransaction: async (tx: any) => tx,
280
+ submitTransaction: async () => "txhash123",
281
+ };
282
+
283
+ await sprinkle.TxDialog(mockBlaze as any, mockTx as any, {
284
+ beforeSign: async () => {
285
+ beforeSignCalled = true;
286
+ },
287
+ });
288
+ console.log = origLog;
289
+ console.warn = origWarn;
290
+
291
+ expect(beforeSignCalled).toBe(true);
292
+ });
293
+ });