@open-operational-state/parser 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.
package/README.md ADDED
@@ -0,0 +1,63 @@
1
+ # @open-operational-state/parser
2
+
3
+ Response parsers and format adapters for [Open Operational State](https://github.com/open-operational-state). Auto-detects response formats and converts them to the canonical core model.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ bun add @open-operational-state/parser
9
+ ```
10
+
11
+ ## API
12
+
13
+ ### `parse( input )`
14
+
15
+ Auto-detect format and parse an HTTP response into a `Snapshot`.
16
+
17
+ ```js
18
+ import { parse } from '@open-operational-state/parser';
19
+
20
+ const snapshot = parse({
21
+ contentType: 'application/json',
22
+ body: { status: 'pass', serviceId: 'my-api' },
23
+ url: 'https://api.example.com/health',
24
+ httpStatus: 200,
25
+ headers: { 'content-type': 'application/json' },
26
+ });
27
+ ```
28
+
29
+ ### `detectFormat( contentType, body )`
30
+
31
+ Identify which adapter to apply based on content-type and structural markers.
32
+
33
+ ```js
34
+ import { detectFormat } from '@open-operational-state/parser';
35
+
36
+ detectFormat( 'application/health+json', body ); // 'native-health-response'
37
+ detectFormat( 'application/status+json', body ); // 'native-service-status'
38
+ detectFormat( 'application/json', { status: 'pass' } ); // 'health-check-draft'
39
+ detectFormat( 'application/json', { status: 'UP', components: {} } ); // 'spring-boot'
40
+ detectFormat( 'text/plain', 'OK' ); // 'plain-http'
41
+ ```
42
+
43
+ ### Individual Adapters
44
+
45
+ | Adapter | Function | Input |
46
+ |---|---|---|
47
+ | **Plain HTTP** | `parsePlainHttp( input )` | HTTP status code + connection result |
48
+ | **Health Check Draft** | `parseHealthCheckDraft( body, options? )` | [draft-inadarei](https://datatracker.ietf.org/doc/html/draft-inadarei-api-health-check) response |
49
+ | **Spring Boot** | `parseSpringBoot( body, options? )` | Spring Boot Actuator `/actuator/health` response |
50
+ | **Native** | `parseHealthResponse( body )` | OOS `application/health+json` response |
51
+
52
+ ### Format Detection Priority
53
+
54
+ 1. Content-type header (`application/status+json`, `application/health+json`)
55
+ 2. Structural markers (OOS native: `profiles` + `condition`)
56
+ 3. Structural markers (Spring Boot: `status` ∈ {UP,DOWN,OUT_OF_SERVICE} + `components`)
57
+ 4. Structural markers (draft-inadarei: `status` ∈ {pass,fail,warn})
58
+ 5. Fallback to plain-http
59
+
60
+ ## Dependencies
61
+
62
+ - `@open-operational-state/types`
63
+ - `@open-operational-state/core`
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Parser Tests
3
+ *
4
+ * Tests adapter parsers against conformance fixtures from status-conformance.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=parser.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/parser.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,277 @@
1
+ /**
2
+ * Parser Tests
3
+ *
4
+ * Tests adapter parsers against conformance fixtures from status-conformance.
5
+ */
6
+ import { describe, it, expect } from 'bun:test';
7
+ import { resolve, join } from 'node:path';
8
+ import { readFileSync, existsSync } from 'node:fs';
9
+ import { parsePlainHttp } from '../plain-http.js';
10
+ import { parseHealthCheckDraft } from '../health-check-draft.js';
11
+ import { parseHealthResponse } from '../health-response.js';
12
+ import { parseSpringBoot } from '../spring-boot.js';
13
+ import { detectFormat } from '../detect.js';
14
+ import { parse, safeParse } from '../parse.js';
15
+ const FIXTURE_ROOT = process.env.OOS_FIXTURE_ROOT
16
+ || resolve(import.meta.dir, '../../../../..', 'status-conformance');
17
+ function loadJson(relPath) {
18
+ const fullPath = join(FIXTURE_ROOT, relPath);
19
+ if (!existsSync(fullPath)) {
20
+ throw new Error(`Fixture not found: ${fullPath}`);
21
+ }
22
+ return JSON.parse(readFileSync(fullPath, 'utf-8'));
23
+ }
24
+ // ---------------------------------------------------------------------------
25
+ // Format detection
26
+ // ---------------------------------------------------------------------------
27
+ describe('detectFormat', () => {
28
+ it('detects native health-response by content type', () => {
29
+ const body = { condition: 'operational', profiles: ['health'], subject: { id: 'x' } };
30
+ expect(detectFormat('application/health+json', body)).toBe('native-health-response');
31
+ });
32
+ it('detects native service-status by content type', () => {
33
+ expect(detectFormat('application/status+json', {})).toBe('native-service-status');
34
+ });
35
+ it('detects draft-inadarei by status field', () => {
36
+ const body = { status: 'pass', serviceId: 'test' };
37
+ expect(detectFormat('application/json', body)).toBe('health-check-draft');
38
+ });
39
+ it('falls back to plain-http for unrecognized', () => {
40
+ expect(detectFormat('text/plain', 'OK')).toBe('plain-http');
41
+ });
42
+ });
43
+ // ---------------------------------------------------------------------------
44
+ // Plain HTTP adapter
45
+ // ---------------------------------------------------------------------------
46
+ describe('plain-http adapter', () => {
47
+ it('parses fixture: positive-200', () => {
48
+ const fixture = loadJson('adapters/plain-http/positive-200.json');
49
+ const input = fixture.input;
50
+ const expected = fixture.expected;
51
+ const result = parsePlainHttp({
52
+ url: input.url,
53
+ httpStatus: input.http_status,
54
+ headers: input.headers,
55
+ });
56
+ expect(result.condition).toBe(expected.condition);
57
+ expect(result.profiles).toEqual(expected.profiles);
58
+ expect(result.subject.id).toBe(expected.subject.id);
59
+ expect(result.provenance).toBe(expected.provenance);
60
+ });
61
+ it('maps connection error to unreachable', () => {
62
+ const result = parsePlainHttp({
63
+ url: 'https://api.example.com/health',
64
+ connectionError: true,
65
+ });
66
+ expect(result.condition).toBe('unreachable');
67
+ expect(result.profiles).toEqual(['liveness']);
68
+ expect(result.provenance).toBe('externally-observed');
69
+ });
70
+ it('maps 500 to alive (service responded)', () => {
71
+ const result = parsePlainHttp({
72
+ url: 'https://api.example.com/health',
73
+ httpStatus: 500,
74
+ });
75
+ expect(result.condition).toBe('alive');
76
+ });
77
+ });
78
+ // ---------------------------------------------------------------------------
79
+ // Health-check-draft adapter
80
+ // ---------------------------------------------------------------------------
81
+ describe('health-check-draft adapter', () => {
82
+ it('parses fixture: positive-pass-with-checks', () => {
83
+ const fixture = loadJson('adapters/health-check-draft/positive-pass-with-checks.json');
84
+ const input = fixture.input;
85
+ const expected = fixture.expected;
86
+ const result = parseHealthCheckDraft(input.body);
87
+ expect(result.condition).toBe(expected.condition);
88
+ expect(result.subject.id).toBe(expected.subject.id);
89
+ expect(result.provenance).toBe(expected.provenance);
90
+ expect(result.checks).toBeDefined();
91
+ if (result.checks && expected.checks) {
92
+ const expectedChecks = expected.checks;
93
+ for (const [key, expectedCheck] of Object.entries(expectedChecks)) {
94
+ expect(result.checks[key]).toBeDefined();
95
+ expect(result.checks[key].condition).toBe(expectedCheck.condition);
96
+ expect(result.checks[key].role).toBe(expectedCheck.role);
97
+ }
98
+ }
99
+ });
100
+ it('maps warn to degraded', () => {
101
+ const result = parseHealthCheckDraft({
102
+ status: 'warn',
103
+ serviceId: 'test-service',
104
+ });
105
+ expect(result.condition).toBe('degraded');
106
+ });
107
+ it('maps fail to down', () => {
108
+ const result = parseHealthCheckDraft({
109
+ status: 'fail',
110
+ serviceId: 'test-service',
111
+ });
112
+ expect(result.condition).toBe('down');
113
+ });
114
+ it('maps pass to operational', () => {
115
+ const result = parseHealthCheckDraft({
116
+ status: 'pass',
117
+ serviceId: 'test-service',
118
+ });
119
+ expect(result.condition).toBe('operational');
120
+ });
121
+ });
122
+ // ---------------------------------------------------------------------------
123
+ // Native health-response parser
124
+ // ---------------------------------------------------------------------------
125
+ describe('native health-response parser', () => {
126
+ it('parses fixture: positive-full', () => {
127
+ const fixture = loadJson('fixtures/serializations/health-response/positive-full.json');
128
+ const input = fixture.input;
129
+ const result = parseHealthResponse(input);
130
+ expect(result.condition).toBeDefined();
131
+ expect(result.profiles).toBeDefined();
132
+ expect(result.subject.id).toBeDefined();
133
+ });
134
+ });
135
+ // ---------------------------------------------------------------------------
136
+ // Spring Boot adapter
137
+ // ---------------------------------------------------------------------------
138
+ describe('spring-boot adapter', () => {
139
+ it('parses fixture: positive-up-with-components', () => {
140
+ const fixture = loadJson('adapters/spring-boot/positive-up-with-components.json');
141
+ const input = fixture.input;
142
+ const expected = fixture.expected;
143
+ const result = parseSpringBoot(input.body, { url: input.url });
144
+ expect(result.condition).toBe(expected.condition);
145
+ expect(result.profiles).toEqual(expected.profiles);
146
+ expect(result.subject.id).toBe(expected.subject.id);
147
+ expect(result.provenance).toBe(expected.provenance);
148
+ expect(result.checks).toBeDefined();
149
+ expect(Object.keys(result.checks).length).toBe(2);
150
+ });
151
+ it('parses fixture: positive-down', () => {
152
+ const fixture = loadJson('adapters/spring-boot/positive-down.json');
153
+ const input = fixture.input;
154
+ const expected = fixture.expected;
155
+ const result = parseSpringBoot(input.body, { url: input.url });
156
+ expect(result.condition).toBe(expected.condition);
157
+ expect(result.checks).toBeDefined();
158
+ // db should be down, diskSpace should be operational
159
+ expect(result.checks['db'].condition).toBe('down');
160
+ expect(result.checks['diskSpace'].condition).toBe('operational');
161
+ });
162
+ it('parses fixture: positive-out-of-service', () => {
163
+ const fixture = loadJson('adapters/spring-boot/positive-out-of-service.json');
164
+ const input = fixture.input;
165
+ const expected = fixture.expected;
166
+ const result = parseSpringBoot(input.body, { url: input.url });
167
+ expect(result.condition).toBe(expected.condition);
168
+ });
169
+ it('maps UP to operational', () => {
170
+ const result = parseSpringBoot({ status: 'UP' }, { url: 'test' });
171
+ expect(result.condition).toBe('operational');
172
+ });
173
+ it('maps UNKNOWN to unknown', () => {
174
+ const result = parseSpringBoot({ status: 'UNKNOWN' }, { url: 'test' });
175
+ expect(result.condition).toBe('unknown');
176
+ });
177
+ });
178
+ // ---------------------------------------------------------------------------
179
+ // Unified parse entry point
180
+ // ---------------------------------------------------------------------------
181
+ describe('unified parse()', () => {
182
+ it('auto-detects and parses health-check-draft', () => {
183
+ const result = parse({
184
+ contentType: 'application/json',
185
+ body: { status: 'pass', serviceId: 'test' },
186
+ url: 'https://example.com/health',
187
+ });
188
+ expect(result.condition).toBe('operational');
189
+ expect(result.subject.id).toBe('test');
190
+ });
191
+ it('auto-detects and parses plain http', () => {
192
+ const result = parse({
193
+ contentType: 'text/plain',
194
+ body: 'OK',
195
+ url: 'https://example.com/health',
196
+ httpStatus: 200,
197
+ });
198
+ expect(result.condition).toBe('alive');
199
+ expect(result.profiles).toEqual(['liveness']);
200
+ });
201
+ it('auto-detects and parses spring-boot', () => {
202
+ const result = parse({
203
+ contentType: 'application/json',
204
+ body: {
205
+ status: 'UP',
206
+ components: {
207
+ db: { status: 'UP' },
208
+ },
209
+ },
210
+ url: 'https://example.com/actuator/health',
211
+ });
212
+ expect(result.condition).toBe('operational');
213
+ expect(result.checks).toBeDefined();
214
+ });
215
+ });
216
+ // ---------------------------------------------------------------------------
217
+ // safeParse — error model
218
+ // ---------------------------------------------------------------------------
219
+ describe('safeParse()', () => {
220
+ it('returns snapshot on valid input', () => {
221
+ const result = safeParse({
222
+ contentType: 'application/json',
223
+ body: { status: 'pass', serviceId: 'test' },
224
+ url: 'https://example.com/health',
225
+ });
226
+ expect(result.snapshot).not.toBeNull();
227
+ expect(result.snapshot.condition).toBe('operational');
228
+ expect(result.errors.length).toBe(0);
229
+ });
230
+ it('never throws on malformed input', () => {
231
+ const result = safeParse({
232
+ contentType: 'application/json',
233
+ body: 'not-an-object',
234
+ url: 'https://example.com/health',
235
+ });
236
+ // Should not throw — returns error instead
237
+ expect(result.snapshot).toBeDefined();
238
+ });
239
+ it('includes lossiness warnings for adapter-based parsing', () => {
240
+ const result = safeParse({
241
+ contentType: 'text/plain',
242
+ body: 'OK',
243
+ url: 'https://example.com/health',
244
+ httpStatus: 200,
245
+ });
246
+ expect(result.snapshot).not.toBeNull();
247
+ expect(result.warnings.length).toBeGreaterThan(0);
248
+ expect(result.warnings[0].code).toBe('LOSSY_PLAIN_HTTP');
249
+ });
250
+ it('includes lossiness warnings for spring-boot', () => {
251
+ const result = safeParse({
252
+ contentType: 'application/json',
253
+ body: {
254
+ status: 'UP',
255
+ components: { db: { status: 'UP' } },
256
+ },
257
+ url: 'https://example.com/actuator/health',
258
+ });
259
+ expect(result.snapshot).not.toBeNull();
260
+ expect(result.warnings.some((w) => w.code === 'LOSSY_SPRING_BOOT')).toBe(true);
261
+ });
262
+ it('returns errors for unrecognized spring boot status', () => {
263
+ const result = safeParse({
264
+ contentType: 'application/json',
265
+ body: {
266
+ status: 'BANANA',
267
+ components: {},
268
+ },
269
+ url: 'https://example.com/actuator/health',
270
+ });
271
+ // BANANA isn't a Spring Boot status, but won't be detected as spring-boot
272
+ // (detection requires UP/DOWN/OUT_OF_SERVICE/UNKNOWN + components)
273
+ // Falls to draft-inadarei or plain-http
274
+ expect(result.snapshot).toBeDefined();
275
+ });
276
+ });
277
+ //# sourceMappingURL=parser.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.test.js","sourceRoot":"","sources":["../../src/__tests__/parser.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Format Detection
3
+ *
4
+ * Identify which parser/adapter to apply based on content-type and
5
+ * structural markers in the response body.
6
+ */
7
+ export type AdapterType = 'native-health-response' | 'native-service-status' | 'health-check-draft' | 'spring-boot' | 'plain-http' | 'unknown';
8
+ /**
9
+ * Detect which adapter to apply to a parsed response body.
10
+ *
11
+ * Priority order:
12
+ * 1. Content-type match
13
+ * 2. Structural markers (native OOS format with `profiles` field)
14
+ * 3. Structural markers (draft-inadarei with `status` = pass/fail/warn)
15
+ * 4. Fall back to plain-http
16
+ */
17
+ export declare function detectFormat(contentType: string | undefined, body: unknown): AdapterType;
18
+ //# sourceMappingURL=detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,MAAM,WAAW,GACjB,wBAAwB,GACxB,uBAAuB,GACvB,oBAAoB,GACpB,aAAa,GACb,YAAY,GACZ,SAAS,CAAC;AAMhB;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CACxB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,IAAI,EAAE,OAAO,GACd,WAAW,CAmCb"}
package/dist/detect.js ADDED
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Format Detection
3
+ *
4
+ * Identify which parser/adapter to apply based on content-type and
5
+ * structural markers in the response body.
6
+ */
7
+ // ---------------------------------------------------------------------------
8
+ // Public API
9
+ // ---------------------------------------------------------------------------
10
+ /**
11
+ * Detect which adapter to apply to a parsed response body.
12
+ *
13
+ * Priority order:
14
+ * 1. Content-type match
15
+ * 2. Structural markers (native OOS format with `profiles` field)
16
+ * 3. Structural markers (draft-inadarei with `status` = pass/fail/warn)
17
+ * 4. Fall back to plain-http
18
+ */
19
+ export function detectFormat(contentType, body) {
20
+ const ct = (contentType || '').toLowerCase().split(';')[0].trim();
21
+ // ── Content-type primary detection ─────────────────────────────────
22
+ if (ct === 'application/status+json') {
23
+ return 'native-service-status';
24
+ }
25
+ // application/health+json could be native OOS or draft-inadarei
26
+ if (ct === 'application/health+json') {
27
+ if (isOosNativeFormat(body)) {
28
+ return 'native-health-response';
29
+ }
30
+ if (isDraftInadarei(body)) {
31
+ return 'health-check-draft';
32
+ }
33
+ // Default for this content type
34
+ return 'native-health-response';
35
+ }
36
+ // ── Structural detection for application/json ──────────────────────
37
+ if (ct === 'application/json' || ct === '') {
38
+ if (isOosNativeFormat(body)) {
39
+ return 'native-health-response';
40
+ }
41
+ if (isSpringBoot(body)) {
42
+ return 'spring-boot';
43
+ }
44
+ if (isDraftInadarei(body)) {
45
+ return 'health-check-draft';
46
+ }
47
+ }
48
+ // ── Fallback ───────────────────────────────────────────────────────
49
+ return 'plain-http';
50
+ }
51
+ // ---------------------------------------------------------------------------
52
+ // Structural markers
53
+ // ---------------------------------------------------------------------------
54
+ function isOosNativeFormat(body) {
55
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
56
+ return false;
57
+ }
58
+ const obj = body;
59
+ // OOS native format has `profiles` array and `condition` field
60
+ return Array.isArray(obj.profiles) && typeof obj.condition === 'string';
61
+ }
62
+ const DRAFT_STATUS_VALUES = new Set([
63
+ 'pass', 'fail', 'warn',
64
+ 'ok', 'error',
65
+ ]);
66
+ // Values shared between Spring Boot and draft-inadarei
67
+ const AMBIGUOUS_STATUS_VALUES = new Set(['up', 'down']);
68
+ const SPRING_BOOT_STATUS_VALUES = new Set([
69
+ 'up', 'down', 'out_of_service', 'unknown',
70
+ ]);
71
+ function isDraftInadarei(body) {
72
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
73
+ return false;
74
+ }
75
+ const obj = body;
76
+ if (typeof obj.status !== 'string') {
77
+ return false;
78
+ }
79
+ const status = obj.status.toLowerCase();
80
+ // Unambiguous draft-inadarei values
81
+ if (DRAFT_STATUS_VALUES.has(status)) {
82
+ return true;
83
+ }
84
+ // Ambiguous values — disambiguate by structure
85
+ if (AMBIGUOUS_STATUS_VALUES.has(status)) {
86
+ // draft-inadarei uses `checks` (object of arrays)
87
+ if (obj.checks && typeof obj.checks === 'object') {
88
+ return true;
89
+ }
90
+ // If neither checks nor components, default to draft-inadarei
91
+ // (more common in the wild)
92
+ if (!obj.components) {
93
+ return true;
94
+ }
95
+ }
96
+ return false;
97
+ }
98
+ function isSpringBoot(body) {
99
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
100
+ return false;
101
+ }
102
+ const obj = body;
103
+ if (typeof obj.status !== 'string') {
104
+ return false;
105
+ }
106
+ const status = obj.status.toLowerCase();
107
+ // Unambiguous Spring Boot values
108
+ if (status === 'out_of_service') {
109
+ return true;
110
+ }
111
+ // Ambiguous values — disambiguate by structure
112
+ if (AMBIGUOUS_STATUS_VALUES.has(status) || status === 'unknown') {
113
+ // Spring Boot uses `components` (object of objects with `status`)
114
+ if (obj.components && typeof obj.components === 'object' && !Array.isArray(obj.components)) {
115
+ return true;
116
+ }
117
+ }
118
+ return false;
119
+ }
120
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../src/detect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Health Check Draft Adapter
3
+ *
4
+ * Implements the health-check-draft adapter spec:
5
+ * status-spec/spec/adapters/health-check-draft.md
6
+ *
7
+ * Maps draft-inadarei-api-health-check responses → core model.
8
+ */
9
+ import type { Snapshot } from '@open-operational-state/types';
10
+ export interface HealthCheckDraftOptions {
11
+ /** Fallback URL for subject identity if serviceId is absent */
12
+ url?: string;
13
+ }
14
+ /**
15
+ * Parse a draft-inadarei health check response into a Snapshot.
16
+ */
17
+ export declare function parseHealthCheckDraft(body: unknown, options?: HealthCheckDraftOptions): Snapshot;
18
+ //# sourceMappingURL=health-check-draft.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health-check-draft.d.ts","sourceRoot":"","sources":["../src/health-check-draft.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAgC,MAAM,+BAA+B,CAAC;AA2B5F,MAAM,WAAW,uBAAuB;IACpC,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD;;GAEG;AACH,wBAAgB,qBAAqB,CACjC,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,uBAAuB,GAClC,QAAQ,CAmDV"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * Health Check Draft Adapter
3
+ *
4
+ * Implements the health-check-draft adapter spec:
5
+ * status-spec/spec/adapters/health-check-draft.md
6
+ *
7
+ * Maps draft-inadarei-api-health-check responses → core model.
8
+ */
9
+ // ---------------------------------------------------------------------------
10
+ // Status value mapping (spec §Mapping Table)
11
+ // ---------------------------------------------------------------------------
12
+ const STATUS_MAP = {
13
+ pass: 'operational',
14
+ ok: 'operational',
15
+ up: 'operational',
16
+ warn: 'degraded',
17
+ fail: 'down',
18
+ error: 'down',
19
+ down: 'down',
20
+ };
21
+ // ComponentType → role inference (spec §Mapping Table)
22
+ const COMPONENT_TYPE_ROLE = {
23
+ system: 'dependency',
24
+ datastore: 'dependency',
25
+ component: 'component',
26
+ };
27
+ // ---------------------------------------------------------------------------
28
+ // Public API
29
+ // ---------------------------------------------------------------------------
30
+ /**
31
+ * Parse a draft-inadarei health check response into a Snapshot.
32
+ */
33
+ export function parseHealthCheckDraft(body, options) {
34
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
35
+ throw new Error('Health check draft body must be a non-null object');
36
+ }
37
+ const obj = body;
38
+ // ── Top-level condition ────────────────────────────────────────────
39
+ const rawStatus = typeof obj.status === 'string' ? obj.status.toLowerCase() : '';
40
+ const condition = STATUS_MAP[rawStatus] || rawStatus;
41
+ if (!condition) {
42
+ throw new Error(`Invalid status value: '${obj.status}'`);
43
+ }
44
+ // ── Subject ────────────────────────────────────────────────────────
45
+ const subjectId = typeof obj.serviceId === 'string'
46
+ ? obj.serviceId
47
+ : (options?.url || '');
48
+ const description = typeof obj.description === 'string'
49
+ ? obj.description
50
+ : undefined;
51
+ // ── Top-level evidence from output ─────────────────────────────────
52
+ let evidence;
53
+ if (typeof obj.output === 'string') {
54
+ evidence = { type: 'output', detail: obj.output };
55
+ }
56
+ // ── Checks ─────────────────────────────────────────────────────────
57
+ const checks = parseChecks(obj.checks);
58
+ // ── Build snapshot ─────────────────────────────────────────────────
59
+ const snapshot = {
60
+ condition,
61
+ profiles: ['health'],
62
+ subject: {
63
+ id: subjectId,
64
+ ...(description ? { description } : {}),
65
+ },
66
+ provenance: 'self-reported',
67
+ };
68
+ if (evidence) {
69
+ snapshot.evidence = evidence;
70
+ }
71
+ if (checks && Object.keys(checks).length > 0) {
72
+ snapshot.checks = checks;
73
+ }
74
+ if (obj.links && typeof obj.links === 'object') {
75
+ snapshot.links = obj.links;
76
+ }
77
+ return snapshot;
78
+ }
79
+ // ---------------------------------------------------------------------------
80
+ // Checks parsing
81
+ //
82
+ // draft-inadarei checks are keyed as "componentName:measurementName"
83
+ // and each value is an array of check entries.
84
+ // ---------------------------------------------------------------------------
85
+ function parseChecks(raw) {
86
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
87
+ return undefined;
88
+ }
89
+ const obj = raw;
90
+ const result = {};
91
+ for (const [key, entries] of Object.entries(obj)) {
92
+ if (!Array.isArray(entries)) {
93
+ continue;
94
+ }
95
+ // Parse "componentName:measurementName"
96
+ const colonIdx = key.indexOf(':');
97
+ const componentName = colonIdx > 0 ? key.substring(0, colonIdx) : key;
98
+ const measurementName = colonIdx > 0 ? key.substring(colonIdx + 1) : undefined;
99
+ // Take the first entry (spec says group multiple under same component)
100
+ const entry = entries[0];
101
+ if (!entry) {
102
+ continue;
103
+ }
104
+ // Condition
105
+ const rawStatus = typeof entry.status === 'string' ? entry.status.toLowerCase() : '';
106
+ const condition = STATUS_MAP[rawStatus] || rawStatus || 'unknown';
107
+ // Role
108
+ const componentType = typeof entry.componentType === 'string'
109
+ ? entry.componentType.toLowerCase()
110
+ : '';
111
+ const role = COMPONENT_TYPE_ROLE[componentType] || 'component';
112
+ // Evidence
113
+ const evidence = buildEvidence(entry, measurementName);
114
+ // Timing
115
+ const timing = typeof entry.time === 'string'
116
+ ? { observed: entry.time }
117
+ : undefined;
118
+ const check = { condition, role };
119
+ if (evidence) {
120
+ check.evidence = evidence;
121
+ }
122
+ if (timing) {
123
+ check.timing = timing;
124
+ }
125
+ result[componentName] = check;
126
+ }
127
+ return Object.keys(result).length > 0 ? result : undefined;
128
+ }
129
+ function buildEvidence(entry, measurementName) {
130
+ const hasValue = entry.observedValue !== undefined;
131
+ const hasUnit = typeof entry.observedUnit === 'string';
132
+ const hasOutput = typeof entry.output === 'string';
133
+ if (!hasValue && !hasUnit && !hasOutput && !measurementName) {
134
+ return undefined;
135
+ }
136
+ const evidence = {
137
+ type: measurementName || 'check',
138
+ };
139
+ if (hasValue) {
140
+ evidence.observedValue = entry.observedValue;
141
+ }
142
+ if (hasUnit) {
143
+ evidence.observedUnit = entry.observedUnit;
144
+ }
145
+ if (hasOutput) {
146
+ evidence.detail = entry.output;
147
+ }
148
+ return evidence;
149
+ }
150
+ //# sourceMappingURL=health-check-draft.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health-check-draft.js","sourceRoot":"","sources":["../src/health-check-draft.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Native Health Response Parser
3
+ *
4
+ * Deserializes application/health+json (our own format) into a Snapshot.
5
+ * This is NOT an adapter — it's native format deserialization.
6
+ */
7
+ import type { Snapshot } from '@open-operational-state/types';
8
+ /**
9
+ * Parse a native health-response body into a Snapshot.
10
+ */
11
+ export declare function parseHealthResponse(body: unknown): Snapshot;
12
+ //# sourceMappingURL=health-response.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health-response.d.ts","sourceRoot":"","sources":["../src/health-response.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,+BAA+B,CAAC;AAE9D;;GAEG;AACH,wBAAgB,mBAAmB,CAAE,IAAI,EAAE,OAAO,GAAI,QAAQ,CAM7D"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Native Health Response Parser
3
+ *
4
+ * Deserializes application/health+json (our own format) into a Snapshot.
5
+ * This is NOT an adapter — it's native format deserialization.
6
+ */
7
+ import { normalizeSnapshot } from '@open-operational-state/core';
8
+ /**
9
+ * Parse a native health-response body into a Snapshot.
10
+ */
11
+ export function parseHealthResponse(body) {
12
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
13
+ throw new Error('Health response body must be a non-null object');
14
+ }
15
+ return normalizeSnapshot(body);
16
+ }
17
+ //# sourceMappingURL=health-response.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"health-response.js","sourceRoot":"","sources":["../src/health-response.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAGjE;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAE,IAAa;IAC9C,IAAK,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAE,IAAI,CAAE,EAAG,CAAC;QAC/D,MAAM,IAAI,KAAK,CAAE,gDAAgD,CAAE,CAAC;IACxE,CAAC;IAED,OAAO,iBAAiB,CAAE,IAA+B,CAAE,CAAC;AAChE,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @open-operational-state/parser
3
+ *
4
+ * Response parsers and adapters for operational-state formats.
5
+ *
6
+ * Depends on @open-operational-state/types and @open-operational-state/core.
7
+ */
8
+ export { parse, safeParse } from './parse.js';
9
+ export type { ParseInput } from './parse.js';
10
+ export { detectFormat } from './detect.js';
11
+ export type { AdapterType } from './detect.js';
12
+ export { parsePlainHttp } from './plain-http.js';
13
+ export type { PlainHttpInput } from './plain-http.js';
14
+ export { parseHealthCheckDraft } from './health-check-draft.js';
15
+ export type { HealthCheckDraftOptions } from './health-check-draft.js';
16
+ export { parseHealthResponse } from './health-response.js';
17
+ export { parseSpringBoot, isSpringBootFormat } from './spring-boot.js';
18
+ export type { SpringBootOptions } from './spring-boot.js';
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC9C,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,YAAY,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAEvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACvE,YAAY,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @open-operational-state/parser
3
+ *
4
+ * Response parsers and adapters for operational-state formats.
5
+ *
6
+ * Depends on @open-operational-state/types and @open-operational-state/core.
7
+ */
8
+ export { parse, safeParse } from './parse.js';
9
+ export { detectFormat } from './detect.js';
10
+ export { parsePlainHttp } from './plain-http.js';
11
+ export { parseHealthCheckDraft } from './health-check-draft.js';
12
+ export { parseHealthResponse } from './health-response.js';
13
+ export { parseSpringBoot, isSpringBootFormat } from './spring-boot.js';
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG9C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAGhE,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Unified Parse Entry Point
3
+ *
4
+ * Auto-detects format and dispatches to the correct parser.
5
+ */
6
+ import type { Snapshot, ParseResult } from '@open-operational-state/types';
7
+ export interface ParseInput {
8
+ /** HTTP Content-Type header */
9
+ contentType?: string;
10
+ /** Parsed response body (JSON object) */
11
+ body?: unknown;
12
+ /** Request URL */
13
+ url?: string;
14
+ /** HTTP status code */
15
+ httpStatus?: number;
16
+ /** Response headers */
17
+ headers?: Record<string, string>;
18
+ /** True if the connection failed entirely */
19
+ connectionError?: boolean;
20
+ }
21
+ /**
22
+ * Parse an HTTP response into a Snapshot.
23
+ *
24
+ * Auto-detects the format from content-type and body structure,
25
+ * then dispatches to the appropriate parser.
26
+ *
27
+ * **Throws** on malformed input. For production use, prefer `safeParse()`.
28
+ */
29
+ export declare function parse(input: ParseInput): Snapshot;
30
+ /**
31
+ * Parse an HTTP response into a ParseResult.
32
+ *
33
+ * Returns `{ snapshot, errors, warnings }` — never throws.
34
+ * Use this in production code, CLIs, and anywhere you handle
35
+ * untrusted input.
36
+ */
37
+ export declare function safeParse(input: ParseInput): ParseResult;
38
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAgB,MAAM,+BAA+B,CAAC;AAYzF,MAAM,WAAW,UAAU;IACvB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yCAAyC;IACzC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,kBAAkB;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,6CAA6C;IAC7C,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAsBD;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAE,KAAK,EAAE,UAAU,GAAI,QAAQ,CAgCnD;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAE,KAAK,EAAE,UAAU,GAAI,WAAW,CAe1D"}
package/dist/parse.js ADDED
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Unified Parse Entry Point
3
+ *
4
+ * Auto-detects format and dispatches to the correct parser.
5
+ */
6
+ import { detectFormat } from './detect.js';
7
+ import { parsePlainHttp } from './plain-http.js';
8
+ import { parseHealthCheckDraft } from './health-check-draft.js';
9
+ import { parseHealthResponse } from './health-response.js';
10
+ import { parseSpringBoot } from './spring-boot.js';
11
+ // ---------------------------------------------------------------------------
12
+ // Lossiness warnings per adapter
13
+ // ---------------------------------------------------------------------------
14
+ const ADAPTER_WARNINGS = {
15
+ 'plain-http': [
16
+ { message: 'Plain HTTP yields liveness only (alive/unreachable). No condition detail is available.', code: 'LOSSY_PLAIN_HTTP' },
17
+ ],
18
+ 'health-check-draft': [
19
+ { message: 'draft-inadarei status values mapped to OOS conditions. Some semantic nuance may be lost.', code: 'LOSSY_HEALTH_CHECK_DRAFT' },
20
+ ],
21
+ 'spring-boot': [
22
+ { message: 'Spring Boot Actuator status values mapped to OOS conditions. OUT_OF_SERVICE and DOWN both map to down.', code: 'LOSSY_SPRING_BOOT' },
23
+ ],
24
+ };
25
+ // ---------------------------------------------------------------------------
26
+ // Public API
27
+ // ---------------------------------------------------------------------------
28
+ /**
29
+ * Parse an HTTP response into a Snapshot.
30
+ *
31
+ * Auto-detects the format from content-type and body structure,
32
+ * then dispatches to the appropriate parser.
33
+ *
34
+ * **Throws** on malformed input. For production use, prefer `safeParse()`.
35
+ */
36
+ export function parse(input) {
37
+ const format = detectFormat(input.contentType, input.body);
38
+ switch (format) {
39
+ // service-status is a superset of health-response; normalizeSnapshot
40
+ // handles both shapes, so a single parser path is intentional.
41
+ case 'native-health-response':
42
+ case 'native-service-status':
43
+ return parseHealthResponse(input.body);
44
+ case 'health-check-draft':
45
+ return parseHealthCheckDraft(input.body, { url: input.url });
46
+ case 'spring-boot':
47
+ return parseSpringBoot(input.body, { url: input.url });
48
+ case 'plain-http':
49
+ return parsePlainHttp({
50
+ url: input.url || '',
51
+ httpStatus: input.httpStatus,
52
+ headers: input.headers,
53
+ connectionError: input.connectionError,
54
+ });
55
+ default:
56
+ return parsePlainHttp({
57
+ url: input.url || '',
58
+ httpStatus: input.httpStatus,
59
+ headers: input.headers,
60
+ connectionError: input.connectionError,
61
+ });
62
+ }
63
+ }
64
+ /**
65
+ * Parse an HTTP response into a ParseResult.
66
+ *
67
+ * Returns `{ snapshot, errors, warnings }` — never throws.
68
+ * Use this in production code, CLIs, and anywhere you handle
69
+ * untrusted input.
70
+ */
71
+ export function safeParse(input) {
72
+ const format = detectFormat(input.contentType, input.body);
73
+ const warnings = ADAPTER_WARNINGS[format] ? [...ADAPTER_WARNINGS[format]] : [];
74
+ try {
75
+ const snapshot = parse(input);
76
+ return { snapshot, errors: [], warnings };
77
+ }
78
+ catch (err) {
79
+ const message = err instanceof Error ? err.message : String(err);
80
+ return {
81
+ snapshot: null,
82
+ errors: [{ message, code: 'PARSE_ERROR' }],
83
+ warnings,
84
+ };
85
+ }
86
+ }
87
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../src/parse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Plain HTTP Adapter
3
+ *
4
+ * Implements the plain-http adapter spec:
5
+ * status-spec/spec/adapters/plain-http.md
6
+ *
7
+ * Maps HTTP status code / connection result → Liveness profile.
8
+ */
9
+ import type { Snapshot } from '@open-operational-state/types';
10
+ export interface PlainHttpInput {
11
+ /** The URL that was probed */
12
+ url: string;
13
+ /** HTTP status code (undefined if connection failed) */
14
+ httpStatus?: number;
15
+ /** Response headers */
16
+ headers?: Record<string, string>;
17
+ /** True if the connection failed entirely (refused, DNS, timeout) */
18
+ connectionError?: boolean;
19
+ }
20
+ /**
21
+ * Parse a plain HTTP probe result into a Liveness Snapshot.
22
+ */
23
+ export declare function parsePlainHttp(input: PlainHttpInput): Snapshot;
24
+ //# sourceMappingURL=plain-http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plain-http.d.ts","sourceRoot":"","sources":["../src/plain-http.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAU,MAAM,+BAA+B,CAAC;AAMtE,MAAM,WAAW,cAAc;IAC3B,8BAA8B;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,qEAAqE;IACrE,eAAe,CAAC,EAAE,OAAO,CAAC;CAC7B;AAMD;;GAEG;AACH,wBAAgB,cAAc,CAAE,KAAK,EAAE,cAAc,GAAI,QAAQ,CA4BhE"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Plain HTTP Adapter
3
+ *
4
+ * Implements the plain-http adapter spec:
5
+ * status-spec/spec/adapters/plain-http.md
6
+ *
7
+ * Maps HTTP status code / connection result → Liveness profile.
8
+ */
9
+ // ---------------------------------------------------------------------------
10
+ // Public API
11
+ // ---------------------------------------------------------------------------
12
+ /**
13
+ * Parse a plain HTTP probe result into a Liveness Snapshot.
14
+ */
15
+ export function parsePlainHttp(input) {
16
+ const isReachable = !input.connectionError && input.httpStatus !== undefined;
17
+ const timing = {
18
+ observed: new Date().toISOString(),
19
+ };
20
+ // Map HTTP Date header to report time
21
+ if (input.headers) {
22
+ const dateHeader = findHeader(input.headers, 'date');
23
+ if (dateHeader) {
24
+ try {
25
+ timing.reported = new Date(dateHeader).toISOString();
26
+ }
27
+ catch {
28
+ // Ignore invalid Date header
29
+ }
30
+ }
31
+ }
32
+ return {
33
+ condition: isReachable ? 'alive' : 'unreachable',
34
+ profiles: ['liveness'],
35
+ subject: {
36
+ id: input.url,
37
+ },
38
+ timing,
39
+ provenance: 'externally-observed',
40
+ };
41
+ }
42
+ // ---------------------------------------------------------------------------
43
+ // Helpers
44
+ // ---------------------------------------------------------------------------
45
+ function findHeader(headers, name) {
46
+ const lower = name.toLowerCase();
47
+ for (const [key, value] of Object.entries(headers)) {
48
+ if (key.toLowerCase() === lower) {
49
+ return value;
50
+ }
51
+ }
52
+ return undefined;
53
+ }
54
+ //# sourceMappingURL=plain-http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plain-http.js","sourceRoot":"","sources":["../src/plain-http.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAmBH,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;GAEG;AACH,MAAM,UAAU,cAAc,CAAE,KAAqB;IACjD,MAAM,WAAW,GAAG,CAAC,KAAK,CAAC,eAAe,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,CAAC;IAE7E,MAAM,MAAM,GAAW;QACnB,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACrC,CAAC;IAEF,sCAAsC;IACtC,IAAK,KAAK,CAAC,OAAO,EAAG,CAAC;QAClB,MAAM,UAAU,GAAG,UAAU,CAAE,KAAK,CAAC,OAAO,EAAE,MAAM,CAAE,CAAC;QACvD,IAAK,UAAU,EAAG,CAAC;YACf,IAAI,CAAC;gBACD,MAAM,CAAC,QAAQ,GAAG,IAAI,IAAI,CAAE,UAAU,CAAE,CAAC,WAAW,EAAE,CAAC;YAC3D,CAAC;YAAC,MAAM,CAAC;gBACL,6BAA6B;YACjC,CAAC;QACL,CAAC;IACL,CAAC;IAED,OAAO;QACH,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa;QAChD,QAAQ,EAAE,CAAE,UAAU,CAAE;QACxB,OAAO,EAAE;YACL,EAAE,EAAE,KAAK,CAAC,GAAG;SAChB;QACD,MAAM;QACN,UAAU,EAAE,qBAAqB;KACpC,CAAC;AACN,CAAC;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,SAAS,UAAU,CAAE,OAA+B,EAAE,IAAY;IAC9D,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,KAAM,MAAM,CAAE,GAAG,EAAE,KAAK,CAAE,IAAI,MAAM,CAAC,OAAO,CAAE,OAAO,CAAE,EAAG,CAAC;QACvD,IAAK,GAAG,CAAC,WAAW,EAAE,KAAK,KAAK,EAAG,CAAC;YAAC,OAAO,KAAK,CAAC;QAAC,CAAC;IACxD,CAAC;IACD,OAAO,SAAS,CAAC;AACrB,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Spring Boot Actuator Adapter
3
+ *
4
+ * Maps Spring Boot Actuator /actuator/health responses → core model.
5
+ *
6
+ * Spring Boot status values:
7
+ * UP → operational
8
+ * DOWN → down
9
+ * OUT_OF_SERVICE → down
10
+ * UNKNOWN → unknown
11
+ */
12
+ import type { Snapshot } from '@open-operational-state/types';
13
+ export interface SpringBootOptions {
14
+ /** Fallback URL for subject identity */
15
+ url?: string;
16
+ }
17
+ /**
18
+ * Parse a Spring Boot Actuator health response into a Snapshot.
19
+ */
20
+ export declare function parseSpringBoot(body: unknown, options?: SpringBootOptions): Snapshot;
21
+ /**
22
+ * Returns true if the body looks like a Spring Boot Actuator response.
23
+ *
24
+ * Distinguished from draft-inadarei by status values:
25
+ * Spring Boot uses UP/DOWN/OUT_OF_SERVICE/UNKNOWN (case-insensitive)
26
+ * while the draft uses pass/fail/warn.
27
+ */
28
+ export declare function isSpringBootFormat(body: unknown): boolean;
29
+ //# sourceMappingURL=spring-boot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spring-boot.d.ts","sourceRoot":"","sources":["../src/spring-boot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAwB,MAAM,+BAA+B,CAAC;AAiBpF,MAAM,WAAW,iBAAiB;IAC9B,wCAAwC;IACxC,GAAG,CAAC,EAAE,MAAM,CAAC;CAChB;AAMD;;GAEG;AACH,wBAAgB,eAAe,CAC3B,IAAI,EAAE,OAAO,EACb,OAAO,CAAC,EAAE,iBAAiB,GAC5B,QAAQ,CAkCV;AAyDD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAE,IAAI,EAAE,OAAO,GAAI,OAAO,CAM3D"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Spring Boot Actuator Adapter
3
+ *
4
+ * Maps Spring Boot Actuator /actuator/health responses → core model.
5
+ *
6
+ * Spring Boot status values:
7
+ * UP → operational
8
+ * DOWN → down
9
+ * OUT_OF_SERVICE → down
10
+ * UNKNOWN → unknown
11
+ */
12
+ // ---------------------------------------------------------------------------
13
+ // Status value mapping
14
+ // ---------------------------------------------------------------------------
15
+ const STATUS_MAP = {
16
+ up: 'operational',
17
+ down: 'down',
18
+ out_of_service: 'down',
19
+ unknown: 'unknown',
20
+ };
21
+ // ---------------------------------------------------------------------------
22
+ // Public API
23
+ // ---------------------------------------------------------------------------
24
+ /**
25
+ * Parse a Spring Boot Actuator health response into a Snapshot.
26
+ */
27
+ export function parseSpringBoot(body, options) {
28
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
29
+ throw new Error('Spring Boot body must be a non-null object');
30
+ }
31
+ const obj = body;
32
+ // ── Top-level condition ────────────────────────────────────────────
33
+ const rawStatus = typeof obj.status === 'string' ? obj.status.toLowerCase() : '';
34
+ const condition = STATUS_MAP[rawStatus];
35
+ if (!condition) {
36
+ throw new Error(`Unrecognized Spring Boot status: '${obj.status}'`);
37
+ }
38
+ // ── Subject ────────────────────────────────────────────────────────
39
+ const subjectId = options?.url || '';
40
+ // ── Components ─────────────────────────────────────────────────────
41
+ const checks = parseComponents(obj.components);
42
+ // ── Build snapshot ─────────────────────────────────────────────────
43
+ const snapshot = {
44
+ condition,
45
+ profiles: ['health'],
46
+ subject: { id: subjectId },
47
+ provenance: 'self-reported',
48
+ };
49
+ if (checks && Object.keys(checks).length > 0) {
50
+ snapshot.checks = checks;
51
+ }
52
+ return snapshot;
53
+ }
54
+ // ---------------------------------------------------------------------------
55
+ // Component parsing
56
+ //
57
+ // Spring Boot components are keyed objects with `status` and optional `details`.
58
+ // ---------------------------------------------------------------------------
59
+ function parseComponents(raw) {
60
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
61
+ return undefined;
62
+ }
63
+ const obj = raw;
64
+ const result = {};
65
+ for (const [key, value] of Object.entries(obj)) {
66
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
67
+ continue;
68
+ }
69
+ const comp = value;
70
+ const rawStatus = typeof comp.status === 'string' ? comp.status.toLowerCase() : '';
71
+ const condition = STATUS_MAP[rawStatus] || rawStatus || 'unknown';
72
+ const check = {
73
+ condition,
74
+ role: 'component',
75
+ };
76
+ // Extract evidence from details
77
+ if (comp.details && typeof comp.details === 'object') {
78
+ const details = comp.details;
79
+ const evidence = { type: 'details' };
80
+ // Common Spring Boot detail patterns
81
+ if (typeof details.database === 'string') {
82
+ evidence.detail = `database: ${details.database}`;
83
+ }
84
+ else if (typeof details.error === 'string') {
85
+ evidence.detail = details.error;
86
+ }
87
+ else {
88
+ // Generic stringification of details
89
+ const entries = Object.entries(details).slice(0, 3);
90
+ if (entries.length > 0) {
91
+ evidence.detail = entries.map(([k, v]) => `${k}: ${v}`).join(', ');
92
+ }
93
+ }
94
+ check.evidence = evidence;
95
+ }
96
+ result[key] = check;
97
+ }
98
+ return Object.keys(result).length > 0 ? result : undefined;
99
+ }
100
+ // ---------------------------------------------------------------------------
101
+ // Structural detection
102
+ // ---------------------------------------------------------------------------
103
+ /**
104
+ * Returns true if the body looks like a Spring Boot Actuator response.
105
+ *
106
+ * Distinguished from draft-inadarei by status values:
107
+ * Spring Boot uses UP/DOWN/OUT_OF_SERVICE/UNKNOWN (case-insensitive)
108
+ * while the draft uses pass/fail/warn.
109
+ */
110
+ export function isSpringBootFormat(body) {
111
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
112
+ return false;
113
+ }
114
+ const obj = body;
115
+ if (typeof obj.status !== 'string') {
116
+ return false;
117
+ }
118
+ const status = obj.status.toLowerCase();
119
+ return status === 'up' || status === 'down' || status === 'out_of_service' || status === 'unknown';
120
+ }
121
+ //# sourceMappingURL=spring-boot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spring-boot.js","sourceRoot":"","sources":["../src/spring-boot.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@open-operational-state/parser",
3
+ "version": "0.1.0",
4
+ "description": "Format detection and parsing adapters for Open Operational State",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/open-operational-state/status-tooling.git",
24
+ "directory": "packages/parser"
25
+ },
26
+ "scripts": {
27
+ "build": "tsc",
28
+ "clean": "rm -rf dist",
29
+ "typecheck": "tsc --noEmit",
30
+ "test": "bun test"
31
+ },
32
+ "dependencies": {
33
+ "@open-operational-state/types": "^0.1.0",
34
+ "@open-operational-state/core": "^0.1.0"
35
+ },
36
+ "devDependencies": {
37
+ "typescript": "^5.8.0"
38
+ }
39
+ }