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

@oasiz/sdk

v1.2.0

Published

Typed SDK for Oasiz game platform bridge APIs.

Readme

@oasiz/sdk

Typed SDK for integrating games with the Oasiz platform. Handles score submission, haptic feedback, cross-session state persistence, multiplayer room codes, navigation hooks, and app lifecycle events for local development.

Install

npm install @oasiz/sdk@^0.1.0

Quick start

import { oasiz } from "@oasiz/sdk";

// 1. Emit score normalization config once on init
oasiz.emitScoreConfig({
  anchors: [
    { raw: 30, normalized: 100 },
    { raw: 60, normalized: 300 },
    { raw: 120, normalized: 600 },
    { raw: 300, normalized: 950 },
  ],
});

// 2. Load persisted state at the start of each session
const state = oasiz.loadGameState();
let level = typeof state.level === "number" ? state.level : 1;

// 3. Save state at checkpoints
oasiz.saveGameState({ level, coins: 42 });

// 4. Trigger haptics on key events
oasiz.triggerHaptic("medium");

// 5. Submit score when the game ends
oasiz.submitScore(score);

Score

oasiz.submitScore(score: number)

Submit the player's final score at game over. Call this exactly once per session, when the game ends. The platform handles leaderboard persistence — do not track high scores locally.

private onGameOver(): void {
  oasiz.submitScore(Math.floor(this.score));
}
  • score must be a non-negative integer. Floats are floored automatically.
  • Do not call on intermediate scores or level completions, only on final game over.

oasiz.emitScoreConfig(config)

Maps raw score values to the platform's normalized 0–1000 scale. Call once during initialization, not every frame.

oasiz.emitScoreConfig({
  anchors: [
    { raw: 10,  normalized: 100 }, // beginner
    { raw: 30,  normalized: 300 }, // good
    { raw: 75,  normalized: 600 }, // great
    { raw: 200, normalized: 950 }, // godlike
  ],
});

Anchor rules:

  • Exactly 4 anchors required.
  • raw values must be strictly increasing.
  • normalized values must end at exactly 950.
  • Choose thresholds based on realistic player skill bands.

Practical guidance by game type:

  • Survival / time games → use seconds survived as raw
  • Score accumulation games → use points as raw
  • Puzzle games → use level reached or stars earned as raw

Haptics

oasiz.triggerHaptic(type: HapticType)

Trigger native haptic feedback. Always guard with the user's haptics setting.

type HapticType = "light" | "medium" | "heavy" | "success" | "error";

| Type | When to use | |---|---| | "light" | UI button taps, menu navigation, D-pad press | | "medium" | Collecting items, standard collisions, scoring | | "heavy" | Explosions, major impacts, screen shake | | "success" | Level complete, new high score, achievement unlocked | | "error" | Damage taken, game over, invalid action |

// UI buttons — always light
button.addEventListener("click", () => {
  oasiz.triggerHaptic("light");
});

// Tiered hit feedback
private onBallHit(zone: "center" | "edge"): void {
  if (this.settings.haptics) {
    oasiz.triggerHaptic(zone === "center" ? "success" : "medium");
  }
}

// Game over
private onGameOver(): void {
  oasiz.submitScore(this.score);
  if (this.settings.haptics) {
    oasiz.triggerHaptic("error");
  }
}

Haptics are throttled internally (50ms cooldown) to prevent spam.


Game state persistence

Persist cross-session data such as unlocked levels, inventory, or lifetime stats. State is stored per-user per-game in the Oasiz backend — available across devices and app reinstalls.

oasiz.loadGameState(): Record<string, unknown>

Returns the player's saved state synchronously. Returns {} if no state has been saved yet. Call once at the start of the game.

private initFromSavedState(): void {
  const state = oasiz.loadGameState();
  this.level        = typeof state.level === "number" ? state.level : 1;
  this.lifetimeHits = typeof state.lifetimeHits === "number" ? state.lifetimeHits : 0;
  this.unlockedSkins = Array.isArray(state.unlockedSkins) ? state.unlockedSkins : [];
}

Always validate the shape of loaded data — it may be {} on first play.

oasiz.saveGameState(state: Record<string, unknown>)

Queues a debounced save. Saves are batched automatically — call freely at checkpoints without worrying about request spam.

// Save after each level completion
private onLevelComplete(): void {
  this.level += 1;
  oasiz.saveGameState({
    level: this.level,
    lifetimeHits: this.lifetimeHits,
    unlockedSkins: this.unlockedSkins,
  });
}

Rules:

  • State must be a plain JSON object (not an array or primitive).
  • Do not use localStorage for cross-session progress — use saveGameState so data syncs across platforms.
  • Do not store scores here — scores are submitted via submitScore.

oasiz.flushGameState()

Forces an immediate write, bypassing the debounce. Use at important checkpoints like game over or before the page unloads.

private onGameOver(): void {
  oasiz.saveGameState({ level: this.level, lifetimeHits: this.lifetimeHits });
  oasiz.flushGameState(); // ensure it lands before the page closes
  oasiz.submitScore(this.score);
}

Lifecycle

The platform dispatches lifecycle events when the app goes to the background or returns to the foreground. Subscribe to pause game loops and audio accordingly.

oasiz.onPause(callback: () => void): Unsubscribe

oasiz.onResume(callback: () => void): Unsubscribe

Both return an unsubscribe function.

const offPause  = oasiz.onPause(() => {
  this.gameLoop.stop();
  this.bgMusic.pause();
});

const offResume = oasiz.onResume(() => {
  this.gameLoop.start();
  this.bgMusic.play();
});

// Clean up when the game is destroyed
offPause();
offResume();

Navigation

Use navigation hooks when your game needs to control back behavior (Android back / web Escape) or participate in host-driven close events.

oasiz.onBackButton(callback: () => void): Unsubscribe

Registers a callback for platform back actions. While at least one back listener is subscribed, back actions are routed to your game instead of immediately closing it.

Use this for pause menus, in-game overlays, or custom back-stack behavior.

const offBack = oasiz.onBackButton(() => {
  if (this.isPauseMenuOpen) {
    this.closePauseMenu();
    return;
  }
  this.openPauseMenu();
});

// Restore default host back behavior when no longer needed
offBack();

oasiz.leaveGame(): void

Programmatically request the host to close the current game (for example, from a Quit button inside your game UI).

quitButton.addEventListener("click", () => {
  oasiz.leaveGame();
});

oasiz.onLeaveGame(callback: () => void): Unsubscribe

Registers a callback fired when the host initiates closing the game (for example, close button, gesture, or host navigation). Use this for lightweight cleanup.

const offLeave = oasiz.onLeaveGame(() => {
  oasiz.flushGameState();
  this.bgMusic.pause();
});

// Clean up listener when destroyed
offLeave();

Multiplayer

oasiz.shareRoomCode(code: string | null, options?: { inviteOverride?: boolean })

Notify the platform of the active multiplayer room so friends can join via the invite system. Pass null when leaving a room.

Set inviteOverride: true when your game wants to hide the platform invite pill and render its own invite button/UI. The platform still tracks the room code, but your game owns the invite entry point.

import { insertCoin, getRoomCode } from "playroomkit";
import { oasiz } from "@oasiz/sdk";

await insertCoin({ skipLobby: true });
oasiz.shareRoomCode(getRoomCode());

// On disconnect
oasiz.shareRoomCode(null);
// Game-owned invite UI: hide the platform pill, keep room tracking
oasiz.shareRoomCode(getRoomCode(), { inviteOverride: true });

If you still want to use the platform invite sheet from your own in-game button, combine it with openInviteModal():

import { openInviteModal, shareRoomCode } from "@oasiz/sdk";

shareRoomCode("ABCD", { inviteOverride: true });

inviteButton.addEventListener("click", () => {
  openInviteModal();
});

Read-only injected values

These are populated by the platform before the game loads. Always check for undefined before using.

// The platform's internal game ID
const gameId = oasiz.gameId;

// Pre-filled room code for auto-joining a friend's session
if (oasiz.roomCode) {
  await connectToRoom(oasiz.roomCode);
}

// Player identity for multiplayer games
const name   = oasiz.playerName;
const avatar = oasiz.playerAvatar;

Named exports

All methods are also available as named exports if you prefer not to use the oasiz namespace object:

import {
  submitScore,
  emitScoreConfig,
  triggerHaptic,
  loadGameState,
  saveGameState,
  flushGameState,
  shareRoomCode,
  onPause,
  onResume,
  onBackButton,
  onLeaveGame,
  leaveGame,
  getGameId,
  getRoomCode,
  getPlayerName,
  getPlayerAvatar,
} from "@oasiz/sdk";

TypeScript types

import type { HapticType, ScoreConfig, ScoreAnchor, GameState } from "@oasiz/sdk";

Local development

All methods safely no-op when the platform bridges are not injected. In development mode a console warning is logged so you know the call was made:

[oasiz/sdk] submitScore bridge is unavailable. This is expected in local development.

No crashes, no special setup required for local dev.