@steempro/sds 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 faisalamin9696
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,329 @@
1
- # Temporary Holding Version
1
+ # @steempro/sds
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Typed JavaScript / TypeScript client for the **Steem Blockchain Data Services
4
+ (SDS)** REST API — [sds0.steemworld.org](https://sds0.steemworld.org).
5
+
6
+ - **Every module and method** of the SDS reference: 22 modules, 285 methods,
7
+ fully typed with generated parameter/result interfaces.
8
+ - **Promise *and* node-style callback** support on every method.
9
+ - **`mapSds()`** — turns SDS's column oriented `{ cols, rows }` payloads into
10
+ plain row objects (and passes everything else through unchanged).
11
+ - **Structured errors** — one error class per failure mode, each with a stable
12
+ machine readable `code`.
13
+ - **Bring your own instance** — `sds0`, `sds1`, `sds`, or any custom SDS URL.
14
+ - **Retries, timeouts, abort signals, hooks**, client-side validation and
15
+ introspection APIs.
16
+ - ESM + CJS builds, zero runtime dependencies, Node 18+ / Deno / Bun / browsers
17
+ (anything with `fetch`).
18
+
19
+ ```bash
20
+ npm i @steempro/sds
21
+ ```
22
+
23
+ 📚 **[Full documentation](documentation.md)** — every one of the 285 methods
24
+ indexed, with signatures, parameters and runnable examples.
25
+ [Method tables](docs/METHODS.md) · [README](README.md).
26
+
27
+ ## Quick start
28
+
29
+ ```ts
30
+ import { SDS } from '@steempro/sds';
31
+
32
+ const sds = new SDS(); // → https://sds0.steemworld.org
33
+
34
+ // Promise style
35
+ const stats = await sds.chain.getChainStats();
36
+
37
+ // Callback style (last argument is always the callback)
38
+ sds.posts.getPost('steemchiller', 'hello', (error, post) => {
39
+ if (error) return console.error(error.code, error.message);
40
+ console.log(post);
41
+ });
42
+ ```
43
+
44
+ ESM and CommonJS are both supported:
45
+
46
+ ```js
47
+ const { SDS } = require('@steempro/sds');
48
+ ```
49
+
50
+ ## Choosing an SDS instance
51
+
52
+ ```ts
53
+ new SDS(); // sds0 (default)
54
+ new SDS({ instance: 'sds1' }); // bundled instance name
55
+ new SDS({ instance: 'sds' }); // bundled instance name
56
+ new SDS({ baseUrl: 'https://my-sds.example.org' }); // any custom URL
57
+ new SDS({ instance: 'sds9', instances: { sds9: 'https://sds9.example.org' } });
58
+ ```
59
+
60
+ Register an instance for the whole process:
61
+
62
+ ```ts
63
+ import { registerInstance, listInstances } from '@steempro/sds';
64
+
65
+ registerInstance('sds9', 'https://sds9.example.org');
66
+ const sds = new SDS({ instance: 'sds9' });
67
+ ```
68
+
69
+ `DEFAULT_SDS_INSTANCES` contains the bundled names (`sds`, `sds0`, `sds1`).
70
+
71
+ ## Calling methods
72
+
73
+ Every generated method accepts its parameters **positionally (in route
74
+ order)** or as a **single object keyed by parameter name**, plus an optional
75
+ trailing callback:
76
+
77
+ ```ts
78
+ // positional (route order)
79
+ await sds.chain.getAccountNamesByPrefix('ste', 50);
80
+
81
+ // object style
82
+ await sds.chain.getAccountNamesByPrefix({ prefix: 'ste', limit: 50 });
83
+
84
+ // callback style — either form
85
+ sds.chain.getAccountNamesByPrefix({ prefix: 'ste' }, (err, names) => { … });
86
+ ```
87
+
88
+ Optional trailing parameters are simply omitted — SDS applies their documented
89
+ defaults. If a parameter is skipped in the middle of a route, the client fills
90
+ it with the documented default (or the literal `null` placeholder SDS accepts).
91
+
92
+ **Community parameters** (e.g. `community` on `communities_api` / `feeds_api`)
93
+ take a **hive community id** — always `hive-` prefixed, e.g. `hive-160125`.
94
+ Bare ids (`160125`), titles (`steemit-news`) and regular accounts (`alice`)
95
+ pass SDS but come back empty, so the client rejects them during validation
96
+ (pass `validate: false` to bypass):
97
+
98
+ ```ts
99
+ await sds.communities.getCommunity('hive-160125'); // ✅ real data
100
+ await sds.communities.getCommunity('160125'); // ❌ SDSValidationError
101
+ // "…must be a hive community id
102
+ // starting with \"hive-\" (e.g. \"hive-160125\")"
103
+ // The rule is exported for your own checks:
104
+ import { isCommunityParam, COMMUNITY_ID_PATTERN } from '@steempro/sds';
105
+ ```
106
+
107
+ **Timestamp parameters** (`fromTime`, `toTime`, `blockTime`, …) accept **any
108
+ date form** — a `Date`, a date string or unix seconds — and are converted to
109
+ unix **seconds** (the unit SDS expects) before the request is sent:
110
+
111
+ ```ts
112
+ await sds.accountHistory.getHistoryByTime(
113
+ 'steemit',
114
+ new Date('2020-09-14T00:00:00Z'), // Date
115
+ '2020-09-15 12:30:00', // date string — zone-less means UTC
116
+ 100,
117
+ );
118
+
119
+ await sds.chain.getBlockInfoByTime('2020-09-13 12:26:40'); // → 1600000000
120
+ await sds.chain.getBlockInfoByTime(1600000000000); // ms auto-detected → 1600000000
121
+
122
+ // A value that cannot be understood throws before anything is sent:
123
+ // SDSValidationError: Parameter "fromTime" of account_history_api.getHistoryByTime
124
+ // must be a timestamp, got "yesterday". Pass a Date, a date/time string (e.g.
125
+ // "2020-09-13", "2020-09-13 12:30:00", "2020-09-13T12:30:00+02:00") or a unix
126
+ // timestamp in seconds (12+ digit numbers are read as milliseconds).
127
+
128
+ // The conversion is exported for your own code:
129
+ import { coerceTimestamp, isTimestampParam } from '@steempro/sds';
130
+ coerceTimestamp('2020-09-13'); // 1599955200 (2020-09-13T00:00:00Z)
131
+ ```
132
+
133
+ Accepted forms: `Date` objects, `2020-09-13`, `2020/09/13`, `2020-09`, date-times
134
+ with or without seconds (`2020-09-13 12:30`, `2020-09-13T12:30:00`), anything with
135
+ an explicit zone (`Z`, `+02:00` — those keep their zone), other formats `Date`
136
+ parses (RFC 2822, `May 1, 2020`, …) and unix seconds. Timestamps inside JSON
137
+ queries (`{ type: 'transfer', fromTime: '2020-09-13' }`) are converted the same
138
+ way.
139
+
140
+ ### Generic entry points
141
+
142
+ Not everything needs a generated wrapper:
143
+
144
+ ```ts
145
+ // By name, with params array + per call options
146
+ await sds.call('chain_api', 'getAccountNames', [10, 0], { timeout: 5000 });
147
+
148
+ // Even methods missing from the bundled reference work (raw segments)
149
+ await sds.call('brand_new_api', 'getThing', ['a', 'b']);
150
+
151
+ // Raw GET, unwraps the { code, result } envelope
152
+ await sds.request('/chain_api/getConfig');
153
+ ```
154
+
155
+ ## Mapping `{ cols, rows }` responses with `mapSds`
156
+
157
+ Some SDS list/search methods return a column oriented table instead of objects:
158
+
159
+ ```json
160
+ { "cols": { "link_id": 0, "author": 6, "permlink": 7 },
161
+ "rows": [[114136936, 0, 69.305, 0, "", "", "alice", "hello"]] }
162
+ ```
163
+
164
+ `mapSds()` converts that to plain row objects, and **passes any other payload
165
+ through unchanged**, so it is safe to chain on any call:
166
+
167
+ ```ts
168
+ import { mapSds, isSDSTable } from '@steempro/sds';
169
+
170
+ const rows = mapSds(await sds.feeds.getActivePostsByCreated({ limit: 1 }));
171
+ // [{ link_id: 114136936, author: 'alice', permlink: 'hello', … }, …]
172
+
173
+ // …or as a promise continuation:
174
+ const rows2 = await sds.feeds.getActivePostsByTrending().then(mapSds);
175
+
176
+ // Narrow first when the payload type is unknown:
177
+ const payload: unknown = await sds.request('/feeds_api/getActivePostsByTrending');
178
+ const rows3 = isSDSTable(payload) ? mapSds(payload) : payload;
179
+ ```
180
+
181
+ It maps by the `cols` **name → index** entries (not by key order), supports
182
+ array style `cols`, keeps already keyed rows, handles single-column scalar
183
+ rows, returns `[]` for an empty table, and unwraps raw `{ code, result }`
184
+ envelopes first. A non-table payload (plain object, array, scalar) is returned
185
+ as-is, and the overloads preserve that type (`mapSds<T>(payload)`).
186
+
187
+ ## Error handling
188
+
189
+ Every failure is an `SDSError` subclass with a stable `code`, plus the
190
+ `module`, `method` and `url` of the failing call:
191
+
192
+ | Class | `code` | When |
193
+ | --------------------- | ---------------------- | ------------------------------------------------ |
194
+ | `SDSValidationError` | `ERR_SDS_VALIDATION` | Parameter failed type/range/allow-list validation |
195
+ | `SDSArgumentError` | `ERR_SDS_ARGUMENT` | Unknown method/instance/parameter, wrong arity |
196
+ | `SDSNetworkError` | `ERR_SDS_NETWORK` | Request never reached SDS (DNS, CORS, offline) |
197
+ | `SDSTimeoutError` | `ERR_SDS_TIMEOUT` | Request exceeded `timeout` |
198
+ | `SDSAbortError` | `ERR_SDS_ABORT` | Cancelled through an `AbortSignal` |
199
+ | `SDSHttpError` | `ERR_SDS_HTTP` | Non-success HTTP status (e.g. 404 unknown route) |
200
+ | `SDSResponseError` | `ERR_SDS_RESPONSE` | HTTP 200 but not the expected JSON envelope |
201
+ | `SDSApiError` | `ERR_SDS_API` | Application error: `{ code: -1, error: "…" }` |
202
+
203
+ ```ts
204
+ import { SDSApiError, isSDSError } from '@steempro/sds';
205
+
206
+ try {
207
+ await sds.posts.getPost('alice', 'missing');
208
+ } catch (error) {
209
+ if (error instanceof SDSApiError) {
210
+ console.log(error.apiCode, error.errorMessage); // -1, "Link id … does not exist"
211
+ }
212
+ if (isSDSError(error)) console.log(error.toJSON()); // safe to log / serialize
213
+ }
214
+ ```
215
+
216
+ Callback style delivers the same errors: `callback(error)` on failure,
217
+ `callback(null, result)` on success. Validation errors are passed to the
218
+ callback rather than thrown.
219
+
220
+ ## Retries, timeouts, cancellation
221
+
222
+ ```ts
223
+ const sds = new SDS({
224
+ timeout: 10_000, // per request, ms (default 30_000)
225
+ retries: 2, // network / timeout / 5xx / 429 retries (default 2)
226
+ retryDelay: 300, // exponential backoff base, ms (default 300)
227
+ });
228
+
229
+ // Per call overrides + cancellation
230
+ const controller = new AbortController();
231
+ await sds.call('chain_api', 'getConfig', null, {
232
+ timeout: 2000,
233
+ retries: 0,
234
+ signal: controller.signal,
235
+ headers: { 'x-api-key': '…' },
236
+ });
237
+ ```
238
+
239
+ Application errors (`ERR_SDS_API`) and 4xx responses are never retried.
240
+
241
+ ## Derived clients
242
+
243
+ ```ts
244
+ const fast = sds.withOptions({ timeout: 3000 });
245
+ const authed = sds.withOptions({ headers: { authorization: `Bearer ${token}` } });
246
+ const quiet = sds.withOptions({ retries: 0 });
247
+ ```
248
+
249
+ ## Hooks (logging / tracing)
250
+
251
+ ```ts
252
+ const sds = new SDS({
253
+ onRequest: ({ url, module, method, attempt }) => console.log('→', url),
254
+ onResponse: ({ url, status, durationMs, ok }) => console.log('←', status, durationMs, ok),
255
+ });
256
+ ```
257
+
258
+ ## Introspection — list all methods and parameters
259
+
260
+ The bundled reference can be queried at runtime:
261
+
262
+ ```ts
263
+ import { listModules, listMethods, findMethod, API_REFERENCE } from '@steempro/sds';
264
+
265
+ sds.listModules(); // 22 modules
266
+ sds.listMethods('chain_api'); // methods of one module
267
+ sds.describeMethod('chain_api.getAccountNames');
268
+ // → { name, path, description, params: [{ name, type, optional, default, min, max, … }], result }
269
+
270
+ sds.hasMethod('posts_api', 'getPost'); // true
271
+ sds.referenceVersion; // SDS reference version of the bundle
272
+ API_REFERENCE; // full metadata (source, version, modules)
273
+ ```
274
+
275
+ A human readable list of **every module, method and parameter** is generated
276
+ into [`docs/METHODS.md`](./docs/METHODS.md).
277
+
278
+ ## TypeScript
279
+
280
+ The package ships its own types (`ESM` + `CJS` + `.d.ts`). Every method has
281
+ generated argument interfaces and result types:
282
+
283
+ ```ts
284
+ import type { ChainGetAccountNamesArgs, PostsGetPostArgs } from '@steempro/sds';
285
+
286
+ const args: ChainGetAccountNamesArgs = { limit: 10, offset: 0 };
287
+ const names = await sds.chain.getAccountNames(args);
288
+ ```
289
+
290
+ Result types map SDS's `JSON Object` / `JSON Array` / `integer` / `string`
291
+ returns to `SDSJsonObject` / `SDSJsonArray` / `number` / `string`; pass a type
292
+ parameter to narrow them:
293
+
294
+ ```ts
295
+ const config = await sds.chain.getConfig<{ STM: string }>();
296
+ ```
297
+
298
+ ## Options reference
299
+
300
+ | Option | Default | Description |
301
+ | -------------- | ------------------ | ------------------------------------------------------- |
302
+ | `instance` | `'sds0'` | Bundled instance name or full URL |
303
+ | `baseUrl` | – | Full base URL (takes precedence over `instance`) |
304
+ | `instances` | – | Extra `{ name: url }` pairs for this client |
305
+ | `timeout` | `30000` | Request timeout in ms (`0` disables) |
306
+ | `retries` | `2` | Retries for network/timeout/5xx/429 |
307
+ | `retryDelay` | `300` | Exponential backoff base in ms |
308
+ | `headers` | `{}` | Extra headers for every request |
309
+ | `fetch` | `globalThis.fetch` | Custom fetch implementation |
310
+ | `validate` | `true` | Client-side parameter validation |
311
+ | `encode` | `true` | URL-encode path segments |
312
+ | `onRequest` | – | Hook before each attempt |
313
+ | `onResponse` | – | Hook after each attempt (success or failure) |
314
+
315
+ ## Development
316
+
317
+ ```bash
318
+ npm install
319
+ npm run scrape # refresh data/api.json from the live SDS reference
320
+ npm run generate # regenerate src/generated/* and docs/METHODS.md
321
+ npm run typecheck
322
+ npm test # unit tests (mocked fetch)
323
+ npm run test:live # tests against the real SDS (SDS_LIVE_INSTANCE=sds1 to switch)
324
+ npm run build # dist/ (ESM + CJS + types)
325
+ ```
326
+
327
+ ## License
328
+
329
+ MIT