Skip to content

Commit 11a21fa

Browse files
committed
tiny steps to make sync-doc more flexible in various ways
1 parent 5901190 commit 11a21fa

File tree

7 files changed

+257
-0
lines changed

7 files changed

+257
-0
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/*
2+
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3+
* License: MS-RSL – see LICENSE.md for details
4+
*/
5+
6+
import { EventEmitter } from "events";
7+
import { bind_methods, keys } from "@cocalc/util/misc";
8+
import {
9+
Client as Client0,
10+
FileWatcher as FileWatcher0,
11+
} from "@cocalc/sync/editor/generic/types";
12+
import { SyncTable } from "@cocalc/sync/table/synctable";
13+
import { ExecuteCodeOptionsWithCallback } from "@cocalc/util/types/execute-code";
14+
import { once } from "@cocalc/util/async-utils";
15+
16+
export class FileWatcher extends EventEmitter implements FileWatcher0 {
17+
private path: string;
18+
constructor(path: string) {
19+
super();
20+
this.path = path;
21+
console.log("FileWatcher", this.path);
22+
}
23+
public close(): void {}
24+
}
25+
26+
export class Client extends EventEmitter implements Client0 {
27+
private _client_id: string;
28+
private initial_get_query: { [table: string]: any[] };
29+
public set_queries: any[] = [];
30+
31+
constructor(
32+
initial_get_query: { [table: string]: any[] },
33+
client_id: string,
34+
) {
35+
super();
36+
this._client_id = client_id;
37+
this.initial_get_query = initial_get_query;
38+
bind_methods(this, ["query", "dbg", "query_cancel"]);
39+
}
40+
41+
public server_time(): Date {
42+
return new Date();
43+
}
44+
45+
isTestClient = () => {
46+
return true;
47+
};
48+
49+
public is_project(): boolean {
50+
return false;
51+
}
52+
53+
public is_browser(): boolean {
54+
return true;
55+
}
56+
57+
public is_compute_server(): boolean {
58+
return false;
59+
}
60+
61+
public dbg(_f: string): Function {
62+
// return (...args) => {
63+
// console.log(_f, ...args);
64+
// };
65+
return (..._) => {};
66+
}
67+
68+
public mark_file(_opts: {
69+
project_id: string;
70+
path: string;
71+
action: string;
72+
ttl: number;
73+
}): void {
74+
//console.log("mark_file", opts);
75+
}
76+
77+
public log_error(opts: {
78+
project_id: string;
79+
path: string;
80+
string_id: string;
81+
error: any;
82+
}): void {
83+
console.log("log_error", opts);
84+
}
85+
86+
public query(opts): void {
87+
if (opts.options && opts.options.length === 1 && opts.options[0].set) {
88+
// set query
89+
this.set_queries.push(opts);
90+
opts.cb();
91+
} else {
92+
// get query -- returns predetermined result
93+
const table = keys(opts.query)[0];
94+
let result = this.initial_get_query[table];
95+
if (result == null) {
96+
result = [];
97+
}
98+
//console.log("GET QUERY ", table, result);
99+
opts.cb(undefined, { query: { [table]: result } });
100+
}
101+
}
102+
103+
path_access(opts: { path: string; mode: string; cb: Function }): void {
104+
console.log("path_access", opts.path, opts.mode);
105+
opts.cb(true);
106+
}
107+
path_exists(opts: { path: string; cb: Function }): void {
108+
console.log("path_access", opts.path);
109+
opts.cb(true);
110+
}
111+
path_stat(opts: { path: string; cb: Function }): void {
112+
console.log("path_state", opts.path);
113+
opts.cb(true);
114+
}
115+
async path_read(opts: {
116+
path: string;
117+
maxsize_MB?: number;
118+
cb: Function;
119+
}): Promise<void> {
120+
console.log("path_ready", opts.path);
121+
opts.cb(true);
122+
}
123+
async write_file(opts: {
124+
path: string;
125+
data: string;
126+
cb: Function;
127+
}): Promise<void> {
128+
console.log("write_file", opts.path, opts.data);
129+
opts.cb(true);
130+
}
131+
watch_file(opts: { path: string }): FileWatcher {
132+
return new FileWatcher(opts.path);
133+
}
134+
135+
public is_connected(): boolean {
136+
return true;
137+
}
138+
139+
public is_signed_in(): boolean {
140+
return true;
141+
}
142+
143+
public touch_project(_): void {}
144+
145+
public query_cancel(_): void {}
146+
147+
public alert_message(_): void {}
148+
149+
public is_deleted(_filename: string, _project_id?: string): boolean {
150+
return false;
151+
}
152+
153+
public set_deleted(_filename: string, _project_id?: string): void {}
154+
155+
async synctable_ephemeral(
156+
_project_id: string,
157+
query: any,
158+
options: any,
159+
throttle_changes?: number,
160+
): Promise<SyncTable> {
161+
const s = new SyncTable(query, options, this, throttle_changes);
162+
await once(s, "connected");
163+
return s;
164+
}
165+
166+
async synctable_conat(_query: any): Promise<SyncTable> {
167+
throw Error("synctable_conat: not implemented");
168+
}
169+
async pubsub_conat(_query: any): Promise<SyncTable> {
170+
throw Error("pubsub_conat: not implemented");
171+
}
172+
173+
// account_id or project_id
174+
public client_id(): string {
175+
return this._client_id;
176+
}
177+
178+
public sage_session({ path }): void {
179+
console.log(`sage_session: path=${path}`);
180+
}
181+
182+
public shell(opts: ExecuteCodeOptionsWithCallback): void {
183+
console.log(`shell: opts=${JSON.stringify(opts)}`);
184+
}
185+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Client } from "./client";
2+
import { SyncString } from "@cocalc/sync/editor/string/sync";
3+
import { a_txt } from "@cocalc/sync/editor/string/test/data";
4+
import { once } from "@cocalc/util/async-utils";
5+
6+
export default async function ephemeralSyncstring() {
7+
const { client_id, project_id, path, init_queries } = a_txt();
8+
const client = new Client(init_queries, client_id);
9+
const syncstring = new SyncString({
10+
project_id,
11+
path,
12+
client,
13+
ephemeral: true,
14+
});
15+
// replace save to disk, since otherwise unless string is empty,
16+
// this will hang forever... and it is called on close.
17+
// @ts-ignore
18+
syncstring.save_to_disk = async () => Promise<void>;
19+
await once(syncstring, "ready");
20+
return syncstring;
21+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import syncstring from "@cocalc/backend/conat/sync-doc/syncstring";
2+
3+
describe("basic tests of a syncstring", () => {
4+
let s;
5+
6+
it("creates a syncstring", async () => {
7+
s = await syncstring();
8+
});
9+
10+
it("initially it is empty", () => {
11+
expect(s.to_str()).toBe("");
12+
expect(s.versions().length).toBe(0);
13+
});
14+
15+
it("set the value", () => {
16+
s.from_str("test");
17+
expect(s.to_str()).toBe("test");
18+
expect(s.versions().length).toBe(0);
19+
});
20+
21+
it("commit the value", () => {
22+
s.commit();
23+
expect(s.versions().length).toBe(1);
24+
});
25+
26+
it("change the value and commit a second time", () => {
27+
s.from_str("bar");
28+
s.commit();
29+
expect(s.versions().length).toBe(2);
30+
});
31+
32+
it("get first version", () => {
33+
expect(s.version(s.versions()[0]).to_str()).toBe("test");
34+
});
35+
});

src/packages/backend/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"./*": "./dist/*.js",
77
"./database": "./dist/database/index.js",
88
"./conat": "./dist/conat/index.js",
9+
"./conat/sync/*": "./dist/conat/sync/*.js",
910
"./server-settings": "./dist/server-settings/index.js",
1011
"./auth/*": "./dist/auth/*.js",
1112
"./auth/tokens/*": "./dist/auth/tokens/*.js"
@@ -34,6 +35,7 @@
3435
"dependencies": {
3536
"@cocalc/backend": "workspace:*",
3637
"@cocalc/conat": "workspace:*",
38+
"@cocalc/sync": "workspace:*",
3739
"@cocalc/util": "workspace:*",
3840
"@types/debug": "^4.1.12",
3941
"@types/jest": "^29.5.14",

src/packages/pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/packages/sync/client/sync-client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export class SyncClient {
9999
data_server: undefined,
100100
client: this.client,
101101
ephemeral: false,
102+
fs: undefined,
102103
});
103104
return new SyncString(opts0);
104105
}
@@ -122,6 +123,8 @@ export class SyncClient {
122123
client: this.client,
123124

124125
ephemeral: false,
126+
127+
fs: undefined,
125128
});
126129
return new SyncDB(opts0);
127130
}

src/packages/sync/editor/generic/sync-doc.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ const DEBUG = false;
125125
export type State = "init" | "ready" | "closed";
126126
export type DataServer = "project" | "database";
127127

128+
export interface SyncDocFilesystem {
129+
readFile: (path: string, encoding?: any) => Promise<string | Buffer>;
130+
writeFile: (path: string, data: string | Buffer) => Promise<void>;
131+
}
132+
128133
export interface SyncOpts0 {
129134
project_id: string;
130135
path: string;
@@ -151,6 +156,9 @@ export interface SyncOpts0 {
151156

152157
// which data/changefeed server to use
153158
data_server?: DataServer;
159+
160+
// optional filesystem interface.
161+
fs?: SyncDocFilesystem;
154162
}
155163

156164
export interface SyncOpts extends SyncOpts0 {

0 commit comments

Comments
 (0)