@graffiti-garden/wrapper-vue 0.6.3 → 0.6.5

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.
@@ -18,6 +18,7 @@ function makeComposable<Schema extends JSONSchema>(
18
18
  GraffitiObjectStreamContinueEntry<Schema>
19
19
  >,
20
20
  toWatch: readonly (() => any)[],
21
+ autopoll: MaybeRefOrGetter<boolean>,
21
22
  ) {
22
23
  let synchronizeIterator:
23
24
  | AsyncGenerator<GraffitiObjectStreamContinueEntry<Schema>>
@@ -33,9 +34,21 @@ function makeComposable<Schema extends JSONSchema>(
33
34
  }
34
35
  }
35
36
 
36
- const poll = () => poller.poll(reducer.onEntry.bind(reducer));
37
+ let isAlreadyPolling = false;
38
+ const poll = async () => {
39
+ if (isAlreadyPolling) return;
40
+ isAlreadyPolling = true;
41
+ try {
42
+ await poller.poll(reducer.onEntry.bind(reducer));
43
+ } finally {
44
+ isAlreadyPolling = false;
45
+ if (toValue(autopoll)) {
46
+ poll();
47
+ }
48
+ }
49
+ };
37
50
 
38
- const isPolling = ref(false);
51
+ const isInitialPolling = ref(false);
39
52
  watch(
40
53
  toWatch,
41
54
  async (newValue, oldValue) => {
@@ -50,11 +63,11 @@ function makeComposable<Schema extends JSONSchema>(
50
63
 
51
64
  pollSynchronize();
52
65
 
53
- isPolling.value = true;
66
+ isInitialPolling.value = true;
54
67
  try {
55
68
  await poll();
56
69
  } finally {
57
- isPolling.value = false;
70
+ isInitialPolling.value = false;
58
71
  }
59
72
  },
60
73
  {
@@ -63,7 +76,7 @@ function makeComposable<Schema extends JSONSchema>(
63
76
  );
64
77
  onScopeDispose(() => synchronizeIterator?.return(null));
65
78
 
66
- return { poll, isPolling };
79
+ return { poll, isInitialPolling };
67
80
  }
68
81
 
69
82
  function toSessionGetter(
@@ -112,10 +125,13 @@ export function useGraffitiDiscover<Schema extends JSONSchema>(
112
125
  * will be used. Otherwise, the provided value will be used.
113
126
  */
114
127
  session?: MaybeRefOrGetter<GraffitiSession | undefined | null>,
128
+ autopoll: MaybeRefOrGetter<boolean> = false,
115
129
  ): {
116
130
  results: Ref<GraffitiObject<Schema>[]>;
131
+ objects: Ref<GraffitiObject<Schema>[]>;
117
132
  poll: () => Promise<void>;
118
133
  isPolling: Ref<boolean>;
134
+ isInitialPolling: Ref<boolean>;
119
135
  } {
120
136
  const graffiti = useGraffitiSynchronize();
121
137
  const sessionInjected = useGraffitiSession();
@@ -132,29 +148,52 @@ export function useGraffitiDiscover<Schema extends JSONSchema>(
132
148
  const reducer = new ArrayReducer<Schema>(graffiti);
133
149
  const poller = new StreamPoller<Schema>(streamFactory);
134
150
 
135
- const { poll, isPolling } = makeComposable<Schema>(
151
+ const { poll, isInitialPolling } = makeComposable<Schema>(
136
152
  reducer,
137
153
  poller,
138
154
  synchronizeFactory,
139
155
  argGetters,
156
+ autopoll,
140
157
  );
141
158
 
142
159
  return {
143
160
  /**
144
- * A [Ref](https://vuejs.org/api/reactivity-core.html#ref) that contains
145
- * an array of Graffiti objects. All tombstoned objects have been filtered out.
161
+ * A [ref](https://vuejs.org/api/reactivity-core.html#ref) that contains
162
+ * an array of Graffiti objects.
146
163
  */
147
- results: reducer.results,
164
+ objects: reducer.results,
165
+ /**
166
+ * @deprecated Use {@link objects} instead.
167
+ */
168
+ get results() {
169
+ console.warn(
170
+ "The `results` property is deprecated. Use `objects` instead.",
171
+ );
172
+ return this.objects;
173
+ },
148
174
  /**
149
175
  * A method to poll for new results.
150
176
  */
151
177
  poll,
152
178
  /**
153
- * A boolean [Ref](https://vuejs.org/api/reactivity-core.html#ref)
154
- * that indicates if the poll is currently running.
179
+ * A boolean [ref](https://vuejs.org/api/reactivity-core.html#ref)
180
+ * that indicates if the *first* poll is currently running.
155
181
  * Useful to show a loading spinner or disable a button.
182
+ *
183
+ * This also tracks new polls when the arguments have changed,
184
+ * but it does not track ongoing polls from either calling
185
+ * {@link poll} or using the `autopoll` argument.
156
186
  */
157
- isPolling,
187
+ isInitialPolling,
188
+ /**
189
+ * @deprecated Use {@link isInitialPolling} instead.
190
+ */
191
+ get isPolling() {
192
+ console.warn(
193
+ "The `isPolling` property is deprecated. Use `isInitialPolling` instead.",
194
+ );
195
+ return this.isInitialPolling;
196
+ },
158
197
  };
159
198
  }
160
199
 
@@ -182,10 +221,13 @@ export function useGraffitiGet<Schema extends JSONSchema>(
182
221
  * will be used. Otherwise, the provided value will be used.
183
222
  */
184
223
  session?: MaybeRefOrGetter<GraffitiSession | undefined | null>,
224
+ autopoll: MaybeRefOrGetter<boolean> = false,
185
225
  ): {
186
226
  result: Ref<GraffitiObject<Schema> | null | undefined>;
227
+ object: Ref<GraffitiObject<Schema> | null | undefined>;
187
228
  poll: () => Promise<void>;
188
229
  isPolling: Ref<boolean>;
230
+ isInitialPolling: Ref<boolean>;
189
231
  } {
190
232
  const graffiti = useGraffitiSynchronize();
191
233
  const sessionInjected = useGraffitiSession();
@@ -206,30 +248,53 @@ export function useGraffitiGet<Schema extends JSONSchema>(
206
248
  const getter = () => graffiti.get<Schema>(...callGetters(argGetters));
207
249
  const poller = new GetPoller<Schema>(getter);
208
250
 
209
- const { poll, isPolling } = makeComposable<Schema>(
251
+ const { poll, isInitialPolling } = makeComposable<Schema>(
210
252
  reducer,
211
253
  poller,
212
254
  synchronizeFactory,
213
255
  argGetters,
256
+ autopoll,
214
257
  );
215
258
 
216
259
  return {
217
260
  /**
218
- * A [Ref](https://vuejs.org/api/reactivity-core.html#ref) that contains
261
+ * A [ref](https://vuejs.org/api/reactivity-core.html#ref) that contains
219
262
  * the retrieved Graffiti object, if it exists. If the object has been deleted,
220
263
  * the result is `null`. If the object is still being fetched, the result is `undefined`.
221
264
  */
222
- result: reducer.result,
265
+ object: reducer.result,
266
+ /**
267
+ * @deprecated Use {@link object} instead.
268
+ */
269
+ get result() {
270
+ console.warn(
271
+ "The `result` property is deprecated. Use `object` instead.",
272
+ );
273
+ return this.object;
274
+ },
223
275
  /**
224
276
  * A method to poll for new results.
225
277
  */
226
278
  poll,
227
279
  /**
228
- * A boolean [Ref](https://vuejs.org/api/reactivity-core.html#ref)
229
- * that indicates if the poll is currently running.
280
+ * A boolean [ref](https://vuejs.org/api/reactivity-core.html#ref)
281
+ * that indicates if the *first* poll is currently running.
230
282
  * Useful to show a loading spinner or disable a button.
283
+ *
284
+ * This also tracks new polls when the arguments have changed,
285
+ * but it does not track ongoing polls from either calling
286
+ * {@link poll} or using the `autopoll` argument.
287
+ */
288
+ isInitialPolling,
289
+ /**
290
+ * @deprecated Use {@link isInitialPolling} instead.
231
291
  */
232
- isPolling,
292
+ get isPolling() {
293
+ console.warn(
294
+ "The `isPolling` property is deprecated. Use `isInitialPolling` instead.",
295
+ );
296
+ return this.isInitialPolling;
297
+ },
233
298
  };
234
299
  }
235
300
 
@@ -251,10 +316,13 @@ export function useGraffitiGet<Schema extends JSONSchema>(
251
316
  export function useGraffitiRecoverOrphans<Schema extends JSONSchema>(
252
317
  schema: MaybeRefOrGetter<Schema>,
253
318
  session: MaybeRefOrGetter<GraffitiSession>,
319
+ autopoll: MaybeRefOrGetter<boolean> = false,
254
320
  ): {
321
+ objects: Ref<GraffitiObject<Schema>[]>;
255
322
  results: Ref<GraffitiObject<Schema>[]>;
256
323
  poll: () => Promise<void>;
257
324
  isPolling: Ref<boolean>;
325
+ isInitialPolling: Ref<boolean>;
258
326
  } {
259
327
  const graffiti = useGraffitiSynchronize();
260
328
 
@@ -270,28 +338,51 @@ export function useGraffitiRecoverOrphans<Schema extends JSONSchema>(
270
338
  graffiti.recoverOrphans<Schema>(...callGetters(argGetters));
271
339
  const poller = new StreamPoller<Schema>(streamFactory);
272
340
 
273
- const { poll, isPolling } = makeComposable<Schema>(
341
+ const { poll, isInitialPolling } = makeComposable<Schema>(
274
342
  reducer,
275
343
  poller,
276
344
  synchronizeFactory,
277
345
  argGetters,
346
+ autopoll,
278
347
  );
279
348
 
280
349
  return {
281
350
  /**
282
- * A [Ref](https://vuejs.org/api/reactivity-core.html#ref) that contains
283
- * an array of Graffiti objects. All tombstoned objects have been filtered out.
351
+ * A [ref](https://vuejs.org/api/reactivity-core.html#ref) that contains
352
+ * an array of Graffiti objects.
353
+ */
354
+ objects: reducer.results,
355
+ /**
356
+ * @deprecated Use {@link objects} instead.
284
357
  */
285
- results: reducer.results,
358
+ get results() {
359
+ console.warn(
360
+ "The `result` property is deprecated. Use `object` instead.",
361
+ );
362
+ return this.objects;
363
+ },
286
364
  /**
287
365
  * A method to poll for new results.
288
366
  */
289
367
  poll,
290
368
  /**
291
- * A boolean [Ref](https://vuejs.org/api/reactivity-core.html#ref)
292
- * that indicates if the poll is currently running.
369
+ * A boolean [ref](https://vuejs.org/api/reactivity-core.html#ref)
370
+ * that indicates if the *first* poll is currently running.
293
371
  * Useful to show a loading spinner or disable a button.
372
+ *
373
+ * This also tracks new polls when the arguments have changed,
374
+ * but it does not track ongoing polls from either calling
375
+ * {@link poll} or using the `autopoll` argument.
294
376
  */
295
- isPolling,
377
+ isInitialPolling,
378
+ /**
379
+ * @deprecated Use {@link isInitialPolling} instead.
380
+ */
381
+ get isPolling() {
382
+ console.warn(
383
+ "The `isPolling` property is deprecated. Use `isInitialPolling` instead.",
384
+ );
385
+ return this.isInitialPolling;
386
+ },
296
387
  };
297
388
  }
package/src/pollers.ts CHANGED
@@ -11,7 +11,9 @@ import {
11
11
 
12
12
  export abstract class Poller<Schema extends JSONSchema> {
13
13
  abstract poll(
14
- onEntry: (entry: GraffitiObjectStreamContinueEntry<Schema> | null) => void,
14
+ onEntry: (
15
+ entry: GraffitiObjectStreamContinueEntry<Schema> | null | "clear",
16
+ ) => void,
15
17
  ): Promise<void>;
16
18
  abstract clear(): void;
17
19
  }
@@ -67,7 +69,17 @@ export class StreamPoller<Schema extends JSONSchema> implements Poller<Schema> {
67
69
  poll: Poller<Schema>["poll"] = async (onEntry) => {
68
70
  if (!this.iterator) {
69
71
  if (this.continue) {
70
- this.iterator = this.continue();
72
+ try {
73
+ this.iterator = this.continue();
74
+ } catch (e) {
75
+ // The cursor has expired, we need to start from scratch.
76
+ if (e instanceof GraffitiErrorNotFound) {
77
+ onEntry("clear");
78
+ this.iterator = this.streamFactory();
79
+ } else {
80
+ throw e;
81
+ }
82
+ }
71
83
  } else {
72
84
  this.iterator = this.streamFactory();
73
85
  }
package/src/reducers.ts CHANGED
@@ -10,7 +10,7 @@ import type {
10
10
  export abstract class Reducer<Schema extends JSONSchema> {
11
11
  abstract clear(): void;
12
12
  abstract onEntry(
13
- entry: GraffitiObjectStreamContinueEntry<Schema> | null,
13
+ entry: GraffitiObjectStreamContinueEntry<Schema> | null | "clear",
14
14
  ): void;
15
15
  }
16
16
 
@@ -54,7 +54,11 @@ export class SingletonReducer<Schema extends JSONSchema>
54
54
  this.entry.value = undefined;
55
55
  }
56
56
 
57
- onEntry(entry: GraffitiObjectStreamContinueEntry<Schema> | null) {
57
+ onEntry(entry: GraffitiObjectStreamContinueEntry<Schema> | null | "clear") {
58
+ if (entry === "clear") {
59
+ this.clear();
60
+ return;
61
+ }
58
62
  if (!entry || isEntryNewer<Schema>(entry, this.entry.value)) {
59
63
  this.entry.value = entry;
60
64
  }
@@ -96,8 +100,12 @@ export class ArrayReducer<Schema extends JSONSchema>
96
100
  }, []);
97
101
  }
98
102
 
99
- onEntry(entry: GraffitiObjectStreamContinueEntry<Schema> | null) {
103
+ onEntry(entry: GraffitiObjectStreamContinueEntry<Schema> | null | "clear") {
100
104
  if (!entry) return;
105
+ if (entry === "clear") {
106
+ this.clear();
107
+ return;
108
+ }
101
109
  const existing = this.resultsRaw.get(entry.object.url);
102
110
  if (!isEntryNewer<Schema>(entry, existing)) return;
103
111
  this.resultsRaw.set(entry.object.url, entry);