@loradb/lora-graphql 0.26.0 → 0.26.2

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/README.md CHANGED
@@ -266,7 +266,9 @@ lists stored values that no longer pass. See the
266
266
  out of the `OR`, and an `OR` or `NOT` with nothing left is left out
267
267
  entirely, so an unset variable never widens a filter to every row; a
268
268
  literal `OR: []` matches nothing. A relationship quantifier whose filter
269
- is empty is left out too: ask `count: { gt: 0 }` for "has any". A single
269
+ is emptied that way is left out too; written as a literal `some: {}`,
270
+ `none: {}` or `single: {}` it is a validation error that names the
271
+ `count` filter to ask instead (`count: { gt: 0 }` for "has any"). A single
270
272
  relationship takes `<field>Exists: Boolean` for "is set" (`venueExists:
271
273
  false`: festivals without a venue); as in every relationship filter, a
272
274
  related node the reader may not see counts as none.
@@ -1987,7 +1989,7 @@ before a following `SET`, list comparison (`[a, b] > $list`) and
1987
1989
  | An aggregate nested in a call (`head(collect(x))`, `collect(x)[0..2]`) is not aggregated | `WITH collect(x) AS c RETURN head(c)`; `reduce` for per-parent aggregates |
1988
1990
  | `max`, `sum` and `avg` over durations are wrong | Duration aggregates are folded with `reduce` |
1989
1991
  | Integer division returns a float; negative list slices (`l[..-1]`) return `[]` | `toInteger(a / b)` for Int fields; `l[..size(l) - n]` |
1990
- | `COUNT { … RETURN DISTINCT x }` and `UNION` inside `CALL` do not parse | `reduce` for distinct counts; per-member subqueries |
1992
+ | `COUNT { … RETURN DISTINCT x }` and `UNION` inside `CALL` do not parse | `list.unique` for distinct counts; per-member subqueries |
1991
1993
 
1992
1994
  ## Development
1993
1995
 
package/dist/cli.js CHANGED
@@ -3,8 +3,8 @@ import { execFileSync as ue } from "node:child_process";
3
3
  import { stat as F, readFile as le, readdir as fe, mkdir as de, writeFile as M } from "node:fs/promises";
4
4
  import { dirname as pe, basename as me, join as k, relative as he } from "node:path";
5
5
  import { parse as x, Kind as p, visit as Z, print as K, typeFromAST as ge, isNonNullType as W, isListType as ve, getNamedType as we, isEnumType as be, isInputObjectType as Ee } from "graphql";
6
- import { d as Te } from "./diff-o9laZ0uY.js";
7
- import { w as X, j as ye, c as ee, e as Ne, M as Se, a as P, l as te, g as C, x as ne, i as $e, r as Ie, y as ke } from "./lora-graphql-lDHxpET-.js";
6
+ import { d as Te } from "./diff-CQtCicn7.js";
7
+ import { w as X, j as ye, c as ee, e as Ne, M as Se, a as P, l as te, g as C, x as ne, i as $e, r as Ie, y as ke } from "./lora-graphql-CjaQGsy7.js";
8
8
  const Oe = /* @__PURE__ */ new Set(["Query", "Mutation", "Subscription"]), z = {
9
9
  coalesce: "@coalesce is not supported: it hides predicates from indexes; store a value instead",
10
10
  subscriptionsAuthorization: "@subscriptionsAuthorization is not supported: SUBSCRIBE rules go in @authorization",
@@ -12,6 +12,7 @@ export interface AggregateOptions {
12
12
  export declare function listAggregate(ctx: CompileContext, kind: ListAggregate, list: Expr, options?: AggregateOptions): Expr;
13
13
  /**
14
14
  * The number of distinct values in a list: related nodes counted once
15
- * even when several relationships lead to the same node.
15
+ * even when several relationships lead to the same node. Node ids dedupe
16
+ * in linear time.
16
17
  */
17
- export declare function distinctCount(ctx: CompileContext, keys: Expr): Expr;
18
+ export declare function distinctCount(keys: Expr): Expr;
@@ -1,5 +1,5 @@
1
1
  import * as b from "graphql";
2
- import { c as $, i as w, r as E, n as C, o as h, q as N, u as k } from "./lora-graphql-lDHxpET-.js";
2
+ import { c as $, i as w, r as E, n as C, o as h, q as N, u as k } from "./lora-graphql-CjaQGsy7.js";
3
3
  function q(i, n, o = {}) {
4
4
  const c = $(i, o), t = $(n, o), f = C, d = new Map(w(c).map((e) => [f(e), e])), s = new Map(w(t).map((e) => [f(e), e])), r = [...s].filter(([e]) => !d.has(e)).map(([, e]) => e), l = [...d].filter(([e]) => !s.has(e)).map(([, e]) => e), a = [], u = [];
5
5
  R(c, t, a, u);
@@ -131,4 +131,4 @@ function S(i, n) {
131
131
  export {
132
132
  q as d
133
133
  };
134
- //# sourceMappingURL=diff-o9laZ0uY.js.map
134
+ //# sourceMappingURL=diff-CQtCicn7.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"diff-o9laZ0uY.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 { compareConstraints } from \"../model/constraints.js\";\nimport { buildModel } from \"../model/build.js\";\nimport { type ModelOptions } from \"../model/build/limits.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 ruleChanges(oldModel, newModel, api, notes);\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\n/**\n * Validation rules are not part of the GraphQL type system, so graphql-js\n * sees no change when one is tightened: a `String` is still a `String`.\n * Compared here, on the model: a rule that now refuses input it accepted\n * breaks clients that send it.\n */\nfunction ruleChanges(\n before: GraphModel,\n after: GraphModel,\n api: SchemaDiff[\"api\"],\n notes: string[],\n) {\n const owners = (model: GraphModel) =>\n new Map<string, ReadonlyMap<string, Field | ScalarField>>([\n ...[...model.nodes].map(([n, t]) => [n, t.fields] as const),\n ...[...model.relationshipProperties].map(\n ([n, t]) => [n, t.fields] as const,\n ),\n ]);\n const old = owners(before);\n let stricter = 0;\n for (const [owner, fields] of owners(after)) {\n const oldFields = old.get(owner);\n if (!oldFields) continue;\n for (const f of fields.values()) {\n const was = oldFields.get(f.name);\n if (f.kind !== \"scalar\" || was?.kind !== \"scalar\") continue;\n const { tightened, changed } = compareConstraints(\n was.constraints,\n f.constraints,\n );\n for (const what of tightened) {\n stricter++;\n api.breaking.push({\n type: \"RULE_TIGHTENED\",\n description: `${owner}.${f.name}: ${what}. Input the old rule accepted is now refused.`,\n });\n }\n for (const what of changed) {\n stricter++;\n api.dangerous.push({\n type: \"RULE_CHANGED\",\n description: `${owner}.${f.name}: ${what}.`,\n });\n }\n }\n }\n if (stricter > 0) {\n notes.push(\n `${stricter === 1 ? \"A validation rule was\" : `${stricter} validation rules were`} added, tightened or changed: values already stored are not revisited. \\`lora-graphql check --database\\` lists the ones that no longer pass.`,\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 previousValue: () => null,\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","ruleChanges","typeName","oldNode","newNode","oldLabel","newLabel","name","oldFields","scalars","newFields","fieldName","oldField","newField","renamed","f","owners","model","n","t","old","stricter","owner","fields","was","tightened","changed","compareConstraints","what","out","never","buildSchema","lib","graphql","pick","c"],"mappings":"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"}
1
+ {"version":3,"file":"diff-CQtCicn7.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 { compareConstraints } from \"../model/constraints.js\";\nimport { buildModel } from \"../model/build.js\";\nimport { type ModelOptions } from \"../model/build/limits.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 ruleChanges(oldModel, newModel, api, notes);\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\n/**\n * Validation rules are not part of the GraphQL type system, so graphql-js\n * sees no change when one is tightened: a `String` is still a `String`.\n * Compared here, on the model: a rule that now refuses input it accepted\n * breaks clients that send it.\n */\nfunction ruleChanges(\n before: GraphModel,\n after: GraphModel,\n api: SchemaDiff[\"api\"],\n notes: string[],\n) {\n const owners = (model: GraphModel) =>\n new Map<string, ReadonlyMap<string, Field | ScalarField>>([\n ...[...model.nodes].map(([n, t]) => [n, t.fields] as const),\n ...[...model.relationshipProperties].map(\n ([n, t]) => [n, t.fields] as const,\n ),\n ]);\n const old = owners(before);\n let stricter = 0;\n for (const [owner, fields] of owners(after)) {\n const oldFields = old.get(owner);\n if (!oldFields) continue;\n for (const f of fields.values()) {\n const was = oldFields.get(f.name);\n if (f.kind !== \"scalar\" || was?.kind !== \"scalar\") continue;\n const { tightened, changed } = compareConstraints(\n was.constraints,\n f.constraints,\n );\n for (const what of tightened) {\n stricter++;\n api.breaking.push({\n type: \"RULE_TIGHTENED\",\n description: `${owner}.${f.name}: ${what}. Input the old rule accepted is now refused.`,\n });\n }\n for (const what of changed) {\n stricter++;\n api.dangerous.push({\n type: \"RULE_CHANGED\",\n description: `${owner}.${f.name}: ${what}.`,\n });\n }\n }\n }\n if (stricter > 0) {\n notes.push(\n `${stricter === 1 ? \"A validation rule was\" : `${stricter} validation rules were`} added, tightened or changed: values already stored are not revisited. \\`lora-graphql check --database\\` lists the ones that no longer pass.`,\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 previousValue: () => null,\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","ruleChanges","typeName","oldNode","newNode","oldLabel","newLabel","name","oldFields","scalars","newFields","fieldName","oldField","newField","renamed","f","owners","model","n","t","old","stricter","owner","fields","was","tightened","changed","compareConstraints","what","out","never","buildSchema","lib","graphql","pick","c"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { D as r, L as e, a as o, b as i, M as l, c as n, d, e as t, f as m, g as p, h as c, i as f, j as L, k as h, l as R, p as D, r as u, s as E, m as G, t as b, v } from "./lora-graphql-lDHxpET-.js";
2
- import { d as O } from "./diff-o9laZ0uY.js";
1
+ import { D as r, L as e, a as o, b as i, M as l, c as n, d, e as t, f as m, g as p, h as c, i as f, j as L, k as h, l as R, p as D, r as u, s as E, m as G, t as b, v } from "./lora-graphql-CjaQGsy7.js";
2
+ import { d as O } from "./diff-CQtCicn7.js";
3
3
  export {
4
4
  r as DEFAULT_GUARDS,
5
5
  e as LORA_GRAPHQL_ERROR_CODES,