@fleetless/contracts 1.0.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/CHANGELOG.md +26 -0
- package/LICENSE +202 -0
- package/NOTICE +17 -0
- package/README.md +88 -0
- package/artifacts/constants.json +24 -0
- package/artifacts/openapi.json +17219 -0
- package/artifacts/routes.json +4605 -0
- package/artifacts/schema/accept-team-invite-request.schema.json +22 -0
- package/artifacts/schema/action-config.schema.json +198 -0
- package/artifacts/schema/alert-list-response.schema.json +172 -0
- package/artifacts/schema/api-error.schema.json +20 -0
- package/artifacts/schema/app-auth-config.schema.json +106 -0
- package/artifacts/schema/app-invitation-list-response.schema.json +57 -0
- package/artifacts/schema/app-invitation.schema.json +69 -0
- package/artifacts/schema/app-list-response.schema.json +82 -0
- package/artifacts/schema/app-mail-template-list-response.schema.json +68 -0
- package/artifacts/schema/app-mail-template.schema.json +54 -0
- package/artifacts/schema/app-oidc-provider-list-response.schema.json +93 -0
- package/artifacts/schema/app-oidc-provider.schema.json +80 -0
- package/artifacts/schema/app-user-list-response.schema.json +111 -0
- package/artifacts/schema/app-user.schema.json +98 -0
- package/artifacts/schema/app.schema.json +69 -0
- package/artifacts/schema/apply-error.schema.json +41 -0
- package/artifacts/schema/asset-list-response.schema.json +288 -0
- package/artifacts/schema/asset-sync-request.schema.json +17 -0
- package/artifacts/schema/asset-sync-response.schema.json +16 -0
- package/artifacts/schema/asset-sync-status.schema.json +136 -0
- package/artifacts/schema/asset.schema.json +68 -0
- package/artifacts/schema/audit-actor.schema.json +32 -0
- package/artifacts/schema/audit-event.schema.json +119 -0
- package/artifacts/schema/audit-list-response.schema.json +144 -0
- package/artifacts/schema/audit-query.schema.json +79 -0
- package/artifacts/schema/auth-error.schema.json +23 -0
- package/artifacts/schema/auth-me-response.schema.json +99 -0
- package/artifacts/schema/auth-ok.schema.json +115 -0
- package/artifacts/schema/authorization-server-metadata.schema.json +80 -0
- package/artifacts/schema/bridge-asset-progress.schema.json +99 -0
- package/artifacts/schema/bridge-assets-available.schema.json +25 -0
- package/artifacts/schema/bridge-camera-state.schema.json +78 -0
- package/artifacts/schema/bridge-config-applied.schema.json +67 -0
- package/artifacts/schema/bridge-hello.schema.json +65 -0
- package/artifacts/schema/bridge-introspect.schema.json +114 -0
- package/artifacts/schema/bridge-job-lost.schema.json +22 -0
- package/artifacts/schema/bridge-job-update.schema.json +100 -0
- package/artifacts/schema/bridge-pong.schema.json +19 -0
- package/artifacts/schema/bridge-pressure.schema.json +292 -0
- package/artifacts/schema/bridge-state.schema.json +24 -0
- package/artifacts/schema/bridge-type-definitions.schema.json +169 -0
- package/artifacts/schema/busy-details.schema.json +115 -0
- package/artifacts/schema/camera-descriptor.schema.json +45 -0
- package/artifacts/schema/camera-list-response.schema.json +58 -0
- package/artifacts/schema/camera-source.schema.json +240 -0
- package/artifacts/schema/cancel-request.schema.json +20 -0
- package/artifacts/schema/client-accept-invitation-request.schema.json +35 -0
- package/artifacts/schema/client-auth.schema.json +18 -0
- package/artifacts/schema/client-cancel.schema.json +45 -0
- package/artifacts/schema/client-identity.schema.json +103 -0
- package/artifacts/schema/client-invoke.schema.json +45 -0
- package/artifacts/schema/client-login-request.schema.json +29 -0
- package/artifacts/schema/client-logout-request.schema.json +14 -0
- package/artifacts/schema/client-mcp-interaction-decision-response.schema.json +15 -0
- package/artifacts/schema/client-mcp-interaction.schema.json +59 -0
- package/artifacts/schema/client-oidc-callback-query.schema.json +28 -0
- package/artifacts/schema/client-oidc-exchange-request.schema.json +21 -0
- package/artifacts/schema/client-oidc-start-query.schema.json +36 -0
- package/artifacts/schema/client-password-reset-confirm-request.schema.json +22 -0
- package/artifacts/schema/client-password-reset-request.schema.json +24 -0
- package/artifacts/schema/client-provider-list-query.schema.json +17 -0
- package/artifacts/schema/client-provider-list-response.schema.json +34 -0
- package/artifacts/schema/client-publish.schema.json +40 -0
- package/artifacts/schema/client-refresh-request.schema.json +14 -0
- package/artifacts/schema/client-register-request.schema.json +44 -0
- package/artifacts/schema/client-resend-verification-request.schema.json +24 -0
- package/artifacts/schema/client-subscribe.schema.json +43 -0
- package/artifacts/schema/client-unsubscribe.schema.json +26 -0
- package/artifacts/schema/client-verify-email-request.schema.json +15 -0
- package/artifacts/schema/cloud-asset-request.schema.json +37 -0
- package/artifacts/schema/cloud-camera-start.schema.json +41 -0
- package/artifacts/schema/cloud-camera-stop.schema.json +26 -0
- package/artifacts/schema/cloud-cancel.schema.json +33 -0
- package/artifacts/schema/cloud-config.schema.json +1635 -0
- package/artifacts/schema/cloud-hello-error.schema.json +23 -0
- package/artifacts/schema/cloud-hello-ok.schema.json +19 -0
- package/artifacts/schema/cloud-introspect-request.schema.json +19 -0
- package/artifacts/schema/cloud-invoke.schema.json +40 -0
- package/artifacts/schema/cloud-ping.schema.json +19 -0
- package/artifacts/schema/cloud-publish.schema.json +28 -0
- package/artifacts/schema/cloud-type-request.schema.json +30 -0
- package/artifacts/schema/command-result.schema.json +175 -0
- package/artifacts/schema/config-draft-response.schema.json +1695 -0
- package/artifacts/schema/config-state.schema.json +124 -0
- package/artifacts/schema/config-version-response.schema.json +1641 -0
- package/artifacts/schema/config-versions-response.schema.json +33 -0
- package/artifacts/schema/create-app-invitation-request.schema.json +40 -0
- package/artifacts/schema/create-app-oidc-provider-request.schema.json +70 -0
- package/artifacts/schema/create-app-request.schema.json +30 -0
- package/artifacts/schema/create-app-user-request.schema.json +42 -0
- package/artifacts/schema/create-robot-request.schema.json +14 -0
- package/artifacts/schema/create-robot-response.schema.json +44 -0
- package/artifacts/schema/create-server-key-response.schema.json +65 -0
- package/artifacts/schema/create-team-invite-request.schema.json +43 -0
- package/artifacts/schema/datapoint-alert-row.schema.json +160 -0
- package/artifacts/schema/datapoint-config.schema.json +366 -0
- package/artifacts/schema/datapoint-display.schema.json +31 -0
- package/artifacts/schema/datapoint-event.schema.json +34 -0
- package/artifacts/schema/datapoint-frame.schema.json +28 -0
- package/artifacts/schema/datapoint-list-response.schema.json +61 -0
- package/artifacts/schema/datapoint-value.schema.json +28 -0
- package/artifacts/schema/developer-login-request.schema.json +19 -0
- package/artifacts/schema/dynamic-client-registration-request.schema.json +60 -0
- package/artifacts/schema/dynamic-client-registration-response.schema.json +68 -0
- package/artifacts/schema/error-frame.schema.json +23 -0
- package/artifacts/schema/exposure-counts.schema.json +39 -0
- package/artifacts/schema/exposure-list-response.schema.json +43 -0
- package/artifacts/schema/fetch-types-request.schema.json +19 -0
- package/artifacts/schema/fetch-types-response.schema.json +163 -0
- package/artifacts/schema/fleetless-user-list-response.schema.json +73 -0
- package/artifacts/schema/fleetless-user.schema.json +60 -0
- package/artifacts/schema/history-buckets-response.schema.json +79 -0
- package/artifacts/schema/history-query.schema.json +58 -0
- package/artifacts/schema/history-response.schema.json +150 -0
- package/artifacts/schema/history-samples-response.schema.json +68 -0
- package/artifacts/schema/introspection-response.schema.json +118 -0
- package/artifacts/schema/invoke-or-service-response.schema.json +141 -0
- package/artifacts/schema/invoke-request.schema.json +23 -0
- package/artifacts/schema/invoke-response.schema.json +125 -0
- package/artifacts/schema/job-actor.schema.json +34 -0
- package/artifacts/schema/job-event.schema.json +158 -0
- package/artifacts/schema/job-response.schema.json +123 -0
- package/artifacts/schema/job-run-list-response.schema.json +222 -0
- package/artifacts/schema/job-run-query.schema.json +95 -0
- package/artifacts/schema/job-run-summary-query.schema.json +23 -0
- package/artifacts/schema/job-run-summary.schema.json +33 -0
- package/artifacts/schema/job-run.schema.json +195 -0
- package/artifacts/schema/job-state.schema.json +11 -0
- package/artifacts/schema/job.schema.json +106 -0
- package/artifacts/schema/latency-bucket.schema.json +63 -0
- package/artifacts/schema/live-session-response.schema.json +41 -0
- package/artifacts/schema/mail-outcome.schema.json +20 -0
- package/artifacts/schema/mail-template-preview-request.schema.json +35 -0
- package/artifacts/schema/mail-template-preview-response.schema.json +31 -0
- package/artifacts/schema/mail-template-problem-details.schema.json +24 -0
- package/artifacts/schema/mcp-consent-grant-list-response.schema.json +52 -0
- package/artifacts/schema/mcp-consent-grant.schema.json +39 -0
- package/artifacts/schema/mcp-robot-datasheet.schema.json +115 -0
- package/artifacts/schema/mcp-role-preview-response.schema.json +134 -0
- package/artifacts/schema/missing-asset-query.schema.json +11 -0
- package/artifacts/schema/oauth-authorize-query.schema.json +47 -0
- package/artifacts/schema/oauth-redirect-response.schema.json +15 -0
- package/artifacts/schema/oauth-token-request.schema.json +47 -0
- package/artifacts/schema/oauth-token-response.schema.json +38 -0
- package/artifacts/schema/org-alerts-query.schema.json +15 -0
- package/artifacts/schema/org-event-dropped.schema.json +26 -0
- package/artifacts/schema/org-event-replay.schema.json +97 -0
- package/artifacts/schema/org-event-subscribe.schema.json +14 -0
- package/artifacts/schema/org-event-unsubscribe.schema.json +14 -0
- package/artifacts/schema/org-event.schema.json +75 -0
- package/artifacts/schema/org-firing-alerts-response.schema.json +178 -0
- package/artifacts/schema/org-health-query.schema.json +13 -0
- package/artifacts/schema/org-latency-query.schema.json +42 -0
- package/artifacts/schema/org-latency-response.schema.json +124 -0
- package/artifacts/schema/org-quota-usage-counts.schema.json +42 -0
- package/artifacts/schema/org-quota-usage.schema.json +102 -0
- package/artifacts/schema/org-quotas.schema.json +51 -0
- package/artifacts/schema/org-usage-query.schema.json +19 -0
- package/artifacts/schema/org-usage-response.schema.json +77 -0
- package/artifacts/schema/org.schema.json +30 -0
- package/artifacts/schema/parameter-invalid-details.schema.json +37 -0
- package/artifacts/schema/parameter-spec.schema.json +120 -0
- package/artifacts/schema/parameter-violation.schema.json +24 -0
- package/artifacts/schema/password-change-request.schema.json +21 -0
- package/artifacts/schema/password-reset-confirm.schema.json +19 -0
- package/artifacts/schema/password-reset-request.schema.json +14 -0
- package/artifacts/schema/patch-app-oidc-provider-request.schema.json +50 -0
- package/artifacts/schema/patch-app-user-request.schema.json +34 -0
- package/artifacts/schema/patch-auth-me-request.schema.json +22 -0
- package/artifacts/schema/patch-fleetless-user-request.schema.json +20 -0
- package/artifacts/schema/patch-org-request.schema.json +15 -0
- package/artifacts/schema/patch-org-response.schema.json +40 -0
- package/artifacts/schema/patch-robot-request.schema.json +15 -0
- package/artifacts/schema/patch-robot-response.schema.json +40 -0
- package/artifacts/schema/pending-team-invite-list-response.schema.json +52 -0
- package/artifacts/schema/pending-team-invite.schema.json +39 -0
- package/artifacts/schema/protected-resource-metadata.schema.json +41 -0
- package/artifacts/schema/publish-config-response.schema.json +21 -0
- package/artifacts/schema/publish-request.schema.json +17 -0
- package/artifacts/schema/publisher-config.schema.json +285 -0
- package/artifacts/schema/put-app-auth-config-request.schema.json +93 -0
- package/artifacts/schema/put-app-mail-template-request.schema.json +35 -0
- package/artifacts/schema/put-config-draft-request.schema.json +13 -0
- package/artifacts/schema/put-datapoint-display-request.schema.json +31 -0
- package/artifacts/schema/put-robot-details-request.schema.json +41 -0
- package/artifacts/schema/put-robot-details-response.schema.json +43 -0
- package/artifacts/schema/rate-limit-details.schema.json +15 -0
- package/artifacts/schema/refresh-request.schema.json +13 -0
- package/artifacts/schema/release-live-query.schema.json +13 -0
- package/artifacts/schema/rename-slug-request.schema.json +23 -0
- package/artifacts/schema/rename-slug-response.schema.json +24 -0
- package/artifacts/schema/resource-health-event.schema.json +72 -0
- package/artifacts/schema/resource-health-list-response.schema.json +80 -0
- package/artifacts/schema/resource-health-state.schema.json +68 -0
- package/artifacts/schema/robot-config-doc.schema.json +1616 -0
- package/artifacts/schema/robot-delete-query.schema.json +12 -0
- package/artifacts/schema/robot-deletion-summary.schema.json +63 -0
- package/artifacts/schema/robot-detail-response.schema.json +262 -0
- package/artifacts/schema/robot-details-doc.schema.json +33 -0
- package/artifacts/schema/robot-jobs-response.schema.json +119 -0
- package/artifacts/schema/robot-latency-series.schema.json +81 -0
- package/artifacts/schema/robot-list-item.schema.json +94 -0
- package/artifacts/schema/robot-list-response.schema.json +106 -0
- package/artifacts/schema/robot.schema.json +30 -0
- package/artifacts/schema/role-list-response.schema.json +48 -0
- package/artifacts/schema/role-permissions.schema.json +61 -0
- package/artifacts/schema/role.schema.json +35 -0
- package/artifacts/schema/ros-graph.schema.json +99 -0
- package/artifacts/schema/server-key-list-response.schema.json +64 -0
- package/artifacts/schema/server-key.schema.json +51 -0
- package/artifacts/schema/service-call-response.schema.json +13 -0
- package/artifacts/schema/service-config.schema.json +198 -0
- package/artifacts/schema/session-tokens.schema.json +28 -0
- package/artifacts/schema/sign-up-request.schema.json +26 -0
- package/artifacts/schema/sign-up-response.schema.json +127 -0
- package/artifacts/schema/slug-usage-response.schema.json +32 -0
- package/artifacts/schema/snapshot-header.schema.json +44 -0
- package/artifacts/schema/snapshot-meta-response.schema.json +85 -0
- package/artifacts/schema/subscribe-error.schema.json +31 -0
- package/artifacts/schema/team-invite.schema.json +57 -0
- package/artifacts/schema/tier-change-request.schema.json +17 -0
- package/artifacts/schema/type-definition.schema.json +144 -0
- package/artifacts/schema/types-response.schema.json +156 -0
- package/artifacts/schema/update-app-request.schema.json +32 -0
- package/artifacts/schema/urdf-completeness.schema.json +50 -0
- package/artifacts/schema/validation-issue.schema.json +43 -0
- package/artifacts/schema/waitlist-request.schema.json +15 -0
- package/artifacts/schema-outgoing/bridge-asset-progress.schema.json +102 -0
- package/artifacts/schema-outgoing/bridge-assets-available.schema.json +26 -0
- package/artifacts/schema-outgoing/bridge-camera-state.schema.json +80 -0
- package/artifacts/schema-outgoing/bridge-config-applied.schema.json +69 -0
- package/artifacts/schema-outgoing/bridge-hello.schema.json +68 -0
- package/artifacts/schema-outgoing/bridge-introspect.schema.json +119 -0
- package/artifacts/schema-outgoing/bridge-job-lost.schema.json +23 -0
- package/artifacts/schema-outgoing/bridge-job-update.schema.json +102 -0
- package/artifacts/schema-outgoing/bridge-pong.schema.json +20 -0
- package/artifacts/schema-outgoing/bridge-type-definitions.schema.json +174 -0
- package/artifacts/schema-outgoing/datapoint-frame.schema.json +29 -0
- package/artifacts/schema-outgoing/snapshot-header.schema.json +45 -0
- package/dist/alerts.d.ts +255 -0
- package/dist/alerts.js +193 -0
- package/dist/app-users.d.ts +606 -0
- package/dist/app-users.js +696 -0
- package/dist/apps.d.ts +175 -0
- package/dist/apps.js +267 -0
- package/dist/assets.d.ts +434 -0
- package/dist/assets.js +546 -0
- package/dist/audit.d.ts +129 -0
- package/dist/audit.js +238 -0
- package/dist/client-auth.d.ts +409 -0
- package/dist/client-auth.js +487 -0
- package/dist/common.d.ts +186 -0
- package/dist/common.js +199 -0
- package/dist/config-issues.d.ts +175 -0
- package/dist/config-issues.js +339 -0
- package/dist/config.d.ts +862 -0
- package/dist/config.js +1988 -0
- package/dist/errors.d.ts +52 -0
- package/dist/errors.js +786 -0
- package/dist/identity.d.ts +549 -0
- package/dist/identity.js +503 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +51 -0
- package/dist/introspection.d.ts +99 -0
- package/dist/introspection.js +97 -0
- package/dist/jobs.d.ts +334 -0
- package/dist/jobs.js +345 -0
- package/dist/mcp.d.ts +239 -0
- package/dist/mcp.js +153 -0
- package/dist/oauth.d.ts +344 -0
- package/dist/oauth.js +488 -0
- package/dist/protocol.d.ts +781 -0
- package/dist/protocol.js +715 -0
- package/dist/realtime.d.ts +494 -0
- package/dist/realtime.js +512 -0
- package/dist/rest.d.ts +1989 -0
- package/dist/rest.js +1963 -0
- package/dist/routes.d.ts +94 -0
- package/dist/routes.js +2298 -0
- package/package.json +61 -0
package/dist/common.d.ts
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Names shared by every layer: the Fleetless slug and the ROS names it is
|
|
4
|
+
* deliberately decoupled from (spec §4.1).
|
|
5
|
+
*
|
|
6
|
+
* They live here rather than in `protocol.ts` so the exposure model
|
|
7
|
+
* (`config.ts`) and the bridge protocol can both use them without importing
|
|
8
|
+
* each other.
|
|
9
|
+
*
|
|
10
|
+
* ## Each grammar's sentence lives beside its pattern
|
|
11
|
+
*
|
|
12
|
+
* A developer whose topic name was wrong used to be shown the regular
|
|
13
|
+
* expression that refused it. The four `*_RULE` constants below are the
|
|
14
|
+
* sentences that replace it — one per grammar rather than one per field,
|
|
15
|
+
* because the message explains why the *pattern* said no, and the same pattern
|
|
16
|
+
* says no for the same reason wherever it appears.
|
|
17
|
+
*
|
|
18
|
+
* Each is used **twice**: as the message zod itself produces, here, and as
|
|
19
|
+
* `patternErrorMessage` in `config.ts`'s exported JSON Schema, which is a
|
|
20
|
+
* published artifact that other tools validate against and that a person
|
|
21
|
+
* reads. Under FL-005 D3 nothing will consume `patternErrorMessage` at runtime
|
|
22
|
+
* **once wave 3 lands** — `useMonacoYaml.ts` still passes `validate: true`
|
|
23
|
+
* today, so until then this sentence IS the live diagnostic in the editor and
|
|
24
|
+
* zod's is the live one on the server. Either way it is a second spelling of a
|
|
25
|
+
* live rule, and an unwatched one would drift word for word,
|
|
26
|
+
* forever and invisibly — the shape that had `buildAcceptUrl` mailing one URL
|
|
27
|
+
* three ways. They are therefore one constant with two readers rather than two
|
|
28
|
+
* strings that happen to agree, and `config-zod-messages.test.ts` asserts the
|
|
29
|
+
* two readings are the same string at all 24 pattern positions the document
|
|
30
|
+
* has.
|
|
31
|
+
*
|
|
32
|
+
* **They are exported because the pattern and its sentence must not be able to
|
|
33
|
+
* move apart**, and the pattern is here while the schema annotation is in
|
|
34
|
+
* `config.ts`. The three grammars that exist only inside a configuration
|
|
35
|
+
* document — the two URL schemes and the capture-device path — are constants in
|
|
36
|
+
* `config.ts` beside their own patterns, on the same rule.
|
|
37
|
+
*
|
|
38
|
+
* **The blast radius of putting the sentence here was measured, and it is
|
|
39
|
+
* zero artifacts.** A `.meta()` on `slug` would reach 42 of the 159 published
|
|
40
|
+
* schema artifacts, the bridge's vendored protocol frames among them — which is
|
|
41
|
+
* why `mapKey` in `config.ts` carries the annotation and `slug` does not. A
|
|
42
|
+
* message on a `.regex()` check is a different thing: zod renders no error
|
|
43
|
+
* message into JSON Schema at all, so every artifact is byte-identical either
|
|
44
|
+
* way (measured across all 278 barrel schemas under both `io` modes,
|
|
45
|
+
* 2026-09-03). What it does reach is the sentence a *parser* produces, in every
|
|
46
|
+
* layer that parses one of these names — which is the improvement, not a cost.
|
|
47
|
+
*/
|
|
48
|
+
export declare const SLUG_RULE = "A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore \u2014 `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused.";
|
|
49
|
+
/**
|
|
50
|
+
* A name: a slug for an exposed service or datapoint, a parameter name, a
|
|
51
|
+
* message name. Lowercase, underscore-separated, letter-initial, 2..63
|
|
52
|
+
* characters, no leading/trailing/doubled underscores.
|
|
53
|
+
*
|
|
54
|
+
* Names are stable and decoupled from ROS names (spec §4.1) — renaming a
|
|
55
|
+
* topic on the robot must never break a client app. The reverse also holds
|
|
56
|
+
* and costs more: changing a name breaks every client, role grant and MCP
|
|
57
|
+
* tool name that uses it.
|
|
58
|
+
*/
|
|
59
|
+
export declare const slug: z.ZodString;
|
|
60
|
+
export declare const ROS_NAME_RULE = "A ROS graph name is absolute: it begins with a slash, and each segment after a slash starts with a letter or an underscore and continues with letters, digits and underscores \u2014 `/camera/image_raw`. A relative name, a trailing slash, a dash or a dot is refused.";
|
|
61
|
+
/**
|
|
62
|
+
* A fully qualified ROS graph name: absolute, slash-separated, each segment
|
|
63
|
+
* letter- or underscore-initial. Relative names are refused — the bridge
|
|
64
|
+
* would have to resolve them against a namespace the cloud cannot see.
|
|
65
|
+
*/
|
|
66
|
+
export declare const rosName: z.ZodString;
|
|
67
|
+
export declare const ROS_TYPE_NAME_RULE = "A ROS 2 type name has three segments: the package, then `msg`, `srv` or `action`, then the type \u2014 `sensor_msgs/msg/BatteryState`, `std_srvs/srv/Trigger`, `nav2_msgs/action/NavigateToPose`. The middle segment is the one usually left out. The package is lower-case with underscores; the type itself is letters and digits, conventionally CamelCase.";
|
|
68
|
+
/**
|
|
69
|
+
* A ROS interface type as ROS 2 spells it: `pkg/msg/Type`, `pkg/srv/Type`,
|
|
70
|
+
* `pkg/action/Type`. W2 resolves field trees for `msg` only (§4.5); the
|
|
71
|
+
* other two are listed by the introspection browser and get their trees in
|
|
72
|
+
* W4, where action and service parameters exist.
|
|
73
|
+
*/
|
|
74
|
+
export declare const rosTypeName: z.ZodString;
|
|
75
|
+
export declare const FIELD_PATH_RULE = "A field path is dotted and lower-case, and each segment may index at most one array level \u2014 `voltage`, `pose.position.x`, `ranges[0]`. ROS 2 has no nested arrays, so a second index on one segment could name nothing that exists.";
|
|
76
|
+
/**
|
|
77
|
+
* A path into a message: dot-separated field names, each carrying **at most
|
|
78
|
+
* one** array index, e.g. `percentage`, `pose.position.x`, `ranges[0]`,
|
|
79
|
+
* `poses[0].pose.position.x`. `null` in a datapoint config means *the whole
|
|
80
|
+
* message* (spec §4.2: one field or one whole topic — never several topics).
|
|
81
|
+
*
|
|
82
|
+
* One index per segment is not a preference but the shape of the target: ROS 2
|
|
83
|
+
* IDL has `float64[]`, `float64[3]` and `float64[<=10]`, and no nested or
|
|
84
|
+
* multi-dimensional arrays at all. A second index on one segment — `a[0][1]` —
|
|
85
|
+
* could therefore denote nothing on any message that exists. The bridge has
|
|
86
|
+
* always refused it (`sampling.py`'s `FieldPathError`, *"ROS has no nested
|
|
87
|
+
* arrays"*); this grammar said otherwise until FL-004, so a hand-written or
|
|
88
|
+
* AI-generated document could pass the cloud and then fail at the robot as a
|
|
89
|
+
* `config_applied` error — the latest and worst place to learn it.
|
|
90
|
+
*/
|
|
91
|
+
export declare const fieldPath: z.ZodString;
|
|
92
|
+
/**
|
|
93
|
+
* A unix-millisecond instant as a **query string** actually carries it, bounded
|
|
94
|
+
* to years 1..9999.
|
|
95
|
+
*
|
|
96
|
+
* The union's input branch **is the wire** — a `z.coerce` cannot be published,
|
|
97
|
+
* because zod renders the coercion's result in either `io` direction, so the
|
|
98
|
+
* artifact would describe a shape a query string can never carry (DEF-059).
|
|
99
|
+
*
|
|
100
|
+
* The year bound is borrowed rather than invented: `nonnegative()` alone let
|
|
101
|
+
* `253402300800000` through, where the Postgres bind path has no representation
|
|
102
|
+
* and the route answered 500 — measured either side of the edge,
|
|
103
|
+
* `253402300799000` -> 200 and `253402300800000` -> 500 (Argus-W9). This moved
|
|
104
|
+
* here from `audit.ts` when `jobRunQuery` needed the same guard; a second copy
|
|
105
|
+
* would have been a second policy for one decision.
|
|
106
|
+
*/
|
|
107
|
+
/**
|
|
108
|
+
* A `seq` cursor as a **query string** actually carries it.
|
|
109
|
+
*
|
|
110
|
+
* The regex admits 19 digits, which is wider than a JavaScript number can
|
|
111
|
+
* represent — `Number('9999999999999999999')` is `1e19`. That is safe, and
|
|
112
|
+
* for a reason worth writing down rather than re-deriving: **zod 4's `.int()`
|
|
113
|
+
* bounds the safe-integer range**, so such a value is refused here with a
|
|
114
|
+
* `too_big` issue and the route answers 400. It never reaches Postgres as an
|
|
115
|
+
* out-of-range `bigint`, and no extra `.max()` is needed — one that merely
|
|
116
|
+
* restated `.int()` would be a second policy for one decision.
|
|
117
|
+
*
|
|
118
|
+
* Lives here because `auditQuery` and `jobRunQuery` had this **twice**, which
|
|
119
|
+
* is how the newer copy ends up the weaker one.
|
|
120
|
+
*
|
|
121
|
+
* **What the published artifact does not say:** zod renders a
|
|
122
|
+
* `.transform().pipe()` from its *input* branch, so the JSON Schema shows the
|
|
123
|
+
* union and none of the constraints below it — a generated client reading it
|
|
124
|
+
* would believe `-5` is acceptable. The runtime is the authority for this
|
|
125
|
+
* field; the artifact describes only what the wire may carry.
|
|
126
|
+
*/
|
|
127
|
+
export declare const wireSeqCursor: z.ZodPipe<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodTransform<number, string | number>>, z.ZodNumber>;
|
|
128
|
+
export declare const wireTimestampMs: z.ZodPipe<z.ZodPipe<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>, z.ZodTransform<number, string | number>>, z.ZodNumber>;
|
|
129
|
+
/**
|
|
130
|
+
* Which kind of exposure failed to apply. Known at every one of the bridge's five apply call sites.
|
|
131
|
+
*
|
|
132
|
+
* Lives here, not in `protocol.ts`, for the same reason `slug` and friends
|
|
133
|
+
* do: `config.ts`'s `configState.applied_errors` is the REST shape the
|
|
134
|
+
* console reads this same error through (spec `2026-08-21-exposure-and-revoke-design`
|
|
135
|
+
* D4), and `protocol.ts` already imports from `config.ts`
|
|
136
|
+
* (`credentialRef`, `robotConfigDoc`) — so `config.ts` importing back from
|
|
137
|
+
* `protocol.ts` would be a cycle. One definition, reachable from both
|
|
138
|
+
* without either importing the other.
|
|
139
|
+
*/
|
|
140
|
+
export declare const applyErrorKind: z.ZodEnum<{
|
|
141
|
+
datapoint: "datapoint";
|
|
142
|
+
action: "action";
|
|
143
|
+
service: "service";
|
|
144
|
+
publisher: "publisher";
|
|
145
|
+
camera: "camera";
|
|
146
|
+
}>;
|
|
147
|
+
export type ApplyErrorKind = z.infer<typeof applyErrorKind>;
|
|
148
|
+
/**
|
|
149
|
+
* One thing that did not apply — carried on the wire by
|
|
150
|
+
* `protocol.ts`'s `bridgeConfigApplied.errors` and read back by the console
|
|
151
|
+
* through `config.ts`'s `configState.applied_errors`. **One definition**:
|
|
152
|
+
* `configState` used to declare its own narrower `{ slug, message }` copy,
|
|
153
|
+
* which silently stripped `kind`, `code` and `details` on every read —
|
|
154
|
+
* exactly the shape of bug this file's own module comment warns about,
|
|
155
|
+
* found only once the plan's console task tried to render the fields that
|
|
156
|
+
* were never there.
|
|
157
|
+
*
|
|
158
|
+
* `slug` is the exposure's slug, or `*` when a whole kind failed before any
|
|
159
|
+
* individual slug was reached (`client.py`'s `_apply_or_report` catch) — which
|
|
160
|
+
* means something different from every other error: not "this slug is wrong"
|
|
161
|
+
* but "this kind was not applied at all and its slugs are in an unknown state".
|
|
162
|
+
*
|
|
163
|
+
* **`code` is a bounded string and not a `z.enum`, deliberately**, following
|
|
164
|
+
* `cloudHelloError.code`. An enum would make every future bridge
|
|
165
|
+
* classification a protocol change on both sides; a string lets the bridge
|
|
166
|
+
* learn to classify without the cloud being taught first, and the cloud renders
|
|
167
|
+
* what it knows and passes the rest through. The codes the bridge produces
|
|
168
|
+
* today are `field_path_invalid`, `whole_kind_failed` and `unknown`.
|
|
169
|
+
*
|
|
170
|
+
* `details` carries whatever a classifier has to add. **Nothing redacts it** —
|
|
171
|
+
* the same rule `auditEvent.details` states.
|
|
172
|
+
*/
|
|
173
|
+
export declare const applyError: z.ZodObject<{
|
|
174
|
+
slug: z.ZodString;
|
|
175
|
+
kind: z.ZodEnum<{
|
|
176
|
+
datapoint: "datapoint";
|
|
177
|
+
action: "action";
|
|
178
|
+
service: "service";
|
|
179
|
+
publisher: "publisher";
|
|
180
|
+
camera: "camera";
|
|
181
|
+
}>;
|
|
182
|
+
code: z.ZodString;
|
|
183
|
+
message: z.ZodString;
|
|
184
|
+
details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
185
|
+
}, z.core.$strip>;
|
|
186
|
+
export type ApplyError = z.infer<typeof applyError>;
|
package/dist/common.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
/**
|
|
4
|
+
* Names shared by every layer: the Fleetless slug and the ROS names it is
|
|
5
|
+
* deliberately decoupled from (spec §4.1).
|
|
6
|
+
*
|
|
7
|
+
* They live here rather than in `protocol.ts` so the exposure model
|
|
8
|
+
* (`config.ts`) and the bridge protocol can both use them without importing
|
|
9
|
+
* each other.
|
|
10
|
+
*
|
|
11
|
+
* ## Each grammar's sentence lives beside its pattern
|
|
12
|
+
*
|
|
13
|
+
* A developer whose topic name was wrong used to be shown the regular
|
|
14
|
+
* expression that refused it. The four `*_RULE` constants below are the
|
|
15
|
+
* sentences that replace it — one per grammar rather than one per field,
|
|
16
|
+
* because the message explains why the *pattern* said no, and the same pattern
|
|
17
|
+
* says no for the same reason wherever it appears.
|
|
18
|
+
*
|
|
19
|
+
* Each is used **twice**: as the message zod itself produces, here, and as
|
|
20
|
+
* `patternErrorMessage` in `config.ts`'s exported JSON Schema, which is a
|
|
21
|
+
* published artifact that other tools validate against and that a person
|
|
22
|
+
* reads. Under FL-005 D3 nothing will consume `patternErrorMessage` at runtime
|
|
23
|
+
* **once wave 3 lands** — `useMonacoYaml.ts` still passes `validate: true`
|
|
24
|
+
* today, so until then this sentence IS the live diagnostic in the editor and
|
|
25
|
+
* zod's is the live one on the server. Either way it is a second spelling of a
|
|
26
|
+
* live rule, and an unwatched one would drift word for word,
|
|
27
|
+
* forever and invisibly — the shape that had `buildAcceptUrl` mailing one URL
|
|
28
|
+
* three ways. They are therefore one constant with two readers rather than two
|
|
29
|
+
* strings that happen to agree, and `config-zod-messages.test.ts` asserts the
|
|
30
|
+
* two readings are the same string at all 24 pattern positions the document
|
|
31
|
+
* has.
|
|
32
|
+
*
|
|
33
|
+
* **They are exported because the pattern and its sentence must not be able to
|
|
34
|
+
* move apart**, and the pattern is here while the schema annotation is in
|
|
35
|
+
* `config.ts`. The three grammars that exist only inside a configuration
|
|
36
|
+
* document — the two URL schemes and the capture-device path — are constants in
|
|
37
|
+
* `config.ts` beside their own patterns, on the same rule.
|
|
38
|
+
*
|
|
39
|
+
* **The blast radius of putting the sentence here was measured, and it is
|
|
40
|
+
* zero artifacts.** A `.meta()` on `slug` would reach 42 of the 159 published
|
|
41
|
+
* schema artifacts, the bridge's vendored protocol frames among them — which is
|
|
42
|
+
* why `mapKey` in `config.ts` carries the annotation and `slug` does not. A
|
|
43
|
+
* message on a `.regex()` check is a different thing: zod renders no error
|
|
44
|
+
* message into JSON Schema at all, so every artifact is byte-identical either
|
|
45
|
+
* way (measured across all 278 barrel schemas under both `io` modes,
|
|
46
|
+
* 2026-09-03). What it does reach is the sentence a *parser* produces, in every
|
|
47
|
+
* layer that parses one of these names — which is the improvement, not a cost.
|
|
48
|
+
*/
|
|
49
|
+
export const SLUG_RULE = 'A name is lower-case: it starts with a letter, continues with letters and digits, and joins further words with a single underscore — `battery_voltage`. Capitals, dashes, dots, spaces, a leading digit and a doubled or trailing underscore are all refused.';
|
|
50
|
+
/**
|
|
51
|
+
* A name: a slug for an exposed service or datapoint, a parameter name, a
|
|
52
|
+
* message name. Lowercase, underscore-separated, letter-initial, 2..63
|
|
53
|
+
* characters, no leading/trailing/doubled underscores.
|
|
54
|
+
*
|
|
55
|
+
* Names are stable and decoupled from ROS names (spec §4.1) — renaming a
|
|
56
|
+
* topic on the robot must never break a client app. The reverse also holds
|
|
57
|
+
* and costs more: changing a name breaks every client, role grant and MCP
|
|
58
|
+
* tool name that uses it.
|
|
59
|
+
*/
|
|
60
|
+
export const slug = z
|
|
61
|
+
.string()
|
|
62
|
+
.min(2)
|
|
63
|
+
.max(63)
|
|
64
|
+
.regex(/^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/, SLUG_RULE);
|
|
65
|
+
export const ROS_NAME_RULE = 'A ROS graph name is absolute: it begins with a slash, and each segment after a slash starts with a letter or an underscore and continues with letters, digits and underscores — `/camera/image_raw`. A relative name, a trailing slash, a dash or a dot is refused.';
|
|
66
|
+
/**
|
|
67
|
+
* A fully qualified ROS graph name: absolute, slash-separated, each segment
|
|
68
|
+
* letter- or underscore-initial. Relative names are refused — the bridge
|
|
69
|
+
* would have to resolve them against a namespace the cloud cannot see.
|
|
70
|
+
*/
|
|
71
|
+
export const rosName = z
|
|
72
|
+
.string()
|
|
73
|
+
.max(255)
|
|
74
|
+
.regex(/^\/[A-Za-z_][A-Za-z0-9_]*(?:\/[A-Za-z_][A-Za-z0-9_]*)*$/, ROS_NAME_RULE);
|
|
75
|
+
export const ROS_TYPE_NAME_RULE = 'A ROS 2 type name has three segments: the package, then `msg`, `srv` or `action`, then the type — `sensor_msgs/msg/BatteryState`, `std_srvs/srv/Trigger`, `nav2_msgs/action/NavigateToPose`. The middle segment is the one usually left out. The package is lower-case with underscores; the type itself is letters and digits, conventionally CamelCase.';
|
|
76
|
+
/**
|
|
77
|
+
* A ROS interface type as ROS 2 spells it: `pkg/msg/Type`, `pkg/srv/Type`,
|
|
78
|
+
* `pkg/action/Type`. W2 resolves field trees for `msg` only (§4.5); the
|
|
79
|
+
* other two are listed by the introspection browser and get their trees in
|
|
80
|
+
* W4, where action and service parameters exist.
|
|
81
|
+
*/
|
|
82
|
+
export const rosTypeName = z
|
|
83
|
+
.string()
|
|
84
|
+
.max(255)
|
|
85
|
+
.regex(/^[a-z][a-z0-9_]*\/(?:msg|srv|action)\/[A-Za-z][A-Za-z0-9]*$/, ROS_TYPE_NAME_RULE);
|
|
86
|
+
export const FIELD_PATH_RULE = 'A field path is dotted and lower-case, and each segment may index at most one array level — `voltage`, `pose.position.x`, `ranges[0]`. ROS 2 has no nested arrays, so a second index on one segment could name nothing that exists.';
|
|
87
|
+
/**
|
|
88
|
+
* A path into a message: dot-separated field names, each carrying **at most
|
|
89
|
+
* one** array index, e.g. `percentage`, `pose.position.x`, `ranges[0]`,
|
|
90
|
+
* `poses[0].pose.position.x`. `null` in a datapoint config means *the whole
|
|
91
|
+
* message* (spec §4.2: one field or one whole topic — never several topics).
|
|
92
|
+
*
|
|
93
|
+
* One index per segment is not a preference but the shape of the target: ROS 2
|
|
94
|
+
* IDL has `float64[]`, `float64[3]` and `float64[<=10]`, and no nested or
|
|
95
|
+
* multi-dimensional arrays at all. A second index on one segment — `a[0][1]` —
|
|
96
|
+
* could therefore denote nothing on any message that exists. The bridge has
|
|
97
|
+
* always refused it (`sampling.py`'s `FieldPathError`, *"ROS has no nested
|
|
98
|
+
* arrays"*); this grammar said otherwise until FL-004, so a hand-written or
|
|
99
|
+
* AI-generated document could pass the cloud and then fail at the robot as a
|
|
100
|
+
* `config_applied` error — the latest and worst place to learn it.
|
|
101
|
+
*/
|
|
102
|
+
export const fieldPath = z
|
|
103
|
+
.string()
|
|
104
|
+
.max(255)
|
|
105
|
+
.regex(/^[a-z_][a-z0-9_]*(?:\[\d+\])?(?:\.[a-z_][a-z0-9_]*(?:\[\d+\])?)*$/, FIELD_PATH_RULE);
|
|
106
|
+
/**
|
|
107
|
+
* A unix-millisecond instant as a **query string** actually carries it, bounded
|
|
108
|
+
* to years 1..9999.
|
|
109
|
+
*
|
|
110
|
+
* The union's input branch **is the wire** — a `z.coerce` cannot be published,
|
|
111
|
+
* because zod renders the coercion's result in either `io` direction, so the
|
|
112
|
+
* artifact would describe a shape a query string can never carry (DEF-059).
|
|
113
|
+
*
|
|
114
|
+
* The year bound is borrowed rather than invented: `nonnegative()` alone let
|
|
115
|
+
* `253402300800000` through, where the Postgres bind path has no representation
|
|
116
|
+
* and the route answered 500 — measured either side of the edge,
|
|
117
|
+
* `253402300799000` -> 200 and `253402300800000` -> 500 (Argus-W9). This moved
|
|
118
|
+
* here from `audit.ts` when `jobRunQuery` needed the same guard; a second copy
|
|
119
|
+
* would have been a second policy for one decision.
|
|
120
|
+
*/
|
|
121
|
+
/**
|
|
122
|
+
* A `seq` cursor as a **query string** actually carries it.
|
|
123
|
+
*
|
|
124
|
+
* The regex admits 19 digits, which is wider than a JavaScript number can
|
|
125
|
+
* represent — `Number('9999999999999999999')` is `1e19`. That is safe, and
|
|
126
|
+
* for a reason worth writing down rather than re-deriving: **zod 4's `.int()`
|
|
127
|
+
* bounds the safe-integer range**, so such a value is refused here with a
|
|
128
|
+
* `too_big` issue and the route answers 400. It never reaches Postgres as an
|
|
129
|
+
* out-of-range `bigint`, and no extra `.max()` is needed — one that merely
|
|
130
|
+
* restated `.int()` would be a second policy for one decision.
|
|
131
|
+
*
|
|
132
|
+
* Lives here because `auditQuery` and `jobRunQuery` had this **twice**, which
|
|
133
|
+
* is how the newer copy ends up the weaker one.
|
|
134
|
+
*
|
|
135
|
+
* **What the published artifact does not say:** zod renders a
|
|
136
|
+
* `.transform().pipe()` from its *input* branch, so the JSON Schema shows the
|
|
137
|
+
* union and none of the constraints below it — a generated client reading it
|
|
138
|
+
* would believe `-5` is acceptable. The runtime is the authority for this
|
|
139
|
+
* field; the artifact describes only what the wire may carry.
|
|
140
|
+
*/
|
|
141
|
+
export const wireSeqCursor = z
|
|
142
|
+
.union([z.string().regex(/^\d{1,19}$/), z.number().int()])
|
|
143
|
+
.transform((v) => Number(v))
|
|
144
|
+
.pipe(z.number().int().positive());
|
|
145
|
+
export const wireTimestampMs = z
|
|
146
|
+
.union([z.string().regex(/^\d{1,15}$/), z.number().int()])
|
|
147
|
+
.transform((v) => Number(v))
|
|
148
|
+
.pipe(z
|
|
149
|
+
.number()
|
|
150
|
+
.int()
|
|
151
|
+
.nonnegative()
|
|
152
|
+
.refine((ms) => {
|
|
153
|
+
const year = new Date(ms).getUTCFullYear();
|
|
154
|
+
return Number.isFinite(year) && year >= 1 && year <= 9999;
|
|
155
|
+
}, 'must fall within years 1..9999'));
|
|
156
|
+
/**
|
|
157
|
+
* Which kind of exposure failed to apply. Known at every one of the bridge's five apply call sites.
|
|
158
|
+
*
|
|
159
|
+
* Lives here, not in `protocol.ts`, for the same reason `slug` and friends
|
|
160
|
+
* do: `config.ts`'s `configState.applied_errors` is the REST shape the
|
|
161
|
+
* console reads this same error through (spec `2026-08-21-exposure-and-revoke-design`
|
|
162
|
+
* D4), and `protocol.ts` already imports from `config.ts`
|
|
163
|
+
* (`credentialRef`, `robotConfigDoc`) — so `config.ts` importing back from
|
|
164
|
+
* `protocol.ts` would be a cycle. One definition, reachable from both
|
|
165
|
+
* without either importing the other.
|
|
166
|
+
*/
|
|
167
|
+
export const applyErrorKind = z.enum(['datapoint', 'action', 'service', 'publisher', 'camera']);
|
|
168
|
+
/**
|
|
169
|
+
* One thing that did not apply — carried on the wire by
|
|
170
|
+
* `protocol.ts`'s `bridgeConfigApplied.errors` and read back by the console
|
|
171
|
+
* through `config.ts`'s `configState.applied_errors`. **One definition**:
|
|
172
|
+
* `configState` used to declare its own narrower `{ slug, message }` copy,
|
|
173
|
+
* which silently stripped `kind`, `code` and `details` on every read —
|
|
174
|
+
* exactly the shape of bug this file's own module comment warns about,
|
|
175
|
+
* found only once the plan's console task tried to render the fields that
|
|
176
|
+
* were never there.
|
|
177
|
+
*
|
|
178
|
+
* `slug` is the exposure's slug, or `*` when a whole kind failed before any
|
|
179
|
+
* individual slug was reached (`client.py`'s `_apply_or_report` catch) — which
|
|
180
|
+
* means something different from every other error: not "this slug is wrong"
|
|
181
|
+
* but "this kind was not applied at all and its slugs are in an unknown state".
|
|
182
|
+
*
|
|
183
|
+
* **`code` is a bounded string and not a `z.enum`, deliberately**, following
|
|
184
|
+
* `cloudHelloError.code`. An enum would make every future bridge
|
|
185
|
+
* classification a protocol change on both sides; a string lets the bridge
|
|
186
|
+
* learn to classify without the cloud being taught first, and the cloud renders
|
|
187
|
+
* what it knows and passes the rest through. The codes the bridge produces
|
|
188
|
+
* today are `field_path_invalid`, `whole_kind_failed` and `unknown`.
|
|
189
|
+
*
|
|
190
|
+
* `details` carries whatever a classifier has to add. **Nothing redacts it** —
|
|
191
|
+
* the same rule `auditEvent.details` states.
|
|
192
|
+
*/
|
|
193
|
+
export const applyError = z.object({
|
|
194
|
+
slug: z.string(),
|
|
195
|
+
kind: applyErrorKind,
|
|
196
|
+
code: z.string().min(1).max(40),
|
|
197
|
+
message: z.string().min(1),
|
|
198
|
+
details: z.record(z.string(), z.unknown()).optional(),
|
|
199
|
+
});
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { ValidationIssue } from './config.js';
|
|
3
|
+
/**
|
|
4
|
+
* What is wrong with a configuration document, in one account.
|
|
5
|
+
*
|
|
6
|
+
* Everything here used to live in `cloud/src/validation.ts`. It is in
|
|
7
|
+
* contracts because the console has to say **exactly** what the server says
|
|
8
|
+
* about a document — same codes, same sentences, same paths — and the only
|
|
9
|
+
* way that is true is if it is the same code. A console that reimplemented
|
|
10
|
+
* this and then disagreed with the server about what is wrong would be worse
|
|
11
|
+
* than a console that said nothing (spec D3).
|
|
12
|
+
*
|
|
13
|
+
* **The second door D3 forbids existed for one wave, and closed in wave 2
|
|
14
|
+
* task 8** (cloud `a307e18`, 2026-09-03). The cloud cannot import a specifier
|
|
15
|
+
* it has not pinned, so its own copy of `schemaIssues`, `refusal`, `slugOf`,
|
|
16
|
+
* `formatPath` and `valueAt` stood from wave 1, when this module landed here,
|
|
17
|
+
* until that re-pin deleted them and imported these. The window is recorded
|
|
18
|
+
* rather than dropped because it cost a live bug while it was open: the
|
|
19
|
+
* cloud's own `formatPath` wrote a blank path segment as the empty string,
|
|
20
|
+
* which `validationIssue.path`'s `min(1)` refuses, so a draft containing
|
|
21
|
+
* `"": 3` rode a 200 whose whole body the console's `safeParse` then dropped.
|
|
22
|
+
* Anything else that lands in contracts ahead of its consumer's pin opens the
|
|
23
|
+
* same window.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* One zod issue.
|
|
27
|
+
*
|
|
28
|
+
* Zod's own issue union, not a structural restatement of it. The cloud's
|
|
29
|
+
* copy described the shape by hand because the cloud has no `zod` dependency
|
|
30
|
+
* of its own — it reaches every schema through this package. Here zod *is* a
|
|
31
|
+
* dependency, and a hand-written shape that drifts from the real one would
|
|
32
|
+
* be a second account of the same thing, on the module whose whole point is
|
|
33
|
+
* that there is one.
|
|
34
|
+
*/
|
|
35
|
+
export type SchemaIssue = z.core.$ZodIssue;
|
|
36
|
+
/** What a path with no segments at all is called, since `path` may not be empty. */
|
|
37
|
+
export declare const DOCUMENT_ROOT_PATH = "(document)";
|
|
38
|
+
/**
|
|
39
|
+
* The five sections whose keys are slugs — one namespace across all of them,
|
|
40
|
+
* which is what lets a role grant say `{robot, slug}` without naming a kind.
|
|
41
|
+
* `messages:` is deliberately not among them: its names are their own
|
|
42
|
+
* namespace.
|
|
43
|
+
*
|
|
44
|
+
* `cloud/src/config-sections.ts` re-exports this constant and drives the
|
|
45
|
+
* cloud's iteration over sections from it; the console reads it directly
|
|
46
|
+
* (`useConfigRepairs.ts`). It was spelled out separately in all three until
|
|
47
|
+
* wave 2 task 8 (cloud `a307e18`, 2026-09-03) — this is the only spelling
|
|
48
|
+
* since.
|
|
49
|
+
*/
|
|
50
|
+
export declare const EXPOSURE_SECTIONS: readonly ["datapoints", "actions", "services", "publishers", "cameras"];
|
|
51
|
+
export type ExposureSection = (typeof EXPOSURE_SECTIONS)[number];
|
|
52
|
+
/**
|
|
53
|
+
* The refusals `robotConfigDoc` already made, reported as validation issues
|
|
54
|
+
* with their FL-002 codes.
|
|
55
|
+
*
|
|
56
|
+
* **This maps; it does not re-decide.** Seven of the thirteen codes are
|
|
57
|
+
* answered by the schema before a document ever becomes a `RobotConfigDoc`,
|
|
58
|
+
* and `config.ts` attaches `params: { code }` at each site for exactly this —
|
|
59
|
+
* its header lists which codes it decides and which it defers. Reading
|
|
60
|
+
* `params.code` is also the only stable join: the prose of a message is not a
|
|
61
|
+
* contract and matching on it is a join nobody notices breaking.
|
|
62
|
+
*
|
|
63
|
+
* Two refusals carry no `params.code` and are recognised by zod's own issue
|
|
64
|
+
* code instead, which the same header says consumers should do:
|
|
65
|
+
*
|
|
66
|
+
* - `unrecognized_keys` is `unknown_key`. One issue per key, so the path
|
|
67
|
+
* names the offending key rather than its parent.
|
|
68
|
+
* - `invalid_type` **where the value at that path is `null`** is
|
|
69
|
+
* `explicit_null`. The condition is checked against the parsed value and
|
|
70
|
+
* not against the message, which says "received null" — see above. Zod 4
|
|
71
|
+
* does not carry the input on the issue, so the value is navigated to. The
|
|
72
|
+
* sentence differs at the document root, where there is no key to remove:
|
|
73
|
+
* see `EMPTY_DOCUMENT_MESSAGE`.
|
|
74
|
+
*
|
|
75
|
+
* Everything else keeps zod's own code. Those are refusals with no FL-002
|
|
76
|
+
* code — a reversed `min_value`/`max_value` pair, a section over its cap, a
|
|
77
|
+
* key that is not a slug — and inventing a fourteenth code for them would put
|
|
78
|
+
* a code on the wire that no table documents.
|
|
79
|
+
*/
|
|
80
|
+
export declare function schemaIssues(value: unknown, issues: readonly SchemaIssue[]): ValidationIssue[];
|
|
81
|
+
/**
|
|
82
|
+
* `['datapoints','a','enum',0]` -> `datapoints.a.enum[0]`, the spelling every
|
|
83
|
+
* other path here uses.
|
|
84
|
+
*
|
|
85
|
+
* **A segment that would render as nothing is written quoted instead.** The
|
|
86
|
+
* last segment of an `unrecognized_keys` or `invalid_key` path is a key the
|
|
87
|
+
* *developer* wrote, and YAML lets that key be empty (`"": 3`), nothing but
|
|
88
|
+
* whitespace, or — see `isBlank` — nothing but characters that occupy no
|
|
89
|
+
* width. Rendered bare, such a key produced a path a reader cannot act
|
|
90
|
+
* on — and at the root it produced the empty string, which
|
|
91
|
+
* `validationIssue.path` (`z.string().min(1)`) refuses. That was the cloud
|
|
92
|
+
* publishing a finding that fails the cloud's own contract for findings, and
|
|
93
|
+
* after D2 stored the draft it cost the whole `configDraftResponse`, not one
|
|
94
|
+
* issue: the console's `safeParse` dropped the response and handed the editor
|
|
95
|
+
* nothing, for two characters typed.
|
|
96
|
+
*
|
|
97
|
+
* The quoted spelling is the segment's JSON string literal, and that is the
|
|
98
|
+
* whole of the reason for choosing it: JSON's string syntax is a subset of
|
|
99
|
+
* YAML's double-quoted scalar syntax, so `""`, `" "` and `"\t"` are each a
|
|
100
|
+
* valid YAML spelling of exactly the key being complained about. The path is
|
|
101
|
+
* therefore text the developer can search their own file for — which is the
|
|
102
|
+
* bar this has to clear. It is also the same move `DOCUMENT_ROOT_PATH` makes
|
|
103
|
+
* for the no-segments case, one level down: give the invisible thing a name.
|
|
104
|
+
*
|
|
105
|
+
* **Only blank segments are quoted.** A segment containing `.` or `[` is
|
|
106
|
+
* still written bare, so it still cannot be read back — see
|
|
107
|
+
* `splitFormatPath`, which documents why escaping those was rejected. That
|
|
108
|
+
* decision is unchanged here on purpose: those paths are wrong for one
|
|
109
|
+
* console lookup, these were wrong on the wire.
|
|
110
|
+
*/
|
|
111
|
+
export declare function formatPath(path: readonly PropertyKey[]): string;
|
|
112
|
+
/**
|
|
113
|
+
* `formatPath` read back — `datapoints.a.enum[0]` -> `['datapoints','a','enum',0]`.
|
|
114
|
+
*
|
|
115
|
+
* It exists because two console call sites split an issue path on `.` alone
|
|
116
|
+
* while the cloud writes sequence indices in brackets, so `ranges[0]` reached
|
|
117
|
+
* a document lookup as one segment that matches no key.
|
|
118
|
+
*
|
|
119
|
+
* **It is not the inverse of `formatPath`, and must not be read as one.**
|
|
120
|
+
* `formatPath` writes `.` and `[n]` as structure and escapes nothing, so a
|
|
121
|
+
* name that contains either is indistinguishable afterwards from the
|
|
122
|
+
* structure it looks like. This is reachable, not theoretical: an
|
|
123
|
+
* `unrecognized_keys` path ends in a key the **developer** chose, and YAML
|
|
124
|
+
* lets that key be `a.b` or `ranges[0]`.
|
|
125
|
+
*
|
|
126
|
+
* Escaping on the way out was the alternative and was rejected: `path` is a
|
|
127
|
+
* wire field (`validationIssue.path`), it is rendered to developers as-is,
|
|
128
|
+
* and every recorded expectation in this repo and the cloud's spells it
|
|
129
|
+
* unescaped. Changing what the server says about every document to make one
|
|
130
|
+
* console lookup total is the larger of the two costs.
|
|
131
|
+
*
|
|
132
|
+
* So the property this has, and the one its test asserts, is the narrow one:
|
|
133
|
+
* **a path round-trips when no string segment contains `.` or `[`, and the
|
|
134
|
+
* path is not the single segment `(document)`.** Outside that, the split is a
|
|
135
|
+
* best guess. What it costs is bounded — the console uses the result to find
|
|
136
|
+
* a line to put a marker on, so a wrong split finds no line and the marker is
|
|
137
|
+
* not placed. It never makes the console assert something false about the
|
|
138
|
+
* document.
|
|
139
|
+
*
|
|
140
|
+
* A **blank** segment is inside that property rather than outside it, and
|
|
141
|
+
* that is new. `formatPath` used to drop an empty first segment entirely
|
|
142
|
+
* (`formatPath(['', 'a'])` was `'a'`, a path naming a different key) and to
|
|
143
|
+
* write a nested one as a trailing `.`; at the root it produced the empty
|
|
144
|
+
* string, which `validationIssue.path`'s `min(1)` refuses outright. It now
|
|
145
|
+
* quotes blank segments, and `unquoteBlank` reads them back, so `['']`,
|
|
146
|
+
* `[' ']` and `['datapoints', 'battery_soc', '']` all round-trip. The single
|
|
147
|
+
* new non-round-trip that buys is a key literally spelled with quote marks
|
|
148
|
+
* around whitespace.
|
|
149
|
+
*/
|
|
150
|
+
export declare function splitFormatPath(path: string): Array<string | number>;
|
|
151
|
+
/**
|
|
152
|
+
* A stable hash of a schema object, for asking *is the thing running the one
|
|
153
|
+
* I think it is?*
|
|
154
|
+
*
|
|
155
|
+
* Wave 5's browser sweep enumerates positions against a schema it holds and
|
|
156
|
+
* has to know that the editor is running the same one; the manifest that
|
|
157
|
+
* makes a schema-side change announce itself uses the same number as its
|
|
158
|
+
* baseline. Both are the same question, so there is one implementation of it:
|
|
159
|
+
* a second one on the sweep side would drift, and the gate would then go red
|
|
160
|
+
* for the drift rather than for the schema.
|
|
161
|
+
*
|
|
162
|
+
* Canonical JSON first — object keys sorted at every depth, so a re-ordered
|
|
163
|
+
* `meta()` block is not a change — then FNV-1a over the result, 64 bits as
|
|
164
|
+
* 16 hex characters. Sorting is done through the `JSON.stringify` replacer,
|
|
165
|
+
* which also means a cyclic input throws the engine's own "converting
|
|
166
|
+
* circular structure" TypeError rather than hanging.
|
|
167
|
+
*
|
|
168
|
+
* **Named residual: this is a change detector, not a digest.** FNV-1a is not
|
|
169
|
+
* a cryptographic hash and a collision can be constructed on purpose. It is
|
|
170
|
+
* asked *did this object change since the baseline was recorded*, by the
|
|
171
|
+
* people who wrote both; nothing here defends against someone choosing the
|
|
172
|
+
* input. `crypto.subtle` would be the answer to the other question and is
|
|
173
|
+
* async, which a `data-` attribute rendered during setup cannot be.
|
|
174
|
+
*/
|
|
175
|
+
export declare function configSchemaHash(schema: unknown): string;
|