@uzuhq/code-sdk 0.7.6 → 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 -416
- 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/roster-params.test.d.ts +0 -1
- package/dist/roster-params.test.js +0 -86
- 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 -181
- package/dist/run/server-action.test.d.ts +0 -1
- package/dist/run/server-action.test.js +0 -105
- 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uzuhq/code-sdk",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.7",
|
|
4
4
|
"description": "UZU PlayScreen SDK - Flutter ↔ JS ゲーム通信ライブラリ",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -23,12 +23,15 @@
|
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"happy-dom": "^20.10.6",
|
|
26
|
+
"tsdown": "^0.22.14",
|
|
26
27
|
"typescript": "^6.0.0",
|
|
27
|
-
"vitest": "^4.1.10"
|
|
28
|
+
"vitest": "^4.1.10",
|
|
29
|
+
"@uzuhq/code-engine-core": "0.0.0"
|
|
28
30
|
},
|
|
29
31
|
"scripts": {
|
|
30
|
-
"build": "
|
|
31
|
-
"watch": "
|
|
32
|
-
"test": "vitest run"
|
|
32
|
+
"build": "tsdown",
|
|
33
|
+
"watch": "tsdown --watch",
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"lint": "tsc --noEmit"
|
|
33
36
|
}
|
|
34
37
|
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `run()` 経由の `inputs` でも同じ縛りが効くことの検証。
|
|
3
|
-
*
|
|
4
|
-
* `SendAction<A & SA>` は `SA` が `Record<string, …>` に落ちた瞬間
|
|
5
|
-
* `keyof` が `string` へ潰れて action 名の検査が丸ごと消える。 直接
|
|
6
|
-
* `SendAction<…>` を declare するだけの検査では踏めないので、 実際に
|
|
7
|
-
* `run()` を通す形で押さえる。
|
|
8
|
-
*
|
|
9
|
-
* 型検査だけが目的なので関数は呼ばない (`run` は window を触る)。
|
|
10
|
-
*/
|
|
11
|
-
export declare function _runTypeChecks(): void;
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* action 名と payload が型で縛られることの検証。
|
|
3
|
-
*
|
|
4
|
-
* 実行時の振る舞いは無いので、 tsc が通ること自体がテストになる。
|
|
5
|
-
* @ts-expect-error が「実際にエラーになる」ことも同時に確かめている
|
|
6
|
-
* (エラーが出なくなったら @ts-expect-error 自体が未使用エラーになる)。
|
|
7
|
-
*
|
|
8
|
-
* SDK は値を出さない。 logic は `satisfies` でキーを保ってから型注釈で組み立てる。
|
|
9
|
-
* 値を export すると scenario の logic.ts が SDK を実行時 import することになり、
|
|
10
|
-
* サーバー用 bundle (Workers) で解決できず落ちる。
|
|
11
|
-
*/
|
|
12
|
-
import { run } from './index.js';
|
|
13
|
-
const actions = {
|
|
14
|
-
// payload に型を付けた handler
|
|
15
|
-
'vote.submit': ({ state, payload }) => {
|
|
16
|
-
state.count += payload.optionId.length;
|
|
17
|
-
},
|
|
18
|
-
// 注釈のない handler は payload: any のまま (段階的移行)
|
|
19
|
-
noop: ({ state }) => {
|
|
20
|
-
state.count += 1;
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
const serverActions = {
|
|
24
|
-
'server.only': ({ payload }) => {
|
|
25
|
-
payload.token.trim();
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
const _logic = {
|
|
29
|
-
setup: () => ({ count: 0 }),
|
|
30
|
-
actions,
|
|
31
|
-
serverActions,
|
|
32
|
-
update: () => { },
|
|
33
|
-
};
|
|
34
|
-
send('vote.submit', { optionId: 'a' });
|
|
35
|
-
send('noop', { anything: 1 });
|
|
36
|
-
send('noop'); // 注釈なし (any) は省略できる
|
|
37
|
-
send('server.only', { token: 't' });
|
|
38
|
-
// @ts-expect-error 存在しない action 名
|
|
39
|
-
send('vote.submi', { optionId: 'a' });
|
|
40
|
-
// @ts-expect-error payload の形違い
|
|
41
|
-
send('vote.submit', { wrong: 1 });
|
|
42
|
-
// @ts-expect-error payload が必須の action で省略はできない
|
|
43
|
-
send('vote.submit');
|
|
44
|
-
/**
|
|
45
|
-
* `run()` 経由の `inputs` でも同じ縛りが効くことの検証。
|
|
46
|
-
*
|
|
47
|
-
* `SendAction<A & SA>` は `SA` が `Record<string, …>` に落ちた瞬間
|
|
48
|
-
* `keyof` が `string` へ潰れて action 名の検査が丸ごと消える。 直接
|
|
49
|
-
* `SendAction<…>` を declare するだけの検査では踏めないので、 実際に
|
|
50
|
-
* `run()` を通す形で押さえる。
|
|
51
|
-
*
|
|
52
|
-
* 型検査だけが目的なので関数は呼ばない (`run` は window を触る)。
|
|
53
|
-
*/
|
|
54
|
-
export function _runTypeChecks() {
|
|
55
|
-
// serverActions を持たない logic (SA が既定値に落ちる経路)
|
|
56
|
-
run({
|
|
57
|
-
logic: { setup: () => ({ count: 0 }), actions, update: () => { } },
|
|
58
|
-
onState: () => { },
|
|
59
|
-
playerCount: 1,
|
|
60
|
-
inputs: (sendAction) => {
|
|
61
|
-
sendAction('vote.submit', { optionId: 'a' });
|
|
62
|
-
sendAction('noop');
|
|
63
|
-
// @ts-expect-error 存在しない action 名
|
|
64
|
-
sendAction('vote.submi', { optionId: 'a' });
|
|
65
|
-
// @ts-expect-error payload の形違い
|
|
66
|
-
sendAction('vote.submit', { wrong: 1 });
|
|
67
|
-
},
|
|
68
|
-
});
|
|
69
|
-
// 型引数を書かない呼び出しは A が推論されるので検査が効く
|
|
70
|
-
run({
|
|
71
|
-
logic: { setup: () => ({ count: 0 }), actions, update: () => { } },
|
|
72
|
-
onState: () => { },
|
|
73
|
-
playerCount: 1,
|
|
74
|
-
inputs: (sendAction) => {
|
|
75
|
-
sendAction('vote.submit', { optionId: 'a' });
|
|
76
|
-
// @ts-expect-error 存在しない action 名
|
|
77
|
-
sendAction('vote.submi', { optionId: 'a' });
|
|
78
|
-
},
|
|
79
|
-
});
|
|
80
|
-
// 既存シナリオの `run<State>({…})`。 A に既定値が無いと TS2558 で落ちる。
|
|
81
|
-
// 検査は効かなくなるが、 型引数を足さずに済むこと自体が後方互換の条件。
|
|
82
|
-
run({
|
|
83
|
-
logic: { setup: () => ({ count: 0 }), actions, update: () => { } },
|
|
84
|
-
onState: () => { },
|
|
85
|
-
playerCount: 1,
|
|
86
|
-
inputs: (sendAction) => {
|
|
87
|
-
sendAction('vote.submit', { optionId: 'a' });
|
|
88
|
-
},
|
|
89
|
-
});
|
|
90
|
-
// serverActions を持つ logic (SA が推論される経路)
|
|
91
|
-
run({
|
|
92
|
-
logic: { setup: () => ({ count: 0 }), actions, serverActions, update: () => { } },
|
|
93
|
-
onState: () => { },
|
|
94
|
-
playerCount: 1,
|
|
95
|
-
inputs: (sendAction) => {
|
|
96
|
-
sendAction('server.only', { token: 't' });
|
|
97
|
-
// @ts-expect-error 存在しない action 名
|
|
98
|
-
sendAction('server.onl', { token: 't' });
|
|
99
|
-
},
|
|
100
|
-
});
|
|
101
|
-
}
|
package/dist/dev-hooks.d.ts
DELETED
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @docs
|
|
3
|
-
* - 外部 automation API: docs/docs/uzu_code/sdk-guide/dev-hooks.md
|
|
4
|
-
*
|
|
5
|
-
* 外部 automation (Playwright / AI agent / E2E test) が SDK が動いている iframe の
|
|
6
|
-
* state を決定論的に読み書きするための window.__uzu_dev API。
|
|
7
|
-
*
|
|
8
|
-
* Flutter native ホスト (window.FlutterHost あり) では一切 attach されない。
|
|
9
|
-
* dev harness の子 iframe / Playwright iframe / 単独 page では attach される。
|
|
10
|
-
*
|
|
11
|
-
* state 書き換え API:
|
|
12
|
-
* - `setRawState(fullState)` : 全置換
|
|
13
|
-
* - `mergeRawState(patch)` : RFC 7396 風 JSON Merge Patch (object 階層の部分更新)
|
|
14
|
-
* - `patchRawState(ops)` : RFC 6902 JSON Patch (path-based ops、array index 単体書換可)
|
|
15
|
-
*
|
|
16
|
-
* online ServerAction では state が Worker DO 側にあるため 3 つとも undefined。
|
|
17
|
-
*
|
|
18
|
-
* SDK は state shape に依存しない generic primitive だけを提供する。
|
|
19
|
-
* scenario 固有の helper (特定 field path の読み書き / phase 遷移時の field reset /
|
|
20
|
-
* 特定 action 名のラッパー等) は scenario 側で window.__<scene>_dev を生やす。
|
|
21
|
-
*/
|
|
22
|
-
import type { JsonMergePatch, JsonPatchOp } from './dev-state-patch.js';
|
|
23
|
-
import type { PredictionWarning } from './dev-prediction-traps.js';
|
|
24
|
-
import type { ServerEvent } from './types.js';
|
|
25
|
-
export type { JsonMergePatch, JsonPatchOp } from './dev-state-patch.js';
|
|
26
|
-
export { applyJsonMergePatch, applyJsonPatch } from './dev-state-patch.js';
|
|
27
|
-
export interface UzuDevHooks<S = unknown> {
|
|
28
|
-
/** 現在の per-player 視点 snapshot (onState コールバックの最新値)。未初期化なら null。 */
|
|
29
|
-
getSnapshot(): unknown;
|
|
30
|
-
/** 生 server-side state (dev/local mode のみ。online ServerAction では null)。 */
|
|
31
|
-
getRawState(): S | null;
|
|
32
|
-
playerId(): string | null;
|
|
33
|
-
/**
|
|
34
|
-
* 親 frame で virtual server を実体化した時の seed。
|
|
35
|
-
* 親 frame の `runDevHarness({ server })` 経由でしか attach されず、子 iframe の
|
|
36
|
-
* dev hooks 側では undefined。bug report 用に `?__dev_seed=<value>` として
|
|
37
|
-
* 貼り付けやすい uint32 を返す。
|
|
38
|
-
*/
|
|
39
|
-
getSeed?(): number;
|
|
40
|
-
/**
|
|
41
|
-
* 素の action handler が先読み実行中に実時刻 / 乱数を読んだ記録。
|
|
42
|
-
*
|
|
43
|
-
* 先読みはサーバーと同じ結果を再現できることが前提なので、ここに何か入っていたら
|
|
44
|
-
* そのシナリオはオンラインでだけ予測がズレる。E2E で `[]` を assert すると回帰を防げる。
|
|
45
|
-
*/
|
|
46
|
-
getPredictionWarnings(): readonly PredictionWarning[];
|
|
47
|
-
/**
|
|
48
|
-
* Server-side で action を直接 dispatch する。
|
|
49
|
-
*
|
|
50
|
-
* **run devHarness の親 frame のみ** で attach される。それ以外 (runLocal /
|
|
51
|
-
* sync / online) では `undefined`。
|
|
52
|
-
* 非 parent モードで action を投げたい時は scenario 側の bridge
|
|
53
|
-
* (`window.__uzu.sendAction`) や実 UI 操作を使う。
|
|
54
|
-
*
|
|
55
|
-
* `args.as` は **必須**。server-side dispatch の `from` をこの値で指定する
|
|
56
|
-
* (バリデーションはしないので、未登録 ID で投げて action handler に弾かせる
|
|
57
|
-
* 運用も可)。turn-based の E2E (将棋・人狼・カードゲーム) で「先手 → 後手」
|
|
58
|
-
* を 1 frame から打ち分けたい時に使う。
|
|
59
|
-
*/
|
|
60
|
-
send?(args: {
|
|
61
|
-
as: string;
|
|
62
|
-
type: string;
|
|
63
|
-
payload?: Record<string, unknown>;
|
|
64
|
-
}): Promise<void>;
|
|
65
|
-
/**
|
|
66
|
-
* 生 state を **全置換** する。state owner の frame (= dev/local) のみ。
|
|
67
|
-
*
|
|
68
|
-
* `setup` 後の任意の構造を流し込めるので、`getRawState()` の dump を読み込んで
|
|
69
|
-
* バグ再現する用途や、scenario の途中 state を JSON で復元する用途に使う。
|
|
70
|
-
* 部分更新は {@link mergeRawState} を、array 要素単体の書換は
|
|
71
|
-
* {@link patchRawState} を使うこと。
|
|
72
|
-
*/
|
|
73
|
-
setRawState?(state: S): Promise<void>;
|
|
74
|
-
/**
|
|
75
|
-
* RFC 7396 風 JSON Merge Patch で生 state を部分更新する (dev/local のみ)。
|
|
76
|
-
*
|
|
77
|
-
* - object 階層は recursive merge
|
|
78
|
-
* - **array field は atomic replace のみ** (要素単位の merge 不可、RFC 7396 仕様)
|
|
79
|
-
* - `undefined` は no-op、`null` は明示セット (RFC 7396 strict と異なる)
|
|
80
|
-
* - array 内の cell 単体を書き換えたい場合は {@link patchRawState} を使う
|
|
81
|
-
*
|
|
82
|
-
* array が pure object に化ける silent な破壊を防ぐため、`array に
|
|
83
|
-
* non-array object patch を当てる`と **throw** する。
|
|
84
|
-
*/
|
|
85
|
-
mergeRawState?(patch: JsonMergePatch<S>): Promise<void>;
|
|
86
|
-
/**
|
|
87
|
-
* RFC 6902 JSON Patch (path-based ops) で生 state を更新する (dev/local のみ)。
|
|
88
|
-
*
|
|
89
|
-
* - JSON Pointer (`/board/1/4`) で array index 単体の書換が可能
|
|
90
|
-
* - サポートする op: `add` / `remove` / `replace` / `move` / `copy` / `test`
|
|
91
|
-
* - 1 op でも失敗するとその場で throw する (atomic ではない)
|
|
92
|
-
*
|
|
93
|
-
* 局面の部分更新 (将棋の一手・盤面の cell 単体書換など) に使う。
|
|
94
|
-
*/
|
|
95
|
-
patchRawState?(ops: JsonPatchOp[]): Promise<void>;
|
|
96
|
-
/**
|
|
97
|
-
* snapshot 更新を event-driven に listen する。返り値は unsubscribe 関数。
|
|
98
|
-
*
|
|
99
|
-
* `waitForSnapshot` が「特定条件まで待つ」のに対し、こちらは「state 変化を全部
|
|
100
|
-
* 取って trace test を書く」「deterministic record を作る」「sequence assert」
|
|
101
|
-
* のような用途で使う canonical な listener。
|
|
102
|
-
*
|
|
103
|
-
* cb には authoritative state の生参照 (run devHarness 親では `getRawState()` と
|
|
104
|
-
* 同一) が渡るので mutate しない。
|
|
105
|
-
*/
|
|
106
|
-
subscribeSnapshot(cb: (snapshot: unknown) => void): () => void;
|
|
107
|
-
/** snapshot が predicate を満たすまで待つ。default 10 秒 timeout。 */
|
|
108
|
-
waitForSnapshot(predicate: (snapshot: unknown) => boolean, options?: {
|
|
109
|
-
timeoutMs?: number;
|
|
110
|
-
}): Promise<unknown>;
|
|
111
|
-
/**
|
|
112
|
-
* action handler / `logic.update` の `emit` を listen する。callback には 1 回の
|
|
113
|
-
* dispatch / tick 内で発火した event を batch でまとめて渡す。**run devHarness 親
|
|
114
|
-
* frame のみ** で attach され、それ以外 (runLocal ソロ /
|
|
115
|
-
* sync / online) では `undefined`。
|
|
116
|
-
*
|
|
117
|
-
* `getSnapshot()` に乗らない raw payload (`gameover` の `reason` 等) を assert
|
|
118
|
-
* したい場合に使う。`config.events` callback とは独立経路で、scenario callback の
|
|
119
|
-
* throw も subscriber 通知を止めない。unsubscribe 関数を返す。
|
|
120
|
-
*/
|
|
121
|
-
subscribeEvents?(cb: (events: readonly ServerEvent[]) => void): () => void;
|
|
122
|
-
/**
|
|
123
|
-
* tick loop を pause する (run() devHarness 親 frame のみ)。
|
|
124
|
-
* 時間駆動の `logic.update` が走らなくなり、shogi の持ち時間減算等の自動進行を
|
|
125
|
-
* 止められる。pause 中も `send` (action dispatch) と state 書換 API は通る。
|
|
126
|
-
* tick loop を持たない frame (子 iframe / sync 単独 frame / online) と
|
|
127
|
-
* `tickRate <= 0` の scenario では undefined / no-op。
|
|
128
|
-
*/
|
|
129
|
-
pauseTick?(): void;
|
|
130
|
-
/** pauseTick を解除する。 */
|
|
131
|
-
resumeTick?(): void;
|
|
132
|
-
/** 手動で n tick 進める。default n = 1。 */
|
|
133
|
-
stepTick?(n?: number): void;
|
|
134
|
-
/** 現在の tick 値を返す。tick loop を持たない frame では undefined。 */
|
|
135
|
-
getCurrentTick?(): number;
|
|
136
|
-
/** pauseTick で止まっているかどうか。 */
|
|
137
|
-
isTickPaused?(): boolean;
|
|
138
|
-
/**
|
|
139
|
-
* `logic.setup` を再実行して virtual server を fresh start させる。
|
|
140
|
-
* run() devHarness の親 frame のみ attach される (state owner でしか reset 不可)。
|
|
141
|
-
*
|
|
142
|
-
* - `seed` 省略時は現在の seed を再利用 (= 決定論的同一初期化)
|
|
143
|
-
* - 数値を渡すとその seed で再初期化、`getSeed()` の戻り値も更新
|
|
144
|
-
* - `'random'` で新 seed (uint32) を生成
|
|
145
|
-
*
|
|
146
|
-
* pause 状態は維持される。`logic.setup` が副作用を持つ場合は再呼出で副作用が再発火する。
|
|
147
|
-
*/
|
|
148
|
-
reset?(opts?: {
|
|
149
|
-
seed?: number | 'random';
|
|
150
|
-
}): void;
|
|
151
|
-
}
|
|
152
|
-
declare global {
|
|
153
|
-
interface Window {
|
|
154
|
-
__uzu_dev?: UzuDevHooks;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* run/* モジュール (run/local-server-action / run/dev-server-action) が
|
|
159
|
-
* dev hooks に state を expose するための handle。
|
|
160
|
-
* online (server-action) モードでは handle 自体を返さない。
|
|
161
|
-
*
|
|
162
|
-
* state 書き換え API は state owner frame のみ提供する。
|
|
163
|
-
*/
|
|
164
|
-
export interface RunHandle<S = unknown> {
|
|
165
|
-
/** 内部 state の getter (dev hooks 用)。host iframe を持たない側では undefined */
|
|
166
|
-
getRawState?(): S | null;
|
|
167
|
-
/** 生 state 全置換。host iframe を持たない側では undefined */
|
|
168
|
-
setRawState?(state: S): Promise<void>;
|
|
169
|
-
/** RFC 7396 風 Merge Patch。host iframe を持たない側では undefined */
|
|
170
|
-
mergeRawState?(patch: JsonMergePatch<S>): Promise<void>;
|
|
171
|
-
/** RFC 6902 JSON Patch。host iframe を持たない側では undefined */
|
|
172
|
-
patchRawState?(ops: JsonPatchOp[]): Promise<void>;
|
|
173
|
-
/** 既存 inputs と同等の action 送信 (dev hooks 用) */
|
|
174
|
-
sendAction(type: string, payload?: Record<string, unknown>): void;
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* sync/* モジュール (sync/dev / sync/local) が dev hooks に state を expose
|
|
178
|
-
* するための handle。online sync では handle 自体を返さない。
|
|
179
|
-
*/
|
|
180
|
-
export interface SyncHandle<S = unknown> {
|
|
181
|
-
/** 内部 state の getter (dev hooks 用) */
|
|
182
|
-
getRawState?(): S | null;
|
|
183
|
-
/** 生 state 全置換 */
|
|
184
|
-
setRawState?(state: S): Promise<void>;
|
|
185
|
-
/** RFC 7396 風 Merge Patch */
|
|
186
|
-
mergeRawState?(patch: JsonMergePatch<S>): Promise<void>;
|
|
187
|
-
/** RFC 6902 JSON Patch */
|
|
188
|
-
patchRawState?(ops: JsonPatchOp[]): Promise<void>;
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* dev-hooks.ts から見た「SDK 内部のコンテキスト」。
|
|
192
|
-
* 各 run/* / sync/* モジュールがこの形の handle を組み立てて attachDevHooks に渡す。
|
|
193
|
-
*/
|
|
194
|
-
export interface DevHooksCtx<S = unknown> {
|
|
195
|
-
/** 最新 snapshot (per-player view)。未初期化なら null。 */
|
|
196
|
-
getSnapshot(): unknown;
|
|
197
|
-
/** 生 state getter (dev/local mode のみ)。 */
|
|
198
|
-
getRawState?: () => S | null;
|
|
199
|
-
/** 生 state 全置換 (dev/local mode のみ)。 */
|
|
200
|
-
setRawState?: (state: S) => Promise<void>;
|
|
201
|
-
/** RFC 7396 風 Merge Patch (dev/local mode のみ)。 */
|
|
202
|
-
mergeRawState?: (patch: JsonMergePatch<S>) => Promise<void>;
|
|
203
|
-
/** RFC 6902 JSON Patch (dev/local mode のみ)。 */
|
|
204
|
-
patchRawState?: (ops: JsonPatchOp[]) => Promise<void>;
|
|
205
|
-
/** 現在の player ID (URL クエリ or runtime 解決)。 */
|
|
206
|
-
playerId(): string | null;
|
|
207
|
-
/**
|
|
208
|
-
* Action 送信。run devHarness 親 frame の `DevHostHandle` のみが
|
|
209
|
-
* 提供する。それ以外のモードでは省略 (= `__uzu_dev.send` も生えない)。
|
|
210
|
-
* `args.as` で server-side dispatch の `from` を指定する (必須)。
|
|
211
|
-
*/
|
|
212
|
-
sendAction?(args: {
|
|
213
|
-
as: string;
|
|
214
|
-
type: string;
|
|
215
|
-
payload?: Record<string, unknown>;
|
|
216
|
-
}): Promise<void>;
|
|
217
|
-
/** snapshot 更新を listen する。unsubscribe 関数を返す。 */
|
|
218
|
-
subscribeSnapshot(cb: (snapshot: unknown) => void): () => void;
|
|
219
|
-
/**
|
|
220
|
-
* action handler / `logic.update` 由来の emit を listen する。run devHarness 親
|
|
221
|
-
* frame の `DevHostHandle` のみが提供する。それ以外のモードでは省略
|
|
222
|
-
* (= `__uzu_dev.subscribeEvents` も生えない)。
|
|
223
|
-
*/
|
|
224
|
-
subscribeEvents?(cb: (events: readonly ServerEvent[]) => void): () => void;
|
|
225
|
-
/** dev harness 親 frame の virtual server seed。親 attach 経路のみ。 */
|
|
226
|
-
getSeed?: () => number;
|
|
227
|
-
pauseTick?: () => void;
|
|
228
|
-
resumeTick?: () => void;
|
|
229
|
-
stepTick?: (n?: number) => void;
|
|
230
|
-
getCurrentTick?: () => number;
|
|
231
|
-
isTickPaused?: () => boolean;
|
|
232
|
-
/**
|
|
233
|
-
* run() devHarness 親 frame の `DevHostHandle.reset` を expose する。
|
|
234
|
-
* state owner でしか fresh start できないので、それ以外の mode では undefined。
|
|
235
|
-
*/
|
|
236
|
-
reset?: (opts?: {
|
|
237
|
-
seed?: number | 'random';
|
|
238
|
-
}) => void;
|
|
239
|
-
}
|
|
240
|
-
export declare function createDevHooks<S>(ctx: DevHooksCtx<S>): UzuDevHooks<S>;
|
|
241
|
-
export declare function attachDevHooks<S>(ctx: DevHooksCtx<S>): void;
|
package/dist/dev-hooks.js
DELETED
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @docs
|
|
3
|
-
* - 外部 automation API: docs/docs/uzu_code/sdk-guide/dev-hooks.md
|
|
4
|
-
*
|
|
5
|
-
* 外部 automation (Playwright / AI agent / E2E test) が SDK が動いている iframe の
|
|
6
|
-
* state を決定論的に読み書きするための window.__uzu_dev API。
|
|
7
|
-
*
|
|
8
|
-
* Flutter native ホスト (window.FlutterHost あり) では一切 attach されない。
|
|
9
|
-
* dev harness の子 iframe / Playwright iframe / 単独 page では attach される。
|
|
10
|
-
*
|
|
11
|
-
* state 書き換え API:
|
|
12
|
-
* - `setRawState(fullState)` : 全置換
|
|
13
|
-
* - `mergeRawState(patch)` : RFC 7396 風 JSON Merge Patch (object 階層の部分更新)
|
|
14
|
-
* - `patchRawState(ops)` : RFC 6902 JSON Patch (path-based ops、array index 単体書換可)
|
|
15
|
-
*
|
|
16
|
-
* online ServerAction では state が Worker DO 側にあるため 3 つとも undefined。
|
|
17
|
-
*
|
|
18
|
-
* SDK は state shape に依存しない generic primitive だけを提供する。
|
|
19
|
-
* scenario 固有の helper (特定 field path の読み書き / phase 遷移時の field reset /
|
|
20
|
-
* 特定 action 名のラッパー等) は scenario 側で window.__<scene>_dev を生やす。
|
|
21
|
-
*/
|
|
22
|
-
import { getPredictionWarnings } from './dev-prediction-traps.js';
|
|
23
|
-
export { applyJsonMergePatch, applyJsonPatch } from './dev-state-patch.js';
|
|
24
|
-
export function createDevHooks(ctx) {
|
|
25
|
-
const getRawState = () => (ctx.getRawState ? ctx.getRawState() : null);
|
|
26
|
-
const waitForSnapshot = (predicate, options) => {
|
|
27
|
-
const timeoutMs = options?.timeoutMs ?? 10000;
|
|
28
|
-
return new Promise((resolve, reject) => {
|
|
29
|
-
const current = ctx.getSnapshot();
|
|
30
|
-
if (current != null) {
|
|
31
|
-
let ok = false;
|
|
32
|
-
try {
|
|
33
|
-
ok = predicate(current);
|
|
34
|
-
}
|
|
35
|
-
catch (err) {
|
|
36
|
-
reject(err);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
if (ok) {
|
|
40
|
-
resolve(current);
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
const timer = setTimeout(() => {
|
|
45
|
-
unsubscribe();
|
|
46
|
-
reject(new Error(`[uzu_dev] waitForSnapshot timed out after ${timeoutMs}ms`));
|
|
47
|
-
}, timeoutMs);
|
|
48
|
-
const unsubscribe = ctx.subscribeSnapshot((snap) => {
|
|
49
|
-
let ok;
|
|
50
|
-
try {
|
|
51
|
-
ok = predicate(snap);
|
|
52
|
-
}
|
|
53
|
-
catch (err) {
|
|
54
|
-
clearTimeout(timer);
|
|
55
|
-
unsubscribe();
|
|
56
|
-
reject(err);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
if (ok) {
|
|
60
|
-
clearTimeout(timer);
|
|
61
|
-
unsubscribe();
|
|
62
|
-
resolve(snap);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
};
|
|
67
|
-
const hooks = {
|
|
68
|
-
getSnapshot: () => ctx.getSnapshot(),
|
|
69
|
-
getRawState,
|
|
70
|
-
playerId: () => ctx.playerId(),
|
|
71
|
-
subscribeSnapshot: (cb) => ctx.subscribeSnapshot(cb),
|
|
72
|
-
waitForSnapshot,
|
|
73
|
-
getPredictionWarnings: () => getPredictionWarnings(),
|
|
74
|
-
};
|
|
75
|
-
if (ctx.sendAction) {
|
|
76
|
-
const sendAction = ctx.sendAction;
|
|
77
|
-
hooks.send = (args) => sendAction(args);
|
|
78
|
-
}
|
|
79
|
-
if (ctx.setRawState) {
|
|
80
|
-
const setRawState = ctx.setRawState;
|
|
81
|
-
hooks.setRawState = (state) => setRawState(state);
|
|
82
|
-
}
|
|
83
|
-
if (ctx.mergeRawState) {
|
|
84
|
-
const mergeRawState = ctx.mergeRawState;
|
|
85
|
-
hooks.mergeRawState = (patch) => mergeRawState(patch);
|
|
86
|
-
}
|
|
87
|
-
if (ctx.patchRawState) {
|
|
88
|
-
const patchRawState = ctx.patchRawState;
|
|
89
|
-
hooks.patchRawState = (ops) => patchRawState(ops);
|
|
90
|
-
}
|
|
91
|
-
if (ctx.getSeed) {
|
|
92
|
-
const getSeed = ctx.getSeed;
|
|
93
|
-
hooks.getSeed = () => getSeed();
|
|
94
|
-
}
|
|
95
|
-
// Tick 制御: ctx 側で提供されている時だけ wire する。子 iframe / sync /
|
|
96
|
-
// online 等 tick loop を持たない frame では undefined のままなので、scenario 側で
|
|
97
|
-
// `__uzu_dev.pauseTick?.()` の optional chain で safe に呼べる。
|
|
98
|
-
if (ctx.pauseTick) {
|
|
99
|
-
const pauseTick = ctx.pauseTick;
|
|
100
|
-
hooks.pauseTick = () => pauseTick();
|
|
101
|
-
}
|
|
102
|
-
if (ctx.resumeTick) {
|
|
103
|
-
const resumeTick = ctx.resumeTick;
|
|
104
|
-
hooks.resumeTick = () => resumeTick();
|
|
105
|
-
}
|
|
106
|
-
if (ctx.stepTick) {
|
|
107
|
-
const stepTick = ctx.stepTick;
|
|
108
|
-
hooks.stepTick = (n) => stepTick(n);
|
|
109
|
-
}
|
|
110
|
-
if (ctx.getCurrentTick) {
|
|
111
|
-
const getCurrentTick = ctx.getCurrentTick;
|
|
112
|
-
hooks.getCurrentTick = () => getCurrentTick();
|
|
113
|
-
}
|
|
114
|
-
if (ctx.isTickPaused) {
|
|
115
|
-
const isTickPaused = ctx.isTickPaused;
|
|
116
|
-
hooks.isTickPaused = () => isTickPaused();
|
|
117
|
-
}
|
|
118
|
-
if (ctx.reset) {
|
|
119
|
-
const reset = ctx.reset;
|
|
120
|
-
hooks.reset = (opts) => reset(opts);
|
|
121
|
-
}
|
|
122
|
-
if (ctx.subscribeEvents) {
|
|
123
|
-
const subscribeEvents = ctx.subscribeEvents;
|
|
124
|
-
hooks.subscribeEvents = (cb) => subscribeEvents(cb);
|
|
125
|
-
}
|
|
126
|
-
return hooks;
|
|
127
|
-
}
|
|
128
|
-
export function attachDevHooks(ctx) {
|
|
129
|
-
// `init()` → `run()` の順で 2 回 attach されるのは正常フロー (run/sync 経由なら 1 回のみ)。
|
|
130
|
-
// silently 上書きする。
|
|
131
|
-
window.__uzu_dev = createDevHooks(ctx);
|
|
132
|
-
}
|
package/dist/dev-hooks.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|