@motherbase/client 1.0.1 → 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/README.md +155 -121
- package/dist/auth.d.ts +50 -45
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +96 -91
- package/dist/auth.js.map +1 -1
- package/dist/channel.d.ts +40 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/channel.js +74 -0
- package/dist/channel.js.map +1 -0
- package/dist/functions.d.ts +13 -0
- package/dist/functions.d.ts.map +1 -0
- package/dist/functions.js +21 -0
- package/dist/functions.js.map +1 -0
- package/dist/http.d.ts +27 -2
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +58 -5
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +52 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +66 -20
- package/dist/index.js.map +1 -1
- package/dist/postgrest.d.ts +58 -0
- package/dist/postgrest.d.ts.map +1 -0
- package/dist/postgrest.js +165 -0
- package/dist/postgrest.js.map +1 -0
- package/dist/realtime.d.ts +63 -2
- package/dist/realtime.d.ts.map +1 -1
- package/dist/realtime.js +192 -9
- package/dist/realtime.js.map +1 -1
- package/dist/storage.d.ts +48 -22
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +89 -26
- package/dist/storage.js.map +1 -1
- package/dist/types.d.ts +44 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { HttpClient } from './http.js';
|
|
2
|
+
import type { MotherbaseResponse } from './types.js';
|
|
3
|
+
type Primitive = string | number | boolean | null;
|
|
4
|
+
/**
|
|
5
|
+
* PostgREST-style, chainable, thenable query builder mapping onto the backend's
|
|
6
|
+
* `/rest/v1/:model` query-param dialect. Awaiting it resolves to `{ data, error }`
|
|
7
|
+
* and never rejects for expected API errors (only for programmer misuse).
|
|
8
|
+
*/
|
|
9
|
+
export declare class PostgrestQueryBuilder<Row extends object = Record<string, unknown>> implements PromiseLike<MotherbaseResponse<Row[]>> {
|
|
10
|
+
private readonly http;
|
|
11
|
+
private readonly model;
|
|
12
|
+
private method;
|
|
13
|
+
private columns;
|
|
14
|
+
private readonly filters;
|
|
15
|
+
private readonly orderParts;
|
|
16
|
+
private limitValue?;
|
|
17
|
+
private offsetValue?;
|
|
18
|
+
private body?;
|
|
19
|
+
private rowMode;
|
|
20
|
+
constructor(http: HttpClient, model: string);
|
|
21
|
+
select(columns?: string): this;
|
|
22
|
+
insert(values: Partial<Row> | Array<Partial<Row>>): this;
|
|
23
|
+
/**
|
|
24
|
+
* Emulated upsert. The backend has no native upsert, so this issues an insert.
|
|
25
|
+
* Documented as best-effort in the README compatibility matrix.
|
|
26
|
+
*/
|
|
27
|
+
upsert(values: Partial<Row> | Array<Partial<Row>>): this;
|
|
28
|
+
update(values: Partial<Row>): this;
|
|
29
|
+
delete(): this;
|
|
30
|
+
eq(column: string, value: Primitive): this;
|
|
31
|
+
neq(column: string, value: Primitive): this;
|
|
32
|
+
gt(column: string, value: Primitive): this;
|
|
33
|
+
gte(column: string, value: Primitive): this;
|
|
34
|
+
lt(column: string, value: Primitive): this;
|
|
35
|
+
lte(column: string, value: Primitive): this;
|
|
36
|
+
like(column: string, pattern: string): this;
|
|
37
|
+
ilike(column: string, pattern: string): this;
|
|
38
|
+
in(column: string, values: readonly Primitive[]): this;
|
|
39
|
+
is(column: string, value: null | boolean): this;
|
|
40
|
+
order(column: string, options?: {
|
|
41
|
+
ascending?: boolean;
|
|
42
|
+
}): this;
|
|
43
|
+
limit(count: number): this;
|
|
44
|
+
/** Inclusive range → offset/limit. `range(0, 9)` fetches 10 rows from offset 0. */
|
|
45
|
+
range(from: number, to: number): this;
|
|
46
|
+
single(): PostgrestSingleBuilder<Row>;
|
|
47
|
+
maybeSingle(): PostgrestSingleBuilder<Row>;
|
|
48
|
+
/** The URL path (with query string) this builder targets. Exposed for tests. */
|
|
49
|
+
toPath(): string;
|
|
50
|
+
private execute;
|
|
51
|
+
then<TResult1 = MotherbaseResponse<Row[]>, TResult2 = never>(onfulfilled?: ((value: MotherbaseResponse<Row[]>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
|
|
52
|
+
}
|
|
53
|
+
/** Same builder, typed to resolve a single row (from `.single()` / `.maybeSingle()`). */
|
|
54
|
+
export interface PostgrestSingleBuilder<Row extends object> extends Omit<PostgrestQueryBuilder<Row>, 'then' | 'single' | 'maybeSingle'>, PromiseLike<MotherbaseResponse<Row>> {
|
|
55
|
+
then<TResult1 = MotherbaseResponse<Row>, TResult2 = never>(onfulfilled?: ((value: MotherbaseResponse<Row>) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: unknown) => TResult2 | PromiseLike<TResult2>) | null): PromiseLike<TResult1 | TResult2>;
|
|
56
|
+
}
|
|
57
|
+
export {};
|
|
58
|
+
//# sourceMappingURL=postgrest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgrest.d.ts","sourceRoot":"","sources":["../src/postgrest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { toError } from './http.js';
|
|
2
|
+
function encodeValue(value) {
|
|
3
|
+
if (value === null)
|
|
4
|
+
return 'null';
|
|
5
|
+
return String(value);
|
|
6
|
+
}
|
|
7
|
+
function encodeList(values) {
|
|
8
|
+
return `(${values.map(encodeValue).join(',')})`;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* PostgREST-style, chainable, thenable query builder mapping onto the backend's
|
|
12
|
+
* `/rest/v1/:model` query-param dialect. Awaiting it resolves to `{ data, error }`
|
|
13
|
+
* and never rejects for expected API errors (only for programmer misuse).
|
|
14
|
+
*/
|
|
15
|
+
export class PostgrestQueryBuilder {
|
|
16
|
+
constructor(http, model) {
|
|
17
|
+
this.http = http;
|
|
18
|
+
this.model = model;
|
|
19
|
+
this.method = 'GET';
|
|
20
|
+
this.columns = '*';
|
|
21
|
+
this.filters = new URLSearchParams();
|
|
22
|
+
this.orderParts = [];
|
|
23
|
+
this.rowMode = 'many';
|
|
24
|
+
}
|
|
25
|
+
// --- verbs -------------------------------------------------------------
|
|
26
|
+
select(columns = '*') {
|
|
27
|
+
if (this.method === 'GET' && this.body === undefined)
|
|
28
|
+
this.method = 'GET';
|
|
29
|
+
this.columns = columns;
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
insert(values) {
|
|
33
|
+
this.method = 'POST';
|
|
34
|
+
this.body = values;
|
|
35
|
+
return this;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Emulated upsert. The backend has no native upsert, so this issues an insert.
|
|
39
|
+
* Documented as best-effort in the README compatibility matrix.
|
|
40
|
+
*/
|
|
41
|
+
upsert(values) {
|
|
42
|
+
this.method = 'POST';
|
|
43
|
+
this.body = values;
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
update(values) {
|
|
47
|
+
this.method = 'PATCH';
|
|
48
|
+
this.body = values;
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
delete() {
|
|
52
|
+
this.method = 'DELETE';
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
// --- filters -----------------------------------------------------------
|
|
56
|
+
eq(column, value) {
|
|
57
|
+
this.filters.set(column, `eq.${encodeValue(value)}`);
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
neq(column, value) {
|
|
61
|
+
this.filters.set(column, `neq.${encodeValue(value)}`);
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
gt(column, value) {
|
|
65
|
+
this.filters.set(column, `gt.${encodeValue(value)}`);
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
gte(column, value) {
|
|
69
|
+
this.filters.set(column, `gte.${encodeValue(value)}`);
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
lt(column, value) {
|
|
73
|
+
this.filters.set(column, `lt.${encodeValue(value)}`);
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
lte(column, value) {
|
|
77
|
+
this.filters.set(column, `lte.${encodeValue(value)}`);
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
like(column, pattern) {
|
|
81
|
+
this.filters.set(column, `like.${pattern}`);
|
|
82
|
+
return this;
|
|
83
|
+
}
|
|
84
|
+
ilike(column, pattern) {
|
|
85
|
+
this.filters.set(column, `ilike.${pattern}`);
|
|
86
|
+
return this;
|
|
87
|
+
}
|
|
88
|
+
in(column, values) {
|
|
89
|
+
this.filters.set(column, `in.${encodeList(values)}`);
|
|
90
|
+
return this;
|
|
91
|
+
}
|
|
92
|
+
is(column, value) {
|
|
93
|
+
this.filters.set(column, `is.${value === null ? 'null' : String(value)}`);
|
|
94
|
+
return this;
|
|
95
|
+
}
|
|
96
|
+
// --- modifiers ---------------------------------------------------------
|
|
97
|
+
order(column, options = {}) {
|
|
98
|
+
this.orderParts.push(`${column}.${options.ascending === false ? 'desc' : 'asc'}`);
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
limit(count) {
|
|
102
|
+
this.limitValue = count;
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
/** Inclusive range → offset/limit. `range(0, 9)` fetches 10 rows from offset 0. */
|
|
106
|
+
range(from, to) {
|
|
107
|
+
this.offsetValue = from;
|
|
108
|
+
this.limitValue = to - from + 1;
|
|
109
|
+
return this;
|
|
110
|
+
}
|
|
111
|
+
single() {
|
|
112
|
+
this.rowMode = 'single';
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
maybeSingle() {
|
|
116
|
+
this.rowMode = 'maybeSingle';
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
// --- execution ---------------------------------------------------------
|
|
120
|
+
/** The URL path (with query string) this builder targets. Exposed for tests. */
|
|
121
|
+
toPath() {
|
|
122
|
+
const params = new URLSearchParams();
|
|
123
|
+
if (this.columns && this.columns !== '*')
|
|
124
|
+
params.set('select', this.columns);
|
|
125
|
+
this.filters.forEach((value, key) => params.set(key, value));
|
|
126
|
+
if (this.orderParts.length)
|
|
127
|
+
params.set('order', this.orderParts.join(','));
|
|
128
|
+
if (typeof this.limitValue === 'number')
|
|
129
|
+
params.set('limit', String(this.limitValue));
|
|
130
|
+
if (typeof this.offsetValue === 'number')
|
|
131
|
+
params.set('offset', String(this.offsetValue));
|
|
132
|
+
const query = params.toString();
|
|
133
|
+
return `/rest/v1/${encodeURIComponent(this.model)}${query ? `?${query}` : ''}`;
|
|
134
|
+
}
|
|
135
|
+
async execute() {
|
|
136
|
+
try {
|
|
137
|
+
const rows = await this.http.request(this.toPath(), {
|
|
138
|
+
method: this.method,
|
|
139
|
+
body: this.body,
|
|
140
|
+
});
|
|
141
|
+
const list = Array.isArray(rows) ? rows : rows == null ? [] : [rows];
|
|
142
|
+
if (this.rowMode === 'single') {
|
|
143
|
+
if (list.length !== 1) {
|
|
144
|
+
const error = {
|
|
145
|
+
message: 'JSON object requested, multiple (or no) rows returned',
|
|
146
|
+
code: 'PGRST116',
|
|
147
|
+
};
|
|
148
|
+
return { data: null, error };
|
|
149
|
+
}
|
|
150
|
+
return { data: list[0], error: null };
|
|
151
|
+
}
|
|
152
|
+
if (this.rowMode === 'maybeSingle') {
|
|
153
|
+
return { data: list.length ? list[0] : null, error: null };
|
|
154
|
+
}
|
|
155
|
+
return { data: list, error: null };
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
return { data: null, error: toError(err) };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
then(onfulfilled, onrejected) {
|
|
162
|
+
return this.execute().then(onfulfilled, onrejected);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=postgrest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgrest.js","sourceRoot":"","sources":["../src/postgrest.ts"],"names":[],"mappings":"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"}
|
package/dist/realtime.d.ts
CHANGED
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
import type { SessionStore } from './session.js';
|
|
2
|
+
export type RealtimeStatus = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'polling' | 'disconnected' | 'error';
|
|
3
|
+
export type RealtimeStatusListener = (status: RealtimeStatus, detail?: unknown) => void;
|
|
4
|
+
export interface RealtimeConnectOptions {
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
fallbackPolling?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface RealtimeWatchOptions {
|
|
9
|
+
intervalMs?: number;
|
|
10
|
+
query?: string;
|
|
11
|
+
polling?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/** Events received are pre-filtered by server-side REST policies for the current session. */
|
|
14
|
+
export type RealtimeWatchEvent<T> = {
|
|
15
|
+
type: 'created' | 'updated' | 'deleted';
|
|
16
|
+
source: 'socket';
|
|
17
|
+
payload: T;
|
|
18
|
+
} | {
|
|
19
|
+
type: 'snapshot';
|
|
20
|
+
source: 'polling';
|
|
21
|
+
rows: T[];
|
|
22
|
+
};
|
|
23
|
+
/** Minimal shape we rely on from socket.io-client's Socket. */
|
|
24
|
+
export interface SocketLike {
|
|
25
|
+
on(event: string, cb: (...args: unknown[]) => void): void;
|
|
26
|
+
off(event: string, cb?: (...args: unknown[]) => void): void;
|
|
27
|
+
emit(event: string, ...args: unknown[]): void;
|
|
28
|
+
disconnect(): void;
|
|
29
|
+
connected: boolean;
|
|
30
|
+
}
|
|
31
|
+
export type IoFactory = (url: string, opts: Record<string, unknown>) => SocketLike;
|
|
2
32
|
/**
|
|
3
33
|
* Lazy wrapper over socket.io-client. The dependency is optional: it is only
|
|
4
34
|
* imported when `connect()` is called, so apps that never use realtime do not
|
|
@@ -7,12 +37,43 @@ import type { SessionStore } from './session.js';
|
|
|
7
37
|
export declare class RealtimeModule {
|
|
8
38
|
private readonly baseUrl;
|
|
9
39
|
private readonly store;
|
|
40
|
+
private readonly fetchImpl;
|
|
41
|
+
/** Inject a socket factory (tests / custom transports). Falls back to socket.io-client. */
|
|
42
|
+
private readonly ioFactory?;
|
|
10
43
|
private socket;
|
|
11
|
-
|
|
12
|
-
|
|
44
|
+
private status;
|
|
45
|
+
private readonly statusListeners;
|
|
46
|
+
private readonly pollers;
|
|
47
|
+
private pollerSeq;
|
|
48
|
+
constructor(baseUrl: string, store: SessionStore, fetchImpl?: typeof fetch,
|
|
49
|
+
/** Inject a socket factory (tests / custom transports). Falls back to socket.io-client. */
|
|
50
|
+
ioFactory?: IoFactory | undefined);
|
|
51
|
+
/** True once a socket is connected. */
|
|
52
|
+
get connected(): boolean;
|
|
53
|
+
getStatus(): RealtimeStatus;
|
|
54
|
+
onStatusChange(listener: RealtimeStatusListener): () => void;
|
|
55
|
+
connect(options?: RealtimeConnectOptions): Promise<void>;
|
|
13
56
|
on(event: string, cb: (...args: unknown[]) => void): void;
|
|
14
57
|
off(event: string, cb?: (...args: unknown[]) => void): void;
|
|
15
58
|
emit(event: string, ...args: unknown[]): void;
|
|
59
|
+
/**
|
|
60
|
+
* Subscribe to create/update/delete events for a model table.
|
|
61
|
+
*
|
|
62
|
+
* Events are filtered server-side: you will only receive events for rows
|
|
63
|
+
* your session is authorized to see (based on REST API SELECT policies).
|
|
64
|
+
* Anonymous sessions receive only public rows; authenticated sessions receive
|
|
65
|
+
* rows matching their access policies (e.g. owner-only rows they own).
|
|
66
|
+
*
|
|
67
|
+
* Falls back to polling the REST endpoint at subscribe time if the WebSocket
|
|
68
|
+
* is not yet connected (polling does not auto-start on later disconnects).
|
|
69
|
+
*/
|
|
70
|
+
watchTable<T>(model: string, cb: (event: RealtimeWatchEvent<T>) => void, options?: RealtimeWatchOptions): () => void;
|
|
16
71
|
disconnect(): void;
|
|
72
|
+
private bindSocketStatus;
|
|
73
|
+
private waitForConnect;
|
|
74
|
+
private startPolling;
|
|
75
|
+
private stopPolling;
|
|
76
|
+
private fetchRows;
|
|
77
|
+
private setStatus;
|
|
17
78
|
}
|
|
18
79
|
//# sourceMappingURL=realtime.d.ts.map
|
package/dist/realtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../src/realtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../src/realtime.ts"],"names":[],"mappings":"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"}
|
package/dist/realtime.js
CHANGED
|
@@ -4,29 +4,64 @@
|
|
|
4
4
|
* need to install it.
|
|
5
5
|
*/
|
|
6
6
|
export class RealtimeModule {
|
|
7
|
-
constructor(baseUrl, store
|
|
7
|
+
constructor(baseUrl, store, fetchImpl = globalThis.fetch,
|
|
8
|
+
/** Inject a socket factory (tests / custom transports). Falls back to socket.io-client. */
|
|
9
|
+
ioFactory) {
|
|
8
10
|
this.baseUrl = baseUrl;
|
|
9
11
|
this.store = store;
|
|
12
|
+
this.fetchImpl = fetchImpl;
|
|
13
|
+
this.ioFactory = ioFactory;
|
|
10
14
|
this.socket = null;
|
|
15
|
+
this.status = 'idle';
|
|
16
|
+
this.statusListeners = new Set();
|
|
17
|
+
this.pollers = new Map();
|
|
18
|
+
this.pollerSeq = 0;
|
|
11
19
|
}
|
|
12
|
-
|
|
20
|
+
/** True once a socket is connected. */
|
|
21
|
+
get connected() {
|
|
22
|
+
return Boolean(this.socket?.connected);
|
|
23
|
+
}
|
|
24
|
+
getStatus() {
|
|
25
|
+
return this.status;
|
|
26
|
+
}
|
|
27
|
+
onStatusChange(listener) {
|
|
28
|
+
this.statusListeners.add(listener);
|
|
29
|
+
listener(this.status);
|
|
30
|
+
return () => this.statusListeners.delete(listener);
|
|
31
|
+
}
|
|
32
|
+
async connect(options = {}) {
|
|
13
33
|
if (this.socket?.connected)
|
|
14
34
|
return;
|
|
35
|
+
this.setStatus('connecting');
|
|
15
36
|
let io;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// @ts-ignore -- the module may not be installed at build time
|
|
19
|
-
({ io } = (await import('socket.io-client')));
|
|
37
|
+
if (this.ioFactory) {
|
|
38
|
+
io = this.ioFactory;
|
|
20
39
|
}
|
|
21
|
-
|
|
22
|
-
|
|
40
|
+
else {
|
|
41
|
+
try {
|
|
42
|
+
// socket.io-client is an optional peer dependency; resolved at runtime only.
|
|
43
|
+
// @ts-ignore -- the module may not be installed at build time
|
|
44
|
+
({ io } = (await import('socket.io-client')));
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
if (options.fallbackPolling) {
|
|
48
|
+
this.setStatus('polling');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this.setStatus('error', 'socket.io-client is not installed');
|
|
52
|
+
throw new Error('Realtime requires the optional dependency "socket.io-client". Install it: npm i socket.io-client');
|
|
53
|
+
}
|
|
23
54
|
}
|
|
24
55
|
const token = this.store.get()?.accessToken;
|
|
25
|
-
|
|
56
|
+
const socket = io(this.baseUrl, {
|
|
26
57
|
path: '/ws',
|
|
27
58
|
transports: ['websocket'],
|
|
59
|
+
reconnection: true,
|
|
28
60
|
auth: token ? { token } : {},
|
|
29
61
|
});
|
|
62
|
+
this.socket = socket;
|
|
63
|
+
this.bindSocketStatus(socket);
|
|
64
|
+
await this.waitForConnect(socket, options.timeoutMs ?? 5000, Boolean(options.fallbackPolling));
|
|
30
65
|
}
|
|
31
66
|
on(event, cb) {
|
|
32
67
|
this.socket?.on(event, cb);
|
|
@@ -37,9 +72,157 @@ export class RealtimeModule {
|
|
|
37
72
|
emit(event, ...args) {
|
|
38
73
|
this.socket?.emit(event, ...args);
|
|
39
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Subscribe to create/update/delete events for a model table.
|
|
77
|
+
*
|
|
78
|
+
* Events are filtered server-side: you will only receive events for rows
|
|
79
|
+
* your session is authorized to see (based on REST API SELECT policies).
|
|
80
|
+
* Anonymous sessions receive only public rows; authenticated sessions receive
|
|
81
|
+
* rows matching their access policies (e.g. owner-only rows they own).
|
|
82
|
+
*
|
|
83
|
+
* Falls back to polling the REST endpoint at subscribe time if the WebSocket
|
|
84
|
+
* is not yet connected (polling does not auto-start on later disconnects).
|
|
85
|
+
*/
|
|
86
|
+
watchTable(model, cb, options = {}) {
|
|
87
|
+
const handlers = [];
|
|
88
|
+
const baseEvent = model.toLowerCase();
|
|
89
|
+
for (const type of ['created', 'updated', 'deleted']) {
|
|
90
|
+
const event = `${baseEvent}:${type}`;
|
|
91
|
+
const handler = (payload) => cb({ type, source: 'socket', payload: payload });
|
|
92
|
+
handlers.push({ event, handler });
|
|
93
|
+
this.socket?.on(event, handler);
|
|
94
|
+
}
|
|
95
|
+
const shouldPoll = options.polling !== false && (!this.socket?.connected || this.status === 'polling');
|
|
96
|
+
const pollerId = shouldPoll ? this.startPolling(model, cb, options) : null;
|
|
97
|
+
return () => {
|
|
98
|
+
for (const item of handlers)
|
|
99
|
+
this.socket?.off(item.event, item.handler);
|
|
100
|
+
if (pollerId !== null)
|
|
101
|
+
this.stopPolling(pollerId);
|
|
102
|
+
};
|
|
103
|
+
}
|
|
40
104
|
disconnect() {
|
|
105
|
+
for (const id of Array.from(this.pollers.keys()))
|
|
106
|
+
this.stopPolling(id);
|
|
41
107
|
this.socket?.disconnect();
|
|
42
108
|
this.socket = null;
|
|
109
|
+
this.setStatus('disconnected');
|
|
110
|
+
}
|
|
111
|
+
bindSocketStatus(socket) {
|
|
112
|
+
socket.on('connect', () => this.setStatus('connected'));
|
|
113
|
+
socket.on('disconnect', () => this.setStatus(this.pollers.size ? 'polling' : 'disconnected'));
|
|
114
|
+
socket.on('connect_error', error => this.setStatus('error', error));
|
|
115
|
+
socket.on('reconnect_attempt', () => this.setStatus('reconnecting'));
|
|
116
|
+
socket.on('reconnect', () => this.setStatus('connected'));
|
|
117
|
+
}
|
|
118
|
+
waitForConnect(socket, timeoutMs, fallbackPolling) {
|
|
119
|
+
if (socket.connected) {
|
|
120
|
+
this.setStatus('connected');
|
|
121
|
+
return Promise.resolve();
|
|
122
|
+
}
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
let settled = false;
|
|
125
|
+
const cleanup = () => {
|
|
126
|
+
socket.off('connect', onConnect);
|
|
127
|
+
socket.off('connect_error', onError);
|
|
128
|
+
clearTimeout(timer);
|
|
129
|
+
};
|
|
130
|
+
const settle = (fn) => {
|
|
131
|
+
if (settled)
|
|
132
|
+
return;
|
|
133
|
+
settled = true;
|
|
134
|
+
cleanup();
|
|
135
|
+
fn();
|
|
136
|
+
};
|
|
137
|
+
const onConnect = () => settle(() => {
|
|
138
|
+
this.setStatus('connected');
|
|
139
|
+
resolve();
|
|
140
|
+
});
|
|
141
|
+
const onError = (error) => {
|
|
142
|
+
if (fallbackPolling) {
|
|
143
|
+
settle(() => {
|
|
144
|
+
this.setStatus('polling', error);
|
|
145
|
+
resolve();
|
|
146
|
+
});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
settle(() => {
|
|
150
|
+
this.setStatus('error', error);
|
|
151
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
const timer = setTimeout(() => {
|
|
155
|
+
if (fallbackPolling) {
|
|
156
|
+
settle(() => {
|
|
157
|
+
this.setStatus('polling');
|
|
158
|
+
resolve();
|
|
159
|
+
});
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
settle(() => {
|
|
163
|
+
const error = new Error('Realtime connection timeout');
|
|
164
|
+
this.setStatus('error', error);
|
|
165
|
+
reject(error);
|
|
166
|
+
});
|
|
167
|
+
}, timeoutMs);
|
|
168
|
+
socket.on('connect', onConnect);
|
|
169
|
+
socket.on('connect_error', onError);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
startPolling(model, cb, options) {
|
|
173
|
+
if (!this.fetchImpl)
|
|
174
|
+
throw new Error('Realtime polling requires a fetch implementation.');
|
|
175
|
+
const id = ++this.pollerSeq;
|
|
176
|
+
let last = '';
|
|
177
|
+
let stopped = false;
|
|
178
|
+
const run = async () => {
|
|
179
|
+
if (stopped)
|
|
180
|
+
return;
|
|
181
|
+
const rows = await this.fetchRows(model, options.query);
|
|
182
|
+
const snapshot = JSON.stringify(rows);
|
|
183
|
+
if (snapshot !== last) {
|
|
184
|
+
last = snapshot;
|
|
185
|
+
cb({ type: 'snapshot', source: 'polling', rows });
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
const timer = setInterval(() => {
|
|
189
|
+
run().catch(error => this.setStatus('error', error));
|
|
190
|
+
}, options.intervalMs ?? 5000);
|
|
191
|
+
this.pollers.set(id, {
|
|
192
|
+
timer,
|
|
193
|
+
stop: () => {
|
|
194
|
+
stopped = true;
|
|
195
|
+
clearInterval(timer);
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
this.setStatus(this.socket?.connected ? 'connected' : 'polling');
|
|
199
|
+
run().catch(error => this.setStatus('error', error));
|
|
200
|
+
return id;
|
|
201
|
+
}
|
|
202
|
+
stopPolling(id) {
|
|
203
|
+
const poller = this.pollers.get(id);
|
|
204
|
+
if (!poller)
|
|
205
|
+
return;
|
|
206
|
+
poller.stop();
|
|
207
|
+
this.pollers.delete(id);
|
|
208
|
+
if (!this.socket?.connected && this.pollers.size === 0)
|
|
209
|
+
this.setStatus('disconnected');
|
|
210
|
+
}
|
|
211
|
+
async fetchRows(model, query) {
|
|
212
|
+
const path = `/rest/v1/${encodeURIComponent(model)}${query ? (query.startsWith('?') ? query : `?${query}`) : ''}`;
|
|
213
|
+
const headers = {};
|
|
214
|
+
const token = this.store.get()?.accessToken;
|
|
215
|
+
if (token)
|
|
216
|
+
headers.Authorization = `Bearer ${token}`;
|
|
217
|
+
const res = await this.fetchImpl(`${this.baseUrl}${path}`, { headers });
|
|
218
|
+
if (!res.ok)
|
|
219
|
+
throw new Error(`Realtime polling failed with ${res.status}`);
|
|
220
|
+
return (await res.json());
|
|
221
|
+
}
|
|
222
|
+
setStatus(status, detail) {
|
|
223
|
+
this.status = status;
|
|
224
|
+
for (const listener of this.statusListeners)
|
|
225
|
+
listener(status, detail);
|
|
43
226
|
}
|
|
44
227
|
}
|
|
45
228
|
//# sourceMappingURL=realtime.js.map
|
package/dist/realtime.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"realtime.js","sourceRoot":"","sources":["../src/realtime.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"realtime.js","sourceRoot":"","sources":["../src/realtime.ts"],"names":[],"mappings":"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"}
|