CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/sync/client/sync-client.ts
Views: 687
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/*
7
Functionality related to Sync.
8
*/
9
10
import { callback2 } from "@cocalc/util/async-utils";
11
import { once } from "@cocalc/util/async-utils";
12
import {
13
defaults,
14
is_valid_uuid_string,
15
merge,
16
required,
17
} from "@cocalc/util/misc";
18
import { SyncDoc, SyncOpts0 } from "@cocalc/sync/editor/generic/sync-doc";
19
import { SyncDB, SyncDBOpts0 } from "@cocalc/sync/editor/db";
20
import { SyncString } from "@cocalc/sync/editor/string/sync";
21
import {
22
synctable,
23
SyncTable,
24
Query,
25
QueryOptions,
26
synctable_no_changefeed,
27
} from "@cocalc/sync/table";
28
import synctable_project from "./synctable-project";
29
import type { Channel, AppClient } from "./types";
30
31
interface SyncOpts extends Omit<SyncOpts0, "client"> {}
32
33
interface SyncDBOpts extends Omit<SyncDBOpts0, "client" | "string_cols"> {
34
string_cols?: string[];
35
}
36
37
export class SyncClient {
38
private client: AppClient;
39
40
constructor(client: AppClient) {
41
this.client = client;
42
}
43
44
public sync_table(
45
query: Query,
46
options: QueryOptions,
47
throttle_changes?: number,
48
): SyncTable {
49
return synctable(query, options, this.client, throttle_changes);
50
}
51
52
public async synctable_database(
53
query: Query,
54
options: QueryOptions,
55
throttle_changes?: number,
56
): Promise<SyncTable> {
57
const s = this.sync_table(query, options, throttle_changes);
58
await once(s, "connected");
59
return s;
60
}
61
62
public synctable_no_changefeed(
63
query: Query,
64
options: QueryOptions,
65
throttle_changes?: number,
66
): SyncTable {
67
return synctable_no_changefeed(
68
query,
69
options,
70
this.client,
71
throttle_changes,
72
);
73
}
74
75
public async synctable_project(
76
project_id: string,
77
query: Query,
78
options: QueryOptions,
79
throttle_changes: number | undefined = undefined,
80
id: string = "",
81
): Promise<SyncTable> {
82
return await synctable_project({
83
project_id,
84
query,
85
options,
86
client: this.client,
87
throttle_changes,
88
id,
89
});
90
}
91
92
// NOT currently used.
93
public async symmetric_channel(
94
name: string,
95
project_id: string,
96
): Promise<Channel> {
97
if (!is_valid_uuid_string(project_id) || typeof name !== "string") {
98
throw Error("project_id must be a valid uuid and name must be a string");
99
}
100
return (await this.client.project_client.api(project_id)).symmetric_channel(
101
name,
102
);
103
}
104
105
public sync_string(opts: SyncOpts): SyncString {
106
const opts0: SyncOpts0 = defaults(opts, {
107
id: undefined,
108
project_id: required,
109
path: required,
110
file_use_interval: "default",
111
cursors: false,
112
patch_interval: 1000,
113
save_interval: 2000,
114
persistent: false,
115
data_server: undefined,
116
client: this.client,
117
ephemeral: false,
118
});
119
return new SyncString(opts0);
120
}
121
122
public sync_db(opts: SyncDBOpts): SyncDoc {
123
const opts0: SyncDBOpts0 = defaults(opts, {
124
id: undefined,
125
project_id: required,
126
path: required,
127
file_use_interval: "default",
128
cursors: false,
129
patch_interval: 1000,
130
save_interval: 2000,
131
change_throttle: undefined,
132
persistent: false,
133
data_server: undefined,
134
135
primary_keys: required,
136
string_cols: [],
137
138
client: this.client,
139
140
ephemeral: false,
141
});
142
return new SyncDB(opts0);
143
}
144
145
public async open_existing_sync_document(opts: {
146
project_id: string;
147
path: string;
148
data_server?: string;
149
persistent?: boolean;
150
}): Promise<SyncDoc | undefined> {
151
const resp = await callback2(this.client.query, {
152
query: {
153
syncstrings: {
154
project_id: opts.project_id,
155
path: opts.path,
156
doctype: null,
157
},
158
},
159
});
160
if (resp.event === "error") {
161
throw Error(resp.error);
162
}
163
if (resp.query?.syncstrings == null) {
164
throw Error(`no document '${opts.path}' in project '${opts.project_id}'`);
165
}
166
const doctype = JSON.parse(
167
resp.query.syncstrings.doctype ?? '{"type":"string"}',
168
);
169
let opts2: any = {
170
project_id: opts.project_id,
171
path: opts.path,
172
};
173
if (opts.data_server) {
174
opts2.data_server = opts.data_server;
175
}
176
if (opts.persistent) {
177
opts2.persistent = opts.persistent;
178
}
179
if (doctype.opts != null) {
180
opts2 = merge(opts2, doctype.opts);
181
}
182
const f = `sync_${doctype.type}`;
183
return (this as any)[f](opts2);
184
}
185
}
186
187