@secondlayer/sdk 0.10.2 → 1.0.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/README.md +1 -22
- package/dist/index.d.ts +195 -55
- package/dist/index.js +143 -89
- package/dist/index.js.map +10 -10
- package/dist/marketplace/index.d.ts +3 -0
- package/dist/marketplace/index.js +22 -4
- package/dist/marketplace/index.js.map +4 -4
- package/dist/subgraphs/index.d.ts +148 -47
- package/dist/subgraphs/index.js +128 -81
- package/dist/subgraphs/index.js.map +8 -9
- package/dist/workflows/index.d.ts +125 -5
- package/dist/workflows/index.js +130 -5
- package/dist/workflows/index.js.map +5 -5
- package/package.json +4 -8
- package/dist/streams/index.d.ts +0 -57
- package/dist/streams/index.js +0 -160
- package/dist/streams/index.js.map +0 -12
|
@@ -4,18 +4,88 @@ interface SecondLayerOptions {
|
|
|
4
4
|
baseUrl: string;
|
|
5
5
|
/** Bearer token for authenticated requests. */
|
|
6
6
|
apiKey?: string;
|
|
7
|
+
/** Deploy origin label sent as `x-sl-origin` (telemetry). Defaults to `cli`. */
|
|
8
|
+
origin?: "cli" | "mcp" | "session";
|
|
7
9
|
}
|
|
8
10
|
declare abstract class BaseClient {
|
|
9
11
|
protected baseUrl: string;
|
|
10
12
|
protected apiKey?: string;
|
|
13
|
+
protected origin: "cli" | "mcp" | "session";
|
|
11
14
|
constructor(options?: Partial<SecondLayerOptions>);
|
|
12
15
|
static authHeaders(apiKey?: string): Record<string, string>;
|
|
13
16
|
protected request<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
14
17
|
}
|
|
18
|
+
interface WorkflowSource {
|
|
19
|
+
name: string;
|
|
20
|
+
version: string;
|
|
21
|
+
sourceCode: string | null;
|
|
22
|
+
readOnly: boolean;
|
|
23
|
+
reason?: string;
|
|
24
|
+
updatedAt: string;
|
|
25
|
+
}
|
|
26
|
+
interface WorkflowStepEvent {
|
|
27
|
+
id: string;
|
|
28
|
+
stepIndex: number;
|
|
29
|
+
stepId: string;
|
|
30
|
+
stepType: string;
|
|
31
|
+
status: string;
|
|
32
|
+
output?: unknown;
|
|
33
|
+
error: string | null;
|
|
34
|
+
retryCount: number;
|
|
35
|
+
aiTokensUsed: number;
|
|
36
|
+
startedAt: string | null;
|
|
37
|
+
completedAt: string | null;
|
|
38
|
+
durationMs: number | null;
|
|
39
|
+
ts: string;
|
|
40
|
+
}
|
|
41
|
+
interface WorkflowRunDoneEvent {
|
|
42
|
+
runId: string;
|
|
43
|
+
status: string;
|
|
44
|
+
error?: string | null;
|
|
45
|
+
completedAt?: string | null;
|
|
46
|
+
}
|
|
47
|
+
type WorkflowTailEvent = {
|
|
48
|
+
type: "step"
|
|
49
|
+
step: WorkflowStepEvent
|
|
50
|
+
} | {
|
|
51
|
+
type: "done"
|
|
52
|
+
done: WorkflowRunDoneEvent
|
|
53
|
+
} | {
|
|
54
|
+
type: "heartbeat"
|
|
55
|
+
ts: string
|
|
56
|
+
} | {
|
|
57
|
+
type: "timeout"
|
|
58
|
+
message: string
|
|
59
|
+
};
|
|
60
|
+
interface DeployDryRunResponse {
|
|
61
|
+
valid: boolean;
|
|
62
|
+
validation?: {
|
|
63
|
+
name: string
|
|
64
|
+
triggerType: string
|
|
65
|
+
};
|
|
66
|
+
bundleSize: number;
|
|
67
|
+
error?: string;
|
|
68
|
+
}
|
|
69
|
+
interface DeployResponse {
|
|
70
|
+
action: "created" | "updated";
|
|
71
|
+
workflowId: string;
|
|
72
|
+
version: string;
|
|
73
|
+
message: string;
|
|
74
|
+
}
|
|
75
|
+
interface BundleWorkflowResponse {
|
|
76
|
+
ok: true;
|
|
77
|
+
name: string;
|
|
78
|
+
trigger: Record<string, unknown>;
|
|
79
|
+
handlerCode: string;
|
|
80
|
+
sourceCode: string;
|
|
81
|
+
retries: Record<string, unknown> | null;
|
|
82
|
+
timeout: number | null;
|
|
83
|
+
bundleSize: number;
|
|
84
|
+
}
|
|
15
85
|
interface WorkflowSummary {
|
|
16
86
|
name: string;
|
|
17
87
|
status: "active" | "paused";
|
|
18
|
-
triggerType: "event" | "
|
|
88
|
+
triggerType: "event" | "schedule" | "manual";
|
|
19
89
|
createdAt: string;
|
|
20
90
|
updatedAt: string;
|
|
21
91
|
}
|
|
@@ -43,13 +113,63 @@ declare class Workflows extends BaseClient {
|
|
|
43
113
|
name: string
|
|
44
114
|
trigger: Record<string, unknown>
|
|
45
115
|
handlerCode: string
|
|
116
|
+
sourceCode?: string
|
|
117
|
+
expectedVersion?: string
|
|
46
118
|
retries?: Record<string, unknown>
|
|
47
119
|
timeout?: number
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
120
|
+
clientRequestId?: string
|
|
121
|
+
} & {
|
|
122
|
+
dryRun?: false
|
|
123
|
+
}): Promise<DeployResponse>;
|
|
124
|
+
deploy(data: {
|
|
125
|
+
name: string
|
|
126
|
+
trigger: Record<string, unknown>
|
|
127
|
+
handlerCode: string
|
|
128
|
+
sourceCode?: string
|
|
129
|
+
expectedVersion?: string
|
|
130
|
+
retries?: Record<string, unknown>
|
|
131
|
+
timeout?: number
|
|
132
|
+
dryRun: true
|
|
133
|
+
}): Promise<DeployDryRunResponse>;
|
|
134
|
+
getSource(name: string): Promise<WorkflowSource>;
|
|
135
|
+
/**
|
|
136
|
+
* Bundle a TypeScript workflow source on the server. Used by the web chat
|
|
137
|
+
* authoring loop so Vercel's serverless runtime doesn't have to run esbuild.
|
|
138
|
+
* CLI and MCP still bundle locally — this method is for clients that can't
|
|
139
|
+
* install `@secondlayer/bundler` directly (e.g. browser tooling, edge
|
|
140
|
+
* functions).
|
|
141
|
+
*/
|
|
142
|
+
bundle(data: {
|
|
143
|
+
code: string
|
|
144
|
+
}): Promise<BundleWorkflowResponse>;
|
|
145
|
+
pauseAll(): Promise<{
|
|
146
|
+
paused: number
|
|
147
|
+
workflows: Array<{
|
|
148
|
+
name: string
|
|
149
|
+
version: string
|
|
150
|
+
status: string
|
|
151
|
+
}>
|
|
152
|
+
}>;
|
|
153
|
+
cancelRun(runId: string): Promise<{
|
|
154
|
+
runId: string
|
|
155
|
+
status: string
|
|
156
|
+
cancelled: boolean
|
|
157
|
+
completedAt?: string
|
|
158
|
+
message?: string
|
|
159
|
+
}>;
|
|
160
|
+
rollback(name: string, toVersion?: string): Promise<{
|
|
161
|
+
action: "rolled-back"
|
|
162
|
+
name: string
|
|
163
|
+
fromVersion: string
|
|
164
|
+
restoredFromVersion: string
|
|
165
|
+
version: string
|
|
52
166
|
}>;
|
|
167
|
+
/**
|
|
168
|
+
* Subscribe to a workflow run's server-sent event stream. Resolves when the
|
|
169
|
+
* run completes, times out, or the signal is aborted. Throws on HTTP errors
|
|
170
|
+
* opening the stream.
|
|
171
|
+
*/
|
|
172
|
+
streamRun(name: string, runId: string, onEvent: (event: WorkflowTailEvent) => void, signal?: AbortSignal): Promise<void>;
|
|
53
173
|
list(): Promise<{
|
|
54
174
|
workflows: WorkflowSummary[]
|
|
55
175
|
}>;
|
package/dist/workflows/index.js
CHANGED
|
@@ -1,35 +1,51 @@
|
|
|
1
1
|
// src/errors.ts
|
|
2
2
|
class ApiError extends Error {
|
|
3
3
|
status;
|
|
4
|
-
|
|
4
|
+
body;
|
|
5
|
+
constructor(status, message, body) {
|
|
5
6
|
super(message);
|
|
6
7
|
this.status = status;
|
|
8
|
+
this.body = body;
|
|
7
9
|
this.name = "ApiError";
|
|
8
10
|
}
|
|
9
11
|
}
|
|
10
12
|
|
|
13
|
+
class VersionConflictError extends ApiError {
|
|
14
|
+
currentVersion;
|
|
15
|
+
expectedVersion;
|
|
16
|
+
constructor(currentVersion, expectedVersion, message = `Version conflict: expected ${expectedVersion}, current ${currentVersion}`) {
|
|
17
|
+
super(409, message, { currentVersion, expectedVersion });
|
|
18
|
+
this.currentVersion = currentVersion;
|
|
19
|
+
this.expectedVersion = expectedVersion;
|
|
20
|
+
this.name = "VersionConflictError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
11
24
|
// src/base.ts
|
|
12
25
|
var DEFAULT_BASE_URL = "https://api.secondlayer.tools";
|
|
13
26
|
|
|
14
27
|
class BaseClient {
|
|
15
28
|
baseUrl;
|
|
16
29
|
apiKey;
|
|
30
|
+
origin;
|
|
17
31
|
constructor(options = {}) {
|
|
18
32
|
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
19
33
|
this.apiKey = options.apiKey;
|
|
34
|
+
this.origin = options.origin ?? "cli";
|
|
20
35
|
}
|
|
21
36
|
static authHeaders(apiKey) {
|
|
22
37
|
const headers = {
|
|
23
38
|
"Content-Type": "application/json"
|
|
24
39
|
};
|
|
25
40
|
if (apiKey) {
|
|
26
|
-
headers
|
|
41
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
27
42
|
}
|
|
28
43
|
return headers;
|
|
29
44
|
}
|
|
30
45
|
async request(method, path, body) {
|
|
31
46
|
const url = `${this.baseUrl}${path}`;
|
|
32
47
|
const headers = BaseClient.authHeaders(this.apiKey);
|
|
48
|
+
headers["x-sl-origin"] = this.origin;
|
|
33
49
|
let response;
|
|
34
50
|
try {
|
|
35
51
|
response = await fetch(url, {
|
|
@@ -54,8 +70,10 @@ class BaseClient {
|
|
|
54
70
|
}
|
|
55
71
|
const errorBody = await response.text();
|
|
56
72
|
let message = `HTTP ${response.status}`;
|
|
73
|
+
let parsedBody = errorBody;
|
|
57
74
|
try {
|
|
58
75
|
const json = JSON.parse(errorBody);
|
|
76
|
+
parsedBody = json;
|
|
59
77
|
const err = json.error ?? json.message;
|
|
60
78
|
if (typeof err === "string") {
|
|
61
79
|
message = err;
|
|
@@ -66,7 +84,7 @@ class BaseClient {
|
|
|
66
84
|
if (errorBody)
|
|
67
85
|
message = errorBody;
|
|
68
86
|
}
|
|
69
|
-
throw new ApiError(response.status, message);
|
|
87
|
+
throw new ApiError(response.status, message, parsedBody);
|
|
70
88
|
}
|
|
71
89
|
if (response.status === 204) {
|
|
72
90
|
return;
|
|
@@ -76,9 +94,116 @@ class BaseClient {
|
|
|
76
94
|
}
|
|
77
95
|
|
|
78
96
|
// src/workflows/client.ts
|
|
97
|
+
function parseSseChunk(raw) {
|
|
98
|
+
let event = "message";
|
|
99
|
+
const dataLines = [];
|
|
100
|
+
for (const line of raw.split(`
|
|
101
|
+
`)) {
|
|
102
|
+
if (line.startsWith("event:")) {
|
|
103
|
+
event = line.slice(6).trim();
|
|
104
|
+
} else if (line.startsWith("data:")) {
|
|
105
|
+
dataLines.push(line.slice(5).trimStart());
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (dataLines.length === 0)
|
|
109
|
+
return null;
|
|
110
|
+
const data = dataLines.join(`
|
|
111
|
+
`);
|
|
112
|
+
try {
|
|
113
|
+
const parsed = JSON.parse(data);
|
|
114
|
+
switch (event) {
|
|
115
|
+
case "step":
|
|
116
|
+
return { type: "step", step: parsed };
|
|
117
|
+
case "done":
|
|
118
|
+
return { type: "done", done: parsed };
|
|
119
|
+
case "heartbeat":
|
|
120
|
+
return {
|
|
121
|
+
type: "heartbeat",
|
|
122
|
+
ts: typeof parsed === "string" ? parsed : String(parsed)
|
|
123
|
+
};
|
|
124
|
+
case "timeout":
|
|
125
|
+
return {
|
|
126
|
+
type: "timeout",
|
|
127
|
+
message: parsed.message ?? "timeout"
|
|
128
|
+
};
|
|
129
|
+
default:
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
} catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
79
137
|
class Workflows extends BaseClient {
|
|
80
138
|
async deploy(data) {
|
|
81
|
-
|
|
139
|
+
try {
|
|
140
|
+
return await this.request("POST", "/api/workflows", data);
|
|
141
|
+
} catch (err) {
|
|
142
|
+
if (err instanceof ApiError && err.status === 409) {
|
|
143
|
+
const body = err.body;
|
|
144
|
+
if (body?.currentVersion && body.expectedVersion) {
|
|
145
|
+
throw new VersionConflictError(body.currentVersion, body.expectedVersion, err.message);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
throw err;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async getSource(name) {
|
|
152
|
+
return this.request("GET", `/api/workflows/${name}/source`);
|
|
153
|
+
}
|
|
154
|
+
async bundle(data) {
|
|
155
|
+
return this.request("POST", "/api/workflows/bundle", data);
|
|
156
|
+
}
|
|
157
|
+
async pauseAll() {
|
|
158
|
+
return this.request("POST", "/api/workflows/pause-all");
|
|
159
|
+
}
|
|
160
|
+
async cancelRun(runId) {
|
|
161
|
+
return this.request("POST", `/api/workflows/runs/${runId}/cancel`);
|
|
162
|
+
}
|
|
163
|
+
async rollback(name, toVersion) {
|
|
164
|
+
return this.request("POST", `/api/workflows/${name}/rollback`, toVersion ? { toVersion } : {});
|
|
165
|
+
}
|
|
166
|
+
async streamRun(name, runId, onEvent, signal) {
|
|
167
|
+
const url = `${this.baseUrl}/api/workflows/${name}/runs/${runId}/stream`;
|
|
168
|
+
const headers = {
|
|
169
|
+
Accept: "text/event-stream",
|
|
170
|
+
"x-sl-origin": this.origin
|
|
171
|
+
};
|
|
172
|
+
if (this.apiKey) {
|
|
173
|
+
headers.Authorization = `Bearer ${this.apiKey}`;
|
|
174
|
+
}
|
|
175
|
+
const res = await fetch(url, { headers, signal });
|
|
176
|
+
if (!res.ok || !res.body) {
|
|
177
|
+
throw new ApiError(res.status, `Failed to open workflow run stream (HTTP ${res.status})`);
|
|
178
|
+
}
|
|
179
|
+
const reader = res.body.pipeThrough(new TextDecoderStream).getReader();
|
|
180
|
+
let buffer = "";
|
|
181
|
+
while (true) {
|
|
182
|
+
const { value, done } = await reader.read();
|
|
183
|
+
if (done)
|
|
184
|
+
break;
|
|
185
|
+
buffer += value;
|
|
186
|
+
let sep = buffer.indexOf(`
|
|
187
|
+
|
|
188
|
+
`);
|
|
189
|
+
while (sep !== -1) {
|
|
190
|
+
const chunk = buffer.slice(0, sep);
|
|
191
|
+
buffer = buffer.slice(sep + 2);
|
|
192
|
+
const parsed = parseSseChunk(chunk);
|
|
193
|
+
if (parsed) {
|
|
194
|
+
onEvent(parsed);
|
|
195
|
+
if (parsed.type === "done" || parsed.type === "timeout") {
|
|
196
|
+
await reader.cancel().catch(() => {
|
|
197
|
+
return;
|
|
198
|
+
});
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
sep = buffer.indexOf(`
|
|
203
|
+
|
|
204
|
+
`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
82
207
|
}
|
|
83
208
|
async list() {
|
|
84
209
|
return this.request("GET", "/api/workflows");
|
|
@@ -115,5 +240,5 @@ export {
|
|
|
115
240
|
Workflows
|
|
116
241
|
};
|
|
117
242
|
|
|
118
|
-
//# debugId=
|
|
243
|
+
//# debugId=653D77D9FE60E3F464756E2164756E21
|
|
119
244
|
//# sourceMappingURL=index.js.map
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/errors.ts", "../src/base.ts", "../src/workflows/client.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"/**\n * Error thrown by {@link SecondLayer} when an API request fails.\n * Includes the HTTP status code for programmatic error handling.\n *\n * @example\n * ```ts\n * try {\n * await client.
|
|
6
|
-
"import { ApiError } from \"./errors.ts\";\n\nexport interface SecondLayerOptions {\n\t/** Base URL of the Secondlayer API (trailing slashes are stripped). */\n\tbaseUrl: string;\n\t/** Bearer token for authenticated requests. */\n\tapiKey?: string;\n}\n\nconst DEFAULT_BASE_URL = \"https://api.secondlayer.tools\";\n\nexport abstract class BaseClient {\n\tprotected baseUrl: string;\n\tprotected apiKey?: string;\n\n\tconstructor(options: Partial<SecondLayerOptions> = {}) {\n\t\tthis.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n\t\tthis.apiKey = options.apiKey;\n\t}\n\n\tstatic authHeaders(apiKey?: string): Record<string, string> {\n\t\tconst headers: Record<string, string> = {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t};\n\t\tif (apiKey) {\n\t\t\theaders
|
|
7
|
-
"import type {
|
|
5
|
+
"/**\n * Error thrown by {@link SecondLayer} when an API request fails.\n * Includes the HTTP status code for programmatic error handling.\n *\n * @example\n * ```ts\n * try {\n * await client.subgraphs.get(\"my-subgraph\");\n * } catch (err) {\n * if (err instanceof ApiError && err.status === 404) {\n * console.log(\"Subgraph not found\");\n * }\n * }\n * ```\n */\nexport class ApiError extends Error {\n\tconstructor(\n\t\t/** HTTP status code (0 for network errors). */\n\t\tpublic status: number,\n\t\tmessage: string,\n\t\t/** Raw response body (parsed JSON if possible) — preserved for callers that need error details. */\n\t\tpublic body?: unknown,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"ApiError\";\n\t}\n}\n\n/**\n * Thrown by {@link Workflows.deploy} when the server rejects a deploy because the\n * provided `expectedVersion` does not match the current stored version.\n */\nexport class VersionConflictError extends ApiError {\n\tconstructor(\n\t\tpublic currentVersion: string,\n\t\tpublic expectedVersion: string,\n\t\tmessage = `Version conflict: expected ${expectedVersion}, current ${currentVersion}`,\n\t) {\n\t\tsuper(409, message, { currentVersion, expectedVersion });\n\t\tthis.name = \"VersionConflictError\";\n\t}\n}\n",
|
|
6
|
+
"import { ApiError } from \"./errors.ts\";\n\nexport interface SecondLayerOptions {\n\t/** Base URL of the Secondlayer API (trailing slashes are stripped). */\n\tbaseUrl: string;\n\t/** Bearer token for authenticated requests. */\n\tapiKey?: string;\n\t/** Deploy origin label sent as `x-sl-origin` (telemetry). Defaults to `cli`. */\n\torigin?: \"cli\" | \"mcp\" | \"session\";\n}\n\nconst DEFAULT_BASE_URL = \"https://api.secondlayer.tools\";\n\nexport abstract class BaseClient {\n\tprotected baseUrl: string;\n\tprotected apiKey?: string;\n\tprotected origin: \"cli\" | \"mcp\" | \"session\";\n\n\tconstructor(options: Partial<SecondLayerOptions> = {}) {\n\t\tthis.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n\t\tthis.apiKey = options.apiKey;\n\t\tthis.origin = options.origin ?? \"cli\";\n\t}\n\n\tstatic authHeaders(apiKey?: string): Record<string, string> {\n\t\tconst headers: Record<string, string> = {\n\t\t\t\"Content-Type\": \"application/json\",\n\t\t};\n\t\tif (apiKey) {\n\t\t\theaders.Authorization = `Bearer ${apiKey}`;\n\t\t}\n\t\treturn headers;\n\t}\n\n\tprotected async request<T>(\n\t\tmethod: string,\n\t\tpath: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tconst url = `${this.baseUrl}${path}`;\n\t\tconst headers = BaseClient.authHeaders(this.apiKey);\n\t\theaders[\"x-sl-origin\"] = this.origin;\n\n\t\tlet response: Response;\n\t\ttry {\n\t\t\tresponse = await fetch(url, {\n\t\t\t\tmethod,\n\t\t\t\theaders,\n\t\t\t\tbody: body ? JSON.stringify(body) : undefined,\n\t\t\t});\n\t\t} catch {\n\t\t\tthrow new ApiError(\n\t\t\t\t0,\n\t\t\t\t`Cannot reach API at ${this.baseUrl}. Check your connection or try again.`,\n\t\t\t);\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\tif (response.status === 401) {\n\t\t\t\tthrow new ApiError(401, \"API key invalid or expired.\");\n\t\t\t}\n\n\t\t\tif (response.status === 429) {\n\t\t\t\tconst retryAfter = response.headers.get(\"Retry-After\");\n\t\t\t\tconst msg = retryAfter\n\t\t\t\t\t? `Rate limited. Wait ${retryAfter} seconds.`\n\t\t\t\t\t: \"Rate limited. Try again later.\";\n\t\t\t\tthrow new ApiError(429, msg);\n\t\t\t}\n\n\t\t\tif (response.status >= 500) {\n\t\t\t\tthrow new ApiError(\n\t\t\t\t\tresponse.status,\n\t\t\t\t\t`Server error. Try again or check status at ${this.baseUrl}/health`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst errorBody = await response.text();\n\t\t\tlet message = `HTTP ${response.status}`;\n\t\t\tlet parsedBody: unknown = errorBody;\n\t\t\ttry {\n\t\t\t\tconst json = JSON.parse(errorBody);\n\t\t\t\tparsedBody = json;\n\t\t\t\tconst err = json.error ?? json.message;\n\t\t\t\tif (typeof err === \"string\") {\n\t\t\t\t\tmessage = err;\n\t\t\t\t} else if (err && typeof err === \"object\") {\n\t\t\t\t\tmessage = JSON.stringify(err);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\tif (errorBody) message = errorBody;\n\t\t\t}\n\t\t\tthrow new ApiError(response.status, message, parsedBody);\n\t\t}\n\n\t\tif (response.status === 204) {\n\t\t\treturn undefined as T;\n\t\t}\n\n\t\treturn response.json() as Promise<T>;\n\t}\n}\n",
|
|
7
|
+
"import type { WorkflowRun, WorkflowRunStatus } from \"@secondlayer/workflows\";\nimport { BaseClient } from \"../base.ts\";\nimport { ApiError, VersionConflictError } from \"../errors.ts\";\n\nfunction parseSseChunk(raw: string): WorkflowTailEvent | null {\n\tlet event = \"message\";\n\tconst dataLines: string[] = [];\n\tfor (const line of raw.split(\"\\n\")) {\n\t\tif (line.startsWith(\"event:\")) {\n\t\t\tevent = line.slice(6).trim();\n\t\t} else if (line.startsWith(\"data:\")) {\n\t\t\tdataLines.push(line.slice(5).trimStart());\n\t\t}\n\t}\n\tif (dataLines.length === 0) return null;\n\tconst data = dataLines.join(\"\\n\");\n\ttry {\n\t\tconst parsed = JSON.parse(data);\n\t\tswitch (event) {\n\t\t\tcase \"step\":\n\t\t\t\treturn { type: \"step\", step: parsed as WorkflowStepEvent };\n\t\t\tcase \"done\":\n\t\t\t\treturn { type: \"done\", done: parsed as WorkflowRunDoneEvent };\n\t\t\tcase \"heartbeat\":\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"heartbeat\",\n\t\t\t\t\tts: typeof parsed === \"string\" ? parsed : String(parsed),\n\t\t\t\t};\n\t\t\tcase \"timeout\":\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"timeout\",\n\t\t\t\t\tmessage: (parsed as { message?: string }).message ?? \"timeout\",\n\t\t\t\t};\n\t\t\tdefault:\n\t\t\t\treturn null;\n\t\t}\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport interface WorkflowSource {\n\tname: string;\n\tversion: string;\n\tsourceCode: string | null;\n\treadOnly: boolean;\n\treason?: string;\n\tupdatedAt: string;\n}\n\nexport interface WorkflowStepEvent {\n\tid: string;\n\tstepIndex: number;\n\tstepId: string;\n\tstepType: string;\n\tstatus: string;\n\toutput?: unknown;\n\terror: string | null;\n\tretryCount: number;\n\taiTokensUsed: number;\n\tstartedAt: string | null;\n\tcompletedAt: string | null;\n\tdurationMs: number | null;\n\tts: string;\n}\n\nexport interface WorkflowRunDoneEvent {\n\trunId: string;\n\tstatus: string;\n\terror?: string | null;\n\tcompletedAt?: string | null;\n}\n\nexport type WorkflowTailEvent =\n\t| { type: \"step\"; step: WorkflowStepEvent }\n\t| { type: \"done\"; done: WorkflowRunDoneEvent }\n\t| { type: \"heartbeat\"; ts: string }\n\t| { type: \"timeout\"; message: string };\n\nexport interface DeployDryRunResponse {\n\tvalid: boolean;\n\tvalidation?: { name: string; triggerType: string };\n\tbundleSize: number;\n\terror?: string;\n}\n\nexport interface DeployResponse {\n\taction: \"created\" | \"updated\";\n\tworkflowId: string;\n\tversion: string;\n\tmessage: string;\n}\n\nexport interface BundleWorkflowResponse {\n\tok: true;\n\tname: string;\n\ttrigger: Record<string, unknown>;\n\thandlerCode: string;\n\tsourceCode: string;\n\tretries: Record<string, unknown> | null;\n\ttimeout: number | null;\n\tbundleSize: number;\n}\n\nexport interface WorkflowSummary {\n\tname: string;\n\tstatus: \"active\" | \"paused\";\n\ttriggerType: \"event\" | \"schedule\" | \"manual\";\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface WorkflowDetail extends WorkflowSummary {\n\ttrigger: Record<string, unknown>;\n\tretries?: { maxAttempts?: number; backoffMs?: number };\n\ttimeout?: number;\n\ttotalRuns: number;\n\tlastRunAt: string | null;\n}\n\nexport interface WorkflowRunSummary {\n\tid: string;\n\tworkflowName: string;\n\tstatus: WorkflowRunStatus;\n\tduration: number;\n\taiTokensUsed: number;\n\ttriggeredAt: string;\n\tcompletedAt: string | null;\n}\n\nexport class Workflows extends BaseClient {\n\tasync deploy(\n\t\tdata: {\n\t\t\tname: string;\n\t\t\ttrigger: Record<string, unknown>;\n\t\t\thandlerCode: string;\n\t\t\tsourceCode?: string;\n\t\t\texpectedVersion?: string;\n\t\t\tretries?: Record<string, unknown>;\n\t\t\ttimeout?: number;\n\t\t\tclientRequestId?: string;\n\t\t} & { dryRun?: false },\n\t): Promise<DeployResponse>;\n\tasync deploy(data: {\n\t\tname: string;\n\t\ttrigger: Record<string, unknown>;\n\t\thandlerCode: string;\n\t\tsourceCode?: string;\n\t\texpectedVersion?: string;\n\t\tretries?: Record<string, unknown>;\n\t\ttimeout?: number;\n\t\tdryRun: true;\n\t}): Promise<DeployDryRunResponse>;\n\tasync deploy(data: {\n\t\tname: string;\n\t\ttrigger: Record<string, unknown>;\n\t\thandlerCode: string;\n\t\tsourceCode?: string;\n\t\texpectedVersion?: string;\n\t\tdryRun?: boolean;\n\t\tclientRequestId?: string;\n\t\tretries?: Record<string, unknown>;\n\t\ttimeout?: number;\n\t}): Promise<DeployResponse | DeployDryRunResponse> {\n\t\ttry {\n\t\t\treturn await this.request(\"POST\", \"/api/workflows\", data);\n\t\t} catch (err) {\n\t\t\tif (err instanceof ApiError && err.status === 409) {\n\t\t\t\tconst body = err.body as\n\t\t\t\t\t| { currentVersion?: string; expectedVersion?: string }\n\t\t\t\t\t| undefined;\n\t\t\t\tif (body?.currentVersion && body.expectedVersion) {\n\t\t\t\t\tthrow new VersionConflictError(\n\t\t\t\t\t\tbody.currentVersion,\n\t\t\t\t\t\tbody.expectedVersion,\n\t\t\t\t\t\terr.message,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\tasync getSource(name: string): Promise<WorkflowSource> {\n\t\treturn this.request(\"GET\", `/api/workflows/${name}/source`);\n\t}\n\n\t/**\n\t * Bundle a TypeScript workflow source on the server. Used by the web chat\n\t * authoring loop so Vercel's serverless runtime doesn't have to run esbuild.\n\t * CLI and MCP still bundle locally — this method is for clients that can't\n\t * install `@secondlayer/bundler` directly (e.g. browser tooling, edge\n\t * functions).\n\t */\n\tasync bundle(data: { code: string }): Promise<BundleWorkflowResponse> {\n\t\treturn this.request(\"POST\", \"/api/workflows/bundle\", data);\n\t}\n\n\tasync pauseAll(): Promise<{\n\t\tpaused: number;\n\t\tworkflows: Array<{ name: string; version: string; status: string }>;\n\t}> {\n\t\treturn this.request(\"POST\", \"/api/workflows/pause-all\");\n\t}\n\n\tasync cancelRun(runId: string): Promise<{\n\t\trunId: string;\n\t\tstatus: string;\n\t\tcancelled: boolean;\n\t\tcompletedAt?: string;\n\t\tmessage?: string;\n\t}> {\n\t\treturn this.request(\"POST\", `/api/workflows/runs/${runId}/cancel`);\n\t}\n\n\tasync rollback(\n\t\tname: string,\n\t\ttoVersion?: string,\n\t): Promise<{\n\t\taction: \"rolled-back\";\n\t\tname: string;\n\t\tfromVersion: string;\n\t\trestoredFromVersion: string;\n\t\tversion: string;\n\t}> {\n\t\treturn this.request(\n\t\t\t\"POST\",\n\t\t\t`/api/workflows/${name}/rollback`,\n\t\t\ttoVersion ? { toVersion } : {},\n\t\t);\n\t}\n\n\t/**\n\t * Subscribe to a workflow run's server-sent event stream. Resolves when the\n\t * run completes, times out, or the signal is aborted. Throws on HTTP errors\n\t * opening the stream.\n\t */\n\tasync streamRun(\n\t\tname: string,\n\t\trunId: string,\n\t\tonEvent: (event: WorkflowTailEvent) => void,\n\t\tsignal?: AbortSignal,\n\t): Promise<void> {\n\t\tconst url = `${this.baseUrl}/api/workflows/${name}/runs/${runId}/stream`;\n\t\tconst headers: Record<string, string> = {\n\t\t\tAccept: \"text/event-stream\",\n\t\t\t\"x-sl-origin\": this.origin,\n\t\t};\n\t\tif (this.apiKey) {\n\t\t\theaders.Authorization = `Bearer ${this.apiKey}`;\n\t\t}\n\n\t\tconst res = await fetch(url, { headers, signal });\n\t\tif (!res.ok || !res.body) {\n\t\t\tthrow new ApiError(\n\t\t\t\tres.status,\n\t\t\t\t`Failed to open workflow run stream (HTTP ${res.status})`,\n\t\t\t);\n\t\t}\n\n\t\tconst reader = res.body.pipeThrough(new TextDecoderStream()).getReader();\n\t\tlet buffer = \"\";\n\n\t\twhile (true) {\n\t\t\tconst { value, done } = await reader.read();\n\t\t\tif (done) break;\n\t\t\tbuffer += value;\n\n\t\t\tlet sep = buffer.indexOf(\"\\n\\n\");\n\t\t\twhile (sep !== -1) {\n\t\t\t\tconst chunk = buffer.slice(0, sep);\n\t\t\t\tbuffer = buffer.slice(sep + 2);\n\t\t\t\tconst parsed = parseSseChunk(chunk);\n\t\t\t\tif (parsed) {\n\t\t\t\t\tonEvent(parsed);\n\t\t\t\t\tif (parsed.type === \"done\" || parsed.type === \"timeout\") {\n\t\t\t\t\t\tawait reader.cancel().catch(() => undefined);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tsep = buffer.indexOf(\"\\n\\n\");\n\t\t\t}\n\t\t}\n\t}\n\n\tasync list(): Promise<{ workflows: WorkflowSummary[] }> {\n\t\treturn this.request(\"GET\", \"/api/workflows\");\n\t}\n\n\tasync get(name: string): Promise<WorkflowDetail> {\n\t\treturn this.request(\"GET\", `/api/workflows/${name}`);\n\t}\n\n\tasync trigger(\n\t\tname: string,\n\t\tinput?: Record<string, unknown>,\n\t): Promise<{ runId: string }> {\n\t\treturn this.request(\n\t\t\t\"POST\",\n\t\t\t`/api/workflows/${name}/trigger`,\n\t\t\tinput ? { input } : undefined,\n\t\t);\n\t}\n\n\tasync pause(name: string): Promise<void> {\n\t\treturn this.request(\"POST\", `/api/workflows/${name}/pause`);\n\t}\n\n\tasync resume(name: string): Promise<void> {\n\t\treturn this.request(\"POST\", `/api/workflows/${name}/resume`);\n\t}\n\n\tasync delete(name: string): Promise<void> {\n\t\treturn this.request(\"DELETE\", `/api/workflows/${name}`);\n\t}\n\n\tasync listRuns(\n\t\tname: string,\n\t\tparams?: { status?: WorkflowRunStatus; limit?: number },\n\t): Promise<{ runs: WorkflowRunSummary[] }> {\n\t\tconst qs = new URLSearchParams();\n\t\tif (params?.status) qs.set(\"status\", params.status);\n\t\tif (params?.limit !== undefined) qs.set(\"limit\", String(params.limit));\n\t\tconst query = qs.toString();\n\t\treturn this.request(\n\t\t\t\"GET\",\n\t\t\t`/api/workflows/${name}/runs${query ? `?${query}` : \"\"}`,\n\t\t);\n\t}\n\n\tasync getRun(runId: string): Promise<WorkflowRun> {\n\t\treturn this.request(\"GET\", `/api/workflows/runs/${runId}`);\n\t}\n}\n"
|
|
8
8
|
],
|
|
9
|
-
"mappings": ";AAeO,MAAM,iBAAiB,MAAM;AAAA,EAG3B;AAAA,
|
|
10
|
-
"debugId": "
|
|
9
|
+
"mappings": "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",
|
|
10
|
+
"debugId": "653D77D9FE60E3F464756E2164756E21",
|
|
11
11
|
"names": []
|
|
12
12
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@secondlayer/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -9,10 +9,6 @@
|
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"import": "./dist/index.js"
|
|
11
11
|
},
|
|
12
|
-
"./streams": {
|
|
13
|
-
"types": "./dist/streams/index.d.ts",
|
|
14
|
-
"import": "./dist/streams/index.js"
|
|
15
|
-
},
|
|
16
12
|
"./subgraphs": {
|
|
17
13
|
"types": "./dist/subgraphs/index.d.ts",
|
|
18
14
|
"import": "./dist/subgraphs/index.js"
|
|
@@ -37,9 +33,9 @@
|
|
|
37
33
|
"prepublishOnly": "bun run build"
|
|
38
34
|
},
|
|
39
35
|
"dependencies": {
|
|
40
|
-
"@secondlayer/shared": "^0.
|
|
41
|
-
"@secondlayer/subgraphs": "^0.11.
|
|
42
|
-
"@secondlayer/workflows": "^0.0
|
|
36
|
+
"@secondlayer/shared": "^1.0.0",
|
|
37
|
+
"@secondlayer/subgraphs": "^0.11.6",
|
|
38
|
+
"@secondlayer/workflows": "^1.0.0"
|
|
43
39
|
},
|
|
44
40
|
"devDependencies": {
|
|
45
41
|
"@types/bun": "latest",
|
package/dist/streams/index.d.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { BulkPauseResponse, BulkResumeResponse, CreateStream, CreateStreamResponse, ListStreamsResponse, StreamResponse, UpdateStream } from "@secondlayer/shared/schemas";
|
|
2
|
-
interface SecondLayerOptions {
|
|
3
|
-
/** Base URL of the Secondlayer API (trailing slashes are stripped). */
|
|
4
|
-
baseUrl: string;
|
|
5
|
-
/** Bearer token for authenticated requests. */
|
|
6
|
-
apiKey?: string;
|
|
7
|
-
}
|
|
8
|
-
declare abstract class BaseClient {
|
|
9
|
-
protected baseUrl: string;
|
|
10
|
-
protected apiKey?: string;
|
|
11
|
-
constructor(options?: Partial<SecondLayerOptions>);
|
|
12
|
-
static authHeaders(apiKey?: string): Record<string, string>;
|
|
13
|
-
protected request<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
14
|
-
}
|
|
15
|
-
interface DeliverySummary {
|
|
16
|
-
id: string;
|
|
17
|
-
blockHeight: number;
|
|
18
|
-
status: string;
|
|
19
|
-
statusCode: number | null;
|
|
20
|
-
responseTimeMs: number | null;
|
|
21
|
-
attempts: number;
|
|
22
|
-
error: string | null;
|
|
23
|
-
createdAt: string;
|
|
24
|
-
}
|
|
25
|
-
interface DeliveryDetail extends DeliverySummary {
|
|
26
|
-
payload: unknown;
|
|
27
|
-
}
|
|
28
|
-
interface DeliveriesResponse {
|
|
29
|
-
deliveries: DeliverySummary[];
|
|
30
|
-
}
|
|
31
|
-
declare class Streams extends BaseClient {
|
|
32
|
-
private requestWithStreamId;
|
|
33
|
-
resolveStreamId(partialId: string): Promise<string>;
|
|
34
|
-
create(data: CreateStream): Promise<CreateStreamResponse>;
|
|
35
|
-
update(id: string, data: UpdateStream): Promise<StreamResponse>;
|
|
36
|
-
updateByName(name: string, data: CreateStream): Promise<StreamResponse>;
|
|
37
|
-
list(params?: {
|
|
38
|
-
status?: string
|
|
39
|
-
}): Promise<ListStreamsResponse>;
|
|
40
|
-
get(id: string): Promise<StreamResponse>;
|
|
41
|
-
delete(id: string): Promise<void>;
|
|
42
|
-
enable(id: string): Promise<StreamResponse>;
|
|
43
|
-
disable(id: string): Promise<StreamResponse>;
|
|
44
|
-
rotateSecret(id: string): Promise<{
|
|
45
|
-
secret: string
|
|
46
|
-
}>;
|
|
47
|
-
/** List recent deliveries for a stream. */
|
|
48
|
-
listDeliveries(id: string, params?: {
|
|
49
|
-
limit?: number
|
|
50
|
-
status?: string
|
|
51
|
-
}): Promise<DeliveriesResponse>;
|
|
52
|
-
/** Get a single delivery with full payload. */
|
|
53
|
-
getDelivery(streamId: string, deliveryId: string): Promise<DeliveryDetail>;
|
|
54
|
-
pauseAll(): Promise<BulkPauseResponse>;
|
|
55
|
-
resumeAll(): Promise<BulkResumeResponse>;
|
|
56
|
-
}
|
|
57
|
-
export { Streams, SecondLayerOptions };
|
package/dist/streams/index.js
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
// src/errors.ts
|
|
2
|
-
class ApiError extends Error {
|
|
3
|
-
status;
|
|
4
|
-
constructor(status, message) {
|
|
5
|
-
super(message);
|
|
6
|
-
this.status = status;
|
|
7
|
-
this.name = "ApiError";
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
// src/base.ts
|
|
12
|
-
var DEFAULT_BASE_URL = "https://api.secondlayer.tools";
|
|
13
|
-
|
|
14
|
-
class BaseClient {
|
|
15
|
-
baseUrl;
|
|
16
|
-
apiKey;
|
|
17
|
-
constructor(options = {}) {
|
|
18
|
-
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
19
|
-
this.apiKey = options.apiKey;
|
|
20
|
-
}
|
|
21
|
-
static authHeaders(apiKey) {
|
|
22
|
-
const headers = {
|
|
23
|
-
"Content-Type": "application/json"
|
|
24
|
-
};
|
|
25
|
-
if (apiKey) {
|
|
26
|
-
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
27
|
-
}
|
|
28
|
-
return headers;
|
|
29
|
-
}
|
|
30
|
-
async request(method, path, body) {
|
|
31
|
-
const url = `${this.baseUrl}${path}`;
|
|
32
|
-
const headers = BaseClient.authHeaders(this.apiKey);
|
|
33
|
-
let response;
|
|
34
|
-
try {
|
|
35
|
-
response = await fetch(url, {
|
|
36
|
-
method,
|
|
37
|
-
headers,
|
|
38
|
-
body: body ? JSON.stringify(body) : undefined
|
|
39
|
-
});
|
|
40
|
-
} catch {
|
|
41
|
-
throw new ApiError(0, `Cannot reach API at ${this.baseUrl}. Check your connection or try again.`);
|
|
42
|
-
}
|
|
43
|
-
if (!response.ok) {
|
|
44
|
-
if (response.status === 401) {
|
|
45
|
-
throw new ApiError(401, "API key invalid or expired.");
|
|
46
|
-
}
|
|
47
|
-
if (response.status === 429) {
|
|
48
|
-
const retryAfter = response.headers.get("Retry-After");
|
|
49
|
-
const msg = retryAfter ? `Rate limited. Wait ${retryAfter} seconds.` : "Rate limited. Try again later.";
|
|
50
|
-
throw new ApiError(429, msg);
|
|
51
|
-
}
|
|
52
|
-
if (response.status >= 500) {
|
|
53
|
-
throw new ApiError(response.status, `Server error. Try again or check status at ${this.baseUrl}/health`);
|
|
54
|
-
}
|
|
55
|
-
const errorBody = await response.text();
|
|
56
|
-
let message = `HTTP ${response.status}`;
|
|
57
|
-
try {
|
|
58
|
-
const json = JSON.parse(errorBody);
|
|
59
|
-
const err = json.error ?? json.message;
|
|
60
|
-
if (typeof err === "string") {
|
|
61
|
-
message = err;
|
|
62
|
-
} else if (err && typeof err === "object") {
|
|
63
|
-
message = JSON.stringify(err);
|
|
64
|
-
}
|
|
65
|
-
} catch {
|
|
66
|
-
if (errorBody)
|
|
67
|
-
message = errorBody;
|
|
68
|
-
}
|
|
69
|
-
throw new ApiError(response.status, message);
|
|
70
|
-
}
|
|
71
|
-
if (response.status === 204) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
return response.json();
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// src/streams/client.ts
|
|
79
|
-
class Streams extends BaseClient {
|
|
80
|
-
async requestWithStreamId(method, pathTemplate, id, body) {
|
|
81
|
-
const fullId = await this.resolveStreamId(id);
|
|
82
|
-
return this.request(method, pathTemplate(fullId), body);
|
|
83
|
-
}
|
|
84
|
-
async resolveStreamId(partialId) {
|
|
85
|
-
if (partialId.length === 36 && partialId.includes("-")) {
|
|
86
|
-
return partialId;
|
|
87
|
-
}
|
|
88
|
-
const { streams } = await this.list();
|
|
89
|
-
const matches = streams.filter((s) => s.id.startsWith(partialId));
|
|
90
|
-
if (matches.length === 0) {
|
|
91
|
-
throw new ApiError(404, `No stream found matching "${partialId}"`);
|
|
92
|
-
}
|
|
93
|
-
if (matches.length > 1) {
|
|
94
|
-
throw new ApiError(400, `Multiple streams match "${partialId}": ${matches.map((s) => s.id.slice(0, 8)).join(", ")}`);
|
|
95
|
-
}
|
|
96
|
-
return matches[0].id;
|
|
97
|
-
}
|
|
98
|
-
async create(data) {
|
|
99
|
-
return this.request("POST", "/api/streams", data);
|
|
100
|
-
}
|
|
101
|
-
async update(id, data) {
|
|
102
|
-
return this.requestWithStreamId("PATCH", (id2) => `/api/streams/${id2}`, id, data);
|
|
103
|
-
}
|
|
104
|
-
async updateByName(name, data) {
|
|
105
|
-
const { streams } = await this.list();
|
|
106
|
-
const existing = streams.find((s) => s.name === name);
|
|
107
|
-
if (!existing) {
|
|
108
|
-
throw new ApiError(404, `Stream with name "${name}" not found`);
|
|
109
|
-
}
|
|
110
|
-
return this.update(existing.id, data);
|
|
111
|
-
}
|
|
112
|
-
async list(params) {
|
|
113
|
-
const searchParams = new URLSearchParams;
|
|
114
|
-
if (params?.status)
|
|
115
|
-
searchParams.set("status", params.status);
|
|
116
|
-
const query = searchParams.toString();
|
|
117
|
-
const path = query ? `/api/streams?${query}` : "/api/streams";
|
|
118
|
-
return this.request("GET", path);
|
|
119
|
-
}
|
|
120
|
-
async get(id) {
|
|
121
|
-
return this.requestWithStreamId("GET", (id2) => `/api/streams/${id2}`, id);
|
|
122
|
-
}
|
|
123
|
-
async delete(id) {
|
|
124
|
-
return this.requestWithStreamId("DELETE", (id2) => `/api/streams/${id2}`, id);
|
|
125
|
-
}
|
|
126
|
-
async enable(id) {
|
|
127
|
-
return this.requestWithStreamId("POST", (id2) => `/api/streams/${id2}/enable`, id);
|
|
128
|
-
}
|
|
129
|
-
async disable(id) {
|
|
130
|
-
return this.requestWithStreamId("POST", (id2) => `/api/streams/${id2}/disable`, id);
|
|
131
|
-
}
|
|
132
|
-
async rotateSecret(id) {
|
|
133
|
-
return this.requestWithStreamId("POST", (id2) => `/api/streams/${id2}/rotate-secret`, id);
|
|
134
|
-
}
|
|
135
|
-
async listDeliveries(id, params) {
|
|
136
|
-
const qs = new URLSearchParams;
|
|
137
|
-
if (params?.limit !== undefined)
|
|
138
|
-
qs.set("limit", String(params.limit));
|
|
139
|
-
if (params?.status)
|
|
140
|
-
qs.set("status", params.status);
|
|
141
|
-
const query = qs.toString();
|
|
142
|
-
return this.requestWithStreamId("GET", (id2) => `/api/streams/${id2}/deliveries${query ? `?${query}` : ""}`, id);
|
|
143
|
-
}
|
|
144
|
-
async getDelivery(streamId, deliveryId) {
|
|
145
|
-
const fullId = await this.resolveStreamId(streamId);
|
|
146
|
-
return this.request("GET", `/api/streams/${fullId}/deliveries/${deliveryId}`);
|
|
147
|
-
}
|
|
148
|
-
async pauseAll() {
|
|
149
|
-
return this.request("POST", "/api/streams/pause");
|
|
150
|
-
}
|
|
151
|
-
async resumeAll() {
|
|
152
|
-
return this.request("POST", "/api/streams/resume");
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
export {
|
|
156
|
-
Streams
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
//# debugId=C6255FAFEE54D50164756E2164756E21
|
|
160
|
-
//# sourceMappingURL=index.js.map
|