lazypock 0.1.1 → 0.1.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.cts CHANGED
@@ -183,6 +183,96 @@ declare class HttpClient {
183
183
  delete<T = unknown>(path: string, options?: RequestOptions): Promise<T | null>;
184
184
  }
185
185
 
186
+ interface RealtimeEvent {
187
+ event: string;
188
+ topic: string;
189
+ payload: Record<string, unknown>;
190
+ }
191
+ type RealtimeConnectOpts = {
192
+ /** WebSocket URL (e.g. ws://localhost:4000/socket/websocket) */
193
+ url: string;
194
+ /** Auth token to pass as query param */
195
+ token?: string;
196
+ };
197
+ /**
198
+ * Derive a WebSocket URL from an HTTP base URL.
199
+ * http://localhost:4000/api → ws://localhost:4000/socket/websocket
200
+ */
201
+ declare function wsUrlFromBaseUrl(baseUrl: string): string;
202
+ /**
203
+ * Phoenix Channel client for real-time collection subscriptions.
204
+ *
205
+ * Connects via WebSocket and subscribes to collection topics.
206
+ * Includes automatic reconnection with exponential backoff.
207
+ *
208
+ * @example
209
+ * ```ts
210
+ * const rt = new RealtimeService();
211
+ * rt.connect({ url: wsUrlFromBaseUrl('http://localhost:4000/api') });
212
+ * rt.subscribe('collection:posts', (e) => console.log(e));
213
+ * ```
214
+ */
215
+ declare class RealtimeService {
216
+ private ws;
217
+ private refCounter;
218
+ private subscriptions;
219
+ private reconnectTimer;
220
+ private reconnectAttempt;
221
+ private maxReconnectDelay;
222
+ onReconnect?: () => void;
223
+ onDisconnect?: () => void;
224
+ onError?: (err: Event) => void;
225
+ private url;
226
+ private token;
227
+ /** Whether the WebSocket is currently open. */
228
+ get isOpen(): boolean;
229
+ /**
230
+ * The most recently used WebSocket URL (set on {@link connect}).
231
+ * Useful for SDK convenience methods that auto-connect before subscribing.
232
+ */
233
+ get lastUrl(): string;
234
+ /** The auth token configured for this connection (set on connect). */
235
+ get lastToken(): string | undefined;
236
+ /**
237
+ * Set the socket URL. Useful before subscribing so the SDK can
238
+ * auto-connect on the first {@link subscribe}.
239
+ */
240
+ setUrl(url: string): void;
241
+ ensureConnected(): void;
242
+ connect(opts: RealtimeConnectOpts): void;
243
+ disconnect(): void;
244
+ /**
245
+ * Subscribe to a topic (e.g. "collection:posts" or "collection:posts:*").
246
+ * The backend Channel authorizes via listRule on join.
247
+ */
248
+ subscribe(topic: string, callback: (e: RealtimeEvent) => void): void;
249
+ /**
250
+ * Unsubscribe a specific callback from a topic.
251
+ */
252
+ unsubscribe(topic: string, callback?: (e: RealtimeEvent) => void): void;
253
+ private resubscribeAll;
254
+ private doConnect;
255
+ private handleMessage;
256
+ private joinTopic;
257
+ private nextRef;
258
+ private heartbeatInterval;
259
+ private startHeartbeat;
260
+ private stopHeartbeat;
261
+ private scheduleReconnect;
262
+ private clearReconnectTimer;
263
+ }
264
+
265
+ /**
266
+ * A realtime record-change event delivered to subscription callbacks.
267
+ * Mirrors PocketBase's RealtimeService result shape (`action` + `record`).
268
+ */
269
+ interface RealtimeMessage {
270
+ action: "create" | "update" | "delete";
271
+ record: Record<string, unknown>;
272
+ topic?: string;
273
+ }
274
+ /** Subscription callback for a collection's realtime events. */
275
+ type RealtimeCallback = (e: RealtimeMessage) => void;
186
276
  /**
187
277
  * Typed CRUD service for a single dynamic collection.
188
278
  * Get an instance via {@link LazypockClient.collection}.
@@ -193,8 +283,9 @@ declare class CollectionService<T = ApiRecord> {
193
283
  private http;
194
284
  private collectionName;
195
285
  private authStore?;
286
+ private realtime?;
196
287
  /** @internal */
197
- constructor(http: HttpClient, collectionName: string, authStore?: AuthStore);
288
+ constructor(http: HttpClient, collectionName: string, authStore?: AuthStore, realtime?: RealtimeService);
198
289
  private encodeId;
199
290
  /**
200
291
  * List records with optional filter/sort/pagination.
@@ -255,6 +346,24 @@ declare class CollectionService<T = ApiRecord> {
255
346
  * ```
256
347
  */
257
348
  typed<TRecord = ApiRecord>(): CollectionService<TRecord>;
349
+ /**
350
+ * Subscribe to realtime changes for this collection.
351
+ * The event's `action` is one of `"create" | "update" | "delete"`.
352
+ *
353
+ * Access is governed by the collection's `listRule` (PocketBase semantics):
354
+ * public collections allow anonymous subscriptions; other collections
355
+ * require a matching logged-in user or superuser.
356
+ *
357
+ * @param callback Received on every record change.
358
+ * @param recordId Optional — subscribe to a single record instead of `*`.
359
+ * @returns A function that unsubscribes this callback.
360
+ */
361
+ subscribe(callback: RealtimeCallback, recordId?: string): () => void;
362
+ /**
363
+ * Unsubscribe all callbacks from this collection (or a specific record).
364
+ * @param recordId Optional record id; omitting it unsubs everything.
365
+ */
366
+ unsubscribe(recordId?: string): void;
258
367
  /**
259
368
  * Authenticate with email/password against this auth collection.
260
369
  * Stores the returned token and user model in the auth store.
@@ -277,70 +386,6 @@ declare class CollectionService<T = ApiRecord> {
277
386
  authMethods(options?: RequestOptions): Promise<Record<string, unknown> | null>;
278
387
  }
279
388
 
280
- interface RealtimeEvent {
281
- event: string;
282
- topic: string;
283
- payload: Record<string, unknown>;
284
- }
285
- type RealtimeConnectOpts = {
286
- /** WebSocket URL (e.g. ws://localhost:4000/socket/websocket) */
287
- url: string;
288
- /** Auth token to pass as query param */
289
- token?: string;
290
- };
291
- /**
292
- * Derive a WebSocket URL from an HTTP base URL.
293
- * http://localhost:4000/api → ws://localhost:4000/socket/websocket
294
- */
295
- declare function wsUrlFromBaseUrl(baseUrl: string): string;
296
- /**
297
- * Phoenix Channel client for real-time collection subscriptions.
298
- *
299
- * Connects via WebSocket and subscribes to collection topics.
300
- * Includes automatic reconnection with exponential backoff.
301
- *
302
- * @example
303
- * ```ts
304
- * const rt = new RealtimeService();
305
- * rt.connect({ url: wsUrlFromBaseUrl('http://localhost:4000/api') });
306
- * rt.subscribe('collection:posts', (e) => console.log(e));
307
- * ```
308
- */
309
- declare class RealtimeService {
310
- private ws;
311
- private refCounter;
312
- private subscriptions;
313
- private reconnectTimer;
314
- private reconnectAttempt;
315
- private maxReconnectDelay;
316
- onReconnect?: () => void;
317
- onDisconnect?: () => void;
318
- onError?: (err: Event) => void;
319
- private url;
320
- private token;
321
- connect(opts: RealtimeConnectOpts): void;
322
- disconnect(): void;
323
- /**
324
- * Subscribe to a topic (e.g. "collection:posts" or "collection:posts:*").
325
- * The backend Channel authorizes via listRule on join.
326
- */
327
- subscribe(topic: string, callback: (e: RealtimeEvent) => void): void;
328
- /**
329
- * Unsubscribe a specific callback from a topic.
330
- */
331
- unsubscribe(topic: string, callback?: (e: RealtimeEvent) => void): void;
332
- private resubscribeAll;
333
- private doConnect;
334
- private handleMessage;
335
- private joinTopic;
336
- private nextRef;
337
- private heartbeatInterval;
338
- private startHeartbeat;
339
- private stopHeartbeat;
340
- private scheduleReconnect;
341
- private clearReconnectTimer;
342
- }
343
-
344
389
  /** Response shape from the server file endpoints */
345
390
  interface FileRecord {
346
391
  id: string;
@@ -693,4 +738,4 @@ declare class TypedClient<TCollections extends LazypockCollections = LazypockCol
693
738
  */
694
739
  declare function createClient<TCollections extends LazypockCollections = LazypockCollections>(options: LazypockClientOptions): TypedClient<TCollections>;
695
740
 
696
- export { ApiError, type ApiRecord, type AuthModel, AuthStore, type CollectionSchema, CollectionService, type CreateData, type FileRecord, FilesService, LazypockClient, type LazypockClientOptions, type LazypockCollections, type ListResult, RealtimeService, type RecordShape, type RequestOptions, type SchemaField, type StorageAdapter, type SystemFields, TypedClient, type UpdateData, collectionTypeName, createClient, fieldTypeKind, fieldTypeScriptType, generateTypes, getFileUrl, schemaFieldType, wsUrlFromBaseUrl };
741
+ export { ApiError, type ApiRecord, type AuthModel, AuthStore, type CollectionSchema, CollectionService, type CreateData, type FileRecord, FilesService, LazypockClient, type LazypockClientOptions, type LazypockCollections, type ListResult, type RealtimeCallback, type RealtimeMessage, RealtimeService, type RecordShape, type RequestOptions, type SchemaField, type StorageAdapter, type SystemFields, TypedClient, type UpdateData, collectionTypeName, createClient, fieldTypeKind, fieldTypeScriptType, generateTypes, getFileUrl, schemaFieldType, wsUrlFromBaseUrl };
package/dist/index.d.ts CHANGED
@@ -183,6 +183,96 @@ declare class HttpClient {
183
183
  delete<T = unknown>(path: string, options?: RequestOptions): Promise<T | null>;
184
184
  }
185
185
 
186
+ interface RealtimeEvent {
187
+ event: string;
188
+ topic: string;
189
+ payload: Record<string, unknown>;
190
+ }
191
+ type RealtimeConnectOpts = {
192
+ /** WebSocket URL (e.g. ws://localhost:4000/socket/websocket) */
193
+ url: string;
194
+ /** Auth token to pass as query param */
195
+ token?: string;
196
+ };
197
+ /**
198
+ * Derive a WebSocket URL from an HTTP base URL.
199
+ * http://localhost:4000/api → ws://localhost:4000/socket/websocket
200
+ */
201
+ declare function wsUrlFromBaseUrl(baseUrl: string): string;
202
+ /**
203
+ * Phoenix Channel client for real-time collection subscriptions.
204
+ *
205
+ * Connects via WebSocket and subscribes to collection topics.
206
+ * Includes automatic reconnection with exponential backoff.
207
+ *
208
+ * @example
209
+ * ```ts
210
+ * const rt = new RealtimeService();
211
+ * rt.connect({ url: wsUrlFromBaseUrl('http://localhost:4000/api') });
212
+ * rt.subscribe('collection:posts', (e) => console.log(e));
213
+ * ```
214
+ */
215
+ declare class RealtimeService {
216
+ private ws;
217
+ private refCounter;
218
+ private subscriptions;
219
+ private reconnectTimer;
220
+ private reconnectAttempt;
221
+ private maxReconnectDelay;
222
+ onReconnect?: () => void;
223
+ onDisconnect?: () => void;
224
+ onError?: (err: Event) => void;
225
+ private url;
226
+ private token;
227
+ /** Whether the WebSocket is currently open. */
228
+ get isOpen(): boolean;
229
+ /**
230
+ * The most recently used WebSocket URL (set on {@link connect}).
231
+ * Useful for SDK convenience methods that auto-connect before subscribing.
232
+ */
233
+ get lastUrl(): string;
234
+ /** The auth token configured for this connection (set on connect). */
235
+ get lastToken(): string | undefined;
236
+ /**
237
+ * Set the socket URL. Useful before subscribing so the SDK can
238
+ * auto-connect on the first {@link subscribe}.
239
+ */
240
+ setUrl(url: string): void;
241
+ ensureConnected(): void;
242
+ connect(opts: RealtimeConnectOpts): void;
243
+ disconnect(): void;
244
+ /**
245
+ * Subscribe to a topic (e.g. "collection:posts" or "collection:posts:*").
246
+ * The backend Channel authorizes via listRule on join.
247
+ */
248
+ subscribe(topic: string, callback: (e: RealtimeEvent) => void): void;
249
+ /**
250
+ * Unsubscribe a specific callback from a topic.
251
+ */
252
+ unsubscribe(topic: string, callback?: (e: RealtimeEvent) => void): void;
253
+ private resubscribeAll;
254
+ private doConnect;
255
+ private handleMessage;
256
+ private joinTopic;
257
+ private nextRef;
258
+ private heartbeatInterval;
259
+ private startHeartbeat;
260
+ private stopHeartbeat;
261
+ private scheduleReconnect;
262
+ private clearReconnectTimer;
263
+ }
264
+
265
+ /**
266
+ * A realtime record-change event delivered to subscription callbacks.
267
+ * Mirrors PocketBase's RealtimeService result shape (`action` + `record`).
268
+ */
269
+ interface RealtimeMessage {
270
+ action: "create" | "update" | "delete";
271
+ record: Record<string, unknown>;
272
+ topic?: string;
273
+ }
274
+ /** Subscription callback for a collection's realtime events. */
275
+ type RealtimeCallback = (e: RealtimeMessage) => void;
186
276
  /**
187
277
  * Typed CRUD service for a single dynamic collection.
188
278
  * Get an instance via {@link LazypockClient.collection}.
@@ -193,8 +283,9 @@ declare class CollectionService<T = ApiRecord> {
193
283
  private http;
194
284
  private collectionName;
195
285
  private authStore?;
286
+ private realtime?;
196
287
  /** @internal */
197
- constructor(http: HttpClient, collectionName: string, authStore?: AuthStore);
288
+ constructor(http: HttpClient, collectionName: string, authStore?: AuthStore, realtime?: RealtimeService);
198
289
  private encodeId;
199
290
  /**
200
291
  * List records with optional filter/sort/pagination.
@@ -255,6 +346,24 @@ declare class CollectionService<T = ApiRecord> {
255
346
  * ```
256
347
  */
257
348
  typed<TRecord = ApiRecord>(): CollectionService<TRecord>;
349
+ /**
350
+ * Subscribe to realtime changes for this collection.
351
+ * The event's `action` is one of `"create" | "update" | "delete"`.
352
+ *
353
+ * Access is governed by the collection's `listRule` (PocketBase semantics):
354
+ * public collections allow anonymous subscriptions; other collections
355
+ * require a matching logged-in user or superuser.
356
+ *
357
+ * @param callback Received on every record change.
358
+ * @param recordId Optional — subscribe to a single record instead of `*`.
359
+ * @returns A function that unsubscribes this callback.
360
+ */
361
+ subscribe(callback: RealtimeCallback, recordId?: string): () => void;
362
+ /**
363
+ * Unsubscribe all callbacks from this collection (or a specific record).
364
+ * @param recordId Optional record id; omitting it unsubs everything.
365
+ */
366
+ unsubscribe(recordId?: string): void;
258
367
  /**
259
368
  * Authenticate with email/password against this auth collection.
260
369
  * Stores the returned token and user model in the auth store.
@@ -277,70 +386,6 @@ declare class CollectionService<T = ApiRecord> {
277
386
  authMethods(options?: RequestOptions): Promise<Record<string, unknown> | null>;
278
387
  }
279
388
 
280
- interface RealtimeEvent {
281
- event: string;
282
- topic: string;
283
- payload: Record<string, unknown>;
284
- }
285
- type RealtimeConnectOpts = {
286
- /** WebSocket URL (e.g. ws://localhost:4000/socket/websocket) */
287
- url: string;
288
- /** Auth token to pass as query param */
289
- token?: string;
290
- };
291
- /**
292
- * Derive a WebSocket URL from an HTTP base URL.
293
- * http://localhost:4000/api → ws://localhost:4000/socket/websocket
294
- */
295
- declare function wsUrlFromBaseUrl(baseUrl: string): string;
296
- /**
297
- * Phoenix Channel client for real-time collection subscriptions.
298
- *
299
- * Connects via WebSocket and subscribes to collection topics.
300
- * Includes automatic reconnection with exponential backoff.
301
- *
302
- * @example
303
- * ```ts
304
- * const rt = new RealtimeService();
305
- * rt.connect({ url: wsUrlFromBaseUrl('http://localhost:4000/api') });
306
- * rt.subscribe('collection:posts', (e) => console.log(e));
307
- * ```
308
- */
309
- declare class RealtimeService {
310
- private ws;
311
- private refCounter;
312
- private subscriptions;
313
- private reconnectTimer;
314
- private reconnectAttempt;
315
- private maxReconnectDelay;
316
- onReconnect?: () => void;
317
- onDisconnect?: () => void;
318
- onError?: (err: Event) => void;
319
- private url;
320
- private token;
321
- connect(opts: RealtimeConnectOpts): void;
322
- disconnect(): void;
323
- /**
324
- * Subscribe to a topic (e.g. "collection:posts" or "collection:posts:*").
325
- * The backend Channel authorizes via listRule on join.
326
- */
327
- subscribe(topic: string, callback: (e: RealtimeEvent) => void): void;
328
- /**
329
- * Unsubscribe a specific callback from a topic.
330
- */
331
- unsubscribe(topic: string, callback?: (e: RealtimeEvent) => void): void;
332
- private resubscribeAll;
333
- private doConnect;
334
- private handleMessage;
335
- private joinTopic;
336
- private nextRef;
337
- private heartbeatInterval;
338
- private startHeartbeat;
339
- private stopHeartbeat;
340
- private scheduleReconnect;
341
- private clearReconnectTimer;
342
- }
343
-
344
389
  /** Response shape from the server file endpoints */
345
390
  interface FileRecord {
346
391
  id: string;
@@ -693,4 +738,4 @@ declare class TypedClient<TCollections extends LazypockCollections = LazypockCol
693
738
  */
694
739
  declare function createClient<TCollections extends LazypockCollections = LazypockCollections>(options: LazypockClientOptions): TypedClient<TCollections>;
695
740
 
696
- export { ApiError, type ApiRecord, type AuthModel, AuthStore, type CollectionSchema, CollectionService, type CreateData, type FileRecord, FilesService, LazypockClient, type LazypockClientOptions, type LazypockCollections, type ListResult, RealtimeService, type RecordShape, type RequestOptions, type SchemaField, type StorageAdapter, type SystemFields, TypedClient, type UpdateData, collectionTypeName, createClient, fieldTypeKind, fieldTypeScriptType, generateTypes, getFileUrl, schemaFieldType, wsUrlFromBaseUrl };
741
+ export { ApiError, type ApiRecord, type AuthModel, AuthStore, type CollectionSchema, CollectionService, type CreateData, type FileRecord, FilesService, LazypockClient, type LazypockClientOptions, type LazypockCollections, type ListResult, type RealtimeCallback, type RealtimeMessage, RealtimeService, type RecordShape, type RequestOptions, type SchemaField, type StorageAdapter, type SystemFields, TypedClient, type UpdateData, collectionTypeName, createClient, fieldTypeKind, fieldTypeScriptType, generateTypes, getFileUrl, schemaFieldType, wsUrlFromBaseUrl };
@@ -329,12 +329,25 @@ var Lazypock = (() => {
329
329
  };
330
330
 
331
331
  // src/collection.ts
332
+ function normalizeAction(event, rawAction) {
333
+ if (typeof rawAction === "string") {
334
+ const a = rawAction.toLowerCase();
335
+ if (a === "create" || a === "update" || a === "delete") {
336
+ return a;
337
+ }
338
+ }
339
+ if (event === "record_change") return "update";
340
+ if (event === "create" || event === "record_create") return "create";
341
+ if (event === "delete" || event === "record_delete") return "delete";
342
+ return "update";
343
+ }
332
344
  var CollectionService = class {
333
345
  /** @internal */
334
- constructor(http, collectionName, authStore) {
346
+ constructor(http, collectionName, authStore, realtime) {
335
347
  this.http = http;
336
348
  this.collectionName = collectionName;
337
349
  this.authStore = authStore;
350
+ this.realtime = realtime;
338
351
  }
339
352
  encodeId(id) {
340
353
  return encodeURIComponent(id);
@@ -406,10 +419,7 @@ var Lazypock = (() => {
406
419
  * Useful for constructing `expand` query parameters.
407
420
  */
408
421
  async expandFields(options) {
409
- const data = await this.http.get(
410
- "/collections/" + this.encodeId(this.collectionName),
411
- options
412
- );
422
+ const data = await this.http.get("/collections/" + this.encodeId(this.collectionName), options);
413
423
  if (!data?.fields) return null;
414
424
  return data.fields.filter((f) => f.type === "relation" && f.options?.collection).map((f) => ({
415
425
  field: f.name,
@@ -436,6 +446,46 @@ var Lazypock = (() => {
436
446
  typed() {
437
447
  return this;
438
448
  }
449
+ // ── Realtime Subscriptions (PocketBase-style) ──
450
+ /**
451
+ * Subscribe to realtime changes for this collection.
452
+ * The event's `action` is one of `"create" | "update" | "delete"`.
453
+ *
454
+ * Access is governed by the collection's `listRule` (PocketBase semantics):
455
+ * public collections allow anonymous subscriptions; other collections
456
+ * require a matching logged-in user or superuser.
457
+ *
458
+ * @param callback Received on every record change.
459
+ * @param recordId Optional — subscribe to a single record instead of `*`.
460
+ * @returns A function that unsubscribes this callback.
461
+ */
462
+ subscribe(callback, recordId) {
463
+ if (!this.realtime) {
464
+ console.warn("[lazypock] No realtime service configured.");
465
+ return () => {
466
+ };
467
+ }
468
+ const topic = "collection:" + this.collectionName + (recordId ? ":" + recordId : "");
469
+ const handler = (raw) => {
470
+ const record = raw.payload?.["record"] ?? {};
471
+ callback({
472
+ action: normalizeAction(raw.event, raw.payload?.["action"]),
473
+ record,
474
+ topic: raw.topic
475
+ });
476
+ };
477
+ this.realtime.ensureConnected();
478
+ this.realtime.subscribe(topic, handler);
479
+ return () => this.realtime?.unsubscribe(topic, handler);
480
+ }
481
+ /**
482
+ * Unsubscribe all callbacks from this collection (or a specific record).
483
+ * @param recordId Optional record id; omitting it unsubs everything.
484
+ */
485
+ unsubscribe(recordId) {
486
+ const topic = "collection:" + this.collectionName + (recordId ? ":" + recordId : "");
487
+ this.realtime?.unsubscribe(topic);
488
+ }
439
489
  // ── Auth Collection Methods ──
440
490
  /**
441
491
  * Authenticate with email/password against this auth collection.
@@ -472,6 +522,7 @@ var Lazypock = (() => {
472
522
  /**
473
523
  * Get available auth methods for this collection.
474
524
  */
525
+ // ── end Realtime ──
475
526
  async authMethods(options) {
476
527
  return this.http.get(
477
528
  "/" + this.encodeId(this.collectionName) + "/auth-methods",
@@ -502,6 +553,39 @@ var Lazypock = (() => {
502
553
  // ── Heartbeat ──
503
554
  this.heartbeatInterval = null;
504
555
  }
556
+ /** Whether the WebSocket is currently open. */
557
+ get isOpen() {
558
+ return this.ws?.readyState === WebSocket.OPEN;
559
+ }
560
+ /**
561
+ * The most recently used WebSocket URL (set on {@link connect}).
562
+ * Useful for SDK convenience methods that auto-connect before subscribing.
563
+ */
564
+ get lastUrl() {
565
+ return this.url;
566
+ }
567
+ /** The auth token configured for this connection (set on connect). */
568
+ get lastToken() {
569
+ return this.token;
570
+ }
571
+ /**
572
+ * Set the socket URL. Useful before subscribing so the SDK can
573
+ * auto-connect on the first {@link subscribe}.
574
+ */
575
+ setUrl(url) {
576
+ this.url = url;
577
+ }
578
+ /*
579
+ * Ensure the socket is connected, then subscribe.
580
+ * Used by collection-level convenience wrappers so a connection is opened
581
+ * automatically on the first subscribe (matching PocketBase behaviour —
582
+ * works for anonymous/public collections too).
583
+ */
584
+ ensureConnected() {
585
+ if (this.isOpen || !this.url) return;
586
+ if (typeof WebSocket === "undefined") return;
587
+ this.doConnect();
588
+ }
505
589
  connect(opts) {
506
590
  this.url = opts.url;
507
591
  this.token = opts.token;
@@ -918,6 +1002,9 @@ ${opts.body}
918
1002
  this.authStore = options.authStore ?? new AuthStore(options.storage ?? memoryStorage);
919
1003
  this.http = new HttpClient(baseUrl, this.authStore);
920
1004
  this.realtime = options.realtime ?? new RealtimeService();
1005
+ if (!options.realtime) {
1006
+ this.realtime.setUrl(wsUrlFromBaseUrl(baseUrl));
1007
+ }
921
1008
  this.files = new FilesService(this.http);
922
1009
  if (options.types?.schemas) {
923
1010
  this.schemaByName = new Map(
@@ -939,7 +1026,12 @@ ${opts.body}
939
1026
  collection(name) {
940
1027
  let svc = this.collectionCache.get(name);
941
1028
  if (!svc) {
942
- svc = new CollectionService(this.http, name, this.authStore);
1029
+ svc = new CollectionService(
1030
+ this.http,
1031
+ name,
1032
+ this.authStore,
1033
+ this.realtime
1034
+ );
943
1035
  this.collectionCache.set(name, svc);
944
1036
  }
945
1037
  return svc;