@honua/app-platform 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 (326) 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/controls/catalog.d.ts.map +1 -1
  40. package/controls/catalog.js +12 -1
  41. package/controls/qualification.d.ts.map +1 -1
  42. package/controls/qualification.js +71 -8
  43. package/core/client.d.ts.map +1 -1
  44. package/core/client.js +29 -31
  45. package/core/credential-redaction.d.ts +163 -0
  46. package/core/credential-redaction.d.ts.map +1 -0
  47. package/core/credential-redaction.js +389 -0
  48. package/core/error-classifications.d.ts +15 -0
  49. package/core/error-classifications.d.ts.map +1 -1
  50. package/core/error-classifications.js +15 -0
  51. package/core/error-code-registry.d.ts +15 -0
  52. package/core/error-code-registry.d.ts.map +1 -1
  53. package/core/error-code-registry.js +15 -0
  54. package/core/error-envelope.d.ts +31 -0
  55. package/core/error-envelope.d.ts.map +1 -1
  56. package/core/error-envelope.js +11 -0
  57. package/core/errors.d.ts +15 -3
  58. package/core/errors.d.ts.map +1 -1
  59. package/core/errors.js +254 -2
  60. package/core/geometry-input-sr.d.ts +3 -0
  61. package/core/geometry-input-sr.d.ts.map +1 -0
  62. package/core/geometry-input-sr.js +28 -0
  63. package/core/geoservices.d.ts.map +1 -1
  64. package/core/geoservices.js +33 -6
  65. package/core/grpc-adapter.d.ts +2 -2
  66. package/core/grpc-adapter.d.ts.map +1 -1
  67. package/core/grpc-adapter.js +41 -9
  68. package/core/job-run-errors.d.ts +10 -0
  69. package/core/job-run-errors.d.ts.map +1 -0
  70. package/core/job-run-errors.js +14 -0
  71. package/core/job-run-lifecycle.d.ts +34 -0
  72. package/core/job-run-lifecycle.d.ts.map +1 -0
  73. package/core/job-run-lifecycle.js +193 -0
  74. package/core/ogc-processes.d.ts +38 -31
  75. package/core/ogc-processes.d.ts.map +1 -1
  76. package/core/ogc-processes.js +123 -173
  77. package/core/pbf-decoder.d.ts.map +1 -1
  78. package/core/pbf-decoder.js +16 -4
  79. package/core/process-runner.js +15 -100
  80. package/core/protocol-transport.d.ts +6 -1
  81. package/core/protocol-transport.d.ts.map +1 -1
  82. package/core/request-pipeline.d.ts +6 -3
  83. package/core/request-pipeline.d.ts.map +1 -1
  84. package/core/request-pipeline.js +28 -11
  85. package/core/spatial-filter.d.ts +4 -4
  86. package/core/spatial-filter.d.ts.map +1 -1
  87. package/core/spatial-filter.js +7 -7
  88. package/core/surfaces.d.ts +2 -8
  89. package/core/surfaces.d.ts.map +1 -1
  90. package/core/surfaces.js +21 -112
  91. package/core/types.d.ts +21 -9
  92. package/core/types.d.ts.map +1 -1
  93. package/custom-elements.json +341 -0
  94. package/esri-compat/class-breaks-renderer.d.ts +2 -0
  95. package/esri-compat/class-breaks-renderer.d.ts.map +1 -1
  96. package/esri-compat/class-breaks-renderer.js +7 -0
  97. package/esri-compat/controls.d.ts +10 -1
  98. package/esri-compat/controls.d.ts.map +1 -1
  99. package/esri-compat/controls.js +86 -2
  100. package/esri-compat/directions.d.ts +35 -0
  101. package/esri-compat/directions.d.ts.map +1 -1
  102. package/esri-compat/directions.js +153 -2
  103. package/esri-compat/expand.d.ts +1 -0
  104. package/esri-compat/expand.d.ts.map +1 -1
  105. package/esri-compat/expand.js +61 -0
  106. package/esri-compat/feature-layer.d.ts +29 -4
  107. package/esri-compat/feature-layer.d.ts.map +1 -1
  108. package/esri-compat/feature-layer.js +335 -13
  109. package/esri-compat/geometry-engine.d.ts +4 -0
  110. package/esri-compat/geometry-engine.d.ts.map +1 -1
  111. package/esri-compat/geometry-engine.js +8 -1
  112. package/esri-compat/graphic.d.ts +7 -4
  113. package/esri-compat/graphic.d.ts.map +1 -1
  114. package/esri-compat/legend.d.ts +2 -1
  115. package/esri-compat/legend.d.ts.map +1 -1
  116. package/esri-compat/legend.js +15 -0
  117. package/esri-compat/map-view-mount.d.ts +48 -0
  118. package/esri-compat/map-view-mount.d.ts.map +1 -0
  119. package/esri-compat/map-view-mount.js +344 -0
  120. package/esri-compat/map-view.d.ts +38 -6
  121. package/esri-compat/map-view.d.ts.map +1 -1
  122. package/esri-compat/map-view.js +467 -33
  123. package/esri-compat/portal.d.ts +2 -0
  124. package/esri-compat/portal.d.ts.map +1 -1
  125. package/esri-compat/portal.js +7 -2
  126. package/esri-compat/renderer-objects.d.ts +7 -2
  127. package/esri-compat/renderer-objects.d.ts.map +1 -1
  128. package/esri-compat/renderer-objects.js +16 -5
  129. package/esri-compat/route-layer.d.ts +8 -0
  130. package/esri-compat/route-layer.d.ts.map +1 -1
  131. package/esri-compat/route-layer.js +24 -4
  132. package/esri-compat/route-task.d.ts +5 -1
  133. package/esri-compat/route-task.d.ts.map +1 -1
  134. package/esri-compat/route-task.js +119 -7
  135. package/esri-compat/scene-view.d.ts.map +1 -1
  136. package/esri-compat/scene-view.js +1 -0
  137. package/esri-compat/search.d.ts +2 -1
  138. package/esri-compat/search.d.ts.map +1 -1
  139. package/esri-compat/search.js +52 -2
  140. package/esri-compat/unique-value-renderer.d.ts +2 -0
  141. package/esri-compat/unique-value-renderer.d.ts.map +1 -1
  142. package/esri-compat/unique-value-renderer.js +7 -0
  143. package/esri-compat/web-map.d.ts +10 -0
  144. package/esri-compat/web-map.d.ts.map +1 -1
  145. package/esri-compat/web-map.js +102 -0
  146. package/gen/geospatial/v1/common_pb.d.ts +514 -0
  147. package/gen/geospatial/v1/common_pb.d.ts.map +1 -0
  148. package/gen/geospatial/v1/common_pb.js +335 -0
  149. package/gen/geospatial/v1/execution_types_pb.d.ts +1186 -0
  150. package/gen/geospatial/v1/execution_types_pb.d.ts.map +1 -0
  151. package/gen/geospatial/v1/execution_types_pb.js +396 -0
  152. package/gen/geospatial/v1/feature_service_pb.d.ts +403 -0
  153. package/gen/geospatial/v1/feature_service_pb.d.ts.map +1 -0
  154. package/gen/geospatial/v1/feature_service_pb.js +47 -0
  155. package/gen/geospatial/v1/index.d.ts +5 -0
  156. package/gen/geospatial/v1/index.d.ts.map +1 -0
  157. package/gen/geospatial/v1/index.js +9 -0
  158. package/gen/geospatial/v1/spatial_types_pb.d.ts +285 -0
  159. package/gen/geospatial/v1/spatial_types_pb.d.ts.map +1 -0
  160. package/gen/geospatial/v1/spatial_types_pb.js +58 -0
  161. package/gen/geospatial/v1/workspace_artifact_types_pb.d.ts +379 -0
  162. package/gen/geospatial/v1/workspace_artifact_types_pb.d.ts.map +1 -0
  163. package/gen/geospatial/v1/workspace_artifact_types_pb.js +151 -0
  164. package/generated-app/projection.d.ts.map +1 -1
  165. package/generated-app/projection.js +10 -1
  166. package/geocoding/index.d.ts +1 -0
  167. package/geocoding/index.d.ts.map +1 -1
  168. package/geocoding/index.js +18 -8
  169. package/geocoding/provider-http.d.ts.map +1 -1
  170. package/geocoding/provider-http.js +3 -1
  171. package/map/webmap-maplibre.d.ts +2 -1
  172. package/map/webmap-maplibre.d.ts.map +1 -1
  173. package/map/webmap-maplibre.js +3 -0
  174. package/operate/client.d.ts +11 -1
  175. package/operate/client.d.ts.map +1 -1
  176. package/operate/client.js +39 -2
  177. package/operate/index.d.ts +1 -1
  178. package/operate/index.d.ts.map +1 -1
  179. package/operate/types.d.ts +40 -6
  180. package/operate/types.d.ts.map +1 -1
  181. package/package.json +6 -4
  182. package/query-planner/geoservices.d.ts.map +1 -1
  183. package/query-planner/geoservices.js +2 -0
  184. package/query-planner/grpc.d.ts +4 -4
  185. package/query-planner/grpc.d.ts.map +1 -1
  186. package/query-planner/grpc.js +11 -11
  187. package/query-planner/types.d.ts +6 -4
  188. package/query-planner/types.d.ts.map +1 -1
  189. package/realtime/honua-server.d.ts +4 -0
  190. package/realtime/honua-server.d.ts.map +1 -1
  191. package/realtime/honua-server.js +10 -0
  192. package/realtime/resumable.js +9 -0
  193. package/realtime/types.d.ts +8 -0
  194. package/realtime/types.d.ts.map +1 -1
  195. package/runtime/errors.d.ts +1 -1
  196. package/runtime/errors.d.ts.map +1 -1
  197. package/runtime/errors.js +2 -0
  198. package/runtime/generated/map-package-schema-meta.d.ts +11 -0
  199. package/runtime/generated/map-package-schema-meta.d.ts.map +1 -0
  200. package/runtime/generated/map-package-schema-meta.js +12 -0
  201. package/runtime/generated/map-package-schema-validator.d.ts +16 -0
  202. package/runtime/generated/map-package-schema-validator.d.ts.map +1 -0
  203. package/runtime/generated/map-package-schema-validator.js +1422 -0
  204. package/runtime/index.d.ts +5 -1
  205. package/runtime/index.d.ts.map +1 -1
  206. package/runtime/index.js +6 -0
  207. package/runtime/map-package-export.d.ts +151 -0
  208. package/runtime/map-package-export.d.ts.map +1 -0
  209. package/runtime/map-package-export.js +636 -0
  210. package/runtime/map-package-validation.d.ts +21 -1
  211. package/runtime/map-package-validation.d.ts.map +1 -1
  212. package/runtime/map-package-validation.js +132 -95
  213. package/runtime/map-package.d.ts +80 -0
  214. package/runtime/map-package.d.ts.map +1 -1
  215. package/scene-workspace/cesium-adapter.d.ts +8 -8
  216. package/scene-workspace/cesium-adapter.d.ts.map +1 -1
  217. package/scene-workspace/cesium-adapter.js +31 -18
  218. package/scene-workspace/index.d.ts +2 -2
  219. package/scene-workspace/index.d.ts.map +1 -1
  220. package/scene-workspace/index.js +1 -1
  221. package/scene-workspace/maplibre-state-sync.d.ts.map +1 -1
  222. package/scene-workspace/maplibre-state-sync.js +35 -3
  223. package/scene-workspace/primitives.d.ts +37 -5
  224. package/scene-workspace/primitives.d.ts.map +1 -1
  225. package/scene-workspace/primitives.js +55 -18
  226. package/scene-workspace/source-to-cesium.js +1 -1
  227. package/scene-workspace/state-sync-port-kit.d.ts +1 -1
  228. package/scene-workspace/state-sync-port-kit.d.ts.map +1 -1
  229. package/studio/capability-manifest.d.ts +10 -0
  230. package/studio/capability-manifest.d.ts.map +1 -1
  231. package/studio/capability-manifest.js +38 -0
  232. package/studio/chart-spec.d.ts +6 -6
  233. package/studio/chart-spec.d.ts.map +1 -1
  234. package/studio/chart-spec.js +6 -6
  235. package/studio/command-adapter.d.ts +35 -0
  236. package/studio/command-adapter.d.ts.map +1 -0
  237. package/studio/command-adapter.js +36 -0
  238. package/studio/generated/studio-api.d.ts +1062 -0
  239. package/studio/generated/studio-api.d.ts.map +1 -0
  240. package/studio/generated/studio-api.js +4 -0
  241. package/studio/index.d.ts +25 -13
  242. package/studio/index.d.ts.map +1 -1
  243. package/studio/index.js +21 -10
  244. package/studio/lifecycle-client.d.ts +262 -22
  245. package/studio/lifecycle-client.d.ts.map +1 -1
  246. package/studio/lifecycle-client.js +543 -24
  247. package/studio/lifecycle-errors.d.ts +8 -2
  248. package/studio/lifecycle-errors.d.ts.map +1 -1
  249. package/studio/lifecycle-errors.js +6 -1
  250. package/studio/lifecycle-types.d.ts +237 -38
  251. package/studio/lifecycle-types.d.ts.map +1 -1
  252. package/studio/lifecycle-types.js +5 -12
  253. package/studio/publication-status.d.ts +55 -0
  254. package/studio/publication-status.d.ts.map +1 -0
  255. package/studio/publication-status.js +103 -0
  256. package/studio/types.d.ts +37 -7
  257. package/studio/types.d.ts.map +1 -1
  258. package/studio/types.js +5 -3
  259. package/studio/validate.d.ts.map +1 -1
  260. package/studio/validate.js +104 -7
  261. package/style/renderers.d.ts +6 -1
  262. package/style/renderers.d.ts.map +1 -1
  263. package/style/renderers.js +11 -6
  264. package/style/visual-variables.d.ts +43 -0
  265. package/style/visual-variables.d.ts.map +1 -0
  266. package/style/visual-variables.js +183 -0
  267. package/web-components/application-context.d.ts +186 -0
  268. package/web-components/application-context.d.ts.map +1 -0
  269. package/web-components/application-context.js +429 -0
  270. package/web-components/elements.d.ts +10 -1
  271. package/web-components/elements.d.ts.map +1 -1
  272. package/web-components/elements.js +187 -7
  273. package/web-components/export-redaction.d.ts +11 -102
  274. package/web-components/export-redaction.d.ts.map +1 -1
  275. package/web-components/export-redaction.js +7 -260
  276. package/web-components/feature-editor-offline.d.ts +32 -0
  277. package/web-components/feature-editor-offline.d.ts.map +1 -0
  278. package/web-components/feature-editor-offline.js +115 -0
  279. package/web-components/feature-editor-workflow.d.ts +85 -22
  280. package/web-components/feature-editor-workflow.d.ts.map +1 -1
  281. package/web-components/feature-editor-workflow.js +240 -0
  282. package/web-components/feature-editor.d.ts +20 -6
  283. package/web-components/feature-editor.d.ts.map +1 -1
  284. package/web-components/feature-editor.js +159 -7
  285. package/web-components/feature-inspection.d.ts +287 -0
  286. package/web-components/feature-inspection.d.ts.map +1 -0
  287. package/web-components/feature-inspection.js +1464 -0
  288. package/web-components/feature-mutation.d.ts +77 -0
  289. package/web-components/feature-mutation.d.ts.map +1 -0
  290. package/web-components/feature-mutation.js +137 -0
  291. package/web-components/feature-table-engine.d.ts +94 -4
  292. package/web-components/feature-table-engine.d.ts.map +1 -1
  293. package/web-components/feature-table-engine.js +365 -18
  294. package/web-components/feature-table-view.d.ts +35 -1
  295. package/web-components/feature-table-view.d.ts.map +1 -1
  296. package/web-components/feature-table-view.js +128 -12
  297. package/web-components/index.d.ts +16 -7
  298. package/web-components/index.d.ts.map +1 -1
  299. package/web-components/index.js +9 -2
  300. package/web-components/map-pointer-claim.d.ts +24 -0
  301. package/web-components/map-pointer-claim.d.ts.map +1 -0
  302. package/web-components/map-pointer-claim.js +40 -0
  303. package/web-components/maplibre-renderer.d.ts.map +1 -1
  304. package/web-components/maplibre-renderer.js +5 -1
  305. package/web-components/measurement-units.d.ts +88 -0
  306. package/web-components/measurement-units.d.ts.map +1 -0
  307. package/web-components/measurement-units.js +278 -0
  308. package/web-components/measurement.d.ts +75 -4
  309. package/web-components/measurement.d.ts.map +1 -1
  310. package/web-components/measurement.js +227 -28
  311. package/web-components/types.d.ts +58 -3
  312. package/web-components/types.d.ts.map +1 -1
  313. package/webmap/convert-layer.d.ts +2 -1
  314. package/webmap/convert-layer.d.ts.map +1 -1
  315. package/webmap/convert-layer.js +20 -3
  316. package/webmap/convert-renderer.d.ts +10 -3
  317. package/webmap/convert-renderer.d.ts.map +1 -1
  318. package/webmap/convert-renderer.js +190 -36
  319. package/webmap/index.d.ts +1 -0
  320. package/webmap/index.d.ts.map +1 -1
  321. package/webmap/parse.d.ts +2 -1
  322. package/webmap/parse.d.ts.map +1 -1
  323. package/webmap/parse.js +1 -1
  324. package/gen/honua/v1/feature_service_pb.d.ts +0 -918
  325. package/gen/honua/v1/feature_service_pb.d.ts.map +0 -1
  326. package/gen/honua/v1/feature_service_pb.js +0 -375
@@ -17,30 +17,38 @@
17
17
  * `./lifecycle-errors.js` for the RFC 7807 problem-detail taxonomy and the
18
18
  * `generation-conflict` discriminant.
19
19
  *
20
- * ## Enumeration gap
20
+ * ## Publication proposals
21
21
  *
22
- * The lifecycle API doc is explicit that **there is no endpoint to enumerate
23
- * drafts or content items** — only single-resource reads
24
- * (`GET /package-drafts/{draftId}`, `GET /content-items/{itemId}/versions`,
25
- * `GET /content-items/{itemId}/versions/{versionId}`) and the
26
- * family-discovery list (`GET /package-families`, which already returns every
27
- * family in one response and does need no list method of its own).
28
- * `honua-server#3003` tracks adding a draft/content-item enumeration
29
- * endpoint. Until it ships:
22
+ * {@link HonuaStudioPublicationRequestsClient} submits an already-saved
23
+ * immutable version for publication, reads the resulting proposal, and walks
24
+ * it to a final state with a bounded, cancellable
25
+ * {@link HonuaStudioPublicationRequestsClient.poll}. Only `Active` yields a
26
+ * final publication URL; `Rejected` and `Failed` are terminal without one,
27
+ * and a status this release does not recognize is treated as neither.
28
+ * Approval is deliberately **not** part of this surface — see that class's
29
+ * doc comment.
30
30
  *
31
- * - `contentVersions.list(itemId)` already covers version enumeration *for a
32
- * known item* — the gap is discovering `itemId`/`draftId` values in the
33
- * first place, not paging a known collection.
34
- * - `.raw()` reaches any endpoint honua-server adds before this client grows
35
- * a dedicated method for it, without a breaking-change window.
36
- * - When the enumeration endpoint ships, it is additive: a new
37
- * `drafts.list(options)` (or a new resource group) can be added without
38
- * touching any existing method signature.
31
+ * ## Enumeration
32
+ *
33
+ * `contentItems.list()` (`GET /content-items`) and `drafts.list()`
34
+ * (`GET /package-drafts`) are the content-browser entry points added by
35
+ * `honua-server#3003`: filters (`family`, `workspaceId`, `ownerId`, `search`, plus
36
+ * `state` for content items), opaque keyset-cursor pagination ordered by
37
+ * `updatedAt` then row id descending, and — for content items — a joined
38
+ * Content Publication Registry lifecycle badge per row, so a lifecycle badge
39
+ * costs no extra request. Both resource groups also expose a bounded
40
+ * `listAll()` async generator and a `collect()` convenience that walk the
41
+ * cursor for you; neither can loop forever (see
42
+ * {@link HONUA_STUDIO_LIST_MAX_PAGES}).
43
+ *
44
+ * Cursors are **opaque**. Pass a `nextCursor` back verbatim; never build,
45
+ * parse, or persist-and-mutate one.
39
46
  *
40
47
  * @module
41
48
  */
42
- import { isHonuaError } from "../core/errors.js";
49
+ import { HonuaTimeoutError, isHonuaError } from "../core/errors.js";
43
50
  import { HonuaStudioError, classifyStudioProblemStatus } from "./lifecycle-errors.js";
51
+ import { isStudioPublicationActive, isStudioPublicationTerminal, normalizeStudioPublicationStatus, studioPublicationUrl, } from "./publication-status.js";
44
52
  /** Default base path for the Studio package lifecycle API. */
45
53
  export const HONUA_STUDIO_LIFECYCLE_BASE_PATH = "/api/v1/studio";
46
54
  /** Construct a {@link HonuaStudioLifecycleClient} from an existing {@link HonuaClient}. */
@@ -57,6 +65,7 @@ export class HonuaStudioLifecycleClient {
57
65
  #basePath;
58
66
  packageFamilies;
59
67
  drafts;
68
+ contentItems;
60
69
  contentVersions;
61
70
  publicationRequests;
62
71
  rollbackRequests;
@@ -65,6 +74,7 @@ export class HonuaStudioLifecycleClient {
65
74
  this.#basePath = normalizeBasePath(options.basePath ?? HONUA_STUDIO_LIFECYCLE_BASE_PATH);
66
75
  this.packageFamilies = new HonuaStudioPackageFamiliesClient(this);
67
76
  this.drafts = new HonuaStudioDraftsClient(this);
77
+ this.contentItems = new HonuaStudioContentItemsClient(this);
68
78
  this.contentVersions = new HonuaStudioContentVersionsClient(this);
69
79
  this.publicationRequests = new HonuaStudioPublicationRequestsClient(this);
70
80
  this.rollbackRequests = new HonuaStudioRollbackRequestsClient(this);
@@ -72,7 +82,7 @@ export class HonuaStudioLifecycleClient {
72
82
  get basePath() {
73
83
  return this.#basePath;
74
84
  }
75
- /** Escape hatch for endpoints without a dedicated method yet (see the module doc's "Enumeration gap"). */
85
+ /** Escape hatch for any endpoint that does not have a dedicated method on this client yet. */
76
86
  raw(request) {
77
87
  return this.request(request.method ?? "GET", request.path, request.body, {
78
88
  signal: request.signal,
@@ -122,6 +132,35 @@ export class HonuaStudioDraftsClient {
122
132
  create(request, options = {}) {
123
133
  return this.#lifecycle.request("POST", "/package-drafts", request, options);
124
134
  }
135
+ /**
136
+ * `GET /package-drafts` — one page of mutable drafts matching `filters`
137
+ * (`family`, `workspaceId`, `ownerId`, `search`), ordered by `updatedAt` then
138
+ * `draftId` descending.
139
+ *
140
+ * Rows are full {@link StudioPackageDraft} objects. Pass the response's
141
+ * `nextCursor` back as `filters.cursor` for the next page; its absence (or
142
+ * `null`) means this was the last one. Prefer {@link listAll} or
143
+ * {@link collect} over hand-rolling that loop.
144
+ */
145
+ // `async` so the client-side `limit` guard in `withStudioListQuery` surfaces
146
+ // as a rejected promise, exactly like every server-side failure on this
147
+ // client, rather than as a synchronous throw callers would have to catch
148
+ // separately.
149
+ async list(filters = {}, options = {}) {
150
+ return await this.#lifecycle.request("GET", withStudioListQuery("/package-drafts", filters), undefined, options);
151
+ }
152
+ /**
153
+ * Walk `GET /package-drafts` to completion, yielding each page verbatim.
154
+ * Bounded by `options.maxPages` — see {@link HonuaStudioContentItemsClient.listAll}
155
+ * for the shared pagination rules.
156
+ */
157
+ listAll(filters = {}, options = {}) {
158
+ return paginateStudioList((pageFilters, pageOptions) => this.list(pageFilters, pageOptions), filters, options);
159
+ }
160
+ /** Accumulate every draft page into one {@link StudioListCollection}. */
161
+ collect(filters = {}, options = {}) {
162
+ return collectStudioList(this.listAll(filters, options));
163
+ }
125
164
  /** `GET /package-drafts/{draftId}` — retrieve a mutable draft. */
126
165
  get(draftId, options = {}) {
127
166
  return this.#lifecycle.request("GET", `/package-drafts/${encodeURIComponent(draftId)}`, undefined, options);
@@ -157,6 +196,178 @@ export class HonuaStudioDraftsClient {
157
196
  return this.#lifecycle.request("POST", `/package-drafts/${encodeURIComponent(draftId)}/content-versions`, undefined, options);
158
197
  }
159
198
  }
199
+ /** Page size `GET /content-items` and `GET /package-drafts` use when `limit` is omitted. */
200
+ export const HONUA_STUDIO_LIST_DEFAULT_LIMIT = 25;
201
+ /**
202
+ * Largest `limit` the Studio list endpoints accept. The server clamps anything
203
+ * larger down to this value rather than erroring, which would silently hand a
204
+ * caller fewer rows than they asked for, so this client rejects an oversized
205
+ * `limit` up front instead.
206
+ */
207
+ export const HONUA_STUDIO_LIST_MAX_LIMIT = 1_000;
208
+ /**
209
+ * Default hard bound on how many pages {@link HonuaStudioContentItemsClient.listAll}
210
+ * / {@link HonuaStudioDraftsClient.listAll} will fetch. There is no
211
+ * "walk forever" mode, and no option that produces one.
212
+ */
213
+ export const HONUA_STUDIO_LIST_MAX_PAGES = 1_000;
214
+ /**
215
+ * True when a page is the last one — the server sends `nextCursor: null` on
216
+ * the final page, and its source-generated JSON context omits `null`
217
+ * properties, so "absent" and "null" mean the same thing. An empty-string
218
+ * cursor is treated as exhausted too rather than replayed as a filter value.
219
+ */
220
+ export function isStudioListExhausted(page) {
221
+ return typeof page.nextCursor !== "string" || page.nextCursor.length === 0;
222
+ }
223
+ /** `/content-items` — enumerate Studio content items with joined publication badges. */
224
+ export class HonuaStudioContentItemsClient {
225
+ #lifecycle;
226
+ constructor(lifecycle) {
227
+ this.#lifecycle = lifecycle;
228
+ }
229
+ /**
230
+ * `GET /content-items` — one page of content items matching `filters`
231
+ * (`family`, `workspaceId`, `ownerId`, `state`, `search`), ordered by `updatedAt`
232
+ * then `itemId` descending so pages stay stable while other rows are
233
+ * concurrently created or updated.
234
+ *
235
+ * Each row is a summary projection — no envelope — carrying the derived
236
+ * `state` (`draft`/`current`/`published`) and, when the Content Publication
237
+ * Registry has a publication whose `sourceContentId` is this `itemId`, a
238
+ * joined `publication` badge with the route's *own* lifecycle. That join is
239
+ * batched server-side, so a badge costs no extra request.
240
+ *
241
+ * A non-admin caller under `Studio:EndUserAuthorization:Enabled` always gets
242
+ * their own content regardless of the `owner` filter they pass, and a caller
243
+ * whose id cannot be resolved at all is refused with `403` rather than shown
244
+ * an unscoped list. An unknown `family` or `state` value is `400`.
245
+ */
246
+ // `async` so the client-side `limit` guard in `withStudioListQuery` surfaces
247
+ // as a rejected promise, exactly like every server-side failure on this
248
+ // client, rather than as a synchronous throw callers would have to catch
249
+ // separately.
250
+ async list(filters = {}, options = {}) {
251
+ return await this.#lifecycle.request("GET", withStudioListQuery("/content-items", filters), undefined, options);
252
+ }
253
+ /**
254
+ * Walk `GET /content-items` to completion, yielding each page verbatim.
255
+ *
256
+ * - **Bounded by construction.** At most `options.maxPages` pages
257
+ * ({@link HONUA_STUDIO_LIST_MAX_PAGES} by default) are fetched. Reaching
258
+ * the cap ends the walk; the last yielded page still carries the
259
+ * `nextCursor` to resume from.
260
+ * - **Cursors stay opaque.** Each request replays the previous page's
261
+ * `nextCursor` verbatim; `filters.cursor` seeds the walk, so a walk can be
262
+ * resumed exactly where a previous one stopped.
263
+ * - **Cancellable.** `options.signal` aborts the in-flight page request and
264
+ * ends the iteration.
265
+ * - **A stuck server is not an infinite loop.** A page that repeats the
266
+ * cursor it was fetched with throws {@link HonuaStudioError} rather than
267
+ * paging forever.
268
+ * - **Standard error handling.** Every non-2xx surfaces as
269
+ * {@link HonuaStudioError} with its RFC 7807 problem details.
270
+ */
271
+ listAll(filters = {}, options = {}) {
272
+ return paginateStudioList((pageFilters, pageOptions) => this.list(pageFilters, pageOptions), filters, options);
273
+ }
274
+ /** Accumulate every content-item page into one {@link StudioListCollection}. */
275
+ collect(filters = {}, options = {}) {
276
+ return collectStudioList(this.listAll(filters, options));
277
+ }
278
+ }
279
+ /**
280
+ * The shared bounded cursor walk behind every `listAll`. Generic over the row
281
+ * type so content items and drafts get identical pagination semantics from one
282
+ * implementation.
283
+ */
284
+ async function* paginateStudioList(fetchPage, filters, options) {
285
+ const maxPages = normalizePageBound(options.maxPages);
286
+ const requestOptions = { signal: options.signal, headers: options.headers };
287
+ let cursor = filters.cursor;
288
+ for (let page = 0; page < maxPages; page += 1) {
289
+ throwIfAborted(options.signal);
290
+ const current = await fetchPage(cursor === undefined ? filters : { ...filters, cursor }, requestOptions);
291
+ yield current;
292
+ if (isStudioListExhausted(current))
293
+ return;
294
+ const next = current.nextCursor;
295
+ // A server that hands back the cursor it was just given would otherwise
296
+ // spin until `maxPages`, re-fetching the same page every time.
297
+ if (next === cursor) {
298
+ throw new HonuaStudioError("internal", 500, `Studio list pagination stalled: the server returned the same cursor it was queried with (${next}).`);
299
+ }
300
+ cursor = next;
301
+ }
302
+ }
303
+ /** Drain a bounded page walk into one {@link StudioListCollection}. */
304
+ async function collectStudioList(pages) {
305
+ const items = [];
306
+ let pageCount = 0;
307
+ let total = 0;
308
+ let last;
309
+ for await (const page of pages) {
310
+ pageCount += 1;
311
+ total = page.total;
312
+ last = page;
313
+ items.push(...page.items);
314
+ }
315
+ // The generator stops either because the server said "no more" or because
316
+ // the page cap fired; only the second leaves a usable resume cursor behind.
317
+ const truncated = last !== undefined && !isStudioListExhausted(last);
318
+ return {
319
+ items,
320
+ pages: pageCount,
321
+ total,
322
+ nextCursor: truncated ? (last?.nextCursor ?? undefined) : undefined,
323
+ truncated,
324
+ };
325
+ }
326
+ function normalizePageBound(value) {
327
+ if (value === undefined)
328
+ return HONUA_STUDIO_LIST_MAX_PAGES;
329
+ if (!Number.isInteger(value) || value < 1) {
330
+ throw new TypeError(`maxPages must be an integer >= 1, received ${String(value)}.`);
331
+ }
332
+ return value;
333
+ }
334
+ /**
335
+ * Serialize the shared Studio list filters onto `path`.
336
+ *
337
+ * Blank strings are dropped rather than sent as an empty filter the server
338
+ * would trim to `null` anyway.
339
+ */
340
+ function withStudioListQuery(path, filters) {
341
+ const params = new URLSearchParams();
342
+ if (filters.family?.trim())
343
+ params.set("family", filters.family);
344
+ if (filters.state?.trim())
345
+ params.set("state", filters.state);
346
+ if (filters.workspaceId?.trim())
347
+ params.set("workspaceId", filters.workspaceId);
348
+ if (filters.ownerId?.trim())
349
+ params.set("ownerId", filters.ownerId);
350
+ if (filters.search?.trim())
351
+ params.set("search", filters.search);
352
+ if (filters.cursor)
353
+ params.set("cursor", filters.cursor);
354
+ if (filters.limit !== undefined)
355
+ params.set("limit", String(normalizeListLimit(filters.limit)));
356
+ const query = params.toString();
357
+ return query ? `${path}?${query}` : path;
358
+ }
359
+ /**
360
+ * Reject a `limit` the server would not honor as asked. Below `1` it would be
361
+ * replaced with the default and above {@link HONUA_STUDIO_LIST_MAX_LIMIT} it
362
+ * would be silently clamped — both hand back a page size the caller did not
363
+ * request, which is worse than a thrown programming error.
364
+ */
365
+ function normalizeListLimit(limit) {
366
+ if (!Number.isInteger(limit) || limit < 1 || limit > HONUA_STUDIO_LIST_MAX_LIMIT) {
367
+ throw new TypeError(`limit must be an integer between 1 and ${HONUA_STUDIO_LIST_MAX_LIMIT}, received ${String(limit)}.`);
368
+ }
369
+ return limit;
370
+ }
160
371
  /** `/content-items/{itemId}/versions*` and `/version-comparisons` — immutable content versions. */
161
372
  export class HonuaStudioContentVersionsClient {
162
373
  #lifecycle;
@@ -187,7 +398,58 @@ export class HonuaStudioContentVersionsClient {
187
398
  return this.#lifecycle.request("POST", `/content-items/${encodeURIComponent(itemId)}/versions/${encodeURIComponent(versionId)}/reopen`, request, options);
188
399
  }
189
400
  }
190
- /** `/content-items/{itemId}/versions/{versionId}/publish-requests`. */
401
+ /** Default delay between {@link HonuaStudioPublicationRequestsClient.poll} attempts. */
402
+ export const HONUA_STUDIO_PUBLICATION_POLL_INTERVAL_MS = 1_000;
403
+ /** Default attempt bound for {@link HonuaStudioPublicationRequestsClient.poll}. */
404
+ export const HONUA_STUDIO_PUBLICATION_POLL_MAX_ATTEMPTS = 30;
405
+ /**
406
+ * Body keys a publication *submission* may never carry. Approval is a
407
+ * separate principal acting through the canonical Admin API/CLI, so the SDK
408
+ * refuses to serialize a submission that tries to assert its own decision,
409
+ * override policy, or seed a terminal status — see
410
+ * {@link HonuaStudioPublicationRequestsClient} for why this is enforced
411
+ * client-side as well as server-side.
412
+ *
413
+ * Matching is on the exact key (case-insensitively); an additive server field
414
+ * that merely *mentions* approval (`approvalPolicyId`, say) is unaffected.
415
+ */
416
+ const REJECTED_SUBMISSION_KEYS = new Set([
417
+ "status",
418
+ "state",
419
+ "approve",
420
+ "approved",
421
+ "approval",
422
+ "approvedat",
423
+ "approvedby",
424
+ "autoapprove",
425
+ "selfapprove",
426
+ "skipapproval",
427
+ "bypassapproval",
428
+ "bypasspolicy",
429
+ "policyoverride",
430
+ "overridepolicy",
431
+ "force",
432
+ ]);
433
+ /**
434
+ * `/content-items/{itemId}/versions/{versionId}/publish-requests` — submit an
435
+ * already-saved immutable version for publication and walk the resulting
436
+ * proposal to a final state.
437
+ *
438
+ * ## Approval is a different principal
439
+ *
440
+ * This client can **create**, **get** and **poll** a publication proposal. It
441
+ * deliberately exposes no approve, authorize, force, or policy-override
442
+ * capability, and {@link create} refuses to serialize a submission body that
443
+ * tries to smuggle one (see {@link REJECTED_SUBMISSION_KEYS}). A proposer
444
+ * therefore cannot approve their own publication, or bypass policy, through
445
+ * any method on this class — approval happens through the canonical Admin
446
+ * API/CLI acting as a separate principal, and the server enforces the same
447
+ * rule with a `403` (`code: "forbidden"`) if a proposer tries anyway.
448
+ *
449
+ * {@link HonuaStudioLifecycleClient.raw} is a generic HTTP escape hatch with
450
+ * no Studio approval semantics of its own; it reaches whatever the server
451
+ * exposes and is subject to exactly the same server-side authorization.
452
+ */
191
453
  export class HonuaStudioPublicationRequestsClient {
192
454
  #lifecycle;
193
455
  constructor(lifecycle) {
@@ -195,12 +457,269 @@ export class HonuaStudioPublicationRequestsClient {
195
457
  }
196
458
  /**
197
459
  * `POST /content-items/{itemId}/versions/{versionId}/publish-requests` —
198
- * persist a publication request and move the published pointer when
199
- * validation permits.
460
+ * submit an already-saved immutable version for publication.
461
+ *
462
+ * The submission carries only the identity of what is being published
463
+ * (`itemId`, `versionId`, the optional `contentHash` pin), the requested
464
+ * route/visibility `intent`, and an optional `idempotencyKey` so a replayed
465
+ * submit resolves to the same proposal. It never carries a decision:
466
+ * an approval, policy-override, or `status` key in `request` throws a
467
+ * {@link HonuaStudioError} with `code: "validation"` **before** anything is
468
+ * sent.
469
+ *
470
+ * The response carries the joined `operationInstanceId`, `proposalId`,
471
+ * `proposalUri`, `auditId` and `correlationId` identifiers verbatim.
472
+ */
473
+ // `async` so the client-side approval guard below surfaces as a rejected
474
+ // promise, exactly like every server-side failure on this client, rather
475
+ // than as a synchronous throw callers would have to catch separately.
476
+ async create(itemId, versionId, request = {}, options = {}) {
477
+ assertNoApprovalFields(request);
478
+ return this.#lifecycle.request("POST", `${this.#path(itemId, versionId)}/publish-requests`, serializeStudioPublicationRequestInput(request), options);
479
+ }
480
+ /**
481
+ * `GET /content-items/{itemId}/versions/{versionId}/publish-requests/{requestId}`
482
+ * — read one publication proposal's current status and joined identifiers.
483
+ *
484
+ * A proposal that does not exist, or is not reachable from this
485
+ * `itemId`/`versionId` pair, throws `code: "not-found"`; one outside the
486
+ * caller's owner/tenant scope throws `code: "forbidden"`.
487
+ */
488
+ get(itemId, versionId, requestId, options = {}) {
489
+ return this.#lifecycle.request("GET", `${this.#path(itemId, versionId)}/publish-requests/${encodeURIComponent(requestId)}`, undefined, options);
490
+ }
491
+ /**
492
+ * Poll one publication proposal until it reaches a terminal state or a
493
+ * bound is reached, whichever comes first.
494
+ *
495
+ * - **Bounded by construction.** Every poll stops after at most
496
+ * `maxAttempts` `GET`s (and `timeoutMs`, when given). There is no
497
+ * unbounded mode.
498
+ * - **A final publication URL only at `Active`.** `Rejected` and `Failed`
499
+ * end the poll with `terminal: true`, `active: false` and no URL.
500
+ * - **An unrecognized status is never final.** A state this release does
501
+ * not know keeps the poll waiting until a bound is hit, and comes back as
502
+ * `state: undefined, terminal: false, active: false`.
503
+ * - **Cancellable.** `options.signal` aborts the in-flight request and the
504
+ * wait between attempts; the returned promise rejects with the signal's
505
+ * abort reason — including when the abort lands mid-request, where the
506
+ * underlying `HonuaClient` would otherwise normalize the rejection to a
507
+ * generic SDK abort error.
508
+ * - **`timeoutMs` is a real wall-clock bound.** It aborts an in-flight `GET`
509
+ * as well as being checked between attempts, and the inter-attempt wait is
510
+ * clamped to the time left, so a hung response cannot leave the poll
511
+ * pending past the deadline even when the underlying `HonuaClient` has no
512
+ * `timeoutMs` of its own. A deadline reached with at least one observation
513
+ * returns `exhausted: "timeout"`; a deadline that expires before the very
514
+ * first response arrives has nothing to report and throws
515
+ * {@link HonuaTimeoutError}.
516
+ * - **Standard error handling.** Every non-2xx surfaces as
517
+ * {@link HonuaStudioError} with its RFC 7807 problem details, exactly as
518
+ * for every other method on this client.
519
+ */
520
+ async poll(itemId, versionId, requestId, options = {}) {
521
+ const maxAttempts = normalizePollBound(options.maxAttempts, HONUA_STUDIO_PUBLICATION_POLL_MAX_ATTEMPTS);
522
+ const intervalMs = normalizeInterval(options.intervalMs);
523
+ const timeoutMs = options.timeoutMs === undefined ? undefined : normalizeInterval(options.timeoutMs);
524
+ // The wall-clock bound owns its own abort signal rather than being a
525
+ // between-attempts `Date.now()` check alone: `HonuaClient` carries no
526
+ // request timeout unless the consumer configured one, so a `GET` that is
527
+ // accepted and never answered would otherwise hold the poll open forever
528
+ // in spite of a finite `timeoutMs`.
529
+ const deadline = createPollDeadline(timeoutMs, options.signal);
530
+ const requestOptions = { signal: deadline.signal, headers: options.headers };
531
+ let observed;
532
+ try {
533
+ // Bounded by construction: the loop counter is the only thing that drives
534
+ // it, `maxAttempts` is validated to be a finite integer >= 1, and every
535
+ // path out of the body either returns or advances the counter.
536
+ for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
537
+ throwIfAborted(options.signal);
538
+ let request;
539
+ try {
540
+ request = await this.get(itemId, versionId, requestId, requestOptions);
541
+ }
542
+ catch (error) {
543
+ // The caller's cancellation is reported with the caller's own
544
+ // `reason`. `HonuaClient.pipelineFetch` normalizes an in-flight fetch
545
+ // abort into a generic SDK abort error, which would otherwise lose it.
546
+ throwIfAborted(options.signal);
547
+ if (deadline.expired())
548
+ return this.#timedOut(observed, attempt - 1, timeoutMs);
549
+ throw error;
550
+ }
551
+ const state = normalizeStudioPublicationStatus(request.status);
552
+ observed = { request, state };
553
+ options.onStatus?.(request, state);
554
+ if (isStudioPublicationTerminal(request.status)) {
555
+ return toPollOutcome(request, state, attempt, undefined);
556
+ }
557
+ if (attempt >= maxAttempts) {
558
+ return toPollOutcome(request, state, attempt, "max-attempts");
559
+ }
560
+ const remainingMs = deadline.remainingMs();
561
+ if (remainingMs !== undefined && remainingMs <= 0) {
562
+ return toPollOutcome(request, state, attempt, "timeout");
563
+ }
564
+ // Never sleep past the deadline: a `intervalMs` larger than the time
565
+ // left would overshoot the documented bound by a whole interval.
566
+ const waitMs = remainingMs === undefined ? intervalMs : Math.min(intervalMs, remainingMs);
567
+ // When the wait is the whole remaining time, waking from it *is* the
568
+ // deadline being reached, and the loop must stop without consulting the
569
+ // clock again. Re-deriving `expiresAt - Date.now()` here made that a
570
+ // race: a timer may fire a tick before `Date.now()` passes its target,
571
+ // so the remainder read back as 1 rather than 0, the `<= 0` guard did
572
+ // not fire, and the loop issued one more GET *after* the documented
573
+ // bound -- the exact overshoot this clamp exists to prevent.
574
+ const waitsOutDeadline = remainingMs !== undefined && waitMs >= remainingMs;
575
+ await delay(waitMs, options.signal);
576
+ if (waitsOutDeadline) {
577
+ return toPollOutcome(request, state, attempt, "timeout");
578
+ }
579
+ const leftAfterWait = deadline.remainingMs();
580
+ if (leftAfterWait !== undefined && leftAfterWait <= 0) {
581
+ return toPollOutcome(request, state, attempt, "timeout");
582
+ }
583
+ }
584
+ }
585
+ finally {
586
+ deadline.dispose();
587
+ }
588
+ // Unreachable while `maxAttempts >= 1`; kept so the bound is enforced by
589
+ // the type system rather than by a comment.
590
+ throw new TypeError(`maxAttempts must be an integer >= 1, received ${String(maxAttempts)}.`);
591
+ }
592
+ /**
593
+ * The wall-clock bound fired. With at least one observation the poll reports
594
+ * it the documented way (`exhausted: "timeout"`); with none there is no
595
+ * proposal to report, so the deadline surfaces as the SDK's own
596
+ * {@link HonuaTimeoutError} rather than an invented empty outcome.
200
597
  */
201
- create(itemId, versionId, request = {}, options = {}) {
202
- return this.#lifecycle.request("POST", `/content-items/${encodeURIComponent(itemId)}/versions/${encodeURIComponent(versionId)}/publish-requests`, request, options);
598
+ #timedOut(observed, attempts, timeoutMs) {
599
+ if (!observed)
600
+ throw new HonuaTimeoutError(timeoutMs ?? 0);
601
+ return toPollOutcome(observed.request, observed.state, attempts, "timeout");
602
+ }
603
+ #path(itemId, versionId) {
604
+ return `/content-items/${encodeURIComponent(itemId)}/versions/${encodeURIComponent(versionId)}`;
605
+ }
606
+ }
607
+ function createPollDeadline(timeoutMs, callerSignal) {
608
+ if (timeoutMs === undefined) {
609
+ return {
610
+ signal: callerSignal,
611
+ expired: () => false,
612
+ remainingMs: () => undefined,
613
+ dispose: () => undefined,
614
+ };
203
615
  }
616
+ const controller = new AbortController();
617
+ const expiresAt = Date.now() + timeoutMs;
618
+ let expired = false;
619
+ const timer = setTimeout(() => {
620
+ expired = true;
621
+ controller.abort();
622
+ }, timeoutMs);
623
+ const onCallerAbort = () => {
624
+ controller.abort(callerSignal ? abortReason(callerSignal) : undefined);
625
+ };
626
+ if (callerSignal?.aborted)
627
+ onCallerAbort();
628
+ else
629
+ callerSignal?.addEventListener("abort", onCallerAbort, { once: true });
630
+ return {
631
+ signal: controller.signal,
632
+ expired: () => expired,
633
+ remainingMs: () => expiresAt - Date.now(),
634
+ dispose: () => {
635
+ clearTimeout(timer);
636
+ callerSignal?.removeEventListener("abort", onCallerAbort);
637
+ },
638
+ };
639
+ }
640
+ function toPollOutcome(request, state, attempts, exhausted) {
641
+ return {
642
+ request,
643
+ state,
644
+ terminal: isStudioPublicationTerminal(request.status),
645
+ active: isStudioPublicationActive(request.status),
646
+ publicationUrl: studioPublicationUrl(request),
647
+ attempts,
648
+ exhausted,
649
+ };
650
+ }
651
+ /**
652
+ * Throw a client-side `validation` {@link HonuaStudioError} — nothing is sent
653
+ * — when a submission body carries an approval or policy-override key.
654
+ */
655
+ function assertNoApprovalFields(request) {
656
+ const offending = Object.keys(request).filter((key) => REJECTED_SUBMISSION_KEYS.has(key.toLowerCase()));
657
+ if (offending.length === 0)
658
+ return;
659
+ const rule = [
660
+ "the proposer cannot approve their own publication or override policy.",
661
+ "Approval is a separate principal acting through the Admin API/CLI.",
662
+ ].join(" ");
663
+ throw new HonuaStudioError("validation", 400, `A publication submission must not carry ${offending.join(", ")}: ${rule}`);
664
+ }
665
+ /**
666
+ * Keep the pre-contract acknowledgement spelling source-compatible while
667
+ * sending only the canonical property understood by the Studio API. An
668
+ * explicitly supplied canonical value always wins.
669
+ */
670
+ function serializeStudioPublicationRequestInput(request) {
671
+ if (!Object.hasOwn(request, "warningAcknowledgment"))
672
+ return request;
673
+ const { warningAcknowledgment, ...canonicalRequest } = request;
674
+ if (warningAcknowledgment === undefined || Object.hasOwn(request, "warningAcknowledgement")) {
675
+ return canonicalRequest;
676
+ }
677
+ return { ...canonicalRequest, warningAcknowledgement: warningAcknowledgment };
678
+ }
679
+ function normalizePollBound(value, fallback) {
680
+ if (value === undefined)
681
+ return fallback;
682
+ if (!Number.isInteger(value) || value < 1) {
683
+ throw new TypeError(`maxAttempts must be an integer >= 1, received ${String(value)}.`);
684
+ }
685
+ return value;
686
+ }
687
+ function normalizeInterval(value) {
688
+ if (value === undefined)
689
+ return HONUA_STUDIO_PUBLICATION_POLL_INTERVAL_MS;
690
+ if (!Number.isFinite(value) || value < 0) {
691
+ throw new TypeError(`Poll interval and timeout must be finite and >= 0, received ${String(value)}.`);
692
+ }
693
+ return value;
694
+ }
695
+ function throwIfAborted(signal) {
696
+ if (signal?.aborted)
697
+ throw abortReason(signal);
698
+ }
699
+ function abortReason(signal) {
700
+ return signal.reason ?? new DOMException("The operation was aborted.", "AbortError");
701
+ }
702
+ /** `setTimeout` that settles early — by rejecting with the abort reason — when `signal` aborts. */
703
+ function delay(ms, signal) {
704
+ if (ms <= 0) {
705
+ throwIfAborted(signal);
706
+ return Promise.resolve();
707
+ }
708
+ return new Promise((resolve, reject) => {
709
+ if (signal?.aborted) {
710
+ reject(abortReason(signal));
711
+ return;
712
+ }
713
+ const timer = setTimeout(() => {
714
+ signal?.removeEventListener("abort", onAbort);
715
+ resolve();
716
+ }, ms);
717
+ const onAbort = () => {
718
+ clearTimeout(timer);
719
+ reject(abortReason(signal));
720
+ };
721
+ signal?.addEventListener("abort", onAbort, { once: true });
722
+ });
204
723
  }
205
724
  /** `/content-items/{itemId}/rollback-requests`. */
206
725
  export class HonuaStudioRollbackRequestsClient {
@@ -3,7 +3,8 @@
3
3
  *
4
4
  * Every non-2xx response from the Studio package lifecycle API
5
5
  * (`/api/v{version}/studio`) is either a handler-generated client error
6
- * (`400`/`404`/`409`) or a caught internal failure (`500`), and both use
6
+ * (`400`/`401`/`403`/`404`/`409`) or a caught internal failure (`500`), and
7
+ * both use
7
8
  * RFC 7807 problem details with `type: "https://honua.io/problems/studio"`
8
9
  * (see `docs/internal/admin-api/studio-package-lifecycle.md` in
9
10
  * `honua-server`). {@link HonuaStudioError} normalizes both into one typed
@@ -30,6 +31,11 @@
30
31
  * status code of the failed response:
31
32
  *
32
33
  * - `validation` — `400`, request or envelope failed validation.
34
+ * - `unauthorized` — `401`, the request carried no usable principal.
35
+ * - `forbidden` — `403`, the principal is authenticated but is not permitted
36
+ * to perform the operation — the owner/tenant scope ceiling, and the
37
+ * separate-approver rule that stops a proposer from approving their own
38
+ * publication (see {@link HonuaStudioPublicationRequestsClient}).
33
39
  * - `not-found` — `404`, the draft/version/content item does not exist (or,
34
40
  * per the API doc, is not reachable from the given `itemId`/`versionId`
35
41
  * pair — cross-item ids are treated as not found).
@@ -41,7 +47,7 @@
41
47
  *
42
48
  * @experimental
43
49
  */
44
- export type HonuaStudioProblemKind = "validation" | "not-found" | "generation-conflict" | "internal" | "unknown";
50
+ export type HonuaStudioProblemKind = "validation" | "unauthorized" | "forbidden" | "not-found" | "generation-conflict" | "internal" | "unknown";
45
51
  /**
46
52
  * RFC 7807 problem-details body returned by the Studio package lifecycle API.
47
53
  * `type` is `"https://honua.io/problems/studio"` on every documented error
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-errors.d.ts","sourceRoot":"","sources":["../../../src/studio/lifecycle-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,sBAAsB,GAAG,YAAY,GAAG,WAAW,GAAG,qBAAqB,GAAG,UAAU,GAAG,SAAS,CAAC;AAEjH;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;IACpD,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CACnC;AAED,8FAA8F;AAC9F,eAAO,MAAM,yBAAyB,EAAG,kCAA2C,CAAC;AAErF;;;;;;;GAOG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,SAAgB,IAAI,EAAE,sBAAsB,CAAC;IAC7C,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,OAAO,EAAE,yBAAyB,GAAG,SAAS,CAAC;gBAG7D,IAAI,EAAE,sBAAsB,EAC5B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,yBAAyB,EACnC,OAAO,GAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;CAQ7C;AAED,+CAA+C;AAC/C,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAE5E;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAEzF;AAED,oFAAoF;AACpF,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,sBAAsB,CAatF"}
1
+ {"version":3,"file":"lifecycle-errors.d.ts","sourceRoot":"","sources":["../../../src/studio/lifecycle-errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,sBAAsB,GAC9B,YAAY,GACZ,cAAc,GACd,WAAW,GACX,WAAW,GACX,qBAAqB,GACrB,UAAU,GACV,SAAS,CAAC;AAEd;;;;;;GAMG;AACH,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;IACpD,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CACnC;AAED,8FAA8F;AAC9F,eAAO,MAAM,yBAAyB,EAAG,kCAA2C,CAAC;AAErF;;;;;;;GAOG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,SAAgB,IAAI,EAAE,sBAAsB,CAAC;IAC7C,SAAgB,UAAU,EAAE,MAAM,CAAC;IACnC,SAAgB,OAAO,EAAE,yBAAyB,GAAG,SAAS,CAAC;gBAG7D,IAAI,EAAE,sBAAsB,EAC5B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,yBAAyB,EACnC,OAAO,GAAE;QAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAO;CAQ7C;AAED,+CAA+C;AAC/C,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAE5E;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,gBAAgB,CAEzF;AAED,oFAAoF;AACpF,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,GAAG,sBAAsB,CAiBtF"}