@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.
- package/dist/project.d.ts.map +1 -1
- package/dist/project.js +51 -127
- package/dist/read-value.d.ts +44 -0
- package/dist/read-value.d.ts.map +1 -0
- package/dist/read-value.js +96 -0
- package/package.json +1 -1
package/dist/project.d.ts.map
CHANGED
|
@@ -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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
//
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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 (
|
|
297
|
-
|
|
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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
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
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
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
|
|
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(
|
|
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.
|
|
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",
|