Skip to content

@scenarix/jaduspine-sdk

Internal realtime messaging SDK wrapping Centrifugo for WebSocket and HTTP communication.

Installation

Note: This is a private package hosted on GitHub Packages.

1. Configure npm registry

Create or update .npmrc in your project root:

@scenarix:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${GITHUB_PKG_TOKEN}

2. Set up authentication

Create a GitHub Personal Access Token with read:packages scope:

export GITHUB_PKG_TOKEN=ghp_xxxxxxxxxxxx

3. Install

npm install @scenarix/jaduspine-sdk
# or
yarn add @scenarix/jaduspine-sdk
# or
pnpm add @scenarix/jaduspine-sdk

Frontend Integration

React Provider & Hook

The SDK provides a React context for sharing a single WebSocket connection across your app.

1. Wrap your app with the provider:

// app/providers.tsx
import {
  JaduSpineProvider,
  FrontendChannels,
} from "@scenarix/jaduspine-sdk/react";

export function Providers({
  children,
  token,
}: {
  children: React.ReactNode;
  token: string;
}) {
  return (
    <JaduSpineProvider
      wsUrl={process.env.NEXT_PUBLIC_CENTRIFUGO_WS_URL!}
      token={token}
      frontend={FrontendChannels.MINIMATICS}
      debug={process.env.NODE_ENV === "development"}
    >
      {children}
    </JaduSpineProvider>
  );
}

2. Use the hook in any component:

import { useEffect } from "react";
import { useJaduSpine, ConnectionState } from "@scenarix/jaduspine-sdk/react";

function NotificationListener() {
  const { connectionState, onMessage, userId } = useJaduSpine();

  useEffect(() => {
    const unsubscribe = onMessage((msg) => {
      console.log("Received:", msg.payload);
      // Handle message based on msg.type
    });
    return unsubscribe;
  }, [onMessage]);

  return (
    <div>
      {connectionState === ConnectionState.CONNECTED
        ? `Connected as ${userId}`
        : "Connecting..."}
    </div>
  );
}

Provider Props

Prop Type Required Default Description
wsUrl string Yes - Centrifugo WebSocket URL
token string Yes - JWT token (must contain sub claim)
frontend FrontendChannels Yes - Frontend app identifier
debug boolean No false Enable debug logging
autoConnect boolean No true Auto-connect on mount

Hook Return Value

const {
  connectionState, // ConnectionState: DISCONNECTED | CONNECTING | CONNECTED
  userId, // string | null - extracted from JWT
  clientId, // string | null - unique client ID
  connect, // () => void - manual connect
  disconnect, // () => void - disconnect
  destroy, // () => void - cleanup
  onMessage, // (handler) => unsubscribe - listen for messages
  onConnectionStateChange, // (handler) => unsubscribe - listen for state changes
  onEvent, // (handler) => unsubscribe - listen for all events
} = useJaduSpine();

Backend Integration

Initialize once at server startup, then use JaduSpineBackend.publish() from anywhere in your codebase.

1. Initialize at server bootstrap:

// server/index.ts or app initialization
import { JaduSpineBackend } from "@scenarix/jaduspine-sdk";

await JaduSpineBackend.init({
  apiUrl: process.env.CENTRIFUGO_API_URL!,
  apiKey: process.env.CENTRIFUGO_API_KEY!,
  debug: process.env.NODE_ENV === "development",
});
// Throws if health check fails (server unreachable or invalid API key)

2. Use anywhere in your codebase:

// services/notifications.ts
import {
  JaduSpineBackend,
  FrontendChannel,
  FrontendChannels,
} from "@scenarix/jaduspine-sdk";

export async function notifyUser(userId: string, message: string) {
  await JaduSpineBackend.publish(
    new FrontendChannel(FrontendChannels.MINIMATICS, userId),
    { type: "notification", message }
  );
}
// api/jobs/complete.ts
import {
  JaduSpineBackend,
  FrontendChannel,
  FrontendChannels,
} from "@scenarix/jaduspine-sdk";

export async function handleJobComplete(userId: string, jobId: string) {
  await JaduSpineBackend.publish(
    new FrontendChannel(FrontendChannels.STUDIO, userId),
    { type: "job:complete", jobId, timestamp: Date.now() }
  );
}

Init Configuration

Option Type Required Default Description
apiUrl string Yes - Centrifugo HTTP API URL
apiKey string No "" API key for authentication
clientId string No Auto-generated Custom client identifier
debug boolean No false Enable debug logging
skipHealthCheck boolean No false Skip server health check on init
retryAttempts number No 0 Number of retry attempts on publish fail
retryDelay number No 1000 Delay between retries (ms)

Static Methods

// Initialize (call once)
await JaduSpineBackend.init(config);

// Check if initialized
JaduSpineBackend.isInitialized(); // boolean

// Publish to a channel
await JaduSpineBackend.publish(channel, payload, type?);

// Broadcast to multiple channels
await JaduSpineBackend.broadcast(channels, payload, type?);

// Publish pre-formatted SpineMessage
await JaduSpineBackend.publishRaw(channel, message);

// Get underlying JaduSpineHttp instance
JaduSpineBackend.getInstance();

// Destroy singleton (for reinit or shutdown)
JaduSpineBackend.destroy();

Channel Classes

import {
  FrontendChannel,
  FrontendChannels,
  BackendChannel,
  BackendChannels,
} from "@scenarix/jaduspine-sdk";

// User-scoped frontend channel: "frontend:minimatics#user123"
const userChannel = new FrontendChannel(FrontendChannels.MINIMATICS, "user123");
console.log(userChannel.value); // "frontend:minimatics#user123"

// Backend channel: "backend:studio"
const studioChannel = new BackendChannel(BackendChannels.STUDIO);
console.log(studioChannel.value); // "backend:studio"

Available Channels

// Frontend (user-scoped)
FrontendChannels.MINIMATICS; // "minimatics"
FrontendChannels.STORYDESK; // "storydesk"
FrontendChannels.STUDIO; // "studio"
FrontendChannels.PLAYGROUND; // "playground"

// Backend (service-to-service)
BackendChannels.STUDIO; // "studio"
BackendChannels.STORYDESK; // "storydesk"
BackendChannels.PLAYGROUND; // "playground"

Error Handling

import { SpineError, SpineErrorCode } from "@scenarix/jaduspine-sdk";

try {
  await JaduSpineBackend.publish(channel, data);
} catch (error) {
  if (error instanceof SpineError) {
    switch (error.code) {
      case SpineErrorCode.HTTP_UNAUTHORIZED:
        console.error("Invalid API key");
        break;
      case SpineErrorCode.HTTP_REQUEST_FAILED:
        console.error("Request failed:", error.message);
        break;
      case SpineErrorCode.INVALID_STATE:
        console.error("Not initialized");
        break;
    }
  }
}

Publishing

Ensure your GITHUB_PKG_TOKEN has write:packages scope (not just read:packages).

# 1. Run pre-publish checks (build, typecheck, test)
npm run prepublish:check

# 2. Bump version in package.json

# 3. Publish to GitHub Packages
npm publish

# Or dry-run first
npm run publish:dry

Available Scripts

Script Description
npm run build Build the SDK (CJS + ESM + types)
npm run typecheck Run TypeScript type checking
npm run test Run tests
npm run prepublish:check Clean, build, typecheck, and test
npm run publish:dry Dry-run publish to verify package

Documentation