Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions .github/workflows/ci-dart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ on:
paths:
- "packages/core-dart/**"
- "spec/**"
- "!spec/vectors.json"
# `verify-parity` owns this file's CI surface; skip here to avoid
# duplicate runs on vectors-only PRs.
paths-ignore:
- "spec/vectors.json"
# Note: paths + paths-ignore combine as an AND across all changed files.
# If a PR changes spec/vectors.json *together with* packages/core-dart/**,
# this workflow is skipped; verify-parity.yml still runs the identical
# commands, so functional coverage is preserved under a different check.
# Negative patterns must come after positive ones. A vectors-only PR is
# handled by verify-parity.yml; PRs touching Dart code still run here.

jobs:
test:
Expand Down
9 changes: 3 additions & 6 deletions .github/workflows/ci-go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ on:
paths:
- "packages/core-go/**"
- "spec/**"
- "!spec/vectors.json"
# `verify-parity` owns this file's CI surface; skip here to avoid
# duplicate runs on vectors-only PRs.
paths-ignore:
- "spec/vectors.json"
# Note: paths + paths-ignore combine as an AND across all changed files.
# If a PR changes spec/vectors.json *together with* packages/core-go/**,
# this workflow is skipped; verify-parity.yml still runs the identical
# commands, so functional coverage is preserved under a different check.
# Negative patterns must come after positive ones. A vectors-only PR is
# handled by verify-parity.yml; PRs touching Go code still run here.

jobs:
test:
Expand Down
9 changes: 3 additions & 6 deletions .github/workflows/ci-ts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ on:
paths:
- "packages/core-ts/**"
- "spec/**"
- "!spec/vectors.json"
# `verify-parity` owns this file's CI surface; skip here to avoid
# duplicate runs on vectors-only PRs.
paths-ignore:
- "spec/vectors.json"
# Note: paths + paths-ignore combine as an AND across all changed files.
# If a PR changes spec/vectors.json *together with* packages/core-ts/**,
# this workflow is skipped; verify-parity.yml still runs the identical
# commands, so functional coverage is preserved under a different check.
# Negative patterns must come after positive ones. A vectors-only PR is
# handled by verify-parity.yml; PRs touching TS code still run here.

jobs:
test:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Extension Discovery Cache
=========================

This folder is used by `package:extension_discovery` to cache lists of
packages that contains extensions for other packages.

DO NOT USE THIS FOLDER
----------------------

* Do not read (or rely) the contents of this folder.
* Do write to this folder.

If you're interested in the lists of extensions stored in this folder use the
API offered by package `extension_discovery` to get this information.

If this package doesn't work for your use-case, then don't try to read the
contents of this folder. It may change, and will not remain stable.

Use package `extension_discovery`
---------------------------------

If you want to access information from this folder.

Feel free to delete this folder
-------------------------------

Files in this folder act as a cache, and the cache is discarded if the files
are older than the modification time of `.dart_tool/package_config.json`.

Hence, it should never be necessary to clear this cache manually, if you find a
need to do please file a bug.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"version":2,"entries":[{"package":"stellar_address_kit","rootUri":"../../../../packages/core-dart/","packageUri":"lib/"},{"package":"conformance_test_runner","rootUri":"../","packageUri":"lib/"}]}
10 changes: 10 additions & 0 deletions packages/core-ts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,16 @@ if (!result.destinationError) {
}
```

## CLI Debugger

After building or installing the package, you can inspect routing behavior from the terminal:

```bash
stellar-route --dest G... --memo 123 --type id
```

The command prints the `RoutingResult` object as pretty JSON, which is useful for checking muxed-address precedence, memo normalization, and routing warnings without writing a scratch script.

## Documentation

For full guides, integration examples, and deep dives into the routing logic, see our [comprehensive Guides](https://github.com/Boxkit-Labs/stellar-address-kit/tree/main/docs/guides).
Expand Down
5 changes: 4 additions & 1 deletion packages/core-ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,14 @@
"main": "dist/index.js",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"bin": {
"stellar-route": "dist/cli.js"
},
"files": [
"dist"
],
"scripts": {
"build": "tsup src/index.ts --format cjs,esm --dts",
"build": "tsup src/index.ts src/cli.ts --format cjs,esm --dts",
"test": "vitest run",
"test:watch": "vitest",
"lint": "eslint src/**/*.ts"
Expand Down
23 changes: 23 additions & 0 deletions packages/core-ts/src/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env node

import { runRouteDebuggerCli } from "./cli/routeDebugger";

declare const process: {
argv: string[];
stdout: { write: (message: string) => void };
stderr: { write: (message: string) => void };
exit: (code?: number) => never;
};

void (async () => {
const exitCode = await runRouteDebuggerCli(process.argv.slice(2), {
stdout: (message) => {
process.stdout.write(message);
},
stderr: (message) => {
process.stderr.write(message);
},
});

process.exit(exitCode);
})();
153 changes: 153 additions & 0 deletions packages/core-ts/src/cli/routeDebugger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { extractRouting, ExtractRoutingError } from "../routing/extract";
import type { KnownMemoType } from "../routing/types";

export type CliIo = {
stdout: (message: string) => void;
stderr: (message: string) => void;
};

function stringifyCliResult(value: unknown): string {
return JSON.stringify(
value,
(_, nestedValue) =>
typeof nestedValue === "bigint" ? nestedValue.toString() : nestedValue,
2
);
}

const VALID_MEMO_TYPES: KnownMemoType[] = [
"none",
"id",
"text",
"hash",
"return",
];

const HELP_TEXT = `Debug Stellar deposit routing from the command line.

Usage:
stellar-route --dest <address> [--memo <value>] [--type <memoType>] [--source-account <account>]

Options:
--dest <address> Destination G-address or M-address
--memo <value> Memo value to test
--type <memoType> Memo type: none, id, text, hash, or return
--source-account <account> Optional source account for completeness in routing input
-h, --help Show this help message

Examples:
stellar-route --dest G... --memo 123 --type id
stellar-route --dest M... --memo customer-42 --type text
`;

type ParsedArgs = {
dest: string;
memo?: string;
type: KnownMemoType;
sourceAccount?: string;
};

function parseMemoType(rawValue: string): KnownMemoType {
if (VALID_MEMO_TYPES.includes(rawValue as KnownMemoType)) {
return rawValue as KnownMemoType;
}

throw new Error(
`Invalid memo type "${rawValue}". Expected one of: ${VALID_MEMO_TYPES.join(", ")}.`
);
}

function readValue(argv: string[], index: number, flag: string): string {
const value = argv[index + 1];

if (!value || value.startsWith("-")) {
throw new Error(`Missing value for ${flag}.`);
}

return value;
}

function parseArgs(argv: string[]): ParsedArgs {
const parsed: ParsedArgs = {
dest: "",
type: "none",
};

for (let index = 0; index < argv.length; index += 1) {
const token = argv[index];

if (token === "--dest") {
parsed.dest = readValue(argv, index, token);
index += 1;
continue;
}

if (token === "--memo") {
parsed.memo = readValue(argv, index, token);
index += 1;
continue;
}

if (token === "--type") {
parsed.type = parseMemoType(readValue(argv, index, token));
index += 1;
continue;
}

if (token === "--source-account") {
parsed.sourceAccount = readValue(argv, index, token);
index += 1;
continue;
}

if (token === "-h" || token === "--help") {
throw new Error("__HELP__");
}

throw new Error(`Unknown option: ${token}`);
}

if (!parsed.dest) {
throw new Error("Missing required option --dest.");
}

return parsed;
}

export async function runRouteDebuggerCli(
argv: string[],
io: CliIo
): Promise<number> {
try {
const options = parseArgs(argv);

if (options.memo !== undefined && options.type === "none") {
throw new Error(
'--memo requires --type to be one of "id", "text", "hash", or "return".'
);
}

const result = extractRouting({
destination: options.dest,
memoType: options.type,
memoValue: options.memo ?? null,
sourceAccount: options.sourceAccount ?? null,
});

io.stdout(`${stringifyCliResult(result)}\n`);
return 0;
} catch (error) {
if (error instanceof Error && error.message === "__HELP__") {
io.stdout(HELP_TEXT);
return 0;
}

const message =
error instanceof ExtractRoutingError || error instanceof Error
? error.message
: "Unknown CLI failure.";

io.stderr(`${message}\n`);
return 1;
}
}
Loading
Loading