@kubun/graphql 0.4.3 → 0.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/context.d.ts CHANGED
@@ -26,9 +26,11 @@ export type SubscriptionContext = {
26
26
  * Query and subscription functions requiring server implementation
27
27
  */
28
28
  export type ReadOnlyContext = QueryContext & SubscriptionContext;
29
+ export type AccessChecker = (doc: DocumentNode, permissionType: 'read' | 'write') => Promise<boolean>;
29
30
  export type ReadContextParams = {
30
31
  db: KubunDB;
31
32
  viewerDID: string;
33
+ accessChecker?: AccessChecker;
32
34
  };
33
35
  export declare function createReadContext(params: ReadContextParams): ReadOnlyContext;
34
36
  /**
@@ -39,6 +41,10 @@ export type MutationContext = {
39
41
  executeSetMutation(modelID: string, unique: Uint8Array, data: DocumentData, info: GraphQLResolveInfo): Promise<DocumentNode>;
40
42
  executeUpdateMutation(input: UpdateMutationInput, info: GraphQLResolveInfo): Promise<DocumentNode>;
41
43
  executeRemoveMutation(id: string, info: GraphQLResolveInfo): Promise<void>;
44
+ executeSetModelAccessDefaults(modelId: string, permissionType: 'read' | 'write', accessLevel: string, allowedDIDs: Array<string> | null): Promise<void>;
45
+ executeRemoveModelAccessDefaults(modelId: string, permissionTypes: Array<'read' | 'write'>): Promise<void>;
46
+ executeSetDocumentAccessOverride(documentId: string, permissionType: 'read' | 'write', accessLevel: string, allowedDIDs: Array<string> | null): Promise<void>;
47
+ executeRemoveDocumentAccessOverride(documentId: string, permissionTypes: Array<'read' | 'write'>): Promise<void>;
42
48
  };
43
49
  export type Context = ReadOnlyContext & MutationContext;
44
50
  //# sourceMappingURL=context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAY,OAAO,EAAE,MAAM,WAAW,CAAA;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AACjD,OAAO,KAAK,EAAE,UAAU,EAAuB,IAAI,EAAE,MAAM,eAAe,CAAA;AAE1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAE7E;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,IAAI,MAAM,GAAG,IAAI,CAAA;IAC1B,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAAA;IACtD,iBAAiB,CACf,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,EACvB,IAAI,EAAE,sBAAsB,GAC3B,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC,CAAA;IACpC,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,CAAA;CACvF,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,0BAA0B,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,cAAc,CAAC,YAAY,CAAC,CAAA;IACjF,0BAA0B,CAAC,EAAE,EAAE,MAAM,GAAG,cAAc,CAAC,YAAY,CAAC,CAAA;IACpE,0BAA0B,IAAI,cAAc,CAAC,MAAM,CAAC,CAAA;IACpD,4BAA4B,CAC1B,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,EACvB,WAAW,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,GAC3C,cAAc,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAA;IACrC,8BAA8B,CAC5B,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,EACvB,WAAW,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,OAAO,GAC3C,cAAc,CAAC,MAAM,CAAC,CAAA;CAC1B,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG,YAAY,GAAG,mBAAmB,CAAA;AAEhE,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,OAAO,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,eAAe,CAiJ5E;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,qBAAqB,CACnB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,YAAY,CAAC,CAAA;IACxB,kBAAkB,CAChB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,YAAY,EAClB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,YAAY,CAAC,CAAA;IACxB,qBAAqB,CAAC,KAAK,EAAE,mBAAmB,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAClG,qBAAqB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3E,CAAA;AAED,MAAM,MAAM,OAAO,GAAG,eAAe,GAAG,eAAe,CAAA"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
package/lib/context.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { defer } from '@enkaku/async';
2
2
  import { DocumentID } from '@kubun/id';
3
3
  export function createReadContext(params) {
4
- const { db, viewerDID } = params;
4
+ const { db, viewerDID, accessChecker } = params;
5
5
  function createEventGenerator(filter, transform) {
6
6
  let isDone = false;
7
7
  let pending = null;
@@ -63,32 +63,130 @@ export function createReadContext(params) {
63
63
  return viewerDID;
64
64
  },
65
65
  async loadDocument (id) {
66
- return await db.getDocument(DocumentID.fromString(id));
66
+ const doc = await db.getDocument(DocumentID.fromString(id));
67
+ if (!doc) return null;
68
+ // Check access if access checker is provided
69
+ if (accessChecker) {
70
+ const hasAccess = await accessChecker(doc, 'read');
71
+ if (!hasAccess) {
72
+ // Access denied - return null (error will be added by GraphQL layer)
73
+ return null;
74
+ }
75
+ }
76
+ return doc;
67
77
  },
68
78
  async resolveConnection (modelIDs, args) {
69
- // TODO: access control filtering
70
- const { entries, hasMore } = await db.queryDocuments({
71
- ...args,
72
- modelIDs
73
- });
79
+ // If no access checker, return all documents
80
+ if (accessChecker == null) {
81
+ const { entries, hasMore } = await db.queryDocuments({
82
+ ...args,
83
+ modelIDs
84
+ });
85
+ return {
86
+ edges: entries.map((e)=>({
87
+ cursor: e.cursor,
88
+ node: e.document
89
+ })),
90
+ pageInfo: {
91
+ hasNextPage: args.first ? hasMore : false,
92
+ hasPreviousPage: args.first ? false : hasMore,
93
+ endCursor: entries.at(-1)?.cursor ?? null,
94
+ startCursor: entries.at(0)?.cursor ?? null
95
+ }
96
+ };
97
+ }
98
+ // With access control: post-fetch filtering with over-fetching
99
+ const allEntries = [];
100
+ let requestedCount = 0;
101
+ let attempts = 0;
102
+ const maxAttempts = 10 // Prevent infinite loops
103
+ ;
104
+ if (args.first != null) {
105
+ requestedCount = args.first;
106
+ const fetchSize = requestedCount * 2 // Over-fetch to reduce round trips
107
+ ;
108
+ let cursor = args.after;
109
+ while(allEntries.length < requestedCount && attempts < maxAttempts){
110
+ // Fetch unfiltered batch from DB
111
+ const { entries, hasMore } = await db.queryDocuments({
112
+ ...args,
113
+ modelIDs,
114
+ first: fetchSize,
115
+ after: cursor
116
+ });
117
+ if (entries.length === 0 && !hasMore) break;
118
+ // Filter based on access rules
119
+ for (const entry of entries){
120
+ if (await accessChecker(entry.document, 'read')) {
121
+ allEntries.push(entry);
122
+ if (allEntries.length >= requestedCount) break;
123
+ }
124
+ }
125
+ if (!hasMore) break;
126
+ cursor = entries.at(-1)?.cursor ?? null;
127
+ attempts++;
128
+ }
129
+ } else if (args.last != null) {
130
+ requestedCount = args.last;
131
+ const fetchSize = requestedCount * 2 // Over-fetch to reduce round trips
132
+ ;
133
+ let cursor = args.before;
134
+ while(allEntries.length < requestedCount && attempts < maxAttempts){
135
+ // Fetch unfiltered batch from DB
136
+ const { entries, hasMore } = await db.queryDocuments({
137
+ ...args,
138
+ modelIDs,
139
+ last: fetchSize,
140
+ before: cursor
141
+ });
142
+ if (entries.length === 0 && !hasMore) break;
143
+ // Filter based on access rules - note we reverse the entries array for backward pagination
144
+ for (const entry of [
145
+ ...entries
146
+ ].reverse()){
147
+ if (await accessChecker(entry.document, 'read')) {
148
+ allEntries.unshift(entry);
149
+ if (allEntries.length >= requestedCount) break;
150
+ }
151
+ }
152
+ if (!hasMore) break;
153
+ cursor = entries.at(0)?.cursor ?? null;
154
+ attempts++;
155
+ }
156
+ }
157
+ // Return only requested count
158
+ const resultEntries = allEntries.slice(0, requestedCount);
159
+ const hasMoreResults = allEntries.length > requestedCount;
74
160
  return {
75
- edges: entries.map((e)=>({
161
+ edges: resultEntries.map((e)=>({
76
162
  cursor: e.cursor,
77
163
  node: e.document
78
164
  })),
79
165
  pageInfo: {
80
- hasNextPage: args.first ? hasMore : false,
81
- hasPreviousPage: args.first ? false : hasMore,
82
- endCursor: entries.at(-1)?.cursor ?? null,
83
- startCursor: entries.at(0)?.cursor ?? null
166
+ hasNextPage: args.first ? hasMoreResults : false,
167
+ hasPreviousPage: args.first ? false : hasMoreResults,
168
+ endCursor: resultEntries.at(-1)?.cursor ?? null,
169
+ startCursor: resultEntries.at(0)?.cursor ?? null
84
170
  }
85
171
  };
86
172
  },
87
173
  async resolveList (modelIDs, docIDs) {
88
- return await db.listDocuments({
174
+ const documents = await db.listDocuments({
89
175
  modelIDs,
90
176
  docIDs
91
177
  });
178
+ // If no access checker, return all documents
179
+ if (!accessChecker) {
180
+ return documents;
181
+ }
182
+ // Filter based on access control
183
+ const accessible = [];
184
+ for (const doc of documents){
185
+ if (await accessChecker(doc, 'read')) {
186
+ accessible.push(doc);
187
+ }
188
+ }
189
+ return accessible;
92
190
  },
93
191
  subscribeToDocumentCreated (modelIDs) {
94
192
  return createEventGenerator((event)=>event.type === 'create' && modelIDs.includes(event.document.model), (event)=>event.document);
package/lib/schema.d.ts CHANGED
@@ -25,22 +25,36 @@ type RelationField = {
25
25
  };
26
26
  type RelationFields = Record<string, Array<RelationField>>;
27
27
  export type SchemaBuilderParams = {
28
- aliases?: Record<string, string>;
29
28
  record: DocumentModelsRecord;
29
+ aliases?: Record<string, string>;
30
+ onlyModels?: Array<string> | null;
31
+ includeInterfaceDependencies?: boolean;
32
+ includeRelationDependencies?: boolean;
33
+ includeMutations?: boolean;
34
+ includeSubscriptions?: boolean;
30
35
  };
31
36
  export declare class SchemaBuilder {
32
37
  _aliases: Record<string, string>;
38
+ _aliasToModelID: Record<string, string>;
33
39
  _definitions: SharedDefinitions | undefined;
34
40
  _implemented: Record<string, Array<string>>;
41
+ _includedModelIDs: Set<string>;
42
+ _includeInterfaceDependencies: boolean;
43
+ _includeMutations: boolean;
44
+ _includeRelationDependencies: boolean;
45
+ _includeSubscriptions: boolean;
35
46
  _inputObjects: Record<string, GraphQLInputObjectType>;
36
47
  _mutations: GraphQLFieldConfigMap<unknown, Context>;
37
48
  _nodeModelIDs: Array<string>;
49
+ _onlyModels: Array<string> | null;
38
50
  _record: DocumentModelsRecord;
39
51
  _references: ReferencesRecordModel;
40
52
  _relationsTo: Record<string, RelationFields>;
41
53
  _subscriptions: GraphQLFieldConfigMap<unknown, Context>;
42
54
  _types: Record<string, GraphQLOutputType>;
43
55
  constructor(params: SchemaBuilderParams);
56
+ _resolveModelIdentifier(identifier: string): string;
57
+ _resolveModelIDsToInclude(): Set<string>;
44
58
  getModelIDs(id: string | null): Array<string>;
45
59
  build(): GraphQLSchema;
46
60
  _getDefinitions(): SharedDefinitions;
@@ -69,6 +83,6 @@ export declare class SchemaBuilder {
69
83
  _buildEnumString(schema: StringEnumModel, parentName?: string): GraphQLEnumType;
70
84
  _buildFormatString(schema: StringFormatModel): GraphQLScalarType<unknown, unknown>;
71
85
  }
72
- export declare function createSchema(record: DocumentModelsRecord, aliases?: Record<string, string>): GraphQLSchema;
86
+ export declare function createSchema(params: SchemaBuilderParams): GraphQLSchema;
73
87
  export {};
74
88
  //# sourceMappingURL=schema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAc,eAAe,EAAY,MAAM,WAAW,CAAA;AAC/E,OAAO,KAAK,EACV,UAAU,EACV,YAAY,EAEZ,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,YAAY,EACZ,WAAW,EACX,qBAAqB,EACrB,WAAW,EAEX,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EACjB,WAAW,EAEX,SAAS,EACV,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EAGL,eAAe,EAEf,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAI1B,sBAAsB,EACtB,KAAK,gBAAgB,EAErB,oBAAoB,EACpB,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,aAAa,EAKd,MAAM,SAAS,CAAA;AAkBhB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAoD3C,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,eAAe,GAAG,MAAM,GAAG,eAAe,CAE/E;AAED,wBAAgB,WAAW,CACzB,EAAE,EAAE,eAAe,GAAG,MAAM,EAC5B,QAAQ,EAAE,eAAe,GAAG,MAAM,GACjC,eAAe,CAGjB;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,gBAAgB,iDAE5C;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,iBAAiB,kDAElD;AAyFD,eAAO,MAAM,cAAc,wBAWzB,CAAA;AAUF,MAAM,MAAM,sBAAsB,GAAG;IACnC,aAAa,EAAE,oBAAoB,CAAA;IACnC,SAAS,EAAE,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;IAC/C,UAAU,EAAE,kBAAkB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;CACjD,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,sBAAsB,GAAG;IACvD,aAAa,EAAE,iBAAiB,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjD,gBAAgB,EAAE,iBAAiB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IAC1D,iBAAiB,EAAE,oBAAoB,CAAA;IACvC,kBAAkB,EAAE,qBAAqB,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IAChE,WAAW,EAAE,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAA;CACrD,CAAA;AAED,KAAK,aAAa,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAA;AACvD,KAAK,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,CAAA;AAE1D,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,MAAM,EAAE,oBAAoB,CAAA;CAC7B,CAAA;AAED,qBAAa,aAAa;IAExB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,YAAY,EAAE,iBAAiB,GAAG,SAAS,CAAA;IAC3C,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAK;IAChD,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAA6C;IAClG,UAAU,EAAE,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAK;IACxD,aAAa,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IAC5B,OAAO,EAAE,oBAAoB,CAAA;IAC7B,WAAW,EAAE,qBAAqB,CAAK;IACvC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAK;IACjD,cAAc,EAAE,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAK;IAC5D,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAuB;gBAEpD,MAAM,EAAE,mBAAmB;IA+CvC,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC;IAI7C,KAAK,IAAI,aAAa;IAiFtB,eAAe,IAAI,iBAAiB;IAOpC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,iBAAiB;IAWnE,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,SAAS;IAQ1C,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,SAAK,GAAG,gBAAgB;IA4CvF,uBAAuB,IAAI,iBAAiB;IAyI5C,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,CAAC,YAAY,EAAE,OAAO,CAAC,GAAG,oBAAoB;IA+G3F,oBAAoB,CAClB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,MAAM,GACX,qBAAqB,CAAC,YAAY,EAAE,OAAO,CAAC;IA2G/C,wBAAwB,CACtB,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,MAAM,GACX,oBAAoB,GAAG,iBAAiB,CAAC,YAAY,EAAE,OAAO,CAAC;IAoClE,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI;IAgC3D,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,SAAK,GAAG,gBAAgB,GAAG,IAAI;IA0ClF,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,SAAK,GAAG,gBAAgB,GAAG,IAAI;IAgBnF,uBAAuB,CACrB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,mBAAmB,GAAG,WAAW,EACzC,UAAU,SAAK,EACf,UAAU,UAAQ;IAqCpB,wBAAwB,CACtB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,mBAAmB,GAAG,WAAW,EACzC,UAAU,SAAK,EACf,UAAU,UAAQ;IA4BpB,wBAAwB,CACtB,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,EACzB,aAAa,CAAC,EAAE,MAAM,GACrB,sBAAsB;IA4BzB,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,SAAK,GAAG,iBAAiB;IAmBlF,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,SAAK,GAAG,iBAAiB;IAsBjF,WAAW,CAAC,MAAM,EAAE,UAAU,EAAE,UAAU,SAAK,GAAG,WAAW,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAOhG,YAAY,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,UAAU,SAAK,GAAG,iBAAiB;IAMjF,kBAAkB,CAChB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,mBAAmB,GAAG,WAAW,EACzC,UAAU,SAAK,GACd,qBAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAc1D,oBAAoB,CAClB,MAAM,EAAE,qBAAqB,CAAC,YAAY,EAAE,OAAO,CAAC,EACpD,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,aAAa,GACtB,IAAI;IA0BP,2BAA2B,CACzB,MAAM,EAAE,qBAAqB,CAAC,OAAO,EAAE,OAAO,CAAC,EAC/C,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,aAAa,GACtB,IAAI;IAsCP,YAAY,CACV,MAAM,EAAE,WAAW,EACnB,UAAU,EAAE,MAAM,GACjB,eAAe,GAAG,iBAAiB,GAAG,iBAAiB;IAa1D,kBAAkB,CAAC,MAAM,EAAE,iBAAiB;IAS5C,gBAAgB,CAAC,MAAM,EAAE,eAAe,EAAE,UAAU,SAAK,GAUvB,eAAe;IAGjD,kBAAkB,CAAC,MAAM,EAAE,iBAAiB;CAO7C;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,oBAAoB,EAC5B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,aAAa,CAEf"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
package/lib/schema.js CHANGED
@@ -26,14 +26,17 @@ function isList(type) {
26
26
  return isListType(type) || isNonNullType(type) && isListType(type.ofType);
27
27
  }
28
28
  function collectAllModelIDs(collected, record, interfaceID) {
29
- const info = record[interfaceID];
30
- if (info == null) {
29
+ const implementedBy = record[interfaceID];
30
+ if (implementedBy == null) {
31
31
  throw new Error(`Interface ${interfaceID} not found in record`);
32
32
  }
33
- for (const id of info.implementedBy){
34
- if (info.isInterface) {
33
+ for (const id of implementedBy){
34
+ // Check if this id is itself an interface (exists in record with implementers)
35
+ if (record[id] != null) {
36
+ // The id is an interface that has implementers, resolve recursively
35
37
  collectAllModelIDs(collected, record, id);
36
38
  } else {
39
+ // The id is a concrete model
37
40
  collected.add(id);
38
41
  }
39
42
  }
@@ -219,14 +222,21 @@ const OrderByDirection = new GraphQLEnumType({
219
222
  export class SchemaBuilder {
220
223
  // Mapping of model or references IDs to their GraphQL object names
221
224
  _aliases;
225
+ _aliasToModelID;
222
226
  _definitions;
223
227
  _implemented = {};
228
+ _includedModelIDs;
229
+ _includeInterfaceDependencies;
230
+ _includeMutations;
231
+ _includeRelationDependencies;
232
+ _includeSubscriptions;
224
233
  _inputObjects = {
225
234
  ...VALUE_FILTER_INPUTS,
226
235
  PatchOperation
227
236
  };
228
237
  _mutations = {};
229
238
  _nodeModelIDs;
239
+ _onlyModels;
230
240
  _record;
231
241
  _references = {};
232
242
  _relationsTo = {};
@@ -235,24 +245,30 @@ export class SchemaBuilder {
235
245
  OrderByDirection
236
246
  };
237
247
  constructor(params){
238
- this._aliases = params.aliases ?? {};
239
248
  this._record = params.record;
249
+ this._onlyModels = params.onlyModels ?? null;
250
+ this._includeInterfaceDependencies = params.includeInterfaceDependencies ?? true;
251
+ this._includeRelationDependencies = params.includeRelationDependencies ?? true;
252
+ this._includeMutations = params.includeMutations ?? true;
253
+ this._includeSubscriptions = params.includeSubscriptions ?? true;
254
+ // params.aliases is { aliasName: modelID }, which is what we need for _aliasToModelID
255
+ this._aliasToModelID = params.aliases ?? {};
256
+ // Build reverse lookup map (modelID -> alias) for internal use
257
+ this._aliases = {};
258
+ for (const [alias, modelID] of Object.entries(this._aliasToModelID)){
259
+ this._aliases[modelID] = alias;
260
+ }
261
+ // Compute which models to include
262
+ this._includedModelIDs = this._resolveModelIDsToInclude();
240
263
  const referencedInterfaces = {};
241
264
  for (const [modelIDstring, model] of Object.entries(params.record)){
242
265
  const modelID = DocumentModelID.fromString(modelIDstring);
243
266
  Object.assign(this._references, model.schema.$defs ?? {});
244
- referencedInterfaces[modelIDstring] ??= {
245
- isInterface: model.behavior === 'interface',
246
- implementedBy: new Set()
247
- };
248
267
  // Extract interfaces
249
268
  for (const iid of model.interfaces){
250
269
  const interfaceID = getGlobalID(iid, modelID).toString();
251
- referencedInterfaces[interfaceID] ??= {
252
- isInterface: model.behavior === 'interface',
253
- implementedBy: new Set()
254
- };
255
- referencedInterfaces[interfaceID].implementedBy.add(modelIDstring);
270
+ referencedInterfaces[interfaceID] ??= new Set();
271
+ referencedInterfaces[interfaceID].add(modelIDstring);
256
272
  }
257
273
  // Extract inverse relations
258
274
  for (const [fieldName, meta] of Object.entries(model.fieldsMeta)){
@@ -274,13 +290,76 @@ export class SchemaBuilder {
274
290
  for (const id of Object.keys(referencedInterfaces)){
275
291
  const implementedBy = new Set();
276
292
  collectAllModelIDs(implementedBy, referencedInterfaces, id);
277
- this._implemented[id] = Array.from(implementedBy);
293
+ if (implementedBy.size !== 0) {
294
+ this._implemented[id] = Array.from(implementedBy);
295
+ }
278
296
  for (const modelID of implementedBy){
279
297
  nodeModelIDs.add(modelID);
280
298
  }
281
299
  }
282
300
  this._nodeModelIDs = Array.from(nodeModelIDs);
283
301
  }
302
+ _resolveModelIdentifier(identifier) {
303
+ // Check if it's a model ID (exists in record)
304
+ if (this._record[identifier] != null) {
305
+ return identifier;
306
+ }
307
+ // Check if it's an alias
308
+ const modelID = this._aliasToModelID[identifier];
309
+ if (modelID != null && this._record[modelID] != null) {
310
+ return modelID;
311
+ }
312
+ throw new Error(`Model identifier "${identifier}" not found. Must be a valid model ID or alias.`);
313
+ }
314
+ _resolveModelIDsToInclude() {
315
+ // If no restriction, include all models
316
+ if (this._onlyModels == null || this._onlyModels.length === 0) {
317
+ return new Set(Object.keys(this._record));
318
+ }
319
+ // Resolve identifiers to model IDs
320
+ const included = new Set();
321
+ for (const identifier of this._onlyModels){
322
+ const modelID = this._resolveModelIdentifier(identifier);
323
+ included.add(modelID);
324
+ }
325
+ // If both dependencies are disabled, return early
326
+ if (!this._includeInterfaceDependencies && !this._includeRelationDependencies) {
327
+ return included;
328
+ }
329
+ // Recursively collect dependencies
330
+ const toProcess = Array.from(included);
331
+ const processed = new Set();
332
+ while(toProcess.length > 0){
333
+ const modelID = toProcess.pop();
334
+ if (processed.has(modelID)) continue;
335
+ processed.add(modelID);
336
+ const model = this._record[modelID];
337
+ if (model == null) continue;
338
+ // Add interface dependencies if enabled
339
+ if (this._includeInterfaceDependencies) {
340
+ for (const iid of model.interfaces){
341
+ const interfaceID = getGlobalID(iid, modelID).toString();
342
+ if (!included.has(interfaceID)) {
343
+ included.add(interfaceID);
344
+ toProcess.push(interfaceID);
345
+ }
346
+ }
347
+ }
348
+ // Add relation dependencies if enabled
349
+ if (this._includeRelationDependencies) {
350
+ for (const meta of Object.values(model.fieldsMeta)){
351
+ if (meta.type === 'document' && meta.model != null && meta.model !== null) {
352
+ const relationID = getGlobalID(meta.model, modelID).toString();
353
+ if (!included.has(relationID)) {
354
+ included.add(relationID);
355
+ toProcess.push(relationID);
356
+ }
357
+ }
358
+ }
359
+ }
360
+ }
361
+ return included;
362
+ }
284
363
  getModelIDs(id) {
285
364
  return id === null ? this._nodeModelIDs : this._implemented[id] ?? [
286
365
  id
@@ -293,7 +372,7 @@ export class SchemaBuilder {
293
372
  nodes: definitions.nodesField,
294
373
  ...definitions.queryFields
295
374
  };
296
- for (const id of Object.keys(this._record)){
375
+ for (const id of this._includedModelIDs){
297
376
  this._buildDocument(id);
298
377
  const name = this._aliases[id];
299
378
  const filterArg = {
@@ -313,16 +392,95 @@ export class SchemaBuilder {
313
392
  }
314
393
  };
315
394
  }
316
- return new GraphQLSchema({
395
+ // Add access control mutations if mutations are enabled
396
+ if (this._includeMutations) {
397
+ this._mutations.setModelAccessDefaults = {
398
+ type: GraphQLBoolean,
399
+ args: {
400
+ modelId: {
401
+ type: new GraphQLNonNull(GraphQLID)
402
+ },
403
+ permissionType: {
404
+ type: new GraphQLNonNull(GraphQLString)
405
+ },
406
+ accessLevel: {
407
+ type: new GraphQLNonNull(GraphQLString)
408
+ },
409
+ allowedDIDs: {
410
+ type: toList(GraphQLString)
411
+ }
412
+ },
413
+ resolve: async (_, args, ctx)=>{
414
+ await ctx.executeSetModelAccessDefaults(args.modelId, args.permissionType, args.accessLevel, args.allowedDIDs ?? null);
415
+ return true;
416
+ }
417
+ };
418
+ this._mutations.removeModelAccessDefaults = {
419
+ type: GraphQLBoolean,
420
+ args: {
421
+ modelId: {
422
+ type: new GraphQLNonNull(GraphQLID)
423
+ },
424
+ permissionTypes: {
425
+ type: new GraphQLNonNull(toList(GraphQLString))
426
+ }
427
+ },
428
+ resolve: async (_, args, ctx)=>{
429
+ await ctx.executeRemoveModelAccessDefaults(args.modelId, args.permissionTypes);
430
+ return true;
431
+ }
432
+ };
433
+ this._mutations.setDocumentAccessOverride = {
434
+ type: GraphQLBoolean,
435
+ args: {
436
+ documentId: {
437
+ type: new GraphQLNonNull(GraphQLID)
438
+ },
439
+ permissionType: {
440
+ type: new GraphQLNonNull(GraphQLString)
441
+ },
442
+ accessLevel: {
443
+ type: new GraphQLNonNull(GraphQLString)
444
+ },
445
+ allowedDIDs: {
446
+ type: toList(GraphQLString)
447
+ }
448
+ },
449
+ resolve: async (_, args, ctx)=>{
450
+ await ctx.executeSetDocumentAccessOverride(args.documentId, args.permissionType, args.accessLevel, args.allowedDIDs ?? null);
451
+ return true;
452
+ }
453
+ };
454
+ this._mutations.removeDocumentAccessOverride = {
455
+ type: GraphQLBoolean,
456
+ args: {
457
+ documentId: {
458
+ type: new GraphQLNonNull(GraphQLID)
459
+ },
460
+ permissionTypes: {
461
+ type: new GraphQLNonNull(toList(GraphQLString))
462
+ }
463
+ },
464
+ resolve: async (_, args, ctx)=>{
465
+ await ctx.executeRemoveDocumentAccessOverride(args.documentId, args.permissionTypes);
466
+ return true;
467
+ }
468
+ };
469
+ }
470
+ const config = {
317
471
  query: new GraphQLObjectType({
318
472
  name: 'Query',
319
473
  fields: queryFields
320
- }),
321
- mutation: new GraphQLObjectType({
474
+ })
475
+ };
476
+ if (this._includeMutations) {
477
+ config.mutation = new GraphQLObjectType({
322
478
  name: 'Mutation',
323
479
  fields: this._mutations
324
- }),
325
- subscription: new GraphQLObjectType({
480
+ });
481
+ }
482
+ if (this._includeSubscriptions) {
483
+ config.subscription = new GraphQLObjectType({
326
484
  name: 'Subscription',
327
485
  fields: ()=>{
328
486
  const fields = {
@@ -347,6 +505,7 @@ export class SchemaBuilder {
347
505
  }
348
506
  };
349
507
  for (const [id, model] of Object.entries(this._record)){
508
+ if (!this._includedModelIDs.has(id)) continue;
350
509
  const name = this._aliases[id];
351
510
  fields[`new${name}Created`] = {
352
511
  type: new GraphQLNonNull(this._types[id]),
@@ -365,14 +524,16 @@ export class SchemaBuilder {
365
524
  }
366
525
  for (const [fieldName, relations] of Object.entries(this._relationsTo[id] ?? {})){
367
526
  for (const relation of relations){
527
+ if (!this._includedModelIDs.has(relation.model)) continue;
368
528
  this._buildRelationSubscriptions(fields, id, fieldName, relation);
369
529
  }
370
530
  }
371
531
  }
372
532
  return fields;
373
533
  }
374
- })
375
- });
534
+ });
535
+ }
536
+ return new GraphQLSchema(config);
376
537
  }
377
538
  _getDefinitions() {
378
539
  if (this._definitions == null) {
@@ -464,6 +625,7 @@ export class SchemaBuilder {
464
625
  }
465
626
  };
466
627
  for (const [id, model] of Object.entries(this._record)){
628
+ if (!this._includedModelIDs.has(id)) continue;
467
629
  switch(model.behavior){
468
630
  case 'interface':
469
631
  continue;
@@ -530,6 +692,30 @@ export class SchemaBuilder {
530
692
  }
531
693
  }
532
694
  });
695
+ // Create AccessRule type for read/write permissions
696
+ const accessRuleObject = new GraphQLObjectType({
697
+ name: 'AccessRule',
698
+ fields: {
699
+ level: {
700
+ type: new GraphQLNonNull(GraphQLString)
701
+ },
702
+ allowedDIDs: {
703
+ type: new GraphQLList(new GraphQLNonNull(GraphQLString))
704
+ }
705
+ }
706
+ });
707
+ // Create AccessPermissions type
708
+ const accessPermissionsObject = new GraphQLObjectType({
709
+ name: 'AccessPermissions',
710
+ fields: {
711
+ read: {
712
+ type: accessRuleObject
713
+ },
714
+ write: {
715
+ type: accessRuleObject
716
+ }
717
+ }
718
+ });
533
719
  const documentNodeFields = {
534
720
  // Node interface
535
721
  id: {
@@ -547,6 +733,11 @@ export class SchemaBuilder {
547
733
  },
548
734
  updatedAt: {
549
735
  type: GraphQLDateTimeISO
736
+ },
737
+ // Access control
738
+ accessPermissions: {
739
+ type: accessPermissionsObject,
740
+ resolve: (doc)=>doc.data?.accessPermissions ?? null
550
741
  }
551
742
  };
552
743
  const documentInterface = new GraphQLInterfaceType({
@@ -592,7 +783,7 @@ export class SchemaBuilder {
592
783
  definitions.nodeInterface
593
784
  ].concat(model.interfaces.map((interfaceID)=>{
594
785
  return this._types[getGlobalID(interfaceID, id).toString()];
595
- }));
786
+ }).filter((type)=>type != null));
596
787
  },
597
788
  fields: ()=>this._buildDocumentFields(id, model, name)
598
789
  };
@@ -612,7 +803,7 @@ export class SchemaBuilder {
612
803
  this._buildObjectFilterInput(id, model.schema, name, true);
613
804
  this._buildObjectOrderByInput(id, model.schema, name, true);
614
805
  // TODO: move to dedicated method
615
- if (!isInterface) {
806
+ if (!isInterface && this._includeMutations) {
616
807
  if (model.behavior === 'default') {
617
808
  this._mutations[`create${name}`] = mutationWithClientMutationId({
618
809
  name: `Create${name}`,
@@ -690,10 +881,18 @@ export class SchemaBuilder {
690
881
  type: new GraphQLNonNull(GraphQLID)
691
882
  }
692
883
  }),
693
- outputFields: ()=>definitions.queryFields,
884
+ outputFields: ()=>({
885
+ ...definitions.queryFields,
886
+ // Return id so it can be used with Relay's @deleteEdge directive
887
+ id: {
888
+ type: new GraphQLNonNull(GraphQLID)
889
+ }
890
+ }),
694
891
  mutateAndGetPayload: async (input, ctx, info)=>{
695
892
  await ctx.executeRemoveMutation(input.id, info);
696
- return {};
893
+ return {
894
+ id: input.id
895
+ };
697
896
  }
698
897
  });
699
898
  }
@@ -763,7 +962,14 @@ export class SchemaBuilder {
763
962
  relationType = definitions.documentInterface;
764
963
  } else {
765
964
  relationModelID = getGlobalID(relationModel, id).toString();
766
- relationType = this._buildDocument(relationModelID);
965
+ // Check if the related model is included in the schema
966
+ if (this._includedModelIDs.has(relationModelID)) {
967
+ relationType = this._buildDocument(relationModelID);
968
+ } else {
969
+ // Fall back to generic DocumentNode interface
970
+ relationType = definitions.documentInterface;
971
+ relationModelID = null; // Treat as unspecified model
972
+ }
767
973
  }
768
974
  if (isList(field.type)) {
769
975
  // TODO: add suport for filters?
@@ -791,12 +997,14 @@ export class SchemaBuilder {
791
997
  const interfaceID = getGlobalID(iid, id).toString();
792
998
  for (const [fieldName, relations] of Object.entries(this._relationsTo[interfaceID] ?? {})){
793
999
  for (const relation of relations){
1000
+ if (!this._includedModelIDs.has(relation.model)) continue;
794
1001
  this._buildRelationFields(fields, fieldName, relation);
795
1002
  }
796
1003
  }
797
1004
  }
798
1005
  for (const [fieldName, relations] of Object.entries(this._relationsTo[id] ?? {})){
799
1006
  for (const relation of relations){
1007
+ if (!this._includedModelIDs.has(relation.model)) continue;
800
1008
  this._buildRelationFields(fields, fieldName, relation);
801
1009
  }
802
1010
  }
@@ -825,7 +1033,7 @@ export class SchemaBuilder {
825
1033
  return model.interfaces.map((interfaceID)=>{
826
1034
  const typeID = `${getGlobalID(interfaceID, id).toString()}-data`;
827
1035
  return this._types[typeID];
828
- });
1036
+ }).filter((type)=>type != null);
829
1037
  },
830
1038
  fields
831
1039
  };
@@ -1196,9 +1404,6 @@ export class SchemaBuilder {
1196
1404
  return scalar;
1197
1405
  }
1198
1406
  }
1199
- export function createSchema(record, aliases) {
1200
- return new SchemaBuilder({
1201
- record,
1202
- aliases
1203
- }).build();
1407
+ export function createSchema(params) {
1408
+ return new SchemaBuilder(params).build();
1204
1409
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/graphql",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "license": "see LICENSE.md",
5
5
  "keywords": [],
6
6
  "type": "module",
@@ -23,10 +23,13 @@
23
23
  "graphql-scalars": "^1.25.0",
24
24
  "multiformats": "^13.4.2",
25
25
  "@kubun/id": "^0.4.0",
26
- "@kubun/protocol": "^0.4.0"
26
+ "@kubun/protocol": "^0.4.1"
27
27
  },
28
28
  "devDependencies": {
29
- "@kubun/db": "^0.4.0"
29
+ "@enkaku/capability": "^0.12.1",
30
+ "@enkaku/token": "0.12.3",
31
+ "@kubun/db": "^0.4.0",
32
+ "@kubun/test-utils": "^0.4.0"
30
33
  },
31
34
  "scripts": {
32
35
  "build:clean": "del lib",