@kubun/graphql 0.8.1 → 0.9.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.
package/lib/context.d.ts CHANGED
@@ -144,12 +144,16 @@ export type SetModelAccessDefaultsParams = {
144
144
  permissionType: 'read' | 'write';
145
145
  accessLevel: string;
146
146
  allowedDIDs: Array<string> | null;
147
+ allowedCircles: Array<string> | null;
148
+ allowedGroups: Array<string> | null;
147
149
  };
148
150
  export type SetDocumentAccessOverrideParams = {
149
151
  documentID: string;
150
152
  permissionType: 'read' | 'write';
151
153
  accessLevel: string;
152
154
  allowedDIDs: Array<string> | null;
155
+ allowedCircles: Array<string> | null;
156
+ allowedGroups: Array<string> | null;
153
157
  };
154
158
  /**
155
159
  * Mutation function requiring client implementation
@@ -173,4 +177,22 @@ export type MutationContext = {
173
177
  success: boolean;
174
178
  };
175
179
  };
176
- export type Context = ReadOnlyContext & MutationContext;
180
+ /**
181
+ * Extension point for plugins to contribute typed fields to the GraphQL
182
+ * resolver context. Plugins augment this interface via module declaration
183
+ * merging:
184
+ *
185
+ * declare module '@kubun/graphql' {
186
+ * interface PluginContextMap {
187
+ * myPlugin: MyPluginContext
188
+ * }
189
+ * }
190
+ *
191
+ * At runtime, engine merges each plugin's context factory output under its
192
+ * `name` key. Fields are `Partial<>` because plugins are optional at build
193
+ * time — consumers use optional chaining or assert when the plugin is
194
+ * known to be wired.
195
+ */
196
+ export interface PluginContextMap {
197
+ }
198
+ export type Context = ReadOnlyContext & MutationContext & Partial<PluginContextMap>;
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { AccessWarning, ComplexityStats, Context, CreateMutationParams, DocumentEventEmitter, DocumentSavedEvent, MutationContext, QueryContext, ReadContextDB, ReadContextParams, ReadOnlyContext, RemoveMutationParams, SetDocumentAccessOverrideParams, SetModelAccessDefaultsParams, SetMutationParams, SubscriptionContext, UpdateMutationParams, } from './context.js';
1
+ export type { AccessWarning, ComplexityStats, Context, CreateMutationParams, DocumentEventEmitter, DocumentSavedEvent, MutationContext, PluginContextMap, QueryContext, ReadContextDB, ReadContextParams, ReadOnlyContext, RemoveMutationParams, SetDocumentAccessOverrideParams, SetModelAccessDefaultsParams, SetMutationParams, SubscriptionContext, UpdateMutationParams, } from './context.js';
2
2
  export { createReadContext } from './context.js';
3
3
  export { GraphQLAttachmentID, GraphQLDocID } from './identifiers.js';
4
4
  export type { ExtensionResolvers, SchemaBuilderParams, SharedDefinitions, } from './schema.js';
package/lib/schema.js CHANGED
@@ -1 +1 @@
1
- import{AttachmentID as e,DocumentID as t,DocumentModelID as i,decodeID as n,PluginNodeID as s}from"@kubun/id";import{REFERENCE_PREFIX as r}from"@kubun/protocol";import{pascalCase as l}from"change-case";import{extendSchema as o,GraphQLBoolean as a,GraphQLEnumType as u,GraphQLFloat as c,GraphQLID as d,GraphQLInputObjectType as p,GraphQLInt as f,GraphQLInterfaceType as h,GraphQLList as y,GraphQLNonNull as m,GraphQLObjectType as _,GraphQLSchema as b,GraphQLString as w,isEnumType as D,isListType as g,isNonNullType as I,isObjectType as O,isScalarType as v,parse as $}from"graphql";import{connectionArgs as j,connectionDefinitions as S,mutationWithClientMutationId as F,nodeDefinitions as M}from"graphql-relay";import{GraphQLDateTimeISO as T,GraphQLDID as R,GraphQLDuration as L,GraphQLEmailAddress as E,GraphQLJSON as x,GraphQLJSONObject as A}from"graphql-scalars";import{GraphQLAttachmentID as q,GraphQLDocID as P}from"./identifiers.js";import{GraphQLURL as C}from"./scalars.js";import{getUniqueDocValue as G}from"./utils.js";function N(e){if(I(e))return N(e.ofType);if(g(e))return[];if(v(e))switch(e.name){case"String":case"ID":return"";case"Int":case"Float":return 0;case"Boolean":return!1;default:return null}if(D(e)){let t=e.getValues();return t[0]?.value??null}return{}}let V={date:w,time:w,"date-time":w,duration:L,email:E,uri:C},k={attachmentid:q,did:R,docid:P};function B(e){return e.slice(r.length)}function Q(e){return g(e)||I(e)&&g(e.ofType)}export function toDocumentModelID(e){return e instanceof i?e:i.fromString(e)}export function getGlobalID(e,t){let i=toDocumentModelID(e);return i.isLocal?i.toGlobal(toDocumentModelID(t)):i}export function toList(e){return new y(new m(e))}export function toOuputList(e){return new y(new m(e))}function U(e){return new p({name:`${e.name}ValueFilter`,isOneOf:!0,fields:{isNull:{type:a},equalTo:{type:e},notEqualTo:{type:e},in:{type:toList(e)},notIn:{type:toList(e)},lessThan:{type:e},lessThanOrEqualTo:{type:e},greaterThan:{type:e},greaterThanOrEqualTo:{type:e}}})}let W={AccountValueFilter:new p({name:"AccountValueFilter",isOneOf:!0,fields:{equalTo:{type:R},notEqualTo:{type:R},in:{type:toList(R)},notIn:{type:toList(R)}}}),BooleanValueFilter:new p({name:"BooleanValueFilter",isOneOf:!0,fields:{isNull:{type:a},equalTo:{type:a}}}),FloatValueFilter:U(c),IntValueFilter:U(f),StringValueFilter:U(w)},z={GraphQLBoolean:"BooleanValueFilter",GraphQLDID:"AccountValueFilter",GraphQLFloat:"FloatValueFilter",GraphQLInt:"IntValueFilter",GraphQLString:"StringValueFilter"},H=new p({name:"PatchSetOperation",fields:{path:{type:new m(w)},value:{type:x}}}),J=new p({name:"PatchRemoveOperation",fields:{path:{type:new m(w)}}}),K=new p({name:"PatchDestinationOperation",fields:{from:{type:new m(w)},path:{type:new m(w)}}});export const PatchOperation=new p({name:"PatchOperation",isOneOf:!0,fields:{add:{type:H},set:{type:H},remove:{type:J},replace:{type:H},copy:{type:K},move:{type:K}}});let X=new u({name:"OrderByDirection",values:{ASC:{value:"asc"},DESC:{value:"desc"}}}),Y=new _({name:"AccessRule",fields:{level:{type:new m(w)},allowedDIDs:{type:new y(new m(w))}}}),Z=new _({name:"AccessPermissions",fields:{read:{type:Y},write:{type:Y}}}),ee=new _({name:"ModelAccessDefaults",fields:()=>({ownerDID:{type:new m(w)},modelID:{type:new m(d)},permissions:{type:new m(Z)}})});export class SchemaBuilder{_aliases;_aliasToModelID;_definitions;_implemented={};_includedModelIDs;_includeInterfaceDependencies;_includeMutations;_includeRelationDependencies;_includeSubscriptions;_inputObjects={...W,PatchOperation};_mutations={};_nodeModelIDs;_onlyModels;_record;_references={};_relationsTo={};_extensionSDL;_extensionResolvers;_stubExtensionResolvers;_pluginNodeResolvers={};_subscriptions={};_types={OrderByDirection:X};constructor(e){for(let[t,i]of(this._record=e.record,this._onlyModels=e.onlyModels??null,this._includeInterfaceDependencies=e.includeInterfaceDependencies??!0,this._includeRelationDependencies=e.includeRelationDependencies??!0,this._includeMutations=e.includeMutations??!0,this._includeSubscriptions=e.includeSubscriptions??!0,this._extensionSDL=e.extensionSDL,this._extensionResolvers=e.extensionResolvers,this._stubExtensionResolvers=e.stubExtensionResolvers??!1,this._aliasToModelID=e.aliases??{},this._aliases={},Object.entries(this._aliasToModelID)))this._aliases[i]=t;for(let[t,i]of Object.entries(e.record))if(null==this._aliases[t]){let e=l(i.name);this._aliases[t]=e,this._aliasToModelID[e]??=t}this._includedModelIDs=this._resolveModelIDsToInclude();let t={};for(let[n,s]of Object.entries(e.record)){let e=i.fromString(n);for(let i of(Object.assign(this._references,s.schema.$defs??{}),s.interfaces)){let s=getGlobalID(i,e).toString();t[s]??=new Set,t[s].add(n)}for(let[t,i]of Object.entries(s.fieldsMeta))if("document"===i.type&&null!=i.model){let r=B(s.schema.properties[t]?.$ref),l="array"===this._getReferenceSchema(r).type,o=getGlobalID(i.model,e).toString();this._relationsTo[o]??={},this._relationsTo[o][t]??=[],this._relationsTo[o][t].push({model:n,isList:l})}}let n=new Set;for(let e of Object.keys(t)){let i=new Set;for(let s of(!function e(t,i,n){let s=i[n];if(null==s)throw Error(`Interface ${n} not found in record`);for(let n of s)null!=i[n]?e(t,i,n):t.add(n)}(i,t,e),0!==i.size&&(this._implemented[e]=Array.from(i)),i))n.add(s)}this._nodeModelIDs=Array.from(n)}_resolveModelIdentifier(e){if(null!=this._record[e])return e;let t=this._aliasToModelID[e];if(null!=t&&null!=this._record[t])return t;throw Error(`Model identifier "${e}" not found. Must be a valid model ID or alias.`)}_resolveModelIDsToInclude(){if(null==this._onlyModels||0===this._onlyModels.length)return new Set(Object.keys(this._record));let e=new Set;for(let t of this._onlyModels){let i=this._resolveModelIdentifier(t);e.add(i)}if(!this._includeInterfaceDependencies&&!this._includeRelationDependencies)return e;let t=Array.from(e),i=new Set;for(;t.length>0;){let n=t.pop();if(null==n||i.has(n))continue;i.add(n);let s=this._record[n];if(null!=s){if(this._includeInterfaceDependencies)for(let i of s.interfaces){let s=getGlobalID(i,n).toString();e.has(s)||(e.add(s),t.push(s))}if(this._includeRelationDependencies){for(let i of Object.values(s.fieldsMeta))if("document"===i.type&&null!=i.model&&null!==i.model){let s=getGlobalID(i.model,n).toString();e.has(s)||(e.add(s),t.push(s))}}}}return e}getModelIDs(e){return null===e?this._nodeModelIDs:this._implemented[e]??[e]}build(){let e=this._buildSharedDefinitions();this._definitions=e;let t={nodes:e.nodesField,...e.queryFields};for(let e of this._includedModelIDs){this._buildDocument(e);let i=this._aliases[e],n={type:this._inputObjects[`${e}-filter`]};t[`all${i}`]={type:this._types[`${e}-connection`],args:{...j,filter:n,orderBy:{type:toList(this._inputObjects[`${e}-order`])},search:{type:w}},resolve:async(t,i,n)=>await n.resolveConnection(this.getModelIDs(e),i)},t[`all${i}Count`]={type:new m(f),args:{filter:n},resolve:(t,i,n)=>n.resolveCount(this.getModelIDs(e),i.filter)}}let i=[...this._includedModelIDs][0];if(null!=i){let n=this._types[`${i}-connection`].getFields().pageInfo.type,s=new _({name:"SearchResultEdge",fields:()=>({cursor:{type:new m(w)},node:{type:new m(e.nodeInterface)},rank:{type:new m(c)}})}),r=new _({name:"SearchResultConnection",fields:()=>({edges:{type:new m(new y(new m(s)))},pageInfo:{type:n}})});t.search={type:new m(r),args:{query:{type:new m(w)},models:{type:new y(new m(w))},first:{type:f},after:{type:w}},resolve:async(e,t,i)=>{let{query:n,models:s,first:r}=t;if(""===n.trim())throw Error("Search query must not be empty");let l=s?s.map(e=>this._aliasToModelID[e]??e):[...this._includedModelIDs],o=await i.searchDocuments(n,l,r??20),a=o.map(e=>e.documentID),u=[...new Set(o.map(e=>e.modelID))],c=new Map((a.length>0?await i.resolveList(u,a):[]).map(e=>[e.id,e])),d=[];for(let e of o){let t=c.get(e.documentID);null!=t&&d.push({cursor:String(d.length),node:t,rank:Math.abs(e.rank)})}return{edges:d,pageInfo:{hasNextPage:!1,hasPreviousPage:!1,startCursor:d.length>0?d[0].cursor:null,endCursor:d.length>0?d[d.length-1].cursor:null}}}}}if(this._includeMutations){this._mutations.setModelAccessDefaults={type:ee,args:{modelID:{type:new m(d)},permissionType:{type:new m(w)},accessLevel:{type:new m(w)},allowedDIDs:{type:toList(w)}},resolve:async(e,t,i)=>await i.executeSetModelAccessDefaults({modelID:t.modelID,permissionType:t.permissionType,accessLevel:t.accessLevel,allowedDIDs:t.allowedDIDs??null})},this._mutations.removeModelAccessDefaults={type:a,args:{modelID:{type:new m(d)},permissionTypes:{type:new m(toList(w))}},resolve:async(e,t,i)=>(await i.executeRemoveModelAccessDefaults(t.modelID,t.permissionTypes),!0)},this._mutations.setDocumentAccessOverride={type:e.documentInterface,args:{documentID:{type:new m(d)},permissionType:{type:new m(w)},accessLevel:{type:new m(w)},allowedDIDs:{type:toList(w)}},resolve:async(e,t,i)=>await i.executeSetDocumentAccessOverride({documentID:t.documentID,permissionType:t.permissionType,accessLevel:t.accessLevel,allowedDIDs:t.allowedDIDs??null})},this._mutations.removeDocumentAccessOverride={type:a,args:{documentID:{type:new m(d)},permissionTypes:{type:new m(toList(w))}},resolve:async(e,t,i)=>(await i.executeRemoveDocumentAccessOverride(t.documentID,t.permissionTypes),!0)};let t=new _({name:"BeginTransactionPayload",fields:{transactionID:{type:new m(d)}}}),i=new _({name:"CommitTransactionPayload",fields:{success:{type:new m(a)}}}),n=new _({name:"RollbackTransactionPayload",fields:{success:{type:new m(a)}}});this._mutations.beginTransaction={type:new m(t),args:{},resolve:(e,t,i)=>i.beginTransaction()},this._mutations.commitTransaction={type:new m(i),args:{transactionID:{type:new m(d)}},resolve:async(e,t,i)=>await i.commitTransaction(t.transactionID)},this._mutations.rollbackTransaction={type:new m(n),args:{transactionID:{type:new m(d)}},resolve:(e,t,i)=>i.rollbackTransaction(t.transactionID)}}let n={query:new _({name:"Query",fields:t})};this._includeMutations&&(n.mutation=new _({name:"Mutation",fields:this._mutations})),this._includeSubscriptions&&(n.subscription=new _({name:"Subscription",fields:()=>{let t={...this._subscriptions};if(this._includeSubscriptions)for(let[i,n]of(t.documentChanged={type:new m(e.documentInterface),args:{id:{type:new m(d)}},resolve:e=>e,subscribe:(e,t,i)=>i.subscribeToDocumentChanged(t.id)},t.documentRemoved={type:new m(d),resolve:e=>e,subscribe:(e,t,i)=>i.subscribeToDocumentRemoved()},Object.entries(this._record))){if(!this._includedModelIDs.has(i))continue;let e=this._aliases[i];for(let s of(t[`new${e}Created`]={type:new m(this._types[i]),args:{owner:{type:d}},resolve:e=>e,subscribe:(e,t,n)=>n.subscribeToDocumentCreated(this.getModelIDs(i),t.owner??void 0)},n.interfaces??[])){let e=getGlobalID(s,i).toString();for(let[i,n]of Object.entries(this._relationsTo[e]??{}))for(let s of n)this._buildRelationSubscriptions(t,e,i,s)}for(let[e,n]of Object.entries(this._relationsTo[i]??{}))for(let s of n)this._includedModelIDs.has(s.model)&&this._buildRelationSubscriptions(t,i,e,s)}return t}}));let s=new b(n);if(null!=this._extensionSDL){let e=new Set(Object.keys(s.getTypeMap()));s=o(s,$(this._extensionSDL)),this._stubExtensionResolvers&&this.#e(s,e)}return null!=this._extensionResolvers&&this.#t(s,this._extensionResolvers),s}#t(e,t){let i=e.getQueryType();if(null!=i&&null!=t.queryFields){let e=i.getFields();for(let[i,n]of Object.entries(t.queryFields)){if(null==e[i])throw Error(`Extension resolver for query field "${i}" has no matching field in SDL`);e[i].resolve=n}}let n=e.getMutationType();if(null!=n&&null!=t.mutationFields){let e=n.getFields();for(let[i,n]of Object.entries(t.mutationFields)){if(null==e[i])throw Error(`Extension resolver for mutation field "${i}" has no matching field in SDL`);e[i].resolve=n}}let s=e.getSubscriptionType();if(null!=s&&null!=t.subscriptionFields){let e=s.getFields();for(let[i,n]of Object.entries(t.subscriptionFields)){if(null==e[i])throw Error(`Extension resolver for subscription field "${i}" has no matching field in SDL`);e[i].resolve=n.resolve,e[i].subscribe=n.subscribe}}if(null!=t.typeFields)for(let[i,n]of Object.entries(t.typeFields)){let t=e.getType(i);if(null==t)throw Error(`Extension resolver references type "${i}" which does not exist in the schema`);if(!O(t))throw Error(`Extension resolver references type "${i}" which is not an object type`);let s=t.getFields();for(let[e,t]of Object.entries(n)){if(null==s[e])throw Error(`Extension resolver for field "${e}" on type "${i}" has no matching field in SDL`);s[e].resolve=t}}if(null!=t.nodeResolvers)for(let[e,i]of Object.entries(t.nodeResolvers))this._pluginNodeResolvers[e]=i}#e(e,t){for(let t of[e.getQueryType(),e.getMutationType()])if(null!=t)for(let e of Object.values(t.getFields()))null==e.resolve&&(e.resolve=()=>N(e.type));for(let[i,n]of Object.entries(e.getTypeMap()))if(!t.has(i)&&O(n)){for(let e of Object.values(n.getFields()))if(null==e.resolve){let t=e.type;e.resolve=i=>i[e.name]??N(t)}}}_getDefinitions(){if(null==this._definitions)throw Error("Definitions must be initialized");return this._definitions}_getReference(e,t){let i=B(e),n=this._types[i];if(null!=n)return n;let s=this._getReferenceSchema(i);return this._buildReference(i,s,t)}_getReferenceSchema(e){let t=this._references[e];if(null==t)throw Error(`Could not find reference: ${e}`);return t}_getReferenceInput(e,t,i=""){let n=B(e),s=this._getReferenceSchema(n);if("array"===s.type)return new y(new m(this._getReferenceInput(s.items.$ref,!1,s.title)));if("object"===s.type){if(s.additionalProperties)return A;let e=t?`${n}-partial`:n,r=this._inputObjects[e];if(null!=r)return r;let o={};for(let[e,i]of Object.entries(s.properties)){let n=this._getReferenceInput(i.$ref,t,s.title);o[e]={type:t||!s.required.includes(e)?n:new m(n)}}let a=this._aliases[n]??l(i+s.title),u=new p({name:t?`Partial${a}Input`:`${a}Input`,fields:o});return this._inputObjects[e]=u,u}return this._buildScalar(n,s,i)}_buildSharedDefinitions(){let i=M(async(i,r)=>{if(i.startsWith("did:"))return i;let l=n(i);if(l instanceof e)return l;if(l instanceof t)return await r.loadDocument(i);if(l instanceof s){let e=this._pluginNodeResolvers[l.typeName];if(null==e)throw Error(`No node resolver registered for plugin type "${l.typeName}"`);let t=await e.resolve(l.localID,r);if(null==t)throw Error(`Node resolver for "${l.typeName}" returned null for localID "${l.localID}"`);return{...t,__typename:l.typeName}}throw Error(`Unsupported node ID: ${i}`)},t=>{if("string"==typeof t)return"AccountNode";if(t instanceof e)return"AttachmentNode";if(null!=t&&"object"==typeof t&&"model"in t)return this._aliases[t.model];if(null!=t&&"object"==typeof t&&"__typename"in t)return t.__typename;throw Error("Unknown node type")}),r=new _({name:"AccountNode",interfaces:[i.nodeInterface],fields:()=>{let e={id:{type:new m(d),description:"Globally unique identifier of the account (DID string)",resolve:e=>e},isViewer:{type:new m(a),description:"Whether the authenticated request issuer is this account or not",resolve:(e,t,i)=>i.getViewer()===e}};for(let[i,n]of Object.entries(this._record))if(this._includedModelIDs.has(i))switch(n.behavior){case"interface":continue;case"default":{let t=this._aliases[i];e[`own${t}Connection`]={type:this._types[`${i}-connection`],args:{...j,filter:{type:this._inputObjects[`${i}-filter`]},orderBy:{type:toList(this._inputObjects[`${i}-order`])}},resolve:async(e,t,n)=>await n.resolveConnection(this.getModelIDs(i),{...t,owner:e})};break}case"unique":{let s=this._aliases[i];e[`own${s}`]={type:this._types[i],args:n.uniqueFields.length?{with:{type:new m(this._buildSetInputObjectType(i,n.uniqueFields))}}:{},resolve:async(e,s,r)=>{let l=G(n.uniqueFields,s.with),o=t.create(i,e,l);return await r.loadDocument(o.toString())}}}}return e}}),l=new _({name:"AttachmentNode",interfaces:[i.nodeInterface],fields:{id:{type:new m(d),resolve:e=>e.toString()},contentLength:{type:new m(f)},mimeType:{type:new m(w)}}}),o={id:{type:new m(d)},model:{type:new m(w)},owner:{type:new m(r)},createdAt:{type:new m(T)},updatedAt:{type:T},accessPermissions:{type:Z,resolve:e=>e.data?.accessPermissions??null}},u=new h({name:"DocumentNode",interfaces:[i.nodeInterface],fields:o,resolveType:e=>this._aliases[e.model]}),c={node:i.nodeField,viewer:{type:r,description:"Account issuing the request, if authenticated",resolve:(e,t,i)=>i.getViewer()}};return{...i,accountObject:r,attachmentObject:l,documentInterface:u,documentNodeFields:o,queryFields:c}}_buildDocument(e){if(null!=this._types[e])return this._types[e];let t=this._record[e];if(null==t)throw Error(`Could not find model id: ${e}`);let i=this._getDefinitions();this._aliases[e]??=l(t.name);let n=this._aliases[e],s={name:n,interfaces:()=>[i.documentInterface,i.nodeInterface].concat(t.interfaces.map(t=>this._types[getGlobalID(t,e).toString()]).filter(e=>null!=e)),fields:()=>this._buildDocumentFields(e,t,n)},r="interface"===t.behavior,o=r?new h({...s,resolveType:e=>this._aliases[e.model]}):new _(s),{connectionType:a,edgeType:u}=S({nodeType:o});return this._types[e]=o,this._types[`${e}-connection`]=a,this._types[`${e}-edge`]=u,this._buildDocumentDataObject(e,t,n),this._buildDocumentInput(e,t),this._buildObjectFilterInput(e,t.schema,n,!0),this._buildObjectOrderByInput(e,t.schema,n,!0),!r&&this._includeMutations&&("default"===t.behavior?this._mutations[`create${n}`]=F({name:`Create${n}`,inputFields:()=>({data:{type:new m(this._inputObjects[e])},transactionID:{type:d}}),outputFields:()=>({...i.queryFields,document:{type:this._types[e]}}),mutateAndGetPayload:async(t,i,n)=>({document:await i.executeCreateMutation({modelID:e,data:t.data,info:n,transactionID:t.transactionID})})}):"unique"===t.behavior&&(this._mutations[`set${n}`]=F({name:`Set${n}`,inputFields:()=>({data:{type:new m(this._inputObjects[e])},transactionID:{type:d}}),outputFields:()=>({...i.queryFields,document:{type:this._types[e]}}),mutateAndGetPayload:async(i,n,s)=>{let r=G(t.uniqueFields,i.data);return{document:await n.executeSetMutation({modelID:e,unique:r,data:i.data,info:s,transactionID:i.transactionID})}}})),this._mutations[`update${n}`]=F({name:`Update${n}`,inputFields:()=>({id:{type:new m(d)},patch:{type:new m(toList(PatchOperation))},from:{type:this._inputObjects[`${e}-update`]},transactionID:{type:d}}),outputFields:()=>({...i.queryFields,document:{type:this._types[e]}}),mutateAndGetPayload:async(e,t,i)=>({document:await t.executeUpdateMutation({input:e,info:i,transactionID:e.transactionID})})}),this._mutations[`remove${n}`]=F({name:`Remove${n}`,inputFields:()=>({id:{type:new m(d)},transactionID:{type:d}}),outputFields:()=>({...i.queryFields,id:{type:new m(d)}}),mutateAndGetPayload:async(e,t,i)=>(await t.executeRemoveMutation({id:e.id,info:i,transactionID:e.transactionID}),{id:e.id})})),o}_buildDocumentFields(t,i,n){let s=this._buildDocumentDataObject(t,i,n),r=this._getDefinitions(),l={...r.documentNodeFields,data:{type:new m(s)}};for(let[n,o]of Object.entries(s.getFields())){let s=i.fieldsMeta[n];if(null!=s)switch(s.type){case"account":Q(o.type)?l[`${n}Accounts`]={type:new m(toOuputList(r.accountObject)),resolve:e=>e.data?.[n]??[]}:l[`${n}Account`]={type:r.accountObject,resolve:e=>e.data?.[n]??null};break;case"attachment":Q(o.type)?l[`${n}Attachments`]={type:new m(toOuputList(r.attachmentObject)),resolve:t=>(t.data?.[n]??[]).map(e.fromString)}:l[`${n}Attachment`]={type:r.attachmentObject,resolve:t=>{let i=t.data?.[n];return i?e.fromString(i):null}};break;case"document":{let e,i,a=s.model;if(void 0===a)continue;null===a?(e=null,i=r.documentInterface):(e=getGlobalID(a,t).toString(),this._includedModelIDs.has(e)?i=this._buildDocument(e):(i=r.documentInterface,e=null)),Q(o.type)?l[`${n}Documents`]={type:new m(toOuputList(i)),resolve:async(t,i,s)=>{let r=t.data?.[n]??[];return r.length?await s.resolveList(this.getModelIDs(e),r):[]}}:l[`${n}Document`]={type:i,resolve:async(e,t,i)=>{let s=e.data?.[n];return s?await i.loadDocument(s):null}}}}}for(let e of i.interfaces??[]){let i=getGlobalID(e,t).toString();for(let[e,t]of Object.entries(this._relationsTo[i]??{}))for(let i of t)this._includedModelIDs.has(i.model)&&this._buildRelationFields(l,e,i)}for(let[e,i]of Object.entries(this._relationsTo[t]??{}))for(let t of i)this._includedModelIDs.has(t.model)&&this._buildRelationFields(l,e,t);return l}_buildDocumentDataObject(e,t,i){let n=`${e}-data`;if(null!=this._types[n])return this._types[n];let s=t.schema.required??[],r={};for(let[i,n]of Object.entries(t.schema.properties??{})){let t=n.$ref;if(null==t)throw Error(`Missing ref for field ${i} of object ${e}`);let l=this._getReference(t);r[i]={type:s.includes(i)?new m(l):l}}let l={name:`${i}Data`,interfaces:()=>t.interfaces.map(t=>{let i=`${getGlobalID(t,e).toString()}-data`;return this._types[i]}).filter(e=>null!=e),fields:r},o="interface"===t.behavior?new h(l):new _(l);return this._types[n]=o,o}_buildDocumentInput(e,t){if("interface"===t.behavior)return;let i={},n={};for(let[e,s]of Object.entries(t.schema.properties)){let r=s.$ref,l=this._getReferenceInput(r,!1);i[e]={type:t.schema.required.includes(e)?new m(l):l};let o=B(r);null==this._getReferenceSchema(o).const&&(n[e]={type:this._getReferenceInput(r,!0)})}let s=this._aliases[e];this._inputObjects[e]=new p({name:`${s}Input`,fields:i}),this._inputObjects[`${e}-update`]=new p({name:`Partial${s}Input`,fields:n})}_buildReferenceFilterInput(e,t=""){let i=B(e),n=this._getReferenceSchema(i),s=t;if("object"===n.type)return n.additionalProperties?null:this._buildObjectFilterInput(i,n,t);if("array"===n.type){let e=this._aliases[i]??l(t+n.title);return s=e,new p({name:`${e}Filter`,isOneOf:!0,fields:{isNull:{type:a}}})}let r=this._buildScalar(i,n,s),o=z[r.name];if(null!=o)return this._inputObjects[o];let u=D(r)?new p({name:`${r.name}ValueFilter`,isOneOf:!0,fields:{isNull:{type:a},equalTo:{type:r},notEqualTo:{type:r},in:{type:toList(r)},notIn:{type:toList(r)}}}):U(r);return null==this._inputObjects[u.name]&&(this._inputObjects[u.name]=u),this._inputObjects[u.name]}_buildReferenceOrderByInput(e,t=""){let i=B(e),n=this._getReferenceSchema(i);switch(n.type){case"array":return null;case"object":return n.additionalProperties?null:this._buildObjectOrderByInput(i,n,t);default:return X}}_buildObjectFilterInput(e,t,i="",n=!1){let s=`${e}-filter`,r=this._inputObjects[s];if(null!=r)return r;let o=this._aliases[e]??l(i+(t.title??"")),a=n?{_owner:{type:this._inputObjects.AccountValueFilter}}:{};for(let[e,i]of Object.entries(t.properties)){let t=this._buildReferenceFilterInput(i.$ref,o);null!==t&&(a[e]={type:t})}let u=new p({name:`${o}ObjectFilter`,fields:a}),c=new p({name:`${o}Filter`,isOneOf:!0,fields:()=>({where:{type:u},and:{type:toList(this._inputObjects[s])},or:{type:toList(this._inputObjects[s])},not:{type:this._inputObjects[s]}})});return this._inputObjects[s]=c,c}_buildObjectOrderByInput(e,t,i="",n=!1){let s=`${e}-order`,r=this._inputObjects[s];if(null!=r)return r;let o=this._aliases[e]??l(i+(t.title??"")),a=n?{_docOwner:{type:X},_createdAt:{type:X}}:{};for(let[e,i]of Object.entries(t.properties)){let t=this._buildReferenceOrderByInput(i.$ref,o);null!=t&&(a[e]={type:t})}let u=new p({name:`${o}OrderBy`,isOneOf:!0,fields:a});return this._inputObjects[s]=u,u}_buildSetInputObjectType(e,t,i){let n=i?l(i):"",s=`${e}-with-${n}`,r=this._inputObjects[s];if(null!=r)return r;let o=this._record[e],a=this._aliases[e];return this._inputObjects[s]=new p({name:`With${n}${a}Input`,fields:()=>{let e={};for(let i of t){let t=o.schema.properties[i];if(null==t)throw Error(`Field ${i} not found on model ${a}`);let n=this._getReferenceInput(t.$ref,!1);e[i]={type:new m(n)}}return e}}),this._inputObjects[s]}_buildReference(e,t,i=""){if(null!=this._types[e])return this._types[e];switch(t.type){case"array":return this._types[e]=this._buildArray(t,i),this._types[e];case"object":return this._types[e]=t.additionalProperties?A:this._buildObject(e,t,i),this._types[e];default:return this._buildScalar(e,t,i)}}_buildScalar(e,t,i=""){if(null!=this._types[e])return this._types[e];switch(t.type){case"boolean":this._types[e]=a;break;case"integer":this._types[e]=f;break;case"number":this._types[e]=c;break;case"string":this._types[e]=this._buildString(t,i)}return this._types[e]}_buildArray(e,t=""){let i=e.items.$ref;return new y(new m(this._getReference(i,l(t+e.title))))}_buildObject(e,t,i=""){let n=l(i+t.title),s=this._buildObjectFields(e,t,n);return new _({name:n,fields:s})}_buildObjectFields(e,t,i=""){let n=t.required??[],s={};for(let[r,l]of Object.entries(t.properties??{})){let t=l.$ref;if(null==t)throw Error(`Missing ref for field ${r} of object ${e}`);let o=this._getReference(t,i);s[r]={type:n.includes(r)?new m(o):o}}return s}_buildRelationFields(e,t,i){if(i.isList)return;let n=i.model,s=`${l(t)}Of${this._aliases[n]}`;e[`in${s}`]={type:new m(this._types[`${n}-connection`]),args:{...j,filter:{type:this._inputObjects[`${n}-filter`]},orderBy:{type:toList(this._inputObjects[`${n}-order`])}},resolve:(e,i,s)=>{let r={where:{[t]:{equalTo:e.id}}},l=i.filter?{and:[i.filter,r]}:r;return s.resolveConnection(this.getModelIDs(n),{...i,filter:l})}},e[`in${s}Count`]={type:new m(f),args:{filter:{type:this._inputObjects[`${n}-filter`]}},resolve:(e,i,s)=>{let r={where:{[t]:{equalTo:e.id}}},l=i.filter?{and:[i.filter,r]}:r;return s.resolveCount(this.getModelIDs(n),l)}}}_buildRelationSubscriptions(e,t,i,n){if(n.isList)return;let s=l(i);e[`edge${this._aliases[n.model]}AddedAs${s}To${this._aliases[t]}`]={type:new m(this._types[`${n.model}-edge`]),args:{id:{type:new m(d)}},resolve:e=>e,subscribe:(e,t,s)=>s.subscribeToDocumentEdgeAdded(this.getModelIDs(n.model),e=>e[i]===t.id)},e[`edge${this._aliases[n.model]}RemovedAs${s}From${this._aliases[t]}`]={type:new m(d),args:{id:{type:new m(d)}},resolve:e=>e,subscribe:(e,t,s)=>s.subscribeToDocumentEdgeRemoved(this.getModelIDs(n.model),e=>e[i]===t.id)}}_buildString(e,t){return null!=e.const?w:null!=e.enum?this._buildEnumString(e,t):null!=e.format?this._buildFormatString(e):this._buildCustomString(e)}_buildCustomString(e){return null==e.title?w:k[e.title.toLowerCase()]??w}_buildEnumString(e,t=""){let i=l(t+e.title),n=`${i}-enum`;if(null==this._types[n]){let t={};for(let i of e.enum)t[i]={value:i};this._types[n]=new u({name:i,values:t})}return this._types[n]}_buildFormatString(e){let t=V[e.format];if(null==t)throw Error(`Unsupported string format: ${e.format}`);return t}}export function createSchema(e){return new SchemaBuilder(e).build()}
1
+ import{AttachmentID as e,DocumentID as t,DocumentModelID as i,decodeID as n,PluginNodeID as s}from"@kubun/id";import{REFERENCE_PREFIX as l}from"@kubun/protocol";import{pascalCase as r}from"change-case";import{extendSchema as o,GraphQLBoolean as a,GraphQLEnumType as u,GraphQLFloat as c,GraphQLID as d,GraphQLInputObjectType as p,GraphQLInt as f,GraphQLInterfaceType as h,GraphQLList as y,GraphQLNonNull as m,GraphQLObjectType as _,GraphQLSchema as b,GraphQLString as w,isEnumType as D,isListType as g,isNonNullType as I,isObjectType as O,isScalarType as v,parse as $}from"graphql";import{connectionArgs as j,connectionDefinitions as S,mutationWithClientMutationId as F,nodeDefinitions as M}from"graphql-relay";import{GraphQLDateTimeISO as T,GraphQLDID as R,GraphQLDuration as L,GraphQLEmailAddress as E,GraphQLJSON as x,GraphQLJSONObject as A}from"graphql-scalars";import{GraphQLAttachmentID as q,GraphQLDocID as C}from"./identifiers.js";import{GraphQLURL as G}from"./scalars.js";import{getUniqueDocValue as P,validateAccessIdentifiers as N}from"./utils.js";function V(e){if(I(e))return V(e.ofType);if(g(e))return[];if(v(e))switch(e.name){case"String":case"ID":return"";case"Int":case"Float":return 0;case"Boolean":return!1;default:return null}if(D(e)){let t=e.getValues();return t[0]?.value??null}return{}}let k={date:w,time:w,"date-time":w,duration:L,email:E,uri:G},B={attachmentid:q,did:R,docid:C};function Q(e){return e.slice(l.length)}function U(e){return g(e)||I(e)&&g(e.ofType)}export function toDocumentModelID(e){return e instanceof i?e:i.fromString(e)}export function getGlobalID(e,t){let i=toDocumentModelID(e);return i.isLocal?i.toGlobal(toDocumentModelID(t)):i}export function toList(e){return new y(new m(e))}export function toOuputList(e){return new y(new m(e))}function W(e){return new p({name:`${e.name}ValueFilter`,isOneOf:!0,fields:{isNull:{type:a},equalTo:{type:e},notEqualTo:{type:e},in:{type:toList(e)},notIn:{type:toList(e)},lessThan:{type:e},lessThanOrEqualTo:{type:e},greaterThan:{type:e},greaterThanOrEqualTo:{type:e}}})}let z={AccountValueFilter:new p({name:"AccountValueFilter",isOneOf:!0,fields:{equalTo:{type:R},notEqualTo:{type:R},in:{type:toList(R)},notIn:{type:toList(R)}}}),BooleanValueFilter:new p({name:"BooleanValueFilter",isOneOf:!0,fields:{isNull:{type:a},equalTo:{type:a}}}),FloatValueFilter:W(c),IntValueFilter:W(f),StringValueFilter:W(w)},H={GraphQLBoolean:"BooleanValueFilter",GraphQLDID:"AccountValueFilter",GraphQLFloat:"FloatValueFilter",GraphQLInt:"IntValueFilter",GraphQLString:"StringValueFilter"},J=new p({name:"PatchSetOperation",fields:{path:{type:new m(w)},value:{type:x}}}),K=new p({name:"PatchRemoveOperation",fields:{path:{type:new m(w)}}}),X=new p({name:"PatchDestinationOperation",fields:{from:{type:new m(w)},path:{type:new m(w)}}});export const PatchOperation=new p({name:"PatchOperation",isOneOf:!0,fields:{add:{type:J},set:{type:J},remove:{type:K},replace:{type:J},copy:{type:X},move:{type:X}}});let Y=new u({name:"OrderByDirection",values:{ASC:{value:"asc"},DESC:{value:"desc"}}}),Z=new _({name:"AccessRule",fields:{level:{type:new m(w)},allowedDIDs:{type:new y(new m(w))},allowedCircles:{type:new y(new m(w))},allowedGroups:{type:new y(new m(w))}}}),ee=new _({name:"AccessPermissions",fields:{read:{type:Z},write:{type:Z}}}),et=new _({name:"ModelAccessDefaults",fields:()=>({ownerDID:{type:new m(w)},modelID:{type:new m(d)},permissions:{type:new m(ee)}})});export class SchemaBuilder{_aliases;_aliasToModelID;_definitions;_implemented={};_includedModelIDs;_includeInterfaceDependencies;_includeMutations;_includeRelationDependencies;_includeSubscriptions;_inputObjects={...z,PatchOperation};_mutations={};_nodeModelIDs;_onlyModels;_record;_references={};_relationsTo={};_extensionSDL;_extensionResolvers;_stubExtensionResolvers;_pluginNodeResolvers={};_subscriptions={};_types={OrderByDirection:Y};constructor(e){for(let[t,i]of(this._record=e.record,this._onlyModels=e.onlyModels??null,this._includeInterfaceDependencies=e.includeInterfaceDependencies??!0,this._includeRelationDependencies=e.includeRelationDependencies??!0,this._includeMutations=e.includeMutations??!0,this._includeSubscriptions=e.includeSubscriptions??!0,this._extensionSDL=e.extensionSDL,this._extensionResolvers=e.extensionResolvers,this._stubExtensionResolvers=e.stubExtensionResolvers??!1,this._aliasToModelID=e.aliases??{},this._aliases={},Object.entries(this._aliasToModelID)))this._aliases[i]=t;for(let[t,i]of Object.entries(e.record))if(null==this._aliases[t]){let e=r(i.name);this._aliases[t]=e,this._aliasToModelID[e]??=t}this._includedModelIDs=this._resolveModelIDsToInclude();let t={};for(let[n,s]of Object.entries(e.record)){let e=i.fromString(n);for(let i of(Object.assign(this._references,s.schema.$defs??{}),s.interfaces)){let s=getGlobalID(i,e).toString();t[s]??=new Set,t[s].add(n)}for(let[t,i]of Object.entries(s.fieldsMeta))if("document"===i.type&&null!=i.model){let l=Q(s.schema.properties[t]?.$ref),r="array"===this._getReferenceSchema(l).type,o=getGlobalID(i.model,e).toString();this._relationsTo[o]??={},this._relationsTo[o][t]??=[],this._relationsTo[o][t].push({model:n,isList:r})}}let n=new Set;for(let e of Object.keys(t)){let i=new Set;for(let s of(!function e(t,i,n){let s=i[n];if(null==s)throw Error(`Interface ${n} not found in record`);for(let n of s)null!=i[n]?e(t,i,n):t.add(n)}(i,t,e),0!==i.size&&(this._implemented[e]=Array.from(i)),i))n.add(s)}this._nodeModelIDs=Array.from(n)}_resolveModelIdentifier(e){if(null!=this._record[e])return e;let t=this._aliasToModelID[e];if(null!=t&&null!=this._record[t])return t;throw Error(`Model identifier "${e}" not found. Must be a valid model ID or alias.`)}_resolveModelIDsToInclude(){if(null==this._onlyModels||0===this._onlyModels.length)return new Set(Object.keys(this._record));let e=new Set;for(let t of this._onlyModels){let i=this._resolveModelIdentifier(t);e.add(i)}if(!this._includeInterfaceDependencies&&!this._includeRelationDependencies)return e;let t=Array.from(e),i=new Set;for(;t.length>0;){let n=t.pop();if(null==n||i.has(n))continue;i.add(n);let s=this._record[n];if(null!=s){if(this._includeInterfaceDependencies)for(let i of s.interfaces){let s=getGlobalID(i,n).toString();e.has(s)||(e.add(s),t.push(s))}if(this._includeRelationDependencies){for(let i of Object.values(s.fieldsMeta))if("document"===i.type&&null!=i.model&&null!==i.model){let s=getGlobalID(i.model,n).toString();e.has(s)||(e.add(s),t.push(s))}}}}return e}getModelIDs(e){return null===e?this._nodeModelIDs:this._implemented[e]??[e]}build(){let e=this._buildSharedDefinitions();this._definitions=e;let t={nodes:e.nodesField,...e.queryFields};for(let e of this._includedModelIDs){this._buildDocument(e);let i=this._aliases[e],n={type:this._inputObjects[`${e}-filter`]};t[`all${i}`]={type:this._types[`${e}-connection`],args:{...j,filter:n,orderBy:{type:toList(this._inputObjects[`${e}-order`])},search:{type:w}},resolve:async(t,i,n)=>await n.resolveConnection(this.getModelIDs(e),i)},t[`all${i}Count`]={type:new m(f),args:{filter:n},resolve:(t,i,n)=>n.resolveCount(this.getModelIDs(e),i.filter)}}let i=[...this._includedModelIDs][0];if(null!=i){let n=this._types[`${i}-connection`].getFields().pageInfo.type,s=new _({name:"SearchResultEdge",fields:()=>({cursor:{type:new m(w)},node:{type:new m(e.nodeInterface)},rank:{type:new m(c)}})}),l=new _({name:"SearchResultConnection",fields:()=>({edges:{type:new m(new y(new m(s)))},pageInfo:{type:n}})});t.search={type:new m(l),args:{query:{type:new m(w)},models:{type:new y(new m(w))},first:{type:f},after:{type:w}},resolve:async(e,t,i)=>{let{query:n,models:s,first:l}=t;if(""===n.trim())throw Error("Search query must not be empty");let r=s?s.map(e=>this._aliasToModelID[e]??e):[...this._includedModelIDs],o=await i.searchDocuments(n,r,l??20),a=o.map(e=>e.documentID),u=[...new Set(o.map(e=>e.modelID))],c=new Map((a.length>0?await i.resolveList(u,a):[]).map(e=>[e.id,e])),d=[];for(let e of o){let t=c.get(e.documentID);null!=t&&d.push({cursor:String(d.length),node:t,rank:Math.abs(e.rank)})}return{edges:d,pageInfo:{hasNextPage:!1,hasPreviousPage:!1,startCursor:d.length>0?d[0].cursor:null,endCursor:d.length>0?d[d.length-1].cursor:null}}}}}if(this._includeMutations){this._mutations.setModelAccessDefaults={type:et,args:{modelID:{type:new m(d)},permissionType:{type:new m(w)},accessLevel:{type:new m(w)},allowedDIDs:{type:toList(w)},allowedCircles:{type:toList(w)},allowedGroups:{type:toList(w)}},resolve:async(e,t,i)=>{let n=t.allowedDIDs??null,s=t.allowedCircles??null,l=t.allowedGroups??null;return N({allowedDIDs:n,allowedCircles:s,allowedGroups:l}),await i.executeSetModelAccessDefaults({modelID:t.modelID,permissionType:t.permissionType,accessLevel:t.accessLevel,allowedDIDs:n,allowedCircles:s,allowedGroups:l})}},this._mutations.removeModelAccessDefaults={type:a,args:{modelID:{type:new m(d)},permissionTypes:{type:new m(toList(w))}},resolve:async(e,t,i)=>(await i.executeRemoveModelAccessDefaults(t.modelID,t.permissionTypes),!0)},this._mutations.setDocumentAccessOverride={type:e.documentInterface,args:{documentID:{type:new m(d)},permissionType:{type:new m(w)},accessLevel:{type:new m(w)},allowedDIDs:{type:toList(w)},allowedCircles:{type:toList(w)},allowedGroups:{type:toList(w)}},resolve:async(e,t,i)=>{let n=t.allowedDIDs??null,s=t.allowedCircles??null,l=t.allowedGroups??null;return N({allowedDIDs:n,allowedCircles:s,allowedGroups:l}),await i.executeSetDocumentAccessOverride({documentID:t.documentID,permissionType:t.permissionType,accessLevel:t.accessLevel,allowedDIDs:n,allowedCircles:s,allowedGroups:l})}},this._mutations.removeDocumentAccessOverride={type:a,args:{documentID:{type:new m(d)},permissionTypes:{type:new m(toList(w))}},resolve:async(e,t,i)=>(await i.executeRemoveDocumentAccessOverride(t.documentID,t.permissionTypes),!0)};let t=new _({name:"BeginTransactionPayload",fields:{transactionID:{type:new m(d)}}}),i=new _({name:"CommitTransactionPayload",fields:{success:{type:new m(a)}}}),n=new _({name:"RollbackTransactionPayload",fields:{success:{type:new m(a)}}});this._mutations.beginTransaction={type:new m(t),args:{},resolve:(e,t,i)=>i.beginTransaction()},this._mutations.commitTransaction={type:new m(i),args:{transactionID:{type:new m(d)}},resolve:async(e,t,i)=>await i.commitTransaction(t.transactionID)},this._mutations.rollbackTransaction={type:new m(n),args:{transactionID:{type:new m(d)}},resolve:(e,t,i)=>i.rollbackTransaction(t.transactionID)}}let n={query:new _({name:"Query",fields:t}),types:[G,R,T]};this._includeMutations&&(n.mutation=new _({name:"Mutation",fields:this._mutations})),this._includeSubscriptions&&(n.subscription=new _({name:"Subscription",fields:()=>{let t={...this._subscriptions};if(this._includeSubscriptions)for(let[i,n]of(t.documentChanged={type:new m(e.documentInterface),args:{id:{type:new m(d)}},resolve:e=>e,subscribe:(e,t,i)=>i.subscribeToDocumentChanged(t.id)},t.documentRemoved={type:new m(d),resolve:e=>e,subscribe:(e,t,i)=>i.subscribeToDocumentRemoved()},Object.entries(this._record))){if(!this._includedModelIDs.has(i))continue;let e=this._aliases[i];for(let s of(t[`new${e}Created`]={type:new m(this._types[i]),args:{owner:{type:d}},resolve:e=>e,subscribe:(e,t,n)=>n.subscribeToDocumentCreated(this.getModelIDs(i),t.owner??void 0)},n.interfaces??[])){let e=getGlobalID(s,i).toString();for(let[i,n]of Object.entries(this._relationsTo[e]??{}))for(let s of n)this._buildRelationSubscriptions(t,e,i,s)}for(let[e,n]of Object.entries(this._relationsTo[i]??{}))for(let s of n)this._includedModelIDs.has(s.model)&&this._buildRelationSubscriptions(t,i,e,s)}return t}}));let s=new b(n);if(null!=this._extensionSDL){let e=new Set(Object.keys(s.getTypeMap()));s=o(s,$(this._extensionSDL)),this._stubExtensionResolvers&&this.#e(s,e)}return null!=this._extensionResolvers&&this.#t(s,this._extensionResolvers),s}#t(e,t){let i=e.getQueryType();if(null!=i&&null!=t.queryFields){let e=i.getFields();for(let[i,n]of Object.entries(t.queryFields)){if(null==e[i])throw Error(`Extension resolver for query field "${i}" has no matching field in SDL`);e[i].resolve=n}}let n=e.getMutationType();if(null!=n&&null!=t.mutationFields){let e=n.getFields();for(let[i,n]of Object.entries(t.mutationFields)){if(null==e[i])throw Error(`Extension resolver for mutation field "${i}" has no matching field in SDL`);e[i].resolve=n}}let s=e.getSubscriptionType();if(null!=s&&null!=t.subscriptionFields){let e=s.getFields();for(let[i,n]of Object.entries(t.subscriptionFields)){if(null==e[i])throw Error(`Extension resolver for subscription field "${i}" has no matching field in SDL`);e[i].resolve=n.resolve,e[i].subscribe=n.subscribe}}if(null!=t.typeFields)for(let[i,n]of Object.entries(t.typeFields)){let t=e.getType(i);if(null==t)throw Error(`Extension resolver references type "${i}" which does not exist in the schema`);if(!O(t))throw Error(`Extension resolver references type "${i}" which is not an object type`);let s=t.getFields();for(let[e,t]of Object.entries(n)){if(null==s[e])throw Error(`Extension resolver for field "${e}" on type "${i}" has no matching field in SDL`);s[e].resolve=t}}if(null!=t.nodeResolvers)for(let[e,i]of Object.entries(t.nodeResolvers))this._pluginNodeResolvers[e]=i}#e(e,t){for(let t of[e.getQueryType(),e.getMutationType()])if(null!=t)for(let e of Object.values(t.getFields()))null==e.resolve&&(e.resolve=()=>V(e.type));for(let[i,n]of Object.entries(e.getTypeMap()))if(!t.has(i)&&O(n)){for(let e of Object.values(n.getFields()))if(null==e.resolve){let t=e.type;e.resolve=i=>i[e.name]??V(t)}}}_getDefinitions(){if(null==this._definitions)throw Error("Definitions must be initialized");return this._definitions}_getReference(e,t){let i=Q(e),n=this._types[i];if(null!=n)return n;let s=this._getReferenceSchema(i);return this._buildReference(i,s,t)}_getReferenceSchema(e){let t=this._references[e];if(null==t)throw Error(`Could not find reference: ${e}`);return t}_getReferenceInput(e,t,i=""){let n=Q(e),s=this._getReferenceSchema(n);if("array"===s.type)return new y(new m(this._getReferenceInput(s.items.$ref,!1,s.title)));if("object"===s.type){if(s.additionalProperties)return A;let e=t?`${n}-partial`:n,l=this._inputObjects[e];if(null!=l)return l;let o={};for(let[e,i]of Object.entries(s.properties)){let n=this._getReferenceInput(i.$ref,t,s.title);o[e]={type:t||!s.required.includes(e)?n:new m(n)}}let a=this._aliases[n]??r(i+s.title),u=new p({name:t?`Partial${a}Input`:`${a}Input`,fields:o});return this._inputObjects[e]=u,u}return this._buildScalar(n,s,i)}_buildSharedDefinitions(){let i=M(async(i,l)=>{if(i.startsWith("did:"))return i;let r=n(i);if(r instanceof e)return r;if(r instanceof t)return await l.loadDocument(i);if(r instanceof s){let e=this._pluginNodeResolvers[r.typeName];if(null==e)throw Error(`No node resolver registered for plugin type "${r.typeName}"`);let t=await e.resolve(r.localID,l);if(null==t)throw Error(`Node resolver for "${r.typeName}" returned null for localID "${r.localID}"`);return{...t,__typename:r.typeName}}throw Error(`Unsupported node ID: ${i}`)},t=>{if("string"==typeof t)return"AccountNode";if(t instanceof e)return"AttachmentNode";if(null!=t&&"object"==typeof t&&"model"in t)return this._aliases[t.model];if(null!=t&&"object"==typeof t&&"__typename"in t)return t.__typename;throw Error("Unknown node type")}),l=new _({name:"AccountNode",interfaces:[i.nodeInterface],fields:()=>{let e={id:{type:new m(d),description:"Globally unique identifier of the account (DID string)",resolve:e=>e},isViewer:{type:new m(a),description:"Whether the authenticated request issuer is this account or not",resolve:(e,t,i)=>i.getViewer()===e}};for(let[i,n]of Object.entries(this._record))if(this._includedModelIDs.has(i))switch(n.behavior){case"interface":continue;case"default":{let t=this._aliases[i];e[`own${t}Connection`]={type:this._types[`${i}-connection`],args:{...j,filter:{type:this._inputObjects[`${i}-filter`]},orderBy:{type:toList(this._inputObjects[`${i}-order`])}},resolve:async(e,t,n)=>await n.resolveConnection(this.getModelIDs(i),{...t,owner:e})};break}case"unique":{let s=this._aliases[i];e[`own${s}`]={type:this._types[i],args:n.uniqueFields.length?{with:{type:new m(this._buildSetInputObjectType(i,n.uniqueFields))}}:{},resolve:async(e,s,l)=>{let r=P(n.uniqueFields,s.with),o=t.create(i,e,r);return await l.loadDocument(o.toString())}}}}return e}}),r=new _({name:"AttachmentNode",interfaces:[i.nodeInterface],fields:{id:{type:new m(d),resolve:e=>e.toString()},contentLength:{type:new m(f)},mimeType:{type:new m(w)}}}),o={id:{type:new m(d)},model:{type:new m(w)},owner:{type:new m(l)},createdAt:{type:new m(T)},updatedAt:{type:T},accessPermissions:{type:ee,resolve:e=>e.data?.accessPermissions??null}},u=new h({name:"DocumentNode",interfaces:[i.nodeInterface],fields:o,resolveType:e=>this._aliases[e.model]}),c={node:i.nodeField,viewer:{type:l,description:"Account issuing the request, if authenticated",resolve:(e,t,i)=>i.getViewer()}};return{...i,accountObject:l,attachmentObject:r,documentInterface:u,documentNodeFields:o,queryFields:c}}_buildDocument(e){if(null!=this._types[e])return this._types[e];let t=this._record[e];if(null==t)throw Error(`Could not find model id: ${e}`);let i=this._getDefinitions();this._aliases[e]??=r(t.name);let n=this._aliases[e],s={name:n,interfaces:()=>[i.documentInterface,i.nodeInterface].concat(t.interfaces.map(t=>this._types[getGlobalID(t,e).toString()]).filter(e=>null!=e)),fields:()=>this._buildDocumentFields(e,t,n)},l="interface"===t.behavior,o=l?new h({...s,resolveType:e=>this._aliases[e.model]}):new _(s),{connectionType:a,edgeType:u}=S({nodeType:o});return this._types[e]=o,this._types[`${e}-connection`]=a,this._types[`${e}-edge`]=u,this._buildDocumentDataObject(e,t,n),this._buildDocumentInput(e,t),this._buildObjectFilterInput(e,t.schema,n,!0),this._buildObjectOrderByInput(e,t.schema,n,!0),!l&&this._includeMutations&&("default"===t.behavior?this._mutations[`create${n}`]=F({name:`Create${n}`,inputFields:()=>({data:{type:new m(this._inputObjects[e])},transactionID:{type:d}}),outputFields:()=>({...i.queryFields,document:{type:this._types[e]}}),mutateAndGetPayload:async(t,i,n)=>({document:await i.executeCreateMutation({modelID:e,data:t.data,info:n,transactionID:t.transactionID})})}):"unique"===t.behavior&&(this._mutations[`set${n}`]=F({name:`Set${n}`,inputFields:()=>({data:{type:new m(this._inputObjects[e])},transactionID:{type:d}}),outputFields:()=>({...i.queryFields,document:{type:this._types[e]}}),mutateAndGetPayload:async(i,n,s)=>{let l=P(t.uniqueFields,i.data);return{document:await n.executeSetMutation({modelID:e,unique:l,data:i.data,info:s,transactionID:i.transactionID})}}})),this._mutations[`update${n}`]=F({name:`Update${n}`,inputFields:()=>({id:{type:new m(d)},patch:{type:new m(toList(PatchOperation))},from:{type:this._inputObjects[`${e}-update`]},transactionID:{type:d}}),outputFields:()=>({...i.queryFields,document:{type:this._types[e]}}),mutateAndGetPayload:async(e,t,i)=>({document:await t.executeUpdateMutation({input:e,info:i,transactionID:e.transactionID})})}),this._mutations[`remove${n}`]=F({name:`Remove${n}`,inputFields:()=>({id:{type:new m(d)},transactionID:{type:d}}),outputFields:()=>({...i.queryFields,id:{type:new m(d)}}),mutateAndGetPayload:async(e,t,i)=>(await t.executeRemoveMutation({id:e.id,info:i,transactionID:e.transactionID}),{id:e.id})})),o}_buildDocumentFields(t,i,n){let s=this._buildDocumentDataObject(t,i,n),l=this._getDefinitions(),r={...l.documentNodeFields,data:{type:new m(s)}};for(let[n,o]of Object.entries(s.getFields())){let s=i.fieldsMeta[n];if(null!=s)switch(s.type){case"account":U(o.type)?r[`${n}Accounts`]={type:new m(toOuputList(l.accountObject)),resolve:e=>e.data?.[n]??[]}:r[`${n}Account`]={type:l.accountObject,resolve:e=>e.data?.[n]??null};break;case"attachment":U(o.type)?r[`${n}Attachments`]={type:new m(toOuputList(l.attachmentObject)),resolve:t=>(t.data?.[n]??[]).map(e.fromString)}:r[`${n}Attachment`]={type:l.attachmentObject,resolve:t=>{let i=t.data?.[n];return i?e.fromString(i):null}};break;case"document":{let e,i,a=s.model;if(void 0===a)continue;null===a?(e=null,i=l.documentInterface):(e=getGlobalID(a,t).toString(),this._includedModelIDs.has(e)?i=this._buildDocument(e):(i=l.documentInterface,e=null)),U(o.type)?r[`${n}Documents`]={type:new m(toOuputList(i)),resolve:async(t,i,s)=>{let l=t.data?.[n]??[];return l.length?await s.resolveList(this.getModelIDs(e),l):[]}}:r[`${n}Document`]={type:i,resolve:async(e,t,i)=>{let s=e.data?.[n];return s?await i.loadDocument(s):null}}}}}for(let e of i.interfaces??[]){let i=getGlobalID(e,t).toString();for(let[e,t]of Object.entries(this._relationsTo[i]??{}))for(let i of t)this._includedModelIDs.has(i.model)&&this._buildRelationFields(r,e,i)}for(let[e,i]of Object.entries(this._relationsTo[t]??{}))for(let t of i)this._includedModelIDs.has(t.model)&&this._buildRelationFields(r,e,t);return r}_buildDocumentDataObject(e,t,i){let n=`${e}-data`;if(null!=this._types[n])return this._types[n];let s=t.schema.required??[],l={};for(let[i,n]of Object.entries(t.schema.properties??{})){let t=n.$ref;if(null==t)throw Error(`Missing ref for field ${i} of object ${e}`);let r=this._getReference(t);l[i]={type:s.includes(i)?new m(r):r}}let r={name:`${i}Data`,interfaces:()=>t.interfaces.map(t=>{let i=`${getGlobalID(t,e).toString()}-data`;return this._types[i]}).filter(e=>null!=e),fields:l},o="interface"===t.behavior?new h(r):new _(r);return this._types[n]=o,o}_buildDocumentInput(e,t){if("interface"===t.behavior)return;let i={},n={};for(let[e,s]of Object.entries(t.schema.properties)){let l=s.$ref,r=this._getReferenceInput(l,!1);i[e]={type:t.schema.required.includes(e)?new m(r):r};let o=Q(l);null==this._getReferenceSchema(o).const&&(n[e]={type:this._getReferenceInput(l,!0)})}let s=this._aliases[e];this._inputObjects[e]=new p({name:`${s}Input`,fields:i}),this._inputObjects[`${e}-update`]=new p({name:`Partial${s}Input`,fields:n})}_buildReferenceFilterInput(e,t=""){let i=Q(e),n=this._getReferenceSchema(i),s=t;if("object"===n.type)return n.additionalProperties?null:this._buildObjectFilterInput(i,n,t);if("array"===n.type){let e=this._aliases[i]??r(t+n.title);return s=e,new p({name:`${e}Filter`,isOneOf:!0,fields:{isNull:{type:a}}})}let l=this._buildScalar(i,n,s),o=H[l.name];if(null!=o)return this._inputObjects[o];let u=D(l)?new p({name:`${l.name}ValueFilter`,isOneOf:!0,fields:{isNull:{type:a},equalTo:{type:l},notEqualTo:{type:l},in:{type:toList(l)},notIn:{type:toList(l)}}}):W(l);return null==this._inputObjects[u.name]&&(this._inputObjects[u.name]=u),this._inputObjects[u.name]}_buildReferenceOrderByInput(e,t=""){let i=Q(e),n=this._getReferenceSchema(i);switch(n.type){case"array":return null;case"object":return n.additionalProperties?null:this._buildObjectOrderByInput(i,n,t);default:return Y}}_buildObjectFilterInput(e,t,i="",n=!1){let s=`${e}-filter`,l=this._inputObjects[s];if(null!=l)return l;let o=this._aliases[e]??r(i+(t.title??"")),a=n?{_owner:{type:this._inputObjects.AccountValueFilter}}:{};for(let[e,i]of Object.entries(t.properties)){let t=this._buildReferenceFilterInput(i.$ref,o);null!==t&&(a[e]={type:t})}let u=new p({name:`${o}ObjectFilter`,fields:a}),c=new p({name:`${o}Filter`,isOneOf:!0,fields:()=>({where:{type:u},and:{type:toList(this._inputObjects[s])},or:{type:toList(this._inputObjects[s])},not:{type:this._inputObjects[s]}})});return this._inputObjects[s]=c,c}_buildObjectOrderByInput(e,t,i="",n=!1){let s=`${e}-order`,l=this._inputObjects[s];if(null!=l)return l;let o=this._aliases[e]??r(i+(t.title??"")),a=n?{_docOwner:{type:Y},_createdAt:{type:Y}}:{};for(let[e,i]of Object.entries(t.properties)){let t=this._buildReferenceOrderByInput(i.$ref,o);null!=t&&(a[e]={type:t})}let u=new p({name:`${o}OrderBy`,isOneOf:!0,fields:a});return this._inputObjects[s]=u,u}_buildSetInputObjectType(e,t,i){let n=i?r(i):"",s=`${e}-with-${n}`,l=this._inputObjects[s];if(null!=l)return l;let o=this._record[e],a=this._aliases[e];return this._inputObjects[s]=new p({name:`With${n}${a}Input`,fields:()=>{let e={};for(let i of t){let t=o.schema.properties[i];if(null==t)throw Error(`Field ${i} not found on model ${a}`);let n=this._getReferenceInput(t.$ref,!1);e[i]={type:new m(n)}}return e}}),this._inputObjects[s]}_buildReference(e,t,i=""){if(null!=this._types[e])return this._types[e];switch(t.type){case"array":return this._types[e]=this._buildArray(t,i),this._types[e];case"object":return this._types[e]=t.additionalProperties?A:this._buildObject(e,t,i),this._types[e];default:return this._buildScalar(e,t,i)}}_buildScalar(e,t,i=""){if(null!=this._types[e])return this._types[e];switch(t.type){case"boolean":this._types[e]=a;break;case"integer":this._types[e]=f;break;case"number":this._types[e]=c;break;case"string":this._types[e]=this._buildString(t,i)}return this._types[e]}_buildArray(e,t=""){let i=e.items.$ref;return new y(new m(this._getReference(i,r(t+e.title))))}_buildObject(e,t,i=""){let n=r(i+t.title),s=this._buildObjectFields(e,t,n);return new _({name:n,fields:s})}_buildObjectFields(e,t,i=""){let n=t.required??[],s={};for(let[l,r]of Object.entries(t.properties??{})){let t=r.$ref;if(null==t)throw Error(`Missing ref for field ${l} of object ${e}`);let o=this._getReference(t,i);s[l]={type:n.includes(l)?new m(o):o}}return s}_buildRelationFields(e,t,i){if(i.isList)return;let n=i.model,s=`${r(t)}Of${this._aliases[n]}`;e[`in${s}`]={type:new m(this._types[`${n}-connection`]),args:{...j,filter:{type:this._inputObjects[`${n}-filter`]},orderBy:{type:toList(this._inputObjects[`${n}-order`])}},resolve:(e,i,s)=>{let l={where:{[t]:{equalTo:e.id}}},r=i.filter?{and:[i.filter,l]}:l;return s.resolveConnection(this.getModelIDs(n),{...i,filter:r})}},e[`in${s}Count`]={type:new m(f),args:{filter:{type:this._inputObjects[`${n}-filter`]}},resolve:(e,i,s)=>{let l={where:{[t]:{equalTo:e.id}}},r=i.filter?{and:[i.filter,l]}:l;return s.resolveCount(this.getModelIDs(n),r)}}}_buildRelationSubscriptions(e,t,i,n){if(n.isList)return;let s=r(i);e[`edge${this._aliases[n.model]}AddedAs${s}To${this._aliases[t]}`]={type:new m(this._types[`${n.model}-edge`]),args:{id:{type:new m(d)}},resolve:e=>e,subscribe:(e,t,s)=>s.subscribeToDocumentEdgeAdded(this.getModelIDs(n.model),e=>e[i]===t.id)},e[`edge${this._aliases[n.model]}RemovedAs${s}From${this._aliases[t]}`]={type:new m(d),args:{id:{type:new m(d)}},resolve:e=>e,subscribe:(e,t,s)=>s.subscribeToDocumentEdgeRemoved(this.getModelIDs(n.model),e=>e[i]===t.id)}}_buildString(e,t){return null!=e.const?w:null!=e.enum?this._buildEnumString(e,t):null!=e.format?this._buildFormatString(e):this._buildCustomString(e)}_buildCustomString(e){return null==e.title?w:B[e.title.toLowerCase()]??w}_buildEnumString(e,t=""){let i=r(t+e.title),n=`${i}-enum`;if(null==this._types[n]){let t={};for(let i of e.enum)t[i]={value:i};this._types[n]=new u({name:i,values:t})}return this._types[n]}_buildFormatString(e){let t=k[e.format];if(null==t)throw Error(`Unsupported string format: ${e.format}`);return t}}export function createSchema(e){return new SchemaBuilder(e).build()}
package/lib/types.d.ts CHANGED
@@ -44,10 +44,14 @@ export type ModelAccessDefaults = {
44
44
  read?: {
45
45
  level: string;
46
46
  allowedDIDs: Array<string>;
47
+ allowedCircles?: Array<string>;
48
+ allowedGroups?: Array<string>;
47
49
  };
48
50
  write?: {
49
51
  level: string;
50
52
  allowedDIDs: Array<string>;
53
+ allowedCircles?: Array<string>;
54
+ allowedGroups?: Array<string>;
51
55
  };
52
56
  };
53
57
  };
package/lib/utils.d.ts CHANGED
@@ -1,2 +1,31 @@
1
1
  import type { DocumentData } from '@kubun/protocol';
2
2
  export declare function getUniqueDocValue(fields: Array<string>, data: DocumentData): Uint8Array;
3
+ /**
4
+ * Validate that every entry of `dids` is a string with the `did:` prefix.
5
+ * Throws a user-facing error on the first malformed entry. Mirrors
6
+ * `validateDIDs` from `@kubun/engine`'s access-control module — duplicated
7
+ * here because `@kubun/graphql` is upstream of `@kubun/engine` in the
8
+ * dependency graph.
9
+ */
10
+ export declare function validateDIDs(dids: Array<string>): void;
11
+ /**
12
+ * Validate that an identifier (circle or group ID) is a non-empty,
13
+ * non-whitespace string. There is no canonical format helper for circle and
14
+ * group IDs in `@kubun/id` (they are app-supplied opaque strings — Kubun ID
15
+ * helpers cover content-addressed IDs only), so this check rejects only
16
+ * obvious garbage. Downstream membership checks won't match if the format is
17
+ * malformed beyond this — callers should treat malformed IDs as an empty
18
+ * scope.
19
+ */
20
+ export declare function validateIDs(ids: Array<string>, kind: 'circle' | 'group'): void;
21
+ /**
22
+ * Validate the three optional access-control identifier arrays passed to
23
+ * `setModelAccessDefaults` / `setDocumentAccessOverride` resolvers. Returns
24
+ * normally on success; throws a user-facing GraphQL error on malformed
25
+ * input. Each array, when non-null, is checked element-wise.
26
+ */
27
+ export declare function validateAccessIdentifiers(args: {
28
+ allowedDIDs: Array<string> | null;
29
+ allowedCircles: Array<string> | null;
30
+ allowedGroups: Array<string> | null;
31
+ }): void;
package/lib/utils.js CHANGED
@@ -1 +1 @@
1
- import{fromUTF as e}from"@enkaku/codec";export function getUniqueDocValue(n,o){return e(n.map(e=>String(o[e]??"")).join("|"))}
1
+ import{fromUTF as t}from"@enkaku/codec";export function getUniqueDocValue(e,o){return t(e.map(t=>String(o[t]??"")).join("|"))}export function validateDIDs(t){for(let e of t)if("string"!=typeof e||!e.startsWith("did:"))throw Error(`Invalid DID format: ${String(e)}`)}export function validateIDs(t,e){for(let o of t)if("string"!=typeof o||0===o.trim().length)throw Error(`Invalid ${e} ID: must be a non-empty string`)}export function validateAccessIdentifiers(t){null!=t.allowedDIDs&&validateDIDs(t.allowedDIDs),null!=t.allowedCircles&&validateIDs(t.allowedCircles,"circle"),null!=t.allowedGroups&&validateIDs(t.allowedGroups,"group")}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/graphql",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "license": "see LICENSE.md",
5
5
  "keywords": [],
6
6
  "type": "module",
@@ -15,24 +15,24 @@
15
15
  ],
16
16
  "sideEffects": false,
17
17
  "dependencies": {
18
- "@enkaku/async": "^0.14.0",
19
- "@enkaku/codec": "^0.14.0",
20
- "@enkaku/event": "^0.14.1",
21
- "@enkaku/otel": "^0.14.0",
18
+ "@enkaku/async": "^0.15.0",
19
+ "@enkaku/codec": "^0.15.0",
20
+ "@enkaku/event": "^0.15.0",
21
+ "@enkaku/otel": "^0.15.0",
22
22
  "change-case": "^5.4.4",
23
23
  "graphql": "^16.13.2",
24
24
  "graphql-relay": "^0.10.2",
25
25
  "graphql-scalars": "^1.25.0",
26
- "multiformats": "^13.4.2",
27
- "@kubun/hlc": "^0.8.0",
28
- "@kubun/id": "^0.8.0",
29
- "@kubun/protocol": "^0.8.0"
26
+ "multiformats": "^14.0.0",
27
+ "@kubun/protocol": "^0.9.0",
28
+ "@kubun/id": "^0.9.0",
29
+ "@kubun/hlc": "^0.9.0"
30
30
  },
31
31
  "devDependencies": {
32
- "@enkaku/capability": "^0.14.0",
33
- "@enkaku/token": "0.14.1",
34
- "@kubun/test-utils": "^0.8.0",
35
- "@kubun/db-better-sqlite": "^0.8.1"
32
+ "@enkaku/capability": "^0.15.0",
33
+ "@enkaku/token": "^0.15.0",
34
+ "@kubun/db-better-sqlite": "^0.9.0",
35
+ "@kubun/test-utils": "^0.9.0"
36
36
  },
37
37
  "scripts": {
38
38
  "build:clean": "del lib",