Mercurial > code > home > repos > light9
annotate light9/web/patch.ts @ 2273:4074dbec5c46
logging
author | drewp@bigasterisk.com |
---|---|
date | Mon, 29 May 2023 13:58:04 -0700 |
parents | 094e6b84b291 |
children | df9a6c457587 |
rev | line source |
---|---|
2091
9324fc8285ad
Effect repairs duplicate :settings edges when it finds them
drewp@bigasterisk.com
parents:
2071
diff
changeset
|
1 import * as async from "async"; |
2071 | 2 import debug from "debug"; |
2258 | 3 import * as N3 from "n3"; |
2091
9324fc8285ad
Effect repairs duplicate :settings edges when it finds them
drewp@bigasterisk.com
parents:
2071
diff
changeset
|
4 import { NamedNode, Parser, Quad, Writer } from "n3"; |
2260 | 5 |
6 export interface QuadPattern { | |
7 subject: N3.Quad_Subject | null; | |
8 predicate: N3.Quad_Predicate | null; | |
9 object: N3.Quad_Object | null; // literals allowed? needs review. probably 'yes'. | |
10 graph: N3.Quad_Graph | null; | |
11 } | |
12 | |
2071 | 13 const log = debug("patch"); |
14 | |
2258 | 15 export class Patch { |
16 // immutable | |
17 private _allPredsCache?: Set<string>; | |
18 private _allSubjsCache?: Set<string>; | |
19 constructor(private dels: Quad[], private adds: Quad[]) { | |
20 this.validate(); | |
21 } | |
22 private validate() { | |
23 // todo: finish porting this from coffeescript | |
24 [this.adds, this.dels].map((qs: Quad[]) => | |
25 (() => { | |
26 const result = []; | |
27 for (let q of Array.from(qs)) { | |
28 if (!q.equals) { | |
29 throw new Error("doesn't look like a proper Quad"); | |
30 } | |
31 if (!q.subject.id || q.graph.id == null || q.predicate.id == null) { | |
32 throw new Error(`corrupt patch: ${JSON.stringify(q)}`); | |
33 } else { | |
34 result.push(undefined); | |
35 } | |
36 } | |
37 return result; | |
38 })() | |
39 ); | |
40 } | |
41 | |
2260 | 42 matches(pat: QuadPattern): boolean { |
43 const allQuads = this.dels.concat(this.adds); | |
44 return allQuads.some((quad) => { | |
45 return ( | |
46 (pat.subject === null || pat.subject.equals(quad.subject)) && // | |
47 (pat.predicate === null || pat.predicate.equals(quad.predicate)) && // | |
48 (pat.object === null || pat.object.equals(quad.object)) && // | |
49 (pat.graph === null || pat.graph.equals(quad.graph)) | |
50 ); | |
51 }); | |
52 } | |
53 | |
2258 | 54 isEmpty() { |
2273 | 55 // sometimes returns bogus false- waiting for port to Immutable |
2258 | 56 return !this.dels.length && !this.adds.length; |
57 } | |
58 | |
59 applyToGraph(g: N3.Store) { | |
60 for (let quad of Array.from(this.dels)) { | |
61 g.removeQuad(quad); | |
62 } | |
63 for (let quad of Array.from(this.adds)) { | |
64 g.addQuad(quad); | |
65 } | |
66 } | |
67 | |
68 update(other: Patch): Patch { | |
69 // this is approx, since it doesnt handle matching existing quads. | |
70 return new Patch(this.dels.concat(other.dels), this.adds.concat(other.adds)); | |
71 } | |
72 | |
73 summary(): string { | |
74 return "-" + this.dels.length + " +" + this.adds.length; | |
75 } | |
76 | |
2268 | 77 dump(): string { |
78 const lines: string[] = []; | |
79 const s = (term: N3.Term): string => { | |
2273 | 80 if (term.termType == "Literal") return term.value; |
81 if (term.termType == "NamedNode") | |
82 return term.value | |
83 .replace("http://light9.bigasterisk.com/effect/", "effect:") | |
84 .replace("http://light9.bigasterisk.com/", ":") | |
85 .replace("http://www.w3.org/2000/01/rdf-schema#", "rdfs:") | |
86 .replace("http://www.w3.org/1999/02/22-rdf-syntax-ns#", "rdf:"); | |
87 if (term.termType == "BlankNode") return "_:" + term.value; | |
2268 | 88 return term.id; |
89 }; | |
2273 | 90 const delPrefix = "- ", |
91 addPrefix = "\u200B+ "; // dels to sort before adds | |
92 this.dels.forEach((d) => lines.push(delPrefix + s(d.subject) + " " + s(d.predicate) + " " + s(d.object))); | |
93 this.adds.forEach((d) => lines.push(addPrefix + s(d.subject) + " " + s(d.predicate) + " " + s(d.object))); | |
2268 | 94 lines.sort(); |
95 return lines.join("\n"); | |
96 } | |
97 | |
2258 | 98 async toJsonPatch(): Promise<string> { |
99 return new Promise((res, rej) => { | |
100 const out: SyncgraphPatchMessage = { patch: { adds: "", deletes: "" } }; | |
101 | |
102 const writeDels = (cb1: () => void) => { | |
103 const writer = new Writer({ format: "N-Quads" }); | |
104 writer.addQuads(this.dels); | |
105 writer.end(function (err: any, result: string) { | |
106 out.patch.deletes = result; | |
107 cb1(); | |
108 }); | |
109 }; | |
110 | |
111 const writeAdds = (cb2: () => void) => { | |
112 const writer = new Writer({ format: "N-Quads" }); | |
113 writer.addQuads(this.adds); | |
114 writer.end(function (err: any, result: string) { | |
115 out.patch.adds = result; | |
116 cb2(); | |
117 }); | |
118 }; | |
119 | |
120 async.parallel([writeDels, writeAdds], (err: any) => res(JSON.stringify(out))); | |
121 }); | |
122 } | |
123 | |
124 containsAnyPreds(preds: NamedNode[]): boolean { | |
125 if (this._allPredsCache === undefined) { | |
126 this._allPredsCache = new Set(); | |
127 for (let qq of [this.adds, this.dels]) { | |
128 for (let q of Array.from(qq)) { | |
129 this._allPredsCache.add(q.predicate.value); | |
130 } | |
131 } | |
132 } | |
133 | |
134 for (let p of Array.from(preds)) { | |
135 if (this._allPredsCache.has(p.value)) { | |
136 return true; | |
137 } | |
138 } | |
139 return false; | |
140 } | |
141 | |
142 allSubjs(): Set<string> { | |
143 // returns subjs as Set of strings | |
144 if (this._allSubjsCache === undefined) { | |
145 this._allSubjsCache = new Set(); | |
146 for (let qq of [this.adds, this.dels]) { | |
147 for (let q of Array.from(qq)) { | |
148 this._allSubjsCache.add(q.subject.value); | |
149 } | |
150 } | |
151 } | |
152 | |
153 return this._allSubjsCache; | |
154 } | |
2267 | 155 |
2258 | 156 allPreds(): Set<NamedNode> { |
157 const ret = new Set<NamedNode>(); | |
158 for (let qq of [this.adds, this.dels]) { | |
159 for (let q of Array.from(qq)) { | |
160 if (q.predicate.termType == "Variable") throw "unsupported"; | |
161 ret.add(q.predicate); | |
162 } | |
163 } | |
164 return ret; | |
165 } | |
2071 | 166 } |
167 | |
2267 | 168 // The schema of the json sent from graph server. |
2258 | 169 export interface SyncgraphPatchMessage { |
2071 | 170 patch: { adds: string; deletes: string }; |
171 } | |
172 | |
2258 | 173 export function patchToDeleteEntireGraph(g: N3.Store) { |
174 return new Patch(g.getQuads(null, null, null, null), []); | |
2071 | 175 } |
176 | |
2108
e92db17f3e7e
effectSequencer can now also process some note-like values coming from the fade/ ui
drewp@bigasterisk.com
parents:
2091
diff
changeset
|
177 export function parseJsonPatch(input: SyncgraphPatchMessage, cb: (p: Patch) => void): void { |
2071 | 178 // note response cb doesn't have an error arg. |
2258 | 179 const dels: Quad[] = []; |
180 const adds: Quad[] = []; | |
2071 | 181 |
2258 | 182 const parseAdds = (cb2: () => any) => { |
2071 | 183 const parser = new Parser(); |
184 return parser.parse(input.patch.adds, (error: any, quad: Quad, prefixes: any) => { | |
185 if (quad) { | |
2258 | 186 return adds.push(quad); |
2071 | 187 } else { |
2258 | 188 return cb2(); |
2071 | 189 } |
190 }); | |
191 }; | |
2258 | 192 const parseDels = (cb3: () => any) => { |
2071 | 193 const parser = new Parser(); |
194 return parser.parse(input.patch.deletes, (error: any, quad: any, prefixes: any) => { | |
195 if (quad) { | |
2258 | 196 return dels.push(quad); |
2071 | 197 } else { |
2258 | 198 return cb3(); |
2071 | 199 } |
200 }); | |
201 }; | |
202 | |
2091
9324fc8285ad
Effect repairs duplicate :settings edges when it finds them
drewp@bigasterisk.com
parents:
2071
diff
changeset
|
203 // todo: is it faster to run them in series? might be |
2258 | 204 async.parallel([parseAdds, parseDels], (err: any) => cb(new Patch(dels, adds))); |
2071 | 205 } |