@_linked/core 2.33.0 → 2.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +26 -0
  3. package/lib/esm/index.d.ts +2 -1
  4. package/lib/esm/index.js +2 -0
  5. package/lib/esm/index.js.map +1 -1
  6. package/lib/esm/live/LiveQueryStore.d.ts +22 -0
  7. package/lib/esm/live/LiveQueryStore.js +86 -19
  8. package/lib/esm/live/LiveQueryStore.js.map +1 -1
  9. package/lib/esm/live/changes.d.ts +9 -1
  10. package/lib/esm/live/changes.js +7 -2
  11. package/lib/esm/live/changes.js.map +1 -1
  12. package/lib/esm/live/keys.d.ts +6 -0
  13. package/lib/esm/live/keys.js +10 -1
  14. package/lib/esm/live/keys.js.map +1 -1
  15. package/lib/esm/live/matcher.d.ts +4 -1
  16. package/lib/esm/live/matcher.js +10 -5
  17. package/lib/esm/live/matcher.js.map +1 -1
  18. package/lib/esm/queries/AskBuilder.d.ts +6 -0
  19. package/lib/esm/queries/AskBuilder.js +14 -0
  20. package/lib/esm/queries/AskBuilder.js.map +1 -1
  21. package/lib/esm/queries/CountBuilder.d.ts +6 -0
  22. package/lib/esm/queries/CountBuilder.js +14 -0
  23. package/lib/esm/queries/CountBuilder.js.map +1 -1
  24. package/lib/esm/queries/CreateBuilder.d.ts +4 -0
  25. package/lib/esm/queries/CreateBuilder.js +15 -1
  26. package/lib/esm/queries/CreateBuilder.js.map +1 -1
  27. package/lib/esm/queries/DeleteBuilder.d.ts +4 -0
  28. package/lib/esm/queries/DeleteBuilder.js +15 -1
  29. package/lib/esm/queries/DeleteBuilder.js.map +1 -1
  30. package/lib/esm/queries/FieldSet.d.ts +10 -0
  31. package/lib/esm/queries/FieldSet.js +85 -18
  32. package/lib/esm/queries/FieldSet.js.map +1 -1
  33. package/lib/esm/queries/IRLower.js +3 -2
  34. package/lib/esm/queries/IRLower.js.map +1 -1
  35. package/lib/esm/queries/IRMutation.js +15 -14
  36. package/lib/esm/queries/IRMutation.js.map +1 -1
  37. package/lib/esm/queries/MutationSerialization.js +2 -2
  38. package/lib/esm/queries/MutationSerialization.js.map +1 -1
  39. package/lib/esm/queries/PropertyPath.js +2 -2
  40. package/lib/esm/queries/PropertyPath.js.map +1 -1
  41. package/lib/esm/queries/QueryBuilder.d.ts +7 -0
  42. package/lib/esm/queries/QueryBuilder.js +40 -3
  43. package/lib/esm/queries/QueryBuilder.js.map +1 -1
  44. package/lib/esm/queries/SelectQuery.js +2 -2
  45. package/lib/esm/queries/SelectQuery.js.map +1 -1
  46. package/lib/esm/queries/UpdateBuilder.d.ts +4 -0
  47. package/lib/esm/queries/UpdateBuilder.js +15 -1
  48. package/lib/esm/queries/UpdateBuilder.js.map +1 -1
  49. package/lib/esm/queries/fromJSON.d.ts +12 -1
  50. package/lib/esm/queries/fromJSON.js +5 -1
  51. package/lib/esm/queries/fromJSON.js.map +1 -1
  52. package/lib/esm/queries/lower.js +12 -5
  53. package/lib/esm/queries/lower.js.map +1 -1
  54. package/lib/esm/queries/queryDependencies.js +14 -10
  55. package/lib/esm/queries/queryDependencies.js.map +1 -1
  56. package/lib/esm/queries/resolveShape.js +14 -2
  57. package/lib/esm/queries/resolveShape.js.map +1 -1
  58. package/lib/esm/shapes/Shape.js +2 -1
  59. package/lib/esm/shapes/Shape.js.map +1 -1
  60. package/lib/esm/shapes/nodeShapeData.js +7 -10
  61. package/lib/esm/shapes/nodeShapeData.js.map +1 -1
  62. package/lib/esm/shapes/registerRuntimeShape.js +3 -1
  63. package/lib/esm/shapes/registerRuntimeShape.js.map +1 -1
  64. package/lib/esm/shapes/syncShapes.d.ts +6 -2
  65. package/lib/esm/shapes/syncShapes.js +16 -9
  66. package/lib/esm/shapes/syncShapes.js.map +1 -1
  67. package/lib/esm/shapes/validation.js +2 -2
  68. package/lib/esm/shapes/validation.js.map +1 -1
  69. package/lib/esm/sparql/irToAlgebra.js +117 -52
  70. package/lib/esm/sparql/irToAlgebra.js.map +1 -1
  71. package/lib/esm/sparql/ownedLinkCheck.js +6 -1
  72. package/lib/esm/sparql/ownedLinkCheck.js.map +1 -1
  73. package/lib/esm/utils/LinkedStorage.d.ts +7 -0
  74. package/lib/esm/utils/LinkedStorage.js +39 -12
  75. package/lib/esm/utils/LinkedStorage.js.map +1 -1
  76. package/lib/esm/utils/ShapeCatalog.d.ts +57 -0
  77. package/lib/esm/utils/ShapeCatalog.js +86 -0
  78. package/lib/esm/utils/ShapeCatalog.js.map +1 -0
  79. package/lib/esm/utils/ShapeClass.d.ts +73 -8
  80. package/lib/esm/utils/ShapeClass.js +245 -66
  81. package/lib/esm/utils/ShapeClass.js.map +1 -1
  82. package/lib/esm/utils/loadStores.d.ts +4 -0
  83. package/lib/esm/utils/loadStores.js +24 -1
  84. package/lib/esm/utils/loadStores.js.map +1 -1
  85. package/lib/esm/utils/registryCache.d.ts +15 -0
  86. package/lib/esm/utils/registryCache.js +50 -0
  87. package/lib/esm/utils/registryCache.js.map +1 -0
  88. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"resolveShape.js","sourceRoot":"","sources":["../../../src/queries/resolveShape.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,uBAAuB,EAAE,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAE9E;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAmC;;IAEnC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,+EAA+E;QAC/E,+EAA+E;QAC/E,MAAM,UAAU,GAAG,MAAA,aAAa,CAAC,KAAK,CAAC,mCAAI,uBAAuB,CAAC,KAAK,CAAC,CAAC;QAC1E,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,6BAA6B,KAAK,GAAG,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,UAAiC,CAAC;IAC3C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import {Shape, type ShapeConstructor} from '../shapes/Shape.js';\nimport {getOrCreateShapeAdapter, getShapeClass} from '../utils/ShapeClass.js';\n\n/**\n * Resolve a shape class or IRI string to a ShapeConstructor.\n *\n * Shared utility used by QueryBuilder, CreateBuilder, UpdateBuilder, and DeleteBuilder\n * to normalize their shape input.\n *\n * @throws If a string IRI cannot be resolved via the shape registry.\n */\nexport function resolveShape<S extends Shape>(\n shape: ShapeConstructor<S> | string,\n): ShapeConstructor<S> {\n if (typeof shape === 'string') {\n // An authored class first; otherwise a constructor derived from the registered\n // metadata, so a project-authored shape that exists only as data resolves too.\n const shapeClass = getShapeClass(shape) ?? getOrCreateShapeAdapter(shape);\n if (!shapeClass) {\n throw new Error(`Cannot resolve shape for '${shape}'`);\n }\n return shapeClass as ShapeConstructor<S>;\n }\n return shape;\n}\n"]}
1
+ {"version":3,"file":"resolveShape.js","sourceRoot":"","sources":["../../../src/queries/resolveShape.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,qBAAqB,EAAE,uBAAuB,EAAE,aAAa,EAAC,MAAM,wBAAwB,CAAC;AAErG;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,KAAmC;;IAEnC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,+EAA+E;QAC/E,+EAA+E;QAC/E,MAAM,UAAU,GAAG,MAAA,aAAa,CAAC,KAAK,CAAC,mCAAI,uBAAuB,CAAC,KAAK,CAAC,CAAC;QAC1E,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,6BAA6B,KAAK,GAAG,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,UAAiC,CAAC;IAC3C,CAAC;IACD,qFAAqF;IACrF,mFAAmF;IACnF,oFAAoF;IACpF,MAAM,OAAO,GAAG,qBAAqB,EAAE,CAAC;IACxC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,EAAE,GAAG,MAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,KAAK,0CAAE,EAAE,CAAC;QAC5B,IAAI,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YAC1B,MAAM,GAAG,GAAG,uBAAuB,CAAC,EAAE,CAAC,CAAC;YACxC,IAAI,GAAG;gBAAE,OAAO,GAAqC,CAAC;QACxD,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import {Shape, type ShapeConstructor} from '../shapes/Shape.js';\nimport {getActiveShapeCatalog, getOrCreateShapeAdapter, getShapeClass} from '../utils/ShapeClass.js';\n\n/**\n * Resolve a shape class or IRI string to a ShapeConstructor.\n *\n * Shared utility used by QueryBuilder, CreateBuilder, UpdateBuilder, and DeleteBuilder\n * to normalize their shape input.\n *\n * @throws If a string IRI cannot be resolved via the shape registry.\n */\nexport function resolveShape<S extends Shape>(\n shape: ShapeConstructor<S> | string,\n): ShapeConstructor<S> {\n if (typeof shape === 'string') {\n // An authored class first; otherwise a constructor derived from the registered\n // metadata, so a project-authored shape that exists only as data resolves too.\n const shapeClass = getShapeClass(shape) ?? getOrCreateShapeAdapter(shape);\n if (!shapeClass) {\n throw new Error(`Cannot resolve shape for '${shape}'`);\n }\n return shapeClass as ShapeConstructor<S>;\n }\n // Inside a catalog the catalog's data is the authority for every IRI it holds, so an\n // authored class for one of them (CN's compiled `Person` for a project's `Person`)\n // resolves to the catalog's shape. A class the catalog does not hold is kept as is.\n const catalog = getActiveShapeCatalog();\n if (catalog !== undefined) {\n const id = shape?.shape?.id;\n if (id && catalog.has(id)) {\n const own = getOrCreateShapeAdapter(id);\n if (own) return own as unknown as ShapeConstructor<S>;\n }\n }\n return shape;\n}\n"]}
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  import { QueryBuilder } from '../queries/QueryBuilder.js';
11
11
  import { AskBuilder } from '../queries/AskBuilder.js';
12
12
  import { CountBuilder } from '../queries/CountBuilder.js';
13
+ import { resolveShape } from '../queries/resolveShape.js';
13
14
  import { CreateBuilder } from '../queries/CreateBuilder.js';
14
15
  import { UpdateBuilder } from '../queries/UpdateBuilder.js';
15
16
  import { DeleteBuilder } from '../queries/DeleteBuilder.js';
@@ -191,7 +192,7 @@ export class Shape {
191
192
  'on the builder (`Person.select().where(…).count()`).');
192
193
  }
193
194
  return CountBuilder.of({
194
- shapeClass: this,
195
+ shapeClass: resolveShape(this),
195
196
  }).exec(target);
196
197
  });
197
198
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Shape.js","sourceRoot":"","sources":["../../../src/shapes/Shape.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\nimport type {NodeShapeData, PropertyShapeData} from './nodeShapeData.js';\nimport type {\n QueryBuildFn,\n QueryResponseToResultType,\n SelectAllQueryResponse,\n WhereClause,\n} from '../queries/SelectQuery.js';\nimport type {NodeReferenceValue, UpdatePartial} from '../queries/QueryFactory.js';\nimport type {NodeId} from '../queries/MutationQuery.js';\nimport {QueryBuilder} from '../queries/QueryBuilder.js';\nimport {AskBuilder} from '../queries/AskBuilder.js';\nimport {CountBuilder} from '../queries/CountBuilder.js';\nimport type {PendingQueryContext} from '../queries/QueryContext.js';\nimport type {IDataset} from '../interfaces/IDataset.js';\nimport {CreateBuilder} from '../queries/CreateBuilder.js';\nimport {UpdateBuilder} from '../queries/UpdateBuilder.js';\nimport {DeleteBuilder, type DeleteId} from '../queries/DeleteBuilder.js';\nimport type {ExpressionUpdateProxy, ExpressionUpdateResult} from '../expressions/ExpressionMethods.js';\nimport {getPropertyShapeByLabel} from '../utils/ShapeClass.js';\nimport {ShapeSet} from '../collections/ShapeSet.js';\n\n//shape that returns property shapes for its keys\ntype AccessPropertiesShape<T extends Shape> = {\n [P in keyof T]: PropertyShapeData;\n};\ntype PropertyShapeMapFunction<T extends Shape, ResponseType> = (\n p: AccessPropertiesShape<T>,\n) => ResponseType;\n\n/**\n * Concrete constructor type for Shape subclasses — used at runtime boundaries\n * (Builder `from()` methods, Shape static `this` parameters, mutation factories).\n *\n * Uses concrete `new` (not `abstract new`) so TypeScript accepts the class as a\n * value and reads its static `.shape`/`.targetClass` without casts. NOTE: `new\n * shape()` is only a *type-level* capability — at runtime the `Shape` constructor\n * throws (shapes are metadata, not data). The framework builds proxy targets via\n * `createShapeTarget()` (Object.create), never `new`.\n */\nexport type ShapeConstructor<S extends Shape = Shape> = (new (\n ...args: any[]\n) => S) & {\n shape: NodeShapeData;\n targetClass?: NodeReferenceValue;\n};\n\n/**\n * @internal\n * Build a constructor-less, prototype-linked Shape used ONLY as a proxy /\n * metadata-carrier target inside the query DSL (never handed to consumers and\n * never persisted). It is a genuine `Shape` on the prototype chain — so\n * `.constructor`, the `nodeShape` getter, `ShapeSet`, and `getLeastSpecificShape`\n * all work — but it deliberately bypasses the `Shape` constructor, which is\n * guarded to reject direct instantiation. Not exported from the package index.\n */\nexport function createShapeTarget<S extends Shape>(\n shapeClass: ShapeConstructor<S> | typeof Shape,\n id?: string,\n): S {\n const target = Object.create(shapeClass.prototype) as S;\n if (id !== undefined) {\n target.id = id;\n }\n return target;\n}\n\nexport abstract class Shape {\n static targetClass: NodeReferenceValue = null;\n static shape: NodeShapeData;\n static typesToShapes: Map<string, Set<typeof Shape>> = new Map();\n\n __queryContextId?: string;\n /** The query-context name this shape was registered under (for `{@ctx}` refs). */\n __queryContextName?: string;\n id?: string;\n\n // Instantiation guard DEFERRED on the `lego-demo` branch (per René): some\n // framework shapes (e.g. server `LocalFileStore`) legitimately `extends Shape`\n // and are constructed as runtime service objects, which the upstream guard\n // (core report 026) rejects at backend boot. The `validate()`/`assertValid()`\n // work from the same dev merge is KEPT; only the constructor throw is relaxed —\n // restoring pre-guard behaviour: accept an optional node ref and set `id`\n // (mirrors `createShapeTarget`). Re-enable the guard once those shapes migrate.\n constructor(node?: string | {id?: string}) {\n if (node !== undefined) {\n this.id = typeof node === 'string' ? node : node.id;\n }\n }\n\n get nodeShape(): NodeShapeData {\n return (this.constructor as typeof Shape).shape;\n }\n\n get uri(): string {\n return this.id;\n }\n\n set uri(value: string) {\n this.id = value;\n }\n\n /**\n * @internal\n * @param shapeClass\n * @param type\n */\n static registerByType(shapeClass: typeof Shape, type?: NodeReferenceValue) {\n if (!type) {\n if (shapeClass === Shape) {\n return;\n }\n const shapeType = shapeClass.targetClass;\n if (shapeType) {\n type = shapeType;\n }\n }\n if (!type) {\n return;\n }\n const typeId = type.id;\n if (!this.typesToShapes.has(typeId)) {\n this.typesToShapes.set(typeId, new Set());\n }\n this.typesToShapes.get(typeId).add(shapeClass);\n }\n\n /**\n * Select properties of instances of this shape.\n * Chain `.for(id)` to target a single entity, or `.forAll(ids)` for multiple.\n * The select callback receives a proxy of the shape for type-safe property access.\n */\n static select<\n S extends Shape,\n R = unknown,\n ResultType = QueryResponseToResultType<R, S>[],\n >(\n this: ShapeConstructor<S>,\n selectFn: QueryBuildFn<S, R>,\n ): QueryBuilder<S, R, ResultType>;\n static select<\n S extends Shape,\n R = unknown,\n ResultType = QueryResponseToResultType<R, S>[],\n >(\n this: ShapeConstructor<S>,\n ): QueryBuilder<S, R, ResultType>;\n static select<\n S extends Shape,\n R = unknown,\n ResultType = QueryResponseToResultType<R, S>[],\n >(\n this: ShapeConstructor<S>,\n selectFn?: QueryBuildFn<S, R>,\n ): QueryBuilder<S, R, ResultType> {\n let builder = QueryBuilder.from(this) as QueryBuilder<S, any, any>;\n if (selectFn) {\n builder = builder.select(selectFn as any);\n }\n return builder as QueryBuilder<S, R, ResultType>;\n }\n\n /**\n * Select all decorated properties of this shape.\n * Chain `.for(id)` to target a single entity.\n */\n static selectAll<\n S extends Shape,\n ResultType = QueryResponseToResultType<\n SelectAllQueryResponse<S>,\n S\n >[],\n >(\n this: ShapeConstructor<S>,\n ): QueryBuilder<S, any, ResultType> {\n return QueryBuilder.from(this).selectAll() as QueryBuilder<S, any, ResultType>;\n }\n\n /**\n * Whether a node with this id exists as an instance of this shape.\n *\n * On the base class — `Shape.exists(uri)` — it means something different and\n * weaker: does a node with this IRI exist **at all**, under any type or none.\n * There is no shape to constrain by, so no `rdf:type` triple is emitted and the\n * query is `ASK { <uri> ?p ?o }`. (`Shape` is free to mean this because the\n * shapes themselves are described by `NodeShape` and `PropertyShape`, so\n * `Shape.exists` is not needed for \"is this a shape?\".) Because a shapeless ask\n * has no shape to route on, a router asks every dataset it knows — see\n * `LinkedStorage.askQuery`.\n *\n * ```typescript\n * if (await SourceDocument.exists({id})) {\n * await SourceDocument.update(values).for({id});\n * } else {\n * await SourceDocument.create({id, ...values});\n * }\n * ```\n *\n * Resolves to a real `boolean` — unlike `select().where(…).one()`, which resolves\n * to a row or `null` and leaves the conversion (and the failure modes) to the\n * caller. Runs the cheapest correct query: against a SPARQL store, an\n * `ASK WHERE { ?a0 rdf:type <ShapeClass> . FILTER(?a0 = <id>) }` — the shape's\n * type triple and an equality filter on the subject, nothing else.\n *\n * Note the type triple: this asks whether the node exists **as an instance of\n * this shape**. A node with that IRI and a different type answers `false`.\n *\n * A `null`/`undefined` id resolves to `false` without touching the store — as does\n * a `PendingQueryContext` whose value has not landed yet, since there is no subject\n * to ask about. (An unresolved context inside a *where clause* rejects instead; see\n * {@link QueryBuilder.exists}.)\n *\n * **Errors reject — they are never reported as `false`.** Do not wrap this in a\n * `.catch(() => false)`: that is exactly how a broken existence check hides,\n * turning every `exists ? update : create` into an unconditional `create`.\n *\n * For \"does *anything* match?\", compose on the builder instead:\n * `await Person.select().where(p => p.name.equals('Semmy')).exists()`.\n *\n * @param id The node id: a string IRI, a `{id}` reference, or a `PendingQueryContext`.\n * A malformed string IRI rejects (it does not throw synchronously).\n * @param target Optional explicit dataset to run against; omitted uses the\n * global query dispatch.\n */\n static async exists<S extends Shape>(\n this: ShapeConstructor<S> | typeof Shape,\n id: string | NodeReferenceValue | PendingQueryContext | null | undefined,\n target?: IDataset,\n ): Promise<boolean> {\n // `async`, so that a bad string IRI (resolveUriOrThrow) rejects rather than\n // throwing synchronously past the caller's .catch().\n if ((this as unknown) === Shape) {\n // Called on the base class: no shape to constrain by, so no rdf:type triple.\n // `ASK { <iri> ?p ?o }` — does this node exist at all?\n return AskBuilder.forNode(id).exec(target);\n }\n return QueryBuilder.from(this as ShapeConstructor<S>).for(id).exists(target);\n }\n\n /**\n * How many instances of this shape exist — a real `number`.\n *\n * ```typescript\n * const total = await Person.count();\n * ```\n *\n * This is the unfiltered total. For a filtered one, compose on the builder, which\n * is the form a paging table wants:\n *\n * ```typescript\n * await Person.select().where(p => p.name.equals('Semmy')).count();\n * ```\n *\n * Runs the cheapest correct query: against a SPARQL store,\n * `SELECT (COUNT(DISTINCT ?a0) AS ?count) WHERE { ?a0 rdf:type <ShapeClass> }` —\n * the shape's type triple and nothing else. Note the type triple: this counts\n * instances **of this shape**, not nodes in general.\n *\n * **On the base class — `Shape.count()` — this throws.** Unlike\n * {@link Shape.exists}, whose shapeless reading (\"does this node exist at all?\")\n * is useful and cheap, a shapeless count would count every node in every dataset\n * the router knows. No caller means that, so it is refused rather than answered.\n *\n * **Errors reject — they are never reported as `0`.** Do not wrap this in a\n * `.catch(() => 0)`: `0` is a plausible count, so a broken one renders an empty\n * table that looks exactly like real data.\n *\n * @param target Optional explicit dataset to run against; omitted uses the\n * global query dispatch.\n */\n static async count<S extends Shape>(\n this: ShapeConstructor<S> | typeof Shape,\n target?: IDataset,\n ): Promise<number> {\n // `async`, so the base-class refusal rejects rather than throwing synchronously\n // past the caller's `.catch()`.\n if ((this as unknown) === Shape) {\n throw new Error(\n 'Shape.count() has no meaning on the base class: there is no shape to ' +\n 'constrain by, so it would count every node in the store, under any type or ' +\n 'none. Call it on a shape subclass (`Person.count()`), or compose a filter ' +\n 'on the builder (`Person.select().where(…).count()`).',\n );\n }\n return CountBuilder.of({\n shapeClass: this as ShapeConstructor<S>,\n }).exec(target);\n }\n\n /**\n * Update properties of an instance of this shape.\n * Chain `.for(id)` to target a specific entity.\n *\n * ```typescript\n * await Person.update({name: 'Alice'}).for({id: '...'});\n * ```\n *\n * **`__id` is for *new* nodes only** — a fixed id for a nested node being\n * created (here, when **adding** to a relation: `update({friends: {add: {__id, name}}})`).\n * Do NOT put `__id` on a plain single-valued *replace* (`update({image: {__id, contentUrl}})`):\n * it re-targets that same node and *adds* another value instead of replacing\n * (breaks `sh:maxCount 1`). For a replace, **omit `__id`** — the engine drops the\n * old edge and writes a fresh node, so the value replaces cleanly. (Use `Shape.create`\n * for `__id` at creation; use `.delete()` or `{remove: […]}` for full owned-node cleanup.)\n *\n * **On a node that does not exist, this still writes — untyped.** `update`'s WHERE is a\n * bare `OPTIONAL`, so it matches whether or not the subject exists, and the INSERT fires\n * either way. What it never writes is `rdf:type`, so the result is an id carrying\n * properties that no shape-scoped select can find, and the call reports success. If the\n * node may be absent, use {@link Shape.upsert} instead, which asserts the type. (Whether\n * `update` should instead no-op on an absent node is an open semantic question — core\n * backlog 039.)\n */\n static update<S extends Shape>(\n this: ShapeConstructor<S>,\n data: (p: ExpressionUpdateProxy<S>) => ExpressionUpdateResult<S>,\n ): UpdateBuilder<S, any>;\n static update<S extends Shape, U extends UpdatePartial<S>>(\n this: ShapeConstructor<S>,\n data: U,\n ): UpdateBuilder<S, U>;\n static update<S extends Shape>(\n this: ShapeConstructor<S>,\n data: any,\n ): UpdateBuilder<S, any> {\n return UpdateBuilder.from(this).set(data) as unknown as UpdateBuilder<S, any>;\n }\n\n /**\n * Create the node if it is absent, replace the named properties if it is present —\n * in one request.\n *\n * ```ts\n * await SourceDocument.upsert(values).for({id});\n * ```\n *\n * This replaces the branch callers otherwise hand-roll:\n *\n * ```ts\n * if (await S.exists({id})) await S.update(values).for({id});\n * else await S.create({id, ...values});\n * ```\n *\n * which costs two round-trips, races between them, and — if the check is wrong in the\n * `false` direction — takes the `create` branch silently, duplicating single-valued\n * properties rather than erroring.\n *\n * Semantics:\n * - Replaces **only the properties named**; others on an existing node are untouched.\n * It is not a whole-node replace.\n * - Always asserts the node's type, which `update().for({id})` does not — an update\n * against an absent id writes its properties onto an untyped node.\n * - Returns what `update` returns. It deliberately does **not** report whether it\n * created or replaced: knowing that needs the extra read this avoids.\n * - `.where()` / `.forAll()` are rejected — an upsert targets one known id.\n * - **The id goes in `.for(id)`, not in the data object** — unlike `create`, where an\n * `id`/`__id` in the data names the node being created. Here the data object is\n * properties only; an `id` in it is rejected.\n * - Expression-valued fields are rejected: an expression reads the node's current\n * value, which does not exist when upsert creates it, and the property would be\n * silently skipped.\n */\n static upsert<S extends Shape, U extends UpdatePartial<S>>(\n this: ShapeConstructor<S>,\n data: U,\n ): UpdateBuilder<S, U> {\n return UpdateBuilder.upsertFrom(this).set(data) as unknown as UpdateBuilder<S, U>;\n }\n\n static create<S extends Shape, U extends UpdatePartial<S>>(\n this: ShapeConstructor<S>,\n updateObjectOrFn?: U,\n ): CreateBuilder<S, U> {\n let builder = CreateBuilder.from(this) as CreateBuilder<S, any>;\n if (updateObjectOrFn) {\n builder = builder.set(updateObjectOrFn);\n }\n return builder as unknown as CreateBuilder<S, U>;\n }\n\n static delete<S extends Shape>(\n this: ShapeConstructor<S>,\n id: DeleteId | DeleteId[] | NodeReferenceValue[],\n ): DeleteBuilder<S> {\n return DeleteBuilder.from(this, id) as DeleteBuilder<S>;\n }\n\n /** Delete all instances of this shape type. Returns void. */\n static deleteAll<S extends Shape>(\n this: ShapeConstructor<S>,\n ): DeleteBuilder<S, void> {\n return (DeleteBuilder.from(this) as DeleteBuilder<S>).all();\n }\n\n /** Delete instances matching a condition. Sugar for `.delete().where(fn)`. Returns void. */\n static deleteWhere<S extends Shape>(\n this: ShapeConstructor<S>,\n fn: WhereClause<S>,\n ): DeleteBuilder<S, void> {\n return (DeleteBuilder.from(this) as DeleteBuilder<S>).where(fn);\n }\n\n /**\n * @deprecated Unused; the DSL reads property shapes via `nodeShapeData` free\n * functions and static metadata. Scheduled for removal.\n */\n static mapPropertyShapes<S extends Shape, ResponseType = unknown>(\n this: ShapeConstructor<S>,\n mapFunction?: PropertyShapeMapFunction<S, ResponseType>,\n ): ResponseType {\n // SAFETY: dummyShape is used as a dynamic proxy target — we assign .proxy and\n // access arbitrary property names on it, which S doesn't declare.\n let dummyShape: any = createShapeTarget(this);\n dummyShape.proxy = new Proxy(dummyShape, {\n get(target, key, receiver) {\n if (typeof key === 'string') {\n if (key in dummyShape) {\n if (typeof dummyShape[key] === 'function') {\n return target[key].bind(target);\n }\n let propertyShape = getPropertyShapeByLabel(\n dummyShape.constructor,\n key.toString(),\n );\n if (propertyShape) {\n return propertyShape;\n }\n throw new Error(\n `${this.name}.${key.toString()} is missing a @linkedProperty decorator. This method can only access decorated get/set methods.`,\n );\n }\n }\n },\n });\n return mapFunction(dummyShape.proxy);\n }\n\n /**\n * @deprecated Unused; shapes are metadata and no longer materialized into\n * `ShapeSet`s of instances. Scheduled for removal.\n */\n static getSetOf<T extends Shape>(\n this: ShapeConstructor<T>,\n values: Iterable<T | NodeReferenceValue | string>,\n ): ShapeSet<T> {\n const set = new ShapeSet<T>();\n for (const value of values) {\n if (value instanceof Shape) {\n set.add(value as T);\n } else {\n const instance = createShapeTarget(\n this,\n typeof value === 'string' ? value : value.id,\n );\n set.add(instance);\n }\n }\n return set;\n }\n}\n"]}
1
+ {"version":3,"file":"Shape.js","sourceRoot":"","sources":["../../../src/shapes/Shape.ts"],"names":[],"mappings":";;;;;;;;;AAcA,OAAO,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAC,UAAU,EAAC,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAC,YAAY,EAAC,MAAM,4BAA4B,CAAC;AAGxD,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAC,aAAa,EAAgB,MAAM,6BAA6B,CAAC;AAEzE,OAAO,EAAC,uBAAuB,EAAC,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAC,QAAQ,EAAC,MAAM,4BAA4B,CAAC;AA2BpD;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAC/B,UAA8C,EAC9C,EAAW;IAEX,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,CAAM,CAAC;IACxD,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;QACrB,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;IACjB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,OAAgB,KAAK;IAUzB,0EAA0E;IAC1E,+EAA+E;IAC/E,2EAA2E;IAC3E,8EAA8E;IAC9E,gFAAgF;IAChF,0EAA0E;IAC1E,gFAAgF;IAChF,YAAY,IAA6B;QACvC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,CAAC,EAAE,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;QACtD,CAAC;IACH,CAAC;IAED,IAAI,SAAS;QACX,OAAQ,IAAI,CAAC,WAA4B,CAAC,KAAK,CAAC;IAClD,CAAC;IAED,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED,IAAI,GAAG,CAAC,KAAa;QACnB,IAAI,CAAC,EAAE,GAAG,KAAK,CAAC;IAClB,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,cAAc,CAAC,UAAwB,EAAE,IAAyB;QACvE,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,UAAU,KAAK,KAAK,EAAE,CAAC;gBACzB,OAAO;YACT,CAAC;YACD,MAAM,SAAS,GAAG,UAAU,CAAC,WAAW,CAAC;YACzC,IAAI,SAAS,EAAE,CAAC;gBACd,IAAI,GAAG,SAAS,CAAC;YACnB,CAAC;QACH,CAAC;QACD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO;QACT,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;QACvB,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACpC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QAC5C,CAAC;QACD,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACjD,CAAC;IAsBD,MAAM,CAAC,MAAM,CAMX,QAA6B;QAE7B,IAAI,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAA8B,CAAC;QACnE,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,QAAe,CAAC,CAAC;QAC5C,CAAC;QACD,OAAO,OAAyC,CAAC;IACnD,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,SAAS;QASd,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,SAAS,EAAsC,CAAC;IACjF,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6CG;IACH,MAAM,CAAO,MAAM,CAEjB,EAAwE,EACxE,MAAiB;;YAEjB,4EAA4E;YAC5E,qDAAqD;YACrD,IAAK,IAAgB,KAAK,KAAK,EAAE,CAAC;gBAChC,6EAA6E;gBAC7E,uDAAuD;gBACvD,OAAO,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC7C,CAAC;YACD,OAAO,YAAY,CAAC,IAAI,CAAC,IAA2B,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC/E,CAAC;KAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,MAAM,CAAO,KAAK,CAEhB,MAAiB;;YAEjB,gFAAgF;YAChF,gCAAgC;YAChC,IAAK,IAAgB,KAAK,KAAK,EAAE,CAAC;gBAChC,MAAM,IAAI,KAAK,CACb,uEAAuE;oBACvE,6EAA6E;oBAC7E,4EAA4E;oBAC5E,sDAAsD,CACvD,CAAC;YACJ,CAAC;YACD,OAAO,YAAY,CAAC,EAAE,CAAC;gBACrB,UAAU,EAAE,YAAY,CAAC,IAA2B,CAAC;aACtD,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC;KAAA;IAkCD,MAAM,CAAC,MAAM,CAEX,IAAS;QAET,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAqC,CAAC;IAChF,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAiCG;IACH,MAAM,CAAC,MAAM,CAEX,IAAO;QAEP,OAAO,aAAa,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAmC,CAAC;IACpF,CAAC;IAED,MAAM,CAAC,MAAM,CAEX,gBAAoB;QAEpB,IAAI,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAA0B,CAAC;QAChE,IAAI,gBAAgB,EAAE,CAAC;YACrB,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,OAAyC,CAAC;IACnD,CAAC;IAED,MAAM,CAAC,MAAM,CAEX,EAAgD;QAEhD,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAqB,CAAC;IAC1D,CAAC;IAED,6DAA6D;IAC7D,MAAM,CAAC,SAAS;QAGd,OAAQ,aAAa,CAAC,IAAI,CAAC,IAAI,CAAsB,CAAC,GAAG,EAAE,CAAC;IAC9D,CAAC;IAED,4FAA4F;IAC5F,MAAM,CAAC,WAAW,CAEhB,EAAkB;QAElB,OAAQ,aAAa,CAAC,IAAI,CAAC,IAAI,CAAsB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClE,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,iBAAiB,CAEtB,WAAuD;QAEvD,8EAA8E;QAC9E,kEAAkE;QAClE,IAAI,UAAU,GAAQ,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC9C,UAAU,CAAC,KAAK,GAAG,IAAI,KAAK,CAAC,UAAU,EAAE;YACvC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ;gBACvB,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;oBAC5B,IAAI,GAAG,IAAI,UAAU,EAAE,CAAC;wBACtB,IAAI,OAAO,UAAU,CAAC,GAAG,CAAC,KAAK,UAAU,EAAE,CAAC;4BAC1C,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;wBAClC,CAAC;wBACD,IAAI,aAAa,GAAG,uBAAuB,CACzC,UAAU,CAAC,WAAW,EACtB,GAAG,CAAC,QAAQ,EAAE,CACf,CAAC;wBACF,IAAI,aAAa,EAAE,CAAC;4BAClB,OAAO,aAAa,CAAC;wBACvB,CAAC;wBACD,MAAM,IAAI,KAAK,CACb,GAAG,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,QAAQ,EAAE,iGAAiG,CAChI,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;SACF,CAAC,CAAC;QACH,OAAO,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,QAAQ,CAEb,MAAiD;QAEjD,MAAM,GAAG,GAAG,IAAI,QAAQ,EAAK,CAAC;QAC9B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;gBAC3B,GAAG,CAAC,GAAG,CAAC,KAAU,CAAC,CAAC;YACtB,CAAC;iBAAM,CAAC;gBACN,MAAM,QAAQ,GAAG,iBAAiB,CAChC,IAAI,EACJ,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAC7C,CAAC;gBACF,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;;AAtYM,iBAAW,GAAuB,IAAI,CAAC;AAEvC,mBAAa,GAAmC,IAAI,GAAG,EAAE,CAAC","sourcesContent":["/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\nimport type {NodeShapeData, PropertyShapeData} from './nodeShapeData.js';\nimport type {\n QueryBuildFn,\n QueryResponseToResultType,\n SelectAllQueryResponse,\n WhereClause,\n} from '../queries/SelectQuery.js';\nimport type {NodeReferenceValue, UpdatePartial} from '../queries/QueryFactory.js';\nimport type {NodeId} from '../queries/MutationQuery.js';\nimport {QueryBuilder} from '../queries/QueryBuilder.js';\nimport {AskBuilder} from '../queries/AskBuilder.js';\nimport {CountBuilder} from '../queries/CountBuilder.js';\nimport {resolveShape} from '../queries/resolveShape.js';\nimport type {PendingQueryContext} from '../queries/QueryContext.js';\nimport type {IDataset} from '../interfaces/IDataset.js';\nimport {CreateBuilder} from '../queries/CreateBuilder.js';\nimport {UpdateBuilder} from '../queries/UpdateBuilder.js';\nimport {DeleteBuilder, type DeleteId} from '../queries/DeleteBuilder.js';\nimport type {ExpressionUpdateProxy, ExpressionUpdateResult} from '../expressions/ExpressionMethods.js';\nimport {getPropertyShapeByLabel} from '../utils/ShapeClass.js';\nimport {ShapeSet} from '../collections/ShapeSet.js';\n\n//shape that returns property shapes for its keys\ntype AccessPropertiesShape<T extends Shape> = {\n [P in keyof T]: PropertyShapeData;\n};\ntype PropertyShapeMapFunction<T extends Shape, ResponseType> = (\n p: AccessPropertiesShape<T>,\n) => ResponseType;\n\n/**\n * Concrete constructor type for Shape subclasses — used at runtime boundaries\n * (Builder `from()` methods, Shape static `this` parameters, mutation factories).\n *\n * Uses concrete `new` (not `abstract new`) so TypeScript accepts the class as a\n * value and reads its static `.shape`/`.targetClass` without casts. NOTE: `new\n * shape()` is only a *type-level* capability — at runtime the `Shape` constructor\n * throws (shapes are metadata, not data). The framework builds proxy targets via\n * `createShapeTarget()` (Object.create), never `new`.\n */\nexport type ShapeConstructor<S extends Shape = Shape> = (new (\n ...args: any[]\n) => S) & {\n shape: NodeShapeData;\n targetClass?: NodeReferenceValue;\n};\n\n/**\n * @internal\n * Build a constructor-less, prototype-linked Shape used ONLY as a proxy /\n * metadata-carrier target inside the query DSL (never handed to consumers and\n * never persisted). It is a genuine `Shape` on the prototype chain — so\n * `.constructor`, the `nodeShape` getter, `ShapeSet`, and `getLeastSpecificShape`\n * all work — but it deliberately bypasses the `Shape` constructor, which is\n * guarded to reject direct instantiation. Not exported from the package index.\n */\nexport function createShapeTarget<S extends Shape>(\n shapeClass: ShapeConstructor<S> | typeof Shape,\n id?: string,\n): S {\n const target = Object.create(shapeClass.prototype) as S;\n if (id !== undefined) {\n target.id = id;\n }\n return target;\n}\n\nexport abstract class Shape {\n static targetClass: NodeReferenceValue = null;\n static shape: NodeShapeData;\n static typesToShapes: Map<string, Set<typeof Shape>> = new Map();\n\n __queryContextId?: string;\n /** The query-context name this shape was registered under (for `{@ctx}` refs). */\n __queryContextName?: string;\n id?: string;\n\n // Instantiation guard DEFERRED on the `lego-demo` branch (per René): some\n // framework shapes (e.g. server `LocalFileStore`) legitimately `extends Shape`\n // and are constructed as runtime service objects, which the upstream guard\n // (core report 026) rejects at backend boot. The `validate()`/`assertValid()`\n // work from the same dev merge is KEPT; only the constructor throw is relaxed —\n // restoring pre-guard behaviour: accept an optional node ref and set `id`\n // (mirrors `createShapeTarget`). Re-enable the guard once those shapes migrate.\n constructor(node?: string | {id?: string}) {\n if (node !== undefined) {\n this.id = typeof node === 'string' ? node : node.id;\n }\n }\n\n get nodeShape(): NodeShapeData {\n return (this.constructor as typeof Shape).shape;\n }\n\n get uri(): string {\n return this.id;\n }\n\n set uri(value: string) {\n this.id = value;\n }\n\n /**\n * @internal\n * @param shapeClass\n * @param type\n */\n static registerByType(shapeClass: typeof Shape, type?: NodeReferenceValue) {\n if (!type) {\n if (shapeClass === Shape) {\n return;\n }\n const shapeType = shapeClass.targetClass;\n if (shapeType) {\n type = shapeType;\n }\n }\n if (!type) {\n return;\n }\n const typeId = type.id;\n if (!this.typesToShapes.has(typeId)) {\n this.typesToShapes.set(typeId, new Set());\n }\n this.typesToShapes.get(typeId).add(shapeClass);\n }\n\n /**\n * Select properties of instances of this shape.\n * Chain `.for(id)` to target a single entity, or `.forAll(ids)` for multiple.\n * The select callback receives a proxy of the shape for type-safe property access.\n */\n static select<\n S extends Shape,\n R = unknown,\n ResultType = QueryResponseToResultType<R, S>[],\n >(\n this: ShapeConstructor<S>,\n selectFn: QueryBuildFn<S, R>,\n ): QueryBuilder<S, R, ResultType>;\n static select<\n S extends Shape,\n R = unknown,\n ResultType = QueryResponseToResultType<R, S>[],\n >(\n this: ShapeConstructor<S>,\n ): QueryBuilder<S, R, ResultType>;\n static select<\n S extends Shape,\n R = unknown,\n ResultType = QueryResponseToResultType<R, S>[],\n >(\n this: ShapeConstructor<S>,\n selectFn?: QueryBuildFn<S, R>,\n ): QueryBuilder<S, R, ResultType> {\n let builder = QueryBuilder.from(this) as QueryBuilder<S, any, any>;\n if (selectFn) {\n builder = builder.select(selectFn as any);\n }\n return builder as QueryBuilder<S, R, ResultType>;\n }\n\n /**\n * Select all decorated properties of this shape.\n * Chain `.for(id)` to target a single entity.\n */\n static selectAll<\n S extends Shape,\n ResultType = QueryResponseToResultType<\n SelectAllQueryResponse<S>,\n S\n >[],\n >(\n this: ShapeConstructor<S>,\n ): QueryBuilder<S, any, ResultType> {\n return QueryBuilder.from(this).selectAll() as QueryBuilder<S, any, ResultType>;\n }\n\n /**\n * Whether a node with this id exists as an instance of this shape.\n *\n * On the base class — `Shape.exists(uri)` — it means something different and\n * weaker: does a node with this IRI exist **at all**, under any type or none.\n * There is no shape to constrain by, so no `rdf:type` triple is emitted and the\n * query is `ASK { <uri> ?p ?o }`. (`Shape` is free to mean this because the\n * shapes themselves are described by `NodeShape` and `PropertyShape`, so\n * `Shape.exists` is not needed for \"is this a shape?\".) Because a shapeless ask\n * has no shape to route on, a router asks every dataset it knows — see\n * `LinkedStorage.askQuery`.\n *\n * ```typescript\n * if (await SourceDocument.exists({id})) {\n * await SourceDocument.update(values).for({id});\n * } else {\n * await SourceDocument.create({id, ...values});\n * }\n * ```\n *\n * Resolves to a real `boolean` — unlike `select().where(…).one()`, which resolves\n * to a row or `null` and leaves the conversion (and the failure modes) to the\n * caller. Runs the cheapest correct query: against a SPARQL store, an\n * `ASK WHERE { ?a0 rdf:type <ShapeClass> . FILTER(?a0 = <id>) }` — the shape's\n * type triple and an equality filter on the subject, nothing else.\n *\n * Note the type triple: this asks whether the node exists **as an instance of\n * this shape**. A node with that IRI and a different type answers `false`.\n *\n * A `null`/`undefined` id resolves to `false` without touching the store — as does\n * a `PendingQueryContext` whose value has not landed yet, since there is no subject\n * to ask about. (An unresolved context inside a *where clause* rejects instead; see\n * {@link QueryBuilder.exists}.)\n *\n * **Errors reject — they are never reported as `false`.** Do not wrap this in a\n * `.catch(() => false)`: that is exactly how a broken existence check hides,\n * turning every `exists ? update : create` into an unconditional `create`.\n *\n * For \"does *anything* match?\", compose on the builder instead:\n * `await Person.select().where(p => p.name.equals('Semmy')).exists()`.\n *\n * @param id The node id: a string IRI, a `{id}` reference, or a `PendingQueryContext`.\n * A malformed string IRI rejects (it does not throw synchronously).\n * @param target Optional explicit dataset to run against; omitted uses the\n * global query dispatch.\n */\n static async exists<S extends Shape>(\n this: ShapeConstructor<S> | typeof Shape,\n id: string | NodeReferenceValue | PendingQueryContext | null | undefined,\n target?: IDataset,\n ): Promise<boolean> {\n // `async`, so that a bad string IRI (resolveUriOrThrow) rejects rather than\n // throwing synchronously past the caller's .catch().\n if ((this as unknown) === Shape) {\n // Called on the base class: no shape to constrain by, so no rdf:type triple.\n // `ASK { <iri> ?p ?o }` — does this node exist at all?\n return AskBuilder.forNode(id).exec(target);\n }\n return QueryBuilder.from(this as ShapeConstructor<S>).for(id).exists(target);\n }\n\n /**\n * How many instances of this shape exist — a real `number`.\n *\n * ```typescript\n * const total = await Person.count();\n * ```\n *\n * This is the unfiltered total. For a filtered one, compose on the builder, which\n * is the form a paging table wants:\n *\n * ```typescript\n * await Person.select().where(p => p.name.equals('Semmy')).count();\n * ```\n *\n * Runs the cheapest correct query: against a SPARQL store,\n * `SELECT (COUNT(DISTINCT ?a0) AS ?count) WHERE { ?a0 rdf:type <ShapeClass> }` —\n * the shape's type triple and nothing else. Note the type triple: this counts\n * instances **of this shape**, not nodes in general.\n *\n * **On the base class — `Shape.count()` — this throws.** Unlike\n * {@link Shape.exists}, whose shapeless reading (\"does this node exist at all?\")\n * is useful and cheap, a shapeless count would count every node in every dataset\n * the router knows. No caller means that, so it is refused rather than answered.\n *\n * **Errors reject — they are never reported as `0`.** Do not wrap this in a\n * `.catch(() => 0)`: `0` is a plausible count, so a broken one renders an empty\n * table that looks exactly like real data.\n *\n * @param target Optional explicit dataset to run against; omitted uses the\n * global query dispatch.\n */\n static async count<S extends Shape>(\n this: ShapeConstructor<S> | typeof Shape,\n target?: IDataset,\n ): Promise<number> {\n // `async`, so the base-class refusal rejects rather than throwing synchronously\n // past the caller's `.catch()`.\n if ((this as unknown) === Shape) {\n throw new Error(\n 'Shape.count() has no meaning on the base class: there is no shape to ' +\n 'constrain by, so it would count every node in the store, under any type or ' +\n 'none. Call it on a shape subclass (`Person.count()`), or compose a filter ' +\n 'on the builder (`Person.select().where(…).count()`).',\n );\n }\n return CountBuilder.of({\n shapeClass: resolveShape(this as ShapeConstructor<S>),\n }).exec(target);\n }\n\n /**\n * Update properties of an instance of this shape.\n * Chain `.for(id)` to target a specific entity.\n *\n * ```typescript\n * await Person.update({name: 'Alice'}).for({id: '...'});\n * ```\n *\n * **`__id` is for *new* nodes only** — a fixed id for a nested node being\n * created (here, when **adding** to a relation: `update({friends: {add: {__id, name}}})`).\n * Do NOT put `__id` on a plain single-valued *replace* (`update({image: {__id, contentUrl}})`):\n * it re-targets that same node and *adds* another value instead of replacing\n * (breaks `sh:maxCount 1`). For a replace, **omit `__id`** — the engine drops the\n * old edge and writes a fresh node, so the value replaces cleanly. (Use `Shape.create`\n * for `__id` at creation; use `.delete()` or `{remove: […]}` for full owned-node cleanup.)\n *\n * **On a node that does not exist, this still writes — untyped.** `update`'s WHERE is a\n * bare `OPTIONAL`, so it matches whether or not the subject exists, and the INSERT fires\n * either way. What it never writes is `rdf:type`, so the result is an id carrying\n * properties that no shape-scoped select can find, and the call reports success. If the\n * node may be absent, use {@link Shape.upsert} instead, which asserts the type. (Whether\n * `update` should instead no-op on an absent node is an open semantic question — core\n * backlog 039.)\n */\n static update<S extends Shape>(\n this: ShapeConstructor<S>,\n data: (p: ExpressionUpdateProxy<S>) => ExpressionUpdateResult<S>,\n ): UpdateBuilder<S, any>;\n static update<S extends Shape, U extends UpdatePartial<S>>(\n this: ShapeConstructor<S>,\n data: U,\n ): UpdateBuilder<S, U>;\n static update<S extends Shape>(\n this: ShapeConstructor<S>,\n data: any,\n ): UpdateBuilder<S, any> {\n return UpdateBuilder.from(this).set(data) as unknown as UpdateBuilder<S, any>;\n }\n\n /**\n * Create the node if it is absent, replace the named properties if it is present —\n * in one request.\n *\n * ```ts\n * await SourceDocument.upsert(values).for({id});\n * ```\n *\n * This replaces the branch callers otherwise hand-roll:\n *\n * ```ts\n * if (await S.exists({id})) await S.update(values).for({id});\n * else await S.create({id, ...values});\n * ```\n *\n * which costs two round-trips, races between them, and — if the check is wrong in the\n * `false` direction — takes the `create` branch silently, duplicating single-valued\n * properties rather than erroring.\n *\n * Semantics:\n * - Replaces **only the properties named**; others on an existing node are untouched.\n * It is not a whole-node replace.\n * - Always asserts the node's type, which `update().for({id})` does not — an update\n * against an absent id writes its properties onto an untyped node.\n * - Returns what `update` returns. It deliberately does **not** report whether it\n * created or replaced: knowing that needs the extra read this avoids.\n * - `.where()` / `.forAll()` are rejected — an upsert targets one known id.\n * - **The id goes in `.for(id)`, not in the data object** — unlike `create`, where an\n * `id`/`__id` in the data names the node being created. Here the data object is\n * properties only; an `id` in it is rejected.\n * - Expression-valued fields are rejected: an expression reads the node's current\n * value, which does not exist when upsert creates it, and the property would be\n * silently skipped.\n */\n static upsert<S extends Shape, U extends UpdatePartial<S>>(\n this: ShapeConstructor<S>,\n data: U,\n ): UpdateBuilder<S, U> {\n return UpdateBuilder.upsertFrom(this).set(data) as unknown as UpdateBuilder<S, U>;\n }\n\n static create<S extends Shape, U extends UpdatePartial<S>>(\n this: ShapeConstructor<S>,\n updateObjectOrFn?: U,\n ): CreateBuilder<S, U> {\n let builder = CreateBuilder.from(this) as CreateBuilder<S, any>;\n if (updateObjectOrFn) {\n builder = builder.set(updateObjectOrFn);\n }\n return builder as unknown as CreateBuilder<S, U>;\n }\n\n static delete<S extends Shape>(\n this: ShapeConstructor<S>,\n id: DeleteId | DeleteId[] | NodeReferenceValue[],\n ): DeleteBuilder<S> {\n return DeleteBuilder.from(this, id) as DeleteBuilder<S>;\n }\n\n /** Delete all instances of this shape type. Returns void. */\n static deleteAll<S extends Shape>(\n this: ShapeConstructor<S>,\n ): DeleteBuilder<S, void> {\n return (DeleteBuilder.from(this) as DeleteBuilder<S>).all();\n }\n\n /** Delete instances matching a condition. Sugar for `.delete().where(fn)`. Returns void. */\n static deleteWhere<S extends Shape>(\n this: ShapeConstructor<S>,\n fn: WhereClause<S>,\n ): DeleteBuilder<S, void> {\n return (DeleteBuilder.from(this) as DeleteBuilder<S>).where(fn);\n }\n\n /**\n * @deprecated Unused; the DSL reads property shapes via `nodeShapeData` free\n * functions and static metadata. Scheduled for removal.\n */\n static mapPropertyShapes<S extends Shape, ResponseType = unknown>(\n this: ShapeConstructor<S>,\n mapFunction?: PropertyShapeMapFunction<S, ResponseType>,\n ): ResponseType {\n // SAFETY: dummyShape is used as a dynamic proxy target — we assign .proxy and\n // access arbitrary property names on it, which S doesn't declare.\n let dummyShape: any = createShapeTarget(this);\n dummyShape.proxy = new Proxy(dummyShape, {\n get(target, key, receiver) {\n if (typeof key === 'string') {\n if (key in dummyShape) {\n if (typeof dummyShape[key] === 'function') {\n return target[key].bind(target);\n }\n let propertyShape = getPropertyShapeByLabel(\n dummyShape.constructor,\n key.toString(),\n );\n if (propertyShape) {\n return propertyShape;\n }\n throw new Error(\n `${this.name}.${key.toString()} is missing a @linkedProperty decorator. This method can only access decorated get/set methods.`,\n );\n }\n }\n },\n });\n return mapFunction(dummyShape.proxy);\n }\n\n /**\n * @deprecated Unused; shapes are metadata and no longer materialized into\n * `ShapeSet`s of instances. Scheduled for removal.\n */\n static getSetOf<T extends Shape>(\n this: ShapeConstructor<T>,\n values: Iterable<T | NodeReferenceValue | string>,\n ): ShapeSet<T> {\n const set = new ShapeSet<T>();\n for (const value of values) {\n if (value instanceof Shape) {\n set.add(value as T);\n } else {\n const instance = createShapeTarget(\n this,\n typeof value === 'string' ? value : value.id,\n );\n set.add(instance);\n }\n }\n return set;\n }\n}\n"]}
@@ -1,4 +1,5 @@
1
- import { getAllNodeShapes, getRegistryVersion, getSuperShapes } from '../utils/ShapeClass.js';
1
+ import { RegistryCache } from '../utils/registryCache.js';
2
+ import { getAllNodeShapes, getSuperShapes } from '../utils/ShapeClass.js';
2
3
  // ---------------------------------------------------------------------------
3
4
  // Factories
4
5
  // ---------------------------------------------------------------------------
@@ -65,8 +66,8 @@ export function getPropertyShapes(nodeShape, includeSuperClasses = false) {
65
66
  // ---------------------------------------------------------------------------
66
67
  // The registry scan behind predicate, datatype and dependency resolution, cached on
67
68
  // successful lookups only and invalidated by the registry version.
68
- const propertyShapeCache = new Map();
69
- let propertyShapeCacheVersion = -1;
69
+ // One map per catalog, as every registry-derived cache.
70
+ const propertyShapeCache = new RegistryCache();
70
71
  /**
71
72
  * Find the property shape declared under a property-shape IRI, across every
72
73
  * registered node shape.
@@ -83,18 +84,14 @@ let propertyShapeCacheVersion = -1;
83
84
  * still resolve correctly afterwards.
84
85
  */
85
86
  export function findPropertyShapeById(propertyId) {
86
- const version = getRegistryVersion();
87
- if (version !== propertyShapeCacheVersion) {
88
- propertyShapeCache.clear();
89
- propertyShapeCacheVersion = version;
90
- }
91
- const cached = propertyShapeCache.get(propertyId);
87
+ const cache = propertyShapeCache.get();
88
+ const cached = cache.get(propertyId);
92
89
  if (cached)
93
90
  return cached;
94
91
  for (const nodeShape of getAllNodeShapes().values()) {
95
92
  const propertyShape = getPropertyShapes(nodeShape, true).find((prop) => prop.id === propertyId);
96
93
  if (propertyShape) {
97
- propertyShapeCache.set(propertyId, propertyShape);
94
+ cache.set(propertyId, propertyShape);
98
95
  return propertyShape;
99
96
  }
100
97
  }
@@ -1 +1 @@
1
- {"version":3,"file":"nodeShapeData.js","sourceRoot":"","sources":["../../../src/shapes/nodeShapeData.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\nimport type {NodeReferenceValue} from '../utils/NodeReference.js';\nimport type {PathExpr} from '../paths/PropertyPathExpr.js';\nimport {getAllNodeShapes, getRegistryVersion, getSuperShapes} from '../utils/ShapeClass.js';\n\n/**\n * Plain-object SHACL metadata — the QResult-like shape of a `sh:PropertyShape`.\n *\n * Shapes are metadata, not data: a shape class's `.shape` and its property shapes\n * are plain objects (no class instance, no methods). Operations that used to be\n * instance methods now live as free functions in this module.\n */\nexport interface PropertyShapeData {\n id: string;\n label: string;\n path: PathExpr;\n nodeKind?: NodeReferenceValue;\n datatype?: NodeReferenceValue;\n minCount?: number;\n maxCount?: number;\n name?: string;\n description?: string;\n order?: number;\n group?: string;\n /**\n * `core:displayRank` — a single linear importance rank, lower = more\n * important. Truncated display contexts (\"the top 3 properties\") derive from it.\n * Distinct from `order`, which is arrangement rather than importance.\n */\n displayRank?: number;\n /** `core:displayHidden` — omit from generic rendering. */\n displayHidden?: boolean;\n class?: NodeReferenceValue;\n in?: (NodeReferenceValue | string | number | boolean)[];\n equalsConstraint?: NodeReferenceValue;\n disjoint?: NodeReferenceValue;\n lessThan?: NodeReferenceValue;\n lessThanOrEquals?: NodeReferenceValue;\n /** Value-range constraints (sh:minInclusive / sh:maxInclusive / sh:minExclusive / sh:maxExclusive). */\n minInclusive?: number;\n maxInclusive?: number;\n minExclusive?: number | string;\n maxExclusive?: number;\n /** String-length constraints (sh:minLength / sh:maxLength). */\n minLength?: number;\n maxLength?: number;\n /** Regex constraint (sh:pattern); serialized as its source string. */\n pattern?: RegExp;\n hasValueConstraint?: NodeReferenceValue | string | number | boolean;\n defaultValue?: unknown;\n sortBy?: PathExpr;\n valueShape?: NodeReferenceValue;\n /** Composition marker: the value(s) of this property are owned by the subject. */\n contains?: boolean;\n parentNodeShape?: NodeShapeData;\n}\n\n/**\n * Plain-object SHACL metadata — the QResult-like shape of a `sh:NodeShape`.\n */\nexport interface NodeShapeData {\n id: string;\n label?: string;\n description?: string;\n targetClass?: NodeReferenceValue;\n extends?: NodeReferenceValue;\n /**\n * @deprecated No effect: every node a `contains` property reaches is deleted with its owner.\n * Kept for shapes that declare it and persisted as `core:dependent`.\n */\n dependent?: boolean;\n /** sh:closed — target nodes with undeclared properties are invalid. */\n closed?: boolean;\n /** sh:ignoredProperties — extra properties permitted when the shape is closed. */\n ignoredProperties?: NodeReferenceValue[];\n propertyShapes: PropertyShapeData[];\n}\n\n/**\n * Result object produced by `propertyShapeToResult()` (SHACL projection).\n * @deprecated No production callers remain (the `NodeShape.properties` getter and\n * `PropertyShape.getResult()` were removed with the plain-object conversion). Read\n * the plain `PropertyShapeData` fields directly. Scheduled for removal.\n */\nexport interface PropertyShapeResult {\n id: string;\n label: string;\n path: PathExpr;\n nodeKind?: NodeReferenceValue;\n datatype?: NodeReferenceValue;\n minCount?: number;\n maxCount?: number;\n name?: string;\n description?: string;\n order?: number;\n group?: string;\n class?: NodeReferenceValue;\n in?: (NodeReferenceValue | string | number | boolean)[];\n equals?: NodeReferenceValue;\n disjoint?: NodeReferenceValue;\n lessThan?: NodeReferenceValue;\n lessThanOrEquals?: NodeReferenceValue;\n hasValue?: NodeReferenceValue | string | number | boolean;\n defaultValue?: unknown;\n sortBy?: PathExpr;\n valueShape?: NodeReferenceValue;\n}\n\n// ---------------------------------------------------------------------------\n// Factories\n// ---------------------------------------------------------------------------\n\n/** Create an empty NodeShape metadata object for the given shape IRI. */\nexport function createNodeShapeData(id: string): NodeShapeData {\n return {id, propertyShapes: []};\n}\n\n/** Create a blank PropertyShape metadata object (fields filled in by the caller). */\nexport function createPropertyShapeData(): PropertyShapeData {\n return {id: '', label: '', path: null as unknown as PathExpr};\n}\n\n// ---------------------------------------------------------------------------\n// Free functions (formerly NodeShape/PropertyShape instance methods)\n// ---------------------------------------------------------------------------\n\n/** One-time warning keys for shapes whose `propertyShapes` is missing/invalid. */\nconst warnedMissingPropertyShapes = new Set<string>();\n\n/**\n * Read a node shape's own property shapes, tolerating a missing/invalid array.\n *\n * A non-array `propertyShapes` almost always means a duplicate `@_linked/core`\n * install or a non-normalized static shape on a superclass; warn once per shape id\n * (diagnostic parity with the former `NodeShape.listPropertyShapesSafe`) and treat\n * it as empty.\n */\nfunction ownPropertyShapes(nodeShape: NodeShapeData): PropertyShapeData[] {\n const own = (nodeShape as {propertyShapes?: PropertyShapeData[]}).propertyShapes;\n if (Array.isArray(own)) {\n return own;\n }\n const id = (nodeShape as {id?: string}).id ?? '';\n if (!warnedMissingPropertyShapes.has(id)) {\n warnedMissingPropertyShapes.add(id);\n console.warn(\n `[@_linked/core] static shape ${id ? `'${id}'` : '(unknown id)'} has missing ` +\n `or invalid propertyShapes. Treating as []. Often caused by duplicate ` +\n `@_linked/core installs or a non-normalized static shape on a superclass.`,\n );\n }\n return [];\n}\n\n/**\n * Property shapes declared on this NodeShape. With `includeSuperClasses`, walks the\n * inheritance chain via `getSuperShapes` and concatenates each ancestor's own property\n * shapes — the prototype chain for a class-backed shape, `extends` for one known only\n * as data.\n */\nexport function getPropertyShapes(\n nodeShape: NodeShapeData,\n includeSuperClasses: boolean = false,\n): PropertyShapeData[] {\n if (!includeSuperClasses) {\n return [...ownPropertyShapes(nodeShape)];\n }\n // One inheritance walk, shared with getSuperShapes: the prototype chain for a\n // class-backed shape (which includes the framework `Shape` root and its `label` /\n // `type` properties), or `extends` through the registry for a shape that exists only\n // as data. Previously this walked the prototype chain directly and returned ONLY own\n // properties when no class existed — so a project-authored shape silently lost\n // everything it inherited.\n const res: PropertyShapeData[] = [...ownPropertyShapes(nodeShape)];\n for (const superShape of getSuperShapes(nodeShape)) {\n res.push(...ownPropertyShapes(superShape));\n }\n return res;\n}\n\n// ---------------------------------------------------------------------------\n// Property-shape lookup by id\n// ---------------------------------------------------------------------------\n\n// The registry scan behind predicate, datatype and dependency resolution, cached on\n// successful lookups only and invalidated by the registry version.\nconst propertyShapeCache = new Map<string, PropertyShapeData>();\nlet propertyShapeCacheVersion = -1;\n\n/**\n * Find the property shape declared under a property-shape IRI, across every\n * registered node shape.\n *\n * Scans the METAMODEL registry, which holds every shape — authored or data-only.\n * Scanning the class registry meant a data-only property was never found, and the\n * caller then fell through to emitting the PROPERTY SHAPE's IRI as the SPARQL\n * predicate: a silently wrong query that matched nothing, with no error.\n *\n * Cache keyed on the registration version rather than the registry SIZE. Size does not\n * change when a shape is re-registered in place, which is exactly what happens when a\n * shape is edited and its metadata replaced. Only successful resolutions are cached — a\n * not-found is never stored, so a property resolved before its shape registers can\n * still resolve correctly afterwards.\n */\nexport function findPropertyShapeById(propertyId: string): PropertyShapeData | undefined {\n const version = getRegistryVersion();\n if (version !== propertyShapeCacheVersion) {\n propertyShapeCache.clear();\n propertyShapeCacheVersion = version;\n }\n const cached = propertyShapeCache.get(propertyId);\n if (cached) return cached;\n\n for (const nodeShape of getAllNodeShapes().values()) {\n const propertyShape = getPropertyShapes(nodeShape, true).find(\n (prop: {id?: string}) => prop.id === propertyId,\n );\n if (propertyShape) {\n propertyShapeCache.set(propertyId, propertyShape);\n return propertyShape;\n }\n }\n return undefined;\n}\n\n/** Property shapes across the inheritance chain, deduped by label (most specific wins). */\nexport function getUniquePropertyShapes(\n nodeShape: NodeShapeData,\n): PropertyShapeData[] {\n const unique: PropertyShapeData[] = [];\n const seen = new Set<string>();\n for (const ps of getPropertyShapes(nodeShape, true)) {\n if (!seen.has(ps.label)) {\n seen.add(ps.label);\n unique.push(ps);\n }\n }\n return unique;\n}\n\n/**\n * Find a property shape by label. With `checkSubShapes` (default true), ascends the\n * inheritance chain if the label isn't found locally.\n *\n * Shares `getSuperShapes` with {@link getPropertyShapes}, so the singular and plural\n * lookups can never disagree about what a shape inherits — and so a shape registered\n * from data resolves inherited properties through `extends` rather than stopping at its\n * own. This is what `getPropertyShapeByLabel` delegates to (PR #211), so closing the gap\n * here closes it for the query proxies too. See docs/backlog/040.\n */\nexport function getPropertyShape(\n nodeShape: NodeShapeData,\n label: string,\n checkSubShapes: boolean = true,\n): PropertyShapeData | undefined {\n const own = ownPropertyShapes(nodeShape).find((ps) => ps.label === label);\n if (own || !checkSubShapes) return own;\n for (const superShape of getSuperShapes(nodeShape)) {\n const inherited = ownPropertyShapes(superShape).find(\n (ps) => ps.label === label,\n );\n if (inherited) return inherited;\n }\n return undefined;\n}\n\n/** Two node shapes are equal when they share the same IRI. */\nexport function nodeShapeEquals(a: NodeShapeData, b?: NodeShapeData): boolean {\n return !!b && a?.id === b.id;\n}\n\n/** Append a property shape to a node shape, wiring the back-reference. */\nexport function addPropertyShape(\n nodeShape: NodeShapeData,\n propertyShape: PropertyShapeData,\n): void {\n propertyShape.parentNodeShape = nodeShape;\n if (!Array.isArray(nodeShape.propertyShapes)) {\n nodeShape.propertyShapes = [];\n }\n nodeShape.propertyShapes.push(propertyShape);\n}\n\n/** Shallow-clone a property shape (used by property override / disallow). */\nexport function clonePropertyShape(\n propertyShape: PropertyShapeData,\n): PropertyShapeData {\n return {...propertyShape};\n}\n\n/**\n * Project a property shape to its SHACL result object.\n * @deprecated No production callers remain. Read the plain `PropertyShapeData`\n * fields directly (note this projection renames `equalsConstraint`→`equals`,\n * `hasValueConstraint`→`hasValue`, and serializes `pattern` to its source string).\n * Scheduled for removal.\n */\nexport function propertyShapeToResult(ps: PropertyShapeData): PropertyShapeResult {\n const result: Record<string, unknown> & {id: string; label: string; path: PathExpr} = {\n id: ps.id,\n label: ps.label,\n path: ps.path,\n };\n if (ps.nodeKind) result.nodeKind = ps.nodeKind;\n if (ps.datatype) result.datatype = ps.datatype;\n if (typeof ps.minCount === 'number') result.minCount = ps.minCount;\n if (typeof ps.maxCount === 'number') result.maxCount = ps.maxCount;\n if (ps.name) result.name = ps.name;\n if (ps.description) result.description = ps.description;\n if (typeof ps.order === 'number') result.order = ps.order;\n if (ps.group) result.group = ps.group;\n if (ps.class) result.class = ps.class;\n if (ps.in) result.in = ps.in;\n if (ps.equalsConstraint) result.equals = ps.equalsConstraint;\n if (ps.disjoint) result.disjoint = ps.disjoint;\n if (ps.lessThan) result.lessThan = ps.lessThan;\n if (ps.lessThanOrEquals) result.lessThanOrEquals = ps.lessThanOrEquals;\n if (ps.minInclusive !== undefined) result.minInclusive = ps.minInclusive;\n if (ps.maxInclusive !== undefined) result.maxInclusive = ps.maxInclusive;\n if (ps.minExclusive !== undefined) result.minExclusive = ps.minExclusive;\n if (ps.maxExclusive !== undefined) result.maxExclusive = ps.maxExclusive;\n if (typeof ps.minLength === 'number') result.minLength = ps.minLength;\n if (typeof ps.maxLength === 'number') result.maxLength = ps.maxLength;\n if (ps.pattern) result.pattern = ps.pattern.source;\n if (ps.hasValueConstraint !== undefined) result.hasValue = ps.hasValueConstraint;\n if (ps.defaultValue !== undefined) result.defaultValue = ps.defaultValue;\n if (ps.sortBy) result.sortBy = ps.sortBy;\n if (ps.valueShape) result.valueShape = ps.valueShape;\n return result as PropertyShapeResult;\n}\n"]}
1
+ {"version":3,"file":"nodeShapeData.js","sourceRoot":"","sources":["../../../src/shapes/nodeShapeData.ts"],"names":[],"mappings":"AAMA,OAAO,EAAC,aAAa,EAAC,MAAM,2BAA2B,CAAC;AAExD,OAAO,EAAC,gBAAgB,EAAE,cAAc,EAAC,MAAM,wBAAwB,CAAC;AAyGxE,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,yEAAyE;AACzE,MAAM,UAAU,mBAAmB,CAAC,EAAU;IAC5C,OAAO,EAAC,EAAE,EAAE,cAAc,EAAE,EAAE,EAAC,CAAC;AAClC,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,uBAAuB;IACrC,OAAO,EAAC,EAAE,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,IAA2B,EAAC,CAAC;AAChE,CAAC;AAED,8EAA8E;AAC9E,qEAAqE;AACrE,8EAA8E;AAE9E,kFAAkF;AAClF,MAAM,2BAA2B,GAAG,IAAI,GAAG,EAAU,CAAC;AAEtD;;;;;;;GAOG;AACH,SAAS,iBAAiB,CAAC,SAAwB;;IACjD,MAAM,GAAG,GAAI,SAAoD,CAAC,cAAc,CAAC;IACjF,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC;IACb,CAAC;IACD,MAAM,EAAE,GAAG,MAAC,SAA2B,CAAC,EAAE,mCAAI,EAAE,CAAC;IACjD,IAAI,CAAC,2BAA2B,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;QACzC,2BAA2B,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACpC,OAAO,CAAC,IAAI,CACV,gCAAgC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,cAAc,eAAe;YAC5E,uEAAuE;YACvE,0EAA0E,CAC7E,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAAwB,EACxB,sBAA+B,KAAK;IAEpC,IAAI,CAAC,mBAAmB,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC;IAC3C,CAAC;IACD,8EAA8E;IAC9E,kFAAkF;IAClF,qFAAqF;IACrF,qFAAqF;IACrF,+EAA+E;IAC/E,2BAA2B;IAC3B,MAAM,GAAG,GAAwB,CAAC,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC;IACnE,KAAK,MAAM,UAAU,IAAI,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC;QACnD,GAAG,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC;IAC7C,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,8EAA8E;AAC9E,8BAA8B;AAC9B,8EAA8E;AAE9E,oFAAoF;AACpF,mEAAmE;AACnE,wDAAwD;AACxD,MAAM,kBAAkB,GAAG,IAAI,aAAa,EAA6B,CAAC;AAE1E;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,qBAAqB,CAAC,UAAkB;IACtD,MAAM,KAAK,GAAG,kBAAkB,CAAC,GAAG,EAAE,CAAC;IACvC,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACrC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,KAAK,MAAM,SAAS,IAAI,gBAAgB,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC;QACpD,MAAM,aAAa,GAAG,iBAAiB,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,IAAI,CAC3D,CAAC,IAAmB,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,UAAU,CAChD,CAAC;QACF,IAAI,aAAa,EAAE,CAAC;YAClB,KAAK,CAAC,GAAG,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;YACrC,OAAO,aAAa,CAAC;QACvB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,2FAA2F;AAC3F,MAAM,UAAU,uBAAuB,CACrC,SAAwB;IAExB,MAAM,MAAM,GAAwB,EAAE,CAAC;IACvC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,EAAE,IAAI,iBAAiB,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC;QACpD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;YACnB,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAAwB,EACxB,KAAa,EACb,iBAA0B,IAAI;IAE9B,MAAM,GAAG,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC;IAC1E,IAAI,GAAG,IAAI,CAAC,cAAc;QAAE,OAAO,GAAG,CAAC;IACvC,KAAK,MAAM,UAAU,IAAI,cAAc,CAAC,SAAS,CAAC,EAAE,CAAC;QACnD,MAAM,SAAS,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC,IAAI,CAClD,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,KAAK,KAAK,CAC3B,CAAC;QACF,IAAI,SAAS;YAAE,OAAO,SAAS,CAAC;IAClC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,eAAe,CAAC,CAAgB,EAAE,CAAiB;IACjE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAA,CAAC,aAAD,CAAC,uBAAD,CAAC,CAAE,EAAE,MAAK,CAAC,CAAC,EAAE,CAAC;AAC/B,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,gBAAgB,CAC9B,SAAwB,EACxB,aAAgC;IAEhC,aAAa,CAAC,eAAe,GAAG,SAAS,CAAC;IAC1C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,CAAC;QAC7C,SAAS,CAAC,cAAc,GAAG,EAAE,CAAC;IAChC,CAAC;IACD,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAC/C,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,kBAAkB,CAChC,aAAgC;IAEhC,yBAAW,aAAa,EAAE;AAC5B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,EAAqB;IACzD,MAAM,MAAM,GAA0E;QACpF,EAAE,EAAE,EAAE,CAAC,EAAE;QACT,KAAK,EAAE,EAAE,CAAC,KAAK;QACf,IAAI,EAAE,EAAE,CAAC,IAAI;KACd,CAAC;IACF,IAAI,EAAE,CAAC,QAAQ;QAAE,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC/C,IAAI,EAAE,CAAC,QAAQ;QAAE,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC/C,IAAI,OAAO,EAAE,CAAC,QAAQ,KAAK,QAAQ;QAAE,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IACnE,IAAI,OAAO,EAAE,CAAC,QAAQ,KAAK,QAAQ;QAAE,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IACnE,IAAI,EAAE,CAAC,IAAI;QAAE,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC;IACnC,IAAI,EAAE,CAAC,WAAW;QAAE,MAAM,CAAC,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC;IACxD,IAAI,OAAO,EAAE,CAAC,KAAK,KAAK,QAAQ;QAAE,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC;IAC1D,IAAI,EAAE,CAAC,KAAK;QAAE,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC;IACtC,IAAI,EAAE,CAAC,KAAK;QAAE,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC;IACtC,IAAI,EAAE,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;IAC7B,IAAI,EAAE,CAAC,gBAAgB;QAAE,MAAM,CAAC,MAAM,GAAG,EAAE,CAAC,gBAAgB,CAAC;IAC7D,IAAI,EAAE,CAAC,QAAQ;QAAE,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC/C,IAAI,EAAE,CAAC,QAAQ;QAAE,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC/C,IAAI,EAAE,CAAC,gBAAgB;QAAE,MAAM,CAAC,gBAAgB,GAAG,EAAE,CAAC,gBAAgB,CAAC;IACvE,IAAI,EAAE,CAAC,YAAY,KAAK,SAAS;QAAE,MAAM,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC;IACzE,IAAI,EAAE,CAAC,YAAY,KAAK,SAAS;QAAE,MAAM,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC;IACzE,IAAI,EAAE,CAAC,YAAY,KAAK,SAAS;QAAE,MAAM,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC;IACzE,IAAI,EAAE,CAAC,YAAY,KAAK,SAAS;QAAE,MAAM,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC;IACzE,IAAI,OAAO,EAAE,CAAC,SAAS,KAAK,QAAQ;QAAE,MAAM,CAAC,SAAS,GAAG,EAAE,CAAC,SAAS,CAAC;IACtE,IAAI,OAAO,EAAE,CAAC,SAAS,KAAK,QAAQ;QAAE,MAAM,CAAC,SAAS,GAAG,EAAE,CAAC,SAAS,CAAC;IACtE,IAAI,EAAE,CAAC,OAAO;QAAE,MAAM,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IACnD,IAAI,EAAE,CAAC,kBAAkB,KAAK,SAAS;QAAE,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,kBAAkB,CAAC;IACjF,IAAI,EAAE,CAAC,YAAY,KAAK,SAAS;QAAE,MAAM,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC;IACzE,IAAI,EAAE,CAAC,MAAM;QAAE,MAAM,CAAC,MAAM,GAAG,EAAE,CAAC,MAAM,CAAC;IACzC,IAAI,EAAE,CAAC,UAAU;QAAE,MAAM,CAAC,UAAU,GAAG,EAAE,CAAC,UAAU,CAAC;IACrD,OAAO,MAA6B,CAAC;AACvC,CAAC","sourcesContent":["/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\nimport type {NodeReferenceValue} from '../utils/NodeReference.js';\nimport {RegistryCache} from '../utils/registryCache.js';\nimport type {PathExpr} from '../paths/PropertyPathExpr.js';\nimport {getAllNodeShapes, getSuperShapes} from '../utils/ShapeClass.js';\n\n/**\n * Plain-object SHACL metadata — the QResult-like shape of a `sh:PropertyShape`.\n *\n * Shapes are metadata, not data: a shape class's `.shape` and its property shapes\n * are plain objects (no class instance, no methods). Operations that used to be\n * instance methods now live as free functions in this module.\n */\nexport interface PropertyShapeData {\n id: string;\n label: string;\n path: PathExpr;\n nodeKind?: NodeReferenceValue;\n datatype?: NodeReferenceValue;\n minCount?: number;\n maxCount?: number;\n name?: string;\n description?: string;\n order?: number;\n group?: string;\n /**\n * `core:displayRank` — a single linear importance rank, lower = more\n * important. Truncated display contexts (\"the top 3 properties\") derive from it.\n * Distinct from `order`, which is arrangement rather than importance.\n */\n displayRank?: number;\n /** `core:displayHidden` — omit from generic rendering. */\n displayHidden?: boolean;\n class?: NodeReferenceValue;\n in?: (NodeReferenceValue | string | number | boolean)[];\n equalsConstraint?: NodeReferenceValue;\n disjoint?: NodeReferenceValue;\n lessThan?: NodeReferenceValue;\n lessThanOrEquals?: NodeReferenceValue;\n /** Value-range constraints (sh:minInclusive / sh:maxInclusive / sh:minExclusive / sh:maxExclusive). */\n minInclusive?: number;\n maxInclusive?: number;\n minExclusive?: number | string;\n maxExclusive?: number;\n /** String-length constraints (sh:minLength / sh:maxLength). */\n minLength?: number;\n maxLength?: number;\n /** Regex constraint (sh:pattern); serialized as its source string. */\n pattern?: RegExp;\n hasValueConstraint?: NodeReferenceValue | string | number | boolean;\n defaultValue?: unknown;\n sortBy?: PathExpr;\n valueShape?: NodeReferenceValue;\n /** Composition marker: the value(s) of this property are owned by the subject. */\n contains?: boolean;\n parentNodeShape?: NodeShapeData;\n}\n\n/**\n * Plain-object SHACL metadata — the QResult-like shape of a `sh:NodeShape`.\n */\nexport interface NodeShapeData {\n id: string;\n label?: string;\n description?: string;\n targetClass?: NodeReferenceValue;\n extends?: NodeReferenceValue;\n /**\n * @deprecated No effect: every node a `contains` property reaches is deleted with its owner.\n * Kept for shapes that declare it and persisted as `core:dependent`.\n */\n dependent?: boolean;\n /** sh:closed — target nodes with undeclared properties are invalid. */\n closed?: boolean;\n /** sh:ignoredProperties — extra properties permitted when the shape is closed. */\n ignoredProperties?: NodeReferenceValue[];\n propertyShapes: PropertyShapeData[];\n}\n\n/**\n * Result object produced by `propertyShapeToResult()` (SHACL projection).\n * @deprecated No production callers remain (the `NodeShape.properties` getter and\n * `PropertyShape.getResult()` were removed with the plain-object conversion). Read\n * the plain `PropertyShapeData` fields directly. Scheduled for removal.\n */\nexport interface PropertyShapeResult {\n id: string;\n label: string;\n path: PathExpr;\n nodeKind?: NodeReferenceValue;\n datatype?: NodeReferenceValue;\n minCount?: number;\n maxCount?: number;\n name?: string;\n description?: string;\n order?: number;\n group?: string;\n class?: NodeReferenceValue;\n in?: (NodeReferenceValue | string | number | boolean)[];\n equals?: NodeReferenceValue;\n disjoint?: NodeReferenceValue;\n lessThan?: NodeReferenceValue;\n lessThanOrEquals?: NodeReferenceValue;\n hasValue?: NodeReferenceValue | string | number | boolean;\n defaultValue?: unknown;\n sortBy?: PathExpr;\n valueShape?: NodeReferenceValue;\n}\n\n// ---------------------------------------------------------------------------\n// Factories\n// ---------------------------------------------------------------------------\n\n/** Create an empty NodeShape metadata object for the given shape IRI. */\nexport function createNodeShapeData(id: string): NodeShapeData {\n return {id, propertyShapes: []};\n}\n\n/** Create a blank PropertyShape metadata object (fields filled in by the caller). */\nexport function createPropertyShapeData(): PropertyShapeData {\n return {id: '', label: '', path: null as unknown as PathExpr};\n}\n\n// ---------------------------------------------------------------------------\n// Free functions (formerly NodeShape/PropertyShape instance methods)\n// ---------------------------------------------------------------------------\n\n/** One-time warning keys for shapes whose `propertyShapes` is missing/invalid. */\nconst warnedMissingPropertyShapes = new Set<string>();\n\n/**\n * Read a node shape's own property shapes, tolerating a missing/invalid array.\n *\n * A non-array `propertyShapes` almost always means a duplicate `@_linked/core`\n * install or a non-normalized static shape on a superclass; warn once per shape id\n * (diagnostic parity with the former `NodeShape.listPropertyShapesSafe`) and treat\n * it as empty.\n */\nfunction ownPropertyShapes(nodeShape: NodeShapeData): PropertyShapeData[] {\n const own = (nodeShape as {propertyShapes?: PropertyShapeData[]}).propertyShapes;\n if (Array.isArray(own)) {\n return own;\n }\n const id = (nodeShape as {id?: string}).id ?? '';\n if (!warnedMissingPropertyShapes.has(id)) {\n warnedMissingPropertyShapes.add(id);\n console.warn(\n `[@_linked/core] static shape ${id ? `'${id}'` : '(unknown id)'} has missing ` +\n `or invalid propertyShapes. Treating as []. Often caused by duplicate ` +\n `@_linked/core installs or a non-normalized static shape on a superclass.`,\n );\n }\n return [];\n}\n\n/**\n * Property shapes declared on this NodeShape. With `includeSuperClasses`, walks the\n * inheritance chain via `getSuperShapes` and concatenates each ancestor's own property\n * shapes — the prototype chain for a class-backed shape, `extends` for one known only\n * as data.\n */\nexport function getPropertyShapes(\n nodeShape: NodeShapeData,\n includeSuperClasses: boolean = false,\n): PropertyShapeData[] {\n if (!includeSuperClasses) {\n return [...ownPropertyShapes(nodeShape)];\n }\n // One inheritance walk, shared with getSuperShapes: the prototype chain for a\n // class-backed shape (which includes the framework `Shape` root and its `label` /\n // `type` properties), or `extends` through the registry for a shape that exists only\n // as data. Previously this walked the prototype chain directly and returned ONLY own\n // properties when no class existed — so a project-authored shape silently lost\n // everything it inherited.\n const res: PropertyShapeData[] = [...ownPropertyShapes(nodeShape)];\n for (const superShape of getSuperShapes(nodeShape)) {\n res.push(...ownPropertyShapes(superShape));\n }\n return res;\n}\n\n// ---------------------------------------------------------------------------\n// Property-shape lookup by id\n// ---------------------------------------------------------------------------\n\n// The registry scan behind predicate, datatype and dependency resolution, cached on\n// successful lookups only and invalidated by the registry version.\n// One map per catalog, as every registry-derived cache.\nconst propertyShapeCache = new RegistryCache<string, PropertyShapeData>();\n\n/**\n * Find the property shape declared under a property-shape IRI, across every\n * registered node shape.\n *\n * Scans the METAMODEL registry, which holds every shape — authored or data-only.\n * Scanning the class registry meant a data-only property was never found, and the\n * caller then fell through to emitting the PROPERTY SHAPE's IRI as the SPARQL\n * predicate: a silently wrong query that matched nothing, with no error.\n *\n * Cache keyed on the registration version rather than the registry SIZE. Size does not\n * change when a shape is re-registered in place, which is exactly what happens when a\n * shape is edited and its metadata replaced. Only successful resolutions are cached — a\n * not-found is never stored, so a property resolved before its shape registers can\n * still resolve correctly afterwards.\n */\nexport function findPropertyShapeById(propertyId: string): PropertyShapeData | undefined {\n const cache = propertyShapeCache.get();\n const cached = cache.get(propertyId);\n if (cached) return cached;\n\n for (const nodeShape of getAllNodeShapes().values()) {\n const propertyShape = getPropertyShapes(nodeShape, true).find(\n (prop: {id?: string}) => prop.id === propertyId,\n );\n if (propertyShape) {\n cache.set(propertyId, propertyShape);\n return propertyShape;\n }\n }\n return undefined;\n}\n\n/** Property shapes across the inheritance chain, deduped by label (most specific wins). */\nexport function getUniquePropertyShapes(\n nodeShape: NodeShapeData,\n): PropertyShapeData[] {\n const unique: PropertyShapeData[] = [];\n const seen = new Set<string>();\n for (const ps of getPropertyShapes(nodeShape, true)) {\n if (!seen.has(ps.label)) {\n seen.add(ps.label);\n unique.push(ps);\n }\n }\n return unique;\n}\n\n/**\n * Find a property shape by label. With `checkSubShapes` (default true), ascends the\n * inheritance chain if the label isn't found locally.\n *\n * Shares `getSuperShapes` with {@link getPropertyShapes}, so the singular and plural\n * lookups can never disagree about what a shape inherits — and so a shape registered\n * from data resolves inherited properties through `extends` rather than stopping at its\n * own. This is what `getPropertyShapeByLabel` delegates to (PR #211), so closing the gap\n * here closes it for the query proxies too. See docs/backlog/040.\n */\nexport function getPropertyShape(\n nodeShape: NodeShapeData,\n label: string,\n checkSubShapes: boolean = true,\n): PropertyShapeData | undefined {\n const own = ownPropertyShapes(nodeShape).find((ps) => ps.label === label);\n if (own || !checkSubShapes) return own;\n for (const superShape of getSuperShapes(nodeShape)) {\n const inherited = ownPropertyShapes(superShape).find(\n (ps) => ps.label === label,\n );\n if (inherited) return inherited;\n }\n return undefined;\n}\n\n/** Two node shapes are equal when they share the same IRI. */\nexport function nodeShapeEquals(a: NodeShapeData, b?: NodeShapeData): boolean {\n return !!b && a?.id === b.id;\n}\n\n/** Append a property shape to a node shape, wiring the back-reference. */\nexport function addPropertyShape(\n nodeShape: NodeShapeData,\n propertyShape: PropertyShapeData,\n): void {\n propertyShape.parentNodeShape = nodeShape;\n if (!Array.isArray(nodeShape.propertyShapes)) {\n nodeShape.propertyShapes = [];\n }\n nodeShape.propertyShapes.push(propertyShape);\n}\n\n/** Shallow-clone a property shape (used by property override / disallow). */\nexport function clonePropertyShape(\n propertyShape: PropertyShapeData,\n): PropertyShapeData {\n return {...propertyShape};\n}\n\n/**\n * Project a property shape to its SHACL result object.\n * @deprecated No production callers remain. Read the plain `PropertyShapeData`\n * fields directly (note this projection renames `equalsConstraint`→`equals`,\n * `hasValueConstraint`→`hasValue`, and serializes `pattern` to its source string).\n * Scheduled for removal.\n */\nexport function propertyShapeToResult(ps: PropertyShapeData): PropertyShapeResult {\n const result: Record<string, unknown> & {id: string; label: string; path: PathExpr} = {\n id: ps.id,\n label: ps.label,\n path: ps.path,\n };\n if (ps.nodeKind) result.nodeKind = ps.nodeKind;\n if (ps.datatype) result.datatype = ps.datatype;\n if (typeof ps.minCount === 'number') result.minCount = ps.minCount;\n if (typeof ps.maxCount === 'number') result.maxCount = ps.maxCount;\n if (ps.name) result.name = ps.name;\n if (ps.description) result.description = ps.description;\n if (typeof ps.order === 'number') result.order = ps.order;\n if (ps.group) result.group = ps.group;\n if (ps.class) result.class = ps.class;\n if (ps.in) result.in = ps.in;\n if (ps.equalsConstraint) result.equals = ps.equalsConstraint;\n if (ps.disjoint) result.disjoint = ps.disjoint;\n if (ps.lessThan) result.lessThan = ps.lessThan;\n if (ps.lessThanOrEquals) result.lessThanOrEquals = ps.lessThanOrEquals;\n if (ps.minInclusive !== undefined) result.minInclusive = ps.minInclusive;\n if (ps.maxInclusive !== undefined) result.maxInclusive = ps.maxInclusive;\n if (ps.minExclusive !== undefined) result.minExclusive = ps.minExclusive;\n if (ps.maxExclusive !== undefined) result.maxExclusive = ps.maxExclusive;\n if (typeof ps.minLength === 'number') result.minLength = ps.minLength;\n if (typeof ps.maxLength === 'number') result.maxLength = ps.maxLength;\n if (ps.pattern) result.pattern = ps.pattern.source;\n if (ps.hasValueConstraint !== undefined) result.hasValue = ps.hasValueConstraint;\n if (ps.defaultValue !== undefined) result.defaultValue = ps.defaultValue;\n if (ps.sortBy) result.sortBy = ps.sortBy;\n if (ps.valueShape) result.valueShape = ps.valueShape;\n return result as PropertyShapeResult;\n}\n"]}
@@ -22,7 +22,7 @@
22
22
  * get one on demand from `getOrCreateShapeAdapter`, so `getShapeClass` keeps reporting
23
23
  * only the shapes that have a real authored class.
24
24
  */
25
- import { getShapeClass, registerNodeShape } from '../utils/ShapeClass.js';
25
+ import { assertNoActiveCatalog, getShapeClass, registerNodeShape } from '../utils/ShapeClass.js';
26
26
  import { warnOnReservedPropertyLabel } from '../queries/reservedQueryNames.js';
27
27
  import { fromWire, isNodeShapeWire } from './nodeShapeWire.js';
28
28
  /**
@@ -38,6 +38,7 @@ import { fromWire, isNodeShapeWire } from './nodeShapeWire.js';
38
38
  */
39
39
  export function registerRuntimeShape(shape) {
40
40
  var _a;
41
+ assertNoActiveCatalog();
41
42
  if (!(shape === null || shape === void 0 ? void 0 : shape.id))
42
43
  return false;
43
44
  if (getShapeClass(shape.id))
@@ -60,6 +61,7 @@ export function registerRuntimeShape(shape) {
60
61
  * arrived. Callers should not have to know that, so this sorts by depth first.
61
62
  */
62
63
  export function registerRuntimeShapes(shapes) {
64
+ assertNoActiveCatalog();
63
65
  const list = Array.isArray(shapes) ? shapes : Object.values(shapes);
64
66
  const byId = new Map(list.map((s) => [s.id, s]));
65
67
  const depth = (shape) => {
@@ -1 +1 @@
1
- {"version":3,"file":"registerRuntimeShape.js","sourceRoot":"","sources":["../../../src/shapes/registerRuntimeShape.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAC,aAAa,EAAE,iBAAiB,EAAC,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAC,2BAA2B,EAAC,MAAM,kCAAkC,CAAC;AAE7E,OAAO,EAAC,QAAQ,EAAE,eAAe,EAAqB,MAAM,oBAAoB,CAAC;AAEjF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAoC;;IAEpC,IAAI,CAAC,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,EAAE,CAAA;QAAE,OAAO,KAAK,CAAC;IAC7B,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,KAAuB,CAAC;IACjF,iFAAiF;IACjF,oFAAoF;IACpF,mEAAmE;IACnE,KAAK,MAAM,EAAE,IAAI,MAAA,IAAI,CAAC,cAAc,mCAAI,EAAE,EAAE,CAAC;QAC3C,2BAA2B,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAyF;IAEzF,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACpE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjD,MAAM,KAAK,GAAG,CAAC,KAAoC,EAAU,EAAE;;QAC7D,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,MAAM,IAAI,GAAG,IAAI,GAAG,CAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACzC,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,OAAO,CAAA,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,0CAAE,EAAE,KAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;YAC7D,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC5C,IAAI,CAAC,MAAM;gBAAE,MAAM,CAAC,4DAA4D;YAChF,OAAO,GAAG,MAAM,CAAC;YACjB,CAAC,EAAE,CAAC;QACN,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC,CAAC;IAEF,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,IAAI,oBAAoB,CAAC,KAAK,CAAC;YAAE,UAAU,EAAE,CAAC;IAChD,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC","sourcesContent":["/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n/**\n * Register a shape that exists only as data.\n *\n * This is the return leg of the round trip. `@linkedShape` is class → metadata and\n * `syncShapes` is metadata → RDF; this is RDF → metadata, so a shape authored in a\n * project and read back from its materialized SHACL resolves in queries exactly like a\n * compiled one.\n *\n * It takes metadata — `NodeShapeData`, or its wire form — rather than a bespoke DTO. The\n * previous implementation of this idea lived outside core and rebuilt the shape field by\n * field from a projection, which meant it silently dropped `extends` (so a child shape\n * lost every inherited property), the value constraints, and any property whose label was\n * absent. Taking the metamodel removes that whole class of loss: there is nothing to\n * re-derive.\n *\n * No class is synthesized here. Consumers that need a constructor — the query builders —\n * get one on demand from `getOrCreateShapeAdapter`, so `getShapeClass` keeps reporting\n * only the shapes that have a real authored class.\n */\n\nimport {getShapeClass, registerNodeShape} from '../utils/ShapeClass.js';\nimport {warnOnReservedPropertyLabel} from '../queries/reservedQueryNames.js';\nimport type {NodeShapeData} from './nodeShapeData.js';\nimport {fromWire, isNodeShapeWire, type NodeShapeWire} from './nodeShapeWire.js';\n\n/**\n * Register one shape's metadata under its IRI.\n *\n * Idempotent by replacement: registering the same IRI again replaces the metadata, which\n * is what a shape edited in a builder needs. A shape that already has a compiled class is\n * left alone — an authored class is always the better answer, and shadowing it would make\n * behaviour depend on import order.\n *\n * @returns true when the shape was registered, false when a compiled class already owns\n * the IRI.\n */\nexport function registerRuntimeShape(\n shape: NodeShapeData | NodeShapeWire,\n): boolean {\n if (!shape?.id) return false;\n if (getShapeClass(shape.id)) return false;\n const data = isNodeShapeWire(shape) ? fromWire(shape) : (shape as NodeShapeData);\n // A data-only shape never passes through registerPropertyShape, so the collision\n // check has to happen here too — otherwise a shape authored in a builder is the one\n // kind of shape that gets no warning until the field tracer fails.\n for (const ps of data.propertyShapes ?? []) {\n warnOnReservedPropertyLabel(ps.label, data.label, data.id);\n }\n registerNodeShape(data);\n return true;\n}\n\n/**\n * Register a whole catalog, parents before children.\n *\n * Order matters: inheritance is resolved by looking `extends` up in the registry, so a\n * child registered before its parent would resolve an empty chain until the parent\n * arrived. Callers should not have to know that, so this sorts by depth first.\n */\nexport function registerRuntimeShapes(\n shapes: Record<string, NodeShapeData | NodeShapeWire> | (NodeShapeData | NodeShapeWire)[],\n): number {\n const list = Array.isArray(shapes) ? shapes : Object.values(shapes);\n const byId = new Map(list.map((s) => [s.id, s]));\n\n const depth = (shape: NodeShapeData | NodeShapeWire): number => {\n let d = 0;\n const seen = new Set<string>([shape.id]);\n let current = shape;\n while (current?.extends?.id && !seen.has(current.extends.id)) {\n seen.add(current.extends.id);\n const parent = byId.get(current.extends.id);\n if (!parent) break; // parent outside this batch — already registered, or absent\n current = parent;\n d++;\n }\n return d;\n };\n\n let registered = 0;\n for (const shape of [...list].sort((a, b) => depth(a) - depth(b))) {\n if (registerRuntimeShape(shape)) registered++;\n }\n return registered;\n}\n"]}
1
+ {"version":3,"file":"registerRuntimeShape.js","sourceRoot":"","sources":["../../../src/shapes/registerRuntimeShape.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAC,qBAAqB,EAAE,aAAa,EAAE,iBAAiB,EAAC,MAAM,wBAAwB,CAAC;AAC/F,OAAO,EAAC,2BAA2B,EAAC,MAAM,kCAAkC,CAAC;AAE7E,OAAO,EAAC,QAAQ,EAAE,eAAe,EAAqB,MAAM,oBAAoB,CAAC;AAEjF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,oBAAoB,CAClC,KAAoC;;IAEpC,qBAAqB,EAAE,CAAC;IACxB,IAAI,CAAC,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,EAAE,CAAA;QAAE,OAAO,KAAK,CAAC;IAC7B,IAAI,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAE,KAAuB,CAAC;IACjF,iFAAiF;IACjF,oFAAoF;IACpF,mEAAmE;IACnE,KAAK,MAAM,EAAE,IAAI,MAAA,IAAI,CAAC,cAAc,mCAAI,EAAE,EAAE,CAAC;QAC3C,2BAA2B,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACxB,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAyF;IAEzF,qBAAqB,EAAE,CAAC;IACxB,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACpE,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAEjD,MAAM,KAAK,GAAG,CAAC,KAAoC,EAAU,EAAE;;QAC7D,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,MAAM,IAAI,GAAG,IAAI,GAAG,CAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACzC,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,OAAO,CAAA,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,OAAO,0CAAE,EAAE,KAAI,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;YAC7D,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC5C,IAAI,CAAC,MAAM;gBAAE,MAAM,CAAC,4DAA4D;YAChF,OAAO,GAAG,MAAM,CAAC;YACjB,CAAC,EAAE,CAAC;QACN,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC,CAAC;IAEF,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,IAAI,oBAAoB,CAAC,KAAK,CAAC;YAAE,UAAU,EAAE,CAAC;IAChD,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC","sourcesContent":["/*\n * This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not distributed with this\n * file, You can obtain one at https://mozilla.org/MPL/2.0/.\n */\n\n/**\n * Register a shape that exists only as data.\n *\n * This is the return leg of the round trip. `@linkedShape` is class → metadata and\n * `syncShapes` is metadata → RDF; this is RDF → metadata, so a shape authored in a\n * project and read back from its materialized SHACL resolves in queries exactly like a\n * compiled one.\n *\n * It takes metadata — `NodeShapeData`, or its wire form — rather than a bespoke DTO. The\n * previous implementation of this idea lived outside core and rebuilt the shape field by\n * field from a projection, which meant it silently dropped `extends` (so a child shape\n * lost every inherited property), the value constraints, and any property whose label was\n * absent. Taking the metamodel removes that whole class of loss: there is nothing to\n * re-derive.\n *\n * No class is synthesized here. Consumers that need a constructor — the query builders —\n * get one on demand from `getOrCreateShapeAdapter`, so `getShapeClass` keeps reporting\n * only the shapes that have a real authored class.\n */\n\nimport {assertNoActiveCatalog, getShapeClass, registerNodeShape} from '../utils/ShapeClass.js';\nimport {warnOnReservedPropertyLabel} from '../queries/reservedQueryNames.js';\nimport type {NodeShapeData} from './nodeShapeData.js';\nimport {fromWire, isNodeShapeWire, type NodeShapeWire} from './nodeShapeWire.js';\n\n/**\n * Register one shape's metadata under its IRI.\n *\n * Idempotent by replacement: registering the same IRI again replaces the metadata, which\n * is what a shape edited in a builder needs. A shape that already has a compiled class is\n * left alone — an authored class is always the better answer, and shadowing it would make\n * behaviour depend on import order.\n *\n * @returns true when the shape was registered, false when a compiled class already owns\n * the IRI.\n */\nexport function registerRuntimeShape(\n shape: NodeShapeData | NodeShapeWire,\n): boolean {\n assertNoActiveCatalog();\n if (!shape?.id) return false;\n if (getShapeClass(shape.id)) return false;\n const data = isNodeShapeWire(shape) ? fromWire(shape) : (shape as NodeShapeData);\n // A data-only shape never passes through registerPropertyShape, so the collision\n // check has to happen here too — otherwise a shape authored in a builder is the one\n // kind of shape that gets no warning until the field tracer fails.\n for (const ps of data.propertyShapes ?? []) {\n warnOnReservedPropertyLabel(ps.label, data.label, data.id);\n }\n registerNodeShape(data);\n return true;\n}\n\n/**\n * Register a whole catalog, parents before children.\n *\n * Order matters: inheritance is resolved by looking `extends` up in the registry, so a\n * child registered before its parent would resolve an empty chain until the parent\n * arrived. Callers should not have to know that, so this sorts by depth first.\n */\nexport function registerRuntimeShapes(\n shapes: Record<string, NodeShapeData | NodeShapeWire> | (NodeShapeData | NodeShapeWire)[],\n): number {\n assertNoActiveCatalog();\n const list = Array.isArray(shapes) ? shapes : Object.values(shapes);\n const byId = new Map(list.map((s) => [s.id, s]));\n\n const depth = (shape: NodeShapeData | NodeShapeWire): number => {\n let d = 0;\n const seen = new Set<string>([shape.id]);\n let current = shape;\n while (current?.extends?.id && !seen.has(current.extends.id)) {\n seen.add(current.extends.id);\n const parent = byId.get(current.extends.id);\n if (!parent) break; // parent outside this batch — already registered, or absent\n current = parent;\n d++;\n }\n return d;\n };\n\n let registered = 0;\n for (const shape of [...list].sort((a, b) => depth(a) - depth(b))) {\n if (registerRuntimeShape(shape)) registered++;\n }\n return registered;\n}\n"]}
@@ -68,8 +68,9 @@ export interface SyncShapeOptions {
68
68
  force?: boolean;
69
69
  }
70
70
  /**
71
- * Plan an idempotent sync of all code-registered (non-framework) NodeShapes into the store as
72
- * SHACL data — the forward (code → graph) materialization of arch-05 "code is canonical".
71
+ * Plan an idempotent sync of all code-registered NodeShapes into the store as SHACL data — the
72
+ * forward (code → graph) materialization of arch-05 "code is canonical". Core's six built-in
73
+ * shapes are included, like any package's; other shapes in the core package are not.
73
74
  *
74
75
  * Returns built-but-unexecuted thunks; the caller controls execution and batching:
75
76
  * ```ts
@@ -106,6 +107,9 @@ export interface SyncShapeOptions {
106
107
  * parameter: it feeds the stored-hash read and the orphan-detection read (so both are computed
107
108
  * against the same store they'll be written to) and every delete/create thunk. Omitted → the global router;
108
109
  * the returned thunks stay nullary either way.
110
+ *
111
+ * It enumerates the globally registered classes, so it must run outside any `withShapeCatalog`
112
+ * scope: inside one it would see only core's built-ins, which it skips.
109
113
  */
110
114
  export declare function syncShapes(ds?: IDataset, options?: SyncShapesOptions): Promise<Array<() => Promise<void>>>;
111
115
  /**
@@ -21,9 +21,10 @@ import { serializePathToNodeData } from './serializePathToNodeData.js';
21
21
  import { buildSingleOwnerConstraints } from './singleOwnerConstraint.js';
22
22
  import { QueryBuilder } from '../queries/QueryBuilder.js';
23
23
  import { sha256Hex } from '../utils/sha256.js';
24
+ import { builtInShapeIris } from '../utils/ShapeCatalog.js';
24
25
  /**
25
- * The npm scope of framework shapes (NodeShape/PropertyShape/List/PathNode/Shape) that
26
- * describe the meta-model itself and must never be synced as user data.
26
+ * The npm scope of core's shapes. Only its six built-ins (`builtInShapeIris`) are synced; any
27
+ * other shape declared with core's own `linkedShape` (core's test fixtures) is not.
27
28
  */
28
29
  const FRAMEWORK_PACKAGE = '@_linked/core';
29
30
  /**
@@ -274,8 +275,9 @@ function shapeNamespace(iri) {
274
275
  return cut >= 0 ? iri.slice(0, cut + 1) : iri;
275
276
  }
276
277
  /**
277
- * Plan an idempotent sync of all code-registered (non-framework) NodeShapes into the store as
278
- * SHACL data — the forward (code → graph) materialization of arch-05 "code is canonical".
278
+ * Plan an idempotent sync of all code-registered NodeShapes into the store as SHACL data — the
279
+ * forward (code → graph) materialization of arch-05 "code is canonical". Core's six built-in
280
+ * shapes are included, like any package's; other shapes in the core package are not.
279
281
  *
280
282
  * Returns built-but-unexecuted thunks; the caller controls execution and batching:
281
283
  * ```ts
@@ -312,20 +314,24 @@ function shapeNamespace(iri) {
312
314
  * parameter: it feeds the stored-hash read and the orphan-detection read (so both are computed
313
315
  * against the same store they'll be written to) and every delete/create thunk. Omitted → the global router;
314
316
  * the returned thunks stay nullary either way.
317
+ *
318
+ * It enumerates the globally registered classes, so it must run outside any `withShapeCatalog`
319
+ * scope: inside one it would see only core's built-ins, which it skips.
315
320
  */
316
321
  export function syncShapes(ds, options) {
317
322
  return __awaiter(this, void 0, void 0, function* () {
318
323
  var _a;
319
324
  const orphanScope = (_a = options === null || options === void 0 ? void 0 : options.orphanScope) !== null && _a !== void 0 ? _a : 'none';
320
- // 1. Enumerate code-registered user shapes (exclude framework/meta shapes).
325
+ // 1. Enumerate code-registered shapes: every package's, and core's six built-ins (`Shape`,
326
+ // `NodeShape`, `PropertyShape`, `List`, `PathNode`, `PathList`), so a dataset is a complete
327
+ // shape catalog on its own. Other core-package shapes and classes with no package are skipped.
328
+ const builtIns = new Set(builtInShapeIris());
321
329
  const userShapes = [];
322
330
  for (const [iri, shapeClass] of getAllShapeClasses()) {
323
331
  if (!(shapeClass === null || shapeClass === void 0 ? void 0 : shapeClass.shape))
324
332
  continue;
325
333
  const pkg = shapeClass.packageName;
326
- // Skip framework/meta shapes: the core package, and the base `Shape` which is registered
327
- // directly (no applyLinkedShape) and therefore carries no packageName.
328
- if (!pkg || pkg === FRAMEWORK_PACKAGE)
334
+ if (!builtIns.has(iri) && (!pkg || pkg === FRAMEWORK_PACKAGE))
329
335
  continue;
330
336
  userShapes.push({ iri, nodeShape: shapeClass.shape });
331
337
  }
@@ -342,7 +348,8 @@ export function syncShapes(ds, options) {
342
348
  localShapeIris.add(generated);
343
349
  }
344
350
  // Namespaces this process actually writes — the ownership scope for `'ownedNamespaces'` pruning.
345
- const ownedNamespaces = new Set(userShapes.map((s) => shapeNamespace(s.iri)));
351
+ // Core's namespace is never one: every app writes the built-ins, and no app owns core's shapes.
352
+ const ownedNamespaces = new Set(userShapes.filter((s) => !builtIns.has(s.iri)).map((s) => shapeNamespace(s.iri)));
346
353
  const thunks = [];
347
354
  // 2. Per in-code shape whose stored content hash is missing or differs: delete (cascade) then
348
355
  // recreate. An unchanged shape gets no thunk, so syncing an unchanged app writes nothing.