stitchdb 1.0.0 → 1.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/dist/index.d.mts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +121 -17
- package/dist/index.mjs +121 -17
- package/package.json +9 -3
package/dist/index.d.mts
CHANGED
|
@@ -30,12 +30,21 @@ interface ExecResult {
|
|
|
30
30
|
}
|
|
31
31
|
declare class StitchDBError extends Error {
|
|
32
32
|
status: number;
|
|
33
|
-
constructor(message: string, status
|
|
33
|
+
constructor(message: string, status?: number);
|
|
34
34
|
}
|
|
35
35
|
declare class StitchDB {
|
|
36
36
|
private url;
|
|
37
|
+
private wsUrl;
|
|
37
38
|
private apiKey;
|
|
39
|
+
private ws;
|
|
40
|
+
private pending;
|
|
41
|
+
private msgId;
|
|
42
|
+
private connecting;
|
|
43
|
+
private useWebSocket;
|
|
38
44
|
constructor(config: StitchDBConfig);
|
|
45
|
+
private connect;
|
|
46
|
+
private wsSend;
|
|
47
|
+
private httpQuery;
|
|
39
48
|
/** Run a SQL query with parameterized bindings. */
|
|
40
49
|
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<QueryResult<T>>;
|
|
41
50
|
/** Run multiple queries atomically in a single batch. */
|
|
@@ -59,7 +68,8 @@ declare class StitchDB {
|
|
|
59
68
|
limit?: number;
|
|
60
69
|
offset?: number;
|
|
61
70
|
}): Promise<T[]>;
|
|
62
|
-
|
|
71
|
+
/** Close the WebSocket connection. */
|
|
72
|
+
close(): void;
|
|
63
73
|
}
|
|
64
74
|
/** Create a StitchDB client. */
|
|
65
75
|
declare function createClient(config: StitchDBConfig): StitchDB;
|
package/dist/index.d.ts
CHANGED
|
@@ -30,12 +30,21 @@ interface ExecResult {
|
|
|
30
30
|
}
|
|
31
31
|
declare class StitchDBError extends Error {
|
|
32
32
|
status: number;
|
|
33
|
-
constructor(message: string, status
|
|
33
|
+
constructor(message: string, status?: number);
|
|
34
34
|
}
|
|
35
35
|
declare class StitchDB {
|
|
36
36
|
private url;
|
|
37
|
+
private wsUrl;
|
|
37
38
|
private apiKey;
|
|
39
|
+
private ws;
|
|
40
|
+
private pending;
|
|
41
|
+
private msgId;
|
|
42
|
+
private connecting;
|
|
43
|
+
private useWebSocket;
|
|
38
44
|
constructor(config: StitchDBConfig);
|
|
45
|
+
private connect;
|
|
46
|
+
private wsSend;
|
|
47
|
+
private httpQuery;
|
|
39
48
|
/** Run a SQL query with parameterized bindings. */
|
|
40
49
|
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<QueryResult<T>>;
|
|
41
50
|
/** Run multiple queries atomically in a single batch. */
|
|
@@ -59,7 +68,8 @@ declare class StitchDB {
|
|
|
59
68
|
limit?: number;
|
|
60
69
|
offset?: number;
|
|
61
70
|
}): Promise<T[]>;
|
|
62
|
-
|
|
71
|
+
/** Close the WebSocket connection. */
|
|
72
|
+
close(): void;
|
|
63
73
|
}
|
|
64
74
|
/** Create a StitchDB client. */
|
|
65
75
|
declare function createClient(config: StitchDBConfig): StitchDB;
|
package/dist/index.js
CHANGED
|
@@ -27,7 +27,7 @@ __export(index_exports, {
|
|
|
27
27
|
});
|
|
28
28
|
module.exports = __toCommonJS(index_exports);
|
|
29
29
|
var StitchDBError = class extends Error {
|
|
30
|
-
constructor(message, status) {
|
|
30
|
+
constructor(message, status = 0) {
|
|
31
31
|
super(message);
|
|
32
32
|
this.name = "StitchDBError";
|
|
33
33
|
this.status = status;
|
|
@@ -35,20 +35,132 @@ var StitchDBError = class extends Error {
|
|
|
35
35
|
};
|
|
36
36
|
var StitchDB = class {
|
|
37
37
|
constructor(config) {
|
|
38
|
+
this.ws = null;
|
|
39
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
40
|
+
this.msgId = 0;
|
|
41
|
+
this.connecting = null;
|
|
38
42
|
this.url = (config.url || "https://db.stitchdb.com").replace(/\/$/, "");
|
|
43
|
+
this.wsUrl = this.url.replace("https://", "wss://").replace("http://", "ws://");
|
|
39
44
|
this.apiKey = config.apiKey;
|
|
45
|
+
this.useWebSocket = typeof WebSocket !== "undefined";
|
|
46
|
+
}
|
|
47
|
+
async connect() {
|
|
48
|
+
if (this.ws?.readyState === 1) return;
|
|
49
|
+
if (this.connecting) return this.connecting;
|
|
50
|
+
this.connecting = new Promise((resolve, reject) => {
|
|
51
|
+
try {
|
|
52
|
+
const ws = new WebSocket(`${this.wsUrl}/ws/query?key=${this.apiKey}`);
|
|
53
|
+
ws.onopen = () => {
|
|
54
|
+
this.ws = ws;
|
|
55
|
+
this.connecting = null;
|
|
56
|
+
resolve();
|
|
57
|
+
};
|
|
58
|
+
ws.onmessage = (event) => {
|
|
59
|
+
try {
|
|
60
|
+
const data = JSON.parse(typeof event.data === "string" ? event.data : "");
|
|
61
|
+
const pending = this.pending.get(data.id);
|
|
62
|
+
if (pending) {
|
|
63
|
+
this.pending.delete(data.id);
|
|
64
|
+
if (data.error) {
|
|
65
|
+
pending.reject(new StitchDBError(data.error));
|
|
66
|
+
} else {
|
|
67
|
+
pending.resolve(data);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
ws.onclose = () => {
|
|
74
|
+
this.ws = null;
|
|
75
|
+
this.connecting = null;
|
|
76
|
+
for (const [id, p] of this.pending) {
|
|
77
|
+
p.reject(new StitchDBError("Connection closed"));
|
|
78
|
+
this.pending.delete(id);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
ws.onerror = () => {
|
|
82
|
+
this.ws = null;
|
|
83
|
+
this.connecting = null;
|
|
84
|
+
reject(new StitchDBError("WebSocket connection failed"));
|
|
85
|
+
};
|
|
86
|
+
} catch {
|
|
87
|
+
this.connecting = null;
|
|
88
|
+
this.useWebSocket = false;
|
|
89
|
+
resolve();
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
return this.connecting;
|
|
93
|
+
}
|
|
94
|
+
async wsSend(msg) {
|
|
95
|
+
await this.connect();
|
|
96
|
+
if (!this.ws || this.ws.readyState !== 1) {
|
|
97
|
+
this.useWebSocket = false;
|
|
98
|
+
return this.httpQuery(msg);
|
|
99
|
+
}
|
|
100
|
+
const id = String(++this.msgId);
|
|
101
|
+
msg.id = id;
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
const timeout = setTimeout(() => {
|
|
104
|
+
this.pending.delete(id);
|
|
105
|
+
reject(new StitchDBError("Query timeout"));
|
|
106
|
+
}, 3e4);
|
|
107
|
+
this.pending.set(id, {
|
|
108
|
+
resolve: (v) => {
|
|
109
|
+
clearTimeout(timeout);
|
|
110
|
+
resolve(v);
|
|
111
|
+
},
|
|
112
|
+
reject: (e) => {
|
|
113
|
+
clearTimeout(timeout);
|
|
114
|
+
reject(e);
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
this.ws.send(JSON.stringify(msg));
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async httpQuery(msg) {
|
|
121
|
+
let path = "/v1/query";
|
|
122
|
+
let body = { sql: msg.sql, params: msg.params };
|
|
123
|
+
if (msg.action === "batch") {
|
|
124
|
+
path = "/v1/batch";
|
|
125
|
+
body = { queries: msg.queries };
|
|
126
|
+
} else if (msg.action === "exec") {
|
|
127
|
+
path = "/v1/exec";
|
|
128
|
+
body = { sql: msg.sql };
|
|
129
|
+
}
|
|
130
|
+
const res = await fetch(`${this.url}${path}`, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
headers: {
|
|
133
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
134
|
+
"Content-Type": "application/json"
|
|
135
|
+
},
|
|
136
|
+
body: JSON.stringify(body)
|
|
137
|
+
});
|
|
138
|
+
const data = await res.json();
|
|
139
|
+
if (!res.ok || data.error) {
|
|
140
|
+
throw new StitchDBError(data.error || `Request failed: ${res.status}`, res.status);
|
|
141
|
+
}
|
|
142
|
+
return data;
|
|
40
143
|
}
|
|
41
144
|
/** Run a SQL query with parameterized bindings. */
|
|
42
145
|
async query(sql, params) {
|
|
43
|
-
|
|
146
|
+
if (this.useWebSocket) {
|
|
147
|
+
return this.wsSend({ action: "query", sql, params });
|
|
148
|
+
}
|
|
149
|
+
return this.httpQuery({ action: "query", sql, params });
|
|
44
150
|
}
|
|
45
151
|
/** Run multiple queries atomically in a single batch. */
|
|
46
152
|
async batch(queries) {
|
|
47
|
-
|
|
153
|
+
if (this.useWebSocket) {
|
|
154
|
+
return this.wsSend({ action: "batch", queries });
|
|
155
|
+
}
|
|
156
|
+
return this.httpQuery({ action: "batch", queries });
|
|
48
157
|
}
|
|
49
158
|
/** Run a DDL statement (CREATE TABLE, ALTER TABLE, DROP TABLE, etc.) */
|
|
50
159
|
async run(sql) {
|
|
51
|
-
|
|
160
|
+
if (this.useWebSocket) {
|
|
161
|
+
return this.wsSend({ action: "exec", sql });
|
|
162
|
+
}
|
|
163
|
+
return this.httpQuery({ action: "exec", sql });
|
|
52
164
|
}
|
|
53
165
|
/** Insert a row. */
|
|
54
166
|
async insert(table, data) {
|
|
@@ -80,20 +192,12 @@ var StitchDB = class {
|
|
|
80
192
|
const { results } = await this.query(sql, params);
|
|
81
193
|
return results;
|
|
82
194
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
"Content-Type": "application/json"
|
|
89
|
-
},
|
|
90
|
-
body: JSON.stringify(body)
|
|
91
|
-
});
|
|
92
|
-
const data = await res.json();
|
|
93
|
-
if (!res.ok || data.error) {
|
|
94
|
-
throw new StitchDBError(data.error || `Request failed: ${res.status}`, res.status);
|
|
195
|
+
/** Close the WebSocket connection. */
|
|
196
|
+
close() {
|
|
197
|
+
if (this.ws) {
|
|
198
|
+
this.ws.close();
|
|
199
|
+
this.ws = null;
|
|
95
200
|
}
|
|
96
|
-
return data;
|
|
97
201
|
}
|
|
98
202
|
};
|
|
99
203
|
function createClient(config) {
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
2
|
var StitchDBError = class extends Error {
|
|
3
|
-
constructor(message, status) {
|
|
3
|
+
constructor(message, status = 0) {
|
|
4
4
|
super(message);
|
|
5
5
|
this.name = "StitchDBError";
|
|
6
6
|
this.status = status;
|
|
@@ -8,20 +8,132 @@ var StitchDBError = class extends Error {
|
|
|
8
8
|
};
|
|
9
9
|
var StitchDB = class {
|
|
10
10
|
constructor(config) {
|
|
11
|
+
this.ws = null;
|
|
12
|
+
this.pending = /* @__PURE__ */ new Map();
|
|
13
|
+
this.msgId = 0;
|
|
14
|
+
this.connecting = null;
|
|
11
15
|
this.url = (config.url || "https://db.stitchdb.com").replace(/\/$/, "");
|
|
16
|
+
this.wsUrl = this.url.replace("https://", "wss://").replace("http://", "ws://");
|
|
12
17
|
this.apiKey = config.apiKey;
|
|
18
|
+
this.useWebSocket = typeof WebSocket !== "undefined";
|
|
19
|
+
}
|
|
20
|
+
async connect() {
|
|
21
|
+
if (this.ws?.readyState === 1) return;
|
|
22
|
+
if (this.connecting) return this.connecting;
|
|
23
|
+
this.connecting = new Promise((resolve, reject) => {
|
|
24
|
+
try {
|
|
25
|
+
const ws = new WebSocket(`${this.wsUrl}/ws/query?key=${this.apiKey}`);
|
|
26
|
+
ws.onopen = () => {
|
|
27
|
+
this.ws = ws;
|
|
28
|
+
this.connecting = null;
|
|
29
|
+
resolve();
|
|
30
|
+
};
|
|
31
|
+
ws.onmessage = (event) => {
|
|
32
|
+
try {
|
|
33
|
+
const data = JSON.parse(typeof event.data === "string" ? event.data : "");
|
|
34
|
+
const pending = this.pending.get(data.id);
|
|
35
|
+
if (pending) {
|
|
36
|
+
this.pending.delete(data.id);
|
|
37
|
+
if (data.error) {
|
|
38
|
+
pending.reject(new StitchDBError(data.error));
|
|
39
|
+
} else {
|
|
40
|
+
pending.resolve(data);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
ws.onclose = () => {
|
|
47
|
+
this.ws = null;
|
|
48
|
+
this.connecting = null;
|
|
49
|
+
for (const [id, p] of this.pending) {
|
|
50
|
+
p.reject(new StitchDBError("Connection closed"));
|
|
51
|
+
this.pending.delete(id);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
ws.onerror = () => {
|
|
55
|
+
this.ws = null;
|
|
56
|
+
this.connecting = null;
|
|
57
|
+
reject(new StitchDBError("WebSocket connection failed"));
|
|
58
|
+
};
|
|
59
|
+
} catch {
|
|
60
|
+
this.connecting = null;
|
|
61
|
+
this.useWebSocket = false;
|
|
62
|
+
resolve();
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
return this.connecting;
|
|
66
|
+
}
|
|
67
|
+
async wsSend(msg) {
|
|
68
|
+
await this.connect();
|
|
69
|
+
if (!this.ws || this.ws.readyState !== 1) {
|
|
70
|
+
this.useWebSocket = false;
|
|
71
|
+
return this.httpQuery(msg);
|
|
72
|
+
}
|
|
73
|
+
const id = String(++this.msgId);
|
|
74
|
+
msg.id = id;
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
const timeout = setTimeout(() => {
|
|
77
|
+
this.pending.delete(id);
|
|
78
|
+
reject(new StitchDBError("Query timeout"));
|
|
79
|
+
}, 3e4);
|
|
80
|
+
this.pending.set(id, {
|
|
81
|
+
resolve: (v) => {
|
|
82
|
+
clearTimeout(timeout);
|
|
83
|
+
resolve(v);
|
|
84
|
+
},
|
|
85
|
+
reject: (e) => {
|
|
86
|
+
clearTimeout(timeout);
|
|
87
|
+
reject(e);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
this.ws.send(JSON.stringify(msg));
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
async httpQuery(msg) {
|
|
94
|
+
let path = "/v1/query";
|
|
95
|
+
let body = { sql: msg.sql, params: msg.params };
|
|
96
|
+
if (msg.action === "batch") {
|
|
97
|
+
path = "/v1/batch";
|
|
98
|
+
body = { queries: msg.queries };
|
|
99
|
+
} else if (msg.action === "exec") {
|
|
100
|
+
path = "/v1/exec";
|
|
101
|
+
body = { sql: msg.sql };
|
|
102
|
+
}
|
|
103
|
+
const res = await fetch(`${this.url}${path}`, {
|
|
104
|
+
method: "POST",
|
|
105
|
+
headers: {
|
|
106
|
+
"Authorization": `Bearer ${this.apiKey}`,
|
|
107
|
+
"Content-Type": "application/json"
|
|
108
|
+
},
|
|
109
|
+
body: JSON.stringify(body)
|
|
110
|
+
});
|
|
111
|
+
const data = await res.json();
|
|
112
|
+
if (!res.ok || data.error) {
|
|
113
|
+
throw new StitchDBError(data.error || `Request failed: ${res.status}`, res.status);
|
|
114
|
+
}
|
|
115
|
+
return data;
|
|
13
116
|
}
|
|
14
117
|
/** Run a SQL query with parameterized bindings. */
|
|
15
118
|
async query(sql, params) {
|
|
16
|
-
|
|
119
|
+
if (this.useWebSocket) {
|
|
120
|
+
return this.wsSend({ action: "query", sql, params });
|
|
121
|
+
}
|
|
122
|
+
return this.httpQuery({ action: "query", sql, params });
|
|
17
123
|
}
|
|
18
124
|
/** Run multiple queries atomically in a single batch. */
|
|
19
125
|
async batch(queries) {
|
|
20
|
-
|
|
126
|
+
if (this.useWebSocket) {
|
|
127
|
+
return this.wsSend({ action: "batch", queries });
|
|
128
|
+
}
|
|
129
|
+
return this.httpQuery({ action: "batch", queries });
|
|
21
130
|
}
|
|
22
131
|
/** Run a DDL statement (CREATE TABLE, ALTER TABLE, DROP TABLE, etc.) */
|
|
23
132
|
async run(sql) {
|
|
24
|
-
|
|
133
|
+
if (this.useWebSocket) {
|
|
134
|
+
return this.wsSend({ action: "exec", sql });
|
|
135
|
+
}
|
|
136
|
+
return this.httpQuery({ action: "exec", sql });
|
|
25
137
|
}
|
|
26
138
|
/** Insert a row. */
|
|
27
139
|
async insert(table, data) {
|
|
@@ -53,20 +165,12 @@ var StitchDB = class {
|
|
|
53
165
|
const { results } = await this.query(sql, params);
|
|
54
166
|
return results;
|
|
55
167
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"Content-Type": "application/json"
|
|
62
|
-
},
|
|
63
|
-
body: JSON.stringify(body)
|
|
64
|
-
});
|
|
65
|
-
const data = await res.json();
|
|
66
|
-
if (!res.ok || data.error) {
|
|
67
|
-
throw new StitchDBError(data.error || `Request failed: ${res.status}`, res.status);
|
|
168
|
+
/** Close the WebSocket connection. */
|
|
169
|
+
close() {
|
|
170
|
+
if (this.ws) {
|
|
171
|
+
this.ws.close();
|
|
172
|
+
this.ws = null;
|
|
68
173
|
}
|
|
69
|
-
return data;
|
|
70
174
|
}
|
|
71
175
|
};
|
|
72
176
|
function createClient(config) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stitchdb",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "StitchDB client for JavaScript and TypeScript",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -19,7 +19,13 @@
|
|
|
19
19
|
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
20
20
|
"prepublishOnly": "npm run build"
|
|
21
21
|
},
|
|
22
|
-
"keywords": [
|
|
22
|
+
"keywords": [
|
|
23
|
+
"stitchdb",
|
|
24
|
+
"database",
|
|
25
|
+
"sql",
|
|
26
|
+
"edge",
|
|
27
|
+
"serverless"
|
|
28
|
+
],
|
|
23
29
|
"license": "MIT",
|
|
24
30
|
"author": {
|
|
25
31
|
"name": "StitchDB",
|
|
@@ -35,4 +41,4 @@
|
|
|
35
41
|
"tsup": "^8.0.0",
|
|
36
42
|
"typescript": "^5.0.0"
|
|
37
43
|
}
|
|
38
|
-
}
|
|
44
|
+
}
|