@graffiti-garden/implementation-local 0.6.3 → 1.0.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/README.md +0 -1
- package/dist/browser/ajv-IY2ZY7VT.js +9 -0
- package/dist/browser/ajv-IY2ZY7VT.js.map +7 -0
- package/dist/browser/{chunk-KNUPPOQC.js → chunk-GE6AZATH.js} +2 -2
- package/dist/browser/{chunk-KNUPPOQC.js.map → chunk-GE6AZATH.js.map} +1 -1
- package/dist/browser/{index-browser.es-G37SKL53.js → index-browser.es-UXYPGJ2M.js} +2 -2
- package/dist/browser/{index-browser.es-G37SKL53.js.map → index-browser.es-UXYPGJ2M.js.map} +1 -1
- package/dist/browser/index.js +11 -2
- package/dist/browser/index.js.map +4 -4
- package/dist/cjs/identity.js +112 -0
- package/dist/cjs/identity.js.map +7 -0
- package/dist/cjs/index.js +43 -22
- package/dist/cjs/index.js.map +2 -2
- package/dist/cjs/media.js +111 -0
- package/dist/cjs/media.js.map +7 -0
- package/dist/cjs/objects.js +307 -0
- package/dist/cjs/objects.js.map +7 -0
- package/dist/cjs/tests.spec.js +1 -2
- package/dist/cjs/tests.spec.js.map +2 -2
- package/dist/cjs/utilities.js +68 -43
- package/dist/cjs/utilities.js.map +2 -2
- package/dist/esm/identity.js +92 -0
- package/dist/esm/identity.js.map +7 -0
- package/dist/esm/index.js +43 -24
- package/dist/esm/index.js.map +2 -2
- package/dist/esm/media.js +91 -0
- package/dist/esm/media.js.map +7 -0
- package/dist/esm/objects.js +285 -0
- package/dist/esm/objects.js.map +7 -0
- package/dist/esm/tests.spec.js +2 -4
- package/dist/esm/tests.spec.js.map +2 -2
- package/dist/esm/utilities.js +69 -48
- package/dist/esm/utilities.js.map +2 -2
- package/dist/{session-manager.d.ts → identity.d.ts} +7 -5
- package/dist/identity.d.ts.map +1 -0
- package/dist/index.d.ts +15 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/media.d.ts +9 -0
- package/dist/media.d.ts.map +1 -0
- package/dist/objects.d.ts +63 -0
- package/dist/objects.d.ts.map +1 -0
- package/dist/utilities.d.ts +19 -8
- package/dist/utilities.d.ts.map +1 -1
- package/package.json +31 -19
- package/src/identity.ts +131 -0
- package/src/index.ts +44 -29
- package/src/media.ts +106 -0
- package/src/objects.ts +431 -0
- package/src/tests.spec.ts +2 -4
- package/src/utilities.ts +67 -87
- package/dist/browser/ajv-6AI3HK2A.js +0 -9
- package/dist/browser/ajv-6AI3HK2A.js.map +0 -7
- package/dist/browser/fast-json-patch-ZE7SZEYK.js +0 -19
- package/dist/browser/fast-json-patch-ZE7SZEYK.js.map +0 -7
- package/dist/cjs/database.js +0 -621
- package/dist/cjs/database.js.map +0 -7
- package/dist/cjs/session-manager.js +0 -107
- package/dist/cjs/session-manager.js.map +0 -7
- package/dist/database.d.ts +0 -105
- package/dist/database.d.ts.map +0 -1
- package/dist/esm/database.js +0 -603
- package/dist/esm/database.js.map +0 -7
- package/dist/esm/session-manager.js +0 -87
- package/dist/esm/session-manager.js.map +0 -7
- package/dist/session-manager.d.ts.map +0 -1
- package/src/database.ts +0 -911
- package/src/session-manager.ts +0 -123
package/dist/cjs/database.js
DELETED
|
@@ -1,621 +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
|
-
const continueBuffer = this.options.continueBuffer ?? 1e3;
|
|
400
|
-
const timeElapsedSinceContinue = Date.now() - ifModifiedSince;
|
|
401
|
-
if (timeElapsedSinceContinue < continueBuffer) {
|
|
402
|
-
await new Promise(
|
|
403
|
-
(resolve) => setTimeout(resolve, continueBuffer - timeElapsedSinceContinue)
|
|
404
|
-
);
|
|
405
|
-
}
|
|
406
|
-
ifModifiedSince -= LAST_MODIFIED_BUFFER;
|
|
407
|
-
}
|
|
408
|
-
const result = await (await this.db).query(index, {
|
|
409
|
-
startkey,
|
|
410
|
-
endkey,
|
|
411
|
-
include_docs: true
|
|
412
|
-
});
|
|
413
|
-
for (const row of result.rows) {
|
|
414
|
-
const doc = row.doc;
|
|
415
|
-
if (!doc) continue;
|
|
416
|
-
if (processedIds?.has(doc._id)) continue;
|
|
417
|
-
processedIds?.add(doc._id);
|
|
418
|
-
if (ifModifiedSince === void 0 && doc.tombstone) continue;
|
|
419
|
-
const object = this.extractGraffitiObject(doc);
|
|
420
|
-
if (channels) {
|
|
421
|
-
if (!(0, import_utilities.isActorAllowedGraffitiObject)(object, session)) continue;
|
|
422
|
-
(0, import_utilities.maskGraffitiObject)(object, channels, session);
|
|
423
|
-
}
|
|
424
|
-
if (!validate(object)) continue;
|
|
425
|
-
yield doc.tombstone ? {
|
|
426
|
-
tombstone: true,
|
|
427
|
-
object: {
|
|
428
|
-
url: object.url,
|
|
429
|
-
lastModified: object.lastModified
|
|
430
|
-
}
|
|
431
|
-
} : { object };
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
async *discoverMeta(args, ifModifiedSince) {
|
|
435
|
-
const [channels, schema, session] = args;
|
|
436
|
-
const validate = (0, import_utilities.compileGraffitiObjectSchema)(await this.ajv, schema);
|
|
437
|
-
const { startKeySuffix, endKeySuffix } = this.queryLastModifiedSuffixes(
|
|
438
|
-
schema,
|
|
439
|
-
ifModifiedSince
|
|
440
|
-
);
|
|
441
|
-
const processedIds = /* @__PURE__ */ new Set();
|
|
442
|
-
const startTime = (/* @__PURE__ */ new Date()).getTime();
|
|
443
|
-
for (const channel of channels) {
|
|
444
|
-
const keyPrefix = encodeURIComponent(channel) + "/";
|
|
445
|
-
const startkey = keyPrefix + startKeySuffix;
|
|
446
|
-
const endkey = keyPrefix + endKeySuffix;
|
|
447
|
-
const iterator = this.streamObjects(
|
|
448
|
-
"indexes/objectsPerChannelAndLastModified",
|
|
449
|
-
startkey,
|
|
450
|
-
endkey,
|
|
451
|
-
validate,
|
|
452
|
-
session,
|
|
453
|
-
ifModifiedSince,
|
|
454
|
-
channels,
|
|
455
|
-
processedIds
|
|
456
|
-
);
|
|
457
|
-
for await (const result of iterator) yield result;
|
|
458
|
-
}
|
|
459
|
-
return startTime;
|
|
460
|
-
}
|
|
461
|
-
async *recoverOrphansMeta(args, ifModifiedSince) {
|
|
462
|
-
const [schema, session] = args;
|
|
463
|
-
const { startKeySuffix, endKeySuffix } = this.queryLastModifiedSuffixes(
|
|
464
|
-
schema,
|
|
465
|
-
ifModifiedSince
|
|
466
|
-
);
|
|
467
|
-
const keyPrefix = encodeURIComponent(session.actor) + "/";
|
|
468
|
-
const startkey = keyPrefix + startKeySuffix;
|
|
469
|
-
const endkey = keyPrefix + endKeySuffix;
|
|
470
|
-
const validate = (0, import_utilities.compileGraffitiObjectSchema)(await this.ajv, schema);
|
|
471
|
-
const startTime = (/* @__PURE__ */ new Date()).getTime();
|
|
472
|
-
const iterator = this.streamObjects(
|
|
473
|
-
"indexes/orphansPerActorAndLastModified",
|
|
474
|
-
startkey,
|
|
475
|
-
endkey,
|
|
476
|
-
validate,
|
|
477
|
-
session,
|
|
478
|
-
ifModifiedSince
|
|
479
|
-
);
|
|
480
|
-
for await (const result of iterator) yield result;
|
|
481
|
-
return startTime;
|
|
482
|
-
}
|
|
483
|
-
discoverCursor(args, ifModifiedSince) {
|
|
484
|
-
return "discover:" + JSON.stringify({
|
|
485
|
-
channels: args[0],
|
|
486
|
-
schema: args[1],
|
|
487
|
-
actor: args[2]?.actor,
|
|
488
|
-
ifModifiedSince
|
|
489
|
-
});
|
|
490
|
-
}
|
|
491
|
-
async *discoverContinue(args, ifModifiedSince) {
|
|
492
|
-
const iterator = this.discoverMeta(args, ifModifiedSince);
|
|
493
|
-
while (true) {
|
|
494
|
-
const result = await iterator.next();
|
|
495
|
-
if (result.done) {
|
|
496
|
-
const ifModifiedSince2 = result.value;
|
|
497
|
-
return {
|
|
498
|
-
continue: () => this.discoverContinue(args, ifModifiedSince2),
|
|
499
|
-
cursor: this.discoverCursor(args, ifModifiedSince2)
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
yield result.value;
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
discover = (...args) => {
|
|
506
|
-
const iterator = this.discoverMeta(args);
|
|
507
|
-
const this_ = this;
|
|
508
|
-
return async function* () {
|
|
509
|
-
while (true) {
|
|
510
|
-
const result = await iterator.next();
|
|
511
|
-
if (result.done) {
|
|
512
|
-
return {
|
|
513
|
-
continue: () => this_.discoverContinue(args, result.value),
|
|
514
|
-
cursor: this_.discoverCursor(args, result.value)
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
if (result.value.tombstone) continue;
|
|
518
|
-
yield result.value;
|
|
519
|
-
}
|
|
520
|
-
}();
|
|
521
|
-
};
|
|
522
|
-
recoverOrphansCursor(args, ifModifiedSince) {
|
|
523
|
-
return "orphans:" + JSON.stringify({
|
|
524
|
-
schema: args[0],
|
|
525
|
-
actor: args[1]?.actor,
|
|
526
|
-
ifModifiedSince
|
|
527
|
-
});
|
|
528
|
-
}
|
|
529
|
-
async *recoverOrphansContinue(args, ifModifiedSince) {
|
|
530
|
-
const iterator = this.recoverOrphansMeta(args, ifModifiedSince);
|
|
531
|
-
while (true) {
|
|
532
|
-
const result = await iterator.next();
|
|
533
|
-
if (result.done) {
|
|
534
|
-
const ifModifiedSince2 = result.value;
|
|
535
|
-
return {
|
|
536
|
-
continue: () => this.recoverOrphansContinue(args, ifModifiedSince2),
|
|
537
|
-
cursor: this.recoverOrphansCursor(args, ifModifiedSince2)
|
|
538
|
-
};
|
|
539
|
-
}
|
|
540
|
-
yield result.value;
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
recoverOrphans = (...args) => {
|
|
544
|
-
const iterator = this.recoverOrphansMeta(args);
|
|
545
|
-
const this_ = this;
|
|
546
|
-
return async function* () {
|
|
547
|
-
while (true) {
|
|
548
|
-
const result = await iterator.next();
|
|
549
|
-
if (result.done) {
|
|
550
|
-
return {
|
|
551
|
-
continue: () => this_.recoverOrphansContinue(
|
|
552
|
-
args,
|
|
553
|
-
result.value
|
|
554
|
-
),
|
|
555
|
-
cursor: this_.recoverOrphansCursor(args, result.value)
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
if (result.value.tombstone) continue;
|
|
559
|
-
yield result.value;
|
|
560
|
-
}
|
|
561
|
-
}();
|
|
562
|
-
};
|
|
563
|
-
channelStats = (session) => {
|
|
564
|
-
const this_ = this;
|
|
565
|
-
return async function* () {
|
|
566
|
-
const keyPrefix = encodeURIComponent(session.actor) + "/";
|
|
567
|
-
const result = await (await this_.db).query("indexes/channelStatsPerActor", {
|
|
568
|
-
startkey: keyPrefix,
|
|
569
|
-
endkey: keyPrefix + "\uFFFF",
|
|
570
|
-
reduce: true,
|
|
571
|
-
group: true
|
|
572
|
-
});
|
|
573
|
-
for (const row of result.rows) {
|
|
574
|
-
const channelEncoded = row.key.split("/")[1];
|
|
575
|
-
if (typeof channelEncoded !== "string") continue;
|
|
576
|
-
const { count, max: lastModified } = row.value;
|
|
577
|
-
if (typeof count !== "number" || typeof lastModified !== "number")
|
|
578
|
-
continue;
|
|
579
|
-
yield {
|
|
580
|
-
value: {
|
|
581
|
-
channel: decodeURIComponent(channelEncoded),
|
|
582
|
-
count,
|
|
583
|
-
lastModified
|
|
584
|
-
}
|
|
585
|
-
};
|
|
586
|
-
}
|
|
587
|
-
}();
|
|
588
|
-
};
|
|
589
|
-
continueObjectStream = (cursor, session) => {
|
|
590
|
-
if (cursor.startsWith("discover:")) {
|
|
591
|
-
const { channels, schema, actor, ifModifiedSince } = JSON.parse(
|
|
592
|
-
cursor.slice("discover:".length)
|
|
593
|
-
);
|
|
594
|
-
if (actor && actor !== session?.actor) {
|
|
595
|
-
throw new import_api.GraffitiErrorForbidden(
|
|
596
|
-
"Cannot continue a cursor for another actor"
|
|
597
|
-
);
|
|
598
|
-
}
|
|
599
|
-
return this.discoverContinue(
|
|
600
|
-
[channels, schema, session],
|
|
601
|
-
ifModifiedSince
|
|
602
|
-
);
|
|
603
|
-
} else if (cursor.startsWith("orphans:")) {
|
|
604
|
-
const { schema, actor, ifModifiedSince } = JSON.parse(
|
|
605
|
-
cursor.slice("orphans:".length)
|
|
606
|
-
);
|
|
607
|
-
if (!session || actor !== session?.actor) {
|
|
608
|
-
throw new import_api.GraffitiErrorForbidden(
|
|
609
|
-
"Cannot continue a cursor for another actor"
|
|
610
|
-
);
|
|
611
|
-
}
|
|
612
|
-
return this.recoverOrphansContinue(
|
|
613
|
-
[schema, session],
|
|
614
|
-
ifModifiedSince
|
|
615
|
-
);
|
|
616
|
-
} else {
|
|
617
|
-
throw new import_api.GraffitiErrorNotFound("Cursor not found");
|
|
618
|
-
}
|
|
619
|
-
};
|
|
620
|
-
}
|
|
621
|
-
//# sourceMappingURL=database.js.map
|