@uzuhq/code-sdk 0.7.5 → 0.7.7
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/dist/dev-globals.d.ts +12 -27
- package/dist/dev-globals.js +0 -15
- package/dist/dev-hooks-ClWM8HzI.d.ts +682 -0
- package/dist/index.d.ts +152 -66
- package/dist/index.js +1837 -414
- package/package.json +8 -5
- package/dist/action-types.test-d.d.ts +0 -11
- package/dist/action-types.test-d.js +0 -101
- package/dist/dev-hooks.d.ts +0 -241
- package/dist/dev-hooks.js +0 -132
- package/dist/dev-hooks.test.d.ts +0 -1
- package/dist/dev-hooks.test.js +0 -294
- package/dist/dev-prediction-traps.d.ts +0 -32
- package/dist/dev-prediction-traps.js +0 -0
- package/dist/dev-prediction-traps.test.d.ts +0 -1
- package/dist/dev-prediction-traps.test.js +0 -178
- package/dist/dev-state-patch.d.ts +0 -81
- package/dist/dev-state-patch.js +0 -295
- package/dist/dev-state-patch.test.d.ts +0 -1
- package/dist/dev-state-patch.test.js +0 -333
- package/dist/json-patch.d.ts +0 -7
- package/dist/json-patch.js +0 -78
- package/dist/random.d.ts +0 -11
- package/dist/random.js +0 -34
- package/dist/reconnectable-ws.d.ts +0 -60
- package/dist/reconnectable-ws.js +0 -229
- package/dist/room.d.ts +0 -23
- package/dist/room.js +0 -36
- package/dist/run/local-server-action.d.ts +0 -16
- package/dist/run/local-server-action.js +0 -217
- package/dist/run/local-server-action.test.d.ts +0 -1
- package/dist/run/local-server-action.test.js +0 -242
- package/dist/run/optimistic-action-client.d.ts +0 -68
- package/dist/run/optimistic-action-client.js +0 -209
- package/dist/run/optimistic-action-client.test.d.ts +0 -1
- package/dist/run/optimistic-action-client.test.js +0 -430
- package/dist/run/server-action.d.ts +0 -16
- package/dist/run/server-action.js +0 -177
- package/dist/run/server-action.test.d.ts +0 -1
- package/dist/run/server-action.test.js +0 -96
- package/dist/server-clock.d.ts +0 -29
- package/dist/server-clock.js +0 -40
- package/dist/server-only.d.ts +0 -33
- package/dist/server-only.js +0 -21
- package/dist/sync/local.d.ts +0 -8
- package/dist/sync/local.js +0 -50
- package/dist/sync/online.d.ts +0 -5
- package/dist/sync/online.js +0 -165
- package/dist/types.d.ts +0 -345
- package/dist/types.js +0 -8
package/dist/json-patch.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
function escapePointer(key) {
|
|
2
|
-
return key.replace(/~/g, '~0').replace(/\//g, '~1');
|
|
3
|
-
}
|
|
4
|
-
function unescapePointer(token) {
|
|
5
|
-
return token.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
6
|
-
}
|
|
7
|
-
export function compare(oldObj, newObj, basePath = '') {
|
|
8
|
-
if (oldObj === newObj)
|
|
9
|
-
return [];
|
|
10
|
-
// Handle nulls / primitives
|
|
11
|
-
if (oldObj === null ||
|
|
12
|
-
newObj === null ||
|
|
13
|
-
typeof oldObj !== 'object' ||
|
|
14
|
-
typeof newObj !== 'object') {
|
|
15
|
-
return [{ op: 'replace', path: basePath || '/', value: newObj }];
|
|
16
|
-
}
|
|
17
|
-
// Arrays: compare as whole (no element-level diff)
|
|
18
|
-
if (Array.isArray(oldObj) || Array.isArray(newObj)) {
|
|
19
|
-
if (JSON.stringify(oldObj) === JSON.stringify(newObj))
|
|
20
|
-
return [];
|
|
21
|
-
return [{ op: 'replace', path: basePath || '/', value: newObj }];
|
|
22
|
-
}
|
|
23
|
-
// Objects: recurse
|
|
24
|
-
const ops = [];
|
|
25
|
-
const oldKeys = Object.keys(oldObj);
|
|
26
|
-
const newKeys = Object.keys(newObj);
|
|
27
|
-
// Removed keys
|
|
28
|
-
for (const key of oldKeys) {
|
|
29
|
-
if (!(key in newObj)) {
|
|
30
|
-
ops.push({ op: 'remove', path: `${basePath}/${escapePointer(key)}` });
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
// Added or changed keys
|
|
34
|
-
for (const key of newKeys) {
|
|
35
|
-
const childPath = `${basePath}/${escapePointer(key)}`;
|
|
36
|
-
if (!(key in oldObj)) {
|
|
37
|
-
ops.push({ op: 'add', path: childPath, value: newObj[key] });
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
const childOps = compare(oldObj[key], newObj[key], childPath);
|
|
41
|
-
ops.push(...childOps);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
return ops;
|
|
45
|
-
}
|
|
46
|
-
export function applyPatch(doc, ops) {
|
|
47
|
-
for (const op of ops) {
|
|
48
|
-
const tokens = op.path.split('/').slice(1).map(unescapePointer);
|
|
49
|
-
if (tokens.length === 0)
|
|
50
|
-
return false;
|
|
51
|
-
if (op.op === 'replace' || op.op === 'add') {
|
|
52
|
-
let target = doc;
|
|
53
|
-
for (let i = 0; i < tokens.length - 1; i++) {
|
|
54
|
-
target = target?.[tokens[i]];
|
|
55
|
-
if (target === undefined || target === null)
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
const lastKey = tokens[tokens.length - 1];
|
|
59
|
-
target[lastKey] = op.value;
|
|
60
|
-
}
|
|
61
|
-
else if (op.op === 'remove') {
|
|
62
|
-
let target = doc;
|
|
63
|
-
for (let i = 0; i < tokens.length - 1; i++) {
|
|
64
|
-
target = target?.[tokens[i]];
|
|
65
|
-
if (target === undefined || target === null)
|
|
66
|
-
return false;
|
|
67
|
-
}
|
|
68
|
-
const lastKey = tokens[tokens.length - 1];
|
|
69
|
-
if (Array.isArray(target)) {
|
|
70
|
-
target.splice(Number(lastKey), 1);
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
delete target[lastKey];
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return true;
|
|
78
|
-
}
|
package/dist/random.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { SeededRandom } from './types.js';
|
|
2
|
-
export declare class SeededRandomImpl implements SeededRandom {
|
|
3
|
-
private _state;
|
|
4
|
-
constructor(seed: number);
|
|
5
|
-
get state(): number;
|
|
6
|
-
static fromState(state: number): SeededRandomImpl;
|
|
7
|
-
float(): number;
|
|
8
|
-
int(max: number): number;
|
|
9
|
-
pick<T>(array: T[]): T;
|
|
10
|
-
shuffle<T>(array: T[]): T[];
|
|
11
|
-
}
|
package/dist/random.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
export class SeededRandomImpl {
|
|
2
|
-
constructor(seed) {
|
|
3
|
-
this._state = seed | 0;
|
|
4
|
-
}
|
|
5
|
-
get state() {
|
|
6
|
-
return this._state;
|
|
7
|
-
}
|
|
8
|
-
static fromState(state) {
|
|
9
|
-
const r = new SeededRandomImpl(0);
|
|
10
|
-
r._state = state;
|
|
11
|
-
return r;
|
|
12
|
-
}
|
|
13
|
-
float() {
|
|
14
|
-
this._state |= 0;
|
|
15
|
-
this._state = (this._state + 0x6d2b79f5) | 0;
|
|
16
|
-
let t = Math.imul(this._state ^ (this._state >>> 15), 1 | this._state);
|
|
17
|
-
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
18
|
-
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
19
|
-
}
|
|
20
|
-
int(max) {
|
|
21
|
-
return Math.floor(this.float() * max);
|
|
22
|
-
}
|
|
23
|
-
pick(array) {
|
|
24
|
-
return array[this.int(array.length)];
|
|
25
|
-
}
|
|
26
|
-
shuffle(array) {
|
|
27
|
-
const a = [...array];
|
|
28
|
-
for (let i = a.length - 1; i > 0; i--) {
|
|
29
|
-
const j = this.int(i + 1);
|
|
30
|
-
[a[i], a[j]] = [a[j], a[i]];
|
|
31
|
-
}
|
|
32
|
-
return a;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 自動再接続 WebSocket ラッパー
|
|
3
|
-
*
|
|
4
|
-
* - Exponential backoff + jitter 付き自動再接続
|
|
5
|
-
* - Heartbeat (__ping / __pong) による接続生存確認
|
|
6
|
-
* - 再接続中のメッセージバッファリング
|
|
7
|
-
*/
|
|
8
|
-
export type ConnectionState = 'connecting' | 'connected' | 'reconnecting' | 'disconnected';
|
|
9
|
-
export interface ReconnectableWSOptions {
|
|
10
|
-
/** 最大再接続試行回数 (default: 15) */
|
|
11
|
-
maxReconnectAttempts?: number;
|
|
12
|
-
/** Backoff の基準遅延 ms (default: 1000) */
|
|
13
|
-
baseDelay?: number;
|
|
14
|
-
/** Backoff の最大遅延 ms (default: 30000) */
|
|
15
|
-
maxDelay?: number;
|
|
16
|
-
/** Jitter 係数 0〜1 (default: 0.5) */
|
|
17
|
-
jitterFactor?: number;
|
|
18
|
-
/** Heartbeat 送信間隔 ms (default: 25000) */
|
|
19
|
-
heartbeatInterval?: number;
|
|
20
|
-
/** Pong 待ちタイムアウト ms (default: 10000) */
|
|
21
|
-
heartbeatTimeout?: number;
|
|
22
|
-
/** 再接続中のバッファ上限 (default: 50) */
|
|
23
|
-
maxBufferSize?: number;
|
|
24
|
-
/** バッファ対象判定。false を返すとドロップ */
|
|
25
|
-
shouldBuffer?: (data: string) => boolean;
|
|
26
|
-
/** 接続状態変更コールバック */
|
|
27
|
-
onConnectionStateChange?: (state: ConnectionState) => void;
|
|
28
|
-
}
|
|
29
|
-
export declare class ReconnectableWebSocket {
|
|
30
|
-
private ws;
|
|
31
|
-
private readonly url;
|
|
32
|
-
private readonly opts;
|
|
33
|
-
private _state;
|
|
34
|
-
private reconnectAttempt;
|
|
35
|
-
private messageBuffer;
|
|
36
|
-
private messageHandlers;
|
|
37
|
-
private heartbeatTimer;
|
|
38
|
-
private heartbeatTimeoutTimer;
|
|
39
|
-
private reconnectTimer;
|
|
40
|
-
private disposed;
|
|
41
|
-
constructor(url: string, options?: ReconnectableWSOptions);
|
|
42
|
-
get connectionState(): ConnectionState;
|
|
43
|
-
/** WebSocket.readyState 互換 (既存コードとの互換用) */
|
|
44
|
-
get readyState(): number;
|
|
45
|
-
send(data: string): void;
|
|
46
|
-
addEventListener(type: 'message', handler: (ev: MessageEvent) => void): void;
|
|
47
|
-
removeEventListener(type: 'message', handler: (ev: MessageEvent) => void): void;
|
|
48
|
-
/** バッファをクリア(権威的 state 受信時に呼ぶ) */
|
|
49
|
-
clearBuffer(): void;
|
|
50
|
-
/** 全リソース解放 */
|
|
51
|
-
dispose(): void;
|
|
52
|
-
private connect;
|
|
53
|
-
private setState;
|
|
54
|
-
private scheduleReconnect;
|
|
55
|
-
private getReconnectDelay;
|
|
56
|
-
private flushBuffer;
|
|
57
|
-
private startHeartbeat;
|
|
58
|
-
private stopHeartbeat;
|
|
59
|
-
private handlePong;
|
|
60
|
-
}
|
package/dist/reconnectable-ws.js
DELETED
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 自動再接続 WebSocket ラッパー
|
|
3
|
-
*
|
|
4
|
-
* - Exponential backoff + jitter 付き自動再接続
|
|
5
|
-
* - Heartbeat (__ping / __pong) による接続生存確認
|
|
6
|
-
* - 再接続中のメッセージバッファリング
|
|
7
|
-
*/
|
|
8
|
-
const DEFAULTS = {
|
|
9
|
-
maxReconnectAttempts: 15,
|
|
10
|
-
baseDelay: 1000,
|
|
11
|
-
maxDelay: 30000,
|
|
12
|
-
jitterFactor: 0.5,
|
|
13
|
-
heartbeatInterval: 25000,
|
|
14
|
-
heartbeatTimeout: 10000,
|
|
15
|
-
maxBufferSize: 50,
|
|
16
|
-
};
|
|
17
|
-
export class ReconnectableWebSocket {
|
|
18
|
-
constructor(url, options = {}) {
|
|
19
|
-
this.ws = null;
|
|
20
|
-
this._state = 'connecting';
|
|
21
|
-
this.reconnectAttempt = 0;
|
|
22
|
-
this.messageBuffer = [];
|
|
23
|
-
this.messageHandlers = [];
|
|
24
|
-
this.heartbeatTimer = null;
|
|
25
|
-
this.heartbeatTimeoutTimer = null;
|
|
26
|
-
this.reconnectTimer = null;
|
|
27
|
-
this.disposed = false;
|
|
28
|
-
this.url = url;
|
|
29
|
-
this.opts = {
|
|
30
|
-
maxReconnectAttempts: options.maxReconnectAttempts ?? DEFAULTS.maxReconnectAttempts,
|
|
31
|
-
baseDelay: options.baseDelay ?? DEFAULTS.baseDelay,
|
|
32
|
-
maxDelay: options.maxDelay ?? DEFAULTS.maxDelay,
|
|
33
|
-
jitterFactor: options.jitterFactor ?? DEFAULTS.jitterFactor,
|
|
34
|
-
heartbeatInterval: options.heartbeatInterval ?? DEFAULTS.heartbeatInterval,
|
|
35
|
-
heartbeatTimeout: options.heartbeatTimeout ?? DEFAULTS.heartbeatTimeout,
|
|
36
|
-
maxBufferSize: options.maxBufferSize ?? DEFAULTS.maxBufferSize,
|
|
37
|
-
shouldBuffer: options.shouldBuffer,
|
|
38
|
-
onConnectionStateChange: options.onConnectionStateChange,
|
|
39
|
-
};
|
|
40
|
-
this.connect();
|
|
41
|
-
}
|
|
42
|
-
// ─── Public API ──────────────────────────────────────────
|
|
43
|
-
get connectionState() {
|
|
44
|
-
return this._state;
|
|
45
|
-
}
|
|
46
|
-
/** WebSocket.readyState 互換 (既存コードとの互換用) */
|
|
47
|
-
get readyState() {
|
|
48
|
-
return this.ws?.readyState ?? WebSocket.CLOSED;
|
|
49
|
-
}
|
|
50
|
-
send(data) {
|
|
51
|
-
if (this._state === 'connected' && this.ws?.readyState === WebSocket.OPEN) {
|
|
52
|
-
this.ws.send(data);
|
|
53
|
-
}
|
|
54
|
-
else if (this._state === 'reconnecting') {
|
|
55
|
-
if (this.opts.shouldBuffer && !this.opts.shouldBuffer(data))
|
|
56
|
-
return;
|
|
57
|
-
if (this.messageBuffer.length < this.opts.maxBufferSize) {
|
|
58
|
-
this.messageBuffer.push(data);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
// disconnected / connecting: ドロップ
|
|
62
|
-
}
|
|
63
|
-
addEventListener(type, handler) {
|
|
64
|
-
if (type === 'message') {
|
|
65
|
-
this.messageHandlers.push(handler);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
removeEventListener(type, handler) {
|
|
69
|
-
if (type === 'message') {
|
|
70
|
-
const index = this.messageHandlers.indexOf(handler);
|
|
71
|
-
if (index !== -1) {
|
|
72
|
-
this.messageHandlers.splice(index, 1);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
/** バッファをクリア(権威的 state 受信時に呼ぶ) */
|
|
77
|
-
clearBuffer() {
|
|
78
|
-
this.messageBuffer = [];
|
|
79
|
-
}
|
|
80
|
-
/** 全リソース解放 */
|
|
81
|
-
dispose() {
|
|
82
|
-
this.disposed = true;
|
|
83
|
-
this.stopHeartbeat();
|
|
84
|
-
if (this.reconnectTimer) {
|
|
85
|
-
clearTimeout(this.reconnectTimer);
|
|
86
|
-
this.reconnectTimer = null;
|
|
87
|
-
}
|
|
88
|
-
if (this.ws) {
|
|
89
|
-
this.ws.onopen = null;
|
|
90
|
-
this.ws.onclose = null;
|
|
91
|
-
this.ws.onerror = null;
|
|
92
|
-
this.ws.onmessage = null;
|
|
93
|
-
try {
|
|
94
|
-
if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {
|
|
95
|
-
this.ws.close();
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
catch (e) {
|
|
99
|
-
console.warn('[ReconnectableWS] Error closing WebSocket:', e);
|
|
100
|
-
}
|
|
101
|
-
this.ws = null;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
// ─── Private ─────────────────────────────────────────────
|
|
105
|
-
connect() {
|
|
106
|
-
if (this.disposed)
|
|
107
|
-
return;
|
|
108
|
-
console.log(`[ReconnectableWS] 🔗 Connecting to ${this.url} (attempt=${this.reconnectAttempt})`);
|
|
109
|
-
const ws = new WebSocket(this.url);
|
|
110
|
-
ws.onopen = () => {
|
|
111
|
-
if (this.disposed)
|
|
112
|
-
return;
|
|
113
|
-
console.log(`[ReconnectableWS] ✅ Connected`);
|
|
114
|
-
this.reconnectAttempt = 0;
|
|
115
|
-
this.setState('connected');
|
|
116
|
-
this.startHeartbeat();
|
|
117
|
-
this.flushBuffer();
|
|
118
|
-
};
|
|
119
|
-
ws.onclose = (ev) => {
|
|
120
|
-
if (this.disposed)
|
|
121
|
-
return;
|
|
122
|
-
// 正常終了 (1000) の場合はリコネクトしない
|
|
123
|
-
if (ev.code === 1000) {
|
|
124
|
-
console.log(`[ReconnectableWS] 🔒 Closed cleanly (code=${ev.code})`);
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
console.log(`[ReconnectableWS] ❌ Closed (code=${ev.code} reason=${ev.reason})`);
|
|
128
|
-
this.stopHeartbeat();
|
|
129
|
-
this.scheduleReconnect();
|
|
130
|
-
};
|
|
131
|
-
ws.onerror = () => {
|
|
132
|
-
if (this.disposed)
|
|
133
|
-
return;
|
|
134
|
-
console.log(`[ReconnectableWS] ⚠️ Error`);
|
|
135
|
-
// onclose が後続で発火するため、ここではリコネクトをトリガーしない
|
|
136
|
-
};
|
|
137
|
-
ws.onmessage = (ev) => {
|
|
138
|
-
if (this.disposed)
|
|
139
|
-
return;
|
|
140
|
-
const data = ev.data;
|
|
141
|
-
// Heartbeat pong のフィルタリング
|
|
142
|
-
if (data === '__pong') {
|
|
143
|
-
this.handlePong();
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
// 通常メッセージをハンドラに配信
|
|
147
|
-
for (const handler of this.messageHandlers) {
|
|
148
|
-
handler(ev);
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
this.ws = ws;
|
|
152
|
-
}
|
|
153
|
-
setState(state) {
|
|
154
|
-
if (this._state === state)
|
|
155
|
-
return;
|
|
156
|
-
this._state = state;
|
|
157
|
-
console.log(`[ReconnectableWS] 📡 State: ${state}`);
|
|
158
|
-
this.opts.onConnectionStateChange?.(state);
|
|
159
|
-
}
|
|
160
|
-
scheduleReconnect() {
|
|
161
|
-
if (this.disposed)
|
|
162
|
-
return;
|
|
163
|
-
if (this.reconnectAttempt >= this.opts.maxReconnectAttempts) {
|
|
164
|
-
console.log(`[ReconnectableWS] 🚫 Max reconnect attempts reached (${this.opts.maxReconnectAttempts})`);
|
|
165
|
-
this.setState('disconnected');
|
|
166
|
-
return;
|
|
167
|
-
}
|
|
168
|
-
this.setState('reconnecting');
|
|
169
|
-
const delay = this.getReconnectDelay();
|
|
170
|
-
console.log(`[ReconnectableWS] ⏳ Reconnecting in ${Math.round(delay)}ms (attempt=${this.reconnectAttempt + 1})`);
|
|
171
|
-
this.reconnectTimer = setTimeout(() => {
|
|
172
|
-
this.reconnectAttempt++;
|
|
173
|
-
this.connect();
|
|
174
|
-
}, delay);
|
|
175
|
-
}
|
|
176
|
-
getReconnectDelay() {
|
|
177
|
-
const { baseDelay, maxDelay, jitterFactor } = this.opts;
|
|
178
|
-
const exponentialDelay = Math.min(baseDelay * Math.pow(2, this.reconnectAttempt), maxDelay);
|
|
179
|
-
const jitter = exponentialDelay * jitterFactor * (Math.random() * 2 - 1);
|
|
180
|
-
return Math.max(0, exponentialDelay + jitter);
|
|
181
|
-
}
|
|
182
|
-
flushBuffer() {
|
|
183
|
-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN)
|
|
184
|
-
return;
|
|
185
|
-
const buffer = this.messageBuffer;
|
|
186
|
-
this.messageBuffer = [];
|
|
187
|
-
for (const data of buffer) {
|
|
188
|
-
try {
|
|
189
|
-
this.ws.send(data);
|
|
190
|
-
}
|
|
191
|
-
catch (e) {
|
|
192
|
-
console.warn(`[ReconnectableWS] ⚠️ Failed to flush buffered message:`, e);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
if (buffer.length > 0) {
|
|
196
|
-
console.log(`[ReconnectableWS] 📤 Flushed ${buffer.length} buffered messages`);
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
// ─── Heartbeat ───────────────────────────────────────────
|
|
200
|
-
startHeartbeat() {
|
|
201
|
-
this.stopHeartbeat();
|
|
202
|
-
this.heartbeatTimer = setInterval(() => {
|
|
203
|
-
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
204
|
-
this.ws.send('__ping');
|
|
205
|
-
this.heartbeatTimeoutTimer = setTimeout(() => {
|
|
206
|
-
console.log(`[ReconnectableWS] 💔 Heartbeat timeout`);
|
|
207
|
-
// 強制切断 → onclose がリコネクトをトリガー
|
|
208
|
-
this.ws?.close(4001, 'heartbeat_timeout');
|
|
209
|
-
}, this.opts.heartbeatTimeout);
|
|
210
|
-
}
|
|
211
|
-
}, this.opts.heartbeatInterval);
|
|
212
|
-
}
|
|
213
|
-
stopHeartbeat() {
|
|
214
|
-
if (this.heartbeatTimer) {
|
|
215
|
-
clearInterval(this.heartbeatTimer);
|
|
216
|
-
this.heartbeatTimer = null;
|
|
217
|
-
}
|
|
218
|
-
if (this.heartbeatTimeoutTimer) {
|
|
219
|
-
clearTimeout(this.heartbeatTimeoutTimer);
|
|
220
|
-
this.heartbeatTimeoutTimer = null;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
handlePong() {
|
|
224
|
-
if (this.heartbeatTimeoutTimer) {
|
|
225
|
-
clearTimeout(this.heartbeatTimeoutTimer);
|
|
226
|
-
this.heartbeatTimeoutTimer = null;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
}
|
package/dist/room.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
type RoomMessageHandler = (data: Record<string, unknown>) => void;
|
|
2
|
-
/** WebSocket 互換の send/addEventListener インターフェース */
|
|
3
|
-
interface WebSocketLike {
|
|
4
|
-
send(data: string): void;
|
|
5
|
-
addEventListener(type: 'message', handler: (ev: MessageEvent) => void): void;
|
|
6
|
-
}
|
|
7
|
-
/** Room の公開インターフェース。WebSocket / BroadcastChannel どちらでも実装可能。 */
|
|
8
|
-
export interface RoomLike {
|
|
9
|
-
readonly myId: string;
|
|
10
|
-
broadcast(msg: Record<string, unknown>): void;
|
|
11
|
-
send(id: string, msg: Record<string, unknown>): void;
|
|
12
|
-
on(type: string, handler: RoomMessageHandler): void;
|
|
13
|
-
}
|
|
14
|
-
export declare class Room implements RoomLike {
|
|
15
|
-
readonly myId: string;
|
|
16
|
-
private ws;
|
|
17
|
-
private handlers;
|
|
18
|
-
constructor(ws: WebSocketLike, myId: string);
|
|
19
|
-
broadcast(msg: Record<string, unknown>): void;
|
|
20
|
-
send(id: string, msg: Record<string, unknown>): void;
|
|
21
|
-
on(type: string, handler: RoomMessageHandler): void;
|
|
22
|
-
}
|
|
23
|
-
export {};
|
package/dist/room.js
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
export class Room {
|
|
2
|
-
constructor(ws, myId) {
|
|
3
|
-
this.handlers = new Map();
|
|
4
|
-
this.ws = ws;
|
|
5
|
-
this.myId = myId;
|
|
6
|
-
this.ws.addEventListener('message', (ev) => {
|
|
7
|
-
let parsed;
|
|
8
|
-
try {
|
|
9
|
-
parsed = JSON.parse(ev.data);
|
|
10
|
-
}
|
|
11
|
-
catch {
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
const type = parsed.type;
|
|
15
|
-
if (!type)
|
|
16
|
-
return;
|
|
17
|
-
const from = parsed.__from;
|
|
18
|
-
console.log(`[SDK Room] ⬅ recv type=${type} from=${from}`, JSON.stringify(parsed));
|
|
19
|
-
const handlers = this.handlers.get(type) || [];
|
|
20
|
-
handlers.forEach((h) => h({ ...parsed, __from: from }));
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
broadcast(msg) {
|
|
24
|
-
console.log(`[SDK Room] ➡ broadcast type=${msg.type}`, JSON.stringify(msg));
|
|
25
|
-
this.ws.send(JSON.stringify(msg));
|
|
26
|
-
}
|
|
27
|
-
send(id, msg) {
|
|
28
|
-
console.log(`[SDK Room] ➡ send to=${id} type=${msg.type}`, JSON.stringify(msg));
|
|
29
|
-
this.ws.send(JSON.stringify({ ...msg, __to: id }));
|
|
30
|
-
}
|
|
31
|
-
on(type, handler) {
|
|
32
|
-
if (!this.handlers.has(type))
|
|
33
|
-
this.handlers.set(type, []);
|
|
34
|
-
this.handlers.get(type).push(handler);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @docs
|
|
3
|
-
* - ServerAction仕様: docs/docs/uzu_code/connection-method/arch3-authority.md
|
|
4
|
-
* - SDK仕様: docs/docs/uzu_code/play-screen-sdk.md
|
|
5
|
-
*
|
|
6
|
-
* ブラウザ内 LocalGameRoom — GameRoom DO の動作をサーバーレスで再現する。
|
|
7
|
-
* server_url がない場合に vite dev だけで ServerAction シナリオをローカル実行する。
|
|
8
|
-
*
|
|
9
|
-
* online (`runOnlineServerAction`) と挙動を揃えるため、`logic.actions` は同期で実行し
|
|
10
|
-
* `sendAction()` 直後に `onState()` を同期発火する (楽観的更新だけで完結する仕様)。
|
|
11
|
-
* `logic.serverActions` は `Promise<void>` を返しうるので `await` で実行し、完了後に
|
|
12
|
-
* `onState()` を呼ぶ。同名なら actions → serverActions の順で走る (サーバーと同じ)。
|
|
13
|
-
*/
|
|
14
|
-
import type { GameConfig } from '../types.js';
|
|
15
|
-
import type { RunHandle } from '../dev-hooks.js';
|
|
16
|
-
export declare function runLocalServerAction<S>(config: GameConfig<S>): RunHandle<S>;
|