@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.
Files changed (287) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +17 -0
  4. package/README.md +88 -0
  5. package/artifacts/constants.json +24 -0
  6. package/artifacts/openapi.json +17219 -0
  7. package/artifacts/routes.json +4605 -0
  8. package/artifacts/schema/accept-team-invite-request.schema.json +22 -0
  9. package/artifacts/schema/action-config.schema.json +198 -0
  10. package/artifacts/schema/alert-list-response.schema.json +172 -0
  11. package/artifacts/schema/api-error.schema.json +20 -0
  12. package/artifacts/schema/app-auth-config.schema.json +106 -0
  13. package/artifacts/schema/app-invitation-list-response.schema.json +57 -0
  14. package/artifacts/schema/app-invitation.schema.json +69 -0
  15. package/artifacts/schema/app-list-response.schema.json +82 -0
  16. package/artifacts/schema/app-mail-template-list-response.schema.json +68 -0
  17. package/artifacts/schema/app-mail-template.schema.json +54 -0
  18. package/artifacts/schema/app-oidc-provider-list-response.schema.json +93 -0
  19. package/artifacts/schema/app-oidc-provider.schema.json +80 -0
  20. package/artifacts/schema/app-user-list-response.schema.json +111 -0
  21. package/artifacts/schema/app-user.schema.json +98 -0
  22. package/artifacts/schema/app.schema.json +69 -0
  23. package/artifacts/schema/apply-error.schema.json +41 -0
  24. package/artifacts/schema/asset-list-response.schema.json +288 -0
  25. package/artifacts/schema/asset-sync-request.schema.json +17 -0
  26. package/artifacts/schema/asset-sync-response.schema.json +16 -0
  27. package/artifacts/schema/asset-sync-status.schema.json +136 -0
  28. package/artifacts/schema/asset.schema.json +68 -0
  29. package/artifacts/schema/audit-actor.schema.json +32 -0
  30. package/artifacts/schema/audit-event.schema.json +119 -0
  31. package/artifacts/schema/audit-list-response.schema.json +144 -0
  32. package/artifacts/schema/audit-query.schema.json +79 -0
  33. package/artifacts/schema/auth-error.schema.json +23 -0
  34. package/artifacts/schema/auth-me-response.schema.json +99 -0
  35. package/artifacts/schema/auth-ok.schema.json +115 -0
  36. package/artifacts/schema/authorization-server-metadata.schema.json +80 -0
  37. package/artifacts/schema/bridge-asset-progress.schema.json +99 -0
  38. package/artifacts/schema/bridge-assets-available.schema.json +25 -0
  39. package/artifacts/schema/bridge-camera-state.schema.json +78 -0
  40. package/artifacts/schema/bridge-config-applied.schema.json +67 -0
  41. package/artifacts/schema/bridge-hello.schema.json +65 -0
  42. package/artifacts/schema/bridge-introspect.schema.json +114 -0
  43. package/artifacts/schema/bridge-job-lost.schema.json +22 -0
  44. package/artifacts/schema/bridge-job-update.schema.json +100 -0
  45. package/artifacts/schema/bridge-pong.schema.json +19 -0
  46. package/artifacts/schema/bridge-pressure.schema.json +292 -0
  47. package/artifacts/schema/bridge-state.schema.json +24 -0
  48. package/artifacts/schema/bridge-type-definitions.schema.json +169 -0
  49. package/artifacts/schema/busy-details.schema.json +115 -0
  50. package/artifacts/schema/camera-descriptor.schema.json +45 -0
  51. package/artifacts/schema/camera-list-response.schema.json +58 -0
  52. package/artifacts/schema/camera-source.schema.json +240 -0
  53. package/artifacts/schema/cancel-request.schema.json +20 -0
  54. package/artifacts/schema/client-accept-invitation-request.schema.json +35 -0
  55. package/artifacts/schema/client-auth.schema.json +18 -0
  56. package/artifacts/schema/client-cancel.schema.json +45 -0
  57. package/artifacts/schema/client-identity.schema.json +103 -0
  58. package/artifacts/schema/client-invoke.schema.json +45 -0
  59. package/artifacts/schema/client-login-request.schema.json +29 -0
  60. package/artifacts/schema/client-logout-request.schema.json +14 -0
  61. package/artifacts/schema/client-mcp-interaction-decision-response.schema.json +15 -0
  62. package/artifacts/schema/client-mcp-interaction.schema.json +59 -0
  63. package/artifacts/schema/client-oidc-callback-query.schema.json +28 -0
  64. package/artifacts/schema/client-oidc-exchange-request.schema.json +21 -0
  65. package/artifacts/schema/client-oidc-start-query.schema.json +36 -0
  66. package/artifacts/schema/client-password-reset-confirm-request.schema.json +22 -0
  67. package/artifacts/schema/client-password-reset-request.schema.json +24 -0
  68. package/artifacts/schema/client-provider-list-query.schema.json +17 -0
  69. package/artifacts/schema/client-provider-list-response.schema.json +34 -0
  70. package/artifacts/schema/client-publish.schema.json +40 -0
  71. package/artifacts/schema/client-refresh-request.schema.json +14 -0
  72. package/artifacts/schema/client-register-request.schema.json +44 -0
  73. package/artifacts/schema/client-resend-verification-request.schema.json +24 -0
  74. package/artifacts/schema/client-subscribe.schema.json +43 -0
  75. package/artifacts/schema/client-unsubscribe.schema.json +26 -0
  76. package/artifacts/schema/client-verify-email-request.schema.json +15 -0
  77. package/artifacts/schema/cloud-asset-request.schema.json +37 -0
  78. package/artifacts/schema/cloud-camera-start.schema.json +41 -0
  79. package/artifacts/schema/cloud-camera-stop.schema.json +26 -0
  80. package/artifacts/schema/cloud-cancel.schema.json +33 -0
  81. package/artifacts/schema/cloud-config.schema.json +1635 -0
  82. package/artifacts/schema/cloud-hello-error.schema.json +23 -0
  83. package/artifacts/schema/cloud-hello-ok.schema.json +19 -0
  84. package/artifacts/schema/cloud-introspect-request.schema.json +19 -0
  85. package/artifacts/schema/cloud-invoke.schema.json +40 -0
  86. package/artifacts/schema/cloud-ping.schema.json +19 -0
  87. package/artifacts/schema/cloud-publish.schema.json +28 -0
  88. package/artifacts/schema/cloud-type-request.schema.json +30 -0
  89. package/artifacts/schema/command-result.schema.json +175 -0
  90. package/artifacts/schema/config-draft-response.schema.json +1695 -0
  91. package/artifacts/schema/config-state.schema.json +124 -0
  92. package/artifacts/schema/config-version-response.schema.json +1641 -0
  93. package/artifacts/schema/config-versions-response.schema.json +33 -0
  94. package/artifacts/schema/create-app-invitation-request.schema.json +40 -0
  95. package/artifacts/schema/create-app-oidc-provider-request.schema.json +70 -0
  96. package/artifacts/schema/create-app-request.schema.json +30 -0
  97. package/artifacts/schema/create-app-user-request.schema.json +42 -0
  98. package/artifacts/schema/create-robot-request.schema.json +14 -0
  99. package/artifacts/schema/create-robot-response.schema.json +44 -0
  100. package/artifacts/schema/create-server-key-response.schema.json +65 -0
  101. package/artifacts/schema/create-team-invite-request.schema.json +43 -0
  102. package/artifacts/schema/datapoint-alert-row.schema.json +160 -0
  103. package/artifacts/schema/datapoint-config.schema.json +366 -0
  104. package/artifacts/schema/datapoint-display.schema.json +31 -0
  105. package/artifacts/schema/datapoint-event.schema.json +34 -0
  106. package/artifacts/schema/datapoint-frame.schema.json +28 -0
  107. package/artifacts/schema/datapoint-list-response.schema.json +61 -0
  108. package/artifacts/schema/datapoint-value.schema.json +28 -0
  109. package/artifacts/schema/developer-login-request.schema.json +19 -0
  110. package/artifacts/schema/dynamic-client-registration-request.schema.json +60 -0
  111. package/artifacts/schema/dynamic-client-registration-response.schema.json +68 -0
  112. package/artifacts/schema/error-frame.schema.json +23 -0
  113. package/artifacts/schema/exposure-counts.schema.json +39 -0
  114. package/artifacts/schema/exposure-list-response.schema.json +43 -0
  115. package/artifacts/schema/fetch-types-request.schema.json +19 -0
  116. package/artifacts/schema/fetch-types-response.schema.json +163 -0
  117. package/artifacts/schema/fleetless-user-list-response.schema.json +73 -0
  118. package/artifacts/schema/fleetless-user.schema.json +60 -0
  119. package/artifacts/schema/history-buckets-response.schema.json +79 -0
  120. package/artifacts/schema/history-query.schema.json +58 -0
  121. package/artifacts/schema/history-response.schema.json +150 -0
  122. package/artifacts/schema/history-samples-response.schema.json +68 -0
  123. package/artifacts/schema/introspection-response.schema.json +118 -0
  124. package/artifacts/schema/invoke-or-service-response.schema.json +141 -0
  125. package/artifacts/schema/invoke-request.schema.json +23 -0
  126. package/artifacts/schema/invoke-response.schema.json +125 -0
  127. package/artifacts/schema/job-actor.schema.json +34 -0
  128. package/artifacts/schema/job-event.schema.json +158 -0
  129. package/artifacts/schema/job-response.schema.json +123 -0
  130. package/artifacts/schema/job-run-list-response.schema.json +222 -0
  131. package/artifacts/schema/job-run-query.schema.json +95 -0
  132. package/artifacts/schema/job-run-summary-query.schema.json +23 -0
  133. package/artifacts/schema/job-run-summary.schema.json +33 -0
  134. package/artifacts/schema/job-run.schema.json +195 -0
  135. package/artifacts/schema/job-state.schema.json +11 -0
  136. package/artifacts/schema/job.schema.json +106 -0
  137. package/artifacts/schema/latency-bucket.schema.json +63 -0
  138. package/artifacts/schema/live-session-response.schema.json +41 -0
  139. package/artifacts/schema/mail-outcome.schema.json +20 -0
  140. package/artifacts/schema/mail-template-preview-request.schema.json +35 -0
  141. package/artifacts/schema/mail-template-preview-response.schema.json +31 -0
  142. package/artifacts/schema/mail-template-problem-details.schema.json +24 -0
  143. package/artifacts/schema/mcp-consent-grant-list-response.schema.json +52 -0
  144. package/artifacts/schema/mcp-consent-grant.schema.json +39 -0
  145. package/artifacts/schema/mcp-robot-datasheet.schema.json +115 -0
  146. package/artifacts/schema/mcp-role-preview-response.schema.json +134 -0
  147. package/artifacts/schema/missing-asset-query.schema.json +11 -0
  148. package/artifacts/schema/oauth-authorize-query.schema.json +47 -0
  149. package/artifacts/schema/oauth-redirect-response.schema.json +15 -0
  150. package/artifacts/schema/oauth-token-request.schema.json +47 -0
  151. package/artifacts/schema/oauth-token-response.schema.json +38 -0
  152. package/artifacts/schema/org-alerts-query.schema.json +15 -0
  153. package/artifacts/schema/org-event-dropped.schema.json +26 -0
  154. package/artifacts/schema/org-event-replay.schema.json +97 -0
  155. package/artifacts/schema/org-event-subscribe.schema.json +14 -0
  156. package/artifacts/schema/org-event-unsubscribe.schema.json +14 -0
  157. package/artifacts/schema/org-event.schema.json +75 -0
  158. package/artifacts/schema/org-firing-alerts-response.schema.json +178 -0
  159. package/artifacts/schema/org-health-query.schema.json +13 -0
  160. package/artifacts/schema/org-latency-query.schema.json +42 -0
  161. package/artifacts/schema/org-latency-response.schema.json +124 -0
  162. package/artifacts/schema/org-quota-usage-counts.schema.json +42 -0
  163. package/artifacts/schema/org-quota-usage.schema.json +102 -0
  164. package/artifacts/schema/org-quotas.schema.json +51 -0
  165. package/artifacts/schema/org-usage-query.schema.json +19 -0
  166. package/artifacts/schema/org-usage-response.schema.json +77 -0
  167. package/artifacts/schema/org.schema.json +30 -0
  168. package/artifacts/schema/parameter-invalid-details.schema.json +37 -0
  169. package/artifacts/schema/parameter-spec.schema.json +120 -0
  170. package/artifacts/schema/parameter-violation.schema.json +24 -0
  171. package/artifacts/schema/password-change-request.schema.json +21 -0
  172. package/artifacts/schema/password-reset-confirm.schema.json +19 -0
  173. package/artifacts/schema/password-reset-request.schema.json +14 -0
  174. package/artifacts/schema/patch-app-oidc-provider-request.schema.json +50 -0
  175. package/artifacts/schema/patch-app-user-request.schema.json +34 -0
  176. package/artifacts/schema/patch-auth-me-request.schema.json +22 -0
  177. package/artifacts/schema/patch-fleetless-user-request.schema.json +20 -0
  178. package/artifacts/schema/patch-org-request.schema.json +15 -0
  179. package/artifacts/schema/patch-org-response.schema.json +40 -0
  180. package/artifacts/schema/patch-robot-request.schema.json +15 -0
  181. package/artifacts/schema/patch-robot-response.schema.json +40 -0
  182. package/artifacts/schema/pending-team-invite-list-response.schema.json +52 -0
  183. package/artifacts/schema/pending-team-invite.schema.json +39 -0
  184. package/artifacts/schema/protected-resource-metadata.schema.json +41 -0
  185. package/artifacts/schema/publish-config-response.schema.json +21 -0
  186. package/artifacts/schema/publish-request.schema.json +17 -0
  187. package/artifacts/schema/publisher-config.schema.json +285 -0
  188. package/artifacts/schema/put-app-auth-config-request.schema.json +93 -0
  189. package/artifacts/schema/put-app-mail-template-request.schema.json +35 -0
  190. package/artifacts/schema/put-config-draft-request.schema.json +13 -0
  191. package/artifacts/schema/put-datapoint-display-request.schema.json +31 -0
  192. package/artifacts/schema/put-robot-details-request.schema.json +41 -0
  193. package/artifacts/schema/put-robot-details-response.schema.json +43 -0
  194. package/artifacts/schema/rate-limit-details.schema.json +15 -0
  195. package/artifacts/schema/refresh-request.schema.json +13 -0
  196. package/artifacts/schema/release-live-query.schema.json +13 -0
  197. package/artifacts/schema/rename-slug-request.schema.json +23 -0
  198. package/artifacts/schema/rename-slug-response.schema.json +24 -0
  199. package/artifacts/schema/resource-health-event.schema.json +72 -0
  200. package/artifacts/schema/resource-health-list-response.schema.json +80 -0
  201. package/artifacts/schema/resource-health-state.schema.json +68 -0
  202. package/artifacts/schema/robot-config-doc.schema.json +1616 -0
  203. package/artifacts/schema/robot-delete-query.schema.json +12 -0
  204. package/artifacts/schema/robot-deletion-summary.schema.json +63 -0
  205. package/artifacts/schema/robot-detail-response.schema.json +262 -0
  206. package/artifacts/schema/robot-details-doc.schema.json +33 -0
  207. package/artifacts/schema/robot-jobs-response.schema.json +119 -0
  208. package/artifacts/schema/robot-latency-series.schema.json +81 -0
  209. package/artifacts/schema/robot-list-item.schema.json +94 -0
  210. package/artifacts/schema/robot-list-response.schema.json +106 -0
  211. package/artifacts/schema/robot.schema.json +30 -0
  212. package/artifacts/schema/role-list-response.schema.json +48 -0
  213. package/artifacts/schema/role-permissions.schema.json +61 -0
  214. package/artifacts/schema/role.schema.json +35 -0
  215. package/artifacts/schema/ros-graph.schema.json +99 -0
  216. package/artifacts/schema/server-key-list-response.schema.json +64 -0
  217. package/artifacts/schema/server-key.schema.json +51 -0
  218. package/artifacts/schema/service-call-response.schema.json +13 -0
  219. package/artifacts/schema/service-config.schema.json +198 -0
  220. package/artifacts/schema/session-tokens.schema.json +28 -0
  221. package/artifacts/schema/sign-up-request.schema.json +26 -0
  222. package/artifacts/schema/sign-up-response.schema.json +127 -0
  223. package/artifacts/schema/slug-usage-response.schema.json +32 -0
  224. package/artifacts/schema/snapshot-header.schema.json +44 -0
  225. package/artifacts/schema/snapshot-meta-response.schema.json +85 -0
  226. package/artifacts/schema/subscribe-error.schema.json +31 -0
  227. package/artifacts/schema/team-invite.schema.json +57 -0
  228. package/artifacts/schema/tier-change-request.schema.json +17 -0
  229. package/artifacts/schema/type-definition.schema.json +144 -0
  230. package/artifacts/schema/types-response.schema.json +156 -0
  231. package/artifacts/schema/update-app-request.schema.json +32 -0
  232. package/artifacts/schema/urdf-completeness.schema.json +50 -0
  233. package/artifacts/schema/validation-issue.schema.json +43 -0
  234. package/artifacts/schema/waitlist-request.schema.json +15 -0
  235. package/artifacts/schema-outgoing/bridge-asset-progress.schema.json +102 -0
  236. package/artifacts/schema-outgoing/bridge-assets-available.schema.json +26 -0
  237. package/artifacts/schema-outgoing/bridge-camera-state.schema.json +80 -0
  238. package/artifacts/schema-outgoing/bridge-config-applied.schema.json +69 -0
  239. package/artifacts/schema-outgoing/bridge-hello.schema.json +68 -0
  240. package/artifacts/schema-outgoing/bridge-introspect.schema.json +119 -0
  241. package/artifacts/schema-outgoing/bridge-job-lost.schema.json +23 -0
  242. package/artifacts/schema-outgoing/bridge-job-update.schema.json +102 -0
  243. package/artifacts/schema-outgoing/bridge-pong.schema.json +20 -0
  244. package/artifacts/schema-outgoing/bridge-type-definitions.schema.json +174 -0
  245. package/artifacts/schema-outgoing/datapoint-frame.schema.json +29 -0
  246. package/artifacts/schema-outgoing/snapshot-header.schema.json +45 -0
  247. package/dist/alerts.d.ts +255 -0
  248. package/dist/alerts.js +193 -0
  249. package/dist/app-users.d.ts +606 -0
  250. package/dist/app-users.js +696 -0
  251. package/dist/apps.d.ts +175 -0
  252. package/dist/apps.js +267 -0
  253. package/dist/assets.d.ts +434 -0
  254. package/dist/assets.js +546 -0
  255. package/dist/audit.d.ts +129 -0
  256. package/dist/audit.js +238 -0
  257. package/dist/client-auth.d.ts +409 -0
  258. package/dist/client-auth.js +487 -0
  259. package/dist/common.d.ts +186 -0
  260. package/dist/common.js +199 -0
  261. package/dist/config-issues.d.ts +175 -0
  262. package/dist/config-issues.js +339 -0
  263. package/dist/config.d.ts +862 -0
  264. package/dist/config.js +1988 -0
  265. package/dist/errors.d.ts +52 -0
  266. package/dist/errors.js +786 -0
  267. package/dist/identity.d.ts +549 -0
  268. package/dist/identity.js +503 -0
  269. package/dist/index.d.ts +51 -0
  270. package/dist/index.js +51 -0
  271. package/dist/introspection.d.ts +99 -0
  272. package/dist/introspection.js +97 -0
  273. package/dist/jobs.d.ts +334 -0
  274. package/dist/jobs.js +345 -0
  275. package/dist/mcp.d.ts +239 -0
  276. package/dist/mcp.js +153 -0
  277. package/dist/oauth.d.ts +344 -0
  278. package/dist/oauth.js +488 -0
  279. package/dist/protocol.d.ts +781 -0
  280. package/dist/protocol.js +715 -0
  281. package/dist/realtime.d.ts +494 -0
  282. package/dist/realtime.js +512 -0
  283. package/dist/rest.d.ts +1989 -0
  284. package/dist/rest.js +1963 -0
  285. package/dist/routes.d.ts +94 -0
  286. package/dist/routes.js +2298 -0
  287. package/package.json +61 -0
@@ -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;