@_linked/core 2.28.4 → 2.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"SparqlDataset.js","sourceRoot":"","sources":["../../../src/sparql/SparqlDataset.ts"],"names":[],"mappings":"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","sourcesContent":["import type {IDataset} from '../interfaces/IDataset.js';\nimport type {SelectQuery} from '../queries/SelectQuery.js';\nimport type {AskQuery} from '../queries/AskQuery.js';\nimport type {CountQuery} from '../queries/CountQuery.js';\nimport type {CreateQuery} from '../queries/CreateQuery.js';\nimport type {UpdateQuery} from '../queries/UpdateQuery.js';\nimport type {DeleteQuery, DeleteResponse} from '../queries/DeleteQuery.js';\nimport type {\n IRSelectQuery,\n IRCountQuery,\n SelectResult,\n CreateResult,\n UpdateResult,\n} from '../queries/IntermediateRepresentation.js';\nimport type {SparqlQueryResults} from './resultMapping.js';\nimport {\n selectToSparql,\n askToSparql,\n countToSparql,\n createToSparql,\n updateToSparql,\n upsertToSparql,\n updateWhereToSparql,\n deleteToSparql,\n deleteAllToSparql,\n deleteWhereToSparql,\n} from './irToAlgebra.js';\nimport {\n mapSparqlSelectResult,\n mapSparqlAskResult,\n mapSparqlCountResult,\n mapSparqlCreateResult,\n mapSparqlUpdateResult,\n isSparqlSelectResults,\n} from './resultMapping.js';\nimport {generateEntityUri, type SparqlOptions} from './sparqlUtils.js';\nimport {lower} from '../queries/lower.js';\n\n/**\n * Abstract base class for SPARQL-backed datasets.\n *\n * Handles the full pipeline: IR query → SPARQL string → execute → map results.\n * Subclasses only need to implement the two transport methods:\n * - `executeSparqlSelect` — send a SPARQL SELECT and return JSON results\n * - `executeSparqlUpdate` — send a SPARQL UPDATE (INSERT DATA / DELETE..INSERT / etc.)\n *\n * Example subclass (Fuseki):\n * ```ts\n * class FusekiDataset extends SparqlDataset {\n * constructor(baseUrl: string, dataset: string) {\n * super({ dataRoot: 'http://data.example.org' });\n * this.queryEndpoint = `${baseUrl}/${dataset}/sparql`;\n * this.updateEndpoint = `${baseUrl}/${dataset}/update`;\n * }\n * protected async executeSparqlSelect(sparql: string) {\n * const res = await fetch(this.queryEndpoint, { ... });\n * return res.json();\n * }\n * protected async executeSparqlUpdate(sparql: string) {\n * await fetch(this.updateEndpoint, { ... });\n * }\n * }\n * ```\n */\nexport abstract class SparqlDataset implements IDataset {\n protected options?: SparqlOptions;\n\n constructor(options?: SparqlOptions) {\n this.options = options;\n }\n\n /**\n * Send a SPARQL SELECT/ASK/CONSTRUCT query and return the parsed\n * SPARQL JSON Results (application/sparql-results+json).\n *\n * The return type is the union of both response shapes a query endpoint can\n * produce: a SELECT result set (`results.bindings`) or an ASK answer\n * (`boolean`). A subclass may still declare the narrower `SparqlJsonResults` —\n * a return type is covariant, so existing implementations stay valid.\n */\n protected abstract executeSparqlSelect(\n sparql: string,\n ): Promise<SparqlQueryResults>;\n\n /**\n * Send a SPARQL UPDATE request (INSERT DATA, DELETE/INSERT, etc.).\n * No return value — the update is fire-and-forget at the SPARQL level.\n */\n protected abstract executeSparqlUpdate(sparql: string): Promise<void>;\n\n /**\n * Answer a select query — **and a count, which is a select.**\n *\n * A count is not a distinct query form the way an ask is. An ask really is\n * `ASK WHERE { … }` and needs its own method; a count is\n * `SELECT (COUNT(DISTINCT ?a0) AS ?count) WHERE { … }` — the same query form, the\n * same transport (`executeSparqlSelect`), the same result-set response, differing\n * only in its projection. `countToAlgebra` says so itself: it builds its plan by\n * calling `selectToAlgebra` and swapping the projection, and yields\n * `{type: 'select'}`.\n *\n * So a count rides this channel rather than a method of its own. That is not\n * tidiness: it is what lets a count reach a store through every router that\n * already forwards a select — `LinkedStorage.selectQuery`, a backend API store —\n * with no per-kind plumbing to add in each of them, and no optional `IDataset`\n * member that a third-party store silently lacks.\n *\n * The branch is on the LOWERED IR, as `updateQuery` and `deleteQuery` branch on\n * theirs: `lower()` is the one place that knows a count from a select, and a\n * rehydrated envelope (`fromJSON`) arrives here as a builder like any other.\n *\n * A count carries only a pattern, so there is nothing to normalise away on that\n * path: no projection, no ordering, and in particular no `LIMIT`/`OFFSET`, which\n * the builder dropped before the query ever reached the IR. Errors reject; a count\n * failure is never reported as `0`.\n */\n selectQuery(query: SelectQuery): Promise<SelectResult>;\n selectQuery(query: CountQuery): Promise<number>;\n async selectQuery(\n query: SelectQuery | CountQuery,\n ): Promise<SelectResult | number> {\n // Cast once for the call rather than lowering in each branch: `lower` is\n // overloaded per query kind and cannot resolve a union, and lowering twice\n // would be the thing this method most needs not to do.\n const ir = lower(query as SelectQuery) as IRSelectQuery | IRCountQuery;\n if (ir.kind === 'count') {\n const sparql = countToSparql(ir, this.options);\n const json = await this.executeSparqlSelect(sparql);\n // `mapSparqlCountResult` is strict in the same way the guard below is: it\n // refuses an ASK response, an empty result set and a non-integer binding\n // rather than answering `0`.\n return mapSparqlCountResult(json, ir);\n }\n const sparql = selectToSparql(ir, this.options);\n const json = await this.executeSparqlSelect(sparql);\n if (!isSparqlSelectResults(json)) {\n throw new Error(\n 'Expected a SPARQL SELECT result set, got an ASK response. The endpoint ' +\n 'did not answer the SELECT query that was sent.',\n );\n }\n return mapSparqlSelectResult(json, ir);\n }\n\n /**\n * Answer an ask query — emitted as `ASK WHERE { … }`.\n *\n * An {@link AskQuery} carries only a pattern, so there is nothing to normalise\n * away here. A query with no shape lowers to `ASK { <iri> ?p ?o }` — existence\n * of the node under any type or none. Errors reject; never reported as `false`.\n */\n async askQuery(query: AskQuery): Promise<boolean> {\n const ir = lower(query);\n const sparql = askToSparql(ir, this.options);\n const json = await this.executeSparqlSelect(sparql);\n return mapSparqlAskResult(json);\n }\n\n async createQuery(query: CreateQuery): Promise<CreateResult> {\n const ir = lower(query);\n const uri = ir.data.id || generateEntityUri(ir.data.shape, this.options);\n ir.data.id = uri;\n const sparql = createToSparql(ir, this.options);\n await this.executeSparqlUpdate(sparql);\n return mapSparqlCreateResult(uri, ir);\n }\n\n async updateQuery(query: UpdateQuery): Promise<UpdateResult> {\n const ir = lower(query);\n if (ir.kind === 'update_where') {\n const sparql = updateWhereToSparql(ir, this.options);\n await this.executeSparqlUpdate(sparql);\n return {id: ''} as UpdateResult;\n }\n const sparql =\n ir.kind === 'upsert'\n ? upsertToSparql(ir, this.options)\n : updateToSparql(ir, this.options);\n await this.executeSparqlUpdate(sparql);\n return mapSparqlUpdateResult(ir);\n }\n\n async deleteQuery(query: DeleteQuery): Promise<DeleteResponse> {\n const ir = lower(query);\n if (ir.kind === 'delete_all') {\n const sparql = deleteAllToSparql(ir, this.options);\n await this.executeSparqlUpdate(sparql);\n return {deleted: [], count: 0};\n }\n if (ir.kind === 'delete_where') {\n const sparql = deleteWhereToSparql(ir, this.options);\n await this.executeSparqlUpdate(sparql);\n return {deleted: [], count: 0};\n }\n const sparql = deleteToSparql(ir, this.options);\n await this.executeSparqlUpdate(sparql);\n return {\n deleted: ir.ids,\n count: ir.ids.length,\n };\n }\n\n /**\n * Execute a raw SPARQL query string directly against the dataset.\n * Defaults to SELECT; pass `'update'` for INSERT/DELETE operations.\n */\n async rawQuery(\n sparql: string,\n mode?: 'query' | 'update',\n ): Promise<SparqlQueryResults | void> {\n if (mode === 'update') {\n return this.executeSparqlUpdate(sparql);\n }\n return this.executeSparqlSelect(sparql);\n }\n}\n\n// Backwards-compatibility re-export — remove once all consumers are updated.\nexport {SparqlDataset as SparqlStore};\n"]}
1
+ {"version":3,"file":"SparqlDataset.js","sourceRoot":"","sources":["../../../src/sparql/SparqlDataset.ts"],"names":[],"mappings":"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","sourcesContent":["import type {IDataset} from '../interfaces/IDataset.js';\nimport type {SelectQuery} from '../queries/SelectQuery.js';\nimport type {AskQuery} from '../queries/AskQuery.js';\nimport type {CountQuery} from '../queries/CountQuery.js';\nimport type {CreateQuery} from '../queries/CreateQuery.js';\nimport type {UpdateQuery} from '../queries/UpdateQuery.js';\nimport type {DeleteQuery, DeleteResponse} from '../queries/DeleteQuery.js';\nimport type {\n IRSelectQuery,\n IRCountQuery,\n IRCreateMutation,\n IRUpdateMutation,\n IRUpdateWhereMutation,\n IRUpsertMutation,\n SelectResult,\n CreateResult,\n UpdateResult,\n} from '../queries/IntermediateRepresentation.js';\nimport type {SparqlQueryResults} from './resultMapping.js';\nimport {\n selectToSparql,\n askToSparql,\n countToSparql,\n createToSparql,\n updateToSparql,\n upsertToSparql,\n updateWhereToSparql,\n deleteToSparql,\n deleteAllToSparql,\n deleteWhereToSparql,\n} from './irToAlgebra.js';\nimport {\n mapSparqlSelectResult,\n mapSparqlAskResult,\n mapSparqlCountResult,\n mapSparqlCreateResult,\n mapSparqlUpdateResult,\n isSparqlSelectResults,\n} from './resultMapping.js';\nimport {generateEntityUri, type SparqlOptions} from './sparqlUtils.js';\nimport {lower} from '../queries/lower.js';\nimport {\n assertOwnedLinkTypes,\n collectOwnedLinks,\n ownedLinkTypesFromResults,\n ownedLinkTypesQuery,\n} from './ownedLinkCheck.js';\n\n/**\n * Abstract base class for SPARQL-backed datasets.\n *\n * Handles the full pipeline: IR query → SPARQL string → execute → map results.\n * Subclasses only need to implement the two transport methods:\n * - `executeSparqlSelect` — send a SPARQL SELECT and return JSON results\n * - `executeSparqlUpdate` — send a SPARQL UPDATE (INSERT DATA / DELETE..INSERT / etc.)\n *\n * Example subclass (Fuseki):\n * ```ts\n * class FusekiDataset extends SparqlDataset {\n * constructor(baseUrl: string, dataset: string) {\n * super({ dataRoot: 'http://data.example.org' });\n * this.queryEndpoint = `${baseUrl}/${dataset}/sparql`;\n * this.updateEndpoint = `${baseUrl}/${dataset}/update`;\n * }\n * protected async executeSparqlSelect(sparql: string) {\n * const res = await fetch(this.queryEndpoint, { ... });\n * return res.json();\n * }\n * protected async executeSparqlUpdate(sparql: string) {\n * await fetch(this.updateEndpoint, { ... });\n * }\n * }\n * ```\n */\nexport abstract class SparqlDataset implements IDataset {\n protected options?: SparqlOptions;\n\n constructor(options?: SparqlOptions) {\n this.options = options;\n }\n\n /**\n * Send a SPARQL SELECT/ASK/CONSTRUCT query and return the parsed\n * SPARQL JSON Results (application/sparql-results+json).\n *\n * The return type is the union of both response shapes a query endpoint can\n * produce: a SELECT result set (`results.bindings`) or an ASK answer\n * (`boolean`). A subclass may still declare the narrower `SparqlJsonResults` —\n * a return type is covariant, so existing implementations stay valid.\n */\n protected abstract executeSparqlSelect(\n sparql: string,\n ): Promise<SparqlQueryResults>;\n\n /**\n * Send a SPARQL UPDATE request (INSERT DATA, DELETE/INSERT, etc.).\n * No return value — the update is fire-and-forget at the SPARQL level.\n */\n protected abstract executeSparqlUpdate(sparql: string): Promise<void>;\n\n /**\n * Answer a select query — **and a count, which is a select.**\n *\n * A count is not a distinct query form the way an ask is. An ask really is\n * `ASK WHERE { … }` and needs its own method; a count is\n * `SELECT (COUNT(DISTINCT ?a0) AS ?count) WHERE { … }` — the same query form, the\n * same transport (`executeSparqlSelect`), the same result-set response, differing\n * only in its projection. `countToAlgebra` says so itself: it builds its plan by\n * calling `selectToAlgebra` and swapping the projection, and yields\n * `{type: 'select'}`.\n *\n * So a count rides this channel rather than a method of its own. That is not\n * tidiness: it is what lets a count reach a store through every router that\n * already forwards a select — `LinkedStorage.selectQuery`, a backend API store —\n * with no per-kind plumbing to add in each of them, and no optional `IDataset`\n * member that a third-party store silently lacks.\n *\n * The branch is on the LOWERED IR, as `updateQuery` and `deleteQuery` branch on\n * theirs: `lower()` is the one place that knows a count from a select, and a\n * rehydrated envelope (`fromJSON`) arrives here as a builder like any other.\n *\n * A count carries only a pattern, so there is nothing to normalise away on that\n * path: no projection, no ordering, and in particular no `LIMIT`/`OFFSET`, which\n * the builder dropped before the query ever reached the IR. Errors reject; a count\n * failure is never reported as `0`.\n */\n selectQuery(query: SelectQuery): Promise<SelectResult>;\n selectQuery(query: CountQuery): Promise<number>;\n async selectQuery(\n query: SelectQuery | CountQuery,\n ): Promise<SelectResult | number> {\n // Cast once for the call rather than lowering in each branch: `lower` is\n // overloaded per query kind and cannot resolve a union, and lowering twice\n // would be the thing this method most needs not to do.\n const ir = lower(query as SelectQuery) as IRSelectQuery | IRCountQuery;\n if (ir.kind === 'count') {\n const sparql = countToSparql(ir, this.options);\n const json = await this.executeSparqlSelect(sparql);\n // `mapSparqlCountResult` is strict in the same way the guard below is: it\n // refuses an ASK response, an empty result set and a non-integer binding\n // rather than answering `0`.\n return mapSparqlCountResult(json, ir);\n }\n const sparql = selectToSparql(ir, this.options);\n const json = await this.executeSparqlSelect(sparql);\n if (!isSparqlSelectResults(json)) {\n throw new Error(\n 'Expected a SPARQL SELECT result set, got an ASK response. The endpoint ' +\n 'did not answer the SELECT query that was sent.',\n );\n }\n return mapSparqlSelectResult(json, ir);\n }\n\n /**\n * Answer an ask query — emitted as `ASK WHERE { … }`.\n *\n * An {@link AskQuery} carries only a pattern, so there is nothing to normalise\n * away here. A query with no shape lowers to `ASK { <iri> ?p ?o }` — existence\n * of the node under any type or none. Errors reject; never reported as `false`.\n */\n async askQuery(query: AskQuery): Promise<boolean> {\n const ir = lower(query);\n const sparql = askToSparql(ir, this.options);\n const json = await this.executeSparqlSelect(sparql);\n return mapSparqlAskResult(json);\n }\n\n async createQuery(query: CreateQuery): Promise<CreateResult> {\n const ir = lower(query);\n const uri = ir.data.id || generateEntityUri(ir.data.shape, this.options);\n ir.data.id = uri;\n await this.checkOwnedLinks(ir);\n const sparql = createToSparql(ir, this.options);\n await this.executeSparqlUpdate(sparql);\n return mapSparqlCreateResult(uri, ir);\n }\n\n async updateQuery(query: UpdateQuery): Promise<UpdateResult> {\n const ir = lower(query);\n await this.checkOwnedLinks(ir);\n if (ir.kind === 'update_where') {\n const sparql = updateWhereToSparql(ir, this.options);\n await this.executeSparqlUpdate(sparql);\n return {id: ''} as UpdateResult;\n }\n const sparql =\n ir.kind === 'upsert'\n ? upsertToSparql(ir, this.options)\n : updateToSparql(ir, this.options);\n await this.executeSparqlUpdate(sparql);\n return mapSparqlUpdateResult(ir);\n }\n\n /**\n * Refuse a mutation that links a node of the wrong class into a `contains` property\n * (see `ownedLinkCheck.ts`). An owner deletes what it contains, so a Person linked into\n * an ImageObject slot would be deleted with its owner. Runs before the update is sent, so\n * a refused mutation writes nothing. With no such link no query is sent; otherwise one\n * SELECT covers every linked id.\n */\n protected async checkOwnedLinks(\n ir: IRCreateMutation | IRUpdateMutation | IRUpsertMutation | IRUpdateWhereMutation,\n ): Promise<void> {\n const links = collectOwnedLinks(ir);\n if (links.length === 0) return;\n const json = await this.executeSparqlSelect(\n ownedLinkTypesQuery(links.map((link) => link.id)),\n );\n assertOwnedLinkTypes(links, ownedLinkTypesFromResults(json));\n }\n\n async deleteQuery(query: DeleteQuery): Promise<DeleteResponse> {\n const ir = lower(query);\n if (ir.kind === 'delete_all') {\n const sparql = deleteAllToSparql(ir, this.options);\n await this.executeSparqlUpdate(sparql);\n return {deleted: [], count: 0};\n }\n if (ir.kind === 'delete_where') {\n const sparql = deleteWhereToSparql(ir, this.options);\n await this.executeSparqlUpdate(sparql);\n return {deleted: [], count: 0};\n }\n const sparql = deleteToSparql(ir, this.options);\n await this.executeSparqlUpdate(sparql);\n return {\n deleted: ir.ids,\n count: ir.ids.length,\n };\n }\n\n /**\n * Execute a raw SPARQL query string directly against the dataset.\n * Defaults to SELECT; pass `'update'` for INSERT/DELETE operations.\n */\n async rawQuery(\n sparql: string,\n mode?: 'query' | 'update',\n ): Promise<SparqlQueryResults | void> {\n if (mode === 'update') {\n return this.executeSparqlUpdate(sparql);\n }\n return this.executeSparqlSelect(sparql);\n }\n}\n\n// Backwards-compatibility re-export — remove once all consumers are updated.\nexport {SparqlDataset as SparqlStore};\n"]}
@@ -1,4 +1,5 @@
1
1
  import type { IRSelectQuery, IRAskQuery, IRCountQuery, IRCreateMutation, IRUpdateMutation, IRUpsertMutation, IRDeleteMutation, IRDeleteAllMutation, IRDeleteWhereMutation, IRUpdateWhereMutation } from '../queries/IntermediateRepresentation.js';
2
+ import type { NodeReferenceValue } from '../utils/NodeReference.js';
2
3
  import type { SparqlSelectPlan, SparqlAskPlan, SparqlInsertDataPlan, SparqlDeleteInsertPlan, SparqlAlgebraNode, SparqlTriple, SparqlTerm } from './SparqlAlgebra.js';
3
4
  import { type SparqlOptions } from './sparqlUtils.js';
4
5
  /**
@@ -9,6 +10,8 @@ export declare function selectToAlgebra(query: IRSelectQuery, _options?: SparqlO
9
10
  * Converts an IRCreateMutation to a SparqlInsertDataPlan.
10
11
  */
11
12
  export declare function createToAlgebra(query: IRCreateMutation, options?: SparqlOptions): SparqlInsertDataPlan;
13
+ /** A bare `{id}` reference: a link to an existing node, not a nested create. */
14
+ export declare function isBareNodeReference(value: unknown): value is NodeReferenceValue;
12
15
  /**
13
16
  * Converts an IRUpdateMutation to a SparqlDeleteInsertPlan.
14
17
  */
@@ -23,6 +26,11 @@ export declare function updateToAlgebra(query: IRUpdateMutation, options?: Sparq
23
26
  * needs no guard.
24
27
  */
25
28
  export declare function upsertToAlgebra(query: IRUpsertMutation, options?: SparqlOptions): SparqlDeleteInsertPlan;
29
+ /** Gather contains-predicate IRIs and dependent targetClass IRIs from the registry. */
30
+ export declare function collectContainment(): {
31
+ containsPreds: string[];
32
+ dependentTypes: string[];
33
+ };
26
34
  /**
27
35
  * Build DELETE patterns + WHERE OPTIONAL blocks that cascade-delete the owned subtree
28
36
  * reachable from `rootTerm` via `contains` edges. `varPrefix` keeps generated variables
@@ -30,8 +38,10 @@ export declare function upsertToAlgebra(query: IRUpsertMutation, options?: Sparq
30
38
  *
31
39
  * `anchor`, when given, leads every block: the triple that binds a variable root, so
32
40
  * each block stays correct on its own (as a UNION branch) when no such root exists.
41
+ * `onlyTypes`, when given, restricts the blocks to those dependent types (a subject delete
42
+ * passes the types its shape can reach); by default every registered dependent type gets one.
33
43
  */
34
- export declare function buildOwnedCascade(rootTerm: SparqlTerm, varPrefix: string, anchor?: SparqlTriple): {
44
+ export declare function buildOwnedCascade(rootTerm: SparqlTerm, varPrefix: string, anchor?: SparqlTriple, keep?: SparqlTerm[], onlyTypes?: string[]): {
35
45
  deletePatterns: SparqlTriple[];
36
46
  whereOptionals: SparqlAlgebraNode[];
37
47
  };
@@ -43,12 +53,21 @@ export declare function buildOwnedCascade(rootTerm: SparqlTerm, varPrefix: strin
43
53
  * does NOT require the child shape to be `dependent`: ownership via the edge is sufficient.
44
54
  * Owned descendants *below* the old node are cascaded separately by `buildOwnedCascade`.
45
55
  *
56
+ * Callers use it only for a property that declares a value shape. A shape-less `contains`
57
+ * slot (like `sh:path`) may hold a shared IRI, so its old value is left to
58
+ * `buildOwnedCascade`, which deletes it only when it is dependent-typed.
59
+ *
46
60
  * The owning edge `<subject> <prop> ?old` is re-asserted INSIDE the WHERE group so `?old`
47
61
  * is bound there: when no old value exists the group matches nothing and the DELETE is a
48
62
  * no-op, rather than an unbound `?old` matching (and deleting) the whole graph. When
49
63
  * `oldTerm` is a concrete IRI (set-remove), the guard just confirms the link still holds.
64
+ *
65
+ * `keep` lists the bare `{id}`s the mutation writes again (a re-sent current value, the
66
+ * retained ids of an array overwrite). With a variable `oldTerm` the block is wrapped in
67
+ * `FILTER(?old NOT IN (…))`, so a kept value is not deleted; its edge is deleted and
68
+ * re-inserted, a no-op. A nested create carrying an id is never kept: it rewrites that node.
50
69
  */
51
- export declare function buildOwnedSelfDelete(subjectTerm: SparqlTerm, propertyTerm: SparqlTerm, oldTerm: SparqlTerm, varPrefix: string): {
70
+ export declare function buildOwnedSelfDelete(subjectTerm: SparqlTerm, propertyTerm: SparqlTerm, oldTerm: SparqlTerm, varPrefix: string, keep?: SparqlTerm[]): {
52
71
  deletePatterns: SparqlTriple[];
53
72
  whereOptionals: SparqlAlgebraNode[];
54
73
  };
@@ -57,7 +76,12 @@ export declare function buildOwnedSelfDelete(subjectTerm: SparqlTerm, propertyTe
57
76
  *
58
77
  * The WHERE is the type guard of every id, joined with ONE UNION of independent
59
78
  * blocks: per id its own triples, its incoming references and, for a shape that
60
- * owns something, one block per dependent type of its owned subtree.
79
+ * owns something, one block per immediately-owned child (`<s> <p> ?dN . ?dN ?dN_p ?dN_o`,
80
+ * for each of the shape's own and inherited `contains` properties with a value shape and
81
+ * a simple path) and one block per dependent type its owned subtree can reach
82
+ * (`reachableDependentTypes`; none for a shape that owns only non-dependent values). The
83
+ * bulk deletes (`deleteAllToAlgebra`, `deleteWhereToAlgebra`) add the same owned blocks,
84
+ * rooted at the subject variable (see `bulkDeleteUnion`).
61
85
  *
62
86
  * The blocks must not be chained as OPTIONALs. A chain of OPTIONALs is a join, so
63
87
  * the solutions multiply: own triples x incoming references x owned triples of each
@@ -75,14 +99,18 @@ export declare function deleteToAlgebra(query: IRDeleteMutation, _options?: Spar
75
99
  /**
76
100
  * Converts an IRDeleteAllMutation to a SparqlDeleteInsertPlan.
77
101
  *
78
- * Generates DELETE { ?a0 ?p ?o . [blank node wildcards] }
79
- * WHERE { ?a0 a <Shape> . ?a0 ?p ?o . OPTIONAL { [blank node traversals] } }
102
+ * Generates DELETE { ?a0 ?p ?o . [blank-node and owned-cleanup wildcards] }
103
+ * WHERE { ?a0 a <Shape> . { ?a0 ?p ?o } UNION [one block per blank node,
104
+ * owned child and dependent type] }
105
+ * (see `bulkDeleteUnion`). Unlike a delete by id, incoming references are not deleted.
80
106
  */
81
107
  export declare function deleteAllToAlgebra(query: IRDeleteAllMutation, _options?: SparqlOptions): SparqlDeleteInsertPlan;
82
108
  /**
83
109
  * Converts an IRDeleteWhereMutation to a SparqlDeleteInsertPlan.
84
110
  *
85
- * Like deleteAllToAlgebra but adds filter conditions from the where clause.
111
+ * Like deleteAllToAlgebra but adds filter conditions from the where clause. The cleanup
112
+ * UNION (blank nodes, owned children, dependent cascade) is joined with the filtered match,
113
+ * so only the matched subjects' children are deleted.
86
114
  */
87
115
  export declare function deleteWhereToAlgebra(query: IRDeleteWhereMutation, _options?: SparqlOptions): SparqlDeleteInsertPlan;
88
116
  /**