@veoow/synx-sdk 0.1.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/index.d.ts +75 -0
- package/dist/index.js +187 -0
- package/package.json +29 -0
- package/src/client/synx-client.ts +64 -0
- package/src/errors.ts +15 -0
- package/src/execution/pipeline.ts +46 -0
- package/src/execution/run.ts +26 -0
- package/src/execution/trace.ts +10 -0
- package/src/index.ts +4 -0
- package/src/types/decision.ts +5 -0
- package/src/types/pipeline.ts +25 -0
- package/src/types/run.ts +15 -0
- package/src/types/trace.ts +15 -0
- package/src/utils/http-client.ts +51 -0
- package/src/utils/jwt.ts +42 -0
- package/tsconfig.json +44 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
interface PipelineStep {
|
|
2
|
+
contract: string;
|
|
3
|
+
function: string;
|
|
4
|
+
payload: Record<string, unknown>;
|
|
5
|
+
}
|
|
6
|
+
interface PipelineStepResult {
|
|
7
|
+
contract: string;
|
|
8
|
+
function: string;
|
|
9
|
+
executionHash: string;
|
|
10
|
+
success: boolean;
|
|
11
|
+
contextId: string;
|
|
12
|
+
events: PipelineEvent[];
|
|
13
|
+
}
|
|
14
|
+
interface PipelineEvent {
|
|
15
|
+
Hash: string;
|
|
16
|
+
Payload: {
|
|
17
|
+
data: Record<string, unknown>;
|
|
18
|
+
};
|
|
19
|
+
Type: string;
|
|
20
|
+
}
|
|
21
|
+
interface PipelineOutput {
|
|
22
|
+
contextId: string;
|
|
23
|
+
steps: PipelineStepResult[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface RunInput {
|
|
27
|
+
contract: string;
|
|
28
|
+
function: string;
|
|
29
|
+
payload: Record<string, unknown>;
|
|
30
|
+
contextId?: string;
|
|
31
|
+
}
|
|
32
|
+
interface RunOutput {
|
|
33
|
+
executionHash: string;
|
|
34
|
+
function: string;
|
|
35
|
+
success: boolean;
|
|
36
|
+
contextId: string;
|
|
37
|
+
events: Record<string, any>;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
interface TraceStep {
|
|
41
|
+
function: string;
|
|
42
|
+
executionHash: string;
|
|
43
|
+
parentHash: string | null;
|
|
44
|
+
contract: string;
|
|
45
|
+
executedAt: number;
|
|
46
|
+
success: boolean;
|
|
47
|
+
data: Record<string, unknown>;
|
|
48
|
+
}
|
|
49
|
+
interface TraceOutput {
|
|
50
|
+
contextId: string;
|
|
51
|
+
status: "COMPLETED" | "PARTIAL" | "FAILED";
|
|
52
|
+
steps: TraceStep[];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
declare class Synx {
|
|
56
|
+
private readonly endpoint;
|
|
57
|
+
private readonly privKey;
|
|
58
|
+
private client;
|
|
59
|
+
private ready;
|
|
60
|
+
constructor(endpoint: string, privKey: string);
|
|
61
|
+
run(input: any): Promise<RunOutput>;
|
|
62
|
+
pipeline(steps: PipelineStep[], contextId?: string): Promise<PipelineOutput>;
|
|
63
|
+
trace(contextId: string): Promise<TraceOutput>;
|
|
64
|
+
private withRetry;
|
|
65
|
+
private authenticate;
|
|
66
|
+
private ensureReady;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
interface Decision {
|
|
70
|
+
decisionId: string;
|
|
71
|
+
blockIndex: number;
|
|
72
|
+
hash: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { type Decision, type RunInput, type RunOutput, Synx };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// src/errors.ts
|
|
2
|
+
var TokenExpiredError = class extends Error {
|
|
3
|
+
constructor() {
|
|
4
|
+
super("Token expired");
|
|
5
|
+
this.name = "TokenExpiredError";
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
var SynxError = class extends Error {
|
|
9
|
+
statusCode;
|
|
10
|
+
constructor(message, statusCode) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "SynxError";
|
|
13
|
+
this.statusCode = statusCode;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// src/execution/pipeline.ts
|
|
18
|
+
async function pipeline(client, steps, contextId) {
|
|
19
|
+
if (steps.length === 0) {
|
|
20
|
+
throw new Error("Pipeline must have at least one step");
|
|
21
|
+
}
|
|
22
|
+
const resolvedContextId = contextId ?? crypto.randomUUID();
|
|
23
|
+
const results = [];
|
|
24
|
+
for (const step of steps) {
|
|
25
|
+
const payload = {
|
|
26
|
+
function: step.function,
|
|
27
|
+
args: step.payload,
|
|
28
|
+
context_id: resolvedContextId
|
|
29
|
+
};
|
|
30
|
+
const res = await client.post(`/contracts/${step.contract}/execute`, payload);
|
|
31
|
+
results.push({
|
|
32
|
+
contract: step.contract,
|
|
33
|
+
function: step.function,
|
|
34
|
+
executionHash: res.executionHash,
|
|
35
|
+
success: res.success,
|
|
36
|
+
contextId: res.contextId,
|
|
37
|
+
events: res.events ?? []
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return { contextId: resolvedContextId, steps: results };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/utils/http-client.ts
|
|
44
|
+
var HttpClient = class {
|
|
45
|
+
constructor(endpoint, token) {
|
|
46
|
+
this.endpoint = endpoint;
|
|
47
|
+
this.token = token;
|
|
48
|
+
}
|
|
49
|
+
// Permite atualizar o token sem recriar o client
|
|
50
|
+
setToken(token) {
|
|
51
|
+
this.token = token;
|
|
52
|
+
}
|
|
53
|
+
async post(path, body) {
|
|
54
|
+
const res = await fetch(`${this.endpoint}${path}`, {
|
|
55
|
+
method: "POST",
|
|
56
|
+
headers: {
|
|
57
|
+
"Content-Type": "application/json",
|
|
58
|
+
Authorization: `Bearer ${this.token}`
|
|
59
|
+
},
|
|
60
|
+
body: JSON.stringify(body)
|
|
61
|
+
});
|
|
62
|
+
if (res.status === 401) throw new TokenExpiredError();
|
|
63
|
+
if (!res.ok)
|
|
64
|
+
throw new SynxError(
|
|
65
|
+
`HTTP POST ${path} failed: ${res.statusText}`,
|
|
66
|
+
res.status
|
|
67
|
+
);
|
|
68
|
+
return res.json();
|
|
69
|
+
}
|
|
70
|
+
async get(path) {
|
|
71
|
+
const res = await fetch(`${this.endpoint}${path}`, {
|
|
72
|
+
method: "GET",
|
|
73
|
+
headers: {
|
|
74
|
+
Authorization: `Bearer ${this.token}`
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
if (res.status === 401) throw new TokenExpiredError();
|
|
78
|
+
if (!res.ok)
|
|
79
|
+
throw new SynxError(
|
|
80
|
+
`HTTP GET ${path} failed: ${res.statusText}`,
|
|
81
|
+
res.status
|
|
82
|
+
);
|
|
83
|
+
return res.json();
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// src/execution/run.ts
|
|
88
|
+
async function run(client, input) {
|
|
89
|
+
const dto = validateCommit(input);
|
|
90
|
+
const body = {
|
|
91
|
+
function: dto.function,
|
|
92
|
+
args: dto.payload,
|
|
93
|
+
...dto.contextId ? { context_id: dto.contextId } : {}
|
|
94
|
+
};
|
|
95
|
+
return client.post(`/contracts/${dto.contract}/execute`, body);
|
|
96
|
+
}
|
|
97
|
+
function validateCommit(input) {
|
|
98
|
+
if (!input.contract) throw new Error("Commit Input: 'contract' is required");
|
|
99
|
+
if (!input.function) throw new Error("Commit Input: 'function' is required");
|
|
100
|
+
if (input.payload === void 0)
|
|
101
|
+
throw new Error("Commit Input: 'payload' is required");
|
|
102
|
+
return input;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/execution/trace.ts
|
|
106
|
+
async function trace(client, contextId) {
|
|
107
|
+
if (!contextId) throw new Error("Trace: 'contextId' is required");
|
|
108
|
+
return client.get(`/trace/${contextId}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/utils/jwt.ts
|
|
112
|
+
import { Buffer } from "buffer";
|
|
113
|
+
import { createPrivateKey, sign } from "crypto";
|
|
114
|
+
function base64url(data) {
|
|
115
|
+
if (typeof data === "string") {
|
|
116
|
+
return Buffer.from(data).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
117
|
+
}
|
|
118
|
+
return Buffer.from(data).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
119
|
+
}
|
|
120
|
+
function generateJWT(privKeyHex) {
|
|
121
|
+
const privKeyBytes = Buffer.from(privKeyHex, "hex");
|
|
122
|
+
const privKey = createPrivateKey({
|
|
123
|
+
key: privKeyBytes,
|
|
124
|
+
format: "der",
|
|
125
|
+
type: "pkcs8"
|
|
126
|
+
});
|
|
127
|
+
const header = base64url(JSON.stringify({ alg: "EdDSA", typ: "JWT" }));
|
|
128
|
+
const payload = base64url(
|
|
129
|
+
JSON.stringify({
|
|
130
|
+
iat: Math.floor(Date.now() / 1e3),
|
|
131
|
+
exp: Math.floor(Date.now() / 1e3) + 3600,
|
|
132
|
+
iss: "synx-sdk",
|
|
133
|
+
aud: "eeapi"
|
|
134
|
+
})
|
|
135
|
+
);
|
|
136
|
+
const data = `${header}.${payload}`;
|
|
137
|
+
const signature = sign(null, Buffer.from(data), privKey);
|
|
138
|
+
return `${data}.${base64url(signature)}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/client/synx-client.ts
|
|
142
|
+
var Synx = class {
|
|
143
|
+
endpoint;
|
|
144
|
+
privKey;
|
|
145
|
+
client;
|
|
146
|
+
ready;
|
|
147
|
+
constructor(endpoint, privKey) {
|
|
148
|
+
this.endpoint = endpoint;
|
|
149
|
+
this.privKey = privKey;
|
|
150
|
+
this.ready = this.authenticate();
|
|
151
|
+
}
|
|
152
|
+
async run(input) {
|
|
153
|
+
return this.withRetry(() => run(this.client, input));
|
|
154
|
+
}
|
|
155
|
+
async pipeline(steps, contextId) {
|
|
156
|
+
return this.withRetry(() => pipeline(this.client, steps, contextId));
|
|
157
|
+
}
|
|
158
|
+
async trace(contextId) {
|
|
159
|
+
return this.withRetry(() => trace(this.client, contextId));
|
|
160
|
+
}
|
|
161
|
+
async withRetry(fn) {
|
|
162
|
+
await this.ensureReady();
|
|
163
|
+
try {
|
|
164
|
+
return await fn();
|
|
165
|
+
} catch (e) {
|
|
166
|
+
if (e instanceof TokenExpiredError) {
|
|
167
|
+
await this.authenticate();
|
|
168
|
+
return fn();
|
|
169
|
+
}
|
|
170
|
+
throw e;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async authenticate() {
|
|
174
|
+
const token = await generateJWT(this.privKey);
|
|
175
|
+
if (this.client) {
|
|
176
|
+
this.client.setToken(token);
|
|
177
|
+
} else {
|
|
178
|
+
this.client = new HttpClient(this.endpoint, token);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async ensureReady() {
|
|
182
|
+
await this.ready;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
export {
|
|
186
|
+
Synx
|
|
187
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@veoow/synx-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"license": "Apache-2.0",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
17
|
+
"dev": "tsup src/index.ts --format esm --dts --watch"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^25.3.5",
|
|
21
|
+
"ts-node": "^10.9.2",
|
|
22
|
+
"tsup": "^8.5.1",
|
|
23
|
+
"typescript": "^5.9.3"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"registry": "https://registry.npmjs.org",
|
|
27
|
+
"access": "public"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { TokenExpiredError } from "../errors.js";
|
|
2
|
+
import { pipeline } from "../execution/pipeline.js";
|
|
3
|
+
import { run } from "../execution/run.js";
|
|
4
|
+
import { trace } from "../execution/trace.js";
|
|
5
|
+
import type { PipelineOutput, PipelineStep } from "../types/pipeline.js";
|
|
6
|
+
import type { RunOutput } from "../types/run.js";
|
|
7
|
+
import type { TraceOutput } from "../types/trace.js";
|
|
8
|
+
import { HttpClient } from "../utils/http-client.js";
|
|
9
|
+
import { generateJWT } from "../utils/jwt.js";
|
|
10
|
+
|
|
11
|
+
export class Synx {
|
|
12
|
+
private readonly endpoint: string;
|
|
13
|
+
private readonly privKey: string;
|
|
14
|
+
private client!: HttpClient;
|
|
15
|
+
private ready: Promise<void>;
|
|
16
|
+
|
|
17
|
+
constructor(endpoint: string, privKey: string) {
|
|
18
|
+
this.endpoint = endpoint;
|
|
19
|
+
this.privKey = privKey;
|
|
20
|
+
this.ready = this.authenticate();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async run(input: any): Promise<RunOutput> {
|
|
24
|
+
return this.withRetry(() => run(this.client, input));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async pipeline(
|
|
28
|
+
steps: PipelineStep[],
|
|
29
|
+
contextId?: string,
|
|
30
|
+
): Promise<PipelineOutput> {
|
|
31
|
+
return this.withRetry(() => pipeline(this.client, steps, contextId));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async trace(contextId: string): Promise<TraceOutput> {
|
|
35
|
+
return this.withRetry(() => trace(this.client, contextId));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private async withRetry<T>(fn: () => Promise<T>): Promise<T> {
|
|
39
|
+
await this.ensureReady();
|
|
40
|
+
try {
|
|
41
|
+
return await fn();
|
|
42
|
+
} catch (e) {
|
|
43
|
+
if (e instanceof TokenExpiredError) {
|
|
44
|
+
await this.authenticate();
|
|
45
|
+
return fn();
|
|
46
|
+
}
|
|
47
|
+
throw e;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private async authenticate(): Promise<void> {
|
|
52
|
+
const token = await generateJWT(this.privKey);
|
|
53
|
+
|
|
54
|
+
if (this.client) {
|
|
55
|
+
this.client.setToken(token);
|
|
56
|
+
} else {
|
|
57
|
+
this.client = new HttpClient(this.endpoint, token);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private async ensureReady(): Promise<void> {
|
|
62
|
+
await this.ready;
|
|
63
|
+
}
|
|
64
|
+
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export class TokenExpiredError extends Error {
|
|
2
|
+
constructor() {
|
|
3
|
+
super("Token expired");
|
|
4
|
+
this.name = "TokenExpiredError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export class SynxError extends Error {
|
|
9
|
+
statusCode: number;
|
|
10
|
+
constructor(message: string, statusCode: number) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.name = "SynxError";
|
|
13
|
+
this.statusCode = statusCode;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
PipelineEvent,
|
|
3
|
+
PipelineOutput,
|
|
4
|
+
PipelineStep,
|
|
5
|
+
PipelineStepResult,
|
|
6
|
+
} from "../types/pipeline.js";
|
|
7
|
+
import type { HttpClient } from "../utils/http-client.js";
|
|
8
|
+
|
|
9
|
+
export async function pipeline(
|
|
10
|
+
client: HttpClient,
|
|
11
|
+
steps: PipelineStep[],
|
|
12
|
+
contextId?: string,
|
|
13
|
+
): Promise<PipelineOutput> {
|
|
14
|
+
if (steps.length === 0) {
|
|
15
|
+
throw new Error("Pipeline must have at least one step");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const resolvedContextId = contextId ?? crypto.randomUUID();
|
|
19
|
+
const results: PipelineStepResult[] = [];
|
|
20
|
+
|
|
21
|
+
for (const step of steps) {
|
|
22
|
+
const payload = {
|
|
23
|
+
function: step.function,
|
|
24
|
+
args: step.payload,
|
|
25
|
+
context_id: resolvedContextId,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const res = await client.post<{
|
|
29
|
+
executionHash: string;
|
|
30
|
+
success: boolean;
|
|
31
|
+
executedAt: string;
|
|
32
|
+
contextId: string;
|
|
33
|
+
events?: PipelineEvent[];
|
|
34
|
+
}>(`/contracts/${step.contract}/execute`, payload);
|
|
35
|
+
results.push({
|
|
36
|
+
contract: step.contract,
|
|
37
|
+
function: step.function,
|
|
38
|
+
executionHash: res.executionHash,
|
|
39
|
+
success: res.success,
|
|
40
|
+
contextId: res.contextId,
|
|
41
|
+
events: res.events ?? [],
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { contextId: resolvedContextId, steps: results };
|
|
46
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { RunInput, RunOutput } from "../types/run.js";
|
|
2
|
+
import { HttpClient } from "../utils/http-client.js";
|
|
3
|
+
|
|
4
|
+
export async function run(
|
|
5
|
+
client: HttpClient,
|
|
6
|
+
input: RunInput,
|
|
7
|
+
): Promise<RunOutput> {
|
|
8
|
+
const dto = validateCommit(input);
|
|
9
|
+
|
|
10
|
+
const body = {
|
|
11
|
+
function: dto.function,
|
|
12
|
+
args: dto.payload,
|
|
13
|
+
...(dto.contextId ? { context_id: dto.contextId } : {}),
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return client.post(`/contracts/${dto.contract}/execute`, body);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function validateCommit(input: Partial<RunInput>): RunInput {
|
|
20
|
+
if (!input.contract) throw new Error("Commit Input: 'contract' is required");
|
|
21
|
+
if (!input.function) throw new Error("Commit Input: 'function' is required");
|
|
22
|
+
if (input.payload === undefined)
|
|
23
|
+
throw new Error("Commit Input: 'payload' is required");
|
|
24
|
+
|
|
25
|
+
return input as RunInput;
|
|
26
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TraceOutput } from "../types/trace.js";
|
|
2
|
+
import { HttpClient } from "../utils/http-client.js";
|
|
3
|
+
|
|
4
|
+
export async function trace(
|
|
5
|
+
client: HttpClient,
|
|
6
|
+
contextId: string,
|
|
7
|
+
): Promise<TraceOutput> {
|
|
8
|
+
if (!contextId) throw new Error("Trace: 'contextId' is required");
|
|
9
|
+
return client.get(`/trace/${contextId}`);
|
|
10
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface PipelineStep {
|
|
2
|
+
contract: string;
|
|
3
|
+
function: string;
|
|
4
|
+
payload: Record<string, unknown>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface PipelineStepResult {
|
|
8
|
+
contract: string;
|
|
9
|
+
function: string;
|
|
10
|
+
executionHash: string;
|
|
11
|
+
success: boolean;
|
|
12
|
+
contextId: string;
|
|
13
|
+
events: PipelineEvent[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface PipelineEvent {
|
|
17
|
+
Hash: string;
|
|
18
|
+
Payload: { data: Record<string, unknown> };
|
|
19
|
+
Type: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface PipelineOutput {
|
|
23
|
+
contextId: string;
|
|
24
|
+
steps: PipelineStepResult[];
|
|
25
|
+
}
|
package/src/types/run.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/types/run.ts
|
|
2
|
+
export interface RunInput {
|
|
3
|
+
contract: string;
|
|
4
|
+
function: string;
|
|
5
|
+
payload: Record<string, unknown>;
|
|
6
|
+
contextId?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface RunOutput {
|
|
10
|
+
executionHash: string;
|
|
11
|
+
function: string;
|
|
12
|
+
success: boolean;
|
|
13
|
+
contextId: string;
|
|
14
|
+
events: Record<string, any>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface TraceStep {
|
|
2
|
+
function: string;
|
|
3
|
+
executionHash: string;
|
|
4
|
+
parentHash: string | null;
|
|
5
|
+
contract: string;
|
|
6
|
+
executedAt: number;
|
|
7
|
+
success: boolean;
|
|
8
|
+
data: Record<string, unknown>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface TraceOutput {
|
|
12
|
+
contextId: string;
|
|
13
|
+
status: "COMPLETED" | "PARTIAL" | "FAILED";
|
|
14
|
+
steps: TraceStep[];
|
|
15
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { TokenExpiredError, SynxError } from "../errors.js";
|
|
2
|
+
|
|
3
|
+
export class HttpClient {
|
|
4
|
+
constructor(
|
|
5
|
+
private readonly endpoint: string,
|
|
6
|
+
private token: string,
|
|
7
|
+
) {}
|
|
8
|
+
|
|
9
|
+
// Permite atualizar o token sem recriar o client
|
|
10
|
+
setToken(token: string) {
|
|
11
|
+
this.token = token;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async post<T>(path: string, body: unknown): Promise<T> {
|
|
15
|
+
const res = await fetch(`${this.endpoint}${path}`, {
|
|
16
|
+
method: "POST",
|
|
17
|
+
headers: {
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
Authorization: `Bearer ${this.token}`,
|
|
20
|
+
},
|
|
21
|
+
body: JSON.stringify(body),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (res.status === 401) throw new TokenExpiredError();
|
|
25
|
+
if (!res.ok)
|
|
26
|
+
throw new SynxError(
|
|
27
|
+
`HTTP POST ${path} failed: ${res.statusText}`,
|
|
28
|
+
res.status,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return res.json();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async get<T>(path: string): Promise<T> {
|
|
35
|
+
const res = await fetch(`${this.endpoint}${path}`, {
|
|
36
|
+
method: "GET",
|
|
37
|
+
headers: {
|
|
38
|
+
Authorization: `Bearer ${this.token}`,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (res.status === 401) throw new TokenExpiredError();
|
|
43
|
+
if (!res.ok)
|
|
44
|
+
throw new SynxError(
|
|
45
|
+
`HTTP GET ${path} failed: ${res.statusText}`,
|
|
46
|
+
res.status,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return res.json();
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/utils/jwt.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Buffer } from "node:buffer";
|
|
2
|
+
import { createPrivateKey, sign } from "node:crypto";
|
|
3
|
+
|
|
4
|
+
function base64url(data: Buffer | Uint8Array | string): string {
|
|
5
|
+
if (typeof data === "string") {
|
|
6
|
+
return Buffer.from(data)
|
|
7
|
+
.toString("base64")
|
|
8
|
+
.replace(/\+/g, "-")
|
|
9
|
+
.replace(/\//g, "_")
|
|
10
|
+
.replace(/=/g, "");
|
|
11
|
+
}
|
|
12
|
+
return Buffer.from(data)
|
|
13
|
+
.toString("base64")
|
|
14
|
+
.replace(/\+/g, "-")
|
|
15
|
+
.replace(/\//g, "_")
|
|
16
|
+
.replace(/=/g, "");
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function generateJWT(privKeyHex: string): string {
|
|
20
|
+
const privKeyBytes = Buffer.from(privKeyHex, "hex");
|
|
21
|
+
|
|
22
|
+
const privKey = createPrivateKey({
|
|
23
|
+
key: privKeyBytes,
|
|
24
|
+
format: "der",
|
|
25
|
+
type: "pkcs8",
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const header = base64url(JSON.stringify({ alg: "EdDSA", typ: "JWT" }));
|
|
29
|
+
const payload = base64url(
|
|
30
|
+
JSON.stringify({
|
|
31
|
+
iat: Math.floor(Date.now() / 1000),
|
|
32
|
+
exp: Math.floor(Date.now() / 1000) + 3600,
|
|
33
|
+
iss: "synx-sdk",
|
|
34
|
+
aud: "eeapi",
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const data = `${header}.${payload}`;
|
|
39
|
+
const signature = sign(null, Buffer.from(data), privKey);
|
|
40
|
+
|
|
41
|
+
return `${data}.${base64url(signature)}`;
|
|
42
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Visit https://aka.ms/tsconfig to read more about this file
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
// File Layout
|
|
5
|
+
// "rootDir": "./src",
|
|
6
|
+
// "outDir": "./dist",
|
|
7
|
+
|
|
8
|
+
// Environment Settings
|
|
9
|
+
// See also https://aka.ms/tsconfig/module
|
|
10
|
+
"module": "nodenext",
|
|
11
|
+
"target": "esnext",
|
|
12
|
+
// "types": [],
|
|
13
|
+
// For nodejs:
|
|
14
|
+
// "lib": ["esnext"],
|
|
15
|
+
"types": ["node"],
|
|
16
|
+
// and npm install -D @types/node
|
|
17
|
+
|
|
18
|
+
// Other Outputs
|
|
19
|
+
"sourceMap": true,
|
|
20
|
+
"declaration": true,
|
|
21
|
+
"declarationMap": true,
|
|
22
|
+
|
|
23
|
+
// Stricter Typechecking Options
|
|
24
|
+
"noUncheckedIndexedAccess": true,
|
|
25
|
+
"exactOptionalPropertyTypes": true,
|
|
26
|
+
|
|
27
|
+
// Style Options
|
|
28
|
+
// "noImplicitReturns": true,
|
|
29
|
+
// "noImplicitOverride": true,
|
|
30
|
+
// "noUnusedLocals": true,
|
|
31
|
+
// "noUnusedParameters": true,
|
|
32
|
+
// "noFallthroughCasesInSwitch": true,
|
|
33
|
+
// "noPropertyAccessFromIndexSignature": true,
|
|
34
|
+
|
|
35
|
+
// Recommended Options
|
|
36
|
+
"strict": true,
|
|
37
|
+
"jsx": "react-jsx",
|
|
38
|
+
"verbatimModuleSyntax": true,
|
|
39
|
+
"isolatedModules": true,
|
|
40
|
+
"noUncheckedSideEffectImports": true,
|
|
41
|
+
"moduleDetection": "force",
|
|
42
|
+
"skipLibCheck": true
|
|
43
|
+
}
|
|
44
|
+
}
|