@rebasepro/server-postgres 0.13.0 → 0.13.1-canary.g3660bd5
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/{ensure-collection-policies-ViG8XiPn.js → ensure-collection-policies-25nZOd87.js} +2 -2
- package/dist/{ensure-collection-policies-ViG8XiPn.js.map → ensure-collection-policies-25nZOd87.js.map} +1 -1
- package/dist/{ensure-collection-tables-CBQdOETu.js → ensure-collection-tables-DRkgVBV5.js} +2 -2
- package/dist/{ensure-collection-tables-CBQdOETu.js.map → ensure-collection-tables-DRkgVBV5.js.map} +1 -1
- package/dist/index.es.js +110 -93
- package/dist/index.es.js.map +1 -1
- package/dist/schema/generated-schema-staleness.d.ts +39 -0
- package/dist/services/FetchService.d.ts +10 -7
- package/dist/services/dataService.d.ts +2 -0
- package/dist/services/realtimeService.d.ts +25 -21
- package/dist/{src-DlPBctw_.js → src-Bs2ZzSi4.js} +92 -15
- package/dist/src-Bs2ZzSi4.js.map +1 -0
- package/package.json +6 -6
- package/src/cli.ts +79 -0
- package/src/collections/validate-relations.ts +124 -17
- package/src/data-transformer.ts +13 -3
- package/src/schema/generated-schema-staleness.ts +164 -0
- package/src/schema/introspect-db-logic.ts +1 -1
- package/src/services/FetchService.ts +10 -93
- package/src/services/PersistService.ts +14 -1
- package/src/services/dataService.ts +2 -0
- package/src/services/realtimeService.ts +36 -33
- package/dist/src-DlPBctw_.js.map +0 -1
|
@@ -4,7 +4,7 @@ import { Client as PgClient } from "pg";
|
|
|
4
4
|
import { randomUUID } from "crypto";
|
|
5
5
|
import { DataService } from "./dataService";
|
|
6
6
|
|
|
7
|
-
import { ANONYMOUS_USER_ID, FetchCollectionProps, ListenCollectionProps, ListenOneProps, DataDriver, CollectionUpdateMessage, SingleUpdateMessage, CollectionPatchMessage, WebSocketMessage, FilterValues, CollectionConfig, RebaseCallContext, resolveClientListLimit } from "@rebasepro/types";
|
|
7
|
+
import { ANONYMOUS_USER_ID, FetchCollectionProps, ListenCollectionProps, ListenOneProps, DataDriver, CollectionUpdateMessage, SingleUpdateMessage, CollectionPatchMessage, WebSocketMessage, FilterValues, LogicalCondition, CollectionConfig, RebaseCallContext, resolveClientListLimit } from "@rebasepro/types";
|
|
8
8
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
9
9
|
import { sql as drizzleSql } from "drizzle-orm";
|
|
10
10
|
import { RealtimeProvider, CollectionSubscriptionConfig, SingleSubscriptionConfig } from "../interfaces";
|
|
@@ -41,6 +41,28 @@ type RealTimeListenCollectionProps = ListenCollectionProps & {
|
|
|
41
41
|
subscriptionId: string
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* The narrowing a collection subscription was created with, kept so that every
|
|
46
|
+
* refetch answers the same query the initial fetch did.
|
|
47
|
+
*
|
|
48
|
+
* Named once because it used to be written out inline in five places, and a
|
|
49
|
+
* field missing from one of them is accepted over the wire and then silently
|
|
50
|
+
* ignored: `offset` was declared on the incoming props and never stored, so a
|
|
51
|
+
* live list on page three served page one, and `logical` was never stored
|
|
52
|
+
* either, so an `or(...)` subscription was pushed every row in the table.
|
|
53
|
+
*/
|
|
54
|
+
type StoredCollectionRequest = {
|
|
55
|
+
filter?: Record<string, unknown>;
|
|
56
|
+
logical?: LogicalCondition;
|
|
57
|
+
orderBy?: string;
|
|
58
|
+
order?: "desc" | "asc";
|
|
59
|
+
limit?: number;
|
|
60
|
+
offset?: number;
|
|
61
|
+
startAfter?: Record<string, unknown>;
|
|
62
|
+
databaseId?: string;
|
|
63
|
+
searchString?: string;
|
|
64
|
+
};
|
|
65
|
+
|
|
44
66
|
type RealTimeListenEntityProps = ListenOneProps & { subscriptionId: string };
|
|
45
67
|
|
|
46
68
|
/**
|
|
@@ -119,16 +141,7 @@ export class RealtimeService extends EventEmitter implements RealtimeProvider {
|
|
|
119
141
|
path: string;
|
|
120
142
|
id?: string | number;
|
|
121
143
|
// Store full collection request parameters for proper refetching
|
|
122
|
-
collectionRequest?:
|
|
123
|
-
filter?: Record<string, unknown>;
|
|
124
|
-
orderBy?: string;
|
|
125
|
-
order?: "desc" | "asc";
|
|
126
|
-
limit?: number;
|
|
127
|
-
offset?: number;
|
|
128
|
-
startAfter?: Record<string, unknown>;
|
|
129
|
-
databaseId?: string;
|
|
130
|
-
searchString?: string;
|
|
131
|
-
};
|
|
144
|
+
collectionRequest?: StoredCollectionRequest;
|
|
132
145
|
// Auth context for RLS — when set, refetches run in a transaction
|
|
133
146
|
// with set_config('app.uid', ...) / set_config('app.user_roles', ...)
|
|
134
147
|
authContext?: SubscriptionAuthContext;
|
|
@@ -212,16 +225,7 @@ export class RealtimeService extends EventEmitter implements RealtimeProvider {
|
|
|
212
225
|
type: "collection" | "single";
|
|
213
226
|
path: string;
|
|
214
227
|
id?: string | number;
|
|
215
|
-
collectionRequest?:
|
|
216
|
-
filter?: Record<string, unknown>;
|
|
217
|
-
orderBy?: string;
|
|
218
|
-
order?: "desc" | "asc";
|
|
219
|
-
limit?: number;
|
|
220
|
-
offset?: number;
|
|
221
|
-
startAfter?: Record<string, unknown>;
|
|
222
|
-
databaseId?: string;
|
|
223
|
-
searchString?: string;
|
|
224
|
-
};
|
|
228
|
+
collectionRequest?: StoredCollectionRequest;
|
|
225
229
|
authContext?: SubscriptionAuthContext;
|
|
226
230
|
}) {
|
|
227
231
|
this.debugLog("📋 [RealtimeService] Registering DataDriver subscription:", subscriptionId, subscription.authContext ? "(with auth)" : "(no auth)");
|
|
@@ -448,9 +452,11 @@ export class RealtimeService extends EventEmitter implements RealtimeProvider {
|
|
|
448
452
|
path: request.path,
|
|
449
453
|
collectionRequest: {
|
|
450
454
|
filter: request.filter,
|
|
455
|
+
logical: request.logical,
|
|
451
456
|
orderBy: request.orderBy,
|
|
452
457
|
order: request.order,
|
|
453
458
|
limit: boundedLimit,
|
|
459
|
+
offset: request.offset,
|
|
454
460
|
startAfter: request.startAfter as Record<string, unknown> | undefined,
|
|
455
461
|
databaseId: request.collection?.databaseId,
|
|
456
462
|
searchString: request.searchString
|
|
@@ -458,17 +464,12 @@ export class RealtimeService extends EventEmitter implements RealtimeProvider {
|
|
|
458
464
|
authContext
|
|
459
465
|
});
|
|
460
466
|
|
|
461
|
-
// Send initial data
|
|
467
|
+
// Send initial data. Built from the request the subscription just
|
|
468
|
+
// stored, so the first answer and every refetch after it cannot
|
|
469
|
+
// describe different queries.
|
|
462
470
|
const rows = await this.fetchCollectionWithAuth(
|
|
463
471
|
request.path,
|
|
464
|
-
|
|
465
|
-
filter: request.filter,
|
|
466
|
-
orderBy: request.orderBy,
|
|
467
|
-
order: request.order,
|
|
468
|
-
limit: boundedLimit,
|
|
469
|
-
startAfter: request.startAfter as Record<string, unknown> | undefined,
|
|
470
|
-
searchString: request.searchString
|
|
471
|
-
},
|
|
472
|
+
this._subscriptions.get(subscriptionId)!.collectionRequest!,
|
|
472
473
|
authContext
|
|
473
474
|
);
|
|
474
475
|
|
|
@@ -679,7 +680,7 @@ export class RealtimeService extends EventEmitter implements RealtimeProvider {
|
|
|
679
680
|
private debouncedCollectionRefetch(
|
|
680
681
|
subscriptionId: string,
|
|
681
682
|
notifyPath: string,
|
|
682
|
-
subscription: { clientId: string; collectionRequest?:
|
|
683
|
+
subscription: { clientId: string; collectionRequest?: StoredCollectionRequest; authContext?: SubscriptionAuthContext }
|
|
683
684
|
) {
|
|
684
685
|
const timerKey = `ws_${subscriptionId}`;
|
|
685
686
|
const existing = this.refetchTimers.get(timerKey);
|
|
@@ -705,7 +706,7 @@ export class RealtimeService extends EventEmitter implements RealtimeProvider {
|
|
|
705
706
|
private debouncedDriverRefetch(
|
|
706
707
|
subscriptionId: string,
|
|
707
708
|
notifyPath: string,
|
|
708
|
-
subscription: { collectionRequest?:
|
|
709
|
+
subscription: { collectionRequest?: StoredCollectionRequest; authContext?: SubscriptionAuthContext },
|
|
709
710
|
callback: (data: Record<string, unknown>[] | Record<string, unknown> | null) => void
|
|
710
711
|
) {
|
|
711
712
|
const timerKey = `drv_${subscriptionId}`;
|
|
@@ -731,7 +732,7 @@ export class RealtimeService extends EventEmitter implements RealtimeProvider {
|
|
|
731
732
|
*/
|
|
732
733
|
private async fetchCollectionWithAuth(
|
|
733
734
|
notifyPath: string,
|
|
734
|
-
collectionRequest:
|
|
735
|
+
collectionRequest: StoredCollectionRequest,
|
|
735
736
|
authContext?: SubscriptionAuthContext
|
|
736
737
|
): Promise<Record<string, unknown>[]> {
|
|
737
738
|
if (this.driver) {
|
|
@@ -740,6 +741,7 @@ export class RealtimeService extends EventEmitter implements RealtimeProvider {
|
|
|
740
741
|
path: notifyPath,
|
|
741
742
|
collection: collection,
|
|
742
743
|
filter: collectionRequest.filter as FetchCollectionProps["filter"],
|
|
744
|
+
logical: collectionRequest.logical,
|
|
743
745
|
orderBy: collectionRequest.orderBy,
|
|
744
746
|
order: collectionRequest.order,
|
|
745
747
|
limit: collectionRequest.limit,
|
|
@@ -772,6 +774,7 @@ roles: ["anon"] };
|
|
|
772
774
|
} else {
|
|
773
775
|
fetchedEntities = await txEntityService.fetchCollection(notifyPath, {
|
|
774
776
|
filter: collectionRequest.filter as FilterValues<string>,
|
|
777
|
+
logical: collectionRequest.logical,
|
|
775
778
|
orderBy: collectionRequest.orderBy,
|
|
776
779
|
order: collectionRequest.order,
|
|
777
780
|
limit: collectionRequest.limit,
|