@sundaeswap/sprinkles 0.8.0 → 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sundaeswap/sprinkles",
3
- "version": "0.8.0",
3
+ "version": "0.8.2",
4
4
  "description": "A TypeScript library for building interactive CLI menus and TUI applications with TypeBox schema validation",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -1,5 +1,6 @@
1
1
  import { describe, expect, test, mock, beforeEach, spyOn } from "bun:test";
2
- import { Sprinkle, Type, WalletSettingsSchema } from "../index.js";
2
+ import { Sprinkle, Type, WalletSettingsSchema, MultisigScriptModule } from "../index.js";
3
+ import { MultisigScript } from "../schemas.js";
3
4
  import { UserCancelledError } from "../types.js";
4
5
 
5
6
  // Mock @inquirer/prompts
@@ -638,4 +639,113 @@ describe("FillInStruct", () => {
638
639
  UserCancelledError,
639
640
  );
640
641
  });
642
+
643
+ // --- Type.Module / Type.Import ($ref resolution) ---
644
+
645
+ test("resolves MultisigScript TImport $ref (Signature variant)", async () => {
646
+ const hash = "a".repeat(56);
647
+
648
+ // Union variant selection: pick Signature
649
+ mockSelectCancellable.mockImplementationOnce(async (opts: any) => {
650
+ const sigVariant = opts.choices.find((c: any) =>
651
+ c.name === "Signature" || JSON.stringify(c.value).includes("Signature"),
652
+ );
653
+ return sigVariant?.value ?? opts.choices[0].value;
654
+ });
655
+ // Signature → single-field → key_hash → single-field → string prompt
656
+ mockInputCancellable.mockResolvedValueOnce(hash);
657
+ // "Save to addressbook?"
658
+ mockSelectCancellable.mockResolvedValueOnce(false);
659
+
660
+ const result = await sprinkle.FillInStruct(MultisigScript);
661
+ expect(result).toEqual({ Signature: { key_hash: hash } });
662
+ });
663
+
664
+ test("resolves MultisigScript $ref inside a wrapping schema", async () => {
665
+ const schema = Type.Object({
666
+ script: MultisigScript,
667
+ });
668
+ const hash = "b".repeat(56);
669
+
670
+ // Object has single required field "script" → skips menu
671
+ // Union variant selection: pick Signature
672
+ mockSelectCancellable.mockImplementationOnce(async (opts: any) => {
673
+ const sigVariant = opts.choices.find((c: any) =>
674
+ c.name === "Signature" || JSON.stringify(c.value).includes("Signature"),
675
+ );
676
+ return sigVariant?.value ?? opts.choices[0].value;
677
+ });
678
+ mockInputCancellable.mockResolvedValueOnce(hash);
679
+ // "Save to addressbook?"
680
+ mockSelectCancellable.mockResolvedValueOnce(false);
681
+
682
+ const result = await sprinkle.FillInStruct(schema);
683
+ expect(result).toEqual({ script: { Signature: { key_hash: hash } } });
684
+ });
685
+
686
+ test("resolves MultisigScript default matching when editing existing value", async () => {
687
+ // When editing an existing MultisigScript value, Value.Check is called
688
+ // to match the default against the selected variant. Variant schemas
689
+ // contain $ref which requires a references array to resolve.
690
+ const hash = "e".repeat(56);
691
+ const existingValue = {
692
+ AllOf: {
693
+ scripts: [{ Signature: { key_hash: hash } }],
694
+ },
695
+ };
696
+
697
+ // Union variant selection: pick AllOf (matches the existing value)
698
+ mockSelectCancellable.mockImplementationOnce(async (opts: any) => {
699
+ const variant = opts.choices.find((c: any) =>
700
+ c.name === "AllOf" || (c.value?.properties && "AllOf" in c.value.properties),
701
+ );
702
+ return variant?.value ?? opts.choices[1].value;
703
+ });
704
+ // AllOf → single field → scripts array → items already populated from default
705
+ // Array menu: Done (keep existing items)
706
+ mockSelectCancellable.mockResolvedValueOnce("done");
707
+ // "Save to addressbook?"
708
+ mockSelectCancellable.mockResolvedValueOnce(false);
709
+
710
+ const result = await sprinkle.FillInStruct(MultisigScript, existingValue as any);
711
+ expect(result).toEqual(existingValue);
712
+ });
713
+
714
+ test("resolves recursive $ref in nested MultisigScript (AllOf with nested Signature)", async () => {
715
+ const hash1 = "c".repeat(56);
716
+
717
+ // Top-level union: pick AllOf
718
+ mockSelectCancellable.mockImplementationOnce(async (opts: any) => {
719
+ const variant = opts.choices.find((c: any) =>
720
+ c.name === "AllOf" || (c.value?.properties && "AllOf" in c.value.properties),
721
+ );
722
+ return variant?.value ?? opts.choices[1].value;
723
+ });
724
+ // AllOf → single field → scripts array
725
+ // Array menu: Add item
726
+ mockSelectCancellable.mockResolvedValueOnce("add");
727
+ // Array item is MultisigScript ($ref) → resolves recursively → shows Union
728
+ // Union: pick Signature
729
+ mockSelectCancellable.mockImplementationOnce(async (opts: any) => {
730
+ const sigVariant = opts.choices.find((c: any) =>
731
+ c.name === "Signature" || (c.value?.properties && "Signature" in c.value.properties),
732
+ );
733
+ return sigVariant?.value ?? opts.choices[0].value;
734
+ });
735
+ // Fill key_hash
736
+ mockInputCancellable.mockResolvedValueOnce(hash1);
737
+ // "Save to addressbook?" for inner MultisigScript
738
+ mockSelectCancellable.mockResolvedValueOnce(false);
739
+ // Array menu: Done
740
+ mockSelectCancellable.mockResolvedValueOnce("done");
741
+ // "Save to addressbook?" for outer MultisigScript
742
+ mockSelectCancellable.mockResolvedValueOnce(false);
743
+
744
+ const result = await sprinkle.FillInStruct(MultisigScript);
745
+ expect(result).toEqual({
746
+ AllOf: {
747
+ scripts: [{ Signature: { key_hash: hash1 } }],
748
+ },
749
+ });
750
+ });
641
751
  });
@@ -196,7 +196,10 @@ export {
196
196
  runMcp,
197
197
  getBuiltinActions,
198
198
  promptAndExecute,
199
+ toNativeScript,
200
+ completeWithScripts,
199
201
  } from "./actions/index.js";
202
+ export type { NativeScriptInput } from "./actions/index.js";
200
203
  import {
201
204
  mintToken,
202
205
  simpleSend,
@@ -511,6 +514,7 @@ export class Sprinkle<S extends TSchema> {
511
514
  jsonContent,
512
515
  "utf-8",
513
516
  );
517
+ await this.loadProfile(id);
514
518
  console.log(`Profile "${name}" created as a copy.`);
515
519
  }
516
520
 
@@ -1539,6 +1543,9 @@ export class Sprinkle<S extends TSchema> {
1539
1543
  // if the literal field values in the selected variant match those in `def`.
1540
1544
  // For non-discriminated unions, fall back to structural matching with Value.Check.
1541
1545
  let matchedDef: unknown = undefined;
1546
+ // Build references array for Value.Check — schemas with $id from defs
1547
+ // so TypeBox can resolve $ref inside variant schemas (e.g. MultisigScript).
1548
+ const references = Object.values(defs).filter((s) => s.$id) as TSchema[];
1542
1549
  if (def !== undefined) {
1543
1550
  if (isObject(selection)) {
1544
1551
  // Check if all literal fields in the selected variant match def
@@ -1556,13 +1563,13 @@ export class Sprinkle<S extends TSchema> {
1556
1563
  }
1557
1564
  } else {
1558
1565
  // No literal discriminators - use structural check
1559
- if (Value.Check(selection, def)) {
1566
+ if (Value.Check(selection, references, def)) {
1560
1567
  matchedDef = def;
1561
1568
  }
1562
1569
  }
1563
1570
  } else {
1564
1571
  // Non-object variant - use structural check
1565
- if (Value.Check(selection, def)) {
1572
+ if (Value.Check(selection, references, def)) {
1566
1573
  matchedDef = def;
1567
1574
  }
1568
1575
  }