@lde/search 0.26.0 → 0.27.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.
@@ -1 +1 @@
1
- {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAkBL,KAAK,eAAe,EAEpB,KAAK,QAAQ,EAEb,KAAK,YAAY,EACjB,KAAK,UAAU,EAEhB,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,CAAC;AAE5D;;;;;;;;;;GAUG;AACH,MAAM,WAAW,iBAAkB,SAAQ,OAAO,CAChD,QAAQ,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAC1C;IACC,4CAA4C;IAC5C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,UAAU,EACtB,MAAM,CAAC,EAAE,YAAY,EACrB,OAAO,GAAE,iBAAyC,GACjD,cAAc,CAgBhB;AAmID;;;;;;;;;;;;GAYG;AACH,wBAAuB,YAAY,CACjC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EACrB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,QAAQ,EACpB,OAAO,GAAE,iBAAyC,GACjD,aAAa,CAAC,cAAc,CAAC,CAc/B"}
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAkBL,KAAK,eAAe,EAEpB,KAAK,QAAQ,EAEb,KAAK,YAAY,EACjB,KAAK,UAAU,EAEhB,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpD,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,CAAC;AAE5D;;;;;;;;;;GAUG;AACH,MAAM,WAAW,iBAAkB,SAAQ,OAAO,CAChD,QAAQ,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAC1C;IACC,4CAA4C;IAC5C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,UAAU,EACtB,MAAM,CAAC,EAAE,YAAY,EACrB,OAAO,GAAE,iBAAyC,GACjD,cAAc,CAgBhB;AAwKD;;;;;;;;;;;;GAYG;AACH,wBAAuB,YAAY,CACjC,KAAK,EAAE,QAAQ,CAAC,IAAI,CAAC,EACrB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,QAAQ,EACpB,OAAO,GAAE,iBAAyC,GACjD,aAAa,CAAC,cAAc,CAAC,CAc/B"}
package/dist/project.js CHANGED
@@ -42,8 +42,20 @@ export function projectDocument(node, searchType, schema, context = NO_PROJECTIO
42
42
  * {@link ReferenceStrategy.local local} lookup projects its endpoint through.
43
43
  * The second matters more than it looks – a Root Type’s reading-device fields
44
44
  * exist for its OWN derives, and nothing in a referring document wants them.
45
+ *
46
+ * A Reference Type entry’s `id` is pruned here too. It is a reading device of
47
+ * the same kind: the node IRI a derive on the referrer may read, and never a
48
+ * document key – an entry is kept in no collection, a weld names the flat
49
+ * identity companion beside it, and an entry’s own key answers no query (ADR
50
+ * 24). Left in, it would make an entry’s shape depend on whether a publisher
51
+ * minted an IRI for a relationship, and be a field the collection never
52
+ * declared. A Root Type keeps its `id` wherever it nests: that is the key its
53
+ * own collection files it under, and a `local` lookup resolves against it.
45
54
  */
46
55
  function pruneInternalFields(document, searchType, schema) {
56
+ if (searchType.class === undefined) {
57
+ delete document.id;
58
+ }
47
59
  for (const field of searchType.fields) {
48
60
  if (isInternalField(field)) {
49
61
  delete document[field.name];
@@ -62,6 +74,11 @@ function pruneInternalFields(document, searchType, schema) {
62
74
  for (const referent of Array.isArray(nested) ? nested : [nested]) {
63
75
  pruneInternalFields(referent, nestedType, schema);
64
76
  }
77
+ // Two entries that differed only in their node IRI are one entry now that
78
+ // it is gone – two edge nodes stating the same fact.
79
+ if (nestedType.class === undefined && Array.isArray(nested)) {
80
+ document[field.name] = dedupeBy(nested, (referent) => JSON.stringify(referent));
81
+ }
65
82
  }
66
83
  }
67
84
  /** Apply every field of `searchType` to a fresh document, without pruning – the
@@ -70,13 +87,26 @@ function pruneInternalFields(document, searchType, schema) {
70
87
  * once the whole nested structure is projected, so a derive at any depth can
71
88
  * still read an internal field before it is removed. */
72
89
  function projectFields(node, searchType, schema, context, nested = false) {
73
- const id = documentIdOf(node, searchType);
74
- const document = id === undefined ? {} : { id };
90
+ const document = emptyDocument(documentIdOf(node, searchType), searchType);
75
91
  for (const field of searchType.fields) {
76
92
  applyField(document, node, field, searchType, schema, context, nested);
77
93
  }
78
94
  return document;
79
95
  }
96
+ /** A document holding only its `id`, if it has one. A Reference Type entry’s
97
+ * IRI is a reading device, so it is not enumerable there: a derive reads
98
+ * `entry.id`, but `Object.keys`, a spread and `JSON.stringify` see only the
99
+ * entry’s fields ({@link documentIdOf}). */
100
+ function emptyDocument(id, searchType) {
101
+ if (id === undefined || searchType.class !== undefined) {
102
+ return id === undefined ? {} : { id };
103
+ }
104
+ return Object.defineProperty({}, 'id', {
105
+ value: id,
106
+ configurable: true,
107
+ writable: true,
108
+ });
109
+ }
80
110
  /**
81
111
  * The document key a framed node carries, if any – an {@link isAbsoluteIri
82
112
  * absolute IRI}, the same rule {@link iriString} applies to a referent, so a
@@ -101,20 +131,21 @@ function documentKey(node) {
101
131
  * The key is assigned before any `derive` runs, so a derive sees the key and
102
132
  * never the node IRI; a deployment that wants the node IRI declares a plain
103
133
  * `idOnly` reference over the same path.
134
+ *
135
+ * A Reference Type has no key, so its entry gets the node IRI itself – as a
136
+ * reading device only. A Reference Type is nested in its referrer and kept in
137
+ * no collection, so nothing downstream resolves an entry by key, and
138
+ * {@link pruneInternalFields} removes the `id` again before the document
139
+ * leaves the projection, exactly as it removes a no-role helper field. Until
140
+ * then it is not enumerable, so no step that walks an entry’s keys mistakes it
141
+ * for a field. What stays is the window in between: a `derive` on the referrer
142
+ * reads the entry’s IRI where that IRI is the data – a IIIF manifest among a
143
+ * work’s media, whose URL is its `@id` and stated nowhere else – and surfaces
144
+ * it under a field of its own. The entry still needs a field of its own (the
145
+ * manifest’s `encodingFormat`) to be a referent at all; an `@id` alone is not
146
+ * one.
104
147
  */
105
148
  function documentIdOf(node, searchType) {
106
- // A Reference Type is nested inside its referrer and keyed in no collection
107
- // of its own, so its entries carry no `id` – whether or not the graph happened
108
- // to name the node. Nesting carries a referent's FIELDS, not a document key
109
- // (ADR 24), and nothing reads one here: the collection declares an `id` for a
110
- // locally-nested Root Type alone, a weld names the flat identity companion
111
- // beside the entry, and an entry's own key answers no query. Emitting it made
112
- // an entry's shape depend on whether a publisher minted an IRI for a
113
- // relationship – arbitrary to a consumer, and a field the collection never
114
- // declared.
115
- if (searchType.class === undefined) {
116
- return undefined;
117
- }
118
149
  const nodeIri = documentKey(node);
119
150
  if (nodeIri === undefined || searchType.key === undefined) {
120
151
  return nodeIri;
@@ -476,10 +507,10 @@ function applyProjectionValue(document, field, from, context, schema) {
476
507
  * {@link ProjectedNode} for a single reference, an array for an `array` one.
477
508
  * A referent needs **no identity**: nesting carries its fields, not a document
478
509
  * key, so a blank-node referent – whose `@id` JSON-LD 1.1 framing prunes when its
479
- * label occurs once – nests exactly like a named one. Neither carries an `id`:
480
- * a Reference Type is kept in no collection, so nothing resolves an entry by
481
- * key, and an entry's shape no longer depends on whether a publisher minted an
482
- * IRI for the relationship ({@link documentIdOf}).
510
+ * label occurs once – nests exactly like a named one. Neither leaves the
511
+ * projection with an `id`: a Reference Type is kept in no collection, so
512
+ * nothing resolves an entry by key. A named referent carries its IRI as a
513
+ * non-enumerable `id` only while derives run ({@link documentIdOf}).
483
514
  * The referent is projected in full – internal fields included – so the
484
515
  * declaring type’s (or the reference type’s own) derives can read them;
485
516
  * {@link pruneInternalFields} then removes the internal fields from a *surfaced*
@@ -636,8 +667,12 @@ function applyNestedReferents(document, values, field, nestedType, schema, conte
636
667
  // or a keyed target re-keying two referent IRIs to the same document key. The
637
668
  // pre-fan-out shape deduped, because those values met inside one entry and
638
669
  // `applyFacet` deduped them there; split across entries they would reach the
639
- // index and the API as byte-identical duplicates instead.
640
- const distinct = dedupeBy(referents, (referent) => JSON.stringify(referent));
670
+ // index and the API as byte-identical duplicates instead. The `id` is part
671
+ // of the key even where it is not enumerable: two named referents with the
672
+ // same fields are two IRIs a derive on the referrer may want, so they stay
673
+ // apart until {@link pruneInternalFields} drops a Reference Type entry’s `id`
674
+ // and dedupes what is left.
675
+ const distinct = dedupeBy(referents, (referent) => JSON.stringify([referent.id, referent]));
641
676
  if (distinct.length === 0) {
642
677
  return distinct;
643
678
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lde/search",
3
- "version": "0.26.0",
3
+ "version": "0.27.1",
4
4
  "description": "Engine- and domain-agnostic core for RDF-backed search: a unified declarative field model (SearchField/SearchType/SearchSchema), a neutral query IR, the SearchEngine port with logical result types, and a streaming CONSTRUCT-to-document projection. Bakes in no engine, protocol, or domain.",
5
5
  "repository": {
6
6
  "url": "git+https://github.com/ldelements/lde.git",
@@ -52,7 +52,7 @@
52
52
  "devDependencies": {
53
53
  "@tpluscode/rdf-ns-builders": "^5.0.0",
54
54
  "@types/jsonld": "^1.5.15",
55
- "n3": "^2.4.0"
55
+ "n3": "^2.7.12"
56
56
  },
57
57
  "peerDependencies": {
58
58
  "vitest": "^4.0.0"