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/editor/db/util.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
// Well-defined JSON.stringify...
7
const json_stable = require("json-stable-stringify");
8
import * as immutable from "immutable";
9
import { isEqual } from "lodash";
10
11
export function to_key(s: any): string {
12
if (immutable.Map.isMap(s)) {
13
s = s.toJS();
14
}
15
// NOTE: s is not undefined.
16
return json_stable(s) as string;
17
}
18
19
/* TODO/worry: I change to json_stable from misc.to_json
20
and misc.from_json, so the string is canonical. However,
21
Date objects will be treated differently. This is fine
22
by me, in that probably we should just ensure no date
23
objects are ever used with db-doc... or have a special
24
column type (like string_cols) for them.
25
I think right now all our applications (e.g., task lists, jupyter)
26
just use ms since epoch explicitly.
27
*/
28
29
export function to_str(obj: any[]): string {
30
const v = obj.map((x) => json_stable(x));
31
/* NOTE: It is *VERY* important to sort v! Otherwise, the hash
32
of this document, which is used by
33
syncstring, isn't stable in terms of the value of the
34
document. This can in theory
35
cause massive trouble with file saves, e.g., of jupyter
36
notebooks, courses, etc. (They save fine, but
37
they appear not to for the user...).
38
*/
39
v.sort();
40
return v.join("\n");
41
}
42
43
// Create an object change such that merge_set(obj1, change) produces obj2.
44
// Thus for each key, value1 of obj1 and key, value2 of obj2:
45
// If value1 is the same as value2, do nothing.
46
// If value1 exists but value2 does not, do change[key] = null
47
// If value2 exists but value1 does not, do change[key] = value2
48
export function map_merge_patch(obj1, obj2) {
49
let val2;
50
const change = {};
51
for (var key in obj1) {
52
const val1 = obj1[key];
53
val2 = obj2[key];
54
if (isEqual(val1, val2)) {
55
// nothing to do
56
} else if (val2 == null) {
57
change[key] = null;
58
} else {
59
change[key] = val2;
60
}
61
}
62
for (key in obj2) {
63
val2 = obj2[key];
64
if (obj1[key] != null) {
65
continue;
66
}
67
change[key] = val2;
68
}
69
return change;
70
}
71
72
// obj and change are both immutable.js Maps. Do the following:
73
// - for each value of change that is null or undefined, we delete that key from obj
74
// - we set the other vals of obj, accordingly.
75
// So this is a shallow merge with the ability to *delete* keys.
76
export function merge_set(
77
obj: immutable.Map<any, any>,
78
change: immutable.Map<any, any>
79
): immutable.Map<any, any> {
80
change.forEach(function (v, k) {
81
if (v === null || v == null) {
82
obj = obj.delete(k);
83
} else {
84
obj = obj.set(k, v);
85
}
86
});
87
return obj;
88
}
89
90
export function nonnull_cols(
91
f: immutable.Map<any, any>
92
): immutable.Map<any, any> {
93
// Yes, "!==" not "!=" below!
94
return immutable.Map(f.filter((v, _) => v !== null));
95
}
96
97