@hanzo/base 0.2.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/chunk-5NHFZRMO.js +1011 -0
- package/dist/chunk-5NHFZRMO.js.map +1 -0
- package/dist/chunk-LBAV5X5P.js +996 -0
- package/dist/chunk-LBAV5X5P.js.map +1 -0
- package/dist/compat/index.d.ts +1 -0
- package/dist/compat/index.js +3 -0
- package/dist/compat/index.js.map +1 -0
- package/dist/core/index.d.ts +368 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -0
- package/dist/crdt/index.d.ts +372 -0
- package/dist/crdt/index.js +3 -0
- package/dist/crdt/index.js.map +1 -0
- package/dist/react/index.d.ts +144 -0
- package/dist/react/index.js +283 -0
- package/dist/react/index.js.map +1 -0
- package/package.json +71 -0
- package/src/compat/index.ts +24 -0
- package/src/core/client.ts +432 -0
- package/src/core/collection.ts +474 -0
- package/src/core/index.ts +37 -0
- package/src/core/realtime.ts +303 -0
- package/src/core/state.ts +112 -0
- package/src/core/store.ts +241 -0
- package/src/crdt/clock.ts +78 -0
- package/src/crdt/counter.ts +130 -0
- package/src/crdt/document.ts +194 -0
- package/src/crdt/index.ts +56 -0
- package/src/crdt/operations.ts +101 -0
- package/src/crdt/register.ts +106 -0
- package/src/crdt/set.ts +172 -0
- package/src/crdt/sync.ts +412 -0
- package/src/crdt/text.ts +274 -0
- package/src/react/context.tsx +87 -0
- package/src/react/hooks.ts +489 -0
- package/src/react/index.ts +56 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CollectionService -- typed CRUD + auth + realtime for a single collection.
|
|
3
|
+
*
|
|
4
|
+
* API-compatible with PocketBase JS SDK's RecordService, extended with
|
|
5
|
+
* reactive features (subscribe/unsubscribe, optimistic writes).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { BaseRecord } from './state.js'
|
|
9
|
+
import type { QueryStore } from './store.js'
|
|
10
|
+
import type { RealtimeService, RealtimeCallback } from './realtime.js'
|
|
11
|
+
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
// Types
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
export interface ListResult<T = BaseRecord> {
|
|
17
|
+
page: number
|
|
18
|
+
perPage: number
|
|
19
|
+
totalItems: number
|
|
20
|
+
totalPages: number
|
|
21
|
+
items: T[]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface RecordQueryOptions {
|
|
25
|
+
filter?: string
|
|
26
|
+
sort?: string
|
|
27
|
+
expand?: string
|
|
28
|
+
fields?: string
|
|
29
|
+
headers?: Record<string, string>
|
|
30
|
+
/** Extra query params merged into the URL. */
|
|
31
|
+
query?: Record<string, string>
|
|
32
|
+
/** Request-scoped AbortSignal. */
|
|
33
|
+
signal?: AbortSignal
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface RecordFullListOptions extends RecordQueryOptions {
|
|
37
|
+
/** Batch size for pagination (default 200). */
|
|
38
|
+
batch?: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface FileOptions {
|
|
42
|
+
thumb?: string
|
|
43
|
+
token?: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface AuthResponse<T = BaseRecord> {
|
|
47
|
+
token: string
|
|
48
|
+
record: T
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface OAuth2Options {
|
|
52
|
+
provider: string
|
|
53
|
+
code: string
|
|
54
|
+
codeVerifier: string
|
|
55
|
+
redirectUrl: string
|
|
56
|
+
createData?: Record<string, unknown>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
// CollectionService
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
export class CollectionService {
|
|
64
|
+
readonly collectionIdOrName: string
|
|
65
|
+
|
|
66
|
+
private readonly _baseUrl: string
|
|
67
|
+
private readonly _getToken: () => string
|
|
68
|
+
private readonly _setAuth: (token: string, record: BaseRecord) => void
|
|
69
|
+
private readonly _store: QueryStore
|
|
70
|
+
private readonly _realtime: RealtimeService
|
|
71
|
+
|
|
72
|
+
constructor(
|
|
73
|
+
collectionIdOrName: string,
|
|
74
|
+
baseUrl: string,
|
|
75
|
+
getToken: () => string,
|
|
76
|
+
setAuth: (token: string, record: BaseRecord) => void,
|
|
77
|
+
store: QueryStore,
|
|
78
|
+
realtime: RealtimeService,
|
|
79
|
+
) {
|
|
80
|
+
this.collectionIdOrName = collectionIdOrName
|
|
81
|
+
this._baseUrl = baseUrl.replace(/\/$/, '')
|
|
82
|
+
this._getToken = getToken
|
|
83
|
+
this._setAuth = setAuth
|
|
84
|
+
this._store = store
|
|
85
|
+
this._realtime = realtime
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ---- CRUD ---------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
async getList<T extends BaseRecord = BaseRecord>(
|
|
91
|
+
page = 1,
|
|
92
|
+
perPage = 30,
|
|
93
|
+
options?: RecordQueryOptions,
|
|
94
|
+
): Promise<ListResult<T>> {
|
|
95
|
+
const params = new URLSearchParams()
|
|
96
|
+
params.set('page', String(page))
|
|
97
|
+
params.set('perPage', String(perPage))
|
|
98
|
+
this._applyOptions(params, options)
|
|
99
|
+
|
|
100
|
+
const result = await this._request<ListResult<T>>(
|
|
101
|
+
'GET',
|
|
102
|
+
`${this._collectionPath()}/records?${params}`,
|
|
103
|
+
undefined,
|
|
104
|
+
options,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
// Cache in store.
|
|
108
|
+
const cacheFilter = options?.filter ?? ''
|
|
109
|
+
this._store.setQuery(
|
|
110
|
+
this.collectionIdOrName,
|
|
111
|
+
cacheFilter,
|
|
112
|
+
result.items as unknown as BaseRecord[],
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return result
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async getFullList<T extends BaseRecord = BaseRecord>(
|
|
119
|
+
options?: RecordFullListOptions,
|
|
120
|
+
): Promise<T[]> {
|
|
121
|
+
const batch = options?.batch ?? 200
|
|
122
|
+
let page = 1
|
|
123
|
+
let all: T[] = []
|
|
124
|
+
|
|
125
|
+
// eslint-disable-next-line no-constant-condition
|
|
126
|
+
while (true) {
|
|
127
|
+
const result = await this.getList<T>(page, batch, options)
|
|
128
|
+
all = all.concat(result.items)
|
|
129
|
+
if (all.length >= result.totalItems || result.items.length < batch) {
|
|
130
|
+
break
|
|
131
|
+
}
|
|
132
|
+
page++
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return all
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async getOne<T extends BaseRecord = BaseRecord>(
|
|
139
|
+
id: string,
|
|
140
|
+
options?: RecordQueryOptions,
|
|
141
|
+
): Promise<T> {
|
|
142
|
+
const params = new URLSearchParams()
|
|
143
|
+
this._applyOptions(params, options)
|
|
144
|
+
const qs = params.toString()
|
|
145
|
+
const path = `${this._collectionPath()}/records/${encodeURIComponent(id)}${qs ? '?' + qs : ''}`
|
|
146
|
+
return this._request<T>('GET', path, undefined, options)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async getFirstListItem<T extends BaseRecord = BaseRecord>(
|
|
150
|
+
filter: string,
|
|
151
|
+
options?: RecordQueryOptions,
|
|
152
|
+
): Promise<T> {
|
|
153
|
+
const opts = { ...options, filter }
|
|
154
|
+
const result = await this.getList<T>(1, 1, opts)
|
|
155
|
+
if (result.items.length === 0) {
|
|
156
|
+
throw new ClientResponseError({
|
|
157
|
+
url: this._baseUrl,
|
|
158
|
+
status: 404,
|
|
159
|
+
data: { message: 'The requested resource wasn\'t found.' },
|
|
160
|
+
})
|
|
161
|
+
}
|
|
162
|
+
return result.items[0]
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async create<T extends BaseRecord = BaseRecord>(
|
|
166
|
+
data: Record<string, unknown> | FormData,
|
|
167
|
+
options?: RecordQueryOptions,
|
|
168
|
+
): Promise<T> {
|
|
169
|
+
const params = new URLSearchParams()
|
|
170
|
+
this._applyOptions(params, options)
|
|
171
|
+
const qs = params.toString()
|
|
172
|
+
const path = `${this._collectionPath()}/records${qs ? '?' + qs : ''}`
|
|
173
|
+
|
|
174
|
+
// Optimistic: generate temp id.
|
|
175
|
+
let mutationId: string | undefined
|
|
176
|
+
if (!(data instanceof FormData) && typeof data === 'object') {
|
|
177
|
+
const tempId = `__temp_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`
|
|
178
|
+
const optimistic: BaseRecord = {
|
|
179
|
+
id: tempId,
|
|
180
|
+
collectionName: this.collectionIdOrName,
|
|
181
|
+
...(data as Record<string, unknown>),
|
|
182
|
+
}
|
|
183
|
+
mutationId = this._store.optimisticSet(this.collectionIdOrName, optimistic)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
const body = data instanceof FormData ? data : JSON.stringify(data)
|
|
188
|
+
const contentType = data instanceof FormData ? undefined : 'application/json'
|
|
189
|
+
const record = await this._request<T>('POST', path, body, options, contentType)
|
|
190
|
+
|
|
191
|
+
// Replace optimistic entry with real server record.
|
|
192
|
+
if (mutationId) {
|
|
193
|
+
this._store.rollbackOptimistic(mutationId)
|
|
194
|
+
}
|
|
195
|
+
this._store.applyServerUpdate(this.collectionIdOrName, 'create', record as unknown as BaseRecord)
|
|
196
|
+
return record
|
|
197
|
+
} catch (err) {
|
|
198
|
+
if (mutationId) {
|
|
199
|
+
this._store.rollbackOptimistic(mutationId)
|
|
200
|
+
}
|
|
201
|
+
throw err
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async update<T extends BaseRecord = BaseRecord>(
|
|
206
|
+
id: string,
|
|
207
|
+
data: Record<string, unknown> | FormData,
|
|
208
|
+
options?: RecordQueryOptions,
|
|
209
|
+
): Promise<T> {
|
|
210
|
+
const params = new URLSearchParams()
|
|
211
|
+
this._applyOptions(params, options)
|
|
212
|
+
const qs = params.toString()
|
|
213
|
+
const path = `${this._collectionPath()}/records/${encodeURIComponent(id)}${qs ? '?' + qs : ''}`
|
|
214
|
+
|
|
215
|
+
// Optimistic update.
|
|
216
|
+
let mutationId: string | undefined
|
|
217
|
+
if (!(data instanceof FormData) && typeof data === 'object') {
|
|
218
|
+
const optimistic: BaseRecord = {
|
|
219
|
+
id,
|
|
220
|
+
collectionName: this.collectionIdOrName,
|
|
221
|
+
...(data as Record<string, unknown>),
|
|
222
|
+
}
|
|
223
|
+
mutationId = this._store.optimisticSet(this.collectionIdOrName, optimistic)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
const body = data instanceof FormData ? data : JSON.stringify(data)
|
|
228
|
+
const contentType = data instanceof FormData ? undefined : 'application/json'
|
|
229
|
+
const record = await this._request<T>('PATCH', path, body, options, contentType)
|
|
230
|
+
|
|
231
|
+
if (mutationId) {
|
|
232
|
+
this._store.rollbackOptimistic(mutationId)
|
|
233
|
+
}
|
|
234
|
+
this._store.applyServerUpdate(this.collectionIdOrName, 'update', record as unknown as BaseRecord)
|
|
235
|
+
return record
|
|
236
|
+
} catch (err) {
|
|
237
|
+
if (mutationId) {
|
|
238
|
+
this._store.rollbackOptimistic(mutationId)
|
|
239
|
+
}
|
|
240
|
+
throw err
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async delete(id: string, options?: RecordQueryOptions): Promise<boolean> {
|
|
245
|
+
const params = new URLSearchParams()
|
|
246
|
+
this._applyOptions(params, options)
|
|
247
|
+
const qs = params.toString()
|
|
248
|
+
const path = `${this._collectionPath()}/records/${encodeURIComponent(id)}${qs ? '?' + qs : ''}`
|
|
249
|
+
|
|
250
|
+
// Optimistic delete.
|
|
251
|
+
const mutationId = this._store.optimisticDelete(this.collectionIdOrName, id)
|
|
252
|
+
|
|
253
|
+
try {
|
|
254
|
+
await this._request<void>('DELETE', path, undefined, options)
|
|
255
|
+
this._store.rollbackOptimistic(mutationId)
|
|
256
|
+
this._store.applyServerUpdate(this.collectionIdOrName, 'delete', { id } as BaseRecord)
|
|
257
|
+
return true
|
|
258
|
+
} catch (err) {
|
|
259
|
+
this._store.rollbackOptimistic(mutationId)
|
|
260
|
+
throw err
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ---- Realtime -----------------------------------------------------------
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Subscribe to realtime events for this collection.
|
|
268
|
+
* `topic` is "*" for all changes or a specific record id.
|
|
269
|
+
*/
|
|
270
|
+
subscribe(topic: string, callback: RealtimeCallback): () => void {
|
|
271
|
+
return this._realtime.subscribe(this.collectionIdOrName, topic, callback)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Unsubscribe from a specific topic or all topics for this collection. */
|
|
275
|
+
unsubscribe(topic?: string): void {
|
|
276
|
+
this._realtime.unsubscribe(
|
|
277
|
+
topic ? `${this.collectionIdOrName}/${topic}` : this.collectionIdOrName,
|
|
278
|
+
)
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// ---- Auth methods (for auth collections) --------------------------------
|
|
282
|
+
|
|
283
|
+
async authWithPassword<T extends BaseRecord = BaseRecord>(
|
|
284
|
+
identity: string,
|
|
285
|
+
password: string,
|
|
286
|
+
options?: RecordQueryOptions,
|
|
287
|
+
): Promise<AuthResponse<T>> {
|
|
288
|
+
const params = new URLSearchParams()
|
|
289
|
+
this._applyOptions(params, options)
|
|
290
|
+
const qs = params.toString()
|
|
291
|
+
const path = `${this._collectionPath()}/auth-with-password${qs ? '?' + qs : ''}`
|
|
292
|
+
|
|
293
|
+
const result = await this._request<AuthResponse<T>>(
|
|
294
|
+
'POST',
|
|
295
|
+
path,
|
|
296
|
+
JSON.stringify({ identity, password }),
|
|
297
|
+
options,
|
|
298
|
+
'application/json',
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
this._setAuth(result.token, result.record as unknown as BaseRecord)
|
|
302
|
+
return result
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async authWithOAuth2<T extends BaseRecord = BaseRecord>(
|
|
306
|
+
oauthOptions: OAuth2Options,
|
|
307
|
+
options?: RecordQueryOptions,
|
|
308
|
+
): Promise<AuthResponse<T>> {
|
|
309
|
+
const params = new URLSearchParams()
|
|
310
|
+
this._applyOptions(params, options)
|
|
311
|
+
const qs = params.toString()
|
|
312
|
+
const path = `${this._collectionPath()}/auth-with-oauth2${qs ? '?' + qs : ''}`
|
|
313
|
+
|
|
314
|
+
const result = await this._request<AuthResponse<T>>(
|
|
315
|
+
'POST',
|
|
316
|
+
path,
|
|
317
|
+
JSON.stringify(oauthOptions),
|
|
318
|
+
options,
|
|
319
|
+
'application/json',
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
this._setAuth(result.token, result.record as unknown as BaseRecord)
|
|
323
|
+
return result
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async requestVerification(email: string, options?: RecordQueryOptions): Promise<boolean> {
|
|
327
|
+
const path = `${this._collectionPath()}/request-verification`
|
|
328
|
+
await this._request<void>(
|
|
329
|
+
'POST',
|
|
330
|
+
path,
|
|
331
|
+
JSON.stringify({ email }),
|
|
332
|
+
options,
|
|
333
|
+
'application/json',
|
|
334
|
+
)
|
|
335
|
+
return true
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
async confirmVerification(token: string, options?: RecordQueryOptions): Promise<boolean> {
|
|
339
|
+
const path = `${this._collectionPath()}/confirm-verification`
|
|
340
|
+
await this._request<void>(
|
|
341
|
+
'POST',
|
|
342
|
+
path,
|
|
343
|
+
JSON.stringify({ token }),
|
|
344
|
+
options,
|
|
345
|
+
'application/json',
|
|
346
|
+
)
|
|
347
|
+
return true
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async requestPasswordReset(email: string, options?: RecordQueryOptions): Promise<boolean> {
|
|
351
|
+
const path = `${this._collectionPath()}/request-password-reset`
|
|
352
|
+
await this._request<void>(
|
|
353
|
+
'POST',
|
|
354
|
+
path,
|
|
355
|
+
JSON.stringify({ email }),
|
|
356
|
+
options,
|
|
357
|
+
'application/json',
|
|
358
|
+
)
|
|
359
|
+
return true
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
async confirmPasswordReset(
|
|
363
|
+
token: string,
|
|
364
|
+
password: string,
|
|
365
|
+
passwordConfirm: string,
|
|
366
|
+
options?: RecordQueryOptions,
|
|
367
|
+
): Promise<boolean> {
|
|
368
|
+
const path = `${this._collectionPath()}/confirm-password-reset`
|
|
369
|
+
await this._request<void>(
|
|
370
|
+
'POST',
|
|
371
|
+
path,
|
|
372
|
+
JSON.stringify({ token, password, passwordConfirm }),
|
|
373
|
+
options,
|
|
374
|
+
'application/json',
|
|
375
|
+
)
|
|
376
|
+
return true
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// ---- Internal -----------------------------------------------------------
|
|
380
|
+
|
|
381
|
+
private _collectionPath(): string {
|
|
382
|
+
return `/api/collections/${encodeURIComponent(this.collectionIdOrName)}`
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
private _applyOptions(params: URLSearchParams, options?: RecordQueryOptions): void {
|
|
386
|
+
if (!options) return
|
|
387
|
+
if (options.filter) params.set('filter', options.filter)
|
|
388
|
+
if (options.sort) params.set('sort', options.sort)
|
|
389
|
+
if (options.expand) params.set('expand', options.expand)
|
|
390
|
+
if (options.fields) params.set('fields', options.fields)
|
|
391
|
+
if (options.query) {
|
|
392
|
+
for (const [k, v] of Object.entries(options.query)) {
|
|
393
|
+
params.set(k, v)
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
private async _request<T>(
|
|
399
|
+
method: string,
|
|
400
|
+
path: string,
|
|
401
|
+
body?: string | FormData,
|
|
402
|
+
options?: RecordQueryOptions,
|
|
403
|
+
contentType?: string,
|
|
404
|
+
): Promise<T> {
|
|
405
|
+
const url = `${this._baseUrl}${path}`
|
|
406
|
+
const token = this._getToken()
|
|
407
|
+
|
|
408
|
+
const headers: Record<string, string> = {
|
|
409
|
+
...(options?.headers ?? {}),
|
|
410
|
+
}
|
|
411
|
+
if (token) {
|
|
412
|
+
headers['Authorization'] = token
|
|
413
|
+
}
|
|
414
|
+
if (contentType) {
|
|
415
|
+
headers['Content-Type'] = contentType
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const response = await fetch(url, {
|
|
419
|
+
method,
|
|
420
|
+
headers,
|
|
421
|
+
body: body ?? undefined,
|
|
422
|
+
signal: options?.signal,
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
if (!response.ok) {
|
|
426
|
+
const data = await response.json().catch(() => ({}))
|
|
427
|
+
throw new ClientResponseError({
|
|
428
|
+
url,
|
|
429
|
+
status: response.status,
|
|
430
|
+
data,
|
|
431
|
+
})
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// DELETE returns 204 with no body.
|
|
435
|
+
if (response.status === 204) {
|
|
436
|
+
return undefined as T
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
return response.json() as Promise<T>
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// ---------------------------------------------------------------------------
|
|
444
|
+
// ClientResponseError
|
|
445
|
+
// ---------------------------------------------------------------------------
|
|
446
|
+
|
|
447
|
+
export interface ClientResponseErrorData {
|
|
448
|
+
url: string
|
|
449
|
+
status: number
|
|
450
|
+
data: Record<string, unknown>
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export class ClientResponseError extends Error {
|
|
454
|
+
url: string
|
|
455
|
+
status: number
|
|
456
|
+
data: Record<string, unknown>
|
|
457
|
+
isAbort: boolean
|
|
458
|
+
|
|
459
|
+
constructor(errorData: ClientResponseErrorData) {
|
|
460
|
+
const message =
|
|
461
|
+
(errorData.data?.message as string) ??
|
|
462
|
+
`ClientResponseError ${errorData.status}`
|
|
463
|
+
super(message)
|
|
464
|
+
this.name = 'ClientResponseError'
|
|
465
|
+
this.url = errorData.url
|
|
466
|
+
this.status = errorData.status
|
|
467
|
+
this.data = errorData.data
|
|
468
|
+
this.isAbort = errorData.status === 0
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
toJSON(): ClientResponseErrorData {
|
|
472
|
+
return { url: this.url, status: this.status, data: this.data }
|
|
473
|
+
}
|
|
474
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hanzoai/base -- Core entry point.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the client, collection, store, state, and realtime modules.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Client
|
|
8
|
+
export { BaseClient, BaseClientError, MemoryAuthStore, FileService } from './client.js'
|
|
9
|
+
export type { AuthStore, AuthChangeCallback, ClientConfig, ListOptions, ListResult } from './client.js'
|
|
10
|
+
|
|
11
|
+
// Collection
|
|
12
|
+
export { CollectionService, ClientResponseError } from './collection.js'
|
|
13
|
+
export type {
|
|
14
|
+
RecordQueryOptions,
|
|
15
|
+
RecordFullListOptions,
|
|
16
|
+
FileOptions,
|
|
17
|
+
AuthResponse,
|
|
18
|
+
OAuth2Options,
|
|
19
|
+
ClientResponseErrorData,
|
|
20
|
+
} from './collection.js'
|
|
21
|
+
|
|
22
|
+
// Store
|
|
23
|
+
export { QueryStore } from './store.js'
|
|
24
|
+
export type { QueryKey, StoreCallback } from './store.js'
|
|
25
|
+
|
|
26
|
+
// State
|
|
27
|
+
export { VersionTracker } from './state.js'
|
|
28
|
+
export type { StateVersion, Modification, Transition, BaseRecord } from './state.js'
|
|
29
|
+
|
|
30
|
+
// Realtime
|
|
31
|
+
export { RealtimeService } from './realtime.js'
|
|
32
|
+
export type {
|
|
33
|
+
ConnectionState,
|
|
34
|
+
RealtimeEvent,
|
|
35
|
+
RealtimeCallback,
|
|
36
|
+
ConnectionCallback,
|
|
37
|
+
} from './realtime.js'
|