@bobbykim/manguito-cms-api 0.2.0 → 0.3.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/graphql/index.ts","../../src/graphql/handler.ts","../../src/graphql/schema.ts","../../src/graphql/scalars.ts","../../src/graphql/type-mapping.ts","../../src/graphql/naming.ts","../../src/graphql/filters.ts","../../src/graphql/resolvers.ts","../../src/graphql/dataloaders.ts","../../src/relations.ts","../../src/graphql/security.ts"],"sourcesContent":["export { createGraphQLHandler } from './handler.js'\nexport type { ResolvedGraphQLOptions } from './handler.js'\n","import { createYoga } from 'graphql-yoga'\nimport type { Handler } from 'hono'\nimport type { SchemaRegistry, ContentRepository } from '@bobbykim/manguito-cms-core'\nimport type { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db'\nimport type { ProgrammaticResolver } from '../programmatic/resolve.js'\nimport type { GraphQLContext } from './context.js'\nimport { buildGraphQLSchema } from './schema.js'\nimport { createRelationLoaders } from './dataloaders.js'\nimport { buildArmorPlugin, introspectionPlugin } from './security.js'\n\nexport type ResolvedGraphQLOptions = {\n enabled: boolean\n maxDepth: number\n maxComplexity: number\n graphiql: boolean\n introspection: boolean\n}\n\nexport function createGraphQLHandler(\n registry: SchemaRegistry,\n repos: Record<string, ContentRepository<unknown>>,\n resolver: ProgrammaticResolver,\n db: DrizzlePostgresInstance,\n options: ResolvedGraphQLOptions\n): Handler {\n const schema = buildGraphQLSchema(registry)\n const { plugins } = buildArmorPlugin({\n maxDepth: options.maxDepth,\n maxComplexity: options.maxComplexity,\n })\n\n const yoga = createYoga<Record<string, never>, GraphQLContext>({\n schema,\n graphqlEndpoint: '/graphql',\n graphiql: options.graphiql,\n landingPage: false,\n // buildArmorPlugin's return type is `unknown[]` (see security.ts) since Armor's\n // plugin shapes aren't part of its public typings; Yoga's `plugins` option only\n // accepts its own `Plugin` type, so the cast bridges the two.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n plugins: [...(plugins as any[]), introspectionPlugin(options.introspection)],\n context: (): GraphQLContext => ({\n db,\n registry,\n repos,\n resolver,\n loaders: createRelationLoaders(db, registry),\n programmaticMemo: new WeakMap(),\n }),\n })\n\n return (c) => yoga.fetch(c.req.raw, {})\n}\n","import {\n GraphQLSchema,\n GraphQLObjectType,\n GraphQLEnumType,\n GraphQLNonNull,\n GraphQLList,\n GraphQLID,\n GraphQLString,\n GraphQLInt,\n GraphQLBoolean,\n type GraphQLFieldConfig,\n type GraphQLOutputType,\n type GraphQLFieldConfigMap,\n} from 'graphql'\nimport type {\n SchemaRegistry,\n ParsedContentType,\n ParsedTaxonomyType,\n ParsedParagraphType,\n ParsedField,\n ParsedEnumType,\n} from '@bobbykim/manguito-cms-core'\nimport type { GraphQLContext } from './context.js'\nimport { DateTimeScalar } from './scalars.js'\nimport { scalarOutputType } from './type-mapping.js'\nimport {\n graphqlTypeName,\n singleQueryName,\n collectionQueryName,\n toCamelCase,\n isValidGraphQLName,\n buildFieldNameMap,\n} from './naming.js'\nimport { SortOrderEnum, buildSortFieldEnum, buildFilterInputType } from './filters.js'\nimport {\n scalarFieldResolver,\n relationFieldResolver,\n programmaticFieldResolver,\n collectionResolver,\n singleBySlugResolver,\n singletonResolver,\n taxonomySingleResolver,\n} from './resolvers.js'\n\nconst PAGE_META = new GraphQLObjectType({\n name: 'PageMeta',\n fields: {\n total: { type: new GraphQLNonNull(GraphQLInt), resolve: (m) => m.total },\n page: { type: new GraphQLNonNull(GraphQLInt), resolve: (m) => m.page },\n perPage: { type: new GraphQLNonNull(GraphQLInt), resolve: (m) => m.per_page },\n totalPages: { type: new GraphQLNonNull(GraphQLInt), resolve: (m) => m.total_pages },\n hasNext: { type: new GraphQLNonNull(GraphQLBoolean), resolve: (m) => m.has_next },\n hasPrev: { type: new GraphQLNonNull(GraphQLBoolean), resolve: (m) => m.has_prev },\n },\n})\n\nconst MEDIA = new GraphQLObjectType({\n name: 'Media',\n fields: {\n id: { type: new GraphQLNonNull(GraphQLID), resolve: (m) => m.id },\n type: { type: new GraphQLNonNull(GraphQLString), resolve: (m) => m.type },\n url: { type: new GraphQLNonNull(GraphQLString), resolve: (m) => m.url },\n mimeType: { type: new GraphQLNonNull(GraphQLString), resolve: (m) => m.mime_type },\n alt: { type: GraphQLString, resolve: (m) => m.alt },\n fileSize: { type: GraphQLInt, resolve: (m) => m.file_size },\n width: { type: GraphQLInt, resolve: (m) => m.width },\n height: { type: GraphQLInt, resolve: (m) => m.height },\n duration: { type: GraphQLInt, resolve: (m) => m.duration },\n },\n})\n\nexport function buildGraphQLSchema(registry: SchemaRegistry): GraphQLSchema {\n const objectTypes = new Map<string, GraphQLObjectType>() // machineName → type\n const enumTypes = new Map<string, GraphQLEnumType>() // enum machineName → type (only when valid)\n\n // Enum types (only when every value is a valid GraphQL identifier).\n for (const [name, enumType] of Object.entries(registry.enum_types) as [string, ParsedEnumType][]) {\n if (enumType.values.every(isValidGraphQLName)) {\n enumTypes.set(\n name,\n new GraphQLEnumType({\n name: graphqlTypeName(name),\n values: Object.fromEntries(enumType.values.map((v) => [v, { value: v }])),\n })\n )\n } else {\n process.stderr.write(\n `⚠ enum '${name}' has values that aren't valid GraphQL identifiers; exposing as String\\n`\n )\n }\n }\n\n // Output type for one field (scalar, enum, relation, media, programmatic).\n function outputTypeForField(field: ParsedField): GraphQLOutputType {\n const scalar = scalarOutputType(field.field_type)\n if (scalar) return field.required ? new GraphQLNonNull(scalar) : scalar\n\n if (field.field_type === 'enum') {\n const ref = field.ui_component.component === 'select' ? field.ui_component.enum_ref : undefined\n const et = ref ? enumTypes.get(ref) : undefined\n const t = et ?? GraphQLString\n return field.required ? new GraphQLNonNull(t) : t\n }\n\n if (field.field_type === 'image' || field.field_type === 'video' || field.field_type === 'file') {\n return MEDIA\n }\n\n if (field.field_type === 'paragraph') {\n const ref = field.ui_component.component === 'paragraph-embed' ? field.ui_component.ref : undefined\n const target = ref ? objectTypes.get(ref) : undefined\n if (ref && !target) {\n process.stderr.write(\n `⚠ paragraph field '${field.name}' targets unknown paragraph type '${ref}'; exposing as Media\\n`\n )\n }\n return new GraphQLList(new GraphQLNonNull(target ?? MEDIA))\n }\n\n if (field.field_type === 'reference') {\n const ref = field.ui_component.component === 'typeahead-select' ? field.ui_component.ref : undefined\n const target = ref ? objectTypes.get(ref) : undefined\n if (ref && !target) {\n process.stderr.write(`⚠ reference field '${field.name}' targets unknown type '${ref}'; exposing as Media\\n`)\n }\n const rel = field.ui_component.component === 'typeahead-select' ? field.ui_component.rel : undefined\n const isMany = rel === 'many-to-many' || rel === 'one-to-many'\n const t = (target ?? MEDIA) as GraphQLObjectType\n return isMany ? new GraphQLList(new GraphQLNonNull(t)) : t\n }\n\n return GraphQLString\n }\n\n // Build the object type for a content/taxonomy/paragraph type. Fields are a\n // thunk so relations can reference types created later (circular graphs).\n function buildObjectType(\n machineName: string,\n type: ParsedContentType | ParsedTaxonomyType | ParsedParagraphType\n ): GraphQLObjectType {\n return new GraphQLObjectType({\n name: graphqlTypeName(machineName),\n fields: () => {\n const fields: GraphQLFieldConfigMap<Record<string, unknown>, GraphQLContext> = {}\n // System fields.\n fields['id'] = { type: new GraphQLNonNull(GraphQLID), resolve: (p) => p['id'] }\n if (type.schema_type !== 'paragraph-type') {\n fields['published'] = { type: new GraphQLNonNull(GraphQLBoolean), resolve: (p) => p['published'] }\n }\n if (type.schema_type === 'content-type') {\n fields['slug'] = { type: new GraphQLNonNull(GraphQLString), resolve: (p) => p['slug'] }\n }\n fields['createdAt'] = { type: new GraphQLNonNull(DateTimeScalar), resolve: (p) => p['created_at'] }\n fields['updatedAt'] = { type: new GraphQLNonNull(DateTimeScalar), resolve: (p) => p['updated_at'] }\n\n for (const field of type.fields) {\n const gqlName = toCamelCase(field.name)\n const outType = outputTypeForField(field)\n let resolve: GraphQLFieldConfig<Record<string, unknown>, GraphQLContext>['resolve']\n if (field.field_type === 'programmatic') {\n resolve = programmaticFieldResolver(machineName, field.name)\n } else if (\n field.field_type === 'reference' ||\n field.field_type === 'paragraph' ||\n field.field_type === 'image' ||\n field.field_type === 'video' ||\n field.field_type === 'file'\n ) {\n resolve = relationFieldResolver(machineName, field.name)\n } else {\n resolve = scalarFieldResolver(field.name)\n }\n fields[gqlName] = { type: outType, resolve }\n }\n return fields\n },\n })\n }\n\n // Pass 1: create every object type (empty-safe thunks resolve later).\n // Track GraphQL type name → machine name so two schemas that collapse to the\n // same GraphQL type name (e.g. content--author / taxonomy--author both → \"Author\")\n // fail loud here instead of surfacing as a cryptic GraphQLSchema build error.\n const typeNameOwners = new Map<string, string>() // graphqlTypeName → machineName\n function registerTypeName(machineName: string): void {\n const gqlName = graphqlTypeName(machineName)\n const owner = typeNameOwners.get(gqlName)\n if (owner !== undefined && owner !== machineName) {\n throw new Error(\n `GraphQL type name collision: '${gqlName}' is produced by both '${owner}' and '${machineName}'. ` +\n `Rename one schema's machine name segment so their GraphQL type names differ.`\n )\n }\n typeNameOwners.set(gqlName, machineName)\n }\n\n for (const [name, ct] of Object.entries(registry.content_types)) {\n registerTypeName(name)\n objectTypes.set(name, buildObjectType(name, ct))\n }\n for (const [name, tt] of Object.entries(registry.taxonomy_types)) {\n registerTypeName(name)\n objectTypes.set(name, buildObjectType(name, tt))\n }\n for (const [name, pt] of Object.entries(registry.paragraph_types)) {\n registerTypeName(name)\n objectTypes.set(name, buildObjectType(name, pt))\n }\n\n // Pass 2: build the root Query type.\n const queryFields: GraphQLFieldConfigMap<unknown, GraphQLContext> = {}\n\n for (const [name, ct] of Object.entries(registry.content_types) as [string, ParsedContentType][]) {\n const objType = objectTypes.get(name)!\n const nameMap = buildFieldNameMap(ct.fields.map((f) => f.name))\n\n if (ct.only_one) {\n queryFields[singleQueryName(name)] = { type: objType, resolve: singletonResolver(name) }\n continue\n }\n\n const listType = new GraphQLObjectType({\n name: `${graphqlTypeName(name)}List`,\n fields: {\n data: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(objType))), resolve: (r) => r.data },\n meta: { type: new GraphQLNonNull(PAGE_META), resolve: (r) => r.meta },\n },\n })\n const filterType = buildFilterInputType(ct)\n\n queryFields[collectionQueryName(name)] = {\n type: new GraphQLNonNull(listType),\n args: {\n page: { type: GraphQLInt },\n perPage: { type: GraphQLInt },\n sortBy: { type: buildSortFieldEnum(graphqlTypeName(name)) },\n sortOrder: { type: SortOrderEnum },\n ...(filterType ? { filter: { type: filterType } } : {}),\n },\n resolve: collectionResolver(name, nameMap),\n }\n queryFields[singleQueryName(name)] = {\n type: objType,\n args: { slug: { type: new GraphQLNonNull(GraphQLString) } },\n resolve: singleBySlugResolver(name),\n }\n }\n\n for (const [name, tt] of Object.entries(registry.taxonomy_types) as [string, ParsedTaxonomyType][]) {\n const objType = objectTypes.get(name)!\n const listType = new GraphQLObjectType({\n name: `${graphqlTypeName(name)}List`,\n fields: {\n data: { type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(objType))), resolve: (r) => r.data },\n meta: { type: new GraphQLNonNull(PAGE_META), resolve: (r) => r.meta },\n },\n })\n queryFields[collectionQueryName(name)] = {\n type: new GraphQLNonNull(listType),\n args: { page: { type: GraphQLInt }, perPage: { type: GraphQLInt } },\n resolve: collectionResolver(name, buildFieldNameMap(tt.fields.map((f) => f.name))),\n }\n queryFields[singleQueryName(name)] = {\n type: objType,\n args: { id: { type: new GraphQLNonNull(GraphQLID) } },\n resolve: taxonomySingleResolver(name),\n }\n }\n\n return new GraphQLSchema({\n query: new GraphQLObjectType({ name: 'Query', fields: queryFields }),\n })\n}\n","import { GraphQLScalarType, Kind, valueFromASTUntyped } from 'graphql'\n\nexport const DateTimeScalar = new GraphQLScalarType({\n name: 'DateTime',\n description: 'ISO-8601 date-time string',\n serialize(value) {\n if (value instanceof Date) return value.toISOString()\n if (typeof value === 'string') return value\n if (typeof value === 'number') return new Date(value).toISOString()\n throw new TypeError(`DateTime cannot serialize value: ${String(value)}`)\n },\n parseValue(value) {\n if (typeof value !== 'string') throw new TypeError('DateTime must be a string')\n return value\n },\n parseLiteral(ast) {\n if (ast.kind !== Kind.STRING) throw new TypeError('DateTime must be a string')\n return ast.value\n },\n})\n\nexport const JSONScalar = new GraphQLScalarType({\n name: 'JSON',\n description: 'Arbitrary JSON value',\n serialize: (value) => value,\n parseValue: (value) => value,\n parseLiteral: (ast, variables) => valueFromASTUntyped(ast, variables ?? undefined),\n})\n","import {\n GraphQLString,\n GraphQLInt,\n GraphQLFloat,\n GraphQLBoolean,\n type GraphQLScalarType,\n} from 'graphql'\nimport type { FieldType } from '@bobbykim/manguito-cms-core'\nimport { DateTimeScalar, JSONScalar } from './scalars.js'\n\n// Scalar output type for a field. Relation/enum fields return null — schema.ts\n// resolves those against its type caches.\nexport function scalarOutputType(fieldType: FieldType): GraphQLScalarType | null {\n switch (fieldType) {\n case 'text/plain':\n case 'text/rich':\n return GraphQLString\n case 'integer':\n return GraphQLInt\n case 'float':\n return GraphQLFloat\n case 'boolean':\n return GraphQLBoolean\n case 'date':\n return DateTimeScalar\n case 'programmatic':\n return JSONScalar\n default:\n return null\n }\n}\n","// \"content--blog_post\" → \"blog_post\"; \"category\" → \"category\"\nexport function schemaSegment(machineName: string): string {\n const idx = machineName.indexOf('--')\n return idx === -1 ? machineName : machineName.slice(idx + 2)\n}\n\nfunction words(input: string): string[] {\n return input.split(/[_\\s-]+/).filter(Boolean)\n}\n\nexport function toPascalCase(input: string): string {\n return words(input)\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n .join('')\n}\n\nexport function toCamelCase(input: string): string {\n const pascal = toPascalCase(input)\n return pascal.charAt(0).toLowerCase() + pascal.slice(1)\n}\n\n// Deterministic English pluralization for the common cases; irregular plurals\n// (person→people) are a known limitation — an optional per-type override can be\n// added later if one is wrong.\nexport function pluralize(word: string): string {\n if (/[^aeiou]y$/.test(word)) return word.slice(0, -1) + 'ies'\n if (/(s|x|z|ch|sh)$/.test(word)) return word + 'es'\n return word + 's'\n}\n\nconst GRAPHQL_NAME = /^[_A-Za-z][_0-9A-Za-z]*$/\nexport function isValidGraphQLName(name: string): boolean {\n return GRAPHQL_NAME.test(name)\n}\n\nexport function graphqlTypeName(machineName: string): string {\n return toPascalCase(schemaSegment(machineName))\n}\n\nexport function singleQueryName(machineName: string): string {\n return toCamelCase(schemaSegment(machineName))\n}\n\nexport function collectionQueryName(machineName: string): string {\n return pluralize(singleQueryName(machineName))\n}\n\nexport function buildFieldNameMap(schemaNames: string[]): {\n toGraphql(schemaName: string): string\n toSchema(graphqlName: string): string\n} {\n const toG = new Map<string, string>()\n const toS = new Map<string, string>()\n for (const name of schemaNames) {\n const g = toCamelCase(name)\n toG.set(name, g)\n toS.set(g, name)\n }\n return {\n toGraphql: (schemaName) => toG.get(schemaName) ?? toCamelCase(schemaName),\n toSchema: (graphqlName) => toS.get(graphqlName) ?? graphqlName,\n }\n}\n","import {\n GraphQLEnumType,\n GraphQLInputObjectType,\n GraphQLList,\n GraphQLNonNull,\n GraphQLString,\n GraphQLBoolean,\n GraphQLInt,\n GraphQLFloat,\n type GraphQLInputType,\n} from 'graphql'\nimport type {\n ParsedContentType,\n ParsedTaxonomyType,\n FilterValue,\n FilterOperator,\n FieldType,\n} from '@bobbykim/manguito-cms-core'\nimport { graphqlTypeName, buildFieldNameMap } from './naming.js'\nimport { scalarOutputType } from './type-mapping.js'\nimport { DateTimeScalar } from './scalars.js'\n\nexport const SortOrderEnum = new GraphQLEnumType({\n name: 'SortOrder',\n values: { ASC: { value: 'asc' }, DESC: { value: 'desc' } },\n})\n\n// Only these system fields are sortable (mirrors the REST SORTABLE_FIELDS).\nconst SORTABLE: Array<{ gql: string; column: string }> = [\n { gql: 'title', column: 'title' },\n { gql: 'createdAt', column: 'created_at' },\n { gql: 'updatedAt', column: 'updated_at' },\n]\n\nexport function buildSortFieldEnum(typeName: string): GraphQLEnumType {\n return new GraphQLEnumType({\n name: `${typeName}SortField`,\n values: Object.fromEntries(SORTABLE.map((s) => [s.gql, { value: s.column }])),\n })\n}\n\n// A comparable filter input (eq/in/gt/gte/lt/lte) for a given scalar input type.\nfunction comparableFilterInput(name: string, scalar: GraphQLInputType): GraphQLInputObjectType {\n return new GraphQLInputObjectType({\n name,\n fields: {\n eq: { type: scalar },\n in: { type: new GraphQLList(new GraphQLNonNull(scalar)) },\n gt: { type: scalar },\n gte: { type: scalar },\n lt: { type: scalar },\n lte: { type: scalar },\n },\n })\n}\n\nfunction equalityFilterInput(name: string, scalar: GraphQLInputType): GraphQLInputObjectType {\n return new GraphQLInputObjectType({\n name,\n fields: {\n eq: { type: scalar },\n in: { type: new GraphQLList(new GraphQLNonNull(scalar)) },\n },\n })\n}\n\n// Shared per-scalar filter inputs (built once).\nconst StringFilter = equalityFilterInput('StringFilter', GraphQLString)\nconst BooleanFilter = new GraphQLInputObjectType({\n name: 'BooleanFilter',\n fields: { eq: { type: GraphQLBoolean } },\n})\nconst DateTimeFilter = comparableFilterInput('DateTimeFilter', DateTimeScalar)\nconst IDFilter = equalityFilterInput('IDFilter', GraphQLString)\nconst IntFilter = comparableFilterInput('IntFilter', GraphQLInt)\nconst FloatFilter = comparableFilterInput('FloatFilter', GraphQLFloat)\n\n// Choose a filter input for a field based on its scalar output type.\nfunction filterInputForField(fieldType: FieldType): GraphQLInputObjectType | null {\n const scalar = scalarOutputType(fieldType)\n if (fieldType === 'boolean') return BooleanFilter\n if (fieldType === 'date') return DateTimeFilter\n if (fieldType === 'integer') return IntFilter\n if (fieldType === 'float') return FloatFilter\n if (fieldType === 'reference') return IDFilter\n if (scalar === null) return null // only 'enum' reaches here — not filterable via a scalar input\n return StringFilter\n}\n\n// Build the <Type>Filter input. Programmatic fields are excluded (no column).\nexport function buildFilterInputType(\n type: ParsedContentType | ParsedTaxonomyType\n): GraphQLInputObjectType | null {\n const fields: Record<string, { type: GraphQLInputObjectType }> = {}\n\n // System fields that are filterable columns.\n fields['id'] = { type: IDFilter }\n fields['published'] = { type: BooleanFilter }\n fields['createdAt'] = { type: DateTimeFilter }\n fields['updatedAt'] = { type: DateTimeFilter }\n if (type.schema_type === 'content-type') fields['slug'] = { type: StringFilter }\n\n for (const f of type.fields) {\n if (f.field_type === 'programmatic' || f.field_type === 'paragraph') continue\n if (f.field_type === 'image' || f.field_type === 'video' || f.field_type === 'file') continue\n const input = filterInputForField(f.field_type)\n if (input) fields[buildFieldNameMap([f.name]).toGraphql(f.name)] = { type: input }\n }\n\n if (Object.keys(fields).length === 0) return null\n return new GraphQLInputObjectType({ name: `${graphqlTypeName(type.name)}Filter`, fields })\n}\n\n// Convert a GraphQL filter argument into the repository's filters map, keyed by\n// snake_case column. eq → scalar, in → array (OR/IN), gt/gte/lt/lte → operator.\nexport function translateFilters(\n input: Record<string, unknown> | undefined,\n nameMap: { toSchema(g: string): string }\n): Record<string, FilterValue> {\n const out: Record<string, FilterValue> = {}\n if (!input) return out\n\n for (const [gqlField, raw] of Object.entries(input)) {\n if (raw === null || typeof raw !== 'object') continue\n const column = nameMap.toSchema(gqlField)\n const spec = raw as Record<string, unknown>\n\n // eq/in take priority over range operators (gt/gte/lt/lte) when a client sends\n // both in one field filter — the range operators are silently dropped.\n if (spec['eq'] !== undefined) {\n out[column] = spec['eq'] as FilterValue\n } else if (Array.isArray(spec['in'])) {\n out[column] = spec['in'] as FilterValue\n } else {\n const op: FilterOperator = {}\n for (const k of ['gt', 'gte', 'lt', 'lte'] as const) {\n if (spec[k] !== undefined) op[k] = spec[k] as number | string\n }\n if (Object.keys(op).length > 0) out[column] = op\n }\n }\n return out\n}\n","import { GraphQLError } from 'graphql'\nimport type { GraphQLContext } from './context.js'\nimport { translateFilters } from './filters.js'\n\ntype Row = Record<string, unknown>\n\nfunction codeError(code: string, message: string): GraphQLError {\n return new GraphQLError(message, { extensions: { code } })\n}\n\nfunction isPublished(item: Row | null): boolean {\n return !!item && item['published'] === true\n}\n\nexport function scalarFieldResolver(schemaName: string) {\n return (parent: Row): unknown => parent[schemaName]\n}\n\nexport function relationFieldResolver(typeName: string, schemaFieldName: string) {\n return (parent: Row, _args: unknown, ctx: GraphQLContext): Promise<unknown> =>\n ctx.loaders.load(typeName, schemaFieldName, parent)\n}\n\nexport function programmaticFieldResolver(typeName: string, schemaFieldName: string) {\n return async (parent: Row, _args: unknown, ctx: GraphQLContext): Promise<unknown> => {\n let p = ctx.programmaticMemo.get(parent)\n if (!p) {\n p = ctx.resolver.resolveItem(typeName, parent)\n ctx.programmaticMemo.set(parent, p)\n }\n return (await p)[schemaFieldName]\n }\n}\n\ntype CollectionArgs = {\n page?: number\n perPage?: number\n sortBy?: string // already the snake_case column (enum internal value)\n sortOrder?: 'asc' | 'desc'\n filter?: Record<string, unknown>\n}\n\nexport function collectionResolver(\n typeName: string,\n nameMap: { toSchema(g: string): string }\n) {\n return async (_root: unknown, args: CollectionArgs, ctx: GraphQLContext) => {\n const page = args.page ?? 1\n const perPage = args.perPage ?? 10\n if (!Number.isInteger(page) || page < 1) {\n throw codeError('INVALID_PAGINATION', 'page must be ≥ 1')\n }\n if (!Number.isInteger(perPage) || perPage < 1 || perPage > 100) {\n throw codeError('INVALID_PAGINATION', 'perPage must be between 1 and 100')\n }\n const repo = ctx.repos[typeName]!\n const result = await repo.findMany({\n published_only: true,\n page,\n per_page: perPage,\n sort_by: (args.sortBy ?? 'created_at') as 'title' | 'created_at' | 'updated_at',\n sort_order: args.sortOrder ?? 'asc',\n filters: translateFilters(args.filter, nameMap),\n })\n return { data: result.data as Row[], meta: result.meta }\n }\n}\n\nexport function singleBySlugResolver(typeName: string) {\n return async (_root: unknown, args: { slug: string }, ctx: GraphQLContext): Promise<Row | null> => {\n const repo = ctx.repos[typeName]!\n const item = (await repo.findBySlug(args.slug)) as Row | null\n return isPublished(item) ? item : null\n }\n}\n\nexport function singletonResolver(typeName: string) {\n return async (_root: unknown, _args: unknown, ctx: GraphQLContext): Promise<Row | null> => {\n const repo = ctx.repos[typeName]!\n const result = await repo.findMany({ published_only: true, page: 1, per_page: 1 })\n return (result.data[0] as Row | undefined) ?? null\n }\n}\n\nexport function taxonomySingleResolver(typeName: string) {\n return async (_root: unknown, args: { id: string }, ctx: GraphQLContext): Promise<Row | null> => {\n const repo = ctx.repos[typeName]!\n const item = (await repo.findOne(args.id)) as Row | null\n return isPublished(item) ? item : null\n }\n}\n","import DataLoader from 'dataloader'\nimport type { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db'\nimport type {\n SchemaRegistry,\n ParsedContentType,\n ParsedTaxonomyType,\n ParsedParagraphType,\n} from '@bobbykim/manguito-cms-core'\nimport { buildRelationsMap, resolveRelationField, type RelationDef } from '../relations.js'\n\nexport interface RelationLoaders {\n load(typeName: string, fieldName: string, parent: Record<string, unknown>): Promise<unknown>\n}\n\ntype ParentRow = Record<string, unknown>\n\n// One RelationLoaders instance per request. Loaders batch sibling parents per\n// (type, field); the shared cache dedupes target lookups across all loaders in\n// the request, mirroring the repository's resolveRows cache.\nexport function createRelationLoaders(\n db: DrizzlePostgresInstance,\n registry: SchemaRegistry\n): RelationLoaders {\n const relMaps = new Map<string, Record<string, RelationDef>>()\n const loaders = new Map<string, DataLoader<ParentRow, unknown>>()\n const cache = new Map<string, unknown>()\n\n function relationsFor(typeName: string): Record<string, RelationDef> {\n let m = relMaps.get(typeName)\n if (!m) {\n const type = (registry.content_types[typeName] ??\n registry.taxonomy_types[typeName] ??\n registry.paragraph_types[typeName]) as\n | ParsedContentType\n | ParsedTaxonomyType\n | ParsedParagraphType\n | undefined\n m = type ? buildRelationsMap(type.fields, registry) : {}\n relMaps.set(typeName, m)\n }\n return m\n }\n\n function loaderFor(typeName: string, fieldName: string, rel: RelationDef): DataLoader<ParentRow, unknown> {\n const key = `${typeName}:${fieldName}`\n let dl = loaders.get(key)\n if (!dl) {\n dl = new DataLoader<ParentRow, unknown>(\n async (parents) => {\n const rows = parents as ParentRow[]\n // GraphQL is public-only (see app.ts) — always filter relation targets\n // to published rows, mirroring the REST public repos' publishedRelations.\n await resolveRelationField(db, rows, fieldName, rel, cache, true)\n return rows.map((r) => r[fieldName])\n },\n // Batch within a tick; do not memoize by parent identity (rows are mutated\n // and may recur across nesting levels).\n { cache: false }\n )\n loaders.set(key, dl)\n }\n return dl\n }\n\n return {\n async load(typeName, fieldName, parent) {\n const rel = relationsFor(typeName)[fieldName]\n if (!rel) return null\n return loaderFor(typeName, fieldName, rel).load(parent)\n },\n }\n}\n","import crypto from 'node:crypto'\nimport { sql } from 'drizzle-orm'\nimport type { ParsedField, ParsedParagraphType, SchemaRegistry } from '@bobbykim/manguito-cms-core'\nimport type { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db'\nimport type { MediaDelta } from './media-references.js'\n\n// ─── Relation persistence ─────────────────────────────────────────────────────\n//\n// Owns paragraph/junction/reference/media relation table SQL — both the write\n// side (persist/delete below) and the read side (descriptors + resolvers further\n// down). Each persist/delete returns the MediaDelta of media ids it gained and\n// lost (see media-references.ts), which the caller merges and reconciles once.\n// The repository's resolveRows() drives the read resolvers.\n\nfunction quoteIdent(name: string): string {\n if (!/^[a-z][a-z0-9_-]*$/.test(name)) throw new Error(`Unsafe identifier: ${name}`)\n return `\"${name}\"`\n}\n\n// Paragraph rows live on a child table with their own image/video/file columns\n// (e.g. a \"photo card\" block embeds an image) — these need the same reference-count\n// bookkeeping as top-level content fields, one level down.\nfunction paragraphMediaFieldNames(pType: ParsedParagraphType): Set<string> {\n return new Set(\n pType.fields\n .filter((f) => f.field_type === 'image' || f.field_type === 'video' || f.field_type === 'file')\n .map((f) => f.name)\n )\n}\n\nasync function fetchParagraphMediaIds(\n db: DrizzlePostgresInstance,\n pType: ParsedParagraphType,\n itemId: string,\n fieldName: string\n): Promise<string[]> {\n const mediaColumns = pType.fields\n .filter((f) => f.field_type === 'image' || f.field_type === 'video' || f.field_type === 'file')\n .map((f) => f.db_column!.column_name)\n if (mediaColumns.length === 0) return []\n\n const tbl = sql.raw(quoteIdent(pType.db.table_name))\n const cols = sql.join(mediaColumns.map((c) => sql.raw(quoteIdent(c))), sql`, `)\n const result = await db.execute(\n sql`SELECT ${cols} FROM ${tbl} WHERE parent_id = ${itemId} AND parent_field = ${fieldName}`\n )\n\n const ids: string[] = []\n for (const row of result.rows as Record<string, unknown>[]) {\n for (const col of mediaColumns) {\n const v = row[col]\n if (typeof v === 'string' && v !== '') ids.push(v)\n }\n }\n return ids\n}\n\n// Replaces every paragraph row for one field of one parent (delete + reinsert),\n// returning the media ids removed (old rows) and added (new rows).\nexport async function persistParagraphField(\n db: DrizzlePostgresInstance,\n itemId: string,\n parentType: string,\n fieldName: string,\n pType: ParsedParagraphType,\n items: unknown[]\n): Promise<MediaDelta> {\n const removed = await fetchParagraphMediaIds(db, pType, itemId, fieldName)\n\n const tbl = sql.raw(quoteIdent(pType.db.table_name))\n await db.execute(sql`DELETE FROM ${tbl} WHERE parent_id = ${itemId} AND parent_field = ${fieldName}`)\n\n const mediaFieldNames = paragraphMediaFieldNames(pType)\n const added: string[] = []\n\n for (let i = 0; i < items.length; i++) {\n const pItem = items[i] as Record<string, unknown>\n const data: Record<string, unknown> = {\n id: crypto.randomUUID(),\n parent_id: itemId,\n parent_type: parentType,\n parent_field: fieldName,\n order: i,\n created_at: new Date(),\n updated_at: new Date(),\n }\n for (const pf of pType.fields) {\n if (!pf.db_column || pf.db_column.junction) continue\n data[pf.db_column.column_name] = pItem[pf.name] ?? null\n }\n const cols = sql.join(Object.keys(data).map((k) => sql.raw(quoteIdent(k))), sql`, `)\n const vals = sql.join(Object.values(data).map((v) => sql`${v}`), sql`, `)\n await db.execute(sql`INSERT INTO ${tbl} (${cols}) VALUES (${vals})`)\n\n for (const name of mediaFieldNames) {\n const val = pItem[name]\n if (typeof val === 'string' && val !== '') added.push(val)\n }\n }\n\n return { added, removed }\n}\n\n// Deletes all paragraph rows for one field of one parent (used when the parent is\n// being deleted — paragraph rows have no FK/cascade back to their parent, so they'd\n// leak otherwise). Returns the media those rows referenced, all removed.\nexport async function deleteParagraphField(\n db: DrizzlePostgresInstance,\n itemId: string,\n fieldName: string,\n pType: ParsedParagraphType\n): Promise<MediaDelta> {\n const removed = await fetchParagraphMediaIds(db, pType, itemId, fieldName)\n const tbl = sql.raw(quoteIdent(pType.db.table_name))\n await db.execute(sql`DELETE FROM ${tbl} WHERE parent_id = ${itemId} AND parent_field = ${fieldName}`)\n return { added: [], removed }\n}\n\n// Replaces every junction row for one many-to-many field of one parent.\nexport async function persistJunctionField(\n db: DrizzlePostgresInstance,\n itemId: string,\n junction: { table_name: string; left_column: string; right_column: string },\n relatedIds: string[]\n): Promise<void> {\n const tbl = sql.raw(quoteIdent(junction.table_name))\n const left = sql.raw(quoteIdent(junction.left_column))\n const right = sql.raw(quoteIdent(junction.right_column))\n await db.execute(sql`DELETE FROM ${tbl} WHERE ${left} = ${itemId}`)\n for (const rid of relatedIds) {\n await db.execute(sql`INSERT INTO ${tbl} (${left}, ${right}) VALUES (${itemId}, ${rid})`)\n }\n}\n\n// ─── Relation descriptors and resolution (read side) ──────────────────────────\n//\n// Relation read resolution. These were lifted out of the repository so that all\n// paragraph/junction/reference/media table SQL lives in one module alongside the\n// write persistence above. The repository's resolveRows() calls into here.\n\nexport type ParagraphRelationDef = {\n type: 'paragraph'\n table: string\n}\n\nexport type ReferenceRelationDef = {\n type: 'reference'\n table: string\n fk_column: string\n}\n\nexport type JunctionRelationDef = {\n type: 'junction'\n table: string\n junction_table: string\n left_column: string\n right_column: string\n // Only ordered junctions have an \"order\" column (db codegen emits it only when\n // order_column is true) — the resolver must not ORDER BY it otherwise.\n order_column: boolean\n}\n\nexport type MediaRelationDef = {\n type: 'media'\n fk_column: string\n}\n\nexport type RelationDef =\n | ParagraphRelationDef\n | ReferenceRelationDef\n | JunctionRelationDef\n | MediaRelationDef\n\n// Derives a content type's relations map from its parsed fields, so paragraph/\n// reference/media fields are actually resolved instead of silently dropped.\n// Junction and foreign_key info is already on each field's db_column; paragraph\n// fields store their target table on the paragraph type, hence the registry.\nexport function buildRelationsMap(\n fields: ParsedField[],\n registry: SchemaRegistry\n): Record<string, RelationDef> {\n const relations: Record<string, RelationDef> = {}\n\n for (const field of fields) {\n if (field.field_type === 'image' || field.field_type === 'video' || field.field_type === 'file') {\n if (!field.db_column) continue\n relations[field.name] = { type: 'media', fk_column: field.db_column.column_name }\n } else if (field.field_type === 'paragraph') {\n const ref = (field.ui_component as { ref?: string }).ref\n const pType = ref ? registry.paragraph_types[ref] : undefined\n if (!pType) continue\n relations[field.name] = { type: 'paragraph', table: pType.db.table_name }\n } else if (field.field_type === 'reference') {\n if (!field.db_column) continue\n if (field.db_column.junction) {\n const j = field.db_column.junction\n relations[field.name] = {\n type: 'junction',\n table: j.right_table,\n junction_table: j.table_name,\n left_column: j.left_column,\n right_column: j.right_column,\n order_column: j.order_column,\n }\n } else if (field.db_column.foreign_key) {\n relations[field.name] = {\n type: 'reference',\n table: field.db_column.foreign_key.table,\n fk_column: field.db_column.column_name,\n }\n }\n }\n }\n\n return relations\n}\n\nfunction groupBy<T extends Record<string, unknown>>(\n items: T[],\n key: string\n): Record<string, T[]> {\n const result: Record<string, T[]> = {}\n for (const item of items) {\n const k = String(item[key])\n if (!result[k]) result[k] = []\n result[k]!.push(item)\n }\n return result\n}\n\n// Fully resolves a relation field on a batch of rows: paragraph → ordered rows,\n// reference → the target row, junction → target rows, media → the media row.\n// The cache (keyed table:id) dedupes target lookups within a request.\n//\n// `publishedOnly` (default false, preserving prior behavior for admin callers)\n// adds `AND published = true` to the reference/junction target-row SELECTs.\n// Reference and junction targets are always content or taxonomy tables, which\n// always carry a `published` boolean system field — see CONTENT_SYSTEM_FIELDS /\n// TAXONOMY_SYSTEM_FIELDS in packages/core/src/parser/parseSchema.ts. Paragraph\n// and media targets are deliberately excluded: paragraph rows are owned by\n// their parent (no independent publish state) and media rows have no\n// `published` column at all.\nexport async function resolveRelationField(\n db: DrizzlePostgresInstance,\n rows: Record<string, unknown>[],\n fieldName: string,\n rel: RelationDef,\n cache: Map<string, unknown>,\n publishedOnly = false\n): Promise<void> {\n if (rows.length === 0) return\n\n if (rel.type === 'paragraph') {\n const parentIds = rows.map((r) => r['id'] as string)\n const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `)\n const result = await db.execute(\n sql`SELECT * FROM ${sql.raw(quoteIdent(rel.table))} WHERE parent_id IN (${inList}) ORDER BY \"order\" ASC`\n )\n const byParent = groupBy(result.rows as Record<string, unknown>[], 'parent_id')\n for (const row of rows) {\n row[fieldName] = byParent[row['id'] as string] ?? []\n }\n } else if (rel.type === 'reference') {\n const fkValues = rows.map((r) => r[rel.fk_column] as string).filter(Boolean)\n if (fkValues.length === 0) {\n for (const row of rows) row[fieldName] = null\n return\n }\n const unique = [...new Set(fkValues)]\n const uncached = unique.filter((id) => !cache.has(`${rel.table}:${id}`))\n if (uncached.length > 0) {\n const inList = sql.join(uncached.map((id) => sql`${id}`), sql`, `)\n const publishedCond = publishedOnly ? sql` AND published = true` : sql``\n const result = await db.execute(\n sql`SELECT * FROM ${sql.raw(quoteIdent(rel.table))} WHERE id IN (${inList})${publishedCond}`\n )\n for (const item of result.rows as Record<string, unknown>[]) {\n cache.set(`${rel.table}:${item['id']}`, item)\n }\n }\n for (const row of rows) {\n const fkVal = row[rel.fk_column] as string\n row[fieldName] = fkVal ? (cache.get(`${rel.table}:${fkVal}`) ?? null) : null\n }\n } else if (rel.type === 'junction') {\n const parentIds = rows.map((r) => r['id'] as string)\n const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `)\n const orderBy = rel.order_column ? sql` ORDER BY \"order\" ASC` : sql``\n const junctionResult = await db.execute(\n sql`SELECT * FROM ${sql.raw(quoteIdent(rel.junction_table))} WHERE ${sql.raw(quoteIdent(rel.left_column))} IN (${inList})${orderBy}`\n )\n const jRows = junctionResult.rows as Record<string, unknown>[]\n const rightIds = [...new Set(jRows.map((r) => r[rel.right_column] as string))]\n const uncached = rightIds.filter((id) => !cache.has(`${rel.table}:${id}`))\n if (uncached.length > 0) {\n const rightInList = sql.join(uncached.map((id) => sql`${id}`), sql`, `)\n const publishedCond = publishedOnly ? sql` AND published = true` : sql``\n const entitiesResult = await db.execute(\n sql`SELECT * FROM ${sql.raw(quoteIdent(rel.table))} WHERE id IN (${rightInList})${publishedCond}`\n )\n for (const item of entitiesResult.rows as Record<string, unknown>[]) {\n cache.set(`${rel.table}:${item['id']}`, item)\n }\n }\n const byLeft = groupBy(jRows, rel.left_column)\n for (const row of rows) {\n const jEntries = byLeft[row['id'] as string] ?? []\n row[fieldName] = jEntries\n .map((jr) => cache.get(`${rel.table}:${jr[rel.right_column] as string}`))\n .filter(Boolean)\n }\n } else if (rel.type === 'media') {\n const fkValues = rows.map((r) => r[rel.fk_column] as string).filter(Boolean)\n if (fkValues.length === 0) {\n for (const row of rows) row[fieldName] = null\n return\n }\n const unique = [...new Set(fkValues)]\n const uncached = unique.filter((id) => !cache.has(`media:${id}`))\n if (uncached.length > 0) {\n const inList = sql.join(uncached.map((id) => sql`${id}`), sql`, `)\n const result = await db.execute(sql`SELECT * FROM \"media\" WHERE id IN (${inList})`)\n for (const item of result.rows as Record<string, unknown>[]) {\n cache.set(`media:${item['id']}`, item)\n }\n }\n for (const row of rows) {\n const fkVal = row[rel.fk_column] as string\n row[fieldName] = fkVal ? (cache.get(`media:${fkVal}`) ?? null) : null\n }\n }\n}\n\n// Populates paragraph/junction fields with bare ID arrays when they are not\n// explicitly included — they have no column on the owning table, so without this\n// they'd be silently absent. Reference fields already carry their bare ID as a\n// real column from `SELECT *`, so they need no extra query.\nexport async function resolveRelationBareIds(\n db: DrizzlePostgresInstance,\n rows: Record<string, unknown>[],\n fieldName: string,\n rel: RelationDef\n): Promise<void> {\n if (rows.length === 0) return\n\n if (rel.type === 'paragraph') {\n const parentIds = rows.map((r) => r['id'] as string)\n const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `)\n const result = await db.execute(\n sql`SELECT id, parent_id FROM ${sql.raw(quoteIdent(rel.table))} WHERE parent_id IN (${inList}) ORDER BY \"order\" ASC`\n )\n const byParent = groupBy(result.rows as Record<string, unknown>[], 'parent_id')\n for (const row of rows) {\n row[fieldName] = (byParent[row['id'] as string] ?? []).map((r) => r['id'])\n }\n } else if (rel.type === 'junction') {\n const parentIds = rows.map((r) => r['id'] as string)\n const inList = sql.join(parentIds.map((id) => sql`${id}`), sql`, `)\n const result = await db.execute(\n sql`SELECT ${sql.raw(quoteIdent(rel.left_column))} AS left_id, ${sql.raw(quoteIdent(rel.right_column))} AS right_id FROM ${sql.raw(quoteIdent(rel.junction_table))} WHERE ${sql.raw(quoteIdent(rel.left_column))} IN (${inList})`\n )\n const byLeft = groupBy(result.rows as Record<string, unknown>[], 'left_id')\n for (const row of rows) {\n row[fieldName] = (byLeft[row['id'] as string] ?? []).map((r) => r['right_id'])\n }\n }\n}\n","import { EnvelopArmor } from '@escape.tech/graphql-armor'\nimport { NoSchemaIntrospectionCustomRule } from 'graphql'\n\n// Depth + cost + alias + directive limits via GraphQL Armor. Returns { plugins } to\n// spread into Yoga's `plugins` array.\n//\n// Note: the installed `@escape.tech/graphql-armor` (3.2.0) exports `EnvelopArmor`, a\n// class whose `.protect()` returns `{ plugins }` — the standalone `EnvelopArmorPlugin`\n// export is a function that returns a single plugin, not a class with `.protect()`.\nexport function buildArmorPlugin(options: { maxDepth: number; maxComplexity: number }): {\n plugins: unknown[]\n} {\n const armor = new EnvelopArmor({\n maxDepth: { n: options.maxDepth },\n costLimit: { maxCost: options.maxComplexity },\n maxAliases: { n: 15 },\n maxDirectives: { n: 50 },\n blockFieldSuggestion: { enabled: true },\n // maxTokens is intentionally left at Armor's secure default (n=1000) to bound\n // GraphQL document token count at parse time, a layer the depth/cost/alias/directive\n // limits do not cover.\n })\n return armor.protect()\n}\n\n// Disables introspection (and the __schema/__type meta-fields) in production by\n// adding graphql's built-in validation rule when `enabled` is false.\nexport function introspectionPlugin(enabled: boolean): {\n onValidate(payload: { addValidationRule: (rule: unknown) => void }): void\n} {\n return {\n onValidate({ addValidationRule }) {\n if (!enabled) addValidationRule(NoSchemaIntrospectionCustomRule)\n },\n }\n}\n"],"mappings":"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","names":["import_graphql","import_graphql","import_graphql","import_graphql","DataLoader","import_graphql"]}
@@ -0,0 +1,8 @@
1
+ import {
2
+ createGraphQLHandler
3
+ } from "../chunk-QXMW74MR.mjs";
4
+ import "../chunk-FBS2RQI3.mjs";
5
+ export {
6
+ createGraphQLHandler
7
+ };
8
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,8 @@
1
+ import {
2
+ createGraphQLHandler
3
+ } from "./chunk-QXMW74MR.mjs";
4
+ import "./chunk-FBS2RQI3.mjs";
5
+ export {
6
+ createGraphQLHandler
7
+ };
8
+ //# sourceMappingURL=handler-GMCFYZY5.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/dist/index.d.mts CHANGED
@@ -1,18 +1,8 @@
1
- import { ProgrammaticFieldDefinition, SchemaRegistry, StorageAdapter, ResolvedRateLimitConfig, CorsConfig, ServerAdapter, APIAdapter } from '@bobbykim/manguito-cms-core';
1
+ import { StorageAdapter, SchemaRegistry, ResolvedRateLimitConfig, CorsConfig, ResolvedGraphQLConfig, ServerAdapter, GraphQLModuleConfig, APIAdapter } from '@bobbykim/manguito-cms-core';
2
2
  import { Hono } from 'hono';
3
3
  import { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db';
4
-
5
- type ResolverMap = Map<string, ProgrammaticFieldDefinition>;
6
- declare function resolverKey(schema: string, field: string): string;
7
- declare function validateResolverBindings(registry: SchemaRegistry, resolvers: ResolverMap): void;
8
- declare function createProgrammaticResolver(resolvers: ResolverMap, options?: {
9
- onError?: (message: string) => void;
10
- }): {
11
- resolveItem: (schema: string, row: Record<string, unknown>) => Promise<Record<string, unknown>>;
12
- resolveList: (schema: string, rows: Record<string, unknown>[]) => Promise<Record<string, unknown>[]>;
13
- hasSchema: (schema: string) => boolean;
14
- };
15
- type ProgrammaticResolver = ReturnType<typeof createProgrammaticResolver>;
4
+ import { R as ResolverMap } from './resolve-Dtsk27aI.mjs';
5
+ export { P as ProgrammaticResolver, c as createProgrammaticResolver, r as resolverKey, v as validateResolverBindings } from './resolve-Dtsk27aI.mjs';
16
6
 
17
7
  type CreateCmsAppOptions = {
18
8
  /** CMS display name shown in GET /admin/api/config. Defaults to 'Manguito CMS'. */
@@ -29,6 +19,8 @@ type CreateCmsAppOptions = {
29
19
  cors?: CorsConfig;
30
20
  /** Programmatic field resolvers, keyed `${schema}::${field}`. */
31
21
  resolvers?: ResolverMap;
22
+ /** GraphQL module config (resolved). When enabled, mounts POST /graphql. */
23
+ graphql?: ResolvedGraphQLConfig;
32
24
  };
33
25
  interface ManguitoCmsAPIAdapter {
34
26
  readonly prefix: string;
@@ -49,7 +41,8 @@ type APIAdapterOptions = {
49
41
  max_file_size?: number;
50
42
  };
51
43
  rateLimit?: ResolvedRateLimitConfig;
44
+ graphql?: GraphQLModuleConfig;
52
45
  };
53
46
  declare function createAPIAdapter(options?: APIAdapterOptions): APIAdapter;
54
47
 
55
- export { type APIAdapterOptions, type CreateCmsAppOptions, type NodeServerOptions, type ProgrammaticResolver, type ResolverMap, createAPIAdapter, createCmsApp, createProgrammaticResolver, createServer, resolverKey, validateResolverBindings };
48
+ export { type APIAdapterOptions, type CreateCmsAppOptions, type NodeServerOptions, ResolverMap, createAPIAdapter, createCmsApp, createServer };
package/dist/index.d.ts CHANGED
@@ -1,18 +1,8 @@
1
- import { ProgrammaticFieldDefinition, SchemaRegistry, StorageAdapter, ResolvedRateLimitConfig, CorsConfig, ServerAdapter, APIAdapter } from '@bobbykim/manguito-cms-core';
1
+ import { StorageAdapter, SchemaRegistry, ResolvedRateLimitConfig, CorsConfig, ResolvedGraphQLConfig, ServerAdapter, GraphQLModuleConfig, APIAdapter } from '@bobbykim/manguito-cms-core';
2
2
  import { Hono } from 'hono';
3
3
  import { DrizzlePostgresInstance } from '@bobbykim/manguito-cms-db';
4
-
5
- type ResolverMap = Map<string, ProgrammaticFieldDefinition>;
6
- declare function resolverKey(schema: string, field: string): string;
7
- declare function validateResolverBindings(registry: SchemaRegistry, resolvers: ResolverMap): void;
8
- declare function createProgrammaticResolver(resolvers: ResolverMap, options?: {
9
- onError?: (message: string) => void;
10
- }): {
11
- resolveItem: (schema: string, row: Record<string, unknown>) => Promise<Record<string, unknown>>;
12
- resolveList: (schema: string, rows: Record<string, unknown>[]) => Promise<Record<string, unknown>[]>;
13
- hasSchema: (schema: string) => boolean;
14
- };
15
- type ProgrammaticResolver = ReturnType<typeof createProgrammaticResolver>;
4
+ import { R as ResolverMap } from './resolve-Dtsk27aI.js';
5
+ export { P as ProgrammaticResolver, c as createProgrammaticResolver, r as resolverKey, v as validateResolverBindings } from './resolve-Dtsk27aI.js';
16
6
 
17
7
  type CreateCmsAppOptions = {
18
8
  /** CMS display name shown in GET /admin/api/config. Defaults to 'Manguito CMS'. */
@@ -29,6 +19,8 @@ type CreateCmsAppOptions = {
29
19
  cors?: CorsConfig;
30
20
  /** Programmatic field resolvers, keyed `${schema}::${field}`. */
31
21
  resolvers?: ResolverMap;
22
+ /** GraphQL module config (resolved). When enabled, mounts POST /graphql. */
23
+ graphql?: ResolvedGraphQLConfig;
32
24
  };
33
25
  interface ManguitoCmsAPIAdapter {
34
26
  readonly prefix: string;
@@ -49,7 +41,8 @@ type APIAdapterOptions = {
49
41
  max_file_size?: number;
50
42
  };
51
43
  rateLimit?: ResolvedRateLimitConfig;
44
+ graphql?: GraphQLModuleConfig;
52
45
  };
53
46
  declare function createAPIAdapter(options?: APIAdapterOptions): APIAdapter;
54
47
 
55
- export { type APIAdapterOptions, type CreateCmsAppOptions, type NodeServerOptions, type ProgrammaticResolver, type ResolverMap, createAPIAdapter, createCmsApp, createProgrammaticResolver, createServer, resolverKey, validateResolverBindings };
48
+ export { type APIAdapterOptions, type CreateCmsAppOptions, type NodeServerOptions, ResolverMap, createAPIAdapter, createCmsApp, createServer };