@loradb/lora-graphql 0.19.0 → 0.20.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sources":["../src/migrate.ts","../src/cli.ts"],"sourcesContent":["// `lora-graphql migrate neo4j`: rewrite an @neo4j/graphql SDL into this\n// package's vocabulary. What maps is rewritten; what does not is removed\n// and listed as a TODO. With the client's operations, the opt-in surface\n// (@mutation, @filterable, @sortable) follows observed usage; without\n// them, mutations stay on for every type (as in @neo4j/graphql).\n\nimport {\n Kind,\n parse,\n print,\n visit,\n type ArgumentNode,\n type ConstArgumentNode,\n type ConstDirectiveNode,\n type ConstValueNode,\n type DocumentNode,\n type FieldDefinitionNode,\n type ObjectTypeDefinitionNode,\n type SelectionSetNode,\n type ValueNode,\n} from \"graphql\";\nimport { defaultPlural } from \"./model/build.js\";\n\nexport interface MigrationResult {\n /** The rewritten SDL. */\n typeDefs: string;\n /** What has no equivalent or needs a decision, by type and field. */\n todos: string[];\n}\n\nconst ROOT = new Set([\"Query\", \"Mutation\", \"Subscription\"]);\n\n/** Removed with a TODO: no equivalent here. */\nconst UNSUPPORTED: Record<string, string> = {\n coalesce:\n \"@coalesce is not supported: it hides predicates from indexes; store a value instead\",\n subscriptionsAuthorization:\n \"@subscriptionsAuthorization is not supported: SUBSCRIBE rules go in @authorization\",\n shareable: \"federation (@shareable) is not supported\",\n external: \"federation (@external) is not supported\",\n requires: \"federation (@requires) is not supported\",\n provides: \"federation (@provides) is not supported\",\n exclude: \"@exclude: use @query / @mutation to choose operations\",\n vector:\n \"@vector on types (with a provider) is not supported: put @vector(dimensions:, similarity:) on a [Float!] field\",\n};\n\n/** neo4j 5 filter suffixes → @filterable operators. */\nconst SUFFIX_OPS: Array<[string, string]> = [\n [\"_NOT_IN\", \"IN\"],\n [\"_STARTS_WITH\", \"STARTS_WITH\"],\n [\"_ENDS_WITH\", \"ENDS_WITH\"],\n [\"_CONTAINS\", \"CONTAINS\"],\n [\"_INCLUDES\", \"INCLUDES\"],\n [\"_GTE\", \"GTE\"],\n [\"_LTE\", \"LTE\"],\n [\"_GT\", \"GT\"],\n [\"_LT\", \"LT\"],\n [\"_IN\", \"IN\"],\n [\"_EQ\", \"EQ\"],\n];\n\n/** neo4j 6 nested filter operators → @filterable operators. */\nconst NESTED_OPS: Record<string, string> = {\n eq: \"EQ\",\n in: \"IN\",\n lt: \"LT\",\n lte: \"LTE\",\n gt: \"GT\",\n gte: \"GTE\",\n contains: \"CONTAINS\",\n startsWith: \"STARTS_WITH\",\n endsWith: \"ENDS_WITH\",\n includes: \"INCLUDES\",\n caseInsensitive: \"CASE_INSENSITIVE\",\n};\n\ninterface Usage {\n filters: Map<string, Map<string, Set<string>>>;\n sorts: Map<string, Set<string>>;\n mutations: Map<string, Set<string>>;\n}\n\nexport function migrateNeo4j(\n sdl: string,\n operations: DocumentNode[] = [],\n): MigrationResult {\n const doc = parse(sdl, { noLocation: true });\n const todos: string[] = [];\n const nodeTypes = doc.definitions.filter(\n (d): d is ObjectTypeDefinitionNode =>\n d.kind === Kind.OBJECT_TYPE_DEFINITION &&\n !ROOT.has(d.name.value) &&\n !has(d.directives, \"relationshipProperties\") &&\n !has(d.directives, \"jwt\"),\n );\n const nodeNames = new Set(nodeTypes.map((t) => t.name.value));\n const usage =\n operations.length > 0 ? observe(operations, nodeTypes) : undefined;\n\n const out = visit(doc, {\n ObjectTypeDefinition(t) {\n if (!nodeNames.has(t.name.value)) return undefined;\n const type = t.name.value;\n let directives = migrateDirectives(\n t.directives ?? [],\n type,\n undefined,\n todos,\n );\n if (!has(directives, \"node\"))\n directives = [...directives, directive(\"node\")];\n const ops = usage?.mutations.get(type);\n if (!has(directives, \"mutation\")) {\n if (!usage) {\n directives = [...directives, directive(\"mutation\")];\n todos.push(\n `${type}: @mutation added (@neo4j/graphql generates mutations for every type); remove it or narrow its operations`,\n );\n } else if (ops && ops.size > 0) {\n directives = [\n ...directives,\n directive(\"mutation\", {\n operations: enumList([...ops].sort()),\n }),\n ];\n }\n }\n const idFields = (t.fields ?? []).filter((f) => has(f.directives, \"id\"));\n if (idFields.length === 0) {\n todos.push(\n `${type}: no @id field; add a required @key field (the sort tie-breaker and mutation address)`,\n );\n } else if (idFields.length > 1) {\n todos.push(`${type}: several @id fields; keep one as the @key`);\n }\n const fields = (t.fields ?? []).map((f) =>\n migrateField(f, type, usage, todos),\n );\n return { ...t, directives, fields };\n },\n });\n return { typeDefs: print(out), todos };\n}\n\nfunction migrateField(\n f: FieldDefinitionNode,\n type: string,\n usage: Usage | undefined,\n todos: string[],\n): FieldDefinitionNode {\n let directives = migrateDirectives(\n f.directives ?? [],\n type,\n f.name.value,\n todos,\n );\n const id = directives.find((d) => d.name.value === \"id\");\n if (id) {\n directives = directives.filter(\n (d) => d.name.value !== \"id\" && d.name.value !== \"unique\",\n );\n directives.push(directive(\"key\", { generate: bool(true) }));\n }\n const used = usage?.filters.get(type)?.get(f.name.value);\n if (used && used.size > 0 && !has(directives, \"filterable\")) {\n directives.push(\n has(directives, \"relationship\")\n ? directive(\"filterable\")\n : directive(\"filterable\", { byValue: enumList([...used].sort()) }),\n );\n }\n if (\n usage?.sorts.get(type)?.has(f.name.value) &&\n !has(directives, \"sortable\")\n ) {\n directives.push(directive(\"sortable\"));\n }\n return { ...f, directives };\n}\n\nfunction migrateDirectives(\n directives: readonly ConstDirectiveNode[],\n type: string,\n field: string | undefined,\n todos: string[],\n): ConstDirectiveNode[] {\n const at = field ? `${type}.${field}` : type;\n const out: ConstDirectiveNode[] = [];\n for (const d of directives) {\n const name = d.name.value;\n if (name in UNSUPPORTED) {\n todos.push(`${at}: ${UNSUPPORTED[name]}`);\n continue;\n }\n if (name === \"key\" && !field) {\n todos.push(`${at}: federation @key(fields:) is not supported`);\n continue;\n }\n if (name === \"unique\") {\n out.push(directive(\"unique\"));\n continue;\n }\n if (name === \"relationship\") {\n out.push(migrateRelationship(d, at, todos));\n continue;\n }\n if (name === \"fulltext\") {\n out.push(renameInList(d, \"indexes\", \"indexName\", \"name\"));\n continue;\n }\n if (name === \"subscription\") {\n out.push(migrateSubscription(d, at, todos));\n continue;\n }\n if (name === \"authorization\" || name === \"authentication\") {\n todos.push(\n `${at}: check the @${name} rule's filters: operators are written { field: { eq: ... } } here`,\n );\n }\n out.push(d);\n }\n return out;\n}\n\nfunction migrateRelationship(\n d: ConstDirectiveNode,\n at: string,\n todos: string[],\n): ConstDirectiveNode {\n const args: ConstArgumentNode[] = [];\n for (const a of d.arguments ?? []) {\n const name = a.name.value;\n if (name === \"queryDirection\" && a.value.kind === Kind.ENUM) {\n if (a.value.value.includes(\"UNDIRECTED\")) {\n args.push(arg(\"queryDirection\", enumValue(\"UNDIRECTED\")));\n }\n continue;\n }\n if (name === \"nestedOperations\" && a.value.kind === Kind.LIST) {\n const kept = a.value.values.filter(\n (v) => v.kind === Kind.ENUM && v.value !== \"CONNECT_OR_CREATE\",\n );\n if (kept.length < a.value.values.length) {\n todos.push(`${at}: connectOrCreate is not supported; use upsert`);\n }\n args.push(arg(\"nestedOperations\", { kind: Kind.LIST, values: kept }));\n continue;\n }\n args.push(a);\n }\n return { ...d, arguments: args };\n}\n\nfunction migrateSubscription(\n d: ConstDirectiveNode,\n at: string,\n todos: string[],\n): ConstDirectiveNode {\n const events = d.arguments?.find((a) => a.name.value === \"events\")?.value;\n if (!events || events.kind !== Kind.LIST) return d;\n const ops = new Set<string>();\n let relationships = false;\n for (const e of events.values) {\n if (e.kind !== Kind.ENUM) continue;\n if (e.value.startsWith(\"CREATED\") && !e.value.includes(\"RELATIONSHIP\"))\n ops.add(\"CREATE\");\n else if (e.value.startsWith(\"UPDATED\")) ops.add(\"UPDATE\");\n else if (e.value.startsWith(\"DELETED\") && !e.value.includes(\"RELATIONSHIP\"))\n ops.add(\"DELETE\");\n else if (e.value.includes(\"RELATIONSHIP\")) relationships = true;\n }\n todos.push(\n `${at}: subscriptions see writes made through this library (or the engine change feed), not CDC`,\n );\n return directive(\"subscription\", {\n operations: enumList([...ops].sort()),\n ...(relationships ? { relationships: bool(true) } : {}),\n });\n}\n\n/** What the client's operations use: filters, sorts and mutations per type. */\nfunction observe(\n operations: DocumentNode[],\n types: ObjectTypeDefinitionNode[],\n): Usage {\n const usage: Usage = {\n filters: new Map(),\n sorts: new Map(),\n mutations: new Map(),\n };\n // Root fields as either naming would call them: this package's plural\n // and @neo4j/graphql's (which knows irregular plurals, e.g. people).\n const byPlural = new Map<string, ObjectTypeDefinitionNode>();\n for (const t of types) {\n byPlural.set(defaultPlural(t.name.value), t);\n byPlural.set(neo4jPlural(t.name.value), t);\n }\n const byName = new Map(types.map((t) => [t.name.value, t]));\n const note = (type: string, field: string, op: string) => {\n const fields = usage.filters.get(type) ?? new Map<string, Set<string>>();\n const ops = fields.get(field) ?? new Set<string>();\n ops.add(op);\n fields.set(field, ops);\n usage.filters.set(type, fields);\n };\n const where = (type: ObjectTypeDefinitionNode, value: ValueNode) => {\n if (value.kind !== Kind.OBJECT) return;\n for (const f of value.fields) {\n const key = f.name.value;\n if (key === \"AND\" || key === \"OR\") {\n if (f.value.kind === Kind.LIST)\n for (const v of f.value.values) where(type, v);\n continue;\n }\n if (key === \"NOT\") {\n where(type, f.value);\n continue;\n }\n // neo4j 5 relationship quantifiers: `actors_SOME: { name_EQ: ... }`.\n const quantifier = /^(.+)_(SOME|ALL|NONE|SINGLE)$/.exec(key);\n if (quantifier) {\n const rel = type.fields?.find((x) => x.name.value === quantifier[1]);\n const target = rel && byName.get(namedTypeOf(rel));\n if (rel && target) {\n note(type.name.value, quantifier[1]!, \"EQ\");\n where(target, f.value);\n }\n continue;\n }\n const suffix = SUFFIX_OPS.find(([s]) => key.endsWith(s));\n const fieldName = suffix ? key.slice(0, -suffix[0].length) : key;\n const def = type.fields?.find((x) => x.name.value === fieldName);\n if (!def) continue;\n const target = byName.get(namedTypeOf(def));\n if (target) {\n note(type.name.value, fieldName, \"EQ\");\n // `some` / `all` / `none` / `single`, or a nested where.\n if (f.value.kind === Kind.OBJECT) {\n for (const inner of f.value.fields) where(target, inner.value);\n where(target, f.value);\n }\n continue;\n }\n if (suffix) note(type.name.value, fieldName, suffix[1]);\n else if (f.value.kind === Kind.OBJECT) {\n for (const op of f.value.fields) {\n const mapped = NESTED_OPS[op.name.value];\n if (mapped) note(type.name.value, fieldName, mapped);\n }\n } else note(type.name.value, fieldName, \"EQ\");\n }\n };\n const walk = (set: SelectionSetNode | undefined, root: boolean) => {\n for (const sel of set?.selections ?? []) {\n if (sel.kind !== Kind.FIELD) continue;\n const name = sel.name.value;\n if (root) {\n const mutation = /^(create|update|delete|upsert)(.+)$/.exec(name);\n const plural = name.replace(/(Connection|Aggregate)$/, \"\");\n const type =\n byPlural.get(plural) ??\n (mutation ? byPlural.get(lowerFirst(mutation[2]!)) : undefined);\n if (type && mutation) {\n const ops = usage.mutations.get(type.name.value) ?? new Set<string>();\n const op = mutation[1]!.toUpperCase();\n if (op === \"UPSERT\") {\n ops.add(\"CREATE\");\n ops.add(\"UPDATE\");\n } else ops.add(op);\n usage.mutations.set(type.name.value, ops);\n }\n if (type) {\n for (const a of sel.arguments ?? []) argumentUse(type, a);\n }\n }\n walk(sel.selectionSet, false);\n }\n };\n const argumentUse = (type: ObjectTypeDefinitionNode, a: ArgumentNode) => {\n if (a.name.value === \"where\") where(type, a.value);\n if (a.name.value === \"sort\" || a.name.value === \"options\") {\n const sorts = usage.sorts.get(type.name.value) ?? new Set<string>();\n const collect = (v: ValueNode) => {\n if (v.kind === Kind.LIST) v.values.forEach(collect);\n else if (v.kind === Kind.OBJECT) {\n for (const f of v.fields) {\n if (f.name.value === \"sort\") collect(f.value);\n else if (f.value.kind === Kind.ENUM) sorts.add(f.name.value);\n }\n }\n };\n collect(a.value);\n usage.sorts.set(type.name.value, sorts);\n }\n };\n for (const doc of operations) {\n for (const def of doc.definitions) {\n if (def.kind === Kind.OPERATION_DEFINITION) walk(def.selectionSet, true);\n }\n }\n return usage;\n}\n\nfunction namedTypeOf(f: FieldDefinitionNode): string {\n let t = f.type;\n while (t.kind !== Kind.NAMED_TYPE) t = t.type;\n return t.name.value;\n}\n\nfunction has(\n directives: readonly ConstDirectiveNode[] | undefined,\n name: string,\n): boolean {\n return (directives ?? []).some((d) => d.name.value === name);\n}\n\nfunction directive(\n name: string,\n args: Record<string, ConstValueNode> = {},\n): ConstDirectiveNode {\n return {\n kind: Kind.DIRECTIVE,\n name: { kind: Kind.NAME, value: name },\n arguments: Object.entries(args).map(([k, v]) => arg(k, v)),\n };\n}\n\nfunction arg(name: string, value: ConstValueNode): ConstArgumentNode {\n return { kind: Kind.ARGUMENT, name: { kind: Kind.NAME, value: name }, value };\n}\n\nconst enumValue = (value: string): ConstValueNode => ({\n kind: Kind.ENUM,\n value,\n});\nconst bool = (value: boolean): ConstValueNode => ({\n kind: Kind.BOOLEAN,\n value,\n});\nconst enumList = (values: string[]): ConstValueNode => ({\n kind: Kind.LIST,\n values: values.map(enumValue),\n});\n\n/** Rename a key inside each object of a list argument. */\nfunction renameInList(\n d: ConstDirectiveNode,\n argName: string,\n from: string,\n to: string,\n): ConstDirectiveNode {\n return {\n ...d,\n arguments: (d.arguments ?? []).map((a) =>\n a.name.value === argName && a.value.kind === Kind.LIST\n ? arg(argName, {\n kind: Kind.LIST,\n values: a.value.values.map((v) =>\n v.kind === Kind.OBJECT\n ? {\n ...v,\n fields: v.fields.map((f) =>\n f.name.value === from\n ? { ...f, name: { kind: Kind.NAME, value: to } }\n : f,\n ),\n }\n : v,\n ),\n })\n : a,\n ),\n };\n}\n\nconst IRREGULAR: Record<string, string> = {\n person: \"people\",\n man: \"men\",\n woman: \"women\",\n child: \"children\",\n mouse: \"mice\",\n goose: \"geese\",\n foot: \"feet\",\n tooth: \"teeth\",\n};\n\nfunction neo4jPlural(typeName: string): string {\n const base = lowerFirst(typeName);\n const lower = base.toLowerCase();\n for (const [one, many] of Object.entries(IRREGULAR)) {\n if (lower.endsWith(one))\n return base.slice(0, base.length - one.length) + many;\n }\n return defaultPlural(typeName);\n}\n\nfunction lowerFirst(s: string): string {\n return s.charAt(0).toLowerCase() + s.slice(1);\n}\n","// lora-graphql: the build-time half of the library.\n//\n// lora-graphql print <schema.graphql>\n// lora-graphql directives\n// lora-graphql requirements <schema.graphql> [--ddl]\n// lora-graphql check <schema.graphql> [--operations <file|dir>]... [--variables <file>] [--context <file>]\n// [--baseline <file> [--update-baseline]] [--row-budget <n>]\n// [--database <dir> [--name <db>]] [--json]\n// lora-graphql compile <schema.graphql> --operations <file|dir>... [--out <dir>]\n// lora-graphql analyze <schema.graphql> --database <dir> [--name <db>] [--sample <n>]\n// lora-graphql migrate neo4j <schema.graphql> [--operations <file|dir>]...\n// lora-graphql diff <old.graphql> <new.graphql> [--allow-breaking] [--json]\n\nimport { mkdir, readdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport {\n getNamedType,\n isEnumType,\n isInputObjectType,\n isListType,\n isNonNullType,\n Kind,\n parse,\n print as printAst,\n typeFromAST,\n visit,\n type DefinitionNode,\n type FragmentDefinitionNode,\n type GraphQLInputType,\n type GraphQLSchema,\n type OperationDefinitionNode,\n} from \"graphql\";\nimport { diffSchemas } from \"./analyze/diff.js\";\nimport { formatProblem, ModelError } from \"./errors.js\";\nimport { buildModel } from \"./model/build.js\";\nimport { directiveTypeDefs } from \"./model/directives.js\";\nimport {\n describeRequirement,\n inferRequirements,\n requirementDdl,\n} from \"./analyze/indexes.js\";\nimport { LoraGraphQL, type CheckOptions } from \"./lora-graphql.js\";\nimport { loraDriver, type LoraDriver } from \"./driver.js\";\nimport { usedFragments } from \"./codegen.js\";\nimport { migrateNeo4j } from \"./migrate.js\";\n\nconst USAGE = `lora-graphql <command>\n\n print <schema.graphql> the public SDL clients see\n directives the directive definitions, for editors\n requirements <schema.graphql> [--ddl] constraints and indexes the API needs\n check <schema.graphql> [--operations <file|dir>]... [--json]\n [--variables <file>] [--context <file>]\n [--baseline <file> [--update-baseline]]\n [--row-budget <n>] [--database <dir> [--name <db>]]\n CI gate: model, lint, @cypher statements and\n the plans of your operations, on an in-memory\n LoraDB (or an existing one with --database)\n compile <schema.graphql> --operations <file|dir>... [--out <dir>]\n persisted-operation manifest.json and\n operations.d.ts types\n analyze <schema.graphql> --database <dir> [--name <db>] [--sample <n>]\n statistics JSON for useStatistics()\n migrate neo4j <schema.graphql> [--operations <file|dir>]...\n rewrite an @neo4j/graphql SDL; TODOs for\n what has no equivalent\n diff <old.graphql> <new.graphql> [--allow-breaking] [--json]\n database statements and API changes`;\n\ninterface Io {\n out: (line: string) => void;\n err: (line: string) => void;\n}\n\nexport async function main(\n argv: string[],\n io: Io = defaultIo,\n): Promise<number> {\n const [command, ...rest] = argv;\n const flags = new Set<string>();\n const values = new Map<string, string>();\n const positional: string[] = [];\n const operations: string[] = [];\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i]!;\n if (a === \"--operations\") operations.push(rest[++i] ?? \"\");\n else if (VALUE_FLAGS.has(a)) values.set(a, rest[++i] ?? \"\");\n else if (a.startsWith(\"--\")) flags.add(a);\n else positional.push(a);\n }\n try {\n switch (command) {\n case \"print\":\n return await print(need(positional, 1), io);\n case \"directives\":\n io.out(directiveTypeDefs.trim());\n return 0;\n case \"requirements\":\n return await requirements(need(positional, 1), flags.has(\"--ddl\"), io);\n case \"check\":\n return await check(need(positional, 1), operations, flags, values, io);\n case \"compile\":\n return await compile(need(positional, 1), operations, values, io);\n case \"analyze\":\n return await analyzeCommand(need(positional, 1), values, io);\n case \"migrate\": {\n const [from, file] = need(positional, 2);\n if (from !== \"neo4j\") throw new UsageError(\"migrate supports neo4j\");\n const docs = [];\n for (const path of operations) {\n for (const f of await graphqlFiles(path)) docs.push(parse(f.source));\n }\n const result = migrateNeo4j(await read(file!), docs);\n for (const t of result.todos) io.out(`# TODO(migrate): ${t}`);\n if (result.todos.length > 0) io.out(\"\");\n io.out(result.typeDefs);\n return 0;\n }\n case \"diff\": {\n const [before, after] = need(positional, 2);\n return await diff(before!, after!, flags, io);\n }\n default:\n io.err(USAGE);\n return 2;\n }\n } catch (err) {\n if (err instanceof UsageError) {\n io.err(`${err.message}\\n\\n${USAGE}`);\n return 2;\n }\n if (err instanceof ModelError) {\n io.err(err.message);\n return 1;\n }\n io.err(err instanceof Error ? err.message : String(err));\n return 1;\n }\n}\n\nclass UsageError extends Error {}\n\nconst VALUE_FLAGS = new Set([\n \"--out\",\n \"--variables\",\n \"--context\",\n \"--baseline\",\n \"--row-budget\",\n \"--database\",\n \"--name\",\n \"--sample\",\n]);\n\nfunction need(positional: string[], n: number): string[] {\n if (positional.length < n)\n throw new UsageError(`expected ${n} file argument(s)`);\n return positional;\n}\n\nconst read = (path: string) => readFile(path, \"utf8\");\n\n/** A LoraGraphQL over a driver that refuses to run anything. */\nfunction offline(typeDefs: string): LoraGraphQL {\n const driver: LoraDriver = {\n run: () => Promise.reject(new Error(\"no database\")),\n };\n return new LoraGraphQL({ typeDefs, driver });\n}\n\nasync function print([file]: string[], io: Io): Promise<number> {\n io.out(offline(await read(file!)).printPublicSchema());\n return 0;\n}\n\nasync function requirements(\n [file]: string[],\n ddl: boolean,\n io: Io,\n): Promise<number> {\n const model = buildModel(await read(file!));\n for (const r of inferRequirements(model)) {\n io.out(\n ddl\n ? `${requirementDdl(r)};`\n : `${describeRequirement(r)}, because ${r.reason}`,\n );\n }\n return 0;\n}\n\n/** The node binding's createDatabase, or a message saying how to get it. */\nasync function nodeBinding(\n io: Io,\n command: string,\n): Promise<\n | ((name?: string, options?: { databaseDir?: string }) => Promise<unknown>)\n | undefined\n> {\n try {\n const { createDatabase } =\n (await import(\"@loradb/lora-node\")) as unknown as {\n createDatabase: (\n name?: string,\n options?: { databaseDir?: string },\n ) => Promise<unknown>;\n };\n return createDatabase;\n } catch {\n io.err(\n `${command} needs @loradb/lora-node: npm install --save-dev @loradb/lora-node`,\n );\n return undefined;\n }\n}\n\nasync function check(\n [file]: string[],\n operationPaths: string[],\n flags: Set<string>,\n values: Map<string, string>,\n io: Io,\n): Promise<number> {\n const json = flags.has(\"--json\");\n const typeDefs = await read(file!);\n const createDatabase = await nodeBinding(io, \"check\");\n if (!createDatabase) return 1;\n // An existing database is checked as it is; an in-memory one gets what\n // the API needs, so only the plans are under test.\n const databaseDir = values.get(\"--database\");\n const db = await createDatabase(\n databaseDir ? (values.get(\"--name\") ?? \"app\") : undefined,\n databaseDir ? { databaseDir } : {},\n );\n const lora = new LoraGraphQL({\n typeDefs,\n driver: loraDriver(db as Parameters<typeof loraDriver>[0]),\n });\n if (!databaseDir) await lora.assertSchema({ create: true });\n const schema = lora.getSchema();\n const fixtures = values.has(\"--variables\")\n ? (JSON.parse(await read(values.get(\"--variables\")!)) as Record<\n string,\n Record<string, unknown>\n >)\n : {};\n // GraphQL contexts per operation name, `*` for every other operation.\n const contexts = values.has(\"--context\")\n ? (JSON.parse(await read(values.get(\"--context\")!)) as Record<\n string,\n unknown\n >)\n : {};\n const operations: NonNullable<CheckOptions[\"operations\"]> = [];\n for (const path of operationPaths) {\n for (const f of await graphqlFiles(path)) {\n for (const op of operationsIn(schema, f.path, f.source)) {\n const short = op.name!.split(\"#\")[1] ?? \"\";\n const given = fixtures[op.name!] ?? fixtures[short];\n const context = contexts[op.name!] ?? contexts[short] ?? contexts[\"*\"];\n operations.push({\n ...op,\n ...(given ? { variables: given } : {}),\n ...(context !== undefined ? { context } : {}),\n });\n }\n }\n }\n const rowBudget = values.has(\"--row-budget\")\n ? Number(values.get(\"--row-budget\"))\n : undefined;\n const report = await lora.check({\n operations,\n ...(rowBudget !== undefined ? { rowBudget } : {}),\n });\n\n // Plan baseline: the operators of every statement, so a plan change\n // shows up in review.\n const baselinePath = values.get(\"--baseline\");\n const current: Record<string, string[][]> = {};\n for (const p of report.plans) {\n current[`${p.operation} › ${p.field}`] = p.reports.map((r) => r.operators);\n }\n const changed: string[] = [];\n if (baselinePath) {\n let before: Record<string, string[][]> | undefined;\n try {\n before = JSON.parse(await read(baselinePath)) as Record<\n string,\n string[][]\n >;\n } catch {\n before = undefined;\n }\n if (flags.has(\"--update-baseline\") || !before) {\n await writeFile(baselinePath, JSON.stringify(current, null, 2) + \"\\n\");\n } else {\n for (const key of new Set([\n ...Object.keys(before),\n ...Object.keys(current),\n ])) {\n if (JSON.stringify(before[key]) !== JSON.stringify(current[key])) {\n changed.push(key);\n }\n }\n }\n }\n const ok = report.ok && changed.length === 0;\n\n if (json) {\n io.out(JSON.stringify({ ...report, ok, planChanges: changed }, null, 2));\n return ok ? 0 : 1;\n }\n for (const w of report.warnings) io.out(`warning ${formatProblem(w)}`);\n for (const w of report.lint) io.out(`lint ${formatProblem(w)}`);\n for (const u of report.unused) {\n io.out(\n `unused ${u.type} index ${u.name} on :${u.labels.join(\":\")}(${u.properties.join(\", \")})`,\n );\n }\n for (const m of report.missing)\n io.out(`error missing ${describeRequirement(m)}`);\n for (const c of report.cypher)\n io.out(`error ${c.type}.${c.field}: ${c.message}`);\n for (const e of report.errors)\n io.out(`error ${e.operation}: ${e.message}`);\n for (const p of report.plans) {\n for (const r of p.reports) {\n for (const f of r.findings) {\n io.out(`error ${p.operation} › ${p.field}: ${f.message}`);\n }\n for (const n of r.notes) {\n io.out(`lint ${p.operation} › ${p.field}: ${n.message}`);\n }\n }\n }\n for (const key of changed) {\n io.out(\n `error ${key}: the plan differs from the baseline (--update-baseline to accept)`,\n );\n }\n const checked = report.plans.length;\n io.out(\n ok\n ? `ok ${checked} root field(s) in ${operations.length} operation(s) seek as expected`\n : \"failed\",\n );\n return ok ? 0 : 1;\n}\n\n/** Persisted operations from .graphql files (one per operation) or JSON (id → source). */\nasync function persistedOperations(\n paths: string[],\n): Promise<Record<string, string>> {\n const out: Record<string, string> = {};\n for (const path of paths) {\n const info = await stat(path);\n if (info.isFile() && path.endsWith(\".json\")) {\n Object.assign(\n out,\n JSON.parse(await read(path)) as Record<string, string>,\n );\n continue;\n }\n for (const f of await graphqlFiles(path)) {\n const doc = parse(f.source);\n const fragments = new Map(\n doc.definitions\n .filter(\n (d): d is FragmentDefinitionNode =>\n d.kind === Kind.FRAGMENT_DEFINITION,\n )\n .map((d) => [d.name.value, d]),\n );\n const rel = relative(process.cwd(), f.path);\n doc.definitions\n .filter(\n (d): d is OperationDefinitionNode =>\n d.kind === Kind.OPERATION_DEFINITION,\n )\n .forEach((op, i) => {\n const id = `${rel}#${op.name?.value ?? i + 1}`;\n out[id] = printAst({\n kind: Kind.DOCUMENT,\n definitions: [op, ...usedFragments(op, fragments)],\n });\n });\n }\n }\n return out;\n}\n\nasync function compile(\n [file]: string[],\n operationPaths: string[],\n values: Map<string, string>,\n io: Io,\n): Promise<number> {\n if (operationPaths.length === 0) {\n throw new UsageError(\"compile needs --operations <file|dir>\");\n }\n const lora = offline(await read(file!));\n const manifest = lora.buildManifest(\n await persistedOperations(operationPaths),\n );\n const outDir = values.get(\"--out\") ?? \"lora-graphql\";\n await mkdir(outDir, { recursive: true });\n await writeFile(\n join(outDir, \"manifest.json\"),\n JSON.stringify(manifest) + \"\\n\",\n );\n await writeFile(\n join(outDir, \"operations.d.ts\"),\n lora.generateTypes(manifest),\n );\n io.out(\n `wrote ${Object.keys(manifest.operations).length} operation(s) to ${join(outDir, \"manifest.json\")} and ${join(outDir, \"operations.d.ts\")}`,\n );\n return 0;\n}\n\nasync function analyzeCommand(\n [file]: string[],\n values: Map<string, string>,\n io: Io,\n): Promise<number> {\n const databaseDir = values.get(\"--database\");\n if (!databaseDir) throw new UsageError(\"analyze needs --database <dir>\");\n const createDatabase = await nodeBinding(io, \"analyze\");\n if (!createDatabase) return 1;\n const db = await createDatabase(values.get(\"--name\") ?? \"app\", {\n databaseDir,\n });\n const lora = new LoraGraphQL({\n typeDefs: await read(file!),\n driver: loraDriver(db as Parameters<typeof loraDriver>[0]),\n });\n const sample = values.has(\"--sample\")\n ? Number(values.get(\"--sample\"))\n : undefined;\n const stats = await lora.analyze(sample !== undefined ? { sample } : {});\n io.out(JSON.stringify(stats, null, 2));\n (db as { dispose?: () => void }).dispose?.();\n return 0;\n}\n\nasync function graphqlFiles(\n path: string,\n): Promise<Array<{ path: string; source: string }>> {\n const info = await stat(path);\n if (info.isFile()) return [{ path, source: await read(path) }];\n const out: Array<{ path: string; source: string }> = [];\n for (const entry of await readdir(path, { withFileTypes: true })) {\n const full = join(path, entry.name);\n if (entry.isDirectory()) out.push(...(await graphqlFiles(full)));\n else if (/\\.(graphql|gql)$/.test(entry.name)) {\n out.push({ path: full, source: await read(full) });\n }\n }\n return out;\n}\n\n/** Each query in a file, with example values for its required variables. */\nfunction operationsIn(\n schema: GraphQLSchema,\n path: string,\n source: string,\n): NonNullable<CheckOptions[\"operations\"]> {\n const doc = parse(source);\n const fragments = new Map(\n doc.definitions\n .filter(\n (d): d is FragmentDefinitionNode => d.kind === Kind.FRAGMENT_DEFINITION,\n )\n .map((f) => [f.name.value, f]),\n );\n // The fragments an operation spreads, transitively.\n const used = (root: DefinitionNode): FragmentDefinitionNode[] => {\n const seen = new Map<string, FragmentDefinitionNode>();\n const walk = (node: DefinitionNode) =>\n visit(node, {\n FragmentSpread(spread) {\n const f = fragments.get(spread.name.value);\n if (f && !seen.has(f.name.value)) {\n seen.set(f.name.value, f);\n walk(f);\n }\n },\n });\n walk(root);\n return [...seen.values()];\n };\n return doc.definitions\n .filter(\n (d): d is OperationDefinitionNode =>\n d.kind === Kind.OPERATION_DEFINITION && d.operation === \"query\",\n )\n .map((op, i) => {\n const variables: Record<string, unknown> = {};\n for (const v of op.variableDefinitions ?? []) {\n if (v.defaultValue || v.type.kind !== Kind.NON_NULL_TYPE) continue;\n const type = typeFromAST(schema, v.type) as\n | GraphQLInputType\n | undefined;\n if (type) variables[v.variable.name.value] = sample(type);\n }\n return {\n name: `${path}${op.name ? `#${op.name.value}` : `#${i + 1}`}`,\n document: { kind: Kind.DOCUMENT, definitions: [op, ...used(op)] },\n variables,\n };\n });\n}\n\nconst SAMPLES: Record<string, unknown> = {\n String: \"x\",\n ID: \"x\",\n Int: 1,\n Float: 1,\n Boolean: true,\n BigInt: \"1\",\n Date: \"2026-01-01\",\n Time: \"12:00:00Z\",\n LocalTime: \"12:00:00\",\n DateTime: \"2026-01-01T12:00:00Z\",\n LocalDateTime: \"2026-01-01T12:00:00\",\n Duration: \"P1D\",\n};\n\nfunction sample(type: GraphQLInputType): unknown {\n const inner = isNonNullType(type) ? type.ofType : type;\n if (isListType(inner)) return [sample(inner.ofType)];\n const named = getNamedType(inner);\n if (isEnumType(named)) return named.getValues()[0]?.value;\n if (isInputObjectType(named)) {\n const out: Record<string, unknown> = {};\n for (const f of Object.values(named.getFields())) {\n if (isNonNullType(f.type) && f.defaultValue === undefined) {\n out[f.name] = sample(f.type);\n }\n }\n return out;\n }\n return SAMPLES[named.name] ?? \"x\";\n}\n\nasync function diff(\n before: string,\n after: string,\n flags: Set<string>,\n io: Io,\n): Promise<number> {\n const result = diffSchemas(await read(before), await read(after));\n const breaking = result.api.breaking.length > 0;\n const code = breaking && !flags.has(\"--allow-breaking\") ? 1 : 0;\n if (flags.has(\"--json\")) {\n io.out(JSON.stringify(result, null, 2));\n return code;\n }\n io.out(\"# Database\");\n if (result.database.statements.length === 0) io.out(\"no changes\");\n for (const s of result.database.statements) {\n io.out(`${s.destructive ? \"// destructive: \" : \"// \"}${s.reason}`);\n io.out(`${s.text};`);\n }\n io.out(\"\\n# API\");\n if (!breaking && result.api.dangerous.length === 0) io.out(\"no changes\");\n for (const c of result.api.breaking) io.out(`breaking ${c.description}`);\n for (const c of result.api.dangerous) io.out(`dangerous ${c.description}`);\n for (const n of result.notes) io.out(`note ${n}`);\n return code;\n}\n\nconst defaultIo: Io = {\n out: (line) => process.stdout.write(line + \"\\n\"),\n err: (line) => process.stderr.write(line + \"\\n\"),\n};\n\nconst invokedDirectly =\n typeof process !== \"undefined\" &&\n process.argv[1] !== undefined &&\n /lora-graphql(\\.js)?$|cli\\.js$/.test(process.argv[1]);\nif (invokedDirectly) {\n main(process.argv.slice(2)).then((code) => process.exit(code));\n}\n"],"names":["ROOT","UNSUPPORTED","SUFFIX_OPS","NESTED_OPS","migrateNeo4j","sdl","operations","doc","parse","todos","nodeTypes","d","Kind","has","nodeNames","t","usage","observe","out","visit","type","directives","migrateDirectives","directive","ops","enumList","idFields","f","fields","migrateField","print","bool","used","field","at","name","migrateRelationship","renameInList","migrateSubscription","args","a","arg","enumValue","kept","v","events","relationships","e","types","byPlural","defaultPlural","neo4jPlural","byName","note","op","where","value","key","quantifier","rel","x","target","namedTypeOf","suffix","s","fieldName","def","inner","mapped","walk","set","root","sel","mutation","plural","lowerFirst","argumentUse","sorts","collect","k","values","argName","from","to","IRREGULAR","typeName","base","lower","one","many","USAGE","main","argv","io","defaultIo","command","rest","flags","positional","VALUE_FLAGS","need","directiveTypeDefs","requirements","check","compile","analyzeCommand","file","UsageError","docs","path","graphqlFiles","result","read","before","after","diff","err","ModelError","readFile","offline","typeDefs","driver","LoraGraphQL","ddl","model","buildModel","r","inferRequirements","requirementDdl","describeRequirement","nodeBinding","createDatabase","operationPaths","json","databaseDir","db","lora","loraDriver","schema","fixtures","contexts","operationsIn","short","given","context","rowBudget","report","baselinePath","current","p","changed","writeFile","ok","w","formatProblem","u","m","c","n","checked","persistedOperations","paths","stat","fragments","relative","i","id","printAst","usedFragments","manifest","outDir","mkdir","join","sample","stats","entry","readdir","full","source","seen","node","spread","variables","typeFromAST","SAMPLES","isNonNullType","isListType","named","getNamedType","isEnumType","isInputObjectType","diffSchemas","breaking","code","line","invokedDirectly"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sources":["../src/migrate.ts","../src/cli.ts"],"sourcesContent":["// `lora-graphql migrate neo4j`: rewrite an @neo4j/graphql SDL into this\n// package's vocabulary. What maps is rewritten; what does not is removed\n// and listed as a TODO. With the client's operations, the opt-in surface\n// (@mutation, @filterable, @sortable) follows observed usage; without\n// them, mutations stay on for every type (as in @neo4j/graphql).\n\nimport {\n Kind,\n parse,\n print,\n visit,\n type ArgumentNode,\n type ConstArgumentNode,\n type ConstDirectiveNode,\n type ConstValueNode,\n type DocumentNode,\n type FieldDefinitionNode,\n type ObjectTypeDefinitionNode,\n type SelectionSetNode,\n type ValueNode,\n} from \"graphql\";\nimport { defaultPlural } from \"./model/build.js\";\n\nexport interface MigrationResult {\n /** The rewritten SDL. */\n typeDefs: string;\n /** What has no equivalent or needs a decision, by type and field. */\n todos: string[];\n}\n\nconst ROOT = new Set([\"Query\", \"Mutation\", \"Subscription\"]);\n\n/** Removed with a TODO: no equivalent here. */\nconst UNSUPPORTED: Record<string, string> = {\n coalesce:\n \"@coalesce is not supported: it hides predicates from indexes; store a value instead\",\n subscriptionsAuthorization:\n \"@subscriptionsAuthorization is not supported: SUBSCRIBE rules go in @authorization\",\n shareable: \"federation (@shareable) is not supported\",\n external: \"federation (@external) is not supported\",\n requires: \"federation (@requires) is not supported\",\n provides: \"federation (@provides) is not supported\",\n exclude: \"@exclude: use @query / @mutation to choose operations\",\n vector:\n \"@vector on types (with a provider) is not supported: put @vector(dimensions:, similarity:) on a [Float!] field\",\n};\n\n/** neo4j 5 filter suffixes → @filterable operators. */\nconst SUFFIX_OPS: Array<[string, string]> = [\n [\"_NOT_IN\", \"IN\"],\n [\"_STARTS_WITH\", \"STARTS_WITH\"],\n [\"_ENDS_WITH\", \"ENDS_WITH\"],\n [\"_CONTAINS\", \"CONTAINS\"],\n [\"_INCLUDES\", \"INCLUDES\"],\n [\"_GTE\", \"GTE\"],\n [\"_LTE\", \"LTE\"],\n [\"_GT\", \"GT\"],\n [\"_LT\", \"LT\"],\n [\"_IN\", \"IN\"],\n [\"_EQ\", \"EQ\"],\n];\n\n/** neo4j 6 nested filter operators → @filterable operators. */\nconst NESTED_OPS: Record<string, string> = {\n eq: \"EQ\",\n in: \"IN\",\n lt: \"LT\",\n lte: \"LTE\",\n gt: \"GT\",\n gte: \"GTE\",\n contains: \"CONTAINS\",\n startsWith: \"STARTS_WITH\",\n endsWith: \"ENDS_WITH\",\n includes: \"INCLUDES\",\n caseInsensitive: \"CASE_INSENSITIVE\",\n};\n\ninterface Usage {\n filters: Map<string, Map<string, Set<string>>>;\n sorts: Map<string, Set<string>>;\n mutations: Map<string, Set<string>>;\n}\n\nexport function migrateNeo4j(\n sdl: string,\n operations: DocumentNode[] = [],\n): MigrationResult {\n const doc = parse(sdl, { noLocation: true });\n const todos: string[] = [];\n const nodeTypes = doc.definitions.filter(\n (d): d is ObjectTypeDefinitionNode =>\n d.kind === Kind.OBJECT_TYPE_DEFINITION &&\n !ROOT.has(d.name.value) &&\n !has(d.directives, \"relationshipProperties\") &&\n !has(d.directives, \"jwt\"),\n );\n const nodeNames = new Set(nodeTypes.map((t) => t.name.value));\n const usage =\n operations.length > 0 ? observe(operations, nodeTypes) : undefined;\n\n const out = visit(doc, {\n ObjectTypeDefinition(t) {\n if (!nodeNames.has(t.name.value)) return undefined;\n const type = t.name.value;\n let directives = migrateDirectives(\n t.directives ?? [],\n type,\n undefined,\n todos,\n );\n if (!has(directives, \"node\"))\n directives = [...directives, directive(\"node\")];\n const ops = usage?.mutations.get(type);\n if (!has(directives, \"mutation\")) {\n if (!usage) {\n directives = [...directives, directive(\"mutation\")];\n todos.push(\n `${type}: @mutation added (@neo4j/graphql generates mutations for every type); remove it or narrow its operations`,\n );\n } else if (ops && ops.size > 0) {\n directives = [\n ...directives,\n directive(\"mutation\", {\n operations: enumList([...ops].sort()),\n }),\n ];\n }\n }\n const idFields = (t.fields ?? []).filter((f) => has(f.directives, \"id\"));\n if (idFields.length === 0) {\n todos.push(\n `${type}: no @id field; add a required @key field (the sort tie-breaker and mutation address)`,\n );\n } else if (idFields.length > 1) {\n todos.push(`${type}: several @id fields; keep one as the @key`);\n }\n const fields = (t.fields ?? []).map((f) =>\n migrateField(f, type, usage, todos),\n );\n return { ...t, directives, fields };\n },\n });\n return { typeDefs: print(out), todos };\n}\n\nfunction migrateField(\n f: FieldDefinitionNode,\n type: string,\n usage: Usage | undefined,\n todos: string[],\n): FieldDefinitionNode {\n let directives = migrateDirectives(\n f.directives ?? [],\n type,\n f.name.value,\n todos,\n );\n const id = directives.find((d) => d.name.value === \"id\");\n if (id) {\n directives = directives.filter(\n (d) => d.name.value !== \"id\" && d.name.value !== \"unique\",\n );\n directives.push(directive(\"key\", { generate: bool(true) }));\n }\n const used = usage?.filters.get(type)?.get(f.name.value);\n if (used && used.size > 0 && !has(directives, \"filterable\")) {\n directives.push(\n has(directives, \"relationship\")\n ? directive(\"filterable\")\n : directive(\"filterable\", { byValue: enumList([...used].sort()) }),\n );\n }\n if (\n usage?.sorts.get(type)?.has(f.name.value) &&\n !has(directives, \"sortable\")\n ) {\n directives.push(directive(\"sortable\"));\n }\n return { ...f, directives };\n}\n\nfunction migrateDirectives(\n directives: readonly ConstDirectiveNode[],\n type: string,\n field: string | undefined,\n todos: string[],\n): ConstDirectiveNode[] {\n const at = field ? `${type}.${field}` : type;\n const out: ConstDirectiveNode[] = [];\n for (const d of directives) {\n const name = d.name.value;\n if (name in UNSUPPORTED) {\n todos.push(`${at}: ${UNSUPPORTED[name]}`);\n continue;\n }\n if (name === \"key\" && !field) {\n todos.push(`${at}: federation @key(fields:) is not supported`);\n continue;\n }\n if (name === \"unique\") {\n out.push(directive(\"unique\"));\n continue;\n }\n if (name === \"relationship\") {\n out.push(migrateRelationship(d, at, todos));\n continue;\n }\n if (name === \"fulltext\") {\n out.push(renameInList(d, \"indexes\", \"indexName\", \"name\"));\n continue;\n }\n if (name === \"subscription\") {\n out.push(migrateSubscription(d, at, todos));\n continue;\n }\n if (name === \"authorization\" || name === \"authentication\") {\n todos.push(\n `${at}: check the @${name} rule's filters: operators are written { field: { eq: ... } } here`,\n );\n }\n out.push(d);\n }\n return out;\n}\n\nfunction migrateRelationship(\n d: ConstDirectiveNode,\n at: string,\n todos: string[],\n): ConstDirectiveNode {\n const args: ConstArgumentNode[] = [];\n for (const a of d.arguments ?? []) {\n const name = a.name.value;\n if (name === \"queryDirection\" && a.value.kind === Kind.ENUM) {\n if (a.value.value.includes(\"UNDIRECTED\")) {\n args.push(arg(\"queryDirection\", enumValue(\"UNDIRECTED\")));\n }\n continue;\n }\n if (name === \"nestedOperations\" && a.value.kind === Kind.LIST) {\n const kept = a.value.values.filter(\n (v) => v.kind === Kind.ENUM && v.value !== \"CONNECT_OR_CREATE\",\n );\n if (kept.length < a.value.values.length) {\n todos.push(`${at}: connectOrCreate is not supported; use upsert`);\n }\n args.push(arg(\"nestedOperations\", { kind: Kind.LIST, values: kept }));\n continue;\n }\n args.push(a);\n }\n return { ...d, arguments: args };\n}\n\nfunction migrateSubscription(\n d: ConstDirectiveNode,\n at: string,\n todos: string[],\n): ConstDirectiveNode {\n const events = d.arguments?.find((a) => a.name.value === \"events\")?.value;\n if (!events || events.kind !== Kind.LIST) return d;\n const ops = new Set<string>();\n let relationships = false;\n for (const e of events.values) {\n if (e.kind !== Kind.ENUM) continue;\n if (e.value.startsWith(\"CREATED\") && !e.value.includes(\"RELATIONSHIP\"))\n ops.add(\"CREATE\");\n else if (e.value.startsWith(\"UPDATED\")) ops.add(\"UPDATE\");\n else if (e.value.startsWith(\"DELETED\") && !e.value.includes(\"RELATIONSHIP\"))\n ops.add(\"DELETE\");\n else if (e.value.includes(\"RELATIONSHIP\")) relationships = true;\n }\n todos.push(\n `${at}: subscriptions see writes made through this library (or the engine change feed), not CDC`,\n );\n return directive(\"subscription\", {\n operations: enumList([...ops].sort()),\n ...(relationships ? { relationships: bool(true) } : {}),\n });\n}\n\n/** What the client's operations use: filters, sorts and mutations per type. */\nfunction observe(\n operations: DocumentNode[],\n types: ObjectTypeDefinitionNode[],\n): Usage {\n const usage: Usage = {\n filters: new Map(),\n sorts: new Map(),\n mutations: new Map(),\n };\n // Root fields as either naming would call them: this package's plural\n // and @neo4j/graphql's (which knows irregular plurals, e.g. people).\n const byPlural = new Map<string, ObjectTypeDefinitionNode>();\n for (const t of types) {\n byPlural.set(defaultPlural(t.name.value), t);\n byPlural.set(neo4jPlural(t.name.value), t);\n }\n const byName = new Map(types.map((t) => [t.name.value, t]));\n const note = (type: string, field: string, op: string) => {\n const fields = usage.filters.get(type) ?? new Map<string, Set<string>>();\n const ops = fields.get(field) ?? new Set<string>();\n ops.add(op);\n fields.set(field, ops);\n usage.filters.set(type, fields);\n };\n const where = (type: ObjectTypeDefinitionNode, value: ValueNode) => {\n if (value.kind !== Kind.OBJECT) return;\n for (const f of value.fields) {\n const key = f.name.value;\n if (key === \"AND\" || key === \"OR\") {\n if (f.value.kind === Kind.LIST)\n for (const v of f.value.values) where(type, v);\n continue;\n }\n if (key === \"NOT\") {\n where(type, f.value);\n continue;\n }\n // neo4j 5 relationship quantifiers: `actors_SOME: { name_EQ: ... }`.\n const quantifier = /^(.+)_(SOME|ALL|NONE|SINGLE)$/.exec(key);\n if (quantifier) {\n const rel = type.fields?.find((x) => x.name.value === quantifier[1]);\n const target = rel && byName.get(namedTypeOf(rel));\n if (rel && target) {\n note(type.name.value, quantifier[1]!, \"EQ\");\n where(target, f.value);\n }\n continue;\n }\n const suffix = SUFFIX_OPS.find(([s]) => key.endsWith(s));\n const fieldName = suffix ? key.slice(0, -suffix[0].length) : key;\n const def = type.fields?.find((x) => x.name.value === fieldName);\n if (!def) continue;\n const target = byName.get(namedTypeOf(def));\n if (target) {\n note(type.name.value, fieldName, \"EQ\");\n // `some` / `all` / `none` / `single`, or a nested where.\n if (f.value.kind === Kind.OBJECT) {\n for (const inner of f.value.fields) where(target, inner.value);\n where(target, f.value);\n }\n continue;\n }\n if (suffix) note(type.name.value, fieldName, suffix[1]);\n else if (f.value.kind === Kind.OBJECT) {\n for (const op of f.value.fields) {\n const mapped = NESTED_OPS[op.name.value];\n if (mapped) note(type.name.value, fieldName, mapped);\n }\n } else note(type.name.value, fieldName, \"EQ\");\n }\n };\n const walk = (set: SelectionSetNode | undefined, root: boolean) => {\n for (const sel of set?.selections ?? []) {\n if (sel.kind !== Kind.FIELD) continue;\n const name = sel.name.value;\n if (root) {\n const mutation = /^(create|update|delete|upsert)(.+)$/.exec(name);\n const plural = name.replace(/(Connection|Aggregate)$/, \"\");\n const type =\n byPlural.get(plural) ??\n (mutation ? byPlural.get(lowerFirst(mutation[2]!)) : undefined);\n if (type && mutation) {\n const ops = usage.mutations.get(type.name.value) ?? new Set<string>();\n const op = mutation[1]!.toUpperCase();\n if (op === \"UPSERT\") {\n ops.add(\"CREATE\");\n ops.add(\"UPDATE\");\n } else ops.add(op);\n usage.mutations.set(type.name.value, ops);\n }\n if (type) {\n for (const a of sel.arguments ?? []) argumentUse(type, a);\n }\n }\n walk(sel.selectionSet, false);\n }\n };\n const argumentUse = (type: ObjectTypeDefinitionNode, a: ArgumentNode) => {\n if (a.name.value === \"where\") where(type, a.value);\n if (a.name.value === \"sort\" || a.name.value === \"options\") {\n const sorts = usage.sorts.get(type.name.value) ?? new Set<string>();\n const collect = (v: ValueNode) => {\n if (v.kind === Kind.LIST) v.values.forEach(collect);\n else if (v.kind === Kind.OBJECT) {\n for (const f of v.fields) {\n if (f.name.value === \"sort\") collect(f.value);\n else if (f.value.kind === Kind.ENUM) sorts.add(f.name.value);\n }\n }\n };\n collect(a.value);\n usage.sorts.set(type.name.value, sorts);\n }\n };\n for (const doc of operations) {\n for (const def of doc.definitions) {\n if (def.kind === Kind.OPERATION_DEFINITION) walk(def.selectionSet, true);\n }\n }\n return usage;\n}\n\nfunction namedTypeOf(f: FieldDefinitionNode): string {\n let t = f.type;\n while (t.kind !== Kind.NAMED_TYPE) t = t.type;\n return t.name.value;\n}\n\nfunction has(\n directives: readonly ConstDirectiveNode[] | undefined,\n name: string,\n): boolean {\n return (directives ?? []).some((d) => d.name.value === name);\n}\n\nfunction directive(\n name: string,\n args: Record<string, ConstValueNode> = {},\n): ConstDirectiveNode {\n return {\n kind: Kind.DIRECTIVE,\n name: { kind: Kind.NAME, value: name },\n arguments: Object.entries(args).map(([k, v]) => arg(k, v)),\n };\n}\n\nfunction arg(name: string, value: ConstValueNode): ConstArgumentNode {\n return { kind: Kind.ARGUMENT, name: { kind: Kind.NAME, value: name }, value };\n}\n\nconst enumValue = (value: string): ConstValueNode => ({\n kind: Kind.ENUM,\n value,\n});\nconst bool = (value: boolean): ConstValueNode => ({\n kind: Kind.BOOLEAN,\n value,\n});\nconst enumList = (values: string[]): ConstValueNode => ({\n kind: Kind.LIST,\n values: values.map(enumValue),\n});\n\n/** Rename a key inside each object of a list argument. */\nfunction renameInList(\n d: ConstDirectiveNode,\n argName: string,\n from: string,\n to: string,\n): ConstDirectiveNode {\n return {\n ...d,\n arguments: (d.arguments ?? []).map((a) =>\n a.name.value === argName && a.value.kind === Kind.LIST\n ? arg(argName, {\n kind: Kind.LIST,\n values: a.value.values.map((v) =>\n v.kind === Kind.OBJECT\n ? {\n ...v,\n fields: v.fields.map((f) =>\n f.name.value === from\n ? { ...f, name: { kind: Kind.NAME, value: to } }\n : f,\n ),\n }\n : v,\n ),\n })\n : a,\n ),\n };\n}\n\nconst IRREGULAR: Record<string, string> = {\n person: \"people\",\n man: \"men\",\n woman: \"women\",\n child: \"children\",\n mouse: \"mice\",\n goose: \"geese\",\n foot: \"feet\",\n tooth: \"teeth\",\n};\n\nfunction neo4jPlural(typeName: string): string {\n const base = lowerFirst(typeName);\n const lower = base.toLowerCase();\n for (const [one, many] of Object.entries(IRREGULAR)) {\n if (lower.endsWith(one))\n return base.slice(0, base.length - one.length) + many;\n }\n return defaultPlural(typeName);\n}\n\nfunction lowerFirst(s: string): string {\n return s.charAt(0).toLowerCase() + s.slice(1);\n}\n","// lora-graphql: the build-time half of the library.\n//\n// lora-graphql print <schema.graphql>\n// lora-graphql directives\n// lora-graphql requirements <schema.graphql> [--ddl]\n// lora-graphql check <schema.graphql> [--operations <file|dir>]... [--variables <file>] [--context <file>]\n// [--baseline <file> [--update-baseline]] [--row-budget <n>]\n// [--database <dir> [--name <db>]] [--json]\n// lora-graphql compile <schema.graphql> --operations <file|dir>... [--out <dir>]\n// lora-graphql analyze <schema.graphql> --database <dir> [--name <db>] [--sample <n>]\n// lora-graphql migrate neo4j <schema.graphql> [--operations <file|dir>]...\n// lora-graphql diff <old.graphql> <new.graphql> [--allow-breaking] [--json]\n\nimport { mkdir, readdir, readFile, stat, writeFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport {\n getNamedType,\n isEnumType,\n isInputObjectType,\n isListType,\n isNonNullType,\n Kind,\n parse,\n print as printAst,\n typeFromAST,\n visit,\n type DefinitionNode,\n type FragmentDefinitionNode,\n type GraphQLInputType,\n type GraphQLSchema,\n type OperationDefinitionNode,\n} from \"graphql\";\nimport { diffSchemas } from \"./analyze/diff.js\";\nimport { formatProblem, ModelError } from \"./errors.js\";\nimport { buildModel } from \"./model/build.js\";\nimport { directiveTypeDefs } from \"./model/directives.js\";\nimport {\n describeRequirement,\n inferRequirements,\n requirementDdl,\n} from \"./analyze/indexes.js\";\nimport { LoraGraphQL, type CheckOptions } from \"./lora-graphql.js\";\nimport { loraDriver, type LoraDriver } from \"./driver.js\";\nimport { usedFragments } from \"./codegen.js\";\nimport { migrateNeo4j } from \"./migrate.js\";\n\nconst USAGE = `lora-graphql <command>\n\n print <schema.graphql> the public SDL clients see\n directives the directive definitions, for editors\n access <schema.graphql> [--json] who may do what: each type and guarded field,\n operation and kind of caller\n requirements <schema.graphql> [--ddl] constraints and indexes the API needs\n check <schema.graphql> [--operations <file|dir>]... [--json]\n [--variables <file>] [--context <file>]\n [--baseline <file> [--update-baseline]]\n [--row-budget <n>] [--database <dir> [--name <db>]]\n CI gate: model, lint, @cypher statements and\n the plans of your operations, on an in-memory\n LoraDB (or an existing one with --database)\n compile <schema.graphql> --operations <file|dir>... [--out <dir>]\n persisted-operation manifest.json and\n operations.d.ts types\n analyze <schema.graphql> --database <dir> [--name <db>] [--sample <n>]\n statistics JSON for useStatistics()\n migrate neo4j <schema.graphql> [--operations <file|dir>]...\n rewrite an @neo4j/graphql SDL; TODOs for\n what has no equivalent\n diff <old.graphql> <new.graphql> [--allow-breaking] [--json]\n database statements and API changes`;\n\ninterface Io {\n out: (line: string) => void;\n err: (line: string) => void;\n}\n\nexport async function main(\n argv: string[],\n io: Io = defaultIo,\n): Promise<number> {\n const [command, ...rest] = argv;\n const flags = new Set<string>();\n const values = new Map<string, string>();\n const positional: string[] = [];\n const operations: string[] = [];\n for (let i = 0; i < rest.length; i++) {\n const a = rest[i]!;\n if (a === \"--operations\") operations.push(rest[++i] ?? \"\");\n else if (VALUE_FLAGS.has(a)) values.set(a, rest[++i] ?? \"\");\n else if (a.startsWith(\"--\")) flags.add(a);\n else positional.push(a);\n }\n try {\n switch (command) {\n case \"print\":\n return await print(need(positional, 1), io);\n case \"directives\":\n io.out(directiveTypeDefs.trim());\n return 0;\n case \"access\":\n return await access(need(positional, 1), flags.has(\"--json\"), io);\n case \"requirements\":\n return await requirements(need(positional, 1), flags.has(\"--ddl\"), io);\n case \"check\":\n return await check(need(positional, 1), operations, flags, values, io);\n case \"compile\":\n return await compile(need(positional, 1), operations, values, io);\n case \"analyze\":\n return await analyzeCommand(need(positional, 1), values, io);\n case \"migrate\": {\n const [from, file] = need(positional, 2);\n if (from !== \"neo4j\") throw new UsageError(\"migrate supports neo4j\");\n const docs = [];\n for (const path of operations) {\n for (const f of await graphqlFiles(path)) docs.push(parse(f.source));\n }\n const result = migrateNeo4j(await read(file!), docs);\n for (const t of result.todos) io.out(`# TODO(migrate): ${t}`);\n if (result.todos.length > 0) io.out(\"\");\n io.out(result.typeDefs);\n return 0;\n }\n case \"diff\": {\n const [before, after] = need(positional, 2);\n return await diff(before!, after!, flags, io);\n }\n default:\n io.err(USAGE);\n return 2;\n }\n } catch (err) {\n if (err instanceof UsageError) {\n io.err(`${err.message}\\n\\n${USAGE}`);\n return 2;\n }\n if (err instanceof ModelError) {\n io.err(err.message);\n return 1;\n }\n io.err(err instanceof Error ? err.message : String(err));\n return 1;\n }\n}\n\nclass UsageError extends Error {}\n\nconst VALUE_FLAGS = new Set([\n \"--out\",\n \"--variables\",\n \"--context\",\n \"--baseline\",\n \"--row-budget\",\n \"--database\",\n \"--name\",\n \"--sample\",\n]);\n\nfunction need(positional: string[], n: number): string[] {\n if (positional.length < n)\n throw new UsageError(`expected ${n} file argument(s)`);\n return positional;\n}\n\nconst read = (path: string) => readFile(path, \"utf8\");\n\n/** A LoraGraphQL over a driver that refuses to run anything. */\nfunction offline(typeDefs: string): LoraGraphQL {\n const driver: LoraDriver = {\n run: () => Promise.reject(new Error(\"no database\")),\n };\n return new LoraGraphQL({ typeDefs, driver });\n}\n\nasync function access(\n [file]: string[],\n json: boolean,\n io: Io,\n): Promise<number> {\n const matrix = offline(await read(file!)).accessMatrix();\n if (json) {\n io.out(JSON.stringify(matrix, null, 2));\n return 0;\n }\n const rows = matrix.map((e) => [\n e.field ? `${e.type}.${e.field}` : e.type,\n e.operation,\n e.principal,\n e.verdict,\n e.by.join(\", \"),\n ]);\n const widths = [0, 1, 2, 3].map((i) =>\n Math.max(...rows.map((r) => r[i]!.length), 0),\n );\n for (const r of rows) {\n io.out(\n r\n .map((cell, i) => (i < 4 ? cell.padEnd(widths[i]!) : cell))\n .join(\" \")\n .trimEnd(),\n );\n }\n return 0;\n}\n\nasync function print([file]: string[], io: Io): Promise<number> {\n io.out(offline(await read(file!)).printPublicSchema());\n return 0;\n}\n\nasync function requirements(\n [file]: string[],\n ddl: boolean,\n io: Io,\n): Promise<number> {\n const model = buildModel(await read(file!));\n for (const r of inferRequirements(model)) {\n io.out(\n ddl\n ? `${requirementDdl(r)};`\n : `${describeRequirement(r)}, because ${r.reason}`,\n );\n }\n return 0;\n}\n\n/** The node binding's createDatabase, or a message saying how to get it. */\nasync function nodeBinding(\n io: Io,\n command: string,\n): Promise<\n | ((name?: string, options?: { databaseDir?: string }) => Promise<unknown>)\n | undefined\n> {\n try {\n const { createDatabase } =\n (await import(\"@loradb/lora-node\")) as unknown as {\n createDatabase: (\n name?: string,\n options?: { databaseDir?: string },\n ) => Promise<unknown>;\n };\n return createDatabase;\n } catch {\n io.err(\n `${command} needs @loradb/lora-node: npm install --save-dev @loradb/lora-node`,\n );\n return undefined;\n }\n}\n\nasync function check(\n [file]: string[],\n operationPaths: string[],\n flags: Set<string>,\n values: Map<string, string>,\n io: Io,\n): Promise<number> {\n const json = flags.has(\"--json\");\n const typeDefs = await read(file!);\n const createDatabase = await nodeBinding(io, \"check\");\n if (!createDatabase) return 1;\n // An existing database is checked as it is; an in-memory one gets what\n // the API needs, so only the plans are under test.\n const databaseDir = values.get(\"--database\");\n const db = await createDatabase(\n databaseDir ? (values.get(\"--name\") ?? \"app\") : undefined,\n databaseDir ? { databaseDir } : {},\n );\n const lora = new LoraGraphQL({\n typeDefs,\n driver: loraDriver(db as Parameters<typeof loraDriver>[0]),\n });\n if (!databaseDir) await lora.assertSchema({ create: true });\n const schema = lora.getSchema();\n const fixtures = values.has(\"--variables\")\n ? (JSON.parse(await read(values.get(\"--variables\")!)) as Record<\n string,\n Record<string, unknown>\n >)\n : {};\n // GraphQL contexts per operation name, `*` for every other operation.\n const contexts = values.has(\"--context\")\n ? (JSON.parse(await read(values.get(\"--context\")!)) as Record<\n string,\n unknown\n >)\n : {};\n const operations: NonNullable<CheckOptions[\"operations\"]> = [];\n for (const path of operationPaths) {\n for (const f of await graphqlFiles(path)) {\n for (const op of operationsIn(schema, f.path, f.source)) {\n const short = op.name!.split(\"#\")[1] ?? \"\";\n const given = fixtures[op.name!] ?? fixtures[short];\n const context = contexts[op.name!] ?? contexts[short] ?? contexts[\"*\"];\n operations.push({\n ...op,\n ...(given ? { variables: given } : {}),\n ...(context !== undefined ? { context } : {}),\n });\n }\n }\n }\n const rowBudget = values.has(\"--row-budget\")\n ? Number(values.get(\"--row-budget\"))\n : undefined;\n const report = await lora.check({\n operations,\n ...(rowBudget !== undefined ? { rowBudget } : {}),\n });\n\n // Plan baseline: the operators of every statement, so a plan change\n // shows up in review.\n const baselinePath = values.get(\"--baseline\");\n const current: Record<string, string[][]> = {};\n for (const p of report.plans) {\n current[`${p.operation} › ${p.field}`] = p.reports.map((r) => r.operators);\n }\n const changed: string[] = [];\n if (baselinePath) {\n let before: Record<string, string[][]> | undefined;\n try {\n before = JSON.parse(await read(baselinePath)) as Record<\n string,\n string[][]\n >;\n } catch {\n before = undefined;\n }\n if (flags.has(\"--update-baseline\") || !before) {\n await writeFile(baselinePath, JSON.stringify(current, null, 2) + \"\\n\");\n } else {\n for (const key of new Set([\n ...Object.keys(before),\n ...Object.keys(current),\n ])) {\n if (JSON.stringify(before[key]) !== JSON.stringify(current[key])) {\n changed.push(key);\n }\n }\n }\n }\n const ok = report.ok && changed.length === 0;\n\n if (json) {\n io.out(JSON.stringify({ ...report, ok, planChanges: changed }, null, 2));\n return ok ? 0 : 1;\n }\n for (const w of report.warnings) io.out(`warning ${formatProblem(w)}`);\n for (const w of report.lint) io.out(`lint ${formatProblem(w)}`);\n for (const u of report.unused) {\n io.out(\n `unused ${u.type} index ${u.name} on :${u.labels.join(\":\")}(${u.properties.join(\", \")})`,\n );\n }\n for (const s of report.security) io.out(`error ${formatProblem(s)}`);\n for (const m of report.missing)\n io.out(`error missing ${describeRequirement(m)}`);\n for (const c of report.cypher)\n io.out(`error ${c.type}.${c.field}: ${c.message}`);\n for (const e of report.errors)\n io.out(`error ${e.operation}: ${e.message}`);\n for (const p of report.plans) {\n for (const r of p.reports) {\n for (const f of r.findings) {\n io.out(`error ${p.operation} › ${p.field}: ${f.message}`);\n }\n for (const n of r.notes) {\n io.out(`lint ${p.operation} › ${p.field}: ${n.message}`);\n }\n }\n }\n for (const key of changed) {\n io.out(\n `error ${key}: the plan differs from the baseline (--update-baseline to accept)`,\n );\n }\n const checked = report.plans.length;\n io.out(\n ok\n ? `ok ${checked} root field(s) in ${operations.length} operation(s) seek as expected`\n : \"failed\",\n );\n return ok ? 0 : 1;\n}\n\n/** Persisted operations from .graphql files (one per operation) or JSON (id → source). */\nasync function persistedOperations(\n paths: string[],\n): Promise<Record<string, string>> {\n const out: Record<string, string> = {};\n for (const path of paths) {\n const info = await stat(path);\n if (info.isFile() && path.endsWith(\".json\")) {\n Object.assign(\n out,\n JSON.parse(await read(path)) as Record<string, string>,\n );\n continue;\n }\n for (const f of await graphqlFiles(path)) {\n const doc = parse(f.source);\n const fragments = new Map(\n doc.definitions\n .filter(\n (d): d is FragmentDefinitionNode =>\n d.kind === Kind.FRAGMENT_DEFINITION,\n )\n .map((d) => [d.name.value, d]),\n );\n const rel = relative(process.cwd(), f.path);\n doc.definitions\n .filter(\n (d): d is OperationDefinitionNode =>\n d.kind === Kind.OPERATION_DEFINITION,\n )\n .forEach((op, i) => {\n const id = `${rel}#${op.name?.value ?? i + 1}`;\n out[id] = printAst({\n kind: Kind.DOCUMENT,\n definitions: [op, ...usedFragments(op, fragments)],\n });\n });\n }\n }\n return out;\n}\n\nasync function compile(\n [file]: string[],\n operationPaths: string[],\n values: Map<string, string>,\n io: Io,\n): Promise<number> {\n if (operationPaths.length === 0) {\n throw new UsageError(\"compile needs --operations <file|dir>\");\n }\n const lora = offline(await read(file!));\n const manifest = lora.buildManifest(\n await persistedOperations(operationPaths),\n );\n const outDir = values.get(\"--out\") ?? \"lora-graphql\";\n await mkdir(outDir, { recursive: true });\n await writeFile(\n join(outDir, \"manifest.json\"),\n JSON.stringify(manifest) + \"\\n\",\n );\n await writeFile(\n join(outDir, \"operations.d.ts\"),\n lora.generateTypes(manifest),\n );\n io.out(\n `wrote ${Object.keys(manifest.operations).length} operation(s) to ${join(outDir, \"manifest.json\")} and ${join(outDir, \"operations.d.ts\")}`,\n );\n return 0;\n}\n\nasync function analyzeCommand(\n [file]: string[],\n values: Map<string, string>,\n io: Io,\n): Promise<number> {\n const databaseDir = values.get(\"--database\");\n if (!databaseDir) throw new UsageError(\"analyze needs --database <dir>\");\n const createDatabase = await nodeBinding(io, \"analyze\");\n if (!createDatabase) return 1;\n const db = await createDatabase(values.get(\"--name\") ?? \"app\", {\n databaseDir,\n });\n const lora = new LoraGraphQL({\n typeDefs: await read(file!),\n driver: loraDriver(db as Parameters<typeof loraDriver>[0]),\n });\n const sample = values.has(\"--sample\")\n ? Number(values.get(\"--sample\"))\n : undefined;\n const stats = await lora.analyze(sample !== undefined ? { sample } : {});\n io.out(JSON.stringify(stats, null, 2));\n (db as { dispose?: () => void }).dispose?.();\n return 0;\n}\n\nasync function graphqlFiles(\n path: string,\n): Promise<Array<{ path: string; source: string }>> {\n const info = await stat(path);\n if (info.isFile()) return [{ path, source: await read(path) }];\n const out: Array<{ path: string; source: string }> = [];\n for (const entry of await readdir(path, { withFileTypes: true })) {\n const full = join(path, entry.name);\n if (entry.isDirectory()) out.push(...(await graphqlFiles(full)));\n else if (/\\.(graphql|gql)$/.test(entry.name)) {\n out.push({ path: full, source: await read(full) });\n }\n }\n return out;\n}\n\n/** Each query in a file, with example values for its required variables. */\nfunction operationsIn(\n schema: GraphQLSchema,\n path: string,\n source: string,\n): NonNullable<CheckOptions[\"operations\"]> {\n const doc = parse(source);\n const fragments = new Map(\n doc.definitions\n .filter(\n (d): d is FragmentDefinitionNode => d.kind === Kind.FRAGMENT_DEFINITION,\n )\n .map((f) => [f.name.value, f]),\n );\n // The fragments an operation spreads, transitively.\n const used = (root: DefinitionNode): FragmentDefinitionNode[] => {\n const seen = new Map<string, FragmentDefinitionNode>();\n const walk = (node: DefinitionNode) =>\n visit(node, {\n FragmentSpread(spread) {\n const f = fragments.get(spread.name.value);\n if (f && !seen.has(f.name.value)) {\n seen.set(f.name.value, f);\n walk(f);\n }\n },\n });\n walk(root);\n return [...seen.values()];\n };\n return doc.definitions\n .filter(\n (d): d is OperationDefinitionNode =>\n d.kind === Kind.OPERATION_DEFINITION && d.operation === \"query\",\n )\n .map((op, i) => {\n const variables: Record<string, unknown> = {};\n for (const v of op.variableDefinitions ?? []) {\n if (v.defaultValue || v.type.kind !== Kind.NON_NULL_TYPE) continue;\n const type = typeFromAST(schema, v.type) as\n | GraphQLInputType\n | undefined;\n if (type) variables[v.variable.name.value] = sample(type);\n }\n return {\n name: `${path}${op.name ? `#${op.name.value}` : `#${i + 1}`}`,\n document: { kind: Kind.DOCUMENT, definitions: [op, ...used(op)] },\n variables,\n };\n });\n}\n\nconst SAMPLES: Record<string, unknown> = {\n String: \"x\",\n ID: \"x\",\n Int: 1,\n Float: 1,\n Boolean: true,\n BigInt: \"1\",\n Date: \"2026-01-01\",\n Time: \"12:00:00Z\",\n LocalTime: \"12:00:00\",\n DateTime: \"2026-01-01T12:00:00Z\",\n LocalDateTime: \"2026-01-01T12:00:00\",\n Duration: \"P1D\",\n};\n\nfunction sample(type: GraphQLInputType): unknown {\n const inner = isNonNullType(type) ? type.ofType : type;\n if (isListType(inner)) return [sample(inner.ofType)];\n const named = getNamedType(inner);\n if (isEnumType(named)) return named.getValues()[0]?.value;\n if (isInputObjectType(named)) {\n const out: Record<string, unknown> = {};\n for (const f of Object.values(named.getFields())) {\n if (isNonNullType(f.type) && f.defaultValue === undefined) {\n out[f.name] = sample(f.type);\n }\n }\n return out;\n }\n return SAMPLES[named.name] ?? \"x\";\n}\n\nasync function diff(\n before: string,\n after: string,\n flags: Set<string>,\n io: Io,\n): Promise<number> {\n const result = diffSchemas(await read(before), await read(after));\n const breaking = result.api.breaking.length > 0;\n const code = breaking && !flags.has(\"--allow-breaking\") ? 1 : 0;\n if (flags.has(\"--json\")) {\n io.out(JSON.stringify(result, null, 2));\n return code;\n }\n io.out(\"# Database\");\n if (result.database.statements.length === 0) io.out(\"no changes\");\n for (const s of result.database.statements) {\n io.out(`${s.destructive ? \"// destructive: \" : \"// \"}${s.reason}`);\n io.out(`${s.text};`);\n }\n io.out(\"\\n# API\");\n if (!breaking && result.api.dangerous.length === 0) io.out(\"no changes\");\n for (const c of result.api.breaking) io.out(`breaking ${c.description}`);\n for (const c of result.api.dangerous) io.out(`dangerous ${c.description}`);\n for (const n of result.notes) io.out(`note ${n}`);\n return code;\n}\n\nconst defaultIo: Io = {\n out: (line) => process.stdout.write(line + \"\\n\"),\n err: (line) => process.stderr.write(line + \"\\n\"),\n};\n\nconst invokedDirectly =\n typeof process !== \"undefined\" &&\n process.argv[1] !== undefined &&\n /lora-graphql(\\.js)?$|cli\\.js$/.test(process.argv[1]);\nif (invokedDirectly) {\n main(process.argv.slice(2)).then((code) => process.exit(code));\n}\n"],"names":["ROOT","UNSUPPORTED","SUFFIX_OPS","NESTED_OPS","migrateNeo4j","sdl","operations","doc","parse","todos","nodeTypes","d","Kind","has","nodeNames","t","usage","observe","out","visit","type","directives","migrateDirectives","directive","ops","enumList","idFields","f","fields","migrateField","print","bool","used","field","at","name","migrateRelationship","renameInList","migrateSubscription","args","a","arg","enumValue","kept","v","events","relationships","e","types","byPlural","defaultPlural","neo4jPlural","byName","note","op","where","value","key","quantifier","rel","x","target","namedTypeOf","suffix","s","fieldName","def","inner","mapped","walk","set","root","sel","mutation","plural","lowerFirst","argumentUse","sorts","collect","k","values","argName","from","to","IRREGULAR","typeName","base","lower","one","many","USAGE","main","argv","io","defaultIo","command","rest","flags","positional","VALUE_FLAGS","need","directiveTypeDefs","access","requirements","check","compile","analyzeCommand","file","UsageError","docs","path","graphqlFiles","result","read","before","after","diff","err","ModelError","n","readFile","offline","typeDefs","driver","LoraGraphQL","json","matrix","rows","widths","i","r","cell","ddl","model","buildModel","inferRequirements","requirementDdl","describeRequirement","nodeBinding","createDatabase","operationPaths","databaseDir","db","lora","loraDriver","schema","fixtures","contexts","operationsIn","short","given","context","rowBudget","report","baselinePath","current","p","changed","writeFile","ok","w","formatProblem","u","m","c","checked","persistedOperations","paths","stat","fragments","relative","id","printAst","usedFragments","manifest","outDir","mkdir","join","sample","stats","entry","readdir","full","source","seen","node","spread","variables","typeFromAST","SAMPLES","isNonNullType","isListType","named","getNamedType","isEnumType","isInputObjectType","diffSchemas","breaking","code","line","invokedDirectly"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { AuthOperation, Authorization, AuthorizationWhere, NodeType } from '../model/types.js';
1
+ import { AuthOperation, Authorization, AuthorizationWhere, NodeType, RelationshipField, RelationshipOperation } from '../model/types.js';
2
2
  import { Expr } from './cypher.js';
3
3
  import { CompileContext } from './context.js';
4
4
  /** Throws UNAUTHENTICATED when reading `field` needs a jwt the request lacks. */
@@ -61,11 +61,78 @@ export declare function checkPropertyAccess(ctx: CompileContext, type: string, f
61
61
  name: string;
62
62
  }, op: AuthOperation): void;
63
63
  export declare function forbidden(node: NodeType, op: AuthOperation): import('graphql').GraphQLError;
64
+ /**
65
+ * The schema's bypass (`@authorizationDefaults(bypass:)`) grants this
66
+ * request: it skips every filter and validate rule, except on a type with
67
+ * `@authorization(bypass: false)`. Claims only, so it is decided here.
68
+ */
69
+ export declare function bypassed(ctx: CompileContext, node: NodeType | undefined): boolean;
70
+ /**
71
+ * A scalar field's value as the reader may see it: under
72
+ * `@authorization(mask:)`, a row failing `unless` reads the mask's value.
73
+ * Rules see the stored value, and so does a request the bypass lets
74
+ * through. `raw` is the stored value's expression.
75
+ */
76
+ export declare function maskedValue(ctx: CompileContext, node: NodeType, field: {
77
+ authorization?: Authorization | undefined;
78
+ }, variable: string, raw: Expr): Expr;
79
+ /**
80
+ * Whether a masked field reads its stored value on every row for this
81
+ * request (no mask, or every `unless` settled true by the claims). Sorting,
82
+ * grouping and aggregating need it: a per-row mask there would reveal or
83
+ * reorder by the hidden values.
84
+ */
85
+ export declare function maskSettled(ctx: CompileContext, node: NodeType, field: {
86
+ authorization?: Authorization | undefined;
87
+ }): boolean;
88
+ /**
89
+ * `@key(scope: VIEWER)`: a created key must start with the caller's
90
+ * `@viewer` claim and the separator. Checked in JavaScript before any
91
+ * statement runs, so the answer never depends on whether the key exists.
92
+ * A claim containing the separator is refused: it could reach into
93
+ * another caller's key space (`a` taking `a:b:…` from `a:b`).
94
+ */
95
+ export declare function checkKeyScope(ctx: CompileContext, node: NodeType, key: unknown,
96
+ /**
97
+ * The caller's node's `@key`, looked up by the claim when `@viewer` maps
98
+ * to another field (an opaque subject); null when there is no such node.
99
+ */
100
+ viewerKey?: unknown): void;
101
+ /**
102
+ * Rules on relationship fields for `op` on relationships of `rel`, with
103
+ * the statement's owner-side, target-side and relationship variables. The
104
+ * rules of `rel` itself apply, and so do those of a field on the other
105
+ * side declaring the same relationship type (a connect from
106
+ * `Person.trips` answers to `Trip.members`' rules), with source and
107
+ * target swapped. Within a field any rule grants; across fields all must.
108
+ *
109
+ * `"write"` throws UNAUTHENTICATED / FORBIDDEN when the claims already
110
+ * decide against the request; `"read"` never throws. Undefined when no
111
+ * rule applies or the claims alone grant.
112
+ */
113
+ export declare function relationshipRules(ctx: CompileContext, rel: RelationshipField, op: RelationshipOperation, vars: {
114
+ owner: string;
115
+ target: string;
116
+ rel: string;
117
+ }, mode?: "write" | "read"): Expr | false | undefined;
118
+ /** FORBIDDEN: using a relationship's properties its READ_EDGE rules guard. */
119
+ export declare function refusedEdgeRead(rel: RelationshipField): Error;
120
+ /**
121
+ * Throws when `rel`'s properties have READ_EDGE rules that are not settled
122
+ * by the claims alone: filtering, sorting or aggregating by them would
123
+ * reveal what the rules hide per relationship.
124
+ */
125
+ export declare function refuseEdgeRowRules(ctx: CompileContext, rel: RelationshipField): void;
126
+ /** Fields whose relationship rules cover `rel`'s relationships. */
127
+ export declare function relationshipRuleFields(ctx: CompileContext, rel: RelationshipField): Array<{
128
+ field: RelationshipField;
129
+ flipped: boolean;
130
+ }>;
64
131
  /**
65
132
  * A claim by name. With a `@jwt` type, the name's first segment maps to its
66
133
  * `@jwtClaim(path:)`, whether the token arrived decoded or not.
67
134
  */
68
- export declare function claim(ctx: CompileContext, path: string): unknown;
135
+ export declare function claim(ctx: CompileContext, path: string, bound?: boolean): unknown;
69
136
  /**
70
137
  * A value by dotted path, through own properties only: `$context.x` must
71
138
  * not reach `constructor`, `__proto__` or anything else inherited.
@@ -0,0 +1,37 @@
1
+ import { FieldNode, FragmentDefinitionNode } from 'graphql';
2
+ import { CompiledRead } from './read.js';
3
+ import { CompileContext } from './context.js';
4
+ /** Compiled root fields kept per field node, across keys. */
5
+ export declare const COMPILED_PER_FIELD = 16;
6
+ /** Compiled root fields kept in total, across field nodes. */
7
+ export declare const COMPILED_TOTAL = 4096;
8
+ type Compile = (jwt: Record<string, unknown> | undefined) => {
9
+ ctx: CompileContext;
10
+ compiled: CompiledRead;
11
+ };
12
+ export interface CacheRequest {
13
+ fieldNodes: readonly FieldNode[];
14
+ fragments: Record<string, FragmentDefinitionNode>;
15
+ /** Coerced variable values, or undefined when there are none. */
16
+ variables: Record<string, unknown> | undefined;
17
+ jwt: Record<string, unknown> | undefined;
18
+ context: unknown;
19
+ statistics: number;
20
+ }
21
+ export declare class CompileCache {
22
+ #private;
23
+ constructor(total?: number);
24
+ get size(): number;
25
+ /**
26
+ * The cached compile for this request, or a fresh one from `compile`
27
+ * (given a context with `jwt` as the claims), stored for later ones.
28
+ */
29
+ get(request: CacheRequest, compile: Compile): CompiledRead;
30
+ }
31
+ /**
32
+ * A deterministic key for JSON-like values (object keys sorted, bigints
33
+ * tagged); undefined when the value cannot be keyed (functions, cycles,
34
+ * class instances), which turns caching off for that request.
35
+ */
36
+ export declare function stableKey(value: unknown): string | undefined;
37
+ export {};
@@ -31,13 +31,34 @@ export interface CompileContext extends SelectionContext {
31
31
  * compile is reused only for a context that agrees on them.
32
32
  */
33
33
  contextReads: Array<[string, unknown]>;
34
+ /**
35
+ * Claims the compile read, by the path looked up in the token (`""` is
36
+ * the whole token). `bound` stays true while every read only substituted
37
+ * the value into a rule's `node` filter, so a cached compile may rebind
38
+ * it as a parameter; otherwise the value may shape the statement text.
39
+ */
40
+ claimReads: Map<string, {
41
+ value: unknown;
42
+ bound: boolean;
43
+ }>;
34
44
  }
35
45
  export declare function newContext(base: SelectionContext, model: GraphModel, options?: {
36
46
  jwt?: Record<string, unknown> | undefined;
37
47
  degrees?: ReadonlyMap<string, number>;
38
48
  requestContext?: unknown;
39
49
  }): CompileContext;
50
+ /** Record that the compile read the claim at `path` (see `claimReads`). */
51
+ export declare function noteClaim(ctx: CompileContext, path: string, value: unknown, bound: boolean): void;
40
52
  /** Bind a value as the next positional parameter `$pN`. */
53
+ /**
54
+ * A rule value that is an expression, not a parameter: a string with
55
+ * `${viewer.field}` placeholders, read from the caller's node in the
56
+ * statement. `bind` returns the expression as is.
57
+ */
58
+ export declare const EXPR: unique symbol;
59
+ export declare function exprValue(expr: Expr): {
60
+ [EXPR]: Expr;
61
+ };
41
62
  export declare function bind(ctx: CompileContext, value: unknown): Expr;
42
63
  /** A variable name derived from `base`, unique within the statement. */
43
64
  export declare function freshVar(ctx: CompileContext, base: string): string;
@@ -92,6 +92,11 @@ export type ProjectionEntry = {
92
92
  export interface NodePattern {
93
93
  variable?: string;
94
94
  labels: string[];
95
+ /** Inline `{key: value}` properties: a start node seeks on them. */
96
+ properties?: Array<{
97
+ key: string;
98
+ value: Expr;
99
+ }>;
95
100
  }
96
101
  export interface RelPattern {
97
102
  variable?: string;
@@ -1,5 +1,5 @@
1
1
  import * as w from "graphql";
2
- import { b as $, i as b, r as E, j as N, n as i, k as S } from "./driver-CuXOoYWT.js";
2
+ import { b as $, i as b, r as E, j as N, n as i, k as S } from "./driver-XeJM24iP.js";
3
3
  function k(t, n, o = {}) {
4
4
  const s = $(t, o), r = $(n, o), l = N, u = new Map(b(s).map((e) => [l(e), e])), c = new Map(b(r).map((e) => [l(e), e])), p = [...c].filter(([e]) => !u.has(e)).map(([, e]) => e), h = [...u].filter(([e]) => !c.has(e)).map(([, e]) => e), d = [], f = [];
5
5
  C(s, r, d, f);
@@ -96,4 +96,4 @@ function M(t, n) {
96
96
  export {
97
97
  k as d
98
98
  };
99
- //# sourceMappingURL=diff-BH4Na9pC.js.map
99
+ //# sourceMappingURL=diff-BO2igqdY.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"diff-BH4Na9pC.js","sources":["../src/analyze/diff.ts"],"sourcesContent":["// S7: one SDL, two diffs. A change to the annotated SDL changes both the\n// database (constraints, indexes, labels, property names) and the public\n// API; `diffSchemas` reports both from the same two documents.\n\nimport * as graphql from \"graphql\";\nimport type { DocumentNode, GraphQLSchema } from \"graphql\";\nimport { name } from \"../compile/cypher.js\";\nimport { buildModel, type ModelOptions } from \"../model/build.js\";\nimport type { Field, GraphModel, ScalarField } from \"../model/types.js\";\nimport { buildSchema } from \"../schema/build.js\";\nimport {\n inferRequirements,\n requirementDdl,\n requirementId,\n type SchemaRequirement,\n} from \"./indexes.js\";\n\nexport interface ApiChange {\n /** graphql-js change type, e.g. `FIELD_REMOVED`. */\n type: string;\n description: string;\n}\n\nexport interface SchemaDiff {\n database: {\n create: SchemaRequirement[];\n drop: SchemaRequirement[];\n /**\n * Statements in the order to run them. Drops and data migrations are\n * destructive: review them before running.\n */\n statements: Array<{ text: string; destructive: boolean; reason: string }>;\n };\n api: {\n /** Changes that break existing clients. */\n breaking: ApiChange[];\n /** Changes that may change behaviour for existing clients. */\n dangerous: ApiChange[];\n };\n /** Consequences worth a human look, e.g. a rename kept by @alias. */\n notes: string[];\n}\n\nexport function diffSchemas(\n before: string | DocumentNode,\n after: string | DocumentNode,\n options: ModelOptions = {},\n): SchemaDiff {\n const oldModel = buildModel(before, options);\n const newModel = buildModel(after, options);\n\n const id = requirementId;\n const oldReqs = new Map(inferRequirements(oldModel).map((r) => [id(r), r]));\n const newReqs = new Map(inferRequirements(newModel).map((r) => [id(r), r]));\n const create = [...newReqs]\n .filter(([k]) => !oldReqs.has(k))\n .map(([, r]) => r);\n const drop = [...oldReqs].filter(([k]) => !newReqs.has(k)).map(([, r]) => r);\n\n const statements: SchemaDiff[\"database\"][\"statements\"] = [];\n const notes: string[] = [];\n migrations(oldModel, newModel, statements, notes);\n for (const r of drop) {\n statements.push({\n text: `DROP ${r.kind === \"constraint\" ? \"CONSTRAINT\" : \"INDEX\"} \\`${r.name.replace(/`/g, \"``\")}\\` IF EXISTS`,\n destructive: true,\n reason: `no longer needed: ${r.reason}`,\n });\n }\n for (const r of create) {\n statements.push({\n text: requirementDdl(r),\n destructive: false,\n reason: r.reason,\n });\n }\n\n const api = apiChanges(publicSchema(oldModel), publicSchema(newModel));\n return { database: { create, drop, statements }, api, notes };\n}\n\n/** Relabelled types and renamed properties need their data moved. */\nfunction migrations(\n before: GraphModel,\n after: GraphModel,\n statements: SchemaDiff[\"database\"][\"statements\"],\n notes: string[],\n) {\n for (const [typeName, oldNode] of before.nodes) {\n const newNode = after.nodes.get(typeName);\n if (!newNode) {\n notes.push(\n `${typeName} was removed; its :${oldNode.labels[0]} nodes stay in the database`,\n );\n continue;\n }\n const oldLabel = oldNode.labels[0]!;\n const newLabel = newNode.labels[0]!;\n if (oldLabel !== newLabel) {\n statements.push({\n text: `MATCH (n:${name(oldLabel)}) SET n:${name(newLabel)} REMOVE n:${name(oldLabel)}`,\n destructive: true,\n reason: `${typeName}'s label changed from ${oldLabel} to ${newLabel}`,\n });\n }\n const oldFields = scalars(oldNode.fields);\n const newFields = scalars(newNode.fields);\n for (const [fieldName, oldField] of oldFields) {\n const newField = newFields.get(fieldName);\n if (newField && newField.property !== oldField.property) {\n statements.push({\n text: `MATCH (n:${name(newLabel)}) WHERE n.${name(oldField.property)} IS NOT NULL SET n.${name(newField.property)} = n.${name(oldField.property)} REMOVE n.${name(oldField.property)}`,\n destructive: true,\n reason: `${typeName}.${fieldName} now stores \\`${newField.property}\\` instead of \\`${oldField.property}\\``,\n });\n }\n if (!newField) {\n const renamed = [...newFields.values()].find(\n (f) => f.property === oldField.property && !oldFields.has(f.name),\n );\n if (renamed) {\n notes.push(\n `${typeName}.${fieldName} is now ${typeName}.${renamed.name} over the same property \\`${oldField.property}\\`: an API break with no data migration`,\n );\n }\n }\n }\n }\n}\n\nfunction scalars(fields: ReadonlyMap<string, Field>): Map<string, ScalarField> {\n const out = new Map<string, ScalarField>();\n for (const f of fields.values()) if (f.kind === \"scalar\") out.set(f.name, f);\n return out;\n}\n\nfunction publicSchema(model: GraphModel): GraphQLSchema {\n const never = () => Promise.reject(new Error(\"not connected\"));\n return buildSchema(model, {\n customResolver: () => () => null,\n resolveSearch: never,\n resolveAbstract: never,\n subscribe: () => {\n throw new Error(\"not connected\");\n },\n resolveChangedNode: never,\n resolveRoot: never,\n resolveNode: never,\n resolveCypher: never,\n resolveMutation: never,\n });\n}\n\ntype ChangeFinder = (\n a: GraphQLSchema,\n b: GraphQLSchema,\n) => Array<{ type: string; description: string }>;\n\nfunction apiChanges(\n before: GraphQLSchema,\n after: GraphQLSchema,\n): SchemaDiff[\"api\"] {\n // graphql-js 16 ships these; 17 removed them.\n const lib = graphql as unknown as {\n findBreakingChanges?: ChangeFinder;\n findDangerousChanges?: ChangeFinder;\n };\n if (!lib.findBreakingChanges || !lib.findDangerousChanges) {\n return {\n breaking: [\n {\n type: \"UNSUPPORTED\",\n description:\n \"this graphql version has no findBreakingChanges; compare printPublicSchema() output with @graphql-inspector/core\",\n },\n ],\n dangerous: [],\n };\n }\n const pick = (c: { type: string; description: string }) => ({\n type: c.type,\n description: c.description,\n });\n return {\n breaking: lib.findBreakingChanges(before, after).map(pick),\n dangerous: lib.findDangerousChanges(before, after).map(pick),\n };\n}\n"],"names":["diffSchemas","before","after","options","oldModel","buildModel","newModel","id","requirementId","oldReqs","inferRequirements","r","newReqs","create","k","drop","statements","notes","migrations","requirementDdl","api","apiChanges","publicSchema","typeName","oldNode","newNode","oldLabel","newLabel","name","oldFields","scalars","newFields","fieldName","oldField","newField","renamed","f","fields","out","model","never","buildSchema","lib","graphql","pick","c"],"mappings":"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"}
1
+ {"version":3,"file":"diff-BO2igqdY.js","sources":["../src/analyze/diff.ts"],"sourcesContent":["// S7: one SDL, two diffs. A change to the annotated SDL changes both the\n// database (constraints, indexes, labels, property names) and the public\n// API; `diffSchemas` reports both from the same two documents.\n\nimport * as graphql from \"graphql\";\nimport type { DocumentNode, GraphQLSchema } from \"graphql\";\nimport { name } from \"../compile/cypher.js\";\nimport { buildModel, type ModelOptions } from \"../model/build.js\";\nimport type { Field, GraphModel, ScalarField } from \"../model/types.js\";\nimport { buildSchema } from \"../schema/build.js\";\nimport {\n inferRequirements,\n requirementDdl,\n requirementId,\n type SchemaRequirement,\n} from \"./indexes.js\";\n\nexport interface ApiChange {\n /** graphql-js change type, e.g. `FIELD_REMOVED`. */\n type: string;\n description: string;\n}\n\nexport interface SchemaDiff {\n database: {\n create: SchemaRequirement[];\n drop: SchemaRequirement[];\n /**\n * Statements in the order to run them. Drops and data migrations are\n * destructive: review them before running.\n */\n statements: Array<{ text: string; destructive: boolean; reason: string }>;\n };\n api: {\n /** Changes that break existing clients. */\n breaking: ApiChange[];\n /** Changes that may change behaviour for existing clients. */\n dangerous: ApiChange[];\n };\n /** Consequences worth a human look, e.g. a rename kept by @alias. */\n notes: string[];\n}\n\nexport function diffSchemas(\n before: string | DocumentNode,\n after: string | DocumentNode,\n options: ModelOptions = {},\n): SchemaDiff {\n const oldModel = buildModel(before, options);\n const newModel = buildModel(after, options);\n\n const id = requirementId;\n const oldReqs = new Map(inferRequirements(oldModel).map((r) => [id(r), r]));\n const newReqs = new Map(inferRequirements(newModel).map((r) => [id(r), r]));\n const create = [...newReqs]\n .filter(([k]) => !oldReqs.has(k))\n .map(([, r]) => r);\n const drop = [...oldReqs].filter(([k]) => !newReqs.has(k)).map(([, r]) => r);\n\n const statements: SchemaDiff[\"database\"][\"statements\"] = [];\n const notes: string[] = [];\n migrations(oldModel, newModel, statements, notes);\n for (const r of drop) {\n statements.push({\n text: `DROP ${r.kind === \"constraint\" ? \"CONSTRAINT\" : \"INDEX\"} \\`${r.name.replace(/`/g, \"``\")}\\` IF EXISTS`,\n destructive: true,\n reason: `no longer needed: ${r.reason}`,\n });\n }\n for (const r of create) {\n statements.push({\n text: requirementDdl(r),\n destructive: false,\n reason: r.reason,\n });\n }\n\n const api = apiChanges(publicSchema(oldModel), publicSchema(newModel));\n return { database: { create, drop, statements }, api, notes };\n}\n\n/** Relabelled types and renamed properties need their data moved. */\nfunction migrations(\n before: GraphModel,\n after: GraphModel,\n statements: SchemaDiff[\"database\"][\"statements\"],\n notes: string[],\n) {\n for (const [typeName, oldNode] of before.nodes) {\n const newNode = after.nodes.get(typeName);\n if (!newNode) {\n notes.push(\n `${typeName} was removed; its :${oldNode.labels[0]} nodes stay in the database`,\n );\n continue;\n }\n const oldLabel = oldNode.labels[0]!;\n const newLabel = newNode.labels[0]!;\n if (oldLabel !== newLabel) {\n statements.push({\n text: `MATCH (n:${name(oldLabel)}) SET n:${name(newLabel)} REMOVE n:${name(oldLabel)}`,\n destructive: true,\n reason: `${typeName}'s label changed from ${oldLabel} to ${newLabel}`,\n });\n }\n const oldFields = scalars(oldNode.fields);\n const newFields = scalars(newNode.fields);\n for (const [fieldName, oldField] of oldFields) {\n const newField = newFields.get(fieldName);\n if (newField && newField.property !== oldField.property) {\n statements.push({\n text: `MATCH (n:${name(newLabel)}) WHERE n.${name(oldField.property)} IS NOT NULL SET n.${name(newField.property)} = n.${name(oldField.property)} REMOVE n.${name(oldField.property)}`,\n destructive: true,\n reason: `${typeName}.${fieldName} now stores \\`${newField.property}\\` instead of \\`${oldField.property}\\``,\n });\n }\n if (!newField) {\n const renamed = [...newFields.values()].find(\n (f) => f.property === oldField.property && !oldFields.has(f.name),\n );\n if (renamed) {\n notes.push(\n `${typeName}.${fieldName} is now ${typeName}.${renamed.name} over the same property \\`${oldField.property}\\`: an API break with no data migration`,\n );\n }\n }\n }\n }\n}\n\nfunction scalars(fields: ReadonlyMap<string, Field>): Map<string, ScalarField> {\n const out = new Map<string, ScalarField>();\n for (const f of fields.values()) if (f.kind === \"scalar\") out.set(f.name, f);\n return out;\n}\n\nfunction publicSchema(model: GraphModel): GraphQLSchema {\n const never = () => Promise.reject(new Error(\"not connected\"));\n return buildSchema(model, {\n customResolver: () => () => null,\n resolveSearch: never,\n resolveAbstract: never,\n subscribe: () => {\n throw new Error(\"not connected\");\n },\n resolveChangedNode: never,\n resolveRoot: never,\n resolveNode: never,\n resolveCypher: never,\n resolveMutation: never,\n });\n}\n\ntype ChangeFinder = (\n a: GraphQLSchema,\n b: GraphQLSchema,\n) => Array<{ type: string; description: string }>;\n\nfunction apiChanges(\n before: GraphQLSchema,\n after: GraphQLSchema,\n): SchemaDiff[\"api\"] {\n // graphql-js 16 ships these; 17 removed them.\n const lib = graphql as unknown as {\n findBreakingChanges?: ChangeFinder;\n findDangerousChanges?: ChangeFinder;\n };\n if (!lib.findBreakingChanges || !lib.findDangerousChanges) {\n return {\n breaking: [\n {\n type: \"UNSUPPORTED\",\n description:\n \"this graphql version has no findBreakingChanges; compare printPublicSchema() output with @graphql-inspector/core\",\n },\n ],\n dangerous: [],\n };\n }\n const pick = (c: { type: string; description: string }) => ({\n type: c.type,\n description: c.description,\n });\n return {\n breaking: lib.findBreakingChanges(before, after).map(pick),\n dangerous: lib.findDangerousChanges(before, after).map(pick),\n };\n}\n"],"names":["diffSchemas","before","after","options","oldModel","buildModel","newModel","id","requirementId","oldReqs","inferRequirements","r","newReqs","create","k","drop","statements","notes","migrations","requirementDdl","api","apiChanges","publicSchema","typeName","oldNode","newNode","oldLabel","newLabel","name","oldFields","scalars","newFields","fieldName","oldField","newField","renamed","f","fields","out","model","never","buildSchema","lib","graphql","pick","c"],"mappings":"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"}