@kubun/server 0.4.3 → 0.5.0

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 (44) hide show
  1. package/lib/data/access-control.d.ts +1 -2
  2. package/lib/data/access-control.js +1 -164
  3. package/lib/data/graphql.d.ts +0 -1
  4. package/lib/data/graphql.js +1 -75
  5. package/lib/data/mutation-capture.d.ts +7 -0
  6. package/lib/data/mutation-capture.js +1 -0
  7. package/lib/data/mutation-log.d.ts +17 -0
  8. package/lib/data/mutation-log.js +1 -0
  9. package/lib/data/mutations.d.ts +3 -3
  10. package/lib/data/mutations.js +1 -114
  11. package/lib/handlers/document.d.ts +0 -1
  12. package/lib/handlers/document.js +1 -28
  13. package/lib/handlers/graph.d.ts +0 -1
  14. package/lib/handlers/graph.js +1 -191
  15. package/lib/handlers/index.d.ts +0 -1
  16. package/lib/handlers/index.js +1 -8
  17. package/lib/handlers/sync.d.ts +4 -0
  18. package/lib/handlers/sync.js +1 -0
  19. package/lib/handlers/types.d.ts +0 -1
  20. package/lib/handlers/types.js +1 -1
  21. package/lib/index.d.ts +0 -1
  22. package/lib/index.js +1 -3
  23. package/lib/server.d.ts +4 -2
  24. package/lib/server.js +1 -68
  25. package/lib/sync/crdt-merge.d.ts +26 -0
  26. package/lib/sync/crdt-merge.js +1 -0
  27. package/lib/sync/mutation-replay.d.ts +31 -0
  28. package/lib/sync/mutation-replay.js +1 -0
  29. package/lib/sync/peer-registry.d.ts +26 -0
  30. package/lib/sync/peer-registry.js +1 -0
  31. package/lib/sync/sync-client.d.ts +66 -0
  32. package/lib/sync/sync-client.js +1 -0
  33. package/lib/sync/sync-manager.d.ts +57 -0
  34. package/lib/sync/sync-manager.js +1 -0
  35. package/package.json +22 -21
  36. package/lib/data/access-control.d.ts.map +0 -1
  37. package/lib/data/graphql.d.ts.map +0 -1
  38. package/lib/data/mutations.d.ts.map +0 -1
  39. package/lib/handlers/document.d.ts.map +0 -1
  40. package/lib/handlers/graph.d.ts.map +0 -1
  41. package/lib/handlers/index.d.ts.map +0 -1
  42. package/lib/handlers/types.d.ts.map +0 -1
  43. package/lib/index.d.ts.map +0 -1
  44. package/lib/server.d.ts.map +0 -1
@@ -18,7 +18,7 @@ export type AccessChecker = (doc: DocumentNode, permissionType: 'read' | 'write'
18
18
  /**
19
19
  * Parse and validate access permissions from document data
20
20
  */
21
- export declare function parseDocumentAccessPermissions(data: any): AccessPermissions | null;
21
+ export declare function parseDocumentAccessPermissions(data: unknown): AccessPermissions | null;
22
22
  /**
23
23
  * Validate that DIDs have the correct format
24
24
  */
@@ -43,4 +43,3 @@ export declare function checkAccess(viewerDID: string | null, document: Document
43
43
  * Create an access checker function bound to specific viewer and delegation tokens
44
44
  */
45
45
  export declare function createAccessChecker(viewerDID: string | null, delegationTokens: Array<string> | undefined, db: KubunDB, serverConfig: ServerAccessConfig): AccessChecker;
46
- //# sourceMappingURL=access-control.d.ts.map
@@ -1,164 +1 @@
1
- import { checkCapability } from '@enkaku/capability';
2
- /**
3
- * Parse and validate access permissions from document data
4
- */ export function parseDocumentAccessPermissions(data) {
5
- try {
6
- if (!data.accessPermissions) return null;
7
- const perms = data.accessPermissions;
8
- // Basic validation - check if the permission object has the expected structure
9
- if (typeof perms !== 'object') {
10
- console.warn('Invalid accessPermissions structure in document, ignoring');
11
- return null;
12
- }
13
- return perms;
14
- } catch (error) {
15
- console.warn('Failed to parse document access permissions:', error);
16
- return null;
17
- }
18
- }
19
- /**
20
- * Validate that DIDs have the correct format
21
- */ export function validateDIDs(dids) {
22
- for (const did of dids){
23
- if (!did.startsWith('did:')) {
24
- throw new Error(`Invalid DID format: ${did}`);
25
- }
26
- }
27
- }
28
- /**
29
- * Check if a level string is valid for the given permission type
30
- */ function isValidAccessLevel(level, permissionType) {
31
- if (permissionType === 'read') {
32
- return level === 'only_owner' || level === 'anyone' || level === 'allowed_dids';
33
- }
34
- // write permission
35
- return level === 'only_owner' || level === 'allowed_dids';
36
- }
37
- /**
38
- * Resolve the effective access rule for a document and permission type
39
- * Order of precedence:
40
- * 1. Document accessPermissions override
41
- * 2. User's model default from database
42
- * 3. Server configuration default
43
- */ export async function resolveAccessRule(document, modelId, ownerDID, permissionType, db, serverConfig) {
44
- // 1. Check document override
45
- const docPerms = parseDocumentAccessPermissions(document.data);
46
- if (docPerms?.[permissionType]) {
47
- const rule = docPerms[permissionType];
48
- if (rule && isValidAccessLevel(rule.level, permissionType)) {
49
- return {
50
- level: rule.level,
51
- allowedDIDs: rule.allowedDIDs || null
52
- };
53
- }
54
- }
55
- // 2. Check user's model default
56
- const modelDefault = await db.getUserModelAccessDefault(ownerDID, modelId, permissionType);
57
- if (modelDefault) {
58
- return {
59
- level: modelDefault.level,
60
- allowedDIDs: modelDefault.allowedDIDs
61
- };
62
- }
63
- // 3. Fall back to server default
64
- const level = serverConfig.defaultAccessLevel[permissionType];
65
- return {
66
- level,
67
- allowedDIDs: null
68
- };
69
- }
70
- /**
71
- * Check if viewer has access to document through delegation tokens
72
- */ export async function checkDelegation(viewerDID, grantor, document, permissionType, delegationTokens) {
73
- if (!delegationTokens || delegationTokens.length === 0) {
74
- return false;
75
- }
76
- // Build expected permission based on specificity
77
- const resources = [
78
- `urn:kubun:document:${document.id}`,
79
- `urn:kubun:model:${document.model}`,
80
- '*'
81
- ];
82
- const action = `document/${permissionType}` // document/read or document/write
83
- ;
84
- // First, try tokens as a delegation chain (for A→B→C scenarios)
85
- for (const res of resources){
86
- try {
87
- await checkCapability({
88
- act: action,
89
- res
90
- }, {
91
- iss: viewerDID,
92
- sub: grantor,
93
- cap: delegationTokens
94
- });
95
- return true;
96
- } catch {}
97
- }
98
- // If chain validation fails, try each token independently (for multiple independent grants)
99
- for (const token of delegationTokens){
100
- for (const res of resources){
101
- try {
102
- await checkCapability({
103
- act: action,
104
- res
105
- }, {
106
- iss: viewerDID,
107
- sub: grantor,
108
- cap: token
109
- });
110
- return true;
111
- } catch {}
112
- }
113
- }
114
- return false;
115
- }
116
- /**
117
- * Check if viewer has access to a document for the specified permission type
118
- */ export async function checkAccess(viewerDID, document, permissionType, db, serverConfig, delegationTokens) {
119
- // Validate document has owner
120
- if (!document.owner) {
121
- throw new Error('Document missing owner field');
122
- }
123
- // FAST PATH: Owner always has access
124
- if (viewerDID === document.owner) {
125
- return true;
126
- }
127
- // Resolve effective access rule
128
- const rule = await resolveAccessRule(document, document.model, document.owner, permissionType, db, serverConfig);
129
- // ANYONE: Always allow (read only)
130
- if (rule.level === 'anyone') {
131
- return true;
132
- }
133
- // No viewer: Deny
134
- if (!viewerDID) {
135
- return false;
136
- }
137
- // ONLY_OWNER: Check delegation from owner
138
- if (rule.level === 'only_owner') {
139
- return await checkDelegation(viewerDID, document.owner, document, permissionType, delegationTokens);
140
- }
141
- // ALLOWED_DIDS: Check if viewer is in list or has delegation
142
- if (rule.level === 'allowed_dids') {
143
- const allowedDIDs = rule.allowedDIDs || [];
144
- if (allowedDIDs.includes(viewerDID)) {
145
- return true;
146
- }
147
- // Check if any allowedDID has delegated to viewer
148
- for (const allowedDID of allowedDIDs){
149
- if (await checkDelegation(viewerDID, allowedDID, document, permissionType, delegationTokens)) {
150
- return true;
151
- }
152
- }
153
- return false;
154
- }
155
- // Unknown access level
156
- return false;
157
- }
158
- /**
159
- * Create an access checker function bound to specific viewer and delegation tokens
160
- */ export function createAccessChecker(viewerDID, delegationTokens, db, serverConfig) {
161
- return async (doc, permissionType)=>{
162
- return await checkAccess(viewerDID, doc, permissionType, db, serverConfig, delegationTokens);
163
- };
164
- }
1
+ import{checkCapability as e}from"@enkaku/capability";export function parseDocumentAccessPermissions(e){try{if(!e||"object"!=typeof e||!("accessPermissions"in e))return null;let r=e.accessPermissions;if("object"!=typeof r)return console.warn("Invalid accessPermissions structure in document, ignoring"),null;return r}catch(e){return console.warn("Failed to parse document access permissions:",e),null}}export function validateDIDs(e){for(let r of e)if(!r.startsWith("did:"))throw Error(`Invalid DID format: ${r}`)}export async function resolveAccessRule(e,r,t,n,l,o){let s=parseDocumentAccessPermissions(e.data);if(s?.[n]){var c;let e=s[n];if(e&&(c=e.level,"read"===n?"only_owner"===c||"anyone"===c||"allowed_dids"===c:"only_owner"===c||"allowed_dids"===c))return{level:e.level,allowedDIDs:e.allowedDIDs||null}}let a=await l.getUserModelAccessDefault(t,r,n);return a?{level:a.level,allowedDIDs:a.allowedDIDs}:{level:o.defaultAccessLevel[n],allowedDIDs:null}}export async function checkDelegation(r,t,n,l,o){if(!o||0===o.length)return!1;let s=[`urn:kubun:document:${n.id}`,`urn:kubun:model:${n.model}`,"*"],c=`document/${l}`;for(let n of s)try{return await e({act:c,res:n},{iss:r,sub:t,cap:o}),!0}catch{}for(let n of o)for(let l of s)try{return await e({act:c,res:l},{iss:r,sub:t,cap:n}),!0}catch{}return!1}export async function checkAccess(e,r,t,n,l,o){if(!r.owner)throw Error("Document missing owner field");if(e===r.owner)return!0;let s=await resolveAccessRule(r,r.model,r.owner,t,n,l);if("anyone"===s.level)return!0;if(!e)return!1;if("only_owner"===s.level)return await checkDelegation(e,r.owner,r,t,o);if("allowed_dids"===s.level){let n=s.allowedDIDs||[];if(n.includes(e))return!0;for(let l of n)if(await checkDelegation(e,l,r,t,o))return!0}return!1}export function createAccessChecker(e,r,t,n){return async(l,o)=>await checkAccess(e,l,o,t,n,r)}
@@ -18,4 +18,3 @@ export type ExecuteGraphQLParams = {
18
18
  variables: Record<string, unknown>;
19
19
  };
20
20
  export declare function getExecutionArgs(params: ExecuteGraphQLParams): ExecutionArgs;
21
- //# sourceMappingURL=graphql.d.ts.map
@@ -1,75 +1 @@
1
- import { createReadContext } from '@kubun/graphql';
2
- import { Kind, parse } from 'graphql';
3
- import { removeDocumentAccessOverride, removeModelAccessDefaults, setDocumentAccessOverride, setModelAccessDefaults } from './mutations.js';
4
- export function createContext(ctx) {
5
- const readContext = createReadContext({
6
- db: ctx.db,
7
- viewerDID: ctx.viewerDID,
8
- accessChecker: ctx.accessChecker
9
- });
10
- function getMutationDocument(info) {
11
- return ctx.mutatedDocuments?.[info.path.key];
12
- }
13
- return {
14
- ...readContext,
15
- async executeCreateMutation (_modelID, _data, info) {
16
- return getMutationDocument(info);
17
- },
18
- async executeSetMutation (_modelID, _unique, _data, info) {
19
- return getMutationDocument(info);
20
- },
21
- async executeUpdateMutation (_input, info) {
22
- return getMutationDocument(info);
23
- },
24
- async executeRemoveMutation (_id, _info) {
25
- // no-op
26
- },
27
- async executeSetModelAccessDefaults (modelId, permissionType, accessLevel, allowedDIDs) {
28
- await setModelAccessDefaults({
29
- ownerDID: ctx.viewerDID,
30
- modelID: modelId,
31
- permissionType,
32
- accessLevel,
33
- allowedDIDs
34
- }, ctx.db);
35
- },
36
- async executeRemoveModelAccessDefaults (modelId, permissionTypes) {
37
- await removeModelAccessDefaults({
38
- ownerDID: ctx.viewerDID,
39
- modelID: modelId,
40
- permissionTypes
41
- }, ctx.db);
42
- },
43
- async executeSetDocumentAccessOverride (documentId, permissionType, accessLevel, allowedDIDs) {
44
- await setDocumentAccessOverride({
45
- documentID: documentId,
46
- permissionType,
47
- accessLevel,
48
- allowedDIDs
49
- }, ctx.db);
50
- },
51
- async executeRemoveDocumentAccessOverride (documentId, permissionTypes) {
52
- await removeDocumentAccessOverride({
53
- documentID: documentId,
54
- permissionTypes
55
- }, ctx.db);
56
- }
57
- };
58
- }
59
- export function getExecutionArgs(params) {
60
- const document = parse(params.text);
61
- const definition = document.definitions[0];
62
- if (definition == null) {
63
- throw new Error('Missing GraphQL document definition');
64
- }
65
- // Ensure the operation matching the expected type
66
- if (definition.kind !== Kind.OPERATION_DEFINITION || definition.operation !== params.type) {
67
- throw new Error(`Invalid GraphQL document definition: expected ${params.type} operation`);
68
- }
69
- return {
70
- document,
71
- schema: params.schema,
72
- variableValues: params.variables,
73
- contextValue: createContext(params.context)
74
- };
75
- }
1
+ import{createReadContext as e}from"@kubun/graphql";import{Kind as t,parse as n}from"graphql";import{removeDocumentAccessOverride as c,removeModelAccessDefaults as o,setDocumentAccessOverride as a,setModelAccessDefaults as r}from"./mutations.js";export function createContext(t){function n(e){return t.mutatedDocuments?.[e.path.key]}return{...e({db:t.db,viewerDID:t.viewerDID,accessChecker:t.accessChecker}),executeCreateMutation:async(e,t,c)=>n(c),executeSetMutation:async(e,t,c,o)=>n(o),executeUpdateMutation:async(e,t)=>n(t),async executeRemoveMutation(e,t){},executeSetModelAccessDefaults:async(e,n,c,o)=>await r({ownerDID:t.viewerDID,modelID:e,permissionType:n,accessLevel:c,allowedDIDs:o},t.db),async executeRemoveModelAccessDefaults(e,n){await o({ownerDID:t.viewerDID,modelID:e,permissionTypes:n},t.db)},executeSetDocumentAccessOverride:async(e,n,c,o)=>await a({documentID:e,permissionType:n,accessLevel:c,allowedDIDs:o},t.db),async executeRemoveDocumentAccessOverride(e,n){await c({documentID:e,permissionTypes:n},t.db)}}}export function getExecutionArgs(e){let c=n(e.text),o=c.definitions[0];if(null==o)throw Error("Missing GraphQL document definition");if(o.kind!==t.OPERATION_DEFINITION||o.operation!==e.type)throw Error(`Invalid GraphQL document definition: expected ${e.type} operation`);return{document:c,schema:e.schema,variableValues:e.variables,contextValue:createContext(e.context)}}
@@ -0,0 +1,7 @@
1
+ import type { KubunDB } from '@kubun/db';
2
+ export declare function captureMutation(params: {
3
+ db: KubunDB;
4
+ documentID: string;
5
+ mutationPayload: string;
6
+ authorDID: string;
7
+ }): Promise<void>;
@@ -0,0 +1 @@
1
+ import{sql as e}from"kysely";import{storeMutationLog as t}from"./mutation-log.js";export async function captureMutation(u){let{db:n,documentID:r,mutationPayload:m,authorDID:o}=u,i=await n.getDB(),a=await i.selectFrom("kubun_document_mutation_log").select("sequence_number").where("document_id","=",r).orderBy(e`CAST(sequence_number AS INTEGER)`,"desc").limit(1).executeTakeFirst(),c=a?String(Number(a.sequence_number)+1):"1";await t(n,{documentID:r,sequenceNumber:c,mutationJWT:m,authorDID:o})}
@@ -0,0 +1,17 @@
1
+ import type { DocumentMutationLog, KubunDB } from '@kubun/db';
2
+ export type MutationLogEntry = {
3
+ documentID: string;
4
+ sequenceNumber: string;
5
+ mutationJWT: string;
6
+ authorDID: string;
7
+ };
8
+ /**
9
+ * Stores a mutation log entry and maintains ring buffer of last 10 mutations per document
10
+ */
11
+ export declare function storeMutationLog(db: KubunDB, entry: MutationLogEntry): Promise<void>;
12
+ /** @deprecated Use DocumentMutationLog from @kubun/db instead */
13
+ export type MutationLogRecord = DocumentMutationLog;
14
+ /**
15
+ * Retrieves mutation log entries for a document starting from a given sequence number
16
+ */
17
+ export declare function getMutationLog(db: KubunDB, documentID: string, fromSequence: number): Promise<Array<DocumentMutationLog>>;
@@ -0,0 +1 @@
1
+ export async function storeMutationLog(t,o){await t.storeMutationLog(o)}export async function getMutationLog(t,o,n){return t.getMutationLog(o,n)}
@@ -1,4 +1,5 @@
1
1
  import type { KubunDB } from '@kubun/db';
2
+ import type { ModelAccessDefaults } from '@kubun/graphql';
2
3
  import { type MutationContext } from '@kubun/mutation';
3
4
  import { type DocumentNode } from '@kubun/protocol';
4
5
  import { type ServerAccessConfig } from './access-control.js';
@@ -12,7 +13,7 @@ export declare function setModelAccessDefaults(params: {
12
13
  permissionType: 'read' | 'write';
13
14
  accessLevel: string;
14
15
  allowedDIDs: Array<string> | null;
15
- }, db: KubunDB): Promise<void>;
16
+ }, db: KubunDB): Promise<ModelAccessDefaults>;
16
17
  /**
17
18
  * Remove model-level access defaults for a user
18
19
  */
@@ -29,7 +30,7 @@ export declare function setDocumentAccessOverride(params: {
29
30
  permissionType: 'read' | 'write';
30
31
  accessLevel: string;
31
32
  allowedDIDs: Array<string> | null;
32
- }, db: KubunDB): Promise<void>;
33
+ }, db: KubunDB): Promise<DocumentNode>;
33
34
  /**
34
35
  * Remove document-level access override
35
36
  */
@@ -38,4 +39,3 @@ export declare function removeDocumentAccessOverride(params: {
38
39
  permissionTypes: Array<'read' | 'write'>;
39
40
  }, db: KubunDB): Promise<void>;
40
41
  export type { ServerAccessConfig };
41
- //# sourceMappingURL=mutations.d.ts.map
@@ -1,114 +1 @@
1
- import { asType, createValidator } from '@enkaku/schema';
2
- import { verifyToken } from '@enkaku/token';
3
- import { DocumentID } from '@kubun/id';
4
- import { applyMutation as apply } from '@kubun/mutation';
5
- import { documentMutation } from '@kubun/protocol';
6
- import { validateDIDs } from './access-control.js';
7
- const validateMutation = createValidator(documentMutation);
8
- export async function applyMutation(ctx, token) {
9
- const verified = await verifyToken(token);
10
- const mutation = asType(validateMutation, verified.payload);
11
- return await apply(ctx, mutation);
12
- }
13
- /**
14
- * Set model-level access defaults for a user
15
- */ export async function setModelAccessDefaults(params, db) {
16
- const { ownerDID, modelID, permissionType, accessLevel, allowedDIDs } = params;
17
- // Validate DIDs if provided
18
- if (allowedDIDs && allowedDIDs.length > 0) {
19
- validateDIDs(allowedDIDs);
20
- }
21
- // Validate access level for permission type
22
- const validLevels = {
23
- read: [
24
- 'only_owner',
25
- 'anyone',
26
- 'allowed_dids'
27
- ],
28
- write: [
29
- 'only_owner',
30
- 'allowed_dids'
31
- ]
32
- };
33
- if (!validLevels[permissionType].includes(accessLevel)) {
34
- throw new Error(`Invalid access level "${accessLevel}" for permission type "${permissionType}"`);
35
- }
36
- await db.setUserModelAccessDefault({
37
- ownerDID,
38
- modelID,
39
- permissionType,
40
- accessLevel,
41
- allowedDIDs
42
- });
43
- }
44
- /**
45
- * Remove model-level access defaults for a user
46
- */ export async function removeModelAccessDefaults(params, db) {
47
- const { ownerDID, modelID, permissionTypes } = params;
48
- await db.removeUserModelAccessDefaults(ownerDID, modelID, permissionTypes);
49
- }
50
- /**
51
- * Set document-level access override
52
- */ export async function setDocumentAccessOverride(params, db) {
53
- const { documentID, permissionType, accessLevel, allowedDIDs } = params;
54
- // Validate DIDs if provided
55
- if (allowedDIDs && allowedDIDs.length > 0) {
56
- validateDIDs(allowedDIDs);
57
- }
58
- // Get the document
59
- const docID = DocumentID.fromString(documentID);
60
- const doc = await db.getDocument(docID);
61
- if (!doc) {
62
- throw new Error(`Document not found: ${documentID}`);
63
- }
64
- // Preserve existing permissions
65
- const existingPerms = doc.data?.accessPermissions || {};
66
- // Update the specific permission type
67
- const updatedPerms = {
68
- ...existingPerms,
69
- [permissionType]: {
70
- level: accessLevel,
71
- allowedDIDs
72
- }
73
- };
74
- // Update the document with new access permissions
75
- await db.saveDocument({
76
- id: docID,
77
- data: {
78
- ...doc.data,
79
- accessPermissions: updatedPerms
80
- },
81
- state: null,
82
- existing: doc
83
- });
84
- }
85
- /**
86
- * Remove document-level access override
87
- */ export async function removeDocumentAccessOverride(params, db) {
88
- const { documentID, permissionTypes } = params;
89
- // Get the document
90
- const docID = DocumentID.fromString(documentID);
91
- const doc = await db.getDocument(docID);
92
- if (!doc) {
93
- // Silently return if document doesn't exist
94
- return;
95
- }
96
- // Preserve existing permissions, removing specified types
97
- const existingPerms = doc.data?.accessPermissions || {};
98
- const updatedPerms = {
99
- ...existingPerms
100
- };
101
- for (const permType of permissionTypes){
102
- delete updatedPerms[permType];
103
- }
104
- // Update the document with updated access permissions
105
- await db.saveDocument({
106
- id: docID,
107
- data: {
108
- ...doc.data,
109
- accessPermissions: Object.keys(updatedPerms).length > 0 ? updatedPerms : undefined
110
- },
111
- state: null,
112
- existing: doc
113
- });
114
- }
1
+ import{asType as e,createValidator as t}from"@enkaku/schema";import{verifyToken as o}from"@enkaku/token";import{DocumentID as a}from"@kubun/id";import{applyMutation as s}from"@kubun/mutation";import{documentMutation as r}from"@kubun/protocol";import{validateDIDs as l}from"./access-control.js";import{captureMutation as n}from"./mutation-capture.js";let i=t(r);export async function applyMutation(t,a){let r=e(i,(await o(a)).payload),l=await s(t,r);return await n({db:t.db,documentID:l.id,mutationPayload:a,authorDID:r.iss}),l}export async function setModelAccessDefaults(e,t){let{ownerDID:o,modelID:a,permissionType:s,accessLevel:r,allowedDIDs:n}=e;if(n&&n.length>0&&l(n),!({read:["only_owner","anyone","allowed_dids"],write:["only_owner","allowed_dids"]})[s].includes(r))throw Error(`Invalid access level "${r}" for permission type "${s}"`);await t.setUserModelAccessDefault({ownerDID:o,modelID:a,permissionType:s,accessLevel:r,allowedDIDs:n});let i=await t.getUserModelAccessDefault(o,a,"read"),c=await t.getUserModelAccessDefault(o,a,"write"),d={};return i&&(d.read={level:i.level,allowedDIDs:i.allowedDIDs??[]}),c&&(d.write={level:c.level,allowedDIDs:c.allowedDIDs??[]}),{ownerDID:o,modelId:a,permissions:d}}export async function removeModelAccessDefaults(e,t){let{ownerDID:o,modelID:a,permissionTypes:s}=e;await t.removeUserModelAccessDefaults(o,a,s)}export async function setDocumentAccessOverride(e,t){let{documentID:o,permissionType:s,accessLevel:r,allowedDIDs:n}=e;n&&n.length>0&&l(n);let i=a.fromString(o),c=await t.getDocument(i);if(!c)throw Error(`Document not found: ${o}`);let d={...c.data?.accessPermissions||{},[s]:{level:r,allowedDIDs:n}};return await t.saveDocument({id:i,data:{...c.data,accessPermissions:d},state:null,existing:c})}export async function removeDocumentAccessOverride(e,t){let{documentID:o,permissionTypes:s}=e,r=a.fromString(o),l=await t.getDocument(r);if(!l)return;let n={...l.data?.accessPermissions||{}};for(let e of s)delete n[e];await t.saveDocument({id:r,data:{...l.data,accessPermissions:Object.keys(n).length>0?n:void 0},state:null,existing:l})}
@@ -2,4 +2,3 @@ import type { ProcedureHandlers } from '@enkaku/server';
2
2
  import type { DocumentProtocol } from '@kubun/protocol';
3
3
  import type { CreateHandlersParams } from './types.js';
4
4
  export declare function createHandlers(handlersParams: CreateHandlersParams): ProcedureHandlers<DocumentProtocol>;
5
- //# sourceMappingURL=document.d.ts.map
@@ -1,28 +1 @@
1
- import { toB64 } from '@enkaku/codec';
2
- export function createHandlers(handlersParams) {
3
- const { db } = handlersParams;
4
- return {
5
- 'document/sync': async (ctx)=>{
6
- const ids = Object.keys(ctx.param.documents);
7
- const loadedStates = await db.getDocumentStates(ids);
8
- const states = ids.reduce((acc, id)=>{
9
- const loaded = loadedStates[id];
10
- if (loaded == null) {
11
- acc[id] = null;
12
- return acc;
13
- }
14
- const provided = ctx.param.documents[id];
15
- if (provided == null) {
16
- acc[id] = toB64(loaded);
17
- } else {
18
- // TODO: send diff rather than full doc state
19
- acc[id] = toB64(loaded);
20
- }
21
- return acc;
22
- }, {});
23
- return {
24
- states
25
- };
26
- }
27
- };
28
- }
1
+ import{toB64 as e}from"@enkaku/codec";export function createHandlers(t){let{db:n}=t;return{"document/sync":async t=>{let r=Object.keys(t.param.documents),a=await n.getDocumentStates(r);return{states:r.reduce((n,r)=>{let c=a[r];return null==c?n[r]=null:(t.param.documents[r],n[r]=e(c)),n},{})}}}}
@@ -2,4 +2,3 @@ import type { ProcedureHandlers } from '@enkaku/server';
2
2
  import type { GraphProtocol } from '@kubun/protocol';
3
3
  import type { CreateHandlersParams } from './types.js';
4
4
  export declare function createHandlers(handlersParams: CreateHandlersParams): ProcedureHandlers<GraphProtocol>;
5
- //# sourceMappingURL=graph.d.ts.map
@@ -1,191 +1 @@
1
- import { fromB64, toB64 } from '@enkaku/codec';
2
- import { consume } from '@enkaku/generator';
3
- import { createSchema } from '@kubun/graphql';
4
- import { AttachmentID } from '@kubun/id';
5
- import { GraphModel } from '@kubun/protocol';
6
- import { execute, OperationTypeNode, // printSchema,
7
- subscribe } from 'graphql';
8
- import { createAccessChecker } from '../data/access-control.js';
9
- import { getExecutionArgs } from '../data/graphql.js';
10
- import { applyMutation } from '../data/mutations.js';
11
- function toGraphResult(result) {
12
- return {
13
- data: result.data,
14
- errors: result.errors?.map((err)=>err.toJSON()),
15
- extensions: result.extensions
16
- };
17
- }
18
- export function createHandlers(handlersParams) {
19
- const { db, logger, serverAccessConfig } = handlersParams;
20
- const graphModels = {};
21
- async function getGraphModels(id) {
22
- if (graphModels[id] == null) {
23
- graphModels[id] = db.getGraph(id).then((graph)=>{
24
- if (graph == null) {
25
- logger.warn('graph {id} not found', {
26
- id
27
- });
28
- delete graphModels[id];
29
- throw new Error(`Graph not found: ${id}`);
30
- }
31
- logger.debug('cached model for graph {id}', {
32
- id
33
- });
34
- return {
35
- record: graph.record,
36
- aliases: graph.aliases
37
- };
38
- });
39
- }
40
- return await graphModels[id];
41
- }
42
- const schemas = {};
43
- async function getGraphQLSchema(id) {
44
- if (schemas[id] == null) {
45
- schemas[id] = getGraphModels(id).then((model)=>{
46
- const schema = createSchema(model);
47
- logger.debug('cached schema for graph {id}', {
48
- id
49
- });
50
- // console.log(printSchema(schema))
51
- return schema;
52
- }).catch((err)=>{
53
- delete schemas[id];
54
- throw err;
55
- });
56
- }
57
- return await schemas[id];
58
- }
59
- async function executeGraphQL(params) {
60
- const result = await execute(getExecutionArgs(params));
61
- return toGraphResult(result);
62
- }
63
- return {
64
- 'graph/deploy': async (ctx)=>{
65
- const model = GraphModel.fromClusters({
66
- clusters: ctx.param.clusters
67
- });
68
- const id = await db.createGraph({
69
- id: ctx.param.id,
70
- name: ctx.param.name,
71
- record: model.record
72
- });
73
- logger.info('deployed graph {id}', {
74
- id
75
- });
76
- return {
77
- id,
78
- ...model.toJSON()
79
- };
80
- },
81
- 'graph/list': async ()=>{
82
- const graphs = await db.listGraphs();
83
- return {
84
- graphs: graphs.map((graph)=>({
85
- id: graph.id,
86
- name: graph.name
87
- }))
88
- };
89
- },
90
- 'graph/load': async (ctx)=>{
91
- return await getGraphModels(ctx.param.id);
92
- },
93
- 'graph/mutate': async (ctx)=>{
94
- const attachments = Object.entries(ctx.param.attachments ?? {}).map(([key, value])=>{
95
- const aid = AttachmentID.fromString(key);
96
- return {
97
- id: toB64(aid.digest),
98
- data: fromB64(value)
99
- };
100
- });
101
- if (attachments.length !== 0) {
102
- await db.addAttachments(attachments);
103
- }
104
- // Apply mutations and make the documents available to the GraphQL resolvers in the context
105
- const mutatedDocuments = {};
106
- const validators = {};
107
- await Promise.all(Object.entries(ctx.param.mutations).map(async ([key, mutation])=>{
108
- mutatedDocuments[key] = await applyMutation({
109
- db,
110
- validators
111
- }, mutation);
112
- }));
113
- const payload = ctx.message.payload;
114
- const viewerDID = payload.sub || payload.iss;
115
- const delegationTokens = payload.cap ? Array.isArray(payload.cap) ? payload.cap : [
116
- payload.cap
117
- ] : undefined;
118
- const accessChecker = createAccessChecker(viewerDID, delegationTokens, db, serverAccessConfig);
119
- return await executeGraphQL({
120
- schema: await getGraphQLSchema(ctx.param.id),
121
- type: OperationTypeNode.MUTATION,
122
- text: ctx.param.text,
123
- variables: ctx.param.variables ?? {},
124
- context: {
125
- db,
126
- mutatedDocuments,
127
- viewerDID,
128
- accessChecker
129
- }
130
- });
131
- },
132
- 'graph/query': async (ctx)=>{
133
- const payload = ctx.message.payload;
134
- const viewerDID = payload.sub || payload.iss;
135
- const delegationTokens = payload.cap ? Array.isArray(payload.cap) ? payload.cap : [
136
- payload.cap
137
- ] : undefined;
138
- const accessChecker = createAccessChecker(viewerDID, delegationTokens, db, serverAccessConfig);
139
- return await executeGraphQL({
140
- schema: await getGraphQLSchema(ctx.param.id),
141
- type: OperationTypeNode.QUERY,
142
- text: ctx.param.text,
143
- variables: ctx.param.variables ?? {},
144
- context: {
145
- db,
146
- viewerDID,
147
- accessChecker
148
- }
149
- });
150
- },
151
- 'graph/subscribe': async (ctx)=>{
152
- const payload = ctx.message.payload;
153
- const viewerDID = payload.sub || payload.iss;
154
- const delegationTokens = payload.cap ? Array.isArray(payload.cap) ? payload.cap : [
155
- payload.cap
156
- ] : undefined;
157
- const accessChecker = createAccessChecker(viewerDID, delegationTokens, db, serverAccessConfig);
158
- const args = getExecutionArgs({
159
- schema: await getGraphQLSchema(ctx.param.id),
160
- type: OperationTypeNode.SUBSCRIPTION,
161
- text: ctx.param.text,
162
- variables: ctx.param.variables ?? {},
163
- context: {
164
- db,
165
- viewerDID,
166
- accessChecker
167
- }
168
- });
169
- const subscription = await subscribe(args);
170
- if (ctx.signal.aborted) {
171
- return null;
172
- }
173
- if ('errors' in subscription) {
174
- return toGraphResult(subscription);
175
- }
176
- const writer = ctx.writable.getWriter();
177
- try {
178
- await consume(subscription, async (value)=>{
179
- await writer.write(toGraphResult(value));
180
- }, ctx.signal);
181
- } catch (reason) {
182
- if (reason !== 'Close') {
183
- throw reason;
184
- }
185
- } finally{
186
- await writer.close();
187
- }
188
- return null;
189
- }
190
- };
191
- }
1
+ import{fromB64 as a,toB64 as e}from"@enkaku/codec";import{consume as r}from"@enkaku/generator";import{createSchema as t}from"@kubun/graphql";import{AttachmentID as i}from"@kubun/id";import{GraphModel as o}from"@kubun/protocol";import{execute as n,OperationTypeNode as s,subscribe as l}from"graphql";import{createAccessChecker as c}from"../data/access-control.js";import{getExecutionArgs as d}from"../data/graphql.js";import{applyMutation as m}from"../data/mutations.js";function p(a){return{data:a.data,errors:a.errors?.map(a=>a.toJSON()),extensions:a.extensions}}export function createHandlers(u){let{db:f,logger:h,serverAccessConfig:g}=u,y={};function b(a){return a?.fieldsMeta?Object.entries(a.fieldsMeta).filter(([a,e])=>!0===e.searchable).map(([a])=>a):[]}async function w(a,e,r){let t;h.info("starting backfill for model {modelID} in graph {graphID}",{modelID:e,graphID:a});let i=0,o=!0;for(;o;){let a=await f.queryDocuments({modelIDs:[e],first:100,after:t});for(let t of a.entries)t.document.data&&(await f.updateSearchEntry(e,t.document.id,t.document.data,r),i++);o=a.hasMore,t=a.entries.at(-1)?.cursor}h.info("backfill completed for model {modelID}: {total} documents indexed",{modelID:e,total:String(i)})}f.events.on("document:saved",async a=>{let e=a.document;for(let[a,r]of Object.entries(y)){let a=r[e.model];if(a&&a.length>0)try{null===e.data?await f.removeSearchEntry(e.model,e.id):await f.updateSearchEntry(e.model,e.id,e.data,a)}catch(a){h.error("failed to update search index for document {id}: {err}",{id:e.id,err:String(a)})}}});let x={};async function v(a){return null==x[a]&&(x[a]=f.getGraph(a).then(e=>{if(null==e)throw h.warn("graph {id} not found",{id:a}),delete x[a],Error(`Graph not found: ${a}`);if(h.debug("cached model for graph {id}",{id:a}),e.search){let r={};for(let[a,t]of Object.entries(e.search))r[a]=t.fields??b(e.record[a]);y[a]=r}return{record:e.record,aliases:e.aliases}})),await x[a]}let S={};async function O(a){return null==S[a]&&(S[a]=v(a).then(e=>{let r=t(e);return h.debug("cached schema for graph {id}",{id:a}),r}).catch(e=>{throw delete S[a],e})),await S[a]}async function j(a){return p(await n(d(a)))}return{"graph/deploy":async a=>{let e=o.fromClusters({clusters:a.param.clusters}),r=a.param.search,t=await f.createGraph({id:a.param.id,name:a.param.name,record:e.record,search:r});if(h.info("deployed graph {id}",{id:t}),r){for(let[a,i]of Object.entries(r)){let r=i.fields??b(e.record[a]);r.length>0&&(await f.createSearchIndex(a,r),h.info("created search index for model {modelID}",{modelID:a}),w(t,a,r).catch(e=>{h.error("backfill failed for model {modelID}: {err}",{modelID:a,err:String(e)})}))}let a={};for(let[t,i]of Object.entries(r))a[t]=i.fields??b(e.record[t]);y[t]=a}return delete x[t],delete S[t],{id:t,...e.toJSON(),search:r}},"graph/list":async()=>({graphs:(await f.listGraphs()).map(a=>({id:a.id,name:a.name}))}),"graph/load":async a=>await v(a.param.id),"graph/mutate":async r=>{let t=Object.entries(r.param.attachments??{}).map(([r,t])=>({id:e(i.fromString(r).digest),data:a(t)}));0!==t.length&&await f.addAttachments(t);let o={},n={};await Promise.all(Object.entries(r.param.mutations).map(async([a,e])=>{o[a]=await m({db:f,validators:n},e)}));let l=r.message.payload,d=l.sub||l.iss,p=c(d,l.cap?Array.isArray(l.cap)?l.cap:[l.cap]:void 0,f,g);return await j({schema:await O(r.param.id),type:s.MUTATION,text:r.param.text,variables:r.param.variables??{},context:{db:f,mutatedDocuments:o,viewerDID:d,accessChecker:p}})},"graph/query":async a=>{let e=a.message.payload,r=e.sub||e.iss,t=c(r,e.cap?Array.isArray(e.cap)?e.cap:[e.cap]:void 0,f,g);return await j({schema:await O(a.param.id),type:s.QUERY,text:a.param.text,variables:a.param.variables??{},context:{db:f,viewerDID:r,accessChecker:t}})},"graph/subscribe":async a=>{let e=a.message.payload,t=e.sub||e.iss,i=c(t,e.cap?Array.isArray(e.cap)?e.cap:[e.cap]:void 0,f,g),o=d({schema:await O(a.param.id),type:s.SUBSCRIPTION,text:a.param.text,variables:a.param.variables??{},context:{db:f,viewerDID:t,accessChecker:i}}),n=await l(o);if(a.signal.aborted)return null;if("errors"in n)return p(n);let m=a.writable.getWriter();try{await r(n,async a=>{await m.write(p(a))},a.signal)}catch(a){if("Close"!==a)throw a}finally{await m.close()}return null}}}
@@ -3,4 +3,3 @@ import type { Protocol } from '@kubun/protocol';
3
3
  import type { CreateHandlersParams } from './types.js';
4
4
  export type Handlers = ProcedureHandlers<Protocol>;
5
5
  export declare function createHandlers(params: CreateHandlersParams): Handlers;
6
- //# sourceMappingURL=index.d.ts.map
@@ -1,8 +1 @@
1
- import { createHandlers as documentHandlers } from './document.js';
2
- import { createHandlers as graphHandlers } from './graph.js';
3
- export function createHandlers(params) {
4
- return {
5
- ...documentHandlers(params),
6
- ...graphHandlers(params)
7
- };
8
- }
1
+ import{createHandlers as r}from"./document.js";import{createHandlers as e}from"./graph.js";import{createHandlers as a}from"./sync.js";export function createHandlers(s){return{...r(s),...e(s),...a(s)}}
@@ -0,0 +1,4 @@
1
+ import type { ProcedureHandlers } from '@enkaku/server';
2
+ import type { SyncProtocol } from '@kubun/protocol';
3
+ import type { CreateHandlersParams } from './types.js';
4
+ export declare function createHandlers(handlersParams: CreateHandlersParams): ProcedureHandlers<SyncProtocol>;
@@ -0,0 +1 @@
1
+ import{checkCapability as e}from"@enkaku/capability";import{toB64 as t}from"@enkaku/codec";import{DocumentID as r}from"@kubun/id";import{blake3 as n}from"@noble/hashes/blake3.js";import{getMutationLog as o}from"../data/mutation-log.js";async function a(t,r,n){if(!n||0===n.length)return!1;let o=["*","urn:kubun:user:*",`urn:kubun:user:${r}`],a="document/read";for(let s of o)try{return await e({act:a,res:s},{iss:t,sub:r,cap:n}),!0}catch{}for(let s of n)for(let n of o)try{return await e({act:a,res:n},{iss:t,sub:r,cap:s}),!0}catch{}return!1}export function createHandlers(e){let{db:s,logger:c}=e;return{"sync/discovery":async e=>{let{userIDs:t,delegationTokens:r}=e.param,i=e.message.payload,u=i.sub||i.iss;c.debug("sync/discovery requested",{userIDs:t,viewerDID:u});let l=[];for(let e of t){let t=u===e,i=!t&&await a(u,e,r);if(!t&&!i){c.debug("sync/discovery: no access for user",{userID:e,viewerDID:u,hasDelegation:i});continue}for(let r of(c.debug("sync/discovery: access granted",{userID:e,viewerDID:u,isOwner:t,hasDelegation:i}),await s.listDocumentModelIDs()))try{for(let t of(await s.queryDocumentsByOwner(r,e))){let e=t.id,a=t.model??r,c=await s.getDocumentState(e),i=c?Array.from(n(c)).map(e=>e.toString(16).padStart(2,"0")).join("").slice(0,16):"",u=await o(s,e,0),m=u.length>0?u[u.length-1]:null,d=m?.timestamp??Date.now();l.push({documentID:e,modelID:a,lastMutationTimestamp:d,checkpointHash:i,sequenceNumber:m?.sequence_number??"0",priority:d})}}catch(e){c.debug("sync/discovery: skipping model table",{modelID:r,error:e})}}return l.sort((e,t)=>t.priority-e.priority),c.info("sync/discovery completed",{documentCount:l.length}),{documents:l}},"sync/stream":async e=>{let{documentIDs:n,delegationTokens:i}=e.param,u=e.message.payload,l=u.sub||u.iss;c.info("sync/stream started",{documentIDs:n,viewerDID:l});let m=0,d=0,y=e.writable.getWriter();try{for(let e of n){let n=await s.getDocumentState(e);if(!n){c.debug("sync/stream: document state not found",{documentID:e});continue}let u=r.fromString(e),f=await s.getDocument(u);if(!f){c.debug("sync/stream: document not found",{documentID:e});continue}let p=l===f.owner,w=!p&&await a(l,f.owner,i);if(!p&&!w){c.debug("sync/stream: access denied for document",{documentID:e,viewerDID:l,owner:f.owner}),await y.write({type:"skipped",documentID:e,reason:"access_denied"}),d++;continue}let g=await o(s,e,0);g.length>0&&g.length<=10?await y.write({type:"document",documentID:e,syncMode:"incremental",mutationJWTs:g.map(e=>e.mutation_jwt)}):await y.write({type:"document",documentID:e,syncMode:"full",fullState:t(n)}),m++}await y.write({type:"complete"})}catch(e){throw c.error("sync/stream error",{error:e}),e}finally{await y.close()}return c.info("sync/stream completed",{syncedDocuments:m,skippedDocuments:d}),{success:!0,syncedDocuments:m}},"sync/checkpoint":async e=>{let{peerServerID:t,checkpointData:r}=e.param;c.debug("sync/checkpoint storing",{peerServerID:t,userID:r.userID});let n=await s.storeSyncCheckpoint(t,r);return n?c.info("sync/checkpoint stored",{peerServerID:t,userID:r.userID}):c.error("sync/checkpoint failed to store"),{success:n}}}}
@@ -6,4 +6,3 @@ export type CreateHandlersParams = {
6
6
  logger: Logger;
7
7
  serverAccessConfig: ServerAccessConfig;
8
8
  };
9
- //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- export { };
1
+ export{};
package/lib/index.d.ts CHANGED
@@ -2,4 +2,3 @@ export { createContext, type ExecutionContext } from './data/graphql.js';
2
2
  export { createHandlers } from './handlers/index.js';
3
3
  export type { CreateHandlersParams } from './handlers/types.js';
4
4
  export { type CreateClientParams, KubunServer, type ServerParams } from './server.js';
5
- //# sourceMappingURL=index.d.ts.map
package/lib/index.js CHANGED
@@ -1,3 +1 @@
1
- export { createContext } from './data/graphql.js';
2
- export { createHandlers } from './handlers/index.js';
3
- export { KubunServer } from './server.js';
1
+ export{createContext}from"./data/graphql.js";export{createHandlers}from"./handlers/index.js";export{KubunServer}from"./server.js";
package/lib/server.d.ts CHANGED
@@ -1,12 +1,14 @@
1
1
  import { type Server } from '@enkaku/server';
2
+ import type { Identity } from '@enkaku/token';
2
3
  import { type ClientParams, KubunClient } from '@kubun/client';
3
4
  import { type DBParams, KubunDB } from '@kubun/db';
4
5
  import { type Logger } from '@kubun/logger';
5
6
  import type { Protocol, ServerTransport } from '@kubun/protocol';
7
+ import { SyncManager } from './sync/sync-manager.js';
6
8
  export type ServerParams = {
7
9
  access?: Record<string, boolean | Array<string>>;
8
10
  db: KubunDB | DBParams;
9
- id: string;
11
+ identity: Identity;
10
12
  logger?: Logger;
11
13
  defaultAccessLevel?: {
12
14
  read?: 'only_owner' | 'anyone' | 'allowed_dids';
@@ -24,7 +26,7 @@ export declare class KubunServer {
24
26
  read: "only_owner" | "anyone" | "allowed_dids";
25
27
  write: "only_owner" | "allowed_dids";
26
28
  };
29
+ get sync(): SyncManager;
27
30
  createClient(params: CreateClientParams): KubunClient;
28
31
  serve(transport: ServerTransport, signal?: AbortSignal): Server<Protocol>;
29
32
  }
30
- //# sourceMappingURL=server.d.ts.map
package/lib/server.js CHANGED
@@ -1,68 +1 @@
1
- import { serve } from '@enkaku/server';
2
- import { DirectTransports } from '@enkaku/transport';
3
- import { KubunClient } from '@kubun/client';
4
- import { KubunDB } from '@kubun/db';
5
- import { getKubunLogger } from '@kubun/logger';
6
- import { createHandlers } from './handlers/index.js';
7
- export class KubunServer {
8
- #access;
9
- #db;
10
- #handlers;
11
- #id;
12
- #logger;
13
- #defaultAccessLevel;
14
- constructor(params){
15
- const { access, db, id } = params;
16
- const logger = params.logger ?? getKubunLogger('server', {
17
- serverID: id
18
- });
19
- this.#access = access ?? {};
20
- this.#db = db instanceof KubunDB ? db : new KubunDB(db);
21
- this.#id = id;
22
- this.#logger = logger;
23
- // Secure by default
24
- this.#defaultAccessLevel = {
25
- read: params.defaultAccessLevel?.read ?? 'only_owner',
26
- write: params.defaultAccessLevel?.write ?? 'only_owner'
27
- };
28
- this.#handlers = createHandlers({
29
- db: this.#db,
30
- logger,
31
- serverAccessConfig: {
32
- defaultAccessLevel: this.#defaultAccessLevel
33
- }
34
- });
35
- }
36
- get db() {
37
- return this.#db;
38
- }
39
- get defaultAccessLevel() {
40
- return this.#defaultAccessLevel;
41
- }
42
- createClient(params) {
43
- const { signal, ...clientParams } = params;
44
- const transports = new DirectTransports({
45
- signal
46
- });
47
- this.serve(transports.server, signal);
48
- const logger = params.logger ?? this.#logger.getChild('client').with({
49
- clientID: params.getRandomID?.() ?? crypto.randomUUID()
50
- });
51
- return new KubunClient({
52
- logger,
53
- serverID: this.#id,
54
- transport: transports.client,
55
- ...clientParams
56
- });
57
- }
58
- serve(transport, signal) {
59
- return serve({
60
- access: this.#access,
61
- handlers: this.#handlers,
62
- id: this.#id,
63
- logger: this.#logger,
64
- signal,
65
- transport
66
- });
67
- }
68
- }
1
+ import{serve as e}from"@enkaku/server";import{DirectTransports as t}from"@enkaku/transport";import{KubunClient as s}from"@kubun/client";import{KubunDB as r}from"@kubun/db";import{getKubunLogger as i}from"@kubun/logger";import{createHandlers as n}from"./handlers/index.js";import{SyncManager as l}from"./sync/sync-manager.js";export class KubunServer{#e;#t;#s;#r;#i;#n;#l;constructor(e){let{access:t,db:s,identity:c}=e,o=e.logger??i("server",{serverID:c.id});this.#e=t??{},this.#t=s instanceof r?s:new r(s),this.#r=c,this.#i=o,this.#n={read:e.defaultAccessLevel?.read??"only_owner",write:e.defaultAccessLevel?.write??"only_owner"},this.#s=n({db:this.#t,logger:o,serverAccessConfig:{defaultAccessLevel:this.#n}}),this.#l=new l({db:this.#t,identity:this.#r,logger:this.#i})}get db(){return this.#t}get defaultAccessLevel(){return this.#n}get sync(){return this.#l}createClient(e){let{signal:r,...i}=e,n=new t({signal:r});return this.serve(n.server,r),new s({logger:e.logger??this.#i.getChild("client").with({clientID:e.getRandomID?.()??crypto.randomUUID()}),serverID:this.#r.id,transport:n.client,...i})}serve(t,s){return e({access:this.#e,handlers:this.#s,identity:this.#r,logger:this.#i,signal:s,transport:t})}}
@@ -0,0 +1,26 @@
1
+ import type { KubunDB } from '@kubun/db';
2
+ import type { Logger } from '@kubun/logger';
3
+ export type CRDTMergeResult = {
4
+ documentID: string;
5
+ success: boolean;
6
+ merged: boolean;
7
+ error?: string;
8
+ };
9
+ export type MergeFullStateParams = {
10
+ db: KubunDB;
11
+ logger: Logger;
12
+ documentID: string;
13
+ fullState: string;
14
+ };
15
+ /**
16
+ * Merge an Automerge state received from a peer with the local document state.
17
+ *
18
+ * This handles the "full sync" mode where we receive a complete Automerge document
19
+ * and need to merge it with our local state using CRDT semantics.
20
+ */
21
+ export declare function mergeFullState(params: MergeFullStateParams): Promise<CRDTMergeResult>;
22
+ /**
23
+ * Check if full sync merge is possible for a document.
24
+ * Full sync requires the document to already exist locally.
25
+ */
26
+ export declare function canMergeFullState(db: KubunDB, documentID: string): Promise<boolean>;
@@ -0,0 +1 @@
1
+ import{automergeWasmBase64 as e}from"@automerge/automerge/automerge.wasm.base64";import*as t from"@automerge/automerge/slim";import{lazy as r}from"@enkaku/async";import{fromB64 as a}from"@enkaku/codec";import{asType as n,createValidator as o}from"@enkaku/schema";import{DocumentID as m}from"@kubun/id";let s=r(()=>t.initializeBase64Wasm(e)),l={};async function i(e,t){return null==l[t]&&(l[t]=e.getDocumentModel(t).then(e=>o({...e.schema,$id:t}))),l[t]}export async function mergeFullState(e){let{db:r,logger:o,documentID:l,fullState:u}=e;try{await s;let e=a(u),c=t.load(e),g=m.fromString(l),d=g.model.toString(),[f,p,y]=await Promise.all([r.getDocument(g),r.getDocumentState(l),i(r,d)]);if(null===f)return o.warn("Cannot apply full sync to non-existent document",{documentID:l}),{documentID:l,success:!1,merged:!1,error:"Document does not exist locally. Use incremental sync with create mutation."};let h=p?t.load(p):t.from(f.data||{}),S=t.merge(h,c),k=n(y,JSON.parse(JSON.stringify(S)));return await r.saveDocument({id:g,existing:f,data:k,state:t.save(S)}),o.info("Merged full state for document",{documentID:l,localHeads:t.getHeads(h).length,receivedHeads:t.getHeads(c).length,mergedHeads:t.getHeads(S).length}),{documentID:l,success:!0,merged:!0}}catch(t){let e=t instanceof Error?t.message:String(t);return o.error("Failed to merge full state",{documentID:l,error:e}),{documentID:l,success:!1,merged:!1,error:e}}}export async function canMergeFullState(e,t){try{let r=m.fromString(t),a=await e.getDocument(r);return null!==a}catch{return!1}}
@@ -0,0 +1,31 @@
1
+ import type { KubunDB } from '@kubun/db';
2
+ import type { Logger } from '@kubun/logger';
3
+ import type { DocumentNode } from '@kubun/protocol';
4
+ export type MutationReplayResult = {
5
+ documentID: string;
6
+ success: boolean;
7
+ error?: string;
8
+ };
9
+ export type ReplayMutationsParams = {
10
+ db: KubunDB;
11
+ logger: Logger;
12
+ mutationJWTs: string[];
13
+ /** Skip capturing mutations in local log (useful for avoiding duplicate captures) */
14
+ skipCapture?: boolean;
15
+ };
16
+ /**
17
+ * Replay mutation JWTs received from a peer server.
18
+ * This applies each mutation sequentially and captures them in the local mutation log
19
+ * for onward synchronization with other peers.
20
+ */
21
+ export declare function replayMutations(params: ReplayMutationsParams): Promise<MutationReplayResult[]>;
22
+ /**
23
+ * Replay a single mutation JWT.
24
+ * Returns the resulting document or throws on error.
25
+ */
26
+ export declare function replaySingleMutation(params: {
27
+ db: KubunDB;
28
+ logger: Logger;
29
+ mutationJWT: string;
30
+ skipCapture?: boolean;
31
+ }): Promise<DocumentNode>;
@@ -0,0 +1 @@
1
+ import{asType as t,createValidator as a}from"@enkaku/schema";import{verifyToken as o}from"@enkaku/token";import{applyMutation as e}from"@kubun/mutation";import{documentMutation as r}from"@kubun/protocol";import{captureMutation as i}from"../data/mutation-capture.js";let u=a(r);export async function replayMutations(a){let{db:r,logger:n,mutationJWTs:s,skipCapture:c=!1}=a,d=[],m={db:r,validators:{},accessChecker:void 0};for(let a of s)try{let s=await o(a),p=t(u,s.payload),l=await e(m,p);c||await i({db:r,documentID:l.id,mutationPayload:a,authorDID:p.iss}),d.push({documentID:l.id,success:!0}),n.debug("Replayed mutation",{documentID:l.id,type:p.typ,author:p.iss})}catch(a){let t=a instanceof Error?a.message:String(a);n.error("Failed to replay mutation",{error:t}),d.push({documentID:"unknown",success:!1,error:t})}return d}export async function replaySingleMutation(a){let{db:r,logger:n,mutationJWT:s,skipCapture:c=!1}=a,d=t(u,(await o(s)).payload),m=await e({db:r,validators:{},accessChecker:void 0},d);return c||await i({db:r,documentID:m.id,mutationPayload:s,authorDID:d.iss}),n.debug("Replayed single mutation",{documentID:m.id,type:d.typ,author:d.iss}),m}
@@ -0,0 +1,26 @@
1
+ import type { KubunDB } from '@kubun/db';
2
+ export type PeerConfig = {
3
+ peerDID: string;
4
+ endpoint: string;
5
+ mode: 'persistent' | 'on-demand';
6
+ allowedUsers: Array<string> | {
7
+ all: boolean;
8
+ };
9
+ priority: string;
10
+ trustLevel: 'trusted' | 'restricted';
11
+ };
12
+ export type PeerConfigWithID = PeerConfig & {
13
+ id: string;
14
+ createdAt: number;
15
+ updatedAt: number;
16
+ };
17
+ export declare class PeerRegistry {
18
+ private db;
19
+ constructor(db: KubunDB);
20
+ addPeer(config: PeerConfig): Promise<void>;
21
+ getPeer(peerDID: string): Promise<PeerConfigWithID | undefined>;
22
+ listPeers(): Promise<Array<PeerConfigWithID>>;
23
+ updatePeer(peerDID: string, updates: Partial<PeerConfig>): Promise<void>;
24
+ removePeer(peerDID: string): Promise<void>;
25
+ isPeerAllowed(peerDID: string): Promise<boolean>;
26
+ }
@@ -0,0 +1 @@
1
+ import{nanoid as e}from"nanoid";export class PeerRegistry{db;constructor(e){this.db=e}async addPeer(t){let r=await this.db.getDB();await r.insertInto("kubun_sync_peers").values({id:e(),peer_did:t.peerDID,endpoint:t.endpoint,mode:t.mode,allowed_users:JSON.stringify(t.allowedUsers),priority:t.priority,trust_level:t.trustLevel,config:JSON.stringify({}),created_at:Date.now(),updated_at:Date.now()}).execute()}async getPeer(e){let t=await this.db.getDB(),r=await t.selectFrom("kubun_sync_peers").selectAll().where("peer_did","=",e).executeTakeFirst();if(r)return{id:r.id,peerDID:r.peer_did,endpoint:r.endpoint,mode:r.mode,allowedUsers:r.allowed_users,priority:r.priority,trustLevel:r.trust_level,createdAt:r.created_at,updatedAt:r.updated_at}}async listPeers(){let e=await this.db.getDB();return(await e.selectFrom("kubun_sync_peers").selectAll().execute()).map(e=>({id:e.id,peerDID:e.peer_did,endpoint:e.endpoint,mode:e.mode,allowedUsers:e.allowed_users,priority:e.priority,trustLevel:e.trust_level,createdAt:e.created_at,updatedAt:e.updated_at}))}async updatePeer(e,t){let r=await this.getPeer(e);if(!r)throw Error(`Peer ${e} not found`);let i={...r,...t},d=await this.db.getDB();await d.updateTable("kubun_sync_peers").set({endpoint:i.endpoint,mode:i.mode,allowed_users:JSON.stringify(i.allowedUsers),priority:i.priority,trust_level:i.trustLevel,config:JSON.stringify({}),updated_at:Date.now()}).where("peer_did","=",e).execute()}async removePeer(e){let t=await this.db.getDB();await t.deleteFrom("kubun_sync_peers").where("peer_did","=",e).execute()}async isPeerAllowed(e){return void 0!==await this.getPeer(e)}}
@@ -0,0 +1,66 @@
1
+ import type { SigningIdentity } from '@enkaku/token';
2
+ import { KubunClient } from '@kubun/client';
3
+ import type { Logger } from '@kubun/logger';
4
+ import type { KubunServer } from '../server.js';
5
+ export type SyncClientParams = {
6
+ identity: SigningIdentity;
7
+ logger: Logger;
8
+ serverResolver?: (serverID: string) => KubunServer | undefined;
9
+ };
10
+ export type SyncStreamMessage = {
11
+ type?: string;
12
+ documentID?: string;
13
+ syncMode?: string;
14
+ mutationJWTs?: Array<string>;
15
+ fullState?: string;
16
+ reason?: string;
17
+ };
18
+ export type DiscoveryDocument = {
19
+ documentID: string;
20
+ modelID: string;
21
+ lastMutationTimestamp: number;
22
+ checkpointHash: string;
23
+ sequenceNumber: string;
24
+ priority: number;
25
+ };
26
+ export type DiscoveryResult = {
27
+ documents: Array<DiscoveryDocument>;
28
+ };
29
+ export type SyncCheckpointData = {
30
+ userID: string;
31
+ lastSyncTimestamp: number;
32
+ documentCheckpoints: Record<string, {
33
+ lastKnownHash: string;
34
+ lastSyncedSequence: number;
35
+ }>;
36
+ vectorClock: Record<string, number>;
37
+ };
38
+ /**
39
+ * Client for connecting to peer servers for sync operations.
40
+ * Supports both direct (in-process) and HTTP transports.
41
+ */
42
+ export declare class SyncClient {
43
+ #private;
44
+ constructor(params: SyncClientParams);
45
+ /**
46
+ * Connect to a peer server using the specified endpoint.
47
+ * - `direct://server-id` - Use in-process direct transport
48
+ * - `http://...` or `https://...` - Use HTTP transport (not yet implemented)
49
+ */
50
+ connect(endpoint: string): Promise<KubunClient>;
51
+ /**
52
+ * Perform sync discovery to get document manifest from peer.
53
+ * Documents are returned sorted by priority (most recently modified first).
54
+ */
55
+ discovery(client: KubunClient, userIDs: Array<string>, delegationTokens?: Array<string>): Promise<DiscoveryResult>;
56
+ /**
57
+ * Request documents via sync/stream and collect all messages.
58
+ */
59
+ streamDocuments(client: KubunClient, documentIDs: Array<string>, delegationTokens?: Array<string>): Promise<Array<SyncStreamMessage>>;
60
+ /**
61
+ * Store a sync checkpoint on the peer server.
62
+ */
63
+ storeCheckpoint(client: KubunClient, peerServerID: string, checkpointData: SyncCheckpointData): Promise<{
64
+ success: boolean;
65
+ }>;
66
+ }
@@ -0,0 +1 @@
1
+ import{createArraySink as e}from"@enkaku/stream";import{DirectTransports as r}from"@enkaku/transport";import{KubunClient as t}from"@kubun/client";export class SyncClient{#e;#r;#t;constructor(e){this.#e=e.identity,this.#r=e.logger,this.#t=e.serverResolver}async connect(e){if(e.startsWith("direct://"))return this.#n(e);throw Error(`HTTP transport not yet implemented: ${e}`)}#n(e){let n=e.replace("direct://","");if(!this.#t)throw Error("Server resolver not configured for direct transport");let s=this.#t(n);if(!s)throw Error(`Server not found: ${n}`);let i=new r;return s.serve(i.server),new t({identity:this.#e,logger:this.#r.getChild("sync-client"),serverID:n,transport:i.client})}async discovery(e,r,t=[]){return e.client.request("sync/discovery",{param:{userIDs:r,delegationTokens:t}})}async streamDocuments(r,t,n=[]){let[s,i]=e(),o=r.client.createStream("sync/stream",{param:{documentIDs:t,delegationTokens:n}});return o.readable.pipeTo(s),await o,i}async storeCheckpoint(e,r,t){return e.client.request("sync/checkpoint",{param:{peerServerID:r,checkpointData:t}})}}
@@ -0,0 +1,57 @@
1
+ import { type Identity, type SigningIdentity } from '@enkaku/token';
2
+ import type { KubunDB } from '@kubun/db';
3
+ import type { Logger } from '@kubun/logger';
4
+ import type { KubunServer } from '../server.js';
5
+ import { type PeerConfig, type PeerConfigWithID } from './peer-registry.js';
6
+ export type SyncOptions = {
7
+ userIDs?: Array<string>;
8
+ documentIDs?: Array<string>;
9
+ };
10
+ export type SyncSessionInfo = {
11
+ peerID: string;
12
+ startTime: number;
13
+ documentsAttempted: number;
14
+ documentsCompleted: number;
15
+ };
16
+ export type SyncStatus = {
17
+ activeSessions: Array<SyncSessionInfo>;
18
+ lastSyncByPeer: Record<string, number>;
19
+ };
20
+ export type SyncEvent = {
21
+ type: 'started' | 'completed' | 'error' | 'document:complete' | 'document:failed' | 'document:skipped';
22
+ peerID: string;
23
+ documentID?: string;
24
+ timestamp: number;
25
+ error?: string;
26
+ reason?: string;
27
+ };
28
+ export type SyncEvents = {
29
+ sync: SyncEvent;
30
+ };
31
+ export type SyncManagerParams = {
32
+ db: KubunDB;
33
+ identity: Identity;
34
+ logger: Logger;
35
+ };
36
+ export declare class SyncManager {
37
+ #private;
38
+ constructor(params: SyncManagerParams);
39
+ /**
40
+ * Configure the signing identity used for sync operations.
41
+ */
42
+ setIdentity(identity: SigningIdentity): void;
43
+ /**
44
+ * Configure the server resolver for direct transport connections.
45
+ */
46
+ setServerResolver(resolver: (serverID: string) => KubunServer | undefined): void;
47
+ addPeer(config: PeerConfig): Promise<void>;
48
+ removePeer(peerDID: string): Promise<void>;
49
+ updatePeerConfig(peerDID: string, updates: Partial<PeerConfig>): Promise<void>;
50
+ listPeers(): Promise<Array<PeerConfigWithID>>;
51
+ getPeer(peerDID: string): Promise<PeerConfigWithID | undefined>;
52
+ syncWithPeer(peerDID: string, options?: SyncOptions): Promise<{
53
+ sessionID: string;
54
+ }>;
55
+ getStatus(peerDID?: string): SyncStatus;
56
+ onSyncEvent(callback: (event: SyncEvent) => void): () => void;
57
+ }
@@ -0,0 +1 @@
1
+ import{EventEmitter as e}from"@enkaku/event";import{isSigningIdentity as t}from"@enkaku/token";import{mergeFullState as r}from"./crdt-merge.js";import{replayMutations as n}from"./mutation-replay.js";import{PeerRegistry as s}from"./peer-registry.js";import{SyncClient as o}from"./sync-client.js";export class SyncManager{#e;#t;#r;#n=new e;#s=new Map;#o=new Map;#i;#m;constructor(e){this.#e=e.db,this.#i=e.identity,this.#t=e.logger,this.#r=new s(e.db)}setIdentity(e){this.#i=e}setServerResolver(e){this.#m=e}async addPeer(e){await this.#r.addPeer(e),this.#t.info("Peer added",{peerDID:e.peerDID})}async removePeer(e){await this.#r.removePeer(e),this.#t.info("Peer removed",{peerDID:e})}async updatePeerConfig(e,t){await this.#r.updatePeer(e,t),this.#t.info("Peer updated",{peerDID:e})}async listPeers(){return this.#r.listPeers()}async getPeer(e){return this.#r.getPeer(e)}async syncWithPeer(e,r){this.#t.info("Starting sync with peer",{peerDID:e,options:r});let n=await this.#r.getPeer(e);if(!n)throw Error(`Peer ${e} not found`);let s=`sync-${e}-${Date.now()}`,i={peerID:e,startTime:Date.now(),documentsAttempted:0,documentsCompleted:0};this.#s.set(s,i),this.#a({type:"started",peerID:e,timestamp:Date.now()});try{let m=this.#i;if(t(m)){let t=new o({identity:m,logger:this.#t,serverResolver:this.#m});this.#t.info("Connecting to peer",{endpoint:n.endpoint});let s=await t.connect(n.endpoint),a=r?.documentIDs??[];if(0===a.length&&r?.userIDs&&r.userIDs.length>0&&(this.#t.info("Discovering documents for users",{userIDs:r.userIDs}),a=(await t.discovery(s,r.userIDs)).documents.map(e=>e.documentID),this.#t.info("Discovered documents",{count:a.length})),0===a.length)this.#t.info("No documents to sync");else{for(let r of(i.documentsAttempted=a.length,this.#t.info("Requesting documents via stream",{count:a.length}),await t.streamDocuments(s,a)))if("document"===r.type&&r.documentID)try{await this.#c(r),i.documentsCompleted++,this.#a({type:"document:complete",peerID:e,documentID:r.documentID,timestamp:Date.now()})}catch(t){this.#t.error("Failed to apply document sync",{documentID:r.documentID,error:t}),this.#a({type:"document:failed",peerID:e,documentID:r.documentID,timestamp:Date.now(),error:t instanceof Error?t.message:String(t)})}else"skipped"===r.type&&r.documentID&&(this.#t.debug("Document skipped by peer",{documentID:r.documentID,reason:r.reason}),this.#a({type:"document:skipped",peerID:e,documentID:r.documentID,timestamp:Date.now(),reason:r.reason}));this.#t.info("Sync complete",{attempted:i.documentsAttempted,completed:i.documentsCompleted})}}else this.#t.warn("Sync session initiated without identity signer (skipping actual sync)",{sessionID:s,peerDID:e,endpoint:n.endpoint,options:r});this.#o.set(e,Date.now()),this.#a({type:"completed",peerID:e,timestamp:Date.now()})}catch(t){throw this.#t.error("Sync failed",{peerDID:e,error:t}),this.#a({type:"error",peerID:e,timestamp:Date.now(),error:t instanceof Error?t.message:String(t)}),t}finally{this.#s.delete(s)}return{sessionID:s}}async #c(e){if(!e.documentID)throw Error("Document ID is required");if("incremental"===e.syncMode&&e.mutationJWTs){this.#t.debug("Applying incremental sync",{documentID:e.documentID,mutationCount:e.mutationJWTs.length});let t=await n({db:this.#e,logger:this.#t,mutationJWTs:e.mutationJWTs,skipCapture:!1}),r=t.filter(e=>!e.success);r.length>0&&this.#t.warn("Some mutations failed to replay",{documentID:e.documentID,total:t.length,failed:r.length,errors:r.map(e=>e.error)}),this.#t.info("Replayed incremental mutations",{documentID:e.documentID,total:t.length,succeeded:t.length-r.length})}else if("full"===e.syncMode&&e.fullState){this.#t.debug("Applying full sync",{documentID:e.documentID});let t=await r({db:this.#e,logger:this.#t,documentID:e.documentID,fullState:e.fullState});if(!t.success)throw Error(t.error||"Failed to merge full state");this.#t.info("Applied full state sync",{documentID:e.documentID,merged:t.merged})}else throw Error(`Unknown sync mode: ${e.syncMode}`)}getStatus(e){let t=Array.from(this.#s.values()).filter(t=>!e||t.peerID===e).map(e=>({peerID:e.peerID,startTime:e.startTime,documentsAttempted:e.documentsAttempted,documentsCompleted:e.documentsCompleted})),r={};for(let[t,n]of this.#o.entries())e&&t!==e||(r[t]=n);return{activeSessions:t,lastSyncByPeer:r}}onSyncEvent(e){return this.#n.on("sync",e)}#a(e){this.#n.emit("sync",e).catch(e=>{this.#t.error("Error in sync event listener",{error:e})})}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/server",
3
- "version": "0.4.3",
3
+ "version": "0.5.0",
4
4
  "license": "see LICENSE.md",
5
5
  "keywords": [],
6
6
  "type": "module",
@@ -15,30 +15,31 @@
15
15
  ],
16
16
  "sideEffects": false,
17
17
  "dependencies": {
18
- "@enkaku/async": "^0.12.2",
19
- "@enkaku/capability": "^0.12.1",
20
- "@enkaku/codec": "^0.12.0",
21
- "@enkaku/generator": "^0.12.1",
22
- "@enkaku/schema": "^0.12.1",
23
- "@enkaku/server": "^0.12.3",
24
- "@enkaku/token": "0.12.3",
25
- "@enkaku/transport": "0.12.0",
18
+ "@enkaku/async": "^0.13.0",
19
+ "@enkaku/capability": "^0.13.0",
20
+ "@enkaku/codec": "^0.13.0",
21
+ "@enkaku/event": "^0.13.0",
22
+ "@enkaku/generator": "^0.13.0",
23
+ "@enkaku/schema": "^0.13.0",
24
+ "@enkaku/server": "^0.13.0",
25
+ "@enkaku/token": "0.13.0",
26
+ "@enkaku/transport": "0.13.1",
26
27
  "graphql": "^16.12.0",
27
- "@kubun/client": "^0.4.1",
28
- "@kubun/mutation": "^0.4.0",
29
- "@kubun/graphql": "^0.4.5",
30
- "@kubun/logger": "^0.4.0",
31
- "@kubun/protocol": "^0.4.1",
32
- "@kubun/db": "^0.4.0",
33
- "@kubun/id": "^0.4.0"
28
+ "@kubun/db": "^0.5.0",
29
+ "@kubun/graphql": "^0.5.0",
30
+ "@kubun/mutation": "^0.5.0",
31
+ "@kubun/client": "^0.5.0",
32
+ "@kubun/protocol": "^0.5.0",
33
+ "@kubun/logger": "^0.5.0",
34
+ "@kubun/id": "^0.5.0"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@databases/pg-test": "^3.1.2",
37
- "@enkaku/stream": "^0.12.1",
38
- "@kubun/db-postgres": "^0.4.0",
39
- "@kubun/db-sqlite": "^0.4.0",
40
- "@kubun/scalars": "^0.4.0",
41
- "@kubun/test-utils": "^0.4.0"
38
+ "@enkaku/stream": "^0.13.0",
39
+ "@kubun/db-sqlite": "^0.5.0",
40
+ "@kubun/db-postgres": "^0.5.0",
41
+ "@kubun/test-utils": "^0.5.0",
42
+ "@kubun/scalars": "^0.5.0"
42
43
  },
43
44
  "scripts": {
44
45
  "build:clean": "del lib",
@@ -1 +0,0 @@
1
- {"version":3,"file":"access-control.d.ts","sourceRoot":"","sources":["../../src/data/access-control.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAEnD,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,kBAAkB,EAAE;QAClB,IAAI,EAAE,YAAY,GAAG,QAAQ,GAAG,cAAc,CAAA;QAC9C,KAAK,EAAE,YAAY,GAAG,cAAc,CAAA;KACrC,CAAA;CACF,CAAA;AAED,MAAM,MAAM,aAAa,GAAG,CAC1B,GAAG,EAAE,YAAY,EACjB,cAAc,EAAE,MAAM,GAAG,OAAO,KAC7B,OAAO,CAAC,OAAO,CAAC,CAAA;AAErB;;GAEG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,GAAG,GAAG,iBAAiB,GAAG,IAAI,CAiBlF;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAMtD;AAaD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,GAAG,OAAO,EAChC,EAAE,EAAE,OAAO,EACX,YAAY,EAAE,kBAAkB,GAC/B,OAAO,CAAC,UAAU,CAAC,CA4BrB;AAED;;GAEG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,YAAY,EACtB,cAAc,EAAE,MAAM,GAAG,OAAO,EAChC,gBAAgB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAC/B,OAAO,CAAC,OAAO,CAAC,CAoClB;AAED;;GAEG;AACH,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,QAAQ,EAAE,YAAY,EACtB,cAAc,EAAE,MAAM,GAAG,OAAO,EAChC,EAAE,EAAE,OAAO,EACX,YAAY,EAAE,kBAAkB,EAChC,gBAAgB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,GAC/B,OAAO,CAAC,OAAO,CAAC,CA+DlB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,gBAAgB,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,SAAS,EAC3C,EAAE,EAAE,OAAO,EACX,YAAY,EAAE,kBAAkB,GAC/B,aAAa,CAIf"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"graphql.d.ts","sourceRoot":"","sources":["../../src/data/graphql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,KAAK,OAAO,EAA+C,MAAM,gBAAgB,CAAA;AAC1F,OAAO,KAAK,EAAgB,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,EACL,KAAK,aAAa,EAElB,KAAK,aAAa,EAElB,KAAK,iBAAiB,EAEvB,MAAM,SAAS,CAAA;AAEhB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAQxD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,EAAE,EAAE,OAAO,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;IAC/C,aAAa,CAAC,EAAE,aAAa,CAAA;CAC9B,CAAA;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAgG5D;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,OAAO,EAAE,gBAAgB,CAAA;IACzB,MAAM,EAAE,aAAa,CAAA;IACrB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,iBAAiB,CAAA;IACvB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACnC,CAAA;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,oBAAoB,GAAG,aAAa,CAiB5E"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mutations.d.ts","sourceRoot":"","sources":["../../src/data/mutations.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAExC,OAAO,EAA0B,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAC9E,OAAO,EAAyB,KAAK,YAAY,EAAoB,MAAM,iBAAiB,CAAA;AAE5F,OAAO,EAAE,KAAK,kBAAkB,EAAgB,MAAM,qBAAqB,CAAA;AAI3E,wBAAsB,aAAa,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAI9F;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE;IACN,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,MAAM,GAAG,OAAO,CAAA;IAChC,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAA;CAClC,EACD,EAAE,EAAE,OAAO,GACV,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE;IACN,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,CAAA;CACzC,EACD,EAAE,EAAE,OAAO,GACV,OAAO,CAAC,IAAI,CAAC,CAGf;AAED;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE;IACN,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,MAAM,GAAG,OAAO,CAAA;IAChC,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,IAAI,CAAA;CAClC,EACD,EAAE,EAAE,OAAO,GACV,OAAO,CAAC,IAAI,CAAC,CAqCf;AAED;;GAEG;AACH,wBAAsB,4BAA4B,CAChD,MAAM,EAAE;IACN,UAAU,EAAE,MAAM,CAAA;IAClB,eAAe,EAAE,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,CAAA;CACzC,EACD,EAAE,EAAE,OAAO,GACV,OAAO,CAAC,IAAI,CAAC,CA6Bf;AAGD,YAAY,EAAE,kBAAkB,EAAE,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"document.d.ts","sourceRoot":"","sources":["../../src/handlers/document.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAEvD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAEtD,wBAAgB,cAAc,CAC5B,cAAc,EAAE,oBAAoB,GACnC,iBAAiB,CAAC,gBAAgB,CAAC,CA8BrC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"graph.d.ts","sourceRoot":"","sources":["../../src/handlers/graph.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAKvD,OAAO,KAAK,EAIV,aAAa,EACd,MAAM,iBAAiB,CAAA;AAexB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAUtD,wBAAgB,cAAc,CAC5B,cAAc,EAAE,oBAAoB,GACnC,iBAAiB,CAAC,aAAa,CAAC,CAqKlC"}
@@ -1 +0,0 @@
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,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAI/C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAEtD,MAAM,MAAM,QAAQ,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAA;AAElD,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,QAAQ,CAKrE"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/handlers/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAE3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAA;AAEnE,MAAM,MAAM,oBAAoB,GAAG;IACjC,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,kBAAkB,EAAE,kBAAkB,CAAA;CACvC,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,YAAY,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,EAAE,KAAK,kBAAkB,EAAE,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAA"}
@@ -1 +0,0 @@
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,EAAkB,KAAK,MAAM,EAAE,MAAM,eAAe,CAAA;AAC3D,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;IACV,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,kBAAkB,CAAC,EAAE;QACnB,IAAI,CAAC,EAAE,YAAY,GAAG,QAAQ,GAAG,cAAc,CAAA;QAC/C,KAAK,CAAC,EAAE,YAAY,GAAG,cAAc,CAAA;KACtC,CAAA;CACF,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;;gBAWV,MAAM,EAAE,YAAY;IAmBhC,IAAI,EAAE,IAAI,OAAO,CAEhB;IAED,IAAI,kBAAkB;cA3Bd,YAAY,GAAG,QAAQ,GAAG,cAAc;eACvC,YAAY,GAAG,cAAc;MA4BrC;IAED,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,WAAW;IAkBrD,KAAK,CAAC,SAAS,EAAE,eAAe,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC;CAU1E"}