@bounded-sh/core 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/client/live.d.ts +44 -4
- package/dist/client/operations.d.ts +7 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +85 -28
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +85 -29
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ function getConfig(): Promise<ClientConfig>;
|
|
|
63
63
|
function get(path: string): Promise<any>;
|
|
64
64
|
function getMany(paths: string[], options?: { bypassCache?: boolean }): Promise<GetManyResult[]>;
|
|
65
65
|
function set(path: string, data: any, options?: SetOptions): Promise<any>;
|
|
66
|
-
function setMany(
|
|
66
|
+
function setMany(many: { path: string; document: any }[], options?: SetOptions): Promise<any>;
|
|
67
67
|
function setFile(path: string, file: File, metadata?: any): Promise<any>;
|
|
68
68
|
function getFiles(path: string): Promise<any>;
|
|
69
69
|
function runQuery(queryString: string, variables?: any): Promise<any>;
|
|
@@ -75,4 +75,4 @@ function subscribe(path: string, options?: SubscriptionOptions): Promise<() => v
|
|
|
75
75
|
|
|
76
76
|
## Contributing
|
|
77
77
|
|
|
78
|
-
Please see the main repository for contribution guidelines.
|
|
78
|
+
Please see the main repository for contribution guidelines.
|
package/dist/client/live.d.ts
CHANGED
|
@@ -12,6 +12,31 @@ export type LiveIntentOptions = {
|
|
|
12
12
|
*/
|
|
13
13
|
fireAndForget?: boolean;
|
|
14
14
|
};
|
|
15
|
+
export type LiveStatusOptions = {
|
|
16
|
+
/** Per-call timeout (ms) for the HTTP request to the realtime worker. */
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
/** Extra headers (advanced/testing). */
|
|
19
|
+
headers?: Record<string, string>;
|
|
20
|
+
};
|
|
21
|
+
export type LiveStopReason = 'idle' | 'lifetime' | 'error' | 'manual' | 'evicted' | string;
|
|
22
|
+
export type LiveStatus = {
|
|
23
|
+
available: boolean;
|
|
24
|
+
started: boolean;
|
|
25
|
+
module?: string;
|
|
26
|
+
etag?: string;
|
|
27
|
+
running?: boolean;
|
|
28
|
+
tick?: number;
|
|
29
|
+
lastErr?: string | null;
|
|
30
|
+
stopReason?: LiveStopReason | null;
|
|
31
|
+
lastTickAt?: number | null;
|
|
32
|
+
nextTickAt?: number | null;
|
|
33
|
+
nextAlarmAt?: number | null;
|
|
34
|
+
generation?: number;
|
|
35
|
+
connections?: number;
|
|
36
|
+
startedAtMs?: number | null;
|
|
37
|
+
stoppedAtMs?: number | null;
|
|
38
|
+
reason?: string;
|
|
39
|
+
};
|
|
15
40
|
export declare class LiveIntentError extends Error {
|
|
16
41
|
statusCode?: number | undefined;
|
|
17
42
|
details?: any | undefined;
|
|
@@ -33,8 +58,21 @@ export declare class LiveIntentError extends Error {
|
|
|
33
58
|
export declare function intent(roomPath: string, intent: unknown, opts?: LiveIntentOptions): Promise<{
|
|
34
59
|
ok: true;
|
|
35
60
|
}>;
|
|
61
|
+
/**
|
|
62
|
+
* Fetch the runtime status for a live room.
|
|
63
|
+
*
|
|
64
|
+
* const s = await bounded.live.status('rooms/abc');
|
|
65
|
+
* console.log(s.running, s.stopReason, s.generation, s.etag);
|
|
66
|
+
*
|
|
67
|
+
* This is diagnostic/ops surface. It reports whether the room facet exists,
|
|
68
|
+
* whether it is currently ticking, why it last stopped, which module etag is
|
|
69
|
+
* loaded, and the current generation used after terminal restarts.
|
|
70
|
+
*/
|
|
71
|
+
export declare function status(roomPath: string, opts?: LiveStatusOptions): Promise<LiveStatus>;
|
|
36
72
|
export type SubscribeViewOptions = {
|
|
37
|
-
/**
|
|
73
|
+
/** User id whose view to read. Defaults to the logged-in user's @user.id. */
|
|
74
|
+
userId?: string;
|
|
75
|
+
/** Legacy alias for wallet-address keyed view docs. Prefer userId for new rooms. */
|
|
38
76
|
address?: string;
|
|
39
77
|
/** Called with the latest per-player view document. */
|
|
40
78
|
onData: (view: any) => void;
|
|
@@ -44,10 +82,11 @@ export type SubscribeViewOptions = {
|
|
|
44
82
|
/**
|
|
45
83
|
* Subscribe to YOUR per-player view of a room. Thin sugar over:
|
|
46
84
|
*
|
|
47
|
-
* subscribe('<roomPath>/view/<
|
|
85
|
+
* subscribe('<roomPath>/view/<myUserId>', { onData, onError })
|
|
48
86
|
*
|
|
49
|
-
* The
|
|
50
|
-
*
|
|
87
|
+
* The view id defaults to the logged-in user's @user.id (from the session token
|
|
88
|
+
* claims); pass `opts.userId` to override. `opts.address` is kept as a legacy
|
|
89
|
+
* alias for older wallet-address keyed policies. Returns the unsubscribe
|
|
51
90
|
* function (a Promise<() => Promise<void>>, same as `subscribe`).
|
|
52
91
|
*
|
|
53
92
|
* Note: this is a browser-first helper (the WS subscription manager is
|
|
@@ -57,5 +96,6 @@ export declare function subscribeView(roomPath: string, opts: SubscribeViewOptio
|
|
|
57
96
|
/** The `bounded.live` namespace surface. */
|
|
58
97
|
export declare const live: {
|
|
59
98
|
intent: typeof intent;
|
|
99
|
+
status: typeof status;
|
|
60
100
|
subscribeView: typeof subscribeView;
|
|
61
101
|
};
|
|
@@ -236,6 +236,13 @@ export declare function runExpressionMany(many: {
|
|
|
236
236
|
returnType?: 'Bool' | 'String' | 'Int' | 'UInt';
|
|
237
237
|
_overrides?: RequestOverrides;
|
|
238
238
|
}[]): Promise<RunExpressionResult[]>;
|
|
239
|
+
/**
|
|
240
|
+
* Write a document at `path`. Sugar for a one-element {@link setMany}.
|
|
241
|
+
*
|
|
242
|
+
* **Delete:** pass `null` as the document to delete it — `set(path, null)` is
|
|
243
|
+
* the delete (there is no separate `del`/`remove`). It is routed through the
|
|
244
|
+
* collection's policy `delete` rule and broadcasts a delete to subscribers.
|
|
245
|
+
*/
|
|
239
246
|
export declare function set(path: string, document: any, options?: SetOptions): Promise<any>;
|
|
240
247
|
export declare function setMany(many: {
|
|
241
248
|
path: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -23,7 +23,7 @@ export { RealtimeStore, getRealtimeStore, resetRealtimeStore } from './client/re
|
|
|
23
23
|
export type { StorageTier, SubscriptionStatus, SubscriptionState, SubscribeOptions, DeltaChange } from './client/realtime-store';
|
|
24
24
|
export { functions, invoke as invokeFunction, FunctionInvokeError } from './client/functions';
|
|
25
25
|
export type { InvokeOptions } from './client/functions';
|
|
26
|
-
export { live, intent as liveIntent, subscribeView as subscribeLiveView, LiveIntentError } from './client/live';
|
|
27
|
-
export type { LiveIntentOptions, SubscribeViewOptions } from './client/live';
|
|
26
|
+
export { live, intent as liveIntent, status as liveStatus, subscribeView as subscribeLiveView, LiveIntentError } from './client/live';
|
|
27
|
+
export type { LiveIntentOptions, LiveStatus, LiveStatusOptions, SubscribeViewOptions } from './client/live';
|
|
28
28
|
export { withEffects, isEffectResult, defineLiveModule, EFFECT_INTENT_ADDRESS } from './client/live-effects';
|
|
29
29
|
export type { Effect, EffectKind, EffectResult, LiveIntent, LiveTickResult, LiveModule } from './client/live-effects';
|
package/dist/index.js
CHANGED
|
@@ -4929,6 +4929,13 @@ async function runExpressionMany(many) {
|
|
|
4929
4929
|
throw error;
|
|
4930
4930
|
}
|
|
4931
4931
|
}
|
|
4932
|
+
/**
|
|
4933
|
+
* Write a document at `path`. Sugar for a one-element {@link setMany}.
|
|
4934
|
+
*
|
|
4935
|
+
* **Delete:** pass `null` as the document to delete it — `set(path, null)` is
|
|
4936
|
+
* the delete (there is no separate `del`/`remove`). It is routed through the
|
|
4937
|
+
* collection's policy `delete` rule and broadcasts a delete to subscribers.
|
|
4938
|
+
*/
|
|
4932
4939
|
async function set(path, document, options) {
|
|
4933
4940
|
const result = await setMany([{ path, document }], options);
|
|
4934
4941
|
// Clear cache entries that might be affected by this update
|
|
@@ -7510,6 +7517,7 @@ const functions = { invoke };
|
|
|
7510
7517
|
// collection. This file is the consumer surface around that runtime:
|
|
7511
7518
|
//
|
|
7512
7519
|
// bounded.live.intent(roomPath, intent) -> POST {realtime}/live/intent
|
|
7520
|
+
// bounded.live.status(roomPath) -> GET {realtime}/live/status
|
|
7513
7521
|
//
|
|
7514
7522
|
// It mirrors `functions.invoke` exactly: it attaches the caller's session token
|
|
7515
7523
|
// AUTOMATICALLY (the same token the SDK uses for data reads/writes), and throws
|
|
@@ -7522,18 +7530,14 @@ const functions = { invoke };
|
|
|
7522
7530
|
// Addressing: clients address rooms BY PATH (the `path` field in the body). The
|
|
7523
7531
|
// worker derives the roomId from `body.path` and sets X-Room-Id internally;
|
|
7524
7532
|
// clients NEVER set X-Room-Id. The caller's address is taken server-side from
|
|
7525
|
-
// the Authorization header (auth.userAddress) — it is NOT sent in the
|
|
7533
|
+
// the Authorization header (auth.userId/userAddress) — it is NOT sent in the
|
|
7534
|
+
// body.
|
|
7526
7535
|
//
|
|
7527
7536
|
// Subscribing to your view: a per-player view doc lives at
|
|
7528
|
-
// `<roomPath>/view/<
|
|
7529
|
-
// `rooms/$roomId/view/$
|
|
7530
|
-
//
|
|
7531
|
-
//
|
|
7532
|
-
// `subscribeView()` below is thin sugar over that (path construction + address
|
|
7533
|
-
// defaulting); it adds zero new transport.
|
|
7534
|
-
//
|
|
7535
|
-
// Status polling (GET /live/status?path=<sessionCollection>/<roomId>) is out of
|
|
7536
|
-
// scope for this piece — see follow-up.
|
|
7537
|
+
// `<roomPath>/view/<myUserId>` (the policy declares
|
|
7538
|
+
// `rooms/$roomId/view/$userId` ephemeral with `read: $userId == @user.id`).
|
|
7539
|
+
// Wallet-address keyed view paths remain supported through opts.address for
|
|
7540
|
+
// older policies, but new live rooms should key views by @user.id.
|
|
7537
7541
|
// ---------------------------------------------------------------------------
|
|
7538
7542
|
class LiveIntentError extends Error {
|
|
7539
7543
|
constructor(message, statusCode, details) {
|
|
@@ -7614,7 +7618,7 @@ async function intent(roomPath, intent, opts = {}) {
|
|
|
7614
7618
|
res = await fetch(`${base}/live/intent`, {
|
|
7615
7619
|
method: 'POST',
|
|
7616
7620
|
headers,
|
|
7617
|
-
body: JSON.stringify({ path:
|
|
7621
|
+
body: JSON.stringify({ path: normalizedRoomPath, intent }),
|
|
7618
7622
|
signal: controller.signal,
|
|
7619
7623
|
});
|
|
7620
7624
|
}
|
|
@@ -7642,43 +7646,95 @@ async function intent(roomPath, intent, opts = {}) {
|
|
|
7642
7646
|
}
|
|
7643
7647
|
return (body !== null && body !== void 0 ? body : { ok: true });
|
|
7644
7648
|
}
|
|
7649
|
+
/**
|
|
7650
|
+
* Fetch the runtime status for a live room.
|
|
7651
|
+
*
|
|
7652
|
+
* const s = await bounded.live.status('rooms/abc');
|
|
7653
|
+
* console.log(s.running, s.stopReason, s.generation, s.etag);
|
|
7654
|
+
*
|
|
7655
|
+
* This is diagnostic/ops surface. It reports whether the room facet exists,
|
|
7656
|
+
* whether it is currently ticking, why it last stopped, which module etag is
|
|
7657
|
+
* loaded, and the current generation used after terminal restarts.
|
|
7658
|
+
*/
|
|
7659
|
+
async function status(roomPath, opts = {}) {
|
|
7660
|
+
var _a, _b, _c, _d, _e;
|
|
7661
|
+
if (!roomPath || typeof roomPath !== 'string') {
|
|
7662
|
+
throw new LiveIntentError('A room path is required');
|
|
7663
|
+
}
|
|
7664
|
+
const normalizedRoomPath = roomPath.replace(/\/$/, '');
|
|
7665
|
+
const config = await getConfig();
|
|
7666
|
+
const base = realtimeHttpBase(config.wsApiUrl);
|
|
7667
|
+
const headers = Object.assign({ 'X-App-Id': config.appId, 'X-Public-App-Id': config.appId }, ((_a = opts.headers) !== null && _a !== void 0 ? _a : {}));
|
|
7668
|
+
const controller = new AbortController();
|
|
7669
|
+
const timeoutMs = (_b = opts.timeoutMs) !== null && _b !== void 0 ? _b : 15000;
|
|
7670
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
7671
|
+
let res;
|
|
7672
|
+
try {
|
|
7673
|
+
res = await fetch(`${base}/live/status?path=${encodeURIComponent(normalizedRoomPath)}`, {
|
|
7674
|
+
method: 'GET',
|
|
7675
|
+
headers,
|
|
7676
|
+
signal: controller.signal,
|
|
7677
|
+
});
|
|
7678
|
+
}
|
|
7679
|
+
catch (err) {
|
|
7680
|
+
clearTimeout(timer);
|
|
7681
|
+
if ((err === null || err === void 0 ? void 0 : err.name) === 'AbortError') {
|
|
7682
|
+
throw new LiveIntentError(`Live status for "${normalizedRoomPath}" timed out after ${timeoutMs}ms`);
|
|
7683
|
+
}
|
|
7684
|
+
throw new LiveIntentError(`Failed to reach the realtime worker: ${(_c = err === null || err === void 0 ? void 0 : err.message) !== null && _c !== void 0 ? _c : String(err)}`);
|
|
7685
|
+
}
|
|
7686
|
+
clearTimeout(timer);
|
|
7687
|
+
let body = null;
|
|
7688
|
+
const text = await res.text();
|
|
7689
|
+
if (text) {
|
|
7690
|
+
try {
|
|
7691
|
+
body = JSON.parse(text);
|
|
7692
|
+
}
|
|
7693
|
+
catch (_f) {
|
|
7694
|
+
body = { raw: text };
|
|
7695
|
+
}
|
|
7696
|
+
}
|
|
7697
|
+
if (!res.ok) {
|
|
7698
|
+
const message = (_e = (_d = body === null || body === void 0 ? void 0 : body.error) !== null && _d !== void 0 ? _d : body === null || body === void 0 ? void 0 : body.message) !== null && _e !== void 0 ? _e : `Live status failed with HTTP ${res.status}`;
|
|
7699
|
+
throw new LiveIntentError(message, res.status, body);
|
|
7700
|
+
}
|
|
7701
|
+
return body;
|
|
7702
|
+
}
|
|
7645
7703
|
/**
|
|
7646
7704
|
* Subscribe to YOUR per-player view of a room. Thin sugar over:
|
|
7647
7705
|
*
|
|
7648
|
-
* subscribe('<roomPath>/view/<
|
|
7706
|
+
* subscribe('<roomPath>/view/<myUserId>', { onData, onError })
|
|
7649
7707
|
*
|
|
7650
|
-
* The
|
|
7651
|
-
*
|
|
7708
|
+
* The view id defaults to the logged-in user's @user.id (from the session token
|
|
7709
|
+
* claims); pass `opts.userId` to override. `opts.address` is kept as a legacy
|
|
7710
|
+
* alias for older wallet-address keyed policies. Returns the unsubscribe
|
|
7652
7711
|
* function (a Promise<() => Promise<void>>, same as `subscribe`).
|
|
7653
7712
|
*
|
|
7654
7713
|
* Note: this is a browser-first helper (the WS subscription manager is
|
|
7655
7714
|
* browser-oriented). Server consumers should use `live.intent`.
|
|
7656
7715
|
*/
|
|
7657
7716
|
async function subscribeView(roomPath, opts) {
|
|
7658
|
-
var _a;
|
|
7717
|
+
var _a, _b, _c;
|
|
7659
7718
|
if (!roomPath || typeof roomPath !== 'string') {
|
|
7660
7719
|
throw new LiveIntentError('A room path is required');
|
|
7661
7720
|
}
|
|
7662
7721
|
if (!opts || typeof opts.onData !== 'function') {
|
|
7663
7722
|
throw new LiveIntentError('subscribeView requires an onData callback');
|
|
7664
7723
|
}
|
|
7665
|
-
let
|
|
7666
|
-
if (!
|
|
7724
|
+
let viewUserId = (_a = opts.userId) !== null && _a !== void 0 ? _a : opts.address;
|
|
7725
|
+
if (!viewUserId) {
|
|
7667
7726
|
const config = await getConfig();
|
|
7668
7727
|
const info = await getUserInfo(config.isServer);
|
|
7669
|
-
// getUserInfo returns the RAW idToken payload
|
|
7670
|
-
//
|
|
7671
|
-
//
|
|
7672
|
-
|
|
7673
|
-
// identity it already authenticated. Resolve it from the actual claim so a
|
|
7674
|
-
// logged-in caller never needs to pass its own address.
|
|
7675
|
-
address = (_a = info === null || info === void 0 ? void 0 : info.address) !== null && _a !== void 0 ? _a : info === null || info === void 0 ? void 0 : info['custom:walletAddress'];
|
|
7728
|
+
// getUserInfo returns the RAW idToken payload. The universal live view key
|
|
7729
|
+
// is @user.id (`custom:userId`); wallet-address keyed views are still
|
|
7730
|
+
// resolved as a compatibility fallback for older policies.
|
|
7731
|
+
viewUserId = (_c = (_b = info === null || info === void 0 ? void 0 : info['custom:userId']) !== null && _b !== void 0 ? _b : info === null || info === void 0 ? void 0 : info['custom:walletAddress']) !== null && _c !== void 0 ? _c : info === null || info === void 0 ? void 0 : info.address;
|
|
7676
7732
|
}
|
|
7677
|
-
if (!
|
|
7678
|
-
throw new LiveIntentError('Could not resolve a player
|
|
7733
|
+
if (!viewUserId || typeof viewUserId !== 'string') {
|
|
7734
|
+
throw new LiveIntentError('Could not resolve a player view id for subscribeView; pass opts.userId or log in first');
|
|
7679
7735
|
}
|
|
7680
7736
|
const normalizedRoomPath = roomPath.replace(/\/$/, '');
|
|
7681
|
-
const viewPath = `${normalizedRoomPath}/view/${
|
|
7737
|
+
const viewPath = `${normalizedRoomPath}/view/${viewUserId}`;
|
|
7682
7738
|
// This is a LIVE/room subscription by construction (the caller hands us the
|
|
7683
7739
|
// room path), so we route its connection to the per-room DO where the live
|
|
7684
7740
|
// view fan-out runs. App code passes NO routing — the room path is the helper's
|
|
@@ -7689,7 +7745,7 @@ async function subscribeView(roomPath, opts) {
|
|
|
7689
7745
|
return subscribeV2(viewPath, { onData: opts.onData, onError: opts.onError }, normalizedRoomPath);
|
|
7690
7746
|
}
|
|
7691
7747
|
/** The `bounded.live` namespace surface. */
|
|
7692
|
-
const live = { intent, subscribeView };
|
|
7748
|
+
const live = { intent, status, subscribeView };
|
|
7693
7749
|
|
|
7694
7750
|
// ---------------------------------------------------------------------------
|
|
7695
7751
|
// live-effects.ts -- AUTHOR-facing types + helpers for writing a Bounded live
|
|
@@ -7778,6 +7834,7 @@ exports.invokeFunction = invoke;
|
|
|
7778
7834
|
exports.isEffectResult = isEffectResult;
|
|
7779
7835
|
exports.live = live;
|
|
7780
7836
|
exports.liveIntent = intent;
|
|
7837
|
+
exports.liveStatus = status;
|
|
7781
7838
|
exports.queryAggregate = queryAggregate;
|
|
7782
7839
|
exports.reconnectWithNewAuth = reconnectWithNewAuth;
|
|
7783
7840
|
exports.refreshSession = refreshSession;
|