@hizi.io/engine-sdk 0.1.9
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 +212 -0
- package/lib/api.d.ts +198 -0
- package/lib/api.js +352 -0
- package/lib/constants.d.ts +44 -0
- package/lib/constants.js +56 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +37 -0
- package/lib/network.d.ts +9 -0
- package/lib/network.js +63 -0
- package/lib/types/balance.d.ts +20 -0
- package/lib/types/balance.js +2 -0
- package/lib/types/blackjack.d.ts +163 -0
- package/lib/types/blackjack.js +22 -0
- package/lib/types/buyFeatureOption.d.ts +11 -0
- package/lib/types/buyFeatureOption.js +2 -0
- package/lib/types/collectOptions.d.ts +8 -0
- package/lib/types/collectOptions.js +2 -0
- package/lib/types/crash.d.ts +223 -0
- package/lib/types/crash.js +80 -0
- package/lib/types/freePlayInfo.d.ts +11 -0
- package/lib/types/freePlayInfo.js +2 -0
- package/lib/types/gameResult.d.ts +48 -0
- package/lib/types/gameResult.js +2 -0
- package/lib/types/gameRoundInfo.d.ts +19 -0
- package/lib/types/gameRoundInfo.js +2 -0
- package/lib/types/gameSettings.d.ts +133 -0
- package/lib/types/gameSettings.js +16 -0
- package/lib/types/gameState.d.ts +20 -0
- package/lib/types/gameState.js +2 -0
- package/lib/types/index.d.ts +32 -0
- package/lib/types/index.js +22 -0
- package/lib/types/keno.d.ts +34 -0
- package/lib/types/keno.js +18 -0
- package/lib/types/loadConfigConfig.d.ts +42 -0
- package/lib/types/loadConfigConfig.js +2 -0
- package/lib/types/loginOptions.d.ts +7 -0
- package/lib/types/loginOptions.js +2 -0
- package/lib/types/mines.d.ts +26 -0
- package/lib/types/mines.js +19 -0
- package/lib/types/network.d.ts +24 -0
- package/lib/types/network.js +2 -0
- package/lib/types/pfVerifyOptions.d.ts +48 -0
- package/lib/types/pfVerifyOptions.js +2 -0
- package/lib/types/placeBetOptions.d.ts +16 -0
- package/lib/types/placeBetOptions.js +2 -0
- package/lib/types/progressionCounter.d.ts +65 -0
- package/lib/types/progressionCounter.js +2 -0
- package/lib/types/replies.d.ts +107 -0
- package/lib/types/replies.js +2 -0
- package/lib/types/roulette.d.ts +53 -0
- package/lib/types/roulette.js +11 -0
- package/lib/types/scenarioInfo.d.ts +12 -0
- package/lib/types/scenarioInfo.js +2 -0
- package/lib/types/sessionOptions.d.ts +7 -0
- package/lib/types/sessionOptions.js +2 -0
- package/lib/types/spinAward.d.ts +31 -0
- package/lib/types/spinAward.js +12 -0
- package/lib/types/spinInfo.d.ts +9 -0
- package/lib/types/spinInfo.js +2 -0
- package/lib/types/tokenData.d.ts +7 -0
- package/lib/types/tokenData.js +2 -0
- package/lib/websocket.d.ts +27 -0
- package/lib/websocket.js +186 -0
- package/package.json +19 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { IBalanceReply } from './balance.js';
|
|
2
|
+
import type { IGameSettings } from './gameSettings.js';
|
|
3
|
+
import type { ITokenData } from './tokenData.js';
|
|
4
|
+
import type { IFreePlayInfo } from './freePlayInfo.js';
|
|
5
|
+
import type { ILoadConfigConfig } from './loadConfigConfig.js';
|
|
6
|
+
import type { IGameResult } from './gameResult.js';
|
|
7
|
+
import type { IGameRoundInfo } from './gameRoundInfo.js';
|
|
8
|
+
/** Response from `login()` and `refresh()` containing the session token and platform info. */
|
|
9
|
+
export interface IConnectReply {
|
|
10
|
+
/** Session token for all subsequent API calls. */
|
|
11
|
+
token: string;
|
|
12
|
+
/** Base URL for game API requests. */
|
|
13
|
+
backendURL: string;
|
|
14
|
+
/** URL for refreshing an expired session token. */
|
|
15
|
+
refreshURL: string;
|
|
16
|
+
/** URL for logging out the session. */
|
|
17
|
+
logoutURL: string;
|
|
18
|
+
/** Optional WebSocket URL for lower-latency communication. */
|
|
19
|
+
webSocketURL?: string;
|
|
20
|
+
/** Player's current balance. */
|
|
21
|
+
balance?: IBalanceReply;
|
|
22
|
+
/** Platform settings (feature toggles and limits). */
|
|
23
|
+
gameSettings?: IGameSettings;
|
|
24
|
+
/** Opaque session metadata. */
|
|
25
|
+
tokenData?: ITokenData;
|
|
26
|
+
/** Operator-granted free plays. */
|
|
27
|
+
freePlaysAvailable?: IFreePlayInfo[];
|
|
28
|
+
}
|
|
29
|
+
/** Response from `loadConfig()` containing game configuration and round resumption data. */
|
|
30
|
+
export interface ILoadConfigReply {
|
|
31
|
+
/** Game configuration (stakes, RTP, buy-features, wager features). */
|
|
32
|
+
config: ILoadConfigConfig;
|
|
33
|
+
/** Updated session metadata. */
|
|
34
|
+
tokenData: ITokenData;
|
|
35
|
+
/** Player's current balance. */
|
|
36
|
+
balance?: IBalanceReply;
|
|
37
|
+
/** Last game result, present when resuming an incomplete round. */
|
|
38
|
+
gameResult?: IGameResult;
|
|
39
|
+
/** Round info, present when resuming an incomplete round. */
|
|
40
|
+
gameRoundInfo?: IGameRoundInfo;
|
|
41
|
+
/** Operator-granted free plays. */
|
|
42
|
+
freePlaysAvailable?: IFreePlayInfo[];
|
|
43
|
+
/** Previous game results for history display. */
|
|
44
|
+
previousResults?: IGameResult[];
|
|
45
|
+
/** Uncollected win amount from a prior incomplete round. */
|
|
46
|
+
amountToCollect?: number;
|
|
47
|
+
}
|
|
48
|
+
/** Response from `placeBet()` containing the game result. */
|
|
49
|
+
export interface IPlaceBetReply {
|
|
50
|
+
/** The game result with scenario data and engine state. */
|
|
51
|
+
result: IGameResult;
|
|
52
|
+
/** Updated session metadata. */
|
|
53
|
+
tokenData: ITokenData;
|
|
54
|
+
/** Updated player balance. */
|
|
55
|
+
balance?: IBalanceReply;
|
|
56
|
+
/** Current round status. */
|
|
57
|
+
gameRoundInfo?: IGameRoundInfo;
|
|
58
|
+
/** Uncollected win amount available for `collect()`. */
|
|
59
|
+
amountToCollect?: number;
|
|
60
|
+
}
|
|
61
|
+
/** Response from `collect()` confirming winnings collection. */
|
|
62
|
+
export interface ICollectReply {
|
|
63
|
+
/** Amount credited to the player's balance. */
|
|
64
|
+
amountCredited: number;
|
|
65
|
+
/** Amount that was requested for collection. */
|
|
66
|
+
amountToCollect: number;
|
|
67
|
+
/** Updated player balance. */
|
|
68
|
+
balance?: IBalanceReply;
|
|
69
|
+
/** Updated session metadata. */
|
|
70
|
+
tokenData: ITokenData;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* One row of the per-round RNG audit trail. Mirrors the shape on the live
|
|
74
|
+
* `pf.rngData` block so a `pfVerify` reply can be diffed against the saved
|
|
75
|
+
* live audit element-for-element.
|
|
76
|
+
*
|
|
77
|
+
* - `'double'` rows carry the raw HMAC float in `[0, 1)` — compare directly.
|
|
78
|
+
* - `'int'` rows carry an integer the engine mapped from the float; compare
|
|
79
|
+
* by exact equality.
|
|
80
|
+
*
|
|
81
|
+
* `id` is `pf:start-end` for PF rounds (the nonce range the batch covers).
|
|
82
|
+
*/
|
|
83
|
+
export interface IRngDataEntry {
|
|
84
|
+
id: string;
|
|
85
|
+
values: number[];
|
|
86
|
+
nonces?: number[];
|
|
87
|
+
type?: 'int' | 'double';
|
|
88
|
+
}
|
|
89
|
+
/** Response from `pfVerify()` — the offline replay's audit + per-step outcomes. */
|
|
90
|
+
export interface IPfVerifyReply {
|
|
91
|
+
/**
|
|
92
|
+
* Per-call RNG audit. Diff element-wise against the round's saved
|
|
93
|
+
* `pf.rngData` — a passing diff is the proof that the engine drew the
|
|
94
|
+
* values it claimed and that the revealed `serverSeed` is the one
|
|
95
|
+
* committed to (a swapped seed would yield different draws).
|
|
96
|
+
*/
|
|
97
|
+
rngData: IRngDataEntry[];
|
|
98
|
+
/**
|
|
99
|
+
* Per-step `IGameResult`s in order; the terminal step is the last
|
|
100
|
+
* element. Length 1 for one-shot rounds (roulette / keno / slot / crash
|
|
101
|
+
* / …), longer for multi-step (blackjack / mines / hi-lo / …). Compare
|
|
102
|
+
* each `scenario` / `totalWin` to the live round; `engineData` carries
|
|
103
|
+
* flow-control state (`playerChoice`, `inProgress`, …) for callers
|
|
104
|
+
* that want to verify the round's flow too.
|
|
105
|
+
*/
|
|
106
|
+
steps: IGameResult[];
|
|
107
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Roulette-specific payloads carried through `IPlaceBetOptions.additionalData`
|
|
3
|
+
* and returned in `IGameResult.scenario`.
|
|
4
|
+
*/
|
|
5
|
+
export type TRouletteBetType = 'straight' | 'split' | 'street' | 'corner' | 'six_line' | 'red' | 'black' | 'odd' | 'even' | 'low_18' | 'high_18' | 'dozen' | 'column';
|
|
6
|
+
export type TRouletteEvenMoneyBetType = 'red' | 'black' | 'odd' | 'even' | 'low_18' | 'high_18';
|
|
7
|
+
/** Bet selection shape by type. */
|
|
8
|
+
export type TRouletteBetSelection = {
|
|
9
|
+
type: 'straight';
|
|
10
|
+
selection: number;
|
|
11
|
+
} | {
|
|
12
|
+
type: 'split';
|
|
13
|
+
selection: [number, number];
|
|
14
|
+
} | {
|
|
15
|
+
type: 'street';
|
|
16
|
+
selection: number;
|
|
17
|
+
} | {
|
|
18
|
+
type: 'corner';
|
|
19
|
+
selection: [number, number, number, number];
|
|
20
|
+
} | {
|
|
21
|
+
type: 'six_line';
|
|
22
|
+
selection: number;
|
|
23
|
+
} | {
|
|
24
|
+
type: 'dozen' | 'column';
|
|
25
|
+
selection: 1 | 2 | 3;
|
|
26
|
+
} | {
|
|
27
|
+
type: TRouletteEvenMoneyBetType;
|
|
28
|
+
selection?: null;
|
|
29
|
+
};
|
|
30
|
+
export type IRouletteBet = TRouletteBetSelection & {
|
|
31
|
+
stake: number;
|
|
32
|
+
};
|
|
33
|
+
export type IRouletteBetSettlement = TRouletteBetSelection & {
|
|
34
|
+
stake: number;
|
|
35
|
+
won: boolean;
|
|
36
|
+
payout: number;
|
|
37
|
+
winAmount: number;
|
|
38
|
+
};
|
|
39
|
+
export interface IRouletteScenario {
|
|
40
|
+
pocket: number;
|
|
41
|
+
color: 'red' | 'black' | 'green';
|
|
42
|
+
oddEven: 'odd' | 'even' | null;
|
|
43
|
+
highLow: 'low_18' | 'high_18' | null;
|
|
44
|
+
dozen: 1 | 2 | 3 | null;
|
|
45
|
+
column: 1 | 2 | 3 | null;
|
|
46
|
+
settlement: IRouletteBetSettlement[];
|
|
47
|
+
totalStake: number;
|
|
48
|
+
totalWin: number;
|
|
49
|
+
}
|
|
50
|
+
/** Build a `placeBet` additionalData payload for a roulette round. */
|
|
51
|
+
export declare function rouletteAdditionalData(bets: IRouletteBet[]): {
|
|
52
|
+
bets: IRouletteBet[];
|
|
53
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Roulette-specific payloads carried through `IPlaceBetOptions.additionalData`
|
|
4
|
+
* and returned in `IGameResult.scenario`.
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.rouletteAdditionalData = rouletteAdditionalData;
|
|
8
|
+
/** Build a `placeBet` additionalData payload for a roulette round. */
|
|
9
|
+
function rouletteAdditionalData(bets) {
|
|
10
|
+
return { bets };
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tracks progression through a multi-result scenario (e.g. cascades, tumbles).
|
|
3
|
+
* Each `placeBet` call advances to the next scenario step.
|
|
4
|
+
*/
|
|
5
|
+
export interface IScenarioInfo {
|
|
6
|
+
/** Index of the randomly selected scenario from the entry's scenario pool. */
|
|
7
|
+
scenarioIndex: number;
|
|
8
|
+
/** Current step within the scenario (0-based, increments per `placeBet` continuation). */
|
|
9
|
+
currentScenarioIndex: number;
|
|
10
|
+
/** Whether there are more scenario steps to play. */
|
|
11
|
+
inProgress: boolean;
|
|
12
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Common options for all authenticated game API calls. */
|
|
2
|
+
export interface ISessionOptions {
|
|
3
|
+
/** Backend URL from the {@link login} response. */
|
|
4
|
+
backendURL: string;
|
|
5
|
+
/** Session token from the {@link login} or {@link refresh} response. */
|
|
6
|
+
token: string;
|
|
7
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One option in a player choice prompt (e.g. "pick 5 or 10 free spins").
|
|
3
|
+
* Present in `IGameResult.engineData.playerChoice` when the engine
|
|
4
|
+
* is waiting for the player to select before continuing.
|
|
5
|
+
*/
|
|
6
|
+
export type TPlayerChoiceFeatureAward = {
|
|
7
|
+
/** Number of spins awarded if this option is selected. */
|
|
8
|
+
count: number;
|
|
9
|
+
/** Target feature DB for the awarded spins (e.g. "freespin"). */
|
|
10
|
+
feature: string;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Cash option in a player choice prompt — selecting this pays the player
|
|
14
|
+
* `winMultiplier × stake` directly, with no feature spin awarded.
|
|
15
|
+
* Present in `IGameResult.engineData.playerChoice` when a cash-awarding
|
|
16
|
+
* progression counter completed via `playerChoice` selection.
|
|
17
|
+
*/
|
|
18
|
+
export type TPlayerChoiceCashAward = {
|
|
19
|
+
/** Cash payout as a multiplier of the player's stake. */
|
|
20
|
+
winMultiplier: number;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* A single option in a `playerChoice` prompt — either a feature-spin award
|
|
24
|
+
* or a cash award. Cash options are only ever introduced via progression
|
|
25
|
+
* counter completions.
|
|
26
|
+
*/
|
|
27
|
+
export type TPlayerChoiceAward = TPlayerChoiceFeatureAward | TPlayerChoiceCashAward;
|
|
28
|
+
/** Type guard: is this player choice option a cash award? */
|
|
29
|
+
export declare function isCashChoice(award: TPlayerChoiceAward): award is TPlayerChoiceCashAward;
|
|
30
|
+
/** Type guard: is this player choice option a feature-spin award? */
|
|
31
|
+
export declare function isFeatureChoice(award: TPlayerChoiceAward): award is TPlayerChoiceFeatureAward;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isCashChoice = isCashChoice;
|
|
4
|
+
exports.isFeatureChoice = isFeatureChoice;
|
|
5
|
+
/** Type guard: is this player choice option a cash award? */
|
|
6
|
+
function isCashChoice(award) {
|
|
7
|
+
return award.winMultiplier !== undefined;
|
|
8
|
+
}
|
|
9
|
+
/** Type guard: is this player choice option a feature-spin award? */
|
|
10
|
+
function isFeatureChoice(award) {
|
|
11
|
+
return award.feature !== undefined;
|
|
12
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Tracks remaining and consumed spins for an active feature (e.g. free spins, bonus round). */
|
|
2
|
+
export interface ISpinInfo {
|
|
3
|
+
/** Feature name identifying the DB to spin from (e.g. "freespin", "bonus"). */
|
|
4
|
+
feature: string;
|
|
5
|
+
/** Spins remaining for this feature. */
|
|
6
|
+
count: number;
|
|
7
|
+
/** Spins already consumed from this feature. */
|
|
8
|
+
used: number;
|
|
9
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/** Handler returned by {@link enableWebSockets} for managing the WebSocket connection. */
|
|
2
|
+
export interface WebSocketHandler {
|
|
3
|
+
/** Close the WebSocket connection and revert all API calls to HTTP. */
|
|
4
|
+
close: () => void;
|
|
5
|
+
/** Returns `true` if the WebSocket connection is open. */
|
|
6
|
+
isConnected: () => boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Enable WebSocket communication for lower latency.
|
|
10
|
+
*
|
|
11
|
+
* Once enabled, all subsequent API calls ({@link loadConfig}, {@link placeBet},
|
|
12
|
+
* {@link collect}, etc.) automatically route through the WebSocket connection
|
|
13
|
+
* instead of HTTP.
|
|
14
|
+
*
|
|
15
|
+
* @param webSocketURL - The WebSocket URL from the {@link login} response (`result.webSocketURL`).
|
|
16
|
+
* @returns A handler to check connection status and close the socket.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const ws = await enableWebSockets(webSocketURL);
|
|
21
|
+
* // All API calls now use WebSocket automatically
|
|
22
|
+
* const response = await placeBet({ backendURL, token, stake });
|
|
23
|
+
* // Later, to disconnect:
|
|
24
|
+
* ws.close();
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function enableWebSockets(webSocketURL: string): Promise<WebSocketHandler>;
|
package/lib/websocket.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.enableWebSockets = enableWebSockets;
|
|
4
|
+
const constants_js_1 = require("./constants.js");
|
|
5
|
+
const network_js_1 = require("./network.js");
|
|
6
|
+
let requestCounter = 0;
|
|
7
|
+
function decodeBase64Utf8(value) {
|
|
8
|
+
try {
|
|
9
|
+
const binary = atob(value);
|
|
10
|
+
const bytes = Uint8Array.from(binary, c => c.charCodeAt(0));
|
|
11
|
+
return new TextDecoder('utf-8').decode(bytes);
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return value;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// The websocket-router forwards a successful response as
|
|
18
|
+
// `{status, headers, reply, requestId}` — same shape as an HTTP response. On
|
|
19
|
+
// failure it surfaces the engine error via headers (x-h-error-* mirroring the
|
|
20
|
+
// HTTP path) and/or fields on `reply` (errorId / errorText / passThroughData).
|
|
21
|
+
function buildErrorFromResponse(headers, reply, statusText) {
|
|
22
|
+
const h = headers ?? {};
|
|
23
|
+
const lookup = (name) => h[name] ?? h[name.toLowerCase()];
|
|
24
|
+
const errorMsgBase64 = lookup('x-h-error-msg-base64');
|
|
25
|
+
const headerMsg = errorMsgBase64 ? decodeBase64Utf8(errorMsgBase64) : lookup('x-h-error-msg');
|
|
26
|
+
const headerId = lookup('x-h-error-id');
|
|
27
|
+
const error = {
|
|
28
|
+
code: String(headerId ?? reply?.errorId ?? reply?.code ?? constants_js_1.API_RETURNCODES.UNEXPECTED),
|
|
29
|
+
message: (headerMsg ?? reply?.errorText ?? reply?.message ?? statusText),
|
|
30
|
+
};
|
|
31
|
+
const passThroughData = reply?.passThroughData;
|
|
32
|
+
if (passThroughData !== undefined && passThroughData !== 'undefined') {
|
|
33
|
+
error.passThroughData = passThroughData;
|
|
34
|
+
}
|
|
35
|
+
return error;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Enable WebSocket communication for lower latency.
|
|
39
|
+
*
|
|
40
|
+
* Once enabled, all subsequent API calls ({@link loadConfig}, {@link placeBet},
|
|
41
|
+
* {@link collect}, etc.) automatically route through the WebSocket connection
|
|
42
|
+
* instead of HTTP.
|
|
43
|
+
*
|
|
44
|
+
* @param webSocketURL - The WebSocket URL from the {@link login} response (`result.webSocketURL`).
|
|
45
|
+
* @returns A handler to check connection status and close the socket.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* const ws = await enableWebSockets(webSocketURL);
|
|
50
|
+
* // All API calls now use WebSocket automatically
|
|
51
|
+
* const response = await placeBet({ backendURL, token, stake });
|
|
52
|
+
* // Later, to disconnect:
|
|
53
|
+
* ws.close();
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
async function enableWebSockets(webSocketURL) {
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const ws = new WebSocket(webSocketURL);
|
|
59
|
+
// Router sends responses as Buffer; parse as ArrayBuffer to avoid Blob async hop.
|
|
60
|
+
ws.binaryType = 'arraybuffer';
|
|
61
|
+
const pending = new Map();
|
|
62
|
+
ws.onopen = () => {
|
|
63
|
+
const transport = {
|
|
64
|
+
send(body) {
|
|
65
|
+
return new Promise(res => {
|
|
66
|
+
const requestId = `ws-${++requestCounter}`;
|
|
67
|
+
// websocket-router's incomingHandler expects this envelope; it then
|
|
68
|
+
// re-queues `data.body` to the game's HTTP backend route, so the
|
|
69
|
+
// engine sees the same payload it would over HTTP.
|
|
70
|
+
const message = {
|
|
71
|
+
cmd: 'game',
|
|
72
|
+
data: {
|
|
73
|
+
body,
|
|
74
|
+
requestId,
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: { 'Content-Type': 'application/json' },
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
const timer = setTimeout(() => {
|
|
80
|
+
pending.delete(requestId);
|
|
81
|
+
res({
|
|
82
|
+
success: false,
|
|
83
|
+
error: {
|
|
84
|
+
code: String(constants_js_1.API_RETURNCODES.NETWORKERROR),
|
|
85
|
+
message: 'WebSocket request timed out',
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}, constants_js_1.defaultNetworkTimeout);
|
|
89
|
+
pending.set(requestId, {
|
|
90
|
+
resolve: res,
|
|
91
|
+
timer,
|
|
92
|
+
});
|
|
93
|
+
try {
|
|
94
|
+
ws.send(JSON.stringify(message));
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
clearTimeout(timer);
|
|
98
|
+
pending.delete(requestId);
|
|
99
|
+
res({
|
|
100
|
+
success: false,
|
|
101
|
+
error: {
|
|
102
|
+
code: String(constants_js_1.API_RETURNCODES.NETWORKERROR),
|
|
103
|
+
message: 'WebSocket send failed',
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
},
|
|
109
|
+
isConnected() {
|
|
110
|
+
return ws.readyState === WebSocket.OPEN;
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
(0, network_js_1.setWsTransport)(transport);
|
|
114
|
+
resolve({
|
|
115
|
+
close() {
|
|
116
|
+
(0, network_js_1.setWsTransport)(null);
|
|
117
|
+
for (const [, req] of pending) {
|
|
118
|
+
clearTimeout(req.timer);
|
|
119
|
+
req.resolve({
|
|
120
|
+
success: false,
|
|
121
|
+
error: {
|
|
122
|
+
code: String(constants_js_1.API_RETURNCODES.NETWORKERROR),
|
|
123
|
+
message: 'WebSocket connection closed',
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
pending.clear();
|
|
128
|
+
ws.close();
|
|
129
|
+
},
|
|
130
|
+
isConnected() {
|
|
131
|
+
return ws.readyState === WebSocket.OPEN;
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
ws.onmessage = event => {
|
|
136
|
+
try {
|
|
137
|
+
const text = typeof event.data === 'string'
|
|
138
|
+
? event.data
|
|
139
|
+
: event.data instanceof ArrayBuffer
|
|
140
|
+
? new TextDecoder('utf-8').decode(event.data)
|
|
141
|
+
: null;
|
|
142
|
+
if (text === null)
|
|
143
|
+
return;
|
|
144
|
+
const data = JSON.parse(text);
|
|
145
|
+
const requestId = data.requestId;
|
|
146
|
+
if (!requestId || !pending.has(requestId))
|
|
147
|
+
return;
|
|
148
|
+
const req = pending.get(requestId);
|
|
149
|
+
pending.delete(requestId);
|
|
150
|
+
clearTimeout(req.timer);
|
|
151
|
+
const status = data.status ?? 0;
|
|
152
|
+
if (status >= 200 && status < 300) {
|
|
153
|
+
req.resolve({ success: true, result: data.reply ?? {} });
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
req.resolve({
|
|
157
|
+
success: false,
|
|
158
|
+
error: buildErrorFromResponse(data.headers, data.reply, `HTTP ${status}`),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Ignore non-JSON messages
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
ws.onclose = () => {
|
|
167
|
+
(0, network_js_1.setWsTransport)(null);
|
|
168
|
+
for (const [, req] of pending) {
|
|
169
|
+
clearTimeout(req.timer);
|
|
170
|
+
req.resolve({
|
|
171
|
+
success: false,
|
|
172
|
+
error: {
|
|
173
|
+
code: String(constants_js_1.API_RETURNCODES.NETWORKERROR),
|
|
174
|
+
message: 'WebSocket connection closed',
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
pending.clear();
|
|
179
|
+
};
|
|
180
|
+
ws.onerror = () => {
|
|
181
|
+
if (ws.readyState !== WebSocket.OPEN) {
|
|
182
|
+
reject(new Error('WebSocket connection failed'));
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
});
|
|
186
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hizi.io/engine-sdk",
|
|
3
|
+
"version": "0.1.9",
|
|
4
|
+
"description": "SDK for communicating with the hizi engine",
|
|
5
|
+
"author": "David Dunnings",
|
|
6
|
+
"license": "ISC",
|
|
7
|
+
"main": "lib/index.js",
|
|
8
|
+
"types": "lib/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"lib"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsc --watch"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"typescript": "5.8.3"
|
|
18
|
+
}
|
|
19
|
+
}
|