@myrobotaxi/contracts 0.5.0 → 0.7.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 +6 -1
- package/dist/index.cjs +445 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +409 -1
- package/dist/index.d.ts +409 -1
- package/dist/index.js +445 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.cts +370 -1
- package/dist/types.d.ts +370 -1
- package/package.json +4 -1
- package/schemas/drive-detail.schema.json +174 -0
- package/schemas/drive-route.schema.json +78 -0
- package/schemas/drives-list.schema.json +184 -0
package/dist/types.d.ts
CHANGED
|
@@ -338,6 +338,375 @@ interface VehicleSummary {
|
|
|
338
338
|
role: 'owner' | 'viewer';
|
|
339
339
|
}
|
|
340
340
|
|
|
341
|
+
/**
|
|
342
|
+
* AUTO-GENERATED by scripts/codegen.mjs from the corresponding JSON Schema
|
|
343
|
+
* in ../schemas/. DO NOT EDIT BY HAND — re-run `npm run codegen` instead.
|
|
344
|
+
*
|
|
345
|
+
* The x-classification / x-atomic-group / x-unit / x-encrypted custom JSON
|
|
346
|
+
* Schema annotations from the source schemas are folded into the generated
|
|
347
|
+
* TSDoc comments so grep can find them without leaving the editor.
|
|
348
|
+
*/
|
|
349
|
+
/**
|
|
350
|
+
* Cursor-paginated envelope returned by GET /api/vehicles/{vehicleId}/drives (rest-api.md §7.2). Wraps an array of DriveSummary rows under `items`, newest first (ORDER BY startTime DESC, id DESC). Mirrors the Go `drivesPageResponse` struct in telemetry/internal/telemetry/vehicle_drives_types.go and the `PaginatedDrives` component in telemetry/docs/contracts/specs/rest.openapi.yaml. Pairs with the WebSocket `drive_ended` subscription for FR-9.1/FR-9.2: the SDK hydrates the first page here and prepends live `drive_ended` frames without re-fetching. The envelope is part of the wire contract: consumers MUST NOT strip it silently.
|
|
351
|
+
*/
|
|
352
|
+
interface DrivesListResponse {
|
|
353
|
+
/**
|
|
354
|
+
* Drive summary rows for this page. Always present; an empty array (never null) means no more drives — e.g. the final page or a vehicle with no completed drives. Ordered startTime DESC, id DESC (rest-api.md §7.2 Ordering).
|
|
355
|
+
*
|
|
356
|
+
* @classification "P0"
|
|
357
|
+
*/
|
|
358
|
+
items: DriveSummary[];
|
|
359
|
+
/**
|
|
360
|
+
* Opaque base64 cursor for the next page, or null on the final page. Pass it back as the `cursor` query param to fetch the next page. Encodes (startTime, id) so pagination is stable across concurrent writes. Nullable on the wire — the Go server emits JSON `null` (not an omitted key) when there is no further page (drivesPageResponse.NextCursor is a `*string` with no omitempty, telemetry/internal/telemetry/vehicle_drives_types.go). Redundant with `hasMore == false`.
|
|
361
|
+
*
|
|
362
|
+
* @classification "P0"
|
|
363
|
+
*/
|
|
364
|
+
nextCursor: string | null;
|
|
365
|
+
/**
|
|
366
|
+
* True iff more pages exist after this one. Redundant with `nextCursor != null`, provided for caller convenience.
|
|
367
|
+
*
|
|
368
|
+
* @classification "P0"
|
|
369
|
+
*/
|
|
370
|
+
hasMore: boolean;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Lightweight per-drive row returned by GET /api/vehicles/{vehicleId}/drives. A list-view projection of a completed drive containing the FR-3.4 headline stats plus origin/destination labels, so the history list renders without a per-row DriveDetail fetch. Includes the four P1 location/address labels (MYR-145) and the P0 fsdMiles/fsdPercentage stats (MYR-152). Deliberately omits energyUsedKwh, interventions, and routePoints — those live on Drive (drive detail, §7.3) and DriveRoute (§7.4). Mirrors the Go `driveSummary` struct + `toMaskMap` in telemetry/internal/telemetry/vehicle_drives_types.go and the `DriveSummary` component in telemetry/docs/contracts/specs/rest.openapi.yaml.
|
|
374
|
+
*
|
|
375
|
+
* This interface was referenced by `DrivesListResponse`'s JSON-Schema
|
|
376
|
+
* via the `definition` "DriveSummary".
|
|
377
|
+
*/
|
|
378
|
+
interface DriveSummary {
|
|
379
|
+
/**
|
|
380
|
+
* Opaque database identifier (cuid) for this drive. Matches the `driveId` carried by `drive_started` / `drive_ended` WebSocket frames and the path param of GET /api/drives/{driveId}.
|
|
381
|
+
*
|
|
382
|
+
* @classification "P0"
|
|
383
|
+
*/
|
|
384
|
+
id: string;
|
|
385
|
+
/**
|
|
386
|
+
* Opaque database identifier (cuid) for the drive's parent vehicle. Matches VehicleState.vehicleId / VehicleSummary.vehicleId.
|
|
387
|
+
*
|
|
388
|
+
* @classification "P0"
|
|
389
|
+
*/
|
|
390
|
+
vehicleId: string;
|
|
391
|
+
/**
|
|
392
|
+
* ISO 8601 / RFC 3339 UTC timestamp when the drive began (e.g., '2026-04-13T18:22:00Z').
|
|
393
|
+
*
|
|
394
|
+
* @classification "P0"
|
|
395
|
+
*/
|
|
396
|
+
startTime: string;
|
|
397
|
+
/**
|
|
398
|
+
* ISO 8601 / RFC 3339 UTC timestamp when the drive ended (e.g., '2026-04-13T18:46:18Z').
|
|
399
|
+
*
|
|
400
|
+
* @classification "P0"
|
|
401
|
+
*/
|
|
402
|
+
endTime: string;
|
|
403
|
+
/**
|
|
404
|
+
* Local calendar date of the drive as a 'YYYY-MM-DD' string (e.g., '2026-04-13'), for day-grouping the history list. Not a full timestamp — use startTime/endTime for instants.
|
|
405
|
+
*
|
|
406
|
+
* @classification "P0"
|
|
407
|
+
*/
|
|
408
|
+
date: string;
|
|
409
|
+
/**
|
|
410
|
+
* Reverse-geocoded place name at the drive's start (e.g., 'Home', 'Whole Foods Market'). OPTIONAL on the wire: the Go handler OMITS this key entirely (never emits '' or null) when the underlying Drive.startLocation column is empty — zero-GPS at drive start or reverse-geocode failed (toMaskMap, telemetry/internal/telemetry/vehicle_drives_types.go). Consumers branch on key presence.
|
|
411
|
+
*
|
|
412
|
+
* @classification "P1"
|
|
413
|
+
*/
|
|
414
|
+
startLocation?: string;
|
|
415
|
+
/**
|
|
416
|
+
* Reverse-geocoded street address at the drive's start. OPTIONAL on the wire: the key is omitted entirely when the underlying Drive.startAddress column is empty (same omit-when-empty convention as startLocation).
|
|
417
|
+
*
|
|
418
|
+
* @classification "P1"
|
|
419
|
+
*/
|
|
420
|
+
startAddress?: string;
|
|
421
|
+
/**
|
|
422
|
+
* Reverse-geocoded place name at the drive's end. OPTIONAL on the wire: the key is omitted entirely when the underlying Drive.endLocation column is empty — drive in progress, zero-GPS at drive end, or reverse-geocode failed.
|
|
423
|
+
*
|
|
424
|
+
* @classification "P1"
|
|
425
|
+
*/
|
|
426
|
+
endLocation?: string;
|
|
427
|
+
/**
|
|
428
|
+
* Reverse-geocoded street address at the drive's end. OPTIONAL on the wire: the key is omitted entirely when the underlying Drive.endAddress column is empty.
|
|
429
|
+
*
|
|
430
|
+
* @classification "P1"
|
|
431
|
+
*/
|
|
432
|
+
endAddress?: string;
|
|
433
|
+
/**
|
|
434
|
+
* Total distance driven, in miles (FR-3.4).
|
|
435
|
+
*
|
|
436
|
+
* @classification "P0"
|
|
437
|
+
* @unit "miles"
|
|
438
|
+
*/
|
|
439
|
+
distanceMiles: number;
|
|
440
|
+
/**
|
|
441
|
+
* Total drive duration, in seconds (FR-3.4). The Go server converts the stored DurationMinutes to seconds (DurationMinutes * 60) before emitting. Matches the WebSocket drive_ended.payload.durationSeconds shape (DV-12).
|
|
442
|
+
*
|
|
443
|
+
* @classification "P0"
|
|
444
|
+
* @unit "seconds"
|
|
445
|
+
*/
|
|
446
|
+
durationSeconds: number;
|
|
447
|
+
/**
|
|
448
|
+
* Average speed over the drive, in miles per hour (FR-3.4).
|
|
449
|
+
*
|
|
450
|
+
* @classification "P0"
|
|
451
|
+
* @unit "mph"
|
|
452
|
+
*/
|
|
453
|
+
avgSpeedMph: number;
|
|
454
|
+
/**
|
|
455
|
+
* Maximum speed reached during the drive, in miles per hour (FR-3.4).
|
|
456
|
+
*
|
|
457
|
+
* @classification "P0"
|
|
458
|
+
* @unit "mph"
|
|
459
|
+
*/
|
|
460
|
+
maxSpeedMph: number;
|
|
461
|
+
/**
|
|
462
|
+
* Battery state of charge at drive start (FR-3.4).
|
|
463
|
+
*
|
|
464
|
+
* @classification "P0"
|
|
465
|
+
* @unit "percent"
|
|
466
|
+
*/
|
|
467
|
+
startChargeLevel: number;
|
|
468
|
+
/**
|
|
469
|
+
* Battery state of charge at drive end (FR-3.4).
|
|
470
|
+
*
|
|
471
|
+
* @classification "P0"
|
|
472
|
+
* @unit "percent"
|
|
473
|
+
*/
|
|
474
|
+
endChargeLevel: number;
|
|
475
|
+
/**
|
|
476
|
+
* Miles driven under Full Self-Driving during this drive (FR-3.4, MYR-152). Always present; defaults to 0 for a drive with no FSD usage.
|
|
477
|
+
*
|
|
478
|
+
* @classification "P0"
|
|
479
|
+
* @unit "miles"
|
|
480
|
+
*/
|
|
481
|
+
fsdMiles: number;
|
|
482
|
+
/**
|
|
483
|
+
* Percent of the drive covered by FSD (FR-3.4, MYR-152). Always present; defaults to 0 for a drive with no FSD usage.
|
|
484
|
+
*
|
|
485
|
+
* @classification "P0"
|
|
486
|
+
* @unit "percent"
|
|
487
|
+
*/
|
|
488
|
+
fsdPercentage: number;
|
|
489
|
+
/**
|
|
490
|
+
* ISO 8601 / RFC 3339 UTC row-creation timestamp. The Go server renders it as `CreatedAt.UTC().Format(time.RFC3339)` (telemetry/internal/telemetry/vehicle_drives_types.go).
|
|
491
|
+
*
|
|
492
|
+
* @classification "P0"
|
|
493
|
+
*/
|
|
494
|
+
createdAt: string;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/**
|
|
498
|
+
* AUTO-GENERATED by scripts/codegen.mjs from the corresponding JSON Schema
|
|
499
|
+
* in ../schemas/. DO NOT EDIT BY HAND — re-run `npm run codegen` instead.
|
|
500
|
+
*
|
|
501
|
+
* The x-classification / x-atomic-group / x-unit / x-encrypted custom JSON
|
|
502
|
+
* Schema annotations from the source schemas are folded into the generated
|
|
503
|
+
* TSDoc comments so grep can find them without leaving the editor.
|
|
504
|
+
*/
|
|
505
|
+
/**
|
|
506
|
+
* Full FR-3.4 record for a single completed drive, returned as a bare object (NOT an envelope) by GET /api/drives/{driveId} (rest-api.md §7.3). Contains every FR-3.4 stat EXCEPT routePoints, which is served separately by GET /api/drives/{driveId}/route (DriveRoute, §7.4). This is the tap-through detail behind a DriveSummary row / a `drive_ended` frame: the SDK's fetchDrive(driveId) helper. Mirrors the Go `driveDetail` struct + `toMaskMap` in telemetry/internal/telemetry/drive_detail_types.go and the `DriveDetail` component in telemetry/docs/contracts/specs/rest.openapi.yaml. Owners and viewers see the same field set including start/end location and address (viewer consent via accepted invite, §5.2.3).
|
|
507
|
+
*/
|
|
508
|
+
interface Drive {
|
|
509
|
+
/**
|
|
510
|
+
* Opaque database identifier (cuid) for this drive. Matches DriveSummary.id and the `driveId` carried by `drive_started` / `drive_ended` WebSocket frames.
|
|
511
|
+
*
|
|
512
|
+
* @classification "P0"
|
|
513
|
+
*/
|
|
514
|
+
id: string;
|
|
515
|
+
/**
|
|
516
|
+
* Opaque database identifier (cuid) for the drive's parent vehicle. Matches VehicleState.vehicleId / VehicleSummary.vehicleId / DriveSummary.vehicleId.
|
|
517
|
+
*
|
|
518
|
+
* @classification "P0"
|
|
519
|
+
*/
|
|
520
|
+
vehicleId: string;
|
|
521
|
+
/**
|
|
522
|
+
* ISO 8601 / RFC 3339 UTC timestamp when the drive began (e.g., '2026-04-13T18:22:00Z').
|
|
523
|
+
*
|
|
524
|
+
* @classification "P0"
|
|
525
|
+
*/
|
|
526
|
+
startTime: string;
|
|
527
|
+
/**
|
|
528
|
+
* ISO 8601 / RFC 3339 UTC timestamp when the drive ended (e.g., '2026-04-13T18:46:18Z').
|
|
529
|
+
*
|
|
530
|
+
* @classification "P0"
|
|
531
|
+
*/
|
|
532
|
+
endTime: string;
|
|
533
|
+
/**
|
|
534
|
+
* Local calendar date of the drive as a 'YYYY-MM-DD' string (e.g., '2026-04-13'). Same field as DriveSummary.date.
|
|
535
|
+
*
|
|
536
|
+
* @classification "P0"
|
|
537
|
+
*/
|
|
538
|
+
date: string;
|
|
539
|
+
/**
|
|
540
|
+
* Total distance driven, in miles (FR-3.4).
|
|
541
|
+
*
|
|
542
|
+
* @classification "P0"
|
|
543
|
+
* @unit "miles"
|
|
544
|
+
*/
|
|
545
|
+
distanceMiles: number;
|
|
546
|
+
/**
|
|
547
|
+
* Total drive duration, in seconds (FR-3.4). The Go server converts the stored DurationMinutes to seconds (DurationMinutes * 60) before emitting.
|
|
548
|
+
*
|
|
549
|
+
* @classification "P0"
|
|
550
|
+
* @unit "seconds"
|
|
551
|
+
*/
|
|
552
|
+
durationSeconds: number;
|
|
553
|
+
/**
|
|
554
|
+
* Average speed over the drive, in miles per hour (FR-3.4).
|
|
555
|
+
*
|
|
556
|
+
* @classification "P0"
|
|
557
|
+
* @unit "mph"
|
|
558
|
+
*/
|
|
559
|
+
avgSpeedMph: number;
|
|
560
|
+
/**
|
|
561
|
+
* Maximum speed reached during the drive, in miles per hour (FR-3.4).
|
|
562
|
+
*
|
|
563
|
+
* @classification "P0"
|
|
564
|
+
* @unit "mph"
|
|
565
|
+
*/
|
|
566
|
+
maxSpeedMph: number;
|
|
567
|
+
/**
|
|
568
|
+
* Energy consumed during the drive, in kilowatt-hours (FR-3.4). Detail-only stat — deliberately absent from DriveSummary.
|
|
569
|
+
*
|
|
570
|
+
* @classification "P0"
|
|
571
|
+
* @unit "kWh"
|
|
572
|
+
*/
|
|
573
|
+
energyUsedKwh: number;
|
|
574
|
+
/**
|
|
575
|
+
* Battery state of charge at drive start (FR-3.4).
|
|
576
|
+
*
|
|
577
|
+
* @classification "P0"
|
|
578
|
+
* @unit "percent"
|
|
579
|
+
*/
|
|
580
|
+
startChargeLevel: number;
|
|
581
|
+
/**
|
|
582
|
+
* Battery state of charge at drive end (FR-3.4).
|
|
583
|
+
*
|
|
584
|
+
* @classification "P0"
|
|
585
|
+
* @unit "percent"
|
|
586
|
+
*/
|
|
587
|
+
endChargeLevel: number;
|
|
588
|
+
/**
|
|
589
|
+
* Miles driven under Full Self-Driving during this drive (FR-3.4). Always present; defaults to 0 for a drive with no FSD usage.
|
|
590
|
+
*
|
|
591
|
+
* @classification "P0"
|
|
592
|
+
* @unit "miles"
|
|
593
|
+
*/
|
|
594
|
+
fsdMiles: number;
|
|
595
|
+
/**
|
|
596
|
+
* Percent of the drive covered by FSD (FR-3.4). Always present; defaults to 0 for a drive with no FSD usage.
|
|
597
|
+
*
|
|
598
|
+
* @classification "P0"
|
|
599
|
+
* @unit "percent"
|
|
600
|
+
*/
|
|
601
|
+
fsdPercentage: number;
|
|
602
|
+
/**
|
|
603
|
+
* Count of FSD interventions recorded during the drive (FR-3.4). Detail-only stat — deliberately absent from DriveSummary.
|
|
604
|
+
*
|
|
605
|
+
* @classification "P0"
|
|
606
|
+
*/
|
|
607
|
+
interventions: number;
|
|
608
|
+
/**
|
|
609
|
+
* Reverse-geocoded place name at the drive's start (e.g., 'Home'). OPTIONAL on the wire: the Go handler OMITS this key entirely (never emits '' or null) when the underlying Drive.startLocation column is empty (toMaskMap, telemetry/internal/telemetry/drive_detail_types.go). Consumers branch on key presence. NOTE: the OpenAPI DriveDetail component types this `[string, null]`, but the Go handler omits the key rather than emitting null — the code is ground truth (see MYR-202 PR body doc-drift note).
|
|
610
|
+
*
|
|
611
|
+
* @classification "P1"
|
|
612
|
+
*/
|
|
613
|
+
startLocation?: string;
|
|
614
|
+
/**
|
|
615
|
+
* Reverse-geocoded street address at the drive's start. OPTIONAL on the wire: the key is omitted entirely when the underlying Drive.startAddress column is empty (same omit-when-empty convention as startLocation).
|
|
616
|
+
*
|
|
617
|
+
* @classification "P1"
|
|
618
|
+
*/
|
|
619
|
+
startAddress?: string;
|
|
620
|
+
/**
|
|
621
|
+
* Reverse-geocoded place name at the drive's end. OPTIONAL on the wire: the key is omitted entirely when the underlying Drive.endLocation column is empty.
|
|
622
|
+
*
|
|
623
|
+
* @classification "P1"
|
|
624
|
+
*/
|
|
625
|
+
endLocation?: string;
|
|
626
|
+
/**
|
|
627
|
+
* Reverse-geocoded street address at the drive's end. OPTIONAL on the wire: the key is omitted entirely when the underlying Drive.endAddress column is empty.
|
|
628
|
+
*
|
|
629
|
+
* @classification "P1"
|
|
630
|
+
*/
|
|
631
|
+
endAddress?: string;
|
|
632
|
+
/**
|
|
633
|
+
* ISO 8601 / RFC 3339 UTC row-creation timestamp. The Go server renders it as `CreatedAt.UTC().Format(time.RFC3339)` (telemetry/internal/telemetry/drive_detail_types.go).
|
|
634
|
+
*
|
|
635
|
+
* @classification "P0"
|
|
636
|
+
*/
|
|
637
|
+
createdAt: string;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* AUTO-GENERATED by scripts/codegen.mjs from the corresponding JSON Schema
|
|
642
|
+
* in ../schemas/. DO NOT EDIT BY HAND — re-run `npm run codegen` instead.
|
|
643
|
+
*
|
|
644
|
+
* The x-classification / x-atomic-group / x-unit / x-encrypted custom JSON
|
|
645
|
+
* Schema annotations from the source schemas are folded into the generated
|
|
646
|
+
* TSDoc comments so grep can find them without leaving the editor.
|
|
647
|
+
*/
|
|
648
|
+
/**
|
|
649
|
+
* Full GPS polyline for a single completed drive, returned as a bare object by GET /api/drives/{driveId}/route (rest-api.md §7.4, FR-3.3). This is the heavy per-drive route payload deliberately excluded from both DriveSummary (§7.2) and Drive detail (§7.3): the SDK fetches it lazily on tap-through of a drive's map view, NOT eagerly per drive-list row (see §7.4 lazy-fetch guidance — cellular bandwidth / perceived latency, not heap pressure). The polyline is AES-256-GCM encrypted at rest on Drive.routePoints (NFR-3.23, data-classification.md §1.5) and decrypted in the store layer (NFR-3.25) before the handler serializes it; the SDK never sees ciphertext. Mirrors the Go `driveRouteResponse` struct in telemetry/internal/telemetry/drive_route_handler.go and the `DriveRoute` component in telemetry/docs/contracts/specs/rest.openapi.yaml. NOTE: the handler passes `routePoints` through as `json.RawMessage` (raw decrypted JSONB) — the per-point shape is owned by the writer that persisted it (store.RoutePointRecord, telemetry/internal/store/types.go), so RoutePoint below documents that persisted format field-for-field.
|
|
650
|
+
*/
|
|
651
|
+
interface DriveRoute {
|
|
652
|
+
/**
|
|
653
|
+
* Opaque database identifier (cuid) for this drive. Echoes the `driveId` path param and matches Drive.id / DriveSummary.id and the `driveId` carried by `drive_started` / `drive_ended` WebSocket frames. Always present (Go `driveRouteResponse.DriveID`, no omitempty).
|
|
654
|
+
*
|
|
655
|
+
* @classification "P0"
|
|
656
|
+
*/
|
|
657
|
+
driveId: string;
|
|
658
|
+
/**
|
|
659
|
+
* Ordered GPS breadcrumb trail for the drive, oldest point first, suitable for rendering the drive's polyline on a map. ALWAYS PRESENT and ALWAYS AN ARRAY, never null: the Go handler substitutes an empty `[]` when the underlying Drive.routePoints column is empty/absent (writeMaskedRoute, telemetry/internal/telemetry/drive_route_handler.go) — so a very short drive with no captured points, or a route that failed to decrypt, yields `[]` rather than a missing key or null. A 60-minute drive captured at ~1 Hz is roughly 3,600 points (~200-300 KB JSON); no server-side downsampling or paging — the entire polyline is returned in one response (there is no cursor/limit on this endpoint, unlike the §7.2 drives list).
|
|
660
|
+
*
|
|
661
|
+
* @classification "mixed"
|
|
662
|
+
*/
|
|
663
|
+
routePoints: RoutePoint[];
|
|
664
|
+
}
|
|
665
|
+
/**
|
|
666
|
+
* A single GPS sample on a drive's polyline. Matches the `RoutePointRecord` shape from data-classification.md §1.5 — the JSONB element format persisted by the Go writer (store.RoutePointRecord, telemetry/internal/store/types.go) and the legacy Next.js app. lat/lng are P1 (identifying GPS); speed, heading, and timestamp are P0 in isolation but are encrypted at rest as a unit inside the P1 routePoints column (NFR-3.23) and MUST NOT be logged separately. All five fields are always present on the wire (the Go struct carries no omitempty). Mirrors the `RoutePoint` component in telemetry/docs/contracts/specs/rest.openapi.yaml.
|
|
667
|
+
*
|
|
668
|
+
* This interface was referenced by `DriveRoute`'s JSON-Schema
|
|
669
|
+
* via the `definition` "RoutePoint".
|
|
670
|
+
*/
|
|
671
|
+
interface RoutePoint {
|
|
672
|
+
/**
|
|
673
|
+
* Latitude in degrees at this point. Emitted as a JSON number (Go float64); whole-degree values serialize without a decimal point (Go shortest-float marshaling), so `10` and `10.0000` are the same value on the wire.
|
|
674
|
+
*
|
|
675
|
+
* @classification "P1"
|
|
676
|
+
* @unit "degrees"
|
|
677
|
+
* @encrypted-at-rest true
|
|
678
|
+
*/
|
|
679
|
+
lat: number;
|
|
680
|
+
/**
|
|
681
|
+
* Longitude in degrees at this point. Emitted as a JSON number (Go float64).
|
|
682
|
+
*
|
|
683
|
+
* @classification "P1"
|
|
684
|
+
* @unit "degrees"
|
|
685
|
+
* @encrypted-at-rest true
|
|
686
|
+
*/
|
|
687
|
+
lng: number;
|
|
688
|
+
/**
|
|
689
|
+
* Instantaneous speed at this point, in miles per hour. P0 in isolation but encrypted at rest alongside the parent P1 routePoints column. Emitted as a JSON number (Go float64).
|
|
690
|
+
*
|
|
691
|
+
* @classification "P0"
|
|
692
|
+
* @unit "mph"
|
|
693
|
+
*/
|
|
694
|
+
speed: number;
|
|
695
|
+
/**
|
|
696
|
+
* Compass heading at this point, in degrees (0-360, 0 = North). P0 in isolation but encrypted at rest alongside the parent P1 routePoints column. Emitted as a JSON number (Go float64).
|
|
697
|
+
*
|
|
698
|
+
* @classification "P0"
|
|
699
|
+
* @unit "degrees"
|
|
700
|
+
*/
|
|
701
|
+
heading: number;
|
|
702
|
+
/**
|
|
703
|
+
* ISO 8601 / RFC 3339 UTC timestamp at which this point was captured. The Go writer renders it as `Timestamp.Format(time.RFC3339)` (telemetry/internal/store/drive_mapper.go). P0 in isolation but encrypted at rest alongside the parent P1 routePoints column.
|
|
704
|
+
*
|
|
705
|
+
* @classification "P0"
|
|
706
|
+
*/
|
|
707
|
+
timestamp: string;
|
|
708
|
+
}
|
|
709
|
+
|
|
341
710
|
/**
|
|
342
711
|
* AUTO-GENERATED by scripts/codegen.mjs from the corresponding JSON Schema
|
|
343
712
|
* in ../schemas/. DO NOT EDIT BY HAND — re-run `npm run codegen` instead.
|
|
@@ -681,4 +1050,4 @@ interface WebSocketEnvelope {
|
|
|
681
1050
|
ts?: string;
|
|
682
1051
|
}
|
|
683
1052
|
|
|
684
|
-
export type { AuthOkPayload, AuthPayload, ConnectivityPayload, DriveEndedPayload, DriveStartedPayload, ErrorPayload, HeartbeatPayload, MessageType, PingPayload, PongPayload, SubscribePayload, UnsubscribePayload, VehicleListResponse, VehicleState, VehicleSummary, VehicleUpdatePayload, WebSocketEnvelope, WebSocketMessages };
|
|
1053
|
+
export type { AuthOkPayload, AuthPayload, ConnectivityPayload, Drive, DriveEndedPayload, DriveRoute, DriveStartedPayload, DriveSummary, DrivesListResponse, ErrorPayload, HeartbeatPayload, MessageType, PingPayload, PongPayload, RoutePoint, SubscribePayload, UnsubscribePayload, VehicleListResponse, VehicleState, VehicleSummary, VehicleUpdatePayload, WebSocketEnvelope, WebSocketMessages };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myrobotaxi/contracts",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Canonical wire-protocol schemas for the MyRoboTaxi platform. JSON Schema (draft-2020-12) + pre-generated TypeScript types. Consumed by SDKs (TS, Swift) and the Go telemetry server.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -39,6 +39,9 @@
|
|
|
39
39
|
},
|
|
40
40
|
"./schemas/vehicle-state.json": "./schemas/vehicle-state.schema.json",
|
|
41
41
|
"./schemas/vehicle-summary.json": "./schemas/vehicle-summary.schema.json",
|
|
42
|
+
"./schemas/drives-list.json": "./schemas/drives-list.schema.json",
|
|
43
|
+
"./schemas/drive-detail.json": "./schemas/drive-detail.schema.json",
|
|
44
|
+
"./schemas/drive-route.json": "./schemas/drive-route.schema.json",
|
|
42
45
|
"./schemas/ws-messages.json": "./schemas/ws-messages.schema.json",
|
|
43
46
|
"./schemas/ws-envelope.json": "./schemas/ws-envelope.schema.json"
|
|
44
47
|
},
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://myrobotaxi.com/schemas/drive-detail.schema.json",
|
|
4
|
+
"title": "Drive",
|
|
5
|
+
"description": "Full FR-3.4 record for a single completed drive, returned as a bare object (NOT an envelope) by GET /api/drives/{driveId} (rest-api.md §7.3). Contains every FR-3.4 stat EXCEPT routePoints, which is served separately by GET /api/drives/{driveId}/route (DriveRoute, §7.4). This is the tap-through detail behind a DriveSummary row / a `drive_ended` frame: the SDK's fetchDrive(driveId) helper. Mirrors the Go `driveDetail` struct + `toMaskMap` in telemetry/internal/telemetry/drive_detail_types.go and the `DriveDetail` component in telemetry/docs/contracts/specs/rest.openapi.yaml. Owners and viewers see the same field set including start/end location and address (viewer consent via accepted invite, §5.2.3).",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": [
|
|
9
|
+
"id",
|
|
10
|
+
"vehicleId",
|
|
11
|
+
"startTime",
|
|
12
|
+
"endTime",
|
|
13
|
+
"date",
|
|
14
|
+
"distanceMiles",
|
|
15
|
+
"durationSeconds",
|
|
16
|
+
"avgSpeedMph",
|
|
17
|
+
"maxSpeedMph",
|
|
18
|
+
"energyUsedKwh",
|
|
19
|
+
"startChargeLevel",
|
|
20
|
+
"endChargeLevel",
|
|
21
|
+
"fsdMiles",
|
|
22
|
+
"fsdPercentage",
|
|
23
|
+
"interventions",
|
|
24
|
+
"createdAt"
|
|
25
|
+
],
|
|
26
|
+
"properties": {
|
|
27
|
+
"id": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "Opaque database identifier (cuid) for this drive. Matches DriveSummary.id and the `driveId` carried by `drive_started` / `drive_ended` WebSocket frames.",
|
|
30
|
+
"x-classification": "P0",
|
|
31
|
+
"examples": ["clmno9876543210zyxw0001"]
|
|
32
|
+
},
|
|
33
|
+
"vehicleId": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "Opaque database identifier (cuid) for the drive's parent vehicle. Matches VehicleState.vehicleId / VehicleSummary.vehicleId / DriveSummary.vehicleId.",
|
|
36
|
+
"x-classification": "P0",
|
|
37
|
+
"examples": ["clxyz1234567890abcdef"]
|
|
38
|
+
},
|
|
39
|
+
"startTime": {
|
|
40
|
+
"type": "string",
|
|
41
|
+
"format": "date-time",
|
|
42
|
+
"description": "ISO 8601 / RFC 3339 UTC timestamp when the drive began (e.g., '2026-04-13T18:22:00Z').",
|
|
43
|
+
"x-classification": "P0",
|
|
44
|
+
"examples": ["2026-04-13T18:22:00Z"]
|
|
45
|
+
},
|
|
46
|
+
"endTime": {
|
|
47
|
+
"type": "string",
|
|
48
|
+
"format": "date-time",
|
|
49
|
+
"description": "ISO 8601 / RFC 3339 UTC timestamp when the drive ended (e.g., '2026-04-13T18:46:18Z').",
|
|
50
|
+
"x-classification": "P0",
|
|
51
|
+
"examples": ["2026-04-13T18:46:18Z"]
|
|
52
|
+
},
|
|
53
|
+
"date": {
|
|
54
|
+
"type": "string",
|
|
55
|
+
"format": "date",
|
|
56
|
+
"description": "Local calendar date of the drive as a 'YYYY-MM-DD' string (e.g., '2026-04-13'). Same field as DriveSummary.date.",
|
|
57
|
+
"x-classification": "P0",
|
|
58
|
+
"examples": ["2026-04-13"]
|
|
59
|
+
},
|
|
60
|
+
"distanceMiles": {
|
|
61
|
+
"type": "number",
|
|
62
|
+
"description": "Total distance driven, in miles (FR-3.4).",
|
|
63
|
+
"x-classification": "P0",
|
|
64
|
+
"x-unit": "miles",
|
|
65
|
+
"minimum": 0,
|
|
66
|
+
"examples": [12.4]
|
|
67
|
+
},
|
|
68
|
+
"durationSeconds": {
|
|
69
|
+
"type": "integer",
|
|
70
|
+
"description": "Total drive duration, in seconds (FR-3.4). The Go server converts the stored DurationMinutes to seconds (DurationMinutes * 60) before emitting.",
|
|
71
|
+
"x-classification": "P0",
|
|
72
|
+
"x-unit": "seconds",
|
|
73
|
+
"minimum": 0,
|
|
74
|
+
"examples": [1458]
|
|
75
|
+
},
|
|
76
|
+
"avgSpeedMph": {
|
|
77
|
+
"type": "number",
|
|
78
|
+
"description": "Average speed over the drive, in miles per hour (FR-3.4).",
|
|
79
|
+
"x-classification": "P0",
|
|
80
|
+
"x-unit": "mph",
|
|
81
|
+
"minimum": 0,
|
|
82
|
+
"examples": [30.5]
|
|
83
|
+
},
|
|
84
|
+
"maxSpeedMph": {
|
|
85
|
+
"type": "number",
|
|
86
|
+
"description": "Maximum speed reached during the drive, in miles per hour (FR-3.4).",
|
|
87
|
+
"x-classification": "P0",
|
|
88
|
+
"x-unit": "mph",
|
|
89
|
+
"minimum": 0,
|
|
90
|
+
"examples": [65.2]
|
|
91
|
+
},
|
|
92
|
+
"energyUsedKwh": {
|
|
93
|
+
"type": "number",
|
|
94
|
+
"description": "Energy consumed during the drive, in kilowatt-hours (FR-3.4). Detail-only stat — deliberately absent from DriveSummary.",
|
|
95
|
+
"x-classification": "P0",
|
|
96
|
+
"x-unit": "kWh",
|
|
97
|
+
"minimum": 0,
|
|
98
|
+
"examples": [4.2]
|
|
99
|
+
},
|
|
100
|
+
"startChargeLevel": {
|
|
101
|
+
"type": "integer",
|
|
102
|
+
"description": "Battery state of charge at drive start (FR-3.4).",
|
|
103
|
+
"x-classification": "P0",
|
|
104
|
+
"x-unit": "percent",
|
|
105
|
+
"minimum": 0,
|
|
106
|
+
"maximum": 100,
|
|
107
|
+
"examples": [82]
|
|
108
|
+
},
|
|
109
|
+
"endChargeLevel": {
|
|
110
|
+
"type": "integer",
|
|
111
|
+
"description": "Battery state of charge at drive end (FR-3.4).",
|
|
112
|
+
"x-classification": "P0",
|
|
113
|
+
"x-unit": "percent",
|
|
114
|
+
"minimum": 0,
|
|
115
|
+
"maximum": 100,
|
|
116
|
+
"examples": [76]
|
|
117
|
+
},
|
|
118
|
+
"fsdMiles": {
|
|
119
|
+
"type": "number",
|
|
120
|
+
"description": "Miles driven under Full Self-Driving during this drive (FR-3.4). Always present; defaults to 0 for a drive with no FSD usage.",
|
|
121
|
+
"x-classification": "P0",
|
|
122
|
+
"x-unit": "miles",
|
|
123
|
+
"minimum": 0,
|
|
124
|
+
"examples": [8.1]
|
|
125
|
+
},
|
|
126
|
+
"fsdPercentage": {
|
|
127
|
+
"type": "number",
|
|
128
|
+
"description": "Percent of the drive covered by FSD (FR-3.4). Always present; defaults to 0 for a drive with no FSD usage.",
|
|
129
|
+
"x-classification": "P0",
|
|
130
|
+
"x-unit": "percent",
|
|
131
|
+
"minimum": 0,
|
|
132
|
+
"maximum": 100,
|
|
133
|
+
"examples": [65.3]
|
|
134
|
+
},
|
|
135
|
+
"interventions": {
|
|
136
|
+
"type": "integer",
|
|
137
|
+
"description": "Count of FSD interventions recorded during the drive (FR-3.4). Detail-only stat — deliberately absent from DriveSummary.",
|
|
138
|
+
"x-classification": "P0",
|
|
139
|
+
"minimum": 0,
|
|
140
|
+
"examples": [1]
|
|
141
|
+
},
|
|
142
|
+
"startLocation": {
|
|
143
|
+
"type": "string",
|
|
144
|
+
"description": "Reverse-geocoded place name at the drive's start (e.g., 'Home'). OPTIONAL on the wire: the Go handler OMITS this key entirely (never emits '' or null) when the underlying Drive.startLocation column is empty (toMaskMap, telemetry/internal/telemetry/drive_detail_types.go). Consumers branch on key presence. NOTE: the OpenAPI DriveDetail component types this `[string, null]`, but the Go handler omits the key rather than emitting null — the code is ground truth (see MYR-202 PR body doc-drift note).",
|
|
145
|
+
"x-classification": "P1",
|
|
146
|
+
"examples": ["Home"]
|
|
147
|
+
},
|
|
148
|
+
"startAddress": {
|
|
149
|
+
"type": "string",
|
|
150
|
+
"description": "Reverse-geocoded street address at the drive's start. OPTIONAL on the wire: the key is omitted entirely when the underlying Drive.startAddress column is empty (same omit-when-empty convention as startLocation).",
|
|
151
|
+
"x-classification": "P1",
|
|
152
|
+
"examples": ["742 Evergreen Terrace, San Francisco, CA 94107"]
|
|
153
|
+
},
|
|
154
|
+
"endLocation": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"description": "Reverse-geocoded place name at the drive's end. OPTIONAL on the wire: the key is omitted entirely when the underlying Drive.endLocation column is empty.",
|
|
157
|
+
"x-classification": "P1",
|
|
158
|
+
"examples": ["Whole Foods Market"]
|
|
159
|
+
},
|
|
160
|
+
"endAddress": {
|
|
161
|
+
"type": "string",
|
|
162
|
+
"description": "Reverse-geocoded street address at the drive's end. OPTIONAL on the wire: the key is omitted entirely when the underlying Drive.endAddress column is empty.",
|
|
163
|
+
"x-classification": "P1",
|
|
164
|
+
"examples": ["399 4th Street, San Francisco, CA 94107"]
|
|
165
|
+
},
|
|
166
|
+
"createdAt": {
|
|
167
|
+
"type": "string",
|
|
168
|
+
"format": "date-time",
|
|
169
|
+
"description": "ISO 8601 / RFC 3339 UTC row-creation timestamp. The Go server renders it as `CreatedAt.UTC().Format(time.RFC3339)` (telemetry/internal/telemetry/drive_detail_types.go).",
|
|
170
|
+
"x-classification": "P0",
|
|
171
|
+
"examples": ["2026-04-13T18:46:19Z"]
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://myrobotaxi.com/schemas/drive-route.schema.json",
|
|
4
|
+
"title": "DriveRoute",
|
|
5
|
+
"description": "Full GPS polyline for a single completed drive, returned as a bare object by GET /api/drives/{driveId}/route (rest-api.md §7.4, FR-3.3). This is the heavy per-drive route payload deliberately excluded from both DriveSummary (§7.2) and Drive detail (§7.3): the SDK fetches it lazily on tap-through of a drive's map view, NOT eagerly per drive-list row (see §7.4 lazy-fetch guidance — cellular bandwidth / perceived latency, not heap pressure). The polyline is AES-256-GCM encrypted at rest on Drive.routePoints (NFR-3.23, data-classification.md §1.5) and decrypted in the store layer (NFR-3.25) before the handler serializes it; the SDK never sees ciphertext. Mirrors the Go `driveRouteResponse` struct in telemetry/internal/telemetry/drive_route_handler.go and the `DriveRoute` component in telemetry/docs/contracts/specs/rest.openapi.yaml. NOTE: the handler passes `routePoints` through as `json.RawMessage` (raw decrypted JSONB) — the per-point shape is owned by the writer that persisted it (store.RoutePointRecord, telemetry/internal/store/types.go), so RoutePoint below documents that persisted format field-for-field.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"required": ["driveId", "routePoints"],
|
|
9
|
+
"properties": {
|
|
10
|
+
"driveId": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Opaque database identifier (cuid) for this drive. Echoes the `driveId` path param and matches Drive.id / DriveSummary.id and the `driveId` carried by `drive_started` / `drive_ended` WebSocket frames. Always present (Go `driveRouteResponse.DriveID`, no omitempty).",
|
|
13
|
+
"x-classification": "P0",
|
|
14
|
+
"examples": ["clmno9876543210zyxw0001"]
|
|
15
|
+
},
|
|
16
|
+
"routePoints": {
|
|
17
|
+
"type": "array",
|
|
18
|
+
"description": "Ordered GPS breadcrumb trail for the drive, oldest point first, suitable for rendering the drive's polyline on a map. ALWAYS PRESENT and ALWAYS AN ARRAY, never null: the Go handler substitutes an empty `[]` when the underlying Drive.routePoints column is empty/absent (writeMaskedRoute, telemetry/internal/telemetry/drive_route_handler.go) — so a very short drive with no captured points, or a route that failed to decrypt, yields `[]` rather than a missing key or null. A 60-minute drive captured at ~1 Hz is roughly 3,600 points (~200-300 KB JSON); no server-side downsampling or paging — the entire polyline is returned in one response (there is no cursor/limit on this endpoint, unlike the §7.2 drives list).",
|
|
19
|
+
"x-classification": "mixed",
|
|
20
|
+
"items": { "$ref": "#/$defs/RoutePoint" }
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"$defs": {
|
|
24
|
+
"RoutePoint": {
|
|
25
|
+
"title": "RoutePoint",
|
|
26
|
+
"description": "A single GPS sample on a drive's polyline. Matches the `RoutePointRecord` shape from data-classification.md §1.5 — the JSONB element format persisted by the Go writer (store.RoutePointRecord, telemetry/internal/store/types.go) and the legacy Next.js app. lat/lng are P1 (identifying GPS); speed, heading, and timestamp are P0 in isolation but are encrypted at rest as a unit inside the P1 routePoints column (NFR-3.23) and MUST NOT be logged separately. All five fields are always present on the wire (the Go struct carries no omitempty). Mirrors the `RoutePoint` component in telemetry/docs/contracts/specs/rest.openapi.yaml.",
|
|
27
|
+
"type": "object",
|
|
28
|
+
"additionalProperties": false,
|
|
29
|
+
"required": ["lat", "lng", "speed", "heading", "timestamp"],
|
|
30
|
+
"properties": {
|
|
31
|
+
"lat": {
|
|
32
|
+
"type": "number",
|
|
33
|
+
"description": "Latitude in degrees at this point. Emitted as a JSON number (Go float64); whole-degree values serialize without a decimal point (Go shortest-float marshaling), so `10` and `10.0000` are the same value on the wire.",
|
|
34
|
+
"x-classification": "P1",
|
|
35
|
+
"x-encrypted-at-rest": true,
|
|
36
|
+
"x-unit": "degrees",
|
|
37
|
+
"minimum": -90,
|
|
38
|
+
"maximum": 90,
|
|
39
|
+
"examples": [37.7749]
|
|
40
|
+
},
|
|
41
|
+
"lng": {
|
|
42
|
+
"type": "number",
|
|
43
|
+
"description": "Longitude in degrees at this point. Emitted as a JSON number (Go float64).",
|
|
44
|
+
"x-classification": "P1",
|
|
45
|
+
"x-encrypted-at-rest": true,
|
|
46
|
+
"x-unit": "degrees",
|
|
47
|
+
"minimum": -180,
|
|
48
|
+
"maximum": 180,
|
|
49
|
+
"examples": [-122.4194]
|
|
50
|
+
},
|
|
51
|
+
"speed": {
|
|
52
|
+
"type": "number",
|
|
53
|
+
"description": "Instantaneous speed at this point, in miles per hour. P0 in isolation but encrypted at rest alongside the parent P1 routePoints column. Emitted as a JSON number (Go float64).",
|
|
54
|
+
"x-classification": "P0",
|
|
55
|
+
"x-unit": "mph",
|
|
56
|
+
"minimum": 0,
|
|
57
|
+
"examples": [15]
|
|
58
|
+
},
|
|
59
|
+
"heading": {
|
|
60
|
+
"type": "number",
|
|
61
|
+
"description": "Compass heading at this point, in degrees (0-360, 0 = North). P0 in isolation but encrypted at rest alongside the parent P1 routePoints column. Emitted as a JSON number (Go float64).",
|
|
62
|
+
"x-classification": "P0",
|
|
63
|
+
"x-unit": "degrees",
|
|
64
|
+
"minimum": 0,
|
|
65
|
+
"maximum": 360,
|
|
66
|
+
"examples": [175]
|
|
67
|
+
},
|
|
68
|
+
"timestamp": {
|
|
69
|
+
"type": "string",
|
|
70
|
+
"format": "date-time",
|
|
71
|
+
"description": "ISO 8601 / RFC 3339 UTC timestamp at which this point was captured. The Go writer renders it as `Timestamp.Format(time.RFC3339)` (telemetry/internal/store/drive_mapper.go). P0 in isolation but encrypted at rest alongside the parent P1 routePoints column.",
|
|
72
|
+
"x-classification": "P0",
|
|
73
|
+
"examples": ["2026-04-13T18:22:00Z"]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|