@kubun/server 0.1.0 → 0.2.1

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.
Files changed (55) hide show
  1. package/lib/data/automerge.d.ts +5 -0
  2. package/lib/data/automerge.d.ts.map +1 -0
  3. package/lib/data/automerge.js +8 -0
  4. package/lib/data/graphql.d.ts +4 -4
  5. package/lib/data/graphql.d.ts.map +1 -1
  6. package/lib/data/graphql.js +7 -4
  7. package/lib/data/mutations.d.ts +12 -3
  8. package/lib/data/mutations.d.ts.map +1 -1
  9. package/lib/data/mutations.js +65 -29
  10. package/lib/handlers/document.d.ts +8 -0
  11. package/lib/handlers/document.d.ts.map +1 -0
  12. package/lib/handlers/document.js +30 -0
  13. package/lib/handlers/graph.d.ts +4 -4
  14. package/lib/handlers/graph.d.ts.map +1 -1
  15. package/lib/handlers/graph.js +46 -22
  16. package/lib/handlers/index.d.ts +4 -4
  17. package/lib/handlers/index.d.ts.map +1 -1
  18. package/lib/handlers/index.js +5 -1
  19. package/lib/index.d.ts +2 -2
  20. package/lib/index.d.ts.map +1 -1
  21. package/lib/index.js +1 -1
  22. package/lib/server.d.ts +11 -9
  23. package/lib/server.d.ts.map +1 -1
  24. package/lib/server.js +15 -16
  25. package/package.json +16 -16
  26. package/lib/data/cursor.d.ts +0 -8
  27. package/lib/data/cursor.d.ts.map +0 -1
  28. package/lib/data/cursor.js +0 -7
  29. package/lib/data/db.d.ts +0 -31
  30. package/lib/data/db.d.ts.map +0 -1
  31. package/lib/data/db.js +0 -192
  32. package/lib/data/engines/engine.d.ts +0 -6
  33. package/lib/data/engines/engine.d.ts.map +0 -1
  34. package/lib/data/engines/engine.js +0 -12
  35. package/lib/data/engines/postgres.d.ts +0 -25
  36. package/lib/data/engines/postgres.d.ts.map +0 -1
  37. package/lib/data/engines/postgres.js +0 -30
  38. package/lib/data/engines/sqlite.d.ts +0 -24
  39. package/lib/data/engines/sqlite.d.ts.map +0 -1
  40. package/lib/data/engines/sqlite.js +0 -29
  41. package/lib/data/engines/types.d.ts +0 -19
  42. package/lib/data/engines/types.d.ts.map +0 -1
  43. package/lib/data/engines/types.js +0 -1
  44. package/lib/data/migrations/0-init.d.ts +0 -4
  45. package/lib/data/migrations/0-init.d.ts.map +0 -1
  46. package/lib/data/migrations/0-init.js +0 -22
  47. package/lib/data/migrations/migrations.d.ts +0 -4
  48. package/lib/data/migrations/migrations.d.ts.map +0 -1
  49. package/lib/data/migrations/migrations.js +0 -6
  50. package/lib/data/query-builder.d.ts +0 -12
  51. package/lib/data/query-builder.d.ts.map +0 -1
  52. package/lib/data/query-builder.js +0 -218
  53. package/lib/data/types.d.ts +0 -134
  54. package/lib/data/types.d.ts.map +0 -1
  55. package/lib/data/types.js +0 -1
@@ -0,0 +1,5 @@
1
+ import * as A from '@automerge/automerge/slim';
2
+ import type { DocumentData } from '@kubun/protocol';
3
+ export declare const automergeReady: PromiseLike<void>;
4
+ export declare function automergeToData(doc: A.Doc<DocumentData>): DocumentData;
5
+ //# sourceMappingURL=automerge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automerge.d.ts","sourceRoot":"","sources":["../../src/data/automerge.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,CAAC,MAAM,2BAA2B,CAAA;AAE9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEnD,eAAO,MAAM,cAAc,mBAA0D,CAAA;AAErF,wBAAgB,eAAe,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,YAAY,CAEtE"}
@@ -0,0 +1,8 @@
1
+ // @ts-ignore missing type definition
2
+ import { automergeWasmBase64 } from '@automerge/automerge/automerge.wasm.base64.js';
3
+ import * as A from '@automerge/automerge/slim';
4
+ import { lazy } from '@enkaku/async';
5
+ export const automergeReady = lazy(()=>A.initializeBase64Wasm(automergeWasmBase64));
6
+ export function automergeToData(doc) {
7
+ return JSON.parse(JSON.stringify(doc));
8
+ }
@@ -1,7 +1,7 @@
1
+ import type { Document, DocumentData, QueryDocumentsParams } from '@kubun/db';
2
+ import { SchemaBuilder, type SharedDefinitions } from '@kubun/graphql';
1
3
  import type { DocumentID } from '@kubun/id';
2
4
  import { type Connection, type ConnectionArguments } from 'graphql-relay';
3
- import { SchemaBuilder, type SharedDefinitions } from '../../../graphql/lib/schema.js';
4
- import type { Document, DocumentData, QueryDocumentsParams } from './types.js';
5
5
  export type ContextListDocumentsParams = {
6
6
  model: string | null;
7
7
  ids: Array<string>;
@@ -11,11 +11,11 @@ export type Context = {
11
11
  listDocuments: (params: ContextListDocumentsParams) => Promise<Array<Document>>;
12
12
  loadDocument: (id: DocumentID | string) => Promise<Document | null>;
13
13
  queryDocuments: (params: QueryDocumentsParams) => Promise<Connection<Document>>;
14
- viewerDID: string | null;
14
+ viewerDID: string;
15
15
  mutatedDocuments?: Record<string, Document>;
16
16
  };
17
17
  export declare class ServerSchemaBuilder extends SchemaBuilder<Document, Context> {
18
- getViewer(ctx: Context): string | null;
18
+ getViewer(ctx: Context): string;
19
19
  loadDocument(id: string, ctx: Context): Promise<Document | null>;
20
20
  resolveConnection(model: string | null, args: ConnectionArguments, ctx: Context): Promise<Connection<Document>>;
21
21
  resolveList(model: string | null, ids: Array<string>, ctx: Context): Promise<Array<Document>>;
@@ -1 +1 @@
1
- {"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../../src/data/graphql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE3C,OAAO,EACL,KAAK,UAAU,EACf,KAAK,mBAAmB,EAEzB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,aAAa,EAAE,KAAK,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAEtF,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAE9E,MAAM,MAAM,0BAA0B,GAAG;IACvC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC1E,aAAa,EAAE,CAAC,MAAM,EAAE,0BAA0B,KAAK,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC/E,YAAY,EAAE,CAAC,EAAE,EAAE,UAAU,GAAG,MAAM,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IACnE,cAAc,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC/E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;CAC5C,CAAA;AAED,qBAAa,mBAAoB,SAAQ,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC;IACvE,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI;IAIhC,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAIhE,iBAAiB,CACrB,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,IAAI,EAAE,mBAAmB,EACzB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAI1B,WAAW,CACf,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAI3B,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,IAAI;CAgEpF"}
1
+ {"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../../src/data/graphql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAC7E,OAAO,EAAkB,aAAa,EAAE,KAAK,iBAAiB,EAAU,MAAM,gBAAgB,CAAA;AAC9F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE3C,OAAO,EACL,KAAK,UAAU,EACf,KAAK,mBAAmB,EAEzB,MAAM,eAAe,CAAA;AAEtB,MAAM,MAAM,0BAA0B,GAAG;IACvC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,OAAO,GAAG;IACpB,cAAc,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC1E,aAAa,EAAE,CAAC,MAAM,EAAE,0BAA0B,KAAK,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC/E,YAAY,EAAE,CAAC,EAAE,EAAE,UAAU,GAAG,MAAM,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IACnE,cAAc,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC/E,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;CAC5C,CAAA;AAED,qBAAa,mBAAoB,SAAQ,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC;IACvE,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM;IAIzB,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAIhE,iBAAiB,CACrB,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,IAAI,EAAE,mBAAmB,EACzB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAI1B,WAAW,CACf,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,EAClB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAI3B,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,iBAAiB,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,IAAI;CAiEpF"}
@@ -1,6 +1,6 @@
1
+ import { PatchOperation, SchemaBuilder, toList } from '@kubun/graphql';
1
2
  import { GraphQLID, GraphQLNonNull } from 'graphql';
2
3
  import { mutationWithClientMutationId } from 'graphql-relay';
3
- import { SchemaBuilder } from '../../../graphql/lib/schema.js';
4
4
  export class ServerSchemaBuilder extends SchemaBuilder {
5
5
  getViewer(ctx) {
6
6
  return ctx.viewerDID;
@@ -30,7 +30,7 @@ export class ServerSchemaBuilder extends SchemaBuilder {
30
30
  case 'interface':
31
31
  // Interfaces don't have mutations
32
32
  return;
33
- case 'list':
33
+ case 'default':
34
34
  {
35
35
  this._mutations[`create${name}`] = mutationWithClientMutationId({
36
36
  name: `Create${name}`,
@@ -54,7 +54,7 @@ export class ServerSchemaBuilder extends SchemaBuilder {
54
54
  });
55
55
  break;
56
56
  }
57
- case 'set':
57
+ case 'unique':
58
58
  {
59
59
  this._mutations[`set${name}`] = mutationWithClientMutationId({
60
60
  name: `Set${name}`,
@@ -85,7 +85,10 @@ export class ServerSchemaBuilder extends SchemaBuilder {
85
85
  id: {
86
86
  type: new GraphQLNonNull(GraphQLID)
87
87
  },
88
- data: {
88
+ patch: {
89
+ type: new GraphQLNonNull(toList(PatchOperation))
90
+ },
91
+ from: {
89
92
  type: this._inputObjects[`${id}-update`]
90
93
  }
91
94
  }),
@@ -1,4 +1,13 @@
1
- import type { KubunDB } from './db.js';
2
- import type { Document } from './types.js';
3
- export declare function applyMutation(db: KubunDB, token: string): Promise<Document>;
1
+ import { type Validator } from '@enkaku/schema';
2
+ import type { Document, KubunDB } from '@kubun/db';
3
+ import { type DocumentData } from '@kubun/protocol';
4
+ type DocumentValidator = Validator<DocumentData>;
5
+ type ValidatorsRecord = Record<string, Promise<DocumentValidator>>;
6
+ export type MutationContext = {
7
+ db: KubunDB;
8
+ validators: ValidatorsRecord;
9
+ };
10
+ export declare function getDocumentValidator(ctx: MutationContext, id: string): Promise<DocumentValidator>;
11
+ export declare function applyMutation(ctx: MutationContext, token: string): Promise<Document>;
12
+ export {};
4
13
  //# sourceMappingURL=mutations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mutations.d.ts","sourceRoot":"","sources":["../../src/data/mutations.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAoB1C,wBAAsB,aAAa,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAqDjF"}
1
+ {"version":3,"file":"mutations.d.ts","sourceRoot":"","sources":["../../src/data/mutations.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAA2B,MAAM,gBAAgB,CAAA;AAExE,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAElD,OAAO,EAAE,KAAK,YAAY,EAA2C,MAAM,iBAAiB,CAAA;AAM5F,KAAK,iBAAiB,GAAG,SAAS,CAAC,YAAY,CAAC,CAAA;AAChD,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAA;AAElE,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,OAAO,CAAA;IACX,UAAU,EAAE,gBAAgB,CAAA;CAC7B,CAAA;AAED,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAOjG;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CA4E1F"}
@@ -1,45 +1,68 @@
1
- import * as A from '@automerge/automerge';
1
+ // @ts-ignore missing type definition
2
+ import * as A from '@automerge/automerge/slim';
2
3
  import { fromB64 } from '@enkaku/codec';
3
- import { verifyToken } from '@enkaku/jwt';
4
- import { createSchemaType } from '@enkaku/schema';
4
+ import { asType, createValidator } from '@enkaku/schema';
5
+ import { verifyToken } from '@enkaku/token';
5
6
  import { DocumentID } from '@kubun/id';
6
7
  import { documentMutation } from '@kubun/protocol';
7
- function automergeToData(doc) {
8
- return JSON.parse(JSON.stringify(doc));
9
- }
10
- function createFromAutomerge(bytes) {
11
- return automergeToData(A.load(bytes));
12
- }
13
- function mergeChanges(doc, changes, incremental = false) {
14
- const current = A.from(doc);
15
- const updated = incremental ? A.loadIncremental(current, changes) : A.merge(current, A.load(changes));
16
- return automergeToData(updated);
8
+ import { automergeReady, automergeToData } from './automerge.js';
9
+ const validateMutation = createValidator(documentMutation);
10
+ export function getDocumentValidator(ctx, id) {
11
+ if (ctx.validators[id] == null) {
12
+ ctx.validators[id] = ctx.db.getDocumentModel(id).then((model)=>createValidator({
13
+ ...model.schema,
14
+ $id: id
15
+ }));
16
+ }
17
+ return ctx.validators[id];
17
18
  }
18
- const mutationType = createSchemaType(documentMutation);
19
- export async function applyMutation(db, token) {
20
- const verified = await verifyToken(token);
21
- const mutation = mutationType.cast(verified.payload);
19
+ export async function applyMutation(ctx, token) {
20
+ const [verified] = await Promise.all([
21
+ verifyToken(token),
22
+ automergeReady
23
+ ]);
24
+ const mutation = asType(validateMutation, verified.payload);
22
25
  switch(mutation.typ){
23
26
  case 'change':
24
27
  {
25
28
  const id = DocumentID.fromString(mutation.sub);
26
- const doc = await db.getDocument(id);
29
+ const docID = id.toString();
30
+ const doc = await ctx.db.getDocument(id);
27
31
  if (doc == null) {
28
- throw new Error(`Document not found: ${id.toString()}`);
32
+ throw new Error(`Document not found: ${docID}`);
29
33
  }
30
34
  if (mutation.iss !== doc.owner) {
31
35
  // TODO: verify capabilities if issuer is not owner
32
36
  throw new Error('Invalid mutation issuer');
33
37
  }
34
38
  if (mutation.data === null) {
35
- return await db.saveDocument(id, null);
39
+ return await ctx.db.saveDocument({
40
+ id,
41
+ data: null,
42
+ state: null
43
+ });
36
44
  }
37
45
  if (doc.data === null) {
38
- throw new Error(`Cannot apply changes to empty document: ${id.toString()}`);
46
+ throw new Error(`Cannot apply changes to empty document: ${docID}`);
39
47
  }
40
- const data = mergeChanges(doc.data, fromB64(mutation.data), mutation.inc);
41
- // TODO: content validation using model's schema
42
- return await db.saveDocument(id, data);
48
+ const [docStates, validator] = await Promise.all([
49
+ ctx.db.getDocumentStates([
50
+ docID
51
+ ]),
52
+ getDocumentValidator(ctx, id.model.toString())
53
+ ]);
54
+ // Create doc from data object if state is not present
55
+ const currentDoc = docStates[docID] ? A.load(docStates[docID]) : A.from(doc.data);
56
+ const changes = fromB64(mutation.data);
57
+ // Apply incremental changes or full merge
58
+ const newDoc = mutation.inc ? A.loadIncremental(currentDoc, changes) : A.merge(currentDoc, A.load(changes));
59
+ // Validate merged data
60
+ const data = asType(validator, automergeToData(newDoc));
61
+ return await ctx.db.saveDocument({
62
+ id,
63
+ data,
64
+ state: A.save(newDoc)
65
+ });
43
66
  }
44
67
  case 'set':
45
68
  {
@@ -49,16 +72,29 @@ export async function applyMutation(db, token) {
49
72
  throw new Error('Invalid mutation issuer');
50
73
  }
51
74
  const id = DocumentID.fromString(mutation.sub);
52
- const data = mutation.data === null ? null : createFromAutomerge(fromB64(mutation.data));
53
- const doc = await db.getDocument(id);
75
+ const [doc, validator] = await Promise.all([
76
+ ctx.db.getDocument(id),
77
+ getDocumentValidator(ctx, id.model.toString())
78
+ ]);
79
+ const mergeDoc = mutation.data === null ? null : A.load(fromB64(mutation.data));
80
+ const data = mergeDoc ? asType(validator, automergeToData(mergeDoc)) : null;
54
81
  if (doc === null) {
55
- return await db.createDocument(id, owner, data, fromB64(mutation.unq));
82
+ return await ctx.db.createDocument({
83
+ id,
84
+ owner,
85
+ data,
86
+ state: mergeDoc ? A.save(mergeDoc) : null,
87
+ unique: fromB64(mutation.unq)
88
+ });
56
89
  }
57
90
  if (doc.owner !== owner) {
58
91
  throw new Error(`Cannot change owner from ${doc.owner} to ${owner} in document: ${id.toString()}`);
59
92
  }
60
- // TODO: content validation using model's schema
61
- return await db.saveDocument(id, data);
93
+ return await ctx.db.saveDocument({
94
+ id,
95
+ data,
96
+ state: mergeDoc ? A.save(mergeDoc) : null
97
+ });
62
98
  }
63
99
  default:
64
100
  throw new Error('Unsupported mutation type');
@@ -0,0 +1,8 @@
1
+ import type { ProcedureHandlers } from '@enkaku/server';
2
+ import type { KubunDB } from '@kubun/db';
3
+ import type { DocumentProtocol } from '@kubun/protocol';
4
+ export type CreateHandlersParams = {
5
+ db: KubunDB;
6
+ };
7
+ export declare function createHandlers(handlersParams: CreateHandlersParams): ProcedureHandlers<DocumentProtocol>;
8
+ //# sourceMappingURL=document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../src/handlers/document.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAIvD,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,OAAO,CAAA;CACZ,CAAA;AAED,wBAAgB,cAAc,CAC5B,cAAc,EAAE,oBAAoB,GACnC,iBAAiB,CAAC,gBAAgB,CAAC,CA+BrC"}
@@ -0,0 +1,30 @@
1
+ // import * as A from '@automerge/automerge/slim'
2
+ import { toB64 } from '@enkaku/codec';
3
+ export function createHandlers(handlersParams) {
4
+ const { db } = handlersParams;
5
+ return {
6
+ 'document/sync': async (ctx)=>{
7
+ const ids = Object.keys(ctx.param.documents);
8
+ // const [loadedStates] = await Promise.all([db.getDocumentStates(ids), automergeReady])
9
+ const loadedStates = await db.getDocumentStates(ids);
10
+ const states = ids.reduce((acc, id)=>{
11
+ const loaded = loadedStates[id];
12
+ if (loaded == null) {
13
+ acc[id] = null;
14
+ return acc;
15
+ }
16
+ const provided = ctx.param.documents[id];
17
+ if (provided == null) {
18
+ acc[id] = toB64(loaded);
19
+ } else {
20
+ // TODO: send diff rather than full doc state
21
+ acc[id] = toB64(loaded);
22
+ }
23
+ return acc;
24
+ }, {});
25
+ return {
26
+ states
27
+ };
28
+ }
29
+ };
30
+ }
@@ -1,8 +1,8 @@
1
- import type { CommandHandlers } from '@enkaku/server';
2
- import { type GraphCommands } from '@kubun/protocol';
3
- import type { KubunDB } from '../data/db.js';
1
+ import type { ProcedureHandlers } from '@enkaku/server';
2
+ import type { KubunDB } from '@kubun/db';
3
+ import { type GraphProtocol } from '@kubun/protocol';
4
4
  export type CreateHandlersParams = {
5
5
  db: KubunDB;
6
6
  };
7
- export declare function createHandlers(handlersParams: CreateHandlersParams): CommandHandlers<GraphCommands>;
7
+ export declare function createHandlers(handlersParams: CreateHandlersParams): ProcedureHandlers<GraphProtocol>;
8
8
  //# sourceMappingURL=graph.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/handlers/graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAErD,OAAO,EAGL,KAAK,aAAa,EAEnB,MAAM,iBAAiB,CAAA;AAKxB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAc5C,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,OAAO,CAAA;CACZ,CAAA;AAcD,wBAAgB,cAAc,CAC5B,cAAc,EAAE,oBAAoB,GACnC,eAAe,CAAC,aAAa,CAAC,CAwKhC"}
1
+ {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/handlers/graph.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEvD,OAAO,KAAK,EAIV,OAAO,EAER,MAAM,WAAW,CAAA;AAElB,OAAO,EAIL,KAAK,aAAa,EACnB,MAAM,iBAAiB,CAAA;AAWxB,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,OAAO,CAAA;CACZ,CAAA;AAcD,wBAAgB,cAAc,CAC5B,cAAc,EAAE,oBAAoB,GACnC,iBAAiB,CAAC,aAAa,CAAC,CA0LlC"}
@@ -1,7 +1,7 @@
1
+ import { fromB64, toB64 } from '@enkaku/codec';
1
2
  import { AttachmentID, DocumentID } from '@kubun/id';
2
3
  import { GraphModel } from '@kubun/protocol';
3
4
  import { Kind, OperationTypeNode, execute, parse } from 'graphql';
4
- import { fromB64 } from '@enkaku/codec';
5
5
  import { ServerSchemaBuilder } from '../data/graphql.js';
6
6
  import { applyMutation } from '../data/mutations.js';
7
7
  export function createHandlers(handlersParams) {
@@ -9,7 +9,12 @@ export function createHandlers(handlersParams) {
9
9
  const graphModels = {};
10
10
  async function getGraphModels(id) {
11
11
  if (graphModels[id] == null) {
12
- graphModels[id] = db.getGraph(id);
12
+ graphModels[id] = db.getGraph(id).then((graph)=>{
13
+ if (graph == null) {
14
+ throw new Error(`Graph not found: ${id}`);
15
+ }
16
+ return graph.record;
17
+ });
13
18
  }
14
19
  return await graphModels[id];
15
20
  }
@@ -88,7 +93,7 @@ export function createHandlers(handlersParams) {
88
93
  loadDocument,
89
94
  queryDocuments,
90
95
  mutatedDocuments: contextParams.mutatedDocuments,
91
- viewerDID: contextParams.viewerDID ?? null
96
+ viewerDID: contextParams.viewerDID
92
97
  };
93
98
  }
94
99
  async function executeGraphQL(params) {
@@ -115,12 +120,13 @@ export function createHandlers(handlersParams) {
115
120
  };
116
121
  }
117
122
  return {
118
- 'graph/create': async (ctx)=>{
123
+ 'graph/deploy': async (ctx)=>{
119
124
  const model = GraphModel.fromClusters({
120
- clusters: ctx.params.clusters
125
+ clusters: ctx.param.clusters
121
126
  });
122
127
  const id = await db.createGraph({
123
- name: ctx.params.name,
128
+ id: ctx.param.id,
129
+ name: ctx.param.name,
124
130
  record: model.record
125
131
  });
126
132
  return {
@@ -128,28 +134,38 @@ export function createHandlers(handlersParams) {
128
134
  models: model.record
129
135
  };
130
136
  },
137
+ 'graph/list': async ()=>{
138
+ const graphs = await db.listGraphs();
139
+ return {
140
+ graphs: graphs.map((graph)=>({
141
+ id: graph.id,
142
+ name: graph.name
143
+ }))
144
+ };
145
+ },
131
146
  'graph/load': async (ctx)=>{
132
147
  return {
133
- models: await getGraphModels(ctx.params.id)
148
+ models: await getGraphModels(ctx.param.id)
134
149
  };
135
150
  },
136
151
  'graph/query': async (ctx)=>{
137
- // TODO(enkaku): request should be JWT:
138
- // schema ID is token subject
139
- // viewer ID is token audience or fallback to the issuer
152
+ const payload = ctx.message.payload;
153
+ // TODO: viewerDID is token subject or fallback to the issuer
140
154
  // also add token capabilities check
155
+ const viewerDID = payload.iss;
141
156
  return await executeGraphQL({
142
157
  type: OperationTypeNode.QUERY,
143
- schemaID: ctx.params.id,
144
- text: ctx.params.text,
145
- variables: ctx.params.variables
158
+ schemaID: ctx.param.id,
159
+ text: ctx.param.text,
160
+ variables: ctx.param.variables ?? {},
161
+ viewerDID
146
162
  });
147
163
  },
148
164
  'graph/mutate': async (ctx)=>{
149
- const attachments = Object.entries(ctx.params.attachments ?? {}).map(([key, value])=>{
165
+ const attachments = Object.entries(ctx.param.attachments ?? {}).map(([key, value])=>{
150
166
  const aid = AttachmentID.fromString(key);
151
167
  return {
152
- id: aid.cid.toString(),
168
+ id: toB64(aid.digest),
153
169
  data: fromB64(value)
154
170
  };
155
171
  });
@@ -158,16 +174,24 @@ export function createHandlers(handlersParams) {
158
174
  }
159
175
  // Apply mutations and make the documents available to the GraphQL resolvers in the context
160
176
  const mutatedDocuments = {};
161
- await Promise.all(Object.entries(ctx.params.mutations).map(async ([key, mutation])=>{
162
- mutatedDocuments[key] = await applyMutation(db, mutation);
177
+ const validators = {};
178
+ await Promise.all(Object.entries(ctx.param.mutations).map(async ([key, mutation])=>{
179
+ mutatedDocuments[key] = await applyMutation({
180
+ db,
181
+ validators
182
+ }, mutation);
163
183
  }));
184
+ const payload = ctx.message.payload;
185
+ // TODO: viewerDID is token subject or fallback to the issuer
186
+ // also add token capabilities check
187
+ const viewerDID = payload.iss;
164
188
  return await executeGraphQL({
165
189
  type: OperationTypeNode.MUTATION,
166
- schemaID: ctx.params.id,
167
- text: ctx.params.text,
168
- variables: ctx.params.variables,
169
- // viewerDID: input.invocation.issuer.did(),
170
- mutatedDocuments
190
+ schemaID: ctx.param.id,
191
+ text: ctx.param.text,
192
+ variables: ctx.param.variables ?? {},
193
+ mutatedDocuments,
194
+ viewerDID
171
195
  });
172
196
  }
173
197
  };
@@ -1,9 +1,9 @@
1
- import type { CommandHandlers } from '@enkaku/server';
2
- import type { GraphCommands } from '@kubun/protocol';
3
- import type { KubunDB } from '../data/db.js';
1
+ import type { ProcedureHandlers } from '@enkaku/server';
2
+ import type { KubunDB } from '@kubun/db';
3
+ import type { Protocol } from '@kubun/protocol';
4
4
  export type CreateHandlersParams = {
5
5
  db: KubunDB;
6
6
  };
7
- export type Handlers = CommandHandlers<GraphCommands>;
7
+ export type Handlers = ProcedureHandlers<Protocol>;
8
8
  export declare function createHandlers(params: CreateHandlersParams): Handlers;
9
9
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAEpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAI5C,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,OAAO,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,eAAe,CAAC,aAAa,CAAC,CAAA;AAErD,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,QAAQ,CAErE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAK/C,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,OAAO,CAAA;CACZ,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAA;AAElD,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,QAAQ,CAKrE"}
@@ -1,4 +1,8 @@
1
+ import { createHandlers as documentHandlers } from './document.js';
1
2
  import { createHandlers as graphHandlers } from './graph.js';
2
3
  export function createHandlers(params) {
3
- return graphHandlers(params);
4
+ return {
5
+ ...documentHandlers(params),
6
+ ...graphHandlers(params)
7
+ };
4
8
  }
package/lib/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { KubunDB, type KubunDBParams } from './data/db.js';
2
- export { KubunServer, type ServerParams } from './server.js';
1
+ export { type CreateHandlersParams, createHandlers } from './handlers/index.js';
2
+ export { type CreateClientParams, KubunServer, type ServerParams } from './server.js';
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AAC1D,OAAO,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,oBAAoB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC/E,OAAO,EAAE,KAAK,kBAAkB,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA"}
package/lib/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { KubunDB } from './data/db.js';
1
+ export { createHandlers } from './handlers/index.js';
2
2
  export { KubunServer } from './server.js';
package/lib/server.d.ts CHANGED
@@ -1,18 +1,20 @@
1
- import type { Signer } from '@enkaku/jwt';
2
1
  import { type Server } from '@enkaku/server';
3
- import { KubunClient } from '@kubun/client';
4
- import type { ServerTransport } from '@kubun/protocol';
5
- import { KubunDB, type KubunDBParams } from './data/db.js';
2
+ import { type ClientParams, KubunClient } from '@kubun/client';
3
+ import { type DBParams, KubunDB } from '@kubun/db';
4
+ import type { Protocol, ServerTransport } from '@kubun/protocol';
6
5
  export type ServerParams = {
7
- db: KubunDB | KubunDBParams;
8
- signer: Signer;
6
+ access?: Record<string, boolean | Array<string>>;
7
+ db: KubunDB | DBParams;
8
+ id: string;
9
+ };
10
+ export type CreateClientParams = Omit<ClientParams, 'serverID' | 'transport'> & {
11
+ signal?: AbortSignal;
9
12
  };
10
13
  export declare class KubunServer {
11
14
  #private;
12
15
  constructor(params: ServerParams);
13
- get client(): KubunClient;
14
16
  get db(): KubunDB;
15
- createClient(signer: Signer, signal?: AbortSignal): KubunClient;
16
- serve(transport: ServerTransport, signal?: AbortSignal): Server;
17
+ createClient(params: CreateClientParams): KubunClient;
18
+ serve(transport: ServerTransport, signal?: AbortSignal): Server<Protocol>;
17
19
  }
18
20
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,EAAE,KAAK,MAAM,EAAS,MAAM,gBAAgB,CAAA;AAEnD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,KAAK,EAAgC,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEpF,OAAO,EAAE,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AAG1D,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,OAAO,GAAG,aAAa,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,qBAAa,WAAW;;gBAMV,MAAM,EAAE,YAAY;IAOhC,IAAI,MAAM,IAAI,WAAW,CAKxB;IAED,IAAI,EAAE,IAAI,OAAO,CAEhB;IAED,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,WAAW;IAM/D,KAAK,CAAC,SAAS,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM;CAGhE"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,MAAM,EAAS,MAAM,gBAAgB,CAAA;AAEnD,OAAO,EAAE,KAAK,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC9D,OAAO,EAAE,KAAK,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,KAAK,EAAiB,QAAQ,EAAiB,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAI9F,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAA;IAChD,EAAE,EAAE,OAAO,GAAG,QAAQ,CAAA;IACtB,EAAE,EAAE,MAAM,CAAA;CACX,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,IAAI,CAAC,YAAY,EAAE,UAAU,GAAG,WAAW,CAAC,GAAG;IAC9E,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,CAAA;AAED,qBAAa,WAAW;;gBAMV,MAAM,EAAE,YAAY;IAQhC,IAAI,EAAE,IAAI,OAAO,CAEhB;IAED,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,WAAW;IAOrD,KAAK,CAAC,SAAS,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC;CAS1E"}
package/lib/server.js CHANGED
@@ -1,43 +1,42 @@
1
1
  import { serve } from '@enkaku/server';
2
- import { createDirectTransports } from '@enkaku/transport';
2
+ import { DirectTransports } from '@enkaku/transport';
3
3
  import { KubunClient } from '@kubun/client';
4
- import { KubunDB } from './data/db.js';
4
+ import { KubunDB } from '@kubun/db';
5
5
  import { createHandlers } from './handlers/index.js';
6
6
  export class KubunServer {
7
- #client;
7
+ #access;
8
8
  #db;
9
9
  #handlers;
10
- #signer;
10
+ #id;
11
11
  constructor(params){
12
- const { db, signer } = params;
12
+ const { access, db, id } = params;
13
+ this.#access = access ?? {};
13
14
  this.#db = db instanceof KubunDB ? db : new KubunDB(db);
14
15
  this.#handlers = createHandlers({
15
16
  db: this.#db
16
17
  });
17
- this.#signer = signer;
18
- }
19
- get client() {
20
- if (this.#client == null) {
21
- this.#client = this.createClient(this.#signer);
22
- }
23
- return this.#client;
18
+ this.#id = id;
24
19
  }
25
20
  get db() {
26
21
  return this.#db;
27
22
  }
28
- createClient(signer, signal) {
29
- const transports = createDirectTransports({
23
+ createClient(params) {
24
+ const { signal, ...clientParams } = params;
25
+ const transports = new DirectTransports({
30
26
  signal
31
27
  });
32
28
  this.serve(transports.server, signal);
33
29
  return new KubunClient({
34
- signer,
35
- transport: transports.client
30
+ serverID: this.#id,
31
+ transport: transports.client,
32
+ ...clientParams
36
33
  });
37
34
  }
38
35
  serve(transport, signal) {
39
36
  return serve({
37
+ access: this.#access,
40
38
  handlers: this.#handlers,
39
+ id: this.#id,
41
40
  signal,
42
41
  transport
43
42
  });