@ezwrld/ezbase 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/dist/index.d.ts +104 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +315 -0
- package/dist/index.js.map +1 -0
- package/package.json +42 -0
- package/src/index.ts +379 -0
- package/tsconfig.json +15 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
type WhereOp = '==' | '!=' | '<' | '>' | '<=' | '>=';
|
|
2
|
+
type OrderDir = 'asc' | 'desc';
|
|
3
|
+
interface Document<T = Record<string, unknown>> {
|
|
4
|
+
id: string;
|
|
5
|
+
data: T;
|
|
6
|
+
created: number;
|
|
7
|
+
updated: number;
|
|
8
|
+
}
|
|
9
|
+
interface EzBaseOptions {
|
|
10
|
+
url: string;
|
|
11
|
+
adminKey?: string;
|
|
12
|
+
}
|
|
13
|
+
interface AuthUser {
|
|
14
|
+
id: string;
|
|
15
|
+
email: string;
|
|
16
|
+
role: string;
|
|
17
|
+
created?: number;
|
|
18
|
+
updated?: number;
|
|
19
|
+
}
|
|
20
|
+
type AuthStateCallback = (user: AuthUser | null) => void;
|
|
21
|
+
declare class AuthClient {
|
|
22
|
+
private ez;
|
|
23
|
+
private _token;
|
|
24
|
+
private _currentUser;
|
|
25
|
+
private _listeners;
|
|
26
|
+
constructor(ez: EzBase);
|
|
27
|
+
get currentUser(): AuthUser | null;
|
|
28
|
+
/** @internal */
|
|
29
|
+
_getToken(): string | null;
|
|
30
|
+
signUp(opts: {
|
|
31
|
+
email: string;
|
|
32
|
+
password: string;
|
|
33
|
+
}): Promise<{
|
|
34
|
+
token: string;
|
|
35
|
+
user: AuthUser;
|
|
36
|
+
}>;
|
|
37
|
+
signIn(opts: {
|
|
38
|
+
email: string;
|
|
39
|
+
password: string;
|
|
40
|
+
}): Promise<{
|
|
41
|
+
token: string;
|
|
42
|
+
user: AuthUser;
|
|
43
|
+
}>;
|
|
44
|
+
signOut(): void;
|
|
45
|
+
restoreSession(token: string, user: AuthUser): void;
|
|
46
|
+
onAuthStateChanged(callback: AuthStateCallback): () => void;
|
|
47
|
+
private _notify;
|
|
48
|
+
}
|
|
49
|
+
declare class EzBase {
|
|
50
|
+
private url;
|
|
51
|
+
private adminKey?;
|
|
52
|
+
readonly auth: AuthClient;
|
|
53
|
+
constructor(urlOrOpts: string | EzBaseOptions);
|
|
54
|
+
collection<T = Record<string, unknown>>(name: string): CollectionRef<T>;
|
|
55
|
+
/** @internal */
|
|
56
|
+
_getUrl(): string;
|
|
57
|
+
/** @internal */
|
|
58
|
+
_authHeaders(): Record<string, string>;
|
|
59
|
+
/** @internal — append token as query param for SSE (EventSource can't send headers) */
|
|
60
|
+
_sseTokenParam(): string;
|
|
61
|
+
}
|
|
62
|
+
declare class CollectionRef<T = Record<string, unknown>> {
|
|
63
|
+
private ez;
|
|
64
|
+
private name;
|
|
65
|
+
constructor(ez: EzBase, name: string);
|
|
66
|
+
doc(id: string): DocRef<T>;
|
|
67
|
+
where(field: string, op: WhereOp, value: unknown): QueryRef<T>;
|
|
68
|
+
orderBy(field: string, direction?: OrderDir): QueryRef<T>;
|
|
69
|
+
limit(n: number): QueryRef<T>;
|
|
70
|
+
add(data: Partial<T>): Promise<Document<T>>;
|
|
71
|
+
get(): Promise<Document<T>[]>;
|
|
72
|
+
onSnapshot(callback: (docs: Document<T>[]) => void, onError?: (err: Error) => void): () => void;
|
|
73
|
+
}
|
|
74
|
+
declare class DocRef<T = Record<string, unknown>> {
|
|
75
|
+
private ez;
|
|
76
|
+
private collection;
|
|
77
|
+
private id;
|
|
78
|
+
constructor(ez: EzBase, collection: string, id: string);
|
|
79
|
+
private path;
|
|
80
|
+
get(): Promise<Document<T> | null>;
|
|
81
|
+
set(data: T): Promise<Document<T>>;
|
|
82
|
+
update(data: Partial<T>): Promise<Document<T>>;
|
|
83
|
+
delete(): Promise<void>;
|
|
84
|
+
onSnapshot(callback: (doc: Document<T> | null) => void, onError?: (err: Error) => void): () => void;
|
|
85
|
+
}
|
|
86
|
+
declare class QueryRef<T = Record<string, unknown>> {
|
|
87
|
+
private ez;
|
|
88
|
+
private collection;
|
|
89
|
+
private wheres;
|
|
90
|
+
private _orderBy?;
|
|
91
|
+
private _order?;
|
|
92
|
+
private _limit?;
|
|
93
|
+
constructor(ez: EzBase, collection: string);
|
|
94
|
+
where(field: string, op: WhereOp, value: unknown): this;
|
|
95
|
+
orderBy(field: string, direction?: OrderDir): this;
|
|
96
|
+
limit(n: number): this;
|
|
97
|
+
private buildParams;
|
|
98
|
+
get(): Promise<Document<T>[]>;
|
|
99
|
+
onSnapshot(callback: (docs: Document<T>[]) => void, onError?: (err: Error) => void): () => void;
|
|
100
|
+
}
|
|
101
|
+
export { EzBase, CollectionRef, DocRef, QueryRef, AuthClient };
|
|
102
|
+
export type { Document, WhereOp, OrderDir, EzBaseOptions, AuthUser };
|
|
103
|
+
export default EzBase;
|
|
104
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
// ── SSE helper (works in Node 18+, browsers, Deno, Bun) ──────
|
|
2
|
+
function sseConnect(url, headers, onSnapshot, onError) {
|
|
3
|
+
const controller = new AbortController();
|
|
4
|
+
(async () => {
|
|
5
|
+
try {
|
|
6
|
+
const res = await fetch(url, {
|
|
7
|
+
headers: { Accept: 'text/event-stream', ...headers },
|
|
8
|
+
signal: controller.signal,
|
|
9
|
+
});
|
|
10
|
+
if (!res.ok || !res.body) {
|
|
11
|
+
throw new Error(`SSE connect failed: ${res.status}`);
|
|
12
|
+
}
|
|
13
|
+
const reader = res.body.getReader();
|
|
14
|
+
const decoder = new TextDecoder();
|
|
15
|
+
let buf = '';
|
|
16
|
+
let eventType = '';
|
|
17
|
+
let dataLines = [];
|
|
18
|
+
while (true) {
|
|
19
|
+
const { done, value } = await reader.read();
|
|
20
|
+
if (done)
|
|
21
|
+
break;
|
|
22
|
+
buf += decoder.decode(value, { stream: true });
|
|
23
|
+
const lines = buf.split('\n');
|
|
24
|
+
buf = lines.pop();
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
if (line.startsWith('event:')) {
|
|
27
|
+
eventType = line.slice(6).trim();
|
|
28
|
+
}
|
|
29
|
+
else if (line.startsWith('data:')) {
|
|
30
|
+
dataLines.push(line.slice(5).trim());
|
|
31
|
+
}
|
|
32
|
+
else if (line === '' || line === '\r') {
|
|
33
|
+
if (eventType === 'snapshot' && dataLines.length > 0) {
|
|
34
|
+
onSnapshot(dataLines.join('\n'));
|
|
35
|
+
}
|
|
36
|
+
eventType = '';
|
|
37
|
+
dataLines = [];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
if (err instanceof Error && err.name !== 'AbortError') {
|
|
44
|
+
onError?.(err);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
})();
|
|
48
|
+
return () => controller.abort();
|
|
49
|
+
}
|
|
50
|
+
// ── Auth client ───────────────────────────────────────────────
|
|
51
|
+
class AuthClient {
|
|
52
|
+
ez;
|
|
53
|
+
_token = null;
|
|
54
|
+
_currentUser = null;
|
|
55
|
+
_listeners = new Set();
|
|
56
|
+
constructor(ez) {
|
|
57
|
+
this.ez = ez;
|
|
58
|
+
}
|
|
59
|
+
get currentUser() {
|
|
60
|
+
return this._currentUser;
|
|
61
|
+
}
|
|
62
|
+
/** @internal */
|
|
63
|
+
_getToken() {
|
|
64
|
+
return this._token;
|
|
65
|
+
}
|
|
66
|
+
async signUp(opts) {
|
|
67
|
+
const res = await fetch(`${this.ez['url']}/api/auth/signup`, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: { 'Content-Type': 'application/json', ...this.ez['_authHeaders']() },
|
|
70
|
+
body: JSON.stringify(opts),
|
|
71
|
+
});
|
|
72
|
+
if (!res.ok) {
|
|
73
|
+
const body = await res.json().catch(() => ({ error: res.statusText }));
|
|
74
|
+
throw new Error(`ezbase: ${body.error || res.statusText}`);
|
|
75
|
+
}
|
|
76
|
+
const data = await res.json();
|
|
77
|
+
this._token = data.token;
|
|
78
|
+
this._currentUser = data.user;
|
|
79
|
+
this._notify();
|
|
80
|
+
return data;
|
|
81
|
+
}
|
|
82
|
+
async signIn(opts) {
|
|
83
|
+
const res = await fetch(`${this.ez['url']}/api/auth/signin`, {
|
|
84
|
+
method: 'POST',
|
|
85
|
+
headers: { 'Content-Type': 'application/json', ...this.ez['_authHeaders']() },
|
|
86
|
+
body: JSON.stringify(opts),
|
|
87
|
+
});
|
|
88
|
+
if (!res.ok) {
|
|
89
|
+
const body = await res.json().catch(() => ({ error: res.statusText }));
|
|
90
|
+
throw new Error(`ezbase: ${body.error || res.statusText}`);
|
|
91
|
+
}
|
|
92
|
+
const data = await res.json();
|
|
93
|
+
this._token = data.token;
|
|
94
|
+
this._currentUser = data.user;
|
|
95
|
+
this._notify();
|
|
96
|
+
return data;
|
|
97
|
+
}
|
|
98
|
+
signOut() {
|
|
99
|
+
this._token = null;
|
|
100
|
+
this._currentUser = null;
|
|
101
|
+
this._notify();
|
|
102
|
+
}
|
|
103
|
+
restoreSession(token, user) {
|
|
104
|
+
this._token = token;
|
|
105
|
+
this._currentUser = user;
|
|
106
|
+
this._notify();
|
|
107
|
+
}
|
|
108
|
+
onAuthStateChanged(callback) {
|
|
109
|
+
this._listeners.add(callback);
|
|
110
|
+
return () => { this._listeners.delete(callback); };
|
|
111
|
+
}
|
|
112
|
+
_notify() {
|
|
113
|
+
for (const cb of this._listeners) {
|
|
114
|
+
try {
|
|
115
|
+
cb(this._currentUser);
|
|
116
|
+
}
|
|
117
|
+
catch { }
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// ── EzBase client ─────────────────────────────────────────────
|
|
122
|
+
class EzBase {
|
|
123
|
+
url;
|
|
124
|
+
adminKey;
|
|
125
|
+
auth;
|
|
126
|
+
constructor(urlOrOpts) {
|
|
127
|
+
if (typeof urlOrOpts === 'string') {
|
|
128
|
+
this.url = urlOrOpts.replace(/\/$/, '');
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
this.url = urlOrOpts.url.replace(/\/$/, '');
|
|
132
|
+
this.adminKey = urlOrOpts.adminKey;
|
|
133
|
+
}
|
|
134
|
+
this.auth = new AuthClient(this);
|
|
135
|
+
}
|
|
136
|
+
collection(name) {
|
|
137
|
+
return new CollectionRef(this, name);
|
|
138
|
+
}
|
|
139
|
+
/** @internal */
|
|
140
|
+
_getUrl() {
|
|
141
|
+
return this.url;
|
|
142
|
+
}
|
|
143
|
+
/** @internal */
|
|
144
|
+
_authHeaders() {
|
|
145
|
+
const token = this.adminKey || this.auth._getToken();
|
|
146
|
+
if (token)
|
|
147
|
+
return { Authorization: `Bearer ${token}` };
|
|
148
|
+
return {};
|
|
149
|
+
}
|
|
150
|
+
/** @internal — append token as query param for SSE (EventSource can't send headers) */
|
|
151
|
+
_sseTokenParam() {
|
|
152
|
+
const token = this.adminKey || this.auth._getToken();
|
|
153
|
+
if (token)
|
|
154
|
+
return `token=${encodeURIComponent(token)}`;
|
|
155
|
+
return '';
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// ── Collection reference ──────────────────────────────────────
|
|
159
|
+
class CollectionRef {
|
|
160
|
+
ez;
|
|
161
|
+
name;
|
|
162
|
+
constructor(ez, name) {
|
|
163
|
+
this.ez = ez;
|
|
164
|
+
this.name = name;
|
|
165
|
+
}
|
|
166
|
+
doc(id) {
|
|
167
|
+
return new DocRef(this.ez, this.name, id);
|
|
168
|
+
}
|
|
169
|
+
where(field, op, value) {
|
|
170
|
+
return new QueryRef(this.ez, this.name).where(field, op, value);
|
|
171
|
+
}
|
|
172
|
+
orderBy(field, direction) {
|
|
173
|
+
return new QueryRef(this.ez, this.name).orderBy(field, direction);
|
|
174
|
+
}
|
|
175
|
+
limit(n) {
|
|
176
|
+
return new QueryRef(this.ez, this.name).limit(n);
|
|
177
|
+
}
|
|
178
|
+
async add(data) {
|
|
179
|
+
const res = await fetch(`${this.ez._getUrl()}/api/collections/${encodeURIComponent(this.name)}`, {
|
|
180
|
+
method: 'POST',
|
|
181
|
+
headers: { 'Content-Type': 'application/json', ...this.ez._authHeaders() },
|
|
182
|
+
body: JSON.stringify(data),
|
|
183
|
+
});
|
|
184
|
+
if (!res.ok)
|
|
185
|
+
throw new Error(`ezbase: ${res.status} ${await res.text()}`);
|
|
186
|
+
return res.json();
|
|
187
|
+
}
|
|
188
|
+
async get() {
|
|
189
|
+
const res = await fetch(`${this.ez._getUrl()}/api/collections/${encodeURIComponent(this.name)}`, { headers: this.ez._authHeaders() });
|
|
190
|
+
if (!res.ok)
|
|
191
|
+
throw new Error(`ezbase: ${res.status} ${await res.text()}`);
|
|
192
|
+
return res.json();
|
|
193
|
+
}
|
|
194
|
+
onSnapshot(callback, onError) {
|
|
195
|
+
const tp = this.ez._sseTokenParam();
|
|
196
|
+
const sep = tp ? '?' : '';
|
|
197
|
+
const url = `${this.ez._getUrl()}/api/collections/${encodeURIComponent(this.name)}/sse${sep}${tp}`;
|
|
198
|
+
return sseConnect(url, this.ez._authHeaders(), (data) => callback(JSON.parse(data)), onError);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// ── Document reference ────────────────────────────────────────
|
|
202
|
+
class DocRef {
|
|
203
|
+
ez;
|
|
204
|
+
collection;
|
|
205
|
+
id;
|
|
206
|
+
constructor(ez, collection, id) {
|
|
207
|
+
this.ez = ez;
|
|
208
|
+
this.collection = collection;
|
|
209
|
+
this.id = id;
|
|
210
|
+
}
|
|
211
|
+
path() {
|
|
212
|
+
return `${this.ez._getUrl()}/api/collections/${encodeURIComponent(this.collection)}/${encodeURIComponent(this.id)}`;
|
|
213
|
+
}
|
|
214
|
+
async get() {
|
|
215
|
+
const res = await fetch(this.path(), { headers: this.ez._authHeaders() });
|
|
216
|
+
if (res.status === 404)
|
|
217
|
+
return null;
|
|
218
|
+
if (!res.ok)
|
|
219
|
+
throw new Error(`ezbase: ${res.status} ${await res.text()}`);
|
|
220
|
+
return res.json();
|
|
221
|
+
}
|
|
222
|
+
async set(data) {
|
|
223
|
+
const res = await fetch(this.path(), {
|
|
224
|
+
method: 'PUT',
|
|
225
|
+
headers: { 'Content-Type': 'application/json', ...this.ez._authHeaders() },
|
|
226
|
+
body: JSON.stringify(data),
|
|
227
|
+
});
|
|
228
|
+
if (!res.ok)
|
|
229
|
+
throw new Error(`ezbase: ${res.status} ${await res.text()}`);
|
|
230
|
+
return res.json();
|
|
231
|
+
}
|
|
232
|
+
async update(data) {
|
|
233
|
+
const res = await fetch(this.path(), {
|
|
234
|
+
method: 'PATCH',
|
|
235
|
+
headers: { 'Content-Type': 'application/json', ...this.ez._authHeaders() },
|
|
236
|
+
body: JSON.stringify(data),
|
|
237
|
+
});
|
|
238
|
+
if (!res.ok)
|
|
239
|
+
throw new Error(`ezbase: ${res.status} ${await res.text()}`);
|
|
240
|
+
return res.json();
|
|
241
|
+
}
|
|
242
|
+
async delete() {
|
|
243
|
+
const res = await fetch(this.path(), {
|
|
244
|
+
method: 'DELETE',
|
|
245
|
+
headers: this.ez._authHeaders(),
|
|
246
|
+
});
|
|
247
|
+
if (!res.ok)
|
|
248
|
+
throw new Error(`ezbase: ${res.status} ${await res.text()}`);
|
|
249
|
+
}
|
|
250
|
+
onSnapshot(callback, onError) {
|
|
251
|
+
const tp = this.ez._sseTokenParam();
|
|
252
|
+
const sep = tp ? '?' : '';
|
|
253
|
+
const url = `${this.path()}/sse${sep}${tp}`;
|
|
254
|
+
return sseConnect(url, this.ez._authHeaders(), (data) => callback(JSON.parse(data)), onError);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// ── Query reference (chainable) ──────────────────────────────
|
|
258
|
+
class QueryRef {
|
|
259
|
+
ez;
|
|
260
|
+
collection;
|
|
261
|
+
wheres = [];
|
|
262
|
+
_orderBy;
|
|
263
|
+
_order;
|
|
264
|
+
_limit;
|
|
265
|
+
constructor(ez, collection) {
|
|
266
|
+
this.ez = ez;
|
|
267
|
+
this.collection = collection;
|
|
268
|
+
}
|
|
269
|
+
where(field, op, value) {
|
|
270
|
+
this.wheres.push([field, op, value]);
|
|
271
|
+
return this;
|
|
272
|
+
}
|
|
273
|
+
orderBy(field, direction = 'asc') {
|
|
274
|
+
this._orderBy = field;
|
|
275
|
+
this._order = direction;
|
|
276
|
+
return this;
|
|
277
|
+
}
|
|
278
|
+
limit(n) {
|
|
279
|
+
this._limit = n;
|
|
280
|
+
return this;
|
|
281
|
+
}
|
|
282
|
+
buildParams() {
|
|
283
|
+
const params = new URLSearchParams();
|
|
284
|
+
if (this.wheres.length > 0) {
|
|
285
|
+
params.set('where', JSON.stringify(this.wheres));
|
|
286
|
+
}
|
|
287
|
+
if (this._orderBy) {
|
|
288
|
+
params.set('orderBy', this._orderBy);
|
|
289
|
+
if (this._order)
|
|
290
|
+
params.set('order', this._order);
|
|
291
|
+
}
|
|
292
|
+
if (this._limit !== undefined) {
|
|
293
|
+
params.set('limit', String(this._limit));
|
|
294
|
+
}
|
|
295
|
+
const qs = params.toString();
|
|
296
|
+
return qs ? '?' + qs : '';
|
|
297
|
+
}
|
|
298
|
+
async get() {
|
|
299
|
+
const url = `${this.ez._getUrl()}/api/collections/${encodeURIComponent(this.collection)}${this.buildParams()}`;
|
|
300
|
+
const res = await fetch(url, { headers: this.ez._authHeaders() });
|
|
301
|
+
if (!res.ok)
|
|
302
|
+
throw new Error(`ezbase: ${res.status} ${await res.text()}`);
|
|
303
|
+
return res.json();
|
|
304
|
+
}
|
|
305
|
+
onSnapshot(callback, onError) {
|
|
306
|
+
const base = `${this.ez._getUrl()}/api/collections/${encodeURIComponent(this.collection)}/sse${this.buildParams()}`;
|
|
307
|
+
const tp = this.ez._sseTokenParam();
|
|
308
|
+
const sep = base.includes('?') ? '&' : '?';
|
|
309
|
+
const url = tp ? `${base}${sep}${tp}` : base;
|
|
310
|
+
return sseConnect(url, this.ez._authHeaders(), (data) => callback(JSON.parse(data)), onError);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
export { EzBase, CollectionRef, DocRef, QueryRef, AuthClient };
|
|
314
|
+
export default EzBase;
|
|
315
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ezwrld/ezbase",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Client SDK for ezbase — a self-hosted document database with real-time subscriptions",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"src",
|
|
17
|
+
"tsconfig.json"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsc",
|
|
21
|
+
"dev": "tsc --watch",
|
|
22
|
+
"prepublishOnly": "tsc"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"database",
|
|
26
|
+
"document-database",
|
|
27
|
+
"realtime",
|
|
28
|
+
"sse",
|
|
29
|
+
"firebase-alternative",
|
|
30
|
+
"self-hosted",
|
|
31
|
+
"baas"
|
|
32
|
+
],
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://github.com/ezwrld/ezbase",
|
|
37
|
+
"directory": "sdk"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"typescript": "^5.5.0"
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
type WhereOp = '==' | '!=' | '<' | '>' | '<=' | '>='
|
|
2
|
+
type WhereClause = [string, WhereOp, unknown]
|
|
3
|
+
type OrderDir = 'asc' | 'desc'
|
|
4
|
+
|
|
5
|
+
interface Document<T = Record<string, unknown>> {
|
|
6
|
+
id: string
|
|
7
|
+
data: T
|
|
8
|
+
created: number
|
|
9
|
+
updated: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface EzBaseOptions {
|
|
13
|
+
url: string
|
|
14
|
+
adminKey?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface AuthUser {
|
|
18
|
+
id: string
|
|
19
|
+
email: string
|
|
20
|
+
role: string
|
|
21
|
+
created?: number
|
|
22
|
+
updated?: number
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
type AuthStateCallback = (user: AuthUser | null) => void
|
|
26
|
+
|
|
27
|
+
// ── SSE helper (works in Node 18+, browsers, Deno, Bun) ──────
|
|
28
|
+
function sseConnect(
|
|
29
|
+
url: string,
|
|
30
|
+
headers: Record<string, string>,
|
|
31
|
+
onSnapshot: (data: string) => void,
|
|
32
|
+
onError?: (err: Error) => void
|
|
33
|
+
): () => void {
|
|
34
|
+
const controller = new AbortController()
|
|
35
|
+
|
|
36
|
+
;(async () => {
|
|
37
|
+
try {
|
|
38
|
+
const res = await fetch(url, {
|
|
39
|
+
headers: { Accept: 'text/event-stream', ...headers },
|
|
40
|
+
signal: controller.signal,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
if (!res.ok || !res.body) {
|
|
44
|
+
throw new Error(`SSE connect failed: ${res.status}`)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const reader = res.body.getReader()
|
|
48
|
+
const decoder = new TextDecoder()
|
|
49
|
+
let buf = ''
|
|
50
|
+
let eventType = ''
|
|
51
|
+
let dataLines: string[] = []
|
|
52
|
+
|
|
53
|
+
while (true) {
|
|
54
|
+
const { done, value } = await reader.read()
|
|
55
|
+
if (done) break
|
|
56
|
+
|
|
57
|
+
buf += decoder.decode(value, { stream: true })
|
|
58
|
+
const lines = buf.split('\n')
|
|
59
|
+
buf = lines.pop()!
|
|
60
|
+
|
|
61
|
+
for (const line of lines) {
|
|
62
|
+
if (line.startsWith('event:')) {
|
|
63
|
+
eventType = line.slice(6).trim()
|
|
64
|
+
} else if (line.startsWith('data:')) {
|
|
65
|
+
dataLines.push(line.slice(5).trim())
|
|
66
|
+
} else if (line === '' || line === '\r') {
|
|
67
|
+
if (eventType === 'snapshot' && dataLines.length > 0) {
|
|
68
|
+
onSnapshot(dataLines.join('\n'))
|
|
69
|
+
}
|
|
70
|
+
eventType = ''
|
|
71
|
+
dataLines = []
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} catch (err: unknown) {
|
|
76
|
+
if (err instanceof Error && err.name !== 'AbortError') {
|
|
77
|
+
onError?.(err)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
})()
|
|
81
|
+
|
|
82
|
+
return () => controller.abort()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ── Auth client ───────────────────────────────────────────────
|
|
86
|
+
class AuthClient {
|
|
87
|
+
private _token: string | null = null
|
|
88
|
+
private _currentUser: AuthUser | null = null
|
|
89
|
+
private _listeners = new Set<AuthStateCallback>()
|
|
90
|
+
|
|
91
|
+
constructor(private ez: EzBase) {}
|
|
92
|
+
|
|
93
|
+
get currentUser(): AuthUser | null {
|
|
94
|
+
return this._currentUser
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** @internal */
|
|
98
|
+
_getToken(): string | null {
|
|
99
|
+
return this._token
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async signUp(opts: { email: string; password: string }): Promise<{ token: string; user: AuthUser }> {
|
|
103
|
+
const res = await fetch(`${this.ez['url']}/api/auth/signup`, {
|
|
104
|
+
method: 'POST',
|
|
105
|
+
headers: { 'Content-Type': 'application/json', ...this.ez['_authHeaders']() },
|
|
106
|
+
body: JSON.stringify(opts),
|
|
107
|
+
})
|
|
108
|
+
if (!res.ok) {
|
|
109
|
+
const body = await res.json().catch(() => ({ error: res.statusText }))
|
|
110
|
+
throw new Error(`ezbase: ${body.error || res.statusText}`)
|
|
111
|
+
}
|
|
112
|
+
const data = await res.json()
|
|
113
|
+
this._token = data.token
|
|
114
|
+
this._currentUser = data.user
|
|
115
|
+
this._notify()
|
|
116
|
+
return data
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
async signIn(opts: { email: string; password: string }): Promise<{ token: string; user: AuthUser }> {
|
|
120
|
+
const res = await fetch(`${this.ez['url']}/api/auth/signin`, {
|
|
121
|
+
method: 'POST',
|
|
122
|
+
headers: { 'Content-Type': 'application/json', ...this.ez['_authHeaders']() },
|
|
123
|
+
body: JSON.stringify(opts),
|
|
124
|
+
})
|
|
125
|
+
if (!res.ok) {
|
|
126
|
+
const body = await res.json().catch(() => ({ error: res.statusText }))
|
|
127
|
+
throw new Error(`ezbase: ${body.error || res.statusText}`)
|
|
128
|
+
}
|
|
129
|
+
const data = await res.json()
|
|
130
|
+
this._token = data.token
|
|
131
|
+
this._currentUser = data.user
|
|
132
|
+
this._notify()
|
|
133
|
+
return data
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
signOut() {
|
|
137
|
+
this._token = null
|
|
138
|
+
this._currentUser = null
|
|
139
|
+
this._notify()
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
restoreSession(token: string, user: AuthUser) {
|
|
143
|
+
this._token = token
|
|
144
|
+
this._currentUser = user
|
|
145
|
+
this._notify()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
onAuthStateChanged(callback: AuthStateCallback): () => void {
|
|
149
|
+
this._listeners.add(callback)
|
|
150
|
+
return () => { this._listeners.delete(callback) }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private _notify() {
|
|
154
|
+
for (const cb of this._listeners) {
|
|
155
|
+
try { cb(this._currentUser) } catch {}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── EzBase client ─────────────────────────────────────────────
|
|
161
|
+
class EzBase {
|
|
162
|
+
private url: string
|
|
163
|
+
private adminKey?: string
|
|
164
|
+
readonly auth: AuthClient
|
|
165
|
+
|
|
166
|
+
constructor(urlOrOpts: string | EzBaseOptions) {
|
|
167
|
+
if (typeof urlOrOpts === 'string') {
|
|
168
|
+
this.url = urlOrOpts.replace(/\/$/, '')
|
|
169
|
+
} else {
|
|
170
|
+
this.url = urlOrOpts.url.replace(/\/$/, '')
|
|
171
|
+
this.adminKey = urlOrOpts.adminKey
|
|
172
|
+
}
|
|
173
|
+
this.auth = new AuthClient(this)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
collection<T = Record<string, unknown>>(name: string): CollectionRef<T> {
|
|
177
|
+
return new CollectionRef<T>(this, name)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** @internal */
|
|
181
|
+
_getUrl(): string {
|
|
182
|
+
return this.url
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** @internal */
|
|
186
|
+
_authHeaders(): Record<string, string> {
|
|
187
|
+
const token = this.adminKey || this.auth._getToken()
|
|
188
|
+
if (token) return { Authorization: `Bearer ${token}` }
|
|
189
|
+
return {}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** @internal — append token as query param for SSE (EventSource can't send headers) */
|
|
193
|
+
_sseTokenParam(): string {
|
|
194
|
+
const token = this.adminKey || this.auth._getToken()
|
|
195
|
+
if (token) return `token=${encodeURIComponent(token)}`
|
|
196
|
+
return ''
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// ── Collection reference ──────────────────────────────────────
|
|
201
|
+
class CollectionRef<T = Record<string, unknown>> {
|
|
202
|
+
constructor(
|
|
203
|
+
private ez: EzBase,
|
|
204
|
+
private name: string
|
|
205
|
+
) {}
|
|
206
|
+
|
|
207
|
+
doc(id: string): DocRef<T> {
|
|
208
|
+
return new DocRef<T>(this.ez, this.name, id)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
where(field: string, op: WhereOp, value: unknown): QueryRef<T> {
|
|
212
|
+
return new QueryRef<T>(this.ez, this.name).where(field, op, value)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
orderBy(field: string, direction?: OrderDir): QueryRef<T> {
|
|
216
|
+
return new QueryRef<T>(this.ez, this.name).orderBy(field, direction)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
limit(n: number): QueryRef<T> {
|
|
220
|
+
return new QueryRef<T>(this.ez, this.name).limit(n)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async add(data: Partial<T>): Promise<Document<T>> {
|
|
224
|
+
const res = await fetch(
|
|
225
|
+
`${this.ez._getUrl()}/api/collections/${encodeURIComponent(this.name)}`,
|
|
226
|
+
{
|
|
227
|
+
method: 'POST',
|
|
228
|
+
headers: { 'Content-Type': 'application/json', ...this.ez._authHeaders() },
|
|
229
|
+
body: JSON.stringify(data),
|
|
230
|
+
}
|
|
231
|
+
)
|
|
232
|
+
if (!res.ok) throw new Error(`ezbase: ${res.status} ${await res.text()}`)
|
|
233
|
+
return res.json()
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async get(): Promise<Document<T>[]> {
|
|
237
|
+
const res = await fetch(
|
|
238
|
+
`${this.ez._getUrl()}/api/collections/${encodeURIComponent(this.name)}`,
|
|
239
|
+
{ headers: this.ez._authHeaders() }
|
|
240
|
+
)
|
|
241
|
+
if (!res.ok) throw new Error(`ezbase: ${res.status} ${await res.text()}`)
|
|
242
|
+
return res.json()
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
onSnapshot(
|
|
246
|
+
callback: (docs: Document<T>[]) => void,
|
|
247
|
+
onError?: (err: Error) => void
|
|
248
|
+
): () => void {
|
|
249
|
+
const tp = this.ez._sseTokenParam()
|
|
250
|
+
const sep = tp ? '?' : ''
|
|
251
|
+
const url = `${this.ez._getUrl()}/api/collections/${encodeURIComponent(this.name)}/sse${sep}${tp}`
|
|
252
|
+
return sseConnect(url, this.ez._authHeaders(), (data) => callback(JSON.parse(data)), onError)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ── Document reference ────────────────────────────────────────
|
|
257
|
+
class DocRef<T = Record<string, unknown>> {
|
|
258
|
+
constructor(
|
|
259
|
+
private ez: EzBase,
|
|
260
|
+
private collection: string,
|
|
261
|
+
private id: string
|
|
262
|
+
) {}
|
|
263
|
+
|
|
264
|
+
private path(): string {
|
|
265
|
+
return `${this.ez._getUrl()}/api/collections/${encodeURIComponent(this.collection)}/${encodeURIComponent(this.id)}`
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async get(): Promise<Document<T> | null> {
|
|
269
|
+
const res = await fetch(this.path(), { headers: this.ez._authHeaders() })
|
|
270
|
+
if (res.status === 404) return null
|
|
271
|
+
if (!res.ok) throw new Error(`ezbase: ${res.status} ${await res.text()}`)
|
|
272
|
+
return res.json()
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async set(data: T): Promise<Document<T>> {
|
|
276
|
+
const res = await fetch(this.path(), {
|
|
277
|
+
method: 'PUT',
|
|
278
|
+
headers: { 'Content-Type': 'application/json', ...this.ez._authHeaders() },
|
|
279
|
+
body: JSON.stringify(data),
|
|
280
|
+
})
|
|
281
|
+
if (!res.ok) throw new Error(`ezbase: ${res.status} ${await res.text()}`)
|
|
282
|
+
return res.json()
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async update(data: Partial<T>): Promise<Document<T>> {
|
|
286
|
+
const res = await fetch(this.path(), {
|
|
287
|
+
method: 'PATCH',
|
|
288
|
+
headers: { 'Content-Type': 'application/json', ...this.ez._authHeaders() },
|
|
289
|
+
body: JSON.stringify(data),
|
|
290
|
+
})
|
|
291
|
+
if (!res.ok) throw new Error(`ezbase: ${res.status} ${await res.text()}`)
|
|
292
|
+
return res.json()
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
async delete(): Promise<void> {
|
|
296
|
+
const res = await fetch(this.path(), {
|
|
297
|
+
method: 'DELETE',
|
|
298
|
+
headers: this.ez._authHeaders(),
|
|
299
|
+
})
|
|
300
|
+
if (!res.ok) throw new Error(`ezbase: ${res.status} ${await res.text()}`)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
onSnapshot(
|
|
304
|
+
callback: (doc: Document<T> | null) => void,
|
|
305
|
+
onError?: (err: Error) => void
|
|
306
|
+
): () => void {
|
|
307
|
+
const tp = this.ez._sseTokenParam()
|
|
308
|
+
const sep = tp ? '?' : ''
|
|
309
|
+
const url = `${this.path()}/sse${sep}${tp}`
|
|
310
|
+
return sseConnect(url, this.ez._authHeaders(), (data) => callback(JSON.parse(data)), onError)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// ── Query reference (chainable) ──────────────────────────────
|
|
315
|
+
class QueryRef<T = Record<string, unknown>> {
|
|
316
|
+
private wheres: WhereClause[] = []
|
|
317
|
+
private _orderBy?: string
|
|
318
|
+
private _order?: OrderDir
|
|
319
|
+
private _limit?: number
|
|
320
|
+
|
|
321
|
+
constructor(
|
|
322
|
+
private ez: EzBase,
|
|
323
|
+
private collection: string
|
|
324
|
+
) {}
|
|
325
|
+
|
|
326
|
+
where(field: string, op: WhereOp, value: unknown): this {
|
|
327
|
+
this.wheres.push([field, op, value])
|
|
328
|
+
return this
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
orderBy(field: string, direction: OrderDir = 'asc'): this {
|
|
332
|
+
this._orderBy = field
|
|
333
|
+
this._order = direction
|
|
334
|
+
return this
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
limit(n: number): this {
|
|
338
|
+
this._limit = n
|
|
339
|
+
return this
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private buildParams(): string {
|
|
343
|
+
const params = new URLSearchParams()
|
|
344
|
+
if (this.wheres.length > 0) {
|
|
345
|
+
params.set('where', JSON.stringify(this.wheres))
|
|
346
|
+
}
|
|
347
|
+
if (this._orderBy) {
|
|
348
|
+
params.set('orderBy', this._orderBy)
|
|
349
|
+
if (this._order) params.set('order', this._order)
|
|
350
|
+
}
|
|
351
|
+
if (this._limit !== undefined) {
|
|
352
|
+
params.set('limit', String(this._limit))
|
|
353
|
+
}
|
|
354
|
+
const qs = params.toString()
|
|
355
|
+
return qs ? '?' + qs : ''
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
async get(): Promise<Document<T>[]> {
|
|
359
|
+
const url = `${this.ez._getUrl()}/api/collections/${encodeURIComponent(this.collection)}${this.buildParams()}`
|
|
360
|
+
const res = await fetch(url, { headers: this.ez._authHeaders() })
|
|
361
|
+
if (!res.ok) throw new Error(`ezbase: ${res.status} ${await res.text()}`)
|
|
362
|
+
return res.json()
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
onSnapshot(
|
|
366
|
+
callback: (docs: Document<T>[]) => void,
|
|
367
|
+
onError?: (err: Error) => void
|
|
368
|
+
): () => void {
|
|
369
|
+
const base = `${this.ez._getUrl()}/api/collections/${encodeURIComponent(this.collection)}/sse${this.buildParams()}`
|
|
370
|
+
const tp = this.ez._sseTokenParam()
|
|
371
|
+
const sep = base.includes('?') ? '&' : '?'
|
|
372
|
+
const url = tp ? `${base}${sep}${tp}` : base
|
|
373
|
+
return sseConnect(url, this.ez._authHeaders(), (data) => callback(JSON.parse(data)), onError)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export { EzBase, CollectionRef, DocRef, QueryRef, AuthClient }
|
|
378
|
+
export type { Document, WhereOp, OrderDir, EzBaseOptions, AuthUser }
|
|
379
|
+
export default EzBase
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"sourceMap": true,
|
|
12
|
+
"skipLibCheck": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src"]
|
|
15
|
+
}
|