@spotto/semantic-query 1.0.70-alpha.18
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/dates.d.ts +32 -0
- package/dist/dates.js +105 -0
- package/dist/dates.js.map +1 -0
- package/dist/evaluate.d.ts +28 -0
- package/dist/evaluate.js +218 -0
- package/dist/evaluate.js.map +1 -0
- package/dist/fixtures.d.ts +248 -0
- package/dist/fixtures.js +156 -0
- package/dist/fixtures.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +48 -0
- package/dist/index.js.map +1 -0
- package/dist/suggest.d.ts +12 -0
- package/dist/suggest.js +70 -0
- package/dist/suggest.js.map +1 -0
- package/dist/types.d.ts +76 -0
- package/dist/types.js +24 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +33 -0
- package/dist/validate.js +487 -0
- package/dist/validate.js.map +1 -0
- package/package.json +29 -0
package/dist/fixtures.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FIXTURE_OPTS = exports.FIXTURE_TZ = exports.FIXTURE_NOW = exports.FIXTURE_CORPUS = exports.FIXTURE_NULLS = exports.FIXTURE_AMBULANCE = exports.FIXTURE_FRIDGE = exports.FIXTURE_EPOC = exports.FIXTURE_SCHEMA = exports.FIXTURE_KIT_PARENT = exports.FIXTURE_MANIFEST = exports.FIXTURE_TYPES = exports.FIXTURE_FIELDS = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* The conformance corpus.
|
|
6
|
+
*
|
|
7
|
+
* Shipped as part of the package, on its own entry point
|
|
8
|
+
* (`@spotto/semantic-query/dist/fixtures`), because it is not test scaffolding
|
|
9
|
+
* — it is the shared statement of the awkward cases every back-end must
|
|
10
|
+
* classify identically. The server's Mongo compiler runs against it, and so
|
|
11
|
+
* does anything else that ever claims to execute this language.
|
|
12
|
+
*
|
|
13
|
+
* Deliberately small and hand-readable. Each document exists for a reason
|
|
14
|
+
* stated beside it; a corpus nobody can hold in their head stops being
|
|
15
|
+
* evidence and starts being decoration.
|
|
16
|
+
*/
|
|
17
|
+
exports.FIXTURE_FIELDS = {
|
|
18
|
+
SERIAL: 'aaaaaaaaaaaaaaaaaaaaaa01',
|
|
19
|
+
MANUFACTURER: 'aaaaaaaaaaaaaaaaaaaaaa02',
|
|
20
|
+
OWNERSHIP: 'aaaaaaaaaaaaaaaaaaaaaa03',
|
|
21
|
+
PURCHASE_DATE: 'aaaaaaaaaaaaaaaaaaaaaa04',
|
|
22
|
+
DECOMMISSIONED: 'aaaaaaaaaaaaaaaaaaaaaa05',
|
|
23
|
+
CALIBRATED_AT: 'aaaaaaaaaaaaaaaaaaaaaa06',
|
|
24
|
+
};
|
|
25
|
+
exports.FIXTURE_TYPES = {
|
|
26
|
+
EPOC: 'bbbbbbbbbbbbbbbbbbbbbb01',
|
|
27
|
+
FRIDGE: 'bbbbbbbbbbbbbbbbbbbbbb02',
|
|
28
|
+
AMBULANCE: 'bbbbbbbbbbbbbbbbbbbbbb03',
|
|
29
|
+
};
|
|
30
|
+
exports.FIXTURE_MANIFEST = 'cccccccccccccccccccccc01';
|
|
31
|
+
exports.FIXTURE_KIT_PARENT = 'dddddddddddddddddddddd01';
|
|
32
|
+
/**
|
|
33
|
+
* One organisation's catalogue: every supported data type, a dropdown with
|
|
34
|
+
* options, a type-level value (Manufacturer, which no asset carries directly),
|
|
35
|
+
* and a DATETIME field that must be refused rather than matched.
|
|
36
|
+
*/
|
|
37
|
+
exports.FIXTURE_SCHEMA = {
|
|
38
|
+
fields: [
|
|
39
|
+
{ id: exports.FIXTURE_FIELDS.SERIAL, name: 'SerialNumber', dataType: 'STRING' },
|
|
40
|
+
{ id: exports.FIXTURE_FIELDS.MANUFACTURER, name: 'Manufacturer', dataType: 'STRING' },
|
|
41
|
+
{
|
|
42
|
+
id: exports.FIXTURE_FIELDS.OWNERSHIP,
|
|
43
|
+
name: 'Ownership',
|
|
44
|
+
dataType: 'STRING',
|
|
45
|
+
options: ['QH', 'LFA'],
|
|
46
|
+
},
|
|
47
|
+
{ id: exports.FIXTURE_FIELDS.PURCHASE_DATE, name: 'PurchaseDate', dataType: 'DATE' },
|
|
48
|
+
{ id: exports.FIXTURE_FIELDS.DECOMMISSIONED, name: 'Decommissioned', dataType: 'BOOLEAN' },
|
|
49
|
+
{ id: exports.FIXTURE_FIELDS.CALIBRATED_AT, name: 'CalibratedAt', dataType: 'DATETIME' },
|
|
50
|
+
],
|
|
51
|
+
types: [
|
|
52
|
+
{
|
|
53
|
+
id: exports.FIXTURE_TYPES.EPOC,
|
|
54
|
+
path: 'Blood Analysers|EPOC|',
|
|
55
|
+
typeFieldValuesAll: [
|
|
56
|
+
{
|
|
57
|
+
_id: exports.FIXTURE_FIELDS.MANUFACTURER,
|
|
58
|
+
name: 'Manufacturer',
|
|
59
|
+
valueString: 'ABBOTT DIAGNOSTICS',
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
id: exports.FIXTURE_TYPES.FRIDGE,
|
|
65
|
+
path: 'Blood Refrigerators|',
|
|
66
|
+
typeFieldValuesAll: [
|
|
67
|
+
{
|
|
68
|
+
_id: exports.FIXTURE_FIELDS.MANUFACTURER,
|
|
69
|
+
name: 'Manufacturer',
|
|
70
|
+
valueString: 'Advanced Health Technology',
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
{ id: exports.FIXTURE_TYPES.AMBULANCE, path: 'Vehicles|Ambulance|', typeFieldValuesAll: [] },
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
/** Carries a value for everything; its Manufacturer comes from its TYPE, not itself. */
|
|
78
|
+
exports.FIXTURE_EPOC = {
|
|
79
|
+
_id: '650000000000000000000001',
|
|
80
|
+
name: 'EPOC Analyser 7',
|
|
81
|
+
typeId: exports.FIXTURE_TYPES.EPOC,
|
|
82
|
+
typePath: 'Blood Analysers|EPOC|',
|
|
83
|
+
fieldValues: [
|
|
84
|
+
{ _id: exports.FIXTURE_FIELDS.SERIAL, name: 'SerialNumber', valueString: 'ABC123' },
|
|
85
|
+
{ _id: exports.FIXTURE_FIELDS.OWNERSHIP, name: 'Ownership', valueString: 'QH' },
|
|
86
|
+
{ _id: exports.FIXTURE_FIELDS.PURCHASE_DATE, name: 'PurchaseDate', valueDate: 20260815 },
|
|
87
|
+
],
|
|
88
|
+
state: {
|
|
89
|
+
locationName: 'Brisbane|Lab|',
|
|
90
|
+
locationStatus: 'HOME',
|
|
91
|
+
lastSeen: 1780000000000,
|
|
92
|
+
supportsWith: true,
|
|
93
|
+
locationWithId: exports.FIXTURE_KIT_PARENT,
|
|
94
|
+
},
|
|
95
|
+
readiness: { ready: 2, readyIssues: [{ reason: 'Overdue' }] },
|
|
96
|
+
tagIds: ['E28011700000021A2C5B0001'],
|
|
97
|
+
labels: [{ name: 'Loan pool' }],
|
|
98
|
+
manifestIds: [exports.FIXTURE_MANIFEST],
|
|
99
|
+
lastUpdated: 1700000000000,
|
|
100
|
+
lastChanged: 1780000000000,
|
|
101
|
+
groupMember: true,
|
|
102
|
+
};
|
|
103
|
+
/** The sparse asset: no state, no readiness, no tags, never updated. */
|
|
104
|
+
exports.FIXTURE_FRIDGE = {
|
|
105
|
+
_id: '650000000000000000000002',
|
|
106
|
+
name: 'Blood Fridge 2',
|
|
107
|
+
typeId: exports.FIXTURE_TYPES.FRIDGE,
|
|
108
|
+
typePath: 'Blood Refrigerators|',
|
|
109
|
+
fieldValues: [],
|
|
110
|
+
tagIds: [],
|
|
111
|
+
};
|
|
112
|
+
/** A kit, dispatched, whose members fall short of its type's template. */
|
|
113
|
+
exports.FIXTURE_AMBULANCE = {
|
|
114
|
+
_id: '650000000000000000000003',
|
|
115
|
+
name: 'Ambulance BC-A1',
|
|
116
|
+
typeId: exports.FIXTURE_TYPES.AMBULANCE,
|
|
117
|
+
typePath: 'Vehicles|Ambulance|',
|
|
118
|
+
fieldValues: [
|
|
119
|
+
{ _id: exports.FIXTURE_FIELDS.OWNERSHIP, name: 'Ownership', valueString: 'LFA' },
|
|
120
|
+
{ _id: exports.FIXTURE_FIELDS.DECOMMISSIONED, name: 'Decommissioned', valueBoolean: true },
|
|
121
|
+
],
|
|
122
|
+
state: { locationName: 'Brisbane|Depot|', locationStatus: 'DISPATCH' },
|
|
123
|
+
readiness: { ready: 1, readyIssues: [] },
|
|
124
|
+
tagIds: [],
|
|
125
|
+
lastUpdated: 1780000000000,
|
|
126
|
+
typeKit: true,
|
|
127
|
+
groupSatisfiesKit: false,
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* The null-slot document. Every field it carries is present-but-null, which is
|
|
131
|
+
* a different shape from absent — and the axis where two back-ends most easily
|
|
132
|
+
* disagree (a database's "does this key exist" versus a language's "is this
|
|
133
|
+
* empty"). Every condition family must classify it identically on both sides.
|
|
134
|
+
*/
|
|
135
|
+
exports.FIXTURE_NULLS = {
|
|
136
|
+
_id: '650000000000000000000004',
|
|
137
|
+
name: 'Null Slots',
|
|
138
|
+
typeId: exports.FIXTURE_TYPES.AMBULANCE,
|
|
139
|
+
typePath: 'Vehicles|Ambulance|',
|
|
140
|
+
fieldValues: [{ _id: exports.FIXTURE_FIELDS.OWNERSHIP, name: 'Ownership', valueString: null }],
|
|
141
|
+
state: { locationWithId: null, locationName: 'Brisbane|Depot|' },
|
|
142
|
+
tagIds: null,
|
|
143
|
+
lastUpdated: null,
|
|
144
|
+
telemetry: { battery: null },
|
|
145
|
+
};
|
|
146
|
+
exports.FIXTURE_CORPUS = [
|
|
147
|
+
exports.FIXTURE_EPOC,
|
|
148
|
+
exports.FIXTURE_FRIDGE,
|
|
149
|
+
exports.FIXTURE_AMBULANCE,
|
|
150
|
+
exports.FIXTURE_NULLS,
|
|
151
|
+
];
|
|
152
|
+
/** Frozen clock for the corpus — 2026-07-27 09:00 in Brisbane. */
|
|
153
|
+
exports.FIXTURE_NOW = new Date('2026-07-26T23:00:00Z');
|
|
154
|
+
exports.FIXTURE_TZ = 'Australia/Brisbane';
|
|
155
|
+
exports.FIXTURE_OPTS = { now: exports.FIXTURE_NOW, timeZone: exports.FIXTURE_TZ };
|
|
156
|
+
//# sourceMappingURL=fixtures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fixtures.js","sourceRoot":"","sources":["../src/fixtures.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@spotto/semantic-query` — the Spotto semantic query language.
|
|
3
|
+
*
|
|
4
|
+
* ONE statement of what a query means, shared by everything that reads or
|
|
5
|
+
* writes one: the client's filter UI and offline evaluation, the API's read
|
|
6
|
+
* path, and the AI layer that generates queries.
|
|
7
|
+
*
|
|
8
|
+
* The split this package exists to enforce:
|
|
9
|
+
*
|
|
10
|
+
* validate() every rule of the language — the gate both back-ends stand behind
|
|
11
|
+
* dates one resolution of "last 30 days", for everyone
|
|
12
|
+
* evaluate() a query -> yes/no for a document, in memory
|
|
13
|
+
* ---------------------------------------------------------------
|
|
14
|
+
* compile() a query -> a MongoDB $match [server-side only]
|
|
15
|
+
*
|
|
16
|
+
* Only the last line is unshareable, because only it is database-specific.
|
|
17
|
+
*
|
|
18
|
+
* Be clear about what that does and does not buy. The rules, the date engine
|
|
19
|
+
* and the field lookup exist once, so no consumer can hold a different opinion
|
|
20
|
+
* about them — before this, they lived inside the Mongo compiler, and a client
|
|
21
|
+
* could not so much as check a query without it. What remains genuinely
|
|
22
|
+
* duplicated is the last step: turning a checked query into a `$match` versus
|
|
23
|
+
* answering it in memory. Those are two interpretations of the same
|
|
24
|
+
* instruction and nothing here forces them to agree — only running both over
|
|
25
|
+
* the same documents can do that, which is what the shipped fixtures are for.
|
|
26
|
+
*/
|
|
27
|
+
export * from './types';
|
|
28
|
+
export * from './dates';
|
|
29
|
+
export * from './suggest';
|
|
30
|
+
export * from './validate';
|
|
31
|
+
export * from './evaluate';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
/**
|
|
18
|
+
* `@spotto/semantic-query` — the Spotto semantic query language.
|
|
19
|
+
*
|
|
20
|
+
* ONE statement of what a query means, shared by everything that reads or
|
|
21
|
+
* writes one: the client's filter UI and offline evaluation, the API's read
|
|
22
|
+
* path, and the AI layer that generates queries.
|
|
23
|
+
*
|
|
24
|
+
* The split this package exists to enforce:
|
|
25
|
+
*
|
|
26
|
+
* validate() every rule of the language — the gate both back-ends stand behind
|
|
27
|
+
* dates one resolution of "last 30 days", for everyone
|
|
28
|
+
* evaluate() a query -> yes/no for a document, in memory
|
|
29
|
+
* ---------------------------------------------------------------
|
|
30
|
+
* compile() a query -> a MongoDB $match [server-side only]
|
|
31
|
+
*
|
|
32
|
+
* Only the last line is unshareable, because only it is database-specific.
|
|
33
|
+
*
|
|
34
|
+
* Be clear about what that does and does not buy. The rules, the date engine
|
|
35
|
+
* and the field lookup exist once, so no consumer can hold a different opinion
|
|
36
|
+
* about them — before this, they lived inside the Mongo compiler, and a client
|
|
37
|
+
* could not so much as check a query without it. What remains genuinely
|
|
38
|
+
* duplicated is the last step: turning a checked query into a `$match` versus
|
|
39
|
+
* answering it in memory. Those are two interpretations of the same
|
|
40
|
+
* instruction and nothing here forces them to agree — only running both over
|
|
41
|
+
* the same documents can do that, which is what the shipped fixtures are for.
|
|
42
|
+
*/
|
|
43
|
+
__exportStar(require("./types"), exports);
|
|
44
|
+
__exportStar(require("./dates"), exports);
|
|
45
|
+
__exportStar(require("./suggest"), exports);
|
|
46
|
+
__exportStar(require("./validate"), exports);
|
|
47
|
+
__exportStar(require("./evaluate"), exports);
|
|
48
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,0CAAuB;AACvB,0CAAuB;AACvB,4CAAyB;AACzB,6CAA0B;AAC1B,6CAA0B"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Nearest-candidate matching for "did you mean…" suggestions on validation
|
|
3
|
+
* errors — an unknown field or dropdown value is rejected WITH the closest
|
|
4
|
+
* real name, so an honest error is also an actionable one.
|
|
5
|
+
*
|
|
6
|
+
* Substring matching alone is not enough: the canonical case is
|
|
7
|
+
* `SerialNo` → `SerialNumber`, and "serialno" is NOT a substring of
|
|
8
|
+
* "serialnumber" (…n-o… vs …n-u…) nor within any sane edit-distance
|
|
9
|
+
* tolerance (5 edits). The longest-shared-prefix pass is what makes that
|
|
10
|
+
* documented case actually produce a suggestion.
|
|
11
|
+
*/
|
|
12
|
+
export declare function nearest(candidates: string[], wanted: string): string | undefined;
|
package/dist/suggest.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Nearest-candidate matching for "did you mean…" suggestions on validation
|
|
4
|
+
* errors — an unknown field or dropdown value is rejected WITH the closest
|
|
5
|
+
* real name, so an honest error is also an actionable one.
|
|
6
|
+
*
|
|
7
|
+
* Substring matching alone is not enough: the canonical case is
|
|
8
|
+
* `SerialNo` → `SerialNumber`, and "serialno" is NOT a substring of
|
|
9
|
+
* "serialnumber" (…n-o… vs …n-u…) nor within any sane edit-distance
|
|
10
|
+
* tolerance (5 edits). The longest-shared-prefix pass is what makes that
|
|
11
|
+
* documented case actually produce a suggestion.
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.nearest = void 0;
|
|
15
|
+
function editDistance(a, b) {
|
|
16
|
+
const prev = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
17
|
+
const cur = new Array(b.length + 1);
|
|
18
|
+
for (let i = 1; i <= a.length; i++) {
|
|
19
|
+
cur[0] = i;
|
|
20
|
+
for (let j = 1; j <= b.length; j++) {
|
|
21
|
+
cur[j] = Math.min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
22
|
+
}
|
|
23
|
+
prev.splice(0, prev.length, ...cur);
|
|
24
|
+
}
|
|
25
|
+
return prev[b.length];
|
|
26
|
+
}
|
|
27
|
+
function nearest(candidates, wanted) {
|
|
28
|
+
const w = wanted.toLowerCase().trim();
|
|
29
|
+
if (!w)
|
|
30
|
+
return undefined;
|
|
31
|
+
const lower = candidates.map((c) => c.toLowerCase());
|
|
32
|
+
const exact = lower.indexOf(w);
|
|
33
|
+
if (exact >= 0)
|
|
34
|
+
return candidates[exact];
|
|
35
|
+
for (let i = 0; i < lower.length; i++) {
|
|
36
|
+
if (lower[i].startsWith(w) || w.startsWith(lower[i]))
|
|
37
|
+
return candidates[i];
|
|
38
|
+
}
|
|
39
|
+
for (let i = 0; i < lower.length; i++) {
|
|
40
|
+
if (lower[i].includes(w) || w.includes(lower[i]))
|
|
41
|
+
return candidates[i];
|
|
42
|
+
}
|
|
43
|
+
let prefixBest;
|
|
44
|
+
let prefixLen = 0;
|
|
45
|
+
for (let i = 0; i < lower.length; i++) {
|
|
46
|
+
let n = 0;
|
|
47
|
+
while (n < w.length && n < lower[i].length && w[n] === lower[i][n])
|
|
48
|
+
n++;
|
|
49
|
+
if (n > prefixLen) {
|
|
50
|
+
prefixLen = n;
|
|
51
|
+
prefixBest = candidates[i];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (prefixBest && prefixLen >= Math.max(4, Math.ceil(w.length / 2))) {
|
|
55
|
+
return prefixBest;
|
|
56
|
+
}
|
|
57
|
+
let best;
|
|
58
|
+
let bestScore = Infinity;
|
|
59
|
+
const tolerance = Math.max(2, Math.floor(w.length / 3));
|
|
60
|
+
for (let i = 0; i < lower.length; i++) {
|
|
61
|
+
const d = editDistance(w, lower[i]);
|
|
62
|
+
if (d < bestScore) {
|
|
63
|
+
bestScore = d;
|
|
64
|
+
best = candidates[i];
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return bestScore <= tolerance ? best : undefined;
|
|
68
|
+
}
|
|
69
|
+
exports.nearest = nearest;
|
|
70
|
+
//# sourceMappingURL=suggest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suggest.js","sourceRoot":"","sources":["../src/suggest.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { FieldDataType } from '@spotto/contract';
|
|
2
|
+
/**
|
|
3
|
+
* What an organisation's catalogue looks like to this package.
|
|
4
|
+
*
|
|
5
|
+
* These are NOT wire shapes — the wire contract lives in `@spotto/contract`.
|
|
6
|
+
* This is the vocabulary a query is resolved AGAINST: which custom fields
|
|
7
|
+
* exist, what type each one is, and (for the evaluator) the type documents
|
|
8
|
+
* whose values an asset inherits.
|
|
9
|
+
*/
|
|
10
|
+
export interface SemanticFieldDef {
|
|
11
|
+
/** The field's id — custom-field matching is by `_id`, not name. */
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
dataType: FieldDataType;
|
|
15
|
+
/** Dropdown options — an eq value must match one exactly or the query silently returns nothing. */
|
|
16
|
+
options?: string[];
|
|
17
|
+
}
|
|
18
|
+
/** A resolved type-level field value, as materialised in `typeFieldValuesAll`. */
|
|
19
|
+
export interface SemanticTypeFieldValue {
|
|
20
|
+
_id?: string;
|
|
21
|
+
name?: string;
|
|
22
|
+
valueString?: string;
|
|
23
|
+
valueInteger?: number;
|
|
24
|
+
valueDecimal?: number;
|
|
25
|
+
valueBoolean?: boolean;
|
|
26
|
+
valueDate?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface SemanticTypeDef {
|
|
29
|
+
id: string;
|
|
30
|
+
/** Pipe-delimited, pipe-terminated. Root type is "". */
|
|
31
|
+
path: string;
|
|
32
|
+
/** Inheritance already resolved by the platform. */
|
|
33
|
+
typeFieldValuesAll: SemanticTypeFieldValue[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* What VALIDATION needs: field definitions only. The Mongo compiler needs no
|
|
37
|
+
* more than this either — type-level values reach it through a `$lookup`.
|
|
38
|
+
*/
|
|
39
|
+
export interface SemanticFieldCatalogue {
|
|
40
|
+
fields: SemanticFieldDef[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* What EVALUATION needs: the catalogue plus types, because matching a
|
|
44
|
+
* type-level field value in memory means walking the asset's type document.
|
|
45
|
+
* (The Mongo compiler gets the same values through a `$lookup` instead.)
|
|
46
|
+
*/
|
|
47
|
+
export interface SemanticAccountSchema extends SemanticFieldCatalogue {
|
|
48
|
+
types: SemanticTypeDef[];
|
|
49
|
+
}
|
|
50
|
+
export interface SemanticValidationIssue {
|
|
51
|
+
path: string;
|
|
52
|
+
code: string;
|
|
53
|
+
message: string;
|
|
54
|
+
suggestion?: string;
|
|
55
|
+
}
|
|
56
|
+
export declare class SemanticQueryValidationError extends Error {
|
|
57
|
+
issues: SemanticValidationIssue[];
|
|
58
|
+
constructor(issues: SemanticValidationIssue[]);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Throw one validation issue — fail-fast, this package's contract (the issues
|
|
62
|
+
* array shape exists for the wire; internally the FIRST problem is the
|
|
63
|
+
* answer). One helper beside the error class it throws, so issue construction
|
|
64
|
+
* cannot drift between layers.
|
|
65
|
+
*/
|
|
66
|
+
export declare function failSemanticValidation(path: string, code: string, message: string, suggestion?: string): never;
|
|
67
|
+
export interface SemanticCompileOptions {
|
|
68
|
+
/**
|
|
69
|
+
* Explicit — never sampled deeper than the entry point, so a run is
|
|
70
|
+
* reproducible: the same query, catalogue, `now` and timezone always give
|
|
71
|
+
* the same answer, whoever is asking.
|
|
72
|
+
*/
|
|
73
|
+
now?: Date;
|
|
74
|
+
/** IANA zone for resolving relative dates. */
|
|
75
|
+
timeZone?: string;
|
|
76
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.failSemanticValidation = exports.SemanticQueryValidationError = void 0;
|
|
4
|
+
class SemanticQueryValidationError extends Error {
|
|
5
|
+
constructor(issues) {
|
|
6
|
+
super(issues.map((i) => i.message).join('; '));
|
|
7
|
+
this.issues = issues;
|
|
8
|
+
this.name = 'SemanticQueryValidationError';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.SemanticQueryValidationError = SemanticQueryValidationError;
|
|
12
|
+
/**
|
|
13
|
+
* Throw one validation issue — fail-fast, this package's contract (the issues
|
|
14
|
+
* array shape exists for the wire; internally the FIRST problem is the
|
|
15
|
+
* answer). One helper beside the error class it throws, so issue construction
|
|
16
|
+
* cannot drift between layers.
|
|
17
|
+
*/
|
|
18
|
+
function failSemanticValidation(path, code, message, suggestion) {
|
|
19
|
+
throw new SemanticQueryValidationError([
|
|
20
|
+
suggestion ? { path, code, message, suggestion } : { path, code, message },
|
|
21
|
+
]);
|
|
22
|
+
}
|
|
23
|
+
exports.failSemanticValidation = failSemanticValidation;
|
|
24
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";;;AAmEA,MAAa,4BAA6B,SAAQ,KAAK;IACrD,YAAmB,MAAiC;QAClD,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QAD7B,WAAM,GAAN,MAAM,CAA2B;QAElD,IAAI,CAAC,IAAI,GAAG,8BAA8B,CAAA;IAC5C,CAAC;CACF;AALD,oEAKC;AAED;;;;;GAKG;AACH,SAAgB,sBAAsB,CACpC,IAAY,EACZ,IAAY,EACZ,OAAe,EACf,UAAmB;IAEnB,MAAM,IAAI,4BAA4B,CAAC;QACrC,UAAU,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE;KAC3E,CAAC,CAAA;AACJ,CAAC;AATD,wDASC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { FieldDataType, SemanticQuery } from '@spotto/contract';
|
|
2
|
+
import { SemanticCompileOptions, SemanticFieldCatalogue, SemanticFieldDef, SemanticValidationIssue } from './types';
|
|
3
|
+
/** The live storage slots. `valueBool` does not exist; the live key is `valueBoolean`. */
|
|
4
|
+
export declare const SEMANTIC_VALUE_SLOT: Record<string, string>;
|
|
5
|
+
/** Millisecond-timestamp fields, by condition type. `createdDate` uses the ObjectId. */
|
|
6
|
+
export declare const SEMANTIC_TIMESTAMP_FIELD: Record<string, string>;
|
|
7
|
+
/**
|
|
8
|
+
* Find a field by name, case-insensitively — the one lookup both back-ends
|
|
9
|
+
* use, so neither can resolve a name to a different field than the other.
|
|
10
|
+
*/
|
|
11
|
+
export declare function findSemanticField(catalogue: SemanticFieldCatalogue, fieldName: unknown): SemanticFieldDef | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Turn a written value into the form its storage slot actually holds.
|
|
14
|
+
*
|
|
15
|
+
* Shared deliberately: the Mongo compiler and the in-memory evaluator must
|
|
16
|
+
* compare against the SAME value, and this is where a numeric literal for a
|
|
17
|
+
* text field, or a relative date for a DATE field, becomes one concrete thing.
|
|
18
|
+
* Two copies of this would classify the same document differently.
|
|
19
|
+
*
|
|
20
|
+
* Throws a plain Error — the caller decides whether that is a validation issue
|
|
21
|
+
* (it is, at the gate) or an impossible state (it is, after the gate).
|
|
22
|
+
*/
|
|
23
|
+
export declare function coerceSemanticValue(value: unknown, dataType: FieldDataType, now: Date, tz: string): string | number | boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Assert a query is valid, throwing `SemanticQueryValidationError` on the
|
|
26
|
+
* first problem. This is the gate: a back-end may assume anything that passed
|
|
27
|
+
* through it is safe to execute.
|
|
28
|
+
*/
|
|
29
|
+
export declare function assertSemanticQuery(semanticQuery: SemanticQuery, catalogue: SemanticFieldCatalogue, opts?: SemanticCompileOptions): void;
|
|
30
|
+
/** Validate without throwing. Empty array = valid. */
|
|
31
|
+
export declare function validateSemanticQuery(semanticQuery: SemanticQuery, catalogue: SemanticFieldCatalogue, opts?: SemanticCompileOptions): SemanticValidationIssue[];
|
|
32
|
+
/** True when any condition touches a custom field — both back-ends need to know. */
|
|
33
|
+
export declare function queryUsesCustomFields(semanticQuery: SemanticQuery): boolean;
|