sdk-typescript
Activity
Contributors
README
Mountaya TypeScript SDKs
The @mountaya/* packages for TypeScript and JavaScript, in one repository.
| Package | What it is |
|---|---|
@mountaya/client | The 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-react | React bindings over the transport: the provider, the query hooks, session handling. |
@mountaya/elements | The 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-react | React 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/studio | The 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-react | Mountaya'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.
| Example | What it shows | Quoted by |
|---|---|---|
elements-react-map | a map, its controls and an itinerary, under a session token that rotates | packages/elements-react/README.md |
next-app | a Next.js Server Component that prefetches, and a Client Component that draws the map | packages/client-react/README.md |
studio-vite | the assembled studio in a Vite application | packages/studio-react/README.md |
studio-headless | the studio's surfaces with the design system left to the host | packages/studio-react/README.md |
studio-node | the studio's tool bus from a Node process: no bundler, no DOM, no React | packages/studio/README.md |
client-node | the transport alone, on a server: no bundler, no DOM | packages/client/README.md |
elements-video | the video pipeline over the renderer | none |
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
| Tool | How it is installed | What needs it |
|---|---|---|
| Node, npm | pinned in mise.toml | Everything. The scripts run on the pinned Node and, in the gate, on the floor |
| Chromium | npx playwright install chromium, which mise run ci:browser runs | The 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
| Command | What it does |
|---|---|
mise run lint | Formatting and linting across every package |
mise run test | Vitest across every package, and the script and consumer tests |
mise run build | Every package |
mise run format | Biome's fixes |
mise run ci:quick | What pre-commit runs: lint |
mise run ci | The gate, on the Node floor and then on the pinned Node, without Playwright |
mise run ci:browser | The 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.