@secondlayer/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/dist/client.d.ts +48 -0
- package/dist/client.js +181 -0
- package/dist/client.js.map +11 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.js +15 -0
- package/dist/errors.js.map +10 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.js +182 -0
- package/dist/index.js.map +11 -0
- package/package.json +41 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { CreateStream, UpdateStream, StreamResponse, CreateStreamResponse, ListStreamsResponse, BulkPauseResponse, BulkResumeResponse, ViewSummary, ViewDetail, ViewQueryParams, ReindexResponse } from "@secondlayer/shared/schemas";
|
|
2
|
+
import { DeployViewRequest, DeployViewResponse } from "@secondlayer/shared/schemas/views";
|
|
3
|
+
import { QueueStats } from "@secondlayer/shared/types";
|
|
4
|
+
interface StreamsClientOptions {
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
}
|
|
8
|
+
declare class StreamsClient {
|
|
9
|
+
private baseUrl;
|
|
10
|
+
private apiKey?;
|
|
11
|
+
constructor(options: StreamsClientOptions);
|
|
12
|
+
static authHeaders(apiKey?: string): Record<string, string>;
|
|
13
|
+
private request;
|
|
14
|
+
resolveStreamId(partialId: string): Promise<string>;
|
|
15
|
+
createStream(data: CreateStream): Promise<CreateStreamResponse>;
|
|
16
|
+
updateStream(id: string, data: UpdateStream): Promise<StreamResponse>;
|
|
17
|
+
updateStreamByName(name: string, data: CreateStream): Promise<StreamResponse>;
|
|
18
|
+
listStreams(params?: {
|
|
19
|
+
status?: string
|
|
20
|
+
}): Promise<ListStreamsResponse>;
|
|
21
|
+
getStream(id: string): Promise<StreamResponse>;
|
|
22
|
+
deleteStream(id: string): Promise<void>;
|
|
23
|
+
enableStream(id: string): Promise<StreamResponse>;
|
|
24
|
+
disableStream(id: string): Promise<StreamResponse>;
|
|
25
|
+
rotateSecret(id: string): Promise<{
|
|
26
|
+
secret: string
|
|
27
|
+
}>;
|
|
28
|
+
pauseAll(): Promise<BulkPauseResponse>;
|
|
29
|
+
resumeAll(): Promise<BulkResumeResponse>;
|
|
30
|
+
getQueueStats(): Promise<QueueStats>;
|
|
31
|
+
listViews(): Promise<{
|
|
32
|
+
data: ViewSummary[]
|
|
33
|
+
}>;
|
|
34
|
+
getView(name: string): Promise<ViewDetail>;
|
|
35
|
+
reindexView(name: string, options?: {
|
|
36
|
+
fromBlock?: number
|
|
37
|
+
toBlock?: number
|
|
38
|
+
}): Promise<ReindexResponse>;
|
|
39
|
+
deleteView(name: string): Promise<{
|
|
40
|
+
message: string
|
|
41
|
+
}>;
|
|
42
|
+
deployView(data: DeployViewRequest): Promise<DeployViewResponse>;
|
|
43
|
+
queryTable(name: string, table: string, params?: ViewQueryParams): Promise<unknown[]>;
|
|
44
|
+
queryTableCount(name: string, table: string, params?: ViewQueryParams): Promise<{
|
|
45
|
+
count: number
|
|
46
|
+
}>;
|
|
47
|
+
}
|
|
48
|
+
export { StreamsClientOptions, StreamsClient };
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
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/client.ts
|
|
12
|
+
class StreamsClient {
|
|
13
|
+
baseUrl;
|
|
14
|
+
apiKey;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
17
|
+
this.apiKey = options.apiKey;
|
|
18
|
+
}
|
|
19
|
+
static authHeaders(apiKey) {
|
|
20
|
+
const headers = { "Content-Type": "application/json" };
|
|
21
|
+
if (apiKey) {
|
|
22
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
23
|
+
}
|
|
24
|
+
return headers;
|
|
25
|
+
}
|
|
26
|
+
async request(method, path, body) {
|
|
27
|
+
const url = `${this.baseUrl}${path}`;
|
|
28
|
+
const headers = StreamsClient.authHeaders(this.apiKey);
|
|
29
|
+
let response;
|
|
30
|
+
try {
|
|
31
|
+
response = await fetch(url, {
|
|
32
|
+
method,
|
|
33
|
+
headers,
|
|
34
|
+
body: body ? JSON.stringify(body) : undefined
|
|
35
|
+
});
|
|
36
|
+
} catch {
|
|
37
|
+
throw new ApiError(0, `Cannot reach API at ${this.baseUrl}. Check your connection or try again.`);
|
|
38
|
+
}
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
if (response.status === 401) {
|
|
41
|
+
throw new ApiError(401, "API key invalid or expired.");
|
|
42
|
+
}
|
|
43
|
+
if (response.status === 429) {
|
|
44
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
45
|
+
const msg = retryAfter ? `Rate limited. Wait ${retryAfter} seconds.` : "Rate limited. Try again later.";
|
|
46
|
+
throw new ApiError(429, msg);
|
|
47
|
+
}
|
|
48
|
+
if (response.status >= 500) {
|
|
49
|
+
throw new ApiError(response.status, `Server error. Try again or check status at ${this.baseUrl}/health`);
|
|
50
|
+
}
|
|
51
|
+
const errorBody = await response.text();
|
|
52
|
+
let message = `HTTP ${response.status}`;
|
|
53
|
+
try {
|
|
54
|
+
const json = JSON.parse(errorBody);
|
|
55
|
+
message = json.error || json.message || message;
|
|
56
|
+
} catch {
|
|
57
|
+
if (errorBody)
|
|
58
|
+
message = errorBody;
|
|
59
|
+
}
|
|
60
|
+
throw new ApiError(response.status, message);
|
|
61
|
+
}
|
|
62
|
+
if (response.status === 204) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
return response.json();
|
|
66
|
+
}
|
|
67
|
+
async resolveStreamId(partialId) {
|
|
68
|
+
if (partialId.length === 36 && partialId.includes("-")) {
|
|
69
|
+
return partialId;
|
|
70
|
+
}
|
|
71
|
+
const { streams } = await this.listStreams();
|
|
72
|
+
const matches = streams.filter((s) => s.id.startsWith(partialId));
|
|
73
|
+
if (matches.length === 0) {
|
|
74
|
+
throw new ApiError(404, `No stream found matching "${partialId}"`);
|
|
75
|
+
}
|
|
76
|
+
if (matches.length > 1) {
|
|
77
|
+
throw new ApiError(400, `Multiple streams match "${partialId}": ${matches.map((s) => s.id.slice(0, 8)).join(", ")}`);
|
|
78
|
+
}
|
|
79
|
+
return matches[0].id;
|
|
80
|
+
}
|
|
81
|
+
async createStream(data) {
|
|
82
|
+
return this.request("POST", "/api/streams", data);
|
|
83
|
+
}
|
|
84
|
+
async updateStream(id, data) {
|
|
85
|
+
const fullId = await this.resolveStreamId(id);
|
|
86
|
+
return this.request("PATCH", `/api/streams/${fullId}`, data);
|
|
87
|
+
}
|
|
88
|
+
async updateStreamByName(name, data) {
|
|
89
|
+
const { streams } = await this.listStreams();
|
|
90
|
+
const existing = streams.find((s) => s.name === name);
|
|
91
|
+
if (!existing) {
|
|
92
|
+
throw new ApiError(404, `Stream with name "${name}" not found`);
|
|
93
|
+
}
|
|
94
|
+
return this.updateStream(existing.id, data);
|
|
95
|
+
}
|
|
96
|
+
async listStreams(params) {
|
|
97
|
+
const searchParams = new URLSearchParams;
|
|
98
|
+
if (params?.status)
|
|
99
|
+
searchParams.set("status", params.status);
|
|
100
|
+
const query = searchParams.toString();
|
|
101
|
+
const path = query ? `/api/streams?${query}` : "/api/streams";
|
|
102
|
+
return this.request("GET", path);
|
|
103
|
+
}
|
|
104
|
+
async getStream(id) {
|
|
105
|
+
const fullId = await this.resolveStreamId(id);
|
|
106
|
+
return this.request("GET", `/api/streams/${fullId}`);
|
|
107
|
+
}
|
|
108
|
+
async deleteStream(id) {
|
|
109
|
+
const fullId = await this.resolveStreamId(id);
|
|
110
|
+
return this.request("DELETE", `/api/streams/${fullId}`);
|
|
111
|
+
}
|
|
112
|
+
async enableStream(id) {
|
|
113
|
+
const fullId = await this.resolveStreamId(id);
|
|
114
|
+
return this.request("POST", `/api/streams/${fullId}/enable`);
|
|
115
|
+
}
|
|
116
|
+
async disableStream(id) {
|
|
117
|
+
const fullId = await this.resolveStreamId(id);
|
|
118
|
+
return this.request("POST", `/api/streams/${fullId}/disable`);
|
|
119
|
+
}
|
|
120
|
+
async rotateSecret(id) {
|
|
121
|
+
const fullId = await this.resolveStreamId(id);
|
|
122
|
+
return this.request("POST", `/api/streams/${fullId}/rotate-secret`);
|
|
123
|
+
}
|
|
124
|
+
async pauseAll() {
|
|
125
|
+
return this.request("POST", "/api/streams/pause");
|
|
126
|
+
}
|
|
127
|
+
async resumeAll() {
|
|
128
|
+
return this.request("POST", "/api/streams/resume");
|
|
129
|
+
}
|
|
130
|
+
async getQueueStats() {
|
|
131
|
+
const status = await this.request("GET", "/status");
|
|
132
|
+
return status.queue;
|
|
133
|
+
}
|
|
134
|
+
async listViews() {
|
|
135
|
+
return this.request("GET", "/api/views");
|
|
136
|
+
}
|
|
137
|
+
async getView(name) {
|
|
138
|
+
return this.request("GET", `/api/views/${name}`);
|
|
139
|
+
}
|
|
140
|
+
async reindexView(name, options) {
|
|
141
|
+
return this.request("POST", `/api/views/${name}/reindex`, options);
|
|
142
|
+
}
|
|
143
|
+
async deleteView(name) {
|
|
144
|
+
return this.request("DELETE", `/api/views/${name}`);
|
|
145
|
+
}
|
|
146
|
+
async deployView(data) {
|
|
147
|
+
return this.request("POST", "/api/views", data);
|
|
148
|
+
}
|
|
149
|
+
async queryTable(name, table, params = {}) {
|
|
150
|
+
return this.request("GET", `/api/views/${name}/${table}${buildViewQueryString(params)}`);
|
|
151
|
+
}
|
|
152
|
+
async queryTableCount(name, table, params = {}) {
|
|
153
|
+
return this.request("GET", `/api/views/${name}/${table}/count${buildViewQueryString(params)}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function buildViewQueryString(params) {
|
|
157
|
+
const qs = new URLSearchParams;
|
|
158
|
+
if (params.sort)
|
|
159
|
+
qs.set("_sort", params.sort);
|
|
160
|
+
if (params.order)
|
|
161
|
+
qs.set("_order", params.order);
|
|
162
|
+
if (params.limit !== undefined)
|
|
163
|
+
qs.set("_limit", String(params.limit));
|
|
164
|
+
if (params.offset !== undefined)
|
|
165
|
+
qs.set("_offset", String(params.offset));
|
|
166
|
+
if (params.fields)
|
|
167
|
+
qs.set("_fields", params.fields);
|
|
168
|
+
if (params.filters) {
|
|
169
|
+
for (const [key, value] of Object.entries(params.filters)) {
|
|
170
|
+
qs.set(key, value);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const str = qs.toString();
|
|
174
|
+
return str ? `?${str}` : "";
|
|
175
|
+
}
|
|
176
|
+
export {
|
|
177
|
+
StreamsClient
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
//# debugId=2645915545BCF30D64756E2164756E21
|
|
181
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/errors.ts", "../src/client.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"export class ApiError extends Error {\n constructor(\n public status: number,\n message: string\n ) {\n super(message);\n this.name = \"ApiError\";\n }\n}\n",
|
|
6
|
+
"import type {\n CreateStream,\n UpdateStream,\n StreamResponse,\n CreateStreamResponse,\n ListStreamsResponse,\n BulkPauseResponse,\n BulkResumeResponse,\n ViewSummary,\n ViewDetail,\n ViewQueryParams,\n ReindexResponse,\n} from \"@secondlayer/shared/schemas\";\nimport type { DeployViewRequest, DeployViewResponse } from \"@secondlayer/shared/schemas/views\";\nimport type { QueueStats } from \"@secondlayer/shared/types\";\nimport { ApiError } from \"./errors.ts\";\n\nexport interface StreamsClientOptions {\n baseUrl: string;\n apiKey?: string;\n}\n\nexport class StreamsClient {\n private baseUrl: string;\n private apiKey?: string;\n\n constructor(options: StreamsClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.apiKey = options.apiKey;\n }\n\n // ── Helpers ───────────────────────────────────────────────────────────\n\n static authHeaders(apiKey?: string): Record<string, string> {\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (apiKey) {\n headers[\"Authorization\"] = `Bearer ${apiKey}`;\n }\n return headers;\n }\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const headers = StreamsClient.authHeaders(this.apiKey);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n } catch {\n throw new ApiError(0, `Cannot reach API at ${this.baseUrl}. Check your connection or try again.`);\n }\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new ApiError(401, \"API key invalid or expired.\");\n }\n\n if (response.status === 429) {\n const retryAfter = response.headers.get(\"Retry-After\");\n const msg = retryAfter\n ? `Rate limited. Wait ${retryAfter} seconds.`\n : \"Rate limited. Try again later.\";\n throw new ApiError(429, msg);\n }\n\n if (response.status >= 500) {\n throw new ApiError(response.status, `Server error. Try again or check status at ${this.baseUrl}/health`);\n }\n\n const errorBody = await response.text();\n let message = `HTTP ${response.status}`;\n try {\n const json = JSON.parse(errorBody);\n message = json.error || json.message || message;\n } catch {\n if (errorBody) message = errorBody;\n }\n throw new ApiError(response.status, message);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return response.json() as Promise<T>;\n }\n\n // ── Stream ID Resolution ──────────────────────────────────────────────\n\n async resolveStreamId(partialId: string): Promise<string> {\n if (partialId.length === 36 && partialId.includes(\"-\")) {\n return partialId;\n }\n\n const { streams } = await this.listStreams();\n const matches = streams.filter((s) => s.id.startsWith(partialId));\n\n if (matches.length === 0) {\n throw new ApiError(404, `No stream found matching \"${partialId}\"`);\n }\n if (matches.length > 1) {\n throw new ApiError(400, `Multiple streams match \"${partialId}\": ${matches.map((s) => s.id.slice(0, 8)).join(\", \")}`);\n }\n\n return matches[0]!.id;\n }\n\n // ── Streams ───────────────────────────────────────────────────────────\n\n async createStream(data: CreateStream): Promise<CreateStreamResponse> {\n return this.request<CreateStreamResponse>(\"POST\", \"/api/streams\", data);\n }\n\n async updateStream(id: string, data: UpdateStream): Promise<StreamResponse> {\n const fullId = await this.resolveStreamId(id);\n return this.request<StreamResponse>(\"PATCH\", `/api/streams/${fullId}`, data);\n }\n\n async updateStreamByName(name: string, data: CreateStream): Promise<StreamResponse> {\n const { streams } = await this.listStreams();\n const existing = streams.find((s) => s.name === name);\n if (!existing) {\n throw new ApiError(404, `Stream with name \"${name}\" not found`);\n }\n return this.updateStream(existing.id, data);\n }\n\n async listStreams(params?: { status?: string }): Promise<ListStreamsResponse> {\n const searchParams = new URLSearchParams();\n if (params?.status) searchParams.set(\"status\", params.status);\n const query = searchParams.toString();\n const path = query ? `/api/streams?${query}` : \"/api/streams\";\n return this.request<ListStreamsResponse>(\"GET\", path);\n }\n\n async getStream(id: string): Promise<StreamResponse> {\n const fullId = await this.resolveStreamId(id);\n return this.request<StreamResponse>(\"GET\", `/api/streams/${fullId}`);\n }\n\n async deleteStream(id: string): Promise<void> {\n const fullId = await this.resolveStreamId(id);\n return this.request<void>(\"DELETE\", `/api/streams/${fullId}`);\n }\n\n async enableStream(id: string): Promise<StreamResponse> {\n const fullId = await this.resolveStreamId(id);\n return this.request<StreamResponse>(\"POST\", `/api/streams/${fullId}/enable`);\n }\n\n async disableStream(id: string): Promise<StreamResponse> {\n const fullId = await this.resolveStreamId(id);\n return this.request<StreamResponse>(\"POST\", `/api/streams/${fullId}/disable`);\n }\n\n async rotateSecret(id: string): Promise<{ secret: string }> {\n const fullId = await this.resolveStreamId(id);\n return this.request<{ secret: string }>(\"POST\", `/api/streams/${fullId}/rotate-secret`);\n }\n\n async pauseAll(): Promise<BulkPauseResponse> {\n return this.request<BulkPauseResponse>(\"POST\", \"/api/streams/pause\");\n }\n\n async resumeAll(): Promise<BulkResumeResponse> {\n return this.request<BulkResumeResponse>(\"POST\", \"/api/streams/resume\");\n }\n\n // ── Queue ─────────────────────────────────────────────────────────────\n\n async getQueueStats(): Promise<QueueStats> {\n const status = await this.request<{ queue: QueueStats }>(\"GET\", \"/status\");\n return status.queue;\n }\n\n // ── Views ─────────────────────────────────────────────────────────────\n\n async listViews(): Promise<{ data: ViewSummary[] }> {\n return this.request<{ data: ViewSummary[] }>(\"GET\", \"/api/views\");\n }\n\n async getView(name: string): Promise<ViewDetail> {\n return this.request<ViewDetail>(\"GET\", `/api/views/${name}`);\n }\n\n async reindexView(name: string, options?: { fromBlock?: number; toBlock?: number }): Promise<ReindexResponse> {\n return this.request<ReindexResponse>(\"POST\", `/api/views/${name}/reindex`, options);\n }\n\n async deleteView(name: string): Promise<{ message: string }> {\n return this.request<{ message: string }>(\"DELETE\", `/api/views/${name}`);\n }\n\n async deployView(data: DeployViewRequest): Promise<DeployViewResponse> {\n return this.request<DeployViewResponse>(\"POST\", \"/api/views\", data);\n }\n\n async queryTable(name: string, table: string, params: ViewQueryParams = {}): Promise<unknown[]> {\n return this.request<unknown[]>(\"GET\", `/api/views/${name}/${table}${buildViewQueryString(params)}`);\n }\n\n async queryTableCount(name: string, table: string, params: ViewQueryParams = {}): Promise<{ count: number }> {\n return this.request<{ count: number }>(\"GET\", `/api/views/${name}/${table}/count${buildViewQueryString(params)}`);\n }\n}\n\nfunction buildViewQueryString(params: ViewQueryParams): string {\n const qs = new URLSearchParams();\n if (params.sort) qs.set(\"_sort\", params.sort);\n if (params.order) qs.set(\"_order\", params.order);\n if (params.limit !== undefined) qs.set(\"_limit\", String(params.limit));\n if (params.offset !== undefined) qs.set(\"_offset\", String(params.offset));\n if (params.fields) qs.set(\"_fields\", params.fields);\n if (params.filters) {\n for (const [key, value] of Object.entries(params.filters)) {\n qs.set(key, value);\n }\n }\n const str = qs.toString();\n return str ? `?${str}` : \"\";\n}\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "2645915545BCF30D64756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
package/dist/errors.d.ts
ADDED
package/dist/errors.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
export {
|
|
11
|
+
ApiError
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
//# debugId=7DCD1AB2054ADEE564756E2164756E21
|
|
15
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/errors.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"export class ApiError extends Error {\n constructor(\n public status: number,\n message: string\n ) {\n super(message);\n this.name = \"ApiError\";\n }\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";AAAO,MAAM,iBAAiB,MAAM;AAAA,EAEzB;AAAA,EADT,WAAW,CACF,QACP,SACA;AAAA,IACA,MAAM,OAAO;AAAA,IAHN;AAAA,IAIP,KAAK,OAAO;AAAA;AAEhB;",
|
|
8
|
+
"debugId": "7DCD1AB2054ADEE564756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { CreateStream, UpdateStream, StreamResponse, CreateStreamResponse, ListStreamsResponse, BulkPauseResponse, BulkResumeResponse, ViewSummary, ViewDetail, ViewQueryParams, ReindexResponse } from "@secondlayer/shared/schemas";
|
|
2
|
+
import { DeployViewRequest, DeployViewResponse } from "@secondlayer/shared/schemas/views";
|
|
3
|
+
import { QueueStats } from "@secondlayer/shared/types";
|
|
4
|
+
interface StreamsClientOptions {
|
|
5
|
+
baseUrl: string;
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
}
|
|
8
|
+
declare class StreamsClient {
|
|
9
|
+
private baseUrl;
|
|
10
|
+
private apiKey?;
|
|
11
|
+
constructor(options: StreamsClientOptions);
|
|
12
|
+
static authHeaders(apiKey?: string): Record<string, string>;
|
|
13
|
+
private request;
|
|
14
|
+
resolveStreamId(partialId: string): Promise<string>;
|
|
15
|
+
createStream(data: CreateStream): Promise<CreateStreamResponse>;
|
|
16
|
+
updateStream(id: string, data: UpdateStream): Promise<StreamResponse>;
|
|
17
|
+
updateStreamByName(name: string, data: CreateStream): Promise<StreamResponse>;
|
|
18
|
+
listStreams(params?: {
|
|
19
|
+
status?: string
|
|
20
|
+
}): Promise<ListStreamsResponse>;
|
|
21
|
+
getStream(id: string): Promise<StreamResponse>;
|
|
22
|
+
deleteStream(id: string): Promise<void>;
|
|
23
|
+
enableStream(id: string): Promise<StreamResponse>;
|
|
24
|
+
disableStream(id: string): Promise<StreamResponse>;
|
|
25
|
+
rotateSecret(id: string): Promise<{
|
|
26
|
+
secret: string
|
|
27
|
+
}>;
|
|
28
|
+
pauseAll(): Promise<BulkPauseResponse>;
|
|
29
|
+
resumeAll(): Promise<BulkResumeResponse>;
|
|
30
|
+
getQueueStats(): Promise<QueueStats>;
|
|
31
|
+
listViews(): Promise<{
|
|
32
|
+
data: ViewSummary[]
|
|
33
|
+
}>;
|
|
34
|
+
getView(name: string): Promise<ViewDetail>;
|
|
35
|
+
reindexView(name: string, options?: {
|
|
36
|
+
fromBlock?: number
|
|
37
|
+
toBlock?: number
|
|
38
|
+
}): Promise<ReindexResponse>;
|
|
39
|
+
deleteView(name: string): Promise<{
|
|
40
|
+
message: string
|
|
41
|
+
}>;
|
|
42
|
+
deployView(data: DeployViewRequest): Promise<DeployViewResponse>;
|
|
43
|
+
queryTable(name: string, table: string, params?: ViewQueryParams): Promise<unknown[]>;
|
|
44
|
+
queryTableCount(name: string, table: string, params?: ViewQueryParams): Promise<{
|
|
45
|
+
count: number
|
|
46
|
+
}>;
|
|
47
|
+
}
|
|
48
|
+
declare class ApiError extends Error {
|
|
49
|
+
status: number;
|
|
50
|
+
constructor(status: number, message: string);
|
|
51
|
+
}
|
|
52
|
+
export { StreamsClientOptions, StreamsClient, ApiError };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
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/client.ts
|
|
12
|
+
class StreamsClient {
|
|
13
|
+
baseUrl;
|
|
14
|
+
apiKey;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
17
|
+
this.apiKey = options.apiKey;
|
|
18
|
+
}
|
|
19
|
+
static authHeaders(apiKey) {
|
|
20
|
+
const headers = { "Content-Type": "application/json" };
|
|
21
|
+
if (apiKey) {
|
|
22
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
23
|
+
}
|
|
24
|
+
return headers;
|
|
25
|
+
}
|
|
26
|
+
async request(method, path, body) {
|
|
27
|
+
const url = `${this.baseUrl}${path}`;
|
|
28
|
+
const headers = StreamsClient.authHeaders(this.apiKey);
|
|
29
|
+
let response;
|
|
30
|
+
try {
|
|
31
|
+
response = await fetch(url, {
|
|
32
|
+
method,
|
|
33
|
+
headers,
|
|
34
|
+
body: body ? JSON.stringify(body) : undefined
|
|
35
|
+
});
|
|
36
|
+
} catch {
|
|
37
|
+
throw new ApiError(0, `Cannot reach API at ${this.baseUrl}. Check your connection or try again.`);
|
|
38
|
+
}
|
|
39
|
+
if (!response.ok) {
|
|
40
|
+
if (response.status === 401) {
|
|
41
|
+
throw new ApiError(401, "API key invalid or expired.");
|
|
42
|
+
}
|
|
43
|
+
if (response.status === 429) {
|
|
44
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
45
|
+
const msg = retryAfter ? `Rate limited. Wait ${retryAfter} seconds.` : "Rate limited. Try again later.";
|
|
46
|
+
throw new ApiError(429, msg);
|
|
47
|
+
}
|
|
48
|
+
if (response.status >= 500) {
|
|
49
|
+
throw new ApiError(response.status, `Server error. Try again or check status at ${this.baseUrl}/health`);
|
|
50
|
+
}
|
|
51
|
+
const errorBody = await response.text();
|
|
52
|
+
let message = `HTTP ${response.status}`;
|
|
53
|
+
try {
|
|
54
|
+
const json = JSON.parse(errorBody);
|
|
55
|
+
message = json.error || json.message || message;
|
|
56
|
+
} catch {
|
|
57
|
+
if (errorBody)
|
|
58
|
+
message = errorBody;
|
|
59
|
+
}
|
|
60
|
+
throw new ApiError(response.status, message);
|
|
61
|
+
}
|
|
62
|
+
if (response.status === 204) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
return response.json();
|
|
66
|
+
}
|
|
67
|
+
async resolveStreamId(partialId) {
|
|
68
|
+
if (partialId.length === 36 && partialId.includes("-")) {
|
|
69
|
+
return partialId;
|
|
70
|
+
}
|
|
71
|
+
const { streams } = await this.listStreams();
|
|
72
|
+
const matches = streams.filter((s) => s.id.startsWith(partialId));
|
|
73
|
+
if (matches.length === 0) {
|
|
74
|
+
throw new ApiError(404, `No stream found matching "${partialId}"`);
|
|
75
|
+
}
|
|
76
|
+
if (matches.length > 1) {
|
|
77
|
+
throw new ApiError(400, `Multiple streams match "${partialId}": ${matches.map((s) => s.id.slice(0, 8)).join(", ")}`);
|
|
78
|
+
}
|
|
79
|
+
return matches[0].id;
|
|
80
|
+
}
|
|
81
|
+
async createStream(data) {
|
|
82
|
+
return this.request("POST", "/api/streams", data);
|
|
83
|
+
}
|
|
84
|
+
async updateStream(id, data) {
|
|
85
|
+
const fullId = await this.resolveStreamId(id);
|
|
86
|
+
return this.request("PATCH", `/api/streams/${fullId}`, data);
|
|
87
|
+
}
|
|
88
|
+
async updateStreamByName(name, data) {
|
|
89
|
+
const { streams } = await this.listStreams();
|
|
90
|
+
const existing = streams.find((s) => s.name === name);
|
|
91
|
+
if (!existing) {
|
|
92
|
+
throw new ApiError(404, `Stream with name "${name}" not found`);
|
|
93
|
+
}
|
|
94
|
+
return this.updateStream(existing.id, data);
|
|
95
|
+
}
|
|
96
|
+
async listStreams(params) {
|
|
97
|
+
const searchParams = new URLSearchParams;
|
|
98
|
+
if (params?.status)
|
|
99
|
+
searchParams.set("status", params.status);
|
|
100
|
+
const query = searchParams.toString();
|
|
101
|
+
const path = query ? `/api/streams?${query}` : "/api/streams";
|
|
102
|
+
return this.request("GET", path);
|
|
103
|
+
}
|
|
104
|
+
async getStream(id) {
|
|
105
|
+
const fullId = await this.resolveStreamId(id);
|
|
106
|
+
return this.request("GET", `/api/streams/${fullId}`);
|
|
107
|
+
}
|
|
108
|
+
async deleteStream(id) {
|
|
109
|
+
const fullId = await this.resolveStreamId(id);
|
|
110
|
+
return this.request("DELETE", `/api/streams/${fullId}`);
|
|
111
|
+
}
|
|
112
|
+
async enableStream(id) {
|
|
113
|
+
const fullId = await this.resolveStreamId(id);
|
|
114
|
+
return this.request("POST", `/api/streams/${fullId}/enable`);
|
|
115
|
+
}
|
|
116
|
+
async disableStream(id) {
|
|
117
|
+
const fullId = await this.resolveStreamId(id);
|
|
118
|
+
return this.request("POST", `/api/streams/${fullId}/disable`);
|
|
119
|
+
}
|
|
120
|
+
async rotateSecret(id) {
|
|
121
|
+
const fullId = await this.resolveStreamId(id);
|
|
122
|
+
return this.request("POST", `/api/streams/${fullId}/rotate-secret`);
|
|
123
|
+
}
|
|
124
|
+
async pauseAll() {
|
|
125
|
+
return this.request("POST", "/api/streams/pause");
|
|
126
|
+
}
|
|
127
|
+
async resumeAll() {
|
|
128
|
+
return this.request("POST", "/api/streams/resume");
|
|
129
|
+
}
|
|
130
|
+
async getQueueStats() {
|
|
131
|
+
const status = await this.request("GET", "/status");
|
|
132
|
+
return status.queue;
|
|
133
|
+
}
|
|
134
|
+
async listViews() {
|
|
135
|
+
return this.request("GET", "/api/views");
|
|
136
|
+
}
|
|
137
|
+
async getView(name) {
|
|
138
|
+
return this.request("GET", `/api/views/${name}`);
|
|
139
|
+
}
|
|
140
|
+
async reindexView(name, options) {
|
|
141
|
+
return this.request("POST", `/api/views/${name}/reindex`, options);
|
|
142
|
+
}
|
|
143
|
+
async deleteView(name) {
|
|
144
|
+
return this.request("DELETE", `/api/views/${name}`);
|
|
145
|
+
}
|
|
146
|
+
async deployView(data) {
|
|
147
|
+
return this.request("POST", "/api/views", data);
|
|
148
|
+
}
|
|
149
|
+
async queryTable(name, table, params = {}) {
|
|
150
|
+
return this.request("GET", `/api/views/${name}/${table}${buildViewQueryString(params)}`);
|
|
151
|
+
}
|
|
152
|
+
async queryTableCount(name, table, params = {}) {
|
|
153
|
+
return this.request("GET", `/api/views/${name}/${table}/count${buildViewQueryString(params)}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function buildViewQueryString(params) {
|
|
157
|
+
const qs = new URLSearchParams;
|
|
158
|
+
if (params.sort)
|
|
159
|
+
qs.set("_sort", params.sort);
|
|
160
|
+
if (params.order)
|
|
161
|
+
qs.set("_order", params.order);
|
|
162
|
+
if (params.limit !== undefined)
|
|
163
|
+
qs.set("_limit", String(params.limit));
|
|
164
|
+
if (params.offset !== undefined)
|
|
165
|
+
qs.set("_offset", String(params.offset));
|
|
166
|
+
if (params.fields)
|
|
167
|
+
qs.set("_fields", params.fields);
|
|
168
|
+
if (params.filters) {
|
|
169
|
+
for (const [key, value] of Object.entries(params.filters)) {
|
|
170
|
+
qs.set(key, value);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const str = qs.toString();
|
|
174
|
+
return str ? `?${str}` : "";
|
|
175
|
+
}
|
|
176
|
+
export {
|
|
177
|
+
StreamsClient,
|
|
178
|
+
ApiError
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
//# debugId=25305F1E09AEB0F664756E2164756E21
|
|
182
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/errors.ts", "../src/client.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"export class ApiError extends Error {\n constructor(\n public status: number,\n message: string\n ) {\n super(message);\n this.name = \"ApiError\";\n }\n}\n",
|
|
6
|
+
"import type {\n CreateStream,\n UpdateStream,\n StreamResponse,\n CreateStreamResponse,\n ListStreamsResponse,\n BulkPauseResponse,\n BulkResumeResponse,\n ViewSummary,\n ViewDetail,\n ViewQueryParams,\n ReindexResponse,\n} from \"@secondlayer/shared/schemas\";\nimport type { DeployViewRequest, DeployViewResponse } from \"@secondlayer/shared/schemas/views\";\nimport type { QueueStats } from \"@secondlayer/shared/types\";\nimport { ApiError } from \"./errors.ts\";\n\nexport interface StreamsClientOptions {\n baseUrl: string;\n apiKey?: string;\n}\n\nexport class StreamsClient {\n private baseUrl: string;\n private apiKey?: string;\n\n constructor(options: StreamsClientOptions) {\n this.baseUrl = options.baseUrl.replace(/\\/+$/, \"\");\n this.apiKey = options.apiKey;\n }\n\n // ── Helpers ───────────────────────────────────────────────────────────\n\n static authHeaders(apiKey?: string): Record<string, string> {\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (apiKey) {\n headers[\"Authorization\"] = `Bearer ${apiKey}`;\n }\n return headers;\n }\n\n private async request<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${this.baseUrl}${path}`;\n const headers = StreamsClient.authHeaders(this.apiKey);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n } catch {\n throw new ApiError(0, `Cannot reach API at ${this.baseUrl}. Check your connection or try again.`);\n }\n\n if (!response.ok) {\n if (response.status === 401) {\n throw new ApiError(401, \"API key invalid or expired.\");\n }\n\n if (response.status === 429) {\n const retryAfter = response.headers.get(\"Retry-After\");\n const msg = retryAfter\n ? `Rate limited. Wait ${retryAfter} seconds.`\n : \"Rate limited. Try again later.\";\n throw new ApiError(429, msg);\n }\n\n if (response.status >= 500) {\n throw new ApiError(response.status, `Server error. Try again or check status at ${this.baseUrl}/health`);\n }\n\n const errorBody = await response.text();\n let message = `HTTP ${response.status}`;\n try {\n const json = JSON.parse(errorBody);\n message = json.error || json.message || message;\n } catch {\n if (errorBody) message = errorBody;\n }\n throw new ApiError(response.status, message);\n }\n\n if (response.status === 204) {\n return undefined as T;\n }\n\n return response.json() as Promise<T>;\n }\n\n // ── Stream ID Resolution ──────────────────────────────────────────────\n\n async resolveStreamId(partialId: string): Promise<string> {\n if (partialId.length === 36 && partialId.includes(\"-\")) {\n return partialId;\n }\n\n const { streams } = await this.listStreams();\n const matches = streams.filter((s) => s.id.startsWith(partialId));\n\n if (matches.length === 0) {\n throw new ApiError(404, `No stream found matching \"${partialId}\"`);\n }\n if (matches.length > 1) {\n throw new ApiError(400, `Multiple streams match \"${partialId}\": ${matches.map((s) => s.id.slice(0, 8)).join(\", \")}`);\n }\n\n return matches[0]!.id;\n }\n\n // ── Streams ───────────────────────────────────────────────────────────\n\n async createStream(data: CreateStream): Promise<CreateStreamResponse> {\n return this.request<CreateStreamResponse>(\"POST\", \"/api/streams\", data);\n }\n\n async updateStream(id: string, data: UpdateStream): Promise<StreamResponse> {\n const fullId = await this.resolveStreamId(id);\n return this.request<StreamResponse>(\"PATCH\", `/api/streams/${fullId}`, data);\n }\n\n async updateStreamByName(name: string, data: CreateStream): Promise<StreamResponse> {\n const { streams } = await this.listStreams();\n const existing = streams.find((s) => s.name === name);\n if (!existing) {\n throw new ApiError(404, `Stream with name \"${name}\" not found`);\n }\n return this.updateStream(existing.id, data);\n }\n\n async listStreams(params?: { status?: string }): Promise<ListStreamsResponse> {\n const searchParams = new URLSearchParams();\n if (params?.status) searchParams.set(\"status\", params.status);\n const query = searchParams.toString();\n const path = query ? `/api/streams?${query}` : \"/api/streams\";\n return this.request<ListStreamsResponse>(\"GET\", path);\n }\n\n async getStream(id: string): Promise<StreamResponse> {\n const fullId = await this.resolveStreamId(id);\n return this.request<StreamResponse>(\"GET\", `/api/streams/${fullId}`);\n }\n\n async deleteStream(id: string): Promise<void> {\n const fullId = await this.resolveStreamId(id);\n return this.request<void>(\"DELETE\", `/api/streams/${fullId}`);\n }\n\n async enableStream(id: string): Promise<StreamResponse> {\n const fullId = await this.resolveStreamId(id);\n return this.request<StreamResponse>(\"POST\", `/api/streams/${fullId}/enable`);\n }\n\n async disableStream(id: string): Promise<StreamResponse> {\n const fullId = await this.resolveStreamId(id);\n return this.request<StreamResponse>(\"POST\", `/api/streams/${fullId}/disable`);\n }\n\n async rotateSecret(id: string): Promise<{ secret: string }> {\n const fullId = await this.resolveStreamId(id);\n return this.request<{ secret: string }>(\"POST\", `/api/streams/${fullId}/rotate-secret`);\n }\n\n async pauseAll(): Promise<BulkPauseResponse> {\n return this.request<BulkPauseResponse>(\"POST\", \"/api/streams/pause\");\n }\n\n async resumeAll(): Promise<BulkResumeResponse> {\n return this.request<BulkResumeResponse>(\"POST\", \"/api/streams/resume\");\n }\n\n // ── Queue ─────────────────────────────────────────────────────────────\n\n async getQueueStats(): Promise<QueueStats> {\n const status = await this.request<{ queue: QueueStats }>(\"GET\", \"/status\");\n return status.queue;\n }\n\n // ── Views ─────────────────────────────────────────────────────────────\n\n async listViews(): Promise<{ data: ViewSummary[] }> {\n return this.request<{ data: ViewSummary[] }>(\"GET\", \"/api/views\");\n }\n\n async getView(name: string): Promise<ViewDetail> {\n return this.request<ViewDetail>(\"GET\", `/api/views/${name}`);\n }\n\n async reindexView(name: string, options?: { fromBlock?: number; toBlock?: number }): Promise<ReindexResponse> {\n return this.request<ReindexResponse>(\"POST\", `/api/views/${name}/reindex`, options);\n }\n\n async deleteView(name: string): Promise<{ message: string }> {\n return this.request<{ message: string }>(\"DELETE\", `/api/views/${name}`);\n }\n\n async deployView(data: DeployViewRequest): Promise<DeployViewResponse> {\n return this.request<DeployViewResponse>(\"POST\", \"/api/views\", data);\n }\n\n async queryTable(name: string, table: string, params: ViewQueryParams = {}): Promise<unknown[]> {\n return this.request<unknown[]>(\"GET\", `/api/views/${name}/${table}${buildViewQueryString(params)}`);\n }\n\n async queryTableCount(name: string, table: string, params: ViewQueryParams = {}): Promise<{ count: number }> {\n return this.request<{ count: number }>(\"GET\", `/api/views/${name}/${table}/count${buildViewQueryString(params)}`);\n }\n}\n\nfunction buildViewQueryString(params: ViewQueryParams): string {\n const qs = new URLSearchParams();\n if (params.sort) qs.set(\"_sort\", params.sort);\n if (params.order) qs.set(\"_order\", params.order);\n if (params.limit !== undefined) qs.set(\"_limit\", String(params.limit));\n if (params.offset !== undefined) qs.set(\"_offset\", String(params.offset));\n if (params.fields) qs.set(\"_fields\", params.fields);\n if (params.filters) {\n for (const [key, value] of Object.entries(params.filters)) {\n qs.set(key, value);\n }\n }\n const str = qs.toString();\n return str ? `?${str}` : \"\";\n}\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "25305F1E09AEB0F664756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@secondlayer/sdk",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./client": {
|
|
13
|
+
"types": "./dist/client.d.ts",
|
|
14
|
+
"import": "./dist/client.js"
|
|
15
|
+
},
|
|
16
|
+
"./errors": {
|
|
17
|
+
"types": "./dist/errors.d.ts",
|
|
18
|
+
"import": "./dist/errors.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "bunup",
|
|
26
|
+
"dev": "bunup --watch",
|
|
27
|
+
"test": "bun test",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"prepublishOnly": "bun run build"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@secondlayer/shared": "^0.2.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/bun": "latest",
|
|
36
|
+
"typescript": "^5"
|
|
37
|
+
},
|
|
38
|
+
"publishConfig": {
|
|
39
|
+
"access": "public"
|
|
40
|
+
}
|
|
41
|
+
}
|