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

@lars-plate/delta-client

v0.2.1

Published

This is a simple utility package for the Delta Client

Readme

@platecms/delta-client

TypeScript utilities for building Delta frontends that integrate with Plate CMS. The package provides:

  • Window connectors — typed postMessage communication between a Plate preview host and an embedded experience (iframe)
  • Content parsers — turn GraphQL ContentItem and experience-component data into plain JavaScript objects keyed by field slug
  • GraphQL types — generated TypeScript definitions for the Plate GraphQL schema

Installation

npm install @platecms/delta-client
# or
pnpm add @platecms/delta-client
# or
yarn add @platecms/delta-client

Peer/runtime: This package targets browser environments (Window, postMessage). Node.js is only needed for development (build, GraphQL codegen).

Package exports

The library is published as an ES module with optional CommonJS builds. Import from the root or from subpaths:

| Import path | Description | |-------------|-------------| | @platecms/delta-client | Everything (connectors, parsers, graphql) | | @platecms/delta-client/connectors | Window connector client & server | | @platecms/delta-client/parsers | Content parsing utilities | | @platecms/delta-client/graphql | Generated GraphQL TypeScript types |

// Full barrel
import { WindowConnectorClient, parseContentItem, type ContentExperience } from '@platecms/delta-client';

// Subpath imports (smaller bundles when tree-shaking)
import { WindowConnectorServer } from '@platecms/delta-client/connectors';
import { parseDataFromExperienceComponent } from '@platecms/delta-client/parsers';
import type { ContentItem, ExperienceComponent } from '@platecms/delta-client/graphql';

Window connectors

Plate’s Delta preview runs your experience inside an iframe while the CMS UI lives in the parent window. Connectors wrap window.postMessage with:

  • Origin checks for security
  • A health-check handshake so the host knows when the iframe is alive
  • Typed events for CMS ↔ experience messaging

Architecture

┌─────────────────────────────┐         postMessage          ┌─────────────────────────────┐
│  Plate CMS (parent window)  │ ◄──────────────────────────► │  Your experience (iframe)   │
│  WindowConnectorServer      │   HEALTH_CHECK / RESPONSE    │  WindowConnectorClient      │
└─────────────────────────────┘   CONTENT_EXPERIENCE_SEND    └─────────────────────────────┘
                                  GRID_PLACEMENT_CLICKED
                                  ROOT_EXPERIENCE_COMPONENT_SEND

Connector status

The server emits status_changed as the link moves through:

| Status | Meaning | |--------|---------| | idle | No client configured or after teardown() | | connecting | Health checks in progress | | connected | Client responded to a health check | | disconnected | Max retries exceeded without a response |

Event types

| ConnectorEventType | Direction (typical) | Payload | |----------------------|---------------------|---------| | content-experience-send | Host → experience | Full ContentExperience GraphQL object | | root-experience-component-send | Host → experience | Root ExperienceComponent | | grid-placement-clicked | Experience → host | { prn, experienceComponentPrn } |

Server (parent / CMS side)

import {
  WindowConnectorServer,
  ConnectorStatus,
  ConnectorEventType,
} from '@platecms/delta-client/connectors';

const iframe = document.querySelector<HTMLIFrameElement>('#delta-preview')!;
const childWindow = iframe.contentWindow!;
const childOrigin = 'https://your-experience.example.com';

const server = new WindowConnectorServer(window, 3); // 3 health-check retries

server
  .setClient(childWindow, childOrigin)
  .connect();

server.on('status_changed', ({ status }) => {
  if (status === ConnectorStatus.CONNECTED) {
    console.log('Experience iframe is ready');
  }
  if (status === ConnectorStatus.DISCONNECTED) {
    console.warn('Lost connection to iframe');
  }
});

server.on('message', (event) => {
  if (event.type === ConnectorEventType.GRID_PLACEMENT_CLICKED) {
    console.log('User clicked placement', event.payload.prn);
  }
});

// Push content into the iframe when the editor selects an experience
function sendContentExperience(contentExperience: ContentExperience) {
  server.send({
    type: ConnectorEventType.CONTENT_EXPERIENCE_SEND,
    payload: contentExperience,
  });
}

// Cleanup when unmounting the preview
function destroy() {
  server.teardown();
}

Client (iframe / experience side)

import {
  WindowConnectorClient,
  ConnectorEventType,
} from '@platecms/delta-client/connectors';
import { parseDataFromExperienceComponent } from '@platecms/delta-client/parsers';

const parentOrigin = 'https://cms.plate.example.com';
const parentWindow = window.parent;

const client = new WindowConnectorClient(window, {
  origin: parentOrigin,
  window: parentWindow,
});

client.on('message', (event) => {
  switch (event.type) {
    case ConnectorEventType.CONTENT_EXPERIENCE_SEND: {
      const experience = event.payload;
      const component = experience.experienceComponent;
      const data = parseDataFromExperienceComponent(
        component.buildingBlock,
        component.buildingBlockFieldFulfillments,
      );
      renderExperience(data);
      break;
    }

    case ConnectorEventType.ROOT_EXPERIENCE_COMPONENT_SEND: {
      const component = event.payload;
      // Handle root component updates (e.g. layout shell)
      break;
    }
  }
});

// Notify the CMS when the user interacts with a grid placement
function onPlacementClick(prn: string, experienceComponentPrn: string) {
  client.send({
    type: ConnectorEventType.GRID_PLACEMENT_CLICKED,
    payload: { prn, experienceComponentPrn },
  });
}

function destroy() {
  client.teardown();
}

Message format

Internally, messages use this shape over postMessage:

// Application events
{ type: string; payload: string } // payload is JSON.stringify'd

// Health check (built-in)
{ type: 'HEALTH_CHECK' }
{ type: 'HEALTH_CHECK_RESPONSE' }

Always pass the correct target origin when constructing connectors so both sides reject messages from unexpected origins.


Content parsers

Parsers convert Plate CMS GraphQL structures into plain objects suitable for templates, React props, or static site generators. Field slugs from the schema become camelCase keys (e.g. hero-titleheroTitle).

parseContentItem

Maps a ContentItem and its contentType.contentFields to { [fieldSlug]: value }.

Supported value types include assets, primitives, nested content items, tags, smart text, grid placements, and more. Nested CONTENT_ITEM values are parsed recursively.

import { parseContentItem } from '@platecms/delta-client/parsers';
import type { ContentItem } from '@platecms/delta-client/graphql';

const contentItem: ContentItem = await fetchContentItemFromApi();

const data = parseContentItem(contentItem);
// Example result:
// {
//   heroTitle: 'Welcome',
//   heroImage: { url: '...', altText: '...', ... },
//   relatedArticles: [{ title: '...' }, { title: '...' }],
// }

parseDataFromExperienceComponent

Maps a BuildingBlock plus its BuildingBlockFieldFulfillment[] (from an ExperienceComponent) to the same slug-keyed shape. Use this when rendering from Delta connector events or live preview data.

import { parseDataFromExperienceComponent } from '@platecms/delta-client/parsers';
import type { ExperienceComponent } from '@platecms/delta-client/graphql';

function componentToProps(component: ExperienceComponent) {
  return parseDataFromExperienceComponent(
    component.buildingBlock,
    component.buildingBlockFieldFulfillments,
  );
}

Placeholder mode (insertPlaceholders)

For local development or Storybook, you can fill required fields with sensible placeholders when values are missing:

import { parseContentItem, type ParseDataConfig } from '@platecms/delta-client/parsers';

const config: ParseDataConfig = { insertPlaceholders: true };

const previewData = parseContentItem(partialContentItem, config);
// Required empty STRING → 'Hello World'
// Required empty ASSET → example asset object
// Required nested CONTENT_ITEM → minimal nested structure

When insertPlaceholders is false (default), missing fields are null or [] depending on whether the field allows multiple values.

Array vs single values

Whether a field becomes an array is derived from CMS validation rules (COUNT with max > 1). Single-value fields return one value or null; multi-value fields return an array (possibly empty).


GraphQL types

The ./graphql export re-exports TypeScript types generated from the Plate GraphQL schema (e.g. ContentExperience, ExperienceComponent, ContentItem, BuildingBlock, Asset, enums like TagVisibility).

Use these types when:

  • Typing connector event payloads
  • Typing parser inputs/outputs
  • Sharing shapes with your own GraphQL client (graphql-request, Apollo, etc.)
import type {
  ContentExperience,
  ExperienceComponent,
  ContentItem,
  BuildingBlockFieldFulfillment,
} from '@platecms/delta-client/graphql';

async function loadExperience(prn: string): Promise<ContentExperience> {
  // Your GraphQL query here — types align with generated definitions
  throw new Error('implement with your API client');
}

Note: This package does not ship a GraphQL client or queries—only types. Run your own queries against your Plate GraphQL endpoint and pass the results to parsers or connectors.

Regenerating types (maintainers)

If you maintain this repo and need to refresh types from a running Plate API:

pnpm codegen

Configure the schema URL and auth in codegen.ts, then commit the updated src/graphql/generated.ts.


End-to-end example

A minimal experience app inside an iframe that renders CMS content and reports grid clicks:

import { WindowConnectorClient, ConnectorEventType } from '@platecms/delta-client/connectors';
import { parseDataFromExperienceComponent } from '@platecms/delta-client/parsers';

const CMS_ORIGIN = import.meta.env.VITE_PLATE_ORIGIN;

const client = new WindowConnectorClient(window, {
  origin: CMS_ORIGIN,
  window: window.parent,
});

client.on('message', (event) => {
  if (event.type !== ConnectorEventType.CONTENT_EXPERIENCE_SEND) return;

  const { experienceComponent } = event.payload;
  const props = parseDataFromExperienceComponent(
    experienceComponent.buildingBlock,
    experienceComponent.buildingBlockFieldFulfillments,
  );

  document.getElementById('app')!.textContent = JSON.stringify(props, null, 2);
});

document.querySelectorAll('[data-grid-placement]').forEach((el) => {
  el.addEventListener('click', () => {
    client.send({
      type: ConnectorEventType.GRID_PLACEMENT_CLICKED,
      payload: {
        prn: el.getAttribute('data-prn')!,
        experienceComponentPrn: el.getAttribute('data-component-prn')!,
      },
    });
  });
});

API reference (summary)

Connectors

| Export | Description | |--------|-------------| | WindowConnectorClient | Iframe-side connector; send(), on('message'), teardown() | | WindowConnectorServer | Parent-side connector; setClient(), connect(), send(), on('message' \| 'status_changed'), teardown() | | ConnectorEventType | Enum of event type strings | | ConnectorStatus | Connection lifecycle enum | | ConnectorEventOption | Union of all typed connector events | | StatusChangedEvent | { status: ConnectorStatus } |

Parsers

| Export | Description | |--------|-------------| | parseContentItem(contentItem, config?) | Content item → slug-keyed object | | parseDataFromExperienceComponent(buildingBlock, fulfillments, config?) | Experience component data → slug-keyed object | | ParseDataConfig | { insertPlaceholders?: boolean } | | defaultParseDataConfig | { insertPlaceholders: false } |

GraphQL

| Export | Description | |--------|-------------| | * from @platecms/delta-client/graphql | Generated schema types (see src/graphql/generated.ts) |


Development

# Install dependencies
pnpm install

# Build library (TypeScript + Vite)
pnpm build

# Regenerate GraphQL types (requires Plate API at configured URL)
pnpm codegen

Build output is written to dist/ with separate entry points matching package.json exports.


License

MIT © Lars Baalmans