@graffiti-garden/wrapper-synchronize 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/index.js +1 -1
- package/dist/browser/index.js.map +3 -3
- package/dist/cjs/index.js +72 -24
- package/dist/cjs/index.js.map +2 -2
- package/dist/esm/index.js +73 -25
- package/dist/esm/index.js.map +2 -2
- package/dist/index.d.ts +11 -17
- package/dist/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/index.spec.ts +181 -133
- package/src/index.ts +97 -41
package/src/index.ts
CHANGED
|
@@ -2,9 +2,10 @@ import type Ajv from "ajv";
|
|
|
2
2
|
import { Graffiti } from "@graffiti-garden/api";
|
|
3
3
|
import type {
|
|
4
4
|
GraffitiSession,
|
|
5
|
-
GraffitiObject,
|
|
6
5
|
JSONSchema,
|
|
7
|
-
|
|
6
|
+
GraffitiObjectStream,
|
|
7
|
+
GraffitiObjectStreamContinueEntry,
|
|
8
|
+
GraffitiObjectStreamContinue,
|
|
8
9
|
} from "@graffiti-garden/api";
|
|
9
10
|
import type { GraffitiObjectBase } from "@graffiti-garden/api";
|
|
10
11
|
import { Repeater } from "@repeaterjs/repeater";
|
|
@@ -14,13 +15,13 @@ import {
|
|
|
14
15
|
compileGraffitiObjectSchema,
|
|
15
16
|
isActorAllowedGraffitiObject,
|
|
16
17
|
maskGraffitiObject,
|
|
17
|
-
|
|
18
|
+
unpackObjectUrl,
|
|
18
19
|
} from "@graffiti-garden/implementation-local/utilities";
|
|
19
20
|
export type * from "@graffiti-garden/api";
|
|
20
21
|
|
|
21
22
|
export type GraffitiSynchronizeCallback = (
|
|
22
|
-
oldObject:
|
|
23
|
-
newObject?:
|
|
23
|
+
oldObject: GraffitiObjectStreamContinueEntry<{}>,
|
|
24
|
+
newObject?: GraffitiObjectStreamContinueEntry<{}>,
|
|
24
25
|
) => void;
|
|
25
26
|
|
|
26
27
|
export interface GraffitiSynchronizeOptions {
|
|
@@ -138,8 +139,10 @@ export class GraffitiSynchronize extends Graffiti {
|
|
|
138
139
|
channels: string[],
|
|
139
140
|
schema: Schema,
|
|
140
141
|
session?: GraffitiSession | null,
|
|
141
|
-
) {
|
|
142
|
-
const
|
|
142
|
+
): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>> {
|
|
143
|
+
const seenUrls = new Set<string>();
|
|
144
|
+
|
|
145
|
+
const repeater = new Repeater<GraffitiObjectStreamContinueEntry<Schema>>(
|
|
143
146
|
async (push, stop) => {
|
|
144
147
|
const validate = compileGraffitiObjectSchema(await this.ajv, schema);
|
|
145
148
|
const callback: GraffitiSynchronizeCallback = (
|
|
@@ -147,18 +150,23 @@ export class GraffitiSynchronize extends Graffiti {
|
|
|
147
150
|
newObjectRaw,
|
|
148
151
|
) => {
|
|
149
152
|
for (const objectRaw of [newObjectRaw, oldObjectRaw]) {
|
|
150
|
-
if (
|
|
153
|
+
if (objectRaw?.tombstone) {
|
|
154
|
+
if (seenUrls.has(objectRaw.object.url)) {
|
|
155
|
+
push(objectRaw);
|
|
156
|
+
}
|
|
157
|
+
} else if (
|
|
151
158
|
objectRaw &&
|
|
152
|
-
matchObject(objectRaw) &&
|
|
159
|
+
matchObject(objectRaw.object) &&
|
|
153
160
|
(this.options.omniscient ||
|
|
154
|
-
isActorAllowedGraffitiObject(objectRaw, session))
|
|
161
|
+
isActorAllowedGraffitiObject(objectRaw.object, session))
|
|
155
162
|
) {
|
|
156
|
-
const object = { ...objectRaw };
|
|
163
|
+
const object = { ...objectRaw.object };
|
|
157
164
|
if (!this.options.omniscient) {
|
|
158
165
|
maskGraffitiObject(object, channels, session);
|
|
159
166
|
}
|
|
160
167
|
if (validate(object)) {
|
|
161
|
-
push({
|
|
168
|
+
push({ object });
|
|
169
|
+
seenUrls.add(object.url);
|
|
162
170
|
break;
|
|
163
171
|
}
|
|
164
172
|
}
|
|
@@ -189,7 +197,7 @@ export class GraffitiSynchronize extends Graffiti {
|
|
|
189
197
|
*/
|
|
190
198
|
synchronizeDiscover<Schema extends JSONSchema>(
|
|
191
199
|
...args: Parameters<typeof Graffiti.prototype.discover<Schema>>
|
|
192
|
-
):
|
|
200
|
+
): GraffitiObjectStreamContinue<Schema> {
|
|
193
201
|
const [channels, schema, session] = args;
|
|
194
202
|
function matchObject(object: GraffitiObjectBase) {
|
|
195
203
|
return object.channels.some((channel) => channels.includes(channel));
|
|
@@ -210,11 +218,11 @@ export class GraffitiSynchronize extends Graffiti {
|
|
|
210
218
|
*/
|
|
211
219
|
synchronizeGet<Schema extends JSONSchema>(
|
|
212
220
|
...args: Parameters<typeof Graffiti.prototype.get<Schema>>
|
|
213
|
-
):
|
|
214
|
-
const [
|
|
215
|
-
const
|
|
221
|
+
): GraffitiObjectStreamContinue<Schema> {
|
|
222
|
+
const [objectUrl, schema, session] = args;
|
|
223
|
+
const url = unpackObjectUrl(objectUrl);
|
|
216
224
|
function matchObject(object: GraffitiObjectBase) {
|
|
217
|
-
return object.url ===
|
|
225
|
+
return object.url === url;
|
|
218
226
|
}
|
|
219
227
|
return this.synchronize<Schema>(matchObject, [], schema, session);
|
|
220
228
|
}
|
|
@@ -234,7 +242,7 @@ export class GraffitiSynchronize extends Graffiti {
|
|
|
234
242
|
*/
|
|
235
243
|
synchronizeRecoverOrphans<Schema extends JSONSchema>(
|
|
236
244
|
...args: Parameters<typeof Graffiti.prototype.recoverOrphans<Schema>>
|
|
237
|
-
):
|
|
245
|
+
): GraffitiObjectStreamContinue<Schema> {
|
|
238
246
|
const [schema, session] = args;
|
|
239
247
|
function matchObject(object: GraffitiObjectBase) {
|
|
240
248
|
return object.actor === session.actor && object.channels.length === 0;
|
|
@@ -253,15 +261,15 @@ export class GraffitiSynchronize extends Graffiti {
|
|
|
253
261
|
* expose the user to content out of context.
|
|
254
262
|
*/
|
|
255
263
|
synchronizeAll<Schema extends JSONSchema>(
|
|
256
|
-
schema
|
|
264
|
+
schema: Schema,
|
|
257
265
|
session?: GraffitiSession | null,
|
|
258
|
-
):
|
|
259
|
-
return this.synchronize(() => true, [], schema
|
|
266
|
+
): GraffitiObjectStreamContinue<Schema> {
|
|
267
|
+
return this.synchronize<Schema>(() => true, [], schema, session);
|
|
260
268
|
}
|
|
261
269
|
|
|
262
270
|
protected async synchronizeDispatch(
|
|
263
|
-
oldObject:
|
|
264
|
-
newObject?:
|
|
271
|
+
oldObject: GraffitiObjectStreamContinueEntry<{}>,
|
|
272
|
+
newObject?: GraffitiObjectStreamContinueEntry<{}>,
|
|
265
273
|
waitForListeners = false,
|
|
266
274
|
) {
|
|
267
275
|
for (const callback of this.callbacks) {
|
|
@@ -294,7 +302,7 @@ export class GraffitiSynchronize extends Graffiti {
|
|
|
294
302
|
|
|
295
303
|
get: Graffiti["get"] = async (...args) => {
|
|
296
304
|
const object = await this.graffiti.get(...args);
|
|
297
|
-
this.synchronizeDispatch(object);
|
|
305
|
+
this.synchronizeDispatch({ object });
|
|
298
306
|
return object;
|
|
299
307
|
};
|
|
300
308
|
|
|
@@ -306,45 +314,88 @@ export class GraffitiSynchronize extends Graffiti {
|
|
|
306
314
|
value: partialObject.value,
|
|
307
315
|
channels: partialObject.channels,
|
|
308
316
|
allowed: partialObject.allowed,
|
|
309
|
-
tombstone: false,
|
|
310
317
|
};
|
|
311
|
-
await this.synchronizeDispatch(
|
|
318
|
+
await this.synchronizeDispatch(
|
|
319
|
+
{
|
|
320
|
+
tombstone: true,
|
|
321
|
+
object: oldObject,
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
object: newObject,
|
|
325
|
+
},
|
|
326
|
+
true,
|
|
327
|
+
);
|
|
312
328
|
return oldObject;
|
|
313
329
|
};
|
|
314
330
|
|
|
315
331
|
patch: Graffiti["patch"] = async (...args) => {
|
|
316
332
|
const oldObject = await this.graffiti.patch(...args);
|
|
317
333
|
const newObject: GraffitiObjectBase = { ...oldObject };
|
|
318
|
-
newObject.tombstone = false;
|
|
319
334
|
for (const prop of ["value", "channels", "allowed"] as const) {
|
|
320
335
|
applyGraffitiPatch(await this.applyPatch, prop, args[0], newObject);
|
|
321
336
|
}
|
|
322
|
-
await this.synchronizeDispatch(
|
|
337
|
+
await this.synchronizeDispatch(
|
|
338
|
+
{
|
|
339
|
+
tombstone: true,
|
|
340
|
+
object: oldObject,
|
|
341
|
+
},
|
|
342
|
+
{
|
|
343
|
+
object: newObject,
|
|
344
|
+
},
|
|
345
|
+
true,
|
|
346
|
+
);
|
|
323
347
|
return oldObject;
|
|
324
348
|
};
|
|
325
349
|
|
|
326
350
|
delete: Graffiti["delete"] = async (...args) => {
|
|
327
351
|
const oldObject = await this.graffiti.delete(...args);
|
|
328
|
-
await this.synchronizeDispatch(
|
|
352
|
+
await this.synchronizeDispatch(
|
|
353
|
+
{
|
|
354
|
+
tombstone: true,
|
|
355
|
+
object: oldObject,
|
|
356
|
+
},
|
|
357
|
+
undefined,
|
|
358
|
+
true,
|
|
359
|
+
);
|
|
329
360
|
return oldObject;
|
|
330
361
|
};
|
|
331
362
|
|
|
332
|
-
protected
|
|
333
|
-
iterator:
|
|
334
|
-
) {
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
363
|
+
protected objectStreamContinue<Schema extends JSONSchema>(
|
|
364
|
+
iterator: GraffitiObjectStreamContinue<Schema>,
|
|
365
|
+
): GraffitiObjectStreamContinue<Schema> {
|
|
366
|
+
const this_ = this;
|
|
367
|
+
return (async function* () {
|
|
368
|
+
while (true) {
|
|
369
|
+
const result = await iterator.next();
|
|
370
|
+
if (result.done) {
|
|
371
|
+
const { continue: continue_, cursor } = result.value;
|
|
372
|
+
return {
|
|
373
|
+
continue: () => this_.objectStreamContinue<Schema>(continue_()),
|
|
374
|
+
cursor,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
339
377
|
if (!result.value.error) {
|
|
340
|
-
|
|
378
|
+
this_.synchronizeDispatch(
|
|
379
|
+
result.value as GraffitiObjectStreamContinueEntry<{}>,
|
|
380
|
+
);
|
|
341
381
|
}
|
|
342
382
|
yield result.value;
|
|
343
|
-
result = await iterator.next();
|
|
344
383
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
384
|
+
})();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
protected objectStream<Schema extends JSONSchema>(
|
|
388
|
+
iterator: GraffitiObjectStream<Schema>,
|
|
389
|
+
): GraffitiObjectStream<Schema> {
|
|
390
|
+
const wrapped = this.objectStreamContinue<Schema>(iterator);
|
|
391
|
+
return (async function* () {
|
|
392
|
+
// Filter out the tombstones for type safety
|
|
393
|
+
while (true) {
|
|
394
|
+
const result = await wrapped.next();
|
|
395
|
+
if (result.done) return result.value;
|
|
396
|
+
if (result.value.error || !result.value.tombstone) yield result.value;
|
|
397
|
+
}
|
|
398
|
+
})();
|
|
348
399
|
}
|
|
349
400
|
|
|
350
401
|
discover: Graffiti["discover"] = (...args) => {
|
|
@@ -356,4 +407,9 @@ export class GraffitiSynchronize extends Graffiti {
|
|
|
356
407
|
const iterator = this.graffiti.recoverOrphans(...args);
|
|
357
408
|
return this.objectStream<(typeof args)[0]>(iterator);
|
|
358
409
|
};
|
|
410
|
+
|
|
411
|
+
continueObjectStream: Graffiti["continueObjectStream"] = (...args) => {
|
|
412
|
+
// TODO!!
|
|
413
|
+
return this.graffiti.continueObjectStream(...args);
|
|
414
|
+
};
|
|
359
415
|
}
|