@graffiti-garden/implementation-local 1.0.3 → 1.0.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,QAAQ,EAEd,MAAM,sBAAsB,CAAC;AAwB9B,qBAAa,kBAAkB;IAC7B,SAAS,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC,CAAC;gBAE5C,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAIzD,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC,CAqB9B;IAEF,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,CAqC5B;IAEF,WAAW,EAAE,QAAQ,CAAC,aAAa,CAAC,CAMlC;CACH"}
1
+ {"version":3,"file":"media.d.ts","sourceRoot":"","sources":["../src/media.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,QAAQ,EAEd,MAAM,sBAAsB,CAAC;AAuB9B,qBAAa,kBAAkB;IAC7B,SAAS,CAAC,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC,CAAC;gBAE5C,EAAE,EAAE,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;IAIzD,SAAS,EAAE,QAAQ,CAAC,WAAW,CAAC,CAqB9B;IAEF,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,CAoC5B;IAEF,WAAW,EAAE,QAAQ,CAAC,aAAa,CAAC,CAMlC;CACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"objects.d.ts","sourceRoot":"","sources":["../src/objects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EAER,UAAU,EACV,eAAe,EACf,4BAA4B,EAC5B,iCAAiC,EAClC,MAAM,sBAAsB,CAAC;AAW9B,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAE3B;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAC;IAC7D;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,KAAK,kBAAkB,GAAG;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,EAAE,CAAC;IACV,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;GAGG;AACH,qBAAa,oBAAoB;IAC/B,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,GAAG,SAAS,CAAC;IACzE,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IACzC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;IAEjD,IAAI,EAAE,kDAkDL;IAED,SAAS,KAAK,GAAG,iBAQhB;cAEe,iBAAiB;gBAIrB,OAAO,CAAC,EAAE,oBAAoB;IAI1C,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,CA4ClB;IAEF,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAgCxB;IAEF,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CA4BpB;cAEe,YAAY,CAAC,MAAM,SAAS,UAAU,EACrD,IAAI,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAC5D,cAAc,CAAC,EAAE;QACf,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,GACA,cAAc,CACf,iCAAiC,CAAC,MAAM,CAAC,EACzC,sBAAsB,CACvB;IAoFD,SAAS,CAAC,cAAc,CACtB,IAAI,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,EACxD,cAAc,EAAE;QACd,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,GACA,MAAM;cAaQ,gBAAgB,CAAC,MAAM,SAAS,UAAU,EACzD,IAAI,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAC5D,cAAc,EAAE;QACd,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,EACD,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,GAC/B,4BAA4B,CAAC,MAAM,CAAC;IAqBvC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,CA4B5B;IAEF,gBAAgB,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAuB5C;CACH"}
1
+ {"version":3,"file":"objects.d.ts","sourceRoot":"","sources":["../src/objects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EAER,UAAU,EACV,eAAe,EACf,4BAA4B,EAC5B,iCAAiC,EAClC,MAAM,sBAAsB,CAAC;AAe9B,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAE3B;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAC;IAC7D;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,KAAK,kBAAkB,GAAG;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,EAAE,CAAC;IACV,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,KAAK,sBAAsB,GAAG;IAC5B,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;GAGG;AACH,qBAAa,oBAAoB;IAC/B,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,GAAG,SAAS,CAAC;IACzE,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,SAAS,CAAC;IACzC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,oBAAoB,CAAC;IAEjD,IAAI,EAAE,kDAkDL;IAED,SAAS,KAAK,GAAG,iBAQhB;cAEe,iBAAiB;gBAIrB,OAAO,CAAC,EAAE,oBAAoB;IAI1C,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,CA4ClB;IAEF,MAAM,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAyCxB;IAEF,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CA4BpB;cAEe,YAAY,CAAC,MAAM,SAAS,UAAU,EACrD,IAAI,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAC5D,cAAc,CAAC,EAAE;QACf,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,GACA,cAAc,CACf,iCAAiC,CAAC,MAAM,CAAC,EACzC,sBAAsB,CACvB;IAwFD,SAAS,CAAC,cAAc,CACtB,IAAI,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,EACxD,cAAc,EAAE;QACd,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,GACA,MAAM;cAaQ,gBAAgB,CAAC,MAAM,SAAS,UAAU,EACzD,IAAI,EAAE,UAAU,CAAC,OAAO,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAC5D,cAAc,EAAE;QACd,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB,EACD,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,GAC/B,4BAA4B,CAAC,MAAM,CAAC;IAqBvC,QAAQ,EAAE,QAAQ,CAAC,UAAU,CAAC,CA4B5B;IAEF,gBAAgB,EAAE,QAAQ,CAAC,kBAAkB,CAAC,CAuB5C;CACH"}
@@ -1,3 +1,5 @@
1
+ import { type GraffitiObject, type GraffitiObjectBase, type JSONSchema } from "@graffiti-garden/api";
2
+ import type Ajv from "ajv";
1
3
  export declare function encodeBase64(bytes: Uint8Array): string;
2
4
  export declare function decodeBase64(base64Url: string): Uint8Array;
3
5
  export declare function randomBase64(numBytes?: number): string;
@@ -18,4 +20,5 @@ export declare function decodeMediaUrl(url: string): {
18
20
  };
19
21
  export declare function blobToBase64(blob: Blob): Promise<string>;
20
22
  export declare function base64ToBlob(dataUrl: string): Promise<Blob>;
23
+ export declare function compileGraffitiObjectSchema<Schema extends JSONSchema>(ajv: Ajv, schema: Schema): (data: GraffitiObjectBase) => data is GraffitiObject<Schema>;
21
24
  //# sourceMappingURL=utilities.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"AAEA,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAKtD;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAO1D;AAED,wBAAgB,YAAY,CAAC,QAAQ,GAAE,MAAW,GAAG,MAAM,CAK1D;AAKD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAE1E;AACD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,UAExD;AACD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,UAEvD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;EAU5D;AACD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM;;;EAE1C;AACD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM;;;EAEzC;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB9D;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,iBAGjD"}
1
+ {"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,UAAU,EAChB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,GAAG,MAAM,KAAK,CAAC;AAE3B,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAKtD;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAO1D;AAED,wBAAgB,YAAY,CAAC,QAAQ,GAAE,MAAW,GAAG,MAAM,CAK1D;AAKD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAE1E;AACD,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,UAExD;AACD,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,UAEvD;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;;;EAU5D;AACD,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM;;;EAE1C;AACD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM;;;EAEzC;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAsB9D;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,iBAGjD;AAED,wBAAgB,2BAA2B,CAAC,MAAM,SAAS,UAAU,EACnE,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,MAAM,UAQJ,kBAAkB,KACrB,IAAI,IAAI,cAAc,CAAC,MAAM,CAAC,CAMtC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graffiti-garden/implementation-local",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "A local implementation of the Graffiti API using PouchDB",
5
5
  "types": "./dist/index.d.ts",
6
6
  "module": "./dist/esm/index.js",
@@ -84,19 +84,18 @@
84
84
  },
85
85
  "devDependencies": {
86
86
  "@types/negotiator": "^0.6.4",
87
- "@types/node": "^25.0.3",
87
+ "@types/node": "^25.0.6",
88
88
  "@types/pouchdb": "^6.4.2",
89
- "@vitest/coverage-v8": "^4.0.16",
89
+ "@vitest/coverage-v8": "^4.0.17",
90
90
  "esbuild": "^0.27.2",
91
91
  "esbuild-plugin-polyfill-node": "^0.3.0",
92
92
  "tsx": "^4.19.2",
93
93
  "typescript": "^5.7.3",
94
- "vitest": "^4.0.16"
94
+ "vitest": "^4.0.17"
95
95
  },
96
96
  "dependencies": {
97
- "@graffiti-garden/api": "^1.0.3",
97
+ "@graffiti-garden/api": "^1.0.4",
98
98
  "ajv": "^8.17.1",
99
- "negotiator": "^1.0.0",
100
99
  "pouchdb": "^9.0.0"
101
100
  }
102
101
  }
package/src/media.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  GraffitiErrorNotAcceptable,
3
3
  GraffitiErrorTooLarge,
4
+ isMediaAcceptable,
4
5
  type Graffiti,
5
6
  type JSONSchema,
6
7
  } from "@graffiti-garden/api";
@@ -12,7 +13,6 @@ import {
12
13
  blobToBase64,
13
14
  base64ToBlob,
14
15
  } from "./utilities";
15
- import Negotiator from "negotiator";
16
16
 
17
17
  const MEDIA_OBJECT_SCHEMA = {
18
18
  properties: {
@@ -58,7 +58,7 @@ export class GraffitiLocalMedia {
58
58
  };
59
59
 
60
60
  getMedia: Graffiti["getMedia"] = async (...args) => {
61
- const [mediaUrl, requirements, session] = args;
61
+ const [mediaUrl, accept, session] = args;
62
62
  const { actor, id } = decodeMediaUrl(mediaUrl);
63
63
  const objectUrl = encodeObjectUrl(actor, id);
64
64
 
@@ -70,17 +70,16 @@ export class GraffitiLocalMedia {
70
70
 
71
71
  const { dataBase64, type, size } = object.value;
72
72
 
73
- if (requirements?.maxBytes && size > requirements.maxBytes) {
73
+ if (accept?.maxBytes && size > accept.maxBytes) {
74
74
  throw new GraffitiErrorTooLarge("File size exceeds limit");
75
75
  }
76
76
 
77
77
  // Make sure it adheres to requirements.accept
78
- if (requirements?.accept) {
79
- const negotiator = new Negotiator({
80
- headers: { accept: requirements.accept },
81
- });
82
- if (negotiator.mediaType([type]) !== type) {
83
- throw new GraffitiErrorNotAcceptable(`Unsupported media type, ${type}`);
78
+ if (accept?.types) {
79
+ if (!isMediaAcceptable(type, accept.types)) {
80
+ throw new GraffitiErrorNotAcceptable(
81
+ `Unacceptable media type, ${type}`,
82
+ );
84
83
  }
85
84
  }
86
85
 
package/src/objects.ts CHANGED
@@ -13,9 +13,13 @@ import {
13
13
  unpackObjectUrl,
14
14
  maskGraffitiObject,
15
15
  isActorAllowedGraffitiObject,
16
- compileGraffitiObjectSchema,
17
16
  } from "@graffiti-garden/api";
18
- import { randomBase64, decodeObjectUrl, encodeObjectUrl } from "./utilities.js";
17
+ import {
18
+ randomBase64,
19
+ decodeObjectUrl,
20
+ encodeObjectUrl,
21
+ compileGraffitiObjectSchema,
22
+ } from "./utilities.js";
19
23
  import type Ajv from "ajv";
20
24
 
21
25
  /**
@@ -166,13 +170,13 @@ export class GraffitiLocalObjects {
166
170
 
167
171
  // Mask out the allowed list and channels
168
172
  // if the user is not the owner
169
- maskGraffitiObject(object, [], session);
173
+ const masked = maskGraffitiObject(object, [], session?.actor);
170
174
 
171
175
  const validate = compileGraffitiObjectSchema(await this.ajv, schema);
172
- if (!validate(object)) {
176
+ if (!validate(masked)) {
173
177
  throw new GraffitiErrorSchemaMismatch();
174
178
  }
175
- return object;
179
+ return masked;
176
180
  };
177
181
 
178
182
  delete: Graffiti["delete"] = async (...args) => {
@@ -206,7 +210,16 @@ export class GraffitiLocalObjects {
206
210
  throw new GraffitiErrorNotFound("Object not found.");
207
211
  }
208
212
 
209
- return;
213
+ // Return the output
214
+ const { value, channels, allowed } = doc;
215
+ const object: GraffitiObjectBase = {
216
+ value,
217
+ channels,
218
+ allowed,
219
+ url,
220
+ actor,
221
+ };
222
+ return object;
210
223
  };
211
224
 
212
225
  post: Graffiti["post"] = async (...args) => {
@@ -313,16 +326,20 @@ export class GraffitiLocalObjects {
313
326
 
314
327
  if (!isActorAllowedGraffitiObject(object, session)) continue;
315
328
 
316
- maskGraffitiObject(object, discoverChannels, session);
329
+ const masked = maskGraffitiObject(
330
+ object,
331
+ discoverChannels,
332
+ session?.actor,
333
+ );
317
334
 
318
- if (!validate(object)) continue;
335
+ if (!validate(masked)) continue;
319
336
 
320
337
  yield tombstone
321
338
  ? {
322
339
  tombstone: true,
323
340
  object: { url },
324
341
  }
325
- : { object };
342
+ : { object: masked };
326
343
  }
327
344
  }
328
345
 
package/src/utilities.ts CHANGED
@@ -1,4 +1,11 @@
1
- import { GraffitiErrorNotFound } from "@graffiti-garden/api";
1
+ import {
2
+ GraffitiErrorInvalidSchema,
3
+ GraffitiErrorNotFound,
4
+ type GraffitiObject,
5
+ type GraffitiObjectBase,
6
+ type JSONSchema,
7
+ } from "@graffiti-garden/api";
8
+ import type Ajv from "ajv";
2
9
 
3
10
  export function encodeBase64(bytes: Uint8Array): string {
4
11
  // Convert it to base64
@@ -82,3 +89,22 @@ export async function base64ToBlob(dataUrl: string) {
82
89
  const response = await fetch(dataUrl);
83
90
  return await response.blob();
84
91
  }
92
+
93
+ export function compileGraffitiObjectSchema<Schema extends JSONSchema>(
94
+ ajv: Ajv,
95
+ schema: Schema,
96
+ ) {
97
+ try {
98
+ // Force the validation guard because
99
+ // it is too big for the type checker.
100
+ // Fortunately json-schema-to-ts is
101
+ // well tested against ajv.
102
+ return ajv.compile(schema) as (
103
+ data: GraffitiObjectBase,
104
+ ) => data is GraffitiObject<Schema>;
105
+ } catch (error) {
106
+ throw new GraffitiErrorInvalidSchema(
107
+ error instanceof Error ? error.message : undefined,
108
+ );
109
+ }
110
+ }