@_linked/core 2.2.0-next.20260320002642 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/CHANGELOG.md +239 -21
  2. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,43 @@
2
2
 
3
3
  ## 2.2.0
4
4
 
5
+ ### Patch Changes
6
+
7
+ - [#34](https://github.com/Semantu/linked/pull/34) [`e2ae4a2`](https://github.com/Semantu/linked/commit/e2ae4a28e5be28716e1634ca81d9c379a291cbc6) Thanks [@flyon](https://github.com/flyon)! - ### SHACL property path support
8
+
9
+ Property decorators now accept full SPARQL property path syntax:
10
+
11
+ ```ts
12
+ @literalProperty({path: 'foaf:knows/foaf:name'}) // sequence
13
+ @literalProperty({path: '<http://ex.org/a>|<http://ex.org/b>'}) // alternative
14
+ @literalProperty({path: '^foaf:knows'}) // inverse
15
+ @literalProperty({path: 'foaf:knows*'}) // zeroOrMore
16
+ ```
17
+
18
+ New exports from `src/paths/`:
19
+
20
+ - `PathExpr`, `PathRef` — AST types for property paths
21
+ - `parsePropertyPath(input): PathExpr` — parser for SPARQL property path strings
22
+ - `normalizePropertyPath(input): PathExpr` — normalizes any input form to canonical AST
23
+ - `pathExprToSparql(expr): string` — renders PathExpr to SPARQL syntax
24
+ - `serializePathToSHACL(expr): SHACLPathResult` — serializes to SHACL RDF triples
25
+
26
+ `PropertyShape.path` is now typed as `PathExpr` (was opaque). Complex paths flow through the full IR pipeline and emit correct SPARQL property path syntax in generated queries.
27
+
28
+ ### Strict prefix resolution in query API
29
+
30
+ `QueryBuilder.for()` and `.forAll()` now throw on unregistered prefixes instead of silently passing through. New export:
31
+
32
+ - `resolveUriOrThrow(str): string` — strict prefix resolution (throws on unknown prefix)
33
+
34
+ ### SHACL constraint field fixes
35
+
36
+ - `hasValue` and `in` config fields now correctly handle literal values (`string`, `number`, `boolean`) — previously all values were wrapped as IRI nodes
37
+ - `lessThan` and `lessThanOrEquals` config fields are now wired into `createPropertyShape` and exposed via `getResult()`
38
+ - New `PropertyShapeResult` interface provides typed access to `getResult()` output
39
+
40
+ ## 2.1.0
41
+
5
42
  ### Minor Changes
6
43
 
7
44
  - [#31](https://github.com/Semantu/linked/pull/31) [`eb88865`](https://github.com/Semantu/linked/commit/eb8886564f2c9663805c4308a834ca615f9a1dab) Thanks [@flyon](https://github.com/flyon)! - Properties in `select()` and `update()` now support expressions — you can compute values dynamically instead of just reading or writing raw fields.
@@ -51,40 +88,221 @@
51
88
 
52
89
  See the [README](./README.md#computed-expressions) for the full method reference and more examples.
53
90
 
91
+ ## 2.0.1
92
+
54
93
  ### Patch Changes
55
94
 
56
- - [#34](https://github.com/Semantu/linked/pull/34) [`e2ae4a2`](https://github.com/Semantu/linked/commit/e2ae4a28e5be28716e1634ca81d9c379a291cbc6) Thanks [@flyon](https://github.com/flyon)! - ### SHACL property path support
95
+ - [#27](https://github.com/Semantu/linked/pull/27) [`d3c1e91`](https://github.com/Semantu/linked/commit/d3c1e918b2a63240ddbf3cb550ec43fa1e019c35) Thanks [@flyon](https://github.com/flyon)! - Add MINUS support on QueryBuilder with multiple call styles:
57
96
 
58
- Property decorators now accept full SPARQL property path syntax:
97
+ - `.minus(Shape)` exclude by shape type
98
+ - `.minus(p => p.prop.equals(val))` — exclude by condition
99
+ - `.minus(p => p.prop)` — exclude by property existence
100
+ - `.minus(p => [p.prop1, p.nested.prop2])` — exclude by multi-property existence with nested path support
59
101
 
60
- ```ts
61
- @literalProperty({path: 'foaf:knows/foaf:name'}) // sequence
62
- @literalProperty({path: '<http://ex.org/a>|<http://ex.org/b>'}) // alternative
63
- @literalProperty({path: '^foaf:knows'}) // inverse
64
- @literalProperty({path: 'foaf:knows*'}) // zeroOrMore
102
+ Add bulk delete operations:
103
+
104
+ - `Shape.deleteAll()` / `DeleteBuilder.from(Shape).all()` — delete all instances with schema-aware blank node cleanup
105
+ - `Shape.deleteWhere(fn)` / `DeleteBuilder.from(Shape).where(fn)` — conditional delete
106
+
107
+ Add conditional update operations:
108
+
109
+ - `.update(data).where(fn)` — update matching instances
110
+ - `.update(data).forAll()` — update all instances
111
+
112
+ API cleanup:
113
+
114
+ - Deprecate `sortBy()` in favor of `orderBy()`
115
+ - Remove `DeleteBuilder.for()` — use `DeleteBuilder.from(shape, ids)` instead
116
+ - Require `data` parameter in `Shape.update(data)`
117
+
118
+ ## 2.0.0
119
+
120
+ ### Major Changes
121
+
122
+ - [#23](https://github.com/Semantu/linked/pull/23) [`d2d1eca`](https://github.com/Semantu/linked/commit/d2d1eca3517af11f39348dc83ba5e60703ef86d2) Thanks [@flyon](https://github.com/flyon)! - ## Breaking Changes
123
+
124
+ ### `Shape.select()` and `Shape.update()` no longer accept an ID as the first argument
125
+
126
+ Use `.for(id)` to target a specific entity instead.
127
+
128
+ **Select:**
129
+
130
+ ```typescript
131
+ // Before
132
+ const result = await Person.select({ id: "..." }, (p) => p.name);
133
+
134
+ // After
135
+ const result = await Person.select((p) => p.name).for({ id: "..." });
65
136
  ```
66
137
 
67
- New exports from `src/paths/`:
138
+ `.for(id)` unwraps the result type from array to single object, matching the old single-subject overload behavior.
68
139
 
69
- - `PathExpr`, `PathRef` — AST types for property paths
70
- - `parsePropertyPath(input): PathExpr` — parser for SPARQL property path strings
71
- - `normalizePropertyPath(input): PathExpr` — normalizes any input form to canonical AST
72
- - `pathExprToSparql(expr): string` — renders PathExpr to SPARQL syntax
73
- - `serializePathToSHACL(expr): SHACLPathResult` — serializes to SHACL RDF triples
140
+ **Update:**
74
141
 
75
- `PropertyShape.path` is now typed as `PathExpr` (was opaque). Complex paths flow through the full IR pipeline and emit correct SPARQL property path syntax in generated queries.
142
+ ```typescript
143
+ // Before
144
+ const result = await Person.update({ id: "..." }, { name: "Alice" });
76
145
 
77
- ### Strict prefix resolution in query API
146
+ // After
147
+ const result = await Person.update({ name: "Alice" }).for({ id: "..." });
148
+ ```
78
149
 
79
- `QueryBuilder.for()` and `.forAll()` now throw on unregistered prefixes instead of silently passing through. New export:
150
+ `Shape.selectAll(id)` also no longer accepts an id use `Person.selectAll().for(id)`.
80
151
 
81
- - `resolveUriOrThrow(str): string` strict prefix resolution (throws on unknown prefix)
152
+ ### `ShapeType` renamed to `ShapeConstructor`
82
153
 
83
- ### SHACL constraint field fixes
154
+ The type alias for concrete Shape subclass constructors has been renamed. Update any imports or references:
84
155
 
85
- - `hasValue` and `in` config fields now correctly handle literal values (`string`, `number`, `boolean`) — previously all values were wrapped as IRI nodes
86
- - `lessThan` and `lessThanOrEquals` config fields are now wired into `createPropertyShape` and exposed via `getResult()`
87
- - New `PropertyShapeResult` interface provides typed access to `getResult()` output
156
+ ```typescript
157
+ // Before
158
+ import type { ShapeType } from "@_linked/core/shapes/Shape";
159
+
160
+ // After
161
+ import type { ShapeConstructor } from "@_linked/core/shapes/Shape";
162
+ ```
163
+
164
+ ### `QueryString`, `QueryNumber`, `QueryBoolean`, `QueryDate` classes removed
165
+
166
+ These have been consolidated into a single generic `QueryPrimitive<T>` class. If you were using `instanceof` checks against these classes, use `instanceof QueryPrimitive` instead and check the value's type.
167
+
168
+ ### Internal IR types removed
169
+
170
+ The following types and functions have been removed from `SelectQuery`. These were internal pipeline types — if you were using them for custom store integrations, the replacement is `FieldSetEntry[]` (available from `FieldSet`):
171
+
172
+ - Types: `SelectPath`, `QueryPath`, `CustomQueryObject`, `SubQueryPaths`, `ComponentQueryPath`
173
+ - Functions: `fieldSetToSelectPath()`, `entryToQueryPath()`
174
+ - Methods: `QueryBuilder.getQueryPaths()`, `BoundComponent.getComponentQueryPaths()`
175
+ - `RawSelectInput.select` field renamed to `RawSelectInput.entries` (type changed from `SelectPath` to `FieldSetEntry[]`)
176
+
177
+ ### `getPackageShape()` return type is now nullable
178
+
179
+ Returns `ShapeConstructor | undefined` instead of `typeof Shape`. Code that didn't null-check the return value will now get TypeScript errors.
180
+
181
+ ## New Features
182
+
183
+ ### `.for(id)` and `.forAll(ids)` chaining
184
+
185
+ Consistent API for targeting entities across select and update operations:
186
+
187
+ ```typescript
188
+ // Single entity (result is unwrapped, not an array)
189
+ await Person.select((p) => p.name).for({ id: "..." });
190
+ await Person.select((p) => p.name).for("https://...");
191
+
192
+ // Multiple specific entities
193
+ await QueryBuilder.from(Person)
194
+ .select((p) => p.name)
195
+ .forAll([{ id: "..." }, { id: "..." }]);
196
+
197
+ // All instances (default — no .for() needed)
198
+ await Person.select((p) => p.name);
199
+ ```
200
+
201
+ ### Dynamic Query Building with `QueryBuilder` and `FieldSet`
202
+
203
+ Build queries programmatically at runtime — for CMS dashboards, API endpoints, configurable reports. See the [Dynamic Query Building](./README.md#dynamic-query-building) section in the README for full documentation and examples.
204
+
205
+ Key capabilities:
206
+
207
+ - `QueryBuilder.from(Person)` or `QueryBuilder.from('https://schema.org/Person')` — fluent, chainable, immutable query construction
208
+ - `FieldSet.for(Person, ['name', 'knows'])` — composable field selections with `.add()`, `.remove()`, `.pick()`, `FieldSet.merge()`
209
+ - `FieldSet.all(Person, {depth: 2})` — select all decorated properties with optional depth
210
+ - JSON serialization: `query.toJSON()` / `QueryBuilder.fromJSON(json)` and `fieldSet.toJSON()` / `FieldSet.fromJSON(json)`
211
+ - All builders are `PromiseLike` — `await` them directly or call `.build()` to inspect the IR
212
+
213
+ ### Mutation Builders
214
+
215
+ `CreateBuilder`, `UpdateBuilder`, and `DeleteBuilder` provide the programmatic equivalent of `Person.create()`, `Person.update()`, and `Person.delete()`, accepting Shape classes or shape IRI strings. See the [Mutation Builders](./README.md#mutation-builders) section in the README.
216
+
217
+ ### `PropertyPath` exported
218
+
219
+ The `PropertyPath` value object is now a public export — a type-safe representation of a sequence of property traversals through a shape graph.
220
+
221
+ ```typescript
222
+ import { PropertyPath, walkPropertyPath } from "@_linked/core";
223
+ ```
224
+
225
+ ### `ShapeConstructor<S>` type
226
+
227
+ New concrete constructor type for Shape subclasses. Eliminates ~30 `as any` casts across the codebase and provides better type safety at runtime boundaries (builder `.from()` methods, Shape static methods).
228
+
229
+ ## 1.3.0
230
+
231
+ ### Minor Changes
232
+
233
+ - [#20](https://github.com/Semantu/linked/pull/20) [`33e9fb0`](https://github.com/Semantu/linked/commit/33e9fb0205343eca8c84723cbabc3f3342e40be5) Thanks [@flyon](https://github.com/flyon)! - **Breaking:** `QueryParser` has been removed. If you imported `QueryParser` directly, replace with `getQueryDispatch()` from `@_linked/core/queries/queryDispatch`. The Shape DSL (`Shape.select()`, `.create()`, `.update()`, `.delete()`) and `SelectQuery.exec()` are unchanged.
234
+
235
+ **New:** `getQueryDispatch()` and `setQueryDispatch()` are now exported, allowing custom query dispatch implementations (e.g. for testing or alternative storage backends) without subclassing `LinkedStorage`.
236
+
237
+ ## 1.2.1
238
+
239
+ ### Patch Changes
240
+
241
+ - [#17](https://github.com/Semantu/linked/pull/17) [`0654780`](https://github.com/Semantu/linked/commit/06547807a7bae56e992eba73263f83e092b7788b) Thanks [@flyon](https://github.com/flyon)! - Preserve nested array sub-select branches in canonical IR so `build()` emits complete traversals, projection fields, and `resultMap` entries for nested selections.
242
+
243
+ This fixes cases where nested branches present in `toRawInput().select` were dropped during desugar/lowering (for example nested `friends.select([name, hobby])` branches under another sub-select).
244
+
245
+ Also adds regression coverage for desugar preservation, IR lowering completeness, and updated SPARQL golden output for nested query fixtures.
246
+
247
+ ## 1.2.0
248
+
249
+ ### Minor Changes
250
+
251
+ - [#9](https://github.com/Semantu/linked/pull/9) [`381067b`](https://github.com/Semantu/linked/commit/381067b0fbc25f4a0446c5f8cc0eec57ddded466) Thanks [@flyon](https://github.com/flyon)! - Replaced internal query representation with a canonical backend-agnostic IR AST. `SelectQuery`, `CreateQuery`, `UpdateQuery`, and `DeleteQuery` are now typed IR objects with `kind` discriminators, compact shape/property ID references, and expression trees — replacing the previous ad-hoc nested arrays. The public Shape DSL is unchanged; what changed is what `IQuadStore` implementations receive. Store result types (`ResultRow`, `SelectResult`, `CreateResult`, `UpdateResult`) are now exported. All factories expose `build()` as the primary method. See `documentation/intermediate-representation.md` for the full IR reference and migration guidance.
252
+
253
+ - [#14](https://github.com/Semantu/linked/pull/14) [`b65e156`](https://github.com/Semantu/linked/commit/b65e15688ac173478e58e1dbb9f26dbaf5fc5a37) Thanks [@flyon](https://github.com/flyon)! - Add SPARQL conversion layer — compiles Linked IR queries into executable SPARQL and maps results back to typed DSL objects.
254
+
255
+ **New exports from `@_linked/core/sparql`:**
256
+
257
+ - **`SparqlStore`** — abstract base class for SPARQL-backed stores. Extend it and implement two methods to connect any SPARQL 1.1 endpoint:
258
+
259
+ ```ts
260
+ import { SparqlStore } from "@_linked/core/sparql";
261
+
262
+ class MyStore extends SparqlStore {
263
+ protected async executeSparqlSelect(
264
+ sparql: string
265
+ ): Promise<SparqlJsonResults> {
266
+ /* ... */
267
+ }
268
+ protected async executeSparqlUpdate(sparql: string): Promise<void> {
269
+ /* ... */
270
+ }
271
+ }
272
+ ```
273
+
274
+ - **IR → SPARQL string** convenience functions (full pipeline in one call):
275
+
276
+ - `selectToSparql(query, options?)` — SelectQuery → SPARQL string
277
+ - `createToSparql(query, options?)` — CreateQuery → SPARQL string
278
+ - `updateToSparql(query, options?)` — UpdateQuery → SPARQL string
279
+ - `deleteToSparql(query, options?)` — DeleteQuery → SPARQL string
280
+
281
+ - **IR → SPARQL algebra** (for stores that want to inspect/optimize the algebra before serialization):
282
+
283
+ - `selectToAlgebra(query, options?)` — returns `SparqlSelectPlan`
284
+ - `createToAlgebra(query, options?)` — returns `SparqlInsertDataPlan`
285
+ - `updateToAlgebra(query, options?)` — returns `SparqlDeleteInsertPlan`
286
+ - `deleteToAlgebra(query, options?)` — returns `SparqlDeleteInsertPlan`
287
+
288
+ - **Algebra → SPARQL string** serialization:
289
+
290
+ - `selectPlanToSparql(plan, options?)`, `insertDataPlanToSparql(plan, options?)`, `deleteInsertPlanToSparql(plan, options?)`, `deleteWherePlanToSparql(plan, options?)`
291
+ - `serializeAlgebraNode(node)`, `serializeExpression(expr)`, `serializeTerm(term)`
292
+
293
+ - **Result mapping** (SPARQL JSON results → typed DSL objects):
294
+
295
+ - `mapSparqlSelectResult(json, query)` — handles flat/nested/aggregated results with XSD type coercion
296
+ - `mapSparqlCreateResult(uri, query)` — echoes created fields with generated URI
297
+ - `mapSparqlUpdateResult(query)` — echoes updated fields
298
+
299
+ - **All algebra types** re-exported: `SparqlTerm`, `SparqlTriple`, `SparqlAlgebraNode`, `SparqlExpression`, `SparqlSelectPlan`, `SparqlInsertDataPlan`, `SparqlDeleteInsertPlan`, `SparqlDeleteWherePlan`, `SparqlPlan`, `SparqlOptions`, etc.
300
+
301
+ **Bug fixes included:**
302
+
303
+ - Fixed `isNodeReference()` in MutationQuery.ts — nested creates with predefined IDs (e.g., `{id: '...', name: 'Bestie'}`) now correctly insert entity data instead of only creating the link.
304
+
305
+ See [SPARQL Algebra Layer docs](./documentation/sparql-algebra.md) for the full type reference, conversion rules, and store implementation guide.
88
306
 
89
307
  ## 1.1.0
90
308
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@_linked/core",
3
- "version": "2.2.0-next.20260320002642",
3
+ "version": "2.2.0",
4
4
  "license": "MIT",
5
5
  "description": "Linked.js core query and SHACL shape DSL (copy-then-prune baseline)",
6
6
  "repository": {