@graffiti-garden/wrapper-synchronize 0.2.4 → 1.0.3

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/esm/index.js CHANGED
@@ -1,22 +1,24 @@
1
- import { Graffiti } from "@graffiti-garden/api";
2
- import { Repeater } from "@repeaterjs/repeater";
3
1
  import {
4
- applyGraffitiPatch,
2
+ GraffitiErrorNotFound,
5
3
  compileGraffitiObjectSchema,
6
4
  isActorAllowedGraffitiObject,
7
5
  maskGraffitiObject,
8
6
  unpackObjectUrl
9
- } from "@graffiti-garden/implementation-local/utilities";
10
- class GraffitiSynchronize extends Graffiti {
7
+ } from "@graffiti-garden/api";
8
+ import { Repeater } from "@repeaterjs/repeater";
9
+ class GraffitiSynchronize {
11
10
  ajv_;
12
- applyPatch_;
13
11
  graffiti;
14
12
  callbacks = /* @__PURE__ */ new Set();
15
13
  options;
16
- channelStats;
17
14
  login;
18
15
  logout;
19
16
  sessionEvents;
17
+ postMedia;
18
+ getMedia;
19
+ deleteMedia;
20
+ actorToHandle;
21
+ handleToActor;
20
22
  get ajv() {
21
23
  if (!this.ajv_) {
22
24
  this.ajv_ = (async () => {
@@ -26,52 +28,42 @@ class GraffitiSynchronize extends Graffiti {
26
28
  }
27
29
  return this.ajv_;
28
30
  }
29
- get applyPatch() {
30
- if (!this.applyPatch_) {
31
- this.applyPatch_ = (async () => {
32
- const { applyPatch } = await import("fast-json-patch");
33
- return applyPatch;
34
- })();
35
- }
36
- return this.applyPatch_;
37
- }
38
31
  /**
39
32
  * Wraps a Graffiti API instance to provide the synchronize methods.
40
33
  * The GraffitiSyncrhonize class rather than the Graffiti class
41
34
  * must be used for all functions for the synchronize methods to work.
42
35
  */
43
36
  constructor(graffiti, options) {
44
- super();
45
37
  this.options = options ?? {};
46
38
  this.graffiti = graffiti;
47
- this.channelStats = graffiti.channelStats.bind(graffiti);
48
39
  this.login = graffiti.login.bind(graffiti);
49
40
  this.logout = graffiti.logout.bind(graffiti);
50
41
  this.sessionEvents = graffiti.sessionEvents;
42
+ this.postMedia = graffiti.postMedia.bind(graffiti);
43
+ this.getMedia = graffiti.getMedia.bind(graffiti);
44
+ this.deleteMedia = graffiti.deleteMedia.bind(graffiti);
45
+ this.actorToHandle = graffiti.actorToHandle.bind(graffiti);
46
+ this.handleToActor = graffiti.handleToActor.bind(graffiti);
51
47
  }
52
- synchronize(matchObject, channels, schema, session) {
53
- const seenUrls = /* @__PURE__ */ new Set();
48
+ synchronize(matchObject, channels, schema, session, seenUrls = /* @__PURE__ */ new Set()) {
54
49
  const repeater = new Repeater(
55
50
  async (push, stop) => {
56
51
  const validate = compileGraffitiObjectSchema(await this.ajv, schema);
57
- const callback = (oldObjectRaw, newObjectRaw) => {
58
- for (const objectRaw of [newObjectRaw, oldObjectRaw]) {
59
- if (objectRaw?.tombstone) {
60
- if (seenUrls.has(objectRaw.object.url)) {
61
- push(objectRaw);
62
- }
63
- } else if (objectRaw && matchObject(objectRaw.object) && (this.options.omniscient || isActorAllowedGraffitiObject(objectRaw.object, session))) {
64
- const object = JSON.parse(
65
- JSON.stringify(objectRaw.object)
66
- );
67
- if (!this.options.omniscient) {
68
- maskGraffitiObject(object, channels, session);
69
- }
70
- if (validate(object)) {
71
- push({ object });
72
- seenUrls.add(object.url);
73
- break;
74
- }
52
+ const callback = (objectUpdate) => {
53
+ if (objectUpdate?.tombstone) {
54
+ if (seenUrls.has(objectUpdate.object.url)) {
55
+ push(objectUpdate);
56
+ }
57
+ } else if (objectUpdate && matchObject(objectUpdate.object) && (this.options.omniscient || isActorAllowedGraffitiObject(objectUpdate.object, session))) {
58
+ const object = JSON.parse(
59
+ JSON.stringify(objectUpdate.object)
60
+ );
61
+ if (!this.options.omniscient) {
62
+ maskGraffitiObject(object, channels, session);
63
+ }
64
+ if (validate(object)) {
65
+ push({ object });
66
+ seenUrls.add(object.url);
75
67
  }
76
68
  }
77
69
  };
@@ -86,8 +78,8 @@ class GraffitiSynchronize extends Graffiti {
86
78
  }
87
79
  /**
88
80
  * This method has the same signature as {@link discover} but listens for
89
- * changes made via {@link put}, {@link patch}, and {@link delete} or
90
- * fetched from {@link get}, {@link discover}, and {@link recoverOrphans}
81
+ * changes made via {@link post} and {@link delete} or
82
+ * fetched from {@link get}, {@link discover}, and {@link continueDiscover}
91
83
  * and then streams appropriate changes to provide a responsive and
92
84
  * consistent user experience.
93
85
  *
@@ -95,10 +87,9 @@ class GraffitiSynchronize extends Graffiti {
95
87
  * and will not terminate unless the user calls the `return` method on the iterator
96
88
  * or `break`s out of the loop.
97
89
  *
98
- * @group Synchronize Methods
90
+ * @group 0 - Synchronize Methods
99
91
  */
100
- synchronizeDiscover(...args) {
101
- const [channels, schema, session] = args;
92
+ synchronizeDiscover(channels, schema, session) {
102
93
  function matchObject(object) {
103
94
  return object.channels.some((channel) => channels.includes(channel));
104
95
  }
@@ -106,43 +97,28 @@ class GraffitiSynchronize extends Graffiti {
106
97
  }
107
98
  /**
108
99
  * This method has the same signature as {@link get} but
109
- * listens for changes made via {@link put}, {@link patch}, and {@link delete} or
110
- * fetched from {@link get}, {@link discover}, and {@link recoverOrphans} and then
100
+ * listens for changes made via {@link post}, and {@link delete} or
101
+ * fetched from {@link get}, {@link discover}, and {@link continueDiscover} and then
111
102
  * streams appropriate changes to provide a responsive and consistent user experience.
112
103
  *
113
104
  * Unlike {@link get}, which returns a single result, this method continuously
114
105
  * listens for changes which are output as an asynchronous stream, similar
115
106
  * to {@link discover}.
116
107
  *
117
- * @group Synchronize Methods
108
+ * @group 0 - Synchronize Methods
118
109
  */
119
- synchronizeGet(...args) {
120
- const [objectUrl, schema, session] = args;
110
+ synchronizeGet(objectUrl, schema, session) {
121
111
  const url = unpackObjectUrl(objectUrl);
122
112
  function matchObject(object) {
123
113
  return object.url === url;
124
114
  }
125
- return this.synchronize(matchObject, [], schema, session);
126
- }
127
- /**
128
- * This method has the same signature as {@link recoverOrphans} but
129
- * listens for changes made via
130
- * {@link put}, {@link patch}, and {@link delete} or fetched from
131
- * {@link get}, {@link discover}, and {@link recoverOrphans} and then
132
- * streams appropriate changes to provide a responsive and consistent user experience.
133
- *
134
- * Unlike {@link recoverOrphans}, this method continuously listens for changes
135
- * and will not terminate unless the user calls the `return` method on the iterator
136
- * or `break`s out of the loop.
137
- *
138
- * @group Synchronize Methods
139
- */
140
- synchronizeRecoverOrphans(...args) {
141
- const [schema, session] = args;
142
- function matchObject(object) {
143
- return object.actor === session.actor && object.channels.length === 0;
144
- }
145
- return this.synchronize(matchObject, [], schema, session);
115
+ return this.synchronize(
116
+ matchObject,
117
+ [],
118
+ schema,
119
+ session,
120
+ /* @__PURE__ */ new Set([url])
121
+ );
146
122
  }
147
123
  /**
148
124
  * Streams changes made to *any* object in *any* channel
@@ -154,74 +130,54 @@ class GraffitiSynchronize extends Graffiti {
154
130
  * Be careful using this method. Without additional filters it can
155
131
  * expose the user to content out of context.
156
132
  *
157
- * @group Synchronize Methods
133
+ * @group 0 - Synchronize Methods
158
134
  */
159
135
  synchronizeAll(schema, session) {
160
136
  return this.synchronize(() => true, [], schema, session);
161
137
  }
162
- async synchronizeDispatch(oldObject, newObject, waitForListeners = false) {
138
+ async synchronizeDispatch(objectUpdate, waitForListeners = false) {
163
139
  for (const callback of this.callbacks) {
164
- callback(oldObject, newObject);
140
+ callback(objectUpdate);
165
141
  }
166
142
  if (waitForListeners) {
167
143
  await new Promise((resolve) => setTimeout(resolve, 0));
168
144
  }
169
145
  }
170
146
  get = async (...args) => {
171
- const object = await this.graffiti.get(...args);
172
- this.synchronizeDispatch({ object });
147
+ try {
148
+ const object = await this.graffiti.get(...args);
149
+ this.synchronizeDispatch({ object });
150
+ return object;
151
+ } catch (error) {
152
+ if (error instanceof GraffitiErrorNotFound) {
153
+ this.synchronizeDispatch({
154
+ tombstone: true,
155
+ object: { url: unpackObjectUrl(args[0]) }
156
+ });
157
+ }
158
+ throw error;
159
+ }
160
+ };
161
+ post = async (...args) => {
162
+ const object = await this.graffiti.post(...args);
163
+ await this.synchronizeDispatch({ object }, true);
173
164
  return object;
174
165
  };
175
- put = async (...args) => {
176
- const oldObject = await this.graffiti.put(...args);
177
- const partialObject = args[0];
178
- const newObject = {
179
- ...oldObject,
180
- value: partialObject.value,
181
- channels: partialObject.channels,
182
- allowed: partialObject.allowed
166
+ delete = async (...args) => {
167
+ const update = {
168
+ tombstone: true,
169
+ object: { url: unpackObjectUrl(args[0]) }
183
170
  };
184
- await this.synchronizeDispatch(
185
- {
186
- tombstone: true,
187
- object: oldObject
188
- },
189
- {
190
- object: newObject
191
- },
192
- true
193
- );
194
- return oldObject;
195
- };
196
- patch = async (...args) => {
197
- const oldObject = await this.graffiti.patch(...args);
198
- const newObject = { ...oldObject };
199
- for (const prop of ["value", "channels", "allowed"]) {
200
- applyGraffitiPatch(await this.applyPatch, prop, args[0], newObject);
171
+ try {
172
+ const oldObject = await this.graffiti.delete(...args);
173
+ await this.synchronizeDispatch(update, true);
174
+ return oldObject;
175
+ } catch (error) {
176
+ if (error instanceof GraffitiErrorNotFound) {
177
+ await this.synchronizeDispatch(update, true);
178
+ }
179
+ throw error;
201
180
  }
202
- await this.synchronizeDispatch(
203
- {
204
- tombstone: true,
205
- object: oldObject
206
- },
207
- {
208
- object: newObject
209
- },
210
- true
211
- );
212
- return oldObject;
213
- };
214
- delete = async (...args) => {
215
- const oldObject = await this.graffiti.delete(...args);
216
- await this.synchronizeDispatch(
217
- {
218
- tombstone: true,
219
- object: oldObject
220
- },
221
- void 0,
222
- true
223
- );
224
- return oldObject;
225
181
  };
226
182
  objectStreamContinue(iterator) {
227
183
  const this_ = this;
@@ -231,14 +187,12 @@ class GraffitiSynchronize extends Graffiti {
231
187
  if (result.done) {
232
188
  const { continue: continue_, cursor } = result.value;
233
189
  return {
234
- continue: () => this_.objectStreamContinue(continue_()),
190
+ continue: (session) => this_.objectStreamContinue(continue_(session)),
235
191
  cursor
236
192
  };
237
193
  }
238
194
  if (!result.value.error) {
239
- this_.synchronizeDispatch(
240
- result.value
241
- );
195
+ this_.synchronizeDispatch(result.value);
242
196
  }
243
197
  yield result.value;
244
198
  }
@@ -258,12 +212,8 @@ class GraffitiSynchronize extends Graffiti {
258
212
  const iterator = this.graffiti.discover(...args);
259
213
  return this.objectStream(iterator);
260
214
  };
261
- recoverOrphans = (...args) => {
262
- const iterator = this.graffiti.recoverOrphans(...args);
263
- return this.objectStream(iterator);
264
- };
265
- continueObjectStream = (...args) => {
266
- const iterator = this.graffiti.continueObjectStream(...args);
215
+ continueDiscover = (...args) => {
216
+ const iterator = this.graffiti.continueDiscover(...args);
267
217
  return this.objectStreamContinue(iterator);
268
218
  };
269
219
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/index.ts"],
4
- "sourcesContent": ["import type Ajv from \"ajv\";\nimport { Graffiti } from \"@graffiti-garden/api\";\nimport type {\n GraffitiSession,\n JSONSchema,\n GraffitiObjectStream,\n GraffitiObjectStreamContinueEntry,\n GraffitiObjectStreamContinue,\n GraffitiObject,\n} from \"@graffiti-garden/api\";\nimport type { GraffitiObjectBase } from \"@graffiti-garden/api\";\nimport { Repeater } from \"@repeaterjs/repeater\";\nimport type { applyPatch } from \"fast-json-patch\";\nimport {\n applyGraffitiPatch,\n compileGraffitiObjectSchema,\n isActorAllowedGraffitiObject,\n maskGraffitiObject,\n unpackObjectUrl,\n} from \"@graffiti-garden/implementation-local/utilities\";\nexport type * from \"@graffiti-garden/api\";\n\nexport type GraffitiSynchronizeCallback = (\n oldObject: GraffitiObjectStreamContinueEntry<{}>,\n newObject?: GraffitiObjectStreamContinueEntry<{}>,\n) => void;\n\nexport interface GraffitiSynchronizeOptions {\n /**\n * Allows synchronize to listen to all objects, not just those\n * that the session is allowed to see. This is useful to get a\n * global view of all Graffiti objects passsing through the system,\n * for example to build a client-side cache. Additional mechanisms\n * should be in place to ensure that users do not see objects or\n * properties they are not allowed to see.\n *\n * Default: `false`\n */\n omniscient?: boolean;\n}\n\n/**\n * Wraps the [Graffiti API](https://api.graffiti.garden/classes/Graffiti.html)\n * so that changes made or received in one part of an application\n * are automatically routed to other parts of the application.\n * This is an important tool for building responsive\n * and consistent user interfaces, and is built upon to make\n * the [Graffiti Vue Plugin](https://vue.graffiti.garden/variables/GraffitiPlugin.html)\n * and possibly other front-end libraries in the future.\n *\n * [See a live example](/example).\n *\n * Specifically, this library provides the following *synchronize*\n * methods to correspond with each of the following Graffiti API methods:\n *\n * | API Method | Synchronize Method |\n * |------------|--------------------|\n * | {@link get} | {@link synchronizeGet} |\n * | {@link discover} | {@link synchronizeDiscover} |\n * | {@link recoverOrphans} | {@link synchronizeRecoverOrphans} |\n *\n * Whenever a change is made via {@link put}, {@link patch}, and {@link delete} or\n * received from {@link get}, {@link discover}, and {@link recoverOrphans},\n * those changes are forwarded to the appropriate synchronize method.\n * Each synchronize method returns an iterator that streams these changes\n * continually until the user calls `return` on the iterator or `break`s out of the loop,\n * allowing for live updates without additional polling.\n *\n * Example 1: Suppose a user publishes a post using {@link put}. If the feed\n * displaying that user's posts is using {@link synchronizeDiscover} to listen for changes,\n * then the user's new post will instantly appear in their feed, giving the UI a\n * responsive feel.\n *\n * Example 2: Suppose one of a user's friends changes their name. As soon as the\n * user's application receives one notice of that change (using {@link get}\n * or {@link discover}), then {@link synchronizeDiscover} listeners can be used to update\n * all instance's of that friend's name in the user's application instantly,\n * providing a consistent user experience.\n *\n * The source code for this library is [available on GitHub](https://github.com/graffiti-garden/wrapper-synchronize/).\n *\n * @groupDescription Synchronize Methods\n * This group contains methods that listen for changes made via\n * {@link put}, {@link patch}, and {@link delete} or fetched from\n * {@link get}, {@link discover}, and {@link recoverOrphans} and then\n * streams appropriate changes to provide a responsive and consistent user experience.\n */\nexport class GraffitiSynchronize extends Graffiti {\n protected ajv_: Promise<Ajv> | undefined;\n protected applyPatch_: Promise<typeof applyPatch> | undefined;\n protected readonly graffiti: Graffiti;\n protected readonly callbacks = new Set<GraffitiSynchronizeCallback>();\n protected readonly options: GraffitiSynchronizeOptions;\n\n channelStats: Graffiti[\"channelStats\"];\n login: Graffiti[\"login\"];\n logout: Graffiti[\"logout\"];\n sessionEvents: Graffiti[\"sessionEvents\"];\n\n protected get ajv() {\n if (!this.ajv_) {\n this.ajv_ = (async () => {\n const { default: Ajv } = await import(\"ajv\");\n return new Ajv({ strict: false });\n })();\n }\n return this.ajv_;\n }\n\n protected get applyPatch() {\n if (!this.applyPatch_) {\n this.applyPatch_ = (async () => {\n const { applyPatch } = await import(\"fast-json-patch\");\n return applyPatch;\n })();\n }\n return this.applyPatch_;\n }\n\n /**\n * Wraps a Graffiti API instance to provide the synchronize methods.\n * The GraffitiSyncrhonize class rather than the Graffiti class\n * must be used for all functions for the synchronize methods to work.\n */\n constructor(\n /**\n * The [Graffiti API](https://api.graffiti.garden/classes/Graffiti.html)\n * instance to wrap.\n */\n graffiti: Graffiti,\n options?: GraffitiSynchronizeOptions,\n ) {\n super();\n this.options = options ?? {};\n this.graffiti = graffiti;\n this.channelStats = graffiti.channelStats.bind(graffiti);\n this.login = graffiti.login.bind(graffiti);\n this.logout = graffiti.logout.bind(graffiti);\n this.sessionEvents = graffiti.sessionEvents;\n }\n\n protected synchronize<Schema extends JSONSchema>(\n matchObject: (object: GraffitiObjectBase) => boolean,\n channels: string[],\n schema: Schema,\n session?: GraffitiSession | null,\n ): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>> {\n const seenUrls = new Set<string>();\n\n const repeater = new Repeater<GraffitiObjectStreamContinueEntry<Schema>>(\n async (push, stop) => {\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n const callback: GraffitiSynchronizeCallback = (\n oldObjectRaw,\n newObjectRaw,\n ) => {\n for (const objectRaw of [newObjectRaw, oldObjectRaw]) {\n if (objectRaw?.tombstone) {\n if (seenUrls.has(objectRaw.object.url)) {\n push(objectRaw);\n }\n } else if (\n objectRaw &&\n matchObject(objectRaw.object) &&\n (this.options.omniscient ||\n isActorAllowedGraffitiObject(objectRaw.object, session))\n ) {\n // Deep clone the object to prevent mutation\n const object = JSON.parse(\n JSON.stringify(objectRaw.object),\n ) as GraffitiObject<{}>;\n if (!this.options.omniscient) {\n maskGraffitiObject(object, channels, session);\n }\n if (validate(object)) {\n push({ object });\n seenUrls.add(object.url);\n break;\n }\n }\n }\n };\n\n this.callbacks.add(callback);\n await stop;\n this.callbacks.delete(callback);\n },\n );\n\n return (async function* () {\n for await (const i of repeater) yield i;\n })();\n }\n\n /**\n * This method has the same signature as {@link discover} but listens for\n * changes made via {@link put}, {@link patch}, and {@link delete} or\n * fetched from {@link get}, {@link discover}, and {@link recoverOrphans}\n * and then streams appropriate changes to provide a responsive and\n * consistent user experience.\n *\n * Unlike {@link discover}, this method continuously listens for changes\n * and will not terminate unless the user calls the `return` method on the iterator\n * or `break`s out of the loop.\n *\n * @group Synchronize Methods\n */\n synchronizeDiscover<Schema extends JSONSchema>(\n ...args: Parameters<typeof Graffiti.prototype.discover<Schema>>\n ): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>> {\n const [channels, schema, session] = args;\n function matchObject(object: GraffitiObjectBase) {\n return object.channels.some((channel) => channels.includes(channel));\n }\n return this.synchronize<Schema>(matchObject, channels, schema, session);\n }\n\n /**\n * This method has the same signature as {@link get} but\n * listens for changes made via {@link put}, {@link patch}, and {@link delete} or\n * fetched from {@link get}, {@link discover}, and {@link recoverOrphans} and then\n * streams appropriate changes to provide a responsive and consistent user experience.\n *\n * Unlike {@link get}, which returns a single result, this method continuously\n * listens for changes which are output as an asynchronous stream, similar\n * to {@link discover}.\n *\n * @group Synchronize Methods\n */\n synchronizeGet<Schema extends JSONSchema>(\n ...args: Parameters<typeof Graffiti.prototype.get<Schema>>\n ): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>> {\n const [objectUrl, schema, session] = args;\n const url = unpackObjectUrl(objectUrl);\n function matchObject(object: GraffitiObjectBase) {\n return object.url === url;\n }\n return this.synchronize<Schema>(matchObject, [], schema, session);\n }\n\n /**\n * This method has the same signature as {@link recoverOrphans} but\n * listens for changes made via\n * {@link put}, {@link patch}, and {@link delete} or fetched from\n * {@link get}, {@link discover}, and {@link recoverOrphans} and then\n * streams appropriate changes to provide a responsive and consistent user experience.\n *\n * Unlike {@link recoverOrphans}, this method continuously listens for changes\n * and will not terminate unless the user calls the `return` method on the iterator\n * or `break`s out of the loop.\n *\n * @group Synchronize Methods\n */\n synchronizeRecoverOrphans<Schema extends JSONSchema>(\n ...args: Parameters<typeof Graffiti.prototype.recoverOrphans<Schema>>\n ): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>> {\n const [schema, session] = args;\n function matchObject(object: GraffitiObjectBase) {\n return object.actor === session.actor && object.channels.length === 0;\n }\n return this.synchronize<Schema>(matchObject, [], schema, session);\n }\n\n /**\n * Streams changes made to *any* object in *any* channel\n * and made by *any* user. You may want to use it in conjuction with\n * {@link GraffitiSynchronizeOptions.omniscient} to get a global view\n * of all Graffiti objects passing through the system. This is useful\n * for building a client-side cache, for example.\n *\n * Be careful using this method. Without additional filters it can\n * expose the user to content out of context.\n *\n * @group Synchronize Methods\n */\n synchronizeAll<Schema extends JSONSchema>(\n schema: Schema,\n session?: GraffitiSession | null,\n ): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>> {\n return this.synchronize<Schema>(() => true, [], schema, session);\n }\n\n protected async synchronizeDispatch(\n oldObject: GraffitiObjectStreamContinueEntry<{}>,\n newObject?: GraffitiObjectStreamContinueEntry<{}>,\n waitForListeners = false,\n ) {\n for (const callback of this.callbacks) {\n callback(oldObject, newObject);\n }\n if (waitForListeners) {\n // Wait for the listeners to receive\n // their objects, before returning the operation\n // that triggered them.\n //\n // This is important for mutators (put, patch, delete)\n // to ensure the application state has been updated\n // everywhere before returning, giving consistent\n // feedback to the user that the operation has completed.\n //\n // The opposite is true for accessors (get, discover, recoverOrphans),\n // where it is a weird user experience to call `get`\n // in one place and have the application update\n // somewhere else first. It is also less efficient.\n //\n // The hack is simply to await one \"macro task cycle\".\n // We need to wait for this cycle rather than using\n // `await push` in the callback, because it turns out\n // that `await push` won't resolve until the following\n // .next() call of the iterator, so if only\n // one .next() is called, this dispatch will hang.\n await new Promise((resolve) => setTimeout(resolve, 0));\n }\n }\n\n get: Graffiti[\"get\"] = async (...args) => {\n const object = await this.graffiti.get(...args);\n this.synchronizeDispatch({ object });\n return object;\n };\n\n put: Graffiti[\"put\"] = async (...args) => {\n const oldObject = await this.graffiti.put<{}>(...args);\n const partialObject = args[0];\n const newObject: GraffitiObjectBase = {\n ...oldObject,\n value: partialObject.value,\n channels: partialObject.channels,\n allowed: partialObject.allowed,\n };\n await this.synchronizeDispatch(\n {\n tombstone: true,\n object: oldObject,\n },\n {\n object: newObject,\n },\n true,\n );\n return oldObject;\n };\n\n patch: Graffiti[\"patch\"] = async (...args) => {\n const oldObject = await this.graffiti.patch(...args);\n const newObject: GraffitiObjectBase = { ...oldObject };\n for (const prop of [\"value\", \"channels\", \"allowed\"] as const) {\n applyGraffitiPatch(await this.applyPatch, prop, args[0], newObject);\n }\n await this.synchronizeDispatch(\n {\n tombstone: true,\n object: oldObject,\n },\n {\n object: newObject,\n },\n true,\n );\n return oldObject;\n };\n\n delete: Graffiti[\"delete\"] = async (...args) => {\n const oldObject = await this.graffiti.delete(...args);\n await this.synchronizeDispatch(\n {\n tombstone: true,\n object: oldObject,\n },\n undefined,\n true,\n );\n return oldObject;\n };\n\n protected objectStreamContinue<Schema extends JSONSchema>(\n iterator: GraffitiObjectStreamContinue<Schema>,\n ): GraffitiObjectStreamContinue<Schema> {\n const this_ = this;\n return (async function* () {\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n const { continue: continue_, cursor } = result.value;\n return {\n continue: () => this_.objectStreamContinue<Schema>(continue_()),\n cursor,\n };\n }\n if (!result.value.error) {\n this_.synchronizeDispatch(\n result.value as GraffitiObjectStreamContinueEntry<{}>,\n );\n }\n yield result.value;\n }\n })();\n }\n\n protected objectStream<Schema extends JSONSchema>(\n iterator: GraffitiObjectStream<Schema>,\n ): GraffitiObjectStream<Schema> {\n const wrapped = this.objectStreamContinue<Schema>(iterator);\n return (async function* () {\n // Filter out the tombstones for type safety\n while (true) {\n const result = await wrapped.next();\n if (result.done) return result.value;\n if (result.value.error || !result.value.tombstone) yield result.value;\n }\n })();\n }\n\n discover: Graffiti[\"discover\"] = (...args) => {\n const iterator = this.graffiti.discover(...args);\n return this.objectStream<(typeof args)[1]>(iterator);\n };\n\n recoverOrphans: Graffiti[\"recoverOrphans\"] = (...args) => {\n const iterator = this.graffiti.recoverOrphans(...args);\n return this.objectStream<(typeof args)[0]>(iterator);\n };\n\n continueObjectStream: Graffiti[\"continueObjectStream\"] = (...args) => {\n const iterator = this.graffiti.continueObjectStream(...args);\n return this.objectStreamContinue<{}>(iterator);\n };\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type Ajv from \"ajv\";\nimport type {\n Graffiti,\n GraffitiSession,\n JSONSchema,\n GraffitiObjectBase,\n GraffitiObjectStream,\n GraffitiObjectStreamContinueEntry,\n GraffitiObjectStreamContinue,\n GraffitiObjectUrl,\n} from \"@graffiti-garden/api\";\nimport {\n GraffitiErrorNotFound,\n compileGraffitiObjectSchema,\n isActorAllowedGraffitiObject,\n maskGraffitiObject,\n unpackObjectUrl,\n} from \"@graffiti-garden/api\";\nimport { Repeater } from \"@repeaterjs/repeater\";\nexport type * from \"@graffiti-garden/api\";\n\nexport type GraffitiSynchronizeCallback = (\n object: GraffitiObjectStreamContinueEntry<{}>,\n) => void;\n\nexport interface GraffitiSynchronizeOptions {\n /**\n * Allows synchronize to listen to all objects, not just those\n * that the session is allowed to see. This is useful to get a\n * global view of all Graffiti objects passsing through the system,\n * for example to build a client-side cache. Additional mechanisms\n * should be in place to ensure that users do not see objects or\n * properties they are not allowed to see.\n *\n * Default: `false`\n */\n omniscient?: boolean;\n}\n\n/**\n * Wraps the [Graffiti API](https://api.graffiti.garden/classes/Graffiti.html)\n * so that changes made or received in one part of an application\n * are automatically routed to other parts of the application.\n * This is an important tool for building responsive\n * and consistent user interfaces, and is built upon to make\n * the [Graffiti Vue Plugin](https://vue.graffiti.garden/variables/GraffitiPlugin.html)\n * and possibly other front-end libraries in the future.\n *\n * [See a live example](/example).\n *\n * Specifically, this library provides the following *synchronize*\n * methods to correspond with each of the following Graffiti API methods:\n *\n * | API Method | Synchronize Method |\n * |------------|--------------------|\n * | {@link get} | {@link synchronizeGet} |\n * | {@link discover} | {@link synchronizeDiscover} |\n *\n * Whenever a change is made via {@link post} and {@link delete} or\n * received from {@link get}, {@link discover}, and {@link continueDiscover},\n * those changes are forwarded to the appropriate synchronize method.\n * Each synchronize method returns an iterator that streams these changes\n * continually until the user calls `return` on the iterator or `break`s out of the loop,\n * allowing for live updates without additional polling.\n *\n * Example 1: Suppose a user publishes a post using {@link post}. If the feed\n * displaying that user's posts is using {@link synchronizeDiscover} to listen for changes,\n * then the user's new post will instantly appear in their feed, giving the UI a\n * responsive feel.\n *\n * Example 2: Suppose one of a user's friends changes their name. As soon as the\n * user's application receives one notice of that change (using {@link get}\n * or {@link discover}), then {@link synchronizeDiscover} listeners can be used to update\n * all instance's of that friend's name in the user's application instantly,\n * providing a consistent user experience.\n *\n * Additionally, the library supplies a {@link synchronizeAll} method that can be used\n * to stream all the Graffiti changes that an application is aware of, which can be used\n * for caching or history building.\n *\n * The source code for this library is [available on GitHub](https://github.com/graffiti-garden/wrapper-synchronize/).\n *\n * @groupDescription 0 - Synchronize Methods\n * This group contains methods that listen for changes made via\n * {@link post}, and {@link delete} or fetched from\n * {@link get}, {@link discover}, or {@link continueDiscover} and then\n * streams appropriate changes to provide a responsive and consistent user experience.\n */\nexport class GraffitiSynchronize implements Graffiti {\n protected ajv_: Promise<Ajv> | undefined;\n protected readonly graffiti: Graffiti;\n protected readonly callbacks = new Set<GraffitiSynchronizeCallback>();\n protected readonly options: GraffitiSynchronizeOptions;\n\n login: Graffiti[\"login\"];\n logout: Graffiti[\"logout\"];\n sessionEvents: Graffiti[\"sessionEvents\"];\n postMedia: Graffiti[\"postMedia\"];\n getMedia: Graffiti[\"getMedia\"];\n deleteMedia: Graffiti[\"deleteMedia\"];\n actorToHandle: Graffiti[\"actorToHandle\"];\n handleToActor: Graffiti[\"handleToActor\"];\n\n protected get ajv() {\n if (!this.ajv_) {\n this.ajv_ = (async () => {\n const { default: Ajv } = await import(\"ajv\");\n return new Ajv({ strict: false });\n })();\n }\n return this.ajv_;\n }\n\n /**\n * Wraps a Graffiti API instance to provide the synchronize methods.\n * The GraffitiSyncrhonize class rather than the Graffiti class\n * must be used for all functions for the synchronize methods to work.\n */\n constructor(\n /**\n * The [Graffiti API](https://api.graffiti.garden/classes/Graffiti.html)\n * instance to wrap.\n */\n graffiti: Graffiti,\n options?: GraffitiSynchronizeOptions,\n ) {\n this.options = options ?? {};\n this.graffiti = graffiti;\n this.login = graffiti.login.bind(graffiti);\n this.logout = graffiti.logout.bind(graffiti);\n this.sessionEvents = graffiti.sessionEvents;\n this.postMedia = graffiti.postMedia.bind(graffiti);\n this.getMedia = graffiti.getMedia.bind(graffiti);\n this.deleteMedia = graffiti.deleteMedia.bind(graffiti);\n this.actorToHandle = graffiti.actorToHandle.bind(graffiti);\n this.handleToActor = graffiti.handleToActor.bind(graffiti);\n }\n\n protected synchronize<Schema extends JSONSchema>(\n matchObject: (object: GraffitiObjectBase) => boolean,\n channels: string[],\n schema: Schema,\n session?: GraffitiSession | null,\n seenUrls: Set<string> = new Set<string>(),\n ): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>> {\n const repeater = new Repeater<GraffitiObjectStreamContinueEntry<Schema>>(\n async (push, stop) => {\n const validate = compileGraffitiObjectSchema(await this.ajv, schema);\n const callback: GraffitiSynchronizeCallback = (objectUpdate) => {\n if (objectUpdate?.tombstone) {\n if (seenUrls.has(objectUpdate.object.url)) {\n push(objectUpdate);\n }\n } else if (\n objectUpdate &&\n matchObject(objectUpdate.object) &&\n (this.options.omniscient ||\n isActorAllowedGraffitiObject(objectUpdate.object, session))\n ) {\n // Deep clone the object to prevent mutation\n const object = JSON.parse(\n JSON.stringify(objectUpdate.object),\n ) as GraffitiObjectBase;\n if (!this.options.omniscient) {\n maskGraffitiObject(object, channels, session);\n }\n if (validate(object)) {\n push({ object });\n seenUrls.add(object.url);\n }\n }\n };\n\n this.callbacks.add(callback);\n await stop;\n this.callbacks.delete(callback);\n },\n );\n\n return (async function* () {\n for await (const i of repeater) yield i;\n })();\n }\n\n /**\n * This method has the same signature as {@link discover} but listens for\n * changes made via {@link post} and {@link delete} or\n * fetched from {@link get}, {@link discover}, and {@link continueDiscover}\n * and then streams appropriate changes to provide a responsive and\n * consistent user experience.\n *\n * Unlike {@link discover}, this method continuously listens for changes\n * and will not terminate unless the user calls the `return` method on the iterator\n * or `break`s out of the loop.\n *\n * @group 0 - Synchronize Methods\n */\n synchronizeDiscover<Schema extends JSONSchema>(\n channels: string[],\n schema: Schema,\n session?: GraffitiSession | null,\n ): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>> {\n function matchObject(object: GraffitiObjectBase) {\n return object.channels.some((channel) => channels.includes(channel));\n }\n return this.synchronize<Schema>(matchObject, channels, schema, session);\n }\n\n /**\n * This method has the same signature as {@link get} but\n * listens for changes made via {@link post}, and {@link delete} or\n * fetched from {@link get}, {@link discover}, and {@link continueDiscover} and then\n * streams appropriate changes to provide a responsive and consistent user experience.\n *\n * Unlike {@link get}, which returns a single result, this method continuously\n * listens for changes which are output as an asynchronous stream, similar\n * to {@link discover}.\n *\n * @group 0 - Synchronize Methods\n */\n synchronizeGet<Schema extends JSONSchema>(\n objectUrl: string | GraffitiObjectUrl,\n schema: Schema,\n session?: GraffitiSession | null | undefined,\n ): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>> {\n const url = unpackObjectUrl(objectUrl);\n function matchObject(object: GraffitiObjectBase) {\n return object.url === url;\n }\n return this.synchronize<Schema>(\n matchObject,\n [],\n schema,\n session,\n new Set<string>([url]),\n );\n }\n\n /**\n * Streams changes made to *any* object in *any* channel\n * and made by *any* user. You may want to use it in conjuction with\n * {@link GraffitiSynchronizeOptions.omniscient} to get a global view\n * of all Graffiti objects passing through the system. This is useful\n * for building a client-side cache, for example.\n *\n * Be careful using this method. Without additional filters it can\n * expose the user to content out of context.\n *\n * @group 0 - Synchronize Methods\n */\n synchronizeAll<Schema extends JSONSchema>(\n schema: Schema,\n session?: GraffitiSession | null,\n ): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>> {\n return this.synchronize<Schema>(() => true, [], schema, session);\n }\n\n protected async synchronizeDispatch(\n objectUpdate: GraffitiObjectStreamContinueEntry<{}>,\n waitForListeners = false,\n ) {\n for (const callback of this.callbacks) {\n callback(objectUpdate);\n }\n if (waitForListeners) {\n // Wait for the listeners to receive\n // their objects, before returning the operation\n // that triggered them.\n //\n // This is important for mutators (put, patch, delete)\n // to ensure the application state has been updated\n // everywhere before returning, giving consistent\n // feedback to the user that the operation has completed.\n //\n // The opposite is true for accessors (get, discover, recoverOrphans),\n // where it is a weird user experience to call `get`\n // in one place and have the application update\n // somewhere else first. It is also less efficient.\n //\n // The hack is simply to await one \"macro task cycle\".\n // We need to wait for this cycle rather than using\n // `await push` in the callback, because it turns out\n // that `await push` won't resolve until the following\n // .next() call of the iterator, so if only\n // one .next() is called, this dispatch will hang.\n await new Promise((resolve) => setTimeout(resolve, 0));\n }\n }\n\n get: Graffiti[\"get\"] = async (...args) => {\n try {\n const object = await this.graffiti.get(...args);\n this.synchronizeDispatch({ object });\n return object;\n } catch (error) {\n if (error instanceof GraffitiErrorNotFound) {\n this.synchronizeDispatch({\n tombstone: true,\n object: { url: unpackObjectUrl(args[0]) },\n });\n }\n throw error;\n }\n };\n\n post: Graffiti[\"post\"] = async (...args) => {\n // @ts-ignore\n const object = await this.graffiti.post(...args);\n await this.synchronizeDispatch({ object }, true);\n return object;\n };\n\n delete: Graffiti[\"delete\"] = async (...args) => {\n const update = {\n tombstone: true,\n object: { url: unpackObjectUrl(args[0]) },\n } as const;\n try {\n const oldObject = await this.graffiti.delete(...args);\n await this.synchronizeDispatch(update, true);\n return oldObject;\n } catch (error) {\n if (error instanceof GraffitiErrorNotFound) {\n await this.synchronizeDispatch(update, true);\n }\n throw error;\n }\n };\n\n protected objectStreamContinue<Schema extends JSONSchema>(\n iterator: GraffitiObjectStreamContinue<Schema>,\n ): GraffitiObjectStreamContinue<Schema> {\n const this_ = this;\n return (async function* () {\n while (true) {\n const result = await iterator.next();\n if (result.done) {\n const { continue: continue_, cursor } = result.value;\n return {\n continue: (session?: GraffitiSession | null) =>\n this_.objectStreamContinue<Schema>(continue_(session)),\n cursor,\n };\n }\n if (!result.value.error) {\n this_.synchronizeDispatch(result.value);\n }\n yield result.value;\n }\n })();\n }\n\n protected objectStream<Schema extends JSONSchema>(\n iterator: GraffitiObjectStream<Schema>,\n ): GraffitiObjectStream<Schema> {\n const wrapped = this.objectStreamContinue<Schema>(iterator);\n return (async function* () {\n // Filter out the tombstones for type safety\n while (true) {\n const result = await wrapped.next();\n if (result.done) return result.value;\n if (result.value.error || !result.value.tombstone) yield result.value;\n }\n })();\n }\n\n discover: Graffiti[\"discover\"] = (...args) => {\n const iterator = this.graffiti.discover(...args);\n return this.objectStream<(typeof args)[1]>(iterator);\n };\n\n continueDiscover: Graffiti[\"continueDiscover\"] = (...args) => {\n const iterator = this.graffiti.continueDiscover(...args);\n return this.objectStreamContinue<{}>(iterator);\n };\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/index.d.ts CHANGED
@@ -1,10 +1,7 @@
1
1
  import type Ajv from "ajv";
2
- import { Graffiti } from "@graffiti-garden/api";
3
- import type { GraffitiSession, JSONSchema, GraffitiObjectStream, GraffitiObjectStreamContinueEntry, GraffitiObjectStreamContinue } from "@graffiti-garden/api";
4
- import type { GraffitiObjectBase } from "@graffiti-garden/api";
5
- import type { applyPatch } from "fast-json-patch";
2
+ import type { Graffiti, GraffitiSession, JSONSchema, GraffitiObjectBase, GraffitiObjectStream, GraffitiObjectStreamContinueEntry, GraffitiObjectStreamContinue, GraffitiObjectUrl } from "@graffiti-garden/api";
6
3
  export type * from "@graffiti-garden/api";
7
- export type GraffitiSynchronizeCallback = (oldObject: GraffitiObjectStreamContinueEntry<{}>, newObject?: GraffitiObjectStreamContinueEntry<{}>) => void;
4
+ export type GraffitiSynchronizeCallback = (object: GraffitiObjectStreamContinueEntry<{}>) => void;
8
5
  export interface GraffitiSynchronizeOptions {
9
6
  /**
10
7
  * Allows synchronize to listen to all objects, not just those
@@ -36,16 +33,15 @@ export interface GraffitiSynchronizeOptions {
36
33
  * |------------|--------------------|
37
34
  * | {@link get} | {@link synchronizeGet} |
38
35
  * | {@link discover} | {@link synchronizeDiscover} |
39
- * | {@link recoverOrphans} | {@link synchronizeRecoverOrphans} |
40
36
  *
41
- * Whenever a change is made via {@link put}, {@link patch}, and {@link delete} or
42
- * received from {@link get}, {@link discover}, and {@link recoverOrphans},
37
+ * Whenever a change is made via {@link post} and {@link delete} or
38
+ * received from {@link get}, {@link discover}, and {@link continueDiscover},
43
39
  * those changes are forwarded to the appropriate synchronize method.
44
40
  * Each synchronize method returns an iterator that streams these changes
45
41
  * continually until the user calls `return` on the iterator or `break`s out of the loop,
46
42
  * allowing for live updates without additional polling.
47
43
  *
48
- * Example 1: Suppose a user publishes a post using {@link put}. If the feed
44
+ * Example 1: Suppose a user publishes a post using {@link post}. If the feed
49
45
  * displaying that user's posts is using {@link synchronizeDiscover} to listen for changes,
50
46
  * then the user's new post will instantly appear in their feed, giving the UI a
51
47
  * responsive feel.
@@ -56,26 +52,32 @@ export interface GraffitiSynchronizeOptions {
56
52
  * all instance's of that friend's name in the user's application instantly,
57
53
  * providing a consistent user experience.
58
54
  *
55
+ * Additionally, the library supplies a {@link synchronizeAll} method that can be used
56
+ * to stream all the Graffiti changes that an application is aware of, which can be used
57
+ * for caching or history building.
58
+ *
59
59
  * The source code for this library is [available on GitHub](https://github.com/graffiti-garden/wrapper-synchronize/).
60
60
  *
61
- * @groupDescription Synchronize Methods
61
+ * @groupDescription 0 - Synchronize Methods
62
62
  * This group contains methods that listen for changes made via
63
- * {@link put}, {@link patch}, and {@link delete} or fetched from
64
- * {@link get}, {@link discover}, and {@link recoverOrphans} and then
63
+ * {@link post}, and {@link delete} or fetched from
64
+ * {@link get}, {@link discover}, or {@link continueDiscover} and then
65
65
  * streams appropriate changes to provide a responsive and consistent user experience.
66
66
  */
67
- export declare class GraffitiSynchronize extends Graffiti {
67
+ export declare class GraffitiSynchronize implements Graffiti {
68
68
  protected ajv_: Promise<Ajv> | undefined;
69
- protected applyPatch_: Promise<typeof applyPatch> | undefined;
70
69
  protected readonly graffiti: Graffiti;
71
70
  protected readonly callbacks: Set<GraffitiSynchronizeCallback>;
72
71
  protected readonly options: GraffitiSynchronizeOptions;
73
- channelStats: Graffiti["channelStats"];
74
72
  login: Graffiti["login"];
75
73
  logout: Graffiti["logout"];
76
74
  sessionEvents: Graffiti["sessionEvents"];
75
+ postMedia: Graffiti["postMedia"];
76
+ getMedia: Graffiti["getMedia"];
77
+ deleteMedia: Graffiti["deleteMedia"];
78
+ actorToHandle: Graffiti["actorToHandle"];
79
+ handleToActor: Graffiti["handleToActor"];
77
80
  protected get ajv(): Promise<Ajv>;
78
- protected get applyPatch(): Promise<typeof applyPatch>;
79
81
  /**
80
82
  * Wraps a Graffiti API instance to provide the synchronize methods.
81
83
  * The GraffitiSyncrhonize class rather than the Graffiti class
@@ -87,11 +89,11 @@ export declare class GraffitiSynchronize extends Graffiti {
87
89
  * instance to wrap.
88
90
  */
89
91
  graffiti: Graffiti, options?: GraffitiSynchronizeOptions);
90
- protected synchronize<Schema extends JSONSchema>(matchObject: (object: GraffitiObjectBase) => boolean, channels: string[], schema: Schema, session?: GraffitiSession | null): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>>;
92
+ protected synchronize<Schema extends JSONSchema>(matchObject: (object: GraffitiObjectBase) => boolean, channels: string[], schema: Schema, session?: GraffitiSession | null, seenUrls?: Set<string>): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>>;
91
93
  /**
92
94
  * This method has the same signature as {@link discover} but listens for
93
- * changes made via {@link put}, {@link patch}, and {@link delete} or
94
- * fetched from {@link get}, {@link discover}, and {@link recoverOrphans}
95
+ * changes made via {@link post} and {@link delete} or
96
+ * fetched from {@link get}, {@link discover}, and {@link continueDiscover}
95
97
  * and then streams appropriate changes to provide a responsive and
96
98
  * consistent user experience.
97
99
  *
@@ -99,36 +101,22 @@ export declare class GraffitiSynchronize extends Graffiti {
99
101
  * and will not terminate unless the user calls the `return` method on the iterator
100
102
  * or `break`s out of the loop.
101
103
  *
102
- * @group Synchronize Methods
104
+ * @group 0 - Synchronize Methods
103
105
  */
104
- synchronizeDiscover<Schema extends JSONSchema>(...args: Parameters<typeof Graffiti.prototype.discover<Schema>>): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>>;
106
+ synchronizeDiscover<Schema extends JSONSchema>(channels: string[], schema: Schema, session?: GraffitiSession | null): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>>;
105
107
  /**
106
108
  * This method has the same signature as {@link get} but
107
- * listens for changes made via {@link put}, {@link patch}, and {@link delete} or
108
- * fetched from {@link get}, {@link discover}, and {@link recoverOrphans} and then
109
+ * listens for changes made via {@link post}, and {@link delete} or
110
+ * fetched from {@link get}, {@link discover}, and {@link continueDiscover} and then
109
111
  * streams appropriate changes to provide a responsive and consistent user experience.
110
112
  *
111
113
  * Unlike {@link get}, which returns a single result, this method continuously
112
114
  * listens for changes which are output as an asynchronous stream, similar
113
115
  * to {@link discover}.
114
116
  *
115
- * @group Synchronize Methods
116
- */
117
- synchronizeGet<Schema extends JSONSchema>(...args: Parameters<typeof Graffiti.prototype.get<Schema>>): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>>;
118
- /**
119
- * This method has the same signature as {@link recoverOrphans} but
120
- * listens for changes made via
121
- * {@link put}, {@link patch}, and {@link delete} or fetched from
122
- * {@link get}, {@link discover}, and {@link recoverOrphans} and then
123
- * streams appropriate changes to provide a responsive and consistent user experience.
124
- *
125
- * Unlike {@link recoverOrphans}, this method continuously listens for changes
126
- * and will not terminate unless the user calls the `return` method on the iterator
127
- * or `break`s out of the loop.
128
- *
129
- * @group Synchronize Methods
117
+ * @group 0 - Synchronize Methods
130
118
  */
131
- synchronizeRecoverOrphans<Schema extends JSONSchema>(...args: Parameters<typeof Graffiti.prototype.recoverOrphans<Schema>>): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>>;
119
+ synchronizeGet<Schema extends JSONSchema>(objectUrl: string | GraffitiObjectUrl, schema: Schema, session?: GraffitiSession | null | undefined): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>>;
132
120
  /**
133
121
  * Streams changes made to *any* object in *any* channel
134
122
  * and made by *any* user. You may want to use it in conjuction with
@@ -139,18 +127,16 @@ export declare class GraffitiSynchronize extends Graffiti {
139
127
  * Be careful using this method. Without additional filters it can
140
128
  * expose the user to content out of context.
141
129
  *
142
- * @group Synchronize Methods
130
+ * @group 0 - Synchronize Methods
143
131
  */
144
132
  synchronizeAll<Schema extends JSONSchema>(schema: Schema, session?: GraffitiSession | null): AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>>;
145
- protected synchronizeDispatch(oldObject: GraffitiObjectStreamContinueEntry<{}>, newObject?: GraffitiObjectStreamContinueEntry<{}>, waitForListeners?: boolean): Promise<void>;
133
+ protected synchronizeDispatch(objectUpdate: GraffitiObjectStreamContinueEntry<{}>, waitForListeners?: boolean): Promise<void>;
146
134
  get: Graffiti["get"];
147
- put: Graffiti["put"];
148
- patch: Graffiti["patch"];
135
+ post: Graffiti["post"];
149
136
  delete: Graffiti["delete"];
150
137
  protected objectStreamContinue<Schema extends JSONSchema>(iterator: GraffitiObjectStreamContinue<Schema>): GraffitiObjectStreamContinue<Schema>;
151
138
  protected objectStream<Schema extends JSONSchema>(iterator: GraffitiObjectStream<Schema>): GraffitiObjectStream<Schema>;
152
139
  discover: Graffiti["discover"];
153
- recoverOrphans: Graffiti["recoverOrphans"];
154
- continueObjectStream: Graffiti["continueObjectStream"];
140
+ continueDiscover: Graffiti["continueDiscover"];
155
141
  }
156
142
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graffiti-garden/wrapper-synchronize",
3
- "version": "0.2.4",
3
+ "version": "1.0.3",
4
4
  "description": "Internal synchronization for the Graffiti API",
5
5
  "types": "./dist/index.d.ts",
6
6
  "module": "./dist/esm/index.js",
@@ -45,19 +45,18 @@
45
45
  },
46
46
  "homepage": "https://sync.graffiti.garden/classes/GraffitiSynchronize.html",
47
47
  "devDependencies": {
48
- "@types/node": "^24.7.2",
49
- "@vitest/coverage-v8": "^3.2.4",
48
+ "@graffiti-garden/implementation-local": "^1.0.3",
49
+ "@types/node": "^25.0.3",
50
+ "@vitest/coverage-v8": "^4.0.16",
50
51
  "esbuild-plugin-polyfill-node": "^0.3.0",
51
52
  "tsx": "^4.20.6",
52
53
  "typedoc": "^0.28.14",
53
54
  "typescript": "^5.9.3",
54
- "vitest": "^3.2.4"
55
+ "vitest": "^4.0.16"
55
56
  },
56
57
  "dependencies": {
57
- "@graffiti-garden/api": "^0.6.4",
58
- "@graffiti-garden/implementation-local": "^0.6.4",
58
+ "@graffiti-garden/api": "^1.0.3",
59
59
  "@repeaterjs/repeater": "^3.0.6",
60
- "ajv": "^8.17.1",
61
- "fast-json-patch": "^3.1.1"
60
+ "ajv": "^8.17.1"
62
61
  }
63
62
  }