@honua/react 0.1.7-beta.0 → 0.1.10-beta.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 (249) hide show
  1. package/control-plane/admin-client.d.ts +89 -0
  2. package/control-plane/admin-client.d.ts.map +1 -0
  3. package/control-plane/admin-client.js +200 -0
  4. package/control-plane/client.d.ts +1 -1
  5. package/control-plane/client.d.ts.map +1 -1
  6. package/control-plane/client.js +145 -11
  7. package/control-plane/commands/catalog.d.ts +133 -0
  8. package/control-plane/commands/catalog.d.ts.map +1 -0
  9. package/control-plane/commands/catalog.js +365 -0
  10. package/control-plane/commands/errors.d.ts +96 -0
  11. package/control-plane/commands/errors.d.ts.map +1 -0
  12. package/control-plane/commands/errors.js +130 -0
  13. package/control-plane/commands/identity.d.ts +71 -0
  14. package/control-plane/commands/identity.d.ts.map +1 -0
  15. package/control-plane/commands/identity.js +0 -0
  16. package/control-plane/commands/index.d.ts +39 -0
  17. package/control-plane/commands/index.d.ts.map +1 -0
  18. package/control-plane/commands/index.js +34 -0
  19. package/control-plane/commands/runtime.d.ts +146 -0
  20. package/control-plane/commands/runtime.d.ts.map +1 -0
  21. package/control-plane/commands/runtime.js +330 -0
  22. package/control-plane/commands/types.d.ts +228 -0
  23. package/control-plane/commands/types.d.ts.map +1 -0
  24. package/control-plane/commands/types.js +30 -0
  25. package/control-plane/commands/validate.d.ts +27 -0
  26. package/control-plane/commands/validate.d.ts.map +1 -0
  27. package/control-plane/commands/validate.js +132 -0
  28. package/control-plane/generated/admin-api.d.ts +30265 -0
  29. package/control-plane/generated/admin-api.d.ts.map +1 -0
  30. package/control-plane/generated/admin-api.js +5 -0
  31. package/control-plane/generated/admin-operations.d.ts +4053 -0
  32. package/control-plane/generated/admin-operations.d.ts.map +1 -0
  33. package/control-plane/generated/admin-operations.js +820 -0
  34. package/control-plane/index.d.ts +11 -0
  35. package/control-plane/index.d.ts.map +1 -1
  36. package/control-plane/index.js +8 -0
  37. package/control-plane/types.d.ts +14 -0
  38. package/control-plane/types.d.ts.map +1 -1
  39. package/core/client.d.ts.map +1 -1
  40. package/core/client.js +29 -31
  41. package/core/credential-redaction.d.ts +163 -0
  42. package/core/credential-redaction.d.ts.map +1 -0
  43. package/core/credential-redaction.js +389 -0
  44. package/core/error-classifications.d.ts +15 -0
  45. package/core/error-classifications.d.ts.map +1 -1
  46. package/core/error-classifications.js +15 -0
  47. package/core/error-code-registry.d.ts +15 -0
  48. package/core/error-code-registry.d.ts.map +1 -1
  49. package/core/error-code-registry.js +15 -0
  50. package/core/error-envelope.d.ts +31 -0
  51. package/core/error-envelope.d.ts.map +1 -1
  52. package/core/error-envelope.js +11 -0
  53. package/core/errors.d.ts +15 -3
  54. package/core/errors.d.ts.map +1 -1
  55. package/core/errors.js +254 -2
  56. package/core/geometry-input-sr.d.ts +3 -0
  57. package/core/geometry-input-sr.d.ts.map +1 -0
  58. package/core/geometry-input-sr.js +28 -0
  59. package/core/geoservices.d.ts.map +1 -1
  60. package/core/geoservices.js +33 -6
  61. package/core/grpc-adapter.d.ts +2 -2
  62. package/core/grpc-adapter.d.ts.map +1 -1
  63. package/core/grpc-adapter.js +41 -9
  64. package/core/job-run-errors.d.ts +10 -0
  65. package/core/job-run-errors.d.ts.map +1 -0
  66. package/core/job-run-errors.js +14 -0
  67. package/core/job-run-lifecycle.d.ts +34 -0
  68. package/core/job-run-lifecycle.d.ts.map +1 -0
  69. package/core/job-run-lifecycle.js +193 -0
  70. package/core/ogc-processes.d.ts +38 -31
  71. package/core/ogc-processes.d.ts.map +1 -1
  72. package/core/ogc-processes.js +123 -173
  73. package/core/pbf-decoder.d.ts.map +1 -1
  74. package/core/pbf-decoder.js +16 -4
  75. package/core/process-runner.js +15 -100
  76. package/core/protocol-transport.d.ts +6 -1
  77. package/core/protocol-transport.d.ts.map +1 -1
  78. package/core/request-pipeline.d.ts +6 -3
  79. package/core/request-pipeline.d.ts.map +1 -1
  80. package/core/request-pipeline.js +28 -11
  81. package/core/spatial-filter.d.ts +4 -4
  82. package/core/spatial-filter.d.ts.map +1 -1
  83. package/core/spatial-filter.js +7 -7
  84. package/core/surfaces.d.ts +2 -8
  85. package/core/surfaces.d.ts.map +1 -1
  86. package/core/surfaces.js +21 -112
  87. package/core/types.d.ts +21 -9
  88. package/core/types.d.ts.map +1 -1
  89. package/esri-compat/class-breaks-renderer.d.ts +2 -0
  90. package/esri-compat/class-breaks-renderer.d.ts.map +1 -1
  91. package/esri-compat/class-breaks-renderer.js +7 -0
  92. package/esri-compat/controls.d.ts +10 -1
  93. package/esri-compat/controls.d.ts.map +1 -1
  94. package/esri-compat/controls.js +86 -2
  95. package/esri-compat/directions.d.ts +35 -0
  96. package/esri-compat/directions.d.ts.map +1 -1
  97. package/esri-compat/directions.js +153 -2
  98. package/esri-compat/expand.d.ts +1 -0
  99. package/esri-compat/expand.d.ts.map +1 -1
  100. package/esri-compat/expand.js +61 -0
  101. package/esri-compat/feature-layer.d.ts +29 -4
  102. package/esri-compat/feature-layer.d.ts.map +1 -1
  103. package/esri-compat/feature-layer.js +335 -13
  104. package/esri-compat/geometry-engine.d.ts +4 -0
  105. package/esri-compat/geometry-engine.d.ts.map +1 -1
  106. package/esri-compat/geometry-engine.js +8 -1
  107. package/esri-compat/graphic.d.ts +7 -4
  108. package/esri-compat/graphic.d.ts.map +1 -1
  109. package/esri-compat/legend.d.ts +2 -1
  110. package/esri-compat/legend.d.ts.map +1 -1
  111. package/esri-compat/legend.js +15 -0
  112. package/esri-compat/map-view-mount.d.ts +48 -0
  113. package/esri-compat/map-view-mount.d.ts.map +1 -0
  114. package/esri-compat/map-view-mount.js +344 -0
  115. package/esri-compat/map-view.d.ts +38 -6
  116. package/esri-compat/map-view.d.ts.map +1 -1
  117. package/esri-compat/map-view.js +467 -33
  118. package/esri-compat/portal.d.ts +2 -0
  119. package/esri-compat/portal.d.ts.map +1 -1
  120. package/esri-compat/portal.js +7 -2
  121. package/esri-compat/renderer-objects.d.ts +7 -2
  122. package/esri-compat/renderer-objects.d.ts.map +1 -1
  123. package/esri-compat/renderer-objects.js +16 -5
  124. package/esri-compat/route-layer.d.ts +8 -0
  125. package/esri-compat/route-layer.d.ts.map +1 -1
  126. package/esri-compat/route-layer.js +24 -4
  127. package/esri-compat/route-task.d.ts +5 -1
  128. package/esri-compat/route-task.d.ts.map +1 -1
  129. package/esri-compat/route-task.js +119 -7
  130. package/esri-compat/scene-view.d.ts.map +1 -1
  131. package/esri-compat/scene-view.js +1 -0
  132. package/esri-compat/search.d.ts +2 -1
  133. package/esri-compat/search.d.ts.map +1 -1
  134. package/esri-compat/search.js +52 -2
  135. package/esri-compat/unique-value-renderer.d.ts +2 -0
  136. package/esri-compat/unique-value-renderer.d.ts.map +1 -1
  137. package/esri-compat/unique-value-renderer.js +7 -0
  138. package/esri-compat/web-map.d.ts +10 -0
  139. package/esri-compat/web-map.d.ts.map +1 -1
  140. package/esri-compat/web-map.js +102 -0
  141. package/gen/geospatial/v1/common_pb.d.ts +514 -0
  142. package/gen/geospatial/v1/common_pb.d.ts.map +1 -0
  143. package/gen/geospatial/v1/common_pb.js +335 -0
  144. package/gen/geospatial/v1/execution_types_pb.d.ts +1186 -0
  145. package/gen/geospatial/v1/execution_types_pb.d.ts.map +1 -0
  146. package/gen/geospatial/v1/execution_types_pb.js +396 -0
  147. package/gen/geospatial/v1/feature_service_pb.d.ts +403 -0
  148. package/gen/geospatial/v1/feature_service_pb.d.ts.map +1 -0
  149. package/gen/geospatial/v1/feature_service_pb.js +47 -0
  150. package/gen/geospatial/v1/index.d.ts +5 -0
  151. package/gen/geospatial/v1/index.d.ts.map +1 -0
  152. package/gen/geospatial/v1/index.js +9 -0
  153. package/gen/geospatial/v1/spatial_types_pb.d.ts +285 -0
  154. package/gen/geospatial/v1/spatial_types_pb.d.ts.map +1 -0
  155. package/gen/geospatial/v1/spatial_types_pb.js +58 -0
  156. package/gen/geospatial/v1/workspace_artifact_types_pb.d.ts +379 -0
  157. package/gen/geospatial/v1/workspace_artifact_types_pb.d.ts.map +1 -0
  158. package/gen/geospatial/v1/workspace_artifact_types_pb.js +151 -0
  159. package/generated-app/projection.d.ts.map +1 -1
  160. package/generated-app/projection.js +10 -1
  161. package/map/webmap-maplibre.d.ts +2 -1
  162. package/map/webmap-maplibre.d.ts.map +1 -1
  163. package/map/webmap-maplibre.js +3 -0
  164. package/package.json +3 -3
  165. package/query-planner/geoservices.d.ts.map +1 -1
  166. package/query-planner/geoservices.js +2 -0
  167. package/query-planner/grpc.d.ts +4 -4
  168. package/query-planner/grpc.d.ts.map +1 -1
  169. package/query-planner/grpc.js +11 -11
  170. package/query-planner/types.d.ts +6 -4
  171. package/query-planner/types.d.ts.map +1 -1
  172. package/realtime/honua-server.d.ts +4 -0
  173. package/realtime/honua-server.d.ts.map +1 -1
  174. package/realtime/honua-server.js +10 -0
  175. package/realtime/resumable.js +9 -0
  176. package/realtime/types.d.ts +8 -0
  177. package/realtime/types.d.ts.map +1 -1
  178. package/runtime/errors.d.ts +1 -1
  179. package/runtime/errors.d.ts.map +1 -1
  180. package/runtime/errors.js +2 -0
  181. package/runtime/generated/map-package-schema-meta.d.ts +11 -0
  182. package/runtime/generated/map-package-schema-meta.d.ts.map +1 -0
  183. package/runtime/generated/map-package-schema-meta.js +12 -0
  184. package/runtime/generated/map-package-schema-validator.d.ts +16 -0
  185. package/runtime/generated/map-package-schema-validator.d.ts.map +1 -0
  186. package/runtime/generated/map-package-schema-validator.js +1422 -0
  187. package/runtime/index.d.ts +5 -1
  188. package/runtime/index.d.ts.map +1 -1
  189. package/runtime/index.js +6 -0
  190. package/runtime/map-package-export.d.ts +151 -0
  191. package/runtime/map-package-export.d.ts.map +1 -0
  192. package/runtime/map-package-export.js +636 -0
  193. package/runtime/map-package-validation.d.ts +21 -1
  194. package/runtime/map-package-validation.d.ts.map +1 -1
  195. package/runtime/map-package-validation.js +132 -95
  196. package/runtime/map-package.d.ts +80 -0
  197. package/runtime/map-package.d.ts.map +1 -1
  198. package/studio/capability-manifest.d.ts +10 -0
  199. package/studio/capability-manifest.d.ts.map +1 -1
  200. package/studio/capability-manifest.js +38 -0
  201. package/studio/chart-spec.d.ts +6 -6
  202. package/studio/chart-spec.d.ts.map +1 -1
  203. package/studio/chart-spec.js +6 -6
  204. package/studio/command-adapter.d.ts +35 -0
  205. package/studio/command-adapter.d.ts.map +1 -0
  206. package/studio/command-adapter.js +36 -0
  207. package/studio/generated/studio-api.d.ts +1062 -0
  208. package/studio/generated/studio-api.d.ts.map +1 -0
  209. package/studio/generated/studio-api.js +4 -0
  210. package/studio/index.d.ts +25 -13
  211. package/studio/index.d.ts.map +1 -1
  212. package/studio/index.js +21 -10
  213. package/studio/lifecycle-client.d.ts +262 -22
  214. package/studio/lifecycle-client.d.ts.map +1 -1
  215. package/studio/lifecycle-client.js +543 -24
  216. package/studio/lifecycle-errors.d.ts +8 -2
  217. package/studio/lifecycle-errors.d.ts.map +1 -1
  218. package/studio/lifecycle-errors.js +6 -1
  219. package/studio/lifecycle-types.d.ts +237 -38
  220. package/studio/lifecycle-types.d.ts.map +1 -1
  221. package/studio/lifecycle-types.js +5 -12
  222. package/studio/publication-status.d.ts +55 -0
  223. package/studio/publication-status.d.ts.map +1 -0
  224. package/studio/publication-status.js +103 -0
  225. package/studio/types.d.ts +37 -7
  226. package/studio/types.d.ts.map +1 -1
  227. package/studio/types.js +5 -3
  228. package/studio/validate.d.ts.map +1 -1
  229. package/studio/validate.js +104 -7
  230. package/style/renderers.d.ts +6 -1
  231. package/style/renderers.d.ts.map +1 -1
  232. package/style/renderers.js +11 -6
  233. package/style/visual-variables.d.ts +43 -0
  234. package/style/visual-variables.d.ts.map +1 -0
  235. package/style/visual-variables.js +183 -0
  236. package/webmap/convert-layer.d.ts +2 -1
  237. package/webmap/convert-layer.d.ts.map +1 -1
  238. package/webmap/convert-layer.js +20 -3
  239. package/webmap/convert-renderer.d.ts +10 -3
  240. package/webmap/convert-renderer.d.ts.map +1 -1
  241. package/webmap/convert-renderer.js +190 -36
  242. package/webmap/index.d.ts +1 -0
  243. package/webmap/index.d.ts.map +1 -1
  244. package/webmap/parse.d.ts +2 -1
  245. package/webmap/parse.d.ts.map +1 -1
  246. package/webmap/parse.js +1 -1
  247. package/gen/honua/v1/feature_service_pb.d.ts +0 -918
  248. package/gen/honua/v1/feature_service_pb.d.ts.map +0 -1
  249. package/gen/honua/v1/feature_service_pb.js +0 -375
@@ -0,0 +1,330 @@
1
+ /**
2
+ * The one place a command actually runs.
3
+ *
4
+ * `HonuaCommandRuntime.execute` owns everything that must be identical across
5
+ * transports: authority-header refusal, schema validation, idempotency-key and
6
+ * correlation-id derivation, dry-run short-circuiting, cancellation, error
7
+ * normalization, and receipt assembly. A transport contributes argument
8
+ * parsing on the way in and rendering on the way out — nothing else.
9
+ *
10
+ * ## Security contract
11
+ *
12
+ * - **No shared administrator credential.** The runtime is built from a
13
+ * {@link HonuaControlPlaneClient} (and optionally a Studio lifecycle client),
14
+ * both of which carry the *caller's own* credential from the `HonuaClient`
15
+ * they were constructed with. There is no admin-key path here and no
16
+ * `HonuaAdminClient` dependency.
17
+ * - **No client-side authorization bypass.** Caller-supplied per-call headers
18
+ * are screened against {@link HONUA_COMMAND_RESERVED_HEADERS}; any attempt to
19
+ * set a credential, actor, tenant, policy, or approver header fails with an
20
+ * `authorization` {@link HonuaCommandError} *before* the command runs, on
21
+ * every transport. The claimed {@link HonuaCommandIdentity} is echoed onto
22
+ * the receipt and never placed on the wire.
23
+ * - **No caller-owned request identity.** The same screen refuses
24
+ * {@link HONUA_COMMAND_OWNED_HEADERS} — the `Idempotency-Key`, `If-Match`, and `X-Correlation-ID`
25
+ * the runtime derives and records on the receipt. Both screens compare
26
+ * header names case-insensitively, so `Idempotency-Key`, `idempotency-key`,
27
+ * and `IDEMPOTENCY-KEY` are the same header to them. Without this a caller
28
+ * could put one key on the wire while the receipt and the invocation record
29
+ * claimed another, breaking retry collapsing and the audit join.
30
+ * - **No self-approval.** Command input schemas are closed, so no transport can
31
+ * introduce an approval field the others lack; every receipt records
32
+ * `authorization: "server-enforced"`.
33
+ * - **No credential in the receipt's rendered fields.** A receipt is built to be
34
+ * persisted and printed, so the plan's human-readable `summary` goes through
35
+ * the SDK's one credential recognizer before it is recorded — an import's
36
+ * presigned `sourceUrl` must not outlive its request in an audit log.
37
+ *
38
+ * @experimental
39
+ * @module
40
+ */
41
+ import { redactHonuaExportText } from "../../core/credential-redaction.js";
42
+ import { HonuaControlPlaneClient } from "../client.js";
43
+ import { HonuaCommandError, toHonuaCommandError } from "./errors.js";
44
+ import { canonicalCommandJson, commandDigest, deriveCorrelationId, deriveIdempotencyKey, normalizeCommandIdentity, } from "./identity.js";
45
+ import { HONUA_COMMAND_RECEIPT_KIND, HONUA_COMMAND_TRANSPORTS, } from "./types.js";
46
+ import { validateCommandInput } from "./validate.js";
47
+ /**
48
+ * Headers a caller may never set on a command invocation.
49
+ *
50
+ * Two families: credential headers (which would let a transport swap in a
51
+ * different — possibly shared administrator — identity) and authority headers
52
+ * (which would let a transport assert an actor, tenant, policy decision, or
53
+ * approval the server did not derive itself). Compared case-insensitively.
54
+ */
55
+ export const HONUA_COMMAND_RESERVED_HEADERS = [
56
+ "authorization",
57
+ "proxy-authorization",
58
+ "cookie",
59
+ "x-api-key",
60
+ "x-honua-api-key",
61
+ "x-honua-admin-key",
62
+ "x-honua-actor",
63
+ "x-honua-act-as",
64
+ "x-honua-impersonate",
65
+ "x-honua-tenant",
66
+ "x-honua-tenant-id",
67
+ "x-honua-scope",
68
+ "x-honua-scopes",
69
+ "x-honua-policy-decision",
70
+ "x-honua-approver",
71
+ "x-honua-approved-by",
72
+ "x-forwarded-user",
73
+ "x-forwarded-access-token",
74
+ ];
75
+ const RESERVED_HEADER_SET = new Set(HONUA_COMMAND_RESERVED_HEADERS);
76
+ /**
77
+ * Headers the runtime derives and records on the receipt.
78
+ *
79
+ * A caller-supplied value would put a different key on the wire
80
+ * than the receipt claims, which breaks exactly the two guarantees the receipt
81
+ * exists to provide: that a retry collapses to one server-side effect, and that
82
+ * an audit join can match a receipt to the request it describes. Callers set
83
+ * these through {@link HonuaCommandInvocation.idempotencyKey},
84
+ * {@link HonuaCommandInvocation.ifMatch}, and {@link HonuaCommandInvocation.correlationId}, which are recorded.
85
+ */
86
+ export const HONUA_COMMAND_OWNED_HEADERS = ["idempotency-key", "if-match", "x-correlation-id"];
87
+ const OWNED_HEADER_SET = new Set(HONUA_COMMAND_OWNED_HEADERS);
88
+ /** Build a {@link HonuaCommandRuntime}. */
89
+ export function createHonuaCommandRuntime(options) {
90
+ return new HonuaCommandRuntime(options);
91
+ }
92
+ /**
93
+ * Executes {@link HonuaCommand}s. One instance per connection; share it across
94
+ * every transport a process hosts.
95
+ *
96
+ * @experimental
97
+ */
98
+ export class HonuaCommandRuntime {
99
+ #controlPlane;
100
+ #studio;
101
+ #identity;
102
+ constructor(options) {
103
+ const controlPlane = options.controlPlane ?? (options.client ? new HonuaControlPlaneClient({ client: options.client }) : undefined);
104
+ if (!controlPlane) {
105
+ throw new TypeError("createHonuaCommandRuntime requires either a `client` or a prebuilt `controlPlane`.");
106
+ }
107
+ this.#controlPlane = controlPlane;
108
+ this.#studio = options.studio;
109
+ this.#identity = normalizeCommandIdentity(options.identity);
110
+ }
111
+ get controlPlane() {
112
+ return this.#controlPlane;
113
+ }
114
+ get studio() {
115
+ return this.#studio;
116
+ }
117
+ /**
118
+ * Validate, plan, and (unless `dryRun`) execute `command`, returning a
119
+ * deterministic {@link HonuaCommandReceipt}.
120
+ *
121
+ * @throws {HonuaCommandError} for every failure mode, already classified.
122
+ */
123
+ async execute(command, input, invocation) {
124
+ const transport = assertTransport(command.id, invocation.transport);
125
+ const identity = normalizeCommandIdentity({ ...this.#identity, ...invocation.identity });
126
+ const idempotencyKey = invocation.idempotencyKey?.trim() || deriveIdempotencyKey(command.id, input, identity);
127
+ const correlationId = invocation.correlationId?.trim() || deriveCorrelationId(command.id, idempotencyKey);
128
+ const failureContext = { correlationId, idempotencyKey, signal: invocation.signal };
129
+ assertNoAuthorityOverride(command.id, invocation.headers, { correlationId, idempotencyKey });
130
+ assertNoCommandKeyOverride(command.id, invocation.headers, { correlationId, idempotencyKey });
131
+ // Match the server's CorrelationIdMiddleware contract. An unsupported id
132
+ // would be silently replaced there, leaving the receipt unable to join it.
133
+ if (!/^[A-Za-z0-9_.:-]{1,64}$/.test(correlationId)) {
134
+ throw new HonuaCommandError("validation", command.id, "Command correlationId must contain 1–64 ASCII letters, digits, hyphens, underscores, dots, or colons.", {
135
+ ...failureContext,
136
+ issues: [{ path: "correlationId", message: "must match the server correlation-id format" }],
137
+ });
138
+ }
139
+ // Schema first, then the command's own cross-field rules. Both run before
140
+ // `plan`, so the dry-run preview below is reachable only for input the real
141
+ // invocation would also accept.
142
+ const issues = [...validateCommandInput(command.inputSchema, input), ...(command.validate?.(input) ?? [])];
143
+ if (issues.length > 0) {
144
+ throw new HonuaCommandError("validation", command.id, `Command ${command.id} received invalid input: ${issues.map((issue) => `${issue.path || "(root)"} ${issue.message}`).join("; ")}.`, { ...failureContext, issues });
145
+ }
146
+ if (invocation.signal?.aborted) {
147
+ throw new HonuaCommandError("cancelled", command.id, `Command ${command.id} was cancelled before it started.`, {
148
+ ...failureContext,
149
+ cause: invocation.signal.reason,
150
+ });
151
+ }
152
+ const context = {
153
+ input,
154
+ controlPlane: this.#controlPlane,
155
+ ...(this.#studio ? { studio: this.#studio } : {}),
156
+ identity,
157
+ transport,
158
+ idempotencyKey,
159
+ correlationId,
160
+ dryRun: invocation.dryRun === true,
161
+ ...(invocation.signal ? { signal: invocation.signal } : {}),
162
+ requestOptions: (overrides = {}) => {
163
+ const headers = new Headers(overrides.headers ?? invocation.headers);
164
+ headers.set("X-Correlation-ID", correlationId);
165
+ return {
166
+ ...(invocation.signal ? { signal: invocation.signal } : {}),
167
+ ...(command.mode === "action" ? { idempotencyKey } : {}),
168
+ ...(invocation.ifMatch ? { ifMatch: invocation.ifMatch } : {}),
169
+ ...overrides,
170
+ headers,
171
+ };
172
+ },
173
+ };
174
+ let plan;
175
+ try {
176
+ plan = withoutResourceLink(command.plan(context));
177
+ }
178
+ catch (error) {
179
+ throw toHonuaCommandError(error, command.id, failureContext);
180
+ }
181
+ if (context.dryRun) {
182
+ return buildReceipt({
183
+ command,
184
+ status: "dry-run",
185
+ idempotencyKey,
186
+ correlationId,
187
+ identity,
188
+ transport,
189
+ plan,
190
+ ...(plan.resourceRef ? { resourceRef: plan.resourceRef } : {}),
191
+ });
192
+ }
193
+ try {
194
+ const outcome = await command.execute(context);
195
+ return buildReceipt({
196
+ command,
197
+ status: outcome.status ?? "ok",
198
+ idempotencyKey,
199
+ correlationId,
200
+ identity,
201
+ transport,
202
+ plan,
203
+ ...((outcome.resourceRef ?? plan.resourceRef) ? { resourceRef: outcome.resourceRef ?? plan.resourceRef } : {}),
204
+ ...(outcome.validators ? { validators: outcome.validators } : {}),
205
+ ...(outcome.problem ? { problem: outcome.problem } : {}),
206
+ output: outcome.output,
207
+ });
208
+ }
209
+ catch (error) {
210
+ throw toHonuaCommandError(error, command.id, failureContext);
211
+ }
212
+ }
213
+ }
214
+ /**
215
+ * Refuse a caller-supplied credential or authority header.
216
+ *
217
+ * Exported so a transport can screen its own header pass-through before it
218
+ * builds an invocation, but the runtime applies it unconditionally — a
219
+ * transport that forgets cannot weaken the guarantee.
220
+ */
221
+ export function assertNoAuthorityOverride(commandId, headers, context = {}) {
222
+ const offending = headerNames(headers).filter((name) => RESERVED_HEADER_SET.has(name));
223
+ if (offending.length === 0)
224
+ return;
225
+ const named = offending.sort().join(", ");
226
+ throw new HonuaCommandError("authorization", commandId, `Command ${commandId} refused caller-supplied authority header(s): ${named}. Authorization is derived server-side from the connection credential; no transport may assert it.`, context);
227
+ }
228
+ /**
229
+ * Refuse a caller-supplied value for a header the runtime derives.
230
+ *
231
+ * Classified `validation` rather than `authorization`: this is caller misuse of
232
+ * the invocation contract, not an attempt to assert authority. Use
233
+ * {@link HonuaCommandInvocation.idempotencyKey} / `.ifMatch` instead, so the
234
+ * value that travels is the value the receipt records.
235
+ */
236
+ export function assertNoCommandKeyOverride(commandId, headers, context = {}) {
237
+ const offending = headerNames(headers).filter((name) => OWNED_HEADER_SET.has(name));
238
+ if (offending.length === 0)
239
+ return;
240
+ const named = offending.sort().join(", ");
241
+ throw new HonuaCommandError("validation", commandId, `Command ${commandId} refused caller-supplied header(s) it derives itself: ${named}. Set them through the invocation's \`idempotencyKey\` / \`ifMatch\` / \`correlationId\` so the value on the wire is the value the receipt records.`, {
242
+ ...context,
243
+ issues: offending.sort().map((name) => ({
244
+ path: `headers.${name}`,
245
+ message: "is derived by the command runtime and cannot be set by a caller",
246
+ })),
247
+ });
248
+ }
249
+ /**
250
+ * Strip the resource link from a plan.
251
+ *
252
+ * A plan describes a request that has *not been issued* — and on a dry run it
253
+ * is the whole receipt, because `execute` is never reached. A link on it would
254
+ * therefore be a URL for a resource that may not exist, printed by the CLI and
255
+ * Studio next to the word "dry run", which is precisely honua-sdk-js#1426's
256
+ * "preview is never reported as persisted or published" failing. `plan` is
257
+ * already documented to carry no request body for a related reason; carrying no
258
+ * resource link is the same rule applied to the response side.
259
+ *
260
+ * The executed path is unaffected: a real invocation reports the `href` the
261
+ * *server* returned, on `HonuaCommandOutcome.resourceRef`. What a command can
262
+ * no longer do is predict one.
263
+ */
264
+ function withoutResourceLink(plan) {
265
+ const ref = plan.resourceRef;
266
+ if (!ref || ref.href === undefined)
267
+ return plan;
268
+ const { href: _href, ...rest } = ref;
269
+ return { ...plan, resourceRef: rest };
270
+ }
271
+ function headerNames(headers) {
272
+ if (!headers)
273
+ return [];
274
+ if (headers instanceof Headers)
275
+ return [...headers.keys()].map((name) => name.toLowerCase());
276
+ if (Array.isArray(headers))
277
+ return headers.map(([name]) => String(name).toLowerCase());
278
+ return Object.keys(headers).map((name) => name.toLowerCase());
279
+ }
280
+ function assertTransport(commandId, transport) {
281
+ if (HONUA_COMMAND_TRANSPORTS.includes(transport))
282
+ return transport;
283
+ throw new HonuaCommandError("validation", commandId, `Unknown command transport "${String(transport)}"; expected one of ${HONUA_COMMAND_TRANSPORTS.join(", ")}.`);
284
+ }
285
+ /**
286
+ * Withhold credential material from the plan's human-readable summary.
287
+ *
288
+ * {@link HonuaCommandPlan} promises it "carries no request body: command inputs
289
+ * can contain connection credentials, and a plan is rendered to terminals and
290
+ * logs". Omitting the body is not on its own enough: a command builds its
291
+ * `summary` from its own input, and an import's `sourceUrl` is routinely a
292
+ * presigned URL whose query string *is* the credential. Interpolated into a
293
+ * receipt — which `serializeHonuaCommandReceipt` exists to persist, and which
294
+ * `honua map publish --json` prints verbatim — that URL outlives the request it
295
+ * authorized, in a log the caller believes is safe to keep.
296
+ *
297
+ * So the summary goes through the SDK's one credential recognizer
298
+ * (`src/core/credential-redaction.ts`) on the way onto the receipt. The
299
+ * substitution is deterministic, so two equivalent calls still agree on
300
+ * `auditKey`. `path` is deliberately left alone: it is documented as the
301
+ * request exactly as issued, is built only from percent-encoded resource ids,
302
+ * and redacting an id would misreport the request. `output` is left alone too
303
+ * — it is the command's return value, not a rendered preview.
304
+ */
305
+ function redactPlanSummary(plan) {
306
+ const summary = redactHonuaExportText(plan.summary);
307
+ return summary === plan.summary ? plan : { ...plan, summary };
308
+ }
309
+ function buildReceipt(parts) {
310
+ const projection = {
311
+ kind: HONUA_COMMAND_RECEIPT_KIND,
312
+ commandId: parts.command.id,
313
+ mode: parts.command.mode,
314
+ status: parts.status,
315
+ ...(parts.command.mode === "action" ? { idempotencyKey: parts.idempotencyKey } : {}),
316
+ correlationId: parts.correlationId,
317
+ identity: parts.identity,
318
+ authorization: "server-enforced",
319
+ plan: redactPlanSummary(parts.plan),
320
+ ...(parts.resourceRef ? { resourceRef: parts.resourceRef } : {}),
321
+ ...(parts.validators ? { validators: parts.validators } : {}),
322
+ ...(parts.problem ? { problem: parts.problem } : {}),
323
+ ...(parts.output !== undefined ? { output: parts.output } : {}),
324
+ };
325
+ return {
326
+ ...projection,
327
+ transport: parts.transport,
328
+ auditKey: commandDigest(canonicalCommandJson(projection)),
329
+ };
330
+ }
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Shared application-command contract for the Honua control plane.
3
+ *
4
+ * Every surface — the `honua` CLI, the MCP server, Studio, and direct JS —
5
+ * invokes the *same* {@link HonuaCommand} objects. A transport adapts input
6
+ * and output only: it parses its own argument shape into the command's
7
+ * declared JSON-schema input, hands the result to
8
+ * `HonuaCommandRuntime.execute`, and renders the returned
9
+ * {@link HonuaCommandReceipt}. Domain sequencing (read-modify-write,
10
+ * optimistic-concurrency checks, job hand-off) and the dry-run projection live
11
+ * on the command, never on the transport.
12
+ *
13
+ * Authorization is deliberately **not** modelled here beyond an echo. The
14
+ * command layer carries no administrator credential of its own, refuses
15
+ * caller-supplied authority headers and the request-identity headers it derives
16
+ * itself (see `assertNoAuthorityOverride` and `assertNoCommandKeyOverride` in
17
+ * `./runtime.js`), and stamps every receipt `authorization:
18
+ * "server-enforced"`. A client cannot approve its own publication by choosing
19
+ * a different transport because no transport can express approval at all —
20
+ * the shared command inputs have no approval field and every command input
21
+ * schema is closed (`additionalProperties: false`).
22
+ *
23
+ * @experimental Part of the `@honua/sdk-js/control-plane` experimental
24
+ * entrypoint; not yet covered by the SDK's semver contract.
25
+ * @module
26
+ */
27
+ import type { QueryMethod } from "../../core/types.js";
28
+ import type { HonuaStudioLifecycleClient } from "../../studio/lifecycle-client.js";
29
+ import type { HonuaControlPlaneClient } from "../client.js";
30
+ import type { HonuaControlPlaneRequestOptions, HonuaEntityValidators, HonuaProblemDetails } from "../types.js";
31
+ import type { HonuaCommandValidationIssue } from "./errors.js";
32
+ /**
33
+ * JSON-Schema subset used to describe a command's input.
34
+ *
35
+ * Structurally the same dialect `HonuaAgentToolDefinition.inputSchema` uses
36
+ * (`src/agent-tools/index.ts`) so MCP tool schemas, CLI help, and Studio forms
37
+ * can all project from one description. It is redeclared here rather than
38
+ * imported so the control-plane split package does not pull in the agent-tool
39
+ * runtime. `src/` is deliberately zod-free.
40
+ */
41
+ export interface HonuaCommandJsonSchema {
42
+ readonly type?: string | readonly string[];
43
+ readonly description?: string;
44
+ readonly enum?: readonly unknown[];
45
+ readonly format?: string;
46
+ readonly properties?: Readonly<Record<string, HonuaCommandJsonSchema>>;
47
+ readonly items?: HonuaCommandJsonSchema;
48
+ readonly required?: readonly string[];
49
+ readonly additionalProperties?: boolean | HonuaCommandJsonSchema;
50
+ readonly minLength?: number;
51
+ readonly maxLength?: number;
52
+ readonly minimum?: number;
53
+ readonly maximum?: number;
54
+ readonly default?: unknown;
55
+ }
56
+ /**
57
+ * `read` commands never mutate and never carry an idempotency key on the
58
+ * wire; `action` commands do both. Mirrors `HonuaAgentToolMode`.
59
+ */
60
+ export type HonuaCommandMode = "read" | "action";
61
+ /**
62
+ * Terminal state recorded on a {@link HonuaCommandReceipt}. Mirrors
63
+ * `HonuaAgentToolStatus` and adds `cancelled` for `AbortSignal` aborts.
64
+ */
65
+ export type HonuaCommandStatus = "ok" | "dry-run" | "denied" | "cancelled" | "error";
66
+ /** Surfaces that may invoke a command. Recorded, never trusted. */
67
+ export declare const HONUA_COMMAND_TRANSPORTS: readonly ["cli", "mcp", "studio", "sdk"];
68
+ /** One of {@link HONUA_COMMAND_TRANSPORTS}. */
69
+ export type HonuaCommandTransport = (typeof HONUA_COMMAND_TRANSPORTS)[number];
70
+ /**
71
+ * Acting identity and tenant, as the *caller claims them*.
72
+ *
73
+ * These values are echoed onto the receipt so a downstream audit join can line
74
+ * a receipt up with the server-side record. They are never sent as request
75
+ * headers and never used to make a local authorization decision — the server
76
+ * derives the real acting identity from the credential on the
77
+ * {@link HonuaControlPlaneClient}'s underlying `HonuaClient`.
78
+ */
79
+ export interface HonuaCommandIdentity {
80
+ /** Acting principal id as claimed by the caller. */
81
+ readonly actor?: string;
82
+ /** Tenant / workspace-owner echo. */
83
+ readonly tenantId?: string;
84
+ /** OAuth scope ceiling the caller believes it holds, sorted for determinism. */
85
+ readonly scopes?: readonly string[];
86
+ }
87
+ /** Stable pointer to the resource a command read or affected. */
88
+ export interface HonuaCommandResourceRef {
89
+ /** Resource kind, e.g. `map-package`, `import-job`, `connection`. */
90
+ readonly type: string;
91
+ /** Server-assigned identifier, when the server returned one. */
92
+ readonly id?: string;
93
+ /** Owning workspace, when scoped. */
94
+ readonly workspaceId?: string;
95
+ /** Absolute or root-relative link the server returned for the resource. */
96
+ readonly href?: string;
97
+ }
98
+ /**
99
+ * The request a command *would* issue. Produced by {@link HonuaCommand.plan},
100
+ * recorded on every receipt, and the only thing a dry run evaluates — a dry
101
+ * run never reaches {@link HonuaCommand.execute}, so no transport can
102
+ * accidentally execute while previewing.
103
+ *
104
+ * Deliberately carries no request body: command inputs can contain connection
105
+ * credentials, and a plan is rendered to terminals and logs.
106
+ *
107
+ * It also carries no **resource link**. `resourceRef.href` is dropped by the
108
+ * runtime before the plan reaches a receipt, so a dry run — which is nothing
109
+ * but this plan — cannot print a URL for a resource that was never created or
110
+ * published (honua-sdk-js#1426). A link appears on a receipt only when the
111
+ * *server* returned one to {@link HonuaCommand.execute}.
112
+ */
113
+ export interface HonuaCommandPlan {
114
+ readonly method: QueryMethod;
115
+ /** Path relative to the client's base path, exactly as the command will call it. */
116
+ readonly path: string;
117
+ /**
118
+ * One-line human summary for CLI/Studio previews.
119
+ *
120
+ * A command builds this from its own input, so it can name a source URL. The
121
+ * runtime passes it through the SDK's credential recognizer before it reaches
122
+ * a receipt, so a presigned URL or an embedded secret is withheld rather than
123
+ * persisted into an audit record. Commands should still prefer identifiers to
124
+ * URLs here.
125
+ */
126
+ readonly summary: string;
127
+ /** Resource this request addresses. Any `href` is stripped by the runtime. */
128
+ readonly resourceRef?: HonuaCommandResourceRef;
129
+ }
130
+ /** What {@link HonuaCommand.execute} returns to the runtime. */
131
+ export interface HonuaCommandOutcome<TOutput> {
132
+ readonly output: TOutput;
133
+ /** Overrides `ok`; use `denied` when the server durably recorded a refusal. */
134
+ readonly status?: Extract<HonuaCommandStatus, "ok" | "denied">;
135
+ readonly resourceRef?: HonuaCommandResourceRef;
136
+ readonly validators?: HonuaEntityValidators;
137
+ readonly problem?: HonuaProblemDetails;
138
+ }
139
+ /** Discriminating tag on {@link HonuaCommandReceipt}. */
140
+ export declare const HONUA_COMMAND_RECEIPT_KIND: "honua.command.receipt.v1";
141
+ /**
142
+ * Deterministic, serializable record of one command invocation.
143
+ *
144
+ * Two equivalent calls made from different transports produce identical
145
+ * receipts apart from {@link HonuaCommandReceipt.transport}; `auditKey` hashes
146
+ * the transport-independent projection so a server-side audit join can match
147
+ * them. Nothing here is time-, host-, or random-dependent: `idempotencyKey`
148
+ * and `correlationId` are derived from the command id, the canonicalized
149
+ * input, and the tenant unless the caller supplies them. Read commands omit
150
+ * `idempotencyKey` because no such key is sent to the server.
151
+ */
152
+ export interface HonuaCommandReceipt<TOutput = unknown> {
153
+ readonly kind: typeof HONUA_COMMAND_RECEIPT_KIND;
154
+ readonly commandId: string;
155
+ readonly mode: HonuaCommandMode;
156
+ readonly status: HonuaCommandStatus;
157
+ readonly idempotencyKey?: string;
158
+ /** Sent as `X-Correlation-ID` on every request belonging to this command. */
159
+ readonly correlationId: string;
160
+ readonly identity: HonuaCommandIdentity;
161
+ /** Surface that issued the call. Recorded for audit; excluded from `auditKey`. */
162
+ readonly transport: HonuaCommandTransport;
163
+ /** Always `server-enforced`: the command layer makes no authorization decision. */
164
+ readonly authorization: "server-enforced";
165
+ readonly plan: HonuaCommandPlan;
166
+ readonly resourceRef?: HonuaCommandResourceRef;
167
+ readonly validators?: HonuaEntityValidators;
168
+ readonly problem?: HonuaProblemDetails;
169
+ readonly output?: TOutput;
170
+ /** Hash of the transport-independent projection. Equal across transports. */
171
+ readonly auditKey: string;
172
+ }
173
+ /** Per-invocation context handed to {@link HonuaCommand.plan} and `execute`. */
174
+ export interface HonuaCommandContext<TInput> {
175
+ /** Input after schema validation. */
176
+ readonly input: TInput;
177
+ readonly controlPlane: HonuaControlPlaneClient;
178
+ /**
179
+ * Studio lifecycle client, present only when the host supplied one. Commands
180
+ * in the `studio.*` namespace throw a `transport` `HonuaCommandError` when it
181
+ * is absent rather than reaching for a second credential.
182
+ */
183
+ readonly studio?: HonuaStudioLifecycleClient;
184
+ readonly identity: HonuaCommandIdentity;
185
+ readonly transport: HonuaCommandTransport;
186
+ readonly idempotencyKey: string;
187
+ readonly correlationId: string;
188
+ readonly dryRun: boolean;
189
+ readonly signal?: AbortSignal;
190
+ /**
191
+ * Request options for the underlying resource client, with `Idempotency-Key`
192
+ * (action commands only), `If-Match`, the caller's `AbortSignal`, and the
193
+ * caller's remaining headers already threaded. The headers that reach here
194
+ * can carry neither an authority claim nor a command-owned key: both
195
+ * families are refused before `plan` runs.
196
+ */
197
+ requestOptions(overrides?: HonuaControlPlaneRequestOptions): HonuaControlPlaneRequestOptions;
198
+ }
199
+ /**
200
+ * One idempotent application command, shared verbatim by every transport.
201
+ *
202
+ * @typeParam TInput - Validated input shape, described by `inputSchema`.
203
+ * @typeParam TOutput - Value carried on the receipt's `output`.
204
+ */
205
+ export interface HonuaCommand<TInput, TOutput> {
206
+ /** Stable dotted id, e.g. `map-package.publish`. */
207
+ readonly id: string;
208
+ readonly title: string;
209
+ readonly description: string;
210
+ readonly mode: HonuaCommandMode;
211
+ /** Resource kind this command addresses; used for the receipt's `resourceRef`. */
212
+ readonly resourceKind: string;
213
+ readonly inputSchema: HonuaCommandJsonSchema;
214
+ /**
215
+ * Cross-field checks the JSON schema cannot express ("one of these two is
216
+ * required"). Called by the runtime after schema validation and **before**
217
+ * `plan`, so a dry run can never approve an input the real invocation would
218
+ * reject. Must be pure. Return an empty array when the input is valid.
219
+ */
220
+ validate?(input: TInput): readonly HonuaCommandValidationIssue[];
221
+ /** Describe the request without issuing it. Must be pure and side-effect free. */
222
+ plan(context: HonuaCommandContext<TInput>): HonuaCommandPlan;
223
+ /** Issue the request. Never called during a dry run. */
224
+ execute(context: HonuaCommandContext<TInput>): Promise<HonuaCommandOutcome<TOutput>>;
225
+ }
226
+ /** Any command, for registries and transport dispatch tables. */
227
+ export type HonuaAnyCommand = HonuaCommand<any, any>;
228
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/commands/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,kCAAkC,CAAC;AACnF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,KAAK,EAAE,+BAA+B,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC/G,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAE/D;;;;;;;;GAQG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;IACnC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC,CAAC;IACvE,QAAQ,CAAC,KAAK,CAAC,EAAE,sBAAsB,CAAC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,OAAO,GAAG,sBAAsB,CAAC;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEjD;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,IAAI,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,OAAO,CAAC;AAErF,mEAAmE;AACnE,eAAO,MAAM,wBAAwB,0CAA2C,CAAC;AAEjF,+CAA+C;AAC/C,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE9E;;;;;;;;GAQG;AACH,MAAM,WAAW,oBAAoB;IACnC,oDAAoD;IACpD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,qCAAqC;IACrC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,gFAAgF;IAChF,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,iEAAiE;AACjE,MAAM,WAAW,uBAAuB;IACtC,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,qCAAqC;IACrC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,oFAAoF;IACpF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,CAAC,EAAE,uBAAuB,CAAC;CAChD;AAED,gEAAgE;AAChE,MAAM,WAAW,mBAAmB,CAAC,OAAO;IAC1C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,kBAAkB,EAAE,IAAI,GAAG,QAAQ,CAAC,CAAC;IAC/D,QAAQ,CAAC,WAAW,CAAC,EAAE,uBAAuB,CAAC;IAC/C,QAAQ,CAAC,UAAU,CAAC,EAAE,qBAAqB,CAAC;IAC5C,QAAQ,CAAC,OAAO,CAAC,EAAE,mBAAmB,CAAC;CACxC;AAED,yDAAyD;AACzD,eAAO,MAAM,0BAA0B,EAAG,0BAAmC,CAAC;AAE9E;;;;;;;;;;GAUG;AACH,MAAM,WAAW,mBAAmB,CAAC,OAAO,GAAG,OAAO;IACpD,QAAQ,CAAC,IAAI,EAAE,OAAO,0BAA0B,CAAC;IACjD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,kFAAkF;IAClF,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC;IAC1C,mFAAmF;IACnF,QAAQ,CAAC,aAAa,EAAE,iBAAiB,CAAC;IAC1C,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,uBAAuB,CAAC;IAC/C,QAAQ,CAAC,UAAU,CAAC,EAAE,qBAAqB,CAAC;IAC5C,QAAQ,CAAC,OAAO,CAAC,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,gFAAgF;AAChF,MAAM,WAAW,mBAAmB,CAAC,MAAM;IACzC,qCAAqC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,YAAY,EAAE,uBAAuB,CAAC;IAC/C;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,0BAA0B,CAAC;IAC7C,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;IACxC,QAAQ,CAAC,SAAS,EAAE,qBAAqB,CAAC;IAC1C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B;;;;;;OAMG;IACH,cAAc,CAAC,SAAS,CAAC,EAAE,+BAA+B,GAAG,+BAA+B,CAAC;CAC9F;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY,CAAC,MAAM,EAAE,OAAO;IAC3C,oDAAoD;IACpD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,kFAAkF;IAClF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,EAAE,sBAAsB,CAAC;IAC7C;;;;;OAKG;IACH,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,2BAA2B,EAAE,CAAC;IACjE,kFAAkF;IAClF,IAAI,CAAC,OAAO,EAAE,mBAAmB,CAAC,MAAM,CAAC,GAAG,gBAAgB,CAAC;IAC7D,wDAAwD;IACxD,OAAO,CAAC,OAAO,EAAE,mBAAmB,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC,CAAC;CACtF;AAED,iEAAiE;AAEjE,MAAM,MAAM,eAAe,GAAG,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Shared application-command contract for the Honua control plane.
3
+ *
4
+ * Every surface — the `honua` CLI, the MCP server, Studio, and direct JS —
5
+ * invokes the *same* {@link HonuaCommand} objects. A transport adapts input
6
+ * and output only: it parses its own argument shape into the command's
7
+ * declared JSON-schema input, hands the result to
8
+ * `HonuaCommandRuntime.execute`, and renders the returned
9
+ * {@link HonuaCommandReceipt}. Domain sequencing (read-modify-write,
10
+ * optimistic-concurrency checks, job hand-off) and the dry-run projection live
11
+ * on the command, never on the transport.
12
+ *
13
+ * Authorization is deliberately **not** modelled here beyond an echo. The
14
+ * command layer carries no administrator credential of its own, refuses
15
+ * caller-supplied authority headers and the request-identity headers it derives
16
+ * itself (see `assertNoAuthorityOverride` and `assertNoCommandKeyOverride` in
17
+ * `./runtime.js`), and stamps every receipt `authorization:
18
+ * "server-enforced"`. A client cannot approve its own publication by choosing
19
+ * a different transport because no transport can express approval at all —
20
+ * the shared command inputs have no approval field and every command input
21
+ * schema is closed (`additionalProperties: false`).
22
+ *
23
+ * @experimental Part of the `@honua/sdk-js/control-plane` experimental
24
+ * entrypoint; not yet covered by the SDK's semver contract.
25
+ * @module
26
+ */
27
+ /** Surfaces that may invoke a command. Recorded, never trusted. */
28
+ export const HONUA_COMMAND_TRANSPORTS = ["cli", "mcp", "studio", "sdk"];
29
+ /** Discriminating tag on {@link HonuaCommandReceipt}. */
30
+ export const HONUA_COMMAND_RECEIPT_KIND = "honua.command.receipt.v1";
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Structural validation for command inputs.
3
+ *
4
+ * Commands describe their input with the JSON-Schema subset in `./types.js`,
5
+ * the same dialect `src/agent-tools/index.ts` uses for agent tool definitions.
6
+ * That one description drives CLI argument checking, MCP tool schemas, Studio
7
+ * forms, and this validator — so an input rejected by one transport is
8
+ * rejected identically by all of them.
9
+ *
10
+ * The validator covers exactly the keywords the dialect declares. It is not a
11
+ * general JSON-Schema implementation and deliberately adds no dependency;
12
+ * `src/` is zod-free.
13
+ *
14
+ * @experimental
15
+ * @module
16
+ */
17
+ import type { HonuaCommandValidationIssue } from "./errors.js";
18
+ import type { HonuaCommandJsonSchema } from "./types.js";
19
+ /**
20
+ * Validate `value` against `schema`, returning every finding.
21
+ *
22
+ * `additionalProperties: false` is the load-bearing rule for the security
23
+ * contract: a closed input schema is why no transport can smuggle an approval
24
+ * or authority field into a shared command.
25
+ */
26
+ export declare function validateCommandInput(schema: HonuaCommandJsonSchema, value: unknown): HonuaCommandValidationIssue[];
27
+ //# sourceMappingURL=validate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../../../src/control-plane/commands/validate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,sBAAsB,EAAE,KAAK,EAAE,OAAO,GAAG,2BAA2B,EAAE,CAIlH"}