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/lib/index.ts
Views: 687
1
/*
2
This is specifically meant for connecting to one project.
3
4
Example use:
5
6
~/cocalc/src/packages/sync-client$ PROJECT_PORT=33177 DEBUG='cocalc:sync*' node
7
...
8
9
> c = new (require('.').default)(); s = c.sync_client.sync_db({project_id:'97ce5a7c-25c1-4059-8670-c7de96a0db92',path:'b.tasks',primary_keys:["task_id"], string_cols:['desc']})
10
11
> s.set({task_id:'cf163fb4-b198-4664-b32b-82ce4ec71701',desc:"fubar"})
12
> await s.save()
13
> s.to_str()
14
'{"desc":"fubar","last_edited":1684420716277,"position":0,"task_id":"cf163fb4-b198-4664-b32b-82ce4ec71701"}'
15
> s.set({task_id:'cf163fb4-b198-4664-b32b-82ce4ec71701',desc:"figure it out"})
16
undefined
17
> await s.save()
18
undefined
19
> s.to_str()
20
'{"desc":"figure it out","last_edited":1684420716277,"position":0,"task_id":"cf163fb4-b198-4664-b32b-82ce4ec71701"}'
21
*/
22
23
import { EventEmitter } from "events";
24
import type { AppClient } from "@cocalc/sync/client/types";
25
import { SyncClient } from "@cocalc/sync/client/sync-client";
26
import ProjectClient from "./project-client";
27
import debug from "debug";
28
import { bind_methods, isValidUUID, uuid } from "@cocalc/util/misc";
29
import { project } from "@cocalc/api-client";
30
import { reuseInFlight } from "@cocalc/util/reuse-in-flight";
31
32
export type Role = "project" | "browser" | "compute_server";
33
34
interface Options {
35
project_id: string;
36
client_id?: string;
37
role: Role;
38
}
39
40
export default class Client extends EventEmitter implements AppClient {
41
project_client: ProjectClient;
42
sync_client: SyncClient;
43
synctable_project: Function;
44
project_id: string;
45
_client_id: string;
46
private role: Role;
47
48
constructor({ project_id, client_id = uuid(), role }: Options) {
49
super();
50
this._client_id = client_id;
51
this.project_id = project_id;
52
this.role = role;
53
54
if (!isValidUUID(project_id)) {
55
throw Error("project_id must be a valid uuid");
56
}
57
58
this.project_client = bind_methods(new ProjectClient());
59
this.sync_client = bind_methods(new SyncClient(this));
60
this.synctable_project = this.sync_client.synctable_project.bind(
61
this.sync_client,
62
);
63
}
64
65
client_id = () => {
66
return this._client_id;
67
};
68
69
// [ ] TODO: is this something we should worry about? Probably yes.
70
is_deleted = (_filename: string, _project_id: string) => {
71
return false;
72
};
73
74
set_deleted = async (_filename: string, _project_id?: string) => {
75
// TODO -- implement in fs aware clients
76
};
77
78
mark_file = async (_opts: any) => {
79
// [ ] TODO: should we?
80
};
81
82
is_project = () => {
83
return this.role == "project";
84
};
85
86
is_browser = () => {
87
return this.role == "browser";
88
};
89
90
is_compute_server = () => {
91
return this.role == "compute_server";
92
};
93
94
dbg = (str: string) => {
95
return debug(`cocalc:sync:client.${str}`);
96
};
97
98
query = (opts) => {
99
this.dbg("query")(opts);
100
if (typeof opts?.query != "object") {
101
throw Error("opts.query must be specified");
102
}
103
let project_id = this.project_id;
104
for (const table in opts.query) {
105
if (opts.query[table].project_id) {
106
project_id = opts.query[table].project_id;
107
break;
108
}
109
if (opts.query[table][0]?.project_id) {
110
project_id = opts.query[table][0]?.project_id;
111
break;
112
}
113
}
114
if (!project_id) {
115
throw Error(
116
"query involving an explicit project_id or clients with project_id set are supported",
117
);
118
}
119
(async () => {
120
try {
121
const api = await this.project_client.api(project_id);
122
const result = await api.query(opts);
123
opts.cb?.(undefined, result);
124
} catch (err) {
125
opts.cb?.(`${err}`);
126
}
127
})();
128
};
129
130
query_cancel = () => {
131
console.log("query_cancel");
132
};
133
134
server_time = () => {
135
return new Date();
136
};
137
138
is_connected = () => {
139
return true;
140
};
141
142
is_signed_in = () => {
143
return true;
144
};
145
146
private _touchProject = reuseInFlight(async (project_id: string) => {
147
const dbg = this.dbg("sync-client:_touchProject");
148
dbg(project_id);
149
try {
150
await project.touch({ project_id });
151
} catch (err) {
152
dbg("error ", err);
153
}
154
});
155
156
touch_project = (project_id: string) => {
157
this._touchProject(project_id);
158
};
159
}
160
161