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 🙏

© 2024 – Pkg Stats / Ryan Hefner

narrative-studio-sdk

v0.1.4

Published

Narrative SDK for building apps on the Narrative Studio

Downloads

637

Readme

Narrative SDK

npm version

The Narrative SDK provides an interface for interacting with Narrative Studio. It enables you to manage entities, handle events, and maintain a responsive read model, leveraging CQRS (Command Query Responsibility Segregation) for better scalability and performance.

Installation

Install the SDK using npm or yarn:

npm install narrative-studio-sdk

# or

yarn add narrative-studio-sdk

App Manifest

The App Manifest defines your app, its entities, and settings. It must be hosted on a publicly accessible URL to register the app with Narrative Studio.

Manifest Example

{
  "name": "My Workflow App",
  "description": "Single source model for workflow management",
  "icon": "https://example.com/icon.png",
  "version": "1.0.0",
  "appUrl": "https://example.com/app.js",
  "vendor": {
    "name": "VendorName",
    "website": "https://vendor.com",
    "supportUrl": "https://vendor.com/support"
  },
  "entities": {
    "constructs": [
      {
        "caption": "Custom Construct",
        "type": "CustomConstruct",
        "backgroundColor": "#C6A2D2",
        "textColor": "#000000",
        "shape": "rectangle"
      }
    ],
    "assets": [
      {
        "caption": "Custom Asset",
        "type": "CustomType",
        "icon": "/custom-asset.svg",
        "dataSource": "custom.dataSource",
        "detailsPane": {
          "url": "https://example.com/details-pane.xml"
        }
      }
    ]
  },
  "settings": {
    "fields": [
      {
        "name": "apiKey",
        "label": "API Key",
        "type": "text",
        "description": "Your personal API key for integration",
        "required": true
      },
      {
        "name": "projectId",
        "label": "Default Project ID",
        "type": "number",
        "description": "The ID of your default project",
        "required": false
      },
      {
        "name": "enableNotifications",
        "label": "Enable Notifications",
        "type": "boolean",
        "description": "Turn on/off notifications",
        "required": false,
        "default": true
      }
    ]
  }
}

Fields Description

  • name - App display name in Narrative Studio.
  • description - Description shown in Narrative Studio.
  • icon - URL for the app icon.
  • version - App version.
  • appUrl - Public URL of the app’s JavaScript file.
  • vendor -
    • name - Organization’s name.
    • website - Organization’s website URL.
    • supportUrl - URL for app support.
  • entities Configurable building blocks in Narrative Studio.
    • constructs (optional) - Define new constructs for users.
      • caption - The caption of the construct.
      • type - The type of the construct.
      • backgroundColor - Hex Color code for the background color of the construct.
      • textColor - Hex Color code for the text color.
      • shape - The shape of the construct. Supported shapes are rectangle and square.
      • url (optional) - A URL to an XML file that describes the details pane for the asset. The details pane is a custom view that appears when the asset is clicked on in the Narrative Studio.
    • assets (optional) - Define new assets for users. Assets are reusable resources which can be referenced in one or more constructs.
      • caption - The caption of the asset.
      • type - The type of the asset.
      • icon - The URL to the icon of the asset.
      • dataSource - Key to bind the asset to the read model.
      • detailsPane
      • url (optional) - A URL to an XML file that describes the details pane for the asset. The details pane is a custom view that appears when the asset is clicked on in the Narrative Studio.
  • settings (optional) - The settings that the app will make available to users in the Narrative Studio. The settings saved by the user are available to the app via the SDK.
  • fields - The fields that the app will make available to users in the Narrative Studio
    • name - A unique name for the field.
    • label - The label of the field as it will appear in the Narrative Studio.
    • type - The type of the field. Supported types are text, number, boolean.
    • description - The description of the field as it will appear in the Narrative Studio.
    • required - Whether the field is required or not.
    • default (optional) - The default value of the field.

Details Pane

The details pane is a custom view that appears when an entity is clicked on in the Narrative Studio. By default, the details pane is a simple view that displays the name of the entity and allows the user to enter a description. You can customize the details pane by providing a URL to an XML file that describes the elements you want to appear and optionally bind its data to a read model. The XML file should be hosted on a publicly accessible network location.

The following elements are supported in the XML file:

  • detailsPane - The root element of the XML file
  • tabs (optional) - The tabs that will appear in the details pane.
  • tab (optional) - A tab that will appear in the details pane.
    • label - The label of the tab.
    • icon (optional) - The icon of the tab.
  • items - Items can be under a tab or directly under the detailsPane.
    • item - An item that will appear in the details pane.
      • label - The label of the item.
      • icon (optional) - The icon of the item.
      • type - The type of the item. Supported types are textbox, textarea, select.
      • value - The value that you would like the item to be set to. You can bind items to a read model by enclosing the item in double curley brackets, e.g. {{asset.summary}}. You can use the SDK to update items in the read model.
      • options (optional) - for use with the select type, the options that will appear in the select dropdown.
      • option - An option of the item.
        • value - The value of the option.
        • label - The label of the option.

Details Pane Example

The following is an example of a details pane XML file that uses tabs:

<detailsPane>
    <tabs>
        <tab label="Details">
            <textbox label="Summary" value="{{asset.summary}}" />
            <textbox label="Type" value="{{asset.type}}" />
            <textbox label="Priority" value="{{asset.priority}}" icon="{{asset.priorityIcon}}" />
            <select label="Status" value="{{asset.status}}">
                <option value="Open">Open</option>
                <option value="In Progress">In Progress</option>
                <option value="Closed">Closed</option>
            </select>
        </tab>
        <tab label="Description">
            <textarea value="{{asset.description}}" />
        </tab>
    </tabs>
</detailsPane>

Without tabs:

<detailsPane>
    <item label="Summary" type="textbox" value="{{asset.summary}}" />
    <item label="Type" type="textbox" value="{{asset.type}}" />
    <item label="Priority" type="textbox" value="{{asset.priority}}" icon="{{asset.priorityIcon}}" />
    <item label="Status" type="select" value="{{asset.status}}">
        <option value="Open">Open</option>
        <option value="In Progress">In Progress</option>
        <option value="Closed">Closed</option>
    </item>
    <item label="Description" type="textarea" value="{{asset.description}}" />
</detailsPane>

Usage

The Narrative SDK leverages CQRS (Command Query Responsibility Segregation) to manage application state efficiently, separating the command (write) and query (read) sides for better scalability and performance. Here’s how to use its core features:

1. Initialize the SDK

Start by importing and initializing the SDK. This gives you access to event handling, command sending, and data querying.

import {Narrative} from 'narrative-studio-sdk';

const narrative = new Narrative();

2. Send Commands

Commands are used to perform actions that modify the system’s state. They are the primary way to make changes, such as adding, updating, or deleting entities.

Example: Adding a New Entity

import { Narrative, AddEntityCommand } from 'narrative-studio-sdk';

narrative.sendCommand(AddEntityCommand, {
  id: 'task-001',
  type: 'Task',
  position: { x: 200, y: 150 },
  name: 'New Task',
});

2. Subscribe to events

Events inform your app of changes or interactions occurring within the Narrative Studio. You can subscribe to these events to react to system changes.

Example: Listening for Changes

narrative.subscribeToEvents([ChangesSavedEvent], (event: ChangesSavedEvent) => {
    console.log('Changes detected:', event.changes);
});

3. Update the Read Model

The Read Model provides a simplified and optimized view of your data, making it easy to bind to UI components. It allows you to maintain a reactive and up-to-date interface.

You can use the updateReadModel method to add, update, or delete entities in your Read Model.

Example: Updating the Read Model Based on Events

narrative.subscribeToEvents([ChangesSavedEvent], (event: ChangesSavedEvent) => {
    // Handle added entities
    event.changes.added
        .filter((entity) => entity.type === 'CustomType') // Filter by type
        .forEach((entity) => {
            narrative.updateReadModel([
                {
                    id: entity.id,
                    type: entity.type,
                    data: entity,
                },
            ]);
        });

    // Handle updated entities
    event.changes.updated
        .filter((update) => update.entity.type === 'CustomType') // Filter by type
        .forEach((update) => {
            narrative.updateReadModel([
                {
                    id: update.entity.id,
                    type: update.entity.type,
                    data: update.entity,
                },
            ]);
        });

    // Handle deleted entities
    event.changes.deleted
        .filter((deleted) => deleted.type === 'CustomType') // Filter by type
        .forEach((deleted) => {
            narrative.updateReadModel((currentState) =>
                currentState.filter((item) => item.id !== deleted.id)
            );
        });
});

4. Integrate with Your Backend

To create a seamless user experience, you can integrate Narrative SDK with your backend services. This allows you to:

  • Store data for persistence or auditing.
  • Enrich the read model with additional backend data.
  • Trigger backend workflows based on Narrative Studio events.

Example: Sending Custom Entity Changes to Your Backend

narrative.subscribeToEvents([ChangesSavedEvent], async (event: ChangesSavedEvent) => {
  const customAddedEntities = event.changes.added.filter(
    (entity) => entity.type === 'CustomType'
  );

  const customUpdatedEntities = event.changes.updated.filter(
    (change) => change.entity.type === 'CustomType'
  );

  const customDeletedEntities = event.changes.deleted.filter(
    (deleted) => deleted.type === 'CustomType'
  );

  if (customAddedEntities.length > 0 || customUpdatedEntities.length > 0 || customDeletedEntities.length > 0) {
    await fetch('https://your-backend-api.com/entity-changes', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        added: customAddedEntities,
        updated: customUpdatedEntities,
        deleted: customDeletedEntities,
      }),
    });
  }
});