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

@eka-care/ekam-cli

v0.1.9

Published

Ekam app developer CLI — scaffold, build, dev, publish

Downloads

916

Readme

@eka-care/ekam-cli

Official CLI for developing Ekam apps — scaffold, build, dev server, and publish.

Installation

As a project dependency (recommended)

npm install --save-dev @eka-care/ekam-cli

Global installation

npm install -g @eka-care/ekam-cli

Via npx (no installation)

npx ekam <command>

Commands

ekam create <app-name>

Scaffold a new Ekam app with best practices and ready-to-use templates.

ekam create my-vitals-app
cd my-vitals-app
npm install

Shadow DOM only. Every app created by ekam create is mounted inside a Shadow DOM container managed by ekam-runtime. The mount(container, sdk, shadowRoot) signature is mandatory — do not remove the shadowRoot parameter.

Interactive prompt:

When no flag is provided, the CLI asks:

? Use Aakaar design system? (Y/n)
  • Yes — scaffolds an app that uses @eka/aakaar components wrapped in AakaarProvider. Aakaar's styles are injected into the shadow root by ekam-runtime before mount() runs; the app itself does not call adoptedStyleSheets.
  • No — scaffolds a minimal React app with a plain <button>. No design-system dependency.

Non-interactive flags (for CI or scripts):

ekam create my-app --aakaar      # Aakaar setup, no prompt
ekam create my-app --no-aakaar   # Plain React, no prompt

Generated structure:

my-vitals-app/
├── src/
│   ├── App.tsx              # Main component
│   ├── App.module.css       # CSS modules
│   └── index.ts             # Entry point (mount/unmount/onContextChange)
├── types/
│   ├── ekam-sdk.d.ts        # SDK type definitions
│   └── css-modules.d.ts     # CSS module types
├── assets/
│   └── icon.svg             # App icon
├── eka.manifest.yaml        # App manifest (metadata, permissions, slots)
├── package.json
├── tsconfig.json
└── .gitignore

Options:

  • App name must be in kebab-case (e.g., my-app, growth-chart)

ekam dev

Start a local dev server. Three modes, pick based on what you're doing:

ekam dev                                    # Default: open hosted runtime (recommended)
ekam dev --attach https://staging.eka.care  # Custom: attach to a specific runtime URL
ekam dev --standalone                       # Standalone: direct mount, no runtime, fastest HMR

Three modes:

| Mode | Flag | What it runs | When to use | | --- | --- | --- | --- | | Hosted (default) | (none) | Builds UMD locally + opens the hosted ekam-runtime page. Devtools auto-attach; real shell, real terminology, full FHIR stack. | Default starting point — closest to production without any setup. | | Custom runtime | --attach <url> | Same as hosted, but targets a URL you provide (staging, cockpit, local runtime build, ngrok tunnel). | Testing against a specific runtime version or environment. | | Standalone | --standalone | Vite serves dev/index.html directly. Mounts your app with a mock SDK into a real ShadowRoot. Full HMR, no UMD build step. | Day-to-day UI work — ~10× faster iteration than hosted. |

Hosted mode (default) opens https://elixir-dr.dev.eka.care/ekam-runtime/main/?__ekam_cli=http://localhost:5173 in your browser. The runtime's built-in devtools panel auto-attaches and hot-reloads your app whenever you save a file.

Custom runtime — use --attach when you need a specific runtime:

# Staging runtime
ekam dev --attach https://staging.eka.care/runtime/

# Local runtime build
ekam dev --attach http://localhost:4000/

# Remote machine via ngrok tunnel
ngrok http 5173
ekam dev --attach https://abc123.ngrok-free.app --no-open
# Then open: https://your-runtime.eka.care/?__ekam_cli=https://abc123.ngrok-free.app

Standalone scaffolding--standalone lazily creates dev/ (never overwrites):

dev/
├── index.html          # Host page (real ShadowRoot)
├── bootstrap.ts        # Wires ShadowRoot + mock SDK + HMR
├── mock-sdk.ts         # Customisable mock SDK
├── README.md
└── fixtures/
    └── patient.json

Edit dev/mock-sdk.ts to mirror the SDK surface your app consumes.

Options:

  • --port <port> — Dev server port (default: 5173)

  • --attach <url> — Attach to a specific runtime URL instead of the hosted default

  • --standalone — Use standalone mode (see above)

  • --no-open — Do not open the browser automatically (useful with ngrok / CI)

  • Mock encounter context

  • Host controls to switch patient / end encounter

  • Runtime controls (destroy / reload)


ekam validate

Run all build-time checks without bundling. Fast — meant for CI and pre-commit hooks.

ekam validate

Checks:

  1. eka.manifest.yaml parses + passes schema
  2. No banned imports (e.g., antd directly)
  3. Lint rules (warnings only — never blocks)

Exits non-zero on any blocker. Use this in CI before ekam build to fail fast.


ekam info

Print metadata from eka.manifest.yaml plus the local build status. Read-only.

ekam info

Shows: app id/name/version, framework version, slot, FHIR resources, event subscriptions, capabilities, Aakaar usage, and (if dist/ exists) bundle size + gzipped size + last-build time.


ekam build

Build production-ready UMD bundle.

ekam build

Output:

dist/
├── index.js       # UMD bundle (minified)
├── index.js.map   # Source map
└── index.css      # Extracted CSS

Features:

  • ✅ Manifest validation
  • 🚫 Banned imports check (no node builtins)
  • 🔍 ESLint validation
  • 📦 UMD format with externalized deps
  • 🗜️ Minification & tree-shaking
  • 📏 Bundle size validation (max 200KB gzipped)
  • ✓ Export validation (mount/unmount)

Build process:

  1. Validates eka.manifest.yaml
  2. Checks for banned imports (fs, path, etc.)
  3. Runs lint checks
  4. Builds UMD bundle with Vite
  5. Validates bundle size
  6. Verifies required exports exist

ekam publish (out of scope for v1)

The marketplace upload flow is not implemented in this CLI. Until the registry contract lands, publish your app by:

  1. Running ekam build
  2. Uploading dist/index.js (and dist/index.css if present) to your CDN
  3. Pointing the host's app-loader at that URL

Login + automated upload (ekam login, ekam publish to a registry) are deferred until the marketplace API is finalised.


Manifest (eka.manifest.yaml)

Every Ekam app requires a manifest file:

# Identity
app:
  id: "com.eka.vitals"
  name: "Vitals"
  version: "1.0.0"
  description: "Record patient vitals"
  author:
    name: "Developer"
    email: "[email protected]"
  icon: "./assets/icon.svg"
  category: "general-medicine"
  tags: ["vitals", "observations"]

# Framework version
framework:
  version: "1.0.0"

# UI slots
slots:
  primary: "patient-detail-panel"
  trigger:
    type: "menu"
    label: "Vitals"

# FHIR permissions
fhir:
  resources:
    - type: "Patient"
      access: "read"
    - type: "Observation"
      access: "read-write"

# Event subscriptions
events:
  subscribes:
    - resource: "Observation"
      on: ["created", "updated"]

# Capabilities
capabilities:
  storage: true
  media: false
  print: false
  notifications: false

# Aakaar UI library (only present when --aakaar was chosen)
aakaar:
  packages:
    - "@eka/aakaar"

# Constraints
constraints:
  maxBundleSize: "200KB"

App API

All Ekam apps run inside a Shadow DOM managed by ekam-runtime. The runtime creates the shadow root, injects Aakaar's stylesheets if needed, and then calls mount(container, sdk, shadowRoot). Your app's src/index.ts must export at minimum mount and unmount.

Required Exports — with Aakaar

import React from 'react'
import ReactDOM from 'react-dom/client'
import { AakaarProvider } from '@eka/aakaar'
import { App } from './App'
import type { EkamSDK } from '@ekam/sdk'

let root: ReactDOM.Root | null = null
let shadowRoot: ShadowRoot | null = null

// Mount the app — shadow is always provided by ekam-runtime
export function mount(
  container: HTMLElement,
  sdk: EkamSDK,
  shadow: ShadowRoot   // required — never optional
): void {
  shadowRoot = shadow
  root = ReactDOM.createRoot(container)
  root.render(
    React.createElement(AakaarProvider, { shadowRoot },
      React.createElement(App, { sdk })
    )
  )
}

export function unmount(_container: HTMLElement): void {
  if (root) {
    root.unmount()
    root = null
    shadowRoot = null
  }
}

export function onContextChange(sdk: EkamSDK): void {
  if (root && shadowRoot) {
    root.render(
      React.createElement(AakaarProvider, { shadowRoot },
        React.createElement(App, { sdk })
      )
    )
  }
}

Required Exports — without Aakaar

import React from 'react'
import ReactDOM from 'react-dom/client'
import { App } from './App'
import type { EkamSDK } from '@ekam/sdk'

let root: ReactDOM.Root | null = null

// _shadow is received but unused — kept for contract compliance
export function mount(
  container: HTMLElement,
  sdk: EkamSDK,
  _shadow: ShadowRoot
): void {
  root = ReactDOM.createRoot(container)
  root.render(React.createElement(App, { sdk }))
}

export function unmount(_container: HTMLElement): void {
  if (root) {
    root.unmount()
    root = null
  }
}

export function onContextChange(sdk: EkamSDK): void {
  if (root) {
    root.render(React.createElement(App, { sdk }))
  }
}

Optional Export

// Optional: Prevent unmount
export function canUnmount(): boolean | string {
  // return false to prevent unmount
  // return "Unsaved changes. Continue?" for confirmation
  return true
}

SDK API

The sdk object passed to your app:

// Context (read-only + onChange)
sdk.context.patient        // Current patient
sdk.context.encounter      // Current encounter
sdk.context.practitioner   // Logged-in user
sdk.context.organization   // Organization
sdk.context.locale         // User locale
sdk.context.slot           // UI slot name

sdk.context.onChange((ctx) => {
  // Handle patient/encounter changes
})

// FHIR operations
await sdk.fhir.read('Patient', '123')
await sdk.fhir.search('Observation', { patient: '123' })
await sdk.fhir.create('Observation', resource)
await sdk.fhir.update('Observation', 'id', resource)
await sdk.fhir.delete('Observation', 'id')

// Event subscriptions
const unsub = sdk.events.on('Observation', 'created', (resource) => {
  // Handle new observation
})
unsub() // Unsubscribe

// Storage (scoped to your app)
await sdk.storage.set('key', value)
const value = await sdk.storage.get('key')
await sdk.storage.delete('key')
const keys = await sdk.storage.list('prefix')

// UI helpers
sdk.ui.toast('Saved!', { type: 'success' })
const confirmed = await sdk.ui.confirm({
  title: 'Delete?',
  message: 'This cannot be undone',
})

CSS Modules

Use CSS modules for scoped styling:

// App.tsx
import classes from './App.module.css'

export function App() {
  return (
    <div className={classes.root}>
      <h2 className={classes.title}>My App</h2>
    </div>
  )
}
/* App.module.css */
.root {
  padding: 24px;
}

.title {
  font-size: 18px;
  color: #1e293b;
}

TypeScript definitions are auto-generated via types/css-modules.d.ts.


External Dependencies

These packages are provided by the runtime and should NOT be bundled:

  • react
  • react-dom
  • react-dom/client
  • react/jsx-runtime
  • @ekam/sdk
  • @eka/aakaar

They are automatically externalized during build.


Bundle Constraints

  • Max size: 200KB gzipped (configurable in manifest)
  • No Node.js builtins: Cannot import fs, path, crypto, etc.
  • No dynamic imports: Use static imports only
  • No global pollution: Apps run in shadow DOM

Monorepo Setup

For monorepo development with a local runtime build, point EKAM_RUNTIME_URL at the local file served by any static server:

  1. Build ekam-runtime and serve it:

    cd ekam-runtime
    npm run build
    npx serve dist --cors -p 4000
  2. Build ekam-cli:

    cd ekam-cli
    npm run build
  3. Link CLI:

    cd ekam-cli
    npm link
  4. Create app and run dev with local runtime:

    ekam create my-app
    cd my-app
    npm install
    EKAM_RUNTIME_URL=http://localhost:4000/ekam-runtime.js ekam dev

Troubleshooting

CSS not loading in dev mode

  • Check that dist/index.css exists after build
  • Ensure hot reload plugin is active (added in CLI v0.1.0)
  • Try hard refresh (Cmd+Shift+R / Ctrl+Shift+R)

"Cannot find module '@ekam/sdk'"

  • SDK types are in types/ekam-sdk.d.ts
  • Not a real npm package, provided by runtime
  • If missing, run ekam create again or copy from template

Build fails with "Banned import"

  • Cannot import Node.js built-ins (fs, path, etc.)
  • Use browser-compatible alternatives
  • Check manifest for allowed packages

Bundle size too large

  • Check dist/index.js.map to see what's bundled
  • Externalize large dependencies in manifest
  • Use code splitting (if supported in future)
  • Remove unused imports

Development

Build CLI

cd ekam-cli
npm install
npm run build

Watch mode

npm run dev

Link locally

npm link

License

Proprietary - Eka Care


Support

  • Issues: Contact Eka Care development team
  • Docs: See Ekam developer portal
  • Examples: Check vitals and bmi-calculator sample apps