@picobase_app/client 0.5.2 → 1.0.2

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 CHANGED
@@ -1,5 +1,39 @@
1
- import PocketBase, { RecordModel, RecordSubscription, ListResult, SendOptions } from 'pocketbase';
2
- export { RecordModel } from 'pocketbase';
1
+ /**
2
+ * Internal HTTP client for PicoBase SDK.
3
+ *
4
+ * Not exported. Used internally by all modules.
5
+ * Handles: URL building, header injection, timeout, error mapping, 503 retry.
6
+ */
7
+ interface HttpSendOptions extends Omit<RequestInit, 'body'> {
8
+ /** Query parameters appended to the URL. */
9
+ query?: Record<string, unknown>;
10
+ /** Request body — plain object (serialized to JSON), FormData, or string. */
11
+ body?: Record<string, unknown> | FormData | string | null;
12
+ }
13
+ interface PicoBaseHttpClientOptions {
14
+ /** Timeout in ms for each request. Default: 30_000 */
15
+ timeout?: number;
16
+ /** Max retries on 503 (cold-start). Default: 3 */
17
+ maxColdStartRetries?: number;
18
+ /** Custom fetch implementation. */
19
+ fetch?: typeof globalThis.fetch;
20
+ /** Returns the current user JWT for Authorization header. */
21
+ getToken?: () => string;
22
+ }
23
+ /**
24
+ * Minimal fetch-based HTTP client.
25
+ * Injects X-PicoBase-Key and Authorization headers, handles cold-start retry.
26
+ */
27
+ declare class PicoBaseHttpClient {
28
+ readonly baseUrl: string;
29
+ private readonly apiKey;
30
+ private readonly timeout;
31
+ private readonly maxColdStartRetries;
32
+ private readonly fetchImpl;
33
+ private readonly getToken?;
34
+ constructor(baseUrl: string, apiKey: string, options?: PicoBaseHttpClientOptions);
35
+ send<T>(path: string, init?: HttpSendOptions): Promise<T>;
36
+ }
3
37
 
4
38
  interface PicoBaseClientOptions {
5
39
  /**
@@ -14,10 +48,28 @@ interface PicoBaseClientOptions {
14
48
  * Custom fetch implementation (useful for testing or server-side usage).
15
49
  */
16
50
  fetch?: typeof globalThis.fetch;
17
- /**
18
- * Language code sent as Accept-Language header. Default: 'en-US'.
19
- */
20
- lang?: string;
51
+ }
52
+ interface RecordModel {
53
+ id: string;
54
+ created: string;
55
+ updated: string;
56
+ collectionName?: string;
57
+ [key: string]: unknown;
58
+ }
59
+ interface ListResult<T = RecordModel> {
60
+ page: number;
61
+ perPage: number;
62
+ totalItems: number;
63
+ totalPages: number;
64
+ items: T[];
65
+ }
66
+ interface RecordSubscription<T = RecordModel> {
67
+ action: RealtimeAction;
68
+ record: T;
69
+ }
70
+ interface SendOptions extends Omit<RequestInit, 'body'> {
71
+ query?: Record<string, unknown>;
72
+ body?: Record<string, unknown> | FormData | string | null;
21
73
  }
22
74
  interface AuthResponse {
23
75
  token: string;
@@ -57,40 +109,62 @@ interface FileOptions {
57
109
  token?: string;
58
110
  download?: boolean;
59
111
  }
60
- type CollectionType = 'base' | 'auth' | 'view';
112
+ /**
113
+ * Breaking change from v0: "base"|"auth"|"view" → "flexible"|"strict"
114
+ */
115
+ type CollectionType = 'flexible' | 'strict';
116
+ interface SchemaField {
117
+ name: string;
118
+ type: string;
119
+ required?: boolean;
120
+ default?: unknown;
121
+ }
61
122
  interface CollectionModel {
62
123
  id: string;
63
124
  created: string;
64
125
  updated: string;
65
126
  name: string;
66
127
  type: CollectionType;
67
- system: boolean;
68
128
  schema: SchemaField[];
69
- indexes: string[];
70
- listRule: string | null;
71
- viewRule: string | null;
72
- createRule: string | null;
73
- updateRule: string | null;
74
- deleteRule: string | null;
75
- options: Record<string, unknown>;
76
129
  }
77
- interface SchemaField {
78
- system: boolean;
79
- id: string;
80
- name: string;
81
- type: string;
82
- required: boolean;
83
- presentable: boolean;
84
- unique: boolean;
85
- options: Record<string, unknown>;
130
+
131
+ /**
132
+ * AuthStore — manages the end-user JWT and record in memory + localStorage.
133
+ *
134
+ * Not exported. Used by PicoBaseAuth and PicoBaseClient.
135
+ */
136
+
137
+ type AuthChangeCallback = (token: string, record: RecordModel | null) => void;
138
+ declare class AuthStore {
139
+ private _token;
140
+ private _record;
141
+ private _listeners;
142
+ constructor();
143
+ /** Current JWT, or empty string if not signed in. */
144
+ get token(): string;
145
+ /** Currently signed-in user record, or null. */
146
+ get record(): RecordModel | null;
147
+ /**
148
+ * Returns true if a token is present and not yet expired.
149
+ * Checks JWT `exp` claim — no signature verification (the server does that).
150
+ */
151
+ get isValid(): boolean;
152
+ /** Save a token (and optionally the user record). Persists to localStorage. */
153
+ save(token: string, record?: RecordModel | null): void;
154
+ /** Clear the token and user record. Removes from localStorage. */
155
+ clear(): void;
156
+ /**
157
+ * Register a listener for auth state changes.
158
+ * @returns Unsubscribe function.
159
+ */
160
+ onChange(callback: AuthChangeCallback): () => void;
161
+ private _loadFromStorage;
162
+ private _notify;
86
163
  }
87
164
 
88
165
  /**
89
166
  * Auth module — handles user sign-up, sign-in, OAuth, and session management.
90
167
  *
91
- * PicoBase uses PocketBase's built-in `users` collection for auth. Each
92
- * instance has its own isolated user pool.
93
- *
94
168
  * @example
95
169
  * ```ts
96
170
  * // Sign up
@@ -112,17 +186,18 @@ interface SchemaField {
112
186
  * ```
113
187
  */
114
188
  declare class PicoBaseAuth {
115
- private pb;
189
+ private http;
190
+ private authStore;
116
191
  private listeners;
117
192
  private _collection;
118
- constructor(pb: PocketBase);
193
+ constructor(http: PicoBaseHttpClient, authStore: AuthStore);
119
194
  /**
120
195
  * Set which collection to authenticate against.
121
- * Defaults to 'users'. Use this if you have a custom auth collection.
196
+ * Defaults to '_auth_users'. Use this if you have a custom auth collection.
122
197
  */
123
198
  setCollection(name: string): this;
124
199
  /**
125
- * Create a new user account.
200
+ * Create a new user account. Automatically signs the user in after creation.
126
201
  */
127
202
  signUp(options: SignUpOptions): Promise<AuthResponse>;
128
203
  /**
@@ -131,9 +206,6 @@ declare class PicoBaseAuth {
131
206
  signIn(options: SignInOptions): Promise<AuthResponse>;
132
207
  /**
133
208
  * Sign in with an OAuth2 provider (Google, GitHub, etc.).
134
- *
135
- * In browser environments this opens a popup/redirect to the provider.
136
- * Configure providers in your PicoBase dashboard.
137
209
  */
138
210
  signInWithOAuth(options: OAuthSignInOptions): Promise<AuthResponse>;
139
211
  /**
@@ -178,9 +250,7 @@ declare class PicoBaseAuth {
178
250
  * @example
179
251
  * ```ts
180
252
  * const unsubscribe = pb.auth.onStateChange((event, record) => {
181
- * if (event === 'SIGNED_IN') {
182
- * console.log('Welcome', record.email)
183
- * }
253
+ * if (event === 'SIGNED_IN') console.log('Welcome', record.email)
184
254
  * })
185
255
  *
186
256
  * // Later:
@@ -191,6 +261,71 @@ declare class PicoBaseAuth {
191
261
  private _notify;
192
262
  }
193
263
 
264
+ /**
265
+ * Realtime module — subscribe to record changes via Server-Sent Events (SSE).
266
+ *
267
+ * For collection-level subscriptions, prefer `pb.collection('name').subscribe()`.
268
+ * This module is for lower-level control.
269
+ *
270
+ * @example
271
+ * ```ts
272
+ * // Subscribe to all changes on a collection
273
+ * const unsub = await pb.realtime.subscribe('posts', (e) => {
274
+ * console.log(e.action, e.record)
275
+ * })
276
+ *
277
+ * // Unsubscribe
278
+ * await unsub()
279
+ *
280
+ * // Disconnect all realtime connections
281
+ * await pb.realtime.disconnectAll()
282
+ * ```
283
+ */
284
+ declare class PicoBaseRealtime {
285
+ private baseUrl;
286
+ private apiKey;
287
+ private getToken;
288
+ private eventSource;
289
+ private subs;
290
+ private counter;
291
+ constructor(baseUrl: string, apiKey: string, getToken: () => string);
292
+ /**
293
+ * Subscribe to realtime events on a collection.
294
+ *
295
+ * @param collection - Collection name (e.g. 'posts').
296
+ * @param callback - Called on every create/update/delete event.
297
+ * @returns Unsubscribe function.
298
+ */
299
+ subscribe<T = RecordModel>(collection: string, callback: (data: RecordSubscription<T>) => void): Promise<() => Promise<void>>;
300
+ /**
301
+ * Subscribe to realtime events on a specific record.
302
+ *
303
+ * @param collection - Collection name.
304
+ * @param recordId - Record ID.
305
+ * @param callback - Called on update/delete events.
306
+ * @returns Unsubscribe function.
307
+ */
308
+ subscribeRecord<T = RecordModel>(collection: string, recordId: string, callback: (data: RecordSubscription<T>) => void): Promise<() => Promise<void>>;
309
+ /**
310
+ * Unsubscribe from all realtime events on a collection.
311
+ */
312
+ unsubscribe(collection: string): Promise<void>;
313
+ /**
314
+ * Unsubscribe from ALL realtime events and close the SSE connection.
315
+ */
316
+ disconnectAll(): Promise<void>;
317
+ private _addSub;
318
+ private _removeSub;
319
+ private _pruneIfEmpty;
320
+ /**
321
+ * Open SSE connection to `/api/db/realtime`.
322
+ * No-ops in environments where EventSource is unavailable (SSR / Node).
323
+ */
324
+ private _ensureConnection;
325
+ private _closeConnection;
326
+ private _dispatch;
327
+ }
328
+
194
329
  /** Options for list queries. */
195
330
  interface ListOptions {
196
331
  sort?: string;
@@ -207,7 +342,7 @@ interface RecordQueryOptions {
207
342
  [key: string]: unknown;
208
343
  }
209
344
  /**
210
- * Collection module — CRUD operations on a PocketBase collection.
345
+ * Collection module — CRUD operations on a PicoBase collection.
211
346
  *
212
347
  * @example
213
348
  * ```ts
@@ -224,10 +359,7 @@ interface RecordQueryOptions {
224
359
  * const post = await posts.getOne('record_id')
225
360
  *
226
361
  * // Create a record
227
- * const newPost = await posts.create({
228
- * title: 'Hello World',
229
- * content: 'My first post',
230
- * })
362
+ * const newPost = await posts.create({ title: 'Hello World', content: 'My first post' })
231
363
  *
232
364
  * // Update a record
233
365
  * const updated = await posts.update('record_id', { title: 'Updated' })
@@ -237,9 +369,10 @@ interface RecordQueryOptions {
237
369
  * ```
238
370
  */
239
371
  declare class PicoBaseCollection<T = RecordModel> {
240
- private pb;
372
+ private http;
241
373
  private name;
242
- constructor(pb: PocketBase, name: string);
374
+ private rt;
375
+ constructor(http: PicoBaseHttpClient, name: string, realtime: PicoBaseRealtime);
243
376
  /**
244
377
  * Fetch a paginated list of records.
245
378
  *
@@ -288,7 +421,7 @@ declare class PicoBaseCollection<T = RecordModel> {
288
421
  * Subscribe to realtime changes on this collection.
289
422
  *
290
423
  * @param callback - Called on every create/update/delete event.
291
- * @param filter - Optional: only receive events matching this filter.
424
+ * @param filter - Optional: only receive events matching this filter (client-side).
292
425
  * @returns Unsubscribe function.
293
426
  *
294
427
  * @example
@@ -319,61 +452,9 @@ declare class PicoBaseCollection<T = RecordModel> {
319
452
  }
320
453
 
321
454
  /**
322
- * Realtime module — manage global realtime subscriptions.
455
+ * Storage module — work with file fields on PicoBase records.
323
456
  *
324
- * For collection-level subscriptions, prefer `pb.collection('name').subscribe()`.
325
- * This module is for lower-level control over the SSE connection.
326
- *
327
- * @example
328
- * ```ts
329
- * // Subscribe to all changes on a collection
330
- * const unsub = await pb.realtime.subscribe('posts', (e) => {
331
- * console.log(e.action, e.record)
332
- * })
333
- *
334
- * // Unsubscribe
335
- * await unsub()
336
- *
337
- * // Disconnect all realtime connections
338
- * pb.realtime.disconnect()
339
- * ```
340
- */
341
- declare class PicoBaseRealtime {
342
- private pb;
343
- constructor(pb: PocketBase);
344
- /**
345
- * Subscribe to realtime events on a collection.
346
- *
347
- * @param collection - Collection name (e.g. 'posts').
348
- * @param callback - Called on every create/update/delete event.
349
- * @returns Unsubscribe function.
350
- */
351
- subscribe<T = RecordModel>(collection: string, callback: (data: RecordSubscription<T>) => void): Promise<() => Promise<void>>;
352
- /**
353
- * Subscribe to realtime events on a specific record.
354
- *
355
- * @param collection - Collection name.
356
- * @param recordId - Record ID.
357
- * @param callback - Called on update/delete events.
358
- * @returns Unsubscribe function.
359
- */
360
- subscribeRecord<T = RecordModel>(collection: string, recordId: string, callback: (data: RecordSubscription<T>) => void): Promise<() => Promise<void>>;
361
- /**
362
- * Unsubscribe from all realtime events on a collection.
363
- */
364
- unsubscribe(collection: string): Promise<void>;
365
- /**
366
- * Unsubscribe from ALL realtime events. The SSE connection will be
367
- * automatically closed when there are no remaining subscriptions.
368
- */
369
- disconnectAll(): Promise<void>;
370
- }
371
-
372
- /**
373
- * Storage module — work with file fields on PocketBase records.
374
- *
375
- * PocketBase stores files as record fields. This module provides helpers
376
- * to get file URLs and generate access tokens for protected files.
457
+ * Files are served from `/api/db/files/:collection/:recordId/:filename`.
377
458
  *
378
459
  * @example
379
460
  * ```ts
@@ -383,9 +464,7 @@ declare class PicoBaseRealtime {
383
464
  * const avatarUrl = pb.storage.getFileUrl(user, 'avatar.jpg')
384
465
  *
385
466
  * // Get a thumbnail URL (100x100)
386
- * const thumbUrl = pb.storage.getFileUrl(user, 'avatar.jpg', {
387
- * thumb: '100x100',
388
- * })
467
+ * const thumbUrl = pb.storage.getFileUrl(user, 'avatar.jpg', { thumb: '100x100' })
389
468
  *
390
469
  * // Get a temporary token for protected files
391
470
  * const token = await pb.storage.getFileToken()
@@ -393,27 +472,33 @@ declare class PicoBaseRealtime {
393
472
  * ```
394
473
  */
395
474
  declare class PicoBaseStorage {
396
- private pb;
397
- constructor(pb: PocketBase);
475
+ private http;
476
+ private baseUrl;
477
+ constructor(baseUrl: string, http: PicoBaseHttpClient);
398
478
  /**
399
- * Get the public URL for a file attached to a record.
479
+ * Get the URL for a file attached to a record.
400
480
  *
401
- * @param record - The record that owns the file.
481
+ * @param record - The record that owns the file. Must have `collectionName` and `id`.
402
482
  * @param filename - The filename (as stored in the record's file field).
403
483
  * @param options - Optional: thumb size, token for protected files, download flag.
404
484
  */
405
485
  getFileUrl(record: RecordModel, filename: string, options?: FileOptions): string;
406
486
  /**
407
- * Generate a temporary file access token.
408
- *
409
- * Use this for accessing protected files. Tokens are short-lived.
487
+ * Generate a temporary file access token for protected files.
488
+ * Tokens are short-lived. Available after Phase 7 ships.
410
489
  */
411
490
  getFileToken(): Promise<string>;
412
491
  }
413
492
 
493
+ /**
494
+ * Admin module — programmatic collection management.
495
+ * Requires an admin API key.
496
+ *
497
+ * Breaking change from v0: `type` is now `"flexible" | "strict"` instead of `"base" | "auth" | "view"`.
498
+ */
414
499
  declare class PicoBaseAdmin {
415
- private pb;
416
- constructor(pb: PocketBase);
500
+ private http;
501
+ constructor(http: PicoBaseHttpClient);
417
502
  /**
418
503
  * Fetch a list of all collections.
419
504
  */
@@ -424,6 +509,8 @@ declare class PicoBaseAdmin {
424
509
  getCollection(idOrName: string): Promise<CollectionModel>;
425
510
  /**
426
511
  * Create a new collection.
512
+ *
513
+ * @param data.type - `"flexible"` (JSONB, schema-free) or `"strict"` (typed SQL columns).
427
514
  */
428
515
  createCollection(data: Partial<CollectionModel>): Promise<CollectionModel>;
429
516
  /**
@@ -437,8 +524,6 @@ declare class PicoBaseAdmin {
437
524
  }
438
525
 
439
526
  declare class PicoBaseClient {
440
- /** The underlying PocketBase SDK instance. Exposed for advanced usage. */
441
- readonly pb: PocketBase;
442
527
  /** Auth module — sign up, sign in, OAuth, session management. */
443
528
  readonly auth: PicoBaseAuth;
444
529
  /** Realtime module — subscribe to record changes. */
@@ -447,8 +532,12 @@ declare class PicoBaseClient {
447
532
  readonly storage: PicoBaseStorage;
448
533
  /** Admin module — manage collections (requires admin API key). */
449
534
  readonly admin: PicoBaseAdmin;
450
- private readonly apiKey;
451
- private readonly options;
535
+ /** Internal HTTP client. */
536
+ private readonly _http;
537
+ /** Internal auth store. */
538
+ private readonly _authStore;
539
+ /** Normalized base URL (no trailing slash). */
540
+ readonly baseUrl: string;
452
541
  constructor(url: string, apiKey: string, options?: PicoBaseClientOptions);
453
542
  /**
454
543
  * Access a collection for CRUD operations.
@@ -460,52 +549,25 @@ declare class PicoBaseClient {
460
549
  */
461
550
  collection<T = RecordModel>(name: string): PicoBaseCollection<T>;
462
551
  /**
463
- * Call a server-side function (PocketBase custom API endpoint).
464
- * Proxies to PocketBase's send() method.
552
+ * Send a raw HTTP request to the PicoBase API.
465
553
  */
466
554
  send<T = unknown>(path: string, options?: SendOptions): Promise<T>;
467
555
  /**
468
- * Call a remote procedure (RPC) - a convenience method for calling custom API endpoints.
556
+ * Call a remote procedure (RPC) convenience wrapper for custom API endpoints.
469
557
  *
470
- * Maps Supabase-style RPC calls to PicoBase custom endpoints:
471
- * - `pb.rpc('my_function', params)` → `POST /api/rpc/my_function` with params as body
558
+ * Maps Supabase-style RPC calls: `pb.rpc('my_fn', params)` `POST /api/rpc/my_fn`
472
559
  *
473
560
  * @example
474
561
  * ```ts
475
- * // Simple RPC call
476
562
  * const result = await pb.rpc('calculate_total', { cart_id: '123' })
477
- *
478
- * // Complex RPC with typed response
479
- * interface DashboardStats {
480
- * posts: number
481
- * comments: number
482
- * followers: number
483
- * }
484
- * const stats = await pb.rpc<DashboardStats>('get_dashboard_stats', {
485
- * user_id: currentUser.id
486
- * })
487
563
  * ```
488
- *
489
- * @param functionName The name of the RPC function to call
490
- * @param params Optional parameters to pass to the function
491
- * @returns The function result
492
564
  */
493
565
  rpc<T = unknown>(functionName: string, params?: Record<string, unknown>): Promise<T>;
494
566
  /**
495
- * Proactively wake up the instance if it's sleeping and wait for it to be ready.
496
- * Useful to call when a user navigates to a login page or before critical operations,
497
- * to absorb the cold-start latency upfront.
567
+ * Proactively warm up the Neon connection after scale-to-zero.
498
568
  *
499
- * This method guarantees that the instance is fully running and able to serve requests
500
- * when the promise resolves.
501
- *
502
- * @example
503
- * ```ts
504
- * // In a useEffect on your login page:
505
- * useEffect(() => {
506
- * pb.wake().catch(console.error)
507
- * }, [])
508
- * ```
569
+ * Useful to call when a user navigates to a login page or before critical
570
+ * operations, to absorb cold-start latency upfront.
509
571
  */
510
572
  wake(): Promise<boolean>;
511
573
  /**
@@ -516,14 +578,6 @@ declare class PicoBaseClient {
516
578
  * Check if a user is currently authenticated.
517
579
  */
518
580
  get isAuthenticated(): boolean;
519
- /**
520
- * Monkey-patch pb.send to retry on 503 (cold start).
521
- *
522
- * When an instance is stopped or starting, the proxy returns 503.
523
- * The SDK automatically retries with exponential backoff so the developer
524
- * doesn't have to handle cold-start logic.
525
- */
526
- private _wrapSendWithRetry;
527
581
  }
528
582
  /**
529
583
  * Create a new PicoBase client.
@@ -541,18 +595,6 @@ declare class PicoBaseClient {
541
595
  *
542
596
  * // Or explicit
543
597
  * const pb = createClient('https://myapp.picobase.com', 'pbk_abc123_secret')
544
- *
545
- * // Sign up a user
546
- * const user = await pb.auth.signUp({
547
- * email: 'user@example.com',
548
- * password: 'securepassword',
549
- * })
550
- *
551
- * // Query records
552
- * const posts = await pb.collection('posts').getList(1, 20, {
553
- * filter: 'published = true',
554
- * sort: '-created',
555
- * })
556
598
  * ```
557
599
  */
558
600
  declare function createClient(options?: PicoBaseClientOptions): PicoBaseClient;
@@ -619,4 +661,4 @@ declare class RpcError extends PicoBaseError {
619
661
  constructor(functionName: string, status: number, details?: unknown);
620
662
  }
621
663
 
622
- export { type AuthEvent, type AuthResponse, type AuthStateChange, type AuthStateChangeCallback, AuthorizationError, CollectionNotFoundError, ConfigurationError, type FileOptions, InstanceUnavailableError, type ListOptions, type OAuthSignInOptions, PicoBaseAdmin, PicoBaseAuth, PicoBaseClient, type PicoBaseClientOptions, PicoBaseCollection, PicoBaseError, PicoBaseRealtime, PicoBaseStorage, type RealtimeAction, type RealtimeCallback, RecordNotFoundError, type RecordQueryOptions, RequestError, RpcError, type SignInOptions, type SignUpOptions, type UnsubscribeFunc, createClient };
664
+ export { type AuthEvent, type AuthResponse, type AuthStateChange, type AuthStateChangeCallback, AuthorizationError, type CollectionModel, CollectionNotFoundError, type CollectionType, ConfigurationError, type FileOptions, InstanceUnavailableError, type ListOptions, type ListResult, type OAuthSignInOptions, PicoBaseAdmin, PicoBaseAuth, PicoBaseClient, type PicoBaseClientOptions, PicoBaseCollection, PicoBaseError, PicoBaseRealtime, PicoBaseStorage, type RealtimeAction, type RealtimeCallback, type RecordModel, RecordNotFoundError, type RecordQueryOptions, type RecordSubscription, RequestError, RpcError, type SchemaField, type SendOptions, type SignInOptions, type SignUpOptions, type UnsubscribeFunc, createClient };