@tabai/sdk 0.2.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/LICENSE +23 -0
- package/README.md +401 -0
- package/bin/tab.mjs +23 -0
- package/dist/_shared/abi.d.ts +150 -0
- package/dist/_shared/abi.d.ts.map +1 -0
- package/dist/_shared/abi.js +197 -0
- package/dist/_shared/abi.js.map +1 -0
- package/dist/_shared/chains.d.ts +118 -0
- package/dist/_shared/chains.d.ts.map +1 -0
- package/dist/_shared/chains.js +89 -0
- package/dist/_shared/chains.js.map +1 -0
- package/dist/_shared/hex.d.ts +35 -0
- package/dist/_shared/hex.d.ts.map +1 -0
- package/dist/_shared/hex.js +40 -0
- package/dist/_shared/hex.js.map +1 -0
- package/dist/_shared/index.d.ts +14 -0
- package/dist/_shared/index.d.ts.map +1 -0
- package/dist/_shared/index.js +14 -0
- package/dist/_shared/index.js.map +1 -0
- package/dist/_shared/keccak256.d.ts +29 -0
- package/dist/_shared/keccak256.d.ts.map +1 -0
- package/dist/_shared/keccak256.js +145 -0
- package/dist/_shared/keccak256.js.map +1 -0
- package/dist/_shared/result.d.ts +78 -0
- package/dist/_shared/result.d.ts.map +1 -0
- package/dist/_shared/result.js +61 -0
- package/dist/_shared/result.js.map +1 -0
- package/dist/cli/client-config.d.ts +155 -0
- package/dist/cli/client-config.d.ts.map +1 -0
- package/dist/cli/client-config.js +382 -0
- package/dist/cli/client-config.js.map +1 -0
- package/dist/cli/connect.d.ts +76 -0
- package/dist/cli/connect.d.ts.map +1 -0
- package/dist/cli/connect.js +158 -0
- package/dist/cli/connect.js.map +1 -0
- package/dist/cli/doctor.d.ts +57 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +253 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.d.ts +13 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +13 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/main.d.ts +45 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +371 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/errors.d.ts +29 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +37 -0
- package/dist/errors.js.map +1 -0
- package/dist/http/client-402.d.ts +243 -0
- package/dist/http/client-402.d.ts.map +1 -0
- package/dist/http/client-402.js +515 -0
- package/dist/http/client-402.js.map +1 -0
- package/dist/http/headers.d.ts +173 -0
- package/dist/http/headers.d.ts.map +1 -0
- package/dist/http/headers.js +284 -0
- package/dist/http/headers.js.map +1 -0
- package/dist/http/index.d.ts +15 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +15 -0
- package/dist/http/index.js.map +1 -0
- package/dist/http/metering-claim.d.ts +82 -0
- package/dist/http/metering-claim.d.ts.map +1 -0
- package/dist/http/metering-claim.js +99 -0
- package/dist/http/metering-claim.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +40 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +50 -0
- package/dist/logger.js.map +1 -0
- package/dist/mcp/assets.d.ts +31 -0
- package/dist/mcp/assets.d.ts.map +1 -0
- package/dist/mcp/assets.js +78 -0
- package/dist/mcp/assets.js.map +1 -0
- package/dist/mcp/index.d.ts +19 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +19 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/json-schema.d.ts +86 -0
- package/dist/mcp/json-schema.d.ts.map +1 -0
- package/dist/mcp/json-schema.js +215 -0
- package/dist/mcp/json-schema.js.map +1 -0
- package/dist/mcp/json.d.ts +43 -0
- package/dist/mcp/json.d.ts.map +1 -0
- package/dist/mcp/json.js +69 -0
- package/dist/mcp/json.js.map +1 -0
- package/dist/mcp/registry-client.d.ts +88 -0
- package/dist/mcp/registry-client.d.ts.map +1 -0
- package/dist/mcp/registry-client.js +158 -0
- package/dist/mcp/registry-client.js.map +1 -0
- package/dist/mcp/schemas.d.ts +82 -0
- package/dist/mcp/schemas.d.ts.map +1 -0
- package/dist/mcp/schemas.js +493 -0
- package/dist/mcp/schemas.js.map +1 -0
- package/dist/mcp/server.d.ts +97 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +285 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/settings.d.ts +90 -0
- package/dist/mcp/settings.d.ts.map +1 -0
- package/dist/mcp/settings.js +160 -0
- package/dist/mcp/settings.js.map +1 -0
- package/dist/mcp/toolset.d.ts +231 -0
- package/dist/mcp/toolset.d.ts.map +1 -0
- package/dist/mcp/toolset.js +760 -0
- package/dist/mcp/toolset.js.map +1 -0
- package/dist/payments/abi.d.ts +9 -0
- package/dist/payments/abi.d.ts.map +1 -0
- package/dist/payments/abi.js +17 -0
- package/dist/payments/abi.js.map +1 -0
- package/dist/payments/config.d.ts +199 -0
- package/dist/payments/config.d.ts.map +1 -0
- package/dist/payments/config.js +259 -0
- package/dist/payments/config.js.map +1 -0
- package/dist/payments/index.d.ts +13 -0
- package/dist/payments/index.d.ts.map +1 -0
- package/dist/payments/index.js +13 -0
- package/dist/payments/index.js.map +1 -0
- package/dist/payments/kuru.d.ts +191 -0
- package/dist/payments/kuru.d.ts.map +1 -0
- package/dist/payments/kuru.js +377 -0
- package/dist/payments/kuru.js.map +1 -0
- package/dist/payments/monad.d.ts +69 -0
- package/dist/payments/monad.d.ts.map +1 -0
- package/dist/payments/monad.js +306 -0
- package/dist/payments/monad.js.map +1 -0
- package/dist/payments/permit2.d.ts +118 -0
- package/dist/payments/permit2.d.ts.map +1 -0
- package/dist/payments/permit2.js +366 -0
- package/dist/payments/permit2.js.map +1 -0
- package/dist/payments/registry.d.ts +119 -0
- package/dist/payments/registry.d.ts.map +1 -0
- package/dist/payments/registry.js +199 -0
- package/dist/payments/registry.js.map +1 -0
- package/dist/payments/strategy.d.ts +80 -0
- package/dist/payments/strategy.d.ts.map +1 -0
- package/dist/payments/strategy.js +103 -0
- package/dist/payments/strategy.js.map +1 -0
- package/dist/proxy/hooks.d.ts +90 -0
- package/dist/proxy/hooks.d.ts.map +1 -0
- package/dist/proxy/hooks.js +35 -0
- package/dist/proxy/hooks.js.map +1 -0
- package/dist/proxy/index.d.ts +9 -0
- package/dist/proxy/index.d.ts.map +1 -0
- package/dist/proxy/index.js +9 -0
- package/dist/proxy/index.js.map +1 -0
- package/dist/proxy/proxy.d.ts +156 -0
- package/dist/proxy/proxy.d.ts.map +1 -0
- package/dist/proxy/proxy.js +366 -0
- package/dist/proxy/proxy.js.map +1 -0
- package/dist/server/adapters/express.d.ts +89 -0
- package/dist/server/adapters/express.d.ts.map +1 -0
- package/dist/server/adapters/express.js +215 -0
- package/dist/server/adapters/express.js.map +1 -0
- package/dist/server/adapters/hono.d.ts +52 -0
- package/dist/server/adapters/hono.d.ts.map +1 -0
- package/dist/server/adapters/hono.js +61 -0
- package/dist/server/adapters/hono.js.map +1 -0
- package/dist/server/adapters/next.d.ts +52 -0
- package/dist/server/adapters/next.d.ts.map +1 -0
- package/dist/server/adapters/next.js +56 -0
- package/dist/server/adapters/next.js.map +1 -0
- package/dist/server/index.d.ts +30 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +30 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/metering.d.ts +209 -0
- package/dist/server/metering.d.ts.map +1 -0
- package/dist/server/metering.js +365 -0
- package/dist/server/metering.js.map +1 -0
- package/dist/server/post-paid.d.ts +355 -0
- package/dist/server/post-paid.d.ts.map +1 -0
- package/dist/server/post-paid.js +512 -0
- package/dist/server/post-paid.js.map +1 -0
- package/dist/x402/client.d.ts +203 -0
- package/dist/x402/client.d.ts.map +1 -0
- package/dist/x402/client.js +337 -0
- package/dist/x402/client.js.map +1 -0
- package/dist/x402/hub.d.ts +79 -0
- package/dist/x402/hub.d.ts.map +1 -0
- package/dist/x402/hub.js +164 -0
- package/dist/x402/hub.js.map +1 -0
- package/dist/x402/index.d.ts +27 -0
- package/dist/x402/index.d.ts.map +1 -0
- package/dist/x402/index.js +27 -0
- package/dist/x402/index.js.map +1 -0
- package/dist/x402/proxy.d.ts +162 -0
- package/dist/x402/proxy.d.ts.map +1 -0
- package/dist/x402/proxy.js +198 -0
- package/dist/x402/proxy.js.map +1 -0
- package/dist/x402/server.d.ts +162 -0
- package/dist/x402/server.d.ts.map +1 -0
- package/dist/x402/server.js +306 -0
- package/dist/x402/server.js.map +1 -0
- package/dist/x402/wire.d.ts +104 -0
- package/dist/x402/wire.d.ts.map +1 -0
- package/dist/x402/wire.js +265 -0
- package/dist/x402/wire.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keccak-256, written against native `bigint`.
|
|
3
|
+
*
|
|
4
|
+
* `@tabai/shared` carries zero runtime dependencies, and Keccak-256 is not
|
|
5
|
+
* SHA3-256, the two differ in their padding byte, so the platform hash
|
|
6
|
+
* primitives cannot stand in for it. The permutation below is therefore
|
|
7
|
+
* implemented here: 24 rounds of Keccak-f[1600] over 25 64-bit lanes, rate 136
|
|
8
|
+
* bytes, capacity 64 bytes, `0x01` domain padding.
|
|
9
|
+
*
|
|
10
|
+
* Correctness is pinned by published digests in `test/keccak256.test.mjs`: the
|
|
11
|
+
* empty-input digest, the `abc` digest, and the ERC-20 `Transfer` and `Approval`
|
|
12
|
+
* event topics any chain explorer shows. The permutation was additionally
|
|
13
|
+
* cross-checked against the platform SHA3-256, same permutation, same rate,
|
|
14
|
+
* different padding byte, at input lengths spanning the 136-byte block
|
|
15
|
+
* boundary. It is used only to derive a handful of event topic hashes at module
|
|
16
|
+
* load, so the `bigint` lane representation costs nothing that matters.
|
|
17
|
+
*/
|
|
18
|
+
const LANE_MASK = (1n << 64n) - 1n;
|
|
19
|
+
/** Rate in bytes for a 256-bit digest: 200 - 2 * 32. */
|
|
20
|
+
const RATE_BYTES = 136;
|
|
21
|
+
/** Lanes of the state absorbed per block: RATE_BYTES / 8. */
|
|
22
|
+
const RATE_LANES = RATE_BYTES / 8;
|
|
23
|
+
/** Iota round constants. */
|
|
24
|
+
const ROUND_CONSTANTS = [
|
|
25
|
+
0x0000000000000001n,
|
|
26
|
+
0x0000000000008082n,
|
|
27
|
+
0x800000000000808an,
|
|
28
|
+
0x8000000080008000n,
|
|
29
|
+
0x000000000000808bn,
|
|
30
|
+
0x0000000080000001n,
|
|
31
|
+
0x8000000080008081n,
|
|
32
|
+
0x8000000000008009n,
|
|
33
|
+
0x000000000000008an,
|
|
34
|
+
0x0000000000000088n,
|
|
35
|
+
0x0000000080008009n,
|
|
36
|
+
0x000000008000000an,
|
|
37
|
+
0x000000008000808bn,
|
|
38
|
+
0x800000000000008bn,
|
|
39
|
+
0x8000000000008089n,
|
|
40
|
+
0x8000000000008003n,
|
|
41
|
+
0x8000000000008002n,
|
|
42
|
+
0x8000000000000080n,
|
|
43
|
+
0x000000000000800an,
|
|
44
|
+
0x800000008000000an,
|
|
45
|
+
0x8000000080008081n,
|
|
46
|
+
0x8000000000008080n,
|
|
47
|
+
0x0000000080000001n,
|
|
48
|
+
0x8000000080008008n,
|
|
49
|
+
];
|
|
50
|
+
/**
|
|
51
|
+
* Rho rotation offsets, in lane-permutation order: `((t + 1)(t + 2) / 2) mod 64`
|
|
52
|
+
* for step `t`, which is the offset of the lane the walk sits on at that step.
|
|
53
|
+
*/
|
|
54
|
+
const ROTATIONS = [
|
|
55
|
+
1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,
|
|
56
|
+
];
|
|
57
|
+
/** Pi lane permutation. */
|
|
58
|
+
const LANE_ORDER = [
|
|
59
|
+
10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,
|
|
60
|
+
];
|
|
61
|
+
const rotl = (lane, bits) => {
|
|
62
|
+
const shift = BigInt(bits);
|
|
63
|
+
return ((lane << shift) | (lane >> (64n - shift))) & LANE_MASK;
|
|
64
|
+
};
|
|
65
|
+
/** Applies Keccak-f[1600] to `state` in place. */
|
|
66
|
+
function permute(state) {
|
|
67
|
+
const column = new Array(5).fill(0n);
|
|
68
|
+
for (let round = 0; round < 24; round += 1) {
|
|
69
|
+
// theta
|
|
70
|
+
for (let x = 0; x < 5; x += 1) {
|
|
71
|
+
column[x] =
|
|
72
|
+
state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20];
|
|
73
|
+
}
|
|
74
|
+
for (let x = 0; x < 5; x += 1) {
|
|
75
|
+
const parity = column[(x + 4) % 5] ^ rotl(column[(x + 1) % 5], 1);
|
|
76
|
+
for (let y = 0; y < 25; y += 5)
|
|
77
|
+
state[y + x] = state[y + x] ^ parity;
|
|
78
|
+
}
|
|
79
|
+
// rho and pi
|
|
80
|
+
let carried = state[1];
|
|
81
|
+
for (let step = 0; step < 24; step += 1) {
|
|
82
|
+
const target = LANE_ORDER[step];
|
|
83
|
+
const displaced = state[target];
|
|
84
|
+
state[target] = rotl(carried, ROTATIONS[step]);
|
|
85
|
+
carried = displaced;
|
|
86
|
+
}
|
|
87
|
+
// chi
|
|
88
|
+
for (let y = 0; y < 25; y += 5) {
|
|
89
|
+
for (let x = 0; x < 5; x += 1)
|
|
90
|
+
column[x] = state[y + x];
|
|
91
|
+
for (let x = 0; x < 5; x += 1) {
|
|
92
|
+
state[y + x] = state[y + x] ^ ((column[(x + 1) % 5] ^ LANE_MASK) & column[(x + 2) % 5]);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// iota
|
|
96
|
+
state[0] = state[0] ^ ROUND_CONSTANTS[round];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Hashes raw bytes and renders the digest as a 32-byte word. */
|
|
100
|
+
export function keccak256(data) {
|
|
101
|
+
const state = new Array(25).fill(0n);
|
|
102
|
+
const blocks = Math.ceil((data.length + 1) / RATE_BYTES);
|
|
103
|
+
const padded = new Uint8Array(blocks * RATE_BYTES);
|
|
104
|
+
padded.set(data);
|
|
105
|
+
padded[data.length] = 0x01;
|
|
106
|
+
const finalByte = padded.length - 1;
|
|
107
|
+
padded[finalByte] = padded[finalByte] | 0x80;
|
|
108
|
+
for (let offset = 0; offset < padded.length; offset += RATE_BYTES) {
|
|
109
|
+
for (let lane = 0; lane < RATE_LANES; lane += 1) {
|
|
110
|
+
let word = 0n;
|
|
111
|
+
for (let byte = 7; byte >= 0; byte -= 1) {
|
|
112
|
+
word = (word << 8n) | BigInt(padded[offset + lane * 8 + byte]);
|
|
113
|
+
}
|
|
114
|
+
state[lane] = state[lane] ^ word;
|
|
115
|
+
}
|
|
116
|
+
permute(state);
|
|
117
|
+
}
|
|
118
|
+
let digest = "";
|
|
119
|
+
for (let lane = 0; lane < 4; lane += 1) {
|
|
120
|
+
const word = state[lane];
|
|
121
|
+
for (let byte = 0; byte < 8; byte += 1) {
|
|
122
|
+
digest += ((word >> BigInt(byte * 8)) & 0xffn).toString(16).padStart(2, "0");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return `0x${digest}`;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Hashes an ASCII string. Event and function signatures are ASCII by
|
|
129
|
+
* construction, so refusing anything above `0x7f` keeps encoding out of the
|
|
130
|
+
* picture entirely.
|
|
131
|
+
*
|
|
132
|
+
* @throws RangeError when `text` carries a non-ASCII code unit.
|
|
133
|
+
*/
|
|
134
|
+
export function keccak256Ascii(text) {
|
|
135
|
+
const bytes = new Uint8Array(text.length);
|
|
136
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
137
|
+
const code = text.charCodeAt(i);
|
|
138
|
+
if (code > 0x7f) {
|
|
139
|
+
throw new RangeError(`keccak256Ascii: expected ASCII only, found code unit ${code} at index ${i}`);
|
|
140
|
+
}
|
|
141
|
+
bytes[i] = code;
|
|
142
|
+
}
|
|
143
|
+
return keccak256(bytes);
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=keccak256.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keccak256.js","sourceRoot":"","sources":["../src/keccak256.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,MAAM,SAAS,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,GAAG,EAAE,CAAC;AAEnC,wDAAwD;AACxD,MAAM,UAAU,GAAG,GAAG,CAAC;AAEvB,6DAA6D;AAC7D,MAAM,UAAU,GAAG,UAAU,GAAG,CAAC,CAAC;AAElC,4BAA4B;AAC5B,MAAM,eAAe,GAAsB;IACzC,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;IACnB,mBAAmB;CACpB,CAAC;AAEF;;;GAGG;AACH,MAAM,SAAS,GAAsB;IACnC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE;CAC1F,CAAC;AAEF,2BAA2B;AAC3B,MAAM,UAAU,GAAsB;IACpC,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;CACtF,CAAC;AAEF,MAAM,IAAI,GAAG,CAAC,IAAY,EAAE,IAAY,EAAU,EAAE;IAClD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3B,OAAO,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;AACjE,CAAC,CAAC;AAEF,kDAAkD;AAClD,SAAS,OAAO,CAAC,KAAe;IAC9B,MAAM,MAAM,GAAG,IAAI,KAAK,CAAS,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAE7C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,EAAE,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QAC3C,QAAQ;QACR,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,CAAC,CAAC,CAAC;gBACP,KAAK,CAAC,CAAC,CAAE,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAE,GAAG,KAAK,CAAC,CAAC,GAAG,EAAE,CAAE,GAAG,KAAK,CAAC,CAAC,GAAG,EAAE,CAAE,GAAG,KAAK,CAAC,CAAC,GAAG,EAAE,CAAE,CAAC;QACjF,CAAC;QACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAE,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAE,EAAE,CAAC,CAAC,CAAC;YACpE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC;gBAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAE,GAAG,MAAM,CAAC;QACxE,CAAC;QAED,aAAa;QACb,IAAI,OAAO,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACxB,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,EAAE,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;YACxC,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAE,CAAC;YACjC,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAE,CAAC;YACjC,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,CAAE,CAAC,CAAC;YAChD,OAAO,GAAG,SAAS,CAAC;QACtB,CAAC;QAED,MAAM;QACN,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC;gBAAE,MAAM,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC;YACzD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9B,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAE,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAE,GAAG,SAAS,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC,CAAC;YAC7F,CAAC;QACH,CAAC;QAED,OAAO;QACP,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAE,GAAG,eAAe,CAAC,KAAK,CAAE,CAAC;IACjD,CAAC;AACH,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,SAAS,CAAC,IAAgB;IACxC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAS,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAE7C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC;IACzD,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM,GAAG,UAAU,CAAC,CAAC;IACnD,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC;IAC3B,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACpC,MAAM,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,SAAS,CAAE,GAAG,IAAI,CAAC;IAE9C,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,IAAI,UAAU,EAAE,CAAC;QAClE,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,UAAU,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;YAChD,IAAI,IAAI,GAAG,EAAE,CAAC;YACd,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;gBACxC,IAAI,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,IAAI,CAAE,CAAC,CAAC;YAClE,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,CAAE,GAAG,IAAI,CAAC;QACpC,CAAC;QACD,OAAO,CAAC,KAAK,CAAC,CAAC;IACjB,CAAC;IAED,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAE,CAAC;QAC1B,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,CAAC,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;IACD,OAAO,KAAK,MAAM,EAAE,CAAC;AACvB,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAChC,IAAI,IAAI,GAAG,IAAI,EAAE,CAAC;YAChB,MAAM,IAAI,UAAU,CAClB,wDAAwD,IAAI,aAAa,CAAC,EAAE,CAC7E,CAAC;QACJ,CAAC;QACD,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAClB,CAAC;IACD,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;AAC1B,CAAC","sourcesContent":["/**\n * Keccak-256, written against native `bigint`.\n *\n * `@tabai/shared` carries zero runtime dependencies, and Keccak-256 is not\n * SHA3-256, the two differ in their padding byte, so the platform hash\n * primitives cannot stand in for it. The permutation below is therefore\n * implemented here: 24 rounds of Keccak-f[1600] over 25 64-bit lanes, rate 136\n * bytes, capacity 64 bytes, `0x01` domain padding.\n *\n * Correctness is pinned by published digests in `test/keccak256.test.mjs`: the\n * empty-input digest, the `abc` digest, and the ERC-20 `Transfer` and `Approval`\n * event topics any chain explorer shows. The permutation was additionally\n * cross-checked against the platform SHA3-256, same permutation, same rate,\n * different padding byte, at input lengths spanning the 136-byte block\n * boundary. It is used only to derive a handful of event topic hashes at module\n * load, so the `bigint` lane representation costs nothing that matters.\n */\n\nimport type { Bytes32 } from \"./hex.js\";\n\nconst LANE_MASK = (1n << 64n) - 1n;\n\n/** Rate in bytes for a 256-bit digest: 200 - 2 * 32. */\nconst RATE_BYTES = 136;\n\n/** Lanes of the state absorbed per block: RATE_BYTES / 8. */\nconst RATE_LANES = RATE_BYTES / 8;\n\n/** Iota round constants. */\nconst ROUND_CONSTANTS: readonly bigint[] = [\n 0x0000000000000001n,\n 0x0000000000008082n,\n 0x800000000000808an,\n 0x8000000080008000n,\n 0x000000000000808bn,\n 0x0000000080000001n,\n 0x8000000080008081n,\n 0x8000000000008009n,\n 0x000000000000008an,\n 0x0000000000000088n,\n 0x0000000080008009n,\n 0x000000008000000an,\n 0x000000008000808bn,\n 0x800000000000008bn,\n 0x8000000000008089n,\n 0x8000000000008003n,\n 0x8000000000008002n,\n 0x8000000000000080n,\n 0x000000000000800an,\n 0x800000008000000an,\n 0x8000000080008081n,\n 0x8000000000008080n,\n 0x0000000080000001n,\n 0x8000000080008008n,\n];\n\n/**\n * Rho rotation offsets, in lane-permutation order: `((t + 1)(t + 2) / 2) mod 64`\n * for step `t`, which is the offset of the lane the walk sits on at that step.\n */\nconst ROTATIONS: readonly number[] = [\n 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 2, 14, 27, 41, 56, 8, 25, 43, 62, 18, 39, 61, 20, 44,\n];\n\n/** Pi lane permutation. */\nconst LANE_ORDER: readonly number[] = [\n 10, 7, 11, 17, 18, 3, 5, 16, 8, 21, 24, 4, 15, 23, 19, 13, 12, 2, 20, 14, 22, 9, 6, 1,\n];\n\nconst rotl = (lane: bigint, bits: number): bigint => {\n const shift = BigInt(bits);\n return ((lane << shift) | (lane >> (64n - shift))) & LANE_MASK;\n};\n\n/** Applies Keccak-f[1600] to `state` in place. */\nfunction permute(state: bigint[]): void {\n const column = new Array<bigint>(5).fill(0n);\n\n for (let round = 0; round < 24; round += 1) {\n // theta\n for (let x = 0; x < 5; x += 1) {\n column[x] =\n state[x]! ^ state[x + 5]! ^ state[x + 10]! ^ state[x + 15]! ^ state[x + 20]!;\n }\n for (let x = 0; x < 5; x += 1) {\n const parity = column[(x + 4) % 5]! ^ rotl(column[(x + 1) % 5]!, 1);\n for (let y = 0; y < 25; y += 5) state[y + x] = state[y + x]! ^ parity;\n }\n\n // rho and pi\n let carried = state[1]!;\n for (let step = 0; step < 24; step += 1) {\n const target = LANE_ORDER[step]!;\n const displaced = state[target]!;\n state[target] = rotl(carried, ROTATIONS[step]!);\n carried = displaced;\n }\n\n // chi\n for (let y = 0; y < 25; y += 5) {\n for (let x = 0; x < 5; x += 1) column[x] = state[y + x]!;\n for (let x = 0; x < 5; x += 1) {\n state[y + x] = state[y + x]! ^ ((column[(x + 1) % 5]! ^ LANE_MASK) & column[(x + 2) % 5]!);\n }\n }\n\n // iota\n state[0] = state[0]! ^ ROUND_CONSTANTS[round]!;\n }\n}\n\n/** Hashes raw bytes and renders the digest as a 32-byte word. */\nexport function keccak256(data: Uint8Array): Bytes32 {\n const state = new Array<bigint>(25).fill(0n);\n\n const blocks = Math.ceil((data.length + 1) / RATE_BYTES);\n const padded = new Uint8Array(blocks * RATE_BYTES);\n padded.set(data);\n padded[data.length] = 0x01;\n const finalByte = padded.length - 1;\n padded[finalByte] = padded[finalByte]! | 0x80;\n\n for (let offset = 0; offset < padded.length; offset += RATE_BYTES) {\n for (let lane = 0; lane < RATE_LANES; lane += 1) {\n let word = 0n;\n for (let byte = 7; byte >= 0; byte -= 1) {\n word = (word << 8n) | BigInt(padded[offset + lane * 8 + byte]!);\n }\n state[lane] = state[lane]! ^ word;\n }\n permute(state);\n }\n\n let digest = \"\";\n for (let lane = 0; lane < 4; lane += 1) {\n const word = state[lane]!;\n for (let byte = 0; byte < 8; byte += 1) {\n digest += ((word >> BigInt(byte * 8)) & 0xffn).toString(16).padStart(2, \"0\");\n }\n }\n return `0x${digest}`;\n}\n\n/**\n * Hashes an ASCII string. Event and function signatures are ASCII by\n * construction, so refusing anything above `0x7f` keeps encoding out of the\n * picture entirely.\n *\n * @throws RangeError when `text` carries a non-ASCII code unit.\n */\nexport function keccak256Ascii(text: string): Bytes32 {\n const bytes = new Uint8Array(text.length);\n for (let i = 0; i < text.length; i += 1) {\n const code = text.charCodeAt(i);\n if (code > 0x7f) {\n throw new RangeError(\n `keccak256Ascii: expected ASCII only, found code unit ${code} at index ${i}`,\n );\n }\n bytes[i] = code;\n }\n return keccak256(bytes);\n}\n"]}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The zero-throw result type.
|
|
3
|
+
*
|
|
4
|
+
* No exported SDK function throws. `wrap` is the single boundary at which a
|
|
5
|
+
* thrown value from a dependency becomes a `TabError`, so a caller only ever
|
|
6
|
+
* branches on `result.ok` and never writes a `try` block of its own.
|
|
7
|
+
*
|
|
8
|
+
* HTTP surfaces map `category` to a status code:
|
|
9
|
+
* `VALIDATION` 400, `AUTHORISATION` 403, `NOT_FOUND` 404, `LIMIT` 402,
|
|
10
|
+
* `CONFLICT` 409, `UPSTREAM` 502, `UNAVAILABLE` 503, and
|
|
11
|
+
* `CHAIN`/`INTERNAL` 500.
|
|
12
|
+
*
|
|
13
|
+
* Requirements: 21.5
|
|
14
|
+
*/
|
|
15
|
+
export type ErrorCategory =
|
|
16
|
+
/** caller-supplied input is malformed */
|
|
17
|
+
"VALIDATION"
|
|
18
|
+
/** missing, expired, or exceeded spending authorisation */
|
|
19
|
+
| "AUTHORISATION"
|
|
20
|
+
/** unknown service, agent, asset, tab, or settlement */
|
|
21
|
+
| "NOT_FOUND"
|
|
22
|
+
/** credit limit or headroom exhausted */
|
|
23
|
+
| "LIMIT"
|
|
24
|
+
/** A Monad transaction failed or reverted */
|
|
25
|
+
| "CHAIN"
|
|
26
|
+
/** RPC endpoint, module loader, or service endpoint failure */
|
|
27
|
+
| "UPSTREAM"
|
|
28
|
+
/** duplicate registration, timelock still pending */
|
|
29
|
+
| "CONFLICT"
|
|
30
|
+
/** the write path is unavailable */
|
|
31
|
+
| "UNAVAILABLE"
|
|
32
|
+
/** invariant violated inside our own code */
|
|
33
|
+
| "INTERNAL";
|
|
34
|
+
/** The HTTP status every category maps to, so services cannot disagree. */
|
|
35
|
+
export declare const HTTP_STATUS_BY_CATEGORY: Readonly<Record<ErrorCategory, number>>;
|
|
36
|
+
export interface TabError {
|
|
37
|
+
category: ErrorCategory;
|
|
38
|
+
/** stable and machine-readable, for example `LIMIT_EXCEEDED` */
|
|
39
|
+
code: string;
|
|
40
|
+
/** human-readable, carrying no secret and no stack trace */
|
|
41
|
+
message: string;
|
|
42
|
+
retryable: boolean;
|
|
43
|
+
retryAfterMs?: number;
|
|
44
|
+
details?: Record<string, string | number | boolean>;
|
|
45
|
+
cause?: {
|
|
46
|
+
code: string;
|
|
47
|
+
message: string;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export type Result<T> = {
|
|
51
|
+
ok: true;
|
|
52
|
+
value: T;
|
|
53
|
+
} | {
|
|
54
|
+
ok: false;
|
|
55
|
+
error: TabError;
|
|
56
|
+
};
|
|
57
|
+
export declare const ok: <T>(value: T) => Result<T>;
|
|
58
|
+
export declare const err: (error: TabError) => Result<never>;
|
|
59
|
+
/**
|
|
60
|
+
* Runs `fn` and converts a thrown value into a `TabError` through `map`.
|
|
61
|
+
* This is the only place in the workspace where a `catch` belongs.
|
|
62
|
+
*/
|
|
63
|
+
export declare const wrap: <T>(fn: () => Promise<T>, map: (e: unknown) => TabError) => Promise<Result<T>>;
|
|
64
|
+
/** The synchronous sibling of `wrap`, for pure computations that validate their input. */
|
|
65
|
+
export declare const wrapSync: <T>(fn: () => T, map: (e: unknown) => TabError) => Result<T>;
|
|
66
|
+
/** Narrows an unknown thrown value into the `cause` shape a `TabError` accepts. */
|
|
67
|
+
export declare const causeOf: (e: unknown) => {
|
|
68
|
+
code: string;
|
|
69
|
+
message: string;
|
|
70
|
+
};
|
|
71
|
+
/** True when the result carries a value. Present so callers can filter without a cast. */
|
|
72
|
+
export declare const isOk: <T>(result: Result<T>) => result is {
|
|
73
|
+
ok: true;
|
|
74
|
+
value: T;
|
|
75
|
+
};
|
|
76
|
+
/** Returns the HTTP status a failed result maps to. */
|
|
77
|
+
export declare const httpStatusOf: (error: TabError) => number;
|
|
78
|
+
//# sourceMappingURL=result.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.d.ts","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,MAAM,aAAa;AACvB,yCAAyC;AACvC,YAAY;AACd,2DAA2D;GACzD,eAAe;AACjB,wDAAwD;GACtD,WAAW;AACb,yCAAyC;GACvC,OAAO;AACT,6CAA6C;GAC3C,OAAO;AACT,+DAA+D;GAC7D,UAAU;AACZ,qDAAqD;GACnD,UAAU;AACZ,oCAAoC;GAClC,aAAa;AACf,6CAA6C;GAC3C,UAAU,CAAC;AAEf,2EAA2E;AAC3E,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAU3E,CAAC;AAEF,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,aAAa,CAAC;IACxB,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IACpD,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAED,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEhF,eAAO,MAAM,EAAE,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,MAAM,CAAC,CAAC,CAA0B,CAAC;AAEpE,eAAO,MAAM,GAAG,GAAI,OAAO,QAAQ,KAAG,MAAM,CAAC,KAAK,CAA2B,CAAC;AAE9E;;;GAGG;AACH,eAAO,MAAM,IAAI,GAAU,CAAC,EAC1B,IAAI,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,KAAK,CAAC,CAAC,EAAE,OAAO,KAAK,QAAQ,KAC5B,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAMnB,CAAC;AAEF,0FAA0F;AAC1F,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,IAAI,MAAM,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,OAAO,KAAK,QAAQ,KAAG,MAAM,CAAC,CAAC,CAMhF,CAAC;AAEF,mFAAmF;AACnF,eAAO,MAAM,OAAO,GAAI,GAAG,OAAO,KAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAKnE,CAAC;AAEF,0FAA0F;AAC1F,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,QAAQ,MAAM,CAAC,CAAC,CAAC,KAAG,MAAM,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAe,CAAC;AAE1F,uDAAuD;AACvD,eAAO,MAAM,YAAY,GAAI,OAAO,QAAQ,KAAG,MAAiD,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The zero-throw result type.
|
|
3
|
+
*
|
|
4
|
+
* No exported SDK function throws. `wrap` is the single boundary at which a
|
|
5
|
+
* thrown value from a dependency becomes a `TabError`, so a caller only ever
|
|
6
|
+
* branches on `result.ok` and never writes a `try` block of its own.
|
|
7
|
+
*
|
|
8
|
+
* HTTP surfaces map `category` to a status code:
|
|
9
|
+
* `VALIDATION` 400, `AUTHORISATION` 403, `NOT_FOUND` 404, `LIMIT` 402,
|
|
10
|
+
* `CONFLICT` 409, `UPSTREAM` 502, `UNAVAILABLE` 503, and
|
|
11
|
+
* `CHAIN`/`INTERNAL` 500.
|
|
12
|
+
*
|
|
13
|
+
* Requirements: 21.5
|
|
14
|
+
*/
|
|
15
|
+
/** The HTTP status every category maps to, so services cannot disagree. */
|
|
16
|
+
export const HTTP_STATUS_BY_CATEGORY = {
|
|
17
|
+
VALIDATION: 400,
|
|
18
|
+
AUTHORISATION: 403,
|
|
19
|
+
NOT_FOUND: 404,
|
|
20
|
+
LIMIT: 402,
|
|
21
|
+
CHAIN: 500,
|
|
22
|
+
UPSTREAM: 502,
|
|
23
|
+
CONFLICT: 409,
|
|
24
|
+
UNAVAILABLE: 503,
|
|
25
|
+
INTERNAL: 500,
|
|
26
|
+
};
|
|
27
|
+
export const ok = (value) => ({ ok: true, value });
|
|
28
|
+
export const err = (error) => ({ ok: false, error });
|
|
29
|
+
/**
|
|
30
|
+
* Runs `fn` and converts a thrown value into a `TabError` through `map`.
|
|
31
|
+
* This is the only place in the workspace where a `catch` belongs.
|
|
32
|
+
*/
|
|
33
|
+
export const wrap = async (fn, map) => {
|
|
34
|
+
try {
|
|
35
|
+
return ok(await fn());
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
return err(map(e));
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
/** The synchronous sibling of `wrap`, for pure computations that validate their input. */
|
|
42
|
+
export const wrapSync = (fn, map) => {
|
|
43
|
+
try {
|
|
44
|
+
return ok(fn());
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
return err(map(e));
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
/** Narrows an unknown thrown value into the `cause` shape a `TabError` accepts. */
|
|
51
|
+
export const causeOf = (e) => {
|
|
52
|
+
if (e instanceof Error) {
|
|
53
|
+
return { code: e.name, message: e.message };
|
|
54
|
+
}
|
|
55
|
+
return { code: "UNKNOWN", message: String(e) };
|
|
56
|
+
};
|
|
57
|
+
/** True when the result carries a value. Present so callers can filter without a cast. */
|
|
58
|
+
export const isOk = (result) => result.ok;
|
|
59
|
+
/** Returns the HTTP status a failed result maps to. */
|
|
60
|
+
export const httpStatusOf = (error) => HTTP_STATUS_BY_CATEGORY[error.category];
|
|
61
|
+
//# sourceMappingURL=result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAsBH,2EAA2E;AAC3E,MAAM,CAAC,MAAM,uBAAuB,GAA4C;IAC9E,UAAU,EAAE,GAAG;IACf,aAAa,EAAE,GAAG;IAClB,SAAS,EAAE,GAAG;IACd,KAAK,EAAE,GAAG;IACV,KAAK,EAAE,GAAG;IACV,QAAQ,EAAE,GAAG;IACb,QAAQ,EAAE,GAAG;IACb,WAAW,EAAE,GAAG;IAChB,QAAQ,EAAE,GAAG;CACd,CAAC;AAgBF,MAAM,CAAC,MAAM,EAAE,GAAG,CAAI,KAAQ,EAAa,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAEpE,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,KAAe,EAAiB,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;AAE9E;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,KAAK,EACvB,EAAoB,EACpB,GAA6B,EACT,EAAE;IACtB,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACxB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrB,CAAC;AACH,CAAC,CAAC;AAEF,0FAA0F;AAC1F,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAI,EAAW,EAAE,GAA6B,EAAa,EAAE;IACnF,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrB,CAAC;AACH,CAAC,CAAC;AAEF,mFAAmF;AACnF,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAU,EAAqC,EAAE;IACvE,IAAI,CAAC,YAAY,KAAK,EAAE,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IAC9C,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;AACjD,CAAC,CAAC;AAEF,0FAA0F;AAC1F,MAAM,CAAC,MAAM,IAAI,GAAG,CAAI,MAAiB,EAAoC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;AAE1F,uDAAuD;AACvD,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAe,EAAU,EAAE,CAAC,uBAAuB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC","sourcesContent":["/**\n * The zero-throw result type.\n *\n * No exported SDK function throws. `wrap` is the single boundary at which a\n * thrown value from a dependency becomes a `TabError`, so a caller only ever\n * branches on `result.ok` and never writes a `try` block of its own.\n *\n * HTTP surfaces map `category` to a status code:\n * `VALIDATION` 400, `AUTHORISATION` 403, `NOT_FOUND` 404, `LIMIT` 402,\n * `CONFLICT` 409, `UPSTREAM` 502, `UNAVAILABLE` 503, and\n * `CHAIN`/`INTERNAL` 500.\n *\n * Requirements: 21.5\n */\n\nexport type ErrorCategory =\n /** caller-supplied input is malformed */\n | \"VALIDATION\"\n /** missing, expired, or exceeded spending authorisation */\n | \"AUTHORISATION\"\n /** unknown service, agent, asset, tab, or settlement */\n | \"NOT_FOUND\"\n /** credit limit or headroom exhausted */\n | \"LIMIT\"\n /** A Monad transaction failed or reverted */\n | \"CHAIN\"\n /** RPC endpoint, module loader, or service endpoint failure */\n | \"UPSTREAM\"\n /** duplicate registration, timelock still pending */\n | \"CONFLICT\"\n /** the write path is unavailable */\n | \"UNAVAILABLE\"\n /** invariant violated inside our own code */\n | \"INTERNAL\";\n\n/** The HTTP status every category maps to, so services cannot disagree. */\nexport const HTTP_STATUS_BY_CATEGORY: Readonly<Record<ErrorCategory, number>> = {\n VALIDATION: 400,\n AUTHORISATION: 403,\n NOT_FOUND: 404,\n LIMIT: 402,\n CHAIN: 500,\n UPSTREAM: 502,\n CONFLICT: 409,\n UNAVAILABLE: 503,\n INTERNAL: 500,\n};\n\nexport interface TabError {\n category: ErrorCategory;\n /** stable and machine-readable, for example `LIMIT_EXCEEDED` */\n code: string;\n /** human-readable, carrying no secret and no stack trace */\n message: string;\n retryable: boolean;\n retryAfterMs?: number;\n details?: Record<string, string | number | boolean>;\n cause?: { code: string; message: string };\n}\n\nexport type Result<T> = { ok: true; value: T } | { ok: false; error: TabError };\n\nexport const ok = <T>(value: T): Result<T> => ({ ok: true, value });\n\nexport const err = (error: TabError): Result<never> => ({ ok: false, error });\n\n/**\n * Runs `fn` and converts a thrown value into a `TabError` through `map`.\n * This is the only place in the workspace where a `catch` belongs.\n */\nexport const wrap = async <T>(\n fn: () => Promise<T>,\n map: (e: unknown) => TabError,\n): Promise<Result<T>> => {\n try {\n return ok(await fn());\n } catch (e) {\n return err(map(e));\n }\n};\n\n/** The synchronous sibling of `wrap`, for pure computations that validate their input. */\nexport const wrapSync = <T>(fn: () => T, map: (e: unknown) => TabError): Result<T> => {\n try {\n return ok(fn());\n } catch (e) {\n return err(map(e));\n }\n};\n\n/** Narrows an unknown thrown value into the `cause` shape a `TabError` accepts. */\nexport const causeOf = (e: unknown): { code: string; message: string } => {\n if (e instanceof Error) {\n return { code: e.name, message: e.message };\n }\n return { code: \"UNKNOWN\", message: String(e) };\n};\n\n/** True when the result carries a value. Present so callers can filter without a cast. */\nexport const isOk = <T>(result: Result<T>): result is { ok: true; value: T } => result.ok;\n\n/** Returns the HTTP status a failed result maps to. */\nexport const httpStatusOf = (error: TabError): number => HTTP_STATUS_BY_CATEGORY[error.category];\n"]}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an MCP client keeps its server list, and how `tab connect` edits it.
|
|
3
|
+
*
|
|
4
|
+
* ## The claim this file has to earn
|
|
5
|
+
*
|
|
6
|
+
* Adopting Tab costs one line of configuration. That line is a `mcpServers`
|
|
7
|
+
* stanza in a file the client already owns, and `tab connect` writes it. The
|
|
8
|
+
* claim is only worth making if the write is safe to run, so three properties
|
|
9
|
+
* are enforced here rather than documented:
|
|
10
|
+
*
|
|
11
|
+
* - **Idempotent.** Running it twice writes once. The second run compares the
|
|
12
|
+
* merged document against what is on disk and, finding no change, writes
|
|
13
|
+
* nothing and takes no backup. A connect command that rewrote the file every
|
|
14
|
+
* time would churn a file the client also writes.
|
|
15
|
+
* - **Backed up.** Any run that does change the file copies the previous
|
|
16
|
+
* contents beside it first, named with the instant. This is somebody's editor
|
|
17
|
+
* configuration, and it may hold servers this package has never heard of.
|
|
18
|
+
* - **Never a key.** {@link assertNoSecret} walks the stanza before it is
|
|
19
|
+
* written and refuses on anything key-shaped. The settlement signing key is
|
|
20
|
+
* read from the environment at the moment a Settlement is signed. It does not
|
|
21
|
+
* belong in a JSON file that a desktop client rewrites, syncs, and quotes in
|
|
22
|
+
* its own logs.
|
|
23
|
+
*
|
|
24
|
+
* ## Only `mcpServers.tab` is touched
|
|
25
|
+
*
|
|
26
|
+
* The merge is by key, and every other server in the document is carried through
|
|
27
|
+
* untouched, as is every top-level key the client keeps beside `mcpServers`. A
|
|
28
|
+
* file that fails to parse is not overwritten: it is reported, because a
|
|
29
|
+
* half-written config file is a client that will not start and the caller is the
|
|
30
|
+
* only one who knows what was in it.
|
|
31
|
+
*
|
|
32
|
+
* Requirements: 25.5, 25.6
|
|
33
|
+
*/
|
|
34
|
+
import type { Result } from "../_shared/index.js";
|
|
35
|
+
import { type JsonRecord } from "../mcp/json.js";
|
|
36
|
+
/** One MCP client this command knows how to find. */
|
|
37
|
+
export interface McpClientTarget {
|
|
38
|
+
/** The `--client` value, for example `claude-desktop`. */
|
|
39
|
+
readonly id: string;
|
|
40
|
+
/** What to call it in a message a person reads. */
|
|
41
|
+
readonly label: string;
|
|
42
|
+
/** Absolute path to its server list on this platform, or undefined where it has none. */
|
|
43
|
+
readonly configPath: string | undefined;
|
|
44
|
+
/** True when the file exists. A known client with no file yet is still a valid target. */
|
|
45
|
+
readonly present: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* True when the directory the file would live in exists, which is the signal
|
|
48
|
+
* that the client is installed even though it has never been configured.
|
|
49
|
+
*/
|
|
50
|
+
readonly installed: boolean;
|
|
51
|
+
}
|
|
52
|
+
/** Every client `connect` can target, in the order detection prefers them. */
|
|
53
|
+
export declare const MCP_CLIENT_IDS: readonly ["claude-code", "claude-desktop", "cursor", "windsurf", "vscode"];
|
|
54
|
+
export interface DetectOptions {
|
|
55
|
+
readonly os?: string;
|
|
56
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
57
|
+
readonly cwd?: string;
|
|
58
|
+
}
|
|
59
|
+
/** Every known client on this platform, whether or not it is installed. */
|
|
60
|
+
export declare function detectMcpClients(options?: DetectOptions): readonly McpClientTarget[];
|
|
61
|
+
/**
|
|
62
|
+
* The client to write to when the caller named none.
|
|
63
|
+
*
|
|
64
|
+
* A client with an existing config file wins over one that is merely installed,
|
|
65
|
+
* because an existing file is evidence somebody uses it. Detection never picks
|
|
66
|
+
* between two files silently: with more than one candidate present, the caller
|
|
67
|
+
* is asked to name one.
|
|
68
|
+
*/
|
|
69
|
+
export declare function chooseMcpClient(targets: readonly McpClientTarget[]): Result<McpClientTarget>;
|
|
70
|
+
/** The `env` block the stanza carries. Non-secret values only, by construction. */
|
|
71
|
+
export type StanzaEnv = Readonly<Record<string, string>>;
|
|
72
|
+
export interface TabStanza {
|
|
73
|
+
readonly command: string;
|
|
74
|
+
readonly args: readonly string[];
|
|
75
|
+
readonly env?: StanzaEnv;
|
|
76
|
+
}
|
|
77
|
+
/** The key the stanza is written under. One server, one name, always this one. */
|
|
78
|
+
export declare const TAB_SERVER_KEY = "tab";
|
|
79
|
+
/**
|
|
80
|
+
* Builds the stanza.
|
|
81
|
+
*
|
|
82
|
+
* `npx -y @tabai/sdk mcp` rather than a path: the client launches it, `npx`
|
|
83
|
+
* resolves the published package, and nothing has to be installed first. That is
|
|
84
|
+
* the one line the adoption claim rests on.
|
|
85
|
+
*
|
|
86
|
+
* The `env` block carries only what the server reads and a person could publish:
|
|
87
|
+
* an RPC endpoint, a chain id and a read API URL. The names are the ones in the
|
|
88
|
+
* tracked environment contract, `MONAD_RPC_URL`, `MONAD_CHAIN_ID` and
|
|
89
|
+
* `NEXT_PUBLIC_REGISTRY_API_URL`, so a stanza and a `.env` agree.
|
|
90
|
+
*/
|
|
91
|
+
export declare function buildTabStanza(env?: StanzaEnv): TabStanza;
|
|
92
|
+
/**
|
|
93
|
+
* Refuses a stanza that carries a secret.
|
|
94
|
+
*
|
|
95
|
+
* Both a name check and a shape check, because either alone is defeated by the
|
|
96
|
+
* obvious mistake: a key under an innocuous name has the shape, and an empty
|
|
97
|
+
* placeholder under `PRIVATE_KEY` has the name. This runs on the merged document
|
|
98
|
+
* before it is written, so it also catches a secret that was already in the file
|
|
99
|
+
* under the `tab` key from an earlier hand edit.
|
|
100
|
+
*/
|
|
101
|
+
export declare function assertNoSecret(stanza: unknown, where?: string): Result<void>;
|
|
102
|
+
export interface MergeOutcome {
|
|
103
|
+
/** The document as it should be on disk. */
|
|
104
|
+
readonly merged: JsonRecord;
|
|
105
|
+
/** The document that was on disk, or an empty object when the file did not exist. */
|
|
106
|
+
readonly previous: JsonRecord;
|
|
107
|
+
/** False when the file already said exactly this, which is the idempotent case. */
|
|
108
|
+
readonly changed: boolean;
|
|
109
|
+
/** Whether a `tab` stanza was already there, so a message can say added or updated. */
|
|
110
|
+
readonly hadStanza: boolean;
|
|
111
|
+
}
|
|
112
|
+
/** Two-space JSON with a trailing newline, which is what every one of these files uses. */
|
|
113
|
+
export declare const renderConfig: (document: unknown) => string;
|
|
114
|
+
/**
|
|
115
|
+
* Merges the stanza into a document, leaving every other key alone.
|
|
116
|
+
*
|
|
117
|
+
* `changed` compares rendered text rather than object identity, so a run that
|
|
118
|
+
* produces the same file is reported as no change even when the objects differ
|
|
119
|
+
* by key order.
|
|
120
|
+
*/
|
|
121
|
+
export declare function mergeTabStanza(previous: JsonRecord, stanza: TabStanza): Result<MergeOutcome>;
|
|
122
|
+
export interface ReadConfigOutcome {
|
|
123
|
+
readonly document: JsonRecord;
|
|
124
|
+
readonly existed: boolean;
|
|
125
|
+
readonly raw: string;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Reads a client config.
|
|
129
|
+
*
|
|
130
|
+
* A file that does not exist is an empty document and not an error: connecting a
|
|
131
|
+
* client that has never been configured is the common case. A file that exists
|
|
132
|
+
* and does not parse *is* an error, and nothing is written over it.
|
|
133
|
+
*/
|
|
134
|
+
export declare function readClientConfig(path: string): Result<ReadConfigOutcome>;
|
|
135
|
+
/** Where a backup of `path` goes, named with the instant so two runs never collide. */
|
|
136
|
+
export declare const backupPathFor: (path: string, at: Date) => string;
|
|
137
|
+
export interface WriteOutcome {
|
|
138
|
+
readonly path: string;
|
|
139
|
+
/** Absent when nothing was written, which is the idempotent case. */
|
|
140
|
+
readonly backupPath?: string;
|
|
141
|
+
readonly wrote: boolean;
|
|
142
|
+
}
|
|
143
|
+
/** Writes the merged document, taking a backup first when a file was already there. */
|
|
144
|
+
export declare function writeClientConfig(path: string, outcome: MergeOutcome, existed: boolean, at: Date): Result<WriteOutcome>;
|
|
145
|
+
/**
|
|
146
|
+
* A unified diff of two JSON documents, line by line.
|
|
147
|
+
*
|
|
148
|
+
* Written here rather than pulled in because it is a longest-common-subsequence
|
|
149
|
+
* over at most a few hundred lines and the alternative is a dependency for a
|
|
150
|
+
* command that prints one. `connect` shows this before it claims to have done
|
|
151
|
+
* anything, because a command that edits a file the user did not open owes them
|
|
152
|
+
* a look at what it changed.
|
|
153
|
+
*/
|
|
154
|
+
export declare function unifiedDiff(before: string, after: string, context?: number): string;
|
|
155
|
+
//# sourceMappingURL=client-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client-config.d.ts","sourceRoot":"","sources":["../../src/cli/client-config.ts"],"names":[],"mappings":"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"}
|