@verse8/platform
v2.1.0
Published
Verse8 platform SDK — React hook + framework-agnostic browser API for VX Shop
Readme
@verse8/platform
Verse8 platform SDK — exposes the VX Shop to game clients through two surfaces:
- React hook —
useVXShop(unchanged from v1.0.x). - Framework-agnostic API —
VXShopnamespace, importable as ESM/CJS or loadable via<script>tag withwindow.VXShop.
Both surfaces share a single in-memory store: calling VXShop.refresh() from
a vanilla page updates state observed by a mounted useVXShop hook, and vice
versa.
Install
pnpm add @verse8/platform
# or
npm install @verse8/platform
# or
yarn add @verse8/platformScript tag / CDN
<script src="https://unpkg.com/@verse8/platform/dist/index.global.js"></script>
<script>
// window.VXShop is now available
VXShop.init({ verseId: 'my-verse' });
VXShop.subscribe(({ items, isLoading, error }) => {
if (isLoading || error) return;
renderShop(items);
});
document.getElementById('buy').onclick = () =>
VXShop.buyItem('event-pack');
</script>A jsdelivr field is also published; swap unpkg.com for
cdn.jsdelivr.net/npm/... if preferred.
Quick start
React (existing)
import { useVXShop } from '@verse8/platform';
import { useEffect } from 'react';
function Shop() {
const { items, getItem, buyItem, refresh, onClose } = useVXShop();
useEffect(() => {
const cancel = onClose(() => refresh());
return cancel;
}, [onClose, refresh]);
return (
<button onClick={() => buyItem('event-pack')}>
Buy event pack — {getItem('event-pack')?.price ?? '...'}
</button>
);
}Vanilla ESM
For pure vanilla use with no React installed, import from the /vanilla
subpath — the default entry pulls in react at module load time because it
re-exports useVXShop:
import { VXShop } from '@verse8/platform/vanilla';
VXShop.init({ verseId: 'my-verse' });
const unsub = VXShop.subscribe((state) => {
if (state.error) console.error(state.error);
if (!state.isLoading) updateUI(state.items);
});
document.getElementById('buy')!.onclick = () => {
VXShop.buyItem('event-pack');
};Named imports tree-shake too:
import { init, getItem, buyItem } from '@verse8/platform/vanilla';
init();
buyItem(getItem('event-pack')!.productId);CommonJS
// Without React installed
const { VXShop } = require('@verse8/platform/vanilla');
VXShop.init();
console.log(VXShop.getItems());
// With React installed (also exposes useVXShop)
const { VXShop, useVXShop } = require('@verse8/platform');<script> tag
<script src="https://unpkg.com/@verse8/platform/dist/index.global.js"></script>
<script>
VXShop.init();
VXShop.refresh().then(() => {
console.log(VXShop.getItem('event-pack'));
});
</script>API
VXShop.init(opts?: VXShopInitOptions): void
Sets the shop parameters and (by default) triggers an initial refresh. Safe to
call multiple times — the underlying fetch is throttled to 5 seconds, so rapid
repeat init() calls coalesce into a single network request.
interface VXShopInitOptions {
/** Override the verseId. Otherwise resolved from VITE_AGENT8_VERSE env or `verseId` query param. */
verseId?: string;
/** Override the account. Otherwise resolved from `account` query param or AGENT8_ACCOUNT env on Verse8 hosts. */
account?: string;
/** Set to false to skip the initial fetch. Default: true. */
autoRefresh?: boolean;
}Calling init is optional: every public function lazy-inits with defaults the
first time it's invoked. Call init explicitly only to override verseId,
account, or disable auto-refresh.
VXShop.getItem(productId): VXShopItem | undefined
Synchronous lookup against the current cache. Returns undefined until the
first refresh() resolves.
VXShop.getItems(): VXShopItem[]
Snapshot of the current items array. Empty until the first fetch resolves.
VXShop.buyItem(productId): void
Posts an OPEN_VX_SHOP_DIALOG message to window.parent so the Verse8 host
opens the payment dialog. Use onClose to observe the result.
VXShop.refresh(): Promise<void>
Re-fetches the item list. Throttled to 5 seconds — rapid repeat calls coalesce.
VXShop.onClose(cb): () => void
Registers a callback invoked when the payment dialog closes (with
{ purchased: boolean; productId: string; action: 'purchased' | 'closed' }).
Returns an unsubscribe function.
VXShop.subscribe(listener): () => void
Registers a listener invoked on every state change with the current
{ items, isLoading, error }. Returns an unsubscribe function.
VXShop.getState(): VXShopState
Snapshot of current public state: { items, isLoading, error }.
useVXShop(options?): UseVXShopResult (React)
Returns:
| Property | Type |
|---------------|------------------------------------------------------------|
| items | VXShopItem[] |
| isLoading | boolean |
| error | string \| null |
| getItem | (productId: string) => VXShopItem \| undefined |
| buyItem | (productId: string) => void |
| refresh | () => Promise<void> |
| onClose | (cb: (payload) => void) => () => void |
Auto-refreshes on first mount. Backward-compatible with v1.0.x — no public-API changes.
Parameter resolution
Both surfaces resolve verseId and account from the same sources. Priority
differs slightly by field:
verseId — explicit option → VITE_AGENT8_VERSE env (or
NEXT_PUBLIC_AGENT8_VERSE on Next.js) → ?verseId= query parameter.
account — explicit option → ?account= query parameter →
VITE_AGENT8_ACCOUNT env (only honored on agent8-games.verse8.io and
agent8-container.* hostnames).
If either field cannot be resolved, the next refresh() records an error in
state and subscribe listeners are notified.
Migration from v1.0.x
No breaking changes. The useVXShop hook's return shape, semantics, and
auto-refresh trigger are byte-identical to v1.0.13. Just upgrade.
- "@verse8/platform": "^1.0.13"
+ "@verse8/platform": "^1.1.0"License
UNLICENSED — Verse8 internal.
