@graffiti-garden/implementation-local 1.0.3 → 1.0.7

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/src/objects.ts CHANGED
@@ -14,9 +14,9 @@ import {
14
14
  maskGraffitiObject,
15
15
  isActorAllowedGraffitiObject,
16
16
  compileGraffitiObjectSchema,
17
+ GraffitiErrorCursorExpired,
17
18
  } from "@graffiti-garden/api";
18
19
  import { randomBase64, decodeObjectUrl, encodeObjectUrl } from "./utilities.js";
19
- import type Ajv from "ajv";
20
20
 
21
21
  /**
22
22
  * Constructor options for the GraffitiPoubchDB class.
@@ -56,7 +56,6 @@ type ContinueDiscoverParams = {
56
56
  */
57
57
  export class GraffitiLocalObjects {
58
58
  protected db_: Promise<PouchDB.Database<GraffitiObjectData>> | undefined;
59
- protected ajv_: Promise<Ajv> | undefined;
60
59
  protected readonly options: GraffitiLocalOptions;
61
60
 
62
61
  get db() {
@@ -111,16 +110,6 @@ export class GraffitiLocalObjects {
111
110
  return this.db_;
112
111
  }
113
112
 
114
- protected get ajv() {
115
- if (!this.ajv_) {
116
- this.ajv_ = (async () => {
117
- const { default: Ajv } = await import("ajv");
118
- return new Ajv({ strict: false });
119
- })();
120
- }
121
- return this.ajv_;
122
- }
123
-
124
113
  protected async getOperationClock() {
125
114
  return Number((await (await this.db).info()).update_seq);
126
115
  }
@@ -166,13 +155,13 @@ export class GraffitiLocalObjects {
166
155
 
167
156
  // Mask out the allowed list and channels
168
157
  // if the user is not the owner
169
- maskGraffitiObject(object, [], session);
158
+ const masked = maskGraffitiObject(object, [], session?.actor);
170
159
 
171
- const validate = compileGraffitiObjectSchema(await this.ajv, schema);
172
- if (!validate(object)) {
160
+ const validate = await compileGraffitiObjectSchema(schema);
161
+ if (!validate(masked)) {
173
162
  throw new GraffitiErrorSchemaMismatch();
174
163
  }
175
- return object;
164
+ return masked;
176
165
  };
177
166
 
178
167
  delete: Graffiti["delete"] = async (...args) => {
@@ -206,7 +195,16 @@ export class GraffitiLocalObjects {
206
195
  throw new GraffitiErrorNotFound("Object not found.");
207
196
  }
208
197
 
209
- return;
198
+ // Return the output
199
+ const { value, channels, allowed } = doc;
200
+ const object: GraffitiObjectBase = {
201
+ value,
202
+ channels,
203
+ allowed,
204
+ url,
205
+ actor,
206
+ };
207
+ return object;
210
208
  };
211
209
 
212
210
  post: Graffiti["post"] = async (...args) => {
@@ -265,7 +263,7 @@ export class GraffitiLocalObjects {
265
263
  }
266
264
 
267
265
  const [discoverChannels, schema, session] = args;
268
- const validate = compileGraffitiObjectSchema(await this.ajv, schema);
266
+ const validate = await compileGraffitiObjectSchema(schema);
269
267
  const startKeySuffix = continueParams
270
268
  ? continueParams.ifModifiedSince.toString().padStart(15, "0")
271
269
  : "";
@@ -313,16 +311,20 @@ export class GraffitiLocalObjects {
313
311
 
314
312
  if (!isActorAllowedGraffitiObject(object, session)) continue;
315
313
 
316
- maskGraffitiObject(object, discoverChannels, session);
314
+ const masked = maskGraffitiObject(
315
+ object,
316
+ discoverChannels,
317
+ session?.actor,
318
+ );
317
319
 
318
- if (!validate(object)) continue;
320
+ if (!validate(masked)) continue;
319
321
 
320
322
  yield tombstone
321
323
  ? {
322
324
  tombstone: true,
323
325
  object: { url },
324
326
  }
325
- : { object };
327
+ : { object: masked };
326
328
  }
327
329
  }
328
330
 
@@ -429,7 +431,7 @@ export class GraffitiLocalObjects {
429
431
  );
430
432
  } else {
431
433
  return (async function* () {
432
- throw new GraffitiErrorNotFound("Cursor not found");
434
+ throw new GraffitiErrorCursorExpired("Cursor not found");
433
435
  })();
434
436
  }
435
437
  };
package/src/tests.spec.ts CHANGED
@@ -6,9 +6,10 @@ import {
6
6
  import { GraffitiLocal } from "./index";
7
7
 
8
8
  const useGraffiti = () => new GraffitiLocal();
9
- const useSession1 = () => ({ actor: "someone" });
10
- const useSession2 = () => ({ actor: "someoneelse" });
9
+ const useSession1 = () => ({ actor: "did:example:someone" });
10
+ const useSession2 = () => ({ actor: "did:example:someoneelse" });
11
11
 
12
+ // @ts-ignore
12
13
  graffitiCRUDTests(useGraffiti, useSession1, useSession2);
13
14
  graffitiDiscoverTests(useGraffiti, useSession1, useSession2);
14
15
  graffitiMediaTests(useGraffiti, useSession1, useSession2);
package/src/utilities.ts CHANGED
@@ -23,18 +23,22 @@ export function randomBase64(numBytes: number = 32): string {
23
23
  return encodeBase64(bytes);
24
24
  }
25
25
 
26
- const OBJECT_URL_PREFIX = "graffiti:object:";
27
- const MEDIA_URL_PREFIX = "graffiti:media:";
26
+ const OBJECT_URL_PREFIX = "graffiti:";
28
27
 
28
+ export function encodeObjectUrlComponent(value: string) {
29
+ const replaced = value.replace(/:/g, "!").replace(/\//g, "~");
30
+ return encodeURIComponent(replaced);
31
+ }
32
+ export function decodeObjectUrlComponent(value: string) {
33
+ const decoded = decodeURIComponent(value);
34
+ return decoded.replace(/!/g, ":").replace(/~/g, "/");
35
+ }
29
36
  export function encodeGraffitiUrl(actor: string, id: string, prefix: string) {
30
- return `${prefix}${encodeURIComponent(actor)}:${encodeURIComponent(id)}`;
37
+ return `${prefix}${encodeObjectUrlComponent(actor)}:${encodeObjectUrlComponent(id)}`;
31
38
  }
32
39
  export function encodeObjectUrl(actor: string, id: string) {
33
40
  return encodeGraffitiUrl(actor, id, OBJECT_URL_PREFIX);
34
41
  }
35
- export function encodeMediaUrl(actor: string, id: string) {
36
- return encodeGraffitiUrl(actor, id, MEDIA_URL_PREFIX);
37
- }
38
42
 
39
43
  export function decodeGraffitiUrl(url: string, prefix: string) {
40
44
  if (!url.startsWith(prefix)) {
@@ -44,15 +48,12 @@ export function decodeGraffitiUrl(url: string, prefix: string) {
44
48
  if (slices.length !== 2) {
45
49
  throw new GraffitiErrorNotFound("URL has too many colon-seperated parts");
46
50
  }
47
- const [actor, id] = slices.map(decodeURIComponent);
51
+ const [actor, id] = slices.map(decodeObjectUrlComponent);
48
52
  return { actor, id };
49
53
  }
50
54
  export function decodeObjectUrl(url: string) {
51
55
  return decodeGraffitiUrl(url, OBJECT_URL_PREFIX);
52
56
  }
53
- export function decodeMediaUrl(url: string) {
54
- return decodeGraffitiUrl(url, MEDIA_URL_PREFIX);
55
- }
56
57
 
57
58
  export async function blobToBase64(blob: Blob): Promise<string> {
58
59
  if (typeof FileReader !== "undefined") {