@graffiti-garden/api 0.1.7 → 0.1.8

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)}}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;
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;
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)}}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};
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};
2
2
  //# sourceMappingURL=index.js.map
@@ -22,4 +22,7 @@ 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
+ }
25
28
  //# 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;AAED,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,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).toBeGreaterThan(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).toBeGreaterThan(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),await o(a.delete({name:"asdf",source:"asdf",actor:l.actor},t)).rejects.toThrow(s),await o(a.patch({},{name:"asdf",source:"asdf",actor:l.actor},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,f=(await w).value;if(!E||E.error||!q||q.error||!f||f.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(f.value.value).toEqual(p),o(f.value.channels).toEqual([i]),o(f.value.tombstone).toBe(!1),o(E.value.lastModified).toEqual(q.value.lastModified),o(f.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 f=e.discover(t.channels,{properties:{lastModified:{maximum:s.lastModified}}}),y=await h(f);o(y.name).toEqual(s.name),o(await f.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",(()=>{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};
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,SAucnC,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,SA4bnC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@graffiti-garden/api",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "The heart of Graffiti",
5
5
  "types": "./dist/src/index.d.ts",
6
6
  "module": "./dist/index.js",
package/src/3-errors.ts CHANGED
@@ -61,3 +61,11 @@ 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
@@ -58,7 +58,9 @@ export const graffitiCRUDTests = (
58
58
  expect(beforeReplaced.name).toEqual(previous.name);
59
59
  expect(beforeReplaced.actor).toEqual(previous.actor);
60
60
  expect(beforeReplaced.source).toEqual(previous.source);
61
- expect(beforeReplaced.lastModified).toBeGreaterThan(gotten.lastModified);
61
+ expect(beforeReplaced.lastModified).toBeGreaterThanOrEqual(
62
+ gotten.lastModified,
63
+ );
62
64
 
63
65
  // Get it again
64
66
  const afterReplaced = await graffiti.get(previous, {});
@@ -70,7 +72,7 @@ export const graffitiCRUDTests = (
70
72
  const beforeDeleted = await graffiti.delete(afterReplaced, session);
71
73
  expect(beforeDeleted.tombstone).toEqual(true);
72
74
  expect(beforeDeleted.value).toEqual(newValue);
73
- expect(beforeDeleted.lastModified).toBeGreaterThan(
75
+ expect(beforeDeleted.lastModified).toBeGreaterThanOrEqual(
74
76
  beforeReplaced.lastModified,
75
77
  );
76
78
 
@@ -92,28 +94,15 @@ export const graffitiCRUDTests = (
92
94
  ),
93
95
  ).rejects.toThrow(GraffitiErrorForbidden);
94
96
 
95
- await expect(
96
- graffiti.delete(
97
- {
98
- name: "asdf",
99
- source: "asdf",
100
- actor: session2.actor,
101
- },
102
- session1,
103
- ),
104
- ).rejects.toThrow(GraffitiErrorForbidden);
97
+ const putted = await graffiti.put({ value: {}, channels: [] }, session2);
105
98
 
106
- await expect(
107
- graffiti.patch(
108
- {},
109
- {
110
- name: "asdf",
111
- source: "asdf",
112
- actor: session2.actor,
113
- },
114
- session1,
115
- ),
116
- ).rejects.toThrow(GraffitiErrorForbidden);
99
+ await expect(graffiti.delete(putted, session1)).rejects.toThrow(
100
+ GraffitiErrorForbidden,
101
+ );
102
+
103
+ await expect(graffiti.patch({}, putted, session1)).rejects.toThrow(
104
+ GraffitiErrorForbidden,
105
+ );
117
106
  });
118
107
 
119
108
  it("put and get with schema", async () => {