@graffiti-garden/api 0.1.8 → 0.1.10

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/dist/index.cjs.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";class r extends Error{constructor(t){super(t),this.name="GraffitiErrorUnauthorized",Object.setPrototypeOf(this,r.prototype)}}class t extends Error{constructor(r){super(r),this.name="GraffitiErrorForbidden",Object.setPrototypeOf(this,t.prototype)}}class o extends Error{constructor(r){super(r),this.name="GraffitiErrorNotFound",Object.setPrototypeOf(this,o.prototype)}}class e extends Error{constructor(r){super(r),this.name="GraffitiErrorInvalidSchema",Object.setPrototypeOf(this,e.prototype)}}class s extends Error{constructor(r){super(r),this.name="GraffitiErrorSchemaMismatch",Object.setPrototypeOf(this,s.prototype)}}class i extends Error{constructor(r){super(r),this.name="GraffitiErrorPatchTestFailed",Object.setPrototypeOf(this,i.prototype)}}class a extends Error{constructor(r){super(r),this.name="GraffitiErrorPatchError",Object.setPrototypeOf(this,a.prototype)}}class c extends Error{constructor(r){super(r),this.name="GraffitiErrorInvalidUri",Object.setPrototypeOf(this,c.prototype)}}class f extends Error{constructor(r){super(r),this.name="GraffitiErrorOther",Object.setPrototypeOf(this,f.prototype)}}exports.Graffiti=class{objectToUri(r){return this.locationToUri(r)}},exports.GraffitiErrorForbidden=t,exports.GraffitiErrorInvalidSchema=e,exports.GraffitiErrorInvalidUri=c,exports.GraffitiErrorNotFound=o,exports.GraffitiErrorOther=f,exports.GraffitiErrorPatchError=a,exports.GraffitiErrorPatchTestFailed=i,exports.GraffitiErrorSchemaMismatch=s,exports.GraffitiErrorUnauthorized=r;
1
+ "use strict";class r extends Error{constructor(t){super(t),this.name="GraffitiErrorUnauthorized",Object.setPrototypeOf(this,r.prototype)}}class t extends Error{constructor(r){super(r),this.name="GraffitiErrorForbidden",Object.setPrototypeOf(this,t.prototype)}}class o extends Error{constructor(r){super(r),this.name="GraffitiErrorNotFound",Object.setPrototypeOf(this,o.prototype)}}class e extends Error{constructor(r){super(r),this.name="GraffitiErrorInvalidSchema",Object.setPrototypeOf(this,e.prototype)}}class s extends Error{constructor(r){super(r),this.name="GraffitiErrorSchemaMismatch",Object.setPrototypeOf(this,s.prototype)}}class i extends Error{constructor(r){super(r),this.name="GraffitiErrorPatchTestFailed",Object.setPrototypeOf(this,i.prototype)}}class a extends Error{constructor(r){super(r),this.name="GraffitiErrorPatchError",Object.setPrototypeOf(this,a.prototype)}}class c extends Error{constructor(r){super(r),this.name="GraffitiErrorInvalidUri",Object.setPrototypeOf(this,c.prototype)}}exports.Graffiti=class{objectToUri(r){return this.locationToUri(r)}},exports.GraffitiErrorForbidden=t,exports.GraffitiErrorInvalidSchema=e,exports.GraffitiErrorInvalidUri=c,exports.GraffitiErrorNotFound=o,exports.GraffitiErrorPatchError=a,exports.GraffitiErrorPatchTestFailed=i,exports.GraffitiErrorSchemaMismatch=s,exports.GraffitiErrorUnauthorized=r;
2
2
  //# sourceMappingURL=index.cjs.js.map
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- class r{objectToUri(r){return this.locationToUri(r)}}class t extends Error{constructor(r){super(r),this.name="GraffitiErrorUnauthorized",Object.setPrototypeOf(this,t.prototype)}}class o extends Error{constructor(r){super(r),this.name="GraffitiErrorForbidden",Object.setPrototypeOf(this,o.prototype)}}class e extends Error{constructor(r){super(r),this.name="GraffitiErrorNotFound",Object.setPrototypeOf(this,e.prototype)}}class s extends Error{constructor(r){super(r),this.name="GraffitiErrorInvalidSchema",Object.setPrototypeOf(this,s.prototype)}}class i extends Error{constructor(r){super(r),this.name="GraffitiErrorSchemaMismatch",Object.setPrototypeOf(this,i.prototype)}}class c extends Error{constructor(r){super(r),this.name="GraffitiErrorPatchTestFailed",Object.setPrototypeOf(this,c.prototype)}}class a extends Error{constructor(r){super(r),this.name="GraffitiErrorPatchError",Object.setPrototypeOf(this,a.prototype)}}class p extends Error{constructor(r){super(r),this.name="GraffitiErrorInvalidUri",Object.setPrototypeOf(this,p.prototype)}}class n extends Error{constructor(r){super(r),this.name="GraffitiErrorOther",Object.setPrototypeOf(this,n.prototype)}}export{r as Graffiti,o as GraffitiErrorForbidden,s as GraffitiErrorInvalidSchema,p as GraffitiErrorInvalidUri,e as GraffitiErrorNotFound,n as GraffitiErrorOther,a as GraffitiErrorPatchError,c as GraffitiErrorPatchTestFailed,i as GraffitiErrorSchemaMismatch,t as GraffitiErrorUnauthorized};
1
+ class r{objectToUri(r){return this.locationToUri(r)}}class t extends Error{constructor(r){super(r),this.name="GraffitiErrorUnauthorized",Object.setPrototypeOf(this,t.prototype)}}class o extends Error{constructor(r){super(r),this.name="GraffitiErrorForbidden",Object.setPrototypeOf(this,o.prototype)}}class e extends Error{constructor(r){super(r),this.name="GraffitiErrorNotFound",Object.setPrototypeOf(this,e.prototype)}}class s extends Error{constructor(r){super(r),this.name="GraffitiErrorInvalidSchema",Object.setPrototypeOf(this,s.prototype)}}class i extends Error{constructor(r){super(r),this.name="GraffitiErrorSchemaMismatch",Object.setPrototypeOf(this,i.prototype)}}class c extends Error{constructor(r){super(r),this.name="GraffitiErrorPatchTestFailed",Object.setPrototypeOf(this,c.prototype)}}class a extends Error{constructor(r){super(r),this.name="GraffitiErrorPatchError",Object.setPrototypeOf(this,a.prototype)}}class p extends Error{constructor(r){super(r),this.name="GraffitiErrorInvalidUri",Object.setPrototypeOf(this,p.prototype)}}export{r as Graffiti,o as GraffitiErrorForbidden,s as GraffitiErrorInvalidSchema,p as GraffitiErrorInvalidUri,e as GraffitiErrorNotFound,a as GraffitiErrorPatchError,c as GraffitiErrorPatchTestFailed,i as GraffitiErrorSchemaMismatch,t as GraffitiErrorUnauthorized};
2
2
  //# sourceMappingURL=index.js.map
@@ -588,14 +588,7 @@ export declare abstract class Graffiti {
588
588
  * scope as necessary.
589
589
  */
590
590
  scope?: {};
591
- },
592
- /**
593
- * An arbitrary string that will be returned with the
594
- * {@link GraffitiSession | session} object
595
- * when the login process is complete.
596
- * See {@link GraffitiLoginEvent}.
597
- */
598
- state?: string): Promise<void>;
591
+ }): Promise<void>;
599
592
  /**
600
593
  * Begins the logout process. Depending on the implementation, this may
601
594
  * involve redirecting the user to a logout page or opening a popup,
@@ -611,18 +604,13 @@ export declare abstract class Graffiti {
611
604
  /**
612
605
  * The {@link GraffitiSession | session} object to logout.
613
606
  */
614
- session: GraffitiSession,
615
- /**
616
- * An arbitrary string that will be returned with the
617
- * when the logout process is complete.
618
- * See {@link GraffitiLogoutEvent}.
619
- */
620
- state?: string): Promise<void>;
607
+ session: GraffitiSession): Promise<void>;
621
608
  /**
622
- * An event target that can be used to listen for `login`
623
- * and `logout` events. They are custom events of types
624
- * {@link GraffitiLoginEvent} and {@link GraffitiLogoutEvent }
625
- * respectively.
609
+ * An event target that can be used to listen for the following
610
+ * events and they're corresponding event types:
611
+ * - `login` - {@link GraffitiLoginEvent}
612
+ * - `logout` - {@link GraffitiLogoutEvent}
613
+ * - `initialized` - {@link GraffitiSessionInitializedEvent}
626
614
  *
627
615
  * @group Session Management
628
616
  */
@@ -1 +1 @@
1
- {"version":3,"file":"1-api.d.ts","sourceRoot":"","sources":["../../src/1-api.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"1-api.d.ts","sourceRoot":"","sources":["../../src/1-api.ts"],"names":[],"mappings":"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"}
@@ -248,14 +248,12 @@ export type GraffitiStream<TValue, TReturn = void> = AsyncGenerator<{
248
248
  * The event name to listen for is `login`.
249
249
  */
250
250
  export type GraffitiLoginEvent = CustomEvent<{
251
- state?: string;
252
- } & ({
253
251
  error: Error;
254
252
  session?: undefined;
255
253
  } | {
256
254
  error?: undefined;
257
255
  session: GraffitiSession;
258
- })>;
256
+ }>;
259
257
  /**
260
258
  * The event type produced in {@link Graffiti.sessionEvents}
261
259
  * when a user logs out either manually with {@link Graffiti.logout}
@@ -263,8 +261,24 @@ export type GraffitiLoginEvent = CustomEvent<{
263
261
  * The event name to listen for is `logout`.
264
262
  */
265
263
  export type GraffitiLogoutEvent = CustomEvent<{
264
+ error: Error;
265
+ actor?: string;
266
+ } | {
267
+ error?: undefined;
266
268
  actor: string;
267
- state?: string;
269
+ }>;
270
+ /**
271
+ * The event type produced in {@link Graffiti.sessionEvents}
272
+ * after an application has attempted to complete any login redirects
273
+ * and restore any previously active sessions.
274
+ * Successful session restores will be returned in parallel as
275
+ * their own {@link GraffitiLoginEvent} events.
276
+ * This event optionally return an `href` property
277
+ * if there were any redirects during the restoration process.
278
+ * The event name to listen for is `initialized`.
279
+ */
280
+ export type GraffitiSessionInitializedEvent = CustomEvent<{
268
281
  error?: Error;
282
+ href?: string;
269
283
  }>;
270
284
  //# sourceMappingURL=2-types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"2-types.d.ts","sourceRoot":"","sources":["../../src/2-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,IAAI,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,KAAK,EAAE,EAAE,CAAC;IAEV;;;;;;;;;;;;OAYG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;IAEnB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;;;;;;;;;;;OAYG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;;;;;;;OASG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,CAAC,MAAM,IAAI,kBAAkB,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;AAE9E;;;;;;;;;GASG;AACH,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,kBAAkB,EAClB,OAAO,GAAG,MAAM,GAAG,QAAQ,CAC5B,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,iBAAiB,CAAC,MAAM,IAAI,IAAI,CAC1C,kBAAkB,EAClB,OAAO,GAAG,UAAU,GAAG,SAAS,CACjC,GACC,OAAO,CAAC,gBAAgB,CAAC,GACzB,WAAW,CAAC,MAAM,CAAC,CAAC;AAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,KAAK,CAAC,EAAE,EAAE,CAAC;CACZ;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAE7B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAEhC;;;;OAIG;IACH,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAChC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,IAAI,cAAc,CAC/D;IACE,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,GACD;IACE,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,EACH,OAAO,CACR,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,WAAW,CAC1C;IACE,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GAAG,CACA;IACE,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB,GACD;IACE,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,eAAe,CAAC;CAC1B,CACJ,CACF,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,WAAW,CAAC;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,CAAC,CAAC"}
1
+ {"version":3,"file":"2-types.d.ts","sourceRoot":"","sources":["../../src/2-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,SAAS,IAAI,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,KAAK,EAAE,EAAE,CAAC;IAEV;;;;;;;;;;;;OAYG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;IAEnB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAEnB;;;;;;;;;;;;OAYG;IACH,KAAK,EAAE,MAAM,CAAC;IAEd;;;;;;OAMG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;;;;;;;OASG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,CAAC,MAAM,IAAI,kBAAkB,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;AAE9E;;;;;;;;;GASG;AACH,MAAM,MAAM,gBAAgB,GAAG,IAAI,CACjC,kBAAkB,EAClB,OAAO,GAAG,MAAM,GAAG,QAAQ,CAC5B,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,iBAAiB,CAAC,MAAM,IAAI,IAAI,CAC1C,kBAAkB,EAClB,OAAO,GAAG,UAAU,GAAG,SAAS,CACjC,GACC,OAAO,CAAC,gBAAgB,CAAC,GACzB,WAAW,CAAC,MAAM,CAAC,CAAC;AAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,WAAW,eAAe;IAC9B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,KAAK,CAAC,EAAE,EAAE,CAAC;CACZ;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,KAAK,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAE7B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAEhC;;;;OAIG;IACH,OAAO,CAAC,EAAE,kBAAkB,EAAE,CAAC;CAChC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,IAAI,cAAc,CAC/D;IACE,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,GACD;IACE,KAAK,EAAE,KAAK,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,EACH,OAAO,CACR,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,WAAW,CACxC;IACE,KAAK,EAAE,KAAK,CAAC;IACb,OAAO,CAAC,EAAE,SAAS,CAAC;CACrB,GACD;IACE,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,OAAO,EAAE,eAAe,CAAC;CAC1B,CACJ,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG,WAAW,CACzC;IACE,KAAK,EAAE,KAAK,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,GACD;IACE,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,CACJ,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,MAAM,+BAA+B,GAAG,WAAW,CAAC;IACxD,KAAK,CAAC,EAAE,KAAK,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC,CAAC"}
@@ -22,7 +22,4 @@ export declare class GraffitiErrorPatchError extends Error {
22
22
  export declare class GraffitiErrorInvalidUri extends Error {
23
23
  constructor(message?: string);
24
24
  }
25
- export declare class GraffitiErrorOther extends Error {
26
- constructor(message?: string);
27
- }
28
25
  //# sourceMappingURL=3-errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"3-errors.d.ts","sourceRoot":"","sources":["../../src/3-errors.ts"],"names":[],"mappings":"AAAA,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,0BAA2B,SAAQ,KAAK;gBACvC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,4BAA6B,SAAQ,KAAK;gBACzC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,CAAC,EAAE,MAAM;CAK7B"}
1
+ {"version":3,"file":"3-errors.d.ts","sourceRoot":"","sources":["../../src/3-errors.ts"],"names":[],"mappings":"AAAA,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,0BAA2B,SAAQ,KAAK;gBACvC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,2BAA4B,SAAQ,KAAK;gBACxC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,4BAA6B,SAAQ,KAAK;gBACzC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,CAAC,EAAE,MAAM;CAK7B;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACpC,OAAO,CAAC,EAAE,MAAM;CAK7B"}
@@ -1,2 +1,2 @@
1
- import{assert as e,describe as a,it as t,expect as o}from"vitest";import{GraffitiErrorInvalidUri as n,GraffitiErrorNotFound as l,GraffitiErrorForbidden as s,GraffitiErrorInvalidSchema as i,GraffitiErrorSchemaMismatch as r,GraffitiErrorPatchTestFailed as c,GraffitiErrorPatchError as u}from"@graffiti-garden/api";function d(){const e=new Uint8Array(16);return crypto.getRandomValues(e),Array.from(e).map((e=>e.toString(16).padStart(2,"0"))).join("")}function v(){return{value:{[d()]:d()},channels:[d(),d()]}}async function h(a){const t=await a.next();return e(!t.done&&!t.value.error,"result has no value"),t.value.value}const w=e=>{a("URI and location conversion",(()=>{t("location to uri and back",(async()=>{const a=e(),t={name:d(),actor:d(),source:d()},n=a.locationToUri(t),l=a.uriToLocation(n);o(t).toEqual(l)})),t("collision resistance",(async()=>{const a=e(),t={name:d(),actor:d(),source:d()};for(const e of["name","actor","source"]){const n={...t,[e]:d()},l=a.locationToUri(t),s=a.locationToUri(n);o(l).not.toEqual(s)}})),t("random URI should not be a valid location",(async()=>{const a=e();o((()=>a.uriToLocation(""))).toThrow(n)}))}))},p=(e,n,h)=>{a("CRUD",(()=>{t("put, get, delete",(async()=>{const a=e(),t=n(),s={something:"hello, world~ c:"},i=[d(),d()],r=await a.put({value:s,channels:i},t);o(r.value).toEqual({}),o(r.channels).toEqual([]),o(r.allowed).toBeUndefined(),o(r.actor).toEqual(t.actor);const c=await a.get(r,{});o(c.value).toEqual(s),o(c.channels).toEqual([]),o(c.allowed).toBeUndefined(),o(c.name).toEqual(r.name),o(c.actor).toEqual(r.actor),o(c.source).toEqual(r.source),o(c.lastModified).toEqual(r.lastModified);const u={something:"goodbye, world~ :c"},v=await a.put({...r,value:u,channels:[]},t);o(v.value).toEqual(s),o(v.tombstone).toEqual(!0),o(v.name).toEqual(r.name),o(v.actor).toEqual(r.actor),o(v.source).toEqual(r.source),o(v.lastModified).toBeGreaterThanOrEqual(c.lastModified);const h=await a.get(r,{});o(h.value).toEqual(u),o(h.lastModified).toEqual(v.lastModified),o(h.tombstone).toEqual(!1);const w=await a.delete(h,t);o(w.tombstone).toEqual(!0),o(w.value).toEqual(u),o(w.lastModified).toBeGreaterThanOrEqual(v.lastModified),await o(a.get(h,{})).rejects.toThrow(l)})),t("put, get, delete with wrong actor",(async()=>{const a=e(),t=n(),l=h();await o(a.put({value:{},channels:[],actor:l.actor},t)).rejects.toThrow(s);const i=await a.put({value:{},channels:[]},l);await o(a.delete(i,t)).rejects.toThrow(s),await o(a.patch({},i,t)).rejects.toThrow(s)})),t("put and get with schema",(async()=>{const a=e(),t=n(),l={something:"hello",another:42},s=await a.put({value:l,channels:[]},t),i=await a.get(s,{properties:{value:{properties:{something:{type:"string"},another:{type:"integer"}}}}});o(i.value.something).toEqual(l.something),o(i.value.another).toEqual(l.another)})),t("put and get with invalid schema",(async()=>{const a=e(),t=n(),l=await a.put({value:{},channels:[]},t);await o(a.get(l,{properties:{value:{type:"asdf"}}})).rejects.toThrow(i)})),t("put and get with wrong schema",(async()=>{const a=e(),t=n(),l=await a.put({value:{hello:"world"},channels:[]},t);await o(a.get(l,{properties:{value:{properties:{hello:{type:"number"}}}}})).rejects.toThrow(r)})),t("put and get with empty access control",(async()=>{const a=e(),t=n(),l=h(),s={um:"hi"},i=[d()],r=[d()],c=await a.put({value:s,allowed:i,channels:r},t),u=await a.get(c,{},t);o(u.value).toEqual(s),o(u.allowed).toEqual(i),o(u.channels).toEqual(r),await o(a.get(c,{})).rejects.toThrow(),await o(a.get(c,{},l)).rejects.toThrow()})),t("put and get with specific access control",(async()=>{const a=e(),t=n(),l=h(),s={um:"hi"},i=[d(),l.actor,d()],r=[d()],c=await a.put({value:s,allowed:i,channels:r},t),u=await a.get(c,{},t);o(u.value).toEqual(s),o(u.allowed).toEqual(i),o(u.channels).toEqual(r),await o(a.get(c,{})).rejects.toThrow();const v=await a.get(c,{},l);o(v.value).toEqual(s),o(v.allowed).toEqual([l.actor]),o(v.channels).toEqual([])})),t("patch value",(async()=>{const a=e(),t=n(),l={something:"hello, world~ c:"},s=await a.put({value:l,channels:[]},t),i=await a.patch({value:[{op:"replace",path:"/something",value:"goodbye, world~ :c"}]},s,t);o(i.value).toEqual(l),o(i.tombstone).toBe(!0);const r=await a.get(s,{});o(r.value).toEqual({something:"goodbye, world~ :c"}),o(i.lastModified).toBe(r.lastModified),await a.delete(s,t)})),t("deep patch",(async()=>{const a=e(),t=n(),l={something:{another:{somethingElse:"hello"}}},s=await a.put({value:l,channels:[]},t),i=await a.patch({value:[{op:"replace",path:"/something/another/somethingElse",value:"goodbye"}]},s,t),r=await a.get(s,{});o(i.value).toEqual(l),o(r.value).toEqual({something:{another:{somethingElse:"goodbye"}}})})),t("patch channels",(async()=>{const a=e(),t=n(),l=[d()],s=[d()],i=await a.put({value:{},channels:l},t),r={channels:[{op:"replace",path:"/0",value:s[0]}]},c=await a.patch(r,i,t);o(c.channels).toEqual(l);const u=await a.get(i,{},t);o(u.channels).toEqual(s),await a.delete(i,t)})),t("patch 'increment' with test",(async()=>{const a=e(),t=n(),l=await a.put({value:{counter:1},channels:[]},t),s=await a.patch({value:[{op:"test",path:"/counter",value:1},{op:"replace",path:"/counter",value:2}]},l,t);o(s.value).toEqual({counter:1});const i=await a.get(s,{properties:{value:{properties:{counter:{type:"integer"}}}}});o(i.value.counter).toEqual(2),await o(a.patch({value:[{op:"test",path:"/counter",value:1},{op:"replace",path:"/counter",value:3}]},l,t)).rejects.toThrow(c)})),t("invalid patch",(async()=>{const a=e(),t=n(),l=v(),s=await a.put(l,t);await o(a.patch({value:[{op:"add",path:"/root",value:[]},{op:"add",path:"/root/2",value:2}]},s,t)).rejects.toThrow(u)})),t("patch channels to be wrong",(async()=>{const a=e(),t=n(),l=v();l.allowed=[d()];const s=await a.put(l,t),i=[{channels:[{op:"replace",path:"",value:null}]},{channels:[{op:"replace",path:"",value:{}}]},{channels:[{op:"replace",path:"",value:["hello",["hi"]]}]},{channels:[{op:"add",path:"/0",value:1}]},{value:[{op:"replace",path:"",value:"not an object"}]},{value:[{op:"replace",path:"",value:null}]},{value:[{op:"replace",path:"",value:[]}]},{allowed:[{op:"replace",path:"",value:{}}]},{allowed:[{op:"replace",path:"",value:["hello",["hi"]]}]}];for(const e of i)await o(a.patch(e,s,t)).rejects.toThrow(u);const r=await a.get(s,{},t);o(r.value).toEqual(l.value),o(r.channels).toEqual(l.channels),o(r.allowed).toEqual(l.allowed),o(r.lastModified).toEqual(s.lastModified)}))}))},m=(e,n,l)=>{a("synchronize",(()=>{t("get",(async()=>{const a=e(),t=n(),l=v(),s=l.channels.slice(1),i=await a.put(l,t),r=e(),c=r.synchronize(s,{}).next(),u=await r.get(i,{},t),d=(await c).value;if(!d||d.error)throw new Error("Error in synchronize");o(d.value.value).toEqual(l.value),o(d.value.channels).toEqual(s),o(d.value.tombstone).toBe(!1),o(d.value.lastModified).toEqual(u.lastModified)})),t("put",(async()=>{const a=e(),t=n(),l=d(),s=d(),i=d(),r={hello:"world"},c=[l,i],u=await a.put({value:r,channels:c},t),v=a.synchronize([l],{}).next(),h=a.synchronize([s],{}).next(),w=a.synchronize([i],{}).next(),p={goodbye:"world"},m=[s,i];await a.put({...u,value:p,channels:m},t);const E=(await v).value,q=(await h).value,y=(await w).value;if(!E||E.error||!q||q.error||!y||y.error)throw new Error("Error in synchronize");o(E.value.value).toEqual(r),o(E.value.channels).toEqual([l]),o(E.value.tombstone).toBe(!0),o(q.value.value).toEqual(p),o(q.value.channels).toEqual([s]),o(q.value.tombstone).toBe(!1),o(y.value.value).toEqual(p),o(y.value.channels).toEqual([i]),o(y.value.tombstone).toBe(!1),o(E.value.lastModified).toEqual(q.value.lastModified),o(y.value.lastModified).toEqual(q.value.lastModified)})),t("patch",(async()=>{const a=e(),t=n(),l=d(),s=d(),i=d(),r={hello:"world"},c=[l,i],u=await a.put({value:r,channels:c},t),v=a.synchronize([l],{}).next(),h=a.synchronize([s],{}).next(),w=a.synchronize([i],{}).next();await a.patch({value:[{op:"add",path:"/something",value:"new value"}],channels:[{op:"add",path:"/-",value:s},{op:"remove",path:`/${c.indexOf(l)}`}]},u,t);const p=(await v).value,m=(await h).value,E=(await w).value;if(!p||p.error||!m||m.error||!E||E.error)throw new Error("Error in synchronize");const q={...r,something:"new value"};o(p.value.value).toEqual(r),o(p.value.channels).toEqual([l]),o(p.value.tombstone).toBe(!0),o(m.value.value).toEqual(q),o(m.value.channels).toEqual([s]),o(m.value.tombstone).toBe(!1),o(E.value.value).toEqual(q),o(E.value.channels).toEqual([i]),o(E.value.tombstone).toBe(!1),o(p.value.lastModified).toEqual(m.value.lastModified),o(E.value.lastModified).toEqual(m.value.lastModified)})),t("delete",(async()=>{const a=e(),t=n(),l=[d(),d(),d()],s={hello:"world"},i=[d(),...l.slice(1)],r=await a.put({value:s,channels:i},t),c=a.synchronize(l,{}).next();a.delete(r,t);const u=(await c).value;if(!u||u.error)throw new Error("Error in synchronize");o(u.value.tombstone).toBe(!0),o(u.value.value).toEqual(s),o(u.value.channels).toEqual(l.filter((e=>i.includes(e))))})),t("not allowed",(async()=>{const a=e(),t=n(),s=l(),i=[d(),d(),d()],r=i.slice(1),c=a.synchronize(r,{},t).next(),u=a.synchronize(r,{},s).next(),v=a.synchronize(r,{}).next(),h={hello:"world"},w=[d(),s.actor];await a.put({value:h,channels:i,allowed:w},t),await o(Promise.race([v,new Promise(((e,a)=>setTimeout(a,100,"Timeout")))])).rejects.toThrow("Timeout");const p=(await c).value,m=(await u).value;if(!p||p.error||!m||m.error)throw new Error("Error in synchronize");o(p.value.value).toEqual(h),o(p.value.allowed).toEqual(w),o(p.value.channels).toEqual(i),o(m.value.value).toEqual(h),o(m.value.allowed).toEqual([s.actor]),o(m.value.channels).toEqual(r)}))}))},E=(n,l,s)=>{a("discover",(()=>{t("discover nothing",(async()=>{const e=n().discover([],{});o(await e.next()).toHaveProperty("done",!0)})),t("discover single",(async()=>{const e=n(),a=l(),t=v(),s=await e.put(t,a),i=[d(),t.channels[0]],r=e.discover(i,{}),c=await h(r);o(c.value).toEqual(t.value),o(c.channels).toEqual([t.channels[0]]),o(c.allowed).toBeUndefined(),o(c.actor).toEqual(a.actor),o(c.tombstone).toBe(!1),o(c.lastModified).toEqual(s.lastModified);const u=await r.next();o(u.done).toBe(!0)})),t("discover wrong channel",(async()=>{const e=n(),a=l(),t=v();await e.put(t,a);const s=e.discover([d()],{});await o(s.next()).resolves.toHaveProperty("done",!0)})),t("discover not allowed",(async()=>{const e=n(),a=l(),t=s(),i=v();i.allowed=[d(),d()];const r=await e.put(i,a),c=e.discover(i.channels,{},a),u=await h(c);o(u.value).toEqual(i.value),o(u.channels).toEqual(i.channels),o(u.allowed).toEqual(i.allowed),o(u.actor).toEqual(a.actor),o(u.tombstone).toBe(!1),o(u.lastModified).toEqual(r.lastModified);const w=e.discover(i.channels,{},t);o(await w.next()).toHaveProperty("done",!0);const p=e.discover(i.channels,{});o(await p.next()).toHaveProperty("done",!0)})),t("discover allowed",(async()=>{const e=n(),a=l(),t=s(),i=v();i.allowed=[d(),t.actor,d()];const r=await e.put(i,a),c=e.discover(i.channels,{},t),u=await h(c);o(u.value).toEqual(i.value),o(u.allowed).toEqual([t.actor]),o(u.channels).toEqual(i.channels),o(u.actor).toEqual(a.actor),o(u.tombstone).toBe(!1),o(u.lastModified).toEqual(r.lastModified)}));for(const e of["name","actor","lastModified"])t(`discover for ${e}`,(async()=>{const a=n(),t=l(),i=s(),r=v(),c=await a.put(r,t),u=v();u.channels=r.channels,await new Promise((e=>setTimeout(e,20)));const d=await a.put(u,i),w=a.discover(r.channels,{properties:{[e]:{enum:[c[e]]}}}),p=await h(w);o(p.name).toEqual(c.name),o(p.name).not.toEqual(d.name),o(p.value).toEqual(r.value),await o(w.next()).resolves.toHaveProperty("done",!0)}));t("discover with lastModified range",(async()=>{const e=n(),a=l(),t=v(),s=await e.put(t,a);await new Promise((e=>setTimeout(e,20)));const i=await e.put(t,a);o(s.name).not.toEqual(i.name),o(s.lastModified).toBeLessThan(i.lastModified);const r=e.discover([t.channels[0]],{properties:{lastModified:{minimum:i.lastModified,exclusiveMinimum:!0}}});o(await r.next()).toHaveProperty("done",!0);const c=e.discover([t.channels[0]],{properties:{lastModified:{minimum:i.lastModified-.1,exclusiveMinimum:!0}}}),u=await h(c);o(u.name).toEqual(i.name),o(await c.next()).toHaveProperty("done",!0);const d=e.discover(t.channels,{properties:{value:{},lastModified:{minimum:i.lastModified}}}),w=await h(d);o(w.name).toEqual(i.name),o(await d.next()).toHaveProperty("done",!0);const p=e.discover(t.channels,{properties:{lastModified:{minimum:i.lastModified+.1}}});o(await p.next()).toHaveProperty("done",!0);const m=e.discover(t.channels,{properties:{lastModified:{maximum:s.lastModified,exclusiveMaximum:!0}}});o(await m.next()).toHaveProperty("done",!0);const E=e.discover(t.channels,{properties:{lastModified:{maximum:s.lastModified+.1,exclusiveMaximum:!0}}}),q=await h(E);o(q.name).toEqual(s.name),o(await E.next()).toHaveProperty("done",!0);const y=e.discover(t.channels,{properties:{lastModified:{maximum:s.lastModified}}}),f=await h(y);o(f.name).toEqual(s.name),o(await y.next()).toHaveProperty("done",!0);const g=e.discover(t.channels,{properties:{lastModified:{maximum:s.lastModified-.1}}});o(await g.next()).toHaveProperty("done",!0)})),t("discover schema allowed, as and not as owner",(async()=>{const e=n(),a=l(),t=s(),i=v();i.allowed=[d(),t.actor,d()],await e.put(i,a);const r=e.discover(i.channels,{properties:{allowed:{minItems:3,not:{items:{not:{enum:[t.actor]}}}}}},a),c=await h(r);o(c.value).toEqual(i.value),await o(r.next()).resolves.toHaveProperty("done",!0);const u=e.discover(i.channels,{properties:{allowed:{minItems:3}}},t);await o(u.next()).resolves.toHaveProperty("done",!0);const w=e.discover(i.channels,{properties:{allowed:{not:{items:{not:{enum:[i.channels[0]]}}}}}},t);await o(w.next()).resolves.toHaveProperty("done",!0);const p=e.discover(i.channels,{properties:{allowed:{maxItems:1,not:{items:{not:{enum:[t.actor]}}}}}},t),m=await h(p);o(m.value).toEqual(i.value),await o(p.next()).resolves.toHaveProperty("done",!0)})),t("discover schema channels, as and not as owner",(async()=>{const e=n(),a=l(),t=s(),i=v();i.channels=[d(),d(),d()],await e.put(i,a);const r=e.discover([i.channels[0],i.channels[2]],{properties:{channels:{minItems:3,not:{items:{not:{enum:[i.channels[1]]}}}}}},a),c=await h(r);o(c.value).toEqual(i.value),await o(r.next()).resolves.toHaveProperty("done",!0);const u=e.discover([i.channels[0],i.channels[2]],{properties:{channels:{minItems:3}}},t);await o(u.next()).resolves.toHaveProperty("done",!0);const w=e.discover([i.channels[0],i.channels[2]],{properties:{channels:{not:{items:{not:{enum:[i.channels[1]]}}}}}},t);await o(w.next()).resolves.toHaveProperty("done",!0);const p=e.discover([i.channels[0],i.channels[2]],{properties:{allowed:{maxItems:2,not:{items:{not:{enum:[i.channels[2]]}}}}}},t),m=await h(p);o(m.value).toEqual(i.value),await o(p.next()).resolves.toHaveProperty("done",!0)})),t("discover query for empty allowed",(async()=>{const e=n(),a=l(),t=v(),s={not:{required:["allowed"]}};await e.put(t,a);const i=e.discover(t.channels,s,a),r=await h(i);o(r.value).toEqual(t.value),o(r.allowed).toBeUndefined(),await o(i.next()).resolves.toHaveProperty("done",!0);const c=v();c.allowed=[],await e.put(c,a);const u=e.discover(c.channels,s,a);await o(u.next()).resolves.toHaveProperty("done",!0)})),t("discover query for values",(async()=>{const a=n(),t=l(),s=v();s.value={test:d()},await a.put(s,t);const i=v();i.channels=s.channels,i.value={test:d(),something:d()},await a.put(i,t);const r=v();r.channels=s.channels,r.value={other:d(),something:d()},await a.put(r,t);const c=new Map;for(const t of["test","something","other"]){let o=0;for await(const n of a.discover(s.channels,{properties:{value:{required:[t]}}}))e(!n.error,"result has error"),t in n.value.value&&o++;c.set(t,o)}o(c.get("test")).toBe(2),o(c.get("something")).toBe(2),o(c.get("other")).toBe(1)})),t("discover for deleted content",(async()=>{const e=n(),a=l(),t=v(),s=await e.put(t,a),i=await e.delete(s,a),r=e.discover(t.channels,{}),c=await h(r);o(c.tombstone).toBe(!0),o(c.value).toEqual(t.value),o(c.channels).toEqual(t.channels),o(c.actor).toEqual(a.actor),o(c.lastModified).toEqual(i.lastModified),await o(r.next()).resolves.toHaveProperty("done",!0)})),t("discover for replaced channels",(async()=>{for(let e=0;e<10;e++){const e=n(),a=l(),t=v(),s=await e.put(t,a),i=v(),r=await e.put({...s,...i},a),c=e.discover(t.channels,{}),u=await h(c);await o(c.next()).resolves.toHaveProperty("done",!0);const d=e.discover(i.channels,{}),w=await h(d);await o(d.next()).resolves.toHaveProperty("done",!0),s.lastModified===r.lastModified?(o(u.tombstone||w.tombstone).toBe(!0),o(u.tombstone&&w.tombstone).toBe(!1)):(o(u.tombstone).toBe(!0),o(u.value).toEqual(t.value),o(u.channels).toEqual(t.channels),o(u.lastModified).toEqual(r.lastModified),o(w.tombstone).toBe(!1),o(w.value).toEqual(i.value),o(w.channels).toEqual(i.channels),o(w.lastModified).toEqual(r.lastModified))}})),t("discover for patched allowed",(async()=>{const e=n(),a=l(),t=v(),s=await e.put(t,a);await e.patch({allowed:[{op:"add",path:"",value:[]}]},s,a);const i=e.discover(t.channels,{}),r=await h(i);o(r.tombstone).toBe(!0),o(r.value).toEqual(t.value),o(r.channels).toEqual(t.channels),o(r.allowed).toBeUndefined(),await o(i.next()).resolves.toHaveProperty("done",!0)})),t("put concurrently and discover one",(async()=>{const a=n(),t=l(),s=v();s.name=d();const i=Array(100).fill(0).map((()=>a.put(s,t)));await Promise.all(i);const r=a.discover(s.channels,{});let c=0,u=0;for await(const a of r)e(!a.error,"result has error"),a.value.tombstone?c++:u++;o(c).toBe(99),o(u).toBe(1)}))}))};export{p as graffitiCRUDTests,E as graffitiDiscoverTests,w as graffitiLocationTests,m as graffitiSynchronizeTests};
1
+ import{assert as e,describe as a,it as t,expect as o}from"vitest";import{GraffitiErrorInvalidUri as n,GraffitiErrorNotFound as l,GraffitiErrorForbidden as s,GraffitiErrorInvalidSchema as i,GraffitiErrorSchemaMismatch as r,GraffitiErrorPatchTestFailed as c,GraffitiErrorPatchError as u}from"@graffiti-garden/api";function d(){const e=new Uint8Array(16);return crypto.getRandomValues(e),Array.from(e).map((e=>e.toString(16).padStart(2,"0"))).join("")}function v(){return{value:{[d()]:d()},channels:[d(),d()]}}async function h(a){const t=await a.next();return e(!t.done&&!t.value.error,"result has no value"),t.value.value}const w=e=>{a("URI and location conversion",(()=>{t("location to uri and back",(async()=>{const a=e(),t={name:d(),actor:d(),source:d()},n=a.locationToUri(t),l=a.uriToLocation(n);o(t).toEqual(l)})),t("collision resistance",(async()=>{const a=e(),t={name:d(),actor:d(),source:d()};for(const e of["name","actor","source"]){const n={...t,[e]:d()},l=a.locationToUri(t),s=a.locationToUri(n);o(l).not.toEqual(s)}})),t("random URI should not be a valid location",(async()=>{const a=e();o((()=>a.uriToLocation(""))).toThrow(n)}))}))},p=(e,n,h)=>{a("CRUD",{timeout:2e4},(()=>{t("put, get, delete",(async()=>{const a=e(),t=n(),s={something:"hello, world~ c:"},i=[d(),d()],r=await a.put({value:s,channels:i},t);o(r.value).toEqual({}),o(r.channels).toEqual([]),o(r.allowed).toBeUndefined(),o(r.actor).toEqual(t.actor);const c=await a.get(r,{});o(c.value).toEqual(s),o(c.channels).toEqual([]),o(c.allowed).toBeUndefined(),o(c.name).toEqual(r.name),o(c.actor).toEqual(r.actor),o(c.source).toEqual(r.source),o(c.lastModified).toEqual(r.lastModified);const u={something:"goodbye, world~ :c"},v=await a.put({...r,value:u,channels:[]},t);o(v.value).toEqual(s),o(v.tombstone).toEqual(!0),o(v.name).toEqual(r.name),o(v.actor).toEqual(r.actor),o(v.source).toEqual(r.source),o(v.lastModified).toBeGreaterThanOrEqual(c.lastModified);const h=await a.get(r,{});o(h.value).toEqual(u),o(h.lastModified).toEqual(v.lastModified),o(h.tombstone).toEqual(!1);const w=await a.delete(h,t);o(w.tombstone).toEqual(!0),o(w.value).toEqual(u),o(w.lastModified).toBeGreaterThanOrEqual(v.lastModified),await o(a.get(h,{})).rejects.toThrow(l)})),t("put, get, delete with wrong actor",(async()=>{const a=e(),t=n(),l=h();await o(a.put({value:{},channels:[],actor:l.actor},t)).rejects.toThrow(s);const i=await a.put({value:{},channels:[]},l);await o(a.delete(i,t)).rejects.toThrow(s),await o(a.patch({},i,t)).rejects.toThrow(s)})),t("put and get with schema",(async()=>{const a=e(),t=n(),l={something:"hello",another:42},s=await a.put({value:l,channels:[]},t),i=await a.get(s,{properties:{value:{properties:{something:{type:"string"},another:{type:"integer"}}}}});o(i.value.something).toEqual(l.something),o(i.value.another).toEqual(l.another)})),t("put and get with invalid schema",(async()=>{const a=e(),t=n(),l=await a.put({value:{},channels:[]},t);await o(a.get(l,{properties:{value:{type:"asdf"}}})).rejects.toThrow(i)})),t("put and get with wrong schema",(async()=>{const a=e(),t=n(),l=await a.put({value:{hello:"world"},channels:[]},t);await o(a.get(l,{properties:{value:{properties:{hello:{type:"number"}}}}})).rejects.toThrow(r)})),t("put and get with empty access control",(async()=>{const a=e(),t=n(),l=h(),s={um:"hi"},i=[d()],r=[d()],c=await a.put({value:s,allowed:i,channels:r},t),u=await a.get(c,{},t);o(u.value).toEqual(s),o(u.allowed).toEqual(i),o(u.channels).toEqual(r),await o(a.get(c,{})).rejects.toThrow(),await o(a.get(c,{},l)).rejects.toThrow()})),t("put and get with specific access control",(async()=>{const a=e(),t=n(),l=h(),s={um:"hi"},i=[d(),l.actor,d()],r=[d()],c=await a.put({value:s,allowed:i,channels:r},t),u=await a.get(c,{},t);o(u.value).toEqual(s),o(u.allowed).toEqual(i),o(u.channels).toEqual(r),await o(a.get(c,{})).rejects.toThrow();const v=await a.get(c,{},l);o(v.value).toEqual(s),o(v.allowed).toEqual([l.actor]),o(v.channels).toEqual([])})),t("patch value",(async()=>{const a=e(),t=n(),l={something:"hello, world~ c:"},s=await a.put({value:l,channels:[]},t),i=await a.patch({value:[{op:"replace",path:"/something",value:"goodbye, world~ :c"}]},s,t);o(i.value).toEqual(l),o(i.tombstone).toBe(!0);const r=await a.get(s,{});o(r.value).toEqual({something:"goodbye, world~ :c"}),o(i.lastModified).toBe(r.lastModified),await a.delete(s,t)})),t("deep patch",(async()=>{const a=e(),t=n(),l={something:{another:{somethingElse:"hello"}}},s=await a.put({value:l,channels:[]},t),i=await a.patch({value:[{op:"replace",path:"/something/another/somethingElse",value:"goodbye"}]},s,t),r=await a.get(s,{});o(i.value).toEqual(l),o(r.value).toEqual({something:{another:{somethingElse:"goodbye"}}})})),t("patch channels",(async()=>{const a=e(),t=n(),l=[d()],s=[d()],i=await a.put({value:{},channels:l},t),r={channels:[{op:"replace",path:"/0",value:s[0]}]},c=await a.patch(r,i,t);o(c.channels).toEqual(l);const u=await a.get(i,{},t);o(u.channels).toEqual(s),await a.delete(i,t)})),t("patch 'increment' with test",(async()=>{const a=e(),t=n(),l=await a.put({value:{counter:1},channels:[]},t),s=await a.patch({value:[{op:"test",path:"/counter",value:1},{op:"replace",path:"/counter",value:2}]},l,t);o(s.value).toEqual({counter:1});const i=await a.get(s,{properties:{value:{properties:{counter:{type:"integer"}}}}});o(i.value.counter).toEqual(2),await o(a.patch({value:[{op:"test",path:"/counter",value:1},{op:"replace",path:"/counter",value:3}]},l,t)).rejects.toThrow(c)})),t("invalid patch",(async()=>{const a=e(),t=n(),l=v(),s=await a.put(l,t);await o(a.patch({value:[{op:"add",path:"/root",value:[]},{op:"add",path:"/root/2",value:2}]},s,t)).rejects.toThrow(u)})),t("patch channels to be wrong",(async()=>{const a=e(),t=n(),l=v();l.allowed=[d()];const s=await a.put(l,t),i=[{channels:[{op:"replace",path:"",value:null}]},{channels:[{op:"replace",path:"",value:{}}]},{channels:[{op:"replace",path:"",value:["hello",["hi"]]}]},{channels:[{op:"add",path:"/0",value:1}]},{value:[{op:"replace",path:"",value:"not an object"}]},{value:[{op:"replace",path:"",value:null}]},{value:[{op:"replace",path:"",value:[]}]},{allowed:[{op:"replace",path:"",value:{}}]},{allowed:[{op:"replace",path:"",value:["hello",["hi"]]}]}];for(const e of i)await o(a.patch(e,s,t)).rejects.toThrow(u);const r=await a.get(s,{},t);o(r.value).toEqual(l.value),o(r.channels).toEqual(l.channels),o(r.allowed).toEqual(l.allowed),o(r.lastModified).toEqual(s.lastModified)}))}))},m=(e,n,l)=>{a("synchronize",(()=>{t("get",(async()=>{const a=e(),t=n(),l=v(),s=l.channels.slice(1),i=await a.put(l,t),r=e(),c=r.synchronize(s,{}).next(),u=await r.get(i,{},t),d=(await c).value;if(!d||d.error)throw new Error("Error in synchronize");o(d.value.value).toEqual(l.value),o(d.value.channels).toEqual(s),o(d.value.tombstone).toBe(!1),o(d.value.lastModified).toEqual(u.lastModified)})),t("put",(async()=>{const a=e(),t=n(),l=d(),s=d(),i=d(),r={hello:"world"},c=[l,i],u=await a.put({value:r,channels:c},t),v=a.synchronize([l],{}).next(),h=a.synchronize([s],{}).next(),w=a.synchronize([i],{}).next(),p={goodbye:"world"},m=[s,i];await a.put({...u,value:p,channels:m},t);const E=(await v).value,q=(await h).value,y=(await w).value;if(!E||E.error||!q||q.error||!y||y.error)throw new Error("Error in synchronize");o(E.value.value).toEqual(r),o(E.value.channels).toEqual([l]),o(E.value.tombstone).toBe(!0),o(q.value.value).toEqual(p),o(q.value.channels).toEqual([s]),o(q.value.tombstone).toBe(!1),o(y.value.value).toEqual(p),o(y.value.channels).toEqual([i]),o(y.value.tombstone).toBe(!1),o(E.value.lastModified).toEqual(q.value.lastModified),o(y.value.lastModified).toEqual(q.value.lastModified)})),t("patch",(async()=>{const a=e(),t=n(),l=d(),s=d(),i=d(),r={hello:"world"},c=[l,i],u=await a.put({value:r,channels:c},t),v=a.synchronize([l],{}).next(),h=a.synchronize([s],{}).next(),w=a.synchronize([i],{}).next();await a.patch({value:[{op:"add",path:"/something",value:"new value"}],channels:[{op:"add",path:"/-",value:s},{op:"remove",path:`/${c.indexOf(l)}`}]},u,t);const p=(await v).value,m=(await h).value,E=(await w).value;if(!p||p.error||!m||m.error||!E||E.error)throw new Error("Error in synchronize");const q={...r,something:"new value"};o(p.value.value).toEqual(r),o(p.value.channels).toEqual([l]),o(p.value.tombstone).toBe(!0),o(m.value.value).toEqual(q),o(m.value.channels).toEqual([s]),o(m.value.tombstone).toBe(!1),o(E.value.value).toEqual(q),o(E.value.channels).toEqual([i]),o(E.value.tombstone).toBe(!1),o(p.value.lastModified).toEqual(m.value.lastModified),o(E.value.lastModified).toEqual(m.value.lastModified)})),t("delete",(async()=>{const a=e(),t=n(),l=[d(),d(),d()],s={hello:"world"},i=[d(),...l.slice(1)],r=await a.put({value:s,channels:i},t),c=a.synchronize(l,{}).next();a.delete(r,t);const u=(await c).value;if(!u||u.error)throw new Error("Error in synchronize");o(u.value.tombstone).toBe(!0),o(u.value.value).toEqual(s),o(u.value.channels).toEqual(l.filter((e=>i.includes(e))))})),t("not allowed",(async()=>{const a=e(),t=n(),s=l(),i=[d(),d(),d()],r=i.slice(1),c=a.synchronize(r,{},t).next(),u=a.synchronize(r,{},s).next(),v=a.synchronize(r,{}).next(),h={hello:"world"},w=[d(),s.actor];await a.put({value:h,channels:i,allowed:w},t),await o(Promise.race([v,new Promise(((e,a)=>setTimeout(a,100,"Timeout")))])).rejects.toThrow("Timeout");const p=(await c).value,m=(await u).value;if(!p||p.error||!m||m.error)throw new Error("Error in synchronize");o(p.value.value).toEqual(h),o(p.value.allowed).toEqual(w),o(p.value.channels).toEqual(i),o(m.value.value).toEqual(h),o(m.value.allowed).toEqual([s.actor]),o(m.value.channels).toEqual(r)}))}))},E=(n,l,s)=>{a("discover",{timeout:2e4},(()=>{t("discover nothing",(async()=>{const e=n().discover([],{});o(await e.next()).toHaveProperty("done",!0)})),t("discover single",(async()=>{const e=n(),a=l(),t=v(),s=await e.put(t,a),i=[d(),t.channels[0]],r=e.discover(i,{}),c=await h(r);o(c.value).toEqual(t.value),o(c.channels).toEqual([t.channels[0]]),o(c.allowed).toBeUndefined(),o(c.actor).toEqual(a.actor),o(c.tombstone).toBe(!1),o(c.lastModified).toEqual(s.lastModified);const u=await r.next();o(u.done).toBe(!0)})),t("discover wrong channel",(async()=>{const e=n(),a=l(),t=v();await e.put(t,a);const s=e.discover([d()],{});await o(s.next()).resolves.toHaveProperty("done",!0)})),t("discover not allowed",(async()=>{const e=n(),a=l(),t=s(),i=v();i.allowed=[d(),d()];const r=await e.put(i,a),c=e.discover(i.channels,{},a),u=await h(c);o(u.value).toEqual(i.value),o(u.channels).toEqual(i.channels),o(u.allowed).toEqual(i.allowed),o(u.actor).toEqual(a.actor),o(u.tombstone).toBe(!1),o(u.lastModified).toEqual(r.lastModified);const w=e.discover(i.channels,{},t);o(await w.next()).toHaveProperty("done",!0);const p=e.discover(i.channels,{});o(await p.next()).toHaveProperty("done",!0)})),t("discover allowed",(async()=>{const e=n(),a=l(),t=s(),i=v();i.allowed=[d(),t.actor,d()];const r=await e.put(i,a),c=e.discover(i.channels,{},t),u=await h(c);o(u.value).toEqual(i.value),o(u.allowed).toEqual([t.actor]),o(u.channels).toEqual(i.channels),o(u.actor).toEqual(a.actor),o(u.tombstone).toBe(!1),o(u.lastModified).toEqual(r.lastModified)}));for(const e of["name","actor","lastModified"])t(`discover for ${e}`,(async()=>{const a=n(),t=l(),i=s(),r=v(),c=await a.put(r,t),u=v();u.channels=r.channels,await new Promise((e=>setTimeout(e,20)));const d=await a.put(u,i),w=a.discover(r.channels,{properties:{[e]:{enum:[c[e]]}}}),p=await h(w);o(p.name).toEqual(c.name),o(p.name).not.toEqual(d.name),o(p.value).toEqual(r.value),await o(w.next()).resolves.toHaveProperty("done",!0)}));t("discover with lastModified range",(async()=>{const e=n(),a=l(),t=v(),s=await e.put(t,a);await new Promise((e=>setTimeout(e,20)));const i=await e.put(t,a);o(s.name).not.toEqual(i.name),o(s.lastModified).toBeLessThan(i.lastModified);const r=e.discover([t.channels[0]],{properties:{lastModified:{minimum:i.lastModified,exclusiveMinimum:!0}}});o(await r.next()).toHaveProperty("done",!0);const c=e.discover([t.channels[0]],{properties:{lastModified:{minimum:i.lastModified-.1,exclusiveMinimum:!0}}}),u=await h(c);o(u.name).toEqual(i.name),o(await c.next()).toHaveProperty("done",!0);const d=e.discover(t.channels,{properties:{value:{},lastModified:{minimum:i.lastModified}}}),w=await h(d);o(w.name).toEqual(i.name),o(await d.next()).toHaveProperty("done",!0);const p=e.discover(t.channels,{properties:{lastModified:{minimum:i.lastModified+.1}}});o(await p.next()).toHaveProperty("done",!0);const m=e.discover(t.channels,{properties:{lastModified:{maximum:s.lastModified,exclusiveMaximum:!0}}});o(await m.next()).toHaveProperty("done",!0);const E=e.discover(t.channels,{properties:{lastModified:{maximum:s.lastModified+.1,exclusiveMaximum:!0}}}),q=await h(E);o(q.name).toEqual(s.name),o(await E.next()).toHaveProperty("done",!0);const y=e.discover(t.channels,{properties:{lastModified:{maximum:s.lastModified}}}),f=await h(y);o(f.name).toEqual(s.name),o(await y.next()).toHaveProperty("done",!0);const g=e.discover(t.channels,{properties:{lastModified:{maximum:s.lastModified-.1}}});o(await g.next()).toHaveProperty("done",!0)})),t("discover schema allowed, as and not as owner",(async()=>{const e=n(),a=l(),t=s(),i=v();i.allowed=[d(),t.actor,d()],await e.put(i,a);const r=e.discover(i.channels,{properties:{allowed:{minItems:3,not:{items:{not:{enum:[t.actor]}}}}}},a),c=await h(r);o(c.value).toEqual(i.value),await o(r.next()).resolves.toHaveProperty("done",!0);const u=e.discover(i.channels,{properties:{allowed:{minItems:3}}},t);await o(u.next()).resolves.toHaveProperty("done",!0);const w=e.discover(i.channels,{properties:{allowed:{not:{items:{not:{enum:[i.channels[0]]}}}}}},t);await o(w.next()).resolves.toHaveProperty("done",!0);const p=e.discover(i.channels,{properties:{allowed:{maxItems:1,not:{items:{not:{enum:[t.actor]}}}}}},t),m=await h(p);o(m.value).toEqual(i.value),await o(p.next()).resolves.toHaveProperty("done",!0)})),t("discover schema channels, as and not as owner",(async()=>{const e=n(),a=l(),t=s(),i=v();i.channels=[d(),d(),d()],await e.put(i,a);const r=e.discover([i.channels[0],i.channels[2]],{properties:{channels:{minItems:3,not:{items:{not:{enum:[i.channels[1]]}}}}}},a),c=await h(r);o(c.value).toEqual(i.value),await o(r.next()).resolves.toHaveProperty("done",!0);const u=e.discover([i.channels[0],i.channels[2]],{properties:{channels:{minItems:3}}},t);await o(u.next()).resolves.toHaveProperty("done",!0);const w=e.discover([i.channels[0],i.channels[2]],{properties:{channels:{not:{items:{not:{enum:[i.channels[1]]}}}}}},t);await o(w.next()).resolves.toHaveProperty("done",!0);const p=e.discover([i.channels[0],i.channels[2]],{properties:{allowed:{maxItems:2,not:{items:{not:{enum:[i.channels[2]]}}}}}},t),m=await h(p);o(m.value).toEqual(i.value),await o(p.next()).resolves.toHaveProperty("done",!0)})),t("discover query for empty allowed",(async()=>{const e=n(),a=l(),t=v(),s={not:{required:["allowed"]}};await e.put(t,a);const i=e.discover(t.channels,s,a),r=await h(i);o(r.value).toEqual(t.value),o(r.allowed).toBeUndefined(),await o(i.next()).resolves.toHaveProperty("done",!0);const c=v();c.allowed=[],await e.put(c,a);const u=e.discover(c.channels,s,a);await o(u.next()).resolves.toHaveProperty("done",!0)})),t("discover query for values",(async()=>{const a=n(),t=l(),s=v();s.value={test:d()},await a.put(s,t);const i=v();i.channels=s.channels,i.value={test:d(),something:d()},await a.put(i,t);const r=v();r.channels=s.channels,r.value={other:d(),something:d()},await a.put(r,t);const c=new Map;for(const t of["test","something","other"]){let o=0;for await(const n of a.discover(s.channels,{properties:{value:{required:[t]}}}))e(!n.error,"result has error"),t in n.value.value&&o++;c.set(t,o)}o(c.get("test")).toBe(2),o(c.get("something")).toBe(2),o(c.get("other")).toBe(1)})),t("discover for deleted content",(async()=>{const e=n(),a=l(),t=v(),s=await e.put(t,a),i=await e.delete(s,a),r=e.discover(t.channels,{}),c=await h(r);o(c.tombstone).toBe(!0),o(c.value).toEqual(t.value),o(c.channels).toEqual(t.channels),o(c.actor).toEqual(a.actor),o(c.lastModified).toEqual(i.lastModified),await o(r.next()).resolves.toHaveProperty("done",!0)})),t("discover for replaced channels",(async()=>{for(let e=0;e<10;e++){const e=n(),a=l(),t=v(),s=await e.put(t,a),i=v(),r=await e.put({...s,...i},a),c=e.discover(t.channels,{}),u=await h(c);await o(c.next()).resolves.toHaveProperty("done",!0);const d=e.discover(i.channels,{}),w=await h(d);await o(d.next()).resolves.toHaveProperty("done",!0),s.lastModified===r.lastModified?(o(u.tombstone||w.tombstone).toBe(!0),o(u.tombstone&&w.tombstone).toBe(!1)):(o(u.tombstone).toBe(!0),o(u.value).toEqual(t.value),o(u.channels).toEqual(t.channels),o(u.lastModified).toEqual(r.lastModified),o(w.tombstone).toBe(!1),o(w.value).toEqual(i.value),o(w.channels).toEqual(i.channels),o(w.lastModified).toEqual(r.lastModified))}})),t("discover for patched allowed",(async()=>{const e=n(),a=l(),t=v(),s=await e.put(t,a);await e.patch({allowed:[{op:"add",path:"",value:[]}]},s,a);const i=e.discover(t.channels,{}),r=await h(i);o(r.tombstone).toBe(!0),o(r.value).toEqual(t.value),o(r.channels).toEqual(t.channels),o(r.allowed).toBeUndefined(),await o(i.next()).resolves.toHaveProperty("done",!0)})),t("put concurrently and discover one",(async()=>{const a=n(),t=l(),s=v();s.name=d();const i=Array(100).fill(0).map((()=>a.put(s,t)));await Promise.all(i);const r=a.discover(s.channels,{});let c=0,u=0;for await(const a of r)e(!a.error,"result has error"),a.value.tombstone?c++:u++;o(c).toBe(99),o(u).toBe(1)}))}))};export{p as graffitiCRUDTests,E as graffitiDiscoverTests,w as graffitiLocationTests,m as graffitiSynchronizeTests};
2
2
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../../tests/src/crud.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,QAAQ,EACR,eAAe,EAEhB,MAAM,sBAAsB,CAAC;AAW9B,eAAO,MAAM,iBAAiB,gBACf,MAAM,IAAI,CAAC,QAAQ,EAAE,KAAK,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC,eACxD,MAAM,eAAe,eACrB,MAAM,eAAe,SA4bnC,CAAC"}
1
+ {"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../../tests/src/crud.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,QAAQ,EACR,eAAe,EAEhB,MAAM,sBAAsB,CAAC;AAW9B,eAAO,MAAM,iBAAiB,gBACf,MAAM,IAAI,CAAC,QAAQ,EAAE,KAAK,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,CAAC,eACxD,MAAM,eAAe,eACrB,MAAM,eAAe,SAwcnC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graffiti-garden/api",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
4
4
  "description": "The heart of Graffiti",
5
5
  "types": "./dist/src/index.d.ts",
6
6
  "module": "./dist/index.js",
@@ -36,7 +36,7 @@
36
36
  "build-api": "rollup -c rollup.config.ts --configPlugin rollup-plugin-typescript2",
37
37
  "build-tests": "cd tests && rollup -c rollup.config.ts --configPlugin rollup-plugin-typescript2",
38
38
  "build": "npm run build-api && npm run build-tests",
39
- "prepublishOnly": "npm install && npm run build"
39
+ "prepublishOnly": "npm update && npm run build"
40
40
  },
41
41
  "repository": {
42
42
  "type": "git",
package/src/1-api.ts CHANGED
@@ -623,13 +623,6 @@ export abstract class Graffiti {
623
623
  */
624
624
  scope?: {};
625
625
  },
626
- /**
627
- * An arbitrary string that will be returned with the
628
- * {@link GraffitiSession | session} object
629
- * when the login process is complete.
630
- * See {@link GraffitiLoginEvent}.
631
- */
632
- state?: string,
633
626
  ): Promise<void>;
634
627
 
635
628
  /**
@@ -648,19 +641,14 @@ export abstract class Graffiti {
648
641
  * The {@link GraffitiSession | session} object to logout.
649
642
  */
650
643
  session: GraffitiSession,
651
- /**
652
- * An arbitrary string that will be returned with the
653
- * when the logout process is complete.
654
- * See {@link GraffitiLogoutEvent}.
655
- */
656
- state?: string,
657
644
  ): Promise<void>;
658
645
 
659
646
  /**
660
- * An event target that can be used to listen for `login`
661
- * and `logout` events. They are custom events of types
662
- * {@link GraffitiLoginEvent} and {@link GraffitiLogoutEvent }
663
- * respectively.
647
+ * An event target that can be used to listen for the following
648
+ * events and they're corresponding event types:
649
+ * - `login` - {@link GraffitiLoginEvent}
650
+ * - `logout` - {@link GraffitiLogoutEvent}
651
+ * - `initialized` - {@link GraffitiSessionInitializedEvent}
664
652
  *
665
653
  * @group Session Management
666
654
  */
package/src/2-types.ts CHANGED
@@ -277,18 +277,14 @@ export type GraffitiStream<TValue, TReturn = void> = AsyncGenerator<
277
277
  * The event name to listen for is `login`.
278
278
  */
279
279
  export type GraffitiLoginEvent = CustomEvent<
280
- {
281
- state?: string;
282
- } & (
283
- | {
284
- error: Error;
285
- session?: undefined;
286
- }
287
- | {
288
- error?: undefined;
289
- session: GraffitiSession;
290
- }
291
- )
280
+ | {
281
+ error: Error;
282
+ session?: undefined;
283
+ }
284
+ | {
285
+ error?: undefined;
286
+ session: GraffitiSession;
287
+ }
292
288
  >;
293
289
 
294
290
  /**
@@ -297,8 +293,28 @@ export type GraffitiLoginEvent = CustomEvent<
297
293
  * or when their session times out or otherwise becomes invalid.
298
294
  * The event name to listen for is `logout`.
299
295
  */
300
- export type GraffitiLogoutEvent = CustomEvent<{
301
- actor: string;
302
- state?: string;
296
+ export type GraffitiLogoutEvent = CustomEvent<
297
+ | {
298
+ error: Error;
299
+ actor?: string;
300
+ }
301
+ | {
302
+ error?: undefined;
303
+ actor: string;
304
+ }
305
+ >;
306
+
307
+ /**
308
+ * The event type produced in {@link Graffiti.sessionEvents}
309
+ * after an application has attempted to complete any login redirects
310
+ * and restore any previously active sessions.
311
+ * Successful session restores will be returned in parallel as
312
+ * their own {@link GraffitiLoginEvent} events.
313
+ * This event optionally return an `href` property
314
+ * if there were any redirects during the restoration process.
315
+ * The event name to listen for is `initialized`.
316
+ */
317
+ export type GraffitiSessionInitializedEvent = CustomEvent<{
303
318
  error?: Error;
319
+ href?: string;
304
320
  }>;
package/src/3-errors.ts CHANGED
@@ -61,11 +61,3 @@ export class GraffitiErrorInvalidUri extends Error {
61
61
  Object.setPrototypeOf(this, GraffitiErrorInvalidUri.prototype);
62
62
  }
63
63
  }
64
-
65
- export class GraffitiErrorOther extends Error {
66
- constructor(message?: string) {
67
- super(message);
68
- this.name = "GraffitiErrorOther";
69
- Object.setPrototypeOf(this, GraffitiErrorOther.prototype);
70
- }
71
- }
package/tests/src/crud.ts CHANGED
@@ -19,446 +19,458 @@ export const graffitiCRUDTests = (
19
19
  useSession1: () => GraffitiSession,
20
20
  useSession2: () => GraffitiSession,
21
21
  ) => {
22
- describe("CRUD", () => {
23
- it("put, get, delete", async () => {
24
- const graffiti = useGraffiti();
25
- const session = useSession1();
26
- const value = {
27
- something: "hello, world~ c:",
28
- };
29
- const channels = [randomString(), randomString()];
30
-
31
- // Put the object
32
- const previous = await graffiti.put({ value, channels }, session);
33
- expect(previous.value).toEqual({});
34
- expect(previous.channels).toEqual([]);
35
- expect(previous.allowed).toBeUndefined();
36
- expect(previous.actor).toEqual(session.actor);
37
-
38
- // Get it back
39
- const gotten = await graffiti.get(previous, {});
40
- expect(gotten.value).toEqual(value);
41
- expect(gotten.channels).toEqual([]);
42
- expect(gotten.allowed).toBeUndefined();
43
- expect(gotten.name).toEqual(previous.name);
44
- expect(gotten.actor).toEqual(previous.actor);
45
- expect(gotten.source).toEqual(previous.source);
46
- expect(gotten.lastModified).toEqual(previous.lastModified);
47
-
48
- // Replace it
49
- const newValue = {
50
- something: "goodbye, world~ :c",
51
- };
52
- const beforeReplaced = await graffiti.put(
53
- { ...previous, value: newValue, channels: [] },
54
- session,
55
- );
56
- expect(beforeReplaced.value).toEqual(value);
57
- expect(beforeReplaced.tombstone).toEqual(true);
58
- expect(beforeReplaced.name).toEqual(previous.name);
59
- expect(beforeReplaced.actor).toEqual(previous.actor);
60
- expect(beforeReplaced.source).toEqual(previous.source);
61
- expect(beforeReplaced.lastModified).toBeGreaterThanOrEqual(
62
- gotten.lastModified,
63
- );
64
-
65
- // Get it again
66
- const afterReplaced = await graffiti.get(previous, {});
67
- expect(afterReplaced.value).toEqual(newValue);
68
- expect(afterReplaced.lastModified).toEqual(beforeReplaced.lastModified);
69
- expect(afterReplaced.tombstone).toEqual(false);
70
-
71
- // Delete it
72
- const beforeDeleted = await graffiti.delete(afterReplaced, session);
73
- expect(beforeDeleted.tombstone).toEqual(true);
74
- expect(beforeDeleted.value).toEqual(newValue);
75
- expect(beforeDeleted.lastModified).toBeGreaterThanOrEqual(
76
- beforeReplaced.lastModified,
77
- );
78
-
79
- // Try to get it and fail
80
- await expect(graffiti.get(afterReplaced, {})).rejects.toThrow(
81
- GraffitiErrorNotFound,
82
- );
83
- });
84
-
85
- it("put, get, delete with wrong actor", async () => {
86
- const graffiti = useGraffiti();
87
- const session1 = useSession1();
88
- const session2 = useSession2();
89
-
90
- await expect(
91
- graffiti.put(
92
- { value: {}, channels: [], actor: session2.actor },
93
- session1,
94
- ),
95
- ).rejects.toThrow(GraffitiErrorForbidden);
22
+ describe(
23
+ "CRUD",
24
+ {
25
+ timeout: 20000,
26
+ },
27
+ () => {
28
+ it("put, get, delete", async () => {
29
+ const graffiti = useGraffiti();
30
+ const session = useSession1();
31
+ const value = {
32
+ something: "hello, world~ c:",
33
+ };
34
+ const channels = [randomString(), randomString()];
35
+
36
+ // Put the object
37
+ const previous = await graffiti.put({ value, channels }, session);
38
+ expect(previous.value).toEqual({});
39
+ expect(previous.channels).toEqual([]);
40
+ expect(previous.allowed).toBeUndefined();
41
+ expect(previous.actor).toEqual(session.actor);
42
+
43
+ // Get it back
44
+ const gotten = await graffiti.get(previous, {});
45
+ expect(gotten.value).toEqual(value);
46
+ expect(gotten.channels).toEqual([]);
47
+ expect(gotten.allowed).toBeUndefined();
48
+ expect(gotten.name).toEqual(previous.name);
49
+ expect(gotten.actor).toEqual(previous.actor);
50
+ expect(gotten.source).toEqual(previous.source);
51
+ expect(gotten.lastModified).toEqual(previous.lastModified);
52
+
53
+ // Replace it
54
+ const newValue = {
55
+ something: "goodbye, world~ :c",
56
+ };
57
+ const beforeReplaced = await graffiti.put(
58
+ { ...previous, value: newValue, channels: [] },
59
+ session,
60
+ );
61
+ expect(beforeReplaced.value).toEqual(value);
62
+ expect(beforeReplaced.tombstone).toEqual(true);
63
+ expect(beforeReplaced.name).toEqual(previous.name);
64
+ expect(beforeReplaced.actor).toEqual(previous.actor);
65
+ expect(beforeReplaced.source).toEqual(previous.source);
66
+ expect(beforeReplaced.lastModified).toBeGreaterThanOrEqual(
67
+ gotten.lastModified,
68
+ );
96
69
 
97
- const putted = await graffiti.put({ value: {}, channels: [] }, session2);
70
+ // Get it again
71
+ const afterReplaced = await graffiti.get(previous, {});
72
+ expect(afterReplaced.value).toEqual(newValue);
73
+ expect(afterReplaced.lastModified).toEqual(beforeReplaced.lastModified);
74
+ expect(afterReplaced.tombstone).toEqual(false);
75
+
76
+ // Delete it
77
+ const beforeDeleted = await graffiti.delete(afterReplaced, session);
78
+ expect(beforeDeleted.tombstone).toEqual(true);
79
+ expect(beforeDeleted.value).toEqual(newValue);
80
+ expect(beforeDeleted.lastModified).toBeGreaterThanOrEqual(
81
+ beforeReplaced.lastModified,
82
+ );
98
83
 
99
- await expect(graffiti.delete(putted, session1)).rejects.toThrow(
100
- GraffitiErrorForbidden,
101
- );
84
+ // Try to get it and fail
85
+ await expect(graffiti.get(afterReplaced, {})).rejects.toThrow(
86
+ GraffitiErrorNotFound,
87
+ );
88
+ });
102
89
 
103
- await expect(graffiti.patch({}, putted, session1)).rejects.toThrow(
104
- GraffitiErrorForbidden,
105
- );
106
- });
90
+ it("put, get, delete with wrong actor", async () => {
91
+ const graffiti = useGraffiti();
92
+ const session1 = useSession1();
93
+ const session2 = useSession2();
94
+
95
+ await expect(
96
+ graffiti.put(
97
+ { value: {}, channels: [], actor: session2.actor },
98
+ session1,
99
+ ),
100
+ ).rejects.toThrow(GraffitiErrorForbidden);
101
+
102
+ const putted = await graffiti.put(
103
+ { value: {}, channels: [] },
104
+ session2,
105
+ );
107
106
 
108
- it("put and get with schema", async () => {
109
- const graffiti = useGraffiti();
110
- const session = useSession1();
107
+ await expect(graffiti.delete(putted, session1)).rejects.toThrow(
108
+ GraffitiErrorForbidden,
109
+ );
111
110
 
112
- const schema = {
113
- properties: {
114
- value: {
115
- properties: {
116
- something: {
117
- type: "string",
118
- },
119
- another: {
120
- type: "integer",
121
- },
122
- },
123
- },
124
- },
125
- } as const;
126
-
127
- const goodValue = {
128
- something: "hello",
129
- another: 42,
130
- } as const;
131
-
132
- const putted = await graffiti.put<typeof schema>(
133
- {
134
- value: goodValue,
135
- channels: [],
136
- },
137
- session,
138
- );
139
-
140
- const gotten = await graffiti.get(putted, schema);
141
- expect(gotten.value.something).toEqual(goodValue.something);
142
- expect(gotten.value.another).toEqual(goodValue.another);
143
- });
144
-
145
- it("put and get with invalid schema", async () => {
146
- const graffiti = useGraffiti();
147
- const session = useSession1();
148
-
149
- const putted = await graffiti.put({ value: {}, channels: [] }, session);
150
- await expect(
151
- graffiti.get(putted, {
152
- properties: {
153
- value: {
154
- //@ts-ignore
155
- type: "asdf",
156
- },
157
- },
158
- }),
159
- ).rejects.toThrow(GraffitiErrorInvalidSchema);
160
- });
161
-
162
- it("put and get with wrong schema", async () => {
163
- const graffiti = useGraffiti();
164
- const session = useSession1();
165
-
166
- const putted = await graffiti.put(
167
- {
168
- value: {
169
- hello: "world",
170
- },
171
- channels: [],
172
- },
173
- session,
174
- );
111
+ await expect(graffiti.patch({}, putted, session1)).rejects.toThrow(
112
+ GraffitiErrorForbidden,
113
+ );
114
+ });
175
115
 
176
- await expect(
177
- graffiti.get(putted, {
116
+ it("put and get with schema", async () => {
117
+ const graffiti = useGraffiti();
118
+ const session = useSession1();
119
+
120
+ const schema = {
178
121
  properties: {
179
122
  value: {
180
123
  properties: {
181
- hello: {
182
- type: "number",
124
+ something: {
125
+ type: "string",
126
+ },
127
+ another: {
128
+ type: "integer",
183
129
  },
184
130
  },
185
131
  },
186
132
  },
187
- }),
188
- ).rejects.toThrow(GraffitiErrorSchemaMismatch);
189
- });
190
-
191
- it("put and get with empty access control", async () => {
192
- const graffiti = useGraffiti();
193
- const session1 = useSession1();
194
- const session2 = useSession2();
195
-
196
- const value = {
197
- um: "hi",
198
- };
199
- const allowed = [randomString()];
200
- const channels = [randomString()];
201
- const putted = await graffiti.put({ value, allowed, channels }, session1);
202
-
203
- // Get it with authenticated session
204
- const gotten = await graffiti.get(putted, {}, session1);
205
- expect(gotten.value).toEqual(value);
206
- expect(gotten.allowed).toEqual(allowed);
207
- expect(gotten.channels).toEqual(channels);
208
-
209
- // But not without session
210
- await expect(graffiti.get(putted, {})).rejects.toThrow();
211
-
212
- // Or the wrong session
213
- await expect(graffiti.get(putted, {}, session2)).rejects.toThrow();
214
- });
215
-
216
- it("put and get with specific access control", async () => {
217
- const graffiti = useGraffiti();
218
- const session1 = useSession1();
219
- const session2 = useSession2();
220
-
221
- const value = {
222
- um: "hi",
223
- };
224
- const allowed = [randomString(), session2.actor, randomString()];
225
- const channels = [randomString()];
226
- const putted = await graffiti.put(
227
- {
228
- value,
229
- allowed,
230
- channels,
231
- },
232
- session1,
233
- );
234
-
235
- // Get it with authenticated session
236
- const gotten = await graffiti.get(putted, {}, session1);
237
- expect(gotten.value).toEqual(value);
238
- expect(gotten.allowed).toEqual(allowed);
239
- expect(gotten.channels).toEqual(channels);
240
-
241
- // But not without session
242
- await expect(graffiti.get(putted, {})).rejects.toThrow();
243
-
244
- const gotten2 = await graffiti.get(putted, {}, session2);
245
- expect(gotten2.value).toEqual(value);
246
- // They should only see that is is private to them
247
- expect(gotten2.allowed).toEqual([session2.actor]);
248
- // And not see any channels
249
- expect(gotten2.channels).toEqual([]);
250
- });
251
-
252
- it("patch value", async () => {
253
- const graffiti = useGraffiti();
254
- const session = useSession1();
255
-
256
- const value = {
257
- something: "hello, world~ c:",
258
- };
259
- const putted = await graffiti.put({ value, channels: [] }, session);
260
-
261
- const patch: GraffitiPatch = {
262
- value: [
263
- { op: "replace", path: "/something", value: "goodbye, world~ :c" },
264
- ],
265
- };
266
- const beforePatched = await graffiti.patch(patch, putted, session);
267
- expect(beforePatched.value).toEqual(value);
268
- expect(beforePatched.tombstone).toBe(true);
269
-
270
- const gotten = await graffiti.get(putted, {});
271
- expect(gotten.value).toEqual({
272
- something: "goodbye, world~ :c",
133
+ } as const;
134
+
135
+ const goodValue = {
136
+ something: "hello",
137
+ another: 42,
138
+ } as const;
139
+
140
+ const putted = await graffiti.put<typeof schema>(
141
+ {
142
+ value: goodValue,
143
+ channels: [],
144
+ },
145
+ session,
146
+ );
147
+
148
+ const gotten = await graffiti.get(putted, schema);
149
+ expect(gotten.value.something).toEqual(goodValue.something);
150
+ expect(gotten.value.another).toEqual(goodValue.another);
273
151
  });
274
- expect(beforePatched.lastModified).toBe(gotten.lastModified);
275
152
 
276
- await graffiti.delete(putted, session);
277
- });
153
+ it("put and get with invalid schema", async () => {
154
+ const graffiti = useGraffiti();
155
+ const session = useSession1();
156
+
157
+ const putted = await graffiti.put({ value: {}, channels: [] }, session);
158
+ await expect(
159
+ graffiti.get(putted, {
160
+ properties: {
161
+ value: {
162
+ //@ts-ignore
163
+ type: "asdf",
164
+ },
165
+ },
166
+ }),
167
+ ).rejects.toThrow(GraffitiErrorInvalidSchema);
168
+ });
278
169
 
279
- it("deep patch", async () => {
280
- const graffiti = useGraffiti();
281
- const session = useSession1();
170
+ it("put and get with wrong schema", async () => {
171
+ const graffiti = useGraffiti();
172
+ const session = useSession1();
282
173
 
283
- const value = {
284
- something: {
285
- another: {
286
- somethingElse: "hello",
287
- },
288
- },
289
- };
290
- const putted = await graffiti.put(
291
- { value: value, channels: [] },
292
- session,
293
- );
294
-
295
- const beforePatch = await graffiti.patch(
296
- {
297
- value: [
298
- {
299
- op: "replace",
300
- path: "/something/another/somethingElse",
301
- value: "goodbye",
174
+ const putted = await graffiti.put(
175
+ {
176
+ value: {
177
+ hello: "world",
302
178
  },
303
- ],
304
- },
305
- putted,
306
- session,
307
- );
308
- const gotten = await graffiti.get(putted, {});
309
-
310
- expect(beforePatch.value).toEqual(value);
311
- expect(gotten.value).toEqual({
312
- something: {
313
- another: {
314
- somethingElse: "goodbye",
179
+ channels: [],
315
180
  },
316
- },
181
+ session,
182
+ );
183
+
184
+ await expect(
185
+ graffiti.get(putted, {
186
+ properties: {
187
+ value: {
188
+ properties: {
189
+ hello: {
190
+ type: "number",
191
+ },
192
+ },
193
+ },
194
+ },
195
+ }),
196
+ ).rejects.toThrow(GraffitiErrorSchemaMismatch);
317
197
  });
318
- });
319
-
320
- it("patch channels", async () => {
321
- const graffiti = useGraffiti();
322
- const session = useSession1();
323
-
324
- const channelsBefore = [randomString()];
325
- const channelsAfter = [randomString()];
326
-
327
- const putted = await graffiti.put(
328
- { value: {}, channels: channelsBefore },
329
- session,
330
- );
331
-
332
- const patch: GraffitiPatch = {
333
- channels: [{ op: "replace", path: "/0", value: channelsAfter[0] }],
334
- };
335
- const patched = await graffiti.patch(patch, putted, session);
336
- expect(patched.channels).toEqual(channelsBefore);
337
- const gotten = await graffiti.get(putted, {}, session);
338
- expect(gotten.channels).toEqual(channelsAfter);
339
- await graffiti.delete(putted, session);
340
- });
341
-
342
- it("patch 'increment' with test", async () => {
343
- const graffiti = useGraffiti();
344
- const session = useSession1();
345
-
346
- const putted = await graffiti.put(
347
- {
348
- value: {
349
- counter: 1,
198
+
199
+ it("put and get with empty access control", async () => {
200
+ const graffiti = useGraffiti();
201
+ const session1 = useSession1();
202
+ const session2 = useSession2();
203
+
204
+ const value = {
205
+ um: "hi",
206
+ };
207
+ const allowed = [randomString()];
208
+ const channels = [randomString()];
209
+ const putted = await graffiti.put(
210
+ { value, allowed, channels },
211
+ session1,
212
+ );
213
+
214
+ // Get it with authenticated session
215
+ const gotten = await graffiti.get(putted, {}, session1);
216
+ expect(gotten.value).toEqual(value);
217
+ expect(gotten.allowed).toEqual(allowed);
218
+ expect(gotten.channels).toEqual(channels);
219
+
220
+ // But not without session
221
+ await expect(graffiti.get(putted, {})).rejects.toThrow();
222
+
223
+ // Or the wrong session
224
+ await expect(graffiti.get(putted, {}, session2)).rejects.toThrow();
225
+ });
226
+
227
+ it("put and get with specific access control", async () => {
228
+ const graffiti = useGraffiti();
229
+ const session1 = useSession1();
230
+ const session2 = useSession2();
231
+
232
+ const value = {
233
+ um: "hi",
234
+ };
235
+ const allowed = [randomString(), session2.actor, randomString()];
236
+ const channels = [randomString()];
237
+ const putted = await graffiti.put(
238
+ {
239
+ value,
240
+ allowed,
241
+ channels,
350
242
  },
351
- channels: [],
352
- },
353
- session,
354
- );
243
+ session1,
244
+ );
245
+
246
+ // Get it with authenticated session
247
+ const gotten = await graffiti.get(putted, {}, session1);
248
+ expect(gotten.value).toEqual(value);
249
+ expect(gotten.allowed).toEqual(allowed);
250
+ expect(gotten.channels).toEqual(channels);
251
+
252
+ // But not without session
253
+ await expect(graffiti.get(putted, {})).rejects.toThrow();
254
+
255
+ const gotten2 = await graffiti.get(putted, {}, session2);
256
+ expect(gotten2.value).toEqual(value);
257
+ // They should only see that is is private to them
258
+ expect(gotten2.allowed).toEqual([session2.actor]);
259
+ // And not see any channels
260
+ expect(gotten2.channels).toEqual([]);
261
+ });
262
+
263
+ it("patch value", async () => {
264
+ const graffiti = useGraffiti();
265
+ const session = useSession1();
355
266
 
356
- const previous = await graffiti.patch(
357
- {
267
+ const value = {
268
+ something: "hello, world~ c:",
269
+ };
270
+ const putted = await graffiti.put({ value, channels: [] }, session);
271
+
272
+ const patch: GraffitiPatch = {
358
273
  value: [
359
- { op: "test", path: "/counter", value: 1 },
360
- { op: "replace", path: "/counter", value: 2 },
274
+ { op: "replace", path: "/something", value: "goodbye, world~ :c" },
361
275
  ],
362
- },
363
- putted,
364
- session,
365
- );
366
- expect(previous.value).toEqual({ counter: 1 });
367
- const result = await graffiti.get(previous, {
368
- properties: {
369
- value: {
370
- properties: {
371
- counter: {
372
- type: "integer",
373
- },
276
+ };
277
+ const beforePatched = await graffiti.patch(patch, putted, session);
278
+ expect(beforePatched.value).toEqual(value);
279
+ expect(beforePatched.tombstone).toBe(true);
280
+
281
+ const gotten = await graffiti.get(putted, {});
282
+ expect(gotten.value).toEqual({
283
+ something: "goodbye, world~ :c",
284
+ });
285
+ expect(beforePatched.lastModified).toBe(gotten.lastModified);
286
+
287
+ await graffiti.delete(putted, session);
288
+ });
289
+
290
+ it("deep patch", async () => {
291
+ const graffiti = useGraffiti();
292
+ const session = useSession1();
293
+
294
+ const value = {
295
+ something: {
296
+ another: {
297
+ somethingElse: "hello",
374
298
  },
375
299
  },
376
- },
377
- });
378
- expect(result.value.counter).toEqual(2);
300
+ };
301
+ const putted = await graffiti.put(
302
+ { value: value, channels: [] },
303
+ session,
304
+ );
379
305
 
380
- await expect(
381
- graffiti.patch(
306
+ const beforePatch = await graffiti.patch(
382
307
  {
383
308
  value: [
384
- { op: "test", path: "/counter", value: 1 },
385
- { op: "replace", path: "/counter", value: 3 },
309
+ {
310
+ op: "replace",
311
+ path: "/something/another/somethingElse",
312
+ value: "goodbye",
313
+ },
386
314
  ],
387
315
  },
388
316
  putted,
389
317
  session,
390
- ),
391
- ).rejects.toThrow(GraffitiErrorPatchTestFailed);
392
- });
393
-
394
- it("invalid patch", async () => {
395
- const graffiti = useGraffiti();
396
- const session = useSession1();
397
- const object = randomPutObject();
398
- const putted = await graffiti.put(object, session);
399
-
400
- await expect(
401
- graffiti.patch(
318
+ );
319
+ const gotten = await graffiti.get(putted, {});
320
+
321
+ expect(beforePatch.value).toEqual(value);
322
+ expect(gotten.value).toEqual({
323
+ something: {
324
+ another: {
325
+ somethingElse: "goodbye",
326
+ },
327
+ },
328
+ });
329
+ });
330
+
331
+ it("patch channels", async () => {
332
+ const graffiti = useGraffiti();
333
+ const session = useSession1();
334
+
335
+ const channelsBefore = [randomString()];
336
+ const channelsAfter = [randomString()];
337
+
338
+ const putted = await graffiti.put(
339
+ { value: {}, channels: channelsBefore },
340
+ session,
341
+ );
342
+
343
+ const patch: GraffitiPatch = {
344
+ channels: [{ op: "replace", path: "/0", value: channelsAfter[0] }],
345
+ };
346
+ const patched = await graffiti.patch(patch, putted, session);
347
+ expect(patched.channels).toEqual(channelsBefore);
348
+ const gotten = await graffiti.get(putted, {}, session);
349
+ expect(gotten.channels).toEqual(channelsAfter);
350
+ await graffiti.delete(putted, session);
351
+ });
352
+
353
+ it("patch 'increment' with test", async () => {
354
+ const graffiti = useGraffiti();
355
+ const session = useSession1();
356
+
357
+ const putted = await graffiti.put(
358
+ {
359
+ value: {
360
+ counter: 1,
361
+ },
362
+ channels: [],
363
+ },
364
+ session,
365
+ );
366
+
367
+ const previous = await graffiti.patch(
402
368
  {
403
369
  value: [
404
- { op: "add", path: "/root", value: [] },
405
- { op: "add", path: "/root/2", value: 2 }, // out of bounds
370
+ { op: "test", path: "/counter", value: 1 },
371
+ { op: "replace", path: "/counter", value: 2 },
406
372
  ],
407
373
  },
408
374
  putted,
409
375
  session,
410
- ),
411
- ).rejects.toThrow(GraffitiErrorPatchError);
412
- });
413
-
414
- it("patch channels to be wrong", async () => {
415
- const graffiti = useGraffiti();
416
- const session = useSession1();
417
- const object = randomPutObject();
418
- object.allowed = [randomString()];
419
- const putted = await graffiti.put(object, session);
420
-
421
- const patches: GraffitiPatch[] = [
422
- {
423
- channels: [{ op: "replace", path: "", value: null }],
424
- },
425
- {
426
- channels: [{ op: "replace", path: "", value: {} }],
427
- },
428
- {
429
- channels: [{ op: "replace", path: "", value: ["hello", ["hi"]] }],
430
- },
431
- {
432
- channels: [{ op: "add", path: "/0", value: 1 }],
433
- },
434
- {
435
- value: [{ op: "replace", path: "", value: "not an object" }],
436
- },
437
- {
438
- value: [{ op: "replace", path: "", value: null }],
439
- },
440
- {
441
- value: [{ op: "replace", path: "", value: [] }],
442
- },
443
- {
444
- allowed: [{ op: "replace", path: "", value: {} }],
445
- },
446
- {
447
- allowed: [{ op: "replace", path: "", value: ["hello", ["hi"]] }],
448
- },
449
- ];
450
-
451
- for (const patch of patches) {
452
- await expect(graffiti.patch(patch, putted, session)).rejects.toThrow(
453
- GraffitiErrorPatchError,
454
376
  );
455
- }
456
-
457
- const gotten = await graffiti.get(putted, {}, session);
458
- expect(gotten.value).toEqual(object.value);
459
- expect(gotten.channels).toEqual(object.channels);
460
- expect(gotten.allowed).toEqual(object.allowed);
461
- expect(gotten.lastModified).toEqual(putted.lastModified);
462
- });
463
- });
377
+ expect(previous.value).toEqual({ counter: 1 });
378
+ const result = await graffiti.get(previous, {
379
+ properties: {
380
+ value: {
381
+ properties: {
382
+ counter: {
383
+ type: "integer",
384
+ },
385
+ },
386
+ },
387
+ },
388
+ });
389
+ expect(result.value.counter).toEqual(2);
390
+
391
+ await expect(
392
+ graffiti.patch(
393
+ {
394
+ value: [
395
+ { op: "test", path: "/counter", value: 1 },
396
+ { op: "replace", path: "/counter", value: 3 },
397
+ ],
398
+ },
399
+ putted,
400
+ session,
401
+ ),
402
+ ).rejects.toThrow(GraffitiErrorPatchTestFailed);
403
+ });
404
+
405
+ it("invalid patch", async () => {
406
+ const graffiti = useGraffiti();
407
+ const session = useSession1();
408
+ const object = randomPutObject();
409
+ const putted = await graffiti.put(object, session);
410
+
411
+ await expect(
412
+ graffiti.patch(
413
+ {
414
+ value: [
415
+ { op: "add", path: "/root", value: [] },
416
+ { op: "add", path: "/root/2", value: 2 }, // out of bounds
417
+ ],
418
+ },
419
+ putted,
420
+ session,
421
+ ),
422
+ ).rejects.toThrow(GraffitiErrorPatchError);
423
+ });
424
+
425
+ it("patch channels to be wrong", async () => {
426
+ const graffiti = useGraffiti();
427
+ const session = useSession1();
428
+ const object = randomPutObject();
429
+ object.allowed = [randomString()];
430
+ const putted = await graffiti.put(object, session);
431
+
432
+ const patches: GraffitiPatch[] = [
433
+ {
434
+ channels: [{ op: "replace", path: "", value: null }],
435
+ },
436
+ {
437
+ channels: [{ op: "replace", path: "", value: {} }],
438
+ },
439
+ {
440
+ channels: [{ op: "replace", path: "", value: ["hello", ["hi"]] }],
441
+ },
442
+ {
443
+ channels: [{ op: "add", path: "/0", value: 1 }],
444
+ },
445
+ {
446
+ value: [{ op: "replace", path: "", value: "not an object" }],
447
+ },
448
+ {
449
+ value: [{ op: "replace", path: "", value: null }],
450
+ },
451
+ {
452
+ value: [{ op: "replace", path: "", value: [] }],
453
+ },
454
+ {
455
+ allowed: [{ op: "replace", path: "", value: {} }],
456
+ },
457
+ {
458
+ allowed: [{ op: "replace", path: "", value: ["hello", ["hi"]] }],
459
+ },
460
+ ];
461
+
462
+ for (const patch of patches) {
463
+ await expect(graffiti.patch(patch, putted, session)).rejects.toThrow(
464
+ GraffitiErrorPatchError,
465
+ );
466
+ }
467
+
468
+ const gotten = await graffiti.get(putted, {}, session);
469
+ expect(gotten.value).toEqual(object.value);
470
+ expect(gotten.channels).toEqual(object.channels);
471
+ expect(gotten.allowed).toEqual(object.allowed);
472
+ expect(gotten.lastModified).toEqual(putted.lastModified);
473
+ });
474
+ },
475
+ );
464
476
  };
@@ -11,7 +11,7 @@ export const graffitiDiscoverTests = (
11
11
  useSession1: () => GraffitiSession,
12
12
  useSession2: () => GraffitiSession,
13
13
  ) => {
14
- describe("discover", () => {
14
+ describe("discover", { timeout: 20000 }, () => {
15
15
  it("discover nothing", async () => {
16
16
  const graffiti = useGraffiti();
17
17
  const iterator = graffiti.discover([], {});