@_linked/core 2.29.0 → 2.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/README.md +20 -2
- package/lib/esm/queries/IRMutation.js +1 -1
- package/lib/esm/queries/IRMutation.js.map +1 -1
- package/lib/esm/queries/IntermediateRepresentation.d.ts +5 -0
- package/lib/esm/queries/IntermediateRepresentation.js.map +1 -1
- package/lib/esm/queries/MutationQuery.js +24 -4
- package/lib/esm/queries/MutationQuery.js.map +1 -1
- package/lib/esm/queries/MutationSerialization.d.ts +5 -4
- package/lib/esm/queries/MutationSerialization.js +38 -5
- package/lib/esm/queries/MutationSerialization.js.map +1 -1
- package/lib/esm/queries/QueryFactory.d.ts +6 -0
- package/lib/esm/queries/QueryFactory.js.map +1 -1
- package/lib/esm/queries/SelectQuery.js +3 -1
- package/lib/esm/queries/SelectQuery.js.map +1 -1
- package/lib/esm/queries/lowerMutationJSON.js +6 -1
- package/lib/esm/queries/lowerMutationJSON.js.map +1 -1
- package/lib/esm/shapes/List.d.ts +1 -0
- package/lib/esm/shapes/List.js.map +1 -1
- package/lib/esm/shapes/PathNode.d.ts +5 -2
- package/lib/esm/shapes/PathNode.js +5 -2
- package/lib/esm/shapes/PathNode.js.map +1 -1
- package/lib/esm/shapes/serializePathToNodeData.d.ts +17 -5
- package/lib/esm/shapes/serializePathToNodeData.js +41 -27
- package/lib/esm/shapes/serializePathToNodeData.js.map +1 -1
- package/lib/esm/shapes/singleOwnerConstraint.js +1 -1
- package/lib/esm/shapes/singleOwnerConstraint.js.map +1 -1
- package/lib/esm/shapes/syncShapes.js +1 -1
- package/lib/esm/shapes/syncShapes.js.map +1 -1
- package/lib/esm/shapes/validation.js +16 -12
- package/lib/esm/shapes/validation.js.map +1 -1
- package/lib/esm/sparql/SparqlAlgebra.d.ts +5 -0
- package/lib/esm/sparql/SparqlAlgebra.js.map +1 -1
- package/lib/esm/sparql/algebraToString.js +2 -0
- package/lib/esm/sparql/algebraToString.js.map +1 -1
- package/lib/esm/sparql/irToAlgebra.js +113 -17
- package/lib/esm/sparql/irToAlgebra.js.map +1 -1
- package/package.json +2 -2
|
@@ -98,7 +98,7 @@ export function decodeNodeData(json, shape) {
|
|
|
98
98
|
const nodeShape = typeof json.__shape === 'string' ? requireShape(json.__shape) : shape;
|
|
99
99
|
const fields = [];
|
|
100
100
|
for (const [key, val] of Object.entries(json)) {
|
|
101
|
-
if (key === '__id' || key === '__shape' || val === undefined)
|
|
101
|
+
if (key === '__id' || key === '__shape' || key === '__blank' || val === undefined)
|
|
102
102
|
continue;
|
|
103
103
|
const prop = getPropertyShape(nodeShape, key);
|
|
104
104
|
if (!prop) {
|
|
@@ -109,6 +109,11 @@ export function decodeNodeData(json, shape) {
|
|
|
109
109
|
const desc = { shape: nodeShape, fields };
|
|
110
110
|
if (typeof json.__id === 'string')
|
|
111
111
|
desc.__id = json.__id;
|
|
112
|
+
if (json.__blank !== undefined) {
|
|
113
|
+
if (json.__blank !== true)
|
|
114
|
+
throw new Error(`__blank must be true when given, got ${JSON.stringify(json.__blank)}`);
|
|
115
|
+
desc.__blank = true;
|
|
116
|
+
}
|
|
112
117
|
return desc;
|
|
113
118
|
}
|
|
114
119
|
function lowerWhere(shape, json) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lowerMutationJSON.js","sourceRoot":"","sources":["../../../src/queries/lowerMutationJSON.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 */\n\n/**\n * Lower mutation DSL-JSON to canonical IR.\n *\n * This is the **IR side** of mutation (de)serialization, kept deliberately\n * separate from the IR-free wire codec in `MutationSerialization.ts`. The\n * canonical-IR builders (`IRMutation` / `IRLower` / …) are reachable only from\n * here and from `lower()`, so a client that serializes mutations to JSON and\n * forwards them (without ever lowering) tree-shakes the whole IR pipeline away.\n */\nimport type {NodeShapeData} from '../shapes/SHACL.js';\nimport {getPropertyShape} from '../shapes/nodeShapeData.js';\nimport {\n type NodeDescriptionValue,\n type PropUpdateValue,\n type SetModificationValue,\n type SinglePropertyUpdateValue,\n type UpdateNodePropertyValue,\n} from './QueryFactory.js';\nimport {ExpressionNode} from '../expressions/ExpressionNode.js';\nimport {getNodeShape, getShapeClass} from '../utils/ShapeClass.js';\nimport type {IRExpression, IRGraphPattern} from './IntermediateRepresentation.js';\nimport {\n deserializeWherePath,\n type WherePathJSON,\n} from './QueryBuilderSerialization.js';\nimport {toWhere} from './IRDesugar.js';\nimport {lowerWhereToIR} from './IRLower.js';\nimport {\n buildCanonicalCreateMutationIR,\n buildCanonicalUpdateMutationIR,\n buildCanonicalUpsertMutationIR,\n buildCanonicalUpdateWhereMutationIR,\n buildCanonicalDeleteMutationIR,\n buildCanonicalDeleteAllMutationIR,\n buildCanonicalDeleteWhereMutationIR,\n} from './IRMutation.js';\nimport type {IRCreateQuery} from './CreateQuery.js';\nimport type {IRUpdateQuery} from './UpdateQuery.js';\nimport type {IRDeleteQuery} from './DeleteQuery.js';\nimport {isContextRefJSON, resolveContextId} from './ContextRef.js';\nimport {assertWireVersion} from './wireVersion.js';\nimport {\n decodeNodeDataToRaw,\n valueShapeOf,\n type MutationJSON,\n type MutationNodeDataJSON,\n type MutationValueJSON,\n} from './MutationSerialization.js';\nimport {assertValid, type ValidationMode} from '../shapes/validation.js';\nimport {decodeValueExpr, type DslJsonValue} from './DslJsonExpression.js';\nimport type {PropertyShapeData} from '../shapes/SHACL.js';\n\nfunction requireShape(shapeId: string): NodeShapeData {\n // Either registry: a project-authored shape arrives as metadata with no class, and\n // requiring a class here made every mutation against one fail at the boundary.\n const shape = getShapeClass(shapeId)?.shape ?? getNodeShape(shapeId);\n if (!shape) {\n throw new Error(\n `Shape '${shapeId}' is not registered. The receiving side must have the shape registered to lower mutation DSL-JSON.`,\n );\n }\n return shape;\n}\n\n// Bound the DSL-JSON decode recursion so a deeply-nested payload from\n// `fromJSON()` cannot exhaust the stack (RangeError DoS). Normal mutation data\n// nests only a handful of levels; 128 is far above any real query.\nconst MAX_DECODE_DEPTH = 128;\nlet _decodeDepth = 0;\n\n/**\n * Decode a DSL-JSON value to the normalized form the canonical-IR builders consume.\n * `currentShape` resolves computed `{@path}`/S-expr; `prop` gives a nested node's shape.\n * Depth-guarded wrapper around {@link decodeValueInner}.\n */\nfunction decodeValue(\n json: MutationValueJSON,\n currentShape: NodeShapeData,\n prop?: PropertyShapeData,\n): PropUpdateValue {\n if (++_decodeDepth > MAX_DECODE_DEPTH) {\n _decodeDepth = 0;\n throw new Error('DSL-JSON value nested too deeply');\n }\n try {\n return decodeValueInner(json, currentShape, prop);\n } finally {\n _decodeDepth--;\n }\n}\n\nfunction decodeValueInner(\n json: MutationValueJSON,\n currentShape: NodeShapeData,\n prop?: PropertyShapeData,\n): PropUpdateValue {\n // S-expr computed value (no IR on the wire)\n if (Array.isArray(json)) {\n const {ir, refs} = decodeValueExpr(json as DslJsonValue, currentShape);\n return new ExpressionNode(ir, refs) as unknown as PropUpdateValue;\n }\n if (json === null) return null as unknown as PropUpdateValue;\n if (typeof json !== 'object') return json as PropUpdateValue; // bare scalar literal\n const o = json as Record<string, unknown>;\n if ('@unset' in o) return undefined;\n if ('@date' in o) return new Date(o['@date'] as string) as unknown as PropUpdateValue;\n if ('@ctx' in o) {\n // Lowering path: a mutation must hit a concrete node, so resolve the\n // context now and throw if it isn't set.\n const id = resolveContextId(o['@ctx'] as string, true)!;\n return {id} as PropUpdateValue;\n }\n if ('@id' in o) return {id: o['@id']} as PropUpdateValue;\n if ('@list' in o) {\n return (o['@list'] as MutationValueJSON[]).map(\n (item) => decodeValue(item, currentShape, prop) as SinglePropertyUpdateValue,\n );\n }\n if ('@add' in o || '@remove' in o) {\n const mod: SetModificationValue = {};\n if (o['@add']) {\n mod.$add = (o['@add'] as MutationValueJSON[]).map((v) =>\n decodeValue(v, currentShape, prop),\n ) as SetModificationValue['$add'];\n }\n if (o['@remove']) mod.$remove = (o['@remove'] as string[]).map((id) => ({id}));\n return mod as PropUpdateValue;\n }\n if ('@path' in o) {\n const {ir, refs} = decodeValueExpr(json as DslJsonValue, currentShape);\n return new ExpressionNode(ir, refs) as unknown as PropUpdateValue;\n }\n // Otherwise: a bare path-keyed nested-node create.\n return decodeNodeData(\n o as MutationNodeDataJSON,\n valueShapeOf(prop) ?? currentShape,\n ) as unknown as PropUpdateValue;\n}\n\n/**\n * Rebuild a normalized node description from path-keyed JSON, resolving labels via the\n * shape. `shape` is the shape inferred from context; `__shape` overrides it.\n */\nexport function decodeNodeData(\n json: MutationNodeDataJSON,\n shape?: NodeShapeData,\n): NodeDescriptionValue {\n const nodeShape =\n typeof json.__shape === 'string' ? requireShape(json.__shape) : shape!;\n const fields: UpdateNodePropertyValue[] = [];\n for (const [key, val] of Object.entries(json)) {\n if (key === '__id' || key === '__shape' || val === undefined) continue;\n const prop = getPropertyShape(nodeShape, key);\n if (!prop) {\n throw new Error(\n `Property '${key}' not found on shape '${nodeShape.label || nodeShape.id}'`,\n );\n }\n fields.push({prop, val: decodeValue(val as MutationValueJSON, nodeShape, prop)} as UpdateNodePropertyValue);\n }\n const desc: NodeDescriptionValue = {shape: nodeShape, fields};\n if (typeof json.__id === 'string') desc.__id = json.__id;\n return desc;\n}\n\nfunction lowerWhere(\n shape: NodeShapeData,\n json: WherePathJSON,\n): {where: IRExpression; wherePatterns: IRGraphPattern[]} {\n const wherePath = deserializeWherePath(shape, json);\n return lowerWhereToIR(toWhere(wherePath));\n}\n\n/**\n * Lower a mutation DSL-JSON envelope to the canonical IR mutation the dataset\n * expects. Mirrors the IR that `lower()` produces from the live builder.\n */\n/**\n * Validate inbound node data against its shape before it becomes IR.\n *\n * The builders validate through `describe()`, which sees raw DSL data. This path\n * never touches a builder, so without this an inbound mutation would be held to\n * a weaker standard than a locally-built one — on the *less* trusted input, at\n * that. `decodeNodeDataToRaw` produces exactly the raw form `describe()` would\n * have validated (it is what `CreateBuilder.fromJSON` feeds through `.set()`),\n * so both paths run the same checks. The second decode is the cost of validating\n * at a boundary; it happens once per inbound mutation.\n */\nfunction assertInboundDataValid(\n json: MutationNodeDataJSON,\n shape: NodeShapeData,\n mode: ValidationMode,\n): void {\n assertValid(shape, decodeNodeDataToRaw(json, shape), {mode});\n}\n\nexport function lowerMutationJSON(\n json: MutationJSON,\n): IRCreateQuery | IRUpdateQuery | IRDeleteQuery {\n assertWireVersion(json.v);\n switch (json.op) {\n case 'create': {\n const shape = requireShape(json.shape);\n assertInboundDataValid(json.data, shape, 'complete');\n return buildCanonicalCreateMutationIR({\n shape,\n description: decodeNodeData(json.data, shape),\n });\n }\n case 'upsert': {\n const shape = requireShape(json.shape);\n assertInboundDataValid(json.data, shape, 'partial');\n const updates = decodeNodeData(json.data, shape);\n const id = isContextRefJSON(json.targetId)\n ? resolveContextId(json.targetId['@ctx'], true)\n : json.targetId;\n if (!id) {\n throw new Error('upsert requires a targetId');\n }\n return buildCanonicalUpsertMutationIR({id, shape, updates});\n }\n case 'update': {\n const shape = requireShape(json.shape);\n assertInboundDataValid(json.data, shape, 'partial');\n const updates = decodeNodeData(json.data, shape);\n if (json.mode === 'for') {\n // A `{@ctx}` target resolves against the current context map; a mutation\n // must hit a concrete subject, so an unresolved context throws.\n const id = isContextRefJSON(json.targetId)\n ? resolveContextId(json.targetId['@ctx'], true)\n : json.targetId;\n if (!id) {\n throw new Error('update mode \"for\" requires a targetId');\n }\n return buildCanonicalUpdateMutationIR({id, shape, updates});\n }\n let where: IRExpression | undefined;\n let wherePatterns: IRGraphPattern[] | undefined;\n if (json.mode === 'where') {\n if (!json.where) throw new Error('update mode \"where\" requires a where clause');\n ({where, wherePatterns} = lowerWhere(shape, json.where));\n }\n return buildCanonicalUpdateWhereMutationIR({shape, updates, where, wherePatterns});\n }\n case 'delete': {\n const shape = requireShape(json.shape);\n if (json.mode === 'ids') {\n return buildCanonicalDeleteMutationIR({\n shape,\n // A `{@ctx}` id resolves against the live map; a delete must hit a\n // concrete node, so an unresolved context throws.\n ids: json.ids.map((id) =>\n isContextRefJSON(id) ? {id: resolveContextId(id['@ctx'], true)!} : {id},\n ),\n });\n }\n if (json.mode === 'all') {\n return buildCanonicalDeleteAllMutationIR({shape});\n }\n const {where, wherePatterns} = lowerWhere(shape, json.where);\n return buildCanonicalDeleteWhereMutationIR({shape, where, wherePatterns});\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"lowerMutationJSON.js","sourceRoot":"","sources":["../../../src/queries/lowerMutationJSON.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 */\n\n/**\n * Lower mutation DSL-JSON to canonical IR.\n *\n * This is the **IR side** of mutation (de)serialization, kept deliberately\n * separate from the IR-free wire codec in `MutationSerialization.ts`. The\n * canonical-IR builders (`IRMutation` / `IRLower` / …) are reachable only from\n * here and from `lower()`, so a client that serializes mutations to JSON and\n * forwards them (without ever lowering) tree-shakes the whole IR pipeline away.\n */\nimport type {NodeShapeData} from '../shapes/SHACL.js';\nimport {getPropertyShape} from '../shapes/nodeShapeData.js';\nimport {\n type NodeDescriptionValue,\n type PropUpdateValue,\n type SetModificationValue,\n type SinglePropertyUpdateValue,\n type UpdateNodePropertyValue,\n} from './QueryFactory.js';\nimport {ExpressionNode} from '../expressions/ExpressionNode.js';\nimport {getNodeShape, getShapeClass} from '../utils/ShapeClass.js';\nimport type {IRExpression, IRGraphPattern} from './IntermediateRepresentation.js';\nimport {\n deserializeWherePath,\n type WherePathJSON,\n} from './QueryBuilderSerialization.js';\nimport {toWhere} from './IRDesugar.js';\nimport {lowerWhereToIR} from './IRLower.js';\nimport {\n buildCanonicalCreateMutationIR,\n buildCanonicalUpdateMutationIR,\n buildCanonicalUpsertMutationIR,\n buildCanonicalUpdateWhereMutationIR,\n buildCanonicalDeleteMutationIR,\n buildCanonicalDeleteAllMutationIR,\n buildCanonicalDeleteWhereMutationIR,\n} from './IRMutation.js';\nimport type {IRCreateQuery} from './CreateQuery.js';\nimport type {IRUpdateQuery} from './UpdateQuery.js';\nimport type {IRDeleteQuery} from './DeleteQuery.js';\nimport {isContextRefJSON, resolveContextId} from './ContextRef.js';\nimport {assertWireVersion} from './wireVersion.js';\nimport {\n decodeNodeDataToRaw,\n valueShapeOf,\n type MutationJSON,\n type MutationNodeDataJSON,\n type MutationValueJSON,\n} from './MutationSerialization.js';\nimport {assertValid, type ValidationMode} from '../shapes/validation.js';\nimport {decodeValueExpr, type DslJsonValue} from './DslJsonExpression.js';\nimport type {PropertyShapeData} from '../shapes/SHACL.js';\n\nfunction requireShape(shapeId: string): NodeShapeData {\n // Either registry: a project-authored shape arrives as metadata with no class, and\n // requiring a class here made every mutation against one fail at the boundary.\n const shape = getShapeClass(shapeId)?.shape ?? getNodeShape(shapeId);\n if (!shape) {\n throw new Error(\n `Shape '${shapeId}' is not registered. The receiving side must have the shape registered to lower mutation DSL-JSON.`,\n );\n }\n return shape;\n}\n\n// Bound the DSL-JSON decode recursion so a deeply-nested payload from\n// `fromJSON()` cannot exhaust the stack (RangeError DoS). Normal mutation data\n// nests only a handful of levels; 128 is far above any real query.\nconst MAX_DECODE_DEPTH = 128;\nlet _decodeDepth = 0;\n\n/**\n * Decode a DSL-JSON value to the normalized form the canonical-IR builders consume.\n * `currentShape` resolves computed `{@path}`/S-expr; `prop` gives a nested node's shape.\n * Depth-guarded wrapper around {@link decodeValueInner}.\n */\nfunction decodeValue(\n json: MutationValueJSON,\n currentShape: NodeShapeData,\n prop?: PropertyShapeData,\n): PropUpdateValue {\n if (++_decodeDepth > MAX_DECODE_DEPTH) {\n _decodeDepth = 0;\n throw new Error('DSL-JSON value nested too deeply');\n }\n try {\n return decodeValueInner(json, currentShape, prop);\n } finally {\n _decodeDepth--;\n }\n}\n\nfunction decodeValueInner(\n json: MutationValueJSON,\n currentShape: NodeShapeData,\n prop?: PropertyShapeData,\n): PropUpdateValue {\n // S-expr computed value (no IR on the wire)\n if (Array.isArray(json)) {\n const {ir, refs} = decodeValueExpr(json as DslJsonValue, currentShape);\n return new ExpressionNode(ir, refs) as unknown as PropUpdateValue;\n }\n if (json === null) return null as unknown as PropUpdateValue;\n if (typeof json !== 'object') return json as PropUpdateValue; // bare scalar literal\n const o = json as Record<string, unknown>;\n if ('@unset' in o) return undefined;\n if ('@date' in o) return new Date(o['@date'] as string) as unknown as PropUpdateValue;\n if ('@ctx' in o) {\n // Lowering path: a mutation must hit a concrete node, so resolve the\n // context now and throw if it isn't set.\n const id = resolveContextId(o['@ctx'] as string, true)!;\n return {id} as PropUpdateValue;\n }\n if ('@id' in o) return {id: o['@id']} as PropUpdateValue;\n if ('@list' in o) {\n return (o['@list'] as MutationValueJSON[]).map(\n (item) => decodeValue(item, currentShape, prop) as SinglePropertyUpdateValue,\n );\n }\n if ('@add' in o || '@remove' in o) {\n const mod: SetModificationValue = {};\n if (o['@add']) {\n mod.$add = (o['@add'] as MutationValueJSON[]).map((v) =>\n decodeValue(v, currentShape, prop),\n ) as SetModificationValue['$add'];\n }\n if (o['@remove']) mod.$remove = (o['@remove'] as string[]).map((id) => ({id}));\n return mod as PropUpdateValue;\n }\n if ('@path' in o) {\n const {ir, refs} = decodeValueExpr(json as DslJsonValue, currentShape);\n return new ExpressionNode(ir, refs) as unknown as PropUpdateValue;\n }\n // Otherwise: a bare path-keyed nested-node create.\n return decodeNodeData(\n o as MutationNodeDataJSON,\n valueShapeOf(prop) ?? currentShape,\n ) as unknown as PropUpdateValue;\n}\n\n/**\n * Rebuild a normalized node description from path-keyed JSON, resolving labels via the\n * shape. `shape` is the shape inferred from context; `__shape` overrides it.\n */\nexport function decodeNodeData(\n json: MutationNodeDataJSON,\n shape?: NodeShapeData,\n): NodeDescriptionValue {\n const nodeShape =\n typeof json.__shape === 'string' ? requireShape(json.__shape) : shape!;\n const fields: UpdateNodePropertyValue[] = [];\n for (const [key, val] of Object.entries(json)) {\n if (key === '__id' || key === '__shape' || key === '__blank' || val === undefined) continue;\n const prop = getPropertyShape(nodeShape, key);\n if (!prop) {\n throw new Error(\n `Property '${key}' not found on shape '${nodeShape.label || nodeShape.id}'`,\n );\n }\n fields.push({prop, val: decodeValue(val as MutationValueJSON, nodeShape, prop)} as UpdateNodePropertyValue);\n }\n const desc: NodeDescriptionValue = {shape: nodeShape, fields};\n if (typeof json.__id === 'string') desc.__id = json.__id;\n if (json.__blank !== undefined) {\n if (json.__blank !== true) throw new Error(`__blank must be true when given, got ${JSON.stringify(json.__blank)}`);\n desc.__blank = true;\n }\n return desc;\n}\n\nfunction lowerWhere(\n shape: NodeShapeData,\n json: WherePathJSON,\n): {where: IRExpression; wherePatterns: IRGraphPattern[]} {\n const wherePath = deserializeWherePath(shape, json);\n return lowerWhereToIR(toWhere(wherePath));\n}\n\n/**\n * Lower a mutation DSL-JSON envelope to the canonical IR mutation the dataset\n * expects. Mirrors the IR that `lower()` produces from the live builder.\n */\n/**\n * Validate inbound node data against its shape before it becomes IR.\n *\n * The builders validate through `describe()`, which sees raw DSL data. This path\n * never touches a builder, so without this an inbound mutation would be held to\n * a weaker standard than a locally-built one — on the *less* trusted input, at\n * that. `decodeNodeDataToRaw` produces exactly the raw form `describe()` would\n * have validated (it is what `CreateBuilder.fromJSON` feeds through `.set()`),\n * so both paths run the same checks. The second decode is the cost of validating\n * at a boundary; it happens once per inbound mutation.\n */\nfunction assertInboundDataValid(\n json: MutationNodeDataJSON,\n shape: NodeShapeData,\n mode: ValidationMode,\n): void {\n assertValid(shape, decodeNodeDataToRaw(json, shape), {mode});\n}\n\nexport function lowerMutationJSON(\n json: MutationJSON,\n): IRCreateQuery | IRUpdateQuery | IRDeleteQuery {\n assertWireVersion(json.v);\n switch (json.op) {\n case 'create': {\n const shape = requireShape(json.shape);\n assertInboundDataValid(json.data, shape, 'complete');\n return buildCanonicalCreateMutationIR({\n shape,\n description: decodeNodeData(json.data, shape),\n });\n }\n case 'upsert': {\n const shape = requireShape(json.shape);\n assertInboundDataValid(json.data, shape, 'partial');\n const updates = decodeNodeData(json.data, shape);\n const id = isContextRefJSON(json.targetId)\n ? resolveContextId(json.targetId['@ctx'], true)\n : json.targetId;\n if (!id) {\n throw new Error('upsert requires a targetId');\n }\n return buildCanonicalUpsertMutationIR({id, shape, updates});\n }\n case 'update': {\n const shape = requireShape(json.shape);\n assertInboundDataValid(json.data, shape, 'partial');\n const updates = decodeNodeData(json.data, shape);\n if (json.mode === 'for') {\n // A `{@ctx}` target resolves against the current context map; a mutation\n // must hit a concrete subject, so an unresolved context throws.\n const id = isContextRefJSON(json.targetId)\n ? resolveContextId(json.targetId['@ctx'], true)\n : json.targetId;\n if (!id) {\n throw new Error('update mode \"for\" requires a targetId');\n }\n return buildCanonicalUpdateMutationIR({id, shape, updates});\n }\n let where: IRExpression | undefined;\n let wherePatterns: IRGraphPattern[] | undefined;\n if (json.mode === 'where') {\n if (!json.where) throw new Error('update mode \"where\" requires a where clause');\n ({where, wherePatterns} = lowerWhere(shape, json.where));\n }\n return buildCanonicalUpdateWhereMutationIR({shape, updates, where, wherePatterns});\n }\n case 'delete': {\n const shape = requireShape(json.shape);\n if (json.mode === 'ids') {\n return buildCanonicalDeleteMutationIR({\n shape,\n // A `{@ctx}` id resolves against the live map; a delete must hit a\n // concrete node, so an unresolved context throws.\n ids: json.ids.map((id) =>\n isContextRefJSON(id) ? {id: resolveContextId(id['@ctx'], true)!} : {id},\n ),\n });\n }\n if (json.mode === 'all') {\n return buildCanonicalDeleteAllMutationIR({shape});\n }\n const {where, wherePatterns} = lowerWhere(shape, json.where);\n return buildCanonicalDeleteWhereMutationIR({shape, where, wherePatterns});\n }\n }\n}\n"]}
|
package/lib/esm/shapes/List.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"List.js","sourceRoot":"","sources":["../../../src/shapes/List.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,2FAA2F;AAC3F,yFAAyF;AACzF,wFAAwF;AACxF,OAAO,qBAAqB,CAAC;AAC7B,OAAO,EAAC,IAAI,EAAC,MAAM,iBAAiB,CAAC;AACrC,OAAO,EAAC,GAAG,EAAC,MAAM,sBAAsB,CAAC;AAEzC,OAAO,EAAC,IAAI,EAAC,CAAC;AASd;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CACrB,KAAU,EACV,IAAsB;IAEtB,MAAM,IAAI,GAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,IAAI,CAAC;IACxB,MAAM,KAAK,GAAG,CAAC,CAAS,EAAmB,EAAE;QAC3C,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACtB,OAAO,EAAC,EAAE,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,EAAC,CAAC;QAC1B,CAAC;QACD,MAAM,IAAI,GAAoB;YAC5B,KAAK,EAAE,IAAI;YACX,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;YACf,IAAI,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC;SACnB,CAAC;QACF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,CAAC,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC;QAC7B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IACF,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;AAClB,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// Registers List (together with the rest of the SHACL metamodel), so importing this module\n// on its own is enough to make List resolvable. The class lives in List.class.ts because\n// utils/Package.ts needs it by value and cannot import this module's registration path.\nimport '../utils/Package.js';\nimport {List} from './List.class.js';\nimport {rdf} from '../ontologies/rdf.js';\n\nexport {List};\n\n/**\n * Node-data for a single cell / the `rdf:nil` terminal. Accepted by the create pipeline.\n */\nexport type RdfListNodeData =\n | {id: string}\n | {shape: typeof List; first: unknown; rest: RdfListNodeData; __id?: string};\n\n/**\n * Build the nested `List` node-data chain for an ordered `rdf:List`, terminating at `rdf:nil`.\n * Pass `opts.base` to mint deterministic cell ids (`{base}/0`, `{base}/1`, …); otherwise the\n * create pipeline mints ids. The empty list serializes to `rdf:nil`.\n *\n * @example Playlist.create({ tracks: rdfList([t1, t2, t3]) })\n */\nexport function rdfList<T>(\n items: T[],\n opts?: {base?: string},\n): RdfListNodeData {\n const base = opts?.base;\n const build = (i: number): RdfListNodeData => {\n if (i >= items.length) {\n return {id: rdf.nil.id};\n }\n const cell: RdfListNodeData = {\n shape: List,\n first: items[i],\n rest: build(i + 1),\n };\n if (base !== undefined) {\n cell.__id = `${base}/${i}`;\n }\n return cell;\n };\n return build(0);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"List.js","sourceRoot":"","sources":["../../../src/shapes/List.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,2FAA2F;AAC3F,yFAAyF;AACzF,wFAAwF;AACxF,OAAO,qBAAqB,CAAC;AAC7B,OAAO,EAAC,IAAI,EAAC,MAAM,iBAAiB,CAAC;AACrC,OAAO,EAAC,GAAG,EAAC,MAAM,sBAAsB,CAAC;AAEzC,OAAO,EAAC,IAAI,EAAC,CAAC;AASd;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CACrB,KAAU,EACV,IAAsB;IAEtB,MAAM,IAAI,GAAG,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,IAAI,CAAC;IACxB,MAAM,KAAK,GAAG,CAAC,CAAS,EAAmB,EAAE;QAC3C,IAAI,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACtB,OAAO,EAAC,EAAE,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,EAAC,CAAC;QAC1B,CAAC;QACD,MAAM,IAAI,GAAoB;YAC5B,KAAK,EAAE,IAAI;YACX,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;YACf,IAAI,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC;SACnB,CAAC;QACF,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,CAAC,IAAI,GAAG,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC;QAC7B,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IACF,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;AAClB,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// Registers List (together with the rest of the SHACL metamodel), so importing this module\n// on its own is enough to make List resolvable. The class lives in List.class.ts because\n// utils/Package.ts needs it by value and cannot import this module's registration path.\nimport '../utils/Package.js';\nimport {List} from './List.class.js';\nimport {rdf} from '../ontologies/rdf.js';\n\nexport {List};\n\n/**\n * Node-data for a single cell / the `rdf:nil` terminal. Accepted by the create pipeline.\n */\nexport type RdfListNodeData =\n | {id: string}\n | {shape: typeof List; first: unknown; rest: RdfListNodeData; __id?: string; __blank?: true};\n\n/**\n * Build the nested `List` node-data chain for an ordered `rdf:List`, terminating at `rdf:nil`.\n * Pass `opts.base` to mint deterministic cell ids (`{base}/0`, `{base}/1`, …); otherwise the\n * create pipeline mints ids. The empty list serializes to `rdf:nil`.\n *\n * @example Playlist.create({ tracks: rdfList([t1, t2, t3]) })\n */\nexport function rdfList<T>(\n items: T[],\n opts?: {base?: string},\n): RdfListNodeData {\n const base = opts?.base;\n const build = (i: number): RdfListNodeData => {\n if (i >= items.length) {\n return {id: rdf.nil.id};\n }\n const cell: RdfListNodeData = {\n shape: List,\n first: items[i],\n rest: build(i + 1),\n };\n if (base !== undefined) {\n cell.__id = `${base}/${i}`;\n }\n return cell;\n };\n return build(0);\n}\n"]}
|
|
@@ -5,8 +5,11 @@ import { List } from './List.js';
|
|
|
5
5
|
* inverse (`sh:inversePath`), alternative (`sh:alternativePath` → an `rdf:List`), and the
|
|
6
6
|
* cardinality forms (`sh:zeroOrMorePath`/`sh:oneOrMorePath`/`sh:zeroOrOnePath`).
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* Shape sync writes these as untyped blank nodes (`__blank`, see `serializePathToNodeData`): the
|
|
9
|
+
* only form SHACL §2.3.1 defines, a blank node that is the subject of exactly one triple. Each
|
|
10
|
+
* operator edge is `contains`, so the blank-node block of the containment cascade removes the
|
|
11
|
+
* structure on delete/replace. The class stays `dependent` with its `core:PathNode` type for the
|
|
12
|
+
* IRI-named, typed nodes earlier versions wrote: the typed cascade removes those on the next sync.
|
|
10
13
|
* Operands are polymorphic (a predicate IRI, a nested `PathNode`, or a `List`), so no fixed
|
|
11
14
|
* `valueShape` is declared except for `alternativePath`, which is always an `rdf:List`.
|
|
12
15
|
*/
|
|
@@ -20,8 +20,11 @@ import { List } from './List.js';
|
|
|
20
20
|
* inverse (`sh:inversePath`), alternative (`sh:alternativePath` → an `rdf:List`), and the
|
|
21
21
|
* cardinality forms (`sh:zeroOrMorePath`/`sh:oneOrMorePath`/`sh:zeroOrOnePath`).
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
23
|
+
* Shape sync writes these as untyped blank nodes (`__blank`, see `serializePathToNodeData`): the
|
|
24
|
+
* only form SHACL §2.3.1 defines, a blank node that is the subject of exactly one triple. Each
|
|
25
|
+
* operator edge is `contains`, so the blank-node block of the containment cascade removes the
|
|
26
|
+
* structure on delete/replace. The class stays `dependent` with its `core:PathNode` type for the
|
|
27
|
+
* IRI-named, typed nodes earlier versions wrote: the typed cascade removes those on the next sync.
|
|
25
28
|
* Operands are polymorphic (a predicate IRI, a nested `PathNode`, or a `List`), so no fixed
|
|
26
29
|
* `valueShape` is declared except for `alternativePath`, which is always an `rdf:List`.
|
|
27
30
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PathNode.js","sourceRoot":"","sources":["../../../src/shapes/PathNode.ts"],"names":[],"mappings":";;;;;;AAAA;;;;GAIG;AACH,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAC,KAAK,EAAC,MAAM,YAAY,CAAC;AACjC,OAAO,EAAC,cAAc,EAAC,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAC,KAAK,EAAC,MAAM,wBAAwB,CAAC;AAC7C,OAAO,EAAC,YAAY,EAAC,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AAE/B
|
|
1
|
+
{"version":3,"file":"PathNode.js","sourceRoot":"","sources":["../../../src/shapes/PathNode.ts"],"names":[],"mappings":";;;;;;AAAA;;;;GAIG;AACH,OAAO,EAAC,WAAW,EAAC,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAC,KAAK,EAAC,MAAM,YAAY,CAAC;AACjC,OAAO,EAAC,cAAc,EAAC,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAC,KAAK,EAAC,MAAM,wBAAwB,CAAC;AAC7C,OAAO,EAAC,YAAY,EAAC,MAAM,8BAA8B,CAAC;AAC1D,OAAO,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AAE/B;;;;;;;;;;;;GAYG;AACH,4FAA4F;AAC5F,2FAA2F;AAC3F,mBAAmB;AAEZ,IAAM,QAAQ,GAAd,MAAM,QAAS,SAAQ,KAAK;IAIjC,IAAI,WAAW;QACb,OAAO,IAAI,CAAC;IACd,CAAC;IAGD,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC;IACd,CAAC;IAGD,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;IAGD,IAAI,aAAa;QACf,OAAO,IAAI,CAAC;IACd,CAAC;IAGD,IAAI,aAAa;QACf,OAAO,IAAI,CAAC;IACd,CAAC;;AAzBM,oBAAW,GAAG,YAAY,CAAC,QAAQ,AAAxB,CAAyB;AAG3C;IADC,cAAc,CAAC,EAAC,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC;2CAGtE;AAGD;IADC,cAAc,CAAC,EAAC,IAAI,EAAE,KAAK,CAAC,eAAe,EAAE,QAAQ,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC;+CAGvF;AAGD;IADC,cAAc,CAAC,EAAC,IAAI,EAAE,KAAK,CAAC,cAAc,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC;8CAGzE;AAGD;IADC,cAAc,CAAC,EAAC,IAAI,EAAE,KAAK,CAAC,aAAa,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC;6CAGxE;AAGD;IADC,cAAc,CAAC,EAAC,IAAI,EAAE,KAAK,CAAC,aAAa,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAC,CAAC;6CAGxE;AA1BU,QAAQ;IADpB,WAAW,CAAC,EAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAC,CAAC;GACpC,QAAQ,CA2BpB","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 {linkedShape} from '../package.js';\nimport {Shape} from './Shape.js';\nimport {objectProperty} from './SHACL.js';\nimport {shacl} from '../ontologies/shacl.js';\nimport {coreOntology} from '../ontologies/linked-core.js';\nimport {List} from './List.js';\n\n/**\n * Operator node for SHACL property paths that are NOT a simple predicate or a sequence:\n * inverse (`sh:inversePath`), alternative (`sh:alternativePath` → an `rdf:List`), and the\n * cardinality forms (`sh:zeroOrMorePath`/`sh:oneOrMorePath`/`sh:zeroOrOnePath`).\n *\n * Shape sync writes these as untyped blank nodes (`__blank`, see `serializePathToNodeData`): the\n * only form SHACL §2.3.1 defines, a blank node that is the subject of exactly one triple. Each\n * operator edge is `contains`, so the blank-node block of the containment cascade removes the\n * structure on delete/replace. The class stays `dependent` with its `core:PathNode` type for the\n * IRI-named, typed nodes earlier versions wrote: the typed cascade removes those on the next sync.\n * Operands are polymorphic (a predicate IRI, a nested `PathNode`, or a `List`), so no fixed\n * `valueShape` is declared except for `alternativePath`, which is always an `rdf:List`.\n */\n// Named explicitly: the class shares its name with the ontology term coreOntology.PathNode,\n// and a bundler resolving that collision renames the class (PathNode2), which would change\n// the shape's IRI.\n@linkedShape({name: 'PathNode', dependent: true})\nexport class PathNode extends Shape {\n static targetClass = coreOntology.PathNode;\n\n @objectProperty({path: shacl.inversePath, maxCount: 1, contains: true})\n get inversePath(): unknown {\n return null;\n }\n\n @objectProperty({path: shacl.alternativePath, maxCount: 1, shape: List, contains: true})\n get alternativePath(): List {\n return null;\n }\n\n @objectProperty({path: shacl.zeroOrMorePath, maxCount: 1, contains: true})\n get zeroOrMorePath(): unknown {\n return null;\n }\n\n @objectProperty({path: shacl.oneOrMorePath, maxCount: 1, contains: true})\n get oneOrMorePath(): unknown {\n return null;\n }\n\n @objectProperty({path: shacl.zeroOrOnePath, maxCount: 1, contains: true})\n get zeroOrOnePath(): unknown {\n return null;\n }\n}\n"]}
|
|
@@ -8,17 +8,29 @@ import { PathNode } from './PathNode.js';
|
|
|
8
8
|
*/
|
|
9
9
|
export type PathNodeData = NodeReferenceValue | {
|
|
10
10
|
shape: typeof List | typeof PathNode;
|
|
11
|
-
|
|
11
|
+
__blank: true;
|
|
12
12
|
[k: string]: unknown;
|
|
13
13
|
};
|
|
14
14
|
/**
|
|
15
15
|
* Translate a `PathExpr` into create-pipeline node-data for `sh:path`.
|
|
16
16
|
*
|
|
17
17
|
* - simple `PathRef` → `{id}` (a shared predicate IRI; NOT owned)
|
|
18
|
-
* - sequence → an `rdf:List` of segments
|
|
18
|
+
* - sequence → an `rdf:List` of segments
|
|
19
19
|
* - inverse / alternative / cardinality → a `PathNode` operator node (alt's operand is an `rdf:List`)
|
|
20
20
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
21
|
+
* Every operator node and list cell is written as an untyped blank node (`__blank`), the only
|
|
22
|
+
* form SHACL §2.3.1 defines: an IRI in `sh:path` is a predicate path, and an inverse,
|
|
23
|
+
* alternative or cardinality path is a blank node that is the subject of exactly one triple.
|
|
24
|
+
*
|
|
25
|
+
* ```turtle
|
|
26
|
+
* <ps> sh:path [ sh:inversePath schema:image ] .
|
|
27
|
+
* <ps> sh:path ( schema:knows [ sh:zeroOrMorePath schema:parent ] ) .
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* The structure is owned through `contains` edges, so a re-sync's delete removes it (the
|
|
31
|
+
* blank-node block of the containment cascade) before the create writes it again.
|
|
32
|
+
*
|
|
33
|
+
* @param _base ignored. It seeded the IRIs of the nodes this used to mint (`{base}/inv`, …);
|
|
34
|
+
* blank nodes have none. Kept so existing two-argument calls still compile.
|
|
23
35
|
*/
|
|
24
|
-
export declare function serializePathToNodeData(path: PathExpr,
|
|
36
|
+
export declare function serializePathToNodeData(path: PathExpr, _base?: string): PathNodeData;
|
|
@@ -1,18 +1,36 @@
|
|
|
1
1
|
import { isPathRef } from '../paths/PropertyPathExpr.js';
|
|
2
|
-
import {
|
|
2
|
+
import { List } from './List.js';
|
|
3
|
+
import { rdf } from '../ontologies/rdf.js';
|
|
3
4
|
import { PathNode } from './PathNode.js';
|
|
4
5
|
/**
|
|
5
6
|
* Translate a `PathExpr` into create-pipeline node-data for `sh:path`.
|
|
6
7
|
*
|
|
7
8
|
* - simple `PathRef` → `{id}` (a shared predicate IRI; NOT owned)
|
|
8
|
-
* - sequence → an `rdf:List` of segments
|
|
9
|
+
* - sequence → an `rdf:List` of segments
|
|
9
10
|
* - inverse / alternative / cardinality → a `PathNode` operator node (alt's operand is an `rdf:List`)
|
|
10
11
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
12
|
+
* Every operator node and list cell is written as an untyped blank node (`__blank`), the only
|
|
13
|
+
* form SHACL §2.3.1 defines: an IRI in `sh:path` is a predicate path, and an inverse,
|
|
14
|
+
* alternative or cardinality path is a blank node that is the subject of exactly one triple.
|
|
15
|
+
*
|
|
16
|
+
* ```turtle
|
|
17
|
+
* <ps> sh:path [ sh:inversePath schema:image ] .
|
|
18
|
+
* <ps> sh:path ( schema:knows [ sh:zeroOrMorePath schema:parent ] ) .
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* The structure is owned through `contains` edges, so a re-sync's delete removes it (the
|
|
22
|
+
* blank-node block of the containment cascade) before the create writes it again.
|
|
23
|
+
*
|
|
24
|
+
* @param _base ignored. It seeded the IRIs of the nodes this used to mint (`{base}/inv`, …);
|
|
25
|
+
* blank nodes have none. Kept so existing two-argument calls still compile.
|
|
13
26
|
*/
|
|
14
|
-
export function serializePathToNodeData(path,
|
|
15
|
-
const
|
|
27
|
+
export function serializePathToNodeData(path, _base) {
|
|
28
|
+
const operator = (key, operand) => ({
|
|
29
|
+
shape: PathNode,
|
|
30
|
+
__blank: true,
|
|
31
|
+
[key]: operand,
|
|
32
|
+
});
|
|
33
|
+
const serialize = (e) => {
|
|
16
34
|
if (isPathRef(e)) {
|
|
17
35
|
return { id: typeof e === 'string' ? e : e.id };
|
|
18
36
|
}
|
|
@@ -25,12 +43,11 @@ export function serializePathToNodeData(path, base) {
|
|
|
25
43
|
throw new Error('Cannot serialize an empty sequence path to SHACL sh:path.');
|
|
26
44
|
}
|
|
27
45
|
if (e.seq.length === 1)
|
|
28
|
-
return serialize(e.seq[0]
|
|
29
|
-
return
|
|
30
|
-
}
|
|
31
|
-
if ('inv' in e) {
|
|
32
|
-
return { shape: PathNode, __id: `${b}/inv`, inversePath: serialize(e.inv, `${b}/inv`) };
|
|
46
|
+
return serialize(e.seq[0]);
|
|
47
|
+
return blankList(e.seq.map(serialize));
|
|
33
48
|
}
|
|
49
|
+
if ('inv' in e)
|
|
50
|
+
return operator('inversePath', serialize(e.inv));
|
|
34
51
|
if ('alt' in e) {
|
|
35
52
|
// SHACL §2.3.2: sh:alternativePath is a list with **at least two** members.
|
|
36
53
|
// Collapse a singleton alternative to its bare member, reject an empty one.
|
|
@@ -38,27 +55,24 @@ export function serializePathToNodeData(path, base) {
|
|
|
38
55
|
throw new Error('Cannot serialize an empty alternative path to SHACL sh:path.');
|
|
39
56
|
}
|
|
40
57
|
if (e.alt.length === 1)
|
|
41
|
-
return serialize(e.alt[0]
|
|
42
|
-
return
|
|
43
|
-
shape: PathNode,
|
|
44
|
-
__id: `${b}/alt`,
|
|
45
|
-
alternativePath: rdfList(e.alt.map((a, i) => serialize(a, `${b}/alt/${i}`)), { base: `${b}/alt` }),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
if ('zeroOrMore' in e) {
|
|
49
|
-
return { shape: PathNode, __id: `${b}/zom`, zeroOrMorePath: serialize(e.zeroOrMore, `${b}/zom`) };
|
|
50
|
-
}
|
|
51
|
-
if ('oneOrMore' in e) {
|
|
52
|
-
return { shape: PathNode, __id: `${b}/oom`, oneOrMorePath: serialize(e.oneOrMore, `${b}/oom`) };
|
|
53
|
-
}
|
|
54
|
-
if ('zeroOrOne' in e) {
|
|
55
|
-
return { shape: PathNode, __id: `${b}/zoo`, zeroOrOnePath: serialize(e.zeroOrOne, `${b}/zoo`) };
|
|
58
|
+
return serialize(e.alt[0]);
|
|
59
|
+
return operator('alternativePath', blankList(e.alt.map(serialize)));
|
|
56
60
|
}
|
|
61
|
+
if ('zeroOrMore' in e)
|
|
62
|
+
return operator('zeroOrMorePath', serialize(e.zeroOrMore));
|
|
63
|
+
if ('oneOrMore' in e)
|
|
64
|
+
return operator('oneOrMorePath', serialize(e.oneOrMore));
|
|
65
|
+
if ('zeroOrOne' in e)
|
|
66
|
+
return operator('zeroOrOnePath', serialize(e.zeroOrOne));
|
|
57
67
|
if ('negatedPropertySet' in e) {
|
|
58
68
|
throw new Error('negatedPropertySet cannot be serialized to SHACL sh:path — no SHACL representation.');
|
|
59
69
|
}
|
|
60
70
|
throw new Error(`Unknown PathExpr shape: ${JSON.stringify(e)}`);
|
|
61
71
|
};
|
|
62
|
-
return serialize(path
|
|
72
|
+
return serialize(path);
|
|
73
|
+
}
|
|
74
|
+
/** An `rdf:List` of blank cells (`rdf:first`/`rdf:rest` only), terminated by `rdf:nil`. */
|
|
75
|
+
function blankList(items) {
|
|
76
|
+
return items.reduceRight((rest, first) => ({ shape: List, __blank: true, first, rest }), { id: rdf.nil.id });
|
|
63
77
|
}
|
|
64
78
|
//# sourceMappingURL=serializePathToNodeData.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serializePathToNodeData.js","sourceRoot":"","sources":["../../../src/shapes/serializePathToNodeData.ts"],"names":[],"mappings":"AAMA,OAAO,EAAC,SAAS,EAAC,MAAM,8BAA8B,CAAC;AAEvD,OAAO,
|
|
1
|
+
{"version":3,"file":"serializePathToNodeData.js","sourceRoot":"","sources":["../../../src/shapes/serializePathToNodeData.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 {PathExpr} from '../paths/PropertyPathExpr.js';\nimport {isPathRef} from '../paths/PropertyPathExpr.js';\nimport type {NodeReferenceValue} from '../utils/NodeReference.js';\nimport {List, type RdfListNodeData} from './List.js';\nimport {rdf} from '../ontologies/rdf.js';\nimport {PathNode} from './PathNode.js';\n\n/**\n * Node-data the create pipeline accepts for a `sh:path` value: an IRI reference (simple path),\n * an `rdf:List` (sequence / alternative members), or a `PathNode` operator node.\n */\nexport type PathNodeData =\n | NodeReferenceValue\n | {shape: typeof List | typeof PathNode; __blank: true; [k: string]: unknown};\n\n/**\n * Translate a `PathExpr` into create-pipeline node-data for `sh:path`.\n *\n * - simple `PathRef` → `{id}` (a shared predicate IRI; NOT owned)\n * - sequence → an `rdf:List` of segments\n * - inverse / alternative / cardinality → a `PathNode` operator node (alt's operand is an `rdf:List`)\n *\n * Every operator node and list cell is written as an untyped blank node (`__blank`), the only\n * form SHACL §2.3.1 defines: an IRI in `sh:path` is a predicate path, and an inverse,\n * alternative or cardinality path is a blank node that is the subject of exactly one triple.\n *\n * ```turtle\n * <ps> sh:path [ sh:inversePath schema:image ] .\n * <ps> sh:path ( schema:knows [ sh:zeroOrMorePath schema:parent ] ) .\n * ```\n *\n * The structure is owned through `contains` edges, so a re-sync's delete removes it (the\n * blank-node block of the containment cascade) before the create writes it again.\n *\n * @param _base ignored. It seeded the IRIs of the nodes this used to mint (`{base}/inv`, …);\n * blank nodes have none. Kept so existing two-argument calls still compile.\n */\nexport function serializePathToNodeData(path: PathExpr, _base?: string): PathNodeData {\n const operator = (key: string, operand: unknown): PathNodeData => ({\n shape: PathNode,\n __blank: true,\n [key]: operand,\n });\n const serialize = (e: PathExpr): PathNodeData => {\n if (isPathRef(e)) {\n return {id: typeof e === 'string' ? e : e.id};\n }\n if ('seq' in e) {\n // SHACL §2.3.1: a sequence path is a list with **at least two** members.\n // A 0/1-member sequence is not a list — collapse a singleton to its bare\n // member, reject an empty one. (normalizePropertyPath already collapses\n // these upstream; this keeps the serializer independently spec-correct.)\n if (e.seq.length === 0) {\n throw new Error('Cannot serialize an empty sequence path to SHACL sh:path.');\n }\n if (e.seq.length === 1) return serialize(e.seq[0]);\n return blankList(e.seq.map(serialize)) as PathNodeData;\n }\n if ('inv' in e) return operator('inversePath', serialize(e.inv));\n if ('alt' in e) {\n // SHACL §2.3.2: sh:alternativePath is a list with **at least two** members.\n // Collapse a singleton alternative to its bare member, reject an empty one.\n if (e.alt.length === 0) {\n throw new Error('Cannot serialize an empty alternative path to SHACL sh:path.');\n }\n if (e.alt.length === 1) return serialize(e.alt[0]);\n return operator('alternativePath', blankList(e.alt.map(serialize)));\n }\n if ('zeroOrMore' in e) return operator('zeroOrMorePath', serialize(e.zeroOrMore));\n if ('oneOrMore' in e) return operator('oneOrMorePath', serialize(e.oneOrMore));\n if ('zeroOrOne' in e) return operator('zeroOrOnePath', serialize(e.zeroOrOne));\n if ('negatedPropertySet' in e) {\n throw new Error(\n 'negatedPropertySet cannot be serialized to SHACL sh:path — no SHACL representation.',\n );\n }\n throw new Error(`Unknown PathExpr shape: ${JSON.stringify(e)}`);\n };\n return serialize(path);\n}\n\n/** An `rdf:List` of blank cells (`rdf:first`/`rdf:rest` only), terminated by `rdf:nil`. */\nfunction blankList(items: PathNodeData[]): RdfListNodeData {\n return items.reduceRight<RdfListNodeData>(\n (rest, first) => ({shape: List, __blank: true, first, rest}),\n {id: rdf.nil.id},\n );\n}\n"]}
|
|
@@ -79,7 +79,7 @@ export function buildSingleOwnerConstraints(nodeShape, shapeIri) {
|
|
|
79
79
|
__id: psIri,
|
|
80
80
|
label: 'owners',
|
|
81
81
|
maxCount: 1,
|
|
82
|
-
path: serializePathToNodeData({ inv: { id: predicate } }
|
|
82
|
+
path: serializePathToNodeData({ inv: { id: predicate } }),
|
|
83
83
|
},
|
|
84
84
|
],
|
|
85
85
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"singleOwnerConstraint.js","sourceRoot":"","sources":["../../../src/shapes/singleOwnerConstraint.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAC,iBAAiB,EAA6C,MAAM,oBAAoB,CAAC;AACjG,OAAO,EAAC,eAAe,EAAC,MAAM,mCAAmC,CAAC;AAClE,OAAO,EAAC,uBAAuB,EAAC,MAAM,8BAA8B,CAAC;AAErE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,4FAA4F;AAC5F,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAE3C;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,gBAAwB;IAC1D,OAAO,GAAG,gBAAgB,GAAG,mBAAmB,EAAE,CAAC;AACrD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAwB;IAC5D,OAAO,iBAAiB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,MAAM,CAC/C,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,UAAU,IAAI,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,IAAI,CAAC,CACrE,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B,CACzC,SAAwB,EACxB,QAAgB;IAEhB,OAAO,qBAAqB,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE;QACrD,2FAA2F;QAC3F,MAAM,SAAS,GAAG,eAAe,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,SAAS;YAAE,OAAO,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,mBAAmB,CAAC,GAAG,QAAQ,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC;QAC3D,MAAM,KAAK,GAAG,GAAG,GAAG,SAAS,CAAC;QAC9B,OAAO,CAAC;gBACN,GAAG;gBACH,IAAI,EAAE;oBACJ,SAAS,EAAE,IAAI;oBACf,eAAe,EAAE,CAAC,EAAC,EAAE,EAAE,SAAS,EAAC,CAAC;oBAClC,UAAU,EAAE;wBACV;4BACE,IAAI,EAAE,KAAK;4BACX,KAAK,EAAE,QAAQ;4BACf,QAAQ,EAAE,CAAC;4BACX,IAAI,EAAE,uBAAuB,CAAC,EAAC,GAAG,EAAE,EAAC,EAAE,EAAE,SAAS,EAAC,EAAC,
|
|
1
|
+
{"version":3,"file":"singleOwnerConstraint.js","sourceRoot":"","sources":["../../../src/shapes/singleOwnerConstraint.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAC,iBAAiB,EAA6C,MAAM,oBAAoB,CAAC;AACjG,OAAO,EAAC,eAAe,EAAC,MAAM,mCAAmC,CAAC;AAClE,OAAO,EAAC,uBAAuB,EAAC,MAAM,8BAA8B,CAAC;AAErE;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,4FAA4F;AAC5F,MAAM,mBAAmB,GAAG,cAAc,CAAC;AAE3C;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,gBAAwB;IAC1D,OAAO,GAAG,gBAAgB,GAAG,mBAAmB,EAAE,CAAC;AACrD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAwB;IAC5D,OAAO,iBAAiB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,MAAM,CAC/C,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,IAAI,CAAC,CAAC,EAAE,CAAC,UAAU,IAAI,CAAC,CAAC,eAAe,CAAC,EAAE,CAAC,IAAI,CAAC,CACrE,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B,CACzC,SAAwB,EACxB,QAAgB;IAEhB,OAAO,qBAAqB,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE;QACrD,2FAA2F;QAC3F,MAAM,SAAS,GAAG,eAAe,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QAC3C,IAAI,CAAC,SAAS;YAAE,OAAO,EAAE,CAAC;QAC1B,MAAM,GAAG,GAAG,mBAAmB,CAAC,GAAG,QAAQ,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC;QAC3D,MAAM,KAAK,GAAG,GAAG,GAAG,SAAS,CAAC;QAC9B,OAAO,CAAC;gBACN,GAAG;gBACH,IAAI,EAAE;oBACJ,SAAS,EAAE,IAAI;oBACf,eAAe,EAAE,CAAC,EAAC,EAAE,EAAE,SAAS,EAAC,CAAC;oBAClC,UAAU,EAAE;wBACV;4BACE,IAAI,EAAE,KAAK;4BACX,KAAK,EAAE,QAAQ;4BACf,QAAQ,EAAE,CAAC;4BACX,IAAI,EAAE,uBAAuB,CAAC,EAAC,GAAG,EAAE,EAAC,EAAE,EAAE,SAAS,EAAC,EAAC,CAAC;yBACtD;qBACF;iBACF;aACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,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 {getPropertyShapes, type NodeShapeData, type PropertyShapeData} from './nodeShapeData.js';\nimport {getSimplePathId} from '../paths/normalizePropertyPath.js';\nimport {serializePathToNodeData} from './serializePathToNodeData.js';\n\n/**\n * The single-owner SHACL constraints generated from `contains` properties.\n *\n * A `contains` property says the subject owns the value node. For every such property, shape\n * sync writes one generated NodeShape stating the matching store-side rule: every object of the\n * property's predicate has at most one subject pointing at it through that predicate.\n *\n * ```turtle\n * <…/Thing/image.singleOwner> a sh:NodeShape ;\n * core:generated true ;\n * sh:targetObjectsOf schema:image ;\n * sh:property <…/Thing/image.singleOwner/owners> .\n * <…/Thing/image.singleOwner/owners> a sh:PropertyShape ;\n * sh:maxCount 1 ;\n * sh:path [ sh:inversePath schema:image ] .\n * ```\n *\n * One constraint per accessor, written only by the class that declares it: subclasses inherit\n * the property but add no constraint, since the rule targets the predicate's objects whoever the\n * subject is. The declaring shape owns its constraints through `core:generatedShape`\n * (`NodeShape.generatedShapes`, `contains`): shape sync writes them nested in the shape's\n * create and deletes them with the shape.\n *\n * Non-owning references through other predicates are not counted, and neither is ownership\n * through two different `contains` predicates (a documented limitation). The shapes are built\n * only at sync time and are never registered in the in-memory shape registry.\n */\n\n/** Suffix that turns a property shape's IRI into its generated single-owner shape's IRI. */\nconst SINGLE_OWNER_SUFFIX = '.singleOwner';\n\n/**\n * The deterministic IRI of the generated single-owner NodeShape of one property shape:\n * `${propertyShapeIri}.singleOwner`, e.g. `…/shape/my-pkg/Thing/image.singleOwner`.\n */\nexport function singleOwnerShapeIri(propertyShapeIri: string): string {\n return `${propertyShapeIri}${SINGLE_OWNER_SUFFIX}`;\n}\n\n/**\n * The shape's OWN `contains` properties that get a single-owner constraint: those that declare\n * a value shape and have a simple path, in declaration order.\n *\n * Inherited properties are not included; their declaring shape writes their constraint.\n * Complex paths are skipped, as the containment cascade skips them. A `contains` property\n * without a value shape is a polymorphic slot (like `sh:path`) whose value may be a shared IRI,\n * so the replace, the subject delete and the class check do not treat it as owning that value,\n * and neither does the single-owner rule.\n */\nexport function singleOwnerProperties(nodeShape: NodeShapeData): PropertyShapeData[] {\n return getPropertyShapes(nodeShape, false).filter(\n (ps) => ps.contains && !!ps.valueShape && !!getSimplePathId(ps.path),\n );\n}\n\n/**\n * Create-pipeline data for the generated single-owner NodeShapes of `nodeShape`, one per\n * {@link singleOwnerProperties} entry. Each is stored at\n * {@link singleOwnerShapeIri}`(${shapeIri}/${label})`: the property shape's IRI exactly as shape\n * sync writes it (`buildPropertyShapeData`).\n */\nexport function buildSingleOwnerConstraints(\n nodeShape: NodeShapeData,\n shapeIri: string,\n): Array<{iri: string; data: Record<string, unknown>}> {\n return singleOwnerProperties(nodeShape).flatMap((ps) => {\n // Always set: `singleOwnerProperties` keeps simple paths only. The guard narrows the type.\n const predicate = getSimplePathId(ps.path);\n if (!predicate) return [];\n const iri = singleOwnerShapeIri(`${shapeIri}/${ps.label}`);\n const psIri = `${iri}/owners`;\n return [{\n iri,\n data: {\n generated: true,\n targetObjectsOf: [{id: predicate}],\n properties: [\n {\n __id: psIri,\n label: 'owners',\n maxCount: 1,\n path: serializePathToNodeData({inv: {id: predicate}}),\n },\n ],\n },\n }];\n });\n}\n"]}
|
|
@@ -33,7 +33,7 @@ export function buildPropertyShapeData(ps, shapeIri) {
|
|
|
33
33
|
const psIri = `${shapeIri}/${ps.label}`;
|
|
34
34
|
const d = {
|
|
35
35
|
__id: psIri,
|
|
36
|
-
path: serializePathToNodeData(ps.path
|
|
36
|
+
path: serializePathToNodeData(ps.path),
|
|
37
37
|
};
|
|
38
38
|
// `rdfs:label` — the DSL label, i.e. the accessor name the property is reached
|
|
39
39
|
// by in code (`person.memberships`). It is NOT `sh:name`, written below, which
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncShapes.js","sourceRoot":"","sources":["../../../src/shapes/syncShapes.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 {getAllShapeClasses, getShapeClass} from '../utils/ShapeClass.js';\nimport {NodeShape} from './SHACL.js';\nimport {\n getUniquePropertyShapes,\n type NodeShapeData,\n type PropertyShapeData,\n} from './nodeShapeData.js';\nimport {Shape} from './Shape.js';\nimport {DeleteBuilder} from '../queries/DeleteBuilder.js';\nimport type {IDataset} from '../interfaces/IDataset.js';\nimport {rdfList} from './List.js';\nimport {serializePathToNodeData} from './serializePathToNodeData.js';\nimport {buildSingleOwnerConstraints} from './singleOwnerConstraint.js';\n\n/**\n * The npm scope of framework shapes (NodeShape/PropertyShape/List/PathNode/Shape) that\n * describe the meta-model itself and must never be synced as user data.\n */\nconst FRAMEWORK_PACKAGE = '@_linked/core';\n\n/**\n * Build the create-pipeline data object for a single PropertyShape (flattened under shapeIri).\n *\n * @internal Exported for tests only — not part of the public API.\n */\nexport function buildPropertyShapeData(ps: PropertyShapeData, shapeIri: string): Record<string, unknown> {\n const psIri = `${shapeIri}/${ps.label}`;\n const d: Record<string, unknown> = {\n __id: psIri,\n path: serializePathToNodeData(ps.path, psIri),\n };\n // `rdfs:label` — the DSL label, i.e. the accessor name the property is reached\n // by in code (`person.memberships`). It is NOT `sh:name`, written below, which\n // is a human-readable display string.\n //\n // Without this the label is only ever implied by `psIri`, and a reader has to\n // re-derive one from the path's local part. That works right up until the two\n // differ — `@_linked/org` declares `memberships` on the path `org:hasMembership`\n // — at which point the catalog reports `hasMembership`, the query proxy indexes\n // the shape with it, and the class (which only knows `memberships`) throws.\n // Readers already prefer a stored label and fall back to the path, so writing it\n // is the whole fix; old catalogs keep working via that fallback.\n if (ps.label) d.label = ps.label;\n if (ps.nodeKind) d.nodeKind = ps.nodeKind;\n if (ps.datatype) d.datatype = ps.datatype;\n if (typeof ps.minCount === 'number') d.minCount = ps.minCount;\n if (typeof ps.maxCount === 'number') d.maxCount = ps.maxCount;\n if (ps.name) d.name = ps.name;\n if (ps.description) d.description = ps.description;\n if (typeof ps.order === 'number') d.order = ps.order;\n if (ps.group) d.group = ps.group;\n if (typeof ps.displayRank === 'number') d.displayRank = ps.displayRank;\n if (ps.displayHidden !== undefined) d.displayHidden = ps.displayHidden;\n if (ps.class) d.class = ps.class;\n if (ps.in) d.in = rdfList(ps.in, {base: `${psIri}/in`});\n if (ps.equalsConstraint) d.equalsConstraint = ps.equalsConstraint;\n if (ps.disjoint) d.disjoint = ps.disjoint;\n if (ps.lessThan) d.lessThan = ps.lessThan;\n if (ps.lessThanOrEquals) d.lessThanOrEquals = ps.lessThanOrEquals;\n if (ps.minInclusive !== undefined) d.minInclusive = ps.minInclusive;\n if (ps.maxInclusive !== undefined) d.maxInclusive = ps.maxInclusive;\n if (ps.minExclusive !== undefined) d.minExclusive = ps.minExclusive;\n if (ps.maxExclusive !== undefined) d.maxExclusive = ps.maxExclusive;\n if (typeof ps.minLength === 'number') d.minLength = ps.minLength;\n if (typeof ps.maxLength === 'number') d.maxLength = ps.maxLength;\n // sh:pattern is serialized as its regex source string.\n if (ps.pattern) d.pattern = ps.pattern.source;\n if (ps.hasValueConstraint !== undefined) d.hasValue = ps.hasValueConstraint;\n if (ps.valueShape) d.valueShape = ps.valueShape;\n if (ps.contains) d.contains = true;\n return d;\n}\n\n/** Build the create-pipeline data object for a NodeShape and its (flattened) property shapes. */\nfunction buildNodeShapeData(nodeShape: NodeShapeData, shapeIri: string): Record<string, unknown> {\n const d: Record<string, unknown> = {};\n if (nodeShape.targetClass) d.targetClass = nodeShape.targetClass;\n if (nodeShape.description) d.description = nodeShape.description;\n if (nodeShape.extends) d.extends = nodeShape.extends;\n if (nodeShape.dependent) d.dependent = true;\n if (nodeShape.closed !== undefined) d.closed = nodeShape.closed;\n if (nodeShape.ignoredProperties && nodeShape.ignoredProperties.length) {\n d.ignoredProperties = nodeShape.ignoredProperties;\n }\n // Flatten: emit own + inherited property shapes (deduped by label) under this shape.\n d.properties = getUniquePropertyShapes(nodeShape).map((ps) =>\n buildPropertyShapeData(ps, shapeIri),\n );\n // The single-owner constraints of the shape's own `contains` properties, owned through\n // `core:generatedShape` and written nested in the same create with their fixed IRIs.\n const generated = buildSingleOwnerConstraints(nodeShape, shapeIri);\n if (generated.length > 0) {\n d.generatedShapes = generated.map(({iri, data}) => ({__id: iri, ...data}));\n }\n return d;\n}\n\n/**\n * The per-shape `delete → create` thunk shared by `syncShapes()` and `syncShape()`: exactly two\n * requests. The delete cascade-cleans the shape's old property-shape / list / path subtrees; the\n * create rebuilds them.\n *\n * The single-owner constraints generated from the shape's own `contains` properties\n * (`${iri}/${label}.singleOwner`, see `singleOwnerConstraint.ts`) are owned by the shape through\n * `core:generatedShape` (a `contains` property with value shape NodeShape). The delete removes\n * each one's own triples (the immediate owned-child block) and their property shapes and path\n * nodes (the dependent cascade); the create writes the current ones nested, in the same request\n * as the shape. A removed or renamed accessor therefore loses its constraint on the next sync,\n * and a NodeShape the shape does not own is never touched.\n *\n * `ds` (optional) targets an explicit dataset — both requests run against it instead of the\n * global router. Omitted → today's global behavior.\n */\nfunction buildSyncThunk(nodeShape: NodeShapeData, iri: string, ds?: IDataset): () => Promise<void> {\n return async () => {\n // Build the data fresh on each invocation, so every run writes from the current\n // shape metadata.\n const data = buildNodeShapeData(nodeShape, iri);\n await DeleteBuilder.from(NodeShape, {id: iri}).exec(ds);\n await (NodeShape.create(data as never).withId(iri) as unknown as {\n exec: (target?: IDataset) => Promise<unknown>;\n }).exec(ds);\n };\n}\n\n/** The generated single-owner shape IRIs a code shape writes. */\nfunction generatedIrisOf(nodeShape: NodeShapeData, iri: string): string[] {\n return buildSingleOwnerConstraints(nodeShape, iri).map((c) => c.iri);\n}\n\n/** Throw when a registered shape IRI equals one of a shape's generated single-owner IRIs. */\nfunction assertNoGeneratedIriCollision(\n nodeShape: NodeShapeData,\n iri: string,\n isRegistered: (iri: string) => boolean,\n): void {\n for (const generated of generatedIrisOf(nodeShape, iri)) {\n if (isRegistered(generated)) {\n throw new Error(`Shape IRI ${generated} collides with a generated single-owner shape`);\n }\n }\n}\n\n/** Options controlling how {@link syncShapes} treats store shapes that are not in code. */\nexport interface SyncShapesOptions {\n /**\n * How to treat NodeShapes present in the store but not in the current process's code:\n * - `'none'` (default) — never prune; purely additive (delete→recreate the code shapes, touch\n * nothing else). The only mode that is safe when the process has loaded a *subset* of the\n * shapes in the dataset, or when the dataset has more than one writer.\n * - `'all'` — prune every store-only shape as an orphan. Only correct when this process has\n * loaded **every** shape the dataset should hold and is its **only** writer; \"not registered\n * here\" is then taken to mean \"deleted from code\".\n * - `'ownedNamespaces'` — prune a store-only shape ONLY if its IRI falls within a namespace\n * this process actually writes (the package-namespace of one of its code-registered shapes,\n * i.e. the IRI prefix up to the last `/` or `#`). Protects other writers' packages (arch-04:\n * one app-data dataset, several writers), but NOT the rest of a package the process only\n * partially loaded — the namespace is the whole package, so a deep import of one shape file\n * still claims all of it.\n *\n * `'all'` and `'ownedNamespaces'` are destructive and must be requested explicitly.\n */\n orphanScope?: 'all' | 'ownedNamespaces' | 'none';\n}\n\n/** The owning namespace of a shape IRI — everything up to and including the last `/` or `#`. */\nfunction shapeNamespace(iri: string): string {\n const cut = Math.max(iri.lastIndexOf('/'), iri.lastIndexOf('#'));\n return cut >= 0 ? iri.slice(0, cut + 1) : iri;\n}\n\n/**\n * Plan an idempotent sync of all code-registered (non-framework) NodeShapes into the store as\n * SHACL data — the forward (code → graph) materialization of arch-05 \"code is canonical\".\n *\n * Returns built-but-unexecuted thunks; the caller controls execution and batching:\n * ```ts\n * await Promise.all((await syncShapes()).map((run) => run()));\n * ```\n * Each in-code shape's thunk runs `delete → create` in order (the delete cascade-cleans the old\n * property shapes / list / path subtrees via the containment cascade, the create rebuilds them).\n *\n * **Shapes in the store but not registered in this process are left alone by default**\n * (`orphanScope: 'none'`). The registry only holds what this process happened to import, and\n * that is routinely a subset of what the dataset holds: shapes are deep-importable one file at a\n * time, and an app-data dataset also holds shapes other writers materialized (CN's\n * `bindShape` / `enableCapability`). \"Not registered here\" therefore does not mean \"deleted from\n * code\". Measured against a dataset holding the 36 `@_linked/schema` shapes: a process that\n * imported only `@_linked/schema/shapes/Thing` (11 shapes registered) deleted the other 25 under\n * both `'all'` and `'ownedNamespaces'`, and a process that imported no shapes deleted all 36.\n *\n * Pruning is opt-in via `options.orphanScope` (see {@link SyncShapesOptions}): pass `'all'` only\n * from a process that has loaded every shape the dataset should hold and is its sole writer, and\n * `'ownedNamespaces'` only when it has loaded every shape of each package it registers. Pruned\n * orphans are deleted with their owned subtree. Pruning reads the existing shape IRIs first;\n * with `'none'` no read is made.\n *\n * `ds` (optional) targets an explicit dataset instead of the global router. It is a **plan-time**\n * parameter: it feeds both the orphan-detection read (so orphans are computed against the same\n * store they'll be pruned from) and every delete/create thunk. Omitted → the global router;\n * the returned thunks stay nullary either way.\n */\nexport async function syncShapes(\n ds?: IDataset,\n options?: SyncShapesOptions,\n): Promise<Array<() => Promise<void>>> {\n const orphanScope = options?.orphanScope ?? 'none';\n // 1. Enumerate code-registered user shapes (exclude framework/meta shapes).\n const userShapes: Array<{iri: string; nodeShape: NodeShapeData}> = [];\n for (const [iri, shapeClass] of getAllShapeClasses()) {\n if (!shapeClass?.shape) continue;\n const pkg = (shapeClass as {packageName?: string}).packageName;\n // Skip framework/meta shapes: the core package, and the base `Shape` which is registered\n // directly (no applyLinkedShape) and therefore carries no packageName.\n if (!pkg || pkg === FRAMEWORK_PACKAGE) continue;\n userShapes.push({iri, nodeShape: shapeClass.shape});\n }\n const localShapeIris = new Set(userShapes.map((s) => s.iri));\n for (const {iri, nodeShape} of userShapes) {\n assertNoGeneratedIriCollision(nodeShape, iri, (id) => localShapeIris.has(id));\n }\n // The generated single-owner shapes of in-code shapes are written by the same thunks, so they\n // are never orphans. Derived in memory from the same function that builds them; a stale\n // generated shape (removed accessor or shape) is not protected and goes with its owner's\n // delete, through the `core:generatedShape` cascade.\n for (const {iri, nodeShape} of userShapes) {\n for (const generated of generatedIrisOf(nodeShape, iri)) localShapeIris.add(generated);\n }\n // Namespaces this process actually writes — the ownership scope for `'ownedNamespaces'` pruning.\n const ownedNamespaces = new Set(userShapes.map((s) => shapeNamespace(s.iri)));\n\n const thunks: Array<() => Promise<void>> = [];\n\n // 2. Per in-code shape: delete (cascade) then recreate.\n for (const {iri, nodeShape} of userShapes) {\n thunks.push(buildSyncThunk(nodeShape, iri, ds));\n }\n\n // 3. Orphan sweep (unless disabled). Identity-read existing NodeShape IRIs (ids only). Must hit\n // the same `ds` the thunks target, or orphans get computed against the wrong store.\n if (orphanScope !== 'none') {\n const existingRows = (await (NodeShape as unknown as {\n select: () => {exec: (target?: IDataset) => Promise<Array<{id: string}>>};\n }).select().exec(ds)) as Array<{id: string}>;\n\n // Orphan shapes (in store, not in code) → delete (cascade cleans their owned subtree). In\n // `'ownedNamespaces'` mode, only shapes within a namespace this process writes are eligible —\n // other writers' shapes (different package namespace) are left untouched.\n for (const {id: iri} of existingRows) {\n if (localShapeIris.has(iri)) continue;\n if (orphanScope === 'ownedNamespaces' && !ownedNamespaces.has(shapeNamespace(iri))) {\n continue;\n }\n thunks.push(() =>\n (DeleteBuilder.from(NodeShape, {id: iri}).exec(ds) as Promise<unknown>).then(\n () => undefined,\n ),\n );\n }\n }\n\n return thunks;\n}\n\n/**\n * Plan an idempotent sync of ONE code-registered NodeShape into the store as SHACL data.\n *\n * Scoped counterpart to {@link syncShapes}: materializes a single shape (delete → recreate, so the\n * delete cascade-cleans the old property-shape / list / path subtrees and the create rebuilds them)\n * and does NOT run the store-wide orphan sweep — other shapes in the store are untouched.\n *\n * @param target a shape class (e.g. `Person`) or its NodeShape IRI string. An IRI is resolved to\n * its registered class via `getShapeClass`; the shape must be code-registered.\n * @param ds optional explicit dataset to materialize into (a store or a router) — both the delete\n * and the create run against it instead of the global router. Omitted → global behavior.\n * @returns a single unexecuted thunk (consistent with {@link syncShapes}), so callers can batch\n * several together — e.g. a shape plus its referenced object-property shapes:\n * `await Promise.all([syncShape(Person), syncShape(Address)].map((run) => run()))`.\n * @throws if `target` is a framework/meta shape, is not registered, or has no `.shape`.\n */\nexport function syncShape(target: typeof Shape | string, ds?: IDataset): () => Promise<void> {\n // Normalize target → its registered shape class.\n const cls =\n typeof target === 'string' ? getShapeClass(target) : (target as typeof Shape);\n if (typeof target === 'string' && !cls) {\n throw new Error(`syncShape: no registered shape for IRI ${target}`);\n }\n const nodeShape = cls?.shape;\n if (!nodeShape) {\n throw new Error(\n typeof target === 'string'\n ? `syncShape: no registered shape for IRI ${target}`\n : `syncShape: shape class has no static .shape`,\n );\n }\n const iri = nodeShape.id;\n\n // Never materialize a framework/meta shape as user data — same invariant syncShapes() enforces\n // by skipping (the base Shape carries no packageName).\n const pkg = (cls as {packageName?: string}).packageName;\n if (!pkg || pkg === FRAMEWORK_PACKAGE) {\n throw new Error(`syncShape: refusing to sync framework/meta shape ${iri}`);\n }\n\n assertNoGeneratedIriCollision(nodeShape, iri, (id) => !!getShapeClass(id));\n return buildSyncThunk(nodeShape, iri, ds);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"syncShapes.js","sourceRoot":"","sources":["../../../src/shapes/syncShapes.ts"],"names":[],"mappings":";;;;;;;;;AAAA;;;;GAIG;AACH,OAAO,EAAC,kBAAkB,EAAE,aAAa,EAAC,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAC,SAAS,EAAC,MAAM,YAAY,CAAC;AACrC,OAAO,EACL,uBAAuB,GAGxB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAE1D,OAAO,EAAC,OAAO,EAAC,MAAM,WAAW,CAAC;AAClC,OAAO,EAAC,uBAAuB,EAAC,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAC,2BAA2B,EAAC,MAAM,4BAA4B,CAAC;AAEvE;;;GAGG;AACH,MAAM,iBAAiB,GAAG,eAAe,CAAC;AAE1C;;;;GAIG;AACH,MAAM,UAAU,sBAAsB,CAAC,EAAqB,EAAE,QAAgB;IAC5E,MAAM,KAAK,GAAG,GAAG,QAAQ,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;IACxC,MAAM,CAAC,GAA4B;QACjC,IAAI,EAAE,KAAK;QACX,IAAI,EAAE,uBAAuB,CAAC,EAAE,CAAC,IAAI,CAAC;KACvC,CAAC;IACF,+EAA+E;IAC/E,+EAA+E;IAC/E,sCAAsC;IACtC,EAAE;IACF,8EAA8E;IAC9E,8EAA8E;IAC9E,iFAAiF;IACjF,gFAAgF;IAChF,4EAA4E;IAC5E,iFAAiF;IACjF,iEAAiE;IACjE,IAAI,EAAE,CAAC,KAAK;QAAE,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC;IACjC,IAAI,EAAE,CAAC,QAAQ;QAAE,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC1C,IAAI,EAAE,CAAC,QAAQ;QAAE,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC1C,IAAI,OAAO,EAAE,CAAC,QAAQ,KAAK,QAAQ;QAAE,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC9D,IAAI,OAAO,EAAE,CAAC,QAAQ,KAAK,QAAQ;QAAE,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC9D,IAAI,EAAE,CAAC,IAAI;QAAE,CAAC,CAAC,IAAI,GAAG,EAAE,CAAC,IAAI,CAAC;IAC9B,IAAI,EAAE,CAAC,WAAW;QAAE,CAAC,CAAC,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC;IACnD,IAAI,OAAO,EAAE,CAAC,KAAK,KAAK,QAAQ;QAAE,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC;IACrD,IAAI,EAAE,CAAC,KAAK;QAAE,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC;IACjC,IAAI,OAAO,EAAE,CAAC,WAAW,KAAK,QAAQ;QAAE,CAAC,CAAC,WAAW,GAAG,EAAE,CAAC,WAAW,CAAC;IACvE,IAAI,EAAE,CAAC,aAAa,KAAK,SAAS;QAAE,CAAC,CAAC,aAAa,GAAG,EAAE,CAAC,aAAa,CAAC;IACvE,IAAI,EAAE,CAAC,KAAK;QAAE,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC;IACjC,IAAI,EAAE,CAAC,EAAE;QAAE,CAAC,CAAC,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,EAAC,IAAI,EAAE,GAAG,KAAK,KAAK,EAAC,CAAC,CAAC;IACxD,IAAI,EAAE,CAAC,gBAAgB;QAAE,CAAC,CAAC,gBAAgB,GAAG,EAAE,CAAC,gBAAgB,CAAC;IAClE,IAAI,EAAE,CAAC,QAAQ;QAAE,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC1C,IAAI,EAAE,CAAC,QAAQ;QAAE,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC;IAC1C,IAAI,EAAE,CAAC,gBAAgB;QAAE,CAAC,CAAC,gBAAgB,GAAG,EAAE,CAAC,gBAAgB,CAAC;IAClE,IAAI,EAAE,CAAC,YAAY,KAAK,SAAS;QAAE,CAAC,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC;IACpE,IAAI,EAAE,CAAC,YAAY,KAAK,SAAS;QAAE,CAAC,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC;IACpE,IAAI,EAAE,CAAC,YAAY,KAAK,SAAS;QAAE,CAAC,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC;IACpE,IAAI,EAAE,CAAC,YAAY,KAAK,SAAS;QAAE,CAAC,CAAC,YAAY,GAAG,EAAE,CAAC,YAAY,CAAC;IACpE,IAAI,OAAO,EAAE,CAAC,SAAS,KAAK,QAAQ;QAAE,CAAC,CAAC,SAAS,GAAG,EAAE,CAAC,SAAS,CAAC;IACjE,IAAI,OAAO,EAAE,CAAC,SAAS,KAAK,QAAQ;QAAE,CAAC,CAAC,SAAS,GAAG,EAAE,CAAC,SAAS,CAAC;IACjE,uDAAuD;IACvD,IAAI,EAAE,CAAC,OAAO;QAAE,CAAC,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC;IAC9C,IAAI,EAAE,CAAC,kBAAkB,KAAK,SAAS;QAAE,CAAC,CAAC,QAAQ,GAAG,EAAE,CAAC,kBAAkB,CAAC;IAC5E,IAAI,EAAE,CAAC,UAAU;QAAE,CAAC,CAAC,UAAU,GAAG,EAAE,CAAC,UAAU,CAAC;IAChD,IAAI,EAAE,CAAC,QAAQ;QAAE,CAAC,CAAC,QAAQ,GAAG,IAAI,CAAC;IACnC,OAAO,CAAC,CAAC;AACX,CAAC;AAED,iGAAiG;AACjG,SAAS,kBAAkB,CAAC,SAAwB,EAAE,QAAgB;IACpE,MAAM,CAAC,GAA4B,EAAE,CAAC;IACtC,IAAI,SAAS,CAAC,WAAW;QAAE,CAAC,CAAC,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC;IACjE,IAAI,SAAS,CAAC,WAAW;QAAE,CAAC,CAAC,WAAW,GAAG,SAAS,CAAC,WAAW,CAAC;IACjE,IAAI,SAAS,CAAC,OAAO;QAAE,CAAC,CAAC,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;IACrD,IAAI,SAAS,CAAC,SAAS;QAAE,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC;IAC5C,IAAI,SAAS,CAAC,MAAM,KAAK,SAAS;QAAE,CAAC,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC;IAChE,IAAI,SAAS,CAAC,iBAAiB,IAAI,SAAS,CAAC,iBAAiB,CAAC,MAAM,EAAE,CAAC;QACtE,CAAC,CAAC,iBAAiB,GAAG,SAAS,CAAC,iBAAiB,CAAC;IACpD,CAAC;IACD,qFAAqF;IACrF,CAAC,CAAC,UAAU,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAC3D,sBAAsB,CAAC,EAAE,EAAE,QAAQ,CAAC,CACrC,CAAC;IACF,uFAAuF;IACvF,qFAAqF;IACrF,MAAM,SAAS,GAAG,2BAA2B,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACnE,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,CAAC,CAAC,eAAe,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,EAAC,GAAG,EAAE,IAAI,EAAC,EAAE,EAAE,CAAC,iBAAE,IAAI,EAAE,GAAG,IAAK,IAAI,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,SAAS,cAAc,CAAC,SAAwB,EAAE,GAAW,EAAE,EAAa;IAC1E,OAAO,GAAS,EAAE;QAChB,gFAAgF;QAChF,kBAAkB;QAClB,MAAM,IAAI,GAAG,kBAAkB,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;QAChD,MAAM,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,EAAC,EAAE,EAAE,GAAG,EAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxD,MAAO,SAAS,CAAC,MAAM,CAAC,IAAa,CAAC,CAAC,MAAM,CAAC,GAAG,CAE/C,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACd,CAAC,CAAA,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,SAAS,eAAe,CAAC,SAAwB,EAAE,GAAW;IAC5D,OAAO,2BAA2B,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;AACvE,CAAC;AAED,6FAA6F;AAC7F,SAAS,6BAA6B,CACpC,SAAwB,EACxB,GAAW,EACX,YAAsC;IAEtC,KAAK,MAAM,SAAS,IAAI,eAAe,CAAC,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC;QACxD,IAAI,YAAY,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,KAAK,CAAC,aAAa,SAAS,+CAA+C,CAAC,CAAC;QACzF,CAAC;IACH,CAAC;AACH,CAAC;AAwBD,gGAAgG;AAChG,SAAS,cAAc,CAAC,GAAW;IACjC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IACjE,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAChD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAgB,UAAU,CAC9B,EAAa,EACb,OAA2B;;;QAE3B,MAAM,WAAW,GAAG,MAAA,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,WAAW,mCAAI,MAAM,CAAC;QACnD,4EAA4E;QAC5E,MAAM,UAAU,GAAmD,EAAE,CAAC;QACtE,KAAK,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,kBAAkB,EAAE,EAAE,CAAC;YACrD,IAAI,CAAC,CAAA,UAAU,aAAV,UAAU,uBAAV,UAAU,CAAE,KAAK,CAAA;gBAAE,SAAS;YACjC,MAAM,GAAG,GAAI,UAAqC,CAAC,WAAW,CAAC;YAC/D,yFAAyF;YACzF,uEAAuE;YACvE,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,iBAAiB;gBAAE,SAAS;YAChD,UAAU,CAAC,IAAI,CAAC,EAAC,GAAG,EAAE,SAAS,EAAE,UAAU,CAAC,KAAK,EAAC,CAAC,CAAC;QACtD,CAAC;QACD,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7D,KAAK,MAAM,EAAC,GAAG,EAAE,SAAS,EAAC,IAAI,UAAU,EAAE,CAAC;YAC1C,6BAA6B,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAChF,CAAC;QACD,8FAA8F;QAC9F,wFAAwF;QACxF,yFAAyF;QACzF,qDAAqD;QACrD,KAAK,MAAM,EAAC,GAAG,EAAE,SAAS,EAAC,IAAI,UAAU,EAAE,CAAC;YAC1C,KAAK,MAAM,SAAS,IAAI,eAAe,CAAC,SAAS,EAAE,GAAG,CAAC;gBAAE,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACzF,CAAC;QACD,iGAAiG;QACjG,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAE9E,MAAM,MAAM,GAA+B,EAAE,CAAC;QAE9C,wDAAwD;QACxD,KAAK,MAAM,EAAC,GAAG,EAAE,SAAS,EAAC,IAAI,UAAU,EAAE,CAAC;YAC1C,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;QAClD,CAAC;QAED,gGAAgG;QAChG,uFAAuF;QACvF,IAAI,WAAW,KAAK,MAAM,EAAE,CAAC;YAC3B,MAAM,YAAY,GAAG,CAAC,MAAO,SAE3B,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAwB,CAAC;YAE7C,0FAA0F;YAC1F,8FAA8F;YAC9F,0EAA0E;YAC1E,KAAK,MAAM,EAAC,EAAE,EAAE,GAAG,EAAC,IAAI,YAAY,EAAE,CAAC;gBACrC,IAAI,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC;oBAAE,SAAS;gBACtC,IAAI,WAAW,KAAK,iBAAiB,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;oBACnF,SAAS;gBACX,CAAC;gBACD,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CACd,aAAa,CAAC,IAAI,CAAC,SAAS,EAAE,EAAC,EAAE,EAAE,GAAG,EAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAsB,CAAC,IAAI,CAC1E,GAAG,EAAE,CAAC,SAAS,CAChB,CACF,CAAC;YACJ,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CAAA;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,SAAS,CAAC,MAA6B,EAAE,EAAa;IACpE,iDAAiD;IACjD,MAAM,GAAG,GACP,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAE,MAAuB,CAAC;IAChF,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,GAAG,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,0CAA0C,MAAM,EAAE,CAAC,CAAC;IACtE,CAAC;IACD,MAAM,SAAS,GAAG,GAAG,aAAH,GAAG,uBAAH,GAAG,CAAE,KAAK,CAAC;IAC7B,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,OAAO,MAAM,KAAK,QAAQ;YACxB,CAAC,CAAC,0CAA0C,MAAM,EAAE;YACpD,CAAC,CAAC,6CAA6C,CAClD,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,GAAG,SAAS,CAAC,EAAE,CAAC;IAEzB,+FAA+F;IAC/F,uDAAuD;IACvD,MAAM,GAAG,GAAI,GAA8B,CAAC,WAAW,CAAC;IACxD,IAAI,CAAC,GAAG,IAAI,GAAG,KAAK,iBAAiB,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,oDAAoD,GAAG,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,6BAA6B,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3E,OAAO,cAAc,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;AAC5C,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 {getAllShapeClasses, getShapeClass} from '../utils/ShapeClass.js';\nimport {NodeShape} from './SHACL.js';\nimport {\n getUniquePropertyShapes,\n type NodeShapeData,\n type PropertyShapeData,\n} from './nodeShapeData.js';\nimport {Shape} from './Shape.js';\nimport {DeleteBuilder} from '../queries/DeleteBuilder.js';\nimport type {IDataset} from '../interfaces/IDataset.js';\nimport {rdfList} from './List.js';\nimport {serializePathToNodeData} from './serializePathToNodeData.js';\nimport {buildSingleOwnerConstraints} from './singleOwnerConstraint.js';\n\n/**\n * The npm scope of framework shapes (NodeShape/PropertyShape/List/PathNode/Shape) that\n * describe the meta-model itself and must never be synced as user data.\n */\nconst FRAMEWORK_PACKAGE = '@_linked/core';\n\n/**\n * Build the create-pipeline data object for a single PropertyShape (flattened under shapeIri).\n *\n * @internal Exported for tests only — not part of the public API.\n */\nexport function buildPropertyShapeData(ps: PropertyShapeData, shapeIri: string): Record<string, unknown> {\n const psIri = `${shapeIri}/${ps.label}`;\n const d: Record<string, unknown> = {\n __id: psIri,\n path: serializePathToNodeData(ps.path),\n };\n // `rdfs:label` — the DSL label, i.e. the accessor name the property is reached\n // by in code (`person.memberships`). It is NOT `sh:name`, written below, which\n // is a human-readable display string.\n //\n // Without this the label is only ever implied by `psIri`, and a reader has to\n // re-derive one from the path's local part. That works right up until the two\n // differ — `@_linked/org` declares `memberships` on the path `org:hasMembership`\n // — at which point the catalog reports `hasMembership`, the query proxy indexes\n // the shape with it, and the class (which only knows `memberships`) throws.\n // Readers already prefer a stored label and fall back to the path, so writing it\n // is the whole fix; old catalogs keep working via that fallback.\n if (ps.label) d.label = ps.label;\n if (ps.nodeKind) d.nodeKind = ps.nodeKind;\n if (ps.datatype) d.datatype = ps.datatype;\n if (typeof ps.minCount === 'number') d.minCount = ps.minCount;\n if (typeof ps.maxCount === 'number') d.maxCount = ps.maxCount;\n if (ps.name) d.name = ps.name;\n if (ps.description) d.description = ps.description;\n if (typeof ps.order === 'number') d.order = ps.order;\n if (ps.group) d.group = ps.group;\n if (typeof ps.displayRank === 'number') d.displayRank = ps.displayRank;\n if (ps.displayHidden !== undefined) d.displayHidden = ps.displayHidden;\n if (ps.class) d.class = ps.class;\n if (ps.in) d.in = rdfList(ps.in, {base: `${psIri}/in`});\n if (ps.equalsConstraint) d.equalsConstraint = ps.equalsConstraint;\n if (ps.disjoint) d.disjoint = ps.disjoint;\n if (ps.lessThan) d.lessThan = ps.lessThan;\n if (ps.lessThanOrEquals) d.lessThanOrEquals = ps.lessThanOrEquals;\n if (ps.minInclusive !== undefined) d.minInclusive = ps.minInclusive;\n if (ps.maxInclusive !== undefined) d.maxInclusive = ps.maxInclusive;\n if (ps.minExclusive !== undefined) d.minExclusive = ps.minExclusive;\n if (ps.maxExclusive !== undefined) d.maxExclusive = ps.maxExclusive;\n if (typeof ps.minLength === 'number') d.minLength = ps.minLength;\n if (typeof ps.maxLength === 'number') d.maxLength = ps.maxLength;\n // sh:pattern is serialized as its regex source string.\n if (ps.pattern) d.pattern = ps.pattern.source;\n if (ps.hasValueConstraint !== undefined) d.hasValue = ps.hasValueConstraint;\n if (ps.valueShape) d.valueShape = ps.valueShape;\n if (ps.contains) d.contains = true;\n return d;\n}\n\n/** Build the create-pipeline data object for a NodeShape and its (flattened) property shapes. */\nfunction buildNodeShapeData(nodeShape: NodeShapeData, shapeIri: string): Record<string, unknown> {\n const d: Record<string, unknown> = {};\n if (nodeShape.targetClass) d.targetClass = nodeShape.targetClass;\n if (nodeShape.description) d.description = nodeShape.description;\n if (nodeShape.extends) d.extends = nodeShape.extends;\n if (nodeShape.dependent) d.dependent = true;\n if (nodeShape.closed !== undefined) d.closed = nodeShape.closed;\n if (nodeShape.ignoredProperties && nodeShape.ignoredProperties.length) {\n d.ignoredProperties = nodeShape.ignoredProperties;\n }\n // Flatten: emit own + inherited property shapes (deduped by label) under this shape.\n d.properties = getUniquePropertyShapes(nodeShape).map((ps) =>\n buildPropertyShapeData(ps, shapeIri),\n );\n // The single-owner constraints of the shape's own `contains` properties, owned through\n // `core:generatedShape` and written nested in the same create with their fixed IRIs.\n const generated = buildSingleOwnerConstraints(nodeShape, shapeIri);\n if (generated.length > 0) {\n d.generatedShapes = generated.map(({iri, data}) => ({__id: iri, ...data}));\n }\n return d;\n}\n\n/**\n * The per-shape `delete → create` thunk shared by `syncShapes()` and `syncShape()`: exactly two\n * requests. The delete cascade-cleans the shape's old property-shape / list / path subtrees; the\n * create rebuilds them.\n *\n * The single-owner constraints generated from the shape's own `contains` properties\n * (`${iri}/${label}.singleOwner`, see `singleOwnerConstraint.ts`) are owned by the shape through\n * `core:generatedShape` (a `contains` property with value shape NodeShape). The delete removes\n * each one's own triples (the immediate owned-child block) and their property shapes and path\n * nodes (the dependent cascade); the create writes the current ones nested, in the same request\n * as the shape. A removed or renamed accessor therefore loses its constraint on the next sync,\n * and a NodeShape the shape does not own is never touched.\n *\n * `ds` (optional) targets an explicit dataset — both requests run against it instead of the\n * global router. Omitted → today's global behavior.\n */\nfunction buildSyncThunk(nodeShape: NodeShapeData, iri: string, ds?: IDataset): () => Promise<void> {\n return async () => {\n // Build the data fresh on each invocation, so every run writes from the current\n // shape metadata.\n const data = buildNodeShapeData(nodeShape, iri);\n await DeleteBuilder.from(NodeShape, {id: iri}).exec(ds);\n await (NodeShape.create(data as never).withId(iri) as unknown as {\n exec: (target?: IDataset) => Promise<unknown>;\n }).exec(ds);\n };\n}\n\n/** The generated single-owner shape IRIs a code shape writes. */\nfunction generatedIrisOf(nodeShape: NodeShapeData, iri: string): string[] {\n return buildSingleOwnerConstraints(nodeShape, iri).map((c) => c.iri);\n}\n\n/** Throw when a registered shape IRI equals one of a shape's generated single-owner IRIs. */\nfunction assertNoGeneratedIriCollision(\n nodeShape: NodeShapeData,\n iri: string,\n isRegistered: (iri: string) => boolean,\n): void {\n for (const generated of generatedIrisOf(nodeShape, iri)) {\n if (isRegistered(generated)) {\n throw new Error(`Shape IRI ${generated} collides with a generated single-owner shape`);\n }\n }\n}\n\n/** Options controlling how {@link syncShapes} treats store shapes that are not in code. */\nexport interface SyncShapesOptions {\n /**\n * How to treat NodeShapes present in the store but not in the current process's code:\n * - `'none'` (default) — never prune; purely additive (delete→recreate the code shapes, touch\n * nothing else). The only mode that is safe when the process has loaded a *subset* of the\n * shapes in the dataset, or when the dataset has more than one writer.\n * - `'all'` — prune every store-only shape as an orphan. Only correct when this process has\n * loaded **every** shape the dataset should hold and is its **only** writer; \"not registered\n * here\" is then taken to mean \"deleted from code\".\n * - `'ownedNamespaces'` — prune a store-only shape ONLY if its IRI falls within a namespace\n * this process actually writes (the package-namespace of one of its code-registered shapes,\n * i.e. the IRI prefix up to the last `/` or `#`). Protects other writers' packages (arch-04:\n * one app-data dataset, several writers), but NOT the rest of a package the process only\n * partially loaded — the namespace is the whole package, so a deep import of one shape file\n * still claims all of it.\n *\n * `'all'` and `'ownedNamespaces'` are destructive and must be requested explicitly.\n */\n orphanScope?: 'all' | 'ownedNamespaces' | 'none';\n}\n\n/** The owning namespace of a shape IRI — everything up to and including the last `/` or `#`. */\nfunction shapeNamespace(iri: string): string {\n const cut = Math.max(iri.lastIndexOf('/'), iri.lastIndexOf('#'));\n return cut >= 0 ? iri.slice(0, cut + 1) : iri;\n}\n\n/**\n * Plan an idempotent sync of all code-registered (non-framework) NodeShapes into the store as\n * SHACL data — the forward (code → graph) materialization of arch-05 \"code is canonical\".\n *\n * Returns built-but-unexecuted thunks; the caller controls execution and batching:\n * ```ts\n * await Promise.all((await syncShapes()).map((run) => run()));\n * ```\n * Each in-code shape's thunk runs `delete → create` in order (the delete cascade-cleans the old\n * property shapes / list / path subtrees via the containment cascade, the create rebuilds them).\n *\n * **Shapes in the store but not registered in this process are left alone by default**\n * (`orphanScope: 'none'`). The registry only holds what this process happened to import, and\n * that is routinely a subset of what the dataset holds: shapes are deep-importable one file at a\n * time, and an app-data dataset also holds shapes other writers materialized (CN's\n * `bindShape` / `enableCapability`). \"Not registered here\" therefore does not mean \"deleted from\n * code\". Measured against a dataset holding the 36 `@_linked/schema` shapes: a process that\n * imported only `@_linked/schema/shapes/Thing` (11 shapes registered) deleted the other 25 under\n * both `'all'` and `'ownedNamespaces'`, and a process that imported no shapes deleted all 36.\n *\n * Pruning is opt-in via `options.orphanScope` (see {@link SyncShapesOptions}): pass `'all'` only\n * from a process that has loaded every shape the dataset should hold and is its sole writer, and\n * `'ownedNamespaces'` only when it has loaded every shape of each package it registers. Pruned\n * orphans are deleted with their owned subtree. Pruning reads the existing shape IRIs first;\n * with `'none'` no read is made.\n *\n * `ds` (optional) targets an explicit dataset instead of the global router. It is a **plan-time**\n * parameter: it feeds both the orphan-detection read (so orphans are computed against the same\n * store they'll be pruned from) and every delete/create thunk. Omitted → the global router;\n * the returned thunks stay nullary either way.\n */\nexport async function syncShapes(\n ds?: IDataset,\n options?: SyncShapesOptions,\n): Promise<Array<() => Promise<void>>> {\n const orphanScope = options?.orphanScope ?? 'none';\n // 1. Enumerate code-registered user shapes (exclude framework/meta shapes).\n const userShapes: Array<{iri: string; nodeShape: NodeShapeData}> = [];\n for (const [iri, shapeClass] of getAllShapeClasses()) {\n if (!shapeClass?.shape) continue;\n const pkg = (shapeClass as {packageName?: string}).packageName;\n // Skip framework/meta shapes: the core package, and the base `Shape` which is registered\n // directly (no applyLinkedShape) and therefore carries no packageName.\n if (!pkg || pkg === FRAMEWORK_PACKAGE) continue;\n userShapes.push({iri, nodeShape: shapeClass.shape});\n }\n const localShapeIris = new Set(userShapes.map((s) => s.iri));\n for (const {iri, nodeShape} of userShapes) {\n assertNoGeneratedIriCollision(nodeShape, iri, (id) => localShapeIris.has(id));\n }\n // The generated single-owner shapes of in-code shapes are written by the same thunks, so they\n // are never orphans. Derived in memory from the same function that builds them; a stale\n // generated shape (removed accessor or shape) is not protected and goes with its owner's\n // delete, through the `core:generatedShape` cascade.\n for (const {iri, nodeShape} of userShapes) {\n for (const generated of generatedIrisOf(nodeShape, iri)) localShapeIris.add(generated);\n }\n // Namespaces this process actually writes — the ownership scope for `'ownedNamespaces'` pruning.\n const ownedNamespaces = new Set(userShapes.map((s) => shapeNamespace(s.iri)));\n\n const thunks: Array<() => Promise<void>> = [];\n\n // 2. Per in-code shape: delete (cascade) then recreate.\n for (const {iri, nodeShape} of userShapes) {\n thunks.push(buildSyncThunk(nodeShape, iri, ds));\n }\n\n // 3. Orphan sweep (unless disabled). Identity-read existing NodeShape IRIs (ids only). Must hit\n // the same `ds` the thunks target, or orphans get computed against the wrong store.\n if (orphanScope !== 'none') {\n const existingRows = (await (NodeShape as unknown as {\n select: () => {exec: (target?: IDataset) => Promise<Array<{id: string}>>};\n }).select().exec(ds)) as Array<{id: string}>;\n\n // Orphan shapes (in store, not in code) → delete (cascade cleans their owned subtree). In\n // `'ownedNamespaces'` mode, only shapes within a namespace this process writes are eligible —\n // other writers' shapes (different package namespace) are left untouched.\n for (const {id: iri} of existingRows) {\n if (localShapeIris.has(iri)) continue;\n if (orphanScope === 'ownedNamespaces' && !ownedNamespaces.has(shapeNamespace(iri))) {\n continue;\n }\n thunks.push(() =>\n (DeleteBuilder.from(NodeShape, {id: iri}).exec(ds) as Promise<unknown>).then(\n () => undefined,\n ),\n );\n }\n }\n\n return thunks;\n}\n\n/**\n * Plan an idempotent sync of ONE code-registered NodeShape into the store as SHACL data.\n *\n * Scoped counterpart to {@link syncShapes}: materializes a single shape (delete → recreate, so the\n * delete cascade-cleans the old property-shape / list / path subtrees and the create rebuilds them)\n * and does NOT run the store-wide orphan sweep — other shapes in the store are untouched.\n *\n * @param target a shape class (e.g. `Person`) or its NodeShape IRI string. An IRI is resolved to\n * its registered class via `getShapeClass`; the shape must be code-registered.\n * @param ds optional explicit dataset to materialize into (a store or a router) — both the delete\n * and the create run against it instead of the global router. Omitted → global behavior.\n * @returns a single unexecuted thunk (consistent with {@link syncShapes}), so callers can batch\n * several together — e.g. a shape plus its referenced object-property shapes:\n * `await Promise.all([syncShape(Person), syncShape(Address)].map((run) => run()))`.\n * @throws if `target` is a framework/meta shape, is not registered, or has no `.shape`.\n */\nexport function syncShape(target: typeof Shape | string, ds?: IDataset): () => Promise<void> {\n // Normalize target → its registered shape class.\n const cls =\n typeof target === 'string' ? getShapeClass(target) : (target as typeof Shape);\n if (typeof target === 'string' && !cls) {\n throw new Error(`syncShape: no registered shape for IRI ${target}`);\n }\n const nodeShape = cls?.shape;\n if (!nodeShape) {\n throw new Error(\n typeof target === 'string'\n ? `syncShape: no registered shape for IRI ${target}`\n : `syncShape: shape class has no static .shape`,\n );\n }\n const iri = nodeShape.id;\n\n // Never materialize a framework/meta shape as user data — same invariant syncShapes() enforces\n // by skipping (the base Shape carries no packageName).\n const pkg = (cls as {packageName?: string}).packageName;\n if (!pkg || pkg === FRAMEWORK_PACKAGE) {\n throw new Error(`syncShape: refusing to sync framework/meta shape ${iri}`);\n }\n\n assertNoGeneratedIriCollision(nodeShape, iri, (id) => !!getShapeClass(id));\n return buildSyncThunk(nodeShape, iri, ds);\n}\n"]}
|
|
@@ -42,7 +42,7 @@ import { shacl } from '../ontologies/shacl.js';
|
|
|
42
42
|
import { xsd } from '../ontologies/xsd.js';
|
|
43
43
|
import { isNodeReferenceValue } from '../utils/NodeReference.js';
|
|
44
44
|
import { getUniquePropertyShapes } from './nodeShapeData.js';
|
|
45
|
-
import { getNodeShape, getShapeClass } from '../utils/ShapeClass.js';
|
|
45
|
+
import { getNodeShape, getShapeClass, isSubShapeOf } from '../utils/ShapeClass.js';
|
|
46
46
|
import { isExpressionNode } from '../expressions/ExpressionNode.js';
|
|
47
47
|
import { asContextRef } from '../queries/QueryContext.js';
|
|
48
48
|
/** Thrown by {@link assertValid} — carries the full report, not just the first violation. */
|
|
@@ -55,10 +55,10 @@ export class ShapeValidationError extends Error {
|
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
57
57
|
* Keys that carry metadata about the node rather than a property value: `id` /
|
|
58
|
-
* `__id` name it, and `shape` names the shape of a nested value when the
|
|
58
|
+
* `__id` name it, `__blank` asks for an anonymous node, and `shape` names the shape of a nested value when the
|
|
59
59
|
* property shape itself doesn't declare one (see `convertUpdateValue`).
|
|
60
60
|
*/
|
|
61
|
-
const RESERVED_KEYS = new Set(['id', '__id', 'shape']);
|
|
61
|
+
const RESERVED_KEYS = new Set(['id', '__id', '__blank', 'shape']);
|
|
62
62
|
/**
|
|
63
63
|
* The message for an undeclared property key. Shared with the normalization path
|
|
64
64
|
* in `MutationQuery`, which needs the same guard to build a field at all.
|
|
@@ -654,17 +654,21 @@ function unresolvedValueShapeMessage(ps) {
|
|
|
654
654
|
`Add a 'shape' to its @objectProperty decorator, or give the value a 'shape' key.`;
|
|
655
655
|
}
|
|
656
656
|
/**
|
|
657
|
-
* The shape a nested value should be validated against: the
|
|
658
|
-
*
|
|
659
|
-
*
|
|
660
|
-
* undefined when neither is available.
|
|
657
|
+
* The shape a nested value should be validated against: the shape class carried
|
|
658
|
+
* in the value's reserved `shape` key when the property declares no `valueShape`
|
|
659
|
+
* or the key names a subclass of it, otherwise the declared `valueShape`. Mirrors
|
|
660
|
+
* `convertUpdateValue`; returns undefined when neither is available.
|
|
661
661
|
*/
|
|
662
662
|
function resolveValueShape(ps, value) {
|
|
663
|
-
var _a;
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
663
|
+
var _a, _b;
|
|
664
|
+
const own = (_a = value.shape) === null || _a === void 0 ? void 0 : _a.shape;
|
|
665
|
+
if (!ps.valueShape)
|
|
666
|
+
return own;
|
|
667
|
+
const declared = (_b = getShapeClass(ps.valueShape)) === null || _b === void 0 ? void 0 : _b.shape;
|
|
668
|
+
// A value naming a subclass of the declared shape is validated as that subclass.
|
|
669
|
+
if (own && declared && isSubShapeOf(own, declared))
|
|
670
|
+
return own;
|
|
671
|
+
return declared;
|
|
668
672
|
}
|
|
669
673
|
function join(prefix, label) {
|
|
670
674
|
return prefix ? `${prefix}.${label}` : label;
|