@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 +1 -1
- package/dist/index.js +1 -1
- package/dist/src/1-api.d.ts +7 -19
- package/dist/src/1-api.d.ts.map +1 -1
- package/dist/src/2-types.d.ts +18 -4
- package/dist/src/2-types.d.ts.map +1 -1
- package/dist/src/3-errors.d.ts +0 -3
- package/dist/src/3-errors.d.ts.map +1 -1
- package/dist/tests/index.js +1 -1
- package/dist/tests/src/crud.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/1-api.ts +5 -17
- package/src/2-types.ts +31 -15
- package/src/3-errors.ts +0 -8
- package/tests/src/crud.ts +414 -402
- package/tests/src/discover.ts +1 -1
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)}}
|
|
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)}}
|
|
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
|
package/dist/src/1-api.d.ts
CHANGED
|
@@ -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
|
|
623
|
-
*
|
|
624
|
-
*
|
|
625
|
-
*
|
|
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
|
*/
|
package/dist/src/1-api.d.ts.map
CHANGED
|
@@ -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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"}
|
package/dist/src/2-types.d.ts
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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"}
|
package/dist/src/3-errors.d.ts
CHANGED
|
@@ -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
|
|
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"}
|
package/dist/tests/index.js
CHANGED
|
@@ -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,
|
|
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.
|
|
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
|
|
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
|
|
661
|
-
*
|
|
662
|
-
*
|
|
663
|
-
*
|
|
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
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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
|
-
|
|
302
|
-
|
|
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(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
84
|
+
// Try to get it and fail
|
|
85
|
+
await expect(graffiti.get(afterReplaced, {})).rejects.toThrow(
|
|
86
|
+
GraffitiErrorNotFound,
|
|
87
|
+
);
|
|
88
|
+
});
|
|
102
89
|
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
await expect(graffiti.delete(putted, session1)).rejects.toThrow(
|
|
108
|
+
GraffitiErrorForbidden,
|
|
109
|
+
);
|
|
111
110
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
177
|
-
graffiti
|
|
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
|
-
|
|
182
|
-
type: "
|
|
124
|
+
something: {
|
|
125
|
+
type: "string",
|
|
126
|
+
},
|
|
127
|
+
another: {
|
|
128
|
+
type: "integer",
|
|
183
129
|
},
|
|
184
130
|
},
|
|
185
131
|
},
|
|
186
132
|
},
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
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
|
-
|
|
280
|
-
|
|
281
|
-
|
|
170
|
+
it("put and get with wrong schema", async () => {
|
|
171
|
+
const graffiti = useGraffiti();
|
|
172
|
+
const session = useSession1();
|
|
282
173
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
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: "
|
|
360
|
-
{ op: "replace", path: "/counter", value: 2 },
|
|
274
|
+
{ op: "replace", path: "/something", value: "goodbye, world~ :c" },
|
|
361
275
|
],
|
|
362
|
-
}
|
|
363
|
-
putted,
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
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
|
-
|
|
300
|
+
};
|
|
301
|
+
const putted = await graffiti.put(
|
|
302
|
+
{ value: value, channels: [] },
|
|
303
|
+
session,
|
|
304
|
+
);
|
|
379
305
|
|
|
380
|
-
|
|
381
|
-
graffiti.patch(
|
|
306
|
+
const beforePatch = await graffiti.patch(
|
|
382
307
|
{
|
|
383
308
|
value: [
|
|
384
|
-
{
|
|
385
|
-
|
|
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
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
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: "
|
|
405
|
-
{ op: "
|
|
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
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
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
|
};
|
package/tests/src/discover.ts
CHANGED
|
@@ -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([], {});
|