@secondlayer/sdk 3.5.1 → 3.5.3
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 +19 -1
- package/dist/index.d.ts +28 -2
- package/dist/index.js +87 -36
- package/dist/index.js.map +7 -7
- package/dist/streams/index.js +8 -1
- package/dist/streams/index.js.map +3 -3
- package/dist/subgraphs/index.d.ts +25 -1
- package/dist/subgraphs/index.js +80 -36
- package/dist/subgraphs/index.js.map +6 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -172,6 +172,17 @@ for await (const transfer of sl.index.ftTransfers.walk({
|
|
|
172
172
|
|
|
173
173
|
Deploy and query app-specific L3 tables.
|
|
174
174
|
|
|
175
|
+
Subgraphs and subscriptions live on per-tenant containers (`https://<slug>.api.secondlayer.tools`), not on the platform `api.secondlayer.tools`. The SDK transparently resolves your tenant URL on the first subgraph or subscription call by hitting `/api/tenants/me` with your API key, then caches the result. You don't need to know the URL — just pass your normal `apiKey`.
|
|
176
|
+
|
|
177
|
+
If you already know your tenant URL (OSS, staging, or a custom routing setup), skip the lookup with `tenantBaseUrl`:
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
const sl = new SecondLayer({
|
|
181
|
+
apiKey: "sk-sl_...",
|
|
182
|
+
tenantBaseUrl: "https://myslug.api.secondlayer.tools", // optional
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
175
186
|
```typescript
|
|
176
187
|
// List
|
|
177
188
|
const { data } = await sl.subgraphs.list();
|
|
@@ -243,7 +254,14 @@ try {
|
|
|
243
254
|
} catch (err) {
|
|
244
255
|
if (err instanceof ApiError) {
|
|
245
256
|
console.log(err.status); // 404
|
|
246
|
-
console.log(err.
|
|
257
|
+
console.log(err.code); // "NOT_FOUND" (from API's {error, code} envelope, if present)
|
|
258
|
+
console.log(err.message); // "Subgraph not found"
|
|
259
|
+
console.log(err.body); // full parsed envelope
|
|
247
260
|
}
|
|
248
261
|
}
|
|
249
262
|
```
|
|
263
|
+
|
|
264
|
+
Tenant-resolution failures surface as `ApiError` with distinctive codes:
|
|
265
|
+
|
|
266
|
+
- `code: "TENANT_SUSPENDED"` — your tenant is suspended (see `err.message` for the limit reason)
|
|
267
|
+
- `code: "NO_TENANT"` — your account has no provisioned tenant yet
|
package/dist/index.d.ts
CHANGED
|
@@ -4,10 +4,16 @@ import { SubgraphAgentSchema, SubgraphSpecOptions } from "@secondlayer/shared/su
|
|
|
4
4
|
import { InferSubgraphClient } from "@secondlayer/subgraphs";
|
|
5
5
|
type FetchLike = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
6
6
|
interface SecondLayerOptions {
|
|
7
|
-
/** Base URL of the Secondlayer API (trailing slashes are stripped). */
|
|
7
|
+
/** Base URL of the Secondlayer platform API (trailing slashes are stripped). */
|
|
8
8
|
baseUrl: string;
|
|
9
9
|
/** Bearer token for authenticated requests. */
|
|
10
10
|
apiKey?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Explicit tenant API base URL — bypass the auto-resolution that calls
|
|
13
|
+
* `/api/tenants/me` on first tenant-resource request. Use when you already
|
|
14
|
+
* know your tenant URL (OSS, staging, or any custom routing setup).
|
|
15
|
+
*/
|
|
16
|
+
tenantBaseUrl?: string;
|
|
11
17
|
/** Fetch implementation. Tests and edge runtimes can provide their own. */
|
|
12
18
|
fetchImpl?: FetchLike;
|
|
13
19
|
/** Deploy origin label sent as `x-sl-origin` (telemetry). Defaults to `cli`. */
|
|
@@ -17,10 +23,28 @@ declare abstract class BaseClient {
|
|
|
17
23
|
protected baseUrl: string;
|
|
18
24
|
protected apiKey?: string;
|
|
19
25
|
protected origin: "cli" | "mcp" | "session";
|
|
26
|
+
protected tenantBaseUrlOverride?: string;
|
|
27
|
+
private _tenantBaseUrlPromise;
|
|
20
28
|
constructor(options?: Partial<SecondLayerOptions>);
|
|
21
29
|
static authHeaders(apiKey?: string): Record<string, string>;
|
|
22
30
|
protected request<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
31
|
+
protected requestAt<T>(baseUrl: string, method: string, path: string, body?: unknown): Promise<T>;
|
|
23
32
|
protected requestText(method: string, path: string, body?: unknown): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Resolve and cache the tenant API base URL for tenant-resource calls
|
|
35
|
+
* (subgraphs, subscriptions). On the platform API, those routes are not
|
|
36
|
+
* mounted — they live on per-tenant containers at
|
|
37
|
+
* `https://<slug>.api.secondlayer.tools`. This method asks
|
|
38
|
+
* `/api/tenants/me` (against the platform baseUrl) for the apiUrl that
|
|
39
|
+
* belongs to the authenticated account.
|
|
40
|
+
*
|
|
41
|
+
* The result is cached on the client instance. Failures are NOT cached, so
|
|
42
|
+
* a flaky platform call doesn't permanently break the SDK.
|
|
43
|
+
*/
|
|
44
|
+
protected getTenantBaseUrl(): Promise<string>;
|
|
45
|
+
private resolveTenantBaseUrl;
|
|
46
|
+
protected requestAtTenant<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
47
|
+
protected requestTextAtTenant(method: string, path: string, body?: unknown): Promise<string>;
|
|
24
48
|
private fetchResponse;
|
|
25
49
|
}
|
|
26
50
|
interface SubgraphSource {
|
|
@@ -475,7 +499,9 @@ declare class ApiError extends Error {
|
|
|
475
499
|
status: number;
|
|
476
500
|
/** Raw response body (parsed JSON if possible) — preserved for callers that need error details. */
|
|
477
501
|
body?: unknown;
|
|
478
|
-
|
|
502
|
+
/** Stable machine-readable code from the API's `{error, code}` error envelope. */
|
|
503
|
+
code?: string;
|
|
504
|
+
constructor(status: number, message: string, body?: unknown, code?: string);
|
|
479
505
|
}
|
|
480
506
|
/**
|
|
481
507
|
* Thrown on optimistic-concurrency conflict when a deploy supplies an
|
package/dist/index.js
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
class ApiError extends Error {
|
|
3
3
|
status;
|
|
4
4
|
body;
|
|
5
|
-
|
|
5
|
+
code;
|
|
6
|
+
constructor(status, message, body, code) {
|
|
6
7
|
super(message);
|
|
7
8
|
this.status = status;
|
|
8
9
|
this.body = body;
|
|
10
|
+
this.code = code;
|
|
9
11
|
this.name = "ApiError";
|
|
10
12
|
}
|
|
11
13
|
}
|
|
@@ -28,10 +30,13 @@ class BaseClient {
|
|
|
28
30
|
baseUrl;
|
|
29
31
|
apiKey;
|
|
30
32
|
origin;
|
|
33
|
+
tenantBaseUrlOverride;
|
|
34
|
+
_tenantBaseUrlPromise = null;
|
|
31
35
|
constructor(options = {}) {
|
|
32
36
|
this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
33
37
|
this.apiKey = options.apiKey;
|
|
34
38
|
this.origin = options.origin ?? "cli";
|
|
39
|
+
this.tenantBaseUrlOverride = options.tenantBaseUrl?.replace(/\/+$/, "");
|
|
35
40
|
}
|
|
36
41
|
static authHeaders(apiKey) {
|
|
37
42
|
const headers = {
|
|
@@ -43,18 +48,53 @@ class BaseClient {
|
|
|
43
48
|
return headers;
|
|
44
49
|
}
|
|
45
50
|
async request(method, path, body) {
|
|
46
|
-
|
|
51
|
+
return this.requestAt(this.baseUrl, method, path, body);
|
|
52
|
+
}
|
|
53
|
+
async requestAt(baseUrl, method, path, body) {
|
|
54
|
+
const response = await this.fetchResponse(baseUrl, method, path, body);
|
|
47
55
|
if (response.status === 204) {
|
|
48
56
|
return;
|
|
49
57
|
}
|
|
50
58
|
return response.json();
|
|
51
59
|
}
|
|
52
60
|
async requestText(method, path, body) {
|
|
53
|
-
const response = await this.fetchResponse(method, path, body);
|
|
61
|
+
const response = await this.fetchResponse(this.baseUrl, method, path, body);
|
|
62
|
+
return response.text();
|
|
63
|
+
}
|
|
64
|
+
getTenantBaseUrl() {
|
|
65
|
+
if (this.tenantBaseUrlOverride) {
|
|
66
|
+
return Promise.resolve(this.tenantBaseUrlOverride);
|
|
67
|
+
}
|
|
68
|
+
if (!this._tenantBaseUrlPromise) {
|
|
69
|
+
this._tenantBaseUrlPromise = this.resolveTenantBaseUrl().catch((err) => {
|
|
70
|
+
this._tenantBaseUrlPromise = null;
|
|
71
|
+
throw err;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
return this._tenantBaseUrlPromise;
|
|
75
|
+
}
|
|
76
|
+
async resolveTenantBaseUrl() {
|
|
77
|
+
const body = await this.request("GET", "/api/tenants/me");
|
|
78
|
+
const tenant = body.tenant;
|
|
79
|
+
if (tenant.suspendedAt) {
|
|
80
|
+
throw new ApiError(403, `Tenant ${tenant.slug} is suspended${tenant.limitReason ? `: ${tenant.limitReason}` : ""}.`, body, "TENANT_SUSPENDED");
|
|
81
|
+
}
|
|
82
|
+
if (!tenant.apiUrl) {
|
|
83
|
+
throw new ApiError(404, "No tenant API URL available for this account. Provision a tenant at https://secondlayer.tools/platform.", body, "NO_TENANT");
|
|
84
|
+
}
|
|
85
|
+
return tenant.apiUrl.replace(/\/+$/, "");
|
|
86
|
+
}
|
|
87
|
+
async requestAtTenant(method, path, body) {
|
|
88
|
+
const tenantUrl = await this.getTenantBaseUrl();
|
|
89
|
+
return this.requestAt(tenantUrl, method, path, body);
|
|
90
|
+
}
|
|
91
|
+
async requestTextAtTenant(method, path, body) {
|
|
92
|
+
const tenantUrl = await this.getTenantBaseUrl();
|
|
93
|
+
const response = await this.fetchResponse(tenantUrl, method, path, body);
|
|
54
94
|
return response.text();
|
|
55
95
|
}
|
|
56
|
-
async fetchResponse(method, path, body) {
|
|
57
|
-
const url = `${
|
|
96
|
+
async fetchResponse(baseUrl, method, path, body) {
|
|
97
|
+
const url = `${baseUrl}${path}`;
|
|
58
98
|
const headers = BaseClient.authHeaders(this.apiKey);
|
|
59
99
|
headers["x-sl-origin"] = this.origin;
|
|
60
100
|
let response;
|
|
@@ -65,7 +105,7 @@ class BaseClient {
|
|
|
65
105
|
body: body ? JSON.stringify(body) : undefined
|
|
66
106
|
});
|
|
67
107
|
} catch {
|
|
68
|
-
throw new ApiError(0, `Cannot reach API at ${
|
|
108
|
+
throw new ApiError(0, `Cannot reach API at ${baseUrl}. Check your connection or try again.`);
|
|
69
109
|
}
|
|
70
110
|
if (!response.ok) {
|
|
71
111
|
if (response.status === 401) {
|
|
@@ -77,11 +117,12 @@ class BaseClient {
|
|
|
77
117
|
throw new ApiError(429, msg);
|
|
78
118
|
}
|
|
79
119
|
if (response.status >= 500) {
|
|
80
|
-
throw new ApiError(response.status, `Server error. Try again or check status at ${
|
|
120
|
+
throw new ApiError(response.status, `Server error. Try again or check status at ${baseUrl}/health`);
|
|
81
121
|
}
|
|
82
122
|
const errorBody = await response.text();
|
|
83
123
|
let message = `HTTP ${response.status}`;
|
|
84
124
|
let parsedBody = errorBody;
|
|
125
|
+
let code;
|
|
85
126
|
try {
|
|
86
127
|
const json = JSON.parse(errorBody);
|
|
87
128
|
parsedBody = json;
|
|
@@ -91,11 +132,14 @@ class BaseClient {
|
|
|
91
132
|
} else if (err && typeof err === "object") {
|
|
92
133
|
message = JSON.stringify(err);
|
|
93
134
|
}
|
|
135
|
+
if (typeof json.code === "string") {
|
|
136
|
+
code = json.code;
|
|
137
|
+
}
|
|
94
138
|
} catch {
|
|
95
139
|
if (errorBody)
|
|
96
140
|
message = errorBody;
|
|
97
141
|
}
|
|
98
|
-
throw new ApiError(response.status, message, parsedBody);
|
|
142
|
+
throw new ApiError(response.status, message, parsedBody, code);
|
|
99
143
|
}
|
|
100
144
|
return response;
|
|
101
145
|
}
|
|
@@ -173,28 +217,28 @@ function buildSpecQueryString(options) {
|
|
|
173
217
|
|
|
174
218
|
class Subgraphs extends BaseClient {
|
|
175
219
|
async list() {
|
|
176
|
-
return this.
|
|
220
|
+
return this.requestAtTenant("GET", "/api/subgraphs");
|
|
177
221
|
}
|
|
178
222
|
async get(name) {
|
|
179
|
-
return this.
|
|
223
|
+
return this.requestAtTenant("GET", `/api/subgraphs/${name}`);
|
|
180
224
|
}
|
|
181
225
|
async openapi(name, options) {
|
|
182
|
-
return this.
|
|
226
|
+
return this.requestAtTenant("GET", `/api/subgraphs/${name}/openapi.json${buildSpecQueryString(options)}`);
|
|
183
227
|
}
|
|
184
228
|
async schema(name, options) {
|
|
185
|
-
return this.
|
|
229
|
+
return this.requestAtTenant("GET", `/api/subgraphs/${name}/schema.json${buildSpecQueryString(options)}`);
|
|
186
230
|
}
|
|
187
231
|
async markdown(name, options) {
|
|
188
|
-
return this.
|
|
232
|
+
return this.requestTextAtTenant("GET", `/api/subgraphs/${name}/docs.md${buildSpecQueryString(options)}`);
|
|
189
233
|
}
|
|
190
234
|
async reindex(name, options) {
|
|
191
|
-
return this.
|
|
235
|
+
return this.requestAtTenant("POST", `/api/subgraphs/${name}/reindex`, options);
|
|
192
236
|
}
|
|
193
237
|
async stop(name) {
|
|
194
|
-
return this.
|
|
238
|
+
return this.requestAtTenant("POST", `/api/subgraphs/${name}/stop`);
|
|
195
239
|
}
|
|
196
240
|
async backfill(name, options) {
|
|
197
|
-
return this.
|
|
241
|
+
return this.requestAtTenant("POST", `/api/subgraphs/${name}/backfill`, options);
|
|
198
242
|
}
|
|
199
243
|
async gaps(name, opts) {
|
|
200
244
|
const qs = new URLSearchParams;
|
|
@@ -205,27 +249,27 @@ class Subgraphs extends BaseClient {
|
|
|
205
249
|
if (opts?.resolved !== undefined)
|
|
206
250
|
qs.set("resolved", String(opts.resolved));
|
|
207
251
|
const query = qs.toString();
|
|
208
|
-
return this.
|
|
252
|
+
return this.requestAtTenant("GET", `/api/subgraphs/${name}/gaps${query ? `?${query}` : ""}`);
|
|
209
253
|
}
|
|
210
254
|
async delete(name, options) {
|
|
211
255
|
const qs = options?.force ? "?force=true" : "";
|
|
212
|
-
return this.
|
|
256
|
+
return this.requestAtTenant("DELETE", `/api/subgraphs/${name}${qs}`);
|
|
213
257
|
}
|
|
214
258
|
async deploy(data) {
|
|
215
|
-
return this.
|
|
259
|
+
return this.requestAtTenant("POST", "/api/subgraphs", data);
|
|
216
260
|
}
|
|
217
261
|
async getSource(name) {
|
|
218
|
-
return this.
|
|
262
|
+
return this.requestAtTenant("GET", `/api/subgraphs/${name}/source`);
|
|
219
263
|
}
|
|
220
264
|
async bundle(data) {
|
|
221
|
-
return this.
|
|
265
|
+
return this.requestAtTenant("POST", "/api/subgraphs/bundle", data);
|
|
222
266
|
}
|
|
223
267
|
async queryTable(name, table, params = {}) {
|
|
224
|
-
const result = await this.
|
|
268
|
+
const result = await this.requestAtTenant("GET", `/api/subgraphs/${name}/${table}${buildSubgraphQueryString(params)}`);
|
|
225
269
|
return Array.isArray(result) ? result : result.data;
|
|
226
270
|
}
|
|
227
271
|
async queryTableCount(name, table, params = {}) {
|
|
228
|
-
return this.
|
|
272
|
+
return this.requestAtTenant("GET", `/api/subgraphs/${name}/${table}/count${buildSubgraphQueryString(params)}`);
|
|
229
273
|
}
|
|
230
274
|
typed(def) {
|
|
231
275
|
const result = {};
|
|
@@ -648,40 +692,40 @@ function createStreamsClient(options) {
|
|
|
648
692
|
// src/subscriptions/client.ts
|
|
649
693
|
class Subscriptions extends BaseClient {
|
|
650
694
|
async list() {
|
|
651
|
-
return this.
|
|
695
|
+
return this.requestAtTenant("GET", "/api/subscriptions");
|
|
652
696
|
}
|
|
653
697
|
async get(id) {
|
|
654
|
-
return this.
|
|
698
|
+
return this.requestAtTenant("GET", `/api/subscriptions/${id}`);
|
|
655
699
|
}
|
|
656
700
|
async create(input) {
|
|
657
|
-
return this.
|
|
701
|
+
return this.requestAtTenant("POST", "/api/subscriptions", input);
|
|
658
702
|
}
|
|
659
703
|
async update(id, patch) {
|
|
660
|
-
return this.
|
|
704
|
+
return this.requestAtTenant("PATCH", `/api/subscriptions/${id}`, patch);
|
|
661
705
|
}
|
|
662
706
|
async pause(id) {
|
|
663
|
-
return this.
|
|
707
|
+
return this.requestAtTenant("POST", `/api/subscriptions/${id}/pause`);
|
|
664
708
|
}
|
|
665
709
|
async resume(id) {
|
|
666
|
-
return this.
|
|
710
|
+
return this.requestAtTenant("POST", `/api/subscriptions/${id}/resume`);
|
|
667
711
|
}
|
|
668
712
|
async delete(id) {
|
|
669
|
-
return this.
|
|
713
|
+
return this.requestAtTenant("DELETE", `/api/subscriptions/${id}`);
|
|
670
714
|
}
|
|
671
715
|
async rotateSecret(id) {
|
|
672
|
-
return this.
|
|
716
|
+
return this.requestAtTenant("POST", `/api/subscriptions/${id}/rotate-secret`);
|
|
673
717
|
}
|
|
674
718
|
async recentDeliveries(id) {
|
|
675
|
-
return this.
|
|
719
|
+
return this.requestAtTenant("GET", `/api/subscriptions/${id}/deliveries`);
|
|
676
720
|
}
|
|
677
721
|
async replay(id, range) {
|
|
678
|
-
return this.
|
|
722
|
+
return this.requestAtTenant("POST", `/api/subscriptions/${id}/replay`, range);
|
|
679
723
|
}
|
|
680
724
|
async dead(id) {
|
|
681
|
-
return this.
|
|
725
|
+
return this.requestAtTenant("GET", `/api/subscriptions/${id}/dead`);
|
|
682
726
|
}
|
|
683
727
|
async requeueDead(id, outboxId) {
|
|
684
|
-
return this.
|
|
728
|
+
return this.requestAtTenant("POST", `/api/subscriptions/${id}/dead/${outboxId}/requeue`);
|
|
685
729
|
}
|
|
686
730
|
}
|
|
687
731
|
|
|
@@ -775,6 +819,13 @@ function requireString2(payload, field) {
|
|
|
775
819
|
return value;
|
|
776
820
|
}
|
|
777
821
|
function requireHexValue(payload) {
|
|
822
|
+
const rawValue = payload.raw_value;
|
|
823
|
+
if (typeof rawValue === "string") {
|
|
824
|
+
if (!/^0x[0-9a-fA-F]*$/.test(rawValue)) {
|
|
825
|
+
throw new Error("nft_transfer payload has malformed value");
|
|
826
|
+
}
|
|
827
|
+
return rawValue;
|
|
828
|
+
}
|
|
778
829
|
const value = payload.value;
|
|
779
830
|
const hex = typeof value === "string" ? value : value && typeof value === "object" && typeof value.hex === "string" ? value.hex : null;
|
|
780
831
|
if (!hex) {
|
|
@@ -865,5 +916,5 @@ export {
|
|
|
865
916
|
ApiError
|
|
866
917
|
};
|
|
867
918
|
|
|
868
|
-
//# debugId=
|
|
919
|
+
//# debugId=AA41F0EF16B7740B64756E2164756E21
|
|
869
920
|
//# sourceMappingURL=index.js.map
|