@sundaeswap/sprinkles 0.4.0 → 0.6.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__/encryption.test.js +22 -8
- package/dist/cjs/Sprinkle/__tests__/encryption.test.js.map +1 -1
- package/dist/cjs/Sprinkle/__tests__/enhancements.test.js +37 -46
- package/dist/cjs/Sprinkle/__tests__/enhancements.test.js.map +1 -1
- package/dist/cjs/Sprinkle/__tests__/field-utils.test.js +170 -0
- package/dist/cjs/Sprinkle/__tests__/field-utils.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js +283 -81
- package/dist/cjs/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -1
- package/dist/cjs/Sprinkle/__tests__/formatting.test.js +97 -0
- package/dist/cjs/Sprinkle/__tests__/formatting.test.js.map +1 -0
- package/dist/cjs/Sprinkle/__tests__/show-menu.test.js +97 -7
- package/dist/cjs/Sprinkle/__tests__/show-menu.test.js.map +1 -1
- package/dist/cjs/Sprinkle/__tests__/tx-dialog.test.js +30 -0
- package/dist/cjs/Sprinkle/__tests__/tx-dialog.test.js.map +1 -1
- package/dist/cjs/Sprinkle/encryption.js +131 -0
- package/dist/cjs/Sprinkle/encryption.js.map +1 -0
- package/dist/cjs/Sprinkle/index.js +427 -438
- package/dist/cjs/Sprinkle/index.js.map +1 -1
- package/dist/cjs/Sprinkle/menus/array-menu.js +195 -0
- package/dist/cjs/Sprinkle/menus/array-menu.js.map +1 -0
- package/dist/cjs/Sprinkle/menus/field-menu.js +161 -0
- package/dist/cjs/Sprinkle/menus/field-menu.js.map +1 -0
- package/dist/cjs/Sprinkle/menus/index.js +33 -0
- package/dist/cjs/Sprinkle/menus/index.js.map +1 -0
- package/dist/cjs/Sprinkle/menus/object-menu.js +324 -0
- package/dist/cjs/Sprinkle/menus/object-menu.js.map +1 -0
- package/dist/cjs/Sprinkle/prompts.js +459 -0
- package/dist/cjs/Sprinkle/prompts.js.map +1 -0
- package/dist/cjs/Sprinkle/schemas.js +97 -0
- package/dist/cjs/Sprinkle/schemas.js.map +1 -0
- package/dist/cjs/Sprinkle/tx-dialog.js +101 -0
- package/dist/cjs/Sprinkle/tx-dialog.js.map +1 -0
- package/dist/cjs/Sprinkle/type-guards.js +89 -0
- package/dist/cjs/Sprinkle/type-guards.js.map +1 -0
- package/dist/cjs/Sprinkle/types.js +73 -0
- package/dist/cjs/Sprinkle/types.js.map +1 -0
- package/dist/cjs/Sprinkle/utils/field-utils.js +154 -0
- package/dist/cjs/Sprinkle/utils/field-utils.js.map +1 -0
- package/dist/cjs/Sprinkle/utils/formatting.js +126 -0
- package/dist/cjs/Sprinkle/utils/formatting.js.map +1 -0
- package/dist/cjs/Sprinkle/utils/index.js +56 -0
- package/dist/cjs/Sprinkle/utils/index.js.map +1 -0
- package/dist/cjs/Sprinkle/wallet.js +98 -0
- package/dist/cjs/Sprinkle/wallet.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/encryption.test.js +22 -8
- package/dist/esm/Sprinkle/__tests__/encryption.test.js.map +1 -1
- package/dist/esm/Sprinkle/__tests__/enhancements.test.js +37 -46
- package/dist/esm/Sprinkle/__tests__/enhancements.test.js.map +1 -1
- package/dist/esm/Sprinkle/__tests__/field-utils.test.js +168 -0
- package/dist/esm/Sprinkle/__tests__/field-utils.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js +284 -82
- package/dist/esm/Sprinkle/__tests__/fill-in-struct.test.js.map +1 -1
- package/dist/esm/Sprinkle/__tests__/formatting.test.js +95 -0
- package/dist/esm/Sprinkle/__tests__/formatting.test.js.map +1 -0
- package/dist/esm/Sprinkle/__tests__/show-menu.test.js +98 -8
- package/dist/esm/Sprinkle/__tests__/show-menu.test.js.map +1 -1
- package/dist/esm/Sprinkle/__tests__/tx-dialog.test.js +30 -0
- package/dist/esm/Sprinkle/__tests__/tx-dialog.test.js.map +1 -1
- package/dist/esm/Sprinkle/encryption.js +117 -0
- package/dist/esm/Sprinkle/encryption.js.map +1 -0
- package/dist/esm/Sprinkle/index.js +248 -425
- package/dist/esm/Sprinkle/index.js.map +1 -1
- package/dist/esm/Sprinkle/menus/array-menu.js +190 -0
- package/dist/esm/Sprinkle/menus/array-menu.js.map +1 -0
- package/dist/esm/Sprinkle/menus/field-menu.js +155 -0
- package/dist/esm/Sprinkle/menus/field-menu.js.map +1 -0
- package/dist/esm/Sprinkle/menus/index.js +8 -0
- package/dist/esm/Sprinkle/menus/index.js.map +1 -0
- package/dist/esm/Sprinkle/menus/object-menu.js +318 -0
- package/dist/esm/Sprinkle/menus/object-menu.js.map +1 -0
- package/dist/esm/Sprinkle/prompts.js +443 -0
- package/dist/esm/Sprinkle/prompts.js.map +1 -0
- package/dist/esm/Sprinkle/schemas.js +91 -0
- package/dist/esm/Sprinkle/schemas.js.map +1 -0
- package/dist/esm/Sprinkle/tx-dialog.js +90 -0
- package/dist/esm/Sprinkle/tx-dialog.js.map +1 -0
- package/dist/esm/Sprinkle/type-guards.js +66 -0
- package/dist/esm/Sprinkle/type-guards.js.map +1 -0
- package/dist/esm/Sprinkle/types.js +66 -0
- package/dist/esm/Sprinkle/types.js.map +1 -0
- package/dist/esm/Sprinkle/utils/field-utils.js +145 -0
- package/dist/esm/Sprinkle/utils/field-utils.js.map +1 -0
- package/dist/esm/Sprinkle/utils/formatting.js +118 -0
- package/dist/esm/Sprinkle/utils/formatting.js.map +1 -0
- package/dist/esm/Sprinkle/utils/index.js +7 -0
- package/dist/esm/Sprinkle/utils/index.js.map +1 -0
- package/dist/esm/Sprinkle/wallet.js +90 -0
- package/dist/esm/Sprinkle/wallet.js.map +1 -0
- package/dist/types/Sprinkle/encryption.d.ts +43 -0
- package/dist/types/Sprinkle/encryption.d.ts.map +1 -0
- package/dist/types/Sprinkle/index.d.ts +17 -177
- package/dist/types/Sprinkle/index.d.ts.map +1 -1
- package/dist/types/Sprinkle/menus/array-menu.d.ts +31 -0
- package/dist/types/Sprinkle/menus/array-menu.d.ts.map +1 -0
- package/dist/types/Sprinkle/menus/field-menu.d.ts +34 -0
- package/dist/types/Sprinkle/menus/field-menu.d.ts.map +1 -0
- package/dist/types/Sprinkle/menus/index.d.ts +10 -0
- package/dist/types/Sprinkle/menus/index.d.ts.map +1 -0
- package/dist/types/Sprinkle/menus/object-menu.d.ts +34 -0
- package/dist/types/Sprinkle/menus/object-menu.d.ts.map +1 -0
- package/dist/types/Sprinkle/prompts.d.ts +119 -0
- package/dist/types/Sprinkle/prompts.d.ts.map +1 -0
- package/dist/types/Sprinkle/schemas.d.ts +125 -0
- package/dist/types/Sprinkle/schemas.d.ts.map +1 -0
- package/dist/types/Sprinkle/tx-dialog.d.ts +37 -0
- package/dist/types/Sprinkle/tx-dialog.d.ts.map +1 -0
- package/dist/types/Sprinkle/type-guards.d.ts +45 -0
- package/dist/types/Sprinkle/type-guards.d.ts.map +1 -0
- package/dist/types/Sprinkle/types.d.ts +115 -0
- package/dist/types/Sprinkle/types.d.ts.map +1 -0
- package/dist/types/Sprinkle/utils/field-utils.d.ts +47 -0
- package/dist/types/Sprinkle/utils/field-utils.d.ts.map +1 -0
- package/dist/types/Sprinkle/utils/formatting.d.ts +30 -0
- package/dist/types/Sprinkle/utils/formatting.d.ts.map +1 -0
- package/dist/types/Sprinkle/wallet.d.ts +27 -0
- package/dist/types/Sprinkle/wallet.d.ts.map +1 -0
- package/dist/types/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/Sprinkle/__tests__/encryption.test.ts +23 -8
- package/src/Sprinkle/__tests__/enhancements.test.ts +34 -47
- package/src/Sprinkle/__tests__/field-utils.test.ts +191 -0
- package/src/Sprinkle/__tests__/fill-in-struct.test.ts +301 -86
- package/src/Sprinkle/__tests__/formatting.test.ts +115 -0
- package/src/Sprinkle/__tests__/show-menu.test.ts +102 -8
- package/src/Sprinkle/__tests__/tx-dialog.test.ts +30 -0
- package/src/Sprinkle/encryption.ts +130 -0
- package/src/Sprinkle/index.ts +368 -598
- package/src/Sprinkle/menus/array-menu.ts +191 -0
- package/src/Sprinkle/menus/field-menu.ts +145 -0
- package/src/Sprinkle/menus/index.ts +12 -0
- package/src/Sprinkle/menus/object-menu.ts +336 -0
- package/src/Sprinkle/prompts.ts +551 -0
- package/src/Sprinkle/schemas.ts +111 -0
- package/src/Sprinkle/tx-dialog.ts +100 -0
- package/src/Sprinkle/type-guards.ts +93 -0
- package/src/Sprinkle/types.ts +116 -0
- package/src/Sprinkle/utils/field-utils.ts +158 -0
- package/src/Sprinkle/utils/formatting.ts +127 -0
- package/src/Sprinkle/utils/index.ts +17 -0
- package/src/Sprinkle/wallet.ts +133 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transaction dialog helper utilities.
|
|
3
|
+
* Functions for working with transaction signatures and display.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { CborSet, VkeyWitness, blake2b_256 } from "@blaze-cardano/core";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get the payment key hash from a HotWallet's first address.
|
|
10
|
+
*/
|
|
11
|
+
export async function getWalletPaymentKeyHash(wallet) {
|
|
12
|
+
try {
|
|
13
|
+
const addresses = await wallet.getUsedAddresses();
|
|
14
|
+
const address = addresses[0];
|
|
15
|
+
if (!address) return null;
|
|
16
|
+
const paymentCredential = address.asBase()?.getPaymentCredential();
|
|
17
|
+
return paymentCredential?.hash?.toString() ?? null;
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Count the number of vkey signatures in a transaction's witness set.
|
|
25
|
+
*/
|
|
26
|
+
export function countSignatures(tx) {
|
|
27
|
+
const vkeys = tx.witnessSet().vkeys();
|
|
28
|
+
return vkeys ? vkeys.size() : 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if a specific public key has already signed the transaction.
|
|
33
|
+
* Compares by vkey (public key bytes).
|
|
34
|
+
*/
|
|
35
|
+
export function hasVkeySigned(tx, vkeyHex) {
|
|
36
|
+
const vkeys = tx.witnessSet().vkeys();
|
|
37
|
+
if (!vkeys) return false;
|
|
38
|
+
const vkeyArray = vkeys.toCore();
|
|
39
|
+
return vkeyArray.some(([vkey]) => vkey === vkeyHex);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get the list of required signer key hashes from the transaction body.
|
|
44
|
+
*/
|
|
45
|
+
export function getRequiredSigners(tx) {
|
|
46
|
+
const requiredSigners = tx.body().requiredSigners();
|
|
47
|
+
if (!requiredSigners) return [];
|
|
48
|
+
return Array.from(requiredSigners.values()).map(s => s.toString());
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Compute the transaction body hash for display.
|
|
53
|
+
*/
|
|
54
|
+
export function getTxBodyHash(tx) {
|
|
55
|
+
const bodyCbor = tx.body().toCbor();
|
|
56
|
+
return blake2b_256(bodyCbor);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Format a hash for display: first 8 chars + ... + last 8 chars.
|
|
61
|
+
*/
|
|
62
|
+
export function formatHash(hash) {
|
|
63
|
+
if (hash.length <= 20) return hash;
|
|
64
|
+
return `${hash.slice(0, 8)}...${hash.slice(-8)}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Merge signatures from source transaction into target transaction.
|
|
69
|
+
* Prevents duplicate signatures by comparing vkey (public key).
|
|
70
|
+
* Returns the count of newly added signatures.
|
|
71
|
+
*/
|
|
72
|
+
export function mergeSignatures(target, source) {
|
|
73
|
+
const targetWs = target.witnessSet();
|
|
74
|
+
const sourceWs = source.witnessSet();
|
|
75
|
+
const targetVkeys = targetWs.vkeys()?.toCore() ?? [];
|
|
76
|
+
const sourceVkeys = sourceWs.vkeys()?.toCore() ?? [];
|
|
77
|
+
|
|
78
|
+
// Find vkeys in source that aren't in target (by comparing public key)
|
|
79
|
+
const existingPubKeys = new Set(targetVkeys.map(([vkey]) => vkey));
|
|
80
|
+
const newVkeys = sourceVkeys.filter(([vkey]) => !existingPubKeys.has(vkey));
|
|
81
|
+
if (newVkeys.length === 0) {
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Merge the new vkeys into target
|
|
86
|
+
targetWs.setVkeys(CborSet.fromCore([...targetVkeys, ...newVkeys], VkeyWitness.fromCore));
|
|
87
|
+
target.setWitnessSet(targetWs);
|
|
88
|
+
return newVkeys.length;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=tx-dialog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tx-dialog.js","names":["CborSet","VkeyWitness","blake2b_256","getWalletPaymentKeyHash","wallet","addresses","getUsedAddresses","address","paymentCredential","asBase","getPaymentCredential","hash","toString","countSignatures","tx","vkeys","witnessSet","size","hasVkeySigned","vkeyHex","vkeyArray","toCore","some","vkey","getRequiredSigners","requiredSigners","body","Array","from","values","map","s","getTxBodyHash","bodyCbor","toCbor","formatHash","length","slice","mergeSignatures","target","source","targetWs","sourceWs","targetVkeys","sourceVkeys","existingPubKeys","Set","newVkeys","filter","has","setVkeys","fromCore","setWitnessSet"],"sources":["../../../src/Sprinkle/tx-dialog.ts"],"sourcesContent":["/**\n * Transaction dialog helper utilities.\n * Functions for working with transaction signatures and display.\n */\n\nimport { Core, HotWallet } from \"@blaze-cardano/sdk\";\nimport { CborSet, VkeyWitness, blake2b_256 } from \"@blaze-cardano/core\";\n\n/**\n * Get the payment key hash from a HotWallet's first address.\n */\nexport async function getWalletPaymentKeyHash(\n wallet: HotWallet,\n): Promise<string | null> {\n try {\n const addresses = await wallet.getUsedAddresses();\n const address = addresses[0];\n if (!address) return null;\n const paymentCredential = address.asBase()?.getPaymentCredential();\n return paymentCredential?.hash?.toString() ?? null;\n } catch {\n return null;\n }\n}\n\n/**\n * Count the number of vkey signatures in a transaction's witness set.\n */\nexport function countSignatures(tx: Core.Transaction): number {\n const vkeys = tx.witnessSet().vkeys();\n return vkeys ? vkeys.size() : 0;\n}\n\n/**\n * Check if a specific public key has already signed the transaction.\n * Compares by vkey (public key bytes).\n */\nexport function hasVkeySigned(tx: Core.Transaction, vkeyHex: string): boolean {\n const vkeys = tx.witnessSet().vkeys();\n if (!vkeys) return false;\n const vkeyArray = vkeys.toCore();\n return vkeyArray.some(([vkey]) => vkey === vkeyHex);\n}\n\n/**\n * Get the list of required signer key hashes from the transaction body.\n */\nexport function getRequiredSigners(tx: Core.Transaction): string[] {\n const requiredSigners = tx.body().requiredSigners();\n if (!requiredSigners) return [];\n return Array.from(requiredSigners.values()).map((s) => s.toString());\n}\n\n/**\n * Compute the transaction body hash for display.\n */\nexport function getTxBodyHash(tx: Core.Transaction): string {\n const bodyCbor = tx.body().toCbor();\n return blake2b_256(bodyCbor);\n}\n\n/**\n * Format a hash for display: first 8 chars + ... + last 8 chars.\n */\nexport function formatHash(hash: string): string {\n if (hash.length <= 20) return hash;\n return `${hash.slice(0, 8)}...${hash.slice(-8)}`;\n}\n\n/**\n * Merge signatures from source transaction into target transaction.\n * Prevents duplicate signatures by comparing vkey (public key).\n * Returns the count of newly added signatures.\n */\nexport function mergeSignatures(\n target: Core.Transaction,\n source: Core.Transaction,\n): number {\n const targetWs = target.witnessSet();\n const sourceWs = source.witnessSet();\n\n const targetVkeys = targetWs.vkeys()?.toCore() ?? [];\n const sourceVkeys = sourceWs.vkeys()?.toCore() ?? [];\n\n // Find vkeys in source that aren't in target (by comparing public key)\n const existingPubKeys = new Set(targetVkeys.map(([vkey]) => vkey));\n const newVkeys = sourceVkeys.filter(([vkey]) => !existingPubKeys.has(vkey));\n\n if (newVkeys.length === 0) {\n return 0;\n }\n\n // Merge the new vkeys into target\n targetWs.setVkeys(\n CborSet.fromCore([...targetVkeys, ...newVkeys], VkeyWitness.fromCore),\n );\n target.setWitnessSet(targetWs);\n\n return newVkeys.length;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type guard utilities for TypeBox schema types.
|
|
3
|
+
* These are internal helpers used by FillInStruct, encryption, and other modules.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Kind, OptionalKind } from "@sinclair/typebox";
|
|
7
|
+
export const isOptional = t => t[OptionalKind] === "Optional";
|
|
8
|
+
export const isImport = t => t[Kind] === "Import";
|
|
9
|
+
export const isArray = t => t[Kind] === "Array";
|
|
10
|
+
export const isBigInt = t => t[Kind] === "BigInt";
|
|
11
|
+
export const isLiteral = t => t[Kind] === "Literal";
|
|
12
|
+
export const isObject = t => t[Kind] === "Object";
|
|
13
|
+
export const isRef = t => t[Kind] === "Ref";
|
|
14
|
+
export const isString = t => t[Kind] === "String";
|
|
15
|
+
export const isThis = t => t[Kind] === "This";
|
|
16
|
+
export const isTuple = t => t[Kind] === "Tuple";
|
|
17
|
+
export const isUnion = t => t[Kind] === "Union";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Check if a schema field is marked as sensitive.
|
|
21
|
+
* Sensitive fields are encrypted when saved and masked when displayed.
|
|
22
|
+
*/
|
|
23
|
+
export const isSensitive = t => isString(t) && t.sensitive === true;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check if a schema is a Null type.
|
|
27
|
+
*/
|
|
28
|
+
export const isNull = t => t[Kind] === "Null";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Check if a schema allows null values.
|
|
32
|
+
* Returns true for TNull or unions containing TNull.
|
|
33
|
+
*/
|
|
34
|
+
export const isNullable = t => {
|
|
35
|
+
if (isNull(t)) return true;
|
|
36
|
+
if (isUnion(t)) return t.anyOf.some(member => isNull(member));
|
|
37
|
+
return false;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Unwrap a nullable type to get the non-null inner type.
|
|
42
|
+
* For unions with Null, returns the union without the Null member.
|
|
43
|
+
* For single non-null member unions, returns just that type.
|
|
44
|
+
*/
|
|
45
|
+
export const unwrapNullable = t => {
|
|
46
|
+
if (isUnion(t)) {
|
|
47
|
+
const nonNull = t.anyOf.filter(member => !isNull(member));
|
|
48
|
+
if (nonNull.length === 1) return nonNull[0];
|
|
49
|
+
return {
|
|
50
|
+
...t,
|
|
51
|
+
anyOf: nonNull
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return t;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if a schema has a default value defined.
|
|
59
|
+
*/
|
|
60
|
+
export const hasDefault = t => "default" in t;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get the default value from a schema, if defined.
|
|
64
|
+
*/
|
|
65
|
+
export const getDefault = t => t.default;
|
|
66
|
+
//# sourceMappingURL=type-guards.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"type-guards.js","names":["Kind","OptionalKind","isOptional","t","isImport","isArray","isBigInt","isLiteral","isObject","isRef","isString","isThis","isTuple","isUnion","isSensitive","sensitive","isNull","isNullable","anyOf","some","member","unwrapNullable","nonNull","filter","length","hasDefault","getDefault","default"],"sources":["../../../src/Sprinkle/type-guards.ts"],"sourcesContent":["/**\n * Type guard utilities for TypeBox schema types.\n * These are internal helpers used by FillInStruct, encryption, and other modules.\n */\n\nimport {\n Kind,\n type TBigInt,\n type TLiteral,\n type TNull,\n type TObject,\n type TSchema,\n type TString,\n type TTuple,\n type TUnion,\n type TArray,\n type TThis,\n type TRef,\n type TImport,\n type TOptional,\n type Static,\n OptionalKind,\n} from \"@sinclair/typebox\";\n\nexport const isOptional = <T extends TSchema>(t: T): t is TOptional<T> =>\n t[OptionalKind] === \"Optional\";\n\nexport const isImport = (t: TSchema): t is TImport => t[Kind] === \"Import\";\n\nexport const isArray = (t: TSchema): t is TArray => t[Kind] === \"Array\";\n\nexport const isBigInt = (t: TSchema): t is TBigInt => t[Kind] === \"BigInt\";\n\nexport const isLiteral = (t: TSchema): t is TLiteral => t[Kind] === \"Literal\";\n\nexport const isObject = (t: TSchema): t is TObject => t[Kind] === \"Object\";\n\nexport const isRef = (t: TSchema): t is TRef => t[Kind] === \"Ref\";\n\nexport const isString = (t: TSchema): t is TString => t[Kind] === \"String\";\n\nexport const isThis = (t: TSchema): t is TThis => t[Kind] === \"This\";\n\nexport const isTuple = (t: TSchema): t is TTuple => t[Kind] === \"Tuple\";\n\nexport const isUnion = (t: TSchema): t is TUnion => t[Kind] === \"Union\";\n\n/**\n * Check if a schema field is marked as sensitive.\n * Sensitive fields are encrypted when saved and masked when displayed.\n */\nexport const isSensitive = (t: TSchema): boolean =>\n isString(t) && t.sensitive === true;\n\n/**\n * Check if a schema is a Null type.\n */\nexport const isNull = (t: TSchema): t is TNull => t[Kind] === \"Null\";\n\n/**\n * Check if a schema allows null values.\n * Returns true for TNull or unions containing TNull.\n */\nexport const isNullable = (t: TSchema): boolean => {\n if (isNull(t)) return true;\n if (isUnion(t)) return t.anyOf.some((member) => isNull(member));\n return false;\n};\n\n/**\n * Unwrap a nullable type to get the non-null inner type.\n * For unions with Null, returns the union without the Null member.\n * For single non-null member unions, returns just that type.\n */\nexport const unwrapNullable = (t: TSchema): TSchema => {\n if (isUnion(t)) {\n const nonNull = t.anyOf.filter((member) => !isNull(member));\n if (nonNull.length === 1) return nonNull[0]!;\n return { ...t, anyOf: nonNull } as TSchema;\n }\n return t;\n};\n\n/**\n * Check if a schema has a default value defined.\n */\nexport const hasDefault = (t: TSchema): boolean => \"default\" in t;\n\n/**\n * Get the default value from a schema, if defined.\n */\nexport const getDefault = <T extends TSchema>(t: T): Static<T> | undefined =>\n t.default as Static<T> | undefined;\n"],"mappings":"AAAA;AACA;AACA;AACA;;AAEA,SACEA,IAAI,EAeJC,YAAY,QACP,mBAAmB;AAE1B,OAAO,MAAMC,UAAU,GAAuBC,CAAI,IAChDA,CAAC,CAACF,YAAY,CAAC,KAAK,UAAU;AAEhC,OAAO,MAAMG,QAAQ,GAAID,CAAU,IAAmBA,CAAC,CAACH,IAAI,CAAC,KAAK,QAAQ;AAE1E,OAAO,MAAMK,OAAO,GAAIF,CAAU,IAAkBA,CAAC,CAACH,IAAI,CAAC,KAAK,OAAO;AAEvE,OAAO,MAAMM,QAAQ,GAAIH,CAAU,IAAmBA,CAAC,CAACH,IAAI,CAAC,KAAK,QAAQ;AAE1E,OAAO,MAAMO,SAAS,GAAIJ,CAAU,IAAoBA,CAAC,CAACH,IAAI,CAAC,KAAK,SAAS;AAE7E,OAAO,MAAMQ,QAAQ,GAAIL,CAAU,IAAmBA,CAAC,CAACH,IAAI,CAAC,KAAK,QAAQ;AAE1E,OAAO,MAAMS,KAAK,GAAIN,CAAU,IAAgBA,CAAC,CAACH,IAAI,CAAC,KAAK,KAAK;AAEjE,OAAO,MAAMU,QAAQ,GAAIP,CAAU,IAAmBA,CAAC,CAACH,IAAI,CAAC,KAAK,QAAQ;AAE1E,OAAO,MAAMW,MAAM,GAAIR,CAAU,IAAiBA,CAAC,CAACH,IAAI,CAAC,KAAK,MAAM;AAEpE,OAAO,MAAMY,OAAO,GAAIT,CAAU,IAAkBA,CAAC,CAACH,IAAI,CAAC,KAAK,OAAO;AAEvE,OAAO,MAAMa,OAAO,GAAIV,CAAU,IAAkBA,CAAC,CAACH,IAAI,CAAC,KAAK,OAAO;;AAEvE;AACA;AACA;AACA;AACA,OAAO,MAAMc,WAAW,GAAIX,CAAU,IACpCO,QAAQ,CAACP,CAAC,CAAC,IAAIA,CAAC,CAACY,SAAS,KAAK,IAAI;;AAErC;AACA;AACA;AACA,OAAO,MAAMC,MAAM,GAAIb,CAAU,IAAiBA,CAAC,CAACH,IAAI,CAAC,KAAK,MAAM;;AAEpE;AACA;AACA;AACA;AACA,OAAO,MAAMiB,UAAU,GAAId,CAAU,IAAc;EACjD,IAAIa,MAAM,CAACb,CAAC,CAAC,EAAE,OAAO,IAAI;EAC1B,IAAIU,OAAO,CAACV,CAAC,CAAC,EAAE,OAAOA,CAAC,CAACe,KAAK,CAACC,IAAI,CAAEC,MAAM,IAAKJ,MAAM,CAACI,MAAM,CAAC,CAAC;EAC/D,OAAO,KAAK;AACd,CAAC;;AAED;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,cAAc,GAAIlB,CAAU,IAAc;EACrD,IAAIU,OAAO,CAACV,CAAC,CAAC,EAAE;IACd,MAAMmB,OAAO,GAAGnB,CAAC,CAACe,KAAK,CAACK,MAAM,CAAEH,MAAM,IAAK,CAACJ,MAAM,CAACI,MAAM,CAAC,CAAC;IAC3D,IAAIE,OAAO,CAACE,MAAM,KAAK,CAAC,EAAE,OAAOF,OAAO,CAAC,CAAC,CAAC;IAC3C,OAAO;MAAE,GAAGnB,CAAC;MAAEe,KAAK,EAAEI;IAAQ,CAAC;EACjC;EACA,OAAOnB,CAAC;AACV,CAAC;;AAED;AACA;AACA;AACA,OAAO,MAAMsB,UAAU,GAAItB,CAAU,IAAc,SAAS,IAAIA,CAAC;;AAEjE;AACA;AACA;AACA,OAAO,MAAMuB,UAAU,GAAuBvB,CAAI,IAChDA,CAAC,CAACwB,OAAgC","ignoreList":[]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility type to extract the static type from a TypeBox schema.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Options for encrypting/decrypting sensitive fields in settings.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Options for configuring a Sprinkle instance.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Metadata for a profile.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Current profile information including ID.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Profile entry as returned by scanProfiles().
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Result of the TxDialog interaction.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Options for TxDialog.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Error thrown when user cancels a prompt via Escape key.
|
|
35
|
+
*/
|
|
36
|
+
export class UserCancelledError extends Error {
|
|
37
|
+
constructor(message = "User cancelled the operation") {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "UserCancelledError";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// --- Menu-based FillInStruct types ---
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* State tracking for a single field during menu-based editing.
|
|
47
|
+
*/
|
|
48
|
+
|
|
49
|
+
// explicitly set to null
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Result of counting required fields in a schema.
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Result from field menu interaction.
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Result from object menu interaction.
|
|
61
|
+
*/
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Result from array menu interaction.
|
|
65
|
+
*/
|
|
66
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","names":["UserCancelledError","Error","constructor","message","name"],"sources":["../../../src/Sprinkle/types.ts"],"sourcesContent":["import { Core } from \"@blaze-cardano/sdk\";\nimport type { Static, TSchema } from \"@sinclair/typebox\";\n\n/**\n * Utility type to extract the static type from a TypeBox schema.\n */\nexport type TExact<T> = T extends TSchema ? Static<T> : T;\n\n/**\n * Options for encrypting/decrypting sensitive fields in settings.\n */\nexport interface IEncryptionOptions {\n encrypt: (plaintext: string) => string;\n decrypt: (ciphertext: string) => Promise<string>;\n}\n\n/**\n * Options for configuring a Sprinkle instance.\n */\nexport interface ISprinkleOptions {\n encryption?: IEncryptionOptions;\n}\n\n/**\n * Metadata for a profile.\n */\nexport interface IProfileMeta {\n name: string;\n description?: string;\n createdAt: string;\n updatedAt: string;\n}\n\n/**\n * Current profile information including ID.\n */\nexport interface ICurrentProfile extends IProfileMeta {\n id: string;\n}\n\n/**\n * Profile entry as returned by scanProfiles().\n */\nexport interface IProfileEntry {\n id: string;\n meta: IProfileMeta;\n}\n\n/**\n * Result of the TxDialog interaction.\n */\nexport interface TxDialogResult {\n action: \"submitted\" | \"signed\" | \"cancelled\";\n txId?: string; // present only if action === 'submitted'\n tx: Core.Transaction; // the (potentially signed) transaction\n}\n\n/**\n * Options for TxDialog.\n */\nexport interface TxDialogOptions {\n beforeSign?: () => Promise<void>;\n}\n\n/**\n * Error thrown when user cancels a prompt via Escape key.\n */\nexport class UserCancelledError extends Error {\n constructor(message: string = \"User cancelled the operation\") {\n super(message);\n this.name = \"UserCancelledError\";\n }\n}\n\n// --- Menu-based FillInStruct types ---\n\n/**\n * State tracking for a single field during menu-based editing.\n */\nexport type FieldState<T = unknown> =\n | { status: \"unset\" }\n | { status: \"set\"; value: T }\n | { status: \"null\" }; // explicitly set to null\n\n/**\n * Result of counting required fields in a schema.\n */\nexport interface RequiredFieldCount {\n total: number;\n filled: number;\n}\n\n/**\n * Result from field menu interaction.\n */\nexport type FieldMenuResult<T = unknown> =\n | { action: \"edit\" }\n | { action: \"view\" }\n | { action: \"clear\" }\n | { action: \"setNull\" }\n | { action: \"reset\"; defaultValue: T }\n | { action: \"back\" };\n\n/**\n * Result from object menu interaction.\n */\nexport type ObjectMenuResult<T = unknown> =\n | { action: \"submit\"; value: T }\n | { action: \"cancel\" };\n\n/**\n * Result from array menu interaction.\n */\nexport type ArrayMenuResult<T = unknown> =\n | { action: \"done\"; value: T[] }\n | { action: \"back\" };\n"],"mappings":"AAGA;AACA;AACA;;AAGA;AACA;AACA;;AAMA;AACA;AACA;;AAKA;AACA;AACA;;AAQA;AACA;AACA;;AAKA;AACA;AACA;;AAMA;AACA;AACA;;AAOA;AACA;AACA;;AAKA;AACA;AACA;AACA,OAAO,MAAMA,kBAAkB,SAASC,KAAK,CAAC;EAC5CC,WAAWA,CAACC,OAAe,GAAG,8BAA8B,EAAE;IAC5D,KAAK,CAACA,OAAO,CAAC;IACd,IAAI,CAACC,IAAI,GAAG,oBAAoB;EAClC;AACF;;AAEA;;AAEA;AACA;AACA;;AAIwB;;AAExB;AACA;AACA;;AAMA;AACA;AACA;;AASA;AACA;AACA;;AAKA;AACA;AACA","ignoreList":[]}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Field utilities for menu-based struct editing.
|
|
3
|
+
* Provides required field counting and label building.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { isOptional, isNullable, hasDefault, getDefault, isLiteral } from "../type-guards.js";
|
|
7
|
+
import { formatValuePreview } from "./formatting.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Count required fields in an object schema and how many are filled.
|
|
11
|
+
*
|
|
12
|
+
* @param type - The object schema
|
|
13
|
+
* @param state - Map of field names to their current state
|
|
14
|
+
* @returns Count of total required fields and how many are filled
|
|
15
|
+
*/
|
|
16
|
+
export function countRequiredFields(type, state) {
|
|
17
|
+
const fields = type.properties;
|
|
18
|
+
let total = 0;
|
|
19
|
+
let filled = 0;
|
|
20
|
+
for (const [fieldName, fieldType] of Object.entries(fields)) {
|
|
21
|
+
// Skip optional fields - they're not required
|
|
22
|
+
if (isOptional(fieldType)) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Non-nullable fields with defaults don't require user input
|
|
27
|
+
if (hasDefault(fieldType) && !isNullable(fieldType)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Literal fields auto-fill, so they're not required from the user
|
|
32
|
+
if (isLiteral(fieldType)) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// This is a required field
|
|
37
|
+
total++;
|
|
38
|
+
|
|
39
|
+
// Check if it's filled
|
|
40
|
+
const fieldState = state.get(fieldName);
|
|
41
|
+
if (fieldState?.status === "set" || fieldState?.status === "null") {
|
|
42
|
+
filled++;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
total,
|
|
47
|
+
filled
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Determine if a field is required (must have a value to submit).
|
|
53
|
+
*
|
|
54
|
+
* @param fieldType - The field's schema
|
|
55
|
+
* @returns true if the field is required
|
|
56
|
+
*/
|
|
57
|
+
export function isFieldRequired(fieldType) {
|
|
58
|
+
// Optional fields are not required
|
|
59
|
+
if (isOptional(fieldType)) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Literal fields auto-fill, so they're not required from the user
|
|
64
|
+
if (isLiteral(fieldType)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Non-nullable fields with defaults are not required (default will be used)
|
|
69
|
+
if (hasDefault(fieldType) && !isNullable(fieldType)) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Build a display label for a field in the menu.
|
|
77
|
+
*
|
|
78
|
+
* @param fieldName - The field name
|
|
79
|
+
* @param fieldType - The field's schema
|
|
80
|
+
* @param state - The current state of the field
|
|
81
|
+
* @returns Formatted label string
|
|
82
|
+
*/
|
|
83
|
+
export function buildFieldLabel(fieldName, fieldType, state) {
|
|
84
|
+
const required = isFieldRequired(fieldType);
|
|
85
|
+
const requiredMarker = required ? " *" : "";
|
|
86
|
+
switch (state.status) {
|
|
87
|
+
case "unset":
|
|
88
|
+
{
|
|
89
|
+
// Check for default value
|
|
90
|
+
if (hasDefault(fieldType)) {
|
|
91
|
+
const defaultVal = getDefault(fieldType);
|
|
92
|
+
const preview = formatValuePreview(defaultVal, 30);
|
|
93
|
+
return `${fieldName}: [default: ${preview}]${requiredMarker}`;
|
|
94
|
+
}
|
|
95
|
+
return `${fieldName}: [not set]${requiredMarker}`;
|
|
96
|
+
}
|
|
97
|
+
case "null":
|
|
98
|
+
return `${fieldName}: null${requiredMarker}`;
|
|
99
|
+
case "set":
|
|
100
|
+
{
|
|
101
|
+
const preview = formatValuePreview(state.value, 40);
|
|
102
|
+
return `${fieldName}: ${preview}${requiredMarker}`;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Get the initial FieldState for a field based on its type and any existing value.
|
|
109
|
+
*
|
|
110
|
+
* @param _fieldType - The field's schema (reserved for future logic)
|
|
111
|
+
* @param existingValue - Existing value if editing
|
|
112
|
+
* @returns Initial FieldState
|
|
113
|
+
*/
|
|
114
|
+
export function getInitialFieldState(_fieldType, existingValue) {
|
|
115
|
+
if (existingValue === undefined) {
|
|
116
|
+
return {
|
|
117
|
+
status: "unset"
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (existingValue === null) {
|
|
121
|
+
return {
|
|
122
|
+
status: "null"
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
status: "set",
|
|
127
|
+
value: existingValue
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Check if all required fields are filled in the state map.
|
|
133
|
+
*
|
|
134
|
+
* @param type - The object schema
|
|
135
|
+
* @param state - Map of field names to their current state
|
|
136
|
+
* @returns true if all required fields have values
|
|
137
|
+
*/
|
|
138
|
+
export function allRequiredFieldsFilled(type, state) {
|
|
139
|
+
const {
|
|
140
|
+
total,
|
|
141
|
+
filled
|
|
142
|
+
} = countRequiredFields(type, state);
|
|
143
|
+
return filled >= total;
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=field-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-utils.js","names":["isOptional","isNullable","hasDefault","getDefault","isLiteral","formatValuePreview","countRequiredFields","type","state","fields","properties","total","filled","fieldName","fieldType","Object","entries","fieldState","get","status","isFieldRequired","buildFieldLabel","required","requiredMarker","defaultVal","preview","value","getInitialFieldState","_fieldType","existingValue","undefined","allRequiredFieldsFilled"],"sources":["../../../../src/Sprinkle/utils/field-utils.ts"],"sourcesContent":["/**\n * Field utilities for menu-based struct editing.\n * Provides required field counting and label building.\n */\n\nimport type { TObject, TSchema } from \"@sinclair/typebox\";\nimport type { FieldState, RequiredFieldCount } from \"../types.js\";\nimport {\n isOptional,\n isNullable,\n hasDefault,\n getDefault,\n isLiteral,\n} from \"../type-guards.js\";\nimport { formatValuePreview } from \"./formatting.js\";\n\n/**\n * Count required fields in an object schema and how many are filled.\n *\n * @param type - The object schema\n * @param state - Map of field names to their current state\n * @returns Count of total required fields and how many are filled\n */\nexport function countRequiredFields(\n type: TObject,\n state: Map<string, FieldState>,\n): RequiredFieldCount {\n const fields = type.properties as Record<string, TSchema>;\n let total = 0;\n let filled = 0;\n\n for (const [fieldName, fieldType] of Object.entries(fields)) {\n // Skip optional fields - they're not required\n if (isOptional(fieldType)) {\n continue;\n }\n\n // Non-nullable fields with defaults don't require user input\n if (hasDefault(fieldType) && !isNullable(fieldType)) {\n continue;\n }\n\n // Literal fields auto-fill, so they're not required from the user\n if (isLiteral(fieldType)) {\n continue;\n }\n\n // This is a required field\n total++;\n\n // Check if it's filled\n const fieldState = state.get(fieldName);\n if (fieldState?.status === \"set\" || fieldState?.status === \"null\") {\n filled++;\n }\n }\n\n return { total, filled };\n}\n\n/**\n * Determine if a field is required (must have a value to submit).\n *\n * @param fieldType - The field's schema\n * @returns true if the field is required\n */\nexport function isFieldRequired(fieldType: TSchema): boolean {\n // Optional fields are not required\n if (isOptional(fieldType)) {\n return false;\n }\n\n // Literal fields auto-fill, so they're not required from the user\n if (isLiteral(fieldType)) {\n return false;\n }\n\n // Non-nullable fields with defaults are not required (default will be used)\n if (hasDefault(fieldType) && !isNullable(fieldType)) {\n return false;\n }\n\n return true;\n}\n\n/**\n * Build a display label for a field in the menu.\n *\n * @param fieldName - The field name\n * @param fieldType - The field's schema\n * @param state - The current state of the field\n * @returns Formatted label string\n */\nexport function buildFieldLabel(\n fieldName: string,\n fieldType: TSchema,\n state: FieldState,\n): string {\n const required = isFieldRequired(fieldType);\n const requiredMarker = required ? \" *\" : \"\";\n\n switch (state.status) {\n case \"unset\": {\n // Check for default value\n if (hasDefault(fieldType)) {\n const defaultVal = getDefault(fieldType);\n const preview = formatValuePreview(defaultVal, 30);\n return `${fieldName}: [default: ${preview}]${requiredMarker}`;\n }\n return `${fieldName}: [not set]${requiredMarker}`;\n }\n\n case \"null\":\n return `${fieldName}: null${requiredMarker}`;\n\n case \"set\": {\n const preview = formatValuePreview(state.value, 40);\n return `${fieldName}: ${preview}${requiredMarker}`;\n }\n }\n}\n\n/**\n * Get the initial FieldState for a field based on its type and any existing value.\n *\n * @param _fieldType - The field's schema (reserved for future logic)\n * @param existingValue - Existing value if editing\n * @returns Initial FieldState\n */\nexport function getInitialFieldState<T>(\n _fieldType: TSchema,\n existingValue?: T,\n): FieldState<T> {\n if (existingValue === undefined) {\n return { status: \"unset\" };\n }\n\n if (existingValue === null) {\n return { status: \"null\" };\n }\n\n return { status: \"set\", value: existingValue };\n}\n\n/**\n * Check if all required fields are filled in the state map.\n *\n * @param type - The object schema\n * @param state - Map of field names to their current state\n * @returns true if all required fields have values\n */\nexport function allRequiredFieldsFilled(\n type: TObject,\n state: Map<string, FieldState>,\n): boolean {\n const { total, filled } = countRequiredFields(type, state);\n return filled >= total;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Formatting utilities for menu-based struct editing.
|
|
3
|
+
* Provides value preview, path formatting, and breadcrumb generation.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Format a value for display in a menu item.
|
|
8
|
+
* Truncates long values and provides type-appropriate previews.
|
|
9
|
+
*
|
|
10
|
+
* @param value - The value to format
|
|
11
|
+
* @param maxLength - Maximum length before truncation (default 40)
|
|
12
|
+
* @returns Formatted string representation
|
|
13
|
+
*/
|
|
14
|
+
export function formatValuePreview(value, maxLength = 40) {
|
|
15
|
+
if (value === null) {
|
|
16
|
+
return "null";
|
|
17
|
+
}
|
|
18
|
+
if (value === undefined) {
|
|
19
|
+
return "[not set]";
|
|
20
|
+
}
|
|
21
|
+
if (typeof value === "string") {
|
|
22
|
+
const escaped = JSON.stringify(value);
|
|
23
|
+
if (escaped.length <= maxLength) {
|
|
24
|
+
return escaped;
|
|
25
|
+
}
|
|
26
|
+
// Truncate inside the quotes (guard against negative maxLength from recursion)
|
|
27
|
+
return `"${value.slice(0, Math.max(0, maxLength - 5))}..."`;
|
|
28
|
+
}
|
|
29
|
+
if (typeof value === "number" || typeof value === "bigint") {
|
|
30
|
+
return String(value);
|
|
31
|
+
}
|
|
32
|
+
if (typeof value === "boolean") {
|
|
33
|
+
return String(value);
|
|
34
|
+
}
|
|
35
|
+
if (Array.isArray(value)) {
|
|
36
|
+
return `[${value.length} item${value.length === 1 ? "" : "s"}]`;
|
|
37
|
+
}
|
|
38
|
+
if (typeof value === "object") {
|
|
39
|
+
// For objects, try to show a meaningful preview
|
|
40
|
+
const keys = Object.keys(value);
|
|
41
|
+
if (keys.length === 0) {
|
|
42
|
+
return "{}";
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// If object has a single key (like union type), show it
|
|
46
|
+
if (keys.length === 1) {
|
|
47
|
+
const key = keys[0];
|
|
48
|
+
const innerValue = value[key];
|
|
49
|
+
const innerPreview = formatValuePreview(innerValue, maxLength - key.length - 6);
|
|
50
|
+
const preview = `{ ${key}: ${innerPreview} }`;
|
|
51
|
+
if (preview.length <= maxLength) {
|
|
52
|
+
return preview;
|
|
53
|
+
}
|
|
54
|
+
return `{ ${key}: ... }`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Multiple keys - show abbreviated
|
|
58
|
+
const firstKey = keys[0];
|
|
59
|
+
return `{ ${firstKey}: ..., +${keys.length - 1} }`;
|
|
60
|
+
}
|
|
61
|
+
return String(value).slice(0, maxLength);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Format a path array for display, stripping the "root" prefix.
|
|
66
|
+
*
|
|
67
|
+
* @param path - Array of path segments (e.g., ["root", "settings", "name"])
|
|
68
|
+
* @returns Formatted path string (e.g., "settings.name")
|
|
69
|
+
*/
|
|
70
|
+
export function formatPath(path) {
|
|
71
|
+
// Filter out "root" prefix
|
|
72
|
+
const filtered = path.filter(segment => segment !== "root");
|
|
73
|
+
if (filtered.length === 0) {
|
|
74
|
+
return "";
|
|
75
|
+
}
|
|
76
|
+
return filtered.join(".");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Format a path as a breadcrumb trail for nested navigation.
|
|
81
|
+
* Truncates from the left if too long.
|
|
82
|
+
*
|
|
83
|
+
* @param path - Array of path segments
|
|
84
|
+
* @param maxLength - Maximum total length (default 50)
|
|
85
|
+
* @returns Breadcrumb string (e.g., "settings → permissions → mint")
|
|
86
|
+
*/
|
|
87
|
+
export function formatBreadcrumb(path, maxLength = 50) {
|
|
88
|
+
// Filter out "root" prefix
|
|
89
|
+
const filtered = path.filter(segment => segment !== "root");
|
|
90
|
+
if (filtered.length === 0) {
|
|
91
|
+
return "";
|
|
92
|
+
}
|
|
93
|
+
const separator = " \u2192 "; // Unicode right arrow
|
|
94
|
+
const joined = filtered.join(separator);
|
|
95
|
+
if (joined.length <= maxLength) {
|
|
96
|
+
return joined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Truncate from left, keeping most recent segments
|
|
100
|
+
const ellipsis = "...";
|
|
101
|
+
let result = "";
|
|
102
|
+
for (let i = filtered.length - 1; i >= 0; i--) {
|
|
103
|
+
const segment = filtered[i];
|
|
104
|
+
const candidate = i === filtered.length - 1 ? segment : segment + separator + result;
|
|
105
|
+
if (candidate.length + ellipsis.length + separator.length > maxLength && i < filtered.length - 1) {
|
|
106
|
+
return ellipsis + separator + result;
|
|
107
|
+
}
|
|
108
|
+
result = candidate;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Safety clamp: if we still exceeded maxLength (e.g., last segment alone is too long),
|
|
112
|
+
// fall back to just the ellipsis to honor the maxLength contract.
|
|
113
|
+
if (result.length > maxLength) {
|
|
114
|
+
return ellipsis;
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=formatting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatting.js","names":["formatValuePreview","value","maxLength","undefined","escaped","JSON","stringify","length","slice","Math","max","String","Array","isArray","keys","Object","key","innerValue","innerPreview","preview","firstKey","formatPath","path","filtered","filter","segment","join","formatBreadcrumb","separator","joined","ellipsis","result","i","candidate"],"sources":["../../../../src/Sprinkle/utils/formatting.ts"],"sourcesContent":["/**\n * Formatting utilities for menu-based struct editing.\n * Provides value preview, path formatting, and breadcrumb generation.\n */\n\n/**\n * Format a value for display in a menu item.\n * Truncates long values and provides type-appropriate previews.\n *\n * @param value - The value to format\n * @param maxLength - Maximum length before truncation (default 40)\n * @returns Formatted string representation\n */\nexport function formatValuePreview(value: unknown, maxLength = 40): string {\n if (value === null) {\n return \"null\";\n }\n\n if (value === undefined) {\n return \"[not set]\";\n }\n\n if (typeof value === \"string\") {\n const escaped = JSON.stringify(value);\n if (escaped.length <= maxLength) {\n return escaped;\n }\n // Truncate inside the quotes (guard against negative maxLength from recursion)\n return `\"${value.slice(0, Math.max(0, maxLength - 5))}...\"`;\n }\n\n if (typeof value === \"number\" || typeof value === \"bigint\") {\n return String(value);\n }\n\n if (typeof value === \"boolean\") {\n return String(value);\n }\n\n if (Array.isArray(value)) {\n return `[${value.length} item${value.length === 1 ? \"\" : \"s\"}]`;\n }\n\n if (typeof value === \"object\") {\n // For objects, try to show a meaningful preview\n const keys = Object.keys(value);\n if (keys.length === 0) {\n return \"{}\";\n }\n\n // If object has a single key (like union type), show it\n if (keys.length === 1) {\n const key = keys[0]!;\n const innerValue = (value as Record<string, unknown>)[key];\n const innerPreview = formatValuePreview(innerValue, maxLength - key.length - 6);\n const preview = `{ ${key}: ${innerPreview} }`;\n if (preview.length <= maxLength) {\n return preview;\n }\n return `{ ${key}: ... }`;\n }\n\n // Multiple keys - show abbreviated\n const firstKey = keys[0]!;\n return `{ ${firstKey}: ..., +${keys.length - 1} }`;\n }\n\n return String(value).slice(0, maxLength);\n}\n\n/**\n * Format a path array for display, stripping the \"root\" prefix.\n *\n * @param path - Array of path segments (e.g., [\"root\", \"settings\", \"name\"])\n * @returns Formatted path string (e.g., \"settings.name\")\n */\nexport function formatPath(path: string[]): string {\n // Filter out \"root\" prefix\n const filtered = path.filter((segment) => segment !== \"root\");\n if (filtered.length === 0) {\n return \"\";\n }\n return filtered.join(\".\");\n}\n\n/**\n * Format a path as a breadcrumb trail for nested navigation.\n * Truncates from the left if too long.\n *\n * @param path - Array of path segments\n * @param maxLength - Maximum total length (default 50)\n * @returns Breadcrumb string (e.g., \"settings → permissions → mint\")\n */\nexport function formatBreadcrumb(path: string[], maxLength = 50): string {\n // Filter out \"root\" prefix\n const filtered = path.filter((segment) => segment !== \"root\");\n if (filtered.length === 0) {\n return \"\";\n }\n\n const separator = \" \\u2192 \"; // Unicode right arrow\n const joined = filtered.join(separator);\n\n if (joined.length <= maxLength) {\n return joined;\n }\n\n // Truncate from left, keeping most recent segments\n const ellipsis = \"...\";\n let result = \"\";\n for (let i = filtered.length - 1; i >= 0; i--) {\n const segment = filtered[i]!;\n const candidate = i === filtered.length - 1 ? segment : segment + separator + result;\n if (candidate.length + ellipsis.length + separator.length > maxLength && i < filtered.length - 1) {\n return ellipsis + separator + result;\n }\n result = candidate;\n }\n\n // Safety clamp: if we still exceeded maxLength (e.g., last segment alone is too long),\n // fall back to just the ellipsis to honor the maxLength contract.\n if (result.length > maxLength) {\n return ellipsis;\n }\n\n return result;\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility functions for menu-based struct editing.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { formatValuePreview, formatPath, formatBreadcrumb } from "./formatting.js";
|
|
6
|
+
export { countRequiredFields, isFieldRequired, buildFieldLabel, getInitialFieldState, allRequiredFieldsFilled } from "./field-utils.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["formatValuePreview","formatPath","formatBreadcrumb","countRequiredFields","isFieldRequired","buildFieldLabel","getInitialFieldState","allRequiredFieldsFilled"],"sources":["../../../../src/Sprinkle/utils/index.ts"],"sourcesContent":["/**\n * Utility functions for menu-based struct editing.\n */\n\nexport {\n formatValuePreview,\n formatPath,\n formatBreadcrumb,\n} from \"./formatting.js\";\n\nexport {\n countRequiredFields,\n isFieldRequired,\n buildFieldLabel,\n getInitialFieldState,\n allRequiredFieldsFilled,\n} from \"./field-utils.js\";\n"],"mappings":"AAAA;AACA;AACA;;AAEA,SACEA,kBAAkB,EAClBC,UAAU,EACVC,gBAAgB,QACX,iBAAiB;AAExB,SACEC,mBAAmB,EACnBC,eAAe,EACfC,eAAe,EACfC,oBAAoB,EACpBC,uBAAuB,QAClB,kBAAkB","ignoreList":[]}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wallet and provider setup utilities.
|
|
3
|
+
* Functions for creating providers, wallets, and Blaze instances.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Blockfrost } from "@blaze-cardano/query";
|
|
7
|
+
import { Blaze, ColdWallet, Core, HotWallet } from "@blaze-cardano/sdk";
|
|
8
|
+
import { wordlist } from "@blaze-cardano/core";
|
|
9
|
+
import { confirmCancellable } from "./prompts.js";
|
|
10
|
+
import { UserCancelledError } from "./types.js";
|
|
11
|
+
/**
|
|
12
|
+
* Creates a blockchain data provider based on settings.
|
|
13
|
+
*/
|
|
14
|
+
export async function GetProvider(network, settings) {
|
|
15
|
+
switch (settings.type) {
|
|
16
|
+
case "blockfrost":
|
|
17
|
+
return new Blockfrost({
|
|
18
|
+
network: `cardano-${network}`,
|
|
19
|
+
projectId: settings.projectId
|
|
20
|
+
});
|
|
21
|
+
case "maestro":
|
|
22
|
+
// Dynamic import - Maestro may or may not be exported depending on @blaze-cardano/query version
|
|
23
|
+
const queryModule = await import("@blaze-cardano/query");
|
|
24
|
+
if (!queryModule.Maestro) {
|
|
25
|
+
throw new Error("Maestro is not available in the installed version of @blaze-cardano/query. Please install a version that includes Maestro support.");
|
|
26
|
+
}
|
|
27
|
+
return new queryModule.Maestro({
|
|
28
|
+
network: network,
|
|
29
|
+
apiKey: settings.apiKey
|
|
30
|
+
});
|
|
31
|
+
default:
|
|
32
|
+
throw new Error("Invalid provider type");
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Creates a wallet instance based on settings.
|
|
38
|
+
*/
|
|
39
|
+
export async function GetWallet(settings, provider) {
|
|
40
|
+
switch (settings.type) {
|
|
41
|
+
case "hot":
|
|
42
|
+
return HotWallet.fromMasterkey(Core.Bip32PrivateKeyHex(settings.privateKey), provider, provider.network);
|
|
43
|
+
case "cold":
|
|
44
|
+
return new ColdWallet(Core.Address.fromBech32(settings.address), provider.network, provider);
|
|
45
|
+
default:
|
|
46
|
+
throw new Error("Invalid wallet type");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Creates a Blaze instance with provider and wallet.
|
|
52
|
+
*/
|
|
53
|
+
export async function GetBlaze(network, providerSettings, walletSettings) {
|
|
54
|
+
const provider = await GetProvider(network, providerSettings);
|
|
55
|
+
const wallet = await GetWallet(walletSettings, provider);
|
|
56
|
+
return Blaze.from(provider, wallet);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Generates a new wallet from a BIP39 mnemonic phrase.
|
|
61
|
+
* Displays the 24-word recovery phrase and requires user confirmation.
|
|
62
|
+
* @returns The Bip32PrivateKey hex string for storage
|
|
63
|
+
*/
|
|
64
|
+
export async function generateWalletFromMnemonic() {
|
|
65
|
+
const mnemonic = Core.generateMnemonic(wordlist, 256); // 24 words
|
|
66
|
+
const words = mnemonic.split(" ");
|
|
67
|
+
console.log("\n=== NEW WALLET GENERATED ===\n");
|
|
68
|
+
console.log("IMPORTANT: Save these 24 words in a secure location.");
|
|
69
|
+
console.log("This is the ONLY way to recover your wallet.\n");
|
|
70
|
+
|
|
71
|
+
// Display in 4 columns
|
|
72
|
+
for (let i = 0; i < 6; i++) {
|
|
73
|
+
console.log(`${(i + 1).toString().padStart(2)}. ${words[i].padEnd(12)} ` + `${(i + 7).toString().padStart(2)}. ${words[i + 6].padEnd(12)} ` + `${(i + 13).toString().padStart(2)}. ${words[i + 12].padEnd(12)} ` + `${(i + 19).toString().padStart(2)}. ${words[i + 18]}`);
|
|
74
|
+
}
|
|
75
|
+
console.log("");
|
|
76
|
+
const confirmed = await confirmCancellable({
|
|
77
|
+
message: "Have you saved your recovery phrase?",
|
|
78
|
+
default: false
|
|
79
|
+
});
|
|
80
|
+
if (confirmed === null) {
|
|
81
|
+
throw new UserCancelledError("Wallet generation cancelled");
|
|
82
|
+
}
|
|
83
|
+
if (!confirmed) {
|
|
84
|
+
throw new Error("Wallet generation cancelled - recovery phrase not saved");
|
|
85
|
+
}
|
|
86
|
+
const entropy = Core.mnemonicToEntropy(mnemonic, wordlist);
|
|
87
|
+
const masterKey = Core.Bip32PrivateKey.fromBip39Entropy(Buffer.from(entropy), "");
|
|
88
|
+
return masterKey.hex();
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=wallet.js.map
|