@lde/search 0.38.0 → 0.39.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":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAGzC,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAwBL,KAAK,eAAe,EAIpB,KAAK,QAAQ,EAEb,KAAK,YAAY,EACjB,KAAK,UAAU,EAGhB,MAAM,aAAa,CAAC;AAGrB;;;;;;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;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,IAAI,CAAC;CAC/D;AAED,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B;0CACsC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,UAAU,EACtB,MAAM,CAAC,EAAE,YAAY,EACrB,OAAO,GAAE,iBAAyC,GACjD,cAAc,CAkBhB;AAgMD;;;;;;;;;;;;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;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,aAAa,EACvB,UAAU,EAAE,UAAU,EACtB,MAAM,CAAC,EAAE,YAAY,GACpB,SAAS,MAAM,EAAE,CAmCnB"}
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAGzC,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAsBL,KAAK,eAAe,EAIpB,KAAK,QAAQ,EAEb,KAAK,YAAY,EACjB,KAAK,UAAU,EAGhB,MAAM,aAAa,CAAC;AAUrB;;;;;;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;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,IAAI,CAAC;CAC/D;AAED,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B;0CACsC;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAKD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,EAChB,UAAU,EAAE,UAAU,EACtB,MAAM,CAAC,EAAE,YAAY,EACrB,OAAO,GAAE,iBAAyC,GACjD,cAAc,CAkBhB;AAgMD;;;;;;;;;;;;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;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,aAAa,EACvB,UAAU,EAAE,UAAU,EACtB,MAAM,CAAC,EAAE,YAAY,GACpB,SAAS,MAAM,EAAE,CAmCnB"}
package/dist/project.js CHANGED
@@ -1,8 +1,9 @@
1
1
  import { fold } from '@lde/text-normalization';
2
2
  import { matchLanguage } from './language.js';
3
3
  import { buildSubjectIndex, frameSubjects, } from './frame-by-type.js';
4
- import { companionNames, assertTypeInSchema, displayFieldName, displayLangOf, documentKeyOf, endpointFieldOf, fieldNamed, identityFieldOf, inheritedFacetPolicies, inlineFramingDepth, irAlias, isAbsoluteIri, isInternalField, isInlineReference, isoToUnixSeconds, localLookupTargetsOf, physicalFields, referencedTargetsOf, referenceTypeNamed, storableUnixSeconds, storedTargetOf, TARGET_FIELD, } from './schema.js';
4
+ import { companionNames, assertTypeInSchema, displayFieldName, displayLangOf, documentKeyOf, endpointFieldOf, fieldNamed, identityFieldOf, inheritedFacetPolicies, inlineFramingDepth, irAlias, isAbsoluteIri, isInternalField, isInlineReference, localLookupTargetsOf, physicalFields, referencedTargetsOf, referenceTypeNamed, storedTargetOf, TARGET_FIELD, } from './schema.js';
5
5
  import { periodCovering, periodOf } from './period.js';
6
+ import { describeValue, isReadKind, readValue, UNREADABLE, } from './read-value.js';
6
7
  /** The context a projection runs with when its caller supplied none. */
7
8
  const NO_PROJECTION_CONTEXT = {};
8
9
  /**
@@ -257,12 +258,7 @@ function hasValue(document, field, searchType) {
257
258
  fieldNamed(searchType, key) === undefined)));
258
259
  }
259
260
  function applyField(document, node, field, searchType, schema, context, sink, nested) {
260
- // A blank value states nothing, so there is nothing to correct at the source.
261
- const unread = (value) => {
262
- if (value.trim() !== '') {
263
- sink(field.name, value);
264
- }
265
- };
261
+ const unread = (value) => sink(field.name, value);
266
262
  // The three value sources, mutually exclusive by declaration
267
263
  // (`validateSearchType`): a projection value, a computed value, a graph path.
268
264
  if ((field.kind === 'keyword' || field.kind === 'reference') &&
@@ -271,30 +267,24 @@ function applyField(document, node, field, searchType, schema, context, sink, ne
271
267
  }
272
268
  if (field.derive !== undefined) {
273
269
  const derived = field.derive(document, context);
274
- // A derived `reference` is held to the same rule as a read one: what it
275
- // stores is a selection key, and the surface types it `IRI`. ADR 21 sends
276
- // an IRI-valued `keyword` derive here by converting it to `reference`, so
277
- // this is the busiest way a non-IRI could otherwise reach the index. A
278
- // value that survives nothing leaves the field absent, exactly as a derive
279
- // returning `undefined` does.
280
- if (field.kind === 'date') {
281
- return applyDerivedDate(document, field, derived, unread);
282
- }
283
270
  if (field.kind === 'period') {
284
271
  return setPeriod(document, field, derivedStrings(derived, unread), unread);
285
272
  }
286
- const value = field.kind === 'reference' && derived !== undefined
287
- ? derivedIris(derived, unread)
288
- : derived;
289
- // NaN is not a value any kind stores, and a derive is the only route that
290
- // can produce one (`setNumber` guards the read route). It serializes as
291
- // `null`, which an engine rejects for a numeric field – so drop it, leaving
292
- // the field absent as an unparseable string already does.
293
- if (value !== undefined && !Number.isNaN(value)) {
294
- document[field.name] = value;
273
+ // A derived value is held to the same rule as a read one, through the same
274
+ // reader: a derived `reference` stores a selection key the surface types
275
+ // `IRI` – ADR 21 sends an IRI-valued `keyword` derive here by converting it
276
+ // to `reference`, so this is the busiest way a non-IRI could otherwise
277
+ // reach the index – and a derived `integer` an int64.
278
+ if (isReadKind(field.kind)) {
279
+ return applyRead(document, field, field.kind, Array.isArray(derived) ? derived : [derived], unread);
280
+ }
281
+ // A `keyword` or `text` field reads any value but NaN, which serializes as
282
+ // `null` – stored by an engine as no value, or refused with the document.
283
+ if (Number.isNaN(derived)) {
284
+ unread('NaN');
295
285
  }
296
- else if (value !== undefined) {
297
- unread(String(value));
286
+ else if (derived !== undefined) {
287
+ document[field.name] = derived;
298
288
  }
299
289
  return;
300
290
  }
@@ -350,38 +340,37 @@ function applyField(document, node, field, searchType, schema, context, sink, ne
350
340
  return applyFacet(document, values, field, schema, targetsById);
351
341
  }
352
342
  case 'integer':
353
- case 'number': {
354
- const literal = firstLiteralOf(node, alias);
355
- const value = field.kind === 'integer' ? toInteger(literal) : toNumber(literal);
356
- if (literal !== undefined && Number.isNaN(value)) {
357
- unread(literal);
358
- }
359
- return setNumber(document, field.name, value);
360
- }
361
- case 'date': {
362
- const literal = firstLiteralOf(node, alias);
363
- const stored = literal === undefined ? undefined : isoToUnixSeconds(literal);
364
- if (literal !== undefined && stored === undefined) {
365
- unread(literal);
366
- }
367
- return setDate(document, field, stored);
368
- }
343
+ case 'number':
344
+ case 'date':
345
+ case 'boolean':
346
+ return applyRead(document, field, field.kind, literalsOf(node, alias), unread);
369
347
  case 'period':
370
348
  return setPeriod(document, field, literalsOf(node, alias), unread);
371
- case 'boolean': {
372
- // The xsd:boolean lexical space: true/false/1/0. Anything else – `ja`,
373
- // `yes` – states neither, so it is left out rather than stored as false.
374
- // Surrounding whitespace is collapsed, as xsd:boolean’s facet says.
375
- const literal = firstLiteralOf(node, alias)?.trim();
376
- if (literal === undefined) {
377
- return;
378
- }
379
- if (!BOOLEAN_LEXICAL_SPACE.has(literal)) {
380
- return unread(literal);
381
- }
382
- document[field.name] = literal === 'true' || literal === '1';
383
- return;
349
+ }
350
+ }
351
+ /**
352
+ * Store what {@link readValue} reads of `values` as `kind`, reporting every
353
+ * value it cannot. An `array` reference stores the list; every other kind is a
354
+ * scalar in the engine, so it stores the first value read – and an unreadable
355
+ * or blank value cannot cost the field a readable one behind it.
356
+ */
357
+ function applyRead(document, field, kind, values, unread) {
358
+ const read = values.flatMap((value) => {
359
+ const reading = readValue(kind, value);
360
+ if (reading === UNREADABLE) {
361
+ unread(describeValue(value));
362
+ return [];
384
363
  }
364
+ return reading === undefined ? [] : [reading];
365
+ });
366
+ if (kind === 'date') {
367
+ return setDate(document, field, read[0]);
368
+ }
369
+ if (kind === 'reference' && field.array === true) {
370
+ document[field.name] = read;
371
+ }
372
+ else if (read.length > 0) {
373
+ document[field.name] = read[0];
385
374
  }
386
375
  }
387
376
  /**
@@ -413,10 +402,10 @@ function applyField(document, node, field, searchType, schema, context, sink, ne
413
402
  function referenceValues(node, alias, field, schema, targetsById, unread) {
414
403
  // A literal where a referent belongs states no IRI to store. A node without
415
404
  // one – a blank node – carries no value a publisher could look up, so only
416
- // literals are reported.
405
+ // literals are reported, and a blank one states nothing to correct.
417
406
  for (const value of valuesOf(node, alias)) {
418
407
  const literal = iriString(value) === undefined ? literalString(value) : undefined;
419
- if (literal !== undefined) {
408
+ if (literal !== undefined && literal.trim() !== '') {
420
409
  unread(literal);
421
410
  }
422
411
  }
@@ -1054,9 +1043,6 @@ function literalsOf(node, key) {
1054
1043
  .map(literalString)
1055
1044
  .filter((value) => value !== undefined);
1056
1045
  }
1057
- function firstLiteralOf(node, key) {
1058
- return literalsOf(node, key)[0];
1059
- }
1060
1046
  function irisOf(node, key) {
1061
1047
  return valuesOf(node, key)
1062
1048
  .map(iriString)
@@ -1119,57 +1105,6 @@ function iriString(value) {
1119
1105
  const iri = isObject(value) ? value['@id'] : value;
1120
1106
  return typeof iri === 'string' && isAbsoluteIri(iri) ? iri : undefined;
1121
1107
  }
1122
- /** What a `derive` on a `reference` field is allowed to have produced: the
1123
- * absolute IRIs among its return value, keeping the shape the declaration
1124
- * promises (a list stays a list, a single value stays single) so `array` still
1125
- * decides the stored shape. A lone non-IRI becomes `undefined`, i.e. absent –
1126
- * the same outcome the graph path gives it. */
1127
- function derivedIris(value, unread) {
1128
- const isIri = (entry) => {
1129
- const iri = typeof entry === 'string' && isAbsoluteIri(entry);
1130
- if (!iri && entry !== undefined) {
1131
- unread(String(entry));
1132
- }
1133
- return iri;
1134
- };
1135
- if (Array.isArray(value)) {
1136
- return value.filter(isIri);
1137
- }
1138
- return isIri(value) ? value : undefined;
1139
- }
1140
- function toInteger(literal) {
1141
- return literal === undefined ? undefined : Math.trunc(Number(literal));
1142
- }
1143
- function toNumber(literal) {
1144
- return literal === undefined ? undefined : Number(literal);
1145
- }
1146
- /**
1147
- * A derived `date` goes through the same storage codec as a read one: the field
1148
- * is stored as Unix seconds, so an ISO string returned here would otherwise
1149
- * land as a string in an int64 field – and it is parsed like any literal, its
1150
- * precision included. A derive that computed seconds itself returns a number,
1151
- * which states no precision and so is stored as a date-time; the codec never
1152
- * saw it and so never range-checked it, and seconds outside the int64 a `date`
1153
- * field is stored in would be refused by the engine where an unparseable
1154
- * string merely leaves the field absent – so drop those too.
1155
- */
1156
- function applyDerivedDate(document, field, derived, unread) {
1157
- let stored;
1158
- if (typeof derived === 'string') {
1159
- stored = isoToUnixSeconds(derived);
1160
- }
1161
- else if (typeof derived === 'number') {
1162
- // Truncated, as the codec truncates a string’s fractional seconds: the
1163
- // field is an int64. `storableUnixSeconds` drops NaN.
1164
- const seconds = storableUnixSeconds(Math.trunc(derived));
1165
- stored =
1166
- seconds === undefined ? undefined : { seconds, precision: 'dateTime' };
1167
- }
1168
- if (derived !== undefined && stored === undefined) {
1169
- unread(String(derived));
1170
- }
1171
- setDate(document, field, stored);
1172
- }
1173
1108
  /** Store a `date` field’s seconds, and its precision in the companion beside
1174
1109
  * them ({@link physicalFields}), so the surface can serve the value in the
1175
1110
  * form its source gave. */
@@ -1220,21 +1155,17 @@ function setPeriod(document, field, values, unread) {
1220
1155
  }
1221
1156
  }
1222
1157
  /** A derive’s value as the strings a `period` stores: one string, or the
1223
- * strings in a list; anything else states no value. */
1158
+ * strings in a list. `null` and `undefined` state no value; anything else
1159
+ * is reported. */
1224
1160
  function derivedStrings(derived, unread) {
1225
1161
  const values = Array.isArray(derived) ? derived : [derived];
1226
1162
  return values.filter((value) => {
1227
- if (typeof value !== 'string' && value !== undefined) {
1228
- unread(String(value));
1163
+ if (typeof value !== 'string' && value !== undefined && value !== null) {
1164
+ unread(describeValue(value));
1229
1165
  }
1230
1166
  return typeof value === 'string';
1231
1167
  });
1232
1168
  }
1233
- function setNumber(document, field, value) {
1234
- if (value !== undefined && !Number.isNaN(value)) {
1235
- document[field] = value;
1236
- }
1237
- }
1238
1169
  function dedupe(values) {
1239
1170
  return [...new Set(values)];
1240
1171
  }
@@ -1275,10 +1206,3 @@ function unreadableSink(context, id) {
1275
1206
  function ignoreUnreadable() {
1276
1207
  // Nobody asked for the report.
1277
1208
  }
1278
- /** The xsd:boolean lexical space. */
1279
- const BOOLEAN_LEXICAL_SPACE = new Set([
1280
- 'true',
1281
- 'false',
1282
- '1',
1283
- '0',
1284
- ]);
@@ -0,0 +1,44 @@
1
+ import { type StoredDate } from './schema.js';
2
+ /** What reading a value that is there, but not one its kind can store, gives:
3
+ * `onbekend` for an integer, `ja` for a boolean. */
4
+ export declare const UNREADABLE: unique symbol;
5
+ /**
6
+ * One value read as a field’s kind: what the field stores, {@link UNREADABLE}
7
+ * for a value it cannot store, or `undefined` for no value at all – a missing
8
+ * or blank one, which states nothing to correct at the source.
9
+ */
10
+ type Reading<T> = T | typeof UNREADABLE | undefined;
11
+ /** The kinds that can refuse a value: the rest store any literal. */
12
+ declare const READ_KINDS: readonly ["integer", "number", "boolean", "date", "reference"];
13
+ /** One of {@link READ_KINDS}. */
14
+ export type ReadKind = (typeof READ_KINDS)[number];
15
+ /** Whether `kind` is one {@link readValue} reads. */
16
+ export declare function isReadKind(kind: string): kind is ReadKind;
17
+ /**
18
+ * Read one value – a literal from the graph, or whatever a `derive` returned –
19
+ * as `kind` stores it. Both value routes read through here, so a kind accepts
20
+ * and refuses the same values whichever route they came by.
21
+ *
22
+ * - `integer`: a finite number, or an `xsd:decimal` or `xsd:double` lexical
23
+ * form, truncated, within ±2⁵³ – beyond that a double no longer holds every
24
+ * integer, so the stored value would not be the stated one. `INF` and `NaN`
25
+ * are refused: an engine cannot store them.
26
+ * - `number`: any finite one, untruncated.
27
+ * - `boolean`: `true` or `false`, or the `xsd:boolean` lexical space
28
+ * (`true`/`false`/`1`/`0`).
29
+ * - `date`: anything the `date` codec reads ({@link isoToUnixSeconds}), or
30
+ * Unix seconds as a number.
31
+ * - `reference`: an absolute IRI.
32
+ *
33
+ * A string is read with surrounding whitespace collapsed, as XSD does.
34
+ */
35
+ export declare function readValue(kind: ReadKind, value: unknown): Reading<number | boolean | string | StoredDate>;
36
+ /**
37
+ * A value as the report shows it: `String(value)`, or its `typeof` when it has
38
+ * no string form, as an object built without a prototype has not. Never
39
+ * throws, so a derive’s odd return is reported rather than crashing the
40
+ * projection.
41
+ */
42
+ export declare function describeValue(value: unknown): string;
43
+ export {};
44
+ //# sourceMappingURL=read-value.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read-value.d.ts","sourceRoot":"","sources":["../src/read-value.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAErB;qDACqD;AACrD,eAAO,MAAM,UAAU,eAAuB,CAAC;AAE/C;;;;GAIG;AACH,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,UAAU,GAAG,SAAS,CAAC;AAEpD,qEAAqE;AACrE,QAAA,MAAM,UAAU,gEAMN,CAAC;AAEX,iCAAiC;AACjC,MAAM,MAAM,QAAQ,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;AAEnD,qDAAqD;AACrD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,QAAQ,CAEzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC,CA8CjD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAMpD"}
@@ -0,0 +1,96 @@
1
+ import { isAbsoluteIri, isoToUnixSeconds, storableUnixSeconds, } from './schema.js';
2
+ /** What reading a value that is there, but not one its kind can store, gives:
3
+ * `onbekend` for an integer, `ja` for a boolean. */
4
+ export const UNREADABLE = Symbol('unreadable');
5
+ /** The kinds that can refuse a value: the rest store any literal. */
6
+ const READ_KINDS = [
7
+ 'integer',
8
+ 'number',
9
+ 'boolean',
10
+ 'date',
11
+ 'reference',
12
+ ];
13
+ /** Whether `kind` is one {@link readValue} reads. */
14
+ export function isReadKind(kind) {
15
+ return READ_KINDS.includes(kind);
16
+ }
17
+ /**
18
+ * Read one value – a literal from the graph, or whatever a `derive` returned –
19
+ * as `kind` stores it. Both value routes read through here, so a kind accepts
20
+ * and refuses the same values whichever route they came by.
21
+ *
22
+ * - `integer`: a finite number, or an `xsd:decimal` or `xsd:double` lexical
23
+ * form, truncated, within ±2⁵³ – beyond that a double no longer holds every
24
+ * integer, so the stored value would not be the stated one. `INF` and `NaN`
25
+ * are refused: an engine cannot store them.
26
+ * - `number`: any finite one, untruncated.
27
+ * - `boolean`: `true` or `false`, or the `xsd:boolean` lexical space
28
+ * (`true`/`false`/`1`/`0`).
29
+ * - `date`: anything the `date` codec reads ({@link isoToUnixSeconds}), or
30
+ * Unix seconds as a number.
31
+ * - `reference`: an absolute IRI.
32
+ *
33
+ * A string is read with surrounding whitespace collapsed, as XSD does.
34
+ */
35
+ export function readValue(kind, value) {
36
+ if (value === undefined || value === null) {
37
+ return undefined;
38
+ }
39
+ const text = typeof value === 'string' ? value.trim() : undefined;
40
+ if (text === '') {
41
+ return undefined;
42
+ }
43
+ switch (kind) {
44
+ case 'integer':
45
+ case 'number': {
46
+ const number = typeof value === 'number'
47
+ ? value
48
+ : text !== undefined && NUMERIC.test(text)
49
+ ? Number(text)
50
+ : Number.NaN;
51
+ if (kind === 'number') {
52
+ return Number.isFinite(number) ? number : UNREADABLE;
53
+ }
54
+ const integer = Math.trunc(number);
55
+ return Number.isSafeInteger(integer) ? integer : UNREADABLE;
56
+ }
57
+ case 'boolean':
58
+ if (typeof value === 'boolean') {
59
+ return value;
60
+ }
61
+ return text !== undefined && BOOLEAN.has(text)
62
+ ? text === 'true' || text === '1'
63
+ : UNREADABLE;
64
+ case 'date': {
65
+ if (typeof value === 'number') {
66
+ // Truncated, as the codec truncates a string’s fractional seconds: the
67
+ // field is an int64. `storableUnixSeconds` drops NaN.
68
+ const seconds = storableUnixSeconds(Math.trunc(value));
69
+ return seconds === undefined
70
+ ? UNREADABLE
71
+ : { seconds, precision: 'dateTime' };
72
+ }
73
+ return ((text === undefined ? undefined : isoToUnixSeconds(text)) ?? UNREADABLE);
74
+ }
75
+ case 'reference':
76
+ return text !== undefined && isAbsoluteIri(text) ? text : UNREADABLE;
77
+ }
78
+ }
79
+ /**
80
+ * A value as the report shows it: `String(value)`, or its `typeof` when it has
81
+ * no string form, as an object built without a prototype has not. Never
82
+ * throws, so a derive’s odd return is reported rather than crashing the
83
+ * projection.
84
+ */
85
+ export function describeValue(value) {
86
+ try {
87
+ return String(value);
88
+ }
89
+ catch {
90
+ return typeof value;
91
+ }
92
+ }
93
+ /** The `xsd:decimal` and `xsd:double` lexical forms, `INF` and `NaN` aside. */
94
+ const NUMERIC = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
95
+ /** The `xsd:boolean` lexical space. */
96
+ const BOOLEAN = new Set(['true', 'false', '1', '0']);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lde/search",
3
- "version": "0.38.0",
3
+ "version": "0.39.0",
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",