@graffiti-garden/implementation-local 0.6.4 → 1.0.1

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.
Files changed (67) hide show
  1. package/README.md +0 -1
  2. package/dist/browser/ajv-IY2ZY7VT.js +9 -0
  3. package/dist/browser/ajv-IY2ZY7VT.js.map +7 -0
  4. package/dist/browser/{chunk-KNUPPOQC.js → chunk-GE6AZATH.js} +2 -2
  5. package/dist/browser/{chunk-KNUPPOQC.js.map → chunk-GE6AZATH.js.map} +1 -1
  6. package/dist/browser/{index-browser.es-G37SKL53.js → index-browser.es-UXYPGJ2M.js} +2 -2
  7. package/dist/browser/{index-browser.es-G37SKL53.js.map → index-browser.es-UXYPGJ2M.js.map} +1 -1
  8. package/dist/browser/index.js +11 -2
  9. package/dist/browser/index.js.map +4 -4
  10. package/dist/cjs/identity.js +112 -0
  11. package/dist/cjs/identity.js.map +7 -0
  12. package/dist/cjs/index.js +43 -22
  13. package/dist/cjs/index.js.map +2 -2
  14. package/dist/cjs/media.js +111 -0
  15. package/dist/cjs/media.js.map +7 -0
  16. package/dist/cjs/objects.js +307 -0
  17. package/dist/cjs/objects.js.map +7 -0
  18. package/dist/cjs/tests.spec.js +1 -2
  19. package/dist/cjs/tests.spec.js.map +2 -2
  20. package/dist/cjs/utilities.js +68 -43
  21. package/dist/cjs/utilities.js.map +2 -2
  22. package/dist/esm/identity.js +92 -0
  23. package/dist/esm/identity.js.map +7 -0
  24. package/dist/esm/index.js +43 -24
  25. package/dist/esm/index.js.map +2 -2
  26. package/dist/esm/media.js +91 -0
  27. package/dist/esm/media.js.map +7 -0
  28. package/dist/esm/objects.js +285 -0
  29. package/dist/esm/objects.js.map +7 -0
  30. package/dist/esm/tests.spec.js +2 -4
  31. package/dist/esm/tests.spec.js.map +2 -2
  32. package/dist/esm/utilities.js +69 -48
  33. package/dist/esm/utilities.js.map +2 -2
  34. package/dist/{session-manager.d.ts → identity.d.ts} +7 -5
  35. package/dist/identity.d.ts.map +1 -0
  36. package/dist/index.d.ts +15 -13
  37. package/dist/index.d.ts.map +1 -1
  38. package/dist/media.d.ts +9 -0
  39. package/dist/media.d.ts.map +1 -0
  40. package/dist/objects.d.ts +63 -0
  41. package/dist/objects.d.ts.map +1 -0
  42. package/dist/utilities.d.ts +19 -8
  43. package/dist/utilities.d.ts.map +1 -1
  44. package/package.json +31 -19
  45. package/src/identity.ts +131 -0
  46. package/src/index.ts +44 -29
  47. package/src/media.ts +106 -0
  48. package/src/objects.ts +432 -0
  49. package/src/tests.spec.ts +2 -4
  50. package/src/utilities.ts +67 -87
  51. package/dist/browser/ajv-6AI3HK2A.js +0 -9
  52. package/dist/browser/ajv-6AI3HK2A.js.map +0 -7
  53. package/dist/browser/fast-json-patch-ZE7SZEYK.js +0 -19
  54. package/dist/browser/fast-json-patch-ZE7SZEYK.js.map +0 -7
  55. package/dist/cjs/database.js +0 -626
  56. package/dist/cjs/database.js.map +0 -7
  57. package/dist/cjs/session-manager.js +0 -107
  58. package/dist/cjs/session-manager.js.map +0 -7
  59. package/dist/database.d.ts +0 -106
  60. package/dist/database.d.ts.map +0 -1
  61. package/dist/esm/database.js +0 -608
  62. package/dist/esm/database.js.map +0 -7
  63. package/dist/esm/session-manager.js +0 -87
  64. package/dist/esm/session-manager.js.map +0 -7
  65. package/dist/session-manager.d.ts.map +0 -1
  66. package/src/database.ts +0 -921
  67. package/src/session-manager.ts +0 -123
@@ -1,626 +0,0 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
- var database_exports = {};
30
- __export(database_exports, {
31
- GraffitiLocalDatabase: () => GraffitiLocalDatabase
32
- });
33
- module.exports = __toCommonJS(database_exports);
34
- var import_api = require("@graffiti-garden/api");
35
- var import_utilities = require("./utilities.js");
36
- const DEFAULT_ORIGIN = "graffiti:local:";
37
- const LAST_MODIFIED_BUFFER = 6e4;
38
- class GraffitiLocalDatabase {
39
- db_;
40
- applyPatch_;
41
- ajv_;
42
- options;
43
- origin;
44
- get db() {
45
- if (!this.db_) {
46
- this.db_ = (async () => {
47
- const { default: PouchDB } = await import("pouchdb");
48
- const pouchDbOptions = {
49
- name: "graffitiDb",
50
- ...this.options.pouchDBOptions
51
- };
52
- const db = new PouchDB(
53
- pouchDbOptions.name,
54
- pouchDbOptions
55
- );
56
- await db.put({
57
- _id: "_design/indexes",
58
- views: {
59
- objectsPerChannelAndLastModified: {
60
- map: function(object) {
61
- const paddedLastModified = object.lastModified.toString().padStart(15, "0");
62
- object.channels.forEach(function(channel) {
63
- const id = encodeURIComponent(channel) + "/" + paddedLastModified;
64
- emit(id);
65
- });
66
- }.toString()
67
- },
68
- orphansPerActorAndLastModified: {
69
- map: function(object) {
70
- if (object.channels.length === 0) {
71
- const paddedLastModified = object.lastModified.toString().padStart(15, "0");
72
- const id = encodeURIComponent(object.actor) + "/" + paddedLastModified;
73
- emit(id);
74
- }
75
- }.toString()
76
- },
77
- channelStatsPerActor: {
78
- map: function(object) {
79
- if (object.tombstone) return;
80
- object.channels.forEach(function(channel) {
81
- const id = encodeURIComponent(object.actor) + "/" + encodeURIComponent(channel);
82
- emit(id, object.lastModified);
83
- });
84
- }.toString(),
85
- reduce: "_stats"
86
- }
87
- }
88
- }).catch((error) => {
89
- if (error && typeof error === "object" && "name" in error && error.name === "conflict") {
90
- return;
91
- } else {
92
- throw error;
93
- }
94
- });
95
- return db;
96
- })();
97
- }
98
- return this.db_;
99
- }
100
- get applyPatch() {
101
- if (!this.applyPatch_) {
102
- this.applyPatch_ = (async () => {
103
- const imported = await import("fast-json-patch");
104
- return imported.applyPatch || imported.default.applyPatch;
105
- })();
106
- }
107
- return this.applyPatch_;
108
- }
109
- get ajv() {
110
- if (!this.ajv_) {
111
- this.ajv_ = this.options.ajv ? Promise.resolve(this.options.ajv) : (async () => {
112
- const { default: Ajv } = await import("ajv");
113
- return new Ajv({ strict: false });
114
- })();
115
- }
116
- return this.ajv_;
117
- }
118
- extractGraffitiObject(object) {
119
- const { value, channels, allowed, url, actor, lastModified } = object;
120
- return {
121
- value,
122
- channels,
123
- allowed,
124
- url,
125
- actor,
126
- lastModified
127
- };
128
- }
129
- constructor(options) {
130
- this.options = options ?? {};
131
- this.origin = this.options.origin ?? DEFAULT_ORIGIN;
132
- if (!this.origin.endsWith(":") && !this.origin.endsWith("/")) {
133
- this.origin += "/";
134
- }
135
- }
136
- async allDocsAtLocation(objectUrl) {
137
- const url = (0, import_utilities.unpackObjectUrl)(objectUrl) + "/";
138
- const results = await (await this.db).allDocs({
139
- startkey: url,
140
- endkey: url + "\uFFFF",
141
- // \uffff is the last unicode character
142
- include_docs: true
143
- });
144
- const docs = results.rows.map((row) => row.doc).reduce((acc, doc) => {
145
- if (doc) acc.push(doc);
146
- return acc;
147
- }, []);
148
- return docs;
149
- }
150
- docId(objectUrl) {
151
- return objectUrl.url + "/" + (0, import_utilities.randomBase64)();
152
- }
153
- get = async (...args) => {
154
- const [urlObject, schema, session] = args;
155
- const docsAll = await this.allDocsAtLocation(urlObject);
156
- const docs = docsAll.filter(
157
- (doc2) => (0, import_utilities.isActorAllowedGraffitiObject)(doc2, session)
158
- );
159
- if (!docs.length)
160
- throw new import_api.GraffitiErrorNotFound(
161
- "The object you are trying to get either does not exist or you are not allowed to see it"
162
- );
163
- const doc = docs.reduce(
164
- (a, b) => a.lastModified > b.lastModified || a.lastModified === b.lastModified && !a.tombstone && b.tombstone ? a : b
165
- );
166
- if (doc.tombstone) {
167
- throw new import_api.GraffitiErrorNotFound(
168
- "The object you are trying to get either does not exist or you are not allowed to see it"
169
- );
170
- }
171
- const object = this.extractGraffitiObject(doc);
172
- (0, import_utilities.maskGraffitiObject)(object, [], session);
173
- const validate = (0, import_utilities.compileGraffitiObjectSchema)(await this.ajv, schema);
174
- if (!validate(object)) {
175
- throw new import_api.GraffitiErrorSchemaMismatch();
176
- }
177
- return object;
178
- };
179
- /**
180
- * Deletes all docs at a particular location.
181
- * If the `keepLatest` flag is set to true,
182
- * the doc with the most recent timestamp will be
183
- * spared. If there are multiple docs with the same
184
- * timestamp, the one with the highest `_id` will be
185
- * spared.
186
- */
187
- async deleteAtLocation(url, options = {
188
- keepLatest: false
189
- }) {
190
- const docsAtLocationAll = await this.allDocsAtLocation(url);
191
- const docsAtLocationAllowed = options.session ? docsAtLocationAll.filter(
192
- (doc) => (0, import_utilities.isActorAllowedGraffitiObject)(doc, options.session)
193
- ) : docsAtLocationAll;
194
- if (!docsAtLocationAllowed.length) {
195
- throw new import_api.GraffitiErrorNotFound(
196
- "The object you are trying to delete either does not exist or you are not allowed to see it"
197
- );
198
- } else if (options.session && docsAtLocationAllowed.some((doc) => doc.actor !== options.session?.actor)) {
199
- throw new import_api.GraffitiErrorForbidden(
200
- "You cannot delete an object owned by another actor"
201
- );
202
- }
203
- const docsAtLocation = docsAtLocationAllowed.filter(
204
- (doc) => !doc.tombstone
205
- );
206
- if (!docsAtLocation.length) return void 0;
207
- const latestModified = docsAtLocation.map((doc) => doc.lastModified).reduce((a, b) => a > b ? a : b);
208
- const docsToDelete = docsAtLocation.filter(
209
- (doc) => !options.keepLatest || doc.lastModified < latestModified
210
- );
211
- const concurrentDocsAll = docsAtLocation.filter(
212
- (doc) => options.keepLatest && doc.lastModified === latestModified
213
- );
214
- if (concurrentDocsAll.length) {
215
- const keepDocId = concurrentDocsAll.map((doc) => doc._id).reduce((a, b) => a > b ? a : b);
216
- const concurrentDocsToDelete = concurrentDocsAll.filter(
217
- (doc) => doc._id !== keepDocId
218
- );
219
- docsToDelete.push(...concurrentDocsToDelete);
220
- }
221
- const lastModified = options.keepLatest ? latestModified : (/* @__PURE__ */ new Date()).getTime();
222
- const deleteResults = await (await this.db).bulkDocs(
223
- docsToDelete.map((doc) => ({
224
- ...doc,
225
- tombstone: true,
226
- lastModified
227
- }))
228
- );
229
- let deletedObject = void 0;
230
- for (const resultOrError of deleteResults) {
231
- if ("ok" in resultOrError) {
232
- const { id } = resultOrError;
233
- const deletedDoc = docsToDelete.find((doc) => doc._id === id);
234
- if (deletedDoc) {
235
- deletedObject = {
236
- ...this.extractGraffitiObject(deletedDoc),
237
- lastModified
238
- };
239
- break;
240
- }
241
- }
242
- }
243
- return deletedObject;
244
- }
245
- delete = async (...args) => {
246
- const [url, session] = args;
247
- const deletedObject = await this.deleteAtLocation(url, {
248
- session
249
- });
250
- if (!deletedObject) {
251
- throw new import_api.GraffitiErrorNotFound("The object has already been deleted");
252
- }
253
- return deletedObject;
254
- };
255
- put = async (...args) => {
256
- const [objectPartial, session] = args;
257
- if (objectPartial.actor && objectPartial.actor !== session.actor) {
258
- throw new import_api.GraffitiErrorForbidden(
259
- "Cannot put an object with a different actor than the session actor"
260
- );
261
- }
262
- if (objectPartial.url) {
263
- let oldObject;
264
- try {
265
- oldObject = await this.get(objectPartial.url, {}, session);
266
- } catch (e) {
267
- if (e instanceof import_api.GraffitiErrorNotFound) {
268
- if (!this.options.allowSettingArbitraryUrls) {
269
- throw new import_api.GraffitiErrorNotFound(
270
- "The object you are trying to replace does not exist or you are not allowed to see it"
271
- );
272
- }
273
- } else {
274
- throw e;
275
- }
276
- }
277
- if (oldObject?.actor !== session.actor) {
278
- throw new import_api.GraffitiErrorForbidden(
279
- "The object you are trying to replace is owned by another actor"
280
- );
281
- }
282
- }
283
- const lastModified = (this.options.allowSettinngLastModified ?? false) && objectPartial.lastModified || (/* @__PURE__ */ new Date()).getTime();
284
- const object = {
285
- value: objectPartial.value,
286
- channels: objectPartial.channels,
287
- allowed: objectPartial.allowed,
288
- url: objectPartial.url ?? this.origin + (0, import_utilities.randomBase64)(),
289
- actor: session.actor,
290
- tombstone: false,
291
- lastModified
292
- };
293
- await (await this.db).put({
294
- _id: this.docId(object),
295
- ...object
296
- });
297
- const previousObject = await this.deleteAtLocation(object, {
298
- keepLatest: true
299
- });
300
- if (previousObject) {
301
- return previousObject;
302
- } else {
303
- return {
304
- ...object,
305
- value: {},
306
- channels: [],
307
- allowed: [],
308
- tombstone: true
309
- };
310
- }
311
- };
312
- patch = async (...args) => {
313
- const [patch, url, session] = args;
314
- let originalObject;
315
- try {
316
- originalObject = await this.get(url, {}, session);
317
- } catch (e) {
318
- if (e instanceof import_api.GraffitiErrorNotFound) {
319
- throw new import_api.GraffitiErrorNotFound(
320
- "The object you are trying to patch does not exist or you are not allowed to see it"
321
- );
322
- } else {
323
- throw e;
324
- }
325
- }
326
- if (originalObject.actor !== session.actor) {
327
- throw new import_api.GraffitiErrorForbidden(
328
- "The object you are trying to patch is owned by another actor"
329
- );
330
- }
331
- const patchObject = { ...originalObject };
332
- for (const prop of ["value", "channels", "allowed"]) {
333
- (0, import_utilities.applyGraffitiPatch)(await this.applyPatch, prop, patch, patchObject);
334
- }
335
- if (typeof patchObject.value !== "object" || Array.isArray(patchObject.value) || !patchObject.value) {
336
- throw new import_api.GraffitiErrorPatchError("value is no longer an object");
337
- }
338
- if (!Array.isArray(patchObject.channels) || !patchObject.channels.every((channel) => typeof channel === "string")) {
339
- throw new import_api.GraffitiErrorPatchError(
340
- "channels are no longer an array of strings"
341
- );
342
- }
343
- if (patchObject.allowed && (!Array.isArray(patchObject.allowed) || !patchObject.allowed.every((allowed) => typeof allowed === "string"))) {
344
- throw new import_api.GraffitiErrorPatchError(
345
- "allowed list is not an array of strings"
346
- );
347
- }
348
- patchObject.lastModified = (/* @__PURE__ */ new Date()).getTime();
349
- await (await this.db).put({
350
- ...patchObject,
351
- tombstone: false,
352
- _id: this.docId(patchObject)
353
- });
354
- await this.deleteAtLocation(patchObject, {
355
- keepLatest: true
356
- });
357
- return {
358
- ...originalObject,
359
- lastModified: patchObject.lastModified
360
- };
361
- };
362
- queryLastModifiedSuffixes(schema, lastModified) {
363
- let startKeySuffix = "";
364
- let endKeySuffix = "\uFFFF";
365
- if (typeof schema === "object" && schema.properties?.lastModified && typeof schema.properties.lastModified === "object") {
366
- const lastModifiedSchema = schema.properties.lastModified;
367
- const minimum = lastModified && lastModifiedSchema.minimum ? Math.max(lastModified, lastModifiedSchema.minimum) : lastModified ?? lastModifiedSchema.minimum;
368
- const exclusiveMinimum = lastModifiedSchema.exclusiveMinimum;
369
- let intMinimum;
370
- if (exclusiveMinimum !== void 0) {
371
- intMinimum = Math.ceil(exclusiveMinimum);
372
- intMinimum === exclusiveMinimum && intMinimum++;
373
- } else if (minimum !== void 0) {
374
- intMinimum = Math.ceil(minimum);
375
- }
376
- if (intMinimum !== void 0) {
377
- startKeySuffix = intMinimum.toString().padStart(15, "0");
378
- }
379
- const maximum = lastModifiedSchema.maximum;
380
- const exclusiveMaximum = lastModifiedSchema.exclusiveMaximum;
381
- let intMaximum;
382
- if (exclusiveMaximum !== void 0) {
383
- intMaximum = Math.floor(exclusiveMaximum);
384
- intMaximum === exclusiveMaximum && intMaximum--;
385
- } else if (maximum !== void 0) {
386
- intMaximum = Math.floor(maximum);
387
- }
388
- if (intMaximum !== void 0) {
389
- endKeySuffix = intMaximum.toString().padStart(15, "0");
390
- }
391
- }
392
- return {
393
- startKeySuffix,
394
- endKeySuffix
395
- };
396
- }
397
- async *streamObjects(index, startkey, endkey, validate, session, ifModifiedSince, channels, processedIds) {
398
- if (ifModifiedSince !== void 0) {
399
- ifModifiedSince -= LAST_MODIFIED_BUFFER;
400
- }
401
- const result = await (await this.db).query(index, {
402
- startkey,
403
- endkey,
404
- include_docs: true
405
- });
406
- for (const row of result.rows) {
407
- const doc = row.doc;
408
- if (!doc) continue;
409
- if (processedIds?.has(doc._id)) continue;
410
- processedIds?.add(doc._id);
411
- if (ifModifiedSince === void 0 && doc.tombstone) continue;
412
- const object = this.extractGraffitiObject(doc);
413
- if (channels) {
414
- if (!(0, import_utilities.isActorAllowedGraffitiObject)(object, session)) continue;
415
- (0, import_utilities.maskGraffitiObject)(object, channels, session);
416
- }
417
- if (!validate(object)) continue;
418
- yield doc.tombstone ? {
419
- tombstone: true,
420
- object: {
421
- url: object.url,
422
- lastModified: object.lastModified
423
- }
424
- } : { object };
425
- }
426
- }
427
- async waitToContinue(ifModifiedSince) {
428
- if (ifModifiedSince === void 0) return;
429
- const continueBuffer = this.options.continueBuffer ?? 1e3;
430
- const timeElapsedSinceContinue = Date.now() - ifModifiedSince;
431
- if (timeElapsedSinceContinue < continueBuffer) {
432
- await new Promise(
433
- (resolve) => setTimeout(resolve, continueBuffer - timeElapsedSinceContinue)
434
- );
435
- }
436
- }
437
- async *discoverMeta(args, ifModifiedSince) {
438
- await this.waitToContinue(ifModifiedSince);
439
- const [channels, schema, session] = args;
440
- const validate = (0, import_utilities.compileGraffitiObjectSchema)(await this.ajv, schema);
441
- const { startKeySuffix, endKeySuffix } = this.queryLastModifiedSuffixes(
442
- schema,
443
- ifModifiedSince
444
- );
445
- const processedIds = /* @__PURE__ */ new Set();
446
- const startTime = (/* @__PURE__ */ new Date()).getTime();
447
- for (const channel of channels) {
448
- const keyPrefix = encodeURIComponent(channel) + "/";
449
- const startkey = keyPrefix + startKeySuffix;
450
- const endkey = keyPrefix + endKeySuffix;
451
- const iterator = this.streamObjects(
452
- "indexes/objectsPerChannelAndLastModified",
453
- startkey,
454
- endkey,
455
- validate,
456
- session,
457
- ifModifiedSince,
458
- channels,
459
- processedIds
460
- );
461
- for await (const result of iterator) yield result;
462
- }
463
- return startTime;
464
- }
465
- async *recoverOrphansMeta(args, ifModifiedSince) {
466
- await this.waitToContinue(ifModifiedSince);
467
- const [schema, session] = args;
468
- const { startKeySuffix, endKeySuffix } = this.queryLastModifiedSuffixes(
469
- schema,
470
- ifModifiedSince
471
- );
472
- const keyPrefix = encodeURIComponent(session.actor) + "/";
473
- const startkey = keyPrefix + startKeySuffix;
474
- const endkey = keyPrefix + endKeySuffix;
475
- const validate = (0, import_utilities.compileGraffitiObjectSchema)(await this.ajv, schema);
476
- const startTime = (/* @__PURE__ */ new Date()).getTime();
477
- const iterator = this.streamObjects(
478
- "indexes/orphansPerActorAndLastModified",
479
- startkey,
480
- endkey,
481
- validate,
482
- session,
483
- ifModifiedSince
484
- );
485
- for await (const result of iterator) yield result;
486
- return startTime;
487
- }
488
- discoverCursor(args, ifModifiedSince) {
489
- return "discover:" + JSON.stringify({
490
- channels: args[0],
491
- schema: args[1],
492
- actor: args[2]?.actor,
493
- ifModifiedSince
494
- });
495
- }
496
- async *discoverContinue(args, ifModifiedSince) {
497
- const iterator = this.discoverMeta(args, ifModifiedSince);
498
- while (true) {
499
- const result = await iterator.next();
500
- if (result.done) {
501
- const ifModifiedSince2 = result.value;
502
- return {
503
- continue: () => this.discoverContinue(args, ifModifiedSince2),
504
- cursor: this.discoverCursor(args, ifModifiedSince2)
505
- };
506
- }
507
- yield result.value;
508
- }
509
- }
510
- discover = (...args) => {
511
- const iterator = this.discoverMeta(args);
512
- const this_ = this;
513
- return async function* () {
514
- while (true) {
515
- const result = await iterator.next();
516
- if (result.done) {
517
- return {
518
- continue: () => this_.discoverContinue(args, result.value),
519
- cursor: this_.discoverCursor(args, result.value)
520
- };
521
- }
522
- if (result.value.tombstone) continue;
523
- yield result.value;
524
- }
525
- }();
526
- };
527
- recoverOrphansCursor(args, ifModifiedSince) {
528
- return "orphans:" + JSON.stringify({
529
- schema: args[0],
530
- actor: args[1]?.actor,
531
- ifModifiedSince
532
- });
533
- }
534
- async *recoverOrphansContinue(args, ifModifiedSince) {
535
- const iterator = this.recoverOrphansMeta(args, ifModifiedSince);
536
- while (true) {
537
- const result = await iterator.next();
538
- if (result.done) {
539
- const ifModifiedSince2 = result.value;
540
- return {
541
- continue: () => this.recoverOrphansContinue(args, ifModifiedSince2),
542
- cursor: this.recoverOrphansCursor(args, ifModifiedSince2)
543
- };
544
- }
545
- yield result.value;
546
- }
547
- }
548
- recoverOrphans = (...args) => {
549
- const iterator = this.recoverOrphansMeta(args);
550
- const this_ = this;
551
- return async function* () {
552
- while (true) {
553
- const result = await iterator.next();
554
- if (result.done) {
555
- return {
556
- continue: () => this_.recoverOrphansContinue(
557
- args,
558
- result.value
559
- ),
560
- cursor: this_.recoverOrphansCursor(args, result.value)
561
- };
562
- }
563
- if (result.value.tombstone) continue;
564
- yield result.value;
565
- }
566
- }();
567
- };
568
- channelStats = (session) => {
569
- const this_ = this;
570
- return async function* () {
571
- const keyPrefix = encodeURIComponent(session.actor) + "/";
572
- const result = await (await this_.db).query("indexes/channelStatsPerActor", {
573
- startkey: keyPrefix,
574
- endkey: keyPrefix + "\uFFFF",
575
- reduce: true,
576
- group: true
577
- });
578
- for (const row of result.rows) {
579
- const channelEncoded = row.key.split("/")[1];
580
- if (typeof channelEncoded !== "string") continue;
581
- const { count, max: lastModified } = row.value;
582
- if (typeof count !== "number" || typeof lastModified !== "number")
583
- continue;
584
- yield {
585
- value: {
586
- channel: decodeURIComponent(channelEncoded),
587
- count,
588
- lastModified
589
- }
590
- };
591
- }
592
- }();
593
- };
594
- continueObjectStream = (cursor, session) => {
595
- if (cursor.startsWith("discover:")) {
596
- const { channels, schema, actor, ifModifiedSince } = JSON.parse(
597
- cursor.slice("discover:".length)
598
- );
599
- if (actor && actor !== session?.actor) {
600
- throw new import_api.GraffitiErrorForbidden(
601
- "Cannot continue a cursor for another actor"
602
- );
603
- }
604
- return this.discoverContinue(
605
- [channels, schema, session],
606
- ifModifiedSince
607
- );
608
- } else if (cursor.startsWith("orphans:")) {
609
- const { schema, actor, ifModifiedSince } = JSON.parse(
610
- cursor.slice("orphans:".length)
611
- );
612
- if (!session || actor !== session?.actor) {
613
- throw new import_api.GraffitiErrorForbidden(
614
- "Cannot continue a cursor for another actor"
615
- );
616
- }
617
- return this.recoverOrphansContinue(
618
- [schema, session],
619
- ifModifiedSince
620
- );
621
- } else {
622
- throw new import_api.GraffitiErrorNotFound("Cursor not found");
623
- }
624
- };
625
- }
626
- //# sourceMappingURL=database.js.map