@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.
@@ -0,0 +1,32 @@
1
+ import { SemanticDateValue } from '@spotto/contract';
2
+ /**
3
+ * The semantic-query date engine.
4
+ *
5
+ * A saved query keeps relative dates SYMBOLIC — "last 30 days" means the last
6
+ * 30 days forever, not the 30 days before it was saved — so every execution
7
+ * resolves them afresh. This module is the one place that resolution happens,
8
+ * for every consumer: the client evaluating against its local cache and the
9
+ * server compiling to Mongo resolve the same words to the same instant.
10
+ *
11
+ * Everything takes an EXPLICIT `now`. Nothing here reads the clock. That is
12
+ * what makes a resolved query reproducible: the same query, catalogue, `now`
13
+ * and timezone always produce the same boundaries, whoever resolved them.
14
+ */
15
+ /** A calendar date with no timezone attached. */
16
+ export interface SemanticCivilDate {
17
+ y: number;
18
+ m: number;
19
+ d: number;
20
+ }
21
+ /** Resolve either semantic date form (ISO string or relative object) to a civil date. */
22
+ export declare function resolveSemanticDate(v: SemanticDateValue, now: Date, tz: string): SemanticCivilDate;
23
+ /** DATE custom fields store day-number integers: 15 Aug 2026 → 20260815. */
24
+ export declare function semanticCivilToYyyymmdd(c: SemanticCivilDate): number;
25
+ /**
26
+ * Epoch-ms boundary for comparing against a millisecond timestamp field.
27
+ *
28
+ * `gt`/`lte` sit at the END of the named day (so "lte 2026-07-27" includes all
29
+ * of the 27th); `gte`/`lt` at its START. Without this, half of every inclusive
30
+ * range query silently drops a day.
31
+ */
32
+ export declare function semanticDateBoundaryMs(v: SemanticDateValue, operator: 'gt' | 'gte' | 'lt' | 'lte', now: Date, tz: string): number;
package/dist/dates.js ADDED
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.semanticDateBoundaryMs = exports.semanticCivilToYyyymmdd = exports.resolveSemanticDate = void 0;
7
+ const dayjs_1 = __importDefault(require("dayjs"));
8
+ const utc_1 = __importDefault(require("dayjs/plugin/utc"));
9
+ const timezone_1 = __importDefault(require("dayjs/plugin/timezone"));
10
+ const isoWeek_1 = __importDefault(require("dayjs/plugin/isoWeek"));
11
+ const quarterOfYear_1 = __importDefault(require("dayjs/plugin/quarterOfYear"));
12
+ dayjs_1.default.extend(utc_1.default);
13
+ dayjs_1.default.extend(timezone_1.default);
14
+ dayjs_1.default.extend(isoWeek_1.default);
15
+ dayjs_1.default.extend(quarterOfYear_1.default);
16
+ const SEMANTIC_ISO_RE = /^(\d{4})-(\d{2})-(\d{2})$/;
17
+ function civilOf(d) {
18
+ return { y: d.year(), m: d.month() + 1, d: d.date() };
19
+ }
20
+ /** Resolve a structured semantic relative date to a civil date in `tz`. */
21
+ function resolveSemanticRelativeDate(v, now, tz) {
22
+ const today = (0, dayjs_1.default)(now).tz(tz);
23
+ const { base, offset, position } = v;
24
+ switch (base) {
25
+ case 'day':
26
+ return civilOf(today.add(offset, 'day'));
27
+ case 'week': {
28
+ // 'same' keeps the weekday — "a week from today" is +7 days, not the
29
+ // following Monday. `start`/`end` snap to the ISO week's boundaries
30
+ // (Monday start, matching the platform's UNIT_MAP choice).
31
+ if (position === 'same')
32
+ return civilOf(today.add(offset, 'week'));
33
+ const monday = today.add(offset, 'week').startOf('isoWeek');
34
+ return civilOf(position === 'end' ? monday.add(6, 'day') : monday);
35
+ }
36
+ case 'month': {
37
+ // 'same' keeps the day-of-month; dayjs clamps short months
38
+ // (31 Jan + 1 month = 28/29 Feb), which is the specified behaviour.
39
+ if (position === 'same')
40
+ return civilOf(today.add(offset, 'month'));
41
+ const t = today.add(offset, 'month');
42
+ return civilOf(position === 'end' ? t.endOf('month') : t.startOf('month'));
43
+ }
44
+ case 'quarter': {
45
+ if (position === 'same')
46
+ return civilOf(today.add(offset * 3, 'month'));
47
+ const t = today.add(offset, 'quarter');
48
+ return civilOf(position === 'end'
49
+ ? t.endOf('quarter')
50
+ : t.startOf('quarter'));
51
+ }
52
+ case 'year': {
53
+ if (position === 'same')
54
+ return civilOf(today.add(offset * 12, 'month'));
55
+ const t = today.add(offset, 'year');
56
+ return civilOf(position === 'end' ? t.endOf('year') : t.startOf('year'));
57
+ }
58
+ }
59
+ throw new Error(`unknown relative base '${String(base)}'`);
60
+ }
61
+ /** Resolve either semantic date form (ISO string or relative object) to a civil date. */
62
+ function resolveSemanticDate(v, now, tz) {
63
+ if (typeof v === 'string') {
64
+ const m = SEMANTIC_ISO_RE.exec(v.trim());
65
+ if (!m)
66
+ throw new Error(`date must be ISO YYYY-MM-DD, got '${v}'`);
67
+ const civil = { y: Number(m[1]), m: Number(m[2]), d: Number(m[3]) };
68
+ // The pattern alone accepts 2026-02-31 and 2026-13-01, which resolve to an
69
+ // Invalid Date and then to a NaN boundary — a query that runs and matches
70
+ // nothing. Reject the impossible date instead. (Checked arithmetically:
71
+ // dayjs' strict parsing needs the customParseFormat plugin, which this
72
+ // module deliberately does not load.)
73
+ const daysInMonth = new Date(Date.UTC(civil.y, civil.m, 0)).getUTCDate();
74
+ if (civil.m < 1 || civil.m > 12 || civil.d < 1 || civil.d > daysInMonth) {
75
+ throw new Error(`'${v}' is not a real calendar date`);
76
+ }
77
+ return civil;
78
+ }
79
+ if (v && typeof v === 'object' && v.relative) {
80
+ return resolveSemanticRelativeDate(v, now, tz);
81
+ }
82
+ throw new Error(`unrecognised date value: ${JSON.stringify(v)}`);
83
+ }
84
+ exports.resolveSemanticDate = resolveSemanticDate;
85
+ /** DATE custom fields store day-number integers: 15 Aug 2026 → 20260815. */
86
+ function semanticCivilToYyyymmdd(c) {
87
+ return c.y * 10000 + c.m * 100 + c.d;
88
+ }
89
+ exports.semanticCivilToYyyymmdd = semanticCivilToYyyymmdd;
90
+ /**
91
+ * Epoch-ms boundary for comparing against a millisecond timestamp field.
92
+ *
93
+ * `gt`/`lte` sit at the END of the named day (so "lte 2026-07-27" includes all
94
+ * of the 27th); `gte`/`lt` at its START. Without this, half of every inclusive
95
+ * range query silently drops a day.
96
+ */
97
+ function semanticDateBoundaryMs(v, operator, now, tz) {
98
+ const c = resolveSemanticDate(v, now, tz);
99
+ const iso = `${String(c.y).padStart(4, '0')}-${String(c.m).padStart(2, '0')}-${String(c.d).padStart(2, '0')}`;
100
+ const day = dayjs_1.default.tz(iso, tz);
101
+ const endOfDay = operator === 'gt' || operator === 'lte';
102
+ return (endOfDay ? day.endOf('day') : day.startOf('day')).valueOf();
103
+ }
104
+ exports.semanticDateBoundaryMs = semanticDateBoundaryMs;
105
+ //# sourceMappingURL=dates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dates.js","sourceRoot":"","sources":["../src/dates.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { SemanticQuery } from '@spotto/contract';
2
+ import { SemanticAccountSchema, SemanticCompileOptions } from './types';
3
+ /**
4
+ * Reference semantics for the semantic-query language, evaluated directly over
5
+ * asset documents in memory.
6
+ *
7
+ * This is deliberately an INDEPENDENT implementation of what each condition
8
+ * MEANS — not a second call into the compiler. It exists to be the oracle in
9
+ * the acceptance property:
10
+ *
11
+ * evaluate(query, docs) === find(compile(query)) restricted to docs
12
+ *
13
+ * If the two disagree, one of them is wrong, and the disagreement is the bug
14
+ * report. This is what catches the silent-wrong-rows class — encoding
15
+ * mistakes that otherwise surface as a plausibly-sized result set.
16
+ *
17
+ * Scope note: like the compiler, this evaluates CONDITIONS ONLY. Org and
18
+ * user-group scoping belong to the pipeline scaffold; the oracle harness
19
+ * restricts its corpus accordingly before comparing.
20
+ *
21
+ * Two deliberate mirrors of compiler decisions:
22
+ * - Custom fields match at EITHER level (asset `fieldValues` or the type's
23
+ * `typeFieldValuesAll`, joined by `typeId`) with no override precedence —
24
+ * the platform's shipped flat-filter semantic.
25
+ */
26
+ declare type Doc = Record<string, any>;
27
+ export declare function evaluateSemanticQuery(query: SemanticQuery, docs: Doc[], schema: SemanticAccountSchema, opts?: SemanticCompileOptions): Doc[];
28
+ export {};
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.evaluateSemanticQuery = void 0;
4
+ // Project Dependencies
5
+ const dates_1 = require("./dates");
6
+ const validate_1 = require("./validate");
7
+ const SLOT = {
8
+ STRING: 'valueString',
9
+ INTEGER: 'valueInteger',
10
+ DECIMAL: 'valueDecimal',
11
+ BOOLEAN: 'valueBoolean',
12
+ DATE: 'valueDate',
13
+ };
14
+ function get(doc, path) {
15
+ return path.split('.').reduce((a, k) => (a == null ? undefined : a[k]), doc);
16
+ }
17
+ function cmp(actual, op, target) {
18
+ if (actual === undefined || actual === null)
19
+ return false;
20
+ switch (op) {
21
+ case 'eq':
22
+ return actual === target;
23
+ case 'ne':
24
+ return actual !== target;
25
+ case 'gt':
26
+ return actual > target;
27
+ case 'gte':
28
+ return actual >= target;
29
+ case 'lt':
30
+ return actual < target;
31
+ case 'lte':
32
+ return actual <= target;
33
+ default:
34
+ return false;
35
+ }
36
+ }
37
+ function evaluateSemanticQuery(query, docs, schema, opts = {}) {
38
+ var _a, _b;
39
+ const ctx = {
40
+ schema,
41
+ now: (_a = opts.now) !== null && _a !== void 0 ? _a : new Date(),
42
+ tz: (_b = opts.timeZone) !== null && _b !== void 0 ? _b : 'UTC',
43
+ fieldByName: new Map(schema.fields.map((f) => [f.name.toLowerCase(), f])),
44
+ typeById: new Map(schema.types.map((t) => [t.id, t])),
45
+ };
46
+ return docs.filter((d) => query.conditions.every((c) => match(d, c, ctx)));
47
+ }
48
+ exports.evaluateSemanticQuery = evaluateSemanticQuery;
49
+ function match(doc, cond, ctx) {
50
+ var _a, _b, _c, _d, _e, _f;
51
+ switch (cond.type) {
52
+ case 'and':
53
+ return cond.conditions.every((c) => match(doc, c, ctx));
54
+ case 'or':
55
+ return cond.conditions.some((c) => match(doc, c, ctx));
56
+ case 'assetType':
57
+ return prefixMatch(String((_a = get(doc, 'typePath')) !== null && _a !== void 0 ? _a : ''), cond.path, cond.includeSubtypes !== false);
58
+ case 'location':
59
+ return prefixMatch(String((_b = get(doc, 'state.locationName')) !== null && _b !== void 0 ? _b : ''), cond.path, cond.includeSublocations !== false);
60
+ case 'homeLocation':
61
+ return prefixMatch(String((_c = get(doc, 'homeLocationPath')) !== null && _c !== void 0 ? _c : ''), cond.path, cond.includeSublocations !== false);
62
+ case 'withAsset': {
63
+ const v = get(doc, 'state.locationWithId');
64
+ if (cond.hasAny !== undefined)
65
+ return (v !== undefined && v !== null) === cond.hasAny;
66
+ return v != null && String(v) === cond.assetId;
67
+ }
68
+ case 'geolocation': {
69
+ const v = get(doc, 'state.geolocation');
70
+ return cond.exists !== false ? v !== undefined : v === undefined;
71
+ }
72
+ case 'tags': {
73
+ const tags = ((_d = doc.tagIds) !== null && _d !== void 0 ? _d : []).map(String);
74
+ const want = ((_e = cond.values) !== null && _e !== void 0 ? _e : []).map(String);
75
+ return cond.operator === 'all'
76
+ ? want.every((t) => tags.includes(t))
77
+ : want.some((t) => tags.includes(t));
78
+ }
79
+ case 'telemetry':
80
+ return cmp(get(doc, `telemetry.${cond.field}`), cond.operator, cond.value);
81
+ case 'readiness': {
82
+ const levels = (_f = cond.levels) !== null && _f !== void 0 ? _f : (cond.level === undefined ? [] : [cond.level]);
83
+ // Missing readiness ≡ green (0).
84
+ const actual = get(doc, 'readiness.ready');
85
+ const effective = typeof actual === 'number' ? actual : 0;
86
+ return levels.includes(effective);
87
+ }
88
+ case 'kit': {
89
+ if (cond.parentAssetId !== undefined) {
90
+ const p = get(doc, 'groupParentId');
91
+ return p != null && String(p) === cond.parentAssetId;
92
+ }
93
+ if (cond.isKit !== undefined)
94
+ return (doc.typeKit === true) === cond.isKit;
95
+ if (cond.isMember !== undefined)
96
+ return (doc.groupMember === true) === cond.isMember;
97
+ // Three-valued, unlike the two flags above: `groupSatisfiesKit` is only
98
+ // set when the asset IS a kit, its type carries a manifest template, and
99
+ // that template declares requirements (groups/satisfies.ts). Absent means
100
+ // the question does not apply — so it is neither satisfied NOR
101
+ // unsatisfied, and an exact match answers both directions.
102
+ return doc.groupSatisfiesKit === cond.satisfied;
103
+ }
104
+ case 'locationStatus': {
105
+ const status = get(doc, 'state.locationStatus');
106
+ if (typeof status === 'string' && cond.values.includes(status))
107
+ return true;
108
+ // NEVERSEEN also means "no state at all".
109
+ return cond.values.includes('NEVERSEEN') && get(doc, 'state') === undefined;
110
+ }
111
+ case 'createdDate':
112
+ case 'lastUpdated':
113
+ case 'lastChanged':
114
+ case 'lastSeen':
115
+ case 'firstSeen':
116
+ return matchTimestamp(doc, cond, ctx);
117
+ case 'customField':
118
+ return matchCustomField(doc, cond, ctx);
119
+ default:
120
+ return false;
121
+ }
122
+ }
123
+ function prefixMatch(actual, path, includeDescendants) {
124
+ return includeDescendants ? actual.startsWith(path) : actual === path;
125
+ }
126
+ const TIMESTAMP_FIELD = {
127
+ lastUpdated: 'lastUpdated',
128
+ lastChanged: 'lastChanged',
129
+ lastSeen: 'state.lastSeen',
130
+ firstSeen: 'state.firstSeen',
131
+ };
132
+ function matchTimestamp(doc, cond, ctx) {
133
+ const ms = (0, dates_1.semanticDateBoundaryMs)(cond.value, cond.operator, ctx.now, ctx.tz);
134
+ if (cond.type === 'createdDate') {
135
+ // The ObjectId's first 4 bytes are the creation time in SECONDS. The
136
+ // compiler steps `lte`/`gt` to the next second (a zero-tail boundary id
137
+ // sorts before every real id in the same second); mirror that exactly,
138
+ // or documents created within the boundary second classify differently.
139
+ const created = parseInt(String(doc._id).slice(0, 8), 16);
140
+ const second = Math.floor(ms / 1000);
141
+ switch (cond.operator) {
142
+ case 'gte':
143
+ return created >= second;
144
+ case 'lt':
145
+ return created < second;
146
+ case 'lte':
147
+ return created < second + 1;
148
+ default: // gt
149
+ return created >= second + 1;
150
+ }
151
+ }
152
+ const actual = get(doc, TIMESTAMP_FIELD[cond.type]);
153
+ const neverSet = actual === undefined || actual === null;
154
+ if (neverSet) {
155
+ return ((cond.operator === 'lt' || cond.operator === 'lte') &&
156
+ cond.includeNeverSet !== false);
157
+ }
158
+ return cmp(actual, cond.operator, ms);
159
+ }
160
+ // ---------------------------------------------------------------------------
161
+ // Custom fields — mirror of the compiler's two-container conjunct
162
+ // ---------------------------------------------------------------------------
163
+ /**
164
+ * Every value the field holds in this container.
165
+ *
166
+ * Matched by field `_id` ONLY — the compiler's `$elemMatch: {_id: …}` cannot
167
+ * match a name — and ALL matching entries are returned, because `$elemMatch`
168
+ * is existential: a duplicated field where one entry is null and another
169
+ * holds the value must match, so inspecting only the first entry would
170
+ * disagree with Mongo.
171
+ */
172
+ function valuesFor(entries, fdef, slot) {
173
+ return (entries !== null && entries !== void 0 ? entries : [])
174
+ .filter((e) => (e === null || e === void 0 ? void 0 : e._id) !== undefined && String(e._id) === fdef.id)
175
+ .map((e) => e[slot]);
176
+ }
177
+ function isPresent(v) {
178
+ return v !== undefined && v !== null && v !== '';
179
+ }
180
+ function matchCustomField(doc, cond, ctx) {
181
+ var _a;
182
+ const fdef = ctx.fieldByName.get(String(cond.fieldName).toLowerCase());
183
+ if (!fdef)
184
+ return false;
185
+ const slot = SLOT[fdef.dataType];
186
+ // The in-memory equivalent of the joinedType $lookup: the asset's own
187
+ // fieldValues entries, and its type's inheritance-resolved entries.
188
+ const type = ctx.typeById.get(String(doc.typeId));
189
+ const values = [
190
+ ...valuesFor(doc.fieldValues, fdef, slot),
191
+ ...valuesFor(type === null || type === void 0 ? void 0 : type.typeFieldValuesAll, fdef, slot),
192
+ ];
193
+ const anyPresent = values.some(isPresent);
194
+ switch (cond.operator) {
195
+ case 'exists':
196
+ return anyPresent;
197
+ case 'notExists':
198
+ return !anyPresent;
199
+ case 'contains': {
200
+ const term = String((_a = cond.value) !== null && _a !== void 0 ? _a : '').toLowerCase();
201
+ return values.some((v) => isPresent(v) && String(v).toLowerCase().includes(term));
202
+ }
203
+ default:
204
+ break;
205
+ }
206
+ // The SAME coercion the Mongo compiler applies — shared, not mirrored.
207
+ // A numeric literal for a text field, or a relative date for a DATE field,
208
+ // has to become one concrete value; two copies of this rule would classify
209
+ // the same document differently.
210
+ const target = (0, validate_1.coerceSemanticValue)(cond.value, fdef.dataType, ctx.now, ctx.tz);
211
+ if (cond.operator === 'ne') {
212
+ // Present-and-different at either level; default also counts fully unset.
213
+ const strict = values.some((v) => isPresent(v) && v !== target);
214
+ return cond.includeUnset === false ? strict : strict || !anyPresent;
215
+ }
216
+ return values.some((v) => cmp(v, cond.operator, target));
217
+ }
218
+ //# sourceMappingURL=evaluate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"evaluate.js","sourceRoot":"","sources":["../src/evaluate.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,248 @@
1
+ import { SemanticAccountSchema } from './types';
2
+ /**
3
+ * The conformance corpus.
4
+ *
5
+ * Shipped as part of the package, on its own entry point
6
+ * (`@spotto/semantic-query/dist/fixtures`), because it is not test scaffolding
7
+ * — it is the shared statement of the awkward cases every back-end must
8
+ * classify identically. The server's Mongo compiler runs against it, and so
9
+ * does anything else that ever claims to execute this language.
10
+ *
11
+ * Deliberately small and hand-readable. Each document exists for a reason
12
+ * stated beside it; a corpus nobody can hold in their head stops being
13
+ * evidence and starts being decoration.
14
+ */
15
+ export declare const FIXTURE_FIELDS: {
16
+ readonly SERIAL: "aaaaaaaaaaaaaaaaaaaaaa01";
17
+ readonly MANUFACTURER: "aaaaaaaaaaaaaaaaaaaaaa02";
18
+ readonly OWNERSHIP: "aaaaaaaaaaaaaaaaaaaaaa03";
19
+ readonly PURCHASE_DATE: "aaaaaaaaaaaaaaaaaaaaaa04";
20
+ readonly DECOMMISSIONED: "aaaaaaaaaaaaaaaaaaaaaa05";
21
+ readonly CALIBRATED_AT: "aaaaaaaaaaaaaaaaaaaaaa06";
22
+ };
23
+ export declare const FIXTURE_TYPES: {
24
+ readonly EPOC: "bbbbbbbbbbbbbbbbbbbbbb01";
25
+ readonly FRIDGE: "bbbbbbbbbbbbbbbbbbbbbb02";
26
+ readonly AMBULANCE: "bbbbbbbbbbbbbbbbbbbbbb03";
27
+ };
28
+ export declare const FIXTURE_MANIFEST = "cccccccccccccccccccccc01";
29
+ export declare const FIXTURE_KIT_PARENT = "dddddddddddddddddddddd01";
30
+ /**
31
+ * One organisation's catalogue: every supported data type, a dropdown with
32
+ * options, a type-level value (Manufacturer, which no asset carries directly),
33
+ * and a DATETIME field that must be refused rather than matched.
34
+ */
35
+ export declare const FIXTURE_SCHEMA: SemanticAccountSchema;
36
+ /** Carries a value for everything; its Manufacturer comes from its TYPE, not itself. */
37
+ export declare const FIXTURE_EPOC: {
38
+ _id: string;
39
+ name: string;
40
+ typeId: "bbbbbbbbbbbbbbbbbbbbbb01";
41
+ typePath: string;
42
+ fieldValues: ({
43
+ _id: "aaaaaaaaaaaaaaaaaaaaaa01";
44
+ name: string;
45
+ valueString: string;
46
+ valueDate?: undefined;
47
+ } | {
48
+ _id: "aaaaaaaaaaaaaaaaaaaaaa03";
49
+ name: string;
50
+ valueString: string;
51
+ valueDate?: undefined;
52
+ } | {
53
+ _id: "aaaaaaaaaaaaaaaaaaaaaa04";
54
+ name: string;
55
+ valueDate: number;
56
+ valueString?: undefined;
57
+ })[];
58
+ state: {
59
+ locationName: string;
60
+ locationStatus: string;
61
+ lastSeen: number;
62
+ supportsWith: boolean;
63
+ locationWithId: string;
64
+ };
65
+ readiness: {
66
+ ready: number;
67
+ readyIssues: {
68
+ reason: string;
69
+ }[];
70
+ };
71
+ tagIds: string[];
72
+ labels: {
73
+ name: string;
74
+ }[];
75
+ manifestIds: string[];
76
+ lastUpdated: number;
77
+ lastChanged: number;
78
+ groupMember: boolean;
79
+ };
80
+ /** The sparse asset: no state, no readiness, no tags, never updated. */
81
+ export declare const FIXTURE_FRIDGE: {
82
+ _id: string;
83
+ name: string;
84
+ typeId: "bbbbbbbbbbbbbbbbbbbbbb02";
85
+ typePath: string;
86
+ fieldValues: never[];
87
+ tagIds: never[];
88
+ };
89
+ /** A kit, dispatched, whose members fall short of its type's template. */
90
+ export declare const FIXTURE_AMBULANCE: {
91
+ _id: string;
92
+ name: string;
93
+ typeId: "bbbbbbbbbbbbbbbbbbbbbb03";
94
+ typePath: string;
95
+ fieldValues: ({
96
+ _id: "aaaaaaaaaaaaaaaaaaaaaa03";
97
+ name: string;
98
+ valueString: string;
99
+ valueBoolean?: undefined;
100
+ } | {
101
+ _id: "aaaaaaaaaaaaaaaaaaaaaa05";
102
+ name: string;
103
+ valueBoolean: boolean;
104
+ valueString?: undefined;
105
+ })[];
106
+ state: {
107
+ locationName: string;
108
+ locationStatus: string;
109
+ };
110
+ readiness: {
111
+ ready: number;
112
+ readyIssues: never[];
113
+ };
114
+ tagIds: never[];
115
+ lastUpdated: number;
116
+ typeKit: boolean;
117
+ groupSatisfiesKit: boolean;
118
+ };
119
+ /**
120
+ * The null-slot document. Every field it carries is present-but-null, which is
121
+ * a different shape from absent — and the axis where two back-ends most easily
122
+ * disagree (a database's "does this key exist" versus a language's "is this
123
+ * empty"). Every condition family must classify it identically on both sides.
124
+ */
125
+ export declare const FIXTURE_NULLS: {
126
+ _id: string;
127
+ name: string;
128
+ typeId: "bbbbbbbbbbbbbbbbbbbbbb03";
129
+ typePath: string;
130
+ fieldValues: {
131
+ _id: "aaaaaaaaaaaaaaaaaaaaaa03";
132
+ name: string;
133
+ valueString: null;
134
+ }[];
135
+ state: {
136
+ locationWithId: null;
137
+ locationName: string;
138
+ };
139
+ tagIds: null;
140
+ lastUpdated: null;
141
+ telemetry: {
142
+ battery: null;
143
+ };
144
+ };
145
+ export declare const FIXTURE_CORPUS: ({
146
+ _id: string;
147
+ name: string;
148
+ typeId: "bbbbbbbbbbbbbbbbbbbbbb01";
149
+ typePath: string;
150
+ fieldValues: ({
151
+ _id: "aaaaaaaaaaaaaaaaaaaaaa01";
152
+ name: string;
153
+ valueString: string;
154
+ valueDate?: undefined;
155
+ } | {
156
+ _id: "aaaaaaaaaaaaaaaaaaaaaa03";
157
+ name: string;
158
+ valueString: string;
159
+ valueDate?: undefined;
160
+ } | {
161
+ _id: "aaaaaaaaaaaaaaaaaaaaaa04";
162
+ name: string;
163
+ valueDate: number;
164
+ valueString?: undefined;
165
+ })[];
166
+ state: {
167
+ locationName: string;
168
+ locationStatus: string;
169
+ lastSeen: number;
170
+ supportsWith: boolean;
171
+ locationWithId: string;
172
+ };
173
+ readiness: {
174
+ ready: number;
175
+ readyIssues: {
176
+ reason: string;
177
+ }[];
178
+ };
179
+ tagIds: string[];
180
+ labels: {
181
+ name: string;
182
+ }[];
183
+ manifestIds: string[];
184
+ lastUpdated: number;
185
+ lastChanged: number;
186
+ groupMember: boolean;
187
+ } | {
188
+ _id: string;
189
+ name: string;
190
+ typeId: "bbbbbbbbbbbbbbbbbbbbbb02";
191
+ typePath: string;
192
+ fieldValues: never[];
193
+ tagIds: never[];
194
+ } | {
195
+ _id: string;
196
+ name: string;
197
+ typeId: "bbbbbbbbbbbbbbbbbbbbbb03";
198
+ typePath: string;
199
+ fieldValues: ({
200
+ _id: "aaaaaaaaaaaaaaaaaaaaaa03";
201
+ name: string;
202
+ valueString: string;
203
+ valueBoolean?: undefined;
204
+ } | {
205
+ _id: "aaaaaaaaaaaaaaaaaaaaaa05";
206
+ name: string;
207
+ valueBoolean: boolean;
208
+ valueString?: undefined;
209
+ })[];
210
+ state: {
211
+ locationName: string;
212
+ locationStatus: string;
213
+ };
214
+ readiness: {
215
+ ready: number;
216
+ readyIssues: never[];
217
+ };
218
+ tagIds: never[];
219
+ lastUpdated: number;
220
+ typeKit: boolean;
221
+ groupSatisfiesKit: boolean;
222
+ } | {
223
+ _id: string;
224
+ name: string;
225
+ typeId: "bbbbbbbbbbbbbbbbbbbbbb03";
226
+ typePath: string;
227
+ fieldValues: {
228
+ _id: "aaaaaaaaaaaaaaaaaaaaaa03";
229
+ name: string;
230
+ valueString: null;
231
+ }[];
232
+ state: {
233
+ locationWithId: null;
234
+ locationName: string;
235
+ };
236
+ tagIds: null;
237
+ lastUpdated: null;
238
+ telemetry: {
239
+ battery: null;
240
+ };
241
+ })[];
242
+ /** Frozen clock for the corpus — 2026-07-27 09:00 in Brisbane. */
243
+ export declare const FIXTURE_NOW: Date;
244
+ export declare const FIXTURE_TZ = "Australia/Brisbane";
245
+ export declare const FIXTURE_OPTS: {
246
+ now: Date;
247
+ timeZone: string;
248
+ };