|
1 | | -import { Database } from './drivers/database'; |
2 | | -import { SQLiteCloudError } from './drivers/types'; |
3 | | -import { Fetch, fetchWithAuth } from './drivers/fetch'; |
4 | | -import { DEFAULT_HEADERS, DEFAULT_WEBLITE_VERSION, WEBLITE_PORT } from './drivers/constants'; |
| 1 | +import { Database } from './drivers/database' |
| 2 | +import { SQLiteCloudError, UploadOptions } from './drivers/types' |
| 3 | +import { Fetch, fetchWithAuth } from './drivers/fetch' |
| 4 | +import { DEFAULT_HEADERS, DEFAULT_WEBLITE_VERSION, WEBLITE_PORT } from './drivers/constants' |
| 5 | +import { PubSub } from './drivers/pubsub-refactor' |
5 | 6 |
|
6 | 7 | interface SQLiteCloudClientConfig { |
7 | | - connectionString: string; |
8 | | - fetch?: Fetch; |
| 8 | + connectionString: string |
| 9 | + fetch?: Fetch |
| 10 | +} |
| 11 | + |
| 12 | +interface ISQLiteCloudClient { |
| 13 | + pubSub: PubSub |
| 14 | + db: Database |
| 15 | + upload(databaseName: string, file: File | Buffer | Blob | string, opts?: UploadOptions): Promise<Response> |
| 16 | + download(databaseName: string): Promise<ArrayBuffer | Blob> |
| 17 | + delete(databaseName: string): Promise<Response> |
| 18 | + listDatabases(): Promise<any> |
9 | 19 | } |
10 | 20 |
|
11 | 21 | const parseConnectionString = (connectionString: string) => { |
12 | | - const url = new URL(connectionString); |
| 22 | + const url = new URL(connectionString) |
13 | 23 | return { |
14 | 24 | host: url.hostname, |
15 | 25 | port: url.port, |
16 | 26 | database: url.pathname.slice(1), |
17 | | - apiKey: url.searchParams.get('apikey'), |
| 27 | + apiKey: url.searchParams.get('apikey') |
18 | 28 | } |
19 | 29 | } |
20 | 30 |
|
21 | | -type UploadOptions = { |
22 | | - replace?: boolean; |
23 | | -} |
24 | | - |
25 | | -class SQLiteCloudClient { |
| 31 | +export class SQLiteCloudClient implements ISQLiteCloudClient { |
26 | 32 | // TODO: Add support for custom fetch |
27 | 33 | private fetch: Fetch |
28 | 34 | private connectionString: string |
29 | 35 | private webliteUrl: string |
30 | | - public db: Database |
| 36 | + private _db: Database |
| 37 | + private _pubSub: PubSub |
31 | 38 |
|
32 | 39 | constructor(config: SQLiteCloudClientConfig | string) { |
33 | | - let connectionString: string; |
| 40 | + let connectionString: string |
34 | 41 | if (typeof config === 'string') { |
35 | | - connectionString = config; |
| 42 | + connectionString = config |
36 | 43 | } else { |
37 | | - connectionString = config.connectionString; |
| 44 | + connectionString = config.connectionString |
38 | 45 | } |
39 | | - // TODO: validate connection string |
40 | | - this.connectionString = connectionString; |
41 | | - this.db = new Database(this.connectionString); |
42 | 46 |
|
43 | | - const { |
44 | | - host, |
45 | | - apiKey, |
46 | | - } = parseConnectionString(this.connectionString); |
| 47 | + this.connectionString = connectionString |
| 48 | + this._db = new Database(this.connectionString) |
| 49 | + this._pubSub = new PubSub(this.db) |
| 50 | + this.fetch = fetchWithAuth(this.connectionString) |
47 | 51 |
|
48 | | - if (!apiKey) { |
49 | | - throw new SQLiteCloudError('apiKey is required'); |
50 | | - } |
| 52 | + const { host } = parseConnectionString(this.connectionString) |
| 53 | + |
| 54 | + this.webliteUrl = `https://${host}:${WEBLITE_PORT}/${DEFAULT_WEBLITE_VERSION}/weblite` |
| 55 | + } |
| 56 | + |
| 57 | + get pubSub() { |
| 58 | + return this._pubSub |
| 59 | + } |
51 | 60 |
|
52 | | - this.fetch = fetchWithAuth(this.connectionString); |
53 | | - this.webliteUrl = `https://${host}:${WEBLITE_PORT}/${DEFAULT_WEBLITE_VERSION}/weblite`; |
| 61 | + get db() { |
| 62 | + return this._db |
54 | 63 | } |
55 | 64 |
|
56 | 65 | async upload(databaseName: string, file: File | Buffer | Blob | string, opts: UploadOptions = {}) { |
57 | | - const url = `${this.webliteUrl}/${databaseName}`; |
58 | | - let body; |
| 66 | + const url = `${this.webliteUrl}/${databaseName}` |
| 67 | + let body |
59 | 68 | if (file instanceof File) { |
60 | | - body = file; |
| 69 | + body = file |
61 | 70 | } else if (file instanceof Buffer) { |
62 | | - body = file; |
| 71 | + body = file |
63 | 72 | } else if (file instanceof Blob) { |
64 | | - body = file; |
| 73 | + body = file |
65 | 74 | } else { |
66 | 75 | // string |
67 | | - body = new Blob([file]); |
| 76 | + body = new Blob([file]) |
68 | 77 | } |
69 | 78 |
|
70 | 79 | const headers = { |
71 | 80 | 'Content-Type': 'application/octet-stream', |
72 | | - 'X-Client-Info': DEFAULT_HEADERS['X-Client-Info'], |
| 81 | + 'X-Client-Info': DEFAULT_HEADERS['X-Client-Info'] |
73 | 82 | } |
74 | 83 |
|
75 | | - const method = opts.replace ? 'PATCH' : 'POST'; |
| 84 | + const method = opts.replace ? 'PATCH' : 'POST' |
76 | 85 |
|
77 | 86 | const response = await this.fetch(url, { method, body, headers }) |
78 | 87 |
|
79 | 88 | if (!response.ok) { |
80 | | - throw new SQLiteCloudError(`Failed to upload database: ${response.statusText}`); |
| 89 | + throw new SQLiteCloudError(`Failed to upload database: ${response.statusText}`) |
81 | 90 | } |
82 | 91 |
|
83 | | - return response; |
| 92 | + return response |
84 | 93 | } |
85 | 94 |
|
86 | 95 | async download(databaseName: string) { |
87 | | - const url = `${this.webliteUrl}/${databaseName}`; |
| 96 | + const url = `${this.webliteUrl}/${databaseName}` |
88 | 97 | const response = await this.fetch(url, { method: 'GET' }) |
89 | 98 | if (!response.ok) { |
90 | | - throw new SQLiteCloudError(`Failed to download database: ${response.statusText}`); |
| 99 | + throw new SQLiteCloudError(`Failed to download database: ${response.statusText}`) |
91 | 100 | } |
92 | | - const isNode = typeof window === 'undefined'; |
93 | | - return isNode ? await response.arrayBuffer() : await response.blob(); |
| 101 | + const isNode = typeof window === 'undefined' |
| 102 | + return isNode ? await response.arrayBuffer() : await response.blob() |
94 | 103 | } |
95 | 104 |
|
96 | 105 | async delete(databaseName: string) { |
97 | | - const url = `${this.webliteUrl}/${databaseName}`; |
| 106 | + const url = `${this.webliteUrl}/${databaseName}` |
98 | 107 | const response = await this.fetch(url, { method: 'DELETE' }) |
99 | 108 | if (!response.ok) { |
100 | | - throw new SQLiteCloudError(`Failed to delete database: ${response.statusText}`); |
| 109 | + throw new SQLiteCloudError(`Failed to delete database: ${response.statusText}`) |
101 | 110 | } |
102 | | - return response; |
| 111 | + return response |
103 | 112 | } |
104 | 113 |
|
105 | 114 | async listDatabases() { |
106 | | - const url = `${this.webliteUrl}/databases`; |
| 115 | + const url = `${this.webliteUrl}/databases` |
107 | 116 | const response = await this.fetch(url, { method: 'GET' }) |
108 | 117 | if (!response.ok) { |
109 | | - throw new SQLiteCloudError(`Failed to list databases: ${response.statusText}`); |
| 118 | + throw new SQLiteCloudError(`Failed to list databases: ${response.statusText}`) |
110 | 119 | } |
111 | | - return await response.json(); |
| 120 | + return await response.json() |
112 | 121 | } |
113 | 122 | } |
114 | 123 |
|
115 | | -export function createClient(config: SQLiteCloudClientConfig | string) { |
116 | | - return new SQLiteCloudClient(config); |
| 124 | +export function createClient(config: SQLiteCloudClientConfig | string): SQLiteCloudClient { |
| 125 | + return new SQLiteCloudClient(config) |
117 | 126 | } |
0 commit comments