@deepwatch/dsh-contracts 0.1.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.
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Rules about how results may be presented.
3
+ *
4
+ * These live in the contracts package rather than in a component because they
5
+ * are product rules, not styling choices. "Green belongs to VERIFIED alone" is
6
+ * the same commitment whether it is rendered by a React card, a terminal
7
+ * summary or a CI annotation, and keeping it here means there is one place to
8
+ * change it and one place to test it.
9
+ *
10
+ * Everything below is a pure function over data that already crossed the wire.
11
+ * No DOM, no React, no Node.
12
+ *
13
+ * @module @deepwatch/dsh-contracts/presentation
14
+ */
15
+ import type { Freshness, Verdict } from './index.js';
16
+ /** How a result is allowed to be presented. */
17
+ export type ResultTone = 'success' | 'error' | 'caution';
18
+ /**
19
+ * The tone one verdict is rendered in.
20
+ *
21
+ * `INCONCLUSIVE`, `STALE`, `BLOCKED` and `UNVERIFIED` share the caution tone
22
+ * on purpose. They are not failures, and styling them as errors teaches people
23
+ * to dismiss them — which is how an unproven result comes to be accepted as a
24
+ * proven one. They are also not successes, which is the more obvious half.
25
+ */
26
+ export declare function verdictTone(verdict: Verdict): ResultTone;
27
+ /** How a freshness state should be labelled, or null when it needs no label. */
28
+ export declare function freshnessLabel(freshness: Freshness): string | null;
29
+ /** One check inside a verification contract, as a result carries it. */
30
+ export interface PresentableCheck {
31
+ readonly checkId: string;
32
+ readonly kind: string;
33
+ readonly description: string | null;
34
+ /**
35
+ * Tri-state on purpose. A check that could not run is not a check that
36
+ * failed, and flattening the two turns "we did not look" into "it is broken"
37
+ * — or, worse, the reverse.
38
+ */
39
+ readonly passed: boolean | null;
40
+ readonly detail: string | null;
41
+ }
42
+ /** A verification result, ready to render. */
43
+ export interface PresentableVerdict {
44
+ readonly verdict: Verdict;
45
+ readonly reason: string;
46
+ readonly checks: readonly PresentableCheck[];
47
+ readonly contractDigest: string;
48
+ readonly assurance: string | null;
49
+ }
50
+ /**
51
+ * Parse a tool result into a verdict.
52
+ *
53
+ * Returns null rather than guessing. A result this cannot read renders as a
54
+ * generic row, which is honest; inventing a verdict to fill a card would not
55
+ * be.
56
+ */
57
+ export declare function parseVerdict(value: unknown): PresentableVerdict | null;
58
+ /** One cited moment, ready to render. */
59
+ export interface PresentableCitation {
60
+ readonly evidenceId: string;
61
+ readonly text: string;
62
+ /** Milliseconds into the source, or null for a citation with no timing. */
63
+ readonly atMs: number | null;
64
+ readonly modality: string;
65
+ readonly provenance: string;
66
+ readonly freshness: Freshness;
67
+ }
68
+ /** An evidence-linked answer, ready to render. */
69
+ export interface PresentableAnswer {
70
+ readonly answer: string;
71
+ readonly citations: readonly PresentableCitation[];
72
+ /** The engine's own assessment of whether it had enough to answer. */
73
+ readonly groundedness: 'sufficient' | 'insufficient' | null;
74
+ }
75
+ /**
76
+ * Parse a source-query result into an answer.
77
+ *
78
+ * Returns null for a refusal or an unreadable payload, so a failure never
79
+ * renders as a grounded answer with nothing behind it.
80
+ */
81
+ export declare function parseAnswer(value: unknown): PresentableAnswer | null;
82
+ /**
83
+ * Format a media position the way a person reads one.
84
+ *
85
+ * @returns `m:ss` under an hour, `h:mm:ss` above it, or null when there is no
86
+ * usable timestamp — which a caller renders as no timestamp rather than as
87
+ * `0:00`, because those mean different things.
88
+ */
89
+ export declare function formatTimestamp(atMs: number | null): string | null;
90
+ //# sourceMappingURL=presentation.d.ts.map
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Rules about how results may be presented.
3
+ *
4
+ * These live in the contracts package rather than in a component because they
5
+ * are product rules, not styling choices. "Green belongs to VERIFIED alone" is
6
+ * the same commitment whether it is rendered by a React card, a terminal
7
+ * summary or a CI annotation, and keeping it here means there is one place to
8
+ * change it and one place to test it.
9
+ *
10
+ * Everything below is a pure function over data that already crossed the wire.
11
+ * No DOM, no React, no Node.
12
+ *
13
+ * @module @deepwatch/dsh-contracts/presentation
14
+ */
15
+ /**
16
+ * The tone one verdict is rendered in.
17
+ *
18
+ * `INCONCLUSIVE`, `STALE`, `BLOCKED` and `UNVERIFIED` share the caution tone
19
+ * on purpose. They are not failures, and styling them as errors teaches people
20
+ * to dismiss them — which is how an unproven result comes to be accepted as a
21
+ * proven one. They are also not successes, which is the more obvious half.
22
+ */
23
+ export function verdictTone(verdict) {
24
+ if (verdict === 'VERIFIED')
25
+ return 'success';
26
+ if (verdict === 'FAILED')
27
+ return 'error';
28
+ return 'caution';
29
+ }
30
+ /** The sentence to show when Watch Core supplied no reason of its own. */
31
+ const FALLBACK_REASON = {
32
+ VERIFIED: 'Every required check passed against valid evidence.',
33
+ FAILED: 'A required check failed.',
34
+ UNVERIFIED: 'Nothing executable was checked, so nothing was established.',
35
+ INCONCLUSIVE: 'The evidence conflicts, or a check could not be run.',
36
+ STALE: 'The evidence no longer describes the current source.',
37
+ BLOCKED: 'Policy or a missing dependency prevented verification.',
38
+ };
39
+ /** Every verdict the taxonomy defines, for exhaustive validation. */
40
+ const VERDICTS = new Set([
41
+ 'VERIFIED', 'FAILED', 'UNVERIFIED', 'INCONCLUSIVE', 'STALE', 'BLOCKED',
42
+ ]);
43
+ /** Wording per freshness state, so the distinction survives without colour. */
44
+ const FRESHNESS_LABEL = {
45
+ current: null,
46
+ stale: 'stale',
47
+ gap: 'gap in capture',
48
+ expired: 'expired',
49
+ unavailable: 'freshness unknown',
50
+ };
51
+ /** How a freshness state should be labelled, or null when it needs no label. */
52
+ export function freshnessLabel(freshness) {
53
+ return FRESHNESS_LABEL[freshness];
54
+ }
55
+ /**
56
+ * Parse a tool result into a verdict.
57
+ *
58
+ * Returns null rather than guessing. A result this cannot read renders as a
59
+ * generic row, which is honest; inventing a verdict to fill a card would not
60
+ * be.
61
+ */
62
+ export function parseVerdict(value) {
63
+ const record = asRecord(value);
64
+ if (record === null)
65
+ return null;
66
+ const verdict = record['verdict'];
67
+ if (typeof verdict !== 'string' || !VERDICTS.has(verdict))
68
+ return null;
69
+ const checks = Array.isArray(record['checks']) ? record['checks'] : [];
70
+ const reason = record['reason'];
71
+ return {
72
+ verdict: verdict,
73
+ reason: typeof reason === 'string' && reason !== ''
74
+ ? reason
75
+ : FALLBACK_REASON[verdict],
76
+ checks: checks.flatMap(parseCheck),
77
+ contractDigest: typeof record['contractDigest'] === 'string' ? record['contractDigest'] : '',
78
+ assurance: typeof record['assurance'] === 'string' ? record['assurance'] : null,
79
+ };
80
+ }
81
+ function parseCheck(value) {
82
+ const record = asRecord(value);
83
+ if (record === null || typeof record['checkId'] !== 'string')
84
+ return [];
85
+ return [{
86
+ checkId: record['checkId'],
87
+ kind: typeof record['kind'] === 'string' ? record['kind'] : 'check',
88
+ description: typeof record['description'] === 'string' ? record['description'] : null,
89
+ passed: typeof record['passed'] === 'boolean' ? record['passed'] : null,
90
+ detail: typeof record['detail'] === 'string' ? record['detail'] : null,
91
+ }];
92
+ }
93
+ /**
94
+ * Parse a source-query result into an answer.
95
+ *
96
+ * Returns null for a refusal or an unreadable payload, so a failure never
97
+ * renders as a grounded answer with nothing behind it.
98
+ */
99
+ export function parseAnswer(value) {
100
+ const record = asRecord(value);
101
+ if (record === null || record['ok'] !== true)
102
+ return null;
103
+ if (typeof record['answer'] !== 'string')
104
+ return null;
105
+ const evidence = Array.isArray(record['evidence']) ? record['evidence'] : [];
106
+ const groundedness = record['groundedness'];
107
+ return {
108
+ answer: record['answer'],
109
+ citations: evidence.flatMap(parseCitation),
110
+ groundedness: groundedness === 'sufficient' || groundedness === 'insufficient'
111
+ ? groundedness
112
+ : null,
113
+ };
114
+ }
115
+ function parseCitation(value) {
116
+ const record = asRecord(value);
117
+ if (record === null || typeof record['evidenceId'] !== 'string')
118
+ return [];
119
+ const range = asRecord(record['temporalRange']);
120
+ const start = range === null ? undefined : range['startMs'];
121
+ const freshness = record['freshness'];
122
+ return [{
123
+ evidenceId: record['evidenceId'],
124
+ text: typeof record['text'] === 'string' ? record['text'] : '',
125
+ atMs: typeof start === 'number' && Number.isFinite(start) ? start : null,
126
+ modality: typeof record['modality'] === 'string' ? record['modality'] : 'text',
127
+ provenance: typeof record['provenance'] === 'string' ? record['provenance'] : 'observation',
128
+ // An unrecognized value becomes `unavailable`, never `current`. Defaulting
129
+ // an unknown to the reassuring answer is exactly the wrong direction.
130
+ freshness: typeof freshness === 'string' && freshness in FRESHNESS_LABEL
131
+ ? freshness
132
+ : 'unavailable',
133
+ }];
134
+ }
135
+ /**
136
+ * Format a media position the way a person reads one.
137
+ *
138
+ * @returns `m:ss` under an hour, `h:mm:ss` above it, or null when there is no
139
+ * usable timestamp — which a caller renders as no timestamp rather than as
140
+ * `0:00`, because those mean different things.
141
+ */
142
+ export function formatTimestamp(atMs) {
143
+ if (atMs === null || !Number.isFinite(atMs))
144
+ return null;
145
+ const total = Math.max(0, Math.floor(atMs / 1000));
146
+ const seconds = String(total % 60).padStart(2, '0');
147
+ if (total < 3600)
148
+ return `${String(Math.floor(total / 60))}:${seconds}`;
149
+ const minutes = String(Math.floor(total / 60) % 60).padStart(2, '0');
150
+ return `${String(Math.floor(total / 3600))}:${minutes}:${seconds}`;
151
+ }
152
+ /** Narrow an unknown to a plain object, excluding null and arrays. */
153
+ function asRecord(value) {
154
+ if (typeof value !== 'object' || value === null || Array.isArray(value))
155
+ return null;
156
+ return value;
157
+ }
158
+ //# sourceMappingURL=presentation.js.map
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Semantic validation for the Library read plane.
3
+ *
4
+ * The generated Typert codec proves *shape*: that `limit` is a number and
5
+ * `modalities` is an array of strings. It does not prove that the number is
6
+ * within a range anybody intended, that the array is a length the host will
7
+ * answer for, or that a record id is an identifier rather than a path. Those
8
+ * are policy, and a structural codec has no opinion about policy.
9
+ *
10
+ * There is one measured behaviour worth stating plainly, because it is easy to
11
+ * assume the opposite. The generated codec is emitted as a plain `z.object`,
12
+ * with neither `.strict()` nor `.passthrough()`, so **unknown fields are
13
+ * stripped, not rejected**. Typert's own `mode: 'strict'` refers to strict
14
+ * codec generation and not to zod's strict object mode. A caller can therefore
15
+ * send extra fields and get a successful call; what it cannot do is have those
16
+ * fields reach anything, because what continues past the boundary is the parsed
17
+ * value and not the input. That is a defensible policy and it is not the one a
18
+ * reader would guess, so it is asserted in the tests rather than described.
19
+ *
20
+ * These functions take `unknown` deliberately, which is why they live here and
21
+ * not in the wire module: `query/wire` describes what crosses the wire and must
22
+ * stay free of parsers, and this describes what the host will accept.
23
+ *
24
+ * Order matters. Everything here runs before the index is touched, so an
25
+ * oversized or malformed request costs a bounds check and never a search.
26
+ *
27
+ * @module @deepwatch/dsh-contracts/query/validate
28
+ */
29
+ import type { CoreHealthRequest, LibraryGetRequest, LibraryRefreshRequest, LibraryRequestRejected, LibrarySearchRequest } from './wire.js';
30
+ /** The modalities the Library indexes. A closed set, not free text. */
31
+ export declare const LIBRARY_MODALITIES: readonly ["video", "audio", "page", "stream", "document", "screen_capture"];
32
+ /** Accepted, or the refusal to send back. */
33
+ export type Validated<T> = {
34
+ readonly ok: true;
35
+ readonly value: T;
36
+ } | {
37
+ readonly ok: false;
38
+ readonly refusal: LibraryRequestRejected;
39
+ };
40
+ /**
41
+ * Accept a Library search, or say why not.
42
+ *
43
+ * Normalises `limit` and `deadlineMs` rather than refusing them, and refuses
44
+ * everything that is a statement about what the caller may name: the query
45
+ * length, the modality vocabulary, and the cursor length.
46
+ */
47
+ export declare function parseLibrarySearchRequest(value: unknown): Validated<LibrarySearchRequest>;
48
+ /**
49
+ * Accept a Library get, or say why not.
50
+ *
51
+ * `recordId` is held to the identifier grammar, which has no separator, no
52
+ * colon and no dot-dot — so it cannot carry a filesystem path, a UNC share or
53
+ * a storage URL. The host decides where it reads; the caller says which record.
54
+ */
55
+ export declare function parseLibraryGetRequest(value: unknown): Validated<LibraryGetRequest>;
56
+ /**
57
+ * Accept a Library refresh, or say why not.
58
+ *
59
+ * The envelope and nothing else. A refresh names no record, no query and no
60
+ * location — it asks the host to read the roots it was configured with, which
61
+ * is the only reason it can be a safe operation to expose at all. There is no
62
+ * field here a caller could point somewhere.
63
+ */
64
+ export declare function parseLibraryRefreshRequest(value: unknown): Validated<LibraryRefreshRequest>;
65
+ /**
66
+ * Validate a `coreHealth` request.
67
+ *
68
+ * The envelope and nothing else. A health read takes no parameters on purpose:
69
+ * every field of the answer is something the Host observed, so there is
70
+ * nothing for a caller to select and nothing it could select that would change
71
+ * what is true.
72
+ */
73
+ export declare function parseCoreHealthRequest(value: unknown): Validated<CoreHealthRequest>;
74
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Semantic validation for the Library read plane.
3
+ *
4
+ * The generated Typert codec proves *shape*: that `limit` is a number and
5
+ * `modalities` is an array of strings. It does not prove that the number is
6
+ * within a range anybody intended, that the array is a length the host will
7
+ * answer for, or that a record id is an identifier rather than a path. Those
8
+ * are policy, and a structural codec has no opinion about policy.
9
+ *
10
+ * There is one measured behaviour worth stating plainly, because it is easy to
11
+ * assume the opposite. The generated codec is emitted as a plain `z.object`,
12
+ * with neither `.strict()` nor `.passthrough()`, so **unknown fields are
13
+ * stripped, not rejected**. Typert's own `mode: 'strict'` refers to strict
14
+ * codec generation and not to zod's strict object mode. A caller can therefore
15
+ * send extra fields and get a successful call; what it cannot do is have those
16
+ * fields reach anything, because what continues past the boundary is the parsed
17
+ * value and not the input. That is a defensible policy and it is not the one a
18
+ * reader would guess, so it is asserted in the tests rather than described.
19
+ *
20
+ * These functions take `unknown` deliberately, which is why they live here and
21
+ * not in the wire module: `query/wire` describes what crosses the wire and must
22
+ * stay free of parsers, and this describes what the host will accept.
23
+ *
24
+ * Order matters. Everything here runs before the index is touched, so an
25
+ * oversized or malformed request costs a bounds check and never a search.
26
+ *
27
+ * @module @deepwatch/dsh-contracts/query/validate
28
+ */
29
+ import { QUERY_LIMITS, isIdentifier, isSafeCount } from '../query.js';
30
+ import { WATCH_QUERY_WIRE_VERSION } from './wire.js';
31
+ /** The modalities the Library indexes. A closed set, not free text. */
32
+ export const LIBRARY_MODALITIES = [
33
+ 'video', 'audio', 'page', 'stream', 'document', 'screen_capture',
34
+ ];
35
+ /** Build the refusal a surface renders. */
36
+ function reject(requestId, reason, field) {
37
+ return {
38
+ ok: false,
39
+ refusal: {
40
+ outcome: 'rejected',
41
+ protocol: WATCH_QUERY_WIRE_VERSION,
42
+ // Echoed only when it is already a safe identifier: a refusal must not
43
+ // become a way to have arbitrary text reflected back.
44
+ requestId: isIdentifier(requestId) ? requestId : '',
45
+ reason,
46
+ field,
47
+ },
48
+ };
49
+ }
50
+ /**
51
+ * How large the request is once serialised.
52
+ *
53
+ * Measured before anything is walked, because the cost of rejecting a
54
+ * malformed request must not be a function of how malformed it is. A value
55
+ * that cannot be serialised at all — a cycle — is refused for the same reason.
56
+ */
57
+ function withinSizeBudget(value) {
58
+ try {
59
+ return JSON.stringify(value)?.length <= QUERY_LIMITS.requestBytes;
60
+ }
61
+ catch {
62
+ return false;
63
+ }
64
+ }
65
+ /** Whether an object nests deeper than the budget allows. */
66
+ function withinDepth(value, depth = 0) {
67
+ if (depth > QUERY_LIMITS.depth)
68
+ return false;
69
+ if (Array.isArray(value))
70
+ return value.every(entry => withinDepth(entry, depth + 1));
71
+ if (typeof value === 'object' && value !== null) {
72
+ return Object.values(value).every(entry => withinDepth(entry, depth + 1));
73
+ }
74
+ return true;
75
+ }
76
+ /** The envelope fields every Library request carries. */
77
+ function envelope(raw) {
78
+ if (!isSafeCount(raw.protocol) || raw.protocol !== WATCH_QUERY_WIRE_VERSION) {
79
+ return reject(raw.requestId, 'protocol_mismatch', 'protocol');
80
+ }
81
+ if (typeof raw.requestId !== 'string'
82
+ || raw.requestId === ''
83
+ || raw.requestId.length > QUERY_LIMITS.requestIdLength
84
+ || !isIdentifier(raw.requestId)) {
85
+ return reject(raw.requestId, 'identifier_invalid', 'requestId');
86
+ }
87
+ if (!isSafeCount(raw.deadlineMs) || raw.deadlineMs <= 0) {
88
+ return reject(raw.requestId, 'malformed_request', 'deadlineMs');
89
+ }
90
+ return {
91
+ ok: true,
92
+ value: {
93
+ protocol: raw.protocol,
94
+ requestId: raw.requestId,
95
+ // Normalised rather than refused: a caller asking to wait longer than the
96
+ // host will wait is not making a mistake worth failing over.
97
+ deadlineMs: Math.min(raw.deadlineMs, QUERY_LIMITS.deadlineMs),
98
+ },
99
+ };
100
+ }
101
+ /** The common front half of both validators. */
102
+ function opened(value) {
103
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
104
+ return reject(undefined, 'malformed_request', null);
105
+ }
106
+ if (!withinSizeBudget(value))
107
+ return reject(value.requestId, 'request_too_large', null);
108
+ if (!withinDepth(value))
109
+ return reject(value.requestId, 'request_too_large', null);
110
+ return { ok: true, value: value };
111
+ }
112
+ /**
113
+ * Accept a Library search, or say why not.
114
+ *
115
+ * Normalises `limit` and `deadlineMs` rather than refusing them, and refuses
116
+ * everything that is a statement about what the caller may name: the query
117
+ * length, the modality vocabulary, and the cursor length.
118
+ */
119
+ export function parseLibrarySearchRequest(value) {
120
+ const open = opened(value);
121
+ if (!open.ok)
122
+ return open;
123
+ const raw = open.value;
124
+ const head = envelope(raw);
125
+ if (!head.ok)
126
+ return head;
127
+ if (typeof raw.query !== 'string' || raw.query.length > QUERY_LIMITS.queryLength) {
128
+ return reject(raw.requestId, 'malformed_request', 'query');
129
+ }
130
+ if (!Array.isArray(raw.modalities) || raw.modalities.length > QUERY_LIMITS.arrayLength) {
131
+ return reject(raw.requestId, 'malformed_request', 'modalities');
132
+ }
133
+ // Collected while checking rather than spread afterwards: `Array.isArray`
134
+ // narrows to `any[]`, and spreading that would carry an `any` into a value
135
+ // the rest of the host treats as validated.
136
+ const modalities = [];
137
+ for (const modality of raw.modalities) {
138
+ if (typeof modality !== 'string'
139
+ || !LIBRARY_MODALITIES.includes(modality)) {
140
+ return reject(raw.requestId, 'malformed_request', 'modalities');
141
+ }
142
+ modalities.push(modality);
143
+ }
144
+ if (raw.cursor !== null && raw.cursor !== undefined) {
145
+ if (typeof raw.cursor !== 'string' || raw.cursor.length > QUERY_LIMITS.cursorLength) {
146
+ return reject(raw.requestId, 'malformed_request', 'cursor');
147
+ }
148
+ }
149
+ if (raw.limit !== undefined && !isSafeCount(raw.limit)) {
150
+ return reject(raw.requestId, 'malformed_request', 'limit');
151
+ }
152
+ return {
153
+ ok: true,
154
+ value: {
155
+ protocol: head.value.protocol,
156
+ requestId: head.value.requestId,
157
+ deadlineMs: head.value.deadlineMs,
158
+ query: raw.query,
159
+ modalities,
160
+ limit: Math.min(typeof raw.limit === 'number' && raw.limit > 0 ? raw.limit : QUERY_LIMITS.limit, QUERY_LIMITS.limit),
161
+ cursor: typeof raw.cursor === 'string' ? raw.cursor : null,
162
+ },
163
+ };
164
+ }
165
+ /**
166
+ * Accept a Library get, or say why not.
167
+ *
168
+ * `recordId` is held to the identifier grammar, which has no separator, no
169
+ * colon and no dot-dot — so it cannot carry a filesystem path, a UNC share or
170
+ * a storage URL. The host decides where it reads; the caller says which record.
171
+ */
172
+ export function parseLibraryGetRequest(value) {
173
+ const open = opened(value);
174
+ if (!open.ok)
175
+ return open;
176
+ const raw = open.value;
177
+ const head = envelope(raw);
178
+ if (!head.ok)
179
+ return head;
180
+ if (typeof raw.recordId !== 'string'
181
+ || raw.recordId.length > QUERY_LIMITS.identifierLength
182
+ || !isIdentifier(raw.recordId)) {
183
+ return reject(raw.requestId, 'identifier_invalid', 'recordId');
184
+ }
185
+ return {
186
+ ok: true,
187
+ value: {
188
+ protocol: head.value.protocol,
189
+ requestId: head.value.requestId,
190
+ deadlineMs: head.value.deadlineMs,
191
+ recordId: raw.recordId,
192
+ },
193
+ };
194
+ }
195
+ /**
196
+ * Accept a Library refresh, or say why not.
197
+ *
198
+ * The envelope and nothing else. A refresh names no record, no query and no
199
+ * location — it asks the host to read the roots it was configured with, which
200
+ * is the only reason it can be a safe operation to expose at all. There is no
201
+ * field here a caller could point somewhere.
202
+ */
203
+ export function parseLibraryRefreshRequest(value) {
204
+ const open = opened(value);
205
+ if (!open.ok)
206
+ return open;
207
+ const head = envelope(open.value);
208
+ if (!head.ok)
209
+ return head;
210
+ return {
211
+ ok: true,
212
+ value: {
213
+ protocol: head.value.protocol,
214
+ requestId: head.value.requestId,
215
+ deadlineMs: head.value.deadlineMs,
216
+ },
217
+ };
218
+ }
219
+ /**
220
+ * Validate a `coreHealth` request.
221
+ *
222
+ * The envelope and nothing else. A health read takes no parameters on purpose:
223
+ * every field of the answer is something the Host observed, so there is
224
+ * nothing for a caller to select and nothing it could select that would change
225
+ * what is true.
226
+ */
227
+ export function parseCoreHealthRequest(value) {
228
+ const open = opened(value);
229
+ if (!open.ok)
230
+ return open;
231
+ const head = envelope(open.value);
232
+ if (!head.ok)
233
+ return head;
234
+ return {
235
+ ok: true,
236
+ value: {
237
+ protocol: head.value.protocol,
238
+ requestId: head.value.requestId,
239
+ deadlineMs: head.value.deadlineMs,
240
+ },
241
+ };
242
+ }
243
+ //# sourceMappingURL=validate.js.map