@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.
- package/LICENSE +21 -0
- package/README.md +260 -0
- package/dist/cjs/Sprinkle/__tests__/bigint-reviver.test.js +40 -0
- package/dist/cjs/Sprinkle/__tests__/bigint-reviver.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/encryption.test.js +267 -0
- package/dist/cjs/Sprinkle/__tests__/encryption.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/enhancements.test.js +147 -0
- package/dist/cjs/Sprinkle/__tests__/enhancements.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/extract-message.test.js +60 -0
- package/dist/cjs/Sprinkle/__tests__/extract-message.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js +131 -0
- package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/schemas.test.js +184 -0
- package/dist/cjs/Sprinkle/__tests__/schemas.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/settings-persistence.test.js +199 -0
- package/dist/cjs/Sprinkle/__tests__/settings-persistence.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/show-menu.test.js +108 -0
- package/dist/cjs/Sprinkle/__tests__/show-menu.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/test-helpers.js +16 -0
- package/dist/cjs/Sprinkle/__tests__/test-helpers.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/tx-dialog.test.js +271 -0
- package/dist/cjs/Sprinkle/__tests__/tx-dialog.test.js.map +1 -0
- package/dist/cjs/Sprinkle/index.js +954 -0
- package/dist/cjs/Sprinkle/index.js.map +1 -0
- package/dist/cjs/index.js +17 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/esm/Sprinkle/__tests__/bigint-reviver.test.js +38 -0
- package/dist/esm/Sprinkle/__tests__/bigint-reviver.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/encryption.test.js +264 -0
- package/dist/esm/Sprinkle/__tests__/encryption.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/enhancements.test.js +145 -0
- package/dist/esm/Sprinkle/__tests__/enhancements.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/extract-message.test.js +58 -0
- package/dist/esm/Sprinkle/__tests__/extract-message.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js +130 -0
- package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/schemas.test.js +182 -0
- package/dist/esm/Sprinkle/__tests__/schemas.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/settings-persistence.test.js +196 -0
- package/dist/esm/Sprinkle/__tests__/settings-persistence.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/show-menu.test.js +106 -0
- package/dist/esm/Sprinkle/__tests__/show-menu.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/test-helpers.js +10 -0
- package/dist/esm/Sprinkle/__tests__/test-helpers.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/tx-dialog.test.js +269 -0
- package/dist/esm/Sprinkle/__tests__/tx-dialog.test.js.map +1 -0
- package/dist/esm/Sprinkle/index.js +928 -0
- package/dist/esm/Sprinkle/index.js.map +1 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/types/Sprinkle/index.d.ts +205 -0
- package/dist/types/Sprinkle/index.d.ts.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/tsconfig.build.tsbuildinfo +1 -0
- package/package.json +85 -0
- package/src/Sprinkle/__tests__/bigint-reviver.test.ts +49 -0
- package/src/Sprinkle/__tests__/encryption.test.ts +266 -0
- package/src/Sprinkle/__tests__/enhancements.test.ts +154 -0
- package/src/Sprinkle/__tests__/extract-message.test.ts +60 -0
- package/src/Sprinkle/__tests__/fill-in-struct.test.ts +159 -0
- package/src/Sprinkle/__tests__/schemas.test.ts +215 -0
- package/src/Sprinkle/__tests__/settings-persistence.test.ts +181 -0
- package/src/Sprinkle/__tests__/show-menu.test.ts +123 -0
- package/src/Sprinkle/__tests__/test-helpers.ts +14 -0
- package/src/Sprinkle/__tests__/tx-dialog.test.ts +293 -0
- package/src/Sprinkle/index.ts +1215 -0
- package/src/index.ts +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SundaeSwap Finance
|
|
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,260 @@
|
|
|
1
|
+
# Sprinkles
|
|
2
|
+
|
|
3
|
+
> **Note:** This is an early release (v0.x). The API may change between minor versions as we refine the library based on community feedback.
|
|
4
|
+
|
|
5
|
+
A TypeScript library for building interactive CLI menus and TUI applications with TypeBox schema validation. Sprinkles provides a declarative way to create nested menus, handle user input, and manage persistent settings with type safety.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Schema-driven UI**: Define your data structures using TypeBox schemas, and Sprinkles automatically generates interactive prompts
|
|
10
|
+
- **Nested menu support**: Create hierarchical menu structures with submenus
|
|
11
|
+
- **Persistent settings**: Automatic JSON-based settings storage with BigInt support
|
|
12
|
+
- **Type-safe**: Full TypeScript support with type inference from schemas
|
|
13
|
+
- **Cardano integration**: Built-in helpers for Blaze SDK wallet and provider management
|
|
14
|
+
- **Transaction dialogs**: Ready-to-use transaction signing and submission flows
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @sundaeswap/sprinkles
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import { Sprinkle, Type } from "sprinkles";
|
|
26
|
+
|
|
27
|
+
// Define your settings schema
|
|
28
|
+
const AppSettingsSchema = Type.Object({
|
|
29
|
+
username: Type.String({ title: "Username" }),
|
|
30
|
+
theme: Type.Union([
|
|
31
|
+
Type.Literal("dark"),
|
|
32
|
+
Type.Literal("light")
|
|
33
|
+
])
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Create a Sprinkle instance
|
|
37
|
+
const app = await Sprinkle.New(AppSettingsSchema, "./config");
|
|
38
|
+
|
|
39
|
+
// Define your menu
|
|
40
|
+
const mainMenu = {
|
|
41
|
+
title: "Main Menu",
|
|
42
|
+
items: [
|
|
43
|
+
{
|
|
44
|
+
title: "Say Hello",
|
|
45
|
+
action: async (sprinkle) => {
|
|
46
|
+
console.log(`Hello, ${sprinkle.settings.username}!`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Show the menu
|
|
53
|
+
await app.showMenu(mainMenu);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## API Reference
|
|
57
|
+
|
|
58
|
+
### Core Classes
|
|
59
|
+
|
|
60
|
+
#### `Sprinkle<S extends TSchema>`
|
|
61
|
+
|
|
62
|
+
The main class for managing your CLI application.
|
|
63
|
+
|
|
64
|
+
##### Constructor
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
constructor(type: S, storagePath: string)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
##### Static Methods
|
|
71
|
+
|
|
72
|
+
- `Sprinkle.New<S>(type: S, storagePath: string): Promise<Sprinkle<S>>` - Create and initialize a new Sprinkle instance
|
|
73
|
+
- `Sprinkle.GetProvider(network, settings): Provider` - Create a Cardano provider instance
|
|
74
|
+
- `Sprinkle.GetWallet(settings, provider): Promise<Wallet>` - Create a Cardano wallet instance
|
|
75
|
+
- `Sprinkle.GetBlaze(network, providerSettings, walletSettings): Promise<Blaze>` - Create a Blaze SDK instance
|
|
76
|
+
- `Sprinkle.SettingsPath(storagePath: string): string` - Get the settings file path
|
|
77
|
+
|
|
78
|
+
##### Instance Methods
|
|
79
|
+
|
|
80
|
+
- `showMenu(menu: IMenu<S>): Promise<void>` - Display a menu and handle user interactions
|
|
81
|
+
- `EditStruct<U>(type: U, current: TExact<U>): Promise<TExact<U>>` - Interactive editor for schema-based structures
|
|
82
|
+
- `FillInStruct<U>(type: U, def?: TExact<U>): Promise<TExact<U>>` - Interactive form to fill in a structure
|
|
83
|
+
- `TxDialog(blaze, tx): Promise<void>` - Display transaction dialog with sign/submit options
|
|
84
|
+
- `saveSettings(): void` - Persist current settings to disk
|
|
85
|
+
- `LoadSettings(type, storagePath): Promise<void>` - Load settings from disk
|
|
86
|
+
|
|
87
|
+
### Types
|
|
88
|
+
|
|
89
|
+
#### `IMenu<S>`
|
|
90
|
+
|
|
91
|
+
Defines a menu structure:
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
interface IMenu<S extends TSchema> {
|
|
95
|
+
title: string;
|
|
96
|
+
items: TMenuItem<S>[];
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
#### `IMenuAction<S>`
|
|
101
|
+
|
|
102
|
+
Defines a menu action:
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
interface IMenuAction<S extends TSchema> {
|
|
106
|
+
title: string;
|
|
107
|
+
action: (sprinkle: Sprinkle<S>) => Promise<Sprinkle<S> | void>;
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
#### `TMenuItem<S>`
|
|
112
|
+
|
|
113
|
+
A menu item can be either an action or a submenu:
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
type TMenuItem<S extends TSchema> = IMenuAction<S> | IMenu<S>;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Built-in Schemas
|
|
120
|
+
|
|
121
|
+
#### `NetworkSchema`
|
|
122
|
+
|
|
123
|
+
Cardano network selection:
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
Type.Union([
|
|
127
|
+
Type.Literal("mainnet"),
|
|
128
|
+
Type.Literal("preview"),
|
|
129
|
+
Type.Literal("preprod")
|
|
130
|
+
])
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### `ProviderSettingsSchema`
|
|
134
|
+
|
|
135
|
+
Cardano provider configuration:
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
Type.Union([
|
|
139
|
+
Type.Object({
|
|
140
|
+
type: Type.Literal("blockfrost"),
|
|
141
|
+
projectId: Type.String({ minLength: 1, title: "Blockfrost Project ID" })
|
|
142
|
+
}),
|
|
143
|
+
Type.Object({
|
|
144
|
+
type: Type.Literal("maestro"),
|
|
145
|
+
apiKey: Type.String({ minLength: 1, title: "Maestro API Key" })
|
|
146
|
+
})
|
|
147
|
+
])
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### `WalletSettingsSchema`
|
|
151
|
+
|
|
152
|
+
Cardano wallet configuration:
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
Type.Union([
|
|
156
|
+
Type.Object({
|
|
157
|
+
type: Type.Literal("hot"),
|
|
158
|
+
privateKey: Type.String({ minLength: 1, title: "Hot Wallet Private Key" })
|
|
159
|
+
}),
|
|
160
|
+
Type.Object({
|
|
161
|
+
type: Type.Literal("cold"),
|
|
162
|
+
address: Type.String({ minLength: 1, title: "Cold Wallet Address" })
|
|
163
|
+
})
|
|
164
|
+
])
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
#### `MultisigScript`
|
|
168
|
+
|
|
169
|
+
Cardano multisig script schema with support for:
|
|
170
|
+
- Signature verification
|
|
171
|
+
- AllOf (all signatures required)
|
|
172
|
+
- AnyOf (any signature works)
|
|
173
|
+
- AtLeast (m-of-n threshold)
|
|
174
|
+
- Before/After (time-based conditions)
|
|
175
|
+
- Script hash references
|
|
176
|
+
|
|
177
|
+
## Advanced Usage
|
|
178
|
+
|
|
179
|
+
### Creating Nested Menus
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const menu = {
|
|
183
|
+
title: "Main Menu",
|
|
184
|
+
items: [
|
|
185
|
+
{
|
|
186
|
+
title: "User Management",
|
|
187
|
+
items: [
|
|
188
|
+
{
|
|
189
|
+
title: "Add User",
|
|
190
|
+
action: async (sprinkle) => {
|
|
191
|
+
// Add user logic
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
title: "Remove User",
|
|
196
|
+
action: async (sprinkle) => {
|
|
197
|
+
// Remove user logic
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
]
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
};
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Modifying Settings from Actions
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
{
|
|
210
|
+
title: "Change Username",
|
|
211
|
+
action: async (sprinkle) => {
|
|
212
|
+
const newSettings = await sprinkle.EditStruct(
|
|
213
|
+
Type.Object({ username: Type.String() }),
|
|
214
|
+
{ username: sprinkle.settings.username }
|
|
215
|
+
);
|
|
216
|
+
return new Sprinkle(sprinkle.type, sprinkle.storagePath);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Working with Cardano Transactions
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
const blaze = await Sprinkle.GetBlaze(
|
|
225
|
+
"preprod",
|
|
226
|
+
providerSettings,
|
|
227
|
+
walletSettings
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
// Build your transaction
|
|
231
|
+
const tx = await blaze
|
|
232
|
+
.newTransaction()
|
|
233
|
+
.payLovelace(recipientAddress, 5_000_000n)
|
|
234
|
+
.complete();
|
|
235
|
+
|
|
236
|
+
// Show transaction dialog
|
|
237
|
+
await app.TxDialog(blaze, tx);
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Settings Persistence
|
|
241
|
+
|
|
242
|
+
Settings are automatically saved to `{storagePath}/settings.json`. The file format includes:
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
{
|
|
246
|
+
"settings": {
|
|
247
|
+
"username": "alice",
|
|
248
|
+
"count": "42n"
|
|
249
|
+
},
|
|
250
|
+
"defaults": {
|
|
251
|
+
"string": "last_entered_value"
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Note: BigInt values are serialized with an 'n' suffix and automatically parsed on load.
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _bunTest = require("bun:test");
|
|
4
|
+
var _index = require("../index.js");
|
|
5
|
+
(0, _bunTest.describe)("bigIntReviver", () => {
|
|
6
|
+
(0, _bunTest.test)("converts string ending with n to BigInt", () => {
|
|
7
|
+
(0, _bunTest.expect)(_index.Sprinkle.bigIntReviver("key", "42n")).toBe(42n);
|
|
8
|
+
});
|
|
9
|
+
(0, _bunTest.test)("converts zero bigint string", () => {
|
|
10
|
+
(0, _bunTest.expect)(_index.Sprinkle.bigIntReviver("key", "0n")).toBe(0n);
|
|
11
|
+
});
|
|
12
|
+
(0, _bunTest.test)("converts large bigint string", () => {
|
|
13
|
+
(0, _bunTest.expect)(_index.Sprinkle.bigIntReviver("key", "99999999999999999999n")).toBe(99999999999999999999n);
|
|
14
|
+
});
|
|
15
|
+
(0, _bunTest.test)("does not convert regular strings", () => {
|
|
16
|
+
(0, _bunTest.expect)(_index.Sprinkle.bigIntReviver("key", "hello")).toBe("hello");
|
|
17
|
+
});
|
|
18
|
+
(0, _bunTest.test)("does not convert strings with n in the middle", () => {
|
|
19
|
+
(0, _bunTest.expect)(_index.Sprinkle.bigIntReviver("key", "12n34")).toBe("12n34");
|
|
20
|
+
});
|
|
21
|
+
(0, _bunTest.test)("does not convert non-numeric n-suffixed strings", () => {
|
|
22
|
+
(0, _bunTest.expect)(_index.Sprinkle.bigIntReviver("key", "abcn")).toBe("abcn");
|
|
23
|
+
});
|
|
24
|
+
(0, _bunTest.test)("passes through numbers unchanged", () => {
|
|
25
|
+
(0, _bunTest.expect)(_index.Sprinkle.bigIntReviver("key", 42)).toBe(42);
|
|
26
|
+
});
|
|
27
|
+
(0, _bunTest.test)("passes through booleans unchanged", () => {
|
|
28
|
+
(0, _bunTest.expect)(_index.Sprinkle.bigIntReviver("key", true)).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
(0, _bunTest.test)("passes through null unchanged", () => {
|
|
31
|
+
(0, _bunTest.expect)(_index.Sprinkle.bigIntReviver("key", null)).toBe(null);
|
|
32
|
+
});
|
|
33
|
+
(0, _bunTest.test)("works with JSON.parse", () => {
|
|
34
|
+
const json = '{"amount": "100n", "name": "test"}';
|
|
35
|
+
const parsed = JSON.parse(json, _index.Sprinkle.bigIntReviver);
|
|
36
|
+
(0, _bunTest.expect)(parsed.amount).toBe(100n);
|
|
37
|
+
(0, _bunTest.expect)(parsed.name).toBe("test");
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=bigint-reviver.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bigint-reviver.test.js","names":["_bunTest","require","_index","describe","test","expect","Sprinkle","bigIntReviver","toBe","json","parsed","JSON","parse","amount","name"],"sources":["../../../../src/Sprinkle/__tests__/bigint-reviver.test.ts"],"sourcesContent":["import { describe, expect, test } from \"bun:test\";\nimport { Sprinkle } from \"../index.js\";\n\ndescribe(\"bigIntReviver\", () => {\n test(\"converts string ending with n to BigInt\", () => {\n expect(Sprinkle.bigIntReviver(\"key\", \"42n\")).toBe(42n);\n });\n\n test(\"converts zero bigint string\", () => {\n expect(Sprinkle.bigIntReviver(\"key\", \"0n\")).toBe(0n);\n });\n\n test(\"converts large bigint string\", () => {\n expect(Sprinkle.bigIntReviver(\"key\", \"99999999999999999999n\")).toBe(\n 99999999999999999999n,\n );\n });\n\n test(\"does not convert regular strings\", () => {\n expect(Sprinkle.bigIntReviver(\"key\", \"hello\")).toBe(\"hello\");\n });\n\n test(\"does not convert strings with n in the middle\", () => {\n expect(Sprinkle.bigIntReviver(\"key\", \"12n34\")).toBe(\"12n34\");\n });\n\n test(\"does not convert non-numeric n-suffixed strings\", () => {\n expect(Sprinkle.bigIntReviver(\"key\", \"abcn\")).toBe(\"abcn\");\n });\n\n test(\"passes through numbers unchanged\", () => {\n expect(Sprinkle.bigIntReviver(\"key\", 42)).toBe(42);\n });\n\n test(\"passes through booleans unchanged\", () => {\n expect(Sprinkle.bigIntReviver(\"key\", true)).toBe(true);\n });\n\n test(\"passes through null unchanged\", () => {\n expect(Sprinkle.bigIntReviver(\"key\", null)).toBe(null);\n });\n\n test(\"works with JSON.parse\", () => {\n const json = '{\"amount\": \"100n\", \"name\": \"test\"}';\n const parsed = JSON.parse(json, Sprinkle.bigIntReviver);\n expect(parsed.amount).toBe(100n);\n expect(parsed.name).toBe(\"test\");\n });\n});\n"],"mappings":";;AAAA,IAAAA,QAAA,GAAAC,OAAA;AACA,IAAAC,MAAA,GAAAD,OAAA;AAEA,IAAAE,iBAAQ,EAAC,eAAe,EAAE,MAAM;EAC9B,IAAAC,aAAI,EAAC,yCAAyC,EAAE,MAAM;IACpD,IAAAC,eAAM,EAACC,eAAQ,CAACC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAACC,IAAI,CAAC,GAAG,CAAC;EACxD,CAAC,CAAC;EAEF,IAAAJ,aAAI,EAAC,6BAA6B,EAAE,MAAM;IACxC,IAAAC,eAAM,EAACC,eAAQ,CAACC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAACC,IAAI,CAAC,EAAE,CAAC;EACtD,CAAC,CAAC;EAEF,IAAAJ,aAAI,EAAC,8BAA8B,EAAE,MAAM;IACzC,IAAAC,eAAM,EAACC,eAAQ,CAACC,aAAa,CAAC,KAAK,EAAE,uBAAuB,CAAC,CAAC,CAACC,IAAI,CACjE,qBACF,CAAC;EACH,CAAC,CAAC;EAEF,IAAAJ,aAAI,EAAC,kCAAkC,EAAE,MAAM;IAC7C,IAAAC,eAAM,EAACC,eAAQ,CAACC,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAACC,IAAI,CAAC,OAAO,CAAC;EAC9D,CAAC,CAAC;EAEF,IAAAJ,aAAI,EAAC,+CAA+C,EAAE,MAAM;IAC1D,IAAAC,eAAM,EAACC,eAAQ,CAACC,aAAa,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAACC,IAAI,CAAC,OAAO,CAAC;EAC9D,CAAC,CAAC;EAEF,IAAAJ,aAAI,EAAC,iDAAiD,EAAE,MAAM;IAC5D,IAAAC,eAAM,EAACC,eAAQ,CAACC,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAACC,IAAI,CAAC,MAAM,CAAC;EAC5D,CAAC,CAAC;EAEF,IAAAJ,aAAI,EAAC,kCAAkC,EAAE,MAAM;IAC7C,IAAAC,eAAM,EAACC,eAAQ,CAACC,aAAa,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAACC,IAAI,CAAC,EAAE,CAAC;EACpD,CAAC,CAAC;EAEF,IAAAJ,aAAI,EAAC,mCAAmC,EAAE,MAAM;IAC9C,IAAAC,eAAM,EAACC,eAAQ,CAACC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAACC,IAAI,CAAC,IAAI,CAAC;EACxD,CAAC,CAAC;EAEF,IAAAJ,aAAI,EAAC,+BAA+B,EAAE,MAAM;IAC1C,IAAAC,eAAM,EAACC,eAAQ,CAACC,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAACC,IAAI,CAAC,IAAI,CAAC;EACxD,CAAC,CAAC;EAEF,IAAAJ,aAAI,EAAC,uBAAuB,EAAE,MAAM;IAClC,MAAMK,IAAI,GAAG,oCAAoC;IACjD,MAAMC,MAAM,GAAGC,IAAI,CAACC,KAAK,CAACH,IAAI,EAAEH,eAAQ,CAACC,aAAa,CAAC;IACvD,IAAAF,eAAM,EAACK,MAAM,CAACG,MAAM,CAAC,CAACL,IAAI,CAAC,IAAI,CAAC;IAChC,IAAAH,eAAM,EAACK,MAAM,CAACI,IAAI,CAAC,CAACN,IAAI,CAAC,MAAM,CAAC;EAClC,CAAC,CAAC;AACJ,CAAC,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _bunTest = require("bun:test");
|
|
4
|
+
var _index = require("../index.js");
|
|
5
|
+
var fs = _interopRequireWildcard(require("fs"));
|
|
6
|
+
var path = _interopRequireWildcard(require("path"));
|
|
7
|
+
var os = _interopRequireWildcard(require("os"));
|
|
8
|
+
var _testHelpers = require("./test-helpers.js");
|
|
9
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
10
|
+
const mockSelect = (0, _bunTest.mock)();
|
|
11
|
+
const mockInput = (0, _bunTest.mock)();
|
|
12
|
+
const mockPassword = (0, _bunTest.mock)();
|
|
13
|
+
_bunTest.mock.module("@inquirer/prompts", () => ({
|
|
14
|
+
select: mockSelect,
|
|
15
|
+
input: mockInput,
|
|
16
|
+
password: mockPassword
|
|
17
|
+
}));
|
|
18
|
+
(0, _bunTest.describe)("Encryption & Sensitive Fields", () => {
|
|
19
|
+
let tmpDir;
|
|
20
|
+
(0, _bunTest.beforeEach)(() => {
|
|
21
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "sprinkles-enc-test-"));
|
|
22
|
+
mockSelect.mockClear();
|
|
23
|
+
mockInput.mockClear();
|
|
24
|
+
mockPassword.mockClear();
|
|
25
|
+
});
|
|
26
|
+
(0, _bunTest.afterEach)(() => {
|
|
27
|
+
fs.rmSync(tmpDir, {
|
|
28
|
+
recursive: true,
|
|
29
|
+
force: true
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
(0, _bunTest.describe)("sensitive field prompts", () => {
|
|
33
|
+
(0, _bunTest.test)("uses password() for sensitive string fields", async () => {
|
|
34
|
+
const schema = _index.Type.Object({
|
|
35
|
+
secret: _index.Type.String({
|
|
36
|
+
sensitive: true,
|
|
37
|
+
title: "Enter secret"
|
|
38
|
+
})
|
|
39
|
+
});
|
|
40
|
+
const sprinkle = new _index.Sprinkle(schema, tmpDir);
|
|
41
|
+
mockPassword.mockResolvedValueOnce("my-secret");
|
|
42
|
+
const result = await sprinkle.FillInStruct(schema);
|
|
43
|
+
(0, _bunTest.expect)(result).toEqual({
|
|
44
|
+
secret: "my-secret"
|
|
45
|
+
});
|
|
46
|
+
(0, _bunTest.expect)(mockPassword).toHaveBeenCalledTimes(1);
|
|
47
|
+
(0, _bunTest.expect)(mockPassword.mock.calls[0][0].message).toBe("Enter secret");
|
|
48
|
+
(0, _bunTest.expect)(mockInput).not.toHaveBeenCalled();
|
|
49
|
+
});
|
|
50
|
+
(0, _bunTest.test)("uses input() for non-sensitive string fields", async () => {
|
|
51
|
+
const schema = _index.Type.Object({
|
|
52
|
+
name: _index.Type.String({
|
|
53
|
+
title: "Enter name"
|
|
54
|
+
})
|
|
55
|
+
});
|
|
56
|
+
const sprinkle = new _index.Sprinkle(schema, tmpDir);
|
|
57
|
+
mockInput.mockResolvedValueOnce("visible");
|
|
58
|
+
const result = await sprinkle.FillInStruct(schema);
|
|
59
|
+
(0, _bunTest.expect)(result).toEqual({
|
|
60
|
+
name: "visible"
|
|
61
|
+
});
|
|
62
|
+
(0, _bunTest.expect)(mockInput).toHaveBeenCalledTimes(1);
|
|
63
|
+
(0, _bunTest.expect)(mockPassword).not.toHaveBeenCalled();
|
|
64
|
+
});
|
|
65
|
+
(0, _bunTest.test)("does not remember sensitive field as default", async () => {
|
|
66
|
+
const schema = _index.Type.String({
|
|
67
|
+
sensitive: true
|
|
68
|
+
});
|
|
69
|
+
const sprinkle = new _index.Sprinkle(_index.Type.Object({
|
|
70
|
+
p: _index.Type.String()
|
|
71
|
+
}), tmpDir);
|
|
72
|
+
mockPassword.mockResolvedValueOnce("secret-val");
|
|
73
|
+
await sprinkle.FillInStruct(schema);
|
|
74
|
+
(0, _bunTest.expect)(sprinkle.defaults["string"]).toBeUndefined();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
(0, _bunTest.describe)("encryption on save/load", () => {
|
|
78
|
+
(0, _bunTest.test)("encrypts sensitive fields when saving with encryption configured", () => {
|
|
79
|
+
const schema = _index.Type.Object({
|
|
80
|
+
name: _index.Type.String(),
|
|
81
|
+
secret: _index.Type.String({
|
|
82
|
+
sensitive: true
|
|
83
|
+
})
|
|
84
|
+
});
|
|
85
|
+
const sprinkle = (0, _testHelpers.withProfile)(new _index.Sprinkle(schema, tmpDir, {
|
|
86
|
+
encryption: {
|
|
87
|
+
encrypt: plain => `ENC:${plain}`,
|
|
88
|
+
decrypt: async cipher => cipher.replace("ENC:", "")
|
|
89
|
+
}
|
|
90
|
+
}));
|
|
91
|
+
sprinkle.settings = {
|
|
92
|
+
name: "visible",
|
|
93
|
+
secret: "hidden"
|
|
94
|
+
};
|
|
95
|
+
sprinkle.saveSettings();
|
|
96
|
+
const content = fs.readFileSync(path.join(tmpDir, "profiles", "test.json"), "utf-8");
|
|
97
|
+
const parsed = JSON.parse(content);
|
|
98
|
+
(0, _bunTest.expect)(parsed.settings.name).toBe("visible");
|
|
99
|
+
(0, _bunTest.expect)(parsed.settings.secret).toBe("ENC:hidden");
|
|
100
|
+
});
|
|
101
|
+
(0, _bunTest.test)("decrypts sensitive fields when loading with encryption configured", async () => {
|
|
102
|
+
const schema = _index.Type.Object({
|
|
103
|
+
name: _index.Type.String(),
|
|
104
|
+
secret: _index.Type.String({
|
|
105
|
+
sensitive: true
|
|
106
|
+
})
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// First save with encryption
|
|
110
|
+
const sprinkle1 = (0, _testHelpers.withProfile)(new _index.Sprinkle(schema, tmpDir, {
|
|
111
|
+
encryption: {
|
|
112
|
+
encrypt: plain => `ENC:${plain}`,
|
|
113
|
+
decrypt: async cipher => cipher.replace("ENC:", "")
|
|
114
|
+
}
|
|
115
|
+
}));
|
|
116
|
+
sprinkle1.settings = {
|
|
117
|
+
name: "visible",
|
|
118
|
+
secret: "hidden"
|
|
119
|
+
};
|
|
120
|
+
sprinkle1.saveSettings();
|
|
121
|
+
|
|
122
|
+
// Then load with same encryption
|
|
123
|
+
const sprinkle2 = new _index.Sprinkle(schema, tmpDir, {
|
|
124
|
+
encryption: {
|
|
125
|
+
encrypt: plain => `ENC:${plain}`,
|
|
126
|
+
decrypt: async cipher => cipher.replace("ENC:", "")
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
await sprinkle2.loadProfile("test");
|
|
130
|
+
(0, _bunTest.expect)(sprinkle2.settings).toEqual({
|
|
131
|
+
name: "visible",
|
|
132
|
+
secret: "hidden"
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
(0, _bunTest.test)("does not encrypt when no encryption configured", () => {
|
|
136
|
+
const schema = _index.Type.Object({
|
|
137
|
+
secret: _index.Type.String({
|
|
138
|
+
sensitive: true
|
|
139
|
+
})
|
|
140
|
+
});
|
|
141
|
+
const sprinkle = (0, _testHelpers.withProfile)(new _index.Sprinkle(schema, tmpDir));
|
|
142
|
+
sprinkle.settings = {
|
|
143
|
+
secret: "plain-value"
|
|
144
|
+
};
|
|
145
|
+
sprinkle.saveSettings();
|
|
146
|
+
const content = fs.readFileSync(path.join(tmpDir, "profiles", "test.json"), "utf-8");
|
|
147
|
+
const parsed = JSON.parse(content);
|
|
148
|
+
(0, _bunTest.expect)(parsed.settings.secret).toBe("plain-value");
|
|
149
|
+
});
|
|
150
|
+
(0, _bunTest.test)("encrypts nested sensitive fields", () => {
|
|
151
|
+
const schema = _index.Type.Object({
|
|
152
|
+
wallet: _index.Type.Object({
|
|
153
|
+
key: _index.Type.String({
|
|
154
|
+
sensitive: true
|
|
155
|
+
}),
|
|
156
|
+
address: _index.Type.String()
|
|
157
|
+
})
|
|
158
|
+
});
|
|
159
|
+
const sprinkle = (0, _testHelpers.withProfile)(new _index.Sprinkle(schema, tmpDir, {
|
|
160
|
+
encryption: {
|
|
161
|
+
encrypt: plain => `ENC:${plain}`,
|
|
162
|
+
decrypt: async cipher => cipher.replace("ENC:", "")
|
|
163
|
+
}
|
|
164
|
+
}));
|
|
165
|
+
sprinkle.settings = {
|
|
166
|
+
wallet: {
|
|
167
|
+
key: "secret-key",
|
|
168
|
+
address: "addr1..."
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
sprinkle.saveSettings();
|
|
172
|
+
const content = fs.readFileSync(path.join(tmpDir, "profiles", "test.json"), "utf-8");
|
|
173
|
+
const parsed = JSON.parse(content);
|
|
174
|
+
(0, _bunTest.expect)(parsed.settings.wallet.key).toBe("ENC:secret-key");
|
|
175
|
+
(0, _bunTest.expect)(parsed.settings.wallet.address).toBe("addr1...");
|
|
176
|
+
});
|
|
177
|
+
(0, _bunTest.test)("handles sensitive fields in union variants", () => {
|
|
178
|
+
const schema = _index.Type.Union([_index.Type.Object({
|
|
179
|
+
type: _index.Type.Literal("hot"),
|
|
180
|
+
privateKey: _index.Type.String({
|
|
181
|
+
sensitive: true
|
|
182
|
+
})
|
|
183
|
+
}), _index.Type.Object({
|
|
184
|
+
type: _index.Type.Literal("cold"),
|
|
185
|
+
address: _index.Type.String()
|
|
186
|
+
})]);
|
|
187
|
+
const outerSchema = _index.Type.Object({
|
|
188
|
+
wallet: schema
|
|
189
|
+
});
|
|
190
|
+
const sprinkle = (0, _testHelpers.withProfile)(new _index.Sprinkle(outerSchema, tmpDir, {
|
|
191
|
+
encryption: {
|
|
192
|
+
encrypt: plain => `ENC:${plain}`,
|
|
193
|
+
decrypt: async cipher => cipher.replace("ENC:", "")
|
|
194
|
+
}
|
|
195
|
+
}));
|
|
196
|
+
sprinkle.settings = {
|
|
197
|
+
wallet: {
|
|
198
|
+
type: "hot",
|
|
199
|
+
privateKey: "my-key"
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
sprinkle.saveSettings();
|
|
203
|
+
const content = fs.readFileSync(path.join(tmpDir, "profiles", "test.json"), "utf-8");
|
|
204
|
+
const parsed = JSON.parse(content);
|
|
205
|
+
(0, _bunTest.expect)(parsed.settings.wallet.privateKey).toBe("ENC:my-key");
|
|
206
|
+
});
|
|
207
|
+
(0, _bunTest.test)("round-trip with encryption preserves all data", async () => {
|
|
208
|
+
const schema = _index.Type.Object({
|
|
209
|
+
network: _index.Type.String(),
|
|
210
|
+
secret: _index.Type.String({
|
|
211
|
+
sensitive: true
|
|
212
|
+
}),
|
|
213
|
+
count: _index.Type.BigInt()
|
|
214
|
+
});
|
|
215
|
+
const opts = {
|
|
216
|
+
encryption: {
|
|
217
|
+
encrypt: plain => Buffer.from(plain).toString("base64"),
|
|
218
|
+
decrypt: async cipher => Buffer.from(cipher, "base64").toString()
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
const s1 = (0, _testHelpers.withProfile)(new _index.Sprinkle(schema, tmpDir, opts));
|
|
222
|
+
s1.settings = {
|
|
223
|
+
network: "mainnet",
|
|
224
|
+
secret: "top-secret",
|
|
225
|
+
count: 42n
|
|
226
|
+
};
|
|
227
|
+
s1.saveSettings();
|
|
228
|
+
const s2 = new _index.Sprinkle(schema, tmpDir, opts);
|
|
229
|
+
await s2.loadProfile("test");
|
|
230
|
+
(0, _bunTest.expect)(s2.settings).toEqual({
|
|
231
|
+
network: "mainnet",
|
|
232
|
+
secret: "top-secret",
|
|
233
|
+
count: 42n
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
(0, _bunTest.describe)("Sprinkle.New with options", () => {
|
|
238
|
+
(0, _bunTest.test)("passes options through to instance and migrates legacy", async () => {
|
|
239
|
+
const schema = _index.Type.Object({
|
|
240
|
+
name: _index.Type.String()
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Write legacy settings file so New triggers migration
|
|
244
|
+
fs.mkdirSync(tmpDir, {
|
|
245
|
+
recursive: true
|
|
246
|
+
});
|
|
247
|
+
fs.writeFileSync(path.join(tmpDir, "settings.json"), JSON.stringify({
|
|
248
|
+
settings: {
|
|
249
|
+
name: "test"
|
|
250
|
+
},
|
|
251
|
+
defaults: {}
|
|
252
|
+
}));
|
|
253
|
+
const sprinkle = await _index.Sprinkle.New(schema, tmpDir, {
|
|
254
|
+
encryption: {
|
|
255
|
+
encrypt: p => p,
|
|
256
|
+
decrypt: async c => c
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
(0, _bunTest.expect)(sprinkle.options.encryption).toBeDefined();
|
|
260
|
+
(0, _bunTest.expect)(sprinkle.profileId).toBe("default");
|
|
261
|
+
(0, _bunTest.expect)(sprinkle.settings).toEqual({
|
|
262
|
+
name: "test"
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
//# sourceMappingURL=encryption.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encryption.test.js","names":["_bunTest","require","_index","fs","_interopRequireWildcard","path","os","_testHelpers","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","mockSelect","mock","mockInput","mockPassword","module","select","input","password","describe","tmpDir","beforeEach","mkdtempSync","join","tmpdir","mockClear","afterEach","rmSync","recursive","force","test","schema","Type","secret","String","sensitive","title","sprinkle","Sprinkle","mockResolvedValueOnce","result","FillInStruct","expect","toEqual","toHaveBeenCalledTimes","calls","message","toBe","not","toHaveBeenCalled","name","p","defaults","toBeUndefined","withProfile","encryption","encrypt","plain","decrypt","cipher","replace","settings","saveSettings","content","readFileSync","parsed","JSON","parse","sprinkle1","sprinkle2","loadProfile","wallet","key","address","Union","type","Literal","privateKey","outerSchema","network","count","BigInt","opts","Buffer","from","toString","s1","s2","mkdirSync","writeFileSync","stringify","New","c","options","toBeDefined","profileId"],"sources":["../../../../src/Sprinkle/__tests__/encryption.test.ts"],"sourcesContent":["import { describe, expect, test, mock, beforeEach, afterEach } from \"bun:test\";\nimport { Sprinkle, Type } from \"../index.js\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport * as os from \"os\";\nimport { withProfile } from \"./test-helpers.js\";\n\nconst mockSelect = mock();\nconst mockInput = mock();\nconst mockPassword = mock();\n\nmock.module(\"@inquirer/prompts\", () => ({\n select: mockSelect,\n input: mockInput,\n password: mockPassword,\n}));\n\ndescribe(\"Encryption & Sensitive Fields\", () => {\n let tmpDir: string;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), \"sprinkles-enc-test-\"));\n mockSelect.mockClear();\n mockInput.mockClear();\n mockPassword.mockClear();\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n describe(\"sensitive field prompts\", () => {\n test(\"uses password() for sensitive string fields\", async () => {\n const schema = Type.Object({\n secret: Type.String({ sensitive: true, title: \"Enter secret\" }),\n });\n const sprinkle = new Sprinkle(schema, tmpDir);\n\n mockPassword.mockResolvedValueOnce(\"my-secret\");\n\n const result = await sprinkle.FillInStruct(schema);\n expect(result).toEqual({ secret: \"my-secret\" });\n expect(mockPassword).toHaveBeenCalledTimes(1);\n expect(mockPassword.mock.calls[0][0].message).toBe(\"Enter secret\");\n expect(mockInput).not.toHaveBeenCalled();\n });\n\n test(\"uses input() for non-sensitive string fields\", async () => {\n const schema = Type.Object({\n name: Type.String({ title: \"Enter name\" }),\n });\n const sprinkle = new Sprinkle(schema, tmpDir);\n\n mockInput.mockResolvedValueOnce(\"visible\");\n\n const result = await sprinkle.FillInStruct(schema);\n expect(result).toEqual({ name: \"visible\" });\n expect(mockInput).toHaveBeenCalledTimes(1);\n expect(mockPassword).not.toHaveBeenCalled();\n });\n\n test(\"does not remember sensitive field as default\", async () => {\n const schema = Type.String({ sensitive: true });\n const sprinkle = new Sprinkle(Type.Object({ p: Type.String() }), tmpDir);\n\n mockPassword.mockResolvedValueOnce(\"secret-val\");\n\n await sprinkle.FillInStruct(schema);\n expect(sprinkle.defaults[\"string\"]).toBeUndefined();\n });\n });\n\n describe(\"encryption on save/load\", () => {\n test(\"encrypts sensitive fields when saving with encryption configured\", () => {\n const schema = Type.Object({\n name: Type.String(),\n secret: Type.String({ sensitive: true }),\n });\n const sprinkle = withProfile(\n new Sprinkle(schema, tmpDir, {\n encryption: {\n encrypt: (plain) => `ENC:${plain}`,\n decrypt: async (cipher) => cipher.replace(\"ENC:\", \"\"),\n },\n }),\n );\n sprinkle.settings = { name: \"visible\", secret: \"hidden\" } as any;\n\n sprinkle.saveSettings();\n\n const content = fs.readFileSync(\n path.join(tmpDir, \"profiles\", \"test.json\"),\n \"utf-8\",\n );\n const parsed = JSON.parse(content);\n expect(parsed.settings.name).toBe(\"visible\");\n expect(parsed.settings.secret).toBe(\"ENC:hidden\");\n });\n\n test(\"decrypts sensitive fields when loading with encryption configured\", async () => {\n const schema = Type.Object({\n name: Type.String(),\n secret: Type.String({ sensitive: true }),\n });\n\n // First save with encryption\n const sprinkle1 = withProfile(\n new Sprinkle(schema, tmpDir, {\n encryption: {\n encrypt: (plain) => `ENC:${plain}`,\n decrypt: async (cipher) => cipher.replace(\"ENC:\", \"\"),\n },\n }),\n );\n sprinkle1.settings = { name: \"visible\", secret: \"hidden\" } as any;\n sprinkle1.saveSettings();\n\n // Then load with same encryption\n const sprinkle2 = new Sprinkle(schema, tmpDir, {\n encryption: {\n encrypt: (plain) => `ENC:${plain}`,\n decrypt: async (cipher) => cipher.replace(\"ENC:\", \"\"),\n },\n });\n await sprinkle2.loadProfile(\"test\");\n\n expect(sprinkle2.settings).toEqual({ name: \"visible\", secret: \"hidden\" });\n });\n\n test(\"does not encrypt when no encryption configured\", () => {\n const schema = Type.Object({\n secret: Type.String({ sensitive: true }),\n });\n const sprinkle = withProfile(new Sprinkle(schema, tmpDir));\n sprinkle.settings = { secret: \"plain-value\" } as any;\n\n sprinkle.saveSettings();\n\n const content = fs.readFileSync(\n path.join(tmpDir, \"profiles\", \"test.json\"),\n \"utf-8\",\n );\n const parsed = JSON.parse(content);\n expect(parsed.settings.secret).toBe(\"plain-value\");\n });\n\n test(\"encrypts nested sensitive fields\", () => {\n const schema = Type.Object({\n wallet: Type.Object({\n key: Type.String({ sensitive: true }),\n address: Type.String(),\n }),\n });\n const sprinkle = withProfile(\n new Sprinkle(schema, tmpDir, {\n encryption: {\n encrypt: (plain) => `ENC:${plain}`,\n decrypt: async (cipher) => cipher.replace(\"ENC:\", \"\"),\n },\n }),\n );\n sprinkle.settings = {\n wallet: { key: \"secret-key\", address: \"addr1...\" },\n } as any;\n\n sprinkle.saveSettings();\n\n const content = fs.readFileSync(\n path.join(tmpDir, \"profiles\", \"test.json\"),\n \"utf-8\",\n );\n const parsed = JSON.parse(content);\n expect(parsed.settings.wallet.key).toBe(\"ENC:secret-key\");\n expect(parsed.settings.wallet.address).toBe(\"addr1...\");\n });\n\n test(\"handles sensitive fields in union variants\", () => {\n const schema = Type.Union([\n Type.Object({\n type: Type.Literal(\"hot\"),\n privateKey: Type.String({ sensitive: true }),\n }),\n Type.Object({\n type: Type.Literal(\"cold\"),\n address: Type.String(),\n }),\n ]);\n const outerSchema = Type.Object({ wallet: schema });\n const sprinkle = withProfile(\n new Sprinkle(outerSchema, tmpDir, {\n encryption: {\n encrypt: (plain) => `ENC:${plain}`,\n decrypt: async (cipher) => cipher.replace(\"ENC:\", \"\"),\n },\n }),\n );\n sprinkle.settings = {\n wallet: { type: \"hot\", privateKey: \"my-key\" },\n } as any;\n\n sprinkle.saveSettings();\n\n const content = fs.readFileSync(\n path.join(tmpDir, \"profiles\", \"test.json\"),\n \"utf-8\",\n );\n const parsed = JSON.parse(content);\n expect(parsed.settings.wallet.privateKey).toBe(\"ENC:my-key\");\n });\n\n test(\"round-trip with encryption preserves all data\", async () => {\n const schema = Type.Object({\n network: Type.String(),\n secret: Type.String({ sensitive: true }),\n count: Type.BigInt(),\n });\n const opts = {\n encryption: {\n encrypt: (plain: string) => Buffer.from(plain).toString(\"base64\"),\n decrypt: async (cipher: string) =>\n Buffer.from(cipher, \"base64\").toString(),\n },\n };\n const s1 = withProfile(new Sprinkle(schema, tmpDir, opts));\n s1.settings = {\n network: \"mainnet\",\n secret: \"top-secret\",\n count: 42n,\n } as any;\n s1.saveSettings();\n\n const s2 = new Sprinkle(schema, tmpDir, opts);\n await s2.loadProfile(\"test\");\n\n expect(s2.settings).toEqual({\n network: \"mainnet\",\n secret: \"top-secret\",\n count: 42n,\n });\n });\n });\n\n describe(\"Sprinkle.New with options\", () => {\n test(\"passes options through to instance and migrates legacy\", async () => {\n const schema = Type.Object({ name: Type.String() });\n\n // Write legacy settings file so New triggers migration\n fs.mkdirSync(tmpDir, { recursive: true });\n fs.writeFileSync(\n path.join(tmpDir, \"settings.json\"),\n JSON.stringify({ settings: { name: \"test\" }, defaults: {} }),\n );\n\n const sprinkle = await Sprinkle.New(schema, tmpDir, {\n encryption: {\n encrypt: (p) => p,\n decrypt: async (c) => c,\n },\n });\n\n expect(sprinkle.options.encryption).toBeDefined();\n expect(sprinkle.profileId).toBe(\"default\");\n expect(sprinkle.settings).toEqual({ name: \"test\" });\n });\n });\n});\n"],"mappings":"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","ignoreList":[]}
|