txo_parser 0.0.1 → 0.0.3

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.
@@ -0,0 +1,31 @@
1
+ {
2
+ "@context": {
3
+ "schema": "https://schema.org/",
4
+ "bt": "https://blocktrails.org/ns/"
5
+ },
6
+ "@id": "#this",
7
+ "@type": "VoucherPool",
8
+ "schema:name": "Voucher Pool",
9
+ "schema:description": "Testnet4 voucher management and faucet",
10
+ "schema:hasPart": [
11
+ {
12
+ "@id": "blocktrail.jsonld#this"
13
+ },
14
+ {
15
+ "@id": "webledger.jsonld#this"
16
+ },
17
+ {
18
+ "@id": "ledger-history.jsonld#this"
19
+ }
20
+ ],
21
+ "schema:itemListElement": [
22
+ {
23
+ "@type": "schema:ListItem",
24
+ "@id": "mn05mqrf",
25
+ "schema:identifier": "txo:btc:d3a54de54a9df9e89b02dfda4274bbb12180c2ac75cb523917a66f515d01b977:0?amount=416366&key=b8f86cd4e9a6f5db705ebfbcbe3093745b10b2781a7ead82647dc770bd60fb02",
26
+ "schema:address": "tb1pgl8m440er9gmhvxepwuscduanw4xf6p2ccfaetqq48pcxfnhfazsqa22kz",
27
+ "schema:status": "unspent",
28
+ "schema:dateCreated": "2026-03-21T09:57:43.035Z"
29
+ }
30
+ ]
31
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "@context": "https://w3id.org/webledgers",
3
+ "@id": "#this",
4
+ "type": "WebLedger",
5
+ "name": "Voucher Pool Ledger",
6
+ "description": "Testnet4 voucher balances",
7
+ "defaultCurrency": "satoshi",
8
+ "entries": []
9
+ }
package/index.js CHANGED
@@ -3,6 +3,7 @@
3
3
  * Implementation of the TXO URI Specification (v0.1)
4
4
  *
5
5
  * Format: txo:<network>:<txid>:<output>?key=value&key=value...
6
+ * Legacy Format: txo:<network>:<txid>:<output> [amount] [privkey]
6
7
  */
7
8
 
8
9
  /**
@@ -16,8 +17,22 @@ export function parseTxoUri (uri) {
16
17
  throw new Error('Invalid URI: URI must be a non-empty string');
17
18
  }
18
19
 
19
- // Split the URI into its components
20
- const [scheme, network, txid, outputAndRest] = uri.split(':');
20
+ // Check if this is using the legacy space-separated format
21
+ const isLegacyFormat = !uri.includes('?') && uri.split(' ').length > 1;
22
+
23
+ // Parse the basic structure first
24
+ let basicParts;
25
+ let extraParts = [];
26
+
27
+ if (isLegacyFormat) {
28
+ const allParts = uri.split(' ');
29
+ basicParts = allParts[0].split(':');
30
+ extraParts = allParts.slice(1);
31
+ } else {
32
+ basicParts = uri.split(':');
33
+ }
34
+
35
+ const [scheme, network, txid, outputAndRest] = basicParts;
21
36
 
22
37
  // Validate scheme
23
38
  if (scheme !== 'txo') {
@@ -41,7 +56,7 @@ export function parseTxoUri (uri) {
41
56
 
42
57
  // Split the output and query string
43
58
  let output, queryString;
44
- if (outputAndRest.includes('?')) {
59
+ if (!isLegacyFormat && outputAndRest.includes('?')) {
45
60
  [output, queryString] = outputAndRest.split('?');
46
61
  } else {
47
62
  output = outputAndRest;
@@ -64,22 +79,41 @@ export function parseTxoUri (uri) {
64
79
  // Convert key to lowercase for case-insensitivity
65
80
  const normalizedKey = key.toLowerCase();
66
81
 
82
+ // Normalize aliases to canonical names
83
+ const canonicalKey = normalizedKey === 'key' ? 'privkey' : normalizedKey;
84
+
67
85
  // Process specific key types
68
- if (normalizedKey === 'amount') {
86
+ if (canonicalKey === 'amount') {
69
87
  // Parse amount as a number
70
88
  const amount = parseFloat(value);
71
89
  if (!isNaN(amount)) {
72
- queryParams[normalizedKey] = amount;
90
+ queryParams[canonicalKey] = amount;
73
91
  } else {
74
- queryParams[normalizedKey] = value;
92
+ queryParams[canonicalKey] = value;
75
93
  }
76
94
  } else {
77
- queryParams[normalizedKey] = value;
95
+ queryParams[canonicalKey] = decodeURIComponent(value);
78
96
  }
79
97
  }
80
98
  });
81
99
  }
82
100
 
101
+ // Handle legacy format with space-separated parameters
102
+ if (isLegacyFormat && extraParts.length > 0) {
103
+ // First extra part is amount
104
+ if (extraParts[0]) {
105
+ const amount = parseFloat(extraParts[0]);
106
+ if (!isNaN(amount)) {
107
+ queryParams.amount = amount;
108
+ }
109
+ }
110
+
111
+ // Second extra part is privkey
112
+ if (extraParts.length > 1 && extraParts[1]) {
113
+ queryParams.privkey = extraParts[1];
114
+ }
115
+ }
116
+
83
117
  // Build the result object
84
118
  const result = {
85
119
  network,
@@ -136,12 +170,30 @@ export function formatTxoUri (data) {
136
170
  // Build the base URI
137
171
  let uri = `txo:${data.network}:${data.txid}:${output}`;
138
172
 
139
- // Add query parameters
173
+ // Add query parameters (canonical key names, stable order: amount first, then privkey, then rest)
174
+ const reserved = ['network', 'txid', 'output'];
175
+ const entries = Object.entries(data).filter(([k]) => !reserved.includes(k) && data[k] !== undefined);
176
+
177
+ // Normalize 'key' alias to 'privkey'
140
178
  const queryParams = [];
141
- Object.entries(data).forEach(([key, value]) => {
142
- if (!['network', 'txid', 'output'].includes(key) && value !== undefined) {
143
- queryParams.push(`${key.toLowerCase()}=${encodeURIComponent(value)}`);
179
+ const seen = new Set();
180
+ for (const [k, v] of entries) {
181
+ const canonical = k.toLowerCase() === 'key' ? 'privkey' : k.toLowerCase();
182
+ if (!seen.has(canonical)) {
183
+ seen.add(canonical);
184
+ queryParams.push(`${canonical}=${encodeURIComponent(v)}`);
144
185
  }
186
+ }
187
+
188
+ // Stable order: amount first, privkey second, rest alphabetical
189
+ queryParams.sort((a, b) => {
190
+ const keyA = a.split('=')[0];
191
+ const keyB = b.split('=')[0];
192
+ if (keyA === 'amount') return -1;
193
+ if (keyB === 'amount') return 1;
194
+ if (keyA === 'privkey') return -1;
195
+ if (keyB === 'privkey') return 1;
196
+ return keyA.localeCompare(keyB);
145
197
  });
146
198
 
147
199
  if (queryParams.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "txo_parser",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "Parser for TXO URI Specification (v0.1)",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -23,7 +23,7 @@
23
23
  "cli"
24
24
  ],
25
25
  "author": "Melvin Carvalho",
26
- "license": "MIT",
26
+ "license": "AGPL-3.0-or-later",
27
27
  "bugs": {
28
28
  "url": "https://github.com/sandy-mount/txo_parser/issues"
29
29
  },