sdk-typescript

View all files

Activity

Contributors

README

Mountaya TypeScript SDKs

The @mountaya/* packages for TypeScript and JavaScript, in one repository.

PackageWhat it is
@mountaya/clientThe transport. No runtime dependencies, fetch-based, and the only package here that calls the Mountaya API. Runs in Node, browsers, workers and React Native.
@mountaya/client-reactReact bindings over the transport: the provider, the query hooks, session handling.
@mountaya/elementsThe renderer. Draws Mountaya data on a MapLibre map, and publishes its maths as renderer-free entry points: geometry, profile, units, time zones, data, layers, style, icons, search, camera and i18n. Never calls fetch.
@mountaya/elements-reactReact bindings over the renderer: the map view, the controls and panels, and the canvas-free elements (an elevation profile, an itinerary table) that need no map at all. Brings no design system.
@mountaya/studioThe map studio's logic, with no renderer and no framework attached: the rules a segment chain obeys, the contracts a tool call is validated against, the stores a surface reads. Renders nothing, calls no fetch.
@mountaya/studio-reactMountaya's map studio, assembled: the rows, lists, pickers, menus and panels a working editor is made of. It brings its own design system, so a host installs no Mantine of its own.

Each package has its own README with its install line, its exports and its examples, and a Troubleshooting section that goes from what a first build shows to why. Start there; this file is about the repository.

Two families, divided by who calls fetch

client and elements are not layers of one thing. The client speaks to the API and renders nothing; the elements render and speak to nothing. A headless consumer takes the client and never installs a renderer, and a consumer drawing someone else's GeoJSON takes the elements and never installs a transport. Each family has one React binding, which is why the split is by family rather than by layer.

studio belongs to neither family, and that is the point of it: the rules a segment chain obeys, the contracts a tool call is validated against, the stores a surface reads, the words it puts on screen. No renderer, no framework, no fetch. Everything a studio DOES lives there, which makes it the package a Flutter or Swift port reimplements MOST of, and everything a studio LOOKS LIKE lives one level up. Most of, not first: it peer-depends on both the client and the elements, so a port starting here is a port writing three at once. The order those edges name is the transport, then the renderer-free maths, then this.

studio-react sits on top of all of them and is divided from them by a different question: who brings a design system. It is the assembled studio, so it brings Mantine as a DEPENDENCY (npm install @mantine/core is not a step a consumer of a studio takes), and that is a boundary only a package can draw, because exports decides what a bundler includes and never what an installer fetches.

Those boundaries are enforced rather than described: packages/elements/src/boundary.test.ts fails a change that reaches fetch, packages/client/src/boundary.test.ts fails one that reaches a renderer, and packages/studio/src/boundary.test.ts fails one that reaches either.

Examples you can run

They live in examples/, each built by a real bundler or run by node. All but one build against the packages' source rather than their published output, so a change to a package shows up in an example without a publish. next-app reads the published output, the way a consumer's Next.js does.

ExampleWhat it showsQuoted by
elements-react-mapa map, its controls and an itinerary, under a session token that rotatespackages/elements-react/README.md
next-appa Next.js Server Component that prefetches, and a Client Component that draws the mappackages/client-react/README.md
studio-vitethe assembled studio in a Vite applicationpackages/studio-react/README.md
studio-headlessthe studio's surfaces with the design system left to the hostpackages/studio-react/README.md
studio-nodethe studio's tool bus from a Node process: no bundler, no DOM, no Reactpackages/studio/README.md
client-nodethe transport alone, on a server: no bundler, no DOMpackages/client/README.md
elements-videothe video pipeline over the renderernone

A quoted example is byte-identical to the snippet that quotes it, and scripts/readme-examples.test.mjs is what keeps that true, so the code you read in a README is the code you are about to run. elements-video is the flight of the elements README's "Recording a fly-through", as a page.

First, a publishable key

An example takes one from the environment or from a placeholder in its source, as its own README says, and pk_… in a snippet is a placeholder, not a working key. Yours comes from your organization's API keys page in the Mountaya dashboard; the reference for what each kind of key reaches is at docs.mountaya.com. A key is its prefix followed by 32 lowercase hex characters, with nothing in between that names an environment. A publishable key (pk_…) is the only one a browser example needs: it is safe to ship to a browser, which is what makes it the one an example may read from your shell. A secret key (sk_…) is not: it mints session tokens, it belongs on a server, and @mountaya/client refuses to construct one where a browser global exists. client-node takes both, because it has no browser: it mints the session token the Data API requires on every request, in the process that uses it. elements-react-map takes both as well, and keeps them apart: its page reads the publishable key, and its dev server alone reads the secret one.

npm install
npm run build                                      # next-app and the Node examples read dist
export MOUNTAYA_PUBLISHABLE_KEY=pk_…               # yours, from the dashboard
export MOUNTAYA_SECRET_KEY=sk_…                    # where a server mints; never in a browser

npm run dev --workspace @mountaya/example-client-node
npm run dev --workspace @mountaya/example-next-app

Running the repository's own gates needs checkouts beside this repository: of mountayaapp/schemas at ../schemas, which the contract gates read, and of mountayaapp/ai at ../ai, whose product skills a gate compiles. A local run tolerates either one missing and skips the gates that read it; npm run ci refuses to start without them, and reaches the npm registry and Playwright's Chromium download as it runs. CONTRIBUTING.md has the layout.

One repository, one version

They are published together at a single version: every @mountaya/* peer here is pinned exact, and an exact pin is only installable if the package it names is published at that version. Changesets holds them in a fixed group so the manifests and the release match.

One repository is what makes that affordable. A contract change lands as one commit, one review and one publish rather than as a chain of them, and every package type-checks against a freshly built version of what it consumes without anyone maintaining a build order by hand.

The contract

The wire types in @mountaya/client are generated from the public OpenAPI bundles at mountayaapp/schemas, which is versioned by tag, independently of these packages. The client records the tag it was generated from in SCHEMA_VERSION, so "which contract does this client speak" has a one-line answer.

Requirements

ToolHow it is installedWhat needs it
Node, npmpinned in mise.tomlEverything. The scripts run on the pinned Node and, in the gate, on the floor
Chromiumnpx playwright install chromium, which mise run ci:browser runsThe browser suites only

Setup

sh .agents/setup           # Entire, mise and the pinned tools, the git hooks, the dependencies
sh .agents/setup --check   # what is missing, changing nothing

Commands

CommandWhat it does
mise run lintFormatting and linting across every package
mise run testVitest across every package, and the script and consumer tests
mise run buildEvery package
mise run formatBiome's fixes
mise run ci:quickWhat pre-commit runs: lint
mise run ciThe gate, on the Node floor and then on the pinned Node, without Playwright
mise run ci:browserThe Playwright suites, which npm run release also runs before it publishes

Pre-commit runs mise run ci:quick. Every push to Entire runs mise run ci on Depot CI (.depot/workflows/ci.yml), and its result shows as checks on the commit. It builds against the ../ai and ../schemas commits siblings.lock pins: sh .agents/siblings pin moves one, once that commit is pushed.

Contributing

See CONTRIBUTING.md for the setup, the build and the release, and AGENTS.md for the rules a change is held to. Security reports go to the address in SECURITY.md, never to an issue.

Licence

The packages are published under the Mountaya SDK License, version 1.0, in LICENSE.txt, with a French translation in LICENCE.fr.txt. Where the two differ, the English text prevails. The licence is source-available, not open source: you may read the code, bundle it and ship it inside your own application, in conjunction with the Mountaya services and for as long as an agreement with Mountaya is in force. That agreement is either the terms you accepted when you opened your Mountaya account or a services agreement you signed.

The licence needs no agreement of its own, but most of what the packages reach does. The Data API is self-serve: an account and a card open it. The Map Studio API, the Tile API and Embedded Studio (@mountaya/studio-react) come with a signed agreement only.

A package that bundles another project's work lists it, with that project's licence, in its THIRD-PARTY-NOTICES.txt. The examples under examples are MIT licensed (examples/LICENSE), so you can copy them into your own code freely.

The licence is stated in one place, the root, and mirrored into each package; scripts/packaging.test.mjs fails on a copy that drifts.