@wavehouse/sdk 0.0.0-dev.hb0c3a577b2f8 → 0.0.1-dev.20260819163639.h62b2f85f436a
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 +7 -5
- package/dist/cli/codegen.js +47 -18
- package/dist/index.cjs +407 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +166 -9
- package/dist/index.d.ts +166 -9
- package/dist/index.global.js +11 -2
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +407 -78
- package/dist/index.js.map +1 -1
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @wavehouse/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
TypeScript client for [WaveHouse](https://github.com/Wave-RF/WaveHouse) — schema-aware real-time API gateway for ClickHouse. One runtime dependency (`eventsource-parser`, ~1.4 KB gzipped, itself dependency-free).
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,6 +8,8 @@ Zero-dependency TypeScript client for [WaveHouse](https://github.com/Wave-RF/Wav
|
|
|
8
8
|
npm install @wavehouse/sdk
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
Requires Node 22 or newer — the only line this SDK is tested against; Node 18 and 20 are past upstream end-of-life. Neither browsers nor Node need a polyfill: streaming runs on `fetch`, like the rest of the SDK — see [Runtime support](https://wavehouse.dev/sdk/#runtime-support).
|
|
12
|
+
|
|
11
13
|
This works in any framework that uses a bundler — React, Vue, Svelte, Angular, Astro, SolidJS, or plain Vite — with `import { createClient } from '@wavehouse/sdk'`.
|
|
12
14
|
|
|
13
15
|
The package is published with [npm provenance](https://docs.npmjs.com/generating-provenance-statements) — npm shows a verified provenance badge on the [package page](https://www.npmjs.com/package/@wavehouse/sdk), and `npm audit signatures` checks the attestation against this repository's signed build.
|
|
@@ -42,7 +44,7 @@ Pin a version for production (`https://esm.sh/@wavehouse/sdk@0.1.0`). jsDelivr (
|
|
|
42
44
|
|
|
43
45
|
**Versioning.** A bare CDN URL serves the latest published **release**; pin for production (`@wavehouse/sdk@0.1.0`) or float on a range (`@0` for the newest 0.x, `@0.1` for 0.1.x). Builds from `main` are published under the `dev` tag — `@wavehouse/sdk@dev` — for trying unreleased changes.
|
|
44
46
|
|
|
45
|
-
Streaming (`.stream()`)
|
|
47
|
+
Streaming (`.stream()`) runs on `fetch`, so it works in both forms with no polyfill.
|
|
46
48
|
|
|
47
49
|
## Quick Start
|
|
48
50
|
|
|
@@ -55,7 +57,7 @@ const wh = createClient({
|
|
|
55
57
|
});
|
|
56
58
|
```
|
|
57
59
|
|
|
58
|
-
`baseURL` may include a path prefix (`https://app.example.com/api/
|
|
60
|
+
`baseURL` may include a path prefix (`https://app.example.com/api/wavehouse`) when WaveHouse is served under one — see [Serving under a path prefix](https://wavehouse.dev/sdk#serving-under-a-path-prefix).
|
|
59
61
|
|
|
60
62
|
### Query Data
|
|
61
63
|
|
|
@@ -129,7 +131,7 @@ const { data } = await wh.from('clicks').select('page').limit(10);
|
|
|
129
131
|
|
|
130
132
|
Async request methods return a `Result<T>` object — destructure it as `{ data, error }` — and never throw for anything the server returns. `.stream()` and `.liveQuery()` return controllers instead, reporting failures through the subscriber's `error` callback.
|
|
131
133
|
|
|
132
|
-
The SDK does throw on caller and environment errors: a non-absolute `baseURL` (REST calls reject with a `TypeError`; streams report
|
|
134
|
+
The SDK does throw on caller and environment errors: a non-absolute `baseURL` (REST calls reject with a `TypeError`; streams report a terminal `SSE_CONNECT_ERROR`), `.stream()` / `.liveQuery()` in a runtime with no global `fetch` and no `options.fetch`, and an `auth` callback that rejects — a token-refresh failure propagates out of the REST call, and on a stream is reported as a retryable `SSE_AUTH_ERROR`. A dropped stream *is* re-dialed by the SDK, on a jittered backoff; only non-retryable failures end it. One more exception can escape an SDK call, though it is yours rather than ours: a subscriber's `status` handler throwing on the first, synchronous `.subscribe()` / `.liveQuery()` call — see [Error Handling](https://wavehouse.dev/sdk/reference#error-handling).
|
|
133
135
|
|
|
134
136
|
```ts
|
|
135
137
|
const { data, error } = await wh.from('clicks').fetch();
|
|
@@ -148,7 +150,7 @@ Generate TypeScript types from your live WaveHouse schema. The package ships a `
|
|
|
148
150
|
npx wavehouse-codegen --url http://localhost:8080 --out ./src/db.d.ts
|
|
149
151
|
```
|
|
150
152
|
|
|
151
|
-
This introspects `/v1/schema`, maps ClickHouse column types to TypeScript, and outputs a `Database` interface you can pass to `createClient<Database>()`. `/v1/schema` is **admin-only** — pass an admin-role token with `--auth <jwt>` (or `-a`) against any non-dev policy.
|
|
153
|
+
This introspects `/v1/ops/schema`, maps ClickHouse column types to TypeScript, and outputs a `Database` interface you can pass to `createClient<Database>()`. `/v1/ops/schema` is **admin-only** — pass an admin-role token with `--auth <jwt>` (or `-a`) against any non-dev policy.
|
|
152
154
|
|
|
153
155
|
## Development & Testing
|
|
154
156
|
|
package/dist/cli/codegen.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// src/cli/codegen.ts
|
|
4
|
+
import { realpathSync } from "fs";
|
|
5
|
+
import { pathToFileURL } from "url";
|
|
6
|
+
|
|
3
7
|
// src/url.ts
|
|
4
8
|
function resolveURL(base, path, params) {
|
|
5
9
|
const root = new URL(base);
|
|
@@ -107,15 +111,29 @@ function findTopLevelComma(s) {
|
|
|
107
111
|
}
|
|
108
112
|
return -1;
|
|
109
113
|
}
|
|
114
|
+
function isColumn(value) {
|
|
115
|
+
if (typeof value !== "object" || value === null) return false;
|
|
116
|
+
const col = value;
|
|
117
|
+
return typeof col.name === "string" && typeof col.type === "string";
|
|
118
|
+
}
|
|
119
|
+
function isTableSchema(value) {
|
|
120
|
+
if (typeof value !== "object" || value === null) return false;
|
|
121
|
+
const table = value;
|
|
122
|
+
return typeof table.name === "string" && Array.isArray(table.columns) && table.columns.every(isColumn);
|
|
123
|
+
}
|
|
110
124
|
async function fetchSchemas(url, auth) {
|
|
111
125
|
const headers = {};
|
|
112
126
|
if (auth) headers.Authorization = `Bearer ${auth}`;
|
|
113
|
-
const res = await fetch(resolveURL(url, "/v1/schema").toString(), { headers });
|
|
127
|
+
const res = await fetch(resolveURL(url, "/v1/ops/schema").toString(), { headers });
|
|
114
128
|
if (!res.ok) {
|
|
115
129
|
const text = await res.text();
|
|
116
130
|
throw new Error(`Schema fetch failed (${res.status}): ${text}`);
|
|
117
131
|
}
|
|
118
|
-
|
|
132
|
+
const body = await res.json();
|
|
133
|
+
if (!Array.isArray(body) || !body.every(isTableSchema)) {
|
|
134
|
+
throw new Error("Unexpected /v1/ops/schema response: expected a JSON array of table schemas");
|
|
135
|
+
}
|
|
136
|
+
return body;
|
|
119
137
|
}
|
|
120
138
|
function generateTypes(schemas) {
|
|
121
139
|
const lines = [
|
|
@@ -125,19 +143,16 @@ function generateTypes(schemas) {
|
|
|
125
143
|
"",
|
|
126
144
|
"export interface Database {"
|
|
127
145
|
];
|
|
128
|
-
const
|
|
129
|
-
for (const
|
|
130
|
-
|
|
131
|
-
const rowType = `${pascalCase(tableName)}Row`;
|
|
132
|
-
lines.push(` ${tableName}: ${rowType};`);
|
|
146
|
+
const tables = [...schemas].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
147
|
+
for (const table of tables) {
|
|
148
|
+
lines.push(` ${table.name}: ${pascalCase(table.name)}Row;`);
|
|
133
149
|
}
|
|
134
150
|
lines.push("}");
|
|
135
151
|
lines.push("");
|
|
136
|
-
for (const
|
|
137
|
-
const
|
|
138
|
-
const rowType = `${pascalCase(tableName)}Row`;
|
|
152
|
+
for (const table of tables) {
|
|
153
|
+
const rowType = `${pascalCase(table.name)}Row`;
|
|
139
154
|
lines.push(`export interface ${rowType} {`);
|
|
140
|
-
for (const col of
|
|
155
|
+
for (const col of table.columns) {
|
|
141
156
|
const tsType = chTypeToTS(col.type);
|
|
142
157
|
const optional = col.has_default ? "?" : "";
|
|
143
158
|
lines.push(` ${col.name}${optional}: ${tsType};`);
|
|
@@ -154,12 +169,11 @@ async function main() {
|
|
|
154
169
|
const args = parseArgs(process.argv);
|
|
155
170
|
console.log(`Fetching schema from ${args.url}...`);
|
|
156
171
|
const schemas = await fetchSchemas(args.url, args.auth);
|
|
157
|
-
|
|
158
|
-
if (tableCount === 0) {
|
|
172
|
+
if (schemas.length === 0) {
|
|
159
173
|
console.warn("No tables found. Is WaveHouse running with tables in ClickHouse?");
|
|
160
174
|
process.exit(1);
|
|
161
175
|
}
|
|
162
|
-
console.log(`Found ${
|
|
176
|
+
console.log(`Found ${schemas.length} table(s): ${schemas.map((t) => t.name).join(", ")}`);
|
|
163
177
|
const output = generateTypes(schemas);
|
|
164
178
|
const { writeFile } = await import("fs/promises");
|
|
165
179
|
const { resolve } = await import("path");
|
|
@@ -167,7 +181,22 @@ async function main() {
|
|
|
167
181
|
await writeFile(outPath, output, "utf-8");
|
|
168
182
|
console.log(`\u2713 Types written to ${outPath}`);
|
|
169
183
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
184
|
+
function isDirectInvocation() {
|
|
185
|
+
if (!process.argv[1]) return false;
|
|
186
|
+
try {
|
|
187
|
+
return import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href;
|
|
188
|
+
} catch {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (isDirectInvocation()) {
|
|
193
|
+
main().catch((err) => {
|
|
194
|
+
console.error("Codegen failed:", err.message);
|
|
195
|
+
process.exit(1);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
export {
|
|
199
|
+
chTypeToTS,
|
|
200
|
+
fetchSchemas,
|
|
201
|
+
generateTypes
|
|
202
|
+
};
|