npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@juuno-sdk/app-sdk

v3.8.0

Published

Juuno App SDK - The contract package for building external Juuno apps

Readme

@juuno-sdk/app-sdk

The contract package for building external Juuno apps

Overview

@juuno-sdk/app-sdk is the contract package for developing external Juuno apps, with one entry per host surface (ADR 0031):

  • @juuno-sdk/app-sdk/player — what a scene's player bundle may import.
  • @juuno-sdk/app-sdk/config — the config UI surface, media library and i18n included.
  • @juuno-sdk/app-sdk/preview — the static preview surface.

The package is a pure re-export shim: it bundles no copies. At runtime the Juuno hosts serve every re-exported package through their import maps, so an app externalises the SDK and ships none of its code. The root export is the union of the three entries, kept for tooling that resolves the bare package name.

Contract version

This package's major.minor version is the player-app contract version (ADR 0007; refined by ADR 0030, landing with the release-resolution work). The contract surface is everything this package re-exports: today that spans @juuno/apps-player-common, @juuno/apps-core, @juuno/apps-config-common, @juuno/apps-slideshow-common, @juuno/media-library, @juuno/i18n-common, and selected members of @juuno/core, @juuno/components, @juuno/icons, @juuno/types, @juuno/common, and @juuno/utils. The shared Vue instance is a peer dependency, not a re-export: its range in package.json is the record of it. Any change to that surface must bump this package's version, including changes made in those source packages without any file in this folder appearing in the diff:

  • Major: a breaking change. Majors are epochs. Release resolution never serves an app release across a major boundary, and every first-party app re-releases at the new major in the same PR.
  • Minor: an additive change (a new component, prop, or capability). Minors are floors. A release stamped 2.1 is served only to players implementing minor 1 or later within major 2.
  • Patch: no contract meaning. Free for fixes that change no surface.

Your app declares the contract version it supports as a required sdk_version field in src/manifest.json, written as major.minor:

{
  "id": "my-app",
  "version": "1.0.4",
  "sdk_version": "2.0"
}

The two version fields are unrelated. version is your app's own release number and carries no compatibility meaning; sdk_version is the SDK surface you build against. A defaulted value would be a floor nobody declared, so it is required rather than inferred.

Write the version your app is built and tested against, and update it when you adopt surface added in a newer SDK. Nothing compares your declaration against the SDK you actually compiled with, so an understated floor is served to players that will fail on it. The player derives and advertises the version it implements from the SDK it compiled against (FE#2079), and the backend records your declaration on the release and rejects a deployment without it (juuno-co/juuno-backend#658).

Where the field is enforced depends on how you build. Apps built in the Juuno monorepo fail at build time, because the manifest plugin validates it there. A third-party app built with your own tooling is checked when you deploy, by the CLI and then by the backend. From 2.0.0 this package also exports its package.json, so tooling can read the SDK version through module resolution.

This package deliberately re-exports nothing from vue. Apps import vue directly: it is a peer of this package, every app externalises it, and the host import map serves the one shared instance. The contract version lives in package.json only.

The surface snapshot

type-surface.player.d.ts, type-surface.config.d.ts and type-surface.preview.d.ts are generated records of everything each entry exports, resolved through the re-exports so the shapes are the ones an app actually compiles against. The root union is deliberately not snapshotted: it would double every diff. CI regenerates it, fails if the committed copy is stale, and fails again if it moved on a pull request whose package.json version did not. That is what makes the bump duty enforceable for a change made in a source package, where nothing under external/app-sdk/ appears in the diff at all.

When CI asks for it, regenerate and commit alongside the bump:

pnpm sdk:surface

It type-checks first, because the extractor reads the declarations vue-tsc emits rather than the source. Expect a diff whenever a re-exported component gains a prop or an exported type changes shape. What it cannot see is a behaviour change behind an unchanged signature, which stays reviewer judgment, as in any library.

Installation

npm install @juuno-sdk/app-sdk

Peer Dependencies

This package requires:

{
  "peerDependencies": {
    "vue": "^3.5.22"
  }
}

What's Included

  • Config UI Framework: Components and utilities for building app configuration interfaces
  • Player Utilities: Components and helpers for rendering app content
  • UI Components: Buttons, modals, inputs, color pickers, and more
  • Icons: Common icon components (SvgAdd, SvgClose, SvgDelete, etc.)
  • Styles: none to import; the hosts serve every component's CSS
  • Types: TypeScript types for all APIs
  • Utilities: Helper functions (createLogger, cloneDeep, etc.)

Usage

Basic App Structure

// src/player/AppSlide.vue
<script setup lang="ts">
import {
  type AppSlideProps,
  createLogger
} from '@juuno-sdk/app-sdk/player';

const Logger = createLogger('MyApp');
const props = defineProps<AppSlideProps>();
</script>

<template>
  <div class="my-app">
    <!-- Your app content -->
  </div>
</template>

Config UI Example

// src/config/AppConfig.vue
<script setup lang="ts">
import { useAppConfig, ColorPickerInput } from '@juuno-sdk/app-sdk/config';

type MyAppMeta = {
  backgroundColor: string;
};

const config = useAppConfig<MyAppMeta>();
// config.meta is the scene's meta, seeded from defaultMeta; the host saves it.
</script>

<template>
  <div class="config">
    <ColorPickerInput
      v-model="config.meta.backgroundColor"
      label="Background Color"
    />
  </div>
</template>

Styles

The SDK ships no stylesheet. Every component the entries re-export is served by the host, and the host page carries that component CSS, so an app imports nothing.

Available Exports

Config UI

  • useAppConfigContext<T>() - Access app config context
  • provideAppConfigContext() - Provide config context
  • AppConfigContainer - Container for config UI
  • SelectTextPosition - Text position selector
  • WysiwygEditor - WYSIWYG editor component
  • useDirtyState() - Track unsaved changes

The full per-entry lists

The generated snapshots are the authoritative record of what each entry exports, with the resolved shapes an app compiles against:

  • type-surface.player.d.ts — what @juuno-sdk/app-sdk/player exports
  • type-surface.config.d.ts — what @juuno-sdk/app-sdk/config exports
  • type-surface.preview.d.ts — what @juuno-sdk/app-sdk/preview exports

A hand-written list here would drift; this one cannot, because CI regenerates the snapshots and fails when the committed copies are stale.

Build Configuration

Externalize Vue and every @juuno-sdk/app-sdk/* specifier; the host provides both at runtime:

rollupOptions: {
  external: ['vue', /^@juuno-sdk\/app-sdk(\/.*)?$/],
},

The full vite.config.ts, with one input per manifest resource and the static-copy step, is in SKILL.md step 4.

What's NOT Included

For external apps, the following features are not available:

  • ❌ Font Management: Use Google Fonts or custom web fonts
  • ❌ GraphQL/Backend Integration: External apps are self-contained; the host's data layer is not part of the contract (ADR 0031 decision 5)

The media library input (InputImage) and i18n (useI18n) are part of the config entry.

Getting Started

SKILL.md, shipped in this package, is the step-by-step guide: creating an app, publishing a new version, and moving onto a newer contract version. It is written for an agent to follow, and it carries the preflight checks and the failure modes this README only describes.

Read it before you start. The short version:

  1. Ask Juuno to create your App record. This is not self-serve yet, and nothing deploys until it exists.
  2. npm i @juuno-sdk/app-sdk vue and npm i -D vite @vitejs/plugin-vue @juuno-sdk/cli.
  3. Write manifest.json, an icon.svg, and a render() entry each for player and config.
  4. npx juuno-cli deploy, then confirm npx juuno-cli info <your-app-id> prints a manifest carrying the version you just deployed.

create-juuno-app is not published yet. When it ships it must seed sdk_version, since a scaffold that omits it produces an app that cannot deploy.

TypeScript Configuration

External apps need type stubs for SVG and GraphQL files that may be imported from SDK dependencies:

// types/svg.d.ts
declare module '*.svg?component' {
  import { DefineComponent } from 'vue';
  const component: DefineComponent<{}, {}, any>;
  export default component;
}

declare module '*.svg?skipsvgo' {
  import { Component } from 'vue';
  const src: string & Component;
  export default src;
}

// types/graphql.d.ts
declare module '*.graphql' {
  import { DocumentNode } from 'graphql';
  const document: DocumentNode;
  export default document;
}

Then configure tsconfig.json to exclude internal packages and skip lib checking:

{
  "extends": "@vue/tsconfig/tsconfig.dom.json",
  "include": ["src/**/*", "src/**/*.vue", "types/**/*"],
  "exclude": [
    "node_modules/@juuno/core/**/*",
    "node_modules/@juuno/icons/**/*"
  ],
  "compilerOptions": {
    "skipLibCheck": true,
    "skipDefaultLibCheck": true
  }
}

Related Packages

  • @juuno-sdk/cli: Development tool for testing and deploying external apps
  • SKILL.md: Step-by-step lifecycle guide shipped in this package

License

MIT