prostgles-client 4.0.165 → 4.0.167

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/lib/prostgles.ts CHANGED
@@ -11,7 +11,6 @@ import type {
11
11
  DBSchema,
12
12
  DBSchemaTable,
13
13
  DbJoinMaker,
14
- DeleteParams,
15
14
  EqualityFilter,
16
15
  FullFilter,
17
16
  GetSelectReturnType,
@@ -20,10 +19,7 @@ import type {
20
19
  SQLResult,
21
20
  SelectParams,
22
21
  SubscribeParams,
23
- SubscriptionChannels,
24
- SubscriptionHandler,
25
22
  TableHandler,
26
- UpdateParams,
27
23
  ViewHandler
28
24
  } from "prostgles-types";
29
25
 
@@ -40,7 +36,9 @@ import { type AuthHandler, setupAuth } from "./Auth";
40
36
  import { FunctionQueuer } from "./FunctionQueuer";
41
37
  import { isEqual, useFetch, useSubscribe, useSync } from "./react-hooks";
42
38
  import { SQL } from "./SQL";
39
+ import { getSubscriptionHandler } from "./subscriptionHandler";
43
40
  import type { DbTableSync, Sync, SyncDataItem, SyncOne, SyncOneOptions, SyncOptions, SyncedTable } from "./SyncedTable/SyncedTable";
41
+ import { getSyncHandler } from "./syncHandler";
44
42
 
45
43
  const DEBUG_KEY = "DEBUG_SYNCEDTABLE";
46
44
  export const hasWnd = typeof window !== "undefined";
@@ -51,9 +49,8 @@ export const debug: any = function (...args: any[]) {
51
49
  };
52
50
 
53
51
  export { MethodHandler, SQLResult, asName };
54
-
55
- export * from "./react-hooks";
56
- export * from "./useProstglesClient";
52
+ export * from "./react-hooks";
53
+ export * from "./useProstglesClient";
57
54
 
58
55
 
59
56
  export type ViewHandlerClient<T extends AnyObject = AnyObject, S extends DBSchema | void = void> = ViewHandler<T, S> & {
@@ -148,11 +145,17 @@ type DebugEvent =
148
145
  command: keyof TableHandlerClient;
149
146
  tableName: string;
150
147
  data: AnyObject;
151
- } | {
148
+ }
149
+ | {
152
150
  type: "method";
153
151
  command: string;
154
152
  data: AnyObject;
155
- } | SyncDebugEvent;
153
+ }
154
+ | SyncDebugEvent
155
+ | {
156
+ type: "schemaChanged";
157
+ data: AnyObject;
158
+ };
156
159
 
157
160
  export type InitOptions<DBSchema = void> = {
158
161
  socket: any;
@@ -178,28 +181,15 @@ export type InitOptions<DBSchema = void> = {
178
181
  onDebug?: (event: DebugEvent) => any;
179
182
  }
180
183
 
181
- type AnyFunction = (...args: any[]) => any;
184
+ export type AnyFunction = (...args: any[]) => any;
182
185
 
183
- type CoreParams = {
186
+ export type CoreParams = {
184
187
  tableName: string;
185
188
  command: string;
186
189
  param1?: AnyObject;
187
190
  param2?: AnyObject;
188
191
  };
189
192
 
190
- type Subscription = CoreParams & {
191
- lastData: any;
192
- onCall: AnyFunction,
193
- handlers: AnyFunction[];
194
- errorHandlers: (AnyFunction | undefined)[];
195
- unsubChannel: string;
196
- destroy: () => any;
197
- };
198
-
199
- type Subscriptions = {
200
- [key: string]: Subscription
201
- };
202
-
203
193
  export type onUpdatesParams = { data: object[]; isSynced: boolean }
204
194
 
205
195
  export type SyncInfo = {
@@ -207,14 +197,6 @@ export type SyncInfo = {
207
197
  synced_field: string,
208
198
  channelName: string
209
199
  }
210
- type SyncConfig = CoreParams & {
211
- onCall: AnyFunction,
212
- syncInfo: SyncInfo;
213
- triggers: ClientSyncHandles[]
214
- };
215
- type Syncs = {
216
- [channelName: string]: SyncConfig;
217
- };
218
200
  type CurrentClientSchema = {
219
201
  origin: "onReady" | "onReconnect";
220
202
  date: Date;
@@ -234,317 +216,15 @@ export function prostgles<DBSchema>(initOpts: InitOptions<DBSchema>, syncedTable
234
216
  }
235
217
 
236
218
  const preffix = CHANNELS._preffix;
237
- const subscriptions: Subscriptions = {};
238
219
 
239
- let syncedTables: Record<string, any> = {};
240
220
 
241
- let syncs: Syncs = {};
242
- let state: undefined | "connected" | "disconnected" | "reconnected";
243
- const sql = new SQL()
244
-
245
- const destroySyncs = async () => {
246
- debug("destroySyncs", { subscriptions, syncedTables });
247
- await Promise.all(Object.values(subscriptions).map(s => s.destroy()));
248
- syncs = {};
249
- Object.values(syncedTables).map((s: any) => {
250
- if (s && s.destroy) s.destroy();
251
- });
252
- syncedTables = {};
253
- }
254
-
255
- function _unsubscribe(channelName: string, unsubChannel: string, handler: AnyFunction, onDebug: InitOptions["onDebug"]) {
256
- debug("_unsubscribe", { channelName, handler });
257
-
258
- return new Promise((resolve, reject) => {
259
- const sub = subscriptions[channelName];
260
- if (sub) {
261
- sub.handlers = sub.handlers.filter(h => h !== handler);
262
- if (!sub.handlers.length) {
263
- onDebug?.({ type: "table", command: "unsubscribe", tableName: sub.tableName });
264
- socket.emit(unsubChannel, {}, (err: any, _res: any) => {
265
- if (err) console.error(err);
266
- else reject(err);
267
- });
268
- socket.removeListener(channelName, sub.onCall);
269
- delete subscriptions[channelName];
270
-
271
- /* Not waiting for server confirmation to speed things up */
272
- resolve(true)
273
- } else {
274
- onDebug?.({ type: "table", command: "unsubscribe", tableName: sub.tableName, unsubChannel });
275
- resolve(true)
276
- }
277
- } else {
278
- resolve(true)
279
- }
280
- });
281
- }
221
+ //@ts-ignore
222
+ const subscriptionHandler = getSubscriptionHandler(initOpts);
223
+ const syncHandler = getSyncHandler(initOpts);
282
224
 
283
- function _unsync(channelName: string, triggers: ClientSyncHandles) {
284
- debug("_unsync", { channelName, triggers })
285
- return new Promise((resolve, reject) => {
286
- if (syncs[channelName]) {
287
- syncs[channelName]!.triggers = syncs[channelName]!.triggers.filter(tr => (
288
- tr.onPullRequest !== triggers.onPullRequest &&
289
- tr.onSyncRequest !== triggers.onSyncRequest &&
290
- tr.onUpdates !== triggers.onUpdates
291
- ));
292
-
293
- if (!syncs[channelName]!.triggers.length) {
294
- socket.emit(channelName + "unsync", {}, (err: any, res: any) => {
295
- if (err) reject(err);
296
- else resolve(res);
297
- });
298
- socket.removeListener(channelName, syncs[channelName]!.onCall);
299
- delete syncs[channelName];
300
- }
301
- }
302
- });
303
- }
304
- function addServerSync({ tableName, command, param1, param2 }: CoreParams, onSyncRequest: ClientSyncHandles["onSyncRequest"]): Promise<SyncInfo> {
305
- return new Promise((resolve, reject) => {
306
- socket.emit(preffix, { tableName, command, param1, param2 }, (err: any, res: SyncInfo) => {
307
- if (err) {
308
- console.error(err);
309
- reject(err);
310
- } else if (res as any) {
311
- const { id_fields, synced_field, channelName } = res;
312
-
313
- socket.emit(channelName, { onSyncRequest: onSyncRequest({}) }, (response: any) => {
314
- console.log(response);
315
- });
316
- resolve({ id_fields, synced_field, channelName });
317
- }
318
- });
319
- });
320
- }
321
- /**
322
- * Obtaines subscribe channel from server
323
- */
324
- function addServerSub({ tableName, command, param1, param2 }: CoreParams): Promise<SubscriptionChannels> {
325
- return new Promise((resolve, reject) => {
326
- socket.emit(preffix, { tableName, command, param1, param2 }, (err?: any, res?: SubscriptionChannels) => {
327
- if (err) {
328
- console.error(err);
329
- reject(err);
330
- } else if (res) {
331
- resolve(res);
332
- }
333
- });
334
- });
335
- }
336
-
337
- const addSyncQueuer = new FunctionQueuer(_addSync, ([{ tableName }]) => tableName);
338
- async function addSync(params: CoreParams, triggers: ClientSyncHandles): Promise<any> {
339
- return addSyncQueuer.run([params, triggers])
340
- }
341
- async function _addSync({ tableName, command, param1, param2 }: CoreParams, triggers: ClientSyncHandles): Promise<any> {
342
- const { onSyncRequest } = triggers;
343
-
344
- function makeHandler(channelName: string) {
345
- const unsync = function () {
346
- _unsync(channelName, triggers);
347
- }
348
-
349
- const syncData: DbTableSync["syncData"] = function (data, deleted, cb) {
350
- socket.emit(channelName,
351
- {
352
- onSyncRequest: {
353
- ...onSyncRequest({}),
354
- ...({ data }),
355
- ...({ deleted })
356
- },
357
- },
358
- !cb ? null : (response?: any) => {
359
- cb(response)
360
- }
361
- );
362
- }
363
-
364
- return Object.freeze({ unsync, syncData });
365
- }
366
-
367
- const existingChannel = Object.keys(syncs).find(ch => {
368
- const s = syncs[ch];
369
- return (
370
- s &&
371
- s.tableName === tableName &&
372
- s.command === command &&
373
- isEqual(s.param1, param1) &&
374
- isEqual(s.param2, param2)
375
- );
376
- });
377
-
378
- if (existingChannel) {
379
- syncs[existingChannel]!.triggers.push(triggers);
380
- return makeHandler(existingChannel);
381
- } else {
382
- const sync_info = await addServerSync({ tableName, command, param1, param2 }, onSyncRequest);
383
- const { channelName } = sync_info;
384
- const onCall = function (data: any | undefined, cb: AnyFunction) {
385
- /*
386
- Client will:
387
- 1. Send last_synced on(onSyncRequest)
388
- 2. Send data >= server_synced on(onPullRequest)
389
- 3. Send data on CRUD emit(data.data)
390
- 4. Upsert data.data on(data.data)
391
- */
392
- if (!data) return;
393
-
394
- if (!syncs[channelName]) return;
395
-
396
- syncs[channelName]!.triggers.map(({ onUpdates, onSyncRequest, onPullRequest }) => {
397
- if (data.data) {
398
- Promise.resolve(onUpdates(data))
399
- .then(() => {
400
- cb({ ok: true })
401
- })
402
- .catch(err => {
403
- cb({ err });
404
- });
405
- } else if (data.onSyncRequest) {
406
- Promise.resolve(onSyncRequest(data.onSyncRequest))
407
- .then(res => cb({ onSyncRequest: res }))
408
- .catch(err => {
409
- cb({ err });
410
- })
411
-
412
- } else if (data.onPullRequest) {
413
- Promise.resolve(onPullRequest(data.onPullRequest))
414
- .then(arr => {
415
- cb({ data: arr });
416
- })
417
- .catch(err => {
418
- cb({ err });
419
- })
420
- } else {
421
- console.log("unexpected response")
422
- }
423
- })
424
-
425
-
426
- }
427
- syncs[channelName] = {
428
- tableName,
429
- command,
430
- param1,
431
- param2,
432
- triggers: [triggers],
433
- syncInfo: sync_info,
434
- onCall
435
- }
436
-
437
- socket.on(channelName, onCall);
438
- return makeHandler(channelName);
439
- }
440
-
441
- }
442
-
443
- /**
444
- * Can be used concurrently
445
- */
446
- const addSubQueuer = new FunctionQueuer(_addSub, ([_, { tableName }]) => tableName);
447
- async function addSub(dbo: any, params: CoreParams, onChange: AnyFunction, _onError?: AnyFunction): Promise<SubscriptionHandler> {
448
- return addSubQueuer.run([dbo, params, onChange, _onError]);
449
- }
450
-
451
- /**
452
- * Do NOT use concurrently
453
- */
454
- async function _addSub(dbo: any, { tableName, command, param1, param2 }: CoreParams, onChange: AnyFunction, _onError?: AnyFunction): Promise<SubscriptionHandler> {
455
-
456
- function makeHandler(channelName: string, unsubChannel: string) {
457
-
458
- const unsubscribe = function () {
459
- return _unsubscribe(channelName, unsubChannel, onChange, onDebug);
460
- }
461
- let res: any = { unsubscribe, filter: { ...param1 } }
462
- /* Some dbo sorting was done to make sure this will work */
463
- if (dbo[tableName].update) {
464
- res = {
465
- ...res,
466
- update: function (newData: AnyObject, updateParams: UpdateParams) {
467
- return dbo[tableName].update(param1, newData, updateParams);
468
- }
469
- }
470
- }
471
- if (dbo[tableName].delete) {
472
- res = {
473
- ...res,
474
- delete: function (deleteParams: DeleteParams) {
475
- return dbo[tableName].delete(param1, deleteParams);
476
- }
477
- }
478
- }
479
- return Object.freeze(res);
480
- }
481
-
482
- const existing = Object.entries(subscriptions).find(([ch, s]) => {
483
- return (
484
- s.tableName === tableName &&
485
- s.command === command &&
486
- JSON.stringify(s.param1 || {}) === JSON.stringify(param1 || {}) &&
487
- JSON.stringify(s.param2 || {}) === JSON.stringify(param2 || {})
488
- );
489
- });
490
-
491
- if (existing) {
492
- const existingCh = existing[0];
493
- existing[1].handlers.push(onChange);
494
- existing[1].errorHandlers.push(_onError);
495
- setTimeout(() => {
496
- if (subscriptions[existingCh]?.lastData) {
497
- onChange(subscriptions[existingCh]?.lastData)
498
- }
499
- }, 10)
500
- return makeHandler(existingCh, existing[1].unsubChannel);
501
- }
225
+ let state: undefined | "connected" | "disconnected" | "reconnected";
226
+ const sql = new SQL();
502
227
 
503
- const { channelName, channelNameReady, channelNameUnsubscribe } = await addServerSub({ tableName, command, param1, param2 })
504
-
505
- const onCall = function (data: any) {
506
- /* TO DO: confirm receiving data or server will unsubscribe */
507
- // if(cb) cb(true);
508
- const sub = subscriptions[channelName];
509
- if (sub) {
510
- if (data.data) {
511
- sub.lastData = data.data;
512
- sub.handlers.forEach(h => {
513
- h(data.data);
514
- });
515
- } else if (data.err) {
516
- sub.errorHandlers.forEach(h => {
517
- h?.(data.err);
518
- });
519
- } else {
520
- console.error("INTERNAL ERROR: Unexpected data format from subscription: ", data)
521
- }
522
- } else {
523
- console.warn("Orphaned subscription: ", channelName)
524
- }
525
- }
526
- const onError = _onError || function (err: any) { console.error(`Uncaught error within running subscription \n ${channelName}`, err) }
527
-
528
- socket.on(channelName, onCall);
529
- subscriptions[channelName] = {
530
- lastData: undefined,
531
- tableName,
532
- command,
533
- param1,
534
- param2,
535
- onCall,
536
- unsubChannel: channelNameUnsubscribe,
537
- handlers: [onChange],
538
- errorHandlers: [onError],
539
- destroy: async () => {
540
- for await(const h of subscriptions[channelName]?.handlers ?? []){
541
- await _unsubscribe(channelName, channelNameUnsubscribe, h, onDebug);
542
- }
543
- }
544
- }
545
- socket.emit(channelNameReady, { now: Date.now() });
546
- return makeHandler(channelName, channelNameUnsubscribe);
547
- }
548
228
 
549
229
  return new Promise((resolve, reject) => {
550
230
 
@@ -571,12 +251,15 @@ export function prostgles<DBSchema>(initOpts: InitOptions<DBSchema>, syncedTable
571
251
 
572
252
  /* Schema = published schema */
573
253
  socket.on(CHANNELS.SCHEMA, async ({ joinTables = [], ...clientSchema }: ClientSchema) => {
254
+ await onDebug?.({ type: "schemaChanged", data: clientSchema });
574
255
  const { schema, methods, tableSchema, auth: authConfig, rawSQL, err } = clientSchema;
256
+
575
257
  /** Only destroy existing syncs if schema changed */
576
258
  const schemaDidNotChange = schemaAge?.clientSchema && isEqual(schemaAge.clientSchema, clientSchema)
577
259
  if(!schemaDidNotChange){
578
- await destroySyncs();
260
+ await syncHandler.destroySyncs();
579
261
  }
262
+
580
263
  if ((state === "connected" || state === "reconnected") && onReconnect) {
581
264
  onReconnect(socket, err);
582
265
  if (err) {
@@ -656,8 +339,8 @@ export function prostgles<DBSchema>(initOpts: InitOptions<DBSchema>, syncedTable
656
339
  }
657
340
  const upsertSyncTable = async (basicFilter = {}, options: SyncOptions = {}, onError) => {
658
341
  const syncName = `${tableName}.${JSON.stringify(basicFilter)}.${JSON.stringify(omitKeys(options, ["handlesOnData"]))}`
659
- if (!syncedTables[syncName]) {
660
- syncedTables[syncName] = await syncedTable.create({
342
+ if (!syncHandler.syncedTables[syncName]) {
343
+ syncHandler.syncedTables[syncName] = await syncedTable.create({
661
344
  ...options,
662
345
  onDebug: onDebug as any,
663
346
  name: tableName,
@@ -666,7 +349,7 @@ export function prostgles<DBSchema>(initOpts: InitOptions<DBSchema>, syncedTable
666
349
  onError
667
350
  });
668
351
  }
669
- return syncedTables[syncName]
352
+ return syncHandler.syncedTables[syncName]
670
353
  }
671
354
  const sync: Sync<AnyObject> = async (basicFilter, options = { handlesOnData: true, select: "*" }, onChange, onError) => {
672
355
  await onDebug?.({ type: "table", command: "sync", tableName, data: { basicFilter, options } });
@@ -690,13 +373,13 @@ export function prostgles<DBSchema>(initOpts: InitOptions<DBSchema>, syncedTable
690
373
 
691
374
  dboTable._sync = async function (param1, param2, syncHandles) {
692
375
  await onDebug?.({ type: "table", command: "_sync", tableName, data: { param1, param2, syncHandles } });
693
- return addSync({ tableName, command, param1, param2 }, syncHandles);
376
+ return syncHandler.addSync({ tableName, command, param1, param2 }, syncHandles);
694
377
  }
695
378
  } else if (sub_commands.includes(command as any)) {
696
379
  const subFunc = async function (param1 = {}, param2 = {}, onChange, onError) {
697
380
  await onDebug?.({ type: "table", command: command as typeof sub_commands[number], tableName, data: { param1, param2, onChange, onError } });
698
381
  checkSubscriptionArgs(param1, param2, onChange, onError);
699
- return addSub(dbo, { tableName, command, param1, param2 }, onChange, onError);
382
+ return subscriptionHandler.addSub(dbo, { tableName, command, param1, param2 }, onChange, onError);
700
383
  };
701
384
  dboTable[command] = subFunc;
702
385
  const SUBONE = "subscribeOne";
@@ -716,7 +399,7 @@ export function prostgles<DBSchema>(initOpts: InitOptions<DBSchema>, syncedTable
716
399
  checkSubscriptionArgs(param1, param2, onChange, onError);
717
400
 
718
401
  const onChangeOne = (rows) => { onChange(rows[0]) };
719
- return addSub(dbo, { tableName, command, param1, param2 }, onChangeOne, onError);
402
+ return subscriptionHandler.addSub(dbo, { tableName, command, param1, param2 }, onChangeOne, onError);
720
403
  };
721
404
  }
722
405
  } else {
@@ -753,28 +436,8 @@ export function prostgles<DBSchema>(initOpts: InitOptions<DBSchema>, syncedTable
753
436
  })
754
437
  });
755
438
 
756
-
757
- // Re-attach listeners
758
- Object.entries(subscriptions).forEach(async ([ch, s]) => {
759
- try {
760
- await addServerSub(s);
761
- socket.on(ch, s.onCall);
762
- } catch (err) {
763
- console.error("There was an issue reconnecting old subscriptions", err)
764
- }
765
- });
766
- Object.entries(syncs).forEach(async ([ch, s]) => {
767
- const firstTrigger = s.triggers[0];
768
- if(firstTrigger){
769
- try {
770
- await addServerSync(s, firstTrigger.onSyncRequest);
771
- socket.on(ch, s.onCall);
772
- } catch (err) {
773
- console.error("There was an issue reconnecting olf subscriptions", err)
774
- }
775
- }
776
- });
777
-
439
+ await subscriptionHandler.reAttachAll();
440
+ await syncHandler.reAttachAll();
778
441
 
779
442
  joinTables.flat().map(table => {
780
443
  dbo.innerJoin ??= {};