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

@jkaibuildiq/bridge-contracts

v1.1.0

Published

Shared JWT contracts, helpers, and TypeScript types for the aiiq.world <-> predict.aiiq.world bridge (SSO + cross-platform wallet + activity feed)

Readme

@jkaibuildiq/bridge-contracts

Shared JWT contracts, helpers, and TypeScript types for the aiiq.world ⇄ predict.aiiq.world bridge:

  • SSO tokens (aiiq → predict, so signed-in aiiq users land on predict already authed)
  • Service tokens (predict ⇄ aiiq, for wallet transfers + cross-platform activity feed)
  • The canonical type definitions (TransferRequest, TransferResponse, ActivityItem, etc.) both apps depend on

Both repos must run against the same version. A drift between sides manifests as silent Invalid service token / Invalid or expired token errors with no other signal — exactly the class of bug this package exists to prevent.

Install

npm install @jkaibuildiq/bridge-contracts
# or
pnpm add @jkaibuildiq/bridge-contracts

jose is a peer dependency (any ^5 or ^6). Both consumer apps already have it; if you're starting fresh:

pnpm add jose

What's exported

Constants (./constants)

import {
  PREDICT_ORIGIN,
  SSO_ISSUER,        // "aiiq.world"
  SSO_AUDIENCE,      // "predict.aiiq.world"
  SERVICE_ISSUER,    // "predict.aiiq.world"
  SERVICE_AUDIENCE,  // "aiiq.world"
  SERVICE_SCOPES,    // { WALLET_TRANSFER, WALLET_READ }
  MIN_SECRET_LENGTH, // 32
  DEFAULT_SSO_TTL,   // "30s"
  DEFAULT_SERVICE_TTL, // "60s"
} from "@jkaibuildiq/bridge-contracts";

Types (./types)

import type {
  BridgeUser,
  BridgeTokenPayload,
  ServiceTokenPayload,
  TransferAction,
  TransferRequest,
  TransferResponse,
  TransferSuccess,
  TransferFailure,
  ActivityItem,
  ActivityType,
  ActivityResponse,
} from "@jkaibuildiq/bridge-contracts";

SSO helpers

import {
  createBridgeToken,
  verifyBridgeToken,
  buildPredictUrl,
} from "@jkaibuildiq/bridge-contracts";

Service-token helpers

import {
  createServiceToken,
  verifyServiceToken,
} from "@jkaibuildiq/bridge-contracts";

Usage — aiiq.world side (token producer for SSO, verifier for service tokens)

Mint an SSO token + redirect

// src/app/api/auth/bridge-redirect/route.ts
import { NextRequest, NextResponse } from "next/server";
import { validateSession } from "@/lib/auth";
import { createBridgeToken, buildPredictUrl } from "@jkaibuildiq/bridge-contracts";

export async function GET(request: NextRequest) {
  const secret = process.env.BRIDGE_JWT_SECRET!;
  const session = await validateSession(/* ... */);
  if (!session) {
    return NextResponse.redirect("https://predict.aiiq.world/dashboard");
  }
  const token = await createBridgeToken(secret, {
    id: session.user.id,
    email: session.user.email,
    username: session.user.username,
  });
  return NextResponse.redirect(buildPredictUrl(token, "/dashboard"));
}

Verify a service token (wallet bridge endpoint)

// src/app/api/aoa/wallet/bridge-transfer/route.ts
import { verifyServiceToken, SERVICE_SCOPES } from "@jkaibuildiq/bridge-contracts";

const auth = req.headers.get("authorization");
if (!auth?.startsWith("Bearer ")) return Response.json(..., { status: 401 });

try {
  await verifyServiceToken(
    process.env.BRIDGE_JWT_SECRET!,
    auth.slice(7),
    SERVICE_SCOPES.WALLET_TRANSFER,
  );
} catch {
  return Response.json(..., { status: 403 });
}

Usage — predict.aiiq.world side (verifier for SSO, producer for service tokens)

Verify an inbound SSO token

// src/app/api/auth/bridge/route.ts
import { verifyBridgeToken } from "@jkaibuildiq/bridge-contracts";

const { token } = await req.json();
const payload = await verifyBridgeToken(process.env.BRIDGE_JWT_SECRET!, token);
// payload: { sub: <aiiq user id>, email, username, iss, aud, iat, exp }
const externalUserId = payload.sub;
const externalEmail  = payload.email?.toLowerCase().trim();
const externalUsername = payload.username.trim();

Mint a service token + call aiiq's wallet bridge

import {
  createServiceToken,
  SERVICE_SCOPES,
  type TransferRequest,
  type TransferResponse,
} from "@jkaibuildiq/bridge-contracts";

async function callMainBridge(payload: TransferRequest): Promise<TransferResponse> {
  const token = await createServiceToken(process.env.BRIDGE_JWT_SECRET!, {
    scope: SERVICE_SCOPES.WALLET_TRANSFER,
    ref: payload.reference,
  });
  const res = await fetch(
    "https://aiiq.world/api/aoa/wallet/bridge-transfer",
    {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${token}`,
      },
      body: JSON.stringify(payload),
    }
  );
  return (await res.json()) as TransferResponse;
}

Mint a service token + read aiiq activity

import {
  createServiceToken,
  SERVICE_SCOPES,
  type ActivityResponse,
} from "@jkaibuildiq/bridge-contracts";

const token = await createServiceToken(process.env.BRIDGE_JWT_SECRET!, {
  scope: SERVICE_SCOPES.WALLET_READ,
  ref: `dashboard-activity-${userId}`,
});
const url = new URL("https://aiiq.world/api/aoa/bridge/activity");
url.searchParams.set("externalUserId", aiiqUserId);
url.searchParams.set("limit", "25");
const res = await fetch(url, { headers: { Authorization: `Bearer ${token}` } });
if (res.status === 404) {
  // endpoint not yet deployed — silently degrade
} else if (!res.ok) {
  // 401/403/500 — surface as "aiiq activity unavailable"
} else {
  const data = (await res.json()) as ActivityResponse;
  // data.items: ActivityItem[]
}

Wire reference

SSO token (aiiq → predict)

header:  { alg: "HS256", typ: "JWT" }
payload: {
  iss: "aiiq.world",
  aud: "predict.aiiq.world",
  sub: <aiiq user UUID>,
  email: <string | undefined>,
  username: <string>,
  iat: <unix>,
  exp: <iat + 30s>,
}

Service token (predict → aiiq)

header:  { alg: "HS256", typ: "JWT" }
payload: {
  iss: "predict.aiiq.world",
  aud: "aiiq.world",
  scope: "wallet_transfer" | "wallet_read" | "position_read",
  ref: <correlation id>,
  iat: <unix>,
  exp: <iat + 60s>,
}

Widget token (aiiq → predict, v1.1+)

header:  { alg: "HS256", typ: "JWT" }
payload: {
  iss: "aiiq.world",
  aud: "predict.aiiq.world",
  scope: "widget_proxy",       // always — bound inside createWidgetToken
  ref: <correlation id>,        // required — per-request crypto.randomUUID()
  iat: <unix>,
  exp: <iat + 60s>,
}

Changelog

1.1.0 — additive, forward-compatible with 1.0

  • createWidgetToken / verifyWidgetToken — bidirectional widget bridge primitive for the aiiq → predict direction (existing createServiceToken / verifyServiceToken go predict → aiiq and stay unchanged). Aiiq mints, predict verifies. Scope is hardcoded to SERVICE_SCOPES.WIDGET_PROXY inside both functions — caller cannot mint with the wrong scope, verifier cannot accept the wrong scope. ref is required on verify (predict's widget audit log keys on it). Backs the four widget bridge endpoints (widget-snapshot / widget-trade / widget-balance / widget-telemetry) — see the spec in aoa-predict docs/aiiq-widget-spec.md / age-of-ai docs/widget-implementation.md.
  • WIDGET_ISSUER / WIDGET_AUDIENCE constants. Hostname values happen to equal SSO_ISSUER / SSO_AUDIENCE today but are kept distinct so the wire-format intent stays self-documenting if either ever moves origin.
  • SERVICE_SCOPES.POSITION_READ = "position_read" for a future GET /api/aoa/bridge/positions endpoint. Reserved under Option B (aiiq gameplay is in-memory; the endpoint returns { positions: [] } until aiiq grows a real-money trading surface).
  • SERVICE_SCOPES.WIDGET_PROXY = "widget_proxy" for the cross-site QuickPredict widget mounted on aiiq.world. Backs the aiiq proxy routes and the matching predict-side widget-* bridge endpoints. Carved out of wallet_transfer so it can be revoked independently and rate-limited differently. Wire shapes for the widget request/response payloads themselves are deferred to v1.2 once the implementation settles.
  • ACTIVITY_TYPES adds "REFUND" and "RAKE". Predict-emitted on its own side; aiiq doesn't emit these because aiiq has no persisted gameplay to refund or rake from.
  • New AiiqPosition + PositionsResponse types for the same future endpoint.

No removals, no shape changes — 1.0.0 consumers continue to work unchanged.

1.0.0 — initial release

Issuer / audience / scope strings, token shapes, TransferRequest / TransferResponse / ActivityItem / ActivityResponse types, createBridgeToken / verifyBridgeToken / createServiceToken / verifyServiceToken helpers, buildPredictUrl.

Versioning

  • Patch (1.0.x) — internal helper refactors, doc fixes, no wire changes.
  • Minor (1.x.0) — additive new types / helpers (e.g. new scope, new activity type). Backwards compatible.
  • Major (x.0.0) — wire-breaking changes (issuer/audience/scope renames, token shape changes). Coordinated deploy required.

License

MIT