grafeo-ogm 2.4.8 → 2.5.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.
Files changed (78) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.md +106 -27
  3. package/assets/brand/grafeo-mark-on-dark.svg +1 -0
  4. package/assets/brand/grafeo-mark-on-light.svg +1 -0
  5. package/dist/cjs/cli/commands/db-push.d.ts.map +1 -1
  6. package/dist/cjs/cli/commands/db-push.js +17 -5
  7. package/dist/cjs/cli/commands/db-push.js.map +1 -1
  8. package/dist/cjs/cli/push-planner.d.ts +6 -0
  9. package/dist/cjs/cli/push-planner.d.ts.map +1 -1
  10. package/dist/cjs/cli/push-planner.js +24 -5
  11. package/dist/cjs/cli/push-planner.js.map +1 -1
  12. package/dist/cjs/compilers/mutation.compiler.d.ts.map +1 -1
  13. package/dist/cjs/compilers/mutation.compiler.js +92 -51
  14. package/dist/cjs/compilers/mutation.compiler.js.map +1 -1
  15. package/dist/cjs/compilers/selection.compiler.d.ts +16 -0
  16. package/dist/cjs/compilers/selection.compiler.d.ts.map +1 -1
  17. package/dist/cjs/compilers/selection.compiler.js +158 -18
  18. package/dist/cjs/compilers/selection.compiler.js.map +1 -1
  19. package/dist/cjs/generator/type-emitters/where-emitter.js +16 -0
  20. package/dist/cjs/generator/type-emitters/where-emitter.js.map +1 -1
  21. package/dist/cjs/interface-model.d.ts +12 -0
  22. package/dist/cjs/interface-model.d.ts.map +1 -1
  23. package/dist/cjs/interface-model.js +21 -13
  24. package/dist/cjs/interface-model.js.map +1 -1
  25. package/dist/cjs/model.d.ts.map +1 -1
  26. package/dist/cjs/model.js +6 -0
  27. package/dist/cjs/model.js.map +1 -1
  28. package/dist/cjs/subgraph/subgraph-operations.d.ts.map +1 -1
  29. package/dist/cjs/subgraph/subgraph-operations.js +71 -6
  30. package/dist/cjs/subgraph/subgraph-operations.js.map +1 -1
  31. package/dist/cjs/subgraph/types.d.ts +14 -0
  32. package/dist/cjs/subgraph/types.d.ts.map +1 -1
  33. package/dist/cjs/testing/cypher-assert.d.ts +19 -1
  34. package/dist/cjs/testing/cypher-assert.d.ts.map +1 -1
  35. package/dist/cjs/testing/cypher-assert.js +192 -11
  36. package/dist/cjs/testing/cypher-assert.js.map +1 -1
  37. package/dist/cjs/testing/neo4j-record-factory.d.ts +8 -4
  38. package/dist/cjs/testing/neo4j-record-factory.d.ts.map +1 -1
  39. package/dist/cjs/testing/neo4j-record-factory.js +18 -7
  40. package/dist/cjs/testing/neo4j-record-factory.js.map +1 -1
  41. package/dist/esm/cli/commands/db-push.d.ts.map +1 -1
  42. package/dist/esm/cli/commands/db-push.js +17 -5
  43. package/dist/esm/cli/commands/db-push.js.map +1 -1
  44. package/dist/esm/cli/push-planner.d.ts +6 -0
  45. package/dist/esm/cli/push-planner.d.ts.map +1 -1
  46. package/dist/esm/cli/push-planner.js +24 -5
  47. package/dist/esm/cli/push-planner.js.map +1 -1
  48. package/dist/esm/compilers/mutation.compiler.d.ts.map +1 -1
  49. package/dist/esm/compilers/mutation.compiler.js +92 -51
  50. package/dist/esm/compilers/mutation.compiler.js.map +1 -1
  51. package/dist/esm/compilers/selection.compiler.d.ts +16 -0
  52. package/dist/esm/compilers/selection.compiler.d.ts.map +1 -1
  53. package/dist/esm/compilers/selection.compiler.js +160 -20
  54. package/dist/esm/compilers/selection.compiler.js.map +1 -1
  55. package/dist/esm/generator/type-emitters/where-emitter.js +16 -0
  56. package/dist/esm/generator/type-emitters/where-emitter.js.map +1 -1
  57. package/dist/esm/interface-model.d.ts +12 -0
  58. package/dist/esm/interface-model.d.ts.map +1 -1
  59. package/dist/esm/interface-model.js +21 -13
  60. package/dist/esm/interface-model.js.map +1 -1
  61. package/dist/esm/model.d.ts.map +1 -1
  62. package/dist/esm/model.js +6 -0
  63. package/dist/esm/model.js.map +1 -1
  64. package/dist/esm/subgraph/subgraph-operations.d.ts.map +1 -1
  65. package/dist/esm/subgraph/subgraph-operations.js +71 -6
  66. package/dist/esm/subgraph/subgraph-operations.js.map +1 -1
  67. package/dist/esm/subgraph/types.d.ts +14 -0
  68. package/dist/esm/subgraph/types.d.ts.map +1 -1
  69. package/dist/esm/testing/cypher-assert.d.ts +19 -1
  70. package/dist/esm/testing/cypher-assert.d.ts.map +1 -1
  71. package/dist/esm/testing/cypher-assert.js +192 -11
  72. package/dist/esm/testing/cypher-assert.js.map +1 -1
  73. package/dist/esm/testing/neo4j-record-factory.d.ts +8 -4
  74. package/dist/esm/testing/neo4j-record-factory.d.ts.map +1 -1
  75. package/dist/esm/testing/neo4j-record-factory.js +18 -7
  76. package/dist/esm/testing/neo4j-record-factory.js.map +1 -1
  77. package/package.json +1 -1
  78. package/assets/logo.jpg +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,77 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.5.1 (2026-10-18) — regression fixes
4
+
5
+ > **Patch release.** Fixes regressions introduced in **2.4.8** (writes) and a `db push --force-drop` bug. No API changes.
6
+
7
+ ### Writes
8
+
9
+ - **`connect: { rel: null }` linked every target node.** Since 2.4.8 a `null` connect value compiled to an unfiltered `MATCH`, so `update({ connect: { tags: null } })` linked the node to **every** `Tag`. A `null` value is now a no-op, and a `null` item inside a connect list (`tags: [null]`) throws `OGMError`.
10
+ - **Sibling nested connects shared variable and parameter names.** Two connect items that each carried a nested `connect` (two siblings, or two members of a union) reused the same prefix, so a link could go to the wrong node and edge properties were overwritten by the last sibling. Each item now gets its own prefix. Nested disconnects get the same fix.
11
+ - **Nested writes through an interface relationship touched non-member nodes.** When an interface relationship (`pets: [Pet!]!`) shares its relationship type with another field (`toys`), a nested `update`, nested `delete` or cascade `delete` on `pets` also matched the `Toy` nodes on that type, renaming or **deleting** them. These now match implementor labels only, as connect and disconnect already did.
12
+
13
+ ### CLI
14
+
15
+ - **`db push --force-drop` no longer drops an index it cannot recreate.** When another fulltext or vector index already held the SDL definition under a different name, push dropped the SDL-named index, the `CREATE … IF NOT EXISTS` was a silent no-op, and push still printed `✓ recreated`. Fulltext queries address the index by its SDL name, so they broke. The planner now reports the index as blocked by the equivalent one, and never drops it. A recreate that Neo4j reports as a no-op is now reported as an error rather than `✓ recreated`.
16
+
17
+ ## 2.5.0 (2026-10-18) — breaking fixes from the bug scan
18
+
19
+ > **Minor release. Every item below changes behaviour that callers may depend on.** Each change fixes a confirmed bug, and the old behaviour was wrong (see each entry). The non-breaking fixes from the same scan shipped in **2.4.8**.
20
+
21
+ ### Upgrade checklist
22
+
23
+ Use this to check whether you're affected. If none of these apply, 2.5.0 is a drop-in upgrade.
24
+
25
+ | If you… | Then… |
26
+ |---|---|
27
+ | use `InterfaceModel` | it now finds implementor nodes **without** the interface label, so result counts can grow (see below) |
28
+ | put arguments or aliases in `selectionSet` strings (`posts(where: …)`, `r: rank`) | they now take effect, and unsupported ones throw |
29
+ | pass non-`@id`/`@unique` keys in `upsert` `where` | they now throw |
30
+ | rely on `createMany({ skipDuplicates: true })` counting the skipped duplicates | it now returns the created count |
31
+ | write keys that aren't in the schema | they now throw `Unknown field …` |
32
+ | pass relationship fields in a later `createMany` item | they now throw |
33
+ | filter list fields with element-typed operators (`tags: 'x'`, `tags_CONTAINS`) | those no longer compile (they never matched) |
34
+ | use `CypherAssert` or `Neo4jRecordFactory.dateTime` in tests | stricter assertions may expose tests that passed by accident |
35
+ | use `cloneSubgraph` / `deleteSubgraph` | traversal is now outgoing-only by default, and the root is matched by label |
36
+
37
+ ### Reads
38
+
39
+ - **`InterfaceModel` matches implementors, not the interface label.** `find`, `count` and `aggregate` used to `MATCH (n:<Interface>)`. Nodes created with `Model.create` never carry that label, so these methods found nothing for nodes the OGM itself wrote. They now `MATCH (n:(A|B|…))` over the implementors, as `@neo4j/graphql` does. Per-member policies, `__typename` and the `labels` filter work as before.
40
+ - **What to do:** nodes carrying *only* the interface label, with no implementor label, are no longer returned; give them their concrete label. Recheck counts and pagination that depended on the old, smaller result. Listing the interface in `@node(labels: [...])` still works but is no longer needed.
41
+ - **`selectionSet` arguments and aliases take effect.** They were silently ignored, so `posts(where: …)` and `posts(options: { sort, limit, offset })` returned every related node, `r: rank` came back as `rank`, and two aliases of one field collapsed into one key. Now relationship fields honour `where` (with the target `read` policy applied) and `options` (`sort` needs APOC; `limit` and `offset` are bound parameters). Connection fields honour `where`, and aliases name the output keys.
42
+ - **Now throw `OGMError`:** any other argument, an unknown option or sort field, `options` on a singular relationship, GraphQL `$variables`, and arguments on a field reached through a union-typed parent.
43
+ - **What to do:** search for `selectionSet` strings with arguments or aliases. Their results now match what they ask for: filtered, paged and re-keyed.
44
+
45
+ ### Writes
46
+
47
+ - **`upsert` `where` accepts only `@id` / `@unique` fields.** A plain key used to join the `MERGE` pattern, so `where: { email, name: 'Bob' }` against an existing `{ email, name: 'Alice' }` created a **duplicate** with the same email. An operator key (`name_CONTAINS`) was silently dropped, which widened the match so the **wrong row** could be updated. These now throw `OGMError`: a plain, operator, logical or unknown key, and a `null` value. At least one unique key is required.
48
+ - **What to do:** keep only unique keys in `where` and move the other fields into `create` / `update`. For a non-unique match, use `findFirst()` followed by `create()` / `update()`.
49
+ - **Unknown keys in write input throw.** A key that is neither a property nor a relationship of the type (a typo like `nmae`) was written as a stray property, and the intended field was left unchanged. It now throws `Unknown field …`, in `create`, `update`, `updateMany`, `createMany`, `upsert`, and nested creates and updates at any depth.
50
+ - **What to do:** declare every property you write in the schema.
51
+ - **`createMany({ skipDuplicates: true })` returns the number of nodes created.** It used to include the duplicates it skipped. It now matches Prisma. Calls without `skipDuplicates` are unchanged.
52
+ - **`createMany` rejects relationship fields in every item.** Only `data[0]` used to be checked; later items were created without their relationship input. The error names the item, e.g. `"tags" in data[1]`. Use `create()` for nested relationship writes.
53
+
54
+ ### Generated types
55
+
56
+ - **List-scalar where operators are typed as lists (compile-time only).** For `tags: [String!]`, equality and `_NOT` take `Array<T>`, and `_IN` / `_NOT_IN` take `Array<Array<T>>`. String and comparison operators are no longer generated for list fields. The old element-typed forms (`{ tags: 'x' }`, `tags_CONTAINS`) compiled but **never matched**, while the working form `{ tags: ['x', 'y'] }` didn't compile. Runtime behaviour is unchanged.
57
+
58
+ ### Testing helpers
59
+
60
+ - **`CypherAssert.assertContainsClause` looks for the pattern inside the clause.** The clause and the pattern were checked independently, so `assertContainsClause(q, 'WHERE', 'n { .name }')` passed when the pattern was in the `RETURN`. `'SET'` also matched `$offset`. Keywords now match whole words, outside strings, names and comments, and the pattern must sit within that clause.
61
+ - **`CypherAssert.normalize` and `assertStructurallyEqual` keep string literals and backticked names verbatim.** `'a b'` used to equal `'a b'`, and a `$name` inside a string was renamed as a parameter.
62
+ - **`Neo4jRecordFactory.dateTime` reads an offset-less string as UTC**, as Neo4j does. It used to use the machine's local zone, so results differed between a laptop and CI. A string that isn't a date now throws a `TypeError`.
63
+
64
+ Assertions that only passed by accident will now fail.
65
+
66
+ ### Subgraph operations (APOC)
67
+
68
+ - **Owned relationships are followed outgoing by default.** A bare type was expanded in **both** directions. With two books sharing a chapter, `deleteSubgraph(bookA)` climbed back up the shared chapter and **deleted book B**, `deleteSubgraph(chapter)` deleted its parent book, and `cloneSubgraph` pulled in the other parents. Now a bare `'TYPE'` is outgoing, `'<TYPE'` is incoming, and `'<TYPE>'` is both directions (the old behaviour).
69
+ - **What to do:** mark relationships you traverse backwards as `'<TYPE'` or `'<TYPE>'`.
70
+ - **The root is matched by label, and owned nodes without an `id` are refused.** The root was matched by `id` alone, so any node with that id became a root, found by scanning every node. It's now `(:rootLabel {id})`. The new optional `SubgraphConfig.rootLabel` defaults to `ownedLabels[0]`. `cloneSubgraph` now aborts before writing anything if an owned node has no `id` or shares one with another owned node; previously those nodes collapsed onto one mapping entry.
71
+ - **What to do:** set `rootLabel` if your root isn't of the first owned label.
72
+
73
+ Every entry above has a regression test, live wherever behaviour depends on the server. The subgraph changes are verified against Neo4j 5.26 with APOC 5.26.
74
+
3
75
  ## 2.4.8 (2026-10-18) — bug-scan fixes across reads, writes, codegen, CLI and testing helpers
4
76
 
5
77
  > **A large patch release. Upgrading is safe, with no breaking changes.** Most of these bugs were found by a third, multi-area bug scan. Each fix was reproduced on Neo4j 5.26 before it was fixed, and is pinned by a regression test, live wherever behaviour depends on the server. The breaking fixes from the same scan ship separately in **2.5.0**. Cypher for queries the fixes don't touch is unchanged; the policy byte-identical suite pins this.
package/README.md CHANGED
@@ -1,8 +1,13 @@
1
1
  <p align="center">
2
- <img src="./assets/logo.jpg" alt="grafeo-ogm — Type-safe OGM for Neo4j" width="640" />
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="./assets/brand/grafeo-mark-on-dark.svg" />
4
+ <img src="./assets/brand/grafeo-mark-on-light.svg" alt="grafeo-ogm" width="96" height="96" />
5
+ </picture>
3
6
  </p>
4
7
 
5
- <p align="center"><strong>A type-safe Object-Graph Mapper for Neo4j, driven by GraphQL SDL.</strong></p>
8
+ <h1 align="center">grafeo-ogm</h1>
9
+
10
+ <p align="center"><strong>Type-safe Neo4j queries, without the boilerplate.</strong><br />A type-safe object-graph mapper for Neo4j, driven by GraphQL SDL.</p>
6
11
 
7
12
  [![npm version](https://img.shields.io/npm/v/grafeo-ogm.svg)](https://www.npmjs.com/package/grafeo-ogm)
8
13
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
@@ -216,8 +221,8 @@ await Book.delete({ where: { isbn: '978-1491930892' } });
216
221
 
217
222
  ```typescript
218
223
  await ogm.$transaction(async (ctx) => {
219
- await Book.create({ input: [{ title: 'Book A' }] }, { context: ctx });
220
- await Book.create({ input: [{ title: 'Book B' }] }, { context: ctx });
224
+ await Book.create({ input: [{ title: 'Book A' }], context: ctx });
225
+ await Book.create({ input: [{ title: 'Book B' }], context: ctx });
221
226
  // Both committed atomically, or both rolled back on error
222
227
  });
223
228
  ```
@@ -397,7 +402,7 @@ grafeo-ogm uses standard GraphQL SDL with Neo4j directives to define your graph
397
402
 
398
403
  | Directive | Target | Description |
399
404
  |---|---|---|
400
- | `@node` | Type | Marks a type as a Neo4j node. Optional `labels` argument for multi-label nodes (implementors of an interface must include the interface name, see [Interfaces](#interfaces)) |
405
+ | `@node` | Type | Marks a type as a Neo4j node. Optional `labels` argument for multi-label nodes (implementors of an interface no longer need to include the interface name since v2.5.0, see [Interfaces](#interfaces)) |
401
406
  | `@relationship` | Field | Defines a relationship with `type`, `direction` (exactly `IN` or `OUT`; anything else is a schema error), and optional `properties` |
402
407
  | `@declareRelationship` | Interface field | Declares a relationship on an interface. Its `type`, `direction` and `properties` come from the implementors' `@relationship`, which must all agree |
403
408
  | `@relationshipProperties` | Type | Marks a type as relationship properties (edge data) |
@@ -483,7 +488,7 @@ type Resource @node {
483
488
 
484
489
  Interface types can be queried polymorphically via `ogm.interfaceModel('Entity')`. Results include `__typename` to discriminate concrete types.
485
490
 
486
- - The interface model matches nodes by the **interface label** (`MATCH (n:Entity)`), so every implementor must carry it: list the interface name in `@node(labels: [...])`. An implementor declared with a bare `@node` is stored with only its own label and never appears in interface queries.
491
+ - The interface model matches nodes by their **implementors' labels** (`MATCH (n:(User|Organization))`), so a node created with `ogm.model('User').create(...)` appears in interface queries with no extra labels. Since v2.5.0 an implementor does **not** need to carry the interface name; before v2.5.0 it had to list it in `@node(labels: [...])`, which still works.
487
492
  - A `@declareRelationship` field takes its relationship `type`, `direction` and `properties` from the implementors. They must all declare the same ones; if they disagree, or an implementor has no `@relationship` on that field, `parseSchema` throws.
488
493
 
489
494
  ### Enums
@@ -587,6 +592,15 @@ const book = await Book.findUniqueOrThrow({
587
592
  | `_NOT_ENDS_WITH` | `NOT ENDS WITH` | `{ name_NOT_ENDS_WITH: 'x' }` |
588
593
  | `_MATCHES` | `=~` (regex) | `{ name_MATCHES: '^A.*' }` |
589
594
 
595
+ **List fields** (`tags: [String!]`) are compared as a whole list, so only these operators exist for them:
596
+
597
+ | Operator | Matches when | Example |
598
+ |---|---|---|
599
+ | *(equality)* | the stored list equals the given list | `{ tags: ['a', 'b'] }` |
600
+ | `_NOT` | the stored list differs from the given list | `{ tags_NOT: ['a'] }` |
601
+ | `_IN` | the stored list equals one of the given lists | `{ tags_IN: [['a'], ['b', 'c']] }` |
602
+ | `_NOT_IN` | the stored list equals none of the given lists | `{ tags_NOT_IN: [['a']] }` |
603
+
590
604
  **Relationship quantifiers:**
591
605
 
592
606
  | Operator | Meaning | Example |
@@ -686,6 +700,21 @@ const books = await Book.find({
686
700
  Book.selectionSet = `{ id title }`;
687
701
  ```
688
702
 
703
+ Since v2.5.0, `selectionSet` field **arguments and aliases** take effect, as in the original `@neo4j/graphql-ogm`:
704
+
705
+ ```typescript
706
+ const books = await Book.find({
707
+ selectionSet: `{
708
+ id
709
+ t: title # alias → key "t"
710
+ recent: categories(where: { name_STARTS_WITH: "Tech" }, options: { limit: 3 }) { name }
711
+ categoriesConnection(where: { node: { name: "Science" } }) { totalCount edges { node { name } } }
712
+ }`,
713
+ });
714
+ ```
715
+
716
+ Relationship fields accept `where` and `options` (`sort` needs APOC; `limit` / `offset`), connection fields accept `where`, and any other argument throws `OGMError` rather than being ignored. Under [Node-Level Security](#node-level-security) the target type's `read` policy applies, exactly as for a typed `select` with `where`. Connection `totalCount` is filtered by the same `where` and policy as `edges`.
717
+
689
718
  **Default (no selection):** Returns all scalar fields of the matched node.
690
719
 
691
720
  ### Nested Sorting
@@ -831,6 +860,10 @@ const { count } = await Book.createMany({
831
860
  });
832
861
  ```
833
862
 
863
+ - Items may have different keys: each item gets every field it supplies. A field an item omits gets what `create()` would give it — a generated `@id`, its `@default`, or nothing — and an explicit `null` stays `null`.
864
+ - With `skipDuplicates`, the `MERGE` key is the `@id` / `@unique` fields that **every** item supplies, and `count` is the number of nodes actually **created** (skipped duplicates aren't counted).
865
+ - Relationship fields are rejected in any item; use `create()` for nested writes.
866
+
834
867
  ### Update
835
868
 
836
869
  ```typescript
@@ -846,6 +879,11 @@ const result = await Book.update({
846
879
  });
847
880
  ```
848
881
 
882
+ - **Connect** links every target matching its `where`. A `where` that matches nothing is a no-op: the update still applies and the node is still returned (as in `@neo4j/graphql`).
883
+ - **Disconnect** with `{}` / `null` removes every relationship of that type — under [Node-Level Security](#node-level-security), only to targets the context can **read**.
884
+ - Every connect and disconnect returns the updated node exactly once, however many relationships it touched.
885
+ - A key that is neither a property nor a relationship of the type throws `OGMError` (`Unknown field …`) instead of being written as a stray property — at every depth.
886
+
849
887
  ### Update Many
850
888
 
851
889
  ```typescript
@@ -900,6 +938,10 @@ const book = await Book.upsert({
900
938
  });
901
939
  ```
902
940
 
941
+ - `where` must contain at least one `@id` / `@unique` field, and **only** such fields — it becomes the `MERGE` key. Any other key (a plain field, an operator like `name_CONTAINS`, `AND` / `OR`) or a `null` value throws. For a non-unique match, use `findFirst()` followed by `create()` / `update()`.
942
+ - On the create branch, `@id` fields are generated (unless supplied) and `@default` values applied, exactly as `create()` does.
943
+ - `upsert` writes scalars only: relationship fields in `create` / `update` throw.
944
+
903
945
  ### Label Management
904
946
 
905
947
  ```typescript
@@ -941,6 +983,27 @@ await Book.create({
941
983
  });
942
984
  ```
943
985
 
986
+ Nested writes inside `update` take one item per operation group:
987
+
988
+ ```typescript
989
+ await Book.update({
990
+ where: { id: 'book-1' },
991
+ update: {
992
+ categories: [
993
+ {
994
+ disconnect: [{}], // drop all current categories…
995
+ create: [{ node: { name: 'New' }, edge: { isPrimary: true } }], // …and link a new one
996
+ },
997
+ ],
998
+ },
999
+ });
1000
+ ```
1001
+
1002
+ - **Order within an item** (as in `@neo4j/graphql`): `delete`, `disconnect`, `connect`, `create`, `update`. So `disconnect: [{}]` + `create` replaces the relationship set.
1003
+ - **Nested connect / disconnect** recurse: `connect: { posts: [{ where, connect: { tags: [...] } }] }` links the post *and* its tags (max 5 levels).
1004
+ - **Interface and union relationships** work for every nested op. Key the input by member type (`pets: { Dog: { create: [...] } }`), or use the flat shape and name the implementer on create (`create: [{ node: { Dog: { name: 'Rex' } } }]`).
1005
+ - With [Node-Level Security](#node-level-security), every target applies its own policy: `read` to connect / disconnect, `create` (+ `WITH CHECK`) to nested creates, `update` to nested updates, `delete` to nested deletes.
1006
+
944
1007
  ---
945
1008
 
946
1009
  ## Advanced Features
@@ -1044,6 +1107,10 @@ The compiler reads `OR`, then `AND`, then `NOT`, and stops at the first one it f
1044
1107
 
1045
1108
  Clone or delete entire subgraphs using APOC procedures. Requires the APOC plugin installed in Neo4j.
1046
1109
 
1110
+ The traversal starts at the root and follows `ownedRelationships` into nodes carrying one of `ownedLabels`. A bare type such as `'HAS_CHAPTER'` is followed **outgoing only**; write `'<CHILD_OF'` to follow a relationship incoming, or `'<LINKED>'` to follow it both ways. Before v2.5.0 a bare type was followed both ways, so deleting one book could climb from a chapter it shared with another book up to that book and delete it too; add `<…>` to keep the old behavior for a type.
1111
+
1112
+ The root is matched as `(:rootLabel {id: rootId})`, with `rootLabel` defaulting to the first entry of `ownedLabels` (v2.5.0; before, a node of any label with that id was a root). Set `rootLabel` when the root is not of that label — a delete whose root isn't found deletes nothing and returns `deletedCount: 0`. `cloneSubgraph` maps every owned node by its `id`, so it refuses, before cloning anything, a subgraph in which an owned node has no `id` or shares one with another owned node.
1113
+
1047
1114
  ```typescript
1048
1115
  import { cloneSubgraph, deleteSubgraph } from 'grafeo-ogm';
1049
1116
 
@@ -1069,12 +1136,13 @@ const cloneResult = await cloneSubgraph(
1069
1136
  // v1.13.0+ — post-process only the nodes of one label, no broad-IN filtering:
1070
1137
  const chapterIds = cloneResult.nodesByLabel.get('Chapter') ?? [];
1071
1138
 
1072
- // Delete a subgraph
1139
+ // Delete a subgraph rooted at a Chapter: its sections, not its Book
1073
1140
  const deleteResult = await deleteSubgraph(
1074
- 'root-node-id',
1141
+ 'chapter-node-id',
1075
1142
  {
1076
- ownedLabels: ['Book', 'Chapter'],
1077
- ownedRelationships: ['HAS_CHAPTER'],
1143
+ rootLabel: 'Chapter', // v2.5.0+ — defaults to ownedLabels[0]
1144
+ ownedLabels: ['Chapter', 'Section'],
1145
+ ownedRelationships: ['HAS_SECTION'], // outgoing; '<TYPE' incoming, '<TYPE>' both
1078
1146
  maxLevel: 5,
1079
1147
  referenceRelationships: [],
1080
1148
  },
@@ -1123,7 +1191,7 @@ type Book @node {
1123
1191
 
1124
1192
  Before v2.4.0, top-level fields dropped nodes whose statement returned no rows and duplicated nodes whose statement returned several. See the [2.4.0 changelog](CHANGELOG.md).
1125
1193
 
1126
- > **⚠️ Security — `@cypher` SDL is trusted code, not data.** The `statement` argument is interpolated **verbatim** into the compiled Cypher (no parameterisation, no escaping). Treat `@cypher` directives the same way you treat `eval()` in your application code: the string MUST be a constant authored at development time, never built from runtime input. If your `typeDefs` are ever assembled from user input, environment variables, database records, or remote configuration, you have a Cypher-injection vector equivalent to RCE on the database (`MATCH (n) DETACH DELETE n RETURN '' AS x` in a `statement` will wipe the graph). The grafeo-ogm runtime cannot distinguish between developer-authored and user-derived SDL — that boundary is the application's responsibility.
1194
+ > **Security — `@cypher` SDL is trusted code, not data.** The `statement` argument is interpolated **verbatim** into the compiled Cypher (no parameterisation, no escaping). Treat `@cypher` directives the same way you treat `eval()` in your application code: the string MUST be a constant authored at development time, never built from runtime input. If your `typeDefs` are ever assembled from user input, environment variables, database records, or remote configuration, you have a Cypher-injection vector equivalent to RCE on the database (`MATCH (n) DETACH DELETE n RETURN '' AS x` in a `statement` will wipe the graph). The grafeo-ogm runtime cannot distinguish between developer-authored and user-derived SDL — that boundary is the application's responsibility.
1127
1195
 
1128
1196
  #### Using @cypher in SELECT
1129
1197
 
@@ -1183,13 +1251,12 @@ await Book.find({
1183
1251
 
1184
1252
  #### Limitations
1185
1253
 
1186
- The following positions throw `OGMError` at compile time with a clear hint, rather than silently misbehaving:
1254
+ `@cypher` fields work at the top level and through relationships — nested selections (`select: { author: { select: { upper: true } } }`) and connection filters (`authorsConnection_SOME: { node: { upper: 'ANN' } }`) included. Two positions throw `OGMError` at compile time with a clear hint, rather than silently misbehaving:
1187
1255
 
1188
- - **Connection `where` and `select.where`**: Prisma-style relationship filtering with `@cypher` is unsupported. Workaround: filter on the parent type, or query the related node directly via its own model.
1189
- - **`_SINGLE` / `Connection_SINGLE` quantifiers**: unsupported. Workaround: combine `_SOME` and `_NONE` to express "exactly one match."
1190
- - **Nested-relationship SELECT projection**: `select: { rel: { select: { cypherField: true } } }` is unsupported. Workaround: query the related node directly via its own model and join in application code.
1256
+ - **An aggregating statement selected through a relationship** — e.g. `postCount: … RETURN count(p) AS postCount` in `{ author { postCount } }`. Nested fields are projected inline (`head(COLLECT { … })`), and Neo4j 5 rejects aggregation there. Select it at the top level (`Author.find({ select: { postCount: true } })`), where it is computed in a `CALL` subquery and works.
1257
+ - **`_SINGLE` / `Connection_SINGLE` on a `@cypher` field.** Workaround: combine `_SOME` and `_NONE` to express "exactly one match."
1191
1258
 
1192
- These are not OGM bugs — they are Cypher-language constraints. Pattern comprehensions (`[(n)-[:R]->(m) | m { ... }]`) cannot host `CALL` subqueries, so resolving an `@cypher` field inside a projection or relationship-scoped predicate is impossible at the language level.
1259
+ These are Cypher-language constraints, not OGM bugs: a pattern comprehension cannot host a `CALL` subquery, and aggregation is not allowed inside one.
1193
1260
 
1194
1261
  #### Performance
1195
1262
 
@@ -1451,18 +1518,19 @@ const { recordsAffected } = await ogm.$executeRaw(
1451
1518
  ```typescript
1452
1519
  // Callback style — interactive transaction
1453
1520
  await ogm.$transaction(async (ctx) => {
1454
- await Book.create({ input: [{ title: 'Book A' }] }, { context: ctx });
1455
- await Book.update(
1456
- { where: { id: 'book-1' }, update: { title: 'Updated' } },
1457
- { context: ctx },
1458
- );
1521
+ await Book.create({ input: [{ title: 'Book A' }], context: ctx });
1522
+ await Book.update({
1523
+ where: { id: 'book-1' },
1524
+ update: { title: 'Updated' },
1525
+ context: ctx,
1526
+ });
1459
1527
  // Committed on success, rolled back on error
1460
1528
  });
1461
1529
 
1462
1530
  // Sequential style — array of operations
1463
1531
  const [books, authors] = await ogm.$transaction([
1464
- (ctx) => Book.find({ select: { id: true } }, { context: ctx }),
1465
- (ctx) => Author.find({ select: { id: true } }, { context: ctx }),
1532
+ (ctx) => Book.find({ select: { id: true }, context: ctx }),
1533
+ (ctx) => Author.find({ select: { id: true }, context: ctx }),
1466
1534
  ]);
1467
1535
 
1468
1536
  // Transaction metadata (v2.4.8) — sent to the server when the tx begins,
@@ -1502,6 +1570,8 @@ const agg = await Entity.aggregate({
1502
1570
 
1503
1571
  Interface models are **read-only**. Use `ogm.model('User')` or `ogm.model('Organization')` for mutations.
1504
1572
 
1573
+ Since v2.5.0 they match nodes by their implementors' labels (`MATCH (n:(User|Organization))`), so nodes written with `ogm.model('User').create(...)` are found without carrying an `Entity` label. Each implementor's own read policy applies to its rows.
1574
+
1505
1575
  ### Aggregation
1506
1576
 
1507
1577
  ```typescript
@@ -1916,7 +1986,7 @@ These are incremental improvements you can adopt at your own pace:
1916
1986
  - All identifiers are **validated and escaped** -- injection-safe by default
1917
1987
  - Type generation is **modular and driver-free** -- smaller output, faster builds
1918
1988
 
1919
- ### ⚠️ Deliberate divergence: `OR: []` matches nothing
1989
+ ### Deliberate divergence: `OR: []` matches nothing
1920
1990
 
1921
1991
  This is the **one intentional behavioral difference** from `@neo4j/graphql-ogm`. The deprecated OGM silently dropped empty `OR` arrays from the WHERE clause, so `delete({ where: { OR: [] } })` compiled to an *unfiltered* `MATCH ... DETACH DELETE` — a full-label wipe. The realistic trigger is a dynamically built filter (`OR: ids.map((id) => ({ id }))`) receiving an empty list.
1922
1992
 
@@ -1954,11 +2024,20 @@ Pure query builders (e.g. `cypher-query-builder`) help you compose Cypher fragme
1954
2024
 
1955
2025
  ### Is grafeo-ogm production-ready?
1956
2026
 
1957
- Yes. The library has over 1,400 unit tests covering compilers, mutations, fulltext, transactions, security, scalar type mapping, the CLI, and edge cases. It targets Neo4j 5.x and follows semver — see [CHANGELOG.md](CHANGELOG.md).
2027
+ Yes. The library has over 2,100 unit tests covering compilers, mutations, fulltext, transactions, security, scalar type mapping, the CLI, and edge cases, plus opt-in **live suites** (`tests/integration/`, run with `NEO4J_URI` set) that execute every fix against a real Neo4j 5.26 and assert on database outcomes. It targets Neo4j 5.x and follows semver — see [CHANGELOG.md](CHANGELOG.md).
1958
2028
 
1959
2029
  ### Does grafeo-ogm work with Neo4j Aura?
1960
2030
 
1961
- Yes. grafeo-ogm uses the standard `neo4j-driver` package — anywhere the official driver works, grafeo-ogm works. Aura, self-hosted Neo4j 5.x, Docker images, embedded — all supported. The only optional dependency is the **APOC plugin**, used for nested `orderBy` (via `apoc.coll.sortMulti`) and subgraph operations (via `apoc.refactor.cloneSubgraph`).
2031
+ Yes. grafeo-ogm uses the standard `neo4j-driver` package (`^5` or `^6`) — anywhere the official driver works, grafeo-ogm works. Aura, self-hosted Neo4j 5.x, Docker images, embedded — all supported. The generated Cypher is kept valid on **Neo4j 5.x**, including 5.26 LTS and Aura 5.27, which are stricter than 2025.x+ (for example, a `CALL` subquery may not directly follow a write clause); the live suites run against 5.26.
2032
+
2033
+ Optional server features:
2034
+
2035
+ | Feature | Needs |
2036
+ |---|---|
2037
+ | Nested `orderBy`, `selectionSet` `options.sort` | APOC (`apoc.coll.sortMulti`) |
2038
+ | `cloneSubgraph` / `deleteSubgraph` | APOC |
2039
+ | `searchByVector` | Neo4j 5.11+ (vector indexes) |
2040
+ | `searchByPhrase` | Neo4j 5.11+ with the GenAI plugin |
1962
2041
 
1963
2042
  ### Do I need to know Cypher?
1964
2043
 
@@ -1974,7 +2053,7 @@ Yes — grafeo-ogm is GraphQL-SDL-driven, so the same `typeDefs` you pass to `ne
1974
2053
 
1975
2054
  ### Does it support transactions?
1976
2055
 
1977
- Yes — `ogm.$transaction(async (ctx) => { ... })` runs multiple operations atomically. Pass the `ctx` object to any model method via `{ context: ctx }` to opt into the active transaction. See [Advanced Features → Transactions](#transactions).
2056
+ Yes — `ogm.$transaction(async (ctx) => { ... })` runs multiple operations atomically. Pass the `ctx` object as the `context` field of any model method's params — `Book.create({ input, context: ctx })` — to run it in the active transaction. It must go *inside* the params object: a second argument is ignored, and the call then runs outside the transaction and is not rolled back. See [Advanced Features → Transactions](#transactions).
1978
2057
 
1979
2058
  ### Does it support multi-database / multi-tenant Neo4j setups?
1980
2059
 
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 48 48" fill="none" xmlns:c2pa="http://c2pa.org/manifest"><metadata><c2pa:manifest>AAAWgmp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAABZcanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46YzJwYTpmM2VlNTBjYS1kMjFkLTQ2MDQtOWIxZC1jNWUxNzQ3OTJmMzQAAAADl2p1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAALxqdW1iAAAARGp1bWRjYm9yABEAEIAAAKoAOJtxE2MycGEuaW5ncmVkaWVudC52MwAAAAAYYzJzaJ6LgRbOrPnt/COm2oGCWpQAAABwY2JvcqNpZGM6Zm9ybWF0bWltYWdlL3N2Zyt4bWxqaW5zdGFuY2VJRHgseG1wOmlpZDozMGJjM2U0NS1lNjE2LTQxNWEtOWEzNy1kOTRhMGI4YTBlY2JscmVsYXRpb25zaGlwaHBhcmVudE9mAAAB4mp1bWIAAABBanVtZGNib3IAEQAQgAAAqgA4m3ETYzJwYS5hY3Rpb25zLnYyAAAAABhjMnNoDW3nuuiYTMScbrMSVe0f8AAAAZljYm9yomdhY3Rpb25zgqJmYWN0aW9ua2MycGEub3BlbmVkanBhcmFtZXRlcnOha2luZ3JlZGllbnRzgaJjdXJseC1zZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmluZ3JlZGllbnQudjNkaGFzaFggAx+5NYZPp6CzKEtL4xpUqYPPJhJCN26xR+H+iDiV5K2kZmFjdGlvbngdY29tLmFudGhyb3BpYy5jbGF1ZGUucHJvdmlkZWRqcGFyYW1ldGVyc6F4H2NvbS5hbnRocm9waWMub3JpZ2luLWNvbmZpZGVuY2VndW5rbm93bmtkZXNjcmlwdGlvbnhmQ2xhdWRlIHByb3ZpZGVkIHRoaXMgZmlsZSBhdCB0aGUgcmVxdWVzdCBvZiBhIHVzZXIgYW5kIG1heSBoYXZlIGNyZWF0ZWQgb3IgbW9kaWZpZWQgdGhlIGZpbGUgY29udGVudHMubXNvZnR3YXJlQWdlbnShZG5hbWVmQ2xhdWRlcmFsbEFjdGlvbnNJbmNsdWRlZPUAAADIanVtYgAAAEBqdW1kY2JvcgARABCAAACqADibcRNjMnBhLmhhc2guZGF0YQAAAAAYYzJzaO9cZsx/cO1LV4sKxSgY4FUAAACAY2JvcqVjYWxnZnNoYTI1NmNwYWRNAAAAAAAAAAAAAAAAAGRoYXNoWCBJ5VPxoexFDUntsFrpfVfHCJdp6sftD27ZYlXsJy/jXWRuYW1lbmp1bWJmIG1hbmlmZXN0amV4Y2x1c2lvbnOBomVzdGFydBigZmxlbmd0aBkeBAAAAj5qdW1iAAAAJ2p1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0udjIAAAACD2Nib3KlY2FsZ2ZzaGEyNTZpc2lnbmF0dXJleE1zZWxmI2p1bWJmPS9jMnBhL3VybjpjMnBhOmYzZWU1MGNhLWQyMWQtNDYwNC05YjFkLWM1ZTE3NDc5MmYzNC9jMnBhLnNpZ25hdHVyZWppbnN0YW5jZUlEeCx4bXA6aWlkOjJjMmRlMWE2LTk3OGYtNDdjYS1iZjE5LWRlY2ZiZTgxMDNiY3JjcmVhdGVkX2Fzc2VydGlvbnODomN1cmx4LXNlbGYjanVtYmY9YzJwYS5hc3NlcnRpb25zL2MycGEuaW5ncmVkaWVudC52M2RoYXNoWCADH7k1hk+noLMoS0vjGlSpg88mEkI3brFH4f6IOJXkraJjdXJseCpzZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmFjdGlvbnMudjJkaGFzaFgg3ARYwzrCU70HC59ahzmlxKmkQKsYGSUgrFmJ3QGFXEGiY3VybHgpc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5oYXNoLmRhdGFkaGFzaFggj4d/2G2my+y3mS+rU5+wtXK5cW9ZYXUUi7pp+vuzihd0Y2xhaW1fZ2VuZXJhdG9yX2luZm+jZG5hbWVvQW50aHJvcGljIEZpbGVzZ3ZlcnNpb25lMS4wLjBrc3BlY1ZlcnNpb25lMi40LjAAABA4anVtYgAAAChqdW1kYzJjcwARABCAAACqADibcQNjMnBhLnNpZ25hdHVyZQAAABAIY2JvctKEWQISogEmGCFZAgowggIGMIIBjaADAgECAhRA5aAK7sI50L64g/oGQgU9Z1UTADAKBggqhkjOPQQDAzBJMRcwFQYDVQQKEw5BbnRocm9waWMsIFBCQzEuMCwGA1UEAxMlQW50aHJvcGljIENvbnRlbnQgQ3JlZGVudGlhbHMgUm9vdCBDQTAeFw0yNjA4MDcxODQzNTZaFw0yODA4MDYxOTQzNTZaMEQxFzAVBgNVBAoTDkFudGhyb3BpYywgUEJDMSkwJwYDVQQDEyBBbnRocm9waWMgQ2xhdWRlIENvbnRlbnQgU2lnbmluZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJh6CmvLUBgFFNU0vUKlOVtE6djd17L5SuwX0LemFisBM3dkd/3cyjxFA3Qo5S46fX0/ihY0VZ7mfb9KF703t5OjWDBWMA4GA1UdDwEB/wQEAwIHgDAVBgNVHSUEDjAMBgorBgEEAYPoXgIBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUzlHiBIFOZFsj+OPEz5o+nMHXXMIwCgYIKoZIzj0EAwMDZwAwZAIwMXMdFJ4BetLLVY7ORuE9noqbbAZOZn/aArXyTwFAZfKrPzxF2vPoJNf1+UCdg1XGAjBwX1zd9WGqYkqmL5SFqw1QySjr1zJfpJM9+1rdDwSPLMOPOjKuiXjoU/pUUeG9RwmhY3BhZFkNngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPZYQKkw2lIRoUTxmh8+kKtVDYaeooE+fMUCYg35kVmK170NSBg96LhJqDCjsU8zsad5ItnO7Chot3WI9ojnuFuJM7E=</c2pa:manifest></metadata><g><circle cx="20" cy="20" r="10.5" stroke="#E9ECF2" stroke-width="5"></circle><line x1="30.5" y1="12" x2="30.5" y2="34" stroke="#E9ECF2" stroke-width="5" stroke-linecap="round"></line><line x1="30.5" y1="35" x2="14" y2="41" stroke="#E9ECF2" stroke-width="3"></line><circle cx="12" cy="41.5" r="5" fill="#FF8A5C"></circle><circle cx="30.5" cy="9.5" r="5" fill="#38D4E8"></circle></g></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 48 48" fill="none" xmlns:c2pa="http://c2pa.org/manifest"><metadata><c2pa:manifest>AAAWgmp1bWIAAAAeanVtZGMycGEAEQAQgAAAqgA4m3EDYzJwYQAAABZcanVtYgAAAEdqdW1kYzJtYQARABCAAACqADibcQN1cm46YzJwYTo4MDhjZTYyNS02ZTEyLTQ5ZDgtYjhhNy1mOGQ2YjUxMWRkY2YAAAADl2p1bWIAAAApanVtZGMyYXMAEQAQgAAAqgA4m3EDYzJwYS5hc3NlcnRpb25zAAAAALxqdW1iAAAARGp1bWRjYm9yABEAEIAAAKoAOJtxE2MycGEuaW5ncmVkaWVudC52MwAAAAAYYzJzaBN9ls+4kolQofT5TF027y0AAABwY2JvcqNpZGM6Zm9ybWF0bWltYWdlL3N2Zyt4bWxqaW5zdGFuY2VJRHgseG1wOmlpZDo3ZTQ0NjAyZC00YWQ4LTQyNmUtYTk1Mi01ZWVhYzMyODA5MDBscmVsYXRpb25zaGlwaHBhcmVudE9mAAAB4mp1bWIAAABBanVtZGNib3IAEQAQgAAAqgA4m3ETYzJwYS5hY3Rpb25zLnYyAAAAABhjMnNodREcllsu1o6dXPQ89zkeaQAAAZljYm9yomdhY3Rpb25zgqJmYWN0aW9ua2MycGEub3BlbmVkanBhcmFtZXRlcnOha2luZ3JlZGllbnRzgaJjdXJseC1zZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmluZ3JlZGllbnQudjNkaGFzaFggDn4Mwid+mRfaUGZ6Kx2SjGRjMMy81sPMNWQx51x7d0ikZmFjdGlvbngdY29tLmFudGhyb3BpYy5jbGF1ZGUucHJvdmlkZWRqcGFyYW1ldGVyc6F4H2NvbS5hbnRocm9waWMub3JpZ2luLWNvbmZpZGVuY2VndW5rbm93bmtkZXNjcmlwdGlvbnhmQ2xhdWRlIHByb3ZpZGVkIHRoaXMgZmlsZSBhdCB0aGUgcmVxdWVzdCBvZiBhIHVzZXIgYW5kIG1heSBoYXZlIGNyZWF0ZWQgb3IgbW9kaWZpZWQgdGhlIGZpbGUgY29udGVudHMubXNvZnR3YXJlQWdlbnShZG5hbWVmQ2xhdWRlcmFsbEFjdGlvbnNJbmNsdWRlZPUAAADIanVtYgAAAEBqdW1kY2JvcgARABCAAACqADibcRNjMnBhLmhhc2guZGF0YQAAAAAYYzJzaKeOJavhoIy6z9HJMkspvAsAAACAY2JvcqVjYWxnZnNoYTI1NmNwYWRNAAAAAAAAAAAAAAAAAGRoYXNoWCA65zpaxIC6CLGak0YcooZp/3N6WqEN78WNqsxJLDks3mRuYW1lbmp1bWJmIG1hbmlmZXN0amV4Y2x1c2lvbnOBomVzdGFydBigZmxlbmd0aBkeBAAAAj5qdW1iAAAAJ2p1bWRjMmNsABEAEIAAAKoAOJtxA2MycGEuY2xhaW0udjIAAAACD2Nib3KlY2FsZ2ZzaGEyNTZpc2lnbmF0dXJleE1zZWxmI2p1bWJmPS9jMnBhL3VybjpjMnBhOjgwOGNlNjI1LTZlMTItNDlkOC1iOGE3LWY4ZDZiNTExZGRjZi9jMnBhLnNpZ25hdHVyZWppbnN0YW5jZUlEeCx4bXA6aWlkOjE0NjI4ZTI0LTBmM2MtNGE4ZS04NmRkLTgxNzk0NzU4MGQ1MnJjcmVhdGVkX2Fzc2VydGlvbnODomN1cmx4LXNlbGYjanVtYmY9YzJwYS5hc3NlcnRpb25zL2MycGEuaW5ncmVkaWVudC52M2RoYXNoWCAOfgzCJ36ZF9pQZnorHZKMZGMwzLzWw8w1ZDHnXHt3SKJjdXJseCpzZWxmI2p1bWJmPWMycGEuYXNzZXJ0aW9ucy9jMnBhLmFjdGlvbnMudjJkaGFzaFgg3YX09/YVEq0au/QwUSmVv2Q70DSglwnZlJ2yQg/eluaiY3VybHgpc2VsZiNqdW1iZj1jMnBhLmFzc2VydGlvbnMvYzJwYS5oYXNoLmRhdGFkaGFzaFggep9V2yzZkdZSZeLFJr1TiR9Wi/4Dv6yKdOq1C3FNo6N0Y2xhaW1fZ2VuZXJhdG9yX2luZm+jZG5hbWVvQW50aHJvcGljIEZpbGVzZ3ZlcnNpb25lMS4wLjBrc3BlY1ZlcnNpb25lMi40LjAAABA4anVtYgAAAChqdW1kYzJjcwARABCAAACqADibcQNjMnBhLnNpZ25hdHVyZQAAABAIY2JvctKEWQISogEmGCFZAgowggIGMIIBjaADAgECAhRA5aAK7sI50L64g/oGQgU9Z1UTADAKBggqhkjOPQQDAzBJMRcwFQYDVQQKEw5BbnRocm9waWMsIFBCQzEuMCwGA1UEAxMlQW50aHJvcGljIENvbnRlbnQgQ3JlZGVudGlhbHMgUm9vdCBDQTAeFw0yNjA4MDcxODQzNTZaFw0yODA4MDYxOTQzNTZaMEQxFzAVBgNVBAoTDkFudGhyb3BpYywgUEJDMSkwJwYDVQQDEyBBbnRocm9waWMgQ2xhdWRlIENvbnRlbnQgU2lnbmluZzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJh6CmvLUBgFFNU0vUKlOVtE6djd17L5SuwX0LemFisBM3dkd/3cyjxFA3Qo5S46fX0/ihY0VZ7mfb9KF703t5OjWDBWMA4GA1UdDwEB/wQEAwIHgDAVBgNVHSUEDjAMBgorBgEEAYPoXgIBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUzlHiBIFOZFsj+OPEz5o+nMHXXMIwCgYIKoZIzj0EAwMDZwAwZAIwMXMdFJ4BetLLVY7ORuE9noqbbAZOZn/aArXyTwFAZfKrPzxF2vPoJNf1+UCdg1XGAjBwX1zd9WGqYkqmL5SFqw1QySjr1zJfpJM9+1rdDwSPLMOPOjKuiXjoU/pUUeG9RwmhY3BhZFkNngAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPZYQIUf2JB3WUq00sw+WDNSjvm94qIKua8SoYHD1enmxug9MVY618ePmRR+1/DhAMbyo70mIo32hle+4THnn6LSYOc=</c2pa:manifest></metadata><g><circle cx="20" cy="20" r="10.5" stroke="#0D111C" stroke-width="5"></circle><line x1="30.5" y1="12" x2="30.5" y2="34" stroke="#0D111C" stroke-width="5" stroke-linecap="round"></line><line x1="30.5" y1="35" x2="14" y2="41" stroke="#0D111C" stroke-width="3"></line><circle cx="12" cy="41.5" r="5" fill="#F0703F"></circle><circle cx="30.5" cy="9.5" r="5" fill="#0FA6BD"></circle></g></svg>
@@ -1 +1 @@
1
- {"version":3,"file":"db-push.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/db-push.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAyNtC,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAkF1E"}
1
+ {"version":3,"file":"db-push.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/db-push.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAgOtC,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,CAwF1E"}
@@ -189,8 +189,14 @@ async function applyDrops(driver, database, plan, io) {
189
189
  // no-op while the old definition still holds the name.
190
190
  for (const change of plan.changed) {
191
191
  await session.run(change.dropCypher);
192
- await session.run(change.statement.cypher);
193
- io.out(`✓ recreated ${change.statement.name}`);
192
+ const result = await session.run(change.statement.cypher);
193
+ // v2.5.1 — never claim "recreated" when the CREATE was a no-op.
194
+ const updates = result?.summary?.counters?.updates?.();
195
+ if (updates && (updates.indexesAdded ?? 0) === 0)
196
+ io.err(`error: ${change.statement.name} was dropped but NOT recreated — Neo4j reports ` +
197
+ `an equivalent index already exists. Schema is NOT in sync.`);
198
+ else
199
+ io.out(`✓ recreated ${change.statement.name}`);
194
200
  }
195
201
  for (const orphan of plan.orphans) {
196
202
  await session.run(orphan.dropCypher);
@@ -223,10 +229,16 @@ async function runDbPush(argv, io) {
223
229
  // v2.4.8 — Neo4j refuses a unique constraint over a property a plain
224
230
  // RANGE index already covers. Report it (the index is not grafeo's to
225
231
  // drop) instead of letting the CREATE crash the whole push.
232
+ // v2.5.1 — same for a fulltext/vector index whose definition another
233
+ // index already holds under a different name.
226
234
  for (const block of plan.blocked)
227
- io.err(`warning: ${block.statement.name} not created — index "${(0, display_1.safeDisplay)(block.by)}" ` +
228
- `already covers the same label and property, and Neo4j will not add a ` +
229
- `unique constraint until that index is dropped. Schema is NOT in sync.`);
235
+ io.err(block.reason === 'range-index'
236
+ ? `warning: ${block.statement.name} not created — index "${(0, display_1.safeDisplay)(block.by)}" ` +
237
+ `already covers the same label and property, and Neo4j will not add a ` +
238
+ `unique constraint until that index is dropped. Schema is NOT in sync.`
239
+ : `warning: ${block.statement.name} not created or updated — index "${(0, display_1.safeDisplay)(block.by)}" ` +
240
+ `already has the SDL definition, and Neo4j will not create an equivalent ` +
241
+ `index under another name. Drop it, or name the SDL index after it. Schema is NOT in sync.`);
230
242
  for (const needs of plan.needsConfig)
231
243
  io.err(`warning: vector index "${needs.indexName}" ` +
232
244
  `(${needs.typeName}.${needs.embeddingProperty}) skipped — set ` +
@@ -1 +1 @@
1
- {"version":3,"file":"db-push.js","sourceRoot":"","sources":["../../../../src/cli/commands/db-push.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"db-push.js","sourceRoot":"","sources":["../../../../src/cli/commands/db-push.ts"],"names":[],"mappings":"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"}
@@ -66,6 +66,12 @@ export interface PushPlanBlocked {
66
66
  statement: SchemaStatement;
67
67
  /** Live index name (from introspection — sanitize before printing). */
68
68
  by: string;
69
+ /**
70
+ * `range-index` — a plain RANGE index blocks a unique constraint.
71
+ * `equivalent-index` — a fulltext/vector index with the same definition
72
+ * exists under another name, so Neo4j will not create the SDL-named one.
73
+ */
74
+ reason: 'range-index' | 'equivalent-index';
69
75
  }
70
76
  export interface PushPlan {
71
77
  /** Declared in SDL, missing in the database. */
@@ -1 +1 @@
1
- {"version":3,"file":"push-planner.d.ts","sourceRoot":"","sources":["../../../src/cli/push-planner.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,eAAe,EAEf,yBAAyB,EACzB,wBAAwB,EACzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAmBtD,2DAA2D;AAC3D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,eAAe,CAAC;IAC3B,+CAA+C;IAC/C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,eAAe,CAAC;IAC3B,4EAA4E;IAC5E,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,eAAe,CAAC;IAC3B,uEAAuE;IACvE,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,QAAQ;IACvB,gDAAgD;IAChD,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,wEAAwE;IACxE,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,yEAAyE;IACzE,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,6EAA6E;IAC7E,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,0EAA0E;IAC1E,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B;;;;OAIG;IACH,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,wEAAwE;IACxE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,0EAA0E;IAC1E,WAAW,EAAE,wBAAwB,EAAE,CAAC;CACzC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,UAAU,EAChB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,GACvD,QAAQ,CA+HV"}
1
+ {"version":3,"file":"push-planner.d.ts","sourceRoot":"","sources":["../../../src/cli/push-planner.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,eAAe,EAEf,yBAAyB,EACzB,wBAAwB,EACzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAmBtD,2DAA2D;AAC3D,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,MAAM,WAAW,UAAU;IACzB,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,OAAO,EAAE,cAAc,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,eAAe,CAAC;IAC3B,+CAA+C;IAC/C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,eAAe,CAAC;IAC3B,4EAA4E;IAC5E,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,eAAe,CAAC;IAC3B,uEAAuE;IACvE,EAAE,EAAE,MAAM,CAAC;IACX;;;;OAIG;IACH,MAAM,EAAE,aAAa,GAAG,kBAAkB,CAAC;CAC5C;AAED,MAAM,WAAW,QAAQ;IACvB,gDAAgD;IAChD,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,wEAAwE;IACxE,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,yEAAyE;IACzE,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,6EAA6E;IAC7E,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,0EAA0E;IAC1E,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B;;;;OAIG;IACH,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,wEAAwE;IACxE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,0EAA0E;IAC1E,WAAW,EAAE,wBAAwB,EAAE,CAAC;CACzC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,UAAU,EAChB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,GACvD,QAAQ,CAwJV"}
@@ -54,6 +54,10 @@ function planSchemaSync(schema, live, vectorConfig) {
54
54
  const changed = [];
55
55
  const covered = [];
56
56
  const blocked = [];
57
+ const equivalentIndex = (name, definition) => live.indexes.find((i) => i.name !== name &&
58
+ !i.owningConstraint &&
59
+ i.type === definition.type &&
60
+ sameSchema(definition, i, definition.unorderedProperties));
57
61
  for (const statement of desired) {
58
62
  const isConstraint = statement.kind === 'unique-constraint';
59
63
  const present = isConstraint
@@ -74,10 +78,17 @@ function planSchemaSync(schema, live, vectorConfig) {
74
78
  !liveConstraintNames.has(i.name) &&
75
79
  sameSchema(definition, i));
76
80
  if (blocker) {
77
- blocked.push({ statement, by: blocker.name });
81
+ blocked.push({ statement, by: blocker.name, reason: 'range-index' });
78
82
  continue;
79
83
  }
80
84
  }
85
+ const twin = !isConstraint &&
86
+ definition &&
87
+ equivalentIndex(statement.name, definition);
88
+ if (twin) {
89
+ blocked.push({ statement, by: twin.name, reason: 'equivalent-index' });
90
+ continue;
91
+ }
81
92
  create.push(statement);
82
93
  continue;
83
94
  }
@@ -89,8 +100,16 @@ function planSchemaSync(schema, live, vectorConfig) {
89
100
  : definitions.get(statement.name);
90
101
  const liveItem = liveIndexesByName.get(statement.name);
91
102
  const differences = liveItem && definition ? diffDefinition(definition, liveItem) : [];
92
- if (differences.length === 0)
103
+ if (differences.length === 0) {
93
104
  inSync.push(statement);
105
+ continue;
106
+ }
107
+ // v2.5.1 — Neo4j will not create a fulltext/vector index equivalent to
108
+ // one that exists under another name, so a drop + recreate would leave
109
+ // the SDL name missing (and fulltext queries address it by that name).
110
+ const twin = definition && equivalentIndex(statement.name, definition);
111
+ if (twin)
112
+ blocked.push({ statement, by: twin.name, reason: 'equivalent-index' });
94
113
  else
95
114
  changed.push({
96
115
  statement,
@@ -261,13 +280,13 @@ function diffDefinition(want, live) {
261
280
  * Same entity, labels and properties? Requires the live row to report its
262
281
  * definition columns — without them nothing is ever called equivalent.
263
282
  */
264
- function sameSchema(want, live) {
283
+ function sameSchema(want, live, unorderedProperties = false) {
265
284
  if (!live.labelsOrTypes || !live.properties)
266
285
  return false;
267
286
  if (live.entityType && live.entityType !== want.entityType)
268
287
  return false;
269
- return (sameList(live.labelsOrTypes, want.labelsOrTypes) &&
270
- sameList(live.properties, want.properties));
288
+ return (sameList(live.labelsOrTypes, want.labelsOrTypes, unorderedProperties) &&
289
+ sameList(live.properties, want.properties, unorderedProperties));
271
290
  }
272
291
  function sameList(a, b, unordered = false) {
273
292
  if (a.length !== b.length)