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

@inizioevoke/veeva-astroclm

v1.1.4

Published

Scaffolding for an @inizioevoke/veeva-astroclm project — an [Astro](https://astro.build)-based framework for building Veeva CLM presentations.

Readme

@inizioevoke/veeva-astroclm

Scaffolding for an @inizioevoke/veeva-astroclm project — an Astro-based framework for building Veeva CLM presentations.

Install in current directory

npx --yes @inizioevoke/veeva-astroclm@latest

Install in target directory

npx --yes @inizioevoke/veeva-astroclm@latest [target]

Templates

When run, the scaffolder prompts you to pick a template:

  • Standard template (default) — includes GlobalLayout
  • Left nav template — includes GlobalLayout and LeftNavLayout
  • Top nav template — includes GlobalLayout and TopNavLayout
  • Empty template — no layouts

Skip the prompt with the --template flag:

npx --yes @inizioevoke/veeva-astroclm@latest [target] --template <name>

Valid values: standard, left-nav, top-nav, empty.


Project structure

.
├── astro.config.ts          Astro config + env schema + pruneBuild integration
├── veeva-config.ts          Presentation metadata, slides, binder fields (created via veeva-config)
├── veeva-metadata.js        Browser-console script that bulk-updates Veeva PromoMats fields
├── .env.development         LOG_LEVEL, WEB_ENV, WEB_SCALING for `astro dev`
├── .env.veeva-client        BUILD_TARGET for client builds/deploys
├── .env.veeva-client-training  BUILD_TARGET + VEEVA_TRAINING for training builds
├── .env.veeva-evoke         BUILD_TARGET + VEEVA_ENV="evoke" for internal builds
├── bin/create.js            Scaffolder entry point
├── tasks/
│   ├── page-manager.ts      Interactive page scaffolder
│   ├── veeva-config.ts      Interactive veeva-config.ts generator/updater
│   └── veeva-deploy.ts      Deploys built output to Veeva
├── public/shared/           Static assets served under /shared/
└── src/
    ├── pages/               Astro route entries (one file per slide)
    ├── contents/            Per-slide content modules imported by pages
    ├── layouts/             GlobalLayout, LeftNavLayout, TopNavLayout, ...
    ├── components/          Project components (content, isi, nav, ui)
    ├── data/pages.tsx       Page registry / nav data
    ├── lib/{client,server}/ Shared client + server helpers (IVA config lives here)
    ├── scss/                Shared SCSS (_mixins, _variables, ipads.scss)
    └── shared/img/          Shared image assets

Pages and contents

Each slide is split in two:

A page at src/pages/about-moa.astro typically imports from src/contents/about/moa.astro. The page-manager task (below) wires this up automatically.


Development

Start the Astro dev server:

npm run dev

The root path / redirects to /home/ (see astro.config.ts).

Dev env lives in .env.development. Useful knobs:

  • LOG_LEVEL — debug, info, warn, error
  • WEB_ENV — set to production to simulate a production build locally
  • WEB_SCALING — when true, the resize script is included if <ResizeScript /> is present in the layout

Creating pages

Manual

Given a page path like about/moa:

  1. Create src/pages/about-moa.astro:
    ---
    import Page from '@/contents/about/moa.astro';
    ---
    <Page />
  2. Create src/contents/about/moa.astro:
    ---
    import GlobalLayout from '@/layouts/GlobalLayout/GlobalLayout.astro';
    
    import './moa.scss';
    ---
    <GlobalLayout>
      <!-- slide content -->
    
      <script>
        import './moa';
      </script>
    </GlobalLayout>
  3. Create empty src/contents/about/moa.scss and src/contents/about/moa.ts.
  4. If a veeva-config.ts exists, add a corresponding entry to presentation.slides (see Veeva config below).

Interactive

npm run page-manager

Prompts for the page path and layout, writes the four files above, and offers to add a slide entry to veeva-config.ts.

Bulk scaffold multiple pages at once by creating _contents.yml in the project root with a list of paths (see _contents.yml.example) before running the task.

As an alternative, Claude Code users with the evoke-page-manager skill installed can ask Claude to create the page — the skill drives the same flow.


Building content

Slide content lives in src/contents/. A slide is an Astro component that wraps a layout and renders markup, scripts, and styles. Pair each slide with these building blocks.

Images — Image.astro

A thin wrapper around Astro's getImage() that fills in width/height from the source and defaults loading="eager" (Veeva slides should not lazy-load). Use for every image imported from src/shared/img/ or a sibling asset directory.

---
import Image from '@components/ui/Image.astro';
import hero from './hero.png';
---
<Image src={hero} width={800} alt="Hero" />

Props: src (required ImageMetadata), width, height (one is enough — the other is derived from aspect ratio), format, quality (default 80), alt, plus any <img> attributes.

Internal links — Link.astro

Routes to another slide. In Veeva builds it emits data-gotoslide (the Veeva slide zip name) and an href="#"; in web builds it emits the real path. External URLs (http(s)://…) and shared/… paths are passed through unchanged. aria-current="true" is set automatically when the link points to the current page.

---
import Link from '@components/ui/Link.astro';
---
<Link href="about/moa" data-evotrk={{ type: 'button', name: 'learn_more' }}>Learn more</Link>
<Link href="https://example.com">External</Link>

Props: href (a PageHref from src/data/pages.tsx, or any string), optional data-evotrk (object form — serialized via @inizioevoke/veeva-astroclm-core), plus any <a> attributes.

Nav links — NavLink.astro

A Link variant for navigation menus. Adds data-current="true" when the link belongs to the currently active section — useful for highlighting a parent nav item on child routes. Match the current section via hasCurrent:

---
import NavLink from '@components/ui/NavLink.astro';
---
<NavLink href="about/moa" hasCurrent="/about/">About</NavLink>
<NavLink href="data" hasCurrent={/^\/data\//}>Data</NavLink>
<NavLink href="resources" hasCurrent={(path, href) => path.startsWith('/resources/')}>Resources</NavLink>

Props: everything Link accepts, plus hasCurrent (string prefix | RegExp | (pathname, href) => boolean).

Cross-presentation links — VeevaLink.astro

For jumping to a slide in another Veeva presentation. Emits a data-gotoslide with the slide name and (optionally) the target presentation ID — this is the only way to leave the current presentation inside Veeva. The href is always #; this component has no web-build fallback.

---
import VeevaLink from '@components/ui/VeevaLink.astro';
---
<VeevaLink slide="safety_main" presentation="my_other_presentation">Safety info</VeevaLink>

Props: slide (required), presentation (optional — defaults to the current one), optional data-evotrk, plus any <a> attributes.

Page registry — src/data/pages.tsx

Pages is a const array that defines each slide's title (JSX) and href. Its type powers the PageHref autocomplete used by Link / NavLink, and the helpers (getPageElement, getPageComponent, PageComponents) let nav components render titles consistently across slides. Add new entries here whenever you add a page that nav or breadcrumbs need to know about.


Veeva config

veeva-config.ts describes the presentation to Veeva: dimensions, iOS resolution, platform (Salesforce or Vault), disabled actions (Swipe, Zoom, etc.), binder fields, and the slide list. tasks/veeva-deploy.ts and the build pipeline both consume it.

Creating it

npm run veeva-config

On first run, it prompts for product name, presentation name/ID, IVA dimensions (1024 × 768, 1366 × 1024, 1376 × 1032), iOS resolution, target platform, and which Veeva features to disable. It then writes veeva-config.ts to the project root with one slide entry per file in src/pages/.

Keeping it in sync

Re-run the same command to audit veeva-config.ts against src/pages/. It reports:

  • Missing slides — pages in src/pages/ with no entry in slides
  • Extraneous slides — entries in slides whose path has no matching page

You can add missing slides, and remove or comment out extraneous ones.

Shape

export default function ({ veevaEnv = 'client', isTraining = false }: IVeevaConfigArgs = {}): IVeevaConfig {
  return {
    dimensions: { width: 1366, height: 1024 },
    presentation: {
      externalId: 'PRD_3K9BW12X',
      presentationId: 'my_presentation',
      product: 'My Product',
      name: 'My Presentation',
      country: 'United States',
      crmTarget: 'Salesforce',
      // ...binderFields
      slideSettings: {
        mediaType: 'HTML',
        disableActions: ['Swipe', 'Zoom'],
        iosResolution: 'Default For Device'
      },
      slides: [
        { path: 'home', title: 'Home', externalId: formatExtId('3K9BW12X') }
      ]
    }
  };
}

The exported function receives { veevaEnv, isTraining } from the deploy task and conditionally shapes the config (e.g. appending _TRN to IDs for training, swapping binder fields for evoke builds).

As an alternative, Claude Code users with the evoke-veeva-config-manager skill installed can ask Claude to create or sync the config.


Building

Three build targets are available, each binds to a different .env.* file via --mode:

| Script | Mode | Env file | Purpose | |---|---|---|---| | npm run build-client | veeva-client | .env.veeva-client | Production build for the client's Veeva org | | npm run build-client-training | veeva-client-training | .env.veeva-client-training | Training variant (VEEVA_TRAINING=true) | | npm run build-evoke | veeva-evoke | .env.veeva-evoke | Internal Evoke build (VEEVA_ENV=evoke) |

npm run build runs an untargeted Astro build. Preview any build with npm run preview.

Supported env vars (schema defined in astro.config.ts):

  • BUILD_TARGET — veeva for Veeva builds
  • VEEVA_ENV — client (default) or evoke
  • VEEVA_PLATFORM — override the config's target platform
  • VEEVA_TRAINING — true for training builds
  • WEB_ENV, WEB_SCALING — web-mode knobs
  • LOG_LEVEL, LOG_OUTPUT, LOG_TIME — logging

The pruneBuild integration from @inizioevoke/astro-core removes unused assets from the build output.


Deploying to Veeva

npm run deploy-client            # client org
npm run deploy-client-training   # client training
npm run deploy-evoke             # evoke internal

Each runs tasks/veeva-deploy.ts, which reads the matching .env.* and uploads the build output to Veeva.

Combined build + deploy shortcuts:

npm run veeva                 # build-client + deploy-client
npm run veeva-training        # build-client-training + deploy-client-training
npm run veeva-evoke           # build-evoke + deploy-evoke

CSV-only variants (push the slide manifest without re-uploading the HTML):

npm run veeva-csv
npm run veeva-training-csv
npm run veeva-evoke-csv

Bulk-updating Veeva metadata

After a deploy, you often need to update metadata on each uploaded slide in Veeva PromoMats (expiration dates, CRM sharing settings, approved material relationships, lifecycle status). veeva-metadata.js is a browser-console script for this:

  1. Open the presentation in Veeva PromoMats in Chrome.
  2. Edit the data object at the top of the file (expiration dates, CRM sharing teams, approved material codes, status, etc.).
  3. Paste the whole script into DevTools and run it. It walks every slide, applies the metadata, and advances through the lifecycle.

Analytics

Click tracking is handled by @inizioevoke/veeva-iva, configured in src/lib/client/ (typically via IVA.configure(...) or IVA.init(...)). Tracked elements emit console.debug lines in the form:

DEBUG: track {slide: 'home', type: 'button', name: 'button_cta_learn_more'}

To audit coverage, start npm run dev, visit each page, click every tracked element ([data-evotrk], <a>, <button>, etc.), and record the emitted events.

As an alternative, Claude Code users with the evoke-analytics-auditor skill installed can ask Claude to run the audit — it drives a Playwright browser through every page and writes a Markdown + CSV report of tracking events, ambiguous events, errors, warnings, and logs.


Related packages