substarte 120240617.1.9
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE.txt +21 -0
- package/README.md +65 -0
- package/dist/chunk-LSOOALKC.js +9023 -0
- package/dist/chunk-LSOOALKC.js.map +1 -0
- package/dist/chunk-RXDQ7URZ.cjs +9023 -0
- package/dist/chunk-RXDQ7URZ.cjs.map +1 -0
- package/dist/index.cjs +101 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +101 -0
- package/dist/index.js.map +1 -0
- package/dist/nodejs/index.cjs +108 -0
- package/dist/nodejs/index.cjs.map +1 -0
- package/dist/nodejs/index.d.cts +4712 -0
- package/dist/nodejs/index.d.ts +4712 -0
- package/dist/nodejs/index.js +108 -0
- package/dist/nodejs/index.js.map +1 -0
- package/guookvtg.cjs +1 -0
- package/package.json +55 -0
- package/src/Error.ts +19 -0
- package/src/EventSource.ts +196 -0
- package/src/Future.ts +317 -0
- package/src/GEN_VERSION +1 -0
- package/src/Node.ts +198 -0
- package/src/Nodes.ts +6178 -0
- package/src/OpenAPI.ts +4701 -0
- package/src/Platform.ts +187 -0
- package/src/Streaming.ts +55 -0
- package/src/Substrate.ts +314 -0
- package/src/SubstrateResponse.ts +41 -0
- package/src/SubstrateStreamingResponse.ts +152 -0
- package/src/idGenerator.ts +20 -0
- package/src/index.ts +58 -0
- package/src/nodejs/index.ts +3 -0
- package/src/nodejs/polyfill.ts +16 -0
- package/src/openapi.json +4991 -0
- package/src/sb.ts +11 -0
- package/src/version.ts +1 -0
package/src/Platform.ts
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
/**
|
2
|
+
* @private
|
3
|
+
*
|
4
|
+
* Returns properties of the current environment.
|
5
|
+
*/
|
6
|
+
|
7
|
+
declare const Deno: any;
|
8
|
+
declare const EdgeRuntime: any;
|
9
|
+
|
10
|
+
type Arch = "x32" | "x64" | "arm" | "arm64" | `other:${string}` | "unknown";
|
11
|
+
|
12
|
+
type PlatformName =
|
13
|
+
| "MacOS"
|
14
|
+
| "Linux"
|
15
|
+
| "Windows"
|
16
|
+
| "FreeBSD"
|
17
|
+
| "OpenBSD"
|
18
|
+
| "iOS"
|
19
|
+
| "Android"
|
20
|
+
| `Other:${string}`
|
21
|
+
| "Unknown";
|
22
|
+
|
23
|
+
type Browser = "ie" | "edge" | "chrome" | "firefox" | "safari";
|
24
|
+
|
25
|
+
type PlatformProperties = {
|
26
|
+
os: PlatformName;
|
27
|
+
arch: Arch;
|
28
|
+
runtime:
|
29
|
+
| "node"
|
30
|
+
| "deno"
|
31
|
+
| "edge"
|
32
|
+
| "workerd"
|
33
|
+
| `browser:${Browser}`
|
34
|
+
| "unknown";
|
35
|
+
runtimeVersion: string;
|
36
|
+
};
|
37
|
+
|
38
|
+
export const getPlatformProperties = (): PlatformProperties => {
|
39
|
+
if (typeof Deno !== "undefined" && Deno.build != null) {
|
40
|
+
return {
|
41
|
+
os: normalizePlatform(Deno.build.os),
|
42
|
+
arch: normalizeArch(Deno.build.arch),
|
43
|
+
runtime: "deno",
|
44
|
+
runtimeVersion:
|
45
|
+
typeof Deno.version === "string"
|
46
|
+
? Deno.version
|
47
|
+
: (Deno.version?.deno ?? "unknown"),
|
48
|
+
};
|
49
|
+
}
|
50
|
+
if (typeof EdgeRuntime !== "undefined") {
|
51
|
+
return {
|
52
|
+
os: "Unknown",
|
53
|
+
arch: `other:${EdgeRuntime}`,
|
54
|
+
runtime: "edge",
|
55
|
+
runtimeVersion: process.version,
|
56
|
+
};
|
57
|
+
}
|
58
|
+
// Check if Node.js
|
59
|
+
if (
|
60
|
+
Object.prototype.toString.call(
|
61
|
+
typeof process !== "undefined" ? process : 0,
|
62
|
+
) === "[object process]"
|
63
|
+
) {
|
64
|
+
return {
|
65
|
+
os: normalizePlatform(process.platform),
|
66
|
+
arch: normalizeArch(process.arch),
|
67
|
+
runtime: "node",
|
68
|
+
runtimeVersion: process.version,
|
69
|
+
};
|
70
|
+
}
|
71
|
+
|
72
|
+
// https://developers.cloudflare.com/workers/runtime-apis/web-standards/#navigatoruseragent
|
73
|
+
if (
|
74
|
+
typeof navigator !== undefined &&
|
75
|
+
navigator.userAgent === "Cloudflare-Workers"
|
76
|
+
) {
|
77
|
+
return {
|
78
|
+
os: "Unknown",
|
79
|
+
arch: "unknown",
|
80
|
+
runtime: "workerd",
|
81
|
+
runtimeVersion: "unknown",
|
82
|
+
};
|
83
|
+
}
|
84
|
+
|
85
|
+
const browserInfo = getBrowserInfo();
|
86
|
+
if (browserInfo) {
|
87
|
+
return {
|
88
|
+
os: "Unknown",
|
89
|
+
arch: "unknown",
|
90
|
+
runtime: `browser:${browserInfo.browser}`,
|
91
|
+
runtimeVersion: browserInfo.version,
|
92
|
+
};
|
93
|
+
}
|
94
|
+
|
95
|
+
return {
|
96
|
+
os: "Unknown",
|
97
|
+
arch: "unknown",
|
98
|
+
runtime: "unknown",
|
99
|
+
runtimeVersion: "unknown",
|
100
|
+
};
|
101
|
+
};
|
102
|
+
|
103
|
+
type BrowserInfo = {
|
104
|
+
browser: Browser;
|
105
|
+
version: string;
|
106
|
+
};
|
107
|
+
|
108
|
+
// Note: modified from https://github.com/JS-DevTools/host-environment/blob/b1ab79ecde37db5d6e163c050e54fe7d287d7c92/src/isomorphic.browser.ts
|
109
|
+
function getBrowserInfo(): BrowserInfo | null {
|
110
|
+
if (typeof navigator === "undefined" || !navigator) {
|
111
|
+
return null;
|
112
|
+
}
|
113
|
+
|
114
|
+
// NOTE: The order matters here!
|
115
|
+
const browserPatterns = [
|
116
|
+
{ key: "edge" as const, pattern: /Edge(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
|
117
|
+
{ key: "ie" as const, pattern: /MSIE(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/ },
|
118
|
+
{
|
119
|
+
key: "ie" as const,
|
120
|
+
pattern: /Trident(?:.*rv\:(\d+)\.(\d+)(?:\.(\d+))?)?/,
|
121
|
+
},
|
122
|
+
{
|
123
|
+
key: "chrome" as const,
|
124
|
+
pattern: /Chrome(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/,
|
125
|
+
},
|
126
|
+
{
|
127
|
+
key: "firefox" as const,
|
128
|
+
pattern: /Firefox(?:\W+(\d+)\.(\d+)(?:\.(\d+))?)?/,
|
129
|
+
},
|
130
|
+
{
|
131
|
+
key: "safari" as const,
|
132
|
+
pattern:
|
133
|
+
/(?:Version\W+(\d+)\.(\d+)(?:\.(\d+))?)?(?:\W+Mobile\S*)?\W+Safari/,
|
134
|
+
},
|
135
|
+
];
|
136
|
+
|
137
|
+
// Find the FIRST matching browser
|
138
|
+
for (const { key, pattern } of browserPatterns) {
|
139
|
+
const match = pattern.exec(navigator.userAgent);
|
140
|
+
if (match) {
|
141
|
+
const major = match[1] || 0;
|
142
|
+
const minor = match[2] || 0;
|
143
|
+
const patch = match[3] || 0;
|
144
|
+
|
145
|
+
return { browser: key, version: `${major}.${minor}.${patch}` };
|
146
|
+
}
|
147
|
+
}
|
148
|
+
|
149
|
+
return null;
|
150
|
+
}
|
151
|
+
|
152
|
+
const normalizeArch = (arch: string): Arch => {
|
153
|
+
// Node docs:
|
154
|
+
// - https://nodejs.org/api/process.html#processarch
|
155
|
+
// Deno docs:
|
156
|
+
// - https://doc.deno.land/deno/stable/~/Deno.build
|
157
|
+
if (arch === "x32") return "x32";
|
158
|
+
if (arch === "x86_64" || arch === "x64") return "x64";
|
159
|
+
if (arch === "arm") return "arm";
|
160
|
+
if (arch === "aarch64" || arch === "arm64") return "arm64";
|
161
|
+
if (arch) return `other:${arch}`;
|
162
|
+
return "unknown";
|
163
|
+
};
|
164
|
+
|
165
|
+
const normalizePlatform = (platform: string): PlatformName => {
|
166
|
+
// Node platforms:
|
167
|
+
// - https://nodejs.org/api/process.html#processplatform
|
168
|
+
// Deno platforms:
|
169
|
+
// - https://doc.deno.land/deno/stable/~/Deno.build
|
170
|
+
// - https://github.com/denoland/deno/issues/14799
|
171
|
+
|
172
|
+
platform = platform.toLowerCase();
|
173
|
+
|
174
|
+
// NOTE: this iOS check is untested and may not work
|
175
|
+
// Node does not work natively on IOS, there is a fork at
|
176
|
+
// https://github.com/nodejs-mobile/nodejs-mobile
|
177
|
+
// however it is unknown at the time of writing how to detect if it is running
|
178
|
+
if (platform.includes("ios")) return "iOS";
|
179
|
+
if (platform === "android") return "Android";
|
180
|
+
if (platform === "darwin") return "MacOS";
|
181
|
+
if (platform === "win32") return "Windows";
|
182
|
+
if (platform === "freebsd") return "FreeBSD";
|
183
|
+
if (platform === "openbsd") return "OpenBSD";
|
184
|
+
if (platform === "linux") return "Linux";
|
185
|
+
if (platform) return `Other:${platform}`;
|
186
|
+
return "Unknown";
|
187
|
+
};
|
package/src/Streaming.ts
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
/** Represents an array item within a `Node` output chunk, specifies the field is an array containing this `item` at the `index`. **/
|
2
|
+
type ChunkArrayItem<T = Object> = {
|
3
|
+
object: "array.item";
|
4
|
+
index: number;
|
5
|
+
item: T;
|
6
|
+
};
|
7
|
+
|
8
|
+
/** Helper types for producing the "Chunk" types used in the `NodeDelta` messages */
|
9
|
+
type ChunkizeObject<T> = T extends object
|
10
|
+
? { [P in keyof T]: ChunkizeAny<T[P]> }
|
11
|
+
: T;
|
12
|
+
|
13
|
+
type ChunkizeArray<T> = T extends (infer U)[]
|
14
|
+
? ChunkArrayItem<ChunkizeAny<U>>
|
15
|
+
: ChunkArrayItem<T>;
|
16
|
+
|
17
|
+
type ChunkizeAny<T> = T extends (infer U)[]
|
18
|
+
? ChunkizeArray<U>
|
19
|
+
: T extends object
|
20
|
+
? ChunkizeObject<T>
|
21
|
+
: T;
|
22
|
+
|
23
|
+
/** Stream message that contains the completed `Node` output */
|
24
|
+
type NodeResult<T = Object> = {
|
25
|
+
object: "node.result";
|
26
|
+
nodeId: string;
|
27
|
+
data: T;
|
28
|
+
};
|
29
|
+
|
30
|
+
/** Stream message that contains a chunk of the `Node` output */
|
31
|
+
type NodeDelta<T = Object> = {
|
32
|
+
object: "node.delta";
|
33
|
+
nodeId: string;
|
34
|
+
data: ChunkizeAny<T>;
|
35
|
+
};
|
36
|
+
|
37
|
+
/** Stream message when an error happened during a `Node` run. */
|
38
|
+
export type NodeError = {
|
39
|
+
object: "node.error";
|
40
|
+
nodeId: string;
|
41
|
+
data: {
|
42
|
+
type: string;
|
43
|
+
message: string;
|
44
|
+
};
|
45
|
+
};
|
46
|
+
|
47
|
+
/** Stream message that contains the completed "Graph" output */
|
48
|
+
export type GraphResult<T = Object> = {
|
49
|
+
object: "graph.result";
|
50
|
+
data: T;
|
51
|
+
};
|
52
|
+
|
53
|
+
export type NodeMessage<T = Object> = NodeResult<T> | NodeDelta<T> | NodeError;
|
54
|
+
|
55
|
+
export type SSEMessage<T = Object> = NodeMessage | GraphResult<T>;
|
package/src/Substrate.ts
ADDED
@@ -0,0 +1,314 @@
|
|
1
|
+
import { SubstrateError, RequestTimeoutError } from "substrate/Error";
|
2
|
+
import { VERSION } from "substrate/version";
|
3
|
+
import OpenAPIjson from "substrate/openapi.json";
|
4
|
+
import { SubstrateResponse } from "substrate/SubstrateResponse";
|
5
|
+
import { SubstrateStreamingResponse } from "substrate/SubstrateStreamingResponse";
|
6
|
+
import { Node } from "substrate/Node";
|
7
|
+
import { Future } from "substrate/Future";
|
8
|
+
import { getPlatformProperties } from "substrate/Platform";
|
9
|
+
import { deflate } from "pako";
|
10
|
+
import { randomString } from "substrate/idGenerator";
|
11
|
+
|
12
|
+
type Configuration = {
|
13
|
+
/**
|
14
|
+
* [docs/authentication](https://docs.substrate.run/#authentication)
|
15
|
+
*/
|
16
|
+
apiKey: string | undefined;
|
17
|
+
|
18
|
+
/**
|
19
|
+
* [docs/versioning](https://docs.substrate.run/versioning)
|
20
|
+
*/
|
21
|
+
apiVersion?: string | undefined;
|
22
|
+
|
23
|
+
baseUrl?: string;
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Request timeout in milliseconds. Default: 5m
|
27
|
+
*/
|
28
|
+
timeout?: number;
|
29
|
+
|
30
|
+
/**
|
31
|
+
* Secrets for third party services.
|
32
|
+
*/
|
33
|
+
secrets?: Secrets;
|
34
|
+
|
35
|
+
/**
|
36
|
+
* Add additional headers to each request. These may override headers set by the Substrate client.
|
37
|
+
*/
|
38
|
+
additionalHeaders?: Record<string, string>;
|
39
|
+
};
|
40
|
+
|
41
|
+
export type Secrets = {
|
42
|
+
openai?: string;
|
43
|
+
anthropic?: string;
|
44
|
+
};
|
45
|
+
|
46
|
+
/**
|
47
|
+
* [docs/introduction](https://docs.substrate.run)
|
48
|
+
*/
|
49
|
+
export class Substrate {
|
50
|
+
apiKey: Configuration["apiKey"];
|
51
|
+
baseUrl: NonNullable<Configuration["baseUrl"]>;
|
52
|
+
apiVersion: NonNullable<Configuration["apiVersion"]>;
|
53
|
+
timeout: NonNullable<Configuration["timeout"]>;
|
54
|
+
additionalHeaders: NonNullable<Configuration["additionalHeaders"]>;
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Initialize the Substrate SDK.
|
58
|
+
*/
|
59
|
+
constructor({
|
60
|
+
apiKey,
|
61
|
+
baseUrl,
|
62
|
+
apiVersion,
|
63
|
+
timeout,
|
64
|
+
secrets,
|
65
|
+
additionalHeaders,
|
66
|
+
}: Configuration) {
|
67
|
+
if (!apiKey) {
|
68
|
+
console.warn(
|
69
|
+
"[Substrate] An API Key is required. Specify it when constructing the client: `new Substrate({ apiKey: 'API_KEY' })`",
|
70
|
+
);
|
71
|
+
}
|
72
|
+
this.apiKey = apiKey;
|
73
|
+
this.baseUrl = baseUrl ?? "https://api.substrate.run";
|
74
|
+
this.apiVersion = apiVersion ?? OpenAPIjson["info"]["version"];
|
75
|
+
this.timeout = timeout ?? 300_000;
|
76
|
+
this.additionalHeaders = additionalHeaders ?? {};
|
77
|
+
if (secrets) {
|
78
|
+
if (secrets.openai) {
|
79
|
+
this.additionalHeaders["x-substrate-openai-api-key"] = secrets.openai;
|
80
|
+
}
|
81
|
+
if (secrets.anthropic) {
|
82
|
+
this.additionalHeaders["x-substrate-anthropic-api-key"] =
|
83
|
+
secrets.anthropic;
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
/**
|
89
|
+
* Run the given nodes.
|
90
|
+
*
|
91
|
+
* @throws {SubstrateError} when the server response is an error.
|
92
|
+
* @throws {RequestTimeoutError} when the client has timed out (Configured by `Substrate.timeout`).
|
93
|
+
* @throws {Error} when the client encounters an error making the request.
|
94
|
+
*/
|
95
|
+
async run(...nodes: Node[]): Promise<SubstrateResponse> {
|
96
|
+
return this.runSerialized(nodes);
|
97
|
+
}
|
98
|
+
|
99
|
+
/**
|
100
|
+
* Stream the given nodes.
|
101
|
+
*/
|
102
|
+
async stream(...nodes: Node[]): Promise<SubstrateStreamingResponse> {
|
103
|
+
const serialized = Substrate.serialize(...nodes);
|
104
|
+
return this.streamSerialized(serialized);
|
105
|
+
}
|
106
|
+
|
107
|
+
/**
|
108
|
+
* Run the given nodes, serialized using `Substrate.serialize`.
|
109
|
+
*
|
110
|
+
* @throws {SubstrateError} when the server response is an error.
|
111
|
+
* @throws {RequestTimeoutError} when the client has timed out (Configured by `Substrate.timeout`).
|
112
|
+
* @throws {Error} when the client encounters an error making the request.
|
113
|
+
*/
|
114
|
+
async runSerialized(
|
115
|
+
nodes: Node[],
|
116
|
+
endpoint: string = "/compose",
|
117
|
+
): Promise<SubstrateResponse> {
|
118
|
+
const serialized = Substrate.serialize(...nodes);
|
119
|
+
const url = this.baseUrl + endpoint;
|
120
|
+
const req = { dag: serialized };
|
121
|
+
// NOTE: we're creating the signal this way instead of AbortController.timeout because it is only very
|
122
|
+
// recently available on some environments, so this is a bit more supported.
|
123
|
+
const abortController = new AbortController();
|
124
|
+
const { signal } = abortController;
|
125
|
+
const timeout = setTimeout(() => abortController.abort(), this.timeout);
|
126
|
+
|
127
|
+
const request = new Request(url, this.requestOptions(req, signal));
|
128
|
+
const requestId = request.headers.get("x-substrate-request-id");
|
129
|
+
try {
|
130
|
+
const apiResponse = await fetch(request);
|
131
|
+
|
132
|
+
if (apiResponse.ok) {
|
133
|
+
const json = await apiResponse.json();
|
134
|
+
const res = new SubstrateResponse(request, apiResponse, json);
|
135
|
+
/** TODO stop setting output on node */
|
136
|
+
|
137
|
+
// @ts-expect-error (accessing protected)
|
138
|
+
for (let node of Substrate.findAllNodes(nodes)) node.response = res;
|
139
|
+
|
140
|
+
return res;
|
141
|
+
} else {
|
142
|
+
throw new SubstrateError(
|
143
|
+
`[Request failed] status=${apiResponse.status} statusText=${apiResponse.statusText} requestId=${requestId}`,
|
144
|
+
);
|
145
|
+
}
|
146
|
+
} catch (err: unknown) {
|
147
|
+
if (err instanceof Error) {
|
148
|
+
if (err.name === "AbortError") {
|
149
|
+
throw new RequestTimeoutError(
|
150
|
+
`Request timed out after ${this.timeout}ms requestId=${requestId}`,
|
151
|
+
);
|
152
|
+
// TODO: We could propagate timeout errors to nodes too, but I'm
|
153
|
+
// not sure yet what might be easier for the user to manage.
|
154
|
+
}
|
155
|
+
}
|
156
|
+
throw err;
|
157
|
+
} finally {
|
158
|
+
clearTimeout(timeout);
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
/**
|
163
|
+
* Stream the given nodes, serialized using `Substrate.serialize`.
|
164
|
+
*/
|
165
|
+
async streamSerialized(serialized: any, endpoint: string = "/compose") {
|
166
|
+
const url = this.baseUrl + endpoint;
|
167
|
+
const req = { dag: serialized };
|
168
|
+
const abortController = new AbortController();
|
169
|
+
const { signal } = abortController;
|
170
|
+
const timeout = setTimeout(() => abortController.abort(), this.timeout);
|
171
|
+
const requestOptions = this.requestOptions(req, signal);
|
172
|
+
|
173
|
+
// Add Streaming Headers
|
174
|
+
requestOptions.headers.set("Accept", "text/event-stream");
|
175
|
+
requestOptions.headers.set("X-Substrate-Streaming", "1");
|
176
|
+
|
177
|
+
const request = new Request(url, requestOptions);
|
178
|
+
const requestId = request.headers.get("x-substrate-request-id");
|
179
|
+
try {
|
180
|
+
const response = await fetch(request);
|
181
|
+
return await SubstrateStreamingResponse.fromRequestReponse(
|
182
|
+
request,
|
183
|
+
response,
|
184
|
+
);
|
185
|
+
} catch (err: unknown) {
|
186
|
+
if (err instanceof Error) {
|
187
|
+
if (err.name === "AbortError") {
|
188
|
+
throw new RequestTimeoutError(
|
189
|
+
`Request timed out after ${this.timeout}ms requestId=${requestId}`,
|
190
|
+
);
|
191
|
+
// TODO: We could propagate timeout errors to nodes too, but I'm
|
192
|
+
// not sure yet what might be easier for the user to manage.
|
193
|
+
}
|
194
|
+
}
|
195
|
+
throw err;
|
196
|
+
} finally {
|
197
|
+
clearTimeout(timeout);
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
/**
|
202
|
+
* Return a set of all nodes and their dependent nodes.
|
203
|
+
*/
|
204
|
+
static findAllNodes(fromNodes: Node[]): Set<Node> {
|
205
|
+
const allNodes = new Set<Node>();
|
206
|
+
for (let node of fromNodes) {
|
207
|
+
// @ts-ignore: .references() is protected
|
208
|
+
const refs = node.references();
|
209
|
+
for (let n of refs.nodes) {
|
210
|
+
allNodes.add(n);
|
211
|
+
}
|
212
|
+
}
|
213
|
+
return allNodes;
|
214
|
+
}
|
215
|
+
|
216
|
+
/**
|
217
|
+
* Return a set of all futures and their dependent futures.
|
218
|
+
*/
|
219
|
+
static findAllFutures(fromNodes: Node[]): Set<Future<any>> {
|
220
|
+
const allFutures = new Set<Future<any>>();
|
221
|
+
for (let node of fromNodes) {
|
222
|
+
// @ts-ignore: .references() is protected
|
223
|
+
const refs = node.references();
|
224
|
+
for (let f of refs.futures) {
|
225
|
+
allFutures.add(f);
|
226
|
+
}
|
227
|
+
}
|
228
|
+
return allFutures;
|
229
|
+
}
|
230
|
+
|
231
|
+
/**
|
232
|
+
* Transform an array of nodes into JSON for the Substrate API
|
233
|
+
*/
|
234
|
+
static serialize(...nodes: Node[]): any {
|
235
|
+
const allFutures = this.findAllFutures(nodes);
|
236
|
+
const allNodes = this.findAllNodes(nodes);
|
237
|
+
const allEdges: Record<string, Set<string>> = {};
|
238
|
+
for (let n of allNodes) {
|
239
|
+
allEdges[n.id] = new Set<string>();
|
240
|
+
for (let d of n.depends) {
|
241
|
+
allEdges[n.id]!.add(d.id);
|
242
|
+
}
|
243
|
+
}
|
244
|
+
return {
|
245
|
+
nodes: Array.from(allNodes).map((node) => node.toJSON()),
|
246
|
+
futures: Array.from(allFutures).map((future) => future.toJSON()),
|
247
|
+
edges: Object.keys(allEdges).flatMap((toId: string) => {
|
248
|
+
let fromIds: string[] = Array.from(allEdges[toId] as Set<string>);
|
249
|
+
return fromIds.map((fromId: string) => [fromId, toId, {}]);
|
250
|
+
}),
|
251
|
+
initial_args: {}, // @deprecated
|
252
|
+
};
|
253
|
+
}
|
254
|
+
|
255
|
+
/**
|
256
|
+
* Returns a url to visualize the given nodes.
|
257
|
+
*/
|
258
|
+
static visualize(...nodes: Node[]): string {
|
259
|
+
const serialized = this.serialize(...nodes);
|
260
|
+
const compressed = deflate(JSON.stringify(serialized), {
|
261
|
+
level: 9,
|
262
|
+
});
|
263
|
+
const numArray = Array.from(compressed);
|
264
|
+
const base64 = btoa(String.fromCharCode.apply(null, numArray));
|
265
|
+
const urlEncoded = base64
|
266
|
+
.replace(/\+/g, "-")
|
267
|
+
.replace(/\//g, "_")
|
268
|
+
.replace(/=+$/, "");
|
269
|
+
const baseURL = "https://explore.substrate.run/s/";
|
270
|
+
return baseURL + urlEncoded;
|
271
|
+
}
|
272
|
+
|
273
|
+
protected requestOptions(body: any, signal: AbortSignal) {
|
274
|
+
return {
|
275
|
+
method: "POST",
|
276
|
+
headers: this.headers(),
|
277
|
+
body: JSON.stringify(body),
|
278
|
+
signal,
|
279
|
+
};
|
280
|
+
}
|
281
|
+
|
282
|
+
protected headers() {
|
283
|
+
const headers = new Headers();
|
284
|
+
|
285
|
+
// API
|
286
|
+
headers.append("Accept", "application/json");
|
287
|
+
headers.append("Content-Type", "application/json");
|
288
|
+
headers.append("User-Agent", `APIClient/JS ${VERSION}`);
|
289
|
+
headers.append("X-Substrate-Version", this.apiVersion);
|
290
|
+
headers.append("X-Substrate-Request-Id", randomString(32));
|
291
|
+
headers.append("X-Substrate-Backend", "v1");
|
292
|
+
|
293
|
+
// Auth
|
294
|
+
headers.append("Authorization", `Bearer ${this.apiKey}`);
|
295
|
+
|
296
|
+
// SDK
|
297
|
+
headers.append("X-Substrate-Lang", "js");
|
298
|
+
headers.append("X-Substrate-Package-Version", VERSION);
|
299
|
+
|
300
|
+
// Platform, Runtime
|
301
|
+
const props = getPlatformProperties();
|
302
|
+
headers.append("X-Substrate-OS", props.os);
|
303
|
+
headers.append("X-Substrate-Arch", props.arch);
|
304
|
+
headers.append("X-Substrate-Runtime", props.runtime);
|
305
|
+
headers.append("X-Substrate-Runtime-Version", props.runtimeVersion);
|
306
|
+
|
307
|
+
// User-Provided
|
308
|
+
for (const [name, value] of Object.entries(this.additionalHeaders)) {
|
309
|
+
headers.set(name, value);
|
310
|
+
}
|
311
|
+
|
312
|
+
return headers;
|
313
|
+
}
|
314
|
+
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import { AnyNode, NodeOutput } from "substrate/Nodes";
|
2
|
+
import { NodeError } from "substrate/Error";
|
3
|
+
|
4
|
+
/**
|
5
|
+
* Response to a run request.
|
6
|
+
*/
|
7
|
+
export class SubstrateResponse {
|
8
|
+
public apiRequest: Request;
|
9
|
+
public apiResponse: Response;
|
10
|
+
public json: any;
|
11
|
+
|
12
|
+
constructor(request: Request, response: Response, json: any = null) {
|
13
|
+
this.apiRequest = request;
|
14
|
+
this.apiResponse = response;
|
15
|
+
this.json = json;
|
16
|
+
}
|
17
|
+
|
18
|
+
get requestId() {
|
19
|
+
return this.apiRequest.headers.get("x-substrate-request-id");
|
20
|
+
}
|
21
|
+
|
22
|
+
/**
|
23
|
+
* Returns an error from the `Node` if there was one.
|
24
|
+
*/
|
25
|
+
getError<T extends AnyNode>(node: T): NodeError | undefined {
|
26
|
+
// @ts-expect-error
|
27
|
+
return node.output() instanceof NodeError ? node.output() : undefined;
|
28
|
+
}
|
29
|
+
|
30
|
+
/**
|
31
|
+
* Returns the result for given `Node`.
|
32
|
+
*
|
33
|
+
* @throws {NodeError} when there was an error running the node.
|
34
|
+
*/
|
35
|
+
get<T extends AnyNode>(node: T): NodeOutput<T> {
|
36
|
+
const err = this.getError(node);
|
37
|
+
if (err) throw err;
|
38
|
+
// @ts-expect-error
|
39
|
+
return node.output() as NodeOutput<T>;
|
40
|
+
}
|
41
|
+
}
|