type: reference title: "Studio Package Contracts (@honua/sdk-js/studio)" description: "One browser-safe import path for the Studio package families, the unified validation and preview envelopes, the capability manifest, and the publish, share and embed contracts." resource: "https://www.npmjs.com/package/@honua/sdk-js"

Studio Package Contracts (@honua/sdk-js/studio)

Status: experimental.

@honua/sdk-js/studio is the single, browser-safe import path for the Studio package families, their unified validation/preview envelopes, the capability manifest, and the publish/share/embed contracts. It exists so Console browser interop, MCP, QGIS, generated apps, and embeds consume one set of server-owned contract projections instead of forking their own package schemas.

import {
  HONUA_QUERY_PACKAGE_FORMAT_V1,
  type HonuaStudioPackage,
  type StudioPackageValidationResponse,
  tagStudioPackage,
  toStudioValidationResponse,
} from "@honua/sdk-js/studio";

MCP / QGIS safety

The barrel exports only types and pure functions and never imports from operator, esri-compat, web-components, interactions, or realtime — none of the MapLibre/DOM/Console-coupled modules. MCP servers and QGIS plugins can import @honua/sdk-js/studio without pulling renderer code or triggering a build error from Console/Esri internals.

The established map and app shapes are re-exported from their leaf modules. The distinct dashboard artifact is authored by the sdk-js Studio agent path; Console and other renderers consume the validated package read-only.

The map family additionally has a published JSON Schema, schemas/honua-map-package.v1.json (https://honua.io/schemas/honua-map-package.v1.json). It is the canonical description of the artifact for JS, the CLI, MCP, and Studio alike, and it is what validateMapPackage is built from. exportMapPackage / importMapPackage (@honua/sdk-js/runtime) move that artifact between clients without embedded credentials or unbounded data.

The schema deliberately stops at the artifact. Lifecycle states (ephemeral preview, mutable draft, immutable saved version, publication proposal, active publication, superseded), content-hash identity, optimistic concurrency, and actor/tenant/authorization fields belong to the canonical honua-server composition contract and are never minted client-side.

Package families and format constants

Each family is gated by a format constant — the canonical version string, exactly as on MapPackage. A loader refuses any other value.

Family Format constant Format string Projection type Stability
query HONUA_QUERY_PACKAGE_FORMAT_V1 honua_query_package.v1 HonuaQueryPackage experimental (stub)
analysis HONUA_ANALYSIS_PACKAGE_FORMAT_V1 honua_analysis_package.v1 HonuaAnalysisPackage experimental (stub)
map HONUA_MAP_PACKAGE_FORMAT_V1 honua_map_package.v1 HonuaMapPackage stable family, re-exported
dashboard HONUA_DASHBOARD_PACKAGE_FORMAT_V1 honua_dashboard_package.v1 HonuaDashboardPackage experimental, Studio-authored
report HONUA_REPORT_PACKAGE_FORMAT_V1 honua_report_package.v1 HonuaReportPackage experimental (stub)
form HONUA_FORM_PACKAGE_FORMAT_V1 honua_form_package.v1 HonuaFormPackage experimental (stub)
app — (uses HonuaGeneratedAppPackage.version) — HonuaGeneratedAppPackage re-exported
workflow HONUA_WORKFLOW_PACKAGE_FORMAT_V1 honua_workflow_package.v1 HonuaWorkflowPackage experimental (stub)
gp HONUA_GP_PACKAGE_FORMAT_V1 honua_gp_package.v1 HonuaGPPackage experimental (stub)
etl HONUA_ETL_PACKAGE_FORMAT_V1 honua_etl_package.v1 HonuaETLPackage experimental (stub)

The stub families (query, analysis, report, form, workflow, gp, etl) have no finalized server contract yet. Their interfaces are minimal and open-ended (every field optional beyond packageId/format, plus [extra: string]: unknown) so additive server changes never break a client. They expand once the matching server contract lands. The map and generated-app (app) families are established shapes; dashboard is a dedicated Studio-authored artifact.

Lifecycle status is shared across families via HonuaStudioPackageStatus ("Draft" | "Composing" | "Ready" | "Failed" | "Expired"), the same union as HonuaMapPackageStatus.

The HonuaStudioPackage discriminated union

HonuaStudioPackage is a union tagged by a client-side packageFamily field. The server wire shape does not carry packageFamily — projection helpers add it. Build a tagged value with tagStudioPackage(family, pkg) rather than casting a raw wire object, then narrow on packageFamily:

const tagged = tagStudioPackage("query", rawQueryPackage);
if (tagged.packageFamily === "query") {
  // tagged is narrowed to { packageFamily: "query" } & HonuaQueryPackage
  console.log(tagged.querySpec?.where);
}

STUDIO_PACKAGE_FAMILIES enumerates every family discriminant and isStudioPackageFamily(value) guards an untrusted string.

Unified validation response

Every family returns the same { valid, diagnostics, pkg? } shape through StudioPackageValidationResponse<T>, so SDK, Console, MCP, and QGIS clients consume one contract:

interface StudioPackageValidationResponse<T> {
  readonly valid: boolean;
  readonly diagnostics: readonly StudioPackageDiagnostic[];
  readonly pkg?: T;
}

The existing map-family ValidateMapPackageResult is not renamed — it is structurally bridged:

import { validateMapPackage } from "@honua/sdk-js/runtime";
import { fromMapPackageValidation } from "@honua/sdk-js/studio";

const response = fromMapPackageValidation(validateMapPackage(rawMapPackage));
// response: StudioPackageValidationResponse<HonuaMapPackage>

For other families, the generic adapter takes the field name holding the package:

toStudioValidationResponse<HonuaQueryPackage>(rawQueryResult, "queryPackage");

HonuaMapPackageDiagnostic is already a structural subtype of StudioPackageDiagnostic (it only adds packageId?), so map diagnostics flow through unchanged. For convenience, fromMapPackageValidation and toStudioValidationResponse are also re-exported from @honua/sdk-js/runtime (tagged @experimental) so existing map-package consumers can bridge from the path they already use.

StudioPackagePreviewResponse<T> is the parallel envelope for previewing a package (composed without publishing), carrying optional artifacts and diagnostics.

Capability manifest

StudioCapabilityManifest lets a client gate UI/tool exposure on what the connected server actually supports:

import { hasCapability, getCapability } from "@honua/sdk-js/studio";

if (hasCapability(manifest, "package.query")) {
  // advertise the query builder
}

hasCapability is true only for an advertised and available capability; getCapability returns the entry regardless of enabled state.

Every entry preserves the server-owned lifecycle and optInRequired governance fields. Lifecycle remains an open string so future values are not coerced to GA; missing governance fields raise HonuaCapabilityManifestContractError instead of silently defaulting.

Publish, share, and embed

StudioPublishRequest<T>, StudioPublishResponse, and StudioEmbedConfig carry the versioned publication contract for generated artifacts. HonuaShareRequest / HonuaShareResponse are re-exported from control-plane/types (single source of truth — not re-declared) so Console and MCP reach the share contract from the one @honua/sdk-js/studio path without taking on the full control-plane client.

StudioEmbedConfig.token / embedUrl describe an already-issued embed descriptor; the type does not imply a token-minting call inside the SDK.

Generated apps

Generated apps consume published map, dashboard, report, and app package projections from @honua/sdk-js/studio without importing Console or MapLibre code. HonuaDashboardPackage is the authored dashboard artifact; HonuaGeneratedAppManifest remains a runtime hydration projection rather than the dashboard authoring wire format. For the preview runtime, see docs/generated-app-runtime.md.

Versioning

Every new export is tagged @experimental in JSDoc and is subpath-only (not re-exported from the root barrels), so it is outside the SDK semver contract until the matching server contracts land. The format constants follow the HONUA_X_PACKAGE_FORMAT_V1 = "honua_x_package.v1" pattern.

For dashboard, additive optional fields remain compatible within honua_dashboard_package.v1. Removing or renaming a field, changing its meaning, or changing requiredness requires a new format discriminator. Readers preserve unknown properties, writers emit only explicitly supported formats, and validators never guess, downgrade, or rewrite an unknown format. Renderer state is derived and must not be serialized back into the authored package.

The dashboard artifact may reference the portable map artifact through mapPackageId; it does not embed credentials or mint another map representation. Publication state, stable lifecycle identity, tenant/actor, authorization, optimistic concurrency, audit, and correlation remain outside the package.

Cross-surface parity (MCP / QGIS)

The parity layer that makes packages portable across Console, MCP, and QGIS — a shared provenance envelope, a family-agnostic validateStudioPackage helper, a documented Vega-Lite chart subset, and cross-surface fixtures — is documented in studio-package-parity.md.